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