Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
backtrace.c
Go to the documentation of this file.
1 /*
2 ** backtrace.c -
3 **
4 ** See Copyright Notice in mruby.h
5 */
6 
7 #include "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 <stdarg.h>
15 
16 typedef void (*output_stream_func)(mrb_state*, void*, int, const char*, ...);
17 
18 #ifdef ENABLE_STDIO
19 static void
20 print_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...)
21 {
22  va_list ap;
23 
24  va_start(ap, format);
25  vfprintf((FILE*)stream, format, ap);
26  va_end(ap);
27 }
28 #endif
29 
30 #define MIN_BUFSIZE 127
31 
32 static void
33 get_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...)
34 {
35  va_list ap;
36  mrb_value ary, str;
37  int ai;
38 
39  if (level > 0) {
40  return;
41  }
42 
43  ai = mrb_gc_arena_save(mrb);
44  ary = mrb_obj_value((struct RArray*)stream);
45 
46  va_start(ap, format);
47  str = mrb_str_new(mrb, 0, vsnprintf(NULL, 0, format, ap) + 1);
48  va_end(ap);
49 
50  va_start(ap, format);
51  vsnprintf(RSTRING_PTR(str), RSTRING_LEN(str), format, ap);
52  va_end(ap);
53 
54  mrb_str_resize(mrb, str, RSTRING_LEN(str) - 1);
55  mrb_ary_push(mrb, ary, str);
56  mrb_gc_arena_restore(mrb, ai);
57 }
58 
59 static void
60 mrb_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream)
61 {
62  mrb_callinfo *ci;
63  mrb_int ciidx;
64  const char *filename, *method, *sep;
65  int i, line;
66 
67  func(mrb, stream, 1, "trace:\n");
68  ciidx = mrb_fixnum(mrb_obj_iv_get(mrb, exc, mrb_intern2(mrb, "ciidx", 5)));
69  if (ciidx >= mrb->c->ciend - mrb->c->cibase)
70  ciidx = 10; /* ciidx is broken... */
71 
72  for (i = ciidx; i >= 0; i--) {
73  ci = &mrb->c->cibase[i];
74  filename = NULL;
75  line = -1;
76 
77  if (MRB_PROC_CFUNC_P(ci->proc)) {
78  continue;
79  }
80  if(!MRB_PROC_CFUNC_P(ci->proc)) {
81  mrb_irep *irep = ci->proc->body.irep;
82  mrb_code *pc;
83 
84  if (i+1 <= ciidx) {
85  pc = mrb->c->cibase[i+1].pc;
86  }
87  else {
88  pc = (mrb_code*)mrb_cptr(mrb_obj_iv_get(mrb, exc, mrb_intern2(mrb, "lastpc", 6)));
89  }
90  filename = mrb_debug_get_filename(irep, pc - irep->iseq - 1);
91  line = mrb_debug_get_line(irep, pc - irep->iseq - 1);
92  }
93  if (line == -1) continue;
94  if (ci->target_class == ci->proc->target_class)
95  sep = ".";
96  else
97  sep = "#";
98 
99  if (!filename) {
100  filename = "(unknown)";
101  }
102 
103  method = mrb_sym2name(mrb, ci->mid);
104  if (method) {
105  const char *cn = mrb_class_name(mrb, ci->proc->target_class);
106 
107  if (cn) {
108  func(mrb, stream, 1, "\t[%d] ", i);
109  func(mrb, stream, 0, "%s:%d:in %s%s%s", filename, line, cn, sep, method);
110  func(mrb, stream, 1, "\n");
111  }
112  else {
113  func(mrb, stream, 1, "\t[%d] ", i);
114  func(mrb, stream, 0, "%s:%d:in %s", filename, line, method);
115  func(mrb, stream, 1, "\n");
116  }
117  }
118  else {
119  func(mrb, stream, 1, "\t[%d] ", i);
120  func(mrb, stream, 0, "%s:%d", filename, line);
121  func(mrb, stream, 1, "\n");
122  }
123  }
124 }
125 
126 void
128 {
129 #ifdef ENABLE_STDIO
130  mrb_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)stderr);
131 #endif
132 }
133 
134 mrb_value
136 {
137  mrb_value ary;
138 
139  ary = mrb_ary_new(mrb);
140  mrb_output_backtrace(mrb, mrb_obj_ptr(self), get_backtrace_i, (void*)mrb_ary_ptr(ary));
141 
142  return ary;
143 }