2002-09-14 Martin Baulig <martin@gnome.org>
[mono.git] / mono / metadata / debug-mono-symfile.c
1 #include <config.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <signal.h>
5 #include <mono/metadata/metadata.h>
6 #include <mono/metadata/tabledefs.h>
7 #include <mono/metadata/rawbuffer.h>
8 #include <mono/metadata/tokentype.h>
9 #include <mono/metadata/appdomain.h>
10 #include <mono/metadata/exception.h>
11 #include <mono/metadata/debug-helpers.h>
12 #include <mono/metadata/debug-mono-symfile.h>
13
14 #include <fcntl.h>
15 #include <unistd.h>
16
17 struct MonoSymbolFilePriv
18 {
19         int fd;
20         int error;
21         char *file_name;
22         char *source_file;
23         MonoImage *image;
24         GHashTable *method_table;
25         GHashTable *method_hash;
26         MonoSymbolFileOffsetTable *offset_table;
27         GPtrArray *range_table;
28 };
29
30 typedef struct
31 {
32         MonoMethod *method;
33         MonoDebugMethodInfo *minfo;
34         MonoSymbolFileMethodEntry *entry;
35 } MonoSymbolFileMethodEntryPriv;
36
37 static int create_symfile (MonoSymbolFile *symfile, gboolean emit_warnings);
38 static void close_symfile (MonoSymbolFile *symfile);
39
40 static void
41 free_method_info (MonoDebugMethodInfo *minfo)
42 {
43         g_free (minfo->jit);
44         g_free (minfo);
45 }
46
47 static int
48 load_symfile (MonoSymbolFile *symfile)
49 {
50         MonoSymbolFilePriv *priv = symfile->_priv;
51         MonoSymbolFileMethodEntry *me;
52         const char *ptr, *start;
53         guint64 magic;
54         long version;
55         int i;
56
57         ptr = start = symfile->raw_contents;
58
59         magic = *((guint64 *) ptr)++;
60         if (magic != MONO_SYMBOL_FILE_MAGIC) {
61                 g_warning ("Symbol file %s has is not a mono symbol file", priv->file_name);
62                 return FALSE;
63         }
64
65         version = *((guint32 *) ptr)++;
66         if (version != MONO_SYMBOL_FILE_VERSION) {
67                 g_warning ("Symbol file %s has incorrect line number table version "
68                            "(expected %d, got %ld)", priv->file_name,
69                            MONO_SYMBOL_FILE_VERSION, version);
70                 return FALSE;
71         }
72
73         priv->offset_table = (MonoSymbolFileOffsetTable *) ptr;
74         symfile->address_table_size = priv->offset_table->address_table_size;
75
76         /*
77          * Read method table.
78          *
79          */
80
81         priv->method_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
82                                                     (GDestroyNotify) g_free);
83         priv->method_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
84                                                    (GDestroyNotify) free_method_info);
85
86         ptr = symfile->raw_contents + priv->offset_table->method_table_offset;
87         me = (MonoSymbolFileMethodEntry *) ptr;
88
89         for (i = 0; i < priv->offset_table->method_count; i++, me++) {
90                 MonoMethod *method = mono_get_method (priv->image, me->token, NULL);
91                 MonoSymbolFileMethodEntryPriv *mep;
92                 MonoDebugMethodInfo *minfo;
93
94                 if (!method)
95                         continue;
96
97                 minfo = g_new0 (MonoDebugMethodInfo, 1);
98                 minfo->file_offset = ((const char *) me) - start;
99                 minfo->method = method;
100                 minfo->symfile = symfile;
101                 minfo->num_il_offsets = me->num_line_numbers;
102                 minfo->il_offsets = (MonoSymbolFileLineNumberEntry *)
103                         (symfile->raw_contents + me->line_number_table_offset);
104
105                 mep = g_new0 (MonoSymbolFileMethodEntryPriv, 1);
106                 mep->method = method;
107                 mep->minfo = minfo;
108                 mep->entry = me;
109
110                 g_hash_table_insert (priv->method_table, method, mep);
111                 g_hash_table_insert (priv->method_hash, method, minfo);
112         }
113
114         return TRUE;
115 }
116
117 MonoSymbolFile *
118 mono_debug_open_mono_symbol_file (MonoImage *image, const char *filename, gboolean emit_warnings)
119 {
120         MonoSymbolFile *symfile;
121         MonoSymbolFilePriv *priv;
122         off_t file_size;
123         void *ptr;
124         int fd;
125
126         g_message (G_STRLOC ": %s - %s", image->name, filename);
127
128         fd = open (filename, O_RDONLY);
129         if (fd == -1) {
130                 if (emit_warnings)
131                         g_warning ("Can't open symbol file: %s", filename);
132                 return NULL;
133         }
134
135         file_size = lseek (fd, 0, SEEK_END);
136         lseek (fd, 0, SEEK_SET);
137
138         if (file_size == (off_t) -1) {
139                 if (emit_warnings)
140                         g_warning ("Can't get size of symbol file: %s", filename);
141                 return NULL;
142         }
143
144         ptr = mono_raw_buffer_load (fd, FALSE, 0, file_size);
145         if (!ptr) {
146                 if (emit_warnings)
147                         g_warning ("Can't read symbol file: %s", filename);
148                 return NULL;
149         }
150
151         symfile = g_new0 (MonoSymbolFile, 1);
152         symfile->magic = MONO_SYMBOL_FILE_MAGIC;
153         symfile->version = MONO_SYMBOL_FILE_VERSION;
154         symfile->image_file = g_strdup (image->name);
155         symfile->raw_contents = ptr;
156         symfile->raw_contents_size = file_size;
157
158         symfile->_priv = priv = g_new0 (MonoSymbolFilePriv, 1);
159
160         priv->fd = fd;
161         priv->image = image;
162         priv->file_name = g_strdup (filename);
163
164         if (!load_symfile (symfile)) {
165                 mono_debug_close_mono_symbol_file (symfile);
166                 return NULL;
167         }
168
169         return symfile;
170 }
171
172 static void
173 close_symfile (MonoSymbolFile *symfile)
174 {
175         MonoSymbolFilePriv *priv = symfile->_priv;
176
177         if (symfile->raw_contents) {
178                 mono_raw_buffer_free (symfile->raw_contents);
179                 symfile->raw_contents = NULL;
180         }
181
182         if (priv->fd) {
183                 close (priv->fd);
184                 priv->fd = 0;
185         }
186
187         if (priv->method_table) {
188                 g_hash_table_destroy (priv->method_table);
189                 priv->method_table = NULL;
190         }
191
192         if (priv->method_hash) {
193                 g_hash_table_destroy (priv->method_hash);
194                 priv->method_hash = NULL;
195         }
196
197         if (symfile->is_dynamic)
198                 unlink (priv->file_name);
199
200         if (symfile->image_file) {
201                 g_free (symfile->image_file);
202                 symfile->image_file = NULL;
203         }
204
205         if (priv->file_name) {
206                 g_free (priv->file_name);
207                 priv->file_name = NULL;
208         }
209
210         priv->error = FALSE;
211 }
212
213 void
214 mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile)
215 {
216         if (!symfile)
217                 return;
218
219         close_symfile (symfile);
220
221         g_free (symfile->_priv->source_file);
222         g_free (symfile->_priv);
223         g_free (symfile);
224 }
225
226 static int
227 read_7bit_encoded_int (const char **ptr)
228 {
229         int ret = 0;
230         int shift = 0;
231         char b;
232
233         do {
234                 b = *(*ptr)++;
235                                 
236                 ret = ret | ((b & 0x7f) << shift);
237                 shift += 7;
238         } while ((b & 0x80) == 0x80);
239
240         return ret;
241 }
242
243 static int
244 write_7bit_encoded_int (int fd, int value)
245 {
246         do {
247                 int high = (value >> 7) & 0x01ffffff;
248                 char b = (char)(value & 0x7f);
249
250                 if (high != 0)
251                         b = (char)(b | 0x80);
252
253                 if (write (fd, &b, 1) < 0)
254                         return FALSE;
255
256                 value = high;
257         } while (value != 0);
258         return TRUE;
259 }
260
261 static int
262 write_string (int fd, const char *string)
263 {
264         if (!write_7bit_encoded_int (fd, strlen (string)))
265                 return FALSE;
266
267         if (write (fd, string, strlen (string)) < 0)
268                 return FALSE;
269
270         return TRUE;
271 }
272
273 static gchar *
274 read_string (const char *ptr)
275 {
276         int len = read_7bit_encoded_int (&ptr);
277         gchar *retval;
278
279         retval = g_malloc0 (len+1);
280         memcpy (retval, ptr, len);
281         return retval;
282 }
283
284 gchar *
285 mono_debug_find_source_location (MonoSymbolFile *symfile, MonoMethod *method, guint32 offset,
286                                  guint32 *line_number)
287 {
288         MonoSymbolFilePriv *priv = symfile->_priv;
289         MonoSymbolFileLineNumberEntry *lne;
290         MonoSymbolFileMethodEntryPriv *mep;
291         gchar *source_file = NULL;
292         const char *ptr;
293         int i;
294
295         if (!priv->method_table || symfile->is_dynamic)
296                 return NULL;
297
298         mep = g_hash_table_lookup (priv->method_table, method);
299         if (!mep)
300                 return NULL;
301
302         if (mep->entry->source_file_offset)
303                 source_file = read_string (symfile->raw_contents + mep->entry->source_file_offset);
304
305         ptr = symfile->raw_contents + mep->entry->line_number_table_offset;
306
307         lne = (MonoSymbolFileLineNumberEntry *) ptr;
308
309         for (i = 0; i < mep->entry->num_line_numbers; i++, lne++) {
310                 if (lne->offset < offset)
311                         continue;
312
313                 if (line_number) {
314                         *line_number = lne->row;
315                         if (source_file)
316                                 return source_file;
317                         else
318                                 return NULL;
319                 } else if (source_file) {
320                         gchar *retval = g_strdup_printf ("%s:%d", source_file, lne->row);
321                         g_free (source_file);
322                         return retval;
323                 } else
324                         return g_strdup_printf ("%d", lne->row);
325         }
326
327         return NULL;
328 }
329
330 static void
331 update_method_func (gpointer key, gpointer value, gpointer user_data)
332 {
333         MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
334         MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
335         MonoSymbolFileMethodAddress *address;
336         MonoSymbolFileLineNumberEntry *lne;
337         MonoDebugRangeInfo *range;
338         int i;
339
340         if (!mep->minfo) {
341                 mep->minfo = g_hash_table_lookup (symfile->_priv->method_hash, mep->method);
342                 if (!mep->minfo)
343                         return;
344         }
345
346         if (!mep->minfo->jit)
347                 return;
348
349         address = (MonoSymbolFileMethodAddress *)
350                 (symfile->address_table + mep->entry->address_table_offset);
351
352         address->is_valid = TRUE;
353         address->start_address = GPOINTER_TO_UINT (mep->minfo->jit->code_start);
354         address->end_address = GPOINTER_TO_UINT (mep->minfo->jit->code_start + mep->minfo->jit->code_size);
355
356         range = g_new0 (MonoDebugRangeInfo, 1);
357         range->start_address = address->start_address;
358         range->end_address = address->end_address;
359         range->file_offset = mep->minfo->file_offset;
360
361         g_ptr_array_add (symfile->_priv->range_table, range);
362
363         lne = (MonoSymbolFileLineNumberEntry *)
364                 (symfile->raw_contents + mep->entry->line_number_table_offset);
365
366         for (i = 0; i < mep->entry->num_line_numbers; i++, lne++) {
367                 int j;
368
369                 if (i == 0) {
370                         address->line_addresses [i] = 0;
371                         continue;
372                 } else if (lne->offset == 0) {
373                         address->line_addresses [i] = mep->minfo->jit->prologue_end;
374                         continue;
375                 }
376
377                 address->line_addresses [i] = mep->minfo->jit->code_size;
378
379                 for (j = 0; j < mep->minfo->num_il_offsets; j++) {
380                         MonoSymbolFileLineNumberEntry *il = &mep->minfo->il_offsets [j];
381
382                         if (il->offset >= lne->offset) {
383                                 address->line_addresses [i] = mep->minfo->jit->il_addresses [j];
384                                 break;
385                         }
386                 }
387         }
388 }
389
390 static gint
391 range_table_compare_func (gconstpointer a, gconstpointer b)
392 {
393         const MonoDebugRangeInfo *r1 = (const MonoDebugRangeInfo *) a;
394         const MonoDebugRangeInfo *r2 = (const MonoDebugRangeInfo *) b;
395
396         if (r1->start_address < r2->start_address)
397                 return -1;
398         else if (r1->start_address > r2->start_address)
399                 return 1;
400         else
401                 return 0;
402 }       
403
404 void
405 mono_debug_update_mono_symbol_file (MonoSymbolFile *symfile)
406 {
407         int i;
408
409         if (!symfile->_priv->method_table)
410                 return;
411
412         symfile->_priv->range_table = g_ptr_array_new ();
413
414         if (!symfile->address_table)
415                 symfile->address_table = g_malloc0 (symfile->address_table_size);
416
417         g_hash_table_foreach (symfile->_priv->method_table, update_method_func, symfile);
418
419         symfile->range_table_size = symfile->_priv->range_table->len * sizeof (MonoDebugRangeInfo);
420         symfile->range_table = g_malloc0 (symfile->range_table_size);
421
422         g_ptr_array_sort (symfile->_priv->range_table, range_table_compare_func);
423
424         for (i = 0; i < symfile->_priv->range_table->len; i++) {
425                 MonoDebugRangeInfo *range = g_ptr_array_index (symfile->_priv->range_table, i);
426
427                 symfile->range_table [i] = *range;
428         }
429
430         g_ptr_array_free (symfile->_priv->range_table, TRUE);
431         symfile->_priv->range_table = NULL;
432 }
433
434 static void
435 free_method_entry (MonoSymbolFileMethodEntryPriv *mep)
436 {
437         g_free (mep->entry);
438         g_free (mep);
439 }
440
441 static void
442 create_method (MonoSymbolFile *symfile, guint32 token, MonoMethod *method)
443 {
444         MonoSymbolFileMethodEntryPriv *mep;
445         MonoDebugMethodInfo *minfo;
446
447         g_assert (method->klass->image == symfile->_priv->image);
448
449         mep = g_new0 (MonoSymbolFileMethodEntryPriv, 1);
450         mep->entry = g_new0 (MonoSymbolFileMethodEntry, 1);
451         mep->entry->token = token;
452         mep->entry->source_file_offset = symfile->_priv->offset_table->source_table_offset;
453
454         minfo = g_new0 (MonoDebugMethodInfo, 1);
455         minfo->method = method;
456         minfo->symfile = symfile;
457
458         mep->minfo = minfo;
459         mep->method = method;
460
461         symfile->_priv->offset_table->method_count++;
462
463         g_hash_table_insert (symfile->_priv->method_table, method, mep);
464         g_hash_table_insert (symfile->_priv->method_hash, method, minfo);
465 }
466
467 static void
468 write_method (gpointer key, gpointer value, gpointer user_data)
469 {
470         MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
471         MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
472
473         if (symfile->_priv->error)
474                 return;
475
476         mep->minfo->file_offset = lseek (symfile->_priv->fd, 0, SEEK_CUR);
477
478         if (write (symfile->_priv->fd, mep->entry, sizeof (MonoSymbolFileMethodEntry)) < 0) {
479                 symfile->_priv->error = TRUE;
480                 return;
481         }
482 }
483
484 static void
485 write_line_numbers (gpointer key, gpointer value, gpointer user_data)
486 {
487         MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
488         MonoSymbolFilePriv *priv = symfile->_priv;
489         MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
490         MonoSymbolFileLineNumberEntry lne;
491         const unsigned char *ip, *start, *end;
492         MonoMethodHeader *header;
493
494         if (priv->error)
495                 return;
496
497         if ((mep->method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
498             (mep->method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
499             (mep->method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
500                 g_assert_not_reached ();
501
502         header = ((MonoMethodNormal *) mep->method)->header;
503
504         mep->entry->line_number_table_offset = lseek (priv->fd, 0, SEEK_CUR);
505         ++mep->entry->num_line_numbers;
506
507         lne.offset = 0;
508         lne.row = -1;
509
510         if (write (priv->fd, &lne, sizeof (MonoSymbolFileLineNumberEntry)) < 0) {
511                 priv->error = TRUE;
512                 return;
513         }
514
515         ip = start = header->code;
516         end = ip + header->code_size;
517
518         while (ip < end) {
519                 gchar *line;
520
521                 ++mep->entry->num_line_numbers;
522                 lne.offset = ip - start;
523                 lne.row = -1;
524
525                 if (write (priv->fd, &lne, sizeof (MonoSymbolFileLineNumberEntry)) < 0) {
526                         priv->error = TRUE;
527                         return;
528                 }
529
530                 line = mono_disasm_code_one (NULL, mep->method, ip, &ip);
531                 g_free (line);
532         }
533
534         mep->entry->address_table_offset = symfile->address_table_size;
535         mep->entry->address_table_size = sizeof (MonoSymbolFileMethodAddress) +
536                 mep->entry->num_line_numbers * sizeof (guint32);
537
538         symfile->address_table_size += mep->entry->address_table_size;
539 }
540
541 static void
542 create_methods (MonoSymbolFile *symfile)
543 {
544         MonoImage *image = symfile->_priv->image;
545         MonoTableInfo *table = &image->tables [MONO_TABLE_METHOD];
546         int idx;
547
548         for (idx = 1; idx <= table->rows; idx++) {
549                 guint32 token = mono_metadata_make_token (MONO_TABLE_METHOD, idx);
550                 MonoMethod *method = mono_get_method (image, token, NULL);
551
552                 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
553                     (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
554                     (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
555                         continue;
556
557                 if (method->wrapper_type != MONO_WRAPPER_NONE)
558                         continue;
559
560                 create_method (symfile, token, method);
561         }
562 }
563
564 static void
565 load_line_numbers (gpointer key, gpointer value, gpointer user_data)
566 {
567         MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
568         MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
569
570         mep->minfo->num_il_offsets = mep->entry->num_line_numbers;
571         mep->minfo->il_offsets = (MonoSymbolFileLineNumberEntry *)
572                 (symfile->raw_contents + mep->entry->line_number_table_offset);
573 }
574
575 static int
576 create_symfile (MonoSymbolFile *symfile, gboolean emit_warnings)
577 {
578         MonoSymbolFilePriv *priv = symfile->_priv;
579         char *ptr;
580         guint64 magic;
581         long version;
582         off_t offset;
583
584         priv->fd = g_file_open_tmp (NULL, &priv->file_name, NULL);
585         if (priv->fd == -1) {
586                 if (emit_warnings)
587                         g_warning ("Can't create symbol file");
588                 return FALSE;
589         }
590
591         magic = MONO_SYMBOL_FILE_MAGIC;
592         if (write (priv->fd, &magic, sizeof (magic)) < 0)
593                 return FALSE;
594
595         version = MONO_SYMBOL_FILE_VERSION;
596         if (write (priv->fd, &version, sizeof (version)) < 0)
597                 return FALSE;
598
599         offset = lseek (priv->fd, 0, SEEK_CUR);
600
601         priv->offset_table = g_new0 (MonoSymbolFileOffsetTable, 1);
602         if (write (priv->fd, priv->offset_table, sizeof (MonoSymbolFileOffsetTable)) < 0)
603                 return FALSE;
604
605         //
606         // Write source file table.
607         //
608         if (priv->source_file) {
609                 priv->offset_table->source_table_offset = lseek (priv->fd, 0, SEEK_CUR);
610                 if (!write_string (priv->fd, priv->source_file))
611                         return FALSE;
612                 priv->offset_table->source_table_size = lseek (priv->fd, 0, SEEK_CUR) -
613                         priv->offset_table->source_table_offset;
614         }
615
616         //
617         // Create method table.
618         //
619
620         priv->method_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
621                                                     (GDestroyNotify) free_method_entry);
622         priv->method_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
623                                                    (GDestroyNotify) free_method_info);
624
625         create_methods (symfile);
626
627         //
628         // Write line numbers.
629         //
630
631         priv->offset_table->line_number_table_offset = lseek (priv->fd, 0, SEEK_CUR);
632
633         g_hash_table_foreach (priv->method_table, write_line_numbers, symfile);
634         if (priv->error)
635                 return FALSE;
636
637         priv->offset_table->line_number_table_size = lseek (priv->fd, 0, SEEK_CUR) -
638                 priv->offset_table->line_number_table_offset;
639
640         //
641         // Write method table.
642         //
643
644         priv->offset_table->method_table_offset = lseek (priv->fd, 0, SEEK_CUR);
645
646         g_hash_table_foreach (priv->method_table, write_method, symfile);
647         if (priv->error)
648                 return FALSE;
649
650         priv->offset_table->method_table_size = lseek (priv->fd, 0, SEEK_CUR) -
651                 priv->offset_table->method_table_offset;
652
653         //
654         // Write offset table.
655         //
656
657         symfile->raw_contents_size = lseek (priv->fd, 0, SEEK_CUR);
658         priv->offset_table->address_table_size = symfile->address_table_size;
659
660         lseek (priv->fd, offset, SEEK_SET);
661         if (write (priv->fd, priv->offset_table, sizeof (MonoSymbolFileOffsetTable)) < 0)
662                 return FALSE;
663
664         lseek (priv->fd, symfile->raw_contents_size, SEEK_SET);
665
666         ptr = mono_raw_buffer_load (priv->fd, TRUE, 0, symfile->raw_contents_size);
667         if (!ptr)
668                 return FALSE;
669
670         symfile->raw_contents = ptr;
671
672         //
673         // Load line number table.
674         //
675         g_hash_table_foreach (priv->method_table, load_line_numbers, symfile);
676         if (priv->error)
677                 return FALSE;
678
679         return TRUE;
680 }
681
682 MonoSymbolFile *
683 mono_debug_create_mono_symbol_file (MonoImage *image)
684 {
685         MonoSymbolFile *symfile;
686
687         symfile = g_new0 (MonoSymbolFile, 1);
688         symfile->magic = MONO_SYMBOL_FILE_MAGIC;
689         symfile->version = MONO_SYMBOL_FILE_VERSION;
690         symfile->is_dynamic = TRUE;
691         symfile->image_file = g_strdup (image->name);
692
693         symfile->_priv = g_new0 (MonoSymbolFilePriv, 1);
694         symfile->_priv->image = image;
695
696         g_message (G_STRLOC ": %s", image->name);
697
698         if (!create_symfile (symfile, TRUE)) {
699                 mono_debug_close_mono_symbol_file (symfile);
700                 return NULL;
701         }
702
703         return symfile;
704 }
705
706 MonoDebugMethodInfo *
707 mono_debug_find_method (MonoSymbolFile *symfile, MonoMethod *method)
708 {
709         return g_hash_table_lookup (symfile->_priv->method_hash, method);
710 }