X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fdebug%2Fdebugger.c;h=9d84c9500a131d6ce9a8922f45d9a456e1e7e226;hb=097a68ff443e267f0de3d5455a580f8394f710b2;hp=18694e42bcc4974f261c198cc5ad5011a7caed2a;hpb=37340a3fe776a835758f3362257c99f3ccb7c535;p=libfirm diff --git a/ir/debug/debugger.c b/ir/debug/debugger.c index 18694e42b..9d84c9500 100644 --- a/ir/debug/debugger.c +++ b/ir/debug/debugger.c @@ -39,27 +39,28 @@ #include "irgraph_t.h" #include "entity_t.h" #include "irprintf.h" +#include "debug.h" #ifdef _WIN32 -/** Break into the debugger. The Win32 way. */ -static void firm_debug_break(void) { +/* Break into the debugger. The Win32 way. */ +void firm_debug_break(void) { DebugBreak(); } #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64)) -/** Break into the debugger. The ia32 way under GCC. */ -static void firm_debug_break(void) { +/* Break into the debugger. The ia32/x86_64 way under GCC. */ +void firm_debug_break(void) { __asm__ __volatile__("int3"); } #else -/** Break into the debugger. Poor Unix way. */ -static void firm_debug_break(void) { +/* Break into the debugger. Poor Unix way. */ +void firm_debug_break(void) { raise(SIGINT); } #endif /* _WIN32 */ /** supported breakpoint kinds */ typedef enum { - BP_NODE = 'n', /**< break on node number. */ + BP_NR = 'n', /**< break on node number. */ BP_IDENT = 'i' /**< break on ident. */ } bp_kind; @@ -67,10 +68,12 @@ typedef enum { * Reasons for node number breakpoints. */ typedef enum _bp_reasons_t { - BP_ON_CREATION = 1, /**< break if node with number is created */ - BP_ON_REPLACE = 2, /**< break if node with number is replaced */ - BP_ON_LOWER = 3, /**< break if node with number is lowered */ - BP_ON_REMIRG = 4, /**< break if an IRG is removed */ + BP_ON_NEW_NODE, /**< break if node with number is created */ + BP_ON_REPLACE, /**< break if node with number is replaced */ + BP_ON_LOWER, /**< break if node with number is lowered */ + BP_ON_REMIRG, /**< break if an IRG is removed */ + BP_ON_NEW_ENT, /**< break if a new entity is created */ + BP_ON_NEW_TYPE, /**< break if a new type is created */ BP_MAX_REASON } bp_reasons_t; @@ -83,14 +86,14 @@ typedef struct _breakpoint { struct _breakpoint *next; /**< link to the next one */ } breakpoint; -/** A node number breakpoint. */ +/** A number breakpoint. */ typedef struct { breakpoint bp; /**< the breakpoint data */ long nr; /**< the node number */ -} bp_node_t; +} bp_nr_t; /** calculate the hash value for a node breakpoint */ -#define HASH_NODE_BP(key) (((key).nr << 2) ^ (key).bp.reason) +#define HASH_NR_BP(key) (((key).nr << 2) ^ (key).bp.reason) /** A ident breakpoint. */ typedef struct { @@ -102,7 +105,7 @@ typedef struct { #define HASH_IDENT_BP(key) (HASH_PTR((key).id) ^ (key).bp.reason) /** The set containing the breakpoints on node numbers. */ -static set *bp_node_numbers; +static set *bp_numbers; /** The set containing the breakpoints on idents. */ static set *bp_idents; @@ -122,15 +125,106 @@ static hook_entry_t debugger_hooks[hook_last]; /** number of active breakpoints to maintain hooks. */ static unsigned num_active_bp[BP_MAX_REASON]; +/** + * The debug message buffer + */ +static char firm_dbg_msg_buf[2048]; + +/** + * If set, the debug extension writes all output to the + * firm_dbg_msg_buf buffer + */ +static int redir_output = 0; + +/** + * Is set to one, if the debug extension is active + */ +static int is_active = 0; + /** hook the hook h with function fkt. */ #define HOOK(h, fkt) \ - debugger_hooks[h].hook._##h = fkt; register_hook(h, &debugger_hooks[h]) +do { \ + debugger_hooks[h].hook._##h = fkt; \ + register_hook(h, &debugger_hooks[h]); \ +} while(0) /** unhook the hook h */ -#define UNHOOK(h) unregister_hook(h, &debugger_hooks[h]) +#define UNHOOK(h) \ +do { \ + unregister_hook(h, &debugger_hooks[h]); \ + debugger_hooks[h].hook._##h = NULL; \ +} while(0) /** returns non-zero if a entry hook h is used */ -#define IS_HOOKED(h) (debugger_hooks[h].next != NULL) +#define IS_HOOKED(h) (debugger_hooks[h].hook._##h != NULL) + +/* some macros needed to create the info string */ +#define _DBG_VERSION(major, minor) #major "." #minor +#define DBG_VERSION(major, minor) _DBG_VERSION(major, minor) +#define API_VERSION(major, minor) "API:" DBG_VERSION(major, minor) + +/* the API version: change if needed */ +#define FIRM_DBG_MAJOR 1 +#define FIRM_DBG_MINOR 0 + +/** for automatic detection of the debug extension */ +static const char *firm_debug_info_string = + API_VERSION(FIRM_DBG_MAJOR, FIRM_DBG_MINOR) + ; + +/** + * Returns non-zero, if the debug extension is active + */ +int firm_debug_active(void) { + return is_active; +} + +/** + * reset the debug text buffer + */ +static void reset_dbg_buf(void) { + firm_dbg_msg_buf[0] = '\0'; +} + +/** + * Add text to the debug text buffer + */ +static void add_to_dbg_buf(const char *buf) { + strncat(firm_dbg_msg_buf, buf, sizeof(firm_dbg_msg_buf)); +} + +/** + * Return the content of the debug text buffer. + * + * To be called from the debugger. + */ +const char *firm_debug_text(void) { + return firm_dbg_msg_buf; +} + +/** + * debug output + */ +static void dbg_printf(const char *fmt, ...) +{ + char buf[1024]; + + va_list args; + va_start(args, fmt); + + if (fmt[0] != '+') + reset_dbg_buf(); + else + ++fmt; + + ir_vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + if (redir_output) + add_to_dbg_buf(buf); + else + puts(buf); +} /** * A new node is created. @@ -141,14 +235,14 @@ static unsigned num_active_bp[BP_MAX_REASON]; */ static void dbg_new_node(void *ctx, ir_graph *irg, ir_node *node) { - bp_node_t key, *elem; + bp_nr_t key, *elem; key.nr = get_irn_node_nr(node); - key.bp.reason = BP_ON_CREATION; + key.bp.reason = BP_ON_NEW_NODE; - elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key)); + elem = set_find(bp_numbers, &key, sizeof(key), HASH_NR_BP(key)); if (elem && elem->bp.active) { - ir_printf("Firm BP %u reached, %+F created\n", elem->bp.bpnr, node); + dbg_printf("Firm BP %u reached, %+F created\n", elem->bp.bpnr, node); firm_debug_break(); } } @@ -162,14 +256,14 @@ static void dbg_new_node(void *ctx, ir_graph *irg, ir_node *node) */ static void dbg_replace(void *ctx, ir_node *old, ir_node *nw) { - bp_node_t key, *elem; + bp_nr_t key, *elem; key.nr = get_irn_node_nr(old); key.bp.reason = BP_ON_REPLACE; - elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key)); + elem = set_find(bp_numbers, &key, sizeof(key), HASH_NR_BP(key)); if (elem && elem->bp.active) { - ir_printf("Firm BP %u reached, %+F will be replaced by %+F\n", elem->bp.bpnr, old, nw); + dbg_printf("Firm BP %u reached, %+F will be replaced by %+F\n", elem->bp.bpnr, old, nw); firm_debug_break(); } } @@ -180,16 +274,16 @@ static void dbg_replace(void *ctx, ir_node *old, ir_node *nw) * @param ctx the hook context * @param node the new IR node that will be lowered */ -static void dbg_lower_node(void *ctx, ir_node *node) +static void dbg_lower(void *ctx, ir_node *node) { - bp_node_t key, *elem; + bp_nr_t key, *elem; key.nr = get_irn_node_nr(node); key.bp.reason = BP_ON_LOWER; - elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key)); + elem = set_find(bp_numbers, &key, sizeof(key), HASH_NR_BP(key)); if (elem && elem->bp.active) { - ir_printf("Firm BP %u reached, %+F will be lowered\n", elem->bp.bpnr, node); + dbg_printf("Firm BP %u reached, %+F will be lowered\n", elem->bp.bpnr, node); firm_debug_break(); } } @@ -200,21 +294,102 @@ static void dbg_lower_node(void *ctx, ir_node *node) * @param ctx the hook context * @param irg the IR graph that will be deleted */ -static void dbg_free_graph(void *context, ir_graph *irg) +static void dbg_free_graph(void *ctx, ir_graph *irg) { - bp_ident_t key, *elem; - entity *ent = get_irg_entity(irg); + { + bp_nr_t key, *elem; + key.nr = get_irg_graph_nr(irg); + key.bp.reason = BP_ON_REMIRG; + + elem = set_find(bp_numbers, &key, sizeof(key), HASH_NR_BP(key)); + if (elem && elem->bp.active) { + ir_printf("Firm BP %u reached, %+F will be deleted\n", elem->bp.bpnr, irg); + firm_debug_break(); + } + } + { + bp_ident_t key, *elem; + entity *ent = get_irg_entity(irg); - if (! ent) - return; + if (! ent) + return; - key.id = get_entity_ident(ent); - key.bp.reason = BP_ON_REMIRG; + key.id = get_entity_ident(ent); + key.bp.reason = BP_ON_REMIRG; - elem = set_find(bp_idents, &key, sizeof(key), HASH_IDENT_BP(key)); - if (elem && elem->bp.active) { - ir_printf("Firm BP %u reached, %+F will be deleted\n", elem->bp.bpnr, ent); - firm_debug_break(); + elem = set_find(bp_idents, &key, sizeof(key), HASH_IDENT_BP(key)); + if (elem && elem->bp.active) { + dbg_printf("Firm BP %u reached, %+F will be deleted\n", elem->bp.bpnr, ent); + firm_debug_break(); + } + } +} + +/** + * An entity was created. + * + * @param ctx the hook context + * @param ent the newly created entity + */ +static void dbg_new_entity(void *ctx, entity *ent) +{ + { + bp_ident_t key, *elem; + + key.id = get_entity_ident(ent); + key.bp.reason = BP_ON_NEW_ENT; + + elem = set_find(bp_idents, &key, sizeof(key), HASH_IDENT_BP(key)); + if (elem && elem->bp.active) { + ir_printf("Firm BP %u reached, %+F was created\n", elem->bp.bpnr, ent); + firm_debug_break(); + } + } + { + bp_nr_t key, *elem; + + key.nr = get_entity_nr(ent); + key.bp.reason = BP_ON_NEW_ENT; + + elem = set_find(bp_numbers, &key, sizeof(key), HASH_NR_BP(key)); + if (elem && elem->bp.active) { + dbg_printf("Firm BP %u reached, %+F was created\n", elem->bp.bpnr, ent); + firm_debug_break(); + } + } +} + +/** + * A type was created. + * + * @param ctx the hook context + * @param tp the newly created type + */ +static void dbg_new_type(void *ctx, ir_type *tp) +{ + { + bp_nr_t key, *elem; + + key.nr = get_type_nr(tp); + key.bp.reason = BP_ON_NEW_TYPE; + + elem = set_find(bp_numbers, &key, sizeof(key), HASH_NR_BP(key)); + if (elem && elem->bp.active) { + ir_printf("Firm BP %u reached, %+F was created\n", elem->bp.bpnr, tp); + firm_debug_break(); + } + } + { + bp_ident_t key, *elem; + + key.id = get_type_ident(tp); + key.bp.reason = BP_ON_NEW_TYPE; + + elem = set_find(bp_idents, &key, sizeof(key), HASH_IDENT_BP(key)); + if (elem && elem->bp.active) { + dbg_printf("Firm BP %u reached, %+F was created\n", elem->bp.bpnr, tp); + firm_debug_break(); + } } } @@ -224,22 +399,24 @@ static void dbg_free_graph(void *context, ir_graph *irg) static const char *reason_str(bp_reasons_t reason) { switch (reason) { - case BP_ON_CREATION: return "creation"; - case BP_ON_REPLACE: return "replacing"; - case BP_ON_LOWER: return "lowering"; + case BP_ON_NEW_NODE: return "node creation"; + case BP_ON_REPLACE: return "node replace"; + case BP_ON_LOWER: return "node lowering"; case BP_ON_REMIRG: return "removing IRG"; + case BP_ON_NEW_ENT: return "entity creation"; + case BP_ON_NEW_TYPE: return "type creation"; default: assert(0); } return "unknown"; } /** - * Compare two node number breakpoints + * Compare two number breakpoints */ -static int cmp_node_bp(const void *elt, const void *key, size_t size) +static int cmp_nr_bp(const void *elt, const void *key, size_t size) { - const bp_node_t *e1 = elt; - const bp_node_t *e2 = key; + const bp_nr_t *e1 = elt; + const bp_nr_t *e2 = key; return (e1->nr - e2->nr) | (e1->bp.reason - e2->bp.reason); } @@ -260,6 +437,9 @@ static int cmp_ident_bp(const void *elt, const void *key, size_t size) */ static void update_hooks(breakpoint *bp) { +#define CASE_ON(a, b) case a: if (! IS_HOOKED(hook_##b)) HOOK(hook_##b, dbg_##b); break +#define CASE_OFF(a, b) case a: if (IS_HOOKED(hook_##b)) UNHOOK(hook_##b); break + if (bp->active) ++num_active_bp[bp->reason]; else @@ -268,25 +448,12 @@ static void update_hooks(breakpoint *bp) if (num_active_bp[bp->reason] > 0) { /* register the hooks on demand */ switch (bp->reason) { - case BP_ON_CREATION: - if (! IS_HOOKED(hook_new_node)) - HOOK(hook_new_node, dbg_new_node); - break; - - case BP_ON_REPLACE: - if (! IS_HOOKED(hook_replace)) - HOOK(hook_replace, dbg_replace); - - case BP_ON_LOWER: - if (! IS_HOOKED(hook_lower)) - HOOK(hook_lower, dbg_lower_node); - break; - - case BP_ON_REMIRG: - if (! IS_HOOKED(hook_free_graph)) - HOOK(hook_free_graph, dbg_free_graph); - break; - + CASE_ON(BP_ON_NEW_NODE, new_node); + CASE_ON(BP_ON_REPLACE, replace); + CASE_ON(BP_ON_LOWER, lower); + CASE_ON(BP_ON_REMIRG, free_graph); + CASE_ON(BP_ON_NEW_ENT, new_entity); + CASE_ON(BP_ON_NEW_TYPE, new_type); default: ; } @@ -294,45 +461,34 @@ static void update_hooks(breakpoint *bp) else { /* unregister the hook on demand */ switch (bp->reason) { - case BP_ON_CREATION: - if (IS_HOOKED(hook_new_node)) - UNHOOK(hook_new_node); - break; - - case BP_ON_REPLACE: - if (IS_HOOKED(hook_replace)) - UNHOOK(hook_replace); - - case BP_ON_LOWER: - if (IS_HOOKED(hook_lower)) - UNHOOK(hook_lower); - break; - - case BP_ON_REMIRG: - if (IS_HOOKED(hook_free_graph)) - UNHOOK(hook_free_graph); - break; - + CASE_OFF(BP_ON_NEW_NODE, new_node); + CASE_OFF(BP_ON_REPLACE, replace); + CASE_OFF(BP_ON_LOWER, lower); + CASE_OFF(BP_ON_REMIRG, free_graph); + CASE_OFF(BP_ON_NEW_ENT, new_entity); + CASE_OFF(BP_ON_NEW_TYPE, new_type); default: ; } } +#undef CASE_ON +#undef CASE_OFF } /** - * Break if node nr is reached. + * Break if nr is reached. */ -static void break_on_node(long nr, bp_reasons_t reason) +static void break_on_nr(long nr, bp_reasons_t reason) { - bp_node_t key, *elem; + bp_nr_t key, *elem; - key.bp.kind = BP_NODE; + key.bp.kind = BP_NR; key.bp.bpnr = 0; key.bp.active = 1; key.bp.reason = reason; key.nr = nr; - elem = set_insert(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key)); + elem = set_insert(bp_numbers, &key, sizeof(key), HASH_NR_BP(key)); if (elem->bp.bpnr == 0) { /* new break point */ @@ -340,7 +496,7 @@ static void break_on_node(long nr, bp_reasons_t reason) elem->bp.next = bp_list; bp_list = &elem->bp; - printf("Firm BP %u: %s of Node %ld\n", elem->bp.bpnr, reason_str(reason), nr); + dbg_printf("Firm BP %u: %s of Nr %ld\n", elem->bp.bpnr, reason_str(reason), nr); update_hooks(&elem->bp); } @@ -366,7 +522,7 @@ static void break_on_ident(const char *name, bp_reasons_t reason) { elem->bp.next = bp_list; bp_list = &elem->bp; - printf("Firm BP %u: %s of ident \"%s\"\n", elem->bp.bpnr, reason_str(reason), name); + dbg_printf("Firm BP %u: %s of ident \"%s\"\n", elem->bp.bpnr, reason_str(reason), name); update_hooks(&elem->bp); } @@ -386,11 +542,11 @@ static void bp_activate(unsigned bp, int active) update_hooks(p); } - printf("Firm BP %u is now %s\n", bp, active ? "enabled" : "disabled"); + dbg_printf("Firm BP %u is now %s\n", bp, active ? "enabled" : "disabled"); return; } } - printf("Error: Firm BP %u not exists.\n", bp); + dbg_printf("Error: Firm BP %u not exists.\n", bp); } @@ -398,16 +554,21 @@ static void bp_activate(unsigned bp, int active) * Show a list of supported commands */ static void show_commands(void) { - printf("Internal Firm debugger extension $Revision$ commands:\n" - ".init break after initialization\n" - ".create nr break if node nr was created\n" - ".replace nr break if node nr is replaced by another node\n" - ".lower nr break before node nr is lowered\n" - ".remirg name break if the irg of entity name is deleted\n" - ".bp show all breakpoints\n" - ".enable nr enable breakpoint nr\n" - ".disable nr disable breakpoint nr\n" - ".help list all commands\n" + dbg_printf("Internal Firm debugger extension $Revision$ commands:\n" + ".init break after initialization\n" + ".create nr break if node nr was created\n" + ".replace nr break if node nr is replaced by another node\n" + ".lower nr break before node nr is lowered\n" + ".remirg nr|name break if the irg of nr or entity name is deleted\n" + ".newent nr|name break if the entity nr or name was created\n" + ".newtype nr|name break if the type nr or name was created\n" + ".bp show all breakpoints\n" + ".enable nr enable breakpoint nr\n" + ".disable nr disable breakpoint nr\n" + ".setmask name msk sets the debug module name to mask msk\n" + ".setlvl name lvl sets the debug module name to level lvl\n" + ".setoutfile name file redirects debug output of module name to file\n" + ".help list all commands\n" ); } @@ -416,26 +577,69 @@ static void show_commands(void) { */ static void show_bp(void) { breakpoint *p; - bp_node_t *node_p; + bp_nr_t *node_p; bp_ident_t *ident_p; + int have_one = 0; + dbg_printf("Firm Breakpoints:"); for (p = bp_list; p; p = p->next) { - printf("Firm BP %u: ", p->bpnr); + have_one = 1; + dbg_printf("+\n BP %u: ", p->bpnr); switch (p->kind) { - case BP_NODE: - node_p = (bp_node_t *)p; - printf("%s of node %ld ", reason_str(p->reason), node_p->nr); + case BP_NR: + node_p = (bp_nr_t *)p; + dbg_printf("%s of Nr %ld ", reason_str(p->reason), node_p->nr); break; case BP_IDENT: ident_p = (bp_ident_t *)p; - printf("%s of ident \"%s\" ", reason_str(p->reason), get_id_str(ident_p->id)); + dbg_printf("+%s of ident \"%s\" ", reason_str(p->reason), get_id_str(ident_p->id)); break; } - printf(p->active ? "enabled\n" : "disabled\n"); + dbg_printf(p->active ? "+enabled" : "+disabled"); } + dbg_printf(have_one ? "+\n" : "+ NONE\n"); +} + +/** + * firm_dbg_register() expects that the name is stored persistent. + * So we need this little helper function + */ +static firm_dbg_module_t *dbg_register(const char *name) { + ident *id = new_id_from_str(name); + + return firm_dbg_register(get_id_str(id)); +} + +/** + * Sets the debug mask of module name to lvl + */ +static void set_dbg_level(const char *name, unsigned lvl) +{ + firm_dbg_module_t *module = dbg_register(name); + + firm_dbg_set_mask(module, lvl); + + dbg_printf("Setting debug mask of module %s to %u\n", name, lvl); +} + +/** + * Redirects the debug output of module name to fname + */ +static void set_dbg_outfile(const char *name, const char *fname) +{ + firm_dbg_module_t *module = dbg_register(name); + FILE *f = fopen(fname, "w"); + + if (! f) { + perror(fname); + return; + } + + firm_dbg_set_file(module, f); + dbg_printf("Redirecting debug output of module %s to file %s\n", name, fname); } /** @@ -445,25 +649,41 @@ static void show_bp(void) { * .create nr break if node nr was created * .help list all commands */ -void firm_break(const char *cmd) { +void firm_debug(const char *cmd) { long nr; unsigned bp; - char name[1024]; + char name[1024], fname[1024]; + unsigned lvl; while (isspace(*cmd)) ++cmd; if (sscanf(cmd, ".create %ld\n", &nr) == 1) { - break_on_node(nr, BP_ON_CREATION); + break_on_nr(nr, BP_ON_NEW_NODE); } else if (sscanf(cmd, ".replace %ld\n", &nr) == 1) { - break_on_node(nr, BP_ON_REPLACE); + break_on_nr(nr, BP_ON_REPLACE); } else if (sscanf(cmd, ".lower %ld\n", &nr) == 1) { - break_on_node(nr, BP_ON_LOWER); + break_on_nr(nr, BP_ON_LOWER); + } + else if (sscanf(cmd, ".remirg %ld\n", &nr) == 1) { + break_on_nr(nr, BP_ON_REMIRG); } else if (sscanf(cmd, ".remirg %s\n", name) == 1) { break_on_ident(name, BP_ON_REMIRG); } + else if (sscanf(cmd, ".newent %ld\n", &nr) == 1) { + break_on_nr(nr, BP_ON_NEW_ENT); + } + else if (sscanf(cmd, ".newent %s\n", name) == 1) { + break_on_ident(name, BP_ON_NEW_ENT); + } + else if (sscanf(cmd, ".newtype %ld\n", &nr) == 1) { + break_on_nr(nr, BP_ON_NEW_TYPE); + } + else if (sscanf(cmd, ".newtype %s\n", name) == 1) { + break_on_ident(name, BP_ON_NEW_TYPE); + } else if (strcmp(cmd, ".init") == 0) break_on_init = 1; else if (strcmp(cmd, ".bp") == 0) @@ -472,6 +692,12 @@ void firm_break(const char *cmd) { bp_activate(bp, 1); else if (sscanf(cmd, ".disable %u", &bp) == 1) bp_activate(bp, 0); + else if (sscanf(cmd, ".setmask %s %u\n", name, &lvl) == 2) + set_dbg_level(name, lvl); + else if (sscanf(cmd, ".setlvl %s %u\n", name, &lvl) == 2) + set_dbg_level(name, (1 << lvl) - 1); + else if (sscanf(cmd, ".setoutfile %s %s\n", name, fname) == 2) + set_dbg_outfile(name, fname); else { show_commands(); } @@ -482,30 +708,38 @@ void firm_init_debugger(void) { char *env; - bp_node_numbers = new_set(cmp_node_bp, 8); - bp_idents = new_set(cmp_ident_bp, 8); + bp_numbers = new_set(cmp_nr_bp, 8); + bp_idents = new_set(cmp_ident_bp, 8); env = getenv("FIRMDBG"); + is_active = 1; + if (env) - firm_break(env); + firm_debug(env); if (break_on_init) firm_debug_break(); } +#else + +/* some picky compiler do not allow empty files */ +static int _firm_only_that_you_can_compile_with_NDEBUG_defined; + #endif /* NDEBUG */ /** - * @page debugger The Firm debugger extension. + * @page debugger The Firm debugger extension * * Firm contains a debugger extension. This allows to set debugger breakpoints * an various events. - * The extension uses a text interface which can be access in the debugger. + * The extension uses a text interface which can be accessed from most debuggers. * - * The following commands are currently supported: + * @section sec_cmd Supported commands * - * .init + * The following commands are currently supported: + * @b .init * * Break immediately after the debugger extension was initialized. * Typically this command is used in the environment to stop the execution @@ -514,50 +748,102 @@ void firm_init_debugger(void) * $export FIRMDBG=".init" * * - * .create nr + * @b .create nr * * Break if a new IR-node with node number nr was created. * Typically used to find the place where wrong nodes are created. * - * .replace nr + * @b .replace nr * * Break before IR-node with node number nr is replaced by another node. * - * .lower nr + * @b .lower nr * * Break before IR-node with node number nr is lowered. * - * .remirg name + * @b .remirg nr + * + * Break if the irg with graph number nr is deleted. + * + * @b .remirg name * * Break if the irg of entity name is deleted. - * .bp + * + * @b .newent nr + * + * Break if the entity with number nr was created. + * + * @b .newent name + * + * Break if the entity name was created. + * + * @b .newtype nr + * + * Break if the type with number nr was created. + * + * @b .newtype name + * + * Break if the type name was created. + * + * @b .bp * * Show all Firm internal breakpoints. * - * .enable nr + * @b .enable nr * * Enables breakpoint nr. * - * .disable nr + * @b .disable nr * * Disables breakpoint nr. * - * .help + * @b .setmask name msk + * + * Sets the debug module name to mask msk. + * + * @b .setlvl name lvl + * + * Sets the debug module name to level lvl. + * + * @b .setoutfile name file + * + * Redirects debug output of module name to file. + * + * @b .help * * List all commands. * * - * The Firm debugger extension can be accessed using the function firm_break(). + * The Firm debugger extension can be accessed using the function firm_debug(). * The following example shows how to set a creation breakpoint in GDB when * node 2101 is created. * - * 1.) set FIRMDBG=".init" - * 2.) start gdb with your compiler - * 3.) after gdb breaks, issue + * -# set FIRMDBG=".init" + * -# start gdb with your compiler + * -# after gdb breaks, issue * * p firm_debug(".create 2101") * * On the console the following text should be issued: * * Firm BP 1: creation of Node 2101 + * + * + * @section gdb_macro GDB macro + * + * Add the following to your .gdbinit file: + * @code + # + # define firm "cmd" Firm debugger extension + # + define firm + p firm_debug($arg0) + end + * @endcode + * + * Then, all Firm debugger extension commands can be access in the gdb + * console using the firm prefix, eg.: + * + * firm ".create 2101" + * firm ".help" */