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+
1726struct 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 * );
2636typedef 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
91101static 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+
144181static void
145182exc_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+
170255MRB_API void
171256mrb_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