1 /*
2 ** apibreak.c
3 **
4 */
5 
6 #include <string.h>
7 #include <mruby.h>
8 #include <mruby/irep.h>
9 #include "mrdb.h"
10 #include <mruby/debug.h>
11 #include <mruby/opcode.h>
12 #include <mruby/class.h>
13 #include <mruby/proc.h>
14 #include <mruby/variable.h>
15 #include "mrdberror.h"
16 #include "apibreak.h"
17 
18 #define MAX_BREAKPOINTNO (MAX_BREAKPOINT * 1024)
19 #define MRB_DEBUG_BP_FILE_OK   (0x0001)
20 #define MRB_DEBUG_BP_LINENO_OK (0x0002)
21 
22 static uint16_t
check_lineno(mrb_irep_debug_info_file * info_file,uint16_t lineno)23 check_lineno(mrb_irep_debug_info_file *info_file, uint16_t lineno)
24 {
25   uint32_t count = info_file->line_entry_count;
26   uint16_t l_idx;
27 
28   if (info_file->line_type == mrb_debug_line_ary) {
29     for (l_idx = 0; l_idx < count; ++l_idx) {
30       if (lineno == info_file->lines.ary[l_idx]) {
31         return lineno;
32       }
33     }
34   }
35   else {
36     for (l_idx = 0; l_idx < count; ++l_idx) {
37       if (lineno == info_file->lines.flat_map[l_idx].line) {
38         return lineno;
39       }
40     }
41   }
42 
43   return 0;
44 }
45 
46 static int32_t
get_break_index(mrb_debug_context * dbg,uint32_t bpno)47 get_break_index(mrb_debug_context *dbg, uint32_t bpno)
48 {
49   uint32_t i;
50   int32_t index;
51   char hit = FALSE;
52 
53   for(i = 0 ; i < dbg->bpnum; i++) {
54     if (dbg->bp[i].bpno == bpno) {
55       hit = TRUE;
56       index = i;
57       break;
58     }
59   }
60 
61   if (hit == FALSE) {
62     return MRB_DEBUG_BREAK_INVALID_NO;
63   }
64 
65   return index;
66 }
67 
68 static void
free_breakpoint(mrb_state * mrb,mrb_debug_breakpoint * bp)69 free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp)
70 {
71   switch(bp->type) {
72     case MRB_DEBUG_BPTYPE_LINE:
73       mrb_free(mrb, (void*)bp->point.linepoint.file);
74       break;
75     case MRB_DEBUG_BPTYPE_METHOD:
76       mrb_free(mrb, (void*)bp->point.methodpoint.method_name);
77       if (bp->point.methodpoint.class_name != NULL) {
78         mrb_free(mrb, (void*)bp->point.methodpoint.class_name);
79       }
80       break;
81     default:
82       break;
83   }
84 }
85 
86 static uint16_t
check_file_lineno(struct mrb_irep * irep,const char * file,uint16_t lineno)87 check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
88 {
89   mrb_irep_debug_info_file *info_file;
90   uint16_t result = 0;
91   uint16_t f_idx;
92   uint16_t fix_lineno;
93   uint16_t i;
94 
95   for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
96     info_file = irep->debug_info->files[f_idx];
97     if (!strcmp(info_file->filename, file)) {
98       result = MRB_DEBUG_BP_FILE_OK;
99 
100       fix_lineno = check_lineno(info_file, lineno);
101       if (fix_lineno != 0) {
102         return result | MRB_DEBUG_BP_LINENO_OK;
103       }
104     }
105     for (i=0; i < irep->rlen; ++i) {
106       result  |= check_file_lineno(irep->reps[i], file, lineno);
107       if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
108         return result;
109       }
110     }
111   }
112   return result;
113 }
114 
115 static int32_t
compare_break_method(mrb_state * mrb,mrb_debug_breakpoint * bp,struct RClass * class_obj,mrb_sym method_sym,mrb_bool * isCfunc)116 compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc)
117 {
118   const char* class_name;
119   const char* method_name;
120   mrb_method_t m;
121   struct RClass* sc;
122   const char* sn;
123   mrb_sym ssym;
124   mrb_debug_methodpoint *method_p;
125   mrb_bool is_defined;
126 
127   method_name = mrb_sym2name(mrb, method_sym);
128 
129   method_p = &bp->point.methodpoint;
130   if (strcmp(method_p->method_name, method_name) == 0) {
131     class_name = mrb_class_name(mrb, class_obj);
132     if (class_name == NULL) {
133       if (method_p->class_name == NULL) {
134         return bp->bpno;
135       }
136     }
137     else if (method_p->class_name != NULL) {
138       m = mrb_method_search_vm(mrb, &class_obj, method_sym);
139       if (MRB_METHOD_UNDEF_P(m)) {
140         return MRB_DEBUG_OK;
141       }
142       if (MRB_METHOD_CFUNC_P(m)) {
143         *isCfunc = TRUE;
144       }
145 
146       is_defined = mrb_class_defined(mrb, method_p->class_name);
147       if (is_defined == FALSE) {
148         return MRB_DEBUG_OK;
149       }
150 
151       sc = mrb_class_get(mrb, method_p->class_name);
152       ssym = mrb_symbol(mrb_check_intern_cstr(mrb, method_p->method_name));
153       m = mrb_method_search_vm(mrb, &sc, ssym);
154       if (MRB_METHOD_UNDEF_P(m)) {
155         return MRB_DEBUG_OK;
156       }
157 
158       class_name = mrb_class_name(mrb, class_obj);
159       sn = mrb_class_name(mrb, sc);
160       if (strcmp(sn, class_name) == 0) {
161         return bp->bpno;
162       }
163     }
164   }
165   return MRB_DEBUG_OK;
166 }
167 
168 int32_t
mrb_debug_set_break_line(mrb_state * mrb,mrb_debug_context * dbg,const char * file,uint16_t lineno)169 mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t lineno)
170 {
171   int32_t index;
172   char* set_file;
173   uint16_t result;
174 
175   if ((mrb == NULL)||(dbg == NULL)||(file == NULL)) {
176     return MRB_DEBUG_INVALID_ARGUMENT;
177   }
178 
179   if (dbg->bpnum >= MAX_BREAKPOINT) {
180     return MRB_DEBUG_BREAK_NUM_OVER;
181   }
182 
183   if (dbg->next_bpno > MAX_BREAKPOINTNO) {
184     return MRB_DEBUG_BREAK_NO_OVER;
185   }
186 
187   /* file and lineno check (line type mrb_debug_line_ary only.) */
188   result = check_file_lineno(dbg->root_irep, file, lineno);
189   if (result == 0) {
190     return MRB_DEBUG_BREAK_INVALID_FILE;
191   }
192   else if (result == MRB_DEBUG_BP_FILE_OK) {
193     return MRB_DEBUG_BREAK_INVALID_LINENO;
194   }
195 
196   set_file = (char*)mrb_malloc(mrb, strlen(file) + 1);
197 
198   index = dbg->bpnum;
199   dbg->bp[index].bpno = dbg->next_bpno;
200   dbg->next_bpno++;
201   dbg->bp[index].enable = TRUE;
202   dbg->bp[index].type = MRB_DEBUG_BPTYPE_LINE;
203   dbg->bp[index].point.linepoint.lineno = lineno;
204   dbg->bpnum++;
205 
206   strncpy(set_file, file, strlen(file) + 1);
207 
208   dbg->bp[index].point.linepoint.file = set_file;
209 
210   return dbg->bp[index].bpno;
211 }
212 
213 int32_t
mrb_debug_set_break_method(mrb_state * mrb,mrb_debug_context * dbg,const char * class_name,const char * method_name)214 mrb_debug_set_break_method(mrb_state *mrb, mrb_debug_context *dbg, const char *class_name, const char *method_name)
215 {
216   int32_t index;
217   char* set_class;
218   char* set_method;
219 
220   if ((mrb == NULL) || (dbg == NULL) || (method_name == NULL)) {
221     return MRB_DEBUG_INVALID_ARGUMENT;
222   }
223 
224   if (dbg->bpnum >= MAX_BREAKPOINT) {
225     return MRB_DEBUG_BREAK_NUM_OVER;
226   }
227 
228   if (dbg->next_bpno > MAX_BREAKPOINTNO) {
229     return MRB_DEBUG_BREAK_NO_OVER;
230   }
231 
232   if (class_name != NULL) {
233     set_class = (char*)mrb_malloc(mrb, strlen(class_name) + 1);
234     strncpy(set_class, class_name, strlen(class_name) + 1);
235   }
236   else {
237     set_class = NULL;
238   }
239 
240   set_method = (char*)mrb_malloc(mrb, strlen(method_name) + 1);
241 
242   strncpy(set_method, method_name, strlen(method_name) + 1);
243 
244   index = dbg->bpnum;
245   dbg->bp[index].bpno = dbg->next_bpno;
246   dbg->next_bpno++;
247   dbg->bp[index].enable = TRUE;
248   dbg->bp[index].type = MRB_DEBUG_BPTYPE_METHOD;
249   dbg->bp[index].point.methodpoint.method_name = set_method;
250   dbg->bp[index].point.methodpoint.class_name = set_class;
251   dbg->bpnum++;
252 
253   return dbg->bp[index].bpno;
254 }
255 
256 int32_t
mrb_debug_get_breaknum(mrb_state * mrb,mrb_debug_context * dbg)257 mrb_debug_get_breaknum(mrb_state *mrb, mrb_debug_context *dbg)
258 {
259   if ((mrb == NULL) || (dbg == NULL)) {
260     return MRB_DEBUG_INVALID_ARGUMENT;
261   }
262 
263   return dbg->bpnum;
264 }
265 
266 int32_t
mrb_debug_get_break_all(mrb_state * mrb,mrb_debug_context * dbg,uint32_t size,mrb_debug_breakpoint * bp)267 mrb_debug_get_break_all(mrb_state *mrb, mrb_debug_context *dbg, uint32_t size, mrb_debug_breakpoint *bp)
268 {
269   uint32_t get_size = 0;
270 
271   if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
272     return MRB_DEBUG_INVALID_ARGUMENT;
273   }
274 
275   if (dbg->bpnum >= size) {
276     get_size = size;
277   }
278   else {
279     get_size = dbg->bpnum;
280   }
281 
282   memcpy(bp, dbg->bp, sizeof(mrb_debug_breakpoint) * get_size);
283 
284   return get_size;
285 }
286 
287 int32_t
mrb_debug_get_break(mrb_state * mrb,mrb_debug_context * dbg,uint32_t bpno,mrb_debug_breakpoint * bp)288 mrb_debug_get_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp)
289 {
290   int32_t index;
291 
292   if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
293     return MRB_DEBUG_INVALID_ARGUMENT;
294   }
295 
296   index = get_break_index(dbg, bpno);
297   if (index == MRB_DEBUG_BREAK_INVALID_NO) {
298     return MRB_DEBUG_BREAK_INVALID_NO;
299   }
300 
301   bp->bpno = dbg->bp[index].bpno;
302   bp->enable = dbg->bp[index].enable;
303   bp->point = dbg->bp[index].point;
304   bp->type = dbg->bp[index].type;
305 
306   return 0;
307 }
308 
309 int32_t
mrb_debug_delete_break(mrb_state * mrb,mrb_debug_context * dbg,uint32_t bpno)310 mrb_debug_delete_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
311 {
312   uint32_t i;
313   int32_t index;
314 
315   if ((mrb == NULL) ||(dbg == NULL)) {
316     return MRB_DEBUG_INVALID_ARGUMENT;
317   }
318 
319   index = get_break_index(dbg, bpno);
320   if (index == MRB_DEBUG_BREAK_INVALID_NO) {
321     return MRB_DEBUG_BREAK_INVALID_NO;
322   }
323 
324   free_breakpoint(mrb, &dbg->bp[index]);
325 
326   for(i = index ; i < dbg->bpnum; i++) {
327     if ((i + 1) == dbg->bpnum) {
328       memset(&dbg->bp[i], 0, sizeof(mrb_debug_breakpoint));
329     }
330     else {
331       memcpy(&dbg->bp[i], &dbg->bp[i + 1], sizeof(mrb_debug_breakpoint));
332     }
333   }
334 
335   dbg->bpnum--;
336 
337   return MRB_DEBUG_OK;
338 }
339 
340 int32_t
mrb_debug_delete_break_all(mrb_state * mrb,mrb_debug_context * dbg)341 mrb_debug_delete_break_all(mrb_state *mrb, mrb_debug_context *dbg)
342 {
343   uint32_t i;
344 
345   if ((mrb == NULL) || (dbg == NULL)) {
346     return MRB_DEBUG_INVALID_ARGUMENT;
347   }
348 
349   for(i = 0 ; i < dbg->bpnum ; i++) {
350     free_breakpoint(mrb, &dbg->bp[i]);
351   }
352 
353   dbg->bpnum = 0;
354 
355   return MRB_DEBUG_OK;
356 }
357 
358 int32_t
mrb_debug_enable_break(mrb_state * mrb,mrb_debug_context * dbg,uint32_t bpno)359 mrb_debug_enable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
360 {
361   int32_t index = 0;
362 
363   if ((mrb == NULL) || (dbg == NULL)) {
364     return MRB_DEBUG_INVALID_ARGUMENT;
365   }
366 
367   index = get_break_index(dbg, bpno);
368   if (index == MRB_DEBUG_BREAK_INVALID_NO) {
369     return MRB_DEBUG_BREAK_INVALID_NO;
370   }
371 
372   dbg->bp[index].enable = TRUE;
373 
374   return MRB_DEBUG_OK;
375 }
376 
377 int32_t
mrb_debug_enable_break_all(mrb_state * mrb,mrb_debug_context * dbg)378 mrb_debug_enable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
379 {
380   uint32_t i;
381 
382   if ((mrb == NULL) || (dbg == NULL)) {
383     return MRB_DEBUG_INVALID_ARGUMENT;
384   }
385 
386   for(i = 0 ; i < dbg->bpnum; i++) {
387     dbg->bp[i].enable = TRUE;
388   }
389 
390   return MRB_DEBUG_OK;
391 }
392 
393 int32_t
mrb_debug_disable_break(mrb_state * mrb,mrb_debug_context * dbg,uint32_t bpno)394 mrb_debug_disable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
395 {
396   int32_t index = 0;
397 
398   if ((mrb == NULL) || (dbg == NULL)) {
399     return MRB_DEBUG_INVALID_ARGUMENT;
400   }
401 
402   index = get_break_index(dbg, bpno);
403   if (index == MRB_DEBUG_BREAK_INVALID_NO) {
404     return MRB_DEBUG_BREAK_INVALID_NO;
405   }
406 
407   dbg->bp[index].enable = FALSE;
408 
409   return MRB_DEBUG_OK;
410 }
411 
412 int32_t
mrb_debug_disable_break_all(mrb_state * mrb,mrb_debug_context * dbg)413 mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
414 {
415   uint32_t i;
416 
417   if ((mrb == NULL) || (dbg == NULL)) {
418     return MRB_DEBUG_INVALID_ARGUMENT;
419   }
420 
421   for(i = 0 ; i < dbg->bpnum; i++) {
422     dbg->bp[i].enable = FALSE;
423   }
424 
425   return MRB_DEBUG_OK;
426 }
427 
428 static mrb_bool
check_start_pc_for_line(mrb_irep * irep,mrb_code * pc,uint16_t line)429 check_start_pc_for_line(mrb_irep *irep, mrb_code *pc, uint16_t line)
430 {
431   if (pc > irep->iseq) {
432     if (line == mrb_debug_get_line(irep, pc - irep->iseq - 1)) {
433       return FALSE;
434     }
435   }
436   return TRUE;
437 }
438 
439 int32_t
mrb_debug_check_breakpoint_line(mrb_state * mrb,mrb_debug_context * dbg,const char * file,uint16_t line)440 mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t line)
441 {
442   mrb_debug_breakpoint *bp;
443   mrb_debug_linepoint *line_p;
444   uint32_t i;
445 
446   if ((mrb == NULL) || (dbg == NULL) || (file == NULL) || (line <= 0)) {
447     return MRB_DEBUG_INVALID_ARGUMENT;
448   }
449 
450   if (!check_start_pc_for_line(dbg->irep, dbg->pc, line)) {
451     return MRB_DEBUG_OK;
452   }
453 
454   bp = dbg->bp;
455   for(i=0; i<dbg->bpnum; i++) {
456     switch (bp->type) {
457       case MRB_DEBUG_BPTYPE_LINE:
458         if (bp->enable == TRUE) {
459           line_p = &bp->point.linepoint;
460           if ((strcmp(line_p->file, file) == 0) && (line_p->lineno == line)) {
461             return bp->bpno;
462           }
463         }
464         break;
465       case MRB_DEBUG_BPTYPE_METHOD:
466         break;
467       case MRB_DEBUG_BPTYPE_NONE:
468       default:
469         return MRB_DEBUG_OK;
470     }
471     bp++;
472   }
473   return MRB_DEBUG_OK;
474 }
475 
476 
477 int32_t
mrb_debug_check_breakpoint_method(mrb_state * mrb,mrb_debug_context * dbg,struct RClass * class_obj,mrb_sym method_sym,mrb_bool * isCfunc)478 mrb_debug_check_breakpoint_method(mrb_state *mrb, mrb_debug_context *dbg, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc)
479 {
480   mrb_debug_breakpoint *bp;
481   int32_t bpno;
482   uint32_t i;
483 
484   if ((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) {
485     return MRB_DEBUG_INVALID_ARGUMENT;
486   }
487 
488   bp = dbg->bp;
489   for(i=0; i<dbg->bpnum; i++) {
490     if (bp->type == MRB_DEBUG_BPTYPE_METHOD) {
491       if (bp->enable == TRUE) {
492         bpno = compare_break_method(mrb, bp, class_obj, method_sym, isCfunc);
493         if (bpno > 0) {
494           return bpno;
495         }
496       }
497     }
498     else if (bp->type == MRB_DEBUG_BPTYPE_NONE) {
499       break;
500     }
501     bp++;
502   }
503 
504   return 0;
505 }
506