2003-01-26 Martin Baulig <martin@ximian.com>
[mono.git] / mono / jit / debug.c
1 #include <config.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <signal.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8 #include <mono/metadata/class.h>
9 #include <mono/metadata/assembly.h>
10 #include <mono/metadata/tabledefs.h>
11 #include <mono/metadata/tokentype.h>
12 #include <mono/metadata/debug-helpers.h>
13 #include <mono/metadata/debug-mono-symfile.h>
14 #include <mono/jit/codegen.h>
15 #include <mono/jit/debug.h>
16
17 #include "debug-private.h"
18 #include "helpers.h"
19
20 /*
21  * NOTE:  Functions and variables starting with `mono_debug_' and `debug_' are
22  *        part of the general debugging code.
23  *
24  *        Functions and variables starting with `mono_debugger_' and `debugger_'
25  *        are only used when the JIT is running inside the Mono Debugger.
26  *
27  * FIXME: This file needs some API loving.
28  */
29
30 /* This is incremented each time the symbol table is modified.
31  * The debugger looks at this variable and if it has a higher value than its current
32  * copy of the symbol table, it must call mono_debug_update_symbol_file_table().
33  */
34 guint32 mono_debugger_symbol_file_table_generation = 0;
35 guint32 mono_debugger_symbol_file_table_modified = 0;
36
37 /* Caution: This variable may be accessed at any time from the debugger;
38  *          it is very important not to modify the memory it is pointing to
39  *          without previously setting this pointer back to NULL.
40  */
41 MonoDebuggerSymbolFileTable *mono_debugger_symbol_file_table = NULL;
42
43 /* Caution: This function MUST be called before touching the symbol table! */
44 static void release_symbol_file_table (void);
45
46 static MonoDebugHandle *mono_debug_handle = NULL;
47 static gboolean mono_debug_initialized = FALSE;
48
49 static CRITICAL_SECTION debugger_lock_mutex;
50
51 extern void (*mono_debugger_class_init_func) (MonoClass *klass);
52
53 static void mono_debug_add_assembly (MonoAssembly *assembly, gpointer user_data);
54 static void mono_debug_close_assembly (AssemblyDebugInfo* info);
55 static AssemblyDebugInfo *mono_debug_open_image (MonoDebugHandle* debug, MonoImage *image);
56
57 static int running_in_the_mono_debugger = FALSE;
58 void (*mono_debugger_event_handler) (MonoDebuggerEvent event, gpointer data, gpointer data2) = NULL;
59
60 #ifndef PLATFORM_WIN32
61
62 MonoDebuggerIOLayer mono_debugger_io_layer = {
63         InitializeCriticalSection, DeleteCriticalSection, TryEnterCriticalSection,
64         EnterCriticalSection, LeaveCriticalSection, WaitForSingleObject, SignalObjectAndWait,
65         WaitForMultipleObjects, CreateSemaphore, ReleaseSemaphore, CreateThread
66 };
67
68 #endif
69
70 void
71 mono_debugger_event (MonoDebuggerEvent event, gpointer data, gpointer data2)
72 {
73         if (mono_debugger_event_handler)
74                 (* mono_debugger_event_handler) (event, data, data2);
75 }
76
77 void
78 mono_debug_init (int in_the_debugger)
79 {
80         if (mono_debug_initialized)
81                 return;
82
83         InitializeCriticalSection (&debugger_lock_mutex);
84         mono_debug_initialized = TRUE;
85         running_in_the_mono_debugger = in_the_debugger;
86 }
87
88 gpointer
89 mono_debug_create_notification_function (gpointer *notification_address)
90 {
91         guint8 *ptr, *buf;
92
93         ptr = buf = g_malloc0 (16);
94         x86_breakpoint (buf);
95         if (notification_address)
96                 *notification_address = buf;
97         x86_ret (buf);
98
99         return ptr;
100 }
101
102 void
103 mono_debug_lock (void)
104 {
105         if (mono_debug_initialized)
106                 EnterCriticalSection (&debugger_lock_mutex);
107 }
108
109 void
110 mono_debug_unlock (void)
111 {
112         if (mono_debug_initialized)
113                 LeaveCriticalSection (&debugger_lock_mutex);
114 }
115
116 static void
117 free_method_info (MonoDebugMethodInfo *minfo)
118 {
119         DebugMethodInfo *priv = minfo->user_data;
120
121         if (priv) {
122                 g_free (priv->name);
123                 g_free (priv);
124         }
125
126         if (minfo->jit) {
127                 g_array_free (minfo->jit->line_numbers, TRUE);
128                 g_free (minfo->jit->this_var);
129                 g_free (minfo->jit->params);
130                 g_free (minfo->jit->locals);
131                 g_free (minfo->jit);
132         }
133
134         g_free (minfo->il_offsets);
135         g_free (minfo);
136 }
137
138 static void
139 debug_arg_warning (const char *message)
140 {
141         g_warning ("Error while processing --debug-args arguments: %s", message);
142 }
143
144 static gchar *
145 replace_suffix (const char *filename, const char *new_suffix)
146 {
147         const char *pos = strrchr (filename, '.');
148
149         if (!pos)
150                 return g_strdup_printf ("%s.%s", filename, new_suffix);
151         else {
152                 int len = pos - filename;
153                 gchar *retval = g_malloc0 (len + strlen (new_suffix) + 2);
154                 memcpy (retval, filename, len);
155                 retval [len] = '.';
156                 memcpy (retval + len + 1, new_suffix, strlen (new_suffix) + 1);
157                 return retval;
158         }
159 }
160
161 MonoDebugHandle*
162 mono_debug_open (MonoAssembly *assembly, MonoDebugFormat format, const char **args)
163 {
164         MonoDebugHandle *debug;
165         const char **ptr;
166
167         g_assert (!mono_debug_handle);
168
169         debug = g_new0 (MonoDebugHandle, 1);
170         debug->name = g_strdup (assembly->image->name);
171         debug->format = format;
172         debug->producer_name = g_strdup_printf ("Mono JIT compiler version %s", VERSION);
173         debug->next_idx = 100;
174         debug->dirty = TRUE;
175
176         debug->type_hash = g_hash_table_new (NULL, NULL);
177         debug->source_files = g_ptr_array_new ();
178
179         debug->images = g_hash_table_new_full (NULL, NULL, NULL,
180                                                (GDestroyNotify) mono_debug_close_assembly);
181
182         for (ptr = args; ptr && *ptr; ptr++) {
183                 const char *arg = *ptr;
184                 gchar *message;
185
186                 switch (debug->format) {
187                 case MONO_DEBUG_FORMAT_STABS:
188                 case MONO_DEBUG_FORMAT_DWARF2:
189                         if (!strncmp (arg, "filename=", 9)) {
190                                 if (debug->filename)
191                                         debug_arg_warning ("The `filename' argument can be given only once.");
192                                 debug->filename = g_strdup (arg + 9);
193                                 continue;
194                         } else if (!strncmp (arg, "objfile=", 8)) {
195                                 if (debug->objfile)
196                                         debug_arg_warning ("The `objfile' argument can be given only once.");
197                                 debug->objfile = g_strdup (arg + 8);
198                                 continue;
199                         }
200                         break;
201                 case MONO_DEBUG_FORMAT_MONO:
202                         debug->flags |= MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES |
203                                 MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES;
204                         break;
205                 default:
206                         break;
207                 }
208
209                 if (debug->format != MONO_DEBUG_FORMAT_MONO) {
210                         if (!strcmp (arg, "dont_assemble")) {
211                                 debug->flags |= MONO_DEBUG_FLAGS_DONT_ASSEMBLE;
212                                 continue;
213                         } else if (!strcmp (arg, "update_on_exit")) {
214                                 debug->flags |= MONO_DEBUG_FLAGS_UPDATE_ON_EXIT;
215                                 continue;
216                         } else if (!strcmp (arg, "install_il_files")) {
217                                 debug->flags |= MONO_DEBUG_FLAGS_INSTALL_IL_FILES;
218                                 continue;
219                         } else if (!strcmp (arg, "dont_update_il_files")) {
220                                 debug->flags |= MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES;
221                                 continue;
222                         } else if (!strcmp (arg, "dont_create_il_files")) {
223                                 debug->flags |= MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES;
224                                 continue;
225                         }
226                 }
227
228                 message = g_strdup_printf ("Unknown argument `%s'.", arg);
229                 debug_arg_warning (message);
230                 g_free (message);
231         }
232
233         switch (debug->format) {
234         case MONO_DEBUG_FORMAT_STABS:
235                 if (!debug->filename)
236                         debug->filename = g_strdup_printf ("%s-stabs.s", g_basename (debug->name));
237                 if (!debug->objfile)
238                         debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name));
239                 break;
240         case MONO_DEBUG_FORMAT_DWARF2:
241                 if (!debug->filename)
242                         debug->filename = g_strdup_printf ("%s-dwarf.s", g_basename (debug->name));
243                 if (!debug->objfile)
244                         debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name));
245                 break;
246         case MONO_DEBUG_FORMAT_MONO:
247                 mono_debugger_class_init_func = mono_debug_add_type;
248                 break;
249         default:
250                 g_assert_not_reached ();
251         }
252
253         mono_debug_lock ();
254         release_symbol_file_table ();
255
256         mono_debug_handle = debug;
257         mono_install_assembly_load_hook (mono_debug_add_assembly, NULL);
258
259         mono_debug_open_image (mono_debug_handle, assembly->image);
260         mono_debug_open_image (mono_debug_handle, mono_defaults.corlib);
261
262         mono_debug_add_type (mono_defaults.object_class);
263         mono_debug_add_type (mono_defaults.object_class);
264         mono_debug_add_type (mono_defaults.byte_class);
265         mono_debug_add_type (mono_defaults.void_class);
266         mono_debug_add_type (mono_defaults.boolean_class);
267         mono_debug_add_type (mono_defaults.sbyte_class);
268         mono_debug_add_type (mono_defaults.int16_class);
269         mono_debug_add_type (mono_defaults.uint16_class);
270         mono_debug_add_type (mono_defaults.int32_class);
271         mono_debug_add_type (mono_defaults.uint32_class);
272         mono_debug_add_type (mono_defaults.int_class);
273         mono_debug_add_type (mono_defaults.uint_class);
274         mono_debug_add_type (mono_defaults.int64_class);
275         mono_debug_add_type (mono_defaults.uint64_class);
276         mono_debug_add_type (mono_defaults.single_class);
277         mono_debug_add_type (mono_defaults.double_class);
278         mono_debug_add_type (mono_defaults.char_class);
279         mono_debug_add_type (mono_defaults.string_class);
280         mono_debug_add_type (mono_defaults.enum_class);
281         mono_debug_add_type (mono_defaults.array_class);
282         mono_debug_add_type (mono_defaults.multicastdelegate_class);
283         mono_debug_add_type (mono_defaults.asyncresult_class);
284         mono_debug_add_type (mono_defaults.waithandle_class);
285         mono_debug_add_type (mono_defaults.typehandle_class);
286         mono_debug_add_type (mono_defaults.fieldhandle_class);
287         mono_debug_add_type (mono_defaults.methodhandle_class);
288         mono_debug_add_type (mono_defaults.monotype_class);
289         mono_debug_add_type (mono_defaults.exception_class);
290         mono_debug_add_type (mono_defaults.threadabortexception_class);
291         mono_debug_add_type (mono_defaults.thread_class);
292         mono_debug_add_type (mono_defaults.transparent_proxy_class);
293         mono_debug_add_type (mono_defaults.real_proxy_class);
294         mono_debug_add_type (mono_defaults.mono_method_message_class);
295         mono_debug_add_type (mono_defaults.appdomain_class);
296         mono_debug_add_type (mono_defaults.field_info_class);
297         mono_debug_add_type (mono_defaults.stringbuilder_class);
298         mono_debug_add_type (mono_defaults.math_class);
299         mono_debug_add_type (mono_defaults.stack_frame_class);
300         mono_debug_add_type (mono_defaults.stack_trace_class);
301         mono_debug_add_type (mono_defaults.marshal_class);
302         mono_debug_add_type (mono_defaults.iserializeable_class);
303         mono_debug_add_type (mono_defaults.serializationinfo_class);
304         mono_debug_add_type (mono_defaults.streamingcontext_class);
305
306         mono_debug_update_symbol_file_table ();
307
308         mono_debug_unlock ();
309
310         return debug;
311 }
312
313 static void
314 mono_debug_add_assembly (MonoAssembly *assembly, gpointer user_data)
315 {
316         if (!mono_debug_handle)
317                 return;
318
319         mono_debug_lock ();
320         mono_debug_open_image (mono_debug_handle, assembly->image);
321         mono_debug_unlock ();
322 }
323
324 static void
325 generate_il_offsets (AssemblyDebugInfo *info, MonoMethod *method)
326 {
327         GPtrArray *il_offsets = g_ptr_array_new ();
328         MonoClass *klass = method->klass;
329         MonoDebugMethodInfo *minfo;
330         DebugMethodInfo *priv;
331         int i;
332
333         g_assert (klass->image == info->image);
334
335         /* FIXME: doesn't work yet. */
336         if (!strcmp (klass->name_space, "System.Runtime.Remoting.Proxies"))
337                 return;
338
339         mono_class_init (klass);
340
341         minfo = g_new0 (MonoDebugMethodInfo, 1);
342         minfo->method = method;
343         minfo->user_data = priv = g_new0 (DebugMethodInfo, 1);
344
345         priv->name = g_strdup_printf ("%s%s%s.%s", klass->name_space, klass->name_space [0]? ".": "",
346                                       klass->name, method->name);
347         priv->source_file = info->source_file;
348         priv->info = info;
349
350         /*
351          * Find the method index in the image.
352          */
353         for (i = 0; klass->methods && i < klass->method.count; ++i) {
354                 if (klass->methods [i] == minfo->method) {
355                         priv->method_number = klass->method.first + i + 1;
356                         priv->first_line = info->mlines [priv->method_number];
357                         break;
358                 }
359         }
360
361         g_assert (priv->method_number);
362
363         /* info->moffsets contains -1 "outside" of functions. */
364         for (i = priv->first_line; (i > 0) && (info->moffsets [i] == 0); i--)
365                 ;
366         priv->start_line = i + 1;
367
368         for (i = priv->start_line; info->moffsets [i] != -1; i++) {
369                 MonoSymbolFileLineNumberEntry *lne = g_new0 (MonoSymbolFileLineNumberEntry, 1);
370
371                 if (!info->moffsets [i] && (i > priv->start_line))
372                         continue;
373
374                 lne->offset = info->moffsets [i];
375                 lne->row = i;
376
377                 g_ptr_array_add (il_offsets, lne);
378         }
379
380         priv->last_line = i;
381
382         minfo->start_line = priv->first_line;
383         minfo->end_line = priv->last_line;
384
385         minfo->num_il_offsets = il_offsets->len;
386         minfo->il_offsets = g_new0 (MonoSymbolFileLineNumberEntry, il_offsets->len);
387         for (i = 0; i < il_offsets->len; i++) {
388                 MonoSymbolFileLineNumberEntry *il = g_ptr_array_index (il_offsets, i);
389
390                 minfo->il_offsets [i] = *il;
391         }
392
393         g_ptr_array_free (il_offsets, TRUE);
394
395         g_hash_table_insert (info->methods, method, minfo);
396 }
397
398 static void
399 debug_load_method_lines (AssemblyDebugInfo* info)
400 {
401         MonoTableInfo *table = &info->image->tables [MONO_TABLE_METHOD];
402         FILE *f;
403         char buf [1024];
404         int i, mnum, idx;
405         int offset = -1;
406
407         if (info->always_create_il || !(info->handle->flags & MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES)) {
408                 char *command = g_strdup_printf ("monodis --output=%s %s",
409                                                  info->ilfile, info->image->name);
410                 struct stat stata, statb;
411                 int need_update = FALSE;
412
413                 if (stat (info->image->name, &stata)) {
414                         g_warning ("cannot access assembly file (%s): %s",
415                                    info->image->name, g_strerror (errno));
416                         g_free (command);
417                         return;
418                 }
419
420                 /* If the stat() failed or the file is older. */
421                 if (stat (info->ilfile, &statb)) {
422                         need_update = TRUE;
423                 } else if (statb.st_mtime < stata.st_mtime)
424                         need_update = TRUE;
425
426                 if (need_update) {
427 #ifndef PLATFORM_WIN32
428                         struct sigaction act, oldact;
429                         sigset_t old_set;
430 #endif
431                         int ret;
432
433 #ifndef PLATFORM_WIN32
434                         act.sa_handler = SIG_IGN;
435                         act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
436                         sigemptyset (&act.sa_mask);
437                         sigaddset (&act.sa_mask, SIGCHLD);
438                         sigprocmask (SIG_BLOCK, &act.sa_mask, &old_set);
439                         sigaction (SIGCHLD, &act, &oldact);
440 #endif
441                         
442                         g_print ("Recreating %s from %s.\n", info->ilfile, info->image->name);
443
444                         ret = system (command);
445
446 #ifndef PLATFORM_WIN32
447                         sigaction (SIGCHLD, &oldact, NULL);
448                         sigprocmask (SIG_SETMASK, &old_set, NULL);
449 #endif
450
451                         if (ret) {
452                                 g_warning ("cannot create IL assembly file (%s): %s",
453                                            command, g_strerror (errno));
454                                 g_free (command);
455                                 return;
456                         }
457                 }
458         }
459
460         /* use an env var with directories for searching. */
461         if (!(f = fopen (info->ilfile, "r"))) {
462                 g_warning ("cannot open IL assembly file %s", info->ilfile);
463                 return;
464         }
465
466         info->total_lines = 100;
467         info->moffsets = g_malloc (info->total_lines * sizeof (int));
468
469         i = 0;
470         while (fgets (buf, sizeof (buf), f)) {
471                 int pos = i;
472
473                 info->moffsets [i++] = offset;
474                 if (i + 2 >= info->total_lines) {
475                         info->total_lines += 100;
476                         info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
477                         g_assert (info->moffsets);
478                 }
479
480                 if (!sscanf (buf, " // method line %d", &mnum))
481                         continue;
482
483                 offset = 0;
484
485                 if (mnum >= info->nmethods)
486                         break;
487
488                 while (fgets (buf, sizeof (buf), f)) {
489                         int newoffset;
490
491                         ++i;
492                         if (i + 2 >= info->total_lines) {
493                                 info->total_lines += 100;
494                                 info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
495                                 g_assert (info->moffsets);
496                         }
497
498                         if (strstr (buf, "}")) {
499                                 offset = -1;
500                                 break;
501                         }
502
503                         if (sscanf (buf, " IL_%x:", &newoffset)) {
504                                 offset = newoffset;
505                                 if (!offset)
506                                         pos = i;
507                         }
508
509                         info->moffsets [i] = offset;
510                 }
511                 /* g_print ("method %d found at %d\n", mnum, pos); */
512                 info->mlines [mnum] = pos;
513         }
514         fclose (f);
515
516         for (idx = 1; idx <= table->rows; idx++) {
517                 guint32 token = mono_metadata_make_token (MONO_TABLE_METHOD, idx);
518                 MonoMethod *method = mono_get_method (info->image, token, NULL);
519
520                 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
521                     (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
522                     (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
523                         continue;
524
525                 if (method->wrapper_type != MONO_WRAPPER_NONE)
526                         continue;
527
528                 generate_il_offsets (info, method);
529         }
530 }
531
532 static void
533 record_line_number (MonoDebugMethodInfo *minfo, guint32 address, guint32 offset, guint32 line)
534 {
535         MonoDebugLineNumberEntry *lne = g_new0 (MonoDebugLineNumberEntry, 1);
536
537         lne->address = address;
538         lne->offset = offset;
539         lne->line = line;
540
541         g_array_append_val (minfo->jit->line_numbers, *lne);
542 }
543
544 static void
545 debug_generate_method_lines (AssemblyDebugInfo *info, MonoDebugMethodInfo *minfo, MonoFlowGraph* cfg)
546 {
547         guint32 st_address, st_line;
548         DebugMethodInfo *priv = minfo->user_data;
549         int i;
550
551         if (!priv || !info->moffsets)
552                 return;
553
554         minfo->jit->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
555
556         st_line = priv->first_line;
557         st_address = minfo->jit->prologue_end;
558
559         /* This is the first actual code line of the method. */
560         record_line_number (minfo, st_address, 0, st_line);
561
562         /* start lines of basic blocks */
563         for (i = 0; i < cfg->block_count; ++i) {
564                 int j;
565
566                 for (j = 0; cfg->bblocks [i].forest && (j < cfg->bblocks [i].forest->len); ++j) {
567                         MBTree *t = (MBTree *) g_ptr_array_index (cfg->bblocks [i].forest, j);
568                         gint32 line_inc = 0, addr_inc;
569
570                         if (!i && !j) {
571                                 st_line = priv->first_line;
572                                 st_address = t->addr;
573                         }
574
575                         addr_inc = t->addr - st_address;
576                         st_address += addr_inc;
577
578                         if (t->cli_addr != -1) {
579                                 int *lines = info->moffsets + st_line;
580                                 int *k = lines;
581
582                                 while ((*k != -1) && (*k < t->cli_addr))
583                                         k++;
584
585                                 line_inc = k - lines;
586                         }
587
588                         st_line += line_inc;
589
590                         if (t->cli_addr != -1)
591                                 record_line_number (minfo, st_address, t->cli_addr, st_line);
592                 }
593         }
594 }
595
596 static void
597 generate_line_number (MonoDebugMethodInfo *minfo, guint32 address, guint32 offset, int debug)
598 {
599         int i;
600
601         if (debug)
602                 g_message (G_STRLOC ": searching IL offset %x", offset);
603
604         for (i = minfo->num_il_offsets - 1; i >= 0; i--) {
605                 MonoDebugLineNumberEntry *lne;
606
607                 if (minfo->il_offsets [i].offset > offset)
608                         continue;
609
610                 if (debug)
611                         g_message (G_STRLOC ": found entry %d: offset = %x, row = %d",
612                                    i, minfo->il_offsets [i].offset, minfo->il_offsets [i].row);
613
614                 if (minfo->jit->line_numbers->len) {
615                         MonoDebugLineNumberEntry last = g_array_index (
616                                 minfo->jit->line_numbers, MonoDebugLineNumberEntry,
617                                 minfo->jit->line_numbers->len - 1);
618
619                         /* Avoid writing more than one entry for the same line. */
620                         if (minfo->il_offsets [i].row == last.line) {
621                                 if (debug)
622                                         g_message (G_STRLOC ": skipping line: line = %d, last line = %d, "
623                                                    "last address = %x, address = %x, "
624                                                    "last offset = %x, offset = %x",
625                                                    last.line, minfo->il_offsets [i].row,
626                                                    last.address, address, last.offset, offset);
627
628                                 return;
629                         }
630                 }
631
632                 if (debug)
633                         g_message (G_STRLOC ": writing entry: line = %d, offfset = %x, address = %x",
634                                    minfo->il_offsets [i].row, offset, address);
635
636                 lne = g_new0 (MonoDebugLineNumberEntry, 1);
637                 lne->address = address;
638                 lne->offset = offset;
639                 lne->line = minfo->il_offsets [i].row;
640
641                 g_array_append_val (minfo->jit->line_numbers, *lne);
642                 return;
643         }
644 }
645
646 static void
647 debug_update_il_offsets (AssemblyDebugInfo *info, MonoDebugMethodInfo *minfo, MonoFlowGraph* cfg)
648 {
649         MonoMethodHeader *header;
650         guint32 address, offset;
651         int debug = 0;
652         int i;
653
654         g_assert (info->symfile);
655         if (info->symfile->is_dynamic)
656                 return;
657
658         g_assert (!minfo->jit->line_numbers);
659         minfo->jit->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
660
661         address = minfo->jit->prologue_end;
662         offset = 0;
663
664         g_assert (((MonoMethodNormal*)minfo->method)->header);
665         header = ((MonoMethodNormal*)minfo->method)->header;
666
667 #if 0
668         if (!strcmp (minfo->method->name, "Test") || !strcmp (minfo->method->name, "Main")) {
669                 MonoMethodHeader *header = ((MonoMethodNormal*)minfo->method)->header;
670
671                 debug = 1;
672                 mono_disassemble_code (minfo->jit->code_start, minfo->jit->code_size,
673                                        minfo->method->name);
674
675                 printf ("\nDisassembly:\n%s\n", mono_disasm_code (
676                         NULL, minfo->method, header->code, header->code + header->code_size));
677                 g_message (G_STRLOC ": %x - %x", minfo->jit->prologue_end, minfo->jit->epilogue_begin);
678         }
679 #endif
680
681         generate_line_number (minfo, address, offset, debug);
682
683         /* start lines of basic blocks */
684         for (i = 0; i < cfg->block_count; ++i) {
685                 int j;
686
687                 for (j = 0; cfg->bblocks [i].forest && (j < cfg->bblocks [i].forest->len); ++j) {
688                         MBTree *t = (MBTree *) g_ptr_array_index (cfg->bblocks [i].forest, j);
689
690                         if ((t->cli_addr == -1) || (t->cli_addr == offset) || (t->addr == address))
691                                 continue;
692
693                         offset = t->cli_addr;
694                         address = t->addr;
695
696                         generate_line_number (minfo, address, offset, debug);
697                 }
698         }
699
700         generate_line_number (minfo, minfo->jit->epilogue_begin, header->code_size, debug);
701
702         if (debug) {
703                 for (i = 0; i < minfo->jit->line_numbers->len; i++) {
704                         MonoDebugLineNumberEntry lne = g_array_index (
705                                 minfo->jit->line_numbers, MonoDebugLineNumberEntry, i);
706
707                         g_message (G_STRLOC ": %x,%x,%d", lne.address, lne.offset, lne.line);
708                 }
709         }
710 }
711
712 static AssemblyDebugInfo *
713 mono_debug_get_image (MonoDebugHandle* debug, MonoImage *image)
714 {
715         return g_hash_table_lookup (debug->images, image);
716 }
717
718 static AssemblyDebugInfo *
719 mono_debug_open_image (MonoDebugHandle* debug, MonoImage *image)
720 {
721         AssemblyDebugInfo *info;
722         MonoAssembly **ptr;
723
724         info = mono_debug_get_image (debug, image);
725         if (info != NULL)
726                 return info;
727
728         debug->dirty = TRUE;
729
730         info = g_new0 (AssemblyDebugInfo, 1);
731         info->image = image;
732         info->image->ref_count++;
733         info->name = g_strdup (image->assembly_name);
734         info->format = debug->format;
735         info->handle = debug;
736         info->methods = g_hash_table_new_full (g_direct_hash, g_direct_equal,
737                                                NULL, (GDestroyNotify) free_method_info);
738
739         info->source_file = debug->source_files->len;
740         g_ptr_array_add (debug->source_files, g_strdup_printf ("%s.il", image->assembly_name));
741
742         g_hash_table_insert (debug->images, image, info);
743
744         info->nmethods = image->tables [MONO_TABLE_METHOD].rows + 1;
745         info->mlines = g_new0 (int, info->nmethods);
746
747         for (ptr = image->references; ptr && *ptr; ptr++)
748                 mono_debug_add_assembly (*ptr, NULL);
749
750         switch (info->format) {
751         case MONO_DEBUG_FORMAT_STABS:
752         case MONO_DEBUG_FORMAT_DWARF2:
753                 if (debug->flags & MONO_DEBUG_FLAGS_INSTALL_IL_FILES) {
754                         gchar *dirname = g_path_get_dirname (image->name);
755                         info->ilfile = g_strdup_printf ("%s/%s.il", dirname, info->name);
756                         g_free (dirname);
757                 } else
758                         info->ilfile = g_strdup_printf ("%s.il", info->name);
759                 break;
760         case MONO_DEBUG_FORMAT_MONO:
761                 info->filename = replace_suffix (image->name, "dbg");
762                 if (g_file_test (info->filename, G_FILE_TEST_EXISTS))
763                         info->symfile = mono_debug_open_mono_symbol_file (info->image, info->filename, TRUE);
764                 else if (running_in_the_mono_debugger)
765                         info->symfile = mono_debug_create_mono_symbol_file (info->image);
766                 mono_debugger_symbol_file_table_generation++;
767                 break;
768
769         default:
770                 break;
771         }
772
773         if (debug->format != MONO_DEBUG_FORMAT_MONO)
774                 debug_load_method_lines (info);
775
776         return info;
777 }
778
779 void
780 mono_debug_write_symbols (MonoDebugHandle *debug)
781 {
782         if (!debug || !debug->dirty)
783                 return;
784
785         release_symbol_file_table ();
786         
787         switch (debug->format) {
788         case MONO_DEBUG_FORMAT_STABS:
789                 mono_debug_write_stabs (debug);
790                 break;
791         case MONO_DEBUG_FORMAT_DWARF2:
792                 mono_debug_write_dwarf2 (debug);
793                 break;
794         case MONO_DEBUG_FORMAT_MONO:
795                 break;
796         default:
797                 g_assert_not_reached ();
798         }
799
800         debug->dirty = FALSE;
801 }
802
803 void
804 mono_debug_make_symbols (void)
805 {
806         if (!mono_debug_handle || !mono_debug_handle->dirty)
807                 return;
808         
809         switch (mono_debug_handle->format) {
810         case MONO_DEBUG_FORMAT_STABS:
811                 mono_debug_write_stabs (mono_debug_handle);
812                 break;
813         case MONO_DEBUG_FORMAT_DWARF2:
814                 mono_debug_write_dwarf2 (mono_debug_handle);
815                 break;
816         case MONO_DEBUG_FORMAT_MONO:
817                 mono_debug_update_symbol_file_table ();
818                 break;
819         default:
820                 g_assert_not_reached ();
821         }
822
823         mono_debug_handle->dirty = FALSE;
824 }
825
826 static void
827 mono_debug_close_assembly (AssemblyDebugInfo* info)
828 {
829         switch (info->format) {
830         case MONO_DEBUG_FORMAT_MONO:
831                 if (info->symfile != NULL)
832                         mono_debug_close_mono_symbol_file (info->symfile);
833                 break;
834         default:
835                 break;
836         }
837         g_hash_table_destroy (info->methods);
838         g_free (info->mlines);
839         g_free (info->moffsets);
840         g_free (info->name);
841         g_free (info->ilfile);
842         g_free (info->filename);
843         g_free (info->objfile);
844         g_free (info);
845 }
846
847 void
848 mono_debug_cleanup (void)
849 {
850         release_symbol_file_table ();
851
852         if (!mono_debug_handle)
853                 return;
854
855         if (mono_debug_handle->flags & MONO_DEBUG_FLAGS_UPDATE_ON_EXIT)
856                 mono_debug_write_symbols (mono_debug_handle);
857
858         g_hash_table_destroy (mono_debug_handle->images);
859         g_ptr_array_free (mono_debug_handle->source_files, TRUE);
860         g_hash_table_destroy (mono_debug_handle->type_hash);
861         g_free (mono_debug_handle->producer_name);
862         g_free (mono_debug_handle->name);
863         g_free (mono_debug_handle);
864
865         mono_debug_handle = NULL;
866 }
867
868 guint32
869 mono_debug_get_type (MonoDebugHandle *debug, MonoClass *klass)
870 {
871         guint index, i;
872
873         mono_class_init (klass);
874
875         index = GPOINTER_TO_INT (g_hash_table_lookup (debug->type_hash, klass));
876         if (index)
877                 return index;
878
879         debug->dirty = TRUE;
880
881         index = ++debug->next_klass_idx;
882         g_hash_table_insert (debug->type_hash, klass, GINT_TO_POINTER (index));
883
884         if (klass->enumtype)
885                 return index;
886
887         switch (klass->byval_arg.type) {
888         case MONO_TYPE_CLASS:
889                 if (klass->parent)
890                         mono_debug_get_type (debug, klass->parent);
891
892                 for (i = 0; i < klass->method.count; i++) {
893                         MonoMethod *method = klass->methods [i];
894                         MonoType *ret_type = NULL;
895                         int j;
896
897                         if (method->signature->ret->type != MONO_TYPE_VOID)
898                                 ret_type = method->signature->ret;
899
900                         if (ret_type) {
901                                 MonoClass *ret_klass = mono_class_from_mono_type (ret_type);
902                                 mono_debug_get_type (debug, ret_klass);
903                         }
904
905                         for (j = 0; j < method->signature->param_count; j++) {
906                                 MonoType *sub_type = method->signature->params [j];
907                                 MonoClass *sub_klass = mono_class_from_mono_type (sub_type);
908                                 mono_debug_get_type (debug, sub_klass);
909                         }
910                 }
911                 // fall through
912         case MONO_TYPE_VALUETYPE:
913                 for (i = 0; i < klass->field.count; i++) {
914                         MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type);
915                         mono_debug_get_type (debug, subclass);
916                 }
917                 break;
918         case MONO_TYPE_ARRAY:
919         case MONO_TYPE_SZARRAY:
920                 mono_debug_get_type (debug, klass->element_class);
921                 break;
922         default:
923                 break;
924         }
925
926         return index;
927 }
928
929 static gint32
930 il_offset_from_address (MonoDebugMethodInfo *minfo, guint32 address)
931 {
932         int i;
933
934         if (!minfo->jit || !minfo->jit->line_numbers)
935                 return -1;
936
937         for (i = minfo->jit->line_numbers->len - 1; i >= 0; i--) {
938                 MonoDebugLineNumberEntry lne = g_array_index (
939                         minfo->jit->line_numbers, MonoDebugLineNumberEntry, i);
940
941                 if (lne.address <= address)
942                         return lne.offset;
943         }
944
945         return -1;
946 }
947
948 static gint32
949 address_from_il_offset (MonoDebugMethodInfo *minfo, guint32 il_offset)
950 {
951         int i;
952
953         if (!minfo->jit || !minfo->jit->line_numbers)
954                 return -1;
955
956         for (i = minfo->jit->line_numbers->len - 1; i >= 0; i--) {
957                 MonoDebugLineNumberEntry lne = g_array_index (
958                         minfo->jit->line_numbers, MonoDebugLineNumberEntry, i);
959
960                 if (lne.offset <= il_offset)
961                         return lne.address;
962         }
963
964         return -1;
965 }
966
967 void
968 mono_debug_add_type (MonoClass *klass)
969 {
970         AssemblyDebugInfo* info;
971
972         if (!mono_debug_handle)
973                 return;
974
975         info = mono_debug_get_image (mono_debug_handle, klass->image);
976         g_assert (info);
977
978         if (mono_debug_handle->format != MONO_DEBUG_FORMAT_MONO)
979                 return;
980
981         if (info->symfile) {
982                 mono_debug_lock ();
983                 mono_debug_symfile_add_type (info->symfile, klass);
984                 mono_debugger_event (MONO_DEBUGGER_EVENT_TYPE_ADDED, info->symfile, klass);
985                 mono_debug_unlock ();
986         }
987 }
988
989 static gint32
990 il_offset_from_position (MonoFlowGraph *cfg, MonoPosition *pos)
991 {
992         MonoBBlock *bblock;
993         MBTree *tree;
994
995         if (pos->abs_pos == 0)
996                 return -1;
997
998         if (pos->pos.bid >= cfg->block_count)
999                 return -1;
1000
1001         bblock = &cfg->bblocks [pos->pos.bid];
1002         if (pos->pos.tid >= bblock->forest->len)
1003                 return -1;
1004
1005         tree = (MBTree *) g_ptr_array_index (bblock->forest, pos->pos.tid);
1006
1007         return tree->cli_addr;
1008 }
1009
1010 struct LookupMethodData
1011 {
1012         MonoDebugMethodInfo *minfo;
1013         MonoMethod *method;
1014 };
1015
1016 static void
1017 lookup_method_func (gpointer key, gpointer value, gpointer user_data)
1018 {
1019         AssemblyDebugInfo *info = (AssemblyDebugInfo *) value;
1020         struct LookupMethodData *data = (struct LookupMethodData *) user_data;
1021
1022         if (data->minfo)
1023                 return;
1024
1025         if (info->symfile)
1026                 data->minfo = mono_debug_find_method (info->symfile, data->method);
1027         else
1028                 data->minfo = g_hash_table_lookup (info->methods, data->method);
1029 }
1030
1031 static MonoDebugMethodInfo *
1032 lookup_method (MonoMethod *method)
1033 {
1034         struct LookupMethodData data = { NULL, method };
1035
1036         if (!mono_debug_handle)
1037                 return NULL;
1038
1039         g_hash_table_foreach (mono_debug_handle->images, lookup_method_func, &data);
1040         return data.minfo;
1041 }
1042
1043 void
1044 mono_debug_add_method (MonoFlowGraph *cfg)
1045 {
1046         MonoMethod *method = cfg->method;
1047         MonoClass *klass = method->klass;
1048         AssemblyDebugInfo* info;
1049         MonoDebugMethodJitInfo *jit;
1050         MonoDebugMethodInfo *minfo;
1051         int i;
1052
1053         if (!mono_debug_handle)
1054                 return;
1055
1056         mono_class_init (klass);
1057
1058         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
1059             (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
1060             (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
1061             (method->flags & METHOD_ATTRIBUTE_ABSTRACT))
1062                 return;
1063
1064         if (method->wrapper_type != MONO_WRAPPER_NONE)
1065                 return;
1066
1067         info = mono_debug_get_image (mono_debug_handle, klass->image);
1068         g_assert (info);
1069
1070         minfo = lookup_method (method);
1071         if (!minfo || minfo->jit)
1072                 return;
1073
1074         mono_debug_lock ();
1075
1076         mono_debug_handle->dirty = TRUE;
1077
1078         minfo->jit = jit = g_new0 (MonoDebugMethodJitInfo, 1);
1079         jit->code_start = cfg->start;
1080         jit->code_size = cfg->epilogue_end;
1081         jit->prologue_end = cfg->prologue_end;
1082         jit->epilogue_begin = cfg->epilog;
1083         jit->num_params = method->signature->param_count;
1084         jit->params = g_new0 (MonoDebugVarInfo, jit->num_params);
1085
1086         if (method->signature->hasthis) {
1087                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->args_start_index;
1088
1089                 jit->this_var = g_new0 (MonoDebugVarInfo, 1);
1090                 jit->this_var->offset = ptr->offset;
1091                 jit->this_var->size = ptr->size;
1092         }
1093
1094         for (i = 0; i < jit->num_params; i++) {
1095                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->args_start_index +
1096                         method->signature->hasthis;
1097
1098                 jit->params [i].offset = ptr [i].offset;
1099                 jit->params [i].size = ptr [i].size;
1100         }
1101
1102         debug_generate_method_lines (info, minfo, cfg);
1103         if (info->format == MONO_DEBUG_FORMAT_MONO)
1104                 debug_update_il_offsets (info, minfo, cfg);
1105
1106         if (!method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
1107                 MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
1108                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->locals_start_index;
1109                 MonoDebugVarInfo *locals;
1110
1111                 locals = g_new0 (MonoDebugVarInfo, header->num_locals);
1112                 for (i = 0; i < header->num_locals; i++) {
1113                         gint32 begin_offset, end_offset;
1114                         gint32 begin_scope, end_scope;
1115
1116                         if (ptr [i].reg >= 0) {
1117                                 locals [i].index = ptr [i].reg | MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER;
1118                                 locals [i].offset = 0;
1119                         } else
1120                                 locals [i].offset = ptr [i].offset;
1121
1122                         locals [i].size = ptr [i].size;
1123
1124                         begin_offset = il_offset_from_position (cfg, &ptr [i].range.first_use);
1125                         end_offset = il_offset_from_position (cfg, &ptr [i].range.last_use);
1126                         if (end_offset >= 0)
1127                                 end_offset++;
1128
1129                         if (begin_offset >= 0)
1130                                 begin_scope = address_from_il_offset (minfo, begin_offset);
1131                         else
1132                                 begin_scope = -1;
1133                         if (end_offset >= 0)
1134                                 end_scope = address_from_il_offset (minfo, end_offset);
1135                         else
1136                                 end_scope = -1;
1137
1138                         if (begin_scope > 0)
1139                                 locals [i].begin_scope = begin_scope;
1140                         else
1141                                 locals [i].begin_scope = jit->prologue_end;
1142                         if (end_scope > 0)
1143                                 locals [i].end_scope = end_scope;
1144                         else
1145                                 locals [i].end_scope = jit->epilogue_begin;
1146                 }
1147
1148                 jit->num_locals = header->num_locals;
1149                 jit->locals = locals;
1150         }
1151
1152         if (info->symfile) {
1153                 mono_debug_symfile_add_method (info->symfile, method);
1154                 mono_debugger_event (MONO_DEBUGGER_EVENT_METHOD_ADDED, info->symfile, method);
1155         }
1156
1157         mono_debug_unlock ();
1158 }
1159
1160 gchar *
1161 mono_debug_source_location_from_address (MonoMethod *method, guint32 address, guint32 *line_number)
1162 {
1163         MonoDebugMethodInfo *minfo = lookup_method (method);
1164
1165         if (!minfo)
1166                 return NULL;
1167
1168         if (minfo->symfile) {
1169                 gint32 offset = il_offset_from_address (minfo, address);
1170                 
1171                 if (offset < 0)
1172                         return NULL;
1173
1174                 return mono_debug_find_source_location (minfo->symfile, method, offset, line_number);
1175         }
1176
1177         return NULL;
1178 }
1179
1180 gint32
1181 mono_debug_il_offset_from_address (MonoMethod *method, gint32 address)
1182 {
1183         MonoDebugMethodInfo *minfo;
1184
1185         if (address < 0)
1186                 return -1;
1187
1188         minfo = lookup_method (method);
1189         if (!minfo || !minfo->il_offsets)
1190                 return -1;
1191
1192         return il_offset_from_address (minfo, address);
1193 }
1194
1195 gint32
1196 mono_debug_address_from_il_offset (MonoMethod *method, gint32 il_offset)
1197 {
1198         MonoDebugMethodInfo *minfo;
1199
1200         if (il_offset < 0)
1201                 return -1;
1202
1203         minfo = lookup_method (method);
1204         if (!minfo || !minfo->il_offsets)
1205                 return -1;
1206
1207         return address_from_il_offset (minfo, il_offset);
1208 }
1209
1210 static void
1211 release_symbol_file_table ()
1212 {
1213         MonoDebuggerSymbolFileTable *temp;
1214
1215         if (!mono_debugger_symbol_file_table)
1216                 return;
1217
1218         /*
1219          * Caution: The debugger may access the memory pointed to by this variable
1220          *          at any time.  It is very important to set the pointer to NULL
1221          *          before freeing the area.
1222          */
1223
1224         temp = mono_debugger_symbol_file_table;
1225         mono_debugger_symbol_file_table = NULL;
1226         g_free (mono_debugger_symbol_file_table);
1227 }
1228
1229 static void
1230 update_symbol_file_table_count_func (gpointer key, gpointer value, gpointer user_data)
1231 {
1232         AssemblyDebugInfo *info = (AssemblyDebugInfo *) value;
1233
1234         if (!info->symfile)
1235                 return;
1236         if (info->format != MONO_DEBUG_FORMAT_MONO)
1237                 return;
1238
1239         ++ (* (int *) user_data);
1240 }
1241
1242 struct SymfileTableData
1243 {
1244         MonoDebuggerSymbolFileTable *symfile_table;
1245         int index;
1246 };
1247
1248 static void
1249 update_symbol_file_table_func (gpointer key, gpointer value, gpointer user_data)
1250 {
1251         AssemblyDebugInfo *info = (AssemblyDebugInfo *) value;
1252         struct SymfileTableData *data = (struct SymfileTableData *) user_data;
1253
1254         if (!info->symfile)
1255                 return;
1256         if (info->format != MONO_DEBUG_FORMAT_MONO)
1257                 return;
1258
1259         data->symfile_table->symfiles [data->index++] = info->symfile;
1260 }
1261
1262 int
1263 mono_debug_update_symbol_file_table (void)
1264 {
1265         int count = 0;
1266         MonoDebuggerSymbolFileTable *symfile_table;
1267         struct SymfileTableData data;
1268         guint32 size;
1269
1270         if (!mono_debug_handle)
1271                 return FALSE;
1272
1273         mono_debug_lock ();
1274
1275         g_hash_table_foreach (mono_debug_handle->images, update_symbol_file_table_count_func, &count);
1276
1277         release_symbol_file_table ();
1278
1279         size = sizeof (MonoDebuggerSymbolFileTable) + count * sizeof (MonoSymbolFile *);
1280         symfile_table = g_malloc0 (size);
1281         symfile_table->magic = MONO_SYMBOL_FILE_DYNAMIC_MAGIC;
1282         symfile_table->version = MONO_SYMBOL_FILE_DYNAMIC_VERSION;
1283         symfile_table->total_size = size;
1284         symfile_table->count = count;
1285         symfile_table->generation = mono_debugger_symbol_file_table_generation;
1286         symfile_table->global_symfile = mono_debugger_global_symbol_file;
1287
1288         data.symfile_table = symfile_table;
1289         data.index = 0;
1290
1291         g_hash_table_foreach (mono_debug_handle->images, update_symbol_file_table_func, &data);
1292
1293         mono_debugger_symbol_file_table = symfile_table;
1294
1295         mono_debug_unlock ();
1296
1297         return TRUE;
1298 }
1299
1300 static GPtrArray *breakpoints = NULL;
1301
1302 int
1303 mono_insert_breakpoint_full (MonoMethodDesc *desc, gboolean use_trampoline)
1304 {
1305         static int last_breakpoint_id = 0;
1306         MonoDebuggerBreakpointInfo *info;
1307
1308         info = g_new0 (MonoDebuggerBreakpointInfo, 1);
1309         info->desc = desc;
1310         info->use_trampoline = use_trampoline;
1311         info->index = ++last_breakpoint_id;
1312
1313         if (!breakpoints)
1314                 breakpoints = g_ptr_array_new ();
1315
1316         g_ptr_array_add (breakpoints, info);
1317
1318         return info->index;
1319 }
1320
1321 int
1322 mono_remove_breakpoint (int breakpoint_id)
1323 {
1324         int i;
1325
1326         if (!breakpoints)
1327                 return 0;
1328
1329         for (i = 0; i < breakpoints->len; i++) {
1330                 MonoDebuggerBreakpointInfo *info = g_ptr_array_index (breakpoints, i);
1331
1332                 if (info->index != breakpoint_id)
1333                         continue;
1334
1335                 mono_method_desc_free (info->desc);
1336                 g_ptr_array_remove (breakpoints, info);
1337                 g_free (info);
1338                 return 1;
1339         }
1340
1341         return 0;
1342 }
1343
1344 int
1345 mono_insert_breakpoint (const gchar *method_name, gboolean include_namespace)
1346 {
1347         MonoMethodDesc *desc;
1348
1349         desc = mono_method_desc_new (method_name, include_namespace);
1350         if (!desc)
1351                 return 0;
1352
1353         return mono_insert_breakpoint_full (desc, running_in_the_mono_debugger);
1354 }
1355
1356 int
1357 mono_method_has_breakpoint (MonoMethod* method, gboolean use_trampoline)
1358 {
1359         int i;
1360
1361         if (!breakpoints || (method->wrapper_type != MONO_WRAPPER_NONE))
1362                 return 0;
1363
1364         for (i = 0; i < breakpoints->len; i++) {
1365                 MonoDebuggerBreakpointInfo *info = g_ptr_array_index (breakpoints, i);
1366
1367                 if (info->use_trampoline != use_trampoline)
1368                         continue;
1369
1370                 if (!mono_method_desc_full_match (info->desc, method))
1371                         continue;
1372
1373                 return info->index;
1374         }
1375
1376         return 0;
1377 }
1378
1379 void
1380 mono_debugger_trampoline_breakpoint_callback (void)
1381 {
1382         mono_debugger_event (MONO_DEBUGGER_EVENT_BREAKPOINT_TRAMPOLINE, NULL, NULL);
1383 }