/**
|
* \file
|
* Support for the portable PDB symbol
|
* file format
|
*
|
*
|
* Author:
|
* Mono Project (http://www.mono-project.com)
|
*
|
* Copyright 2015 Xamarin Inc (http://www.xamarin.com)
|
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
*/
|
|
#include <config.h>
|
#include <stdlib.h>
|
#include <stdio.h>
|
#include <errno.h>
|
#include <string.h>
|
#include <mono/metadata/metadata.h>
|
#include <mono/metadata/tabledefs.h>
|
#include <mono/metadata/tokentype.h>
|
#include <mono/metadata/debug-helpers.h>
|
#include <mono/metadata/mono-debug.h>
|
#include <mono/metadata/debug-internals.h>
|
#include <mono/metadata/mono-endian.h>
|
#include <mono/metadata/metadata-internals.h>
|
#include <mono/metadata/class-internals.h>
|
#include <mono/metadata/cil-coff.h>
|
#include <mono/utils/bsearch.h>
|
#include <mono/utils/mono-logger-internals.h>
|
|
#include "debug-mono-ppdb.h"
|
|
struct _MonoPPDBFile {
|
MonoImage *image;
|
GHashTable *doc_hash;
|
GHashTable *method_hash;
|
};
|
|
/* IMAGE_DEBUG_DIRECTORY structure */
|
typedef struct
|
{
|
gint32 characteristics;
|
gint32 time_date_stamp;
|
gint16 major_version;
|
gint16 minor_version;
|
gint32 type;
|
gint32 size_of_data;
|
gint32 address;
|
gint32 pointer;
|
} ImageDebugDirectory;
|
|
typedef struct {
|
gint32 signature;
|
guint8 guid [16];
|
gint32 age;
|
} CodeviewDebugDirectory;
|
|
typedef struct {
|
guint8 guid [20];
|
guint32 entry_point;
|
guint64 referenced_tables;
|
} PdbStreamHeader;
|
|
static gboolean
|
get_pe_debug_guid (MonoImage *image, guint8 *out_guid, gint32 *out_age, gint32 *out_timestamp)
|
{
|
MonoPEDirEntry *debug_dir_entry;
|
ImageDebugDirectory *debug_dir;
|
|
debug_dir_entry = &((MonoCLIImageInfo*)image->image_info)->cli_header.datadir.pe_debug;
|
if (!debug_dir_entry->size)
|
return FALSE;
|
|
int offset = mono_cli_rva_image_map (image, debug_dir_entry->rva);
|
debug_dir = (ImageDebugDirectory*)(image->raw_data + offset);
|
if (debug_dir->type == 2 && debug_dir->major_version == 0x100 && debug_dir->minor_version == 0x504d) {
|
/* This is a 'CODEVIEW' debug directory */
|
CodeviewDebugDirectory *dir = (CodeviewDebugDirectory*)(image->raw_data + debug_dir->pointer);
|
|
if (dir->signature == 0x53445352) {
|
memcpy (out_guid, dir->guid, 16);
|
*out_age = dir->age;
|
*out_timestamp = debug_dir->time_date_stamp;
|
return TRUE;
|
}
|
}
|
return FALSE;
|
}
|
|
static void
|
doc_free (gpointer key)
|
{
|
MonoDebugSourceInfo *info = (MonoDebugSourceInfo *)key;
|
|
g_free (info->source_file);
|
g_free (info);
|
}
|
|
static MonoPPDBFile*
|
create_ppdb_file (MonoImage *ppdb_image)
|
{
|
MonoPPDBFile *ppdb;
|
|
ppdb = g_new0 (MonoPPDBFile, 1);
|
ppdb->image = ppdb_image;
|
ppdb->doc_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) doc_free);
|
ppdb->method_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_free);
|
return ppdb;
|
}
|
|
MonoPPDBFile*
|
mono_ppdb_load_file (MonoImage *image, const guint8 *raw_contents, int size)
|
{
|
MonoImage *ppdb_image = NULL;
|
const char *filename;
|
char *s, *ppdb_filename;
|
MonoImageOpenStatus status;
|
guint8 pe_guid [16];
|
gint32 pe_age;
|
gint32 pe_timestamp;
|
|
if (image->tables [MONO_TABLE_DOCUMENT].rows) {
|
/* Embedded ppdb */
|
mono_image_addref (image);
|
return create_ppdb_file (image);
|
}
|
|
if (!get_pe_debug_guid (image, pe_guid, &pe_age, &pe_timestamp)) {
|
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Image '%s' has no debug directory.", image->name);
|
return NULL;
|
}
|
|
if (raw_contents) {
|
if (size > 4 && strncmp ((char*)raw_contents, "BSJB", 4) == 0)
|
ppdb_image = mono_image_open_from_data_internal ((char*)raw_contents, size, TRUE, &status, FALSE, TRUE, NULL);
|
} else {
|
/* ppdb files drop the .exe/.dll extension */
|
filename = mono_image_get_filename (image);
|
if (strlen (filename) > 4 && (!strcmp (filename + strlen (filename) - 4, ".exe") || !strcmp (filename + strlen (filename) - 4, ".dll"))) {
|
s = g_strdup (filename);
|
s [strlen (filename) - 4] = '\0';
|
ppdb_filename = g_strdup_printf ("%s.pdb", s);
|
g_free (s);
|
} else {
|
ppdb_filename = g_strdup_printf ("%s.pdb", filename);
|
}
|
|
ppdb_image = mono_image_open_metadata_only (ppdb_filename, &status);
|
if (!ppdb_image)
|
g_free (ppdb_filename);
|
}
|
if (!ppdb_image)
|
return NULL;
|
|
/*
|
* Check that the images match.
|
* The same id is stored in the Debug Directory of the PE file, and in the
|
* #Pdb stream in the ppdb file.
|
*/
|
PdbStreamHeader *pdb_stream = (PdbStreamHeader*)ppdb_image->heap_pdb.data;
|
|
g_assert (pdb_stream);
|
|
/* The pdb id is a concentation of the pe guid and the timestamp */
|
if (memcmp (pe_guid, pdb_stream->guid, 16) != 0 || memcmp (&pe_timestamp, pdb_stream->guid + 16, 4) != 0) {
|
g_warning ("Symbol file %s doesn't match image %s", ppdb_image->name,
|
image->name);
|
mono_image_close (ppdb_image);
|
return NULL;
|
}
|
|
return create_ppdb_file (ppdb_image);
|
}
|
|
void
|
mono_ppdb_close (MonoDebugHandle *handle)
|
{
|
MonoPPDBFile *ppdb = handle->ppdb;
|
|
mono_image_close (ppdb->image);
|
g_hash_table_destroy (ppdb->doc_hash);
|
g_hash_table_destroy (ppdb->method_hash);
|
g_free (ppdb);
|
}
|
|
MonoDebugMethodInfo *
|
mono_ppdb_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
|
{
|
MonoDebugMethodInfo *minfo;
|
MonoPPDBFile *ppdb = handle->ppdb;
|
|
if (handle->image != mono_class_get_image (mono_method_get_class (method)))
|
return NULL;
|
|
mono_debugger_lock ();
|
|
minfo = (MonoDebugMethodInfo *)g_hash_table_lookup (ppdb->method_hash, method);
|
if (minfo) {
|
mono_debugger_unlock ();
|
return minfo;
|
}
|
|
minfo = g_new0 (MonoDebugMethodInfo, 1);
|
minfo->index = 0;
|
minfo->method = method;
|
minfo->handle = handle;
|
|
g_hash_table_insert (ppdb->method_hash, method, minfo);
|
|
mono_debugger_unlock ();
|
|
return minfo;
|
}
|
|
static MonoDebugSourceInfo*
|
get_docinfo (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
|
{
|
MonoTableInfo *tables = image->tables;
|
guint32 cols [MONO_DOCUMENT_SIZE];
|
const char *ptr;
|
const char *start;
|
const char *part_ptr;
|
int size, part_size, partidx, nparts;
|
char sep;
|
GString *s;
|
MonoDebugSourceInfo *res, *cached;
|
|
mono_debugger_lock ();
|
cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
|
mono_debugger_unlock ();
|
if (cached)
|
return cached;
|
|
mono_metadata_decode_row (&tables [MONO_TABLE_DOCUMENT], docidx-1, cols, MONO_DOCUMENT_SIZE);
|
|
ptr = mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_NAME]);
|
size = mono_metadata_decode_blob_size (ptr, &ptr);
|
start = ptr;
|
|
// FIXME: UTF8
|
sep = ptr [0];
|
ptr ++;
|
|
s = g_string_new ("");
|
|
nparts = 0;
|
while (ptr < start + size) {
|
partidx = mono_metadata_decode_value (ptr, &ptr);
|
if (nparts)
|
g_string_append_c (s, sep);
|
if (partidx) {
|
part_ptr = mono_metadata_blob_heap (image, partidx);
|
part_size = mono_metadata_decode_blob_size (part_ptr, &part_ptr);
|
|
// FIXME: UTF8
|
g_string_append_len (s, part_ptr, part_size);
|
}
|
nparts ++;
|
}
|
|
res = g_new0 (MonoDebugSourceInfo, 1);
|
res->source_file = g_string_free (s, FALSE);
|
res->guid = NULL;
|
res->hash = (guint8*)mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_HASH]);
|
|
mono_debugger_lock ();
|
cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
|
if (!cached) {
|
g_hash_table_insert (ppdb->doc_hash, GUINT_TO_POINTER (docidx), res);
|
} else {
|
doc_free (res);
|
res = cached;
|
}
|
mono_debugger_unlock ();
|
return res;
|
}
|
|
static char*
|
get_docname (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
|
{
|
MonoDebugSourceInfo *info;
|
|
info = get_docinfo (ppdb, image, docidx);
|
return g_strdup (info->source_file);
|
}
|
|
/**
|
* mono_ppdb_lookup_location:
|
* \param minfo A \c MonoDebugMethodInfo which can be retrieved by mono_debug_lookup_method().
|
* \param offset IL offset within the corresponding method's CIL code.
|
*
|
* This function is similar to mono_debug_lookup_location(), but we
|
* already looked up the method and also already did the
|
* native address -> IL offset mapping.
|
*/
|
MonoDebugSourceLocation *
|
mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
|
{
|
MonoPPDBFile *ppdb = minfo->handle->ppdb;
|
MonoImage *image = ppdb->image;
|
MonoMethod *method = minfo->method;
|
MonoTableInfo *tables = image->tables;
|
guint32 cols [MONO_METHODBODY_SIZE];
|
const char *ptr;
|
const char *end;
|
char *docname;
|
int idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
|
gboolean first = TRUE, first_non_hidden = TRUE;
|
MonoDebugSourceLocation *location;
|
|
if (!method->token)
|
return NULL;
|
|
idx = mono_metadata_token_index (method->token);
|
|
mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], idx-1, cols, MONO_METHODBODY_SIZE);
|
|
docidx = cols [MONO_METHODBODY_DOCUMENT];
|
|
if (!cols [MONO_METHODBODY_SEQ_POINTS])
|
return NULL;
|
ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
|
size = mono_metadata_decode_blob_size (ptr, &ptr);
|
end = ptr + size;
|
|
/* Header */
|
/* LocalSignature */
|
mono_metadata_decode_value (ptr, &ptr);
|
if (docidx == 0)
|
docidx = mono_metadata_decode_value (ptr, &ptr);
|
docname = get_docname (ppdb, image, docidx);
|
|
iloffset = 0;
|
start_line = 0;
|
start_col = 0;
|
while (ptr < end) {
|
delta_il = mono_metadata_decode_value (ptr, &ptr);
|
if (!first && delta_il == 0) {
|
/* document-record */
|
docidx = mono_metadata_decode_value (ptr, &ptr);
|
docname = get_docname (ppdb, image, docidx);
|
continue;
|
}
|
if (!first && iloffset + delta_il > offset)
|
break;
|
iloffset += delta_il;
|
first = FALSE;
|
|
delta_lines = mono_metadata_decode_value (ptr, &ptr);
|
if (delta_lines == 0)
|
delta_cols = mono_metadata_decode_value (ptr, &ptr);
|
else
|
delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
|
if (delta_lines == 0 && delta_cols == 0)
|
/* hidden-sequence-point-record */
|
continue;
|
if (first_non_hidden) {
|
start_line = mono_metadata_decode_value (ptr, &ptr);
|
start_col = mono_metadata_decode_value (ptr, &ptr);
|
} else {
|
adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
|
adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
|
start_line += adv_line;
|
start_col += adv_col;
|
}
|
first_non_hidden = FALSE;
|
}
|
|
location = g_new0 (MonoDebugSourceLocation, 1);
|
location->source_file = docname;
|
location->row = start_line;
|
location->il_offset = iloffset;
|
|
return location;
|
}
|
|
void
|
mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points)
|
{
|
MonoPPDBFile *ppdb = minfo->handle->ppdb;
|
MonoImage *image = ppdb->image;
|
MonoMethod *method = minfo->method;
|
MonoTableInfo *tables = image->tables;
|
guint32 cols [MONO_METHODBODY_SIZE];
|
const char *ptr;
|
const char *end;
|
MonoDebugSourceInfo *docinfo;
|
int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
|
gboolean first = TRUE, first_non_hidden = TRUE;
|
GArray *sps;
|
MonoSymSeqPoint sp;
|
GPtrArray *sfiles = NULL;
|
GPtrArray *sindexes = NULL;
|
|
if (source_file)
|
*source_file = NULL;
|
if (source_file_list)
|
*source_file_list = NULL;
|
if (source_files)
|
*source_files = NULL;
|
if (seq_points)
|
*seq_points = NULL;
|
if (n_seq_points)
|
*n_seq_points = 0;
|
|
if (source_file_list)
|
*source_file_list = sfiles = g_ptr_array_new ();
|
if (source_files)
|
sindexes = g_ptr_array_new ();
|
|
if (!method->token)
|
return;
|
|
method_idx = mono_metadata_token_index (method->token);
|
|
mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], method_idx-1, cols, MONO_METHODBODY_SIZE);
|
|
docidx = cols [MONO_METHODBODY_DOCUMENT];
|
|
if (!cols [MONO_METHODBODY_SEQ_POINTS])
|
return;
|
|
ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
|
size = mono_metadata_decode_blob_size (ptr, &ptr);
|
end = ptr + size;
|
|
sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint));
|
|
/* Header */
|
/* LocalSignature */
|
mono_metadata_decode_value (ptr, &ptr);
|
if (docidx == 0)
|
docidx = mono_metadata_decode_value (ptr, &ptr);
|
docinfo = get_docinfo (ppdb, image, docidx);
|
|
if (sfiles)
|
g_ptr_array_add (sfiles, docinfo);
|
|
if (source_file)
|
*source_file = g_strdup (docinfo->source_file);
|
|
iloffset = 0;
|
start_line = 0;
|
start_col = 0;
|
while (ptr < end) {
|
delta_il = mono_metadata_decode_value (ptr, &ptr);
|
if (!first && delta_il == 0) {
|
/* subsequent-document-record */
|
docidx = mono_metadata_decode_value (ptr, &ptr);
|
docinfo = get_docinfo (ppdb, image, docidx);
|
if (sfiles)
|
g_ptr_array_add (sfiles, docinfo);
|
continue;
|
}
|
iloffset += delta_il;
|
first = FALSE;
|
|
delta_lines = mono_metadata_decode_value (ptr, &ptr);
|
if (delta_lines == 0)
|
delta_cols = mono_metadata_decode_value (ptr, &ptr);
|
else
|
delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
|
|
if (delta_lines == 0 && delta_cols == 0) {
|
/* Hidden sequence point */
|
continue;
|
}
|
|
if (first_non_hidden) {
|
start_line = mono_metadata_decode_value (ptr, &ptr);
|
start_col = mono_metadata_decode_value (ptr, &ptr);
|
} else {
|
adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
|
adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
|
start_line += adv_line;
|
start_col += adv_col;
|
}
|
first_non_hidden = FALSE;
|
|
memset (&sp, 0, sizeof (sp));
|
sp.il_offset = iloffset;
|
sp.line = start_line;
|
sp.column = start_col;
|
sp.end_line = start_line + delta_lines;
|
sp.end_column = start_col + delta_cols;
|
|
g_array_append_val (sps, sp);
|
if (source_files)
|
g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1));
|
}
|
|
if (n_seq_points) {
|
*n_seq_points = sps->len;
|
g_assert (seq_points);
|
*seq_points = g_new (MonoSymSeqPoint, sps->len);
|
memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint));
|
}
|
|
if (source_files) {
|
*source_files = g_new (int, sps->len);
|
for (i = 0; i < sps->len; ++i)
|
(*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i));
|
g_ptr_array_free (sindexes, TRUE);
|
}
|
|
g_array_free (sps, TRUE);
|
}
|
|
MonoDebugLocalsInfo*
|
mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo)
|
{
|
MonoPPDBFile *ppdb = minfo->handle->ppdb;
|
MonoImage *image = ppdb->image;
|
MonoTableInfo *tables = image->tables;
|
MonoMethod *method = minfo->method;
|
guint32 cols [MONO_LOCALSCOPE_SIZE];
|
guint32 locals_cols [MONO_LOCALVARIABLE_SIZE];
|
int i, lindex, sindex, method_idx, start_scope_idx, scope_idx, locals_idx, locals_end_idx, nscopes;
|
MonoDebugLocalsInfo *res;
|
MonoMethodSignature *sig;
|
|
if (!method->token)
|
return NULL;
|
|
sig = mono_method_signature (method);
|
if (!sig)
|
return NULL;
|
|
method_idx = mono_metadata_token_index (method->token);
|
|
start_scope_idx = mono_metadata_localscope_from_methoddef (image, method_idx);
|
|
if (!start_scope_idx)
|
return NULL;
|
|
/* Compute number of locals and scopes */
|
scope_idx = start_scope_idx;
|
mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
|
locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
|
|
// https://github.com/dotnet/roslyn/blob/2ae8d5fed96ab3f1164031f9b4ac827f53289159/docs/specs/PortablePdb-Metadata.md#LocalScopeTable
|
//
|
// The variableList attribute in the pdb metadata table is a contiguous array that starts at a
|
// given offset (locals_idx) above and
|
//
|
// """
|
// continues to the smaller of:
|
//
|
// the last row of the LocalVariable table
|
// the next run of LocalVariables, found by inspecting the VariableList of the next row in this LocalScope table.
|
// """
|
// this endpoint becomes locals_end_idx below
|
|
// March to the last scope that is in this method
|
while (scope_idx <= tables [MONO_TABLE_LOCALSCOPE].rows) {
|
mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
|
if (cols [MONO_LOCALSCOPE_METHOD] != method_idx)
|
break;
|
scope_idx ++;
|
}
|
// The number of scopes is the difference in the indices
|
// for the first and last scopes
|
nscopes = scope_idx - start_scope_idx;
|
|
// Ends with "the last row of the LocalVariable table"
|
// this happens if the above loop marched one past the end
|
// of the rows
|
if (scope_idx > tables [MONO_TABLE_LOCALSCOPE].rows) {
|
locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
|
} else {
|
// Ends with "the next run of LocalVariables,
|
// found by inspecting the VariableList of the next row in this LocalScope table."
|
locals_end_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
|
}
|
|
res = g_new0 (MonoDebugLocalsInfo, 1);
|
res->num_blocks = nscopes;
|
res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks);
|
res->num_locals = locals_end_idx - locals_idx;
|
res->locals = g_new0 (MonoDebugLocalVar, res->num_locals);
|
|
lindex = 0;
|
for (sindex = 0; sindex < nscopes; ++sindex) {
|
scope_idx = start_scope_idx + sindex;
|
mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
|
|
locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
|
if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) {
|
locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
|
} else {
|
locals_end_idx = mono_metadata_decode_row_col (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1 + 1, MONO_LOCALSCOPE_VARIABLELIST);
|
}
|
|
res->code_blocks [sindex].start_offset = cols [MONO_LOCALSCOPE_STARTOFFSET];
|
res->code_blocks [sindex].end_offset = cols [MONO_LOCALSCOPE_STARTOFFSET] + cols [MONO_LOCALSCOPE_LENGTH];
|
|
//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);
|
|
for (i = locals_idx; i < locals_end_idx; ++i) {
|
mono_metadata_decode_row (&tables [MONO_TABLE_LOCALVARIABLE], i - 1, locals_cols, MONO_LOCALVARIABLE_SIZE);
|
|
res->locals [lindex].name = g_strdup (mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]));
|
res->locals [lindex].index = locals_cols [MONO_LOCALVARIABLE_INDEX];
|
res->locals [lindex].block = &res->code_blocks [sindex];
|
lindex ++;
|
|
//printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]);
|
}
|
}
|
|
return res;
|
}
|
|
/*
|
* We use this to pass context information to the row locator
|
*/
|
typedef struct {
|
int idx; /* The index that we are trying to locate */
|
int col_idx; /* The index in the row where idx may be stored */
|
MonoTableInfo *t; /* pointer to the table */
|
guint32 result;
|
} locator_t;
|
|
static int
|
table_locator (const void *a, const void *b)
|
{
|
locator_t *loc = (locator_t *)a;
|
const char *bb = (const char *)b;
|
guint32 table_index = (bb - loc->t->base) / loc->t->row_size;
|
guint32 col;
|
|
col = mono_metadata_decode_row_col(loc->t, table_index, loc->col_idx);
|
|
if (loc->idx == col) {
|
loc->result = table_index;
|
return 0;
|
}
|
if (loc->idx < col)
|
return -1;
|
else
|
return 1;
|
}
|
|
static gboolean
|
compare_guid (guint8* guid1, guint8* guid2) {
|
for (int i = 0; i < 16; i++) {
|
if (guid1 [i] != guid2 [i])
|
return FALSE;
|
}
|
return TRUE;
|
}
|
|
// for parent_type see HasCustomDebugInformation table at
|
// https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md
|
static const char*
|
lookup_custom_debug_information (MonoImage* image, guint32 token, uint8_t parent_type, guint8* guid)
|
{
|
MonoTableInfo *tables = image->tables;
|
MonoTableInfo *table = &tables[MONO_TABLE_CUSTOMDEBUGINFORMATION];
|
locator_t loc;
|
|
if (!table->base)
|
return 0;
|
|
loc.idx = (mono_metadata_token_index (token) << 5) | parent_type;
|
loc.col_idx = MONO_CUSTOMDEBUGINFORMATION_PARENT;
|
loc.t = table;
|
|
if (!mono_binary_search (&loc, table->base, table->rows, table->row_size, table_locator))
|
return NULL;
|
// Great we found one of possibly many CustomDebugInformations of this entity they are distinguished by KIND guid
|
// 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)
|
if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_KIND))))
|
return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_VALUE));
|
|
// Move forward from binary found index, until parent token differs
|
for (int i = loc.result + 1; i < table->rows; i++)
|
{
|
if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx)
|
break;
|
if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND))))
|
return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE));
|
}
|
|
// Move backward from binary found index, until parent token differs
|
for (int i = loc.result - 1; i >= 0; i--) {
|
if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx)
|
break;
|
if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND))))
|
return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE));
|
}
|
return NULL;
|
}
|
|
MonoDebugMethodAsyncInfo*
|
mono_ppdb_lookup_method_async_debug_info (MonoDebugMethodInfo *minfo)
|
{
|
MonoMethod *method = minfo->method;
|
MonoPPDBFile *ppdb = minfo->handle->ppdb;
|
MonoImage *image = ppdb->image;
|
|
// Guid is taken from Roslyn source code:
|
// https://github.com/dotnet/roslyn/blob/1ad4b58/src/Dependencies/CodeAnalysis.Metadata/PortableCustomDebugInfoKinds.cs#L9
|
guint8 async_method_stepping_information_guid [16] = { 0xC5, 0x2A, 0xFD, 0x54, 0x25, 0xE9, 0x1A, 0x40, 0x9C, 0x2A, 0xF9, 0x4F, 0x17, 0x10, 0x72, 0xF8 };
|
char const *blob = lookup_custom_debug_information (image, method->token, 0, async_method_stepping_information_guid);
|
if (!blob)
|
return NULL;
|
int blob_len = mono_metadata_decode_blob_size (blob, &blob);
|
MonoDebugMethodAsyncInfo* res = g_new0 (MonoDebugMethodAsyncInfo, 1);
|
char const *pointer = blob;
|
|
// Format of this blob is taken from Roslyn source code:
|
// https://github.com/dotnet/roslyn/blob/1ad4b58/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs#L566
|
|
pointer += 4;//catch_handler_offset
|
while (pointer - blob < blob_len) {
|
res->num_awaits++;
|
pointer += 8;//yield_offsets+resume_offsets
|
mono_metadata_decode_value (pointer, &pointer);//move_next_method_token
|
}
|
g_assert(pointer - blob == blob_len); //Check that we used all blob data
|
pointer = blob; //reset pointer after we figured num_awaits
|
|
res->yield_offsets = g_new (uint32_t, res->num_awaits);
|
res->resume_offsets = g_new (uint32_t, res->num_awaits);
|
res->move_next_method_token = g_new (uint32_t, res->num_awaits);
|
|
res->catch_handler_offset = read32 (pointer); pointer += 4;
|
for (int i = 0; i < res->num_awaits; i++) {
|
res->yield_offsets [i] = read32 (pointer); pointer += 4;
|
res->resume_offsets [i] = read32 (pointer); pointer += 4;
|
res->move_next_method_token [i] = mono_metadata_decode_value (pointer, &pointer);
|
}
|
return res;
|
}
|