92a01d6d1b54b0c24c6e785d8970fa828bd2d1d0
[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 "irhooks.h"
38 #include "irprintf.h"
39
40 #ifdef _WIN32
41 /** Break into the debugger. The Win32 way. */
42 static void firm_debug_break(void) {
43   DebugBreak();
44 }
45 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64))
46 /** Break into the debugger. The ia32 way under GCC. */
47 static void firm_debug_break(void) {
48   __asm__ __volatile__("int3");
49 }
50 #else
51 /** Break into the debugger. Poor Unix way. */
52 static void firm_debug_break(void) {
53   raise(SIGINT);
54 }
55 #endif /* _WIN32 */
56
57 /** supported breakpoint kinds */
58 typedef enum {
59   BP_NODE = 0    /**< break on node number. */
60 } bp_kind;
61
62 /** A breakpoint. */
63 typedef struct _breakpoint {
64   bp_kind   kind;           /**< the kind of this break point */
65   unsigned  bpnr;           /**< break point number */
66   int       active;         /**< non-zero, if this break point is active */
67   struct _breakpoint *next; /**< link to the next one */
68 } breakpoint;
69
70 /**
71  * Reasons for node number breakpoints.
72  */
73 typedef enum _bp_reasons_t {
74   BP_ON_CREATION = 1,     /**< break if node with number is created */
75   BP_ON_REPLACE  = 2,     /**< break if node with number is replaced */
76   BP_ON_LOWER    = 3,     /**< break if node with number is lowered */
77   BP_MAX_REASON
78 } bp_reasons_t;
79
80 /** A node number breakpoint. */
81 typedef struct {
82   breakpoint   bp;       /**< the breakpoint data */
83   long         nr;       /**< the node number */
84   bp_reasons_t reason;   /**< reason for the breakpoint */
85 } bp_node_t;
86
87 /** calculate the hash value for a node breakpoint */
88 #define HASH_NODE_BP(key) (((key).nr << 2) ^ (key).reason)
89
90 /** The set containing the breakpoints on node numbers. */
91 static set *bp_node_numbers;
92
93 /**< the list of all breakpoints */
94 static breakpoint *bp_list;
95
96 /** number of the current break point */
97 static unsigned bp_num = 0;
98
99 /** set if break on init command was issued. */
100 static int break_on_init = 0;
101
102 /** the hook entries for the Firm debugger module. */
103 static hook_entry_t debugger_hooks[hook_last];
104
105 /** number of active breakpoints to maintein hooks. */
106 static unsigned num_active_bp[BP_MAX_REASON];
107
108 /** hook the hook h with function fkt. */
109 #define HOOK(h, fkt) \
110   debugger_hooks[h].hook._##h = fkt; register_hook(h, &debugger_hooks[h])
111
112 /** unhook the hook h */
113 #define UNHOOK(h)   unregister_hook(h, &debugger_hooks[h])
114
115 /** returns non-zero if a entry hook h is used */
116 #define IS_HOOKED(h) (debugger_hooks[h].next != NULL)
117
118 /**
119  * A new node is created.
120  *
121  * @param ctx   the hook context
122  * @param irg   the IR graph on which the node is created
123  * @param node  the new IR node that was created
124  */
125 static void dbg_new_node(void *ctx, ir_graph *irg, ir_node *node)
126 {
127   bp_node_t key, *elem;
128
129   key.nr     = get_irn_node_nr(node);
130   key.reason = BP_ON_CREATION;
131
132   elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
133   if (elem && elem->bp.active) {
134     ir_printf("Firm BP %u reached, %+F created\n", elem->bp.bpnr, node);
135     firm_debug_break();
136   }
137 }
138
139 /**
140  * A node is replaced.
141  *
142  * @param ctx   the hook context
143  * @param old   the IR node the is replaced
144  * @param nw    the new IR node that will replace old
145  */
146 static void dbg_replace(void *ctx, ir_node *old, ir_node *nw)
147 {
148   bp_node_t key, *elem;
149
150   key.nr     = get_irn_node_nr(old);
151   key.reason = BP_ON_REPLACE;
152
153   elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
154   if (elem && elem->bp.active) {
155     ir_printf("Firm BP %u reached, %+F will be replaced by %+F\n", elem->bp.bpnr, old, nw);
156     firm_debug_break();
157   }
158 }
159
160 /**
161  * A new node is lowered.
162  *
163  * @param ctx   the hook context
164  * @param node  the new IR node that will be lowered
165  */
166 static void dbg_lower_node(void *ctx, ir_node *node)
167 {
168   bp_node_t key, *elem;
169
170   key.nr     = get_irn_node_nr(node);
171   key.reason = BP_ON_LOWER;
172
173   elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
174   if (elem && elem->bp.active) {
175     ir_printf("Firm BP %u reached, %+F will be lowered\n", elem->bp.bpnr, node);
176     firm_debug_break();
177   }
178 }
179
180
181 /**
182  * return the reason string.
183  */
184 static const char *reason_str(bp_reasons_t reason)
185 {
186   switch (reason) {
187   case BP_ON_CREATION: return "creation";
188   case BP_ON_REPLACE:  return "replacing";
189   case BP_ON_LOWER:    return "lowering";
190   default:             assert(0);
191   }
192   return "unknown";
193 }
194
195 #define HASH_NODE_BP(key) (((key).nr << 2) ^ (key).reason)
196
197 /**
198  * Compare two node number breakpoints
199  */
200 static int cmp_node_bp(const void *elt, const void *key, size_t size)
201 {
202   const bp_node_t *e1 = elt;
203   const bp_node_t *e2 = key;
204
205   return (e1->nr - e2->nr) | (e1->reason - e2->reason);
206 }
207
208 /**
209  * update the hooks
210  */
211 static void update_hooks(breakpoint *bp)
212 {
213   if (bp->kind == BP_NODE) {
214     bp_node_t *elem = (bp_node_t *)bp;
215
216     if (elem->bp.active)
217       ++num_active_bp[elem->reason];
218     else
219       --num_active_bp[elem->reason];
220
221     if (num_active_bp[elem->reason] > 0) {
222       /* register the hooks on demand */
223       switch (elem->reason) {
224       case BP_ON_CREATION:
225         if (! IS_HOOKED(hook_new_node))
226           HOOK(hook_new_node, dbg_new_node);
227         break;
228
229       case BP_ON_REPLACE:
230         if (! IS_HOOKED(hook_replace))
231           HOOK(hook_replace, dbg_replace);
232
233       case BP_ON_LOWER:
234         if (! IS_HOOKED(hook_lower))
235           HOOK(hook_lower, dbg_lower_node);
236         break;
237       }
238     }
239     else {
240       switch (elem->reason) {
241       case BP_ON_CREATION:
242         if (IS_HOOKED(hook_new_node))
243           UNHOOK(hook_new_node);
244         break;
245
246       case BP_ON_REPLACE:
247         if (IS_HOOKED(hook_replace))
248           UNHOOK(hook_replace);
249
250       case BP_ON_LOWER:
251         if (IS_HOOKED(hook_lower))
252           UNHOOK(hook_lower);
253         break;
254       }
255     }
256   }
257 }
258
259 /**
260  * Break if node nr is reached.
261  */
262 static void break_on_node(long nr, bp_reasons_t reason)
263 {
264   bp_node_t key, *elem;
265
266   key.bp.kind   = BP_NODE;
267   key.bp.bpnr   = 0;
268   key.bp.active = 1;
269   key.nr        = nr;
270   key.reason    = reason;
271
272   elem = set_insert(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
273
274   if (elem->bp.bpnr == 0) {
275     /* new break point */
276     elem->bp.bpnr = ++bp_num;
277     elem->bp.next = bp_list;
278     bp_list = &elem->bp;
279
280     printf("Firm BP %u: %s of Node %ld\n", elem->bp.bpnr, reason_str(reason), nr);
281
282     update_hooks(&elem->bp);
283   }
284 }
285
286 /**
287  * Sets/resets the active flag of breakpoint bp.
288  */
289 static void bp_activate(unsigned bp, int active)
290 {
291   breakpoint *p;
292
293   for (p = bp_list; p; p = p->next) {
294     if (p->bpnr == bp) {
295       if (p->active != active) {
296         p->active = active;
297         update_hooks(p);
298       }
299
300       printf("Firm BP %u is now %s\n", bp, active ? "enabled" : "disabled");
301       return;
302     }
303   }
304   printf("Error: Firm BP %u not exists.\n", bp);
305 }
306
307
308 /**
309  * Show a list of supported commands
310  */
311 static void show_commands(void) {
312   printf("Internal Firm debugger extension commands:\n"
313     ".init         break after initialization\n"
314     ".create nr    break if node nr was created\n"
315     ".replace nr   break if node nr is replaced by another node\n"
316     ".lower nr     break before node nr is lowered\n"
317     ".bp           show all breakpoints\n"
318     ".enable nr    enable breakpoint nr\n"
319     ".disable nr   disable breakpoint nr\n"
320     ".help         list all commands\n"
321   );
322 }
323
324 /**
325  * Shows all Firm breakpoints.
326  */
327 static void show_bp(void) {
328   breakpoint *p;
329   bp_node_t *node_p;
330
331   for (p = bp_list; p; p = p->next) {
332     printf("Firm BP %u: ", p->bpnr);
333
334     switch (p->kind) {
335     case BP_NODE:
336       node_p = (bp_node_t *)p;
337       printf("%s of node %ld ", reason_str(node_p->reason), node_p->nr);
338       break;
339     }
340
341     printf(p->active ? "enabled\n" : "disabled\n");
342   }
343 }
344
345 /**
346  * High level function to use from debugger interface
347  *
348  * Supported commands:
349  *  .create nr    break if node nr was created
350  *  .help         list all commands
351  */
352 void firm_break(const char *cmd) {
353   long nr;
354   unsigned bp;
355
356   while (isspace(*cmd)) ++cmd;
357
358   if (sscanf(cmd, ".create %ld\n", &nr) == 1) {
359     break_on_node(nr, BP_ON_CREATION);
360   }
361   else if (sscanf(cmd, ".replace %ld\n", &nr) == 1) {
362     break_on_node(nr, BP_ON_REPLACE);
363   }
364   else if (sscanf(cmd, ".lower %ld\n", &nr) == 1) {
365     break_on_node(nr, BP_ON_LOWER);
366   }
367   else if (strcmp(cmd, ".init") == 0)
368     break_on_init = 1;
369   else if (strcmp(cmd, ".bp") == 0)
370     show_bp();
371   else if (sscanf(cmd, ".enable %u", &bp) == 1)
372     bp_activate(bp, 1);
373   else if (sscanf(cmd, ".disable %u", &bp) == 1)
374     bp_activate(bp, 0);
375   else {
376     show_commands();
377   }
378 }
379
380 /* creates the debugger tables */
381 void firm_init_debugger(void)
382 {
383   char *env;
384
385   bp_node_numbers = new_set(cmp_node_bp, 8);
386
387   env = getenv("FIRMDBG");
388
389   if (env)
390     firm_break(env);
391
392   if (break_on_init)
393     firm_debug_break();
394 }
395
396 #endif /* NDEBUG */
397
398 /**
399  * @page debugger   The Firm debugger extension.
400  *
401  * Firm contains a debugger extension. This allows to set debugger breakpoints
402  * an various events.
403  * The extension uses a text interface which can be access in the debugger.
404  *
405  * The following commands are currently supported:
406  *
407  * .init
408  *
409  * Break immediately after the debugger extension was initialized.
410  * Typically this command is used in the environment to stop the execution
411  * of a Firm compiler right after the initialization, like this:
412  *
413  * $export FIRMDBG=".init"
414  *
415  *
416  * .create nr
417  *
418  * Break if a new IR-node with node number nr was created.
419  * Typically used to find the place where wrong nodes are created.
420  *
421  * .replace nr
422  *
423  * Break before IR-node with node number nr is replaced by another node.
424  *
425  * .lower nr
426  *
427  * Break before IR-node with node number nr is lowered.
428  *
429  * .bp
430  *
431  * Show all Firm internal breakpoints.
432  *
433  * .enable nr
434  *
435  * Enables breakpoint nr.
436  *
437  * .disable nr
438  *
439  * Disables breakpoint nr.
440  *
441  * .help
442  *
443  * List all commands.
444  *
445  *
446  * The Firm debugger extension can be accessed using the function firm_break().
447  * The following example shows how to set a creation breakpoint in GDB when
448  * node 2101 is created.
449  *
450  * 1.) set FIRMDBG=".init"
451  * 2.) start gdb with your compiler
452  * 3.) after gdb breaks, issue
453  *
454  * p firm_debug(".create 2101")
455  *
456  * On the console the following text should be issued:
457  *
458  * Firm BP 1: creation of Node 2101
459  */