Upgrade to 1.46.0
[platform/upstream/nghttp2.git] / third-party / mruby / src / backtrace.c
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 <mruby/error.h>
15 #include <mruby/numeric.h>
16 #include <mruby/data.h>
17
18 struct backtrace_location {
19   int32_t lineno;
20   mrb_sym method_id;
21   const char *filename;
22 };
23
24 typedef void (*each_backtrace_func)(mrb_state*, const struct backtrace_location*, void*);
25
26 static const mrb_data_type bt_type = { "Backtrace", mrb_free };
27
28 mrb_value mrb_exc_inspect(mrb_state *mrb, mrb_value exc);
29 mrb_value mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace);
30
31 static void
32 each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, const mrb_code *pc0, each_backtrace_func func, void *data)
33 {
34   ptrdiff_t i;
35
36   if (ciidx >= mrb->c->ciend - mrb->c->cibase)
37     ciidx = 10; /* ciidx is broken... */
38
39   for (i=ciidx; i >= 0; i--) {
40     struct backtrace_location loc;
41     mrb_callinfo *ci;
42     mrb_irep *irep;
43     const mrb_code *pc;
44
45     ci = &mrb->c->cibase[i];
46
47     if (!ci->proc) continue;
48     if (MRB_PROC_CFUNC_P(ci->proc)) continue;
49
50     irep = ci->proc->body.irep;
51     if (!irep) continue;
52
53     if (mrb->c->cibase[i].err) {
54       pc = mrb->c->cibase[i].err;
55     }
56     else if (i+1 <= ciidx) {
57       if (!mrb->c->cibase[i + 1].pc) continue;
58       pc = &mrb->c->cibase[i+1].pc[-1];
59     }
60     else {
61       pc = pc0;
62     }
63
64     loc.lineno = mrb_debug_get_line(mrb, irep, pc - irep->iseq);
65     if (loc.lineno == -1) continue;
66
67     loc.filename = mrb_debug_get_filename(mrb, irep, pc - irep->iseq);
68     if (!loc.filename) {
69       loc.filename = "(unknown)";
70     }
71
72     loc.method_id = ci->mid;
73     func(mrb, &loc, data);
74   }
75 }
76
77 #ifndef MRB_DISABLE_STDIO
78
79 static void
80 print_backtrace(mrb_state *mrb, struct RObject *exc, mrb_value backtrace)
81 {
82   mrb_int i;
83   mrb_int n = RARRAY_LEN(backtrace);
84   mrb_value *loc, mesg;
85   FILE *stream = stderr;
86
87   if (n != 0) {
88     fprintf(stream, "trace (most recent call last):\n");
89     for (i=n-1,loc=&RARRAY_PTR(backtrace)[i]; i>0; i--,loc--) {
90       if (mrb_string_p(*loc)) {
91         fprintf(stream, "\t[%d] %.*s\n",
92                 (int)i, (int)RSTRING_LEN(*loc), RSTRING_PTR(*loc));
93       }
94     }
95     if (mrb_string_p(*loc)) {
96       fprintf(stream, "%.*s: ", (int)RSTRING_LEN(*loc), RSTRING_PTR(*loc));
97     }
98   }
99   mesg = mrb_exc_inspect(mrb, mrb_obj_value(exc));
100   fprintf(stream, "%.*s\n", (int)RSTRING_LEN(mesg), RSTRING_PTR(mesg));
101 }
102
103 /* mrb_print_backtrace
104
105    function to retrieve backtrace information from the last exception.
106 */
107
108 MRB_API void
109 mrb_print_backtrace(mrb_state *mrb)
110 {
111   mrb_value backtrace;
112
113   if (!mrb->exc) {
114     return;
115   }
116
117   backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace"));
118   if (mrb_nil_p(backtrace)) return;
119   if (!mrb_array_p(backtrace)) backtrace = mrb_unpack_backtrace(mrb, backtrace);
120   print_backtrace(mrb, mrb->exc, backtrace);
121 }
122 #else
123
124 MRB_API void
125 mrb_print_backtrace(mrb_state *mrb)
126 {
127 }
128
129 #endif
130
131 static void
132 count_backtrace_i(mrb_state *mrb,
133                  const struct backtrace_location *loc,
134                  void *data)
135 {
136   int *lenp = (int*)data;
137
138   (*lenp)++;
139 }
140
141 static void
142 pack_backtrace_i(mrb_state *mrb,
143                  const struct backtrace_location *loc,
144                  void *data)
145 {
146   struct backtrace_location **pptr = (struct backtrace_location**)data;
147   struct backtrace_location *ptr = *pptr;
148
149   *ptr = *loc;
150   *pptr = ptr+1;
151 }
152
153 static mrb_value
154 packed_backtrace(mrb_state *mrb)
155 {
156   struct RData *backtrace;
157   ptrdiff_t ciidx = mrb->c->ci - mrb->c->cibase;
158   int len = 0;
159   int size;
160   void *ptr;
161
162   each_backtrace(mrb, ciidx, mrb->c->ci->pc, count_backtrace_i, &len);
163   size = len * sizeof(struct backtrace_location);
164   ptr = mrb_malloc(mrb, size);
165   backtrace = mrb_data_object_alloc(mrb, NULL, ptr, &bt_type);
166   backtrace->flags = (uint32_t)len;
167   each_backtrace(mrb, ciidx, mrb->c->ci->pc, pack_backtrace_i, &ptr);
168   return mrb_obj_value(backtrace);
169 }
170
171 void
172 mrb_keep_backtrace(mrb_state *mrb, mrb_value exc)
173 {
174   mrb_sym sym = mrb_intern_lit(mrb, "backtrace");
175   mrb_value backtrace;
176   int ai;
177
178   if (mrb_iv_defined(mrb, exc, sym)) return;
179   ai = mrb_gc_arena_save(mrb);
180   backtrace = packed_backtrace(mrb);
181   mrb_iv_set(mrb, exc, sym, backtrace);
182   mrb_gc_arena_restore(mrb, ai);
183 }
184
185 mrb_value
186 mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
187 {
188   const struct backtrace_location *bt;
189   mrb_int n, i;
190   int ai;
191
192   if (mrb_nil_p(backtrace)) {
193   empty_backtrace:
194     return mrb_ary_new_capa(mrb, 0);
195   }
196   if (mrb_array_p(backtrace)) return backtrace;
197   bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, backtrace, &bt_type);
198   if (bt == NULL) goto empty_backtrace;
199   n = (mrb_int)RDATA(backtrace)->flags;
200   backtrace = mrb_ary_new_capa(mrb, n);
201   ai = mrb_gc_arena_save(mrb);
202   for (i = 0; i < n; i++) {
203     const struct backtrace_location *entry = &bt[i];
204     mrb_value btline;
205
206     btline = mrb_format(mrb, "%s:%d", entry->filename, (int)entry->lineno);
207     if (entry->method_id != 0) {
208       mrb_str_cat_lit(mrb, btline, ":in ");
209       mrb_str_cat_cstr(mrb, btline, mrb_sym_name(mrb, entry->method_id));
210     }
211     mrb_ary_push(mrb, backtrace, btline);
212     mrb_gc_arena_restore(mrb, ai);
213   }
214
215   return backtrace;
216 }
217
218 MRB_API mrb_value
219 mrb_exc_backtrace(mrb_state *mrb, mrb_value exc)
220 {
221   mrb_sym attr_name;
222   mrb_value backtrace;
223
224   attr_name = mrb_intern_lit(mrb, "backtrace");
225   backtrace = mrb_iv_get(mrb, exc, attr_name);
226   if (mrb_nil_p(backtrace) || mrb_array_p(backtrace)) {
227     return backtrace;
228   }
229   backtrace = mrb_unpack_backtrace(mrb, backtrace);
230   mrb_iv_set(mrb, exc, attr_name, backtrace);
231   return backtrace;
232 }
233
234 MRB_API mrb_value
235 mrb_get_backtrace(mrb_state *mrb)
236 {
237   return mrb_unpack_backtrace(mrb, packed_backtrace(mrb));
238 }