7aa68b1f0a6a9a4dd8a16a43f2538f80b0d04604
[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_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 */
75   BP_MAX_REASON
76 } bp_reasons_t;
77
78 /** A breakpoint. */
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 */
85 } breakpoint;
86
87 /** A node number breakpoint. */
88 typedef struct {
89   breakpoint   bp;       /**< the breakpoint data */
90   long         nr;       /**< the node number */
91 } bp_node_t;
92
93 /** calculate the hash value for a node breakpoint */
94 #define HASH_NODE_BP(key) (((key).nr << 2) ^ (key).bp.reason)
95
96 /** A ident breakpoint. */
97 typedef struct {
98   breakpoint   bp;       /**< the breakpoint data */
99   ident        *id;      /**< the ident */
100 } bp_ident_t;
101
102 /** calculate the hash value for an ident breakpoint */
103 #define HASH_IDENT_BP(key) (HASH_PTR((key).id) ^ (key).bp.reason)
104
105 /** The set containing the breakpoints on node numbers. */
106 static set *bp_node_numbers;
107
108 /** The set containing the breakpoints on idents. */
109 static set *bp_idents;
110
111 /**< the list of all breakpoints */
112 static breakpoint *bp_list;
113
114 /** number of the current break point */
115 static unsigned bp_num = 0;
116
117 /** set if break on init command was issued. */
118 static int break_on_init = 0;
119
120 /** the hook entries for the Firm debugger module. */
121 static hook_entry_t debugger_hooks[hook_last];
122
123 /** number of active breakpoints to maintain hooks. */
124 static unsigned num_active_bp[BP_MAX_REASON];
125
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])
129
130 /** unhook the hook h */
131 #define UNHOOK(h)   unregister_hook(h, &debugger_hooks[h])
132
133 /** returns non-zero if a entry hook h is used */
134 #define IS_HOOKED(h) (debugger_hooks[h].next != NULL)
135
136 /**
137  * A new node is created.
138  *
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
142  */
143 static void dbg_new_node(void *ctx, ir_graph *irg, ir_node *node)
144 {
145   bp_node_t key, *elem;
146
147   key.nr        = get_irn_node_nr(node);
148   key.bp.reason = BP_ON_NEW_NODE;
149
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);
153     firm_debug_break();
154   }
155 }
156
157 /**
158  * A node is replaced.
159  *
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
163  */
164 static void dbg_replace(void *ctx, ir_node *old, ir_node *nw)
165 {
166   bp_node_t key, *elem;
167
168   key.nr        = get_irn_node_nr(old);
169   key.bp.reason = BP_ON_REPLACE;
170
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);
174     firm_debug_break();
175   }
176 }
177
178 /**
179  * A new node is lowered.
180  *
181  * @param ctx   the hook context
182  * @param node  the new IR node that will be lowered
183  */
184 static void dbg_lower_node(void *ctx, ir_node *node)
185 {
186   bp_node_t key, *elem;
187
188   key.nr        = get_irn_node_nr(node);
189   key.bp.reason = BP_ON_LOWER;
190
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);
194     firm_debug_break();
195   }
196 }
197
198 /**
199  * A graph will be deleted.
200  *
201  * @param ctx   the hook context
202  * @param irg   the IR graph that will be deleted
203  */
204 static void dbg_free_graph(void *context, ir_graph *irg)
205 {
206   bp_ident_t key, *elem;
207   entity *ent = get_irg_entity(irg);
208
209   if (! ent)
210     return;
211
212   key.id        = get_entity_ident(ent);
213   key.bp.reason = BP_ON_REMIRG;
214
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);
218     firm_debug_break();
219   }
220 }
221
222 /**
223  * An entity was created.
224  *
225  * @param ctx   the hook context
226  * @param ent   the newly created entity
227  */
228 static void dbg_new_entity(void *context, entity *ent)
229 {
230   bp_ident_t key, *elem;
231
232   key.id        = get_entity_ident(ent);
233   key.bp.reason = BP_ON_NEW_ENT;
234
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);
238     firm_debug_break();
239   }
240 }
241
242
243 /**
244  * return the reason string.
245  */
246 static const char *reason_str(bp_reasons_t reason)
247 {
248   switch (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";
254   default:             assert(0);
255   }
256   return "unknown";
257 }
258
259 /**
260  * Compare two node number breakpoints
261  */
262 static int cmp_node_bp(const void *elt, const void *key, size_t size)
263 {
264   const bp_node_t *e1 = elt;
265   const bp_node_t *e2 = key;
266
267   return (e1->nr - e2->nr) | (e1->bp.reason - e2->bp.reason);
268 }
269
270 /**
271  * Compare two ident breakpoints
272  */
273 static int cmp_ident_bp(const void *elt, const void *key, size_t size)
274 {
275   const bp_ident_t *e1 = elt;
276   const bp_ident_t *e2 = key;
277
278   return (e1->id != e2->id) | (e1->bp.reason - e2->bp.reason);
279 }
280
281 /**
282  * update the hooks
283  */
284 static void update_hooks(breakpoint *bp)
285 {
286   if (bp->active)
287     ++num_active_bp[bp->reason];
288   else
289     --num_active_bp[bp->reason];
290
291   if (num_active_bp[bp->reason] > 0) {
292     /* register the hooks on demand */
293     switch (bp->reason) {
294     case BP_ON_NEW_NODE:
295       if (! IS_HOOKED(hook_new_node))
296         HOOK(hook_new_node, dbg_new_node);
297       break;
298
299     case BP_ON_REPLACE:
300       if (! IS_HOOKED(hook_replace))
301         HOOK(hook_replace, dbg_replace);
302
303     case BP_ON_LOWER:
304       if (! IS_HOOKED(hook_lower))
305         HOOK(hook_lower, dbg_lower_node);
306       break;
307
308     case BP_ON_REMIRG:
309       if (! IS_HOOKED(hook_free_graph))
310         HOOK(hook_free_graph, dbg_free_graph);
311       break;
312
313     case BP_ON_NEW_ENT:
314       if (! IS_HOOKED(hook_new_entity))
315         HOOK(hook_new_entity, dbg_new_entity);
316       break;
317
318     default:
319       ;
320     }
321   }
322   else {
323     /* unregister the hook on demand */
324     switch (bp->reason) {
325     case BP_ON_NEW_NODE:
326       if (IS_HOOKED(hook_new_node))
327         UNHOOK(hook_new_node);
328       break;
329
330     case BP_ON_REPLACE:
331       if (IS_HOOKED(hook_replace))
332         UNHOOK(hook_replace);
333
334     case BP_ON_LOWER:
335       if (IS_HOOKED(hook_lower))
336         UNHOOK(hook_lower);
337       break;
338
339     case BP_ON_REMIRG:
340       if (IS_HOOKED(hook_free_graph))
341         UNHOOK(hook_free_graph);
342       break;
343
344     case BP_ON_NEW_ENT:
345       if (IS_HOOKED(hook_new_entity))
346         UNHOOK(hook_new_entity);
347       break;
348
349     default:
350       ;
351     }
352   }
353 }
354
355 /**
356  * Break if node nr is reached.
357  */
358 static void break_on_node(long nr, bp_reasons_t reason)
359 {
360   bp_node_t key, *elem;
361
362   key.bp.kind   = BP_NODE;
363   key.bp.bpnr   = 0;
364   key.bp.active = 1;
365   key.bp.reason = reason;
366   key.nr        = nr;
367
368   elem = set_insert(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
369
370   if (elem->bp.bpnr == 0) {
371     /* new break point */
372     elem->bp.bpnr = ++bp_num;
373     elem->bp.next = bp_list;
374     bp_list = &elem->bp;
375
376     printf("Firm BP %u: %s of Node %ld\n", elem->bp.bpnr, reason_str(reason), nr);
377
378     update_hooks(&elem->bp);
379   }
380 }
381
382 /**
383  * Break if ident name is reached.
384  */
385 static void break_on_ident(const char *name, bp_reasons_t reason) {
386   bp_ident_t key, *elem;
387
388   key.bp.kind   = BP_IDENT;
389   key.bp.bpnr   = 0;
390   key.bp.active = 1;
391   key.bp.reason = reason;
392   key.id        = new_id_from_str(name);
393
394   elem = set_insert(bp_idents, &key, sizeof(key), HASH_IDENT_BP(key));
395
396   if (elem->bp.bpnr == 0) {
397     /* new break point */
398     elem->bp.bpnr = ++bp_num;
399     elem->bp.next = bp_list;
400     bp_list = &elem->bp;
401
402     printf("Firm BP %u: %s of ident \"%s\"\n", elem->bp.bpnr, reason_str(reason), name);
403
404     update_hooks(&elem->bp);
405   }
406 }
407
408 /**
409  * Sets/resets the active flag of breakpoint bp.
410  */
411 static void bp_activate(unsigned bp, int active)
412 {
413   breakpoint *p;
414
415   for (p = bp_list; p; p = p->next) {
416     if (p->bpnr == bp) {
417       if (p->active != active) {
418         p->active = active;
419         update_hooks(p);
420       }
421
422       printf("Firm BP %u is now %s\n", bp, active ? "enabled" : "disabled");
423       return;
424     }
425   }
426   printf("Error: Firm BP %u not exists.\n", bp);
427 }
428
429
430 /**
431  * Show a list of supported commands
432  */
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"
445   );
446 }
447
448 /**
449  * Shows all Firm breakpoints.
450  */
451 static void show_bp(void) {
452   breakpoint *p;
453   bp_node_t  *node_p;
454   bp_ident_t *ident_p;
455
456   for (p = bp_list; p; p = p->next) {
457     printf("Firm BP %u: ", p->bpnr);
458
459     switch (p->kind) {
460     case BP_NODE:
461       node_p = (bp_node_t *)p;
462       printf("%s of node %ld ", reason_str(p->reason), node_p->nr);
463       break;
464
465     case BP_IDENT:
466       ident_p = (bp_ident_t *)p;
467       printf("%s of ident \"%s\" ", reason_str(p->reason), get_id_str(ident_p->id));
468       break;
469     }
470
471     printf(p->active ? "enabled\n" : "disabled\n");
472   }
473 }
474
475 /**
476  * High level function to use from debugger interface
477  *
478  * Supported commands:
479  *  .create nr    break if node nr was created
480  *  .help         list all commands
481  */
482 void firm_break(const char *cmd) {
483   long nr;
484   unsigned bp;
485   char name[1024];
486
487   while (isspace(*cmd)) ++cmd;
488
489   if (sscanf(cmd, ".create %ld\n", &nr) == 1) {
490     break_on_node(nr, BP_ON_NEW_NODE);
491   }
492   else if (sscanf(cmd, ".replace %ld\n", &nr) == 1) {
493     break_on_node(nr, BP_ON_REPLACE);
494   }
495   else if (sscanf(cmd, ".lower %ld\n", &nr) == 1) {
496     break_on_node(nr, BP_ON_LOWER);
497   }
498   else if (sscanf(cmd, ".remirg %s\n", name) == 1) {
499     break_on_ident(name, BP_ON_REMIRG);
500   }
501   else if (sscanf(cmd, ".newent %s\n", name) == 1) {
502     break_on_ident(name, BP_ON_NEW_ENT);
503   }
504   else if (strcmp(cmd, ".init") == 0)
505     break_on_init = 1;
506   else if (strcmp(cmd, ".bp") == 0)
507     show_bp();
508   else if (sscanf(cmd, ".enable %u", &bp) == 1)
509     bp_activate(bp, 1);
510   else if (sscanf(cmd, ".disable %u", &bp) == 1)
511     bp_activate(bp, 0);
512   else {
513     show_commands();
514   }
515 }
516
517 /* creates the debugger tables */
518 void firm_init_debugger(void)
519 {
520   char *env;
521
522   bp_node_numbers = new_set(cmp_node_bp, 8);
523   bp_idents       = new_set(cmp_ident_bp, 8);
524
525   env = getenv("FIRMDBG");
526
527   if (env)
528     firm_break(env);
529
530   if (break_on_init)
531     firm_debug_break();
532 }
533
534 #endif /* NDEBUG */
535
536 /**
537  * @page debugger   The Firm debugger extension.
538  *
539  * Firm contains a debugger extension. This allows to set debugger breakpoints
540  * an various events.
541  * The extension uses a text interface which can be access in the debugger.
542  *
543  * The following commands are currently supported:
544  *
545  * .init
546  *
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:
550  *
551  * $export FIRMDBG=".init"
552  *
553  *
554  * .create nr
555  *
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.
558  *
559  * .replace nr
560  *
561  * Break before IR-node with node number nr is replaced by another node.
562  *
563  * .lower nr
564  *
565  * Break before IR-node with node number nr is lowered.
566  *
567  * .remirg name
568  *
569  * Break if the irg of entity name is deleted.
570  *
571  * .newent name
572  *
573  * Break if the entity name was created.
574  *
575  * .bp
576  *
577  * Show all Firm internal breakpoints.
578  *
579  * .enable nr
580  *
581  * Enables breakpoint nr.
582  *
583  * .disable nr
584  *
585  * Disables breakpoint nr.
586  *
587  * .help
588  *
589  * List all commands.
590  *
591  *
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.
595  *
596  * 1.) set FIRMDBG=".init"
597  * 2.) start gdb with your compiler
598  * 3.) after gdb breaks, issue
599  *
600  * p firm_debug(".create 2101")
601  *
602  * On the console the following text should be issued:
603  *
604  * Firm BP 1: creation of Node 2101
605  */