1 #include <string.h>
2 #include <mruby.h>
3 #include <mruby/irep.h>
4 #include <mruby/debug.h>
5 
6 static mrb_irep_debug_info_file*
get_file(mrb_irep_debug_info * info,uint32_t pc)7 get_file(mrb_irep_debug_info *info, uint32_t pc)
8 {
9   mrb_irep_debug_info_file **ret;
10   int32_t count;
11 
12   if (pc >= info->pc_count) { return NULL; }
13   /* get upper bound */
14   ret = info->files;
15   count =  info->flen;
16   while (count > 0) {
17     int32_t step = count / 2;
18     mrb_irep_debug_info_file **it = ret + step;
19     if (!(pc < (*it)->start_pos)) {
20       ret = it + 1;
21       count -= step + 1;
22     }
23     else { count = step; }
24   }
25 
26   --ret;
27 
28   /* check returning file exists inside debug info */
29   mrb_assert(info->files <= ret && ret < (info->files + info->flen));
30   /* check pc is within the range of returning file */
31   mrb_assert((*ret)->start_pos <= pc &&
32              pc < (((ret + 1 - info->files) < info->flen)
33                    ? (*(ret+1))->start_pos : info->pc_count));
34 
35   return *ret;
36 }
37 
38 static mrb_debug_line_type
select_line_type(const uint16_t * lines,size_t lines_len)39 select_line_type(const uint16_t *lines, size_t lines_len)
40 {
41   size_t line_count = 0;
42   int prev_line = -1;
43   size_t i;
44   for (i = 0; i < lines_len; ++i) {
45     if (lines[i] != prev_line) {
46       ++line_count;
47     }
48   }
49   return (sizeof(uint16_t) * lines_len) <= (sizeof(mrb_irep_debug_info_line) * line_count)
50       ? mrb_debug_line_ary : mrb_debug_line_flat_map;
51 }
52 
53 MRB_API char const*
mrb_debug_get_filename(mrb_state * mrb,mrb_irep * irep,ptrdiff_t pc)54 mrb_debug_get_filename(mrb_state *mrb, mrb_irep *irep, ptrdiff_t pc)
55 {
56   if (irep && pc >= 0 && pc < irep->ilen) {
57     mrb_irep_debug_info_file* f = NULL;
58     if (!irep->debug_info) return NULL;
59     else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
60       return mrb_sym_name_len(mrb, f->filename_sym, NULL);
61     }
62   }
63   return NULL;
64 }
65 
66 MRB_API int32_t
mrb_debug_get_line(mrb_state * mrb,mrb_irep * irep,ptrdiff_t pc)67 mrb_debug_get_line(mrb_state *mrb, mrb_irep *irep, ptrdiff_t pc)
68 {
69   if (irep && pc >= 0 && pc < irep->ilen) {
70     mrb_irep_debug_info_file* f = NULL;
71     if (!irep->debug_info) {
72       return -1;
73     }
74     else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
75       switch (f->line_type) {
76         case mrb_debug_line_ary:
77           mrb_assert(f->start_pos <= pc && pc < (f->start_pos + f->line_entry_count));
78           return f->lines.ary[pc - f->start_pos];
79 
80         case mrb_debug_line_flat_map: {
81           /* get upper bound */
82           mrb_irep_debug_info_line *ret = f->lines.flat_map;
83           uint32_t count = f->line_entry_count;
84           while (count > 0) {
85             int32_t step = count / 2;
86             mrb_irep_debug_info_line *it = ret + step;
87             if (!(pc < it->start_pos)) {
88               ret = it + 1;
89               count -= step + 1;
90             }
91             else { count = step; }
92           }
93 
94           --ret;
95 
96           /* check line entry pointer range */
97           mrb_assert(f->lines.flat_map <= ret && ret < (f->lines.flat_map + f->line_entry_count));
98           /* check pc range */
99           mrb_assert(ret->start_pos <= pc &&
100                      pc < (((uint32_t)(ret + 1 - f->lines.flat_map) < f->line_entry_count)
101                            ? (ret+1)->start_pos : irep->debug_info->pc_count));
102 
103           return ret->line;
104         }
105       }
106     }
107   }
108   return -1;
109 }
110 
111 MRB_API mrb_irep_debug_info*
mrb_debug_info_alloc(mrb_state * mrb,mrb_irep * irep)112 mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep)
113 {
114   static const mrb_irep_debug_info initial = { 0, 0, NULL };
115   mrb_irep_debug_info *ret;
116 
117   mrb_assert(!irep->debug_info);
118   ret = (mrb_irep_debug_info *)mrb_malloc(mrb, sizeof(*ret));
119   *ret = initial;
120   irep->debug_info = ret;
121   return ret;
122 }
123 
124 MRB_API mrb_irep_debug_info_file*
mrb_debug_info_append_file(mrb_state * mrb,mrb_irep_debug_info * d,const char * filename,uint16_t * lines,uint32_t start_pos,uint32_t end_pos)125 mrb_debug_info_append_file(mrb_state *mrb, mrb_irep_debug_info *d,
126                            const char *filename, uint16_t *lines,
127                            uint32_t start_pos, uint32_t end_pos)
128 {
129   mrb_irep_debug_info_file *f;
130   uint32_t file_pc_count;
131   size_t fn_len;
132   uint32_t i;
133 
134   if (!d) return NULL;
135   if (start_pos == end_pos) return NULL;
136 
137   mrb_assert(filename);
138   mrb_assert(lines);
139 
140   if (d->flen > 0) {
141     const char *fn = mrb_sym_name_len(mrb, d->files[d->flen - 1]->filename_sym, NULL);
142     if (strcmp(filename, fn) == 0)
143       return NULL;
144   }
145 
146   f = (mrb_irep_debug_info_file*)mrb_malloc(mrb, sizeof(*f));
147   d->files = (mrb_irep_debug_info_file**)(
148           d->files
149           ? mrb_realloc(mrb, d->files, sizeof(mrb_irep_debug_info_file*) * (d->flen + 1))
150           : mrb_malloc(mrb, sizeof(mrb_irep_debug_info_file*)));
151   d->files[d->flen++] = f;
152 
153   file_pc_count = end_pos - start_pos;
154 
155   f->start_pos = start_pos;
156   d->pc_count = end_pos;
157 
158   fn_len = strlen(filename);
159   f->filename_sym = mrb_intern(mrb, filename, fn_len);
160 
161   f->line_type = select_line_type(lines + start_pos, end_pos - start_pos);
162   f->lines.ptr = NULL;
163 
164   switch (f->line_type) {
165     case mrb_debug_line_ary:
166       f->line_entry_count = file_pc_count;
167       f->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count);
168       for (i = 0; i < file_pc_count; ++i) {
169         f->lines.ary[i] = lines[start_pos + i];
170       }
171       break;
172 
173     case mrb_debug_line_flat_map: {
174       uint16_t prev_line = 0;
175       mrb_irep_debug_info_line m;
176       f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1);
177       f->line_entry_count = 0;
178       for (i = 0; i < file_pc_count; ++i) {
179         if (lines[start_pos + i] == prev_line) { continue; }
180 
181         f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc(
182             mrb, f->lines.flat_map,
183             sizeof(mrb_irep_debug_info_line) * (f->line_entry_count + 1));
184         m.start_pos = start_pos + i;
185         m.line = lines[start_pos + i];
186         f->lines.flat_map[f->line_entry_count] = m;
187 
188         /* update */
189         ++f->line_entry_count;
190         prev_line = lines[start_pos + i];
191       }
192     } break;
193 
194     default: mrb_assert(0); break;
195   }
196 
197   return f;
198 }
199 
200 MRB_API void
mrb_debug_info_free(mrb_state * mrb,mrb_irep_debug_info * d)201 mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d)
202 {
203   uint32_t i;
204 
205   if (!d) { return; }
206 
207   if (d->files) {
208     for (i = 0; i < d->flen; ++i) {
209       if (d->files[i]) {
210         mrb_free(mrb, d->files[i]->lines.ptr);
211         mrb_free(mrb, d->files[i]);
212       }
213     }
214     mrb_free(mrb, d->files);
215   }
216   mrb_free(mrb, d);
217 }
218