[System] UriKind.RelativeOrAbsolute workaround.
[mono.git] / mono / metadata / debug-mono-ppdb.c
1 /*
2  * debug-mono-ppdb.c: Support for the portable PDB symbol
3  * file format
4  *
5  *
6  * Author:
7  *      Mono Project (http://www.mono-project.com)
8  *
9  * Copyright 2015 Xamarin Inc (http://www.xamarin.com)
10  */
11
12 #include <config.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <mono/metadata/metadata.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/tokentype.h>
20 #include <mono/metadata/debug-helpers.h>
21 #include <mono/metadata/mono-debug.h>
22 #include <mono/metadata/debug-mono-symfile.h>
23 #include <mono/metadata/mono-debug-debugger.h>
24 #include <mono/metadata/mono-endian.h>
25 #include <mono/metadata/metadata-internals.h>
26 #include <mono/metadata/class-internals.h>
27
28 #include "debug-mono-ppdb.h"
29
30 struct _MonoPPDBFile {
31         MonoImage *image;
32         GHashTable *doc_cache;
33 };
34
35 MonoPPDBFile*
36 mono_ppdb_load_file (MonoImage *image)
37 {
38         MonoImage *ppdb_image;
39         const char *filename;
40         char *s, *ppdb_filename;
41         MonoImageOpenStatus status;
42         MonoTableInfo *tables;
43         guint32 cols [MONO_MODULE_SIZE];
44         const char *guid, *ppdb_guid;
45         MonoPPDBFile *ppdb;
46
47         /* ppdb files drop the .exe/.dll extension */
48         filename = mono_image_get_filename (image);
49         if (strlen (filename) > 4 && (!strcmp (filename + strlen (filename) - 4, ".exe"))) {
50                 s = g_strdup (filename);
51                 s [strlen (filename) - 4] = '\0';
52                 ppdb_filename = g_strdup_printf ("%s.pdb", s);
53                 g_free (s);
54         } else {
55                 ppdb_filename = g_strdup_printf ("%s.pdb", filename);
56         }
57
58         ppdb_image = mono_image_open_metadata_only (ppdb_filename, &status);
59         if (!ppdb_image)
60                 return NULL;
61
62         /* Check that the images match */
63         tables = image->tables;
64         g_assert (tables [MONO_TABLE_MODULE].rows);
65         mono_metadata_decode_row (&tables [MONO_TABLE_MODULE], 0, cols, MONO_MODULE_SIZE);
66         guid = mono_metadata_guid_heap (image, cols [MONO_MODULE_MVID]);
67
68         tables = ppdb_image->tables;
69         g_assert (tables [MONO_TABLE_MODULE].rows);
70         mono_metadata_decode_row (&tables [MONO_TABLE_MODULE], 0, cols, MONO_MODULE_SIZE);
71         ppdb_guid = mono_metadata_guid_heap (ppdb_image, cols [MONO_MODULE_MVID]);
72
73         if (memcmp (guid, ppdb_guid, 16) != 0) {
74                 g_warning ("Symbol file %s doesn't match image %s", ppdb_image->name,
75                                    image->name);
76                 mono_image_close (ppdb_image);
77                 return NULL;
78         }
79
80         ppdb = g_new0 (MonoPPDBFile, 1);
81         ppdb->image = ppdb_image;
82
83         return ppdb;
84 }
85
86 void
87 mono_ppdb_close (MonoDebugHandle *handle)
88 {
89         MonoPPDBFile *ppdb = handle->ppdb;
90
91         mono_image_close (ppdb->image);
92         if (ppdb->doc_cache)
93                 g_hash_table_destroy (ppdb->doc_cache);
94         g_free (ppdb);
95 }
96
97 MonoDebugMethodInfo *
98 mono_ppdb_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
99 {
100         MonoDebugMethodInfo *minfo;
101
102         if (handle->image != mono_class_get_image (mono_method_get_class (method)))
103                 return NULL;
104
105         // FIXME: Cache
106
107         // FIXME: Methods without tokens
108
109         minfo = g_new0 (MonoDebugMethodInfo, 1);
110         minfo->index = 0;
111         minfo->method = method;
112         minfo->handle = handle;
113
114         return minfo;
115 }
116
117 static MonoDebugSourceInfo*
118 get_docinfo (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
119 {
120         MonoTableInfo *tables = image->tables;
121         guint32 cols [MONO_DOCUMENT_SIZE];
122         const char *ptr;
123         const char *start;
124         const char *part_ptr;
125         int size, part_size, partidx, nparts;
126         char sep;
127         GString *s;
128         MonoDebugSourceInfo *res;
129
130         // FIXME: Cache
131
132         mono_metadata_decode_row (&tables [MONO_TABLE_DOCUMENT], docidx-1, cols, MONO_DOCUMENT_SIZE);
133
134         ptr = mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_NAME]);
135         size = mono_metadata_decode_blob_size (ptr, &ptr);
136         start = ptr;
137
138         // FIXME: UTF8
139         sep = ptr [0];
140         ptr ++;
141
142         s = g_string_new ("");
143
144         nparts = 0;
145         while (ptr < start + size) {
146                 partidx = mono_metadata_decode_value (ptr, &ptr);
147                 if (nparts)
148                         g_string_append_c (s, sep);
149                 if (partidx) {
150                         part_ptr = mono_metadata_blob_heap (image, partidx);
151                         part_size = mono_metadata_decode_blob_size (part_ptr, &part_ptr);
152
153                         // FIXME: UTF8
154                         g_string_append_len (s, part_ptr, part_size);
155                 }
156                 nparts ++;
157         }
158
159         res = g_new0 (MonoDebugSourceInfo, 1);
160         res->source_file = g_string_free (s, FALSE);
161         res->guid = NULL;
162         res->hash = (guint8*)mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_HASH]);
163
164         return res;
165 }
166
167 static char*
168 get_docname (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
169 {
170         MonoDebugSourceInfo *info;
171
172         info = get_docinfo (ppdb, image, docidx);
173         return g_strdup (info->source_file);
174 }
175
176 /**
177  * mono_ppdb_lookup_location:
178  * @minfo: A `MonoDebugMethodInfo' which can be retrieved by
179  *         mono_debug_lookup_method().
180  * @offset: IL offset within the corresponding method's CIL code.
181  *
182  * This function is similar to mono_debug_lookup_location(), but we
183  * already looked up the method and also already did the
184  * `native address -> IL offset' mapping.
185  */
186 MonoDebugSourceLocation *
187 mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
188 {
189         MonoPPDBFile *ppdb = minfo->handle->ppdb;
190         MonoImage *image = ppdb->image;
191         MonoMethod *method = minfo->method;
192         MonoTableInfo *tables = image->tables;
193         guint32 cols [MONO_METHODBODY_SIZE];
194         const char *ptr;
195         const char *end;
196         char *docname;
197         int idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
198         MonoDebugSourceLocation *location;
199
200         g_assert (method->token);
201
202         idx = mono_metadata_token_index (method->token);
203
204         mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], idx-1, cols, MONO_METHODBODY_SIZE);
205
206         // FIXME:
207         g_assert (cols [MONO_METHODBODY_SEQ_POINTS]);
208
209         ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
210         size = mono_metadata_decode_blob_size (ptr, &ptr);
211         end = ptr + size;
212
213         /* First record */
214         docidx = mono_metadata_decode_value (ptr, &ptr);
215         docname = get_docname (ppdb, image, docidx);
216         iloffset = mono_metadata_decode_value (ptr, &ptr);
217         delta_lines = mono_metadata_decode_value (ptr, &ptr);
218         if (delta_lines == 0)
219                 delta_cols = mono_metadata_decode_value (ptr, &ptr);
220         else
221                 delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
222         start_line = mono_metadata_decode_value (ptr, &ptr);
223         start_col = mono_metadata_decode_value (ptr, &ptr);
224
225         while (ptr < end) {
226                 if (iloffset > offset)
227                         break;
228
229                 delta_il = mono_metadata_decode_value (ptr, &ptr);
230                 if (delta_il == 0)
231                         // FIXME:
232                         g_assert_not_reached ();
233
234                 delta_lines = mono_metadata_decode_value (ptr, &ptr);
235                 if (delta_lines == 0)
236                         delta_cols = mono_metadata_decode_value (ptr, &ptr);
237                 else
238                         delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
239                 if (delta_lines == 0 && delta_cols == 0)
240                         // FIXME:
241                         g_assert_not_reached ();
242                 adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
243                 adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
244
245                 if (iloffset + delta_il > offset)
246                         break;
247
248                 iloffset += delta_il;
249                 start_line += adv_line;
250                 start_col += adv_col;
251         }
252
253         location = g_new0 (MonoDebugSourceLocation, 1);
254         location->source_file = docname;
255         location->row = start_line;
256         location->il_offset = iloffset;
257
258         return location;
259 }
260
261 void
262 mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points)
263 {
264         MonoPPDBFile *ppdb = minfo->handle->ppdb;
265         MonoImage *image = ppdb->image;
266         MonoMethod *method = minfo->method;
267         MonoTableInfo *tables = image->tables;
268         guint32 cols [MONO_METHODBODY_SIZE];
269         const char *ptr;
270         const char *end;
271         MonoDebugSourceInfo *docinfo;
272         int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
273         GArray *sps;
274         MonoSymSeqPoint sp;
275         GPtrArray *sfiles = NULL;
276         GPtrArray *sindexes = NULL;
277
278         if (source_file)
279                 *source_file = NULL;
280         if (source_file_list)
281                 *source_file_list = NULL;
282         if (source_files)
283                 *source_files = NULL;
284         if (seq_points)
285                 *seq_points = NULL;
286         if (n_seq_points)
287                 *n_seq_points = 0;
288
289         if (source_file_list)
290                 *source_file_list = sfiles = g_ptr_array_new ();
291         if (source_files)
292                 sindexes = g_ptr_array_new ();
293
294         if (!method->token)
295                 return;
296
297         method_idx = mono_metadata_token_index (method->token);
298
299         mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], method_idx-1, cols, MONO_METHODBODY_SIZE);
300
301         if (!cols [MONO_METHODBODY_SEQ_POINTS])
302                 return;
303
304         ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
305         size = mono_metadata_decode_blob_size (ptr, &ptr);
306         end = ptr + size;
307
308         sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint));
309
310         /* First record */
311         docidx = mono_metadata_decode_value (ptr, &ptr);
312         docinfo = get_docinfo (ppdb, image, docidx);
313         iloffset = mono_metadata_decode_value (ptr, &ptr);
314         delta_lines = mono_metadata_decode_value (ptr, &ptr);
315         if (delta_lines == 0)
316                 delta_cols = mono_metadata_decode_value (ptr, &ptr);
317         else
318                 delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
319         start_line = mono_metadata_decode_value (ptr, &ptr);
320         start_col = mono_metadata_decode_value (ptr, &ptr);
321
322         if (sfiles)
323                 g_ptr_array_add (sfiles, docinfo);
324         if (source_files)
325                 g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1));
326
327         memset (&sp, 0, sizeof (sp));
328         sp.il_offset = iloffset;
329         sp.line = start_line;
330         sp.column = start_col;
331         sp.end_line = start_line + delta_lines;
332         sp.end_column = start_col + delta_cols;
333
334         g_array_append_val (sps, sp);
335
336         while (ptr < end) {
337                 delta_il = mono_metadata_decode_value (ptr, &ptr);
338                 if (delta_il == 0) {
339                         /* subsequent-document-record */
340                         docidx = mono_metadata_decode_value (ptr, &ptr);
341                         docinfo = get_docinfo (ppdb, image, docidx);
342                         if (sfiles)
343                                 g_ptr_array_add (sfiles, docinfo);
344                         continue;
345                 }
346                 delta_lines = mono_metadata_decode_value (ptr, &ptr);
347                 if (delta_lines == 0)
348                         delta_cols = mono_metadata_decode_value (ptr, &ptr);
349                 else
350                         delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
351
352                 if (delta_lines == 0 && delta_cols == 0) {
353                         /* Hidden sequence point */
354                         // FIXME: This seems to be followed by garbage
355                         continue;
356                 }
357
358                 adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
359                 adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
360
361                 iloffset += delta_il;
362                 start_line += adv_line;
363                 start_col += adv_col;
364
365                 memset (&sp, 0, sizeof (sp));
366                 sp.il_offset = iloffset;
367                 sp.line = start_line;
368                 sp.column = start_col;
369                 sp.end_line = start_line + delta_lines;
370                 sp.end_column = start_col + delta_cols;
371
372                 g_array_append_val (sps, sp);
373                 if (source_files)
374                         g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1));
375         }
376
377         if (n_seq_points) {
378                 *n_seq_points = sps->len;
379                 g_assert (seq_points);
380                 *seq_points = g_new (MonoSymSeqPoint, sps->len);
381                 memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint));
382         }
383
384         if (source_file)
385                 *source_file = g_strdup (((MonoDebugSourceInfo*)g_ptr_array_index (sfiles, 0))->source_file);
386         if (source_files) {
387                 *source_files = g_new (int, sps->len);
388                 for (i = 0; i < sps->len; ++i)
389                         (*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i));
390                 g_ptr_array_free (sindexes, TRUE);
391         }
392
393         g_array_free (sps, TRUE);
394 }
395
396 MonoDebugLocalsInfo*
397 mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo)
398 {
399         MonoPPDBFile *ppdb = minfo->handle->ppdb;
400         MonoImage *image = ppdb->image;
401         MonoTableInfo *tables = image->tables;
402         MonoMethod *method = minfo->method;
403         guint32 cols [MONO_LOCALSCOPE_SIZE];
404         guint32 locals_cols [MONO_LOCALVARIABLE_SIZE];
405         int i, lindex, sindex, method_idx, start_scope_idx, scope_idx, locals_idx, locals_end_idx, nscopes;
406         MonoDebugLocalsInfo *res;
407         MonoMethodSignature *sig;
408
409         if (!method->token)
410                 return NULL;
411
412         sig = mono_method_signature (method);
413         if (!sig)
414                 return NULL;
415
416         method_idx = mono_metadata_token_index (method->token);
417
418         start_scope_idx = mono_metadata_localscope_from_methoddef (image, method_idx);
419
420         if (!start_scope_idx)
421                 return NULL;
422
423         /* Compute number of locals and scopes */
424         scope_idx = start_scope_idx;
425         mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
426         locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
427         while (TRUE) {
428                 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
429                 if (cols [MONO_LOCALSCOPE_METHOD] != method_idx)
430                         break;
431                 scope_idx ++;
432         }
433         nscopes = scope_idx - start_scope_idx;
434         if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) {
435                 // FIXME:
436                 g_assert_not_reached ();
437                 locals_end_idx = -1;
438         } else {
439                 locals_end_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
440         }
441
442         res = g_new0 (MonoDebugLocalsInfo, 1);
443         res->num_blocks = nscopes;
444         res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks);
445         res->num_locals = locals_end_idx - locals_idx;
446         res->locals = g_new0 (MonoDebugLocalVar, res->num_locals);
447
448         lindex = 0;
449         for (sindex = 0; sindex < nscopes; ++sindex) {
450                 scope_idx = start_scope_idx + sindex;
451                 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
452
453                 locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
454                 if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) {
455                         // FIXME:
456                         g_assert_not_reached ();
457                 } else {
458                         locals_end_idx = mono_metadata_decode_row_col (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1 + 1, MONO_LOCALSCOPE_VARIABLELIST);
459                 }
460
461                 res->code_blocks [sindex].start_offset = cols [MONO_LOCALSCOPE_STARTOFFSET];
462                 res->code_blocks [sindex].end_offset = cols [MONO_LOCALSCOPE_STARTOFFSET] + cols [MONO_LOCALSCOPE_LENGTH];
463
464                 //printf ("Scope: %s %d %d %d-%d\n", mono_method_full_name (method, 1), cols [MONO_LOCALSCOPE_STARTOFFSET], cols [MONO_LOCALSCOPE_LENGTH], locals_idx, locals_end_idx);
465
466                 for (i = locals_idx; i < locals_end_idx; ++i) {
467                         mono_metadata_decode_row (&tables [MONO_TABLE_LOCALVARIABLE], i - 1, locals_cols, MONO_LOCALVARIABLE_SIZE);
468
469                         res->locals [lindex].name = g_strdup (mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]));
470                         res->locals [lindex].index = locals_cols [MONO_LOCALVARIABLE_INDEX];
471                         res->locals [lindex].block = &res->code_blocks [sindex];
472                         lindex ++;
473
474                         //printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]);
475                 }
476         }
477
478         return res;
479 }