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