Skip to content

Commit a561bdb

Browse files
committed
Support backtrace after method calls
GitHub: fix mruby#2902, mruby#2917 The current implementation traverses stack to retrieve backtrace. But stack will be changed when some operations are occurred. It means that backtrace may be broken after some operations. This change (1) saves the minimum information to retrieve backtrace when exception is raised and (2) restores backtrace from the minimum information when backtrace is needed. It reduces overhead for creating backtrace Ruby objects. The space for the minimum information is reused by multiple exceptions. So memory allocation isn't occurred for each exception.
1 parent e132de9 commit a561bdb

7 files changed

Lines changed: 350 additions & 31 deletions

File tree

include/mruby.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ struct mrb_context {
113113

114114
struct mrb_jmpbuf;
115115

116+
typedef struct {
117+
const char *filename;
118+
int lineno;
119+
struct RClass *klass;
120+
const char *sep;
121+
mrb_sym method_id;
122+
} mrb_backtrace_entry;
123+
116124
typedef void (*mrb_atexit_func)(struct mrb_state*);
117125

118126
typedef struct mrb_state {
@@ -125,6 +133,12 @@ typedef struct mrb_state {
125133
struct mrb_context *root_c;
126134

127135
struct RObject *exc; /* exception */
136+
struct {
137+
struct RObject *exc;
138+
int n;
139+
int n_allocated;
140+
mrb_backtrace_entry *entries;
141+
} backtrace;
128142
struct iv_tbl *globals; /* global variable table */
129143

130144
struct RObject *top_self;

src/backtrace.c

Lines changed: 212 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@
1414
#include <mruby/error.h>
1515
#include <mruby/numeric.h>
1616

17+
struct backtrace_location_raw {
18+
int i;
19+
int lineno;
20+
const char *filename;
21+
mrb_sym method_id;
22+
const char *sep;
23+
struct RClass *klass;
24+
};
25+
1726
struct backtrace_location {
1827
int i;
1928
int lineno;
@@ -23,6 +32,7 @@ struct backtrace_location {
2332
const char *class_name;
2433
};
2534

35+
typedef void (*each_backtrace_func)(mrb_state*, struct backtrace_location_raw*, void*);
2636
typedef void (*output_stream_func)(mrb_state*, struct backtrace_location*, void*);
2737

2838
#ifndef MRB_DISABLE_STDIO
@@ -89,15 +99,15 @@ get_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data)
8999
}
90100

91101
static void
92-
output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *data)
102+
each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func func, void *data)
93103
{
94104
int i;
95105

96106
if (ciidx >= mrb->c->ciend - mrb->c->cibase)
97107
ciidx = 10; /* ciidx is broken... */
98108

99109
for (i = ciidx; i >= 0; i--) {
100-
struct backtrace_location loc;
110+
struct backtrace_location_raw loc;
101111
mrb_callinfo *ci;
102112
mrb_irep *irep;
103113
mrb_code *pc;
@@ -134,13 +144,40 @@ output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_fun
134144
loc.filename = "(unknown)";
135145
}
136146

137-
loc.method = mrb_sym2name(mrb, ci->mid);
138-
loc.class_name = mrb_class_name(mrb, ci->proc->target_class);
147+
loc.method_id = ci->mid;
148+
loc.klass = ci->proc->target_class;
139149
loc.i = i;
140150
func(mrb, &loc, data);
141151
}
142152
}
143153

154+
struct output_backtrace_args {
155+
output_stream_func func;
156+
void *data;
157+
};
158+
159+
static void
160+
output_backtrace_i(mrb_state *mrb, struct backtrace_location_raw *loc_raw, void *data)
161+
{
162+
struct backtrace_location loc;
163+
struct output_backtrace_args *args = data;
164+
165+
loc.i = loc_raw->i;
166+
loc.lineno = loc_raw->lineno;
167+
loc.filename = loc_raw->filename;
168+
loc.method = mrb_sym2name(mrb, loc_raw->method_id);
169+
loc.sep = loc_raw->sep;
170+
loc.class_name = mrb_class_name(mrb, loc_raw->klass);
171+
172+
args->func(mrb, &loc, args->data);
173+
}
174+
175+
static void
176+
output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *data)
177+
{
178+
each_backtrace(mrb, ciidx, pc0, output_backtrace_i, data);
179+
}
180+
144181
static void
145182
exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream)
146183
{
@@ -167,18 +204,76 @@ exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func fun
167204

168205
#ifndef MRB_DISABLE_STDIO
169206

207+
static void
208+
print_backtrace(mrb_state *mrb, mrb_value backtrace)
209+
{
210+
int i, n;
211+
FILE *stream = stderr;
212+
213+
fprintf(stream, "trace:\n");
214+
215+
n = RARRAY_LEN(backtrace);
216+
for (i = 0; i < n; i++) {
217+
mrb_value entry = RARRAY_PTR(backtrace)[i];
218+
219+
fprintf(stream, "\t[%d] %.*s\n", i, RSTRING_LEN(entry), RSTRING_PTR(entry));
220+
}
221+
}
222+
223+
static void
224+
print_backtrace_saved(mrb_state *mrb)
225+
{
226+
int i;
227+
FILE *stream = stderr;
228+
229+
fprintf(stream, "trace:\n");
230+
for (i = 0; i < mrb->backtrace.n; i++) {
231+
mrb_backtrace_entry *entry;
232+
233+
entry = &(mrb->backtrace.entries[i]);
234+
fprintf(stream, "\t[%d] %s:%d", i, entry->filename, entry->lineno);
235+
236+
if (entry->method_id != 0) {
237+
const char *method_name;
238+
239+
method_name = mrb_sym2name(mrb, entry->method_id);
240+
if (entry->klass) {
241+
fprintf(stream, ":in %s%s%s",
242+
mrb_class_name(mrb, entry->klass),
243+
entry->sep,
244+
method_name);
245+
}
246+
else {
247+
fprintf(stream, ":in %s", method_name);
248+
}
249+
}
250+
251+
fprintf(stream, "\n");
252+
}
253+
}
254+
170255
MRB_API void
171256
mrb_print_backtrace(mrb_state *mrb)
172257
{
173-
struct print_backtrace_args args;
258+
mrb_value backtrace;
174259

175260
if (!mrb->exc || mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), E_SYSSTACK_ERROR)) {
176261
return;
177262
}
178263

179-
args.stream = stderr;
180-
args.tracehead = TRUE;
181-
exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)&args);
264+
backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace"));
265+
if (!mrb_nil_p(backtrace)) {
266+
print_backtrace(mrb, backtrace);
267+
}
268+
else if (mrb->backtrace.n > 0) {
269+
print_backtrace_saved(mrb);
270+
}
271+
else {
272+
struct print_backtrace_args args;
273+
args.stream = stderr;
274+
args.tracehead = TRUE;
275+
exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)&args);
276+
}
182277
}
183278

184279
#else
@@ -215,3 +310,112 @@ mrb_get_backtrace(mrb_state *mrb)
215310

216311
return ary;
217312
}
313+
314+
void
315+
mrb_free_backtrace(mrb_state *mrb)
316+
{
317+
mrb->backtrace.exc = 0;
318+
mrb->backtrace.n = 0;
319+
mrb->backtrace.n_allocated = 0;
320+
mrb_free(mrb, mrb->backtrace.entries);
321+
}
322+
323+
static void
324+
save_backtrace_i(mrb_state *mrb,
325+
struct backtrace_location_raw *loc_raw,
326+
void *data)
327+
{
328+
mrb_backtrace_entry *entry;
329+
330+
if (loc_raw->i >= mrb->backtrace.n_allocated) {
331+
int new_n_allocated;
332+
if (mrb->backtrace.n_allocated == 0) {
333+
new_n_allocated = 8;
334+
}
335+
else {
336+
new_n_allocated = mrb->backtrace.n_allocated * 2;
337+
}
338+
mrb->backtrace.entries =
339+
mrb_realloc(mrb,
340+
mrb->backtrace.entries,
341+
sizeof(mrb_backtrace_entry) * new_n_allocated);
342+
mrb->backtrace.n_allocated = new_n_allocated;
343+
}
344+
345+
entry = &mrb->backtrace.entries[mrb->backtrace.n];
346+
entry->filename = loc_raw->filename;
347+
entry->lineno = loc_raw->lineno;
348+
entry->klass = loc_raw->klass;
349+
entry->sep = loc_raw->sep;
350+
entry->method_id = loc_raw->method_id;
351+
352+
mrb->backtrace.n++;
353+
}
354+
355+
void
356+
mrb_save_backtrace(mrb_state *mrb)
357+
{
358+
mrb_value lastpc;
359+
mrb_code *code;
360+
mrb_int ciidx;
361+
362+
mrb->backtrace.n = 0;
363+
mrb->backtrace.exc = 0;
364+
365+
if (!mrb->exc)
366+
return;
367+
368+
mrb->backtrace.exc = mrb->exc;
369+
370+
lastpc = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "lastpc"));
371+
if (mrb_nil_p(lastpc)) {
372+
code = NULL;
373+
}
374+
else {
375+
code = (mrb_code*)mrb_cptr(lastpc);
376+
}
377+
378+
ciidx = mrb_fixnum(mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "ciidx")));
379+
380+
each_backtrace(mrb, ciidx, code, save_backtrace_i, NULL);
381+
}
382+
383+
mrb_value
384+
mrb_restore_backtrace(mrb_state *mrb)
385+
{
386+
int i;
387+
mrb_value backtrace;
388+
389+
backtrace = mrb_ary_new(mrb);
390+
for (i = 0; i < mrb->backtrace.n; i++) {
391+
int ai;
392+
mrb_backtrace_entry *entry;
393+
mrb_value mrb_entry;
394+
395+
ai = mrb_gc_arena_save(mrb);
396+
entry = &(mrb->backtrace.entries[i]);
397+
398+
mrb_entry = mrb_str_new_cstr(mrb, entry->filename);
399+
mrb_str_cat_lit(mrb, mrb_entry, ":");
400+
mrb_str_concat(mrb, mrb_entry,
401+
mrb_fixnum_to_str(mrb,
402+
mrb_fixnum_value(entry->lineno),
403+
10));
404+
if (entry->method_id != 0) {
405+
mrb_str_cat_lit(mrb, mrb_entry, ":in ");
406+
407+
if (entry->klass) {
408+
mrb_str_cat_cstr(mrb, mrb_entry, mrb_class_name(mrb, entry->klass));
409+
mrb_str_cat_cstr(mrb, mrb_entry, entry->sep);
410+
}
411+
412+
mrb_str_cat_cstr(mrb, mrb_entry, mrb_sym2name(mrb, entry->method_id));
413+
}
414+
415+
mrb_ary_push(mrb, backtrace, mrb_entry);
416+
417+
mrb_gc_arena_restore(mrb, ai);
418+
}
419+
420+
return backtrace;
421+
}

0 commit comments

Comments
 (0)