mbed I/F binding for mruby

Dependents:   mruby_mbed_web mirb_mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers backtrace.c Source File

backtrace.c

00001 /*
00002 ** backtrace.c -
00003 **
00004 ** See Copyright Notice in mruby.h
00005 */
00006 
00007 #include <stdarg.h>
00008 #include "mruby.h"
00009 #include "mruby/variable.h"
00010 #include "mruby/proc.h"
00011 #include "mruby/array.h"
00012 #include "mruby/string.h"
00013 #include "mruby/class.h"
00014 #include "mruby/debug.h"
00015 #include "mruby/error.h"
00016 
00017 #ifdef ENABLE_STDIO
00018 
00019 typedef void (*output_stream_func)(mrb_state*, void*, int, const char*, ...);
00020 
00021 static void
00022 print_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...)
00023 {
00024   va_list ap;
00025 
00026   va_start(ap, format);
00027   vfprintf((FILE*)stream, format, ap);
00028   va_end(ap);
00029 }
00030 
00031 
00032 #define MIN_BUFSIZE 127
00033 
00034 static void
00035 get_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...)
00036 {
00037   va_list ap;
00038   mrb_value ary, str;
00039   int ai;
00040 
00041   if (level > 0) {
00042     return;
00043   }
00044 
00045   ai = mrb_gc_arena_save(mrb);
00046   ary = mrb_obj_value((struct RArray*)stream);
00047 
00048   va_start(ap, format);
00049   str = mrb_str_new(mrb, 0, vsnprintf(NULL, 0, format, ap) + 1);
00050   va_end(ap);
00051 
00052   va_start(ap, format);
00053   vsnprintf(RSTRING_PTR(str), RSTRING_LEN(str), format, ap);
00054   va_end(ap);
00055 
00056   mrb_str_resize(mrb, str, RSTRING_LEN(str) - 1);
00057   mrb_ary_push(mrb, ary, str);
00058   mrb_gc_arena_restore(mrb, ai);
00059 }
00060 
00061 static void
00062 output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *stream)
00063 {
00064   mrb_callinfo *ci;
00065   const char *filename, *method, *sep;
00066   int i, lineno, tracehead = 1;
00067 
00068   if (ciidx >= mrb->c->ciend - mrb->c->cibase)
00069     ciidx = 10; /* ciidx is broken... */
00070 
00071   for (i = ciidx; i >= 0; i--) {
00072     ci = &mrb->c->cibase[i];
00073     filename = NULL;
00074 
00075     if (!ci->proc) continue;
00076     if (MRB_PROC_CFUNC_P(ci->proc)) {
00077       continue;
00078     }
00079     else {
00080       mrb_irep *irep = ci->proc->body.irep;
00081       mrb_code *pc;
00082 
00083       if (mrb->c->cibase[i].err) {
00084         pc = mrb->c->cibase[i].err;
00085       }
00086       else if (i+1 <= ciidx) {
00087         pc = mrb->c->cibase[i+1].pc - 1;
00088       }
00089       else {
00090         pc = pc0;
00091       }
00092       filename = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq));
00093       lineno = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq));
00094     }
00095     if (lineno == -1) continue;
00096     if (ci->target_class == ci->proc->target_class)
00097       sep = ".";
00098     else
00099       sep = "#";
00100 
00101     if (!filename) {
00102       filename = "(unknown)";
00103     }
00104 
00105     if (tracehead) {
00106       func(mrb, stream, 1, "trace:\n");
00107       tracehead = 0;
00108     }
00109     method = mrb_sym2name(mrb, ci->mid);
00110     if (method) {
00111       const char *cn = mrb_class_name(mrb, ci->proc->target_class);
00112 
00113       if (cn) {
00114         func(mrb, stream, 1, "\t[%d] ", i);
00115         func(mrb, stream, 0, "%s:%d:in %s%s%s", filename, lineno, cn, sep, method);
00116         func(mrb, stream, 1, "\n");
00117       }
00118       else {
00119         func(mrb, stream, 1, "\t[%d] ", i);
00120         func(mrb, stream, 0, "%s:%d:in %s", filename, lineno, method);
00121         func(mrb, stream, 1, "\n");
00122       }
00123     }
00124     else {
00125         func(mrb, stream, 1, "\t[%d] ", i);
00126         func(mrb, stream, 0, "%s:%d", filename, lineno);
00127         func(mrb, stream, 1, "\n");
00128     }
00129   }
00130 }
00131 
00132 static void
00133 exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream)
00134 {
00135   output_backtrace(mrb, mrb_fixnum(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "ciidx"))),
00136                    (mrb_code*)mrb_cptr(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "lastpc"))),
00137                    func, stream);
00138 }
00139 
00140 /* mrb_print_backtrace/mrb_get_backtrace:
00141 
00142    function to retrieve backtrace information from the exception.
00143    note that if you call method after the exception, call stack will be
00144    overwritten.  So invoke these functions just after detecting exceptions.
00145 */
00146 
00147 MRB_API void
00148 mrb_print_backtrace(mrb_state *mrb)
00149 {
00150   if (!mrb->exc || mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), E_SYSSTACK_ERROR)) {
00151     return;
00152   }
00153   exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)stderr);
00154 }
00155 
00156 MRB_API mrb_value
00157 mrb_exc_backtrace(mrb_state *mrb, mrb_value self)
00158 {
00159   mrb_value ary;
00160 
00161   ary = mrb_ary_new(mrb);
00162   exc_output_backtrace(mrb, mrb_obj_ptr(self), get_backtrace_i, (void*)mrb_ary_ptr(ary));
00163 
00164   return ary;
00165 }
00166 
00167 MRB_API mrb_value
00168 mrb_get_backtrace(mrb_state *mrb)
00169 {
00170   mrb_value ary;
00171   mrb_callinfo *ci = mrb->c->ci;
00172   mrb_code *pc = ci->pc;
00173   mrb_int ciidx = (mrb_int)(ci - mrb->c->cibase - 1);
00174 
00175   if (ciidx < 0) ciidx = 0;
00176   ary = mrb_ary_new(mrb);
00177   output_backtrace(mrb, ciidx, pc, get_backtrace_i, (void*)mrb_ary_ptr(ary));
00178 
00179   return ary;
00180 }
00181 
00182 #else
00183 
00184 MRB_API void
00185 mrb_print_backtrace(mrb_state *mrb)
00186 {
00187 }
00188 
00189 MRB_API mrb_value
00190 mrb_exc_backtrace(mrb_state *mrb, mrb_value self)
00191 {
00192   return mrb_ary_new(mrb);
00193 }
00194 
00195 MRB_API mrb_value
00196 mrb_get_backtrace(mrb_state *mrb)
00197 {
00198   return mrb_ary_new(mrb);
00199 }
00200 
00201 #endif
00202