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