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