moved dbg_action_2_str() to .c file
[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_LOWER    = 2      /**< break if node with number is lowered */
76 } bp_reasons_t;
77
78 /** A node number breakpoint. */
79 typedef struct {
80   breakpoint   bp;       /**< the breakpoint data */
81   long         nr;       /**< the node number */
82   bp_reasons_t reason;   /**< reason for the breakpoint */
83 } bp_node_t;
84
85 /** The set containing the breakpoints on node numbers. */
86 static set *bp_node_numbers;
87
88 /**< the list of all breakpoints */
89 static breakpoint *bp_list;
90
91 /** number of the current break point */
92 static unsigned bp_num = 0;
93
94 /** set if break on init command was issued. */
95 static int break_on_init = 0;
96
97 /**
98  * return the reason string.
99  */
100 static const char *reason_str(bp_reasons_t reason)
101 {
102   switch (reason) {
103   case BP_ON_CREATION: return "creation";
104   case BP_ON_LOWER:    return "lowering";
105   default:             assert(0);
106   }
107   return "unknown";
108 }
109
110 #define HASH_NODE_BP(key) (((key).nr << 2) ^ (key).reason)
111
112 /**
113  * Compare two node number breakpoints
114  */
115 static int cmp_node_bp(const void *elt, const void *key, size_t size)
116 {
117   const bp_node_t *e1 = elt;
118   const bp_node_t *e2 = key;
119
120   return (e1->nr - e2->nr) | (e1->reason - e2->reason);
121 }
122
123 /**
124  * Break if node nr is reached.
125  */
126 static void break_on_node(long nr, bp_reasons_t reason)
127 {
128   bp_node_t key, *elem;
129
130   key.bp.kind   = BP_NODE;
131   key.bp.bpnr   = 0;
132   key.bp.active = 1;
133   key.nr        = nr;
134   key.reason    = reason;
135
136   elem = set_insert(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
137
138   if (elem->bp.bpnr == 0) {
139     /* new break point */
140     elem->bp.bpnr = ++bp_num;
141     elem->bp.next = bp_list;
142     bp_list = &elem->bp;
143
144     printf("Firm BP %u: %s of Node %ld\n", elem->bp.bpnr, reason_str(reason), nr);
145   }
146 }
147
148 /**
149  * Sets/resets the active flag of breakpoint bp.
150  */
151 static void bp_activate(unsigned bp, int active)
152 {
153   breakpoint *p;
154
155   for (p = bp_list; p; p = p->next) {
156     if (p->bpnr == bp) {
157       p->active = active;
158
159       printf("Firm BP %u is now %s\n", bp, active ? "enabled" : "disabled");
160       return;
161     }
162   }
163   printf("Error: Firm BP %u not exists.\n", bp);
164 }
165
166
167 /**
168  * Show a list of supported commands
169  */
170 static void show_commands(void) {
171   printf("Internal Firm debugger extension commands:\n"
172     ".init         break after initialization\n"
173     ".create nr    break if node nr was created\n"
174     ".lower nr     break before node nr is lowered\n"
175     ".bp           show all breakpoints\n"
176     ".enable nr    enable breakpoint nr\n"
177     ".disable nr   disable breakpoint nr\n"
178     ".help         list all commands\n"
179   );
180 }
181
182 /**
183  * Shows all Firm breakpoints.
184  */
185 static void show_bp(void) {
186   breakpoint *p;
187   bp_node_t *node_p;
188
189   for (p = bp_list; p; p = p->next) {
190     printf("Firm BP %u: ", p->bpnr);
191
192     switch (p->kind) {
193     case BP_NODE:
194       node_p = (bp_node_t *)p;
195       printf("%s of node %ld ", reason_str(node_p->reason), node_p->nr);
196       break;
197     }
198
199     printf(p->active ? "enabled\n" : "disabled\n");
200   }
201 }
202
203 /**
204  * High level function to use from debugger interface
205  *
206  * Supported commands:
207  *  .create nr    break if node nr was created
208  *  .help         list all commands
209  */
210 void firm_break(const char *cmd) {
211   long nr;
212   unsigned bp;
213
214   while (isspace(*cmd)) ++cmd;
215
216   if (sscanf(cmd, ".create %ld\n", &nr) == 1) {
217     break_on_node(nr, BP_ON_CREATION);
218   }
219   else if (sscanf(cmd, ".lower %ld\n", &nr) == 1) {
220     break_on_node(nr, BP_ON_LOWER);
221   }
222   else if (strcmp(cmd, ".init") == 0)
223     break_on_init = 1;
224   else if (strcmp(cmd, ".bp") == 0)
225     show_bp();
226   else if (sscanf(cmd, ".enable %u", &bp) == 1)
227     bp_activate(bp, 1);
228   else if (sscanf(cmd, ".disable %u", &bp) == 1)
229     bp_activate(bp, 0);
230   else {
231     show_commands();
232   }
233 }
234
235 /** the hook entries for the Firm debugger module */
236 static hook_entry_t debugger_hooks[hook_last];
237
238 /**
239  * A new node is created.
240  *
241  * @param ctx   the hook context
242  * @param irg   the IR graph on which the node is created
243  * @param node  the new IR node that was created
244  */
245 static void dbg_new_node(void *ctx, ir_graph *irg, ir_node *node)
246 {
247   bp_node_t key, *elem;
248
249   key.nr     = get_irn_node_nr(node);
250   key.reason = BP_ON_CREATION;
251
252   elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
253   if (elem && elem->bp.active) {
254     ir_printf("Firm BP %u reached, %+F created\n", elem->bp.bpnr, node);
255     firm_debug_break();
256   }
257 }
258
259 /**
260  * A new node is lowered.
261  *
262  * @param ctx   the hook context
263  * @param node  the new IR node that will be lowered
264  */
265 static void dbg_lower_node(void *ctx, ir_node *node)
266 {
267   bp_node_t key, *elem;
268
269   key.nr     = get_irn_node_nr(node);
270   key.reason = BP_ON_LOWER;
271
272   elem = set_find(bp_node_numbers, &key, sizeof(key), HASH_NODE_BP(key));
273   if (elem && elem->bp.active) {
274     ir_printf("Firm BP %u reached, %+F will be lowered\n", elem->bp.bpnr, node);
275     firm_debug_break();
276   }
277 }
278
279 #define HOOK(h, fkt) \
280   debugger_hooks[h].hook._##h = fkt; register_hook(h, &debugger_hooks[h])
281
282 /* creates the debugger tables */
283 void firm_init_debugger(void)
284 {
285   char *env;
286
287   bp_node_numbers = new_set(cmp_node_bp, 8);
288
289   /* register the hooks */
290   HOOK(hook_new_node,                         dbg_new_node);
291   HOOK(hook_lower,                            dbg_lower_node);
292
293   env = getenv("FIRMDBG");
294
295   if (env)
296     firm_break(env);
297
298   if (break_on_init)
299     firm_debug_break();
300 }
301
302 #endif /* NDEBUG */
303
304 /**
305  * @page debugger   The Firm debugger extension.
306  *
307  * Firm contains a debugger extension. This allows to set debugger breakpoints
308  * an various events.
309  * The extension uses a text interface which can be access in the debugger.
310  *
311  * The following commands are currently supported:
312  *
313  * .init
314  *
315  * Break immediately after the debugger extension was initialized.
316  * Typically this command is used in the environment to stop the execution
317  * of a Firm compiler right after the initialization, like this:
318  *
319  * $export FIRMDBG=".init"
320  *
321  *
322  * .create nr
323  *
324  * Break if a new IR-node with node number nr was created.
325  * Typically used to find the place where wrong nodes are created.
326  *
327  * .lower nr
328  *
329  * Break before IR-node with node number nr is lowered.
330  *
331  * .bp
332  *
333  * Show all Firm internal breakpoints.
334  *
335  * .enable nr
336  *
337  * Enables breakpoint nr.
338  *
339  * .disable nr
340  *
341  * Disables breakpoint nr.
342  *
343  * .help
344  *
345  * List all commands.
346  *
347  *
348  * The Firm debugger extension is access using the function firm_break().
349  * The following example shows how to set a creating breakpoint in GDB when
350  * node 2101 is created.
351  *
352  * 1.) set FRIMDBG=".init"
353  * 2.) start gdb with your compiler
354  * 3.) after gdb breaks, issue
355  *
356  * p firm_debug(".create 2101")
357  *
358  * On the console the following text should be issued:
359  *
360  * Firm BP 1: creation of Node 2101
361  */