fixed remirg
[libfirm] / ir / debug / debugger.c
1 /*
2  * Project:     libFIRM
3  * File name:   ir/debug/debugger.c
4  * Purpose:     Helper function for integerated debug support
5  * Author:      Michael Beck
6  * Modified by:
7  * Created:     2005
8  * CVS-ID:      $Id$
9  * Copyright:   (c) 2001-2005 Universität Karlsruhe
10  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
11  */
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif
15
16 #ifndef NDEBUG
17
18 #ifdef _WIN32
19 #define WIN32_LEAN_AND_MEAN
20 #include <windows.h>
21 #endif
22
23 #ifdef HAVE_STDLIB_H
24 #include <stdlib.h>
25 #endif
26
27 #include <stdio.h>
28 #include <signal.h>
29
30 #ifdef HAVE_STRING_H
31 #include <string.h>
32 #endif
33
34 #include <ctype.h>
35
36 #include "set.h"
37 #include "ident.h"
38 #include "irhooks.h"
39 #include "irgraph_t.h"
40 #include "entity_t.h"
41 #include "irprintf.h"
42
43 #ifdef _WIN32
44 /** Break into the debugger. The Win32 way. */
45 static void firm_debug_break(void) {
46   DebugBreak();
47 }
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");
52 }
53 #else
54 /** Break into the debugger. Poor Unix way. */
55 static void firm_debug_break(void) {
56   raise(SIGINT);
57 }
58 #endif /* _WIN32 */
59
60 /** supported breakpoint kinds */
61 typedef enum {
62   BP_NODE  = 'n',   /**< break on node number. */
63   BP_IDENT = 'i'    /**< break on ident. */
64 } bp_kind;
65
66 /**
67  * Reasons for node number breakpoints.
68  */
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 */
74   BP_MAX_REASON
75 } bp_reasons_t;
76
77 /** A breakpoint. */
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 */
84 } breakpoint;
85
86 /** A node number breakpoint. */
87 typedef struct {
88   breakpoint   bp;       /**< the breakpoint data */
89   long         nr;       /**< the node number */
90 } bp_node_t;
91
92 /** calculate the hash value for a node breakpoint */
93 #define HASH_NODE_BP(key) (((key).nr << 2) ^ (key).bp.reason)
94
95 /** A ident breakpoint. */
96 typedef struct {
97   breakpoint   bp;       /**< the breakpoint data */
98   ident        *id;      /**< the ident */
99 } bp_ident_t;
100
101 /** calculate the hash value for an ident breakpoint */
102 #define HASH_IDENT_BP(key) (HASH_PTR((key).id) ^ (key).bp.reason)
103
104 /** The set containing the breakpoints on node numbers. */
105 static set *bp_node_numbers;
106
107 /** The set containing the breakpoints on idents. */
108 static set *bp_idents;
109
110 /**< the list of all breakpoints */
111 static breakpoint *bp_list;
112
113 /** number of the current break point */
114 static unsigned bp_num = 0;
115
116 /** set if break on init command was issued. */
117 static int break_on_init = 0;
118
119 /** the hook entries for the Firm debugger module. */
120 static hook_entry_t debugger_hooks[hook_last];
121
122 /** number of active breakpoints to maintain hooks. */
123 static unsigned num_active_bp[BP_MAX_REASON];
124
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])
128
129 /** unhook the hook h */
130 #define UNHOOK(h)   unregister_hook(h, &debugger_hooks[h])
131
132 /** returns non-zero if a entry hook h is used */
133 #define IS_HOOKED(h) (debugger_hooks[h].next != NULL)
134
135 /**
136  * A new node is created.
137  *
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
141  */
142 static void dbg_new_node(void *ctx, ir_graph *irg, ir_node *node)
143 {
144   bp_node_t key, *elem;
145
146   key.nr        = get_irn_node_nr(node);
147   key.bp.reason = BP_ON_CREATION;
148
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);
152     firm_debug_break();
153   }
154 }
155
156 /**
157  * A node is replaced.
158  *
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
162  */
163 static void dbg_replace(void *ctx, ir_node *old, ir_node *nw)
164 {
165   bp_node_t key, *elem;
166
167   key.nr        = get_irn_node_nr(old);
168   key.bp.reason = BP_ON_REPLACE;
169
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);
173     firm_debug_break();
174   }
175 }
176
177 /**
178  * A new node is lowered.
179  *
180  * @param ctx   the hook context
181  * @param node  the new IR node that will be lowered
182  */
183 static void dbg_lower_node(void *ctx, ir_node *node)
184 {
185   bp_node_t key, *elem;
186
187   key.nr        = get_irn_node_nr(node);
188   key.bp.reason = BP_ON_LOWER;
189
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);
193     firm_debug_break();
194   }
195 }
196
197 /**
198  * A graph will be deleted.
199  *
200  * @param ctx   the hook context
201  * @param irg   the IR graph that will be deleted
202  */
203 static void dbg_free_graph(void *context, ir_graph *irg)
204 {
205   bp_ident_t key, *elem;
206   entity *ent = get_irg_entity(irg);
207
208   if (! ent)
209     return;
210
211   key.id        = get_entity_ident(ent);
212   key.bp.reason = BP_ON_REMIRG;
213
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);
217     firm_debug_break();
218   }
219 }
220
221 /**
222  * return the reason string.
223  */
224 static const char *reason_str(bp_reasons_t reason)
225 {
226   switch (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";
231   default:             assert(0);
232   }
233   return "unknown";
234 }
235
236 /**
237  * Compare two node number breakpoints
238  */
239 static int cmp_node_bp(const void *elt, const void *key, size_t size)
240 {
241   const bp_node_t *e1 = elt;
242   const bp_node_t *e2 = key;
243
244   return (e1->nr - e2->nr) | (e1->bp.reason - e2->bp.reason);
245 }
246
247 /**
248  * Compare two ident breakpoints
249  */
250 static int cmp_ident_bp(const void *elt, const void *key, size_t size)
251 {
252   const bp_ident_t *e1 = elt;
253   const bp_ident_t *e2 = key;
254
255   return (e1->id != e2->id) | (e1->bp.reason - e2->bp.reason);
256 }
257
258 /**
259  * update the hooks
260  */
261 static void update_hooks(breakpoint *bp)
262 {
263   if (bp->active)
264     ++num_active_bp[bp->reason];
265   else
266     --num_active_bp[bp->reason];
267
268   if (num_active_bp[bp->reason] > 0) {
269     /* register the hooks on demand */
270     switch (bp->reason) {
271     case BP_ON_CREATION:
272       if (! IS_HOOKED(hook_new_node))
273         HOOK(hook_new_node, dbg_new_node);
274       break;
275
276     case BP_ON_REPLACE:
277       if (! IS_HOOKED(hook_replace))
278         HOOK(hook_replace, dbg_replace);
279
280     case BP_ON_LOWER:
281       if (! IS_HOOKED(hook_lower))
282         HOOK(hook_lower, dbg_lower_node);
283       break;
284
285     case BP_ON_REMIRG:
286       if (! IS_HOOKED(hook_free_graph))
287         HOOK(hook_free_graph, dbg_free_graph);
288       break;
289
290     default:
291       ;
292     }
293   }
294   else {
295     /* unregister the hook on demand */
296     switch (bp->reason) {
297     case BP_ON_CREATION:
298       if (IS_HOOKED(hook_new_node))
299         UNHOOK(hook_new_node);
300       break;
301
302     case BP_ON_REPLACE:
303       if (IS_HOOKED(hook_replace))
304         UNHOOK(hook_replace);
305
306     case BP_ON_LOWER:
307       if (IS_HOOKED(hook_lower))
308         UNHOOK(hook_lower);
309       break;
310
311     case BP_ON_REMIRG:
312       if (IS_HOOKED(hook_free_graph))
313         UNHOOK(hook_free_graph);
314       break;
315
316     default:
317       ;
318     }
319   }
320 }
321
322 /**
323  * Break if node nr is reached.
324  */
325 static void break_on_node(long nr, bp_reasons_t reason)
326 {
327   bp_node_t key, *elem;
328
329   key.bp.kind   = BP_NODE;
330   key.bp.bpnr   = 0;
331   key.bp.active = 1;
332   key.bp.reason = reason;
333   key.nr        = nr;
334
335   elem = set_insert(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
336
337   if (elem->bp.bpnr == 0) {
338     /* new break point */
339     elem->bp.bpnr = ++bp_num;
340     elem->bp.next = bp_list;
341     bp_list = &elem->bp;
342
343     printf("Firm BP %u: %s of Node %ld\n", elem->bp.bpnr, reason_str(reason), nr);
344
345     update_hooks(&elem->bp);
346   }
347 }
348
349 /**
350  * Break if ident name is reached.
351  */
352 static void break_on_ident(const char *name, bp_reasons_t reason) {
353   bp_ident_t key, *elem;
354
355   key.bp.kind   = BP_IDENT;
356   key.bp.bpnr   = 0;
357   key.bp.active = 1;
358   key.bp.reason = reason;
359   key.id        = new_id_from_str(name);
360
361   elem = set_insert(bp_idents, &key, sizeof(key), HASH_IDENT_BP(key));
362
363   if (elem->bp.bpnr == 0) {
364     /* new break point */
365     elem->bp.bpnr = ++bp_num;
366     elem->bp.next = bp_list;
367     bp_list = &elem->bp;
368
369     printf("Firm BP %u: %s of ident \"%s\"\n", elem->bp.bpnr, reason_str(reason), name);
370
371     update_hooks(&elem->bp);
372   }
373 }
374
375 /**
376  * Sets/resets the active flag of breakpoint bp.
377  */
378 static void bp_activate(unsigned bp, int active)
379 {
380   breakpoint *p;
381
382   for (p = bp_list; p; p = p->next) {
383     if (p->bpnr == bp) {
384       if (p->active != active) {
385         p->active = active;
386         update_hooks(p);
387       }
388
389       printf("Firm BP %u is now %s\n", bp, active ? "enabled" : "disabled");
390       return;
391     }
392   }
393   printf("Error: Firm BP %u not exists.\n", bp);
394 }
395
396
397 /**
398  * Show a list of supported commands
399  */
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"
411   );
412 }
413
414 /**
415  * Shows all Firm breakpoints.
416  */
417 static void show_bp(void) {
418   breakpoint *p;
419   bp_node_t  *node_p;
420   bp_ident_t *ident_p;
421
422   for (p = bp_list; p; p = p->next) {
423     printf("Firm BP %u: ", p->bpnr);
424
425     switch (p->kind) {
426     case BP_NODE:
427       node_p = (bp_node_t *)p;
428       printf("%s of node %ld ", reason_str(p->reason), node_p->nr);
429       break;
430
431     case BP_IDENT:
432       ident_p = (bp_ident_t *)p;
433       printf("%s of ident \"%s\" ", reason_str(p->reason), get_id_str(ident_p->id));
434       break;
435     }
436
437     printf(p->active ? "enabled\n" : "disabled\n");
438   }
439 }
440
441 /**
442  * High level function to use from debugger interface
443  *
444  * Supported commands:
445  *  .create nr    break if node nr was created
446  *  .help         list all commands
447  */
448 void firm_break(const char *cmd) {
449   long nr;
450   unsigned bp;
451   char name[1024];
452
453   while (isspace(*cmd)) ++cmd;
454
455   if (sscanf(cmd, ".create %ld\n", &nr) == 1) {
456     break_on_node(nr, BP_ON_CREATION);
457   }
458   else if (sscanf(cmd, ".replace %ld\n", &nr) == 1) {
459     break_on_node(nr, BP_ON_REPLACE);
460   }
461   else if (sscanf(cmd, ".lower %ld\n", &nr) == 1) {
462     break_on_node(nr, BP_ON_LOWER);
463   }
464   else if (sscanf(cmd, ".remirg %s\n", name) == 1) {
465     break_on_ident(name, BP_ON_REMIRG);
466   }
467   else if (strcmp(cmd, ".init") == 0)
468     break_on_init = 1;
469   else if (strcmp(cmd, ".bp") == 0)
470     show_bp();
471   else if (sscanf(cmd, ".enable %u", &bp) == 1)
472     bp_activate(bp, 1);
473   else if (sscanf(cmd, ".disable %u", &bp) == 1)
474     bp_activate(bp, 0);
475   else {
476     show_commands();
477   }
478 }
479
480 /* creates the debugger tables */
481 void firm_init_debugger(void)
482 {
483   char *env;
484
485   bp_node_numbers = new_set(cmp_node_bp, 8);
486   bp_idents       = new_set(cmp_ident_bp, 8);
487
488   env = getenv("FIRMDBG");
489
490   if (env)
491     firm_break(env);
492
493   if (break_on_init)
494     firm_debug_break();
495 }
496
497 #endif /* NDEBUG */
498
499 /**
500  * @page debugger   The Firm debugger extension.
501  *
502  * Firm contains a debugger extension. This allows to set debugger breakpoints
503  * an various events.
504  * The extension uses a text interface which can be access in the debugger.
505  *
506  * The following commands are currently supported:
507  *
508  * .init
509  *
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:
513  *
514  * $export FIRMDBG=".init"
515  *
516  *
517  * .create nr
518  *
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.
521  *
522  * .replace nr
523  *
524  * Break before IR-node with node number nr is replaced by another node.
525  *
526  * .lower nr
527  *
528  * Break before IR-node with node number nr is lowered.
529  *
530  * .remirg name
531  *
532  * Break if the irg of entity name is deleted.
533  * .bp
534  *
535  * Show all Firm internal breakpoints.
536  *
537  * .enable nr
538  *
539  * Enables breakpoint nr.
540  *
541  * .disable nr
542  *
543  * Disables breakpoint nr.
544  *
545  * .help
546  *
547  * List all commands.
548  *
549  *
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.
553  *
554  * 1.) set FIRMDBG=".init"
555  * 2.) start gdb with your compiler
556  * 3.) after gdb breaks, issue
557  *
558  * p firm_debug(".create 2101")
559  *
560  * On the console the following text should be issued:
561  *
562  * Firm BP 1: creation of Node 2101
563  */