Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / debug-mono-ppdb.c
1 /**
2  * \file
3  * Support for the portable PDB symbol
4  * file format
5  *
6  *
7  * Author:
8  *      Mono Project (http://www.mono-project.com)
9  *
10  * Copyright 2015 Xamarin Inc (http://www.xamarin.com)
11  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12  */
13
14 #include <config.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <mono/metadata/metadata.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/tokentype.h>
22 #include <mono/metadata/debug-helpers.h>
23 #include <mono/metadata/mono-debug.h>
24 #include <mono/metadata/debug-internals.h>
25 #include <mono/metadata/mono-endian.h>
26 #include <mono/metadata/metadata-internals.h>
27 #include <mono/metadata/class-internals.h>
28 #include <mono/metadata/cil-coff.h>
29 #include <mono/utils/bsearch.h>
30
31 #include "debug-mono-ppdb.h"
32
33 struct _MonoPPDBFile {
34         MonoImage *image;
35         GHashTable *doc_hash;
36         GHashTable *method_hash;
37 };
38
39 /* IMAGE_DEBUG_DIRECTORY structure */
40 typedef struct
41 {
42         gint32 characteristics;
43         gint32 time_date_stamp;
44         gint16 major_version;
45         gint16 minor_version;
46         gint32 type;
47         gint32 size_of_data;
48         gint32 address;
49         gint32 pointer;
50 }  ImageDebugDirectory;
51
52 typedef struct {
53         gint32 signature;
54         guint8 guid [16];
55         gint32 age;
56 } CodeviewDebugDirectory;
57
58 typedef struct {
59         guint8 guid [20];
60         guint32 entry_point;
61         guint64 referenced_tables;
62 } PdbStreamHeader;
63
64 static gboolean
65 get_pe_debug_guid (MonoImage *image, guint8 *out_guid, gint32 *out_age, gint32 *out_timestamp)
66 {
67         MonoPEDirEntry *debug_dir_entry;
68         ImageDebugDirectory *debug_dir;
69
70         debug_dir_entry = &((MonoCLIImageInfo*)image->image_info)->cli_header.datadir.pe_debug;
71         if (!debug_dir_entry->size)
72                 return FALSE;
73
74         int offset = mono_cli_rva_image_map (image, debug_dir_entry->rva);
75         debug_dir = (ImageDebugDirectory*)(image->raw_data + offset);
76         if (debug_dir->type == 2 && debug_dir->major_version == 0x100 && debug_dir->minor_version == 0x504d) {
77                 /* This is a 'CODEVIEW' debug directory */
78                 CodeviewDebugDirectory *dir = (CodeviewDebugDirectory*)(image->raw_data + debug_dir->pointer);
79
80                 if (dir->signature == 0x53445352) {
81                         memcpy (out_guid, dir->guid, 16);
82                         *out_age = dir->age;
83                         *out_timestamp = debug_dir->time_date_stamp;
84                         return TRUE;
85                 }
86         }
87         return FALSE;
88 }
89
90 static void
91 doc_free (gpointer key)
92 {
93         MonoDebugSourceInfo *info = (MonoDebugSourceInfo *)key;
94
95         g_free (info->source_file);
96         g_free (info);
97 }
98
99 static MonoPPDBFile*
100 create_ppdb_file (MonoImage *ppdb_image)
101 {
102         MonoPPDBFile *ppdb;
103
104         ppdb = g_new0 (MonoPPDBFile, 1);
105         ppdb->image = ppdb_image;
106         ppdb->doc_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) doc_free);
107         ppdb->method_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_free);
108         return ppdb;
109 }
110
111 MonoPPDBFile*
112 mono_ppdb_load_file (MonoImage *image, const guint8 *raw_contents, int size)
113 {
114         MonoImage *ppdb_image = NULL;
115         const char *filename;
116         char *s, *ppdb_filename;
117         MonoImageOpenStatus status;
118         guint8 pe_guid [16];
119         gint32 pe_age;
120         gint32 pe_timestamp;
121
122         if (image->tables [MONO_TABLE_DOCUMENT].rows) {
123                 /* Embedded ppdb */
124                 mono_image_addref (image);
125                 return create_ppdb_file (image);
126         }
127
128         if (!get_pe_debug_guid (image, pe_guid, &pe_age, &pe_timestamp))
129                 return NULL;
130
131         if (raw_contents) {
132                 if (size > 4 && strncmp ((char*)raw_contents, "BSJB", 4) == 0)
133                         ppdb_image = mono_image_open_from_data_internal ((char*)raw_contents, size, TRUE, &status, FALSE, TRUE, NULL);
134         } else {
135                 /* ppdb files drop the .exe/.dll extension */
136                 filename = mono_image_get_filename (image);
137                 if (strlen (filename) > 4 && (!strcmp (filename + strlen (filename) - 4, ".exe") || !strcmp (filename + strlen (filename) - 4, ".dll"))) {
138                         s = g_strdup (filename);
139                         s [strlen (filename) - 4] = '\0';
140                         ppdb_filename = g_strdup_printf ("%s.pdb", s);
141                         g_free (s);
142                 } else {
143                         ppdb_filename = g_strdup_printf ("%s.pdb", filename);
144                 }
145
146                 ppdb_image = mono_image_open_metadata_only (ppdb_filename, &status);
147                 if (!ppdb_image)
148                         g_free (ppdb_filename);
149         }
150         if (!ppdb_image)
151                 return NULL;
152
153         /*
154          * Check that the images match.
155          * The same id is stored in the Debug Directory of the PE file, and in the
156          * #Pdb stream in the ppdb file.
157          */
158         PdbStreamHeader *pdb_stream = (PdbStreamHeader*)ppdb_image->heap_pdb.data;
159
160         g_assert (pdb_stream);
161
162         /* The pdb id is a concentation of the pe guid and the timestamp */
163         if (memcmp (pe_guid, pdb_stream->guid, 16) != 0 || memcmp (&pe_timestamp, pdb_stream->guid + 16, 4) != 0) {
164                 g_warning ("Symbol file %s doesn't match image %s", ppdb_image->name,
165                                    image->name);
166                 mono_image_close (ppdb_image);
167                 return NULL;
168         }
169
170         return create_ppdb_file (ppdb_image);
171 }
172
173 void
174 mono_ppdb_close (MonoDebugHandle *handle)
175 {
176         MonoPPDBFile *ppdb = handle->ppdb;
177
178         mono_image_close (ppdb->image);
179         g_hash_table_destroy (ppdb->doc_hash);
180         g_hash_table_destroy (ppdb->method_hash);
181         g_free (ppdb);
182 }
183
184 MonoDebugMethodInfo *
185 mono_ppdb_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
186 {
187         MonoDebugMethodInfo *minfo;
188         MonoPPDBFile *ppdb = handle->ppdb;
189
190         if (handle->image != mono_class_get_image (mono_method_get_class (method)))
191                 return NULL;
192
193         mono_debugger_lock ();
194
195         minfo = (MonoDebugMethodInfo *)g_hash_table_lookup (ppdb->method_hash, method);
196         if (minfo) {
197                 mono_debugger_unlock ();
198                 return minfo;
199         }
200
201         minfo = g_new0 (MonoDebugMethodInfo, 1);
202         minfo->index = 0;
203         minfo->method = method;
204         minfo->handle = handle;
205
206         g_hash_table_insert (ppdb->method_hash, method, minfo);
207
208         mono_debugger_unlock ();
209
210         return minfo;
211 }
212
213 static MonoDebugSourceInfo*
214 get_docinfo (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
215 {
216         MonoTableInfo *tables = image->tables;
217         guint32 cols [MONO_DOCUMENT_SIZE];
218         const char *ptr;
219         const char *start;
220         const char *part_ptr;
221         int size, part_size, partidx, nparts;
222         char sep;
223         GString *s;
224         MonoDebugSourceInfo *res, *cached;
225
226         mono_debugger_lock ();
227         cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
228         mono_debugger_unlock ();
229         if (cached)
230                 return cached;
231
232         mono_metadata_decode_row (&tables [MONO_TABLE_DOCUMENT], docidx-1, cols, MONO_DOCUMENT_SIZE);
233
234         ptr = mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_NAME]);
235         size = mono_metadata_decode_blob_size (ptr, &ptr);
236         start = ptr;
237
238         // FIXME: UTF8
239         sep = ptr [0];
240         ptr ++;
241
242         s = g_string_new ("");
243
244         nparts = 0;
245         while (ptr < start + size) {
246                 partidx = mono_metadata_decode_value (ptr, &ptr);
247                 if (nparts)
248                         g_string_append_c (s, sep);
249                 if (partidx) {
250                         part_ptr = mono_metadata_blob_heap (image, partidx);
251                         part_size = mono_metadata_decode_blob_size (part_ptr, &part_ptr);
252
253                         // FIXME: UTF8
254                         g_string_append_len (s, part_ptr, part_size);
255                 }
256                 nparts ++;
257         }
258
259         res = g_new0 (MonoDebugSourceInfo, 1);
260         res->source_file = g_string_free (s, FALSE);
261         res->guid = NULL;
262         res->hash = (guint8*)mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_HASH]);
263
264         mono_debugger_lock ();
265         cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
266         if (!cached) {
267                 g_hash_table_insert (ppdb->doc_hash, GUINT_TO_POINTER (docidx), res);
268         } else {
269                 doc_free (res);
270                 res = cached;
271         }
272         mono_debugger_unlock ();
273         return res;
274 }
275
276 static char*
277 get_docname (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
278 {
279         MonoDebugSourceInfo *info;
280
281         info = get_docinfo (ppdb, image, docidx);
282         return g_strdup (info->source_file);
283 }
284
285 /**
286  * mono_ppdb_lookup_location:
287  * \param minfo A \c MonoDebugMethodInfo which can be retrieved by mono_debug_lookup_method().
288  * \param offset IL offset within the corresponding method's CIL code.
289  *
290  * This function is similar to mono_debug_lookup_location(), but we
291  * already looked up the method and also already did the
292  * native address -> IL offset mapping.
293  */
294 MonoDebugSourceLocation *
295 mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
296 {
297         MonoPPDBFile *ppdb = minfo->handle->ppdb;
298         MonoImage *image = ppdb->image;
299         MonoMethod *method = minfo->method;
300         MonoTableInfo *tables = image->tables;
301         guint32 cols [MONO_METHODBODY_SIZE];
302         const char *ptr;
303         const char *end;
304         char *docname;
305         int idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
306         gboolean first = TRUE, first_non_hidden = TRUE;
307         MonoDebugSourceLocation *location;
308
309         if (!method->token)
310                 return NULL;
311
312         idx = mono_metadata_token_index (method->token);
313
314         mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], idx-1, cols, MONO_METHODBODY_SIZE);
315
316         docidx = cols [MONO_METHODBODY_DOCUMENT];
317
318         if (!cols [MONO_METHODBODY_SEQ_POINTS])
319                 return NULL;
320         ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
321         size = mono_metadata_decode_blob_size (ptr, &ptr);
322         end = ptr + size;
323
324         /* Header */
325         /* LocalSignature */
326         mono_metadata_decode_value (ptr, &ptr);
327         if (docidx == 0)
328                 docidx = mono_metadata_decode_value (ptr, &ptr);
329         docname = get_docname (ppdb, image, docidx);
330
331         iloffset = 0;
332         start_line = 0;
333         start_col = 0;
334         while (ptr < end) {
335                 delta_il = mono_metadata_decode_value (ptr, &ptr);
336                 if (!first && delta_il == 0) {
337                         /* document-record */
338                         docidx = mono_metadata_decode_value (ptr, &ptr);
339                         docname = get_docname (ppdb, image, docidx);
340                         continue;
341                 }
342                 if (!first && iloffset + delta_il > offset)
343                         break;
344                 iloffset += delta_il;
345                 first = FALSE;
346
347                 delta_lines = mono_metadata_decode_value (ptr, &ptr);
348                 if (delta_lines == 0)
349                         delta_cols = mono_metadata_decode_value (ptr, &ptr);
350                 else
351                         delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
352                 if (delta_lines == 0 && delta_cols == 0)
353                         /* hidden-sequence-point-record */
354                         continue;
355                 if (first_non_hidden) {
356                         start_line = mono_metadata_decode_value (ptr, &ptr);
357                         start_col = mono_metadata_decode_value (ptr, &ptr);
358                 } else {
359                         adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
360                         adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
361                         start_line += adv_line;
362                         start_col += adv_col;
363                 }
364                 first_non_hidden = FALSE;
365         }
366
367         location = g_new0 (MonoDebugSourceLocation, 1);
368         location->source_file = docname;
369         location->row = start_line;
370         location->il_offset = iloffset;
371
372         return location;
373 }
374
375 void
376 mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points)
377 {
378         MonoPPDBFile *ppdb = minfo->handle->ppdb;
379         MonoImage *image = ppdb->image;
380         MonoMethod *method = minfo->method;
381         MonoTableInfo *tables = image->tables;
382         guint32 cols [MONO_METHODBODY_SIZE];
383         const char *ptr;
384         const char *end;
385         MonoDebugSourceInfo *docinfo;
386         int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
387         gboolean first = TRUE, first_non_hidden = TRUE;
388         GArray *sps;
389         MonoSymSeqPoint sp;
390         GPtrArray *sfiles = NULL;
391         GPtrArray *sindexes = NULL;
392
393         if (source_file)
394                 *source_file = NULL;
395         if (source_file_list)
396                 *source_file_list = NULL;
397         if (source_files)
398                 *source_files = NULL;
399         if (seq_points)
400                 *seq_points = NULL;
401         if (n_seq_points)
402                 *n_seq_points = 0;
403
404         if (source_file_list)
405                 *source_file_list = sfiles = g_ptr_array_new ();
406         if (source_files)
407                 sindexes = g_ptr_array_new ();
408
409         if (!method->token)
410                 return;
411
412         method_idx = mono_metadata_token_index (method->token);
413
414         mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], method_idx-1, cols, MONO_METHODBODY_SIZE);
415
416         docidx = cols [MONO_METHODBODY_DOCUMENT];
417
418         if (!cols [MONO_METHODBODY_SEQ_POINTS])
419                 return;
420
421         ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
422         size = mono_metadata_decode_blob_size (ptr, &ptr);
423         end = ptr + size;
424
425         sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint));
426
427         /* Header */
428         /* LocalSignature */
429         mono_metadata_decode_value (ptr, &ptr);
430         if (docidx == 0)
431                 docidx = mono_metadata_decode_value (ptr, &ptr);
432         docinfo = get_docinfo (ppdb, image, docidx);
433
434         if (sfiles)
435                 g_ptr_array_add (sfiles, docinfo);
436
437         if (source_file)
438                 *source_file = g_strdup (docinfo->source_file);
439
440         iloffset = 0;
441         start_line = 0;
442         start_col = 0;
443         while (ptr < end) {
444                 delta_il = mono_metadata_decode_value (ptr, &ptr);
445                 if (!first && delta_il == 0) {
446                         /* subsequent-document-record */
447                         docidx = mono_metadata_decode_value (ptr, &ptr);
448                         docinfo = get_docinfo (ppdb, image, docidx);
449                         if (sfiles)
450                                 g_ptr_array_add (sfiles, docinfo);
451                         continue;
452                 }
453                 iloffset += delta_il;
454                 first = FALSE;
455
456                 delta_lines = mono_metadata_decode_value (ptr, &ptr);
457                 if (delta_lines == 0)
458                         delta_cols = mono_metadata_decode_value (ptr, &ptr);
459                 else
460                         delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
461
462                 if (delta_lines == 0 && delta_cols == 0) {
463                         /* Hidden sequence point */
464                         continue;
465                 }
466
467                 if (first_non_hidden) {
468                         start_line = mono_metadata_decode_value (ptr, &ptr);
469                         start_col = mono_metadata_decode_value (ptr, &ptr);
470                 } else {
471                         adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
472                         adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
473                         start_line += adv_line;
474                         start_col += adv_col;
475                 }
476                 first_non_hidden = FALSE;
477
478                 memset (&sp, 0, sizeof (sp));
479                 sp.il_offset = iloffset;
480                 sp.line = start_line;
481                 sp.column = start_col;
482                 sp.end_line = start_line + delta_lines;
483                 sp.end_column = start_col + delta_cols;
484
485                 g_array_append_val (sps, sp);
486                 if (source_files)
487                         g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1));
488         }
489
490         if (n_seq_points) {
491                 *n_seq_points = sps->len;
492                 g_assert (seq_points);
493                 *seq_points = g_new (MonoSymSeqPoint, sps->len);
494                 memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint));
495         }
496
497         if (source_files) {
498                 *source_files = g_new (int, sps->len);
499                 for (i = 0; i < sps->len; ++i)
500                         (*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i));
501                 g_ptr_array_free (sindexes, TRUE);
502         }
503
504         g_array_free (sps, TRUE);
505 }
506
507 MonoDebugLocalsInfo*
508 mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo)
509 {
510         MonoPPDBFile *ppdb = minfo->handle->ppdb;
511         MonoImage *image = ppdb->image;
512         MonoTableInfo *tables = image->tables;
513         MonoMethod *method = minfo->method;
514         guint32 cols [MONO_LOCALSCOPE_SIZE];
515         guint32 locals_cols [MONO_LOCALVARIABLE_SIZE];
516         int i, lindex, sindex, method_idx, start_scope_idx, scope_idx, locals_idx, locals_end_idx, nscopes;
517         MonoDebugLocalsInfo *res;
518         MonoMethodSignature *sig;
519
520         if (!method->token)
521                 return NULL;
522
523         sig = mono_method_signature (method);
524         if (!sig)
525                 return NULL;
526
527         method_idx = mono_metadata_token_index (method->token);
528
529         start_scope_idx = mono_metadata_localscope_from_methoddef (image, method_idx);
530
531         if (!start_scope_idx)
532                 return NULL;
533
534         /* Compute number of locals and scopes */
535         scope_idx = start_scope_idx;
536         mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
537         locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
538
539         // https://github.com/dotnet/roslyn/blob/2ae8d5fed96ab3f1164031f9b4ac827f53289159/docs/specs/PortablePdb-Metadata.md#LocalScopeTable
540         //
541         // The variableList attribute in the pdb metadata table is a contiguous array that starts at a
542         // given offset (locals_idx) above and
543         //
544         // """
545         // continues to the smaller of:
546         //
547         // the last row of the LocalVariable table
548         // the next run of LocalVariables, found by inspecting the VariableList of the next row in this LocalScope table.
549         // """
550         // this endpoint becomes locals_end_idx below
551
552         // March to the last scope that is in this method
553         while (scope_idx <= tables [MONO_TABLE_LOCALSCOPE].rows) {
554                 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
555                 if (cols [MONO_LOCALSCOPE_METHOD] != method_idx)
556                         break;
557                 scope_idx ++;
558         }
559         // The number of scopes is the difference in the indices
560         // for the first and last scopes
561         nscopes = scope_idx - start_scope_idx;
562
563         // Ends with "the last row of the LocalVariable table"
564         // this happens if the above loop marched one past the end
565         // of the rows
566         if (scope_idx > tables [MONO_TABLE_LOCALSCOPE].rows) {
567                 locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
568         } else {
569                 // Ends with "the next run of LocalVariables,
570                 // found by inspecting the VariableList of the next row in this LocalScope table."
571                 locals_end_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
572         }
573
574         res = g_new0 (MonoDebugLocalsInfo, 1);
575         res->num_blocks = nscopes;
576         res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks);
577         res->num_locals = locals_end_idx - locals_idx;
578         res->locals = g_new0 (MonoDebugLocalVar, res->num_locals);
579
580         lindex = 0;
581         for (sindex = 0; sindex < nscopes; ++sindex) {
582                 scope_idx = start_scope_idx + sindex;
583                 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
584
585                 locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
586                 if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) {
587                         locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
588                 } else {
589                         locals_end_idx = mono_metadata_decode_row_col (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1 + 1, MONO_LOCALSCOPE_VARIABLELIST);
590                 }
591
592                 res->code_blocks [sindex].start_offset = cols [MONO_LOCALSCOPE_STARTOFFSET];
593                 res->code_blocks [sindex].end_offset = cols [MONO_LOCALSCOPE_STARTOFFSET] + cols [MONO_LOCALSCOPE_LENGTH];
594
595                 //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);
596
597                 for (i = locals_idx; i < locals_end_idx; ++i) {
598                         mono_metadata_decode_row (&tables [MONO_TABLE_LOCALVARIABLE], i - 1, locals_cols, MONO_LOCALVARIABLE_SIZE);
599
600                         res->locals [lindex].name = g_strdup (mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]));
601                         res->locals [lindex].index = locals_cols [MONO_LOCALVARIABLE_INDEX];
602                         res->locals [lindex].block = &res->code_blocks [sindex];
603                         lindex ++;
604
605                         //printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]);
606                 }
607         }
608
609         return res;
610 }
611
612 /*
613 * We use this to pass context information to the row locator
614 */
615 typedef struct {
616         int idx;                        /* The index that we are trying to locate */
617         int col_idx;            /* The index in the row where idx may be stored */
618         MonoTableInfo *t;       /* pointer to the table */
619         guint32 result;
620 } locator_t;
621
622 static int
623 table_locator (const void *a, const void *b)
624 {
625         locator_t *loc = (locator_t *)a;
626         const char *bb = (const char *)b;
627         guint32 table_index = (bb - loc->t->base) / loc->t->row_size;
628         guint32 col;
629
630         col = mono_metadata_decode_row_col(loc->t, table_index, loc->col_idx);
631
632         if (loc->idx == col) {
633                 loc->result = table_index;
634                 return 0;
635         }
636         if (loc->idx < col)
637                 return -1;
638         else
639                 return 1;
640 }
641
642 static gboolean
643 compare_guid (guint8* guid1, guint8* guid2) {
644         for (int i = 0; i < 16; i++) {
645                 if (guid1 [i] != guid2 [i])
646                         return FALSE;
647         }
648         return TRUE;
649 }
650
651 // for parent_type see HasCustomDebugInformation table at
652 // https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md
653 static const char*
654 lookup_custom_debug_information (MonoImage* image, guint32 token, uint8_t parent_type, guint8* guid)
655 {
656         MonoTableInfo *tables = image->tables;
657         MonoTableInfo *table = &tables[MONO_TABLE_CUSTOMDEBUGINFORMATION];
658         locator_t loc;
659
660         if (!table->base)
661                 return 0;
662
663         loc.idx = (mono_metadata_token_index (token) << 5) | parent_type;
664         loc.col_idx = MONO_CUSTOMDEBUGINFORMATION_PARENT;
665         loc.t = table;
666
667         if (!mono_binary_search (&loc, table->base, table->rows, table->row_size, table_locator))
668                 return NULL;
669         // Great we found one of possibly many CustomDebugInformations of this entity they are distinguished by KIND guid
670         // First try on this index found by binary search...(it's most likeley to be only one and binary search found the one we want)
671         if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_KIND))))
672                 return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_VALUE));
673
674         // Move forward from binary found index, until parent token differs
675         for (int i = loc.result + 1; i < table->rows; i++)
676         {
677                 if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx)
678                         break;
679                 if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND))))
680                         return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE));
681         }
682
683         // Move backward from binary found index, until parent token differs
684         for (int i = loc.result - 1; i >= 0; i--) {
685                 if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx)
686                         break;
687                 if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND))))
688                         return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE));
689         }
690         return NULL;
691 }
692
693 MonoDebugMethodAsyncInfo*
694 mono_ppdb_lookup_method_async_debug_info (MonoDebugMethodInfo *minfo)
695 {
696         MonoMethod *method = minfo->method;
697         MonoPPDBFile *ppdb = minfo->handle->ppdb;
698         MonoImage *image = ppdb->image;
699
700         // Guid is taken from Roslyn source code:
701         // https://github.com/dotnet/roslyn/blob/1ad4b58/src/Dependencies/CodeAnalysis.Metadata/PortableCustomDebugInfoKinds.cs#L9
702         guint8 async_method_stepping_information_guid [16] = { 0xC5, 0x2A, 0xFD, 0x54, 0x25, 0xE9, 0x1A, 0x40, 0x9C, 0x2A, 0xF9, 0x4F, 0x17, 0x10, 0x72, 0xF8 };
703         char const *blob = lookup_custom_debug_information (image, method->token, 0, async_method_stepping_information_guid);
704         if (!blob)
705                 return NULL;
706         int blob_len = mono_metadata_decode_blob_size (blob, &blob);
707         MonoDebugMethodAsyncInfo* res = g_new0 (MonoDebugMethodAsyncInfo, 1);
708         char const *pointer = blob;
709
710         // Format of this blob is taken from Roslyn source code:
711         // https://github.com/dotnet/roslyn/blob/1ad4b58/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs#L566
712
713         pointer += 4;//catch_handler_offset
714         while (pointer - blob < blob_len) {
715                 res->num_awaits++;
716                 pointer += 8;//yield_offsets+resume_offsets
717                 mono_metadata_decode_value (pointer, &pointer);//move_next_method_token
718         }
719         g_assert(pointer - blob == blob_len); //Check that we used all blob data
720         pointer = blob; //reset pointer after we figured num_awaits
721
722         res->yield_offsets = g_new (uint32_t, res->num_awaits);
723         res->resume_offsets = g_new (uint32_t, res->num_awaits);
724         res->move_next_method_token = g_new (uint32_t, res->num_awaits);
725
726         res->catch_handler_offset = read32 (pointer); pointer += 4;
727         for (int i = 0; i < res->num_awaits; i++) {
728                 res->yield_offsets [i] = read32 (pointer); pointer += 4;
729                 res->resume_offsets [i] = read32 (pointer); pointer += 4;
730                 res->move_next_method_token [i] = mono_metadata_decode_value (pointer, &pointer);
731         }
732         return res;
733 }