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_NEW_NODE = 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 */
74 BP_ON_NEW_ENT = 5, /**< break if a new entity is created */
79 typedef struct _breakpoint {
80 bp_kind kind; /**< the kind of this break point */
81 unsigned bpnr; /**< break point number */
82 int active; /**< non-zero, if this break point is active */
83 bp_reasons_t reason; /**< reason for the breakpoint */
84 struct _breakpoint *next; /**< link to the next one */
87 /** A node number breakpoint. */
89 breakpoint bp; /**< the breakpoint data */
90 long nr; /**< the node number */
93 /** calculate the hash value for a node breakpoint */
94 #define HASH_NODE_BP(key) (((key).nr << 2) ^ (key).bp.reason)
96 /** A ident breakpoint. */
98 breakpoint bp; /**< the breakpoint data */
99 ident *id; /**< the ident */
102 /** calculate the hash value for an ident breakpoint */
103 #define HASH_IDENT_BP(key) (HASH_PTR((key).id) ^ (key).bp.reason)
105 /** The set containing the breakpoints on node numbers. */
106 static set *bp_node_numbers;
108 /** The set containing the breakpoints on idents. */
109 static set *bp_idents;
111 /**< the list of all breakpoints */
112 static breakpoint *bp_list;
114 /** number of the current break point */
115 static unsigned bp_num = 0;
117 /** set if break on init command was issued. */
118 static int break_on_init = 0;
120 /** the hook entries for the Firm debugger module. */
121 static hook_entry_t debugger_hooks[hook_last];
123 /** number of active breakpoints to maintain hooks. */
124 static unsigned num_active_bp[BP_MAX_REASON];
126 /** hook the hook h with function fkt. */
127 #define HOOK(h, fkt) \
128 debugger_hooks[h].hook._##h = fkt; register_hook(h, &debugger_hooks[h])
130 /** unhook the hook h */
131 #define UNHOOK(h) unregister_hook(h, &debugger_hooks[h])
133 /** returns non-zero if a entry hook h is used */
134 #define IS_HOOKED(h) (debugger_hooks[h].next != NULL)
137 * A new node is created.
139 * @param ctx the hook context
140 * @param irg the IR graph on which the node is created
141 * @param node the new IR node that was created
143 static void dbg_new_node(void *ctx, ir_graph *irg, ir_node *node)
145 bp_node_t key, *elem;
147 key.nr = get_irn_node_nr(node);
148 key.bp.reason = BP_ON_NEW_NODE;
150 elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
151 if (elem && elem->bp.active) {
152 ir_printf("Firm BP %u reached, %+F created\n", elem->bp.bpnr, node);
158 * A node is replaced.
160 * @param ctx the hook context
161 * @param old the IR node the is replaced
162 * @param nw the new IR node that will replace old
164 static void dbg_replace(void *ctx, ir_node *old, ir_node *nw)
166 bp_node_t key, *elem;
168 key.nr = get_irn_node_nr(old);
169 key.bp.reason = BP_ON_REPLACE;
171 elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
172 if (elem && elem->bp.active) {
173 ir_printf("Firm BP %u reached, %+F will be replaced by %+F\n", elem->bp.bpnr, old, nw);
179 * A new node is lowered.
181 * @param ctx the hook context
182 * @param node the new IR node that will be lowered
184 static void dbg_lower_node(void *ctx, ir_node *node)
186 bp_node_t key, *elem;
188 key.nr = get_irn_node_nr(node);
189 key.bp.reason = BP_ON_LOWER;
191 elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
192 if (elem && elem->bp.active) {
193 ir_printf("Firm BP %u reached, %+F will be lowered\n", elem->bp.bpnr, node);
199 * A graph will be deleted.
201 * @param ctx the hook context
202 * @param irg the IR graph that will be deleted
204 static void dbg_free_graph(void *context, ir_graph *irg)
206 bp_ident_t key, *elem;
207 entity *ent = get_irg_entity(irg);
212 key.id = get_entity_ident(ent);
213 key.bp.reason = BP_ON_REMIRG;
215 elem = set_find(bp_idents, &key, sizeof(key), HASH_IDENT_BP(key));
216 if (elem && elem->bp.active) {
217 ir_printf("Firm BP %u reached, %+F will be deleted\n", elem->bp.bpnr, ent);
223 * An entity was created.
225 * @param ctx the hook context
226 * @param ent the newly created entity
228 static void dbg_new_entity(void *context, entity *ent)
230 bp_ident_t key, *elem;
232 key.id = get_entity_ident(ent);
233 key.bp.reason = BP_ON_NEW_ENT;
235 elem = set_find(bp_idents, &key, sizeof(key), HASH_IDENT_BP(key));
236 if (elem && elem->bp.active) {
237 ir_printf("Firm BP %u reached, %+F was created\n", elem->bp.bpnr, ent);
244 * return the reason string.
246 static const char *reason_str(bp_reasons_t reason)
249 case BP_ON_NEW_NODE: return "node creation";
250 case BP_ON_REPLACE: return "node replace";
251 case BP_ON_LOWER: return "node lowering";
252 case BP_ON_REMIRG: return "removing IRG";
253 case BP_ON_NEW_ENT: return "entity creation";
260 * Compare two node number breakpoints
262 static int cmp_node_bp(const void *elt, const void *key, size_t size)
264 const bp_node_t *e1 = elt;
265 const bp_node_t *e2 = key;
267 return (e1->nr - e2->nr) | (e1->bp.reason - e2->bp.reason);
271 * Compare two ident breakpoints
273 static int cmp_ident_bp(const void *elt, const void *key, size_t size)
275 const bp_ident_t *e1 = elt;
276 const bp_ident_t *e2 = key;
278 return (e1->id != e2->id) | (e1->bp.reason - e2->bp.reason);
284 static void update_hooks(breakpoint *bp)
287 ++num_active_bp[bp->reason];
289 --num_active_bp[bp->reason];
291 if (num_active_bp[bp->reason] > 0) {
292 /* register the hooks on demand */
293 switch (bp->reason) {
295 if (! IS_HOOKED(hook_new_node))
296 HOOK(hook_new_node, dbg_new_node);
300 if (! IS_HOOKED(hook_replace))
301 HOOK(hook_replace, dbg_replace);
304 if (! IS_HOOKED(hook_lower))
305 HOOK(hook_lower, dbg_lower_node);
309 if (! IS_HOOKED(hook_free_graph))
310 HOOK(hook_free_graph, dbg_free_graph);
314 if (! IS_HOOKED(hook_new_entity))
315 HOOK(hook_new_entity, dbg_new_entity);
323 /* unregister the hook on demand */
324 switch (bp->reason) {
326 if (IS_HOOKED(hook_new_node))
327 UNHOOK(hook_new_node);
331 if (IS_HOOKED(hook_replace))
332 UNHOOK(hook_replace);
335 if (IS_HOOKED(hook_lower))
340 if (IS_HOOKED(hook_free_graph))
341 UNHOOK(hook_free_graph);
345 if (IS_HOOKED(hook_new_entity))
346 UNHOOK(hook_new_entity);
356 * Break if node nr is reached.
358 static void break_on_node(long nr, bp_reasons_t reason)
360 bp_node_t key, *elem;
362 key.bp.kind = BP_NODE;
365 key.bp.reason = reason;
368 elem = set_insert(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
370 if (elem->bp.bpnr == 0) {
371 /* new break point */
372 elem->bp.bpnr = ++bp_num;
373 elem->bp.next = bp_list;
376 printf("Firm BP %u: %s of Node %ld\n", elem->bp.bpnr, reason_str(reason), nr);
378 update_hooks(&elem->bp);
383 * Break if ident name is reached.
385 static void break_on_ident(const char *name, bp_reasons_t reason) {
386 bp_ident_t key, *elem;
388 key.bp.kind = BP_IDENT;
391 key.bp.reason = reason;
392 key.id = new_id_from_str(name);
394 elem = set_insert(bp_idents, &key, sizeof(key), HASH_IDENT_BP(key));
396 if (elem->bp.bpnr == 0) {
397 /* new break point */
398 elem->bp.bpnr = ++bp_num;
399 elem->bp.next = bp_list;
402 printf("Firm BP %u: %s of ident \"%s\"\n", elem->bp.bpnr, reason_str(reason), name);
404 update_hooks(&elem->bp);
409 * Sets/resets the active flag of breakpoint bp.
411 static void bp_activate(unsigned bp, int active)
415 for (p = bp_list; p; p = p->next) {
417 if (p->active != active) {
422 printf("Firm BP %u is now %s\n", bp, active ? "enabled" : "disabled");
426 printf("Error: Firm BP %u not exists.\n", bp);
431 * Show a list of supported commands
433 static void show_commands(void) {
434 printf("Internal Firm debugger extension $Revision$ commands:\n"
435 ".init break after initialization\n"
436 ".create nr break if node nr was created\n"
437 ".replace nr break if node nr is replaced by another node\n"
438 ".lower nr break before node nr is lowered\n"
439 ".remirg name break if the irg of entity name is deleted\n"
440 ".newent name break if the entity name was created\n"
441 ".bp show all breakpoints\n"
442 ".enable nr enable breakpoint nr\n"
443 ".disable nr disable breakpoint nr\n"
444 ".help list all commands\n"
449 * Shows all Firm breakpoints.
451 static void show_bp(void) {
456 for (p = bp_list; p; p = p->next) {
457 printf("Firm BP %u: ", p->bpnr);
461 node_p = (bp_node_t *)p;
462 printf("%s of node %ld ", reason_str(p->reason), node_p->nr);
466 ident_p = (bp_ident_t *)p;
467 printf("%s of ident \"%s\" ", reason_str(p->reason), get_id_str(ident_p->id));
471 printf(p->active ? "enabled\n" : "disabled\n");
476 * High level function to use from debugger interface
478 * Supported commands:
479 * .create nr break if node nr was created
480 * .help list all commands
482 void firm_break(const char *cmd) {
487 while (isspace(*cmd)) ++cmd;
489 if (sscanf(cmd, ".create %ld\n", &nr) == 1) {
490 break_on_node(nr, BP_ON_NEW_NODE);
492 else if (sscanf(cmd, ".replace %ld\n", &nr) == 1) {
493 break_on_node(nr, BP_ON_REPLACE);
495 else if (sscanf(cmd, ".lower %ld\n", &nr) == 1) {
496 break_on_node(nr, BP_ON_LOWER);
498 else if (sscanf(cmd, ".remirg %s\n", name) == 1) {
499 break_on_ident(name, BP_ON_REMIRG);
501 else if (sscanf(cmd, ".newent %s\n", name) == 1) {
502 break_on_ident(name, BP_ON_NEW_ENT);
504 else if (strcmp(cmd, ".init") == 0)
506 else if (strcmp(cmd, ".bp") == 0)
508 else if (sscanf(cmd, ".enable %u", &bp) == 1)
510 else if (sscanf(cmd, ".disable %u", &bp) == 1)
517 /* creates the debugger tables */
518 void firm_init_debugger(void)
522 bp_node_numbers = new_set(cmp_node_bp, 8);
523 bp_idents = new_set(cmp_ident_bp, 8);
525 env = getenv("FIRMDBG");
537 * @page debugger The Firm debugger extension.
539 * Firm contains a debugger extension. This allows to set debugger breakpoints
541 * The extension uses a text interface which can be access in the debugger.
543 * The following commands are currently supported:
547 * Break immediately after the debugger extension was initialized.
548 * Typically this command is used in the environment to stop the execution
549 * of a Firm compiler right after the initialization, like this:
551 * $export FIRMDBG=".init"
556 * Break if a new IR-node with node number nr was created.
557 * Typically used to find the place where wrong nodes are created.
561 * Break before IR-node with node number nr is replaced by another node.
565 * Break before IR-node with node number nr is lowered.
569 * Break if the irg of entity name is deleted.
573 * Break if the entity name was created.
577 * Show all Firm internal breakpoints.
581 * Enables breakpoint nr.
585 * Disables breakpoint nr.
592 * The Firm debugger extension can be accessed using the function firm_break().
593 * The following example shows how to set a creation breakpoint in GDB when
594 * node 2101 is created.
596 * 1.) set FIRMDBG=".init"
597 * 2.) start gdb with your compiler
598 * 3.) after gdb breaks, issue
600 * p firm_debug(".create 2101")
602 * On the console the following text should be issued:
604 * Firm BP 1: creation of Node 2101