1 /**
2  * \file
3  * Sequence Points functions
4  *
5  * Authors:
6  *   Marcos Henrich (marcos.henrich@xamarin.com)
7  *
8  * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
9  */
10 
11 #include "seq-points-data.h"
12 
13 typedef struct {
14 	guint8 *data;
15 	int len;
16 	/* When has_debug_data is set to false only il and native deltas are saved */
17 	gboolean has_debug_data;
18 	/* When alloc_data is set to true data allocation/deallocation is managed by this structure */
19 	gboolean alloc_data;
20 } SeqPointInfoInflated;
21 
22 static int
encode_var_int(guint8 * buf,guint8 ** out_buf,int val)23 encode_var_int (guint8 *buf, guint8 **out_buf, int val)
24 {
25 	guint8 size = 0;
26 
27 	do {
28 		guint8 byte = val & 0x7f;
29 		g_assert (size < 4 && "value has more than 28 bits");
30 		val >>= 7;
31 		if(val) byte |= 0x80;
32 		*(buf++) = byte;
33 		size++;
34 	} while (val);
35 
36 	if (out_buf)
37 		*out_buf = buf;
38 
39 	return size;
40 }
41 
42 static int
decode_var_int(guint8 * buf,guint8 ** out_buf)43 decode_var_int (guint8* buf, guint8 **out_buf)
44 {
45 	guint8* p = buf;
46 
47 	int low;
48 	int b;
49 	b = *(p++); low   = (b & 0x7f)      ; if(!(b & 0x80)) goto done;
50 	b = *(p++); low  |= (b & 0x7f) <<  7; if(!(b & 0x80)) goto done;
51 	b = *(p++); low  |= (b & 0x7f) << 14; if(!(b & 0x80)) goto done;
52 	b = *(p++); low  |= (b & 0x7f) << 21; if(!(b & 0x80)) goto done;
53 
54 	g_assert (FALSE && "value has more than 28 bits");
55 
56 done:
57 
58 	if (out_buf)
59 		*out_buf = p;
60 
61 	return low;
62 }
63 
64 static guint32
encode_zig_zag(int val)65 encode_zig_zag (int val)
66 {
67 	return (val << 1) ^ (val >> 31);
68 }
69 
70 static int
decode_zig_zag(guint32 val)71 decode_zig_zag (guint32 val)
72 {
73 	int n = val;
74 	return (n >> 1) ^ (-(n & 1));
75 }
76 
77 static SeqPointInfoInflated
seq_point_info_inflate(MonoSeqPointInfo * info)78 seq_point_info_inflate (MonoSeqPointInfo *info)
79 {
80 	SeqPointInfoInflated info_inflated;
81 	guint8 *ptr = (guint8*) info;
82 	int value;
83 
84 	value = decode_var_int (ptr, &ptr);
85 
86 	info_inflated.len = value >> 2;
87 	info_inflated.has_debug_data = (value & 1) != 0;
88 	info_inflated.alloc_data = (value & 2) != 0;
89 
90 	if (info_inflated.alloc_data)
91 		info_inflated.data = ptr;
92 	else
93 		memcpy (&info_inflated.data, ptr, sizeof (guint8*));
94 
95 	return info_inflated;
96 }
97 
98 MonoSeqPointInfo*
mono_seq_point_info_new(int len,gboolean alloc_data,guint8 * data,gboolean has_debug_data,int * out_size)99 mono_seq_point_info_new (int len, gboolean alloc_data, guint8 *data, gboolean has_debug_data, int *out_size)
100 {
101 	MonoSeqPointInfo *info;
102 	guint8 *info_ptr;
103 	guint8 buffer[4];
104 	int buffer_len;
105 	int value;
106 	int data_size;
107 
108 	value = len << 2;
109 	if (has_debug_data)
110 		value |= 1;
111 	if (alloc_data)
112 		value |= 2;
113 
114 	buffer_len = encode_var_int (buffer, NULL, value);
115 
116 	*out_size = data_size = buffer_len + (alloc_data? len : sizeof (guint8*));
117 	info_ptr = g_new0 (guint8, data_size);
118 	info = (MonoSeqPointInfo*) info_ptr;
119 
120 	memcpy (info_ptr, buffer, buffer_len);
121 	info_ptr += buffer_len;
122 
123 	if (alloc_data)
124 		memcpy (info_ptr, data, len);
125 	else
126 		memcpy (info_ptr, &data, sizeof (guint8*));
127 
128 	return info;
129 }
130 
131 void
mono_seq_point_info_free(gpointer ptr)132 mono_seq_point_info_free (gpointer ptr)
133 {
134 	MonoSeqPointInfo* info = (MonoSeqPointInfo*) ptr;
135 	g_free (info);
136 }
137 
138 static int
seq_point_read(SeqPoint * seq_point,guint8 * ptr,guint8 * buffer_ptr,gboolean has_debug_data)139 seq_point_read (SeqPoint* seq_point, guint8* ptr, guint8* buffer_ptr, gboolean has_debug_data)
140 {
141 	int value, i;
142 	guint8* ptr0 = ptr;
143 
144 	value = decode_var_int (ptr, &ptr);
145 	seq_point->il_offset += decode_zig_zag (value);
146 
147 	value = decode_var_int (ptr, &ptr);
148 	seq_point->native_offset += decode_zig_zag (value);
149 
150 	if (has_debug_data) {
151 		value = decode_var_int (ptr, &ptr);
152 		seq_point->flags = value;
153 
154 		if (seq_point->flags & MONO_SEQ_POINT_FLAG_EXIT_IL)
155 			seq_point->il_offset = METHOD_EXIT_IL_OFFSET;
156 
157 		value = decode_var_int (ptr, &ptr);
158 		seq_point->next_len = value;
159 
160 		if (seq_point->next_len) {
161 			// store next offset and skip it
162 			seq_point->next_offset = ptr - buffer_ptr;
163 			for (i = 0; i < seq_point->next_len; ++i)
164 				decode_var_int (ptr, &ptr);
165 		}
166 	}
167 
168 	return ptr - ptr0;
169 }
170 
171 gboolean
mono_seq_point_info_add_seq_point(GByteArray * array,SeqPoint * sp,SeqPoint * last_seq_point,GSList * next,gboolean has_debug_data)172 mono_seq_point_info_add_seq_point (GByteArray* array, SeqPoint *sp, SeqPoint *last_seq_point, GSList *next, gboolean has_debug_data)
173 {
174 	int il_delta, native_delta;
175 	GSList *l;
176 	guint8 buffer[4];
177 	guint8 len;
178 	int flags;
179 
180 	if (!has_debug_data &&
181 		(sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET))
182 		return FALSE;
183 
184 	il_delta = sp->il_offset - last_seq_point->il_offset;
185 	native_delta = sp->native_offset - last_seq_point->native_offset;
186 
187 	flags = sp->flags;
188 
189 	if (has_debug_data && sp->il_offset == METHOD_EXIT_IL_OFFSET) {
190 		il_delta = 0;
191 		flags |= MONO_SEQ_POINT_FLAG_EXIT_IL;
192 	}
193 
194 	len = encode_var_int (buffer, NULL, encode_zig_zag (il_delta));
195 	g_byte_array_append (array, buffer, len);
196 
197 	len = encode_var_int (buffer, NULL, encode_zig_zag (native_delta));
198 	g_byte_array_append (array, buffer, len);
199 
200 	if (has_debug_data) {
201 		sp->next_offset = array->len;
202 		sp->next_len = g_slist_length (next);
203 
204 		len = encode_var_int (buffer, NULL, flags);
205 		g_byte_array_append (array, buffer, len);
206 
207 		len = encode_var_int (buffer, NULL, sp->next_len);
208 		g_byte_array_append (array, buffer, len);
209 
210 		for (l = next; l; l = l->next) {
211 			int next_index = GPOINTER_TO_UINT (l->data);
212 			guint8 buffer[4];
213 			int len = encode_var_int (buffer, NULL, next_index);
214 			g_byte_array_append (array, buffer, len);
215 		}
216 	}
217 
218 	return TRUE;
219 }
220 
221 gboolean
mono_seq_point_find_next_by_native_offset(MonoSeqPointInfo * info,int native_offset,SeqPoint * seq_point)222 mono_seq_point_find_next_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
223 {
224 	SeqPointIterator it;
225 	mono_seq_point_iterator_init (&it, info);
226 	while (mono_seq_point_iterator_next (&it)) {
227 		if (it.seq_point.native_offset >= native_offset) {
228 			memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
229 			return TRUE;
230 		}
231 	}
232 
233 	return FALSE;
234 }
235 
236 gboolean
mono_seq_point_find_prev_by_native_offset(MonoSeqPointInfo * info,int native_offset,SeqPoint * seq_point)237 mono_seq_point_find_prev_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
238 {
239 	SeqPoint prev_seq_point;
240 	gboolean  is_first = TRUE;
241 	SeqPointIterator it;
242 	mono_seq_point_iterator_init (&it, info);
243 	while (mono_seq_point_iterator_next (&it) && it.seq_point.native_offset <= native_offset) {
244 		memcpy (&prev_seq_point, &it.seq_point, sizeof (SeqPoint));
245 		is_first = FALSE;
246 	}
247 
248 	if (!is_first && prev_seq_point.native_offset <= native_offset) {
249 		memcpy (seq_point, &prev_seq_point, sizeof (SeqPoint));
250 		return TRUE;
251 	}
252 
253 	return FALSE;
254 }
255 
256 gboolean
mono_seq_point_find_by_il_offset(MonoSeqPointInfo * info,int il_offset,SeqPoint * seq_point)257 mono_seq_point_find_by_il_offset (MonoSeqPointInfo* info, int il_offset, SeqPoint* seq_point)
258 {
259 	SeqPointIterator it;
260 	mono_seq_point_iterator_init (&it, info);
261 	while (mono_seq_point_iterator_next (&it)) {
262 		if (it.seq_point.il_offset == il_offset) {
263 			memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
264 			return TRUE;
265 		}
266 	}
267 
268 	return FALSE;
269 }
270 
271 void
mono_seq_point_init_next(MonoSeqPointInfo * info,SeqPoint sp,SeqPoint * next)272 mono_seq_point_init_next (MonoSeqPointInfo* info, SeqPoint sp, SeqPoint* next)
273 {
274 	int i;
275 	guint8* ptr;
276 	SeqPointIterator it;
277 	GArray* seq_points = g_array_new (FALSE, TRUE, sizeof (SeqPoint));
278 	SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
279 
280 	g_assert (info_inflated.has_debug_data);
281 
282 	mono_seq_point_iterator_init (&it, info);
283 	while (mono_seq_point_iterator_next (&it))
284 		g_array_append_vals (seq_points, &it.seq_point, 1);
285 
286 	ptr = info_inflated.data + sp.next_offset;
287 	for (i = 0; i < sp.next_len; i++) {
288 		int next_index;
289 		next_index = decode_var_int (ptr, &ptr);
290 		g_assert (next_index < seq_points->len);
291 		memcpy (&next[i], seq_points->data + next_index * sizeof (SeqPoint), sizeof (SeqPoint));
292 	}
293 
294 	g_array_free (seq_points, TRUE);
295 }
296 
297 gboolean
mono_seq_point_iterator_next(SeqPointIterator * it)298 mono_seq_point_iterator_next (SeqPointIterator* it)
299 {
300 	if (it->ptr >= it->end)
301 		return FALSE;
302 
303 	it->ptr += seq_point_read (&it->seq_point, it->ptr, it->begin, it->has_debug_data);
304 
305 	return TRUE;
306 }
307 
308 void
mono_seq_point_iterator_init(SeqPointIterator * it,MonoSeqPointInfo * info)309 mono_seq_point_iterator_init (SeqPointIterator* it, MonoSeqPointInfo* info)
310 {
311 	SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
312 	it->ptr = info_inflated.data;
313 	it->begin = info_inflated.data;
314 	it->end = it->begin + info_inflated.len;
315 	it->has_debug_data = info_inflated.has_debug_data;
316 	memset(&it->seq_point, 0, sizeof(SeqPoint));
317 }
318 
319 int
mono_seq_point_info_write(MonoSeqPointInfo * info,guint8 * buffer)320 mono_seq_point_info_write (MonoSeqPointInfo* info, guint8* buffer)
321 {
322 	guint8* buffer0 = buffer;
323 	SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
324 
325 	encode_var_int (buffer, &buffer, info_inflated.has_debug_data);
326 
327 	//Write sequence points
328 	encode_var_int (buffer, &buffer, info_inflated.len);
329 	memcpy (buffer, info_inflated.data, info_inflated.len);
330 	buffer += info_inflated.len;
331 
332 	return buffer - buffer0;
333 }
334 
335 int
mono_seq_point_info_read(MonoSeqPointInfo ** info,guint8 * buffer,gboolean copy)336 mono_seq_point_info_read (MonoSeqPointInfo** info, guint8* buffer, gboolean copy)
337 {
338 	guint8* buffer0 = buffer;
339 	int size, info_size;
340 	gboolean has_debug_data;
341 
342 	has_debug_data = decode_var_int (buffer, &buffer);
343 
344 	size = decode_var_int (buffer, &buffer);
345 	(*info) = mono_seq_point_info_new (size, copy, buffer, has_debug_data, &info_size);
346 	buffer += size;
347 
348 	return buffer - buffer0;
349 }
350 
351 /*
352  * Returns the maximum size of mono_seq_point_info_write.
353  */
354 int
mono_seq_point_info_get_write_size(MonoSeqPointInfo * info)355 mono_seq_point_info_get_write_size (MonoSeqPointInfo* info)
356 {
357 	SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
358 
359 	//4 is the maximum size required to store the size of the data.
360 	//1 is the byte used to store has_debug_data.
361 	int size = 4 + 1 + info_inflated.len;
362 
363 	return size;
364 }
365 
366 /*
367  * SeqPointData struct and functions
368  * This is used to store/load/use sequence point from a file
369  */
370 
371 void
mono_seq_point_data_init(SeqPointData * data,int entry_capacity)372 mono_seq_point_data_init (SeqPointData *data, int entry_capacity)
373 {
374 	data->entry_count = 0;
375 	data->entry_capacity = entry_capacity;
376 	data->entries = (SeqPointDataEntry *)g_malloc (sizeof (SeqPointDataEntry) * entry_capacity);
377 }
378 
379 void
mono_seq_point_data_free(SeqPointData * data)380 mono_seq_point_data_free (SeqPointData *data)
381 {
382 	int i;
383 	for (i=0; i<data->entry_count; i++) {
384 		if (data->entries [i].free_seq_points)
385 			g_free (data->entries [i].seq_points);
386 	}
387 	g_free (data->entries);
388 }
389 
390 gboolean
mono_seq_point_data_read(SeqPointData * data,char * path)391 mono_seq_point_data_read (SeqPointData *data, char *path)
392 {
393 	guint8 *buffer, *buffer_orig;
394 	int entry_count, i;
395 	long fsize;
396 	FILE *f;
397 
398 	f = fopen (path, "r");
399 	if (!f)
400 		return FALSE;
401 
402 	fseek(f, 0, SEEK_END);
403 	fsize = ftell(f);
404 	fseek(f, 0, SEEK_SET);
405 
406 	buffer_orig = buffer = (guint8 *)g_malloc (fsize + 1);
407 	fread(buffer_orig, fsize, 1, f);
408 	fclose(f);
409 
410 	entry_count = decode_var_int (buffer, &buffer);
411 	mono_seq_point_data_init (data, entry_count);
412 	data->entry_count = entry_count;
413 
414 	for (i=0; i<entry_count; i++) {
415 		data->entries [i].method_token = decode_var_int (buffer, &buffer);
416 		data->entries [i].method_index = decode_var_int (buffer, &buffer);
417 		buffer += mono_seq_point_info_read (&data->entries [i].seq_points, buffer, TRUE);
418 		data->entries [i].free_seq_points = TRUE;
419 	}
420 
421 	g_free (buffer_orig);
422 	return TRUE;
423 }
424 
425 gboolean
mono_seq_point_data_write(SeqPointData * data,char * path)426 mono_seq_point_data_write (SeqPointData *data, char *path)
427 {
428 	guint8 *buffer, *buffer_orig;
429 	FILE *f;
430 	int i, size = 0;
431 
432 	f = fopen (path, "w+");
433 	if (!f)
434 		return FALSE;
435 
436 	for (i=0; i<data->entry_count; i++) {
437 		size += mono_seq_point_info_get_write_size (data->entries [i].seq_points);
438 	}
439 	// Add size of entry_count and native_base_offsets
440 	size += 4 + data->entry_count * 4;
441 
442 	buffer_orig = buffer = (guint8 *)g_malloc (size);
443 
444 	encode_var_int (buffer, &buffer, data->entry_count);
445 
446 	for (i=0; i<data->entry_count; i++) {
447 		encode_var_int (buffer, &buffer, data->entries [i].method_token);
448 		encode_var_int (buffer, &buffer, data->entries [i].method_index);
449 		buffer += mono_seq_point_info_write (data->entries [i].seq_points, buffer);
450 	}
451 
452 	fwrite (buffer_orig, 1, buffer - buffer_orig, f);
453 	g_free (buffer_orig);
454 	fclose (f);
455 
456 	return TRUE;
457 }
458 
459 void
mono_seq_point_data_add(SeqPointData * data,guint32 method_token,guint32 method_index,MonoSeqPointInfo * info)460 mono_seq_point_data_add (SeqPointData *data, guint32 method_token, guint32 method_index, MonoSeqPointInfo* info)
461 {
462 	int i;
463 
464 	g_assert (data->entry_count < data->entry_capacity);
465 	i = data->entry_count++;
466 	data->entries [i].seq_points = info;
467 	data->entries [i].method_token = method_token;
468 	data->entries [i].method_index = method_index;
469 	data->entries [i].free_seq_points = FALSE;
470 }
471 
472 gboolean
mono_seq_point_data_get(SeqPointData * data,guint32 method_token,guint32 method_index,MonoSeqPointInfo ** info)473 mono_seq_point_data_get (SeqPointData *data, guint32 method_token, guint32 method_index, MonoSeqPointInfo** info)
474 {
475 	int i;
476 
477 	for (i=0; i<data->entry_count; i++) {
478 		if (data->entries [i].method_token == method_token && (method_index == 0xffffff || data->entries [i].method_index == method_index)) {
479 			(*info) = data->entries [i].seq_points;
480 			return TRUE;
481 		}
482 	}
483 	return FALSE;
484 }
485 
486 gboolean
mono_seq_point_data_get_il_offset(char * path,guint32 method_token,guint32 method_index,guint32 native_offset,guint32 * il_offset)487 mono_seq_point_data_get_il_offset (char *path, guint32 method_token, guint32 method_index, guint32 native_offset, guint32 *il_offset)
488 {
489 	SeqPointData sp_data;
490 	MonoSeqPointInfo *seq_points;
491 	SeqPoint sp;
492 
493 	if (!mono_seq_point_data_read (&sp_data, path))
494 		return FALSE;
495 
496 	if (!mono_seq_point_data_get (&sp_data, method_token, method_index, &seq_points))
497 		return FALSE;
498 
499 	if (!mono_seq_point_find_prev_by_native_offset (seq_points, native_offset, &sp))
500 		return FALSE;
501 
502 	*il_offset = sp.il_offset;
503 
504 	return TRUE;
505 }
506