Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / seq-points-data.c
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
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
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
65 encode_zig_zag (int val)
66 {
67         return (val << 1) ^ (val >> 31);
68 }
69
70 static int
71 decode_zig_zag (guint32 val)
72 {
73         int n = val;
74         return (n >> 1) ^ (-(n & 1));
75 }
76
77 static SeqPointInfoInflated
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*
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
132 mono_seq_point_info_free (gpointer ptr)
133 {
134         MonoSeqPointInfo* info = (MonoSeqPointInfo*) ptr;
135         g_free (info);
136 }
137
138 static int
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }