+/** the hook entries for the Firm debugger module. */
+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) \
+do { \
+ debugger_hooks[h].hook._##h = fkt; \
+ register_hook(h, &debugger_hooks[h]); \
+} while(0)
+
+/** unhook the hook 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].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;
+} /* firm_debug_active */
+
+/**
+ * Reset the debug text buffer.
+ */
+static void reset_dbg_buf(void) {
+ firm_dbg_msg_buf[0] = '\0';
+} /* reset_dbg_buf */
+
+/**
+ * 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));
+} /* add_to_dbg_buf */
+
+/**
+ * Return the content of the debug text buffer.
+ *
+ * To be called from the debugger.
+ */
+const char *firm_debug_text(void) {
+ firm_dbg_msg_buf[sizeof(firm_dbg_msg_buf) - 1] = '\0';
+ return firm_dbg_msg_buf;
+} /* firm_debug_text */
+
+/**
+ * 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);
+} /* dbg_printf */
+
+/**
+ * A new node is created.
+ *
+ * @param ctx the hook context
+ * @param irg the IR graph on which the node is created
+ * @param node the new IR node that was created
+ */
+static void dbg_new_node(void *ctx, ir_graph *irg, ir_node *node)
+{
+ bp_nr_t key, *elem;
+
+ key.nr = get_irn_node_nr(node);
+ key.bp.reason = BP_ON_NEW_NODE;
+
+ elem = set_find(bp_numbers, &key, sizeof(key), HASH_NR_BP(key));
+ if (elem && elem->bp.active) {
+ dbg_printf("Firm BP %u reached, %+F created\n", elem->bp.bpnr, node);
+ firm_debug_break();
+ }
+} /* dbg_new_node */
+
+/**
+ * A node is replaced.
+ *
+ * @param ctx the hook context
+ * @param old the IR node the is replaced
+ * @param nw the new IR node that will replace old
+ */
+static void dbg_replace(void *ctx, ir_node *old, ir_node *nw)
+{
+ bp_nr_t key, *elem;
+
+ key.nr = get_irn_node_nr(old);
+ key.bp.reason = BP_ON_REPLACE;
+
+ elem = set_find(bp_numbers, &key, sizeof(key), HASH_NR_BP(key));
+ if (elem && elem->bp.active) {
+ dbg_printf("Firm BP %u reached, %+F will be replaced by %+F\n", elem->bp.bpnr, old, nw);
+ firm_debug_break();
+ }
+} /* dbg_replace */
+
+/**
+ * A new node is lowered.
+ *
+ * @param ctx the hook context
+ * @param node the new IR node that will be lowered
+ */
+static void dbg_lower(void *ctx, ir_node *node)
+{
+ bp_nr_t key, *elem;
+
+ key.nr = get_irn_node_nr(node);
+ key.bp.reason = BP_ON_LOWER;
+
+ elem = set_find(bp_numbers, &key, sizeof(key), HASH_NR_BP(key));
+ if (elem && elem->bp.active) {
+ dbg_printf("Firm BP %u reached, %+F will be lowered\n", elem->bp.bpnr, node);
+ firm_debug_break();
+ }
+} /* dbg_lower */
+
+/**
+ * A graph will be deleted.
+ *
+ * @param ctx the hook context
+ * @param irg the IR graph that will be deleted
+ */
+static void dbg_free_graph(void *ctx, ir_graph *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;
+
+ 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) {
+ dbg_printf("Firm BP %u reached, %+F will be deleted\n", elem->bp.bpnr, ent);
+ firm_debug_break();
+ }
+ }
+} /* dbg_free_graph */
+
+/**
+ * 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();
+ }
+ }
+} /* dbg_new_entity */
+