summaryrefslogtreecommitdiffhomepage
path: root/src/error.c
diff options
context:
space:
mode:
authorKouhei Sutou <[email protected]>2015-12-29 20:36:12 +0900
committerKouhei Sutou <[email protected]>2015-12-29 20:36:12 +0900
commita561bdb25ff51809c5de63ab7083ebf25d37cda9 (patch)
tree7c19c9a51246879dc01b1b112f483352c6f4480c /src/error.c
parente132de9e8eaf095f6f8b826e34a1c145403c3311 (diff)
downloadmruby-a561bdb25ff51809c5de63ab7083ebf25d37cda9.tar.gz
mruby-a561bdb25ff51809c5de63ab7083ebf25d37cda9.zip
Support backtrace after method calls
GitHub: fix #2902, #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.
Diffstat (limited to 'src/error.c')
-rw-r--r--src/error.c87
1 files changed, 79 insertions, 8 deletions
diff --git a/src/error.c b/src/error.c
index 15a969d93..14e4ab4d3 100644
--- a/src/error.c
+++ b/src/error.c
@@ -174,6 +174,42 @@ exc_inspect(mrb_state *mrb, mrb_value exc)
return str;
}
+void mrb_save_backtrace(mrb_state *mrb);
+mrb_value mrb_restore_backtrace(mrb_state *mrb);
+
+static mrb_value
+exc_get_backtrace(mrb_state *mrb, mrb_value exc)
+{
+ mrb_sym attr_name;
+ mrb_value backtrace;
+
+ attr_name = mrb_intern_lit(mrb, "backtrace");
+ backtrace = mrb_iv_get(mrb, exc, attr_name);
+ if (mrb_nil_p(backtrace)) {
+ if (mrb_obj_ptr(exc) == mrb->backtrace.exc && mrb->backtrace.n > 0) {
+ backtrace = mrb_restore_backtrace(mrb);
+ mrb->backtrace.n = 0;
+ mrb->backtrace.exc = 0;
+ }
+ else {
+ backtrace = mrb_exc_backtrace(mrb, exc);
+ }
+ mrb_iv_set(mrb, exc, attr_name, backtrace);
+ }
+
+ return backtrace;
+}
+
+static mrb_value
+exc_set_backtrace(mrb_state *mrb, mrb_value exc)
+{
+ mrb_value backtrace;
+
+ mrb_get_args(mrb, "o", &backtrace);
+ mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace);
+
+ return backtrace;
+}
static void
exc_debug_info(mrb_state *mrb, struct RObject *exc)
@@ -202,12 +238,52 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc)
}
}
+static void
+set_backtrace(mrb_state *mrb, mrb_value info, mrb_value bt)
+{
+ mrb_funcall(mrb, info, "set_backtrace", 1, bt);
+}
+
+static mrb_bool
+have_backtrace(mrb_state *mrb, struct RObject *exc)
+{
+ return !mrb_nil_p(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "backtrace")));
+}
+
+void
+mrb_exc_set(mrb_state *mrb, mrb_value exc)
+{
+ if (!mrb->gc.out_of_memory && mrb->backtrace.n > 0) {
+ mrb_value target_exc = mrb_nil_value();
+ if ((mrb->exc && !have_backtrace(mrb, mrb->exc))) {
+ target_exc = mrb_obj_value(mrb->exc);
+ }
+ else if (!mrb_nil_p(exc) && mrb_obj_ptr(exc) == mrb->backtrace.exc) {
+ target_exc = exc;
+ }
+ if (!mrb_nil_p(target_exc)) {
+ mrb_value backtrace;
+ backtrace = mrb_restore_backtrace(mrb);
+ set_backtrace(mrb, target_exc, backtrace);
+ }
+ }
+
+ mrb->backtrace.n = 0;
+ if (mrb_nil_p(exc)) {
+ mrb->exc = 0;
+ }
+ else {
+ mrb->exc = mrb_obj_ptr(exc);
+ }
+}
+
MRB_API mrb_noreturn void
mrb_exc_raise(mrb_state *mrb, mrb_value exc)
{
- mrb->exc = mrb_obj_ptr(exc);
+ mrb_exc_set(mrb, exc);
if (!mrb->gc.out_of_memory) {
exc_debug_info(mrb, mrb->exc);
+ mrb_save_backtrace(mrb);
}
if (!mrb->jmp) {
mrb_p(mrb, exc);
@@ -337,12 +413,6 @@ mrb_bug(mrb_state *mrb, const char *fmt, ...)
exit(EXIT_FAILURE);
}
-static void
-set_backtrace(mrb_state *mrb, mrb_value info, mrb_value bt)
-{
- mrb_funcall(mrb, info, "set_backtrace", 1, bt);
-}
-
static mrb_value
make_exception(mrb_state *mrb, int argc, const mrb_value *argv, mrb_bool isstr)
{
@@ -449,7 +519,8 @@ mrb_init_exception(mrb_state *mrb)
mrb_define_method(mrb, exception, "to_s", exc_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, exception, "message", exc_message, MRB_ARGS_NONE());
mrb_define_method(mrb, exception, "inspect", exc_inspect, MRB_ARGS_NONE());
- mrb_define_method(mrb, exception, "backtrace", mrb_exc_backtrace, MRB_ARGS_NONE());
+ mrb_define_method(mrb, exception, "backtrace", exc_get_backtrace, MRB_ARGS_NONE());
+ mrb_define_method(mrb, exception, "set_backtrace", exc_set_backtrace, MRB_ARGS_REQ(1));
mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */
runtime_error = mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */