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