2004-06-10 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mono / mini / debug-mini.c
1 /*
2  * debug-mini.c: Mini-specific debugging stuff.
3  *
4  * Author:
5  *   Martin Baulig (martin@ximian.com)
6  *
7  * (C) 2003 Ximian, Inc.
8  */
9
10 #include "mini.h"
11 #include "jit.h"
12 #include <mono/metadata/verify.h>
13 #include <mono/metadata/mono-config.h>
14 #include <mono/metadata/mono-debug.h>
15 #include <mono/metadata/appdomain.h>
16 /* mono-debug-debugger.h needs config.h to work... */
17 #include "config.h"
18 #include <mono/metadata/mono-debug-debugger.h>
19
20 #ifdef HAVE_VALGRIND_H
21 #include <valgrind/valgrind.h>
22 #endif
23
24 static inline void
25 record_line_number (MonoDebugMethodJitInfo *jit, guint32 address, guint32 offset)
26 {
27         MonoDebugLineNumberEntry lne;
28
29         lne.address = address;
30         lne.offset = offset;
31
32         g_array_append_val (jit->line_numbers, lne);
33 }
34
35 typedef struct
36 {
37         MonoDebugMethodJitInfo *jit;
38         guint32 has_line_numbers;
39         guint32 breakpoint_id;
40 } MiniDebugMethodInfo;
41
42 void
43 mono_debug_init_method (MonoCompile *cfg, MonoBasicBlock *start_block, guint32 breakpoint_id)
44 {
45         MonoMethod *method = cfg->method;
46         MiniDebugMethodInfo *info;
47
48         if (mono_debug_format == MONO_DEBUG_FORMAT_NONE)
49                 return;
50
51         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
52             (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
53             (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
54             (method->flags & METHOD_ATTRIBUTE_ABSTRACT) ||
55             (method->wrapper_type != MONO_WRAPPER_NONE))
56                 return;
57
58         info = g_new0 (MiniDebugMethodInfo, 1);
59         info->breakpoint_id = breakpoint_id;
60
61         cfg->debug_info = info;
62 }
63
64 void
65 mono_debug_open_method (MonoCompile *cfg)
66 {
67         MiniDebugMethodInfo *info;
68         MonoDebugMethodJitInfo *jit;
69         MonoMethodHeader *header;
70
71         info = (MiniDebugMethodInfo *) cfg->debug_info;
72         if (!info)
73                 return;
74
75         mono_class_init (cfg->method->klass);
76
77         g_assert (((MonoMethodNormal*)cfg->method)->header);
78         header = ((MonoMethodNormal*)cfg->method)->header;
79
80         info->jit = jit = g_new0 (MonoDebugMethodJitInfo, 1);
81         jit->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
82         jit->num_locals = header->num_locals;
83         jit->locals = g_new0 (MonoDebugVarInfo, jit->num_locals);
84 }
85
86 static void
87 write_variable (MonoInst *inst, MonoDebugVarInfo *var)
88 {
89         if (inst->opcode == OP_REGVAR)
90                 var->index = inst->dreg | MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER;
91         else {
92                 /* the debug interface needs fixing to allow 0(%base) address */
93                 var->index = inst->inst_basereg | MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET;
94                 var->offset = inst->inst_offset;
95         }
96 }
97
98 /*
99  * mono_debug_add_vg_method:
100  *
101  *  Register symbol information for the method with valgrind
102  */
103 static void 
104 mono_debug_add_vg_method (MonoMethod *method, MonoDebugMethodJitInfo *jit)
105 {
106 #ifdef VALGRIND_ADD_LINE_INFO
107         MonoMethodHeader *header;
108         int i;
109         char *filename = NULL;
110         guint32 address, line_number;
111         const char *full_name;
112         guint32 *addresses;
113         guint32 *lines;
114
115         if (!RUNNING_ON_VALGRIND)
116                 return;
117
118         header = ((MonoMethodNormal*)method)->header;
119
120         full_name = mono_method_full_name (method, TRUE);
121
122         addresses = g_new0 (guint32, header->code_size + 1);
123         lines = g_new0 (guint32, header->code_size + 1);
124
125         /* 
126          * Very simple code to convert the addr->offset mappings that mono has
127          * into [addr-addr] ->line number mappings.
128          */
129
130         /* Create offset->line number mapping */
131         for (i = 0; i < header->code_size; ++i) {
132                 char *fname;
133
134                 fname = mono_debug_source_location_from_il_offset (method, i, &lines [i]);
135                 if (!filename)
136                         filename = fname;
137         }
138
139         /* Create address->offset mapping */
140         for (i = 0; i < jit->line_numbers->len; ++i) {
141                 MonoDebugLineNumberEntry *lne = &g_array_index (jit->line_numbers, MonoDebugLineNumberEntry, i);
142
143                 g_assert (lne->offset <= header->code_size);
144
145                 if ((addresses [lne->offset] == 0) || (lne->address < addresses [lne->offset]))
146                         addresses [lne->offset] = lne->address;
147         }
148         /* Fill out missing addresses */
149         address = 0;
150         for (i = 0; i < header->code_size; ++i) {
151                 if (addresses [i] == 0)
152                         addresses [i] = address;
153                 else
154                         address = addresses [i];
155         }
156         
157         address = 0;
158         line_number = 0;
159         i = 0;
160         while (i < header->code_size) {
161                 if (lines [i] == line_number)
162                         i ++;
163                 else {
164                         if (line_number > 0) {
165                                 //g_assert (addresses [i] - 1 >= address);
166                                 
167                                 if (addresses [i] - 1 >= address) {
168                                         VALGRIND_ADD_LINE_INFO (jit->code_start + address, jit->code_start + addresses [i] - 1, filename, line_number);
169                                         //printf ("[%d-%d] -> %d.\n", address, addresses [i] - 1, line_number);
170                                 }
171                         }
172                         address = addresses [i];
173                         line_number = lines [i];
174                 }
175         }
176
177         if (line_number > 0) {
178                 VALGRIND_ADD_LINE_INFO (jit->code_start + address, jit->code_start + jit->code_size - 1, filename, line_number);
179                 //printf ("[%d-%d] -> %d.\n", address, jit->code_size - 1, line_number);
180         }
181
182         VALGRIND_ADD_SYMBOL (jit->code_start, jit->code_size, full_name);
183
184         g_free (addresses);
185         g_free (lines);
186 #endif /* VALGRIND_ADD_LINE_INFO */
187 }
188
189 void
190 mono_debug_close_method (MonoCompile *cfg)
191 {
192         MiniDebugMethodInfo *info;
193         MonoDebugMethodJitInfo *jit;
194         MonoMethodHeader *header;
195         MonoMethod *method;
196         int i;
197
198         info = (MiniDebugMethodInfo *) cfg->debug_info;
199         if (!info || !info->jit)
200                 return;
201
202         method = cfg->method;
203         header = ((MonoMethodNormal*)method)->header;
204
205         jit = info->jit;
206         jit->code_start = cfg->native_code;
207         jit->epilogue_begin = cfg->epilog_begin;
208         jit->code_size = cfg->code_len;
209
210         record_line_number (jit, jit->epilogue_begin, header->code_size);
211
212         jit->num_params = method->signature->param_count;
213         jit->params = g_new0 (MonoDebugVarInfo, jit->num_params);
214
215         for (i = 0; i < jit->num_locals; i++)
216                 write_variable (cfg->varinfo [cfg->locals_start + i], &jit->locals [i]);
217
218         if (method->signature->hasthis) {
219                 jit->this_var = g_new0 (MonoDebugVarInfo, 1);
220                 write_variable (cfg->varinfo [0], jit->this_var);
221         }
222
223         for (i = 0; i < jit->num_params; i++)
224                 write_variable (cfg->varinfo [i + method->signature->hasthis], &jit->params [i]);
225
226         mono_debug_add_method (method, jit, cfg->domain);
227
228         mono_debug_add_vg_method (method, jit);
229
230         if (info->breakpoint_id)
231                 mono_debugger_breakpoint_callback (method, info->breakpoint_id);
232 }
233
234 void
235 mono_debug_record_line_number (MonoCompile *cfg, MonoInst *ins, guint32 address)
236 {
237         MiniDebugMethodInfo *info;
238         MonoMethodHeader *header;
239         guint32 offset;
240
241         info = (MiniDebugMethodInfo *) cfg->debug_info;
242         if (!info || !info->jit || !ins->cil_code)
243                 return;
244
245         g_assert (((MonoMethodNormal*)cfg->method)->header);
246         header = ((MonoMethodNormal*)cfg->method)->header;
247
248         if ((ins->cil_code < header->code) ||
249             (ins->cil_code > header->code + header->code_size))
250                 return;
251
252         offset = ins->cil_code - header->code;
253         if (!info->has_line_numbers) {
254                 info->jit->prologue_end = address;
255                 info->has_line_numbers = TRUE;
256         }
257
258         record_line_number (info->jit, address, offset);
259 }
260
261 static inline void
262 encode_value (gint32 value, char *buf, char **endbuf)
263 {
264         char *p = buf;
265
266         //printf ("ENCODE: %d 0x%x.\n", value, value);
267
268         /* 
269          * Same encoding as the one used in the metadata, extended to handle values
270          * greater than 0x1fffffff.
271          */
272         if ((value >= 0) && (value <= 127))
273                 *p++ = value;
274         else if ((value >= 0) && (value <= 16384)) {
275                 p [0] = 0x80 | (value >> 8);
276                 p [1] = value & 0xff;
277                 p += 2;
278         } else if ((value >= 0) && (value <= 0x1fffffff)) {
279                 p [0] = (value >> 24) | 0xc0;
280                 p [1] = (value >> 16) & 0xff;
281                 p [2] = (value >> 8) & 0xff;
282                 p [3] = value & 0xff;
283                 p += 4;
284         }
285         else {
286                 p [0] = 0xff;
287                 p [1] = (value >> 24) & 0xff;
288                 p [2] = (value >> 16) & 0xff;
289                 p [3] = (value >> 8) & 0xff;
290                 p [4] = value & 0xff;
291                 p += 5;
292         }
293         if (endbuf)
294                 *endbuf = p;
295 }
296
297 static inline gint32
298 decode_value (char *_ptr, char **rptr)
299 {
300         unsigned char *ptr = (unsigned char *) _ptr;
301         unsigned char b = *ptr;
302         gint32 len;
303         
304         if ((b & 0x80) == 0){
305                 len = b;
306                 ++ptr;
307         } else if ((b & 0x40) == 0){
308                 len = ((b & 0x3f) << 8 | ptr [1]);
309                 ptr += 2;
310         } else if (b != 0xff) {
311                 len = ((b & 0x1f) << 24) |
312                         (ptr [1] << 16) |
313                         (ptr [2] << 8) |
314                         ptr [3];
315                 ptr += 4;
316         }
317         else {
318                 len = (ptr [1] << 24) | (ptr [2] << 16) | (ptr [3] << 8) | ptr [4];
319                 ptr += 5;
320         }
321         if (rptr)
322                 *rptr = ptr;
323
324         //printf ("DECODE: %d.\n", len);
325         return len;
326 }
327
328 static void
329 serialize_variable (MonoDebugVarInfo *var, char *p, char **endbuf)
330 {
331         guint32 flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
332
333         switch (flags) {
334         case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
335                 encode_value (var->index, p, &p);
336                 break;
337         case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
338                 encode_value (var->index, p, &p);
339                 encode_value (var->offset, p, &p);
340                 break;
341         default:
342                 g_assert_not_reached ();
343         }
344         *endbuf = p;
345 }
346
347 void
348 mono_debug_serialize_debug_info (MonoCompile *cfg, 
349                                                                  guint8 **out_buf, guint32 *buf_len)
350 {
351         MiniDebugMethodInfo *info;
352         MonoDebugMethodJitInfo *jit;
353         guint32 size, prev_offset, prev_native_offset;
354         char *buf;
355         char *p;
356         int i;
357
358         info = (MiniDebugMethodInfo *) cfg->debug_info;
359         if (!info || !info->jit) {
360                 *buf_len = 0;
361                 return;
362         }
363         jit = info->jit;
364
365         size = ((jit->num_params + jit->num_locals + 1) * 10) + (jit->line_numbers->len * 10) + 64;
366         p = buf = g_malloc (size);
367         encode_value (jit->epilogue_begin, p, &p);
368     encode_value (jit->prologue_end, p, &p);
369         encode_value (jit->code_size, p, &p);
370
371         for (i = 0; i < jit->num_params; ++i)
372                 serialize_variable (&jit->params [i], p, &p);
373
374         if (cfg->method->signature->hasthis)
375                 serialize_variable (jit->this_var, p, &p);
376
377         for (i = 0; i < jit->num_locals; i++)
378                 serialize_variable (&jit->locals [i], p, &p);
379
380         encode_value (jit->line_numbers->len, p, &p);
381
382         prev_offset = 0;
383         prev_native_offset = 0;
384         for (i = 0; i < jit->line_numbers->len; ++i) {
385                 /* Sometimes, the offset values are not in increasing order */
386                 MonoDebugLineNumberEntry *lne = &g_array_index (jit->line_numbers, 
387                                                                                                                 MonoDebugLineNumberEntry,
388                                                                                                                 i);
389                 encode_value (lne->offset - prev_offset, p, &p);
390                 encode_value (lne->address - prev_native_offset, p, &p);
391                 prev_offset = lne->offset;
392                 prev_native_offset = lne->address;
393         }
394
395         g_assert (p - buf < size);
396
397         *out_buf = buf;
398         *buf_len = p - buf;
399 }
400
401 static void
402 deserialize_variable (MonoDebugVarInfo *var, char *p, char **endbuf)
403 {
404         guint32 flags;
405
406         var->index = decode_value (p, &p);
407
408         flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
409
410         switch (flags) {
411         case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
412                 break;
413         case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
414                 var->offset = decode_value (p, &p);
415                 break;
416         default:
417                 g_assert_not_reached ();
418         }
419         *endbuf = p;
420 }
421
422 static MonoDebugMethodJitInfo *
423 deserialize_debug_info (MonoMethod *method,
424                                                 guint8 *code_start, 
425                                                 guint8 *buf, guint32 buf_len)
426 {
427         MonoMethodHeader *header;
428         MonoDebugMethodJitInfo *jit;
429         gint32 offset, native_offset, prev_offset, prev_native_offset, len;
430         char *p;
431         int i;
432
433         g_assert (((MonoMethodNormal*)method)->header);
434         header = ((MonoMethodNormal*)method)->header;
435
436         jit = g_new0 (MonoDebugMethodJitInfo, 1);
437         jit->code_start = code_start;
438         jit->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
439         jit->num_locals = header->num_locals;
440         jit->locals = g_new0 (MonoDebugVarInfo, jit->num_locals);
441         jit->num_params = method->signature->param_count;
442         jit->params = g_new0 (MonoDebugVarInfo, jit->num_params);
443
444         p = buf;
445         jit->epilogue_begin = decode_value (p, &p);
446         jit->prologue_end = decode_value (p, &p);
447         jit->code_size = decode_value (p, &p);
448
449         for (i = 0; i < jit->num_params; ++i)
450                 deserialize_variable (&jit->params [i], p, &p);
451
452         if (method->signature->hasthis) {
453                 jit->this_var = g_new0 (MonoDebugVarInfo, 1);
454                 deserialize_variable (jit->this_var, p, &p);
455         }
456
457         for (i = 0; i < jit->num_locals; i++)
458                 deserialize_variable (&jit->locals [i], p, &p);
459
460         len = decode_value (p, &p);
461
462         prev_offset = 0;
463         prev_native_offset = 0;
464         for (i = 0; i < len; ++i) {
465                 offset = prev_offset + decode_value (p, &p);
466                 native_offset = prev_native_offset + decode_value (p, &p);
467                 record_line_number (jit, native_offset, offset);
468                 prev_offset = offset;
469                 prev_native_offset = native_offset;
470         }
471
472         return jit;
473 }
474
475 void
476 mono_debug_add_aot_method (MonoDomain *domain,
477                                                    MonoMethod *method, guint8 *code_start, 
478                                                    guint8 *debug_info, guint32 debug_info_len)
479 {
480         MonoDebugMethodJitInfo *jit;
481
482         if (mono_debug_format == MONO_DEBUG_FORMAT_NONE)
483                 return;
484
485         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
486             (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
487             (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
488             (method->flags & METHOD_ATTRIBUTE_ABSTRACT) ||
489             (method->wrapper_type != MONO_WRAPPER_NONE))
490                 return;
491
492         if (debug_info_len == 0)
493                 return;
494
495         jit = deserialize_debug_info (method, code_start,
496                                                                   debug_info,
497                                                                   debug_info_len);
498
499         mono_debug_add_method (method, jit, domain);
500
501         mono_debug_add_vg_method (method, jit);
502 }
503
504 MonoDomain *
505 mono_init_debugger (const char *file, const char *opt_flags)
506 {
507         MonoDomain *domain;
508         const guchar *error;
509         int opt;
510
511         g_set_prgname (file);
512
513         opt = mono_parse_default_optimizations (opt_flags);
514         opt |= MONO_OPT_SHARED;
515
516         mono_set_defaults (0, opt);
517
518         domain = mono_jit_init (file);
519
520         mono_config_parse (NULL);
521
522         error = mono_check_corlib_version ();
523         if (error) {
524                 fprintf (stderr, "Corlib not in sync with this runtime: %s\n", error);
525                 fprintf (stderr, "Download a newer corlib or a newer runtime at http://www.go-mono.com/daily.\n");
526                 exit (1);
527         }
528
529         return domain;
530 }