3 * File name: ir/debug/debugger.c
4 * Purpose: Helper function for integerated debug support
9 * Copyright: (c) 2001-2005 Universität Karlsruhe
10 * Licence: This file protected by GPL - GNU GENERAL PUBLIC LICENSE.
19 #define WIN32_LEAN_AND_MEAN
39 #include "irgraph_t.h"
44 /** Break into the debugger. The Win32 way. */
45 static void firm_debug_break(void) {
48 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64))
49 /** Break into the debugger. The ia32 way under GCC. */
50 static void firm_debug_break(void) {
51 __asm__ __volatile__("int3");
54 /** Break into the debugger. Poor Unix way. */
55 static void firm_debug_break(void) {
60 /** supported breakpoint kinds */
62 BP_NODE = 'n', /**< break on node number. */
63 BP_IDENT = 'i' /**< break on ident. */
67 * Reasons for node number breakpoints.
69 typedef enum _bp_reasons_t {
70 BP_ON_CREATION = 1, /**< break if node with number is created */
71 BP_ON_REPLACE = 2, /**< break if node with number is replaced */
72 BP_ON_LOWER = 3, /**< break if node with number is lowered */
73 BP_ON_REMIRG = 4, /**< break if an IRG is removed */
78 typedef struct _breakpoint {
79 bp_kind kind; /**< the kind of this break point */
80 unsigned bpnr; /**< break point number */
81 int active; /**< non-zero, if this break point is active */
82 bp_reasons_t reason; /**< reason for the breakpoint */
83 struct _breakpoint *next; /**< link to the next one */
86 /** A node number breakpoint. */
88 breakpoint bp; /**< the breakpoint data */
89 long nr; /**< the node number */
92 /** calculate the hash value for a node breakpoint */
93 #define HASH_NODE_BP(key) (((key).nr << 2) ^ (key).bp.reason)
95 /** A ident breakpoint. */
97 breakpoint bp; /**< the breakpoint data */
98 ident *id; /**< the ident */
101 /** calculate the hash value for an ident breakpoint */
102 #define HASH_IDENT_BP(key) (HASH_PTR((key).id) ^ (key).bp.reason)
104 /** The set containing the breakpoints on node numbers. */
105 static set *bp_node_numbers;
107 /** The set containing the breakpoints on idents. */
108 static set *bp_idents;
110 /**< the list of all breakpoints */
111 static breakpoint *bp_list;
113 /** number of the current break point */
114 static unsigned bp_num = 0;
116 /** set if break on init command was issued. */
117 static int break_on_init = 0;
119 /** the hook entries for the Firm debugger module. */
120 static hook_entry_t debugger_hooks[hook_last];
122 /** number of active breakpoints to maintain hooks. */
123 static unsigned num_active_bp[BP_MAX_REASON];
125 /** hook the hook h with function fkt. */
126 #define HOOK(h, fkt) \
127 debugger_hooks[h].hook._##h = fkt; register_hook(h, &debugger_hooks[h])
129 /** unhook the hook h */
130 #define UNHOOK(h) unregister_hook(h, &debugger_hooks[h])
132 /** returns non-zero if a entry hook h is used */
133 #define IS_HOOKED(h) (debugger_hooks[h].next != NULL)
136 * A new node is created.
138 * @param ctx the hook context
139 * @param irg the IR graph on which the node is created
140 * @param node the new IR node that was created
142 static void dbg_new_node(void *ctx, ir_graph *irg, ir_node *node)
144 bp_node_t key, *elem;
146 key.nr = get_irn_node_nr(node);
147 key.bp.reason = BP_ON_CREATION;
149 elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
150 if (elem && elem->bp.active) {
151 ir_printf("Firm BP %u reached, %+F created\n", elem->bp.bpnr, node);
157 * A node is replaced.
159 * @param ctx the hook context
160 * @param old the IR node the is replaced
161 * @param nw the new IR node that will replace old
163 static void dbg_replace(void *ctx, ir_node *old, ir_node *nw)
165 bp_node_t key, *elem;
167 key.nr = get_irn_node_nr(old);
168 key.bp.reason = BP_ON_REPLACE;
170 elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
171 if (elem && elem->bp.active) {
172 ir_printf("Firm BP %u reached, %+F will be replaced by %+F\n", elem->bp.bpnr, old, nw);
178 * A new node is lowered.
180 * @param ctx the hook context
181 * @param node the new IR node that will be lowered
183 static void dbg_lower_node(void *ctx, ir_node *node)
185 bp_node_t key, *elem;
187 key.nr = get_irn_node_nr(node);
188 key.bp.reason = BP_ON_LOWER;
190 elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
191 if (elem && elem->bp.active) {
192 ir_printf("Firm BP %u reached, %+F will be lowered\n", elem->bp.bpnr, node);
198 * A graph will be deleted.
200 * @param ctx the hook context
201 * @param irg the IR graph that will be deleted
203 static void dbg_free_graph(void *context, ir_graph *irg)
205 bp_ident_t key, *elem;
206 entity *ent = get_irg_entity(irg);
211 key.id = get_entity_ident(ent);
212 key.bp.reason = BP_ON_REMIRG;
214 elem = set_find(bp_idents, &key, sizeof(key), HASH_IDENT_BP(key));
215 if (elem && elem->bp.active) {
216 ir_printf("Firm BP %u reached, %+F will be deleted\n", elem->bp.bpnr, ent);
222 * return the reason string.
224 static const char *reason_str(bp_reasons_t reason)
227 case BP_ON_CREATION: return "creation";
228 case BP_ON_REPLACE: return "replacing";
229 case BP_ON_LOWER: return "lowering";
230 case BP_ON_REMIRG: return "removing IRG";
237 * Compare two node number breakpoints
239 static int cmp_node_bp(const void *elt, const void *key, size_t size)
241 const bp_node_t *e1 = elt;
242 const bp_node_t *e2 = key;
244 return (e1->nr - e2->nr) | (e1->bp.reason - e2->bp.reason);
248 * Compare two ident breakpoints
250 static int cmp_ident_bp(const void *elt, const void *key, size_t size)
252 const bp_ident_t *e1 = elt;
253 const bp_ident_t *e2 = key;
255 return (e1->id != e2->id) | (e1->bp.reason - e2->bp.reason);
261 static void update_hooks(breakpoint *bp)
264 ++num_active_bp[bp->reason];
266 --num_active_bp[bp->reason];
268 if (num_active_bp[bp->reason] > 0) {
269 /* register the hooks on demand */
270 switch (bp->reason) {
272 if (! IS_HOOKED(hook_new_node))
273 HOOK(hook_new_node, dbg_new_node);
277 if (! IS_HOOKED(hook_replace))
278 HOOK(hook_replace, dbg_replace);
281 if (! IS_HOOKED(hook_lower))
282 HOOK(hook_lower, dbg_lower_node);
286 if (! IS_HOOKED(hook_free_graph))
287 HOOK(hook_free_graph, dbg_free_graph);
295 /* unregister the hook on demand */
296 switch (bp->reason) {
298 if (IS_HOOKED(hook_new_node))
299 UNHOOK(hook_new_node);
303 if (IS_HOOKED(hook_replace))
304 UNHOOK(hook_replace);
307 if (IS_HOOKED(hook_lower))
312 if (IS_HOOKED(hook_free_graph))
313 UNHOOK(hook_free_graph);
323 * Break if node nr is reached.
325 static void break_on_node(long nr, bp_reasons_t reason)
327 bp_node_t key, *elem;
329 key.bp.kind = BP_NODE;
332 key.bp.reason = reason;
335 elem = set_insert(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
337 if (elem->bp.bpnr == 0) {
338 /* new break point */
339 elem->bp.bpnr = ++bp_num;
340 elem->bp.next = bp_list;
343 printf("Firm BP %u: %s of Node %ld\n", elem->bp.bpnr, reason_str(reason), nr);
345 update_hooks(&elem->bp);
350 * Break if ident name is reached.
352 static void break_on_ident(const char *name, bp_reasons_t reason) {
353 bp_ident_t key, *elem;
355 key.bp.kind = BP_IDENT;
358 key.bp.reason = reason;
359 key.id = new_id_from_str(name);
361 elem = set_insert(bp_idents, &key, sizeof(key), HASH_IDENT_BP(key));
363 if (elem->bp.bpnr == 0) {
364 /* new break point */
365 elem->bp.bpnr = ++bp_num;
366 elem->bp.next = bp_list;
369 printf("Firm BP %u: %s of ident \"%s\"\n", elem->bp.bpnr, reason_str(reason), name);
371 update_hooks(&elem->bp);
376 * Sets/resets the active flag of breakpoint bp.
378 static void bp_activate(unsigned bp, int active)
382 for (p = bp_list; p; p = p->next) {
384 if (p->active != active) {
389 printf("Firm BP %u is now %s\n", bp, active ? "enabled" : "disabled");
393 printf("Error: Firm BP %u not exists.\n", bp);
398 * Show a list of supported commands
400 static void show_commands(void) {
401 printf("Internal Firm debugger extension $Revision$ commands:\n"
402 ".init break after initialization\n"
403 ".create nr break if node nr was created\n"
404 ".replace nr break if node nr is replaced by another node\n"
405 ".lower nr break before node nr is lowered\n"
406 ".remirg name break if the irg of entity name is deleted\n"
407 ".bp show all breakpoints\n"
408 ".enable nr enable breakpoint nr\n"
409 ".disable nr disable breakpoint nr\n"
410 ".help list all commands\n"
415 * Shows all Firm breakpoints.
417 static void show_bp(void) {
422 for (p = bp_list; p; p = p->next) {
423 printf("Firm BP %u: ", p->bpnr);
427 node_p = (bp_node_t *)p;
428 printf("%s of node %ld ", reason_str(p->reason), node_p->nr);
432 ident_p = (bp_ident_t *)p;
433 printf("%s of ident \"%s\" ", reason_str(p->reason), get_id_str(ident_p->id));
437 printf(p->active ? "enabled\n" : "disabled\n");
442 * High level function to use from debugger interface
444 * Supported commands:
445 * .create nr break if node nr was created
446 * .help list all commands
448 void firm_break(const char *cmd) {
453 while (isspace(*cmd)) ++cmd;
455 if (sscanf(cmd, ".create %ld\n", &nr) == 1) {
456 break_on_node(nr, BP_ON_CREATION);
458 else if (sscanf(cmd, ".replace %ld\n", &nr) == 1) {
459 break_on_node(nr, BP_ON_REPLACE);
461 else if (sscanf(cmd, ".lower %ld\n", &nr) == 1) {
462 break_on_node(nr, BP_ON_LOWER);
464 else if (sscanf(cmd, ".remirg %s\n", name) == 1) {
465 break_on_ident(name, BP_ON_REMIRG);
467 else if (strcmp(cmd, ".init") == 0)
469 else if (strcmp(cmd, ".bp") == 0)
471 else if (sscanf(cmd, ".enable %u", &bp) == 1)
473 else if (sscanf(cmd, ".disable %u", &bp) == 1)
480 /* creates the debugger tables */
481 void firm_init_debugger(void)
485 bp_node_numbers = new_set(cmp_node_bp, 8);
486 bp_idents = new_set(cmp_ident_bp, 8);
488 env = getenv("FIRMDBG");
500 * @page debugger The Firm debugger extension.
502 * Firm contains a debugger extension. This allows to set debugger breakpoints
504 * The extension uses a text interface which can be access in the debugger.
506 * The following commands are currently supported:
510 * Break immediately after the debugger extension was initialized.
511 * Typically this command is used in the environment to stop the execution
512 * of a Firm compiler right after the initialization, like this:
514 * $export FIRMDBG=".init"
519 * Break if a new IR-node with node number nr was created.
520 * Typically used to find the place where wrong nodes are created.
524 * Break before IR-node with node number nr is replaced by another node.
528 * Break before IR-node with node number nr is lowered.
532 * Break if the irg of entity name is deleted.
535 * Show all Firm internal breakpoints.
539 * Enables breakpoint nr.
543 * Disables breakpoint nr.
550 * The Firm debugger extension can be accessed using the function firm_break().
551 * The following example shows how to set a creation breakpoint in GDB when
552 * node 2101 is created.
554 * 1.) set FIRMDBG=".init"
555 * 2.) start gdb with your compiler
556 * 3.) after gdb breaks, issue
558 * p firm_debug(".create 2101")
560 * On the console the following text should be issued:
562 * Firm BP 1: creation of Node 2101