/**
|
* \file
|
* Sequence Points functions
|
*
|
* Authors:
|
* Marcos Henrich (marcos.henrich@xamarin.com)
|
*
|
* Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
|
*/
|
|
#include "seq-points-data.h"
|
|
typedef struct {
|
guint8 *data;
|
int len;
|
/* When has_debug_data is set to false only il and native deltas are saved */
|
gboolean has_debug_data;
|
/* When alloc_data is set to true data allocation/deallocation is managed by this structure */
|
gboolean alloc_data;
|
} SeqPointInfoInflated;
|
|
static int
|
encode_var_int (guint8 *buf, guint8 **out_buf, int val)
|
{
|
guint8 size = 0;
|
|
do {
|
guint8 byte = val & 0x7f;
|
g_assert (size < 4 && "value has more than 28 bits");
|
val >>= 7;
|
if(val) byte |= 0x80;
|
*(buf++) = byte;
|
size++;
|
} while (val);
|
|
if (out_buf)
|
*out_buf = buf;
|
|
return size;
|
}
|
|
static int
|
decode_var_int (guint8* buf, guint8 **out_buf)
|
{
|
guint8* p = buf;
|
|
int low;
|
int b;
|
b = *(p++); low = (b & 0x7f) ; if(!(b & 0x80)) goto done;
|
b = *(p++); low |= (b & 0x7f) << 7; if(!(b & 0x80)) goto done;
|
b = *(p++); low |= (b & 0x7f) << 14; if(!(b & 0x80)) goto done;
|
b = *(p++); low |= (b & 0x7f) << 21; if(!(b & 0x80)) goto done;
|
|
g_assert (FALSE && "value has more than 28 bits");
|
|
done:
|
|
if (out_buf)
|
*out_buf = p;
|
|
return low;
|
}
|
|
static guint32
|
encode_zig_zag (int val)
|
{
|
return (val << 1) ^ (val >> 31);
|
}
|
|
static int
|
decode_zig_zag (guint32 val)
|
{
|
int n = val;
|
return (n >> 1) ^ (-(n & 1));
|
}
|
|
static SeqPointInfoInflated
|
seq_point_info_inflate (MonoSeqPointInfo *info)
|
{
|
SeqPointInfoInflated info_inflated;
|
guint8 *ptr = (guint8*) info;
|
int value;
|
|
value = decode_var_int (ptr, &ptr);
|
|
info_inflated.len = value >> 2;
|
info_inflated.has_debug_data = (value & 1) != 0;
|
info_inflated.alloc_data = (value & 2) != 0;
|
|
if (info_inflated.alloc_data)
|
info_inflated.data = ptr;
|
else
|
memcpy (&info_inflated.data, ptr, sizeof (guint8*));
|
|
return info_inflated;
|
}
|
|
MonoSeqPointInfo*
|
mono_seq_point_info_new (int len, gboolean alloc_data, guint8 *data, gboolean has_debug_data, int *out_size)
|
{
|
MonoSeqPointInfo *info;
|
guint8 *info_ptr;
|
guint8 buffer[4];
|
int buffer_len;
|
int value;
|
int data_size;
|
|
value = len << 2;
|
if (has_debug_data)
|
value |= 1;
|
if (alloc_data)
|
value |= 2;
|
|
buffer_len = encode_var_int (buffer, NULL, value);
|
|
*out_size = data_size = buffer_len + (alloc_data? len : sizeof (guint8*));
|
info_ptr = g_new0 (guint8, data_size);
|
info = (MonoSeqPointInfo*) info_ptr;
|
|
memcpy (info_ptr, buffer, buffer_len);
|
info_ptr += buffer_len;
|
|
if (alloc_data)
|
memcpy (info_ptr, data, len);
|
else
|
memcpy (info_ptr, &data, sizeof (guint8*));
|
|
return info;
|
}
|
|
void
|
mono_seq_point_info_free (gpointer ptr)
|
{
|
MonoSeqPointInfo* info = (MonoSeqPointInfo*) ptr;
|
g_free (info);
|
}
|
|
static int
|
seq_point_read (SeqPoint* seq_point, guint8* ptr, guint8* buffer_ptr, gboolean has_debug_data)
|
{
|
int value, i;
|
guint8* ptr0 = ptr;
|
|
value = decode_var_int (ptr, &ptr);
|
seq_point->il_offset += decode_zig_zag (value);
|
|
value = decode_var_int (ptr, &ptr);
|
seq_point->native_offset += decode_zig_zag (value);
|
|
if (has_debug_data) {
|
value = decode_var_int (ptr, &ptr);
|
seq_point->flags = value;
|
|
if (seq_point->flags & MONO_SEQ_POINT_FLAG_EXIT_IL)
|
seq_point->il_offset = METHOD_EXIT_IL_OFFSET;
|
|
value = decode_var_int (ptr, &ptr);
|
seq_point->next_len = value;
|
|
if (seq_point->next_len) {
|
// store next offset and skip it
|
seq_point->next_offset = ptr - buffer_ptr;
|
for (i = 0; i < seq_point->next_len; ++i)
|
decode_var_int (ptr, &ptr);
|
}
|
}
|
|
return ptr - ptr0;
|
}
|
|
gboolean
|
mono_seq_point_info_add_seq_point (GByteArray* array, SeqPoint *sp, SeqPoint *last_seq_point, GSList *next, gboolean has_debug_data)
|
{
|
int il_delta, native_delta;
|
GSList *l;
|
guint8 buffer[4];
|
guint8 len;
|
int flags;
|
|
if (!has_debug_data &&
|
(sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET))
|
return FALSE;
|
|
il_delta = sp->il_offset - last_seq_point->il_offset;
|
native_delta = sp->native_offset - last_seq_point->native_offset;
|
|
flags = sp->flags;
|
|
if (has_debug_data && sp->il_offset == METHOD_EXIT_IL_OFFSET) {
|
il_delta = 0;
|
flags |= MONO_SEQ_POINT_FLAG_EXIT_IL;
|
}
|
|
len = encode_var_int (buffer, NULL, encode_zig_zag (il_delta));
|
g_byte_array_append (array, buffer, len);
|
|
len = encode_var_int (buffer, NULL, encode_zig_zag (native_delta));
|
g_byte_array_append (array, buffer, len);
|
|
if (has_debug_data) {
|
sp->next_offset = array->len;
|
sp->next_len = g_slist_length (next);
|
|
len = encode_var_int (buffer, NULL, flags);
|
g_byte_array_append (array, buffer, len);
|
|
len = encode_var_int (buffer, NULL, sp->next_len);
|
g_byte_array_append (array, buffer, len);
|
|
for (l = next; l; l = l->next) {
|
int next_index = GPOINTER_TO_UINT (l->data);
|
guint8 buffer[4];
|
int len = encode_var_int (buffer, NULL, next_index);
|
g_byte_array_append (array, buffer, len);
|
}
|
}
|
|
return TRUE;
|
}
|
|
gboolean
|
mono_seq_point_find_next_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
|
{
|
SeqPointIterator it;
|
mono_seq_point_iterator_init (&it, info);
|
while (mono_seq_point_iterator_next (&it)) {
|
if (it.seq_point.native_offset >= native_offset) {
|
memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
|
return TRUE;
|
}
|
}
|
|
return FALSE;
|
}
|
|
gboolean
|
mono_seq_point_find_prev_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
|
{
|
SeqPoint prev_seq_point;
|
gboolean is_first = TRUE;
|
SeqPointIterator it;
|
mono_seq_point_iterator_init (&it, info);
|
while (mono_seq_point_iterator_next (&it) && it.seq_point.native_offset <= native_offset) {
|
memcpy (&prev_seq_point, &it.seq_point, sizeof (SeqPoint));
|
is_first = FALSE;
|
}
|
|
if (!is_first && prev_seq_point.native_offset <= native_offset) {
|
memcpy (seq_point, &prev_seq_point, sizeof (SeqPoint));
|
return TRUE;
|
}
|
|
return FALSE;
|
}
|
|
gboolean
|
mono_seq_point_find_by_il_offset (MonoSeqPointInfo* info, int il_offset, SeqPoint* seq_point)
|
{
|
SeqPointIterator it;
|
mono_seq_point_iterator_init (&it, info);
|
while (mono_seq_point_iterator_next (&it)) {
|
if (it.seq_point.il_offset == il_offset) {
|
memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
|
return TRUE;
|
}
|
}
|
|
return FALSE;
|
}
|
|
void
|
mono_seq_point_init_next (MonoSeqPointInfo* info, SeqPoint sp, SeqPoint* next)
|
{
|
int i;
|
guint8* ptr;
|
SeqPointIterator it;
|
GArray* seq_points = g_array_new (FALSE, TRUE, sizeof (SeqPoint));
|
SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
|
|
g_assert (info_inflated.has_debug_data);
|
|
mono_seq_point_iterator_init (&it, info);
|
while (mono_seq_point_iterator_next (&it))
|
g_array_append_vals (seq_points, &it.seq_point, 1);
|
|
ptr = info_inflated.data + sp.next_offset;
|
for (i = 0; i < sp.next_len; i++) {
|
int next_index;
|
next_index = decode_var_int (ptr, &ptr);
|
g_assert (next_index < seq_points->len);
|
memcpy (&next[i], seq_points->data + next_index * sizeof (SeqPoint), sizeof (SeqPoint));
|
}
|
|
g_array_free (seq_points, TRUE);
|
}
|
|
gboolean
|
mono_seq_point_iterator_next (SeqPointIterator* it)
|
{
|
if (it->ptr >= it->end)
|
return FALSE;
|
|
it->ptr += seq_point_read (&it->seq_point, it->ptr, it->begin, it->has_debug_data);
|
|
return TRUE;
|
}
|
|
void
|
mono_seq_point_iterator_init (SeqPointIterator* it, MonoSeqPointInfo* info)
|
{
|
SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
|
it->ptr = info_inflated.data;
|
it->begin = info_inflated.data;
|
it->end = it->begin + info_inflated.len;
|
it->has_debug_data = info_inflated.has_debug_data;
|
memset(&it->seq_point, 0, sizeof(SeqPoint));
|
}
|
|
int
|
mono_seq_point_info_write (MonoSeqPointInfo* info, guint8* buffer)
|
{
|
guint8* buffer0 = buffer;
|
SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
|
|
encode_var_int (buffer, &buffer, info_inflated.has_debug_data);
|
|
//Write sequence points
|
encode_var_int (buffer, &buffer, info_inflated.len);
|
memcpy (buffer, info_inflated.data, info_inflated.len);
|
buffer += info_inflated.len;
|
|
return buffer - buffer0;
|
}
|
|
int
|
mono_seq_point_info_read (MonoSeqPointInfo** info, guint8* buffer, gboolean copy)
|
{
|
guint8* buffer0 = buffer;
|
int size, info_size;
|
gboolean has_debug_data;
|
|
has_debug_data = decode_var_int (buffer, &buffer);
|
|
size = decode_var_int (buffer, &buffer);
|
(*info) = mono_seq_point_info_new (size, copy, buffer, has_debug_data, &info_size);
|
buffer += size;
|
|
return buffer - buffer0;
|
}
|
|
/*
|
* Returns the maximum size of mono_seq_point_info_write.
|
*/
|
int
|
mono_seq_point_info_get_write_size (MonoSeqPointInfo* info)
|
{
|
SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
|
|
//4 is the maximum size required to store the size of the data.
|
//1 is the byte used to store has_debug_data.
|
int size = 4 + 1 + info_inflated.len;
|
|
return size;
|
}
|
|
/*
|
* SeqPointData struct and functions
|
* This is used to store/load/use sequence point from a file
|
*/
|
|
void
|
mono_seq_point_data_init (SeqPointData *data, int entry_capacity)
|
{
|
data->entry_count = 0;
|
data->entry_capacity = entry_capacity;
|
data->entries = (SeqPointDataEntry *)g_malloc (sizeof (SeqPointDataEntry) * entry_capacity);
|
}
|
|
void
|
mono_seq_point_data_free (SeqPointData *data)
|
{
|
int i;
|
for (i=0; i<data->entry_count; i++) {
|
if (data->entries [i].free_seq_points)
|
g_free (data->entries [i].seq_points);
|
}
|
g_free (data->entries);
|
}
|
|
gboolean
|
mono_seq_point_data_read (SeqPointData *data, char *path)
|
{
|
guint8 *buffer, *buffer_orig;
|
int entry_count, i;
|
long fsize;
|
FILE *f;
|
|
f = fopen (path, "r");
|
if (!f)
|
return FALSE;
|
|
fseek(f, 0, SEEK_END);
|
fsize = ftell(f);
|
fseek(f, 0, SEEK_SET);
|
|
buffer_orig = buffer = (guint8 *)g_malloc (fsize + 1);
|
fread(buffer_orig, fsize, 1, f);
|
fclose(f);
|
|
entry_count = decode_var_int (buffer, &buffer);
|
mono_seq_point_data_init (data, entry_count);
|
data->entry_count = entry_count;
|
|
for (i=0; i<entry_count; i++) {
|
data->entries [i].method_token = decode_var_int (buffer, &buffer);
|
data->entries [i].method_index = decode_var_int (buffer, &buffer);
|
buffer += mono_seq_point_info_read (&data->entries [i].seq_points, buffer, TRUE);
|
data->entries [i].free_seq_points = TRUE;
|
}
|
|
g_free (buffer_orig);
|
return TRUE;
|
}
|
|
gboolean
|
mono_seq_point_data_write (SeqPointData *data, char *path)
|
{
|
guint8 *buffer, *buffer_orig;
|
FILE *f;
|
int i, size = 0;
|
|
f = fopen (path, "w+");
|
if (!f)
|
return FALSE;
|
|
for (i=0; i<data->entry_count; i++) {
|
size += mono_seq_point_info_get_write_size (data->entries [i].seq_points);
|
}
|
// Add size of entry_count and native_base_offsets
|
size += 4 + data->entry_count * 4;
|
|
buffer_orig = buffer = (guint8 *)g_malloc (size);
|
|
encode_var_int (buffer, &buffer, data->entry_count);
|
|
for (i=0; i<data->entry_count; i++) {
|
encode_var_int (buffer, &buffer, data->entries [i].method_token);
|
encode_var_int (buffer, &buffer, data->entries [i].method_index);
|
buffer += mono_seq_point_info_write (data->entries [i].seq_points, buffer);
|
}
|
|
fwrite (buffer_orig, 1, buffer - buffer_orig, f);
|
g_free (buffer_orig);
|
fclose (f);
|
|
return TRUE;
|
}
|
|
void
|
mono_seq_point_data_add (SeqPointData *data, guint32 method_token, guint32 method_index, MonoSeqPointInfo* info)
|
{
|
int i;
|
|
g_assert (data->entry_count < data->entry_capacity);
|
i = data->entry_count++;
|
data->entries [i].seq_points = info;
|
data->entries [i].method_token = method_token;
|
data->entries [i].method_index = method_index;
|
data->entries [i].free_seq_points = FALSE;
|
}
|
|
gboolean
|
mono_seq_point_data_get (SeqPointData *data, guint32 method_token, guint32 method_index, MonoSeqPointInfo** info)
|
{
|
int i;
|
|
for (i=0; i<data->entry_count; i++) {
|
if (data->entries [i].method_token == method_token && (method_index == 0xffffff || data->entries [i].method_index == method_index)) {
|
(*info) = data->entries [i].seq_points;
|
return TRUE;
|
}
|
}
|
return FALSE;
|
}
|
|
gboolean
|
mono_seq_point_data_get_il_offset (char *path, guint32 method_token, guint32 method_index, guint32 native_offset, guint32 *il_offset)
|
{
|
SeqPointData sp_data;
|
MonoSeqPointInfo *seq_points;
|
SeqPoint sp;
|
|
if (!mono_seq_point_data_read (&sp_data, path))
|
return FALSE;
|
|
if (!mono_seq_point_data_get (&sp_data, method_token, method_index, &seq_points))
|
return FALSE;
|
|
if (!mono_seq_point_find_prev_by_native_offset (seq_points, native_offset, &sp))
|
return FALSE;
|
|
*il_offset = sp.il_offset;
|
|
return TRUE;
|
}
|