2004-07-27 Martin Baulig <martin@ximian.com>
[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 <sys/param.h>
6 #include <mono/metadata/metadata.h>
7 #include <mono/metadata/tabledefs.h>
8 #include <mono/metadata/rawbuffer.h>
9 #include <mono/metadata/tokentype.h>
10 #include <mono/metadata/appdomain.h>
11 #include <mono/metadata/exception.h>
12 #include <mono/metadata/debug-helpers.h>
13 #include <mono/metadata/mono-debug.h>
14 #include <mono/metadata/debug-mono-symfile.h>
15 #include <mono/metadata/mono-endian.h>
16
17 #include <fcntl.h>
18 #include <unistd.h>
19
20 #define RANGE_TABLE_CHUNK_SIZE          256
21 #define CLASS_TABLE_CHUNK_SIZE          256
22 #define TYPE_TABLE_PTR_CHUNK_SIZE       256
23 #define TYPE_TABLE_CHUNK_SIZE           65536
24
25 static void
26 free_method_info (MonoDebugMethodInfo *minfo)
27 {
28         g_free (minfo);
29 }
30
31 static gchar *
32 get_class_name (MonoClass *klass)
33 {
34         MonoClass *nested_in = mono_class_get_nesting_type (klass);
35         const char *name_space;
36         if (nested_in) {
37                 gchar *parent_name = get_class_name (nested_in);
38                 gchar *name = g_strdup_printf ("%s.%s", parent_name, mono_class_get_name (klass));
39                 g_free (parent_name);
40                 return name;
41         }
42
43         name_space = mono_class_get_namespace (klass);
44         return g_strdup_printf ("%s%s%s", name_space,
45                                 name_space [0] ? "." : "", mono_class_get_name (klass));
46 }
47
48 static int
49 load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile)
50 {
51         const char *ptr, *start;
52         guint64 magic;
53         long version;
54
55         ptr = start = symfile->raw_contents;
56         if (!ptr)
57                 return FALSE;
58
59         magic = read64(ptr);
60         ptr += sizeof(guint64);
61         if (magic != MONO_SYMBOL_FILE_MAGIC) {
62                 g_warning ("Symbol file %s is not a mono symbol file", handle->image_file);
63                 return FALSE;
64         }
65
66         version = read32(ptr);
67         ptr += sizeof(guint32);
68         if (version != MONO_SYMBOL_FILE_VERSION) {
69                 g_warning ("Symbol file %s has incorrect version "
70                            "(expected %d, got %ld)", handle->image_file,
71                            MONO_SYMBOL_FILE_VERSION, version);
72                 return FALSE;
73         }
74
75         symfile->offset_table = (MonoSymbolFileOffsetTable *) ptr;
76
77         symfile->method_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
78                                                              (GDestroyNotify) free_method_info);
79
80         return TRUE;
81 }
82
83 static gconstpointer
84 open_symfile (MonoImage *image, guint32 *size)
85 {
86         MonoTableInfo *table = mono_image_get_table_info (image, MONO_TABLE_MANIFESTRESOURCE);
87         guint32 i, num_rows;
88         guint32 cols [MONO_MANIFEST_SIZE];
89         const char *val;
90
91         num_rows = mono_table_info_get_rows (table);
92         for (i = 0; i < num_rows; ++i) {
93                 mono_metadata_decode_row (table, i, cols, MONO_MANIFEST_SIZE);
94                 val = mono_metadata_string_heap (image, cols [MONO_MANIFEST_NAME]);
95                 if (!strcmp (val, "MonoSymbolFile"))
96                         break;
97         }
98         if (i == num_rows)
99                 return NULL;
100         g_assert (!cols [MONO_MANIFEST_IMPLEMENTATION]);
101
102         return mono_image_get_resource (image, cols [MONO_MANIFEST_OFFSET], size);
103 }
104
105 static gconstpointer
106 open_external_symfile (MonoImage *image, guint32 *size)
107 {
108         const gchar *filename;
109         gchar *basename, *name;
110         gchar *contents;
111
112         filename = mono_image_get_filename (image);
113
114         if (strstr (filename, ".dll") || strstr (filename, ".exe")) {
115                 int len = strlen (filename) - 4;
116                 basename = g_malloc0 (len);
117                 strncpy (basename, filename, len);
118         } else {
119                 basename = g_strdup (filename);
120         }
121
122         name = g_strdup_printf ("%s.mdb", basename);
123
124         if (!g_file_get_contents (name, &contents, size, NULL))
125                 contents = NULL;
126
127         g_free (basename);
128         g_free (name);
129
130         return contents;
131 }
132
133 MonoSymbolFile *
134 mono_debug_open_mono_symbol_file (MonoDebugHandle *handle, gboolean create_symfile)
135 {
136         MonoSymbolFile *symfile;
137
138         symfile = g_new0 (MonoSymbolFile, 1);
139
140         symfile->raw_contents = open_symfile (handle->image, &symfile->raw_contents_size);
141         if (!symfile->raw_contents)
142                 symfile->raw_contents = open_external_symfile (
143                         handle->image, &symfile->raw_contents_size);
144
145         if (load_symfile (handle, symfile))
146                 return symfile;
147         else if (!create_symfile) {
148                 mono_debug_close_mono_symbol_file (symfile);
149                 return NULL;
150         }
151
152         return symfile;
153 }
154
155 void
156 mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile)
157 {
158         if (!symfile)
159                 return;
160
161         if (symfile->method_hash)
162                 g_hash_table_destroy (symfile->method_hash);
163
164         g_free (symfile);
165 }
166
167 static gchar *
168 read_string (const char *ptr)
169 {
170         int len = *((guint32 *) ptr);
171         ptr += sizeof(guint32);
172         return g_filename_from_utf8 (ptr, len, NULL, NULL, NULL);
173 }
174
175 gchar *
176 mono_debug_find_source_location (MonoSymbolFile *symfile, MonoMethod *method, guint32 offset,
177                                  guint32 *line_number)
178 {
179         MonoSymbolFileLineNumberEntry *lne;
180         MonoDebugMethodInfo *minfo;
181         gchar *source_file = NULL;
182         const char *ptr;
183         int i;
184
185         if (!symfile->method_hash)
186                 return NULL;
187
188         minfo = g_hash_table_lookup (symfile->method_hash, method);
189         if (!minfo)
190                 return NULL;
191
192         if (read32(&(minfo->entry->_source_index))) {
193                 int offset = read32(&(symfile->offset_table->_source_table_offset)) +
194                         (read32(&(minfo->entry->_source_index)) - 1) * sizeof (MonoSymbolFileSourceEntry);
195                 MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *) (symfile->raw_contents + offset);
196
197                 source_file = read_string (symfile->raw_contents + read32(&(se->_name_offset)));
198         }
199
200         ptr = symfile->raw_contents + read32(&(minfo->entry->_line_number_table_offset));
201
202         lne = (MonoSymbolFileLineNumberEntry *) ptr;
203
204         for (i = 0; i < read32(&(minfo->entry->_num_line_numbers)); i++, lne++) {
205                 if (read32(&(lne->_offset)) < offset)
206                         continue;
207
208                 if (line_number) {
209                         *line_number = read32(&(lne->_row));
210                         if (source_file)
211                                 return source_file;
212                         else
213                                 return NULL;
214                 } else if (source_file) {
215                         gchar *retval = g_strdup_printf ("%s:%d", source_file, read32(&(lne->_row)));
216                         g_free (source_file);
217                         return retval;
218                 } else
219                         return g_strdup_printf ("%d", read32(&(lne->_row)));
220         }
221
222         return NULL;
223 }
224
225 gint32
226 _mono_debug_address_from_il_offset (MonoDebugMethodJitInfo *jit, guint32 il_offset)
227 {
228         int i;
229
230         if (!jit || !jit->line_numbers)
231                 return -1;
232
233         for (i = jit->line_numbers->len - 1; i >= 0; i--) {
234                 MonoDebugLineNumberEntry lne = g_array_index (
235                         jit->line_numbers, MonoDebugLineNumberEntry, i);
236
237                 if (lne.offset <= il_offset)
238                         return lne.address;
239         }
240
241         return -1;
242 }
243
244 static int
245 compare_method (const void *key, const void *object)
246 {
247         guint32 token = GPOINTER_TO_UINT (key);
248         MonoSymbolFileMethodIndexEntry *me = (MonoSymbolFileMethodIndexEntry*)object;
249
250         return token - read32(&(me->_token));
251 }
252
253 MonoDebugMethodInfo *
254 mono_debug_find_method (MonoDebugHandle *handle, MonoMethod *method)
255 {
256         MonoSymbolFileMethodEntry *me;
257         MonoSymbolFileMethodIndexEntry *first_ie, *ie;
258         MonoDebugMethodInfo *minfo;
259         MonoSymbolFile *symfile = handle->symfile;
260
261         if (!symfile->method_hash)
262                 return NULL;
263
264         if (handle->image != mono_class_get_image (mono_method_get_class (method)))
265                 return NULL;
266
267         first_ie = (MonoSymbolFileMethodIndexEntry *)
268                 (symfile->raw_contents + read32(&(symfile->offset_table->_method_table_offset)));
269
270         ie = bsearch (GUINT_TO_POINTER (mono_method_get_token (method)), first_ie,
271                                    read32(&(symfile->offset_table->_method_count)),
272                                    sizeof (MonoSymbolFileMethodIndexEntry), compare_method);
273
274         if (!ie)
275                 return NULL;
276
277         me = (MonoSymbolFileMethodEntry *) (symfile->raw_contents + read32(&(ie->_file_offset)));
278
279         minfo = g_new0 (MonoDebugMethodInfo, 1);
280         minfo->index = (ie - first_ie) + 1;
281         minfo->method = method;
282         minfo->handle = handle;
283         minfo->num_il_offsets = read32(&(me->_num_line_numbers));
284         minfo->il_offsets = (MonoSymbolFileLineNumberEntry *)
285                 (symfile->raw_contents + read32(&(me->_line_number_table_offset)));
286         minfo->entry = me;
287
288         g_hash_table_insert (symfile->method_hash, method, minfo);
289
290         return minfo;
291 }