4 ** See Copyright Notice in mruby.h
8 #include <mruby/variable.h>
9 #include <mruby/proc.h>
10 #include <mruby/array.h>
11 #include <mruby/string.h>
12 #include <mruby/class.h>
13 #include <mruby/debug.h>
14 #include <mruby/error.h>
15 #include <mruby/numeric.h>
16 #include <mruby/data.h>
17 #include <mruby/internal.h>
18 #include <mruby/presym.h>
21 copy_backtrace(mrb_state
*mrb
,
22 const struct mrb_backtrace_location
*loc
,
23 struct mrb_backtrace_location
*ptr
,
28 if (loc
->irep
->refcnt
== UINT16_MAX
) {
32 mrb_irep_incref(mrb
, (mrb_irep
*)loc
->irep
);
38 pack_backtrace(mrb_state
*mrb
, ptrdiff_t ciidx
, struct mrb_backtrace_location
*ptr
)
42 for (ptrdiff_t i
=ciidx
; i
>= 0; i
--) {
43 struct mrb_backtrace_location loc
;
47 ci
= &mrb
->c
->cibase
[i
];
48 loc
.method_id
= ci
->mid
;
50 if (ci
->proc
&& !MRB_PROC_CFUNC_P(ci
->proc
)) {
51 mrb_assert(!MRB_PROC_ALIAS_P(ci
->proc
));
52 loc
.irep
= ci
->proc
->body
.irep
;
53 if (!loc
.irep
) continue;
54 if (!loc
.irep
->debug_info
) continue;
55 if (!ci
->pc
) continue;
57 loc
.idx
= (uint32_t)(pc
- loc
.irep
->iseq
);
60 if (!loc
.method_id
) continue;
62 for (ptrdiff_t j
=i
-1; j
>= 0; j
--) {
63 ci
= &mrb
->c
->cibase
[j
];
65 if (!ci
->proc
) continue;
66 if (MRB_PROC_CFUNC_P(ci
->proc
)) continue;
67 mrb_assert(!MRB_PROC_ALIAS_P(ci
->proc
));
69 const mrb_irep
*irep
= ci
->proc
->body
.irep
;
71 if (!irep
->debug_info
) continue;
72 if (!ci
->pc
) continue;
75 loc
.idx
= (uint32_t)(pc
- irep
->iseq
);
79 copy_backtrace(mrb
, &loc
, ptr
, n
);
86 packed_backtrace(mrb_state
*mrb
)
88 ptrdiff_t ciidx
= mrb
->c
->ci
- mrb
->c
->cibase
;
90 if (ciidx
>= mrb
->c
->ciend
- mrb
->c
->cibase
)
91 ciidx
= mrb
->c
->ciend
- mrb
->c
->cibase
; /* ciidx is broken... */
93 ptrdiff_t len
= ciidx
+ 1;
95 struct RBacktrace
*backtrace
= MRB_OBJ_ALLOC(mrb
, MRB_TT_BACKTRACE
, NULL
);
97 void *ptr
= mrb_malloc(mrb
, len
* sizeof(struct mrb_backtrace_location
));
98 backtrace
->locations
= (struct mrb_backtrace_location
*)ptr
;
99 backtrace
->len
= pack_backtrace(mrb
, ciidx
, backtrace
->locations
);
101 return (struct RBasic
*)backtrace
;
105 store_backtrace(mrb_state
*mrb
, mrb_value exc
, struct RBasic
*backtrace
)
107 struct RException
*e
= mrb_exc_ptr(exc
);
108 e
->backtrace
= backtrace
;
109 mrb_field_write_barrier(mrb
, (struct RBasic
*)e
, backtrace
);
113 mrb_keep_backtrace(mrb_state
*mrb
, mrb_value exc
)
117 if (mrb
->c
->ci
== NULL
) return;
118 if (mrb_exc_ptr(exc
)->backtrace
) return;
119 ai
= mrb_gc_arena_save(mrb
);
120 struct RBasic
*backtrace
= packed_backtrace(mrb
);
121 store_backtrace(mrb
, exc
, backtrace
);
122 mrb_gc_arena_restore(mrb
, ai
);
126 decode_location(mrb_state
*mrb
, const struct mrb_backtrace_location
*entry
)
130 const char *filename
;
132 if (!entry
->irep
|| !mrb_debug_get_position(mrb
, entry
->irep
, entry
->idx
, &lineno
, &filename
)) {
133 btline
= mrb_str_new_lit(mrb
, "(unknown):0");
135 else if (lineno
!= -1) {//debug info was available
136 btline
= mrb_format(mrb
, "%s:%d", filename
, (int)lineno
);
138 else { //all that was left was the stack frame
139 btline
= mrb_format(mrb
, "%s:0", filename
);
141 if (entry
->method_id
!= 0) {
142 mrb_str_cat_lit(mrb
, btline
, ":in ");
143 mrb_str_cat_cstr(mrb
, btline
, mrb_sym_name(mrb
, entry
->method_id
));
148 static struct RBasic
*
149 mrb_unpack_backtrace(mrb_state
*mrb
, struct RBasic
*backtrace
)
151 if (backtrace
== NULL
) {
152 return mrb_basic_ptr(mrb_ary_new_capa(mrb
, 0));
154 if (backtrace
->tt
== MRB_TT_ARRAY
) return backtrace
;
156 mrb_assert(backtrace
->tt
== MRB_TT_BACKTRACE
);
158 struct RBacktrace
*bt
= (struct RBacktrace
*)backtrace
;
159 mrb_int n
= (mrb_int
)bt
->len
;
160 const struct mrb_backtrace_location
*loc
= bt
->locations
;
162 backtrace
= mrb_basic_ptr(mrb_ary_new_capa(mrb
, n
));
163 int ai
= mrb_gc_arena_save(mrb
);
164 for (mrb_int i
= 0; i
< n
; i
++) {
165 mrb_value btline
= decode_location(mrb
, &loc
[i
]);
166 mrb_ary_push(mrb
, mrb_obj_value(backtrace
), btline
);
167 mrb_gc_arena_restore(mrb
, ai
);
174 mrb_exc_backtrace(mrb_state
*mrb
, mrb_value exc
)
176 struct RBasic
*backtrace
= mrb_exc_ptr(exc
)->backtrace
;
177 if (backtrace
== NULL
) {
178 return mrb_nil_value();
180 if (backtrace
->tt
== MRB_TT_ARRAY
) {
181 return mrb_obj_value(backtrace
);
183 /* unpack packed-backtrace */
184 backtrace
= mrb_unpack_backtrace(mrb
, backtrace
);
185 store_backtrace(mrb
, exc
, backtrace
);
186 return mrb_obj_value(backtrace
);
190 mrb_get_backtrace(mrb_state
*mrb
)
192 return mrb_obj_value(mrb_unpack_backtrace(mrb
, packed_backtrace(mrb
)));
198 print_backtrace(mrb_state
*mrb
, struct RObject
*exc
, struct RBasic
*ptr
)
200 struct RArray
*ary
= NULL
;
201 struct RBacktrace
*bt
= NULL
;
205 if (ptr
->tt
== MRB_TT_ARRAY
) {
206 ary
= (struct RArray
*)ptr
;
210 bt
= (struct RBacktrace
*)ptr
;
211 n
= (mrb_int
)bt
->len
;
218 fputs("trace (most recent call last):\n", stderr
);
219 for (mrb_int i
=n
-1; i
>0; i
--) {
220 if (ary
) btline
= ARY_PTR(ary
)[i
];
221 else btline
= decode_location(mrb
, &bt
->locations
[i
]);
222 if (mrb_string_p(btline
)) {
223 fprintf(stderr
, "\t[%d] ", (int)i
);
224 fwrite(RSTRING_PTR(btline
), (int)RSTRING_LEN(btline
), 1, stderr
);
228 if (ary
) btline
= ARY_PTR(ary
)[0];
229 else btline
= decode_location(mrb
, &bt
->locations
[0]);
230 if (mrb_string_p(btline
)) {
231 fwrite(RSTRING_PTR(btline
), (int)RSTRING_LEN(btline
), 1, stderr
);
236 fputs("(unknown):0: ", stderr
);
239 if (exc
== mrb
->nomem_err
) {
240 static const char nomem
[] = "Out of memory (NoMemoryError)\n";
241 fwrite(nomem
, sizeof(nomem
)-1, 1, stderr
);
244 mrb_value mesg
= mrb_exc_inspect(mrb
, mrb_obj_value(exc
));
245 fwrite(RSTRING_PTR(mesg
), RSTRING_LEN(mesg
), 1, stderr
);
250 /* mrb_print_backtrace
252 function to retrieve backtrace information from the last exception.
256 mrb_print_backtrace(mrb_state
*mrb
)
258 if (!mrb
->exc
|| mrb
->exc
->tt
!= MRB_TT_EXCEPTION
) {
262 print_backtrace(mrb
, mrb
->exc
, ((struct RException
*)mrb
->exc
)->backtrace
);
266 mrb_print_backtrace(mrb_state
*mrb
)