backtracie 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8ef71cf1cfd9ca005e0ecb0fe7434a9c8121bc46fc70c34209c74ecad1c705c
4
- data.tar.gz: 723d3c66fb919432481428609a8316243e160805ff4142757f9ec65cf254f2d8
3
+ metadata.gz: e804bb5ef00e6d7a3f9b7a0a57468026f3f58dbc36671e28d49f3a65e0cb6ad5
4
+ data.tar.gz: c1c2851579f8eab2bc9fbbdd8b6efa82636d48e141f8a7ea505b9f8e5329b7ab
5
5
  SHA512:
6
- metadata.gz: ceffbd048f44b6563298f301bde8dd498c1ea92be244e90a42390f4d387d58ef3837d936afbd4c63376ebfa2a20984989a2f8e1426ab9cccc30fc8c844c78a68
7
- data.tar.gz: cc6e34b726934f01ae92bf089954bace7063619ce8e3d71b1d1eea610d153f10641257399388ef70bcd7730b1aea89039d4f7e6e2b960f2b09e937fd4d802969
6
+ metadata.gz: 2bd4613237a00b0c872ba229cb0401af1ea9d9eb9ea559f3ded3a44779c6e5e6c4599a7639abac19c1e3e8069c5dc44de8d157f32dbb3bed160d3d987c8c8458
7
+ data.tar.gz: 3846369e8865a9ec7d8c2589b3d1eb3737c242479ac3df8bb9b781ae64fdb0ed3d10a9da503cae7c5a2568b879251fbe5d74e67d88ca9dbfba98ac56a591a5dd
data/README.adoc CHANGED
@@ -164,6 +164,9 @@ Other interesting links on this matter:
164
164
  * https://ruby-hacking-guide.github.io/: Ruby Hacking Guide
165
165
  ** This is one of the most through and deep guides out there to the MRI internals. Very detailed and in depth, but outdated.
166
166
  * http://patshaughnessy.net/ruby-under-a-microscope: Book with a really good introduction to MRI internals.
167
+ * https://www.youtube.com/watch?v=QDbj4Y0E5xo: Video on the implementation of backtrace APIs in MRI and how partial backtraces were optimized in 3.0/3.1.
168
+ * https://github.com/ruby/ruby/pull/4336 and https://github.com/ruby/ruby/pull/4108: Segfaults due to execution context changes to support Ractors in Ruby 3.0
169
+ * https://kumisystems.dl.sourceforge.net/project/elftoolchain/Documentation/libelf-by-example/20120308/libelf-by-example.pdf: Looking inside elf files 😱
167
170
 
168
171
  My blog posts on better backtraces:
169
172
 
@@ -16,21 +16,30 @@
16
16
  // You should have received a copy of the GNU Lesser General Public License
17
17
  // along with backtracie. If not, see <http://www.gnu.org/licenses/>.
18
18
 
19
- #include <stdbool.h>
20
-
21
- #include "ruby/ruby.h"
22
- #include "ruby/debug.h"
23
-
24
19
  #include "extconf.h"
25
20
 
26
- #include "ruby_shards.h"
21
+ #if 0
22
+ // Disabled until this is fixed to not break Windows
23
+ #include <dlfcn.h>
24
+ #endif
27
25
 
28
- #define MAX_STACK_DEPTH 2000 // FIXME: Need to handle when this is not enough
26
+ #include <ruby.h>
27
+ #include <ruby/debug.h>
28
+ #include <ruby/intern.h>
29
+ #include <stdbool.h>
30
+ #include <stdio.h>
31
+
32
+ #include "backtracie_private.h"
33
+ #include "public/backtracie.h"
29
34
 
30
35
  #define VALUE_COUNT(array) (sizeof(array) / sizeof(VALUE))
31
- #define SAFE_NAVIGATION(function, maybe_nil) ((maybe_nil) != Qnil ? function(maybe_nil) : Qnil)
36
+ #define SAFE_NAVIGATION(function, maybe_nil) \
37
+ ((maybe_nil) != Qnil ? function(maybe_nil) : Qnil)
38
+
39
+ // non-static, used in backtracie_frames.c
40
+ VALUE backtracie_main_object_instance = Qnil;
41
+ VALUE backtracie_frame_wrapper_class = Qnil;
32
42
 
33
- static VALUE main_object_instance = Qnil;
34
43
  static ID ensure_object_is_thread_id;
35
44
  static ID to_s_id;
36
45
  static VALUE backtracie_module = Qnil;
@@ -38,80 +47,108 @@ static VALUE backtracie_location_class = Qnil;
38
47
 
39
48
  static VALUE primitive_caller_locations(VALUE self);
40
49
  static VALUE primitive_backtrace_locations(VALUE self, VALUE thread);
41
- static VALUE collect_backtrace_locations(VALUE self, VALUE thread, int ignored_stack_top_frames);
42
- inline static VALUE new_location(VALUE absolute_path, VALUE base_label, VALUE label, VALUE lineno, VALUE path, VALUE qualified_method_name, VALUE debug);
43
- static VALUE ruby_frame_to_location(raw_location *the_location);
44
- static VALUE qualified_method_name_for_location(raw_location *the_location);
45
- static VALUE cfunc_frame_to_location(raw_location *the_location, raw_location *last_ruby_location);
46
- static VALUE frame_from_location(raw_location *the_location);
47
- static VALUE qualified_method_name_for_block(raw_location *the_location);
48
- static VALUE qualified_method_name_from_self(raw_location *the_location);
49
- static bool is_self_class_singleton(raw_location *the_location);
50
- static bool is_defined_class_a_refinement(raw_location *the_location);
51
- static VALUE debug_raw_location(raw_location *the_location);
50
+ static VALUE collect_backtrace_locations(VALUE self, VALUE thread,
51
+ int ignored_stack_top_frames);
52
+ inline static VALUE new_location(VALUE absolute_path, VALUE base_label,
53
+ VALUE label, VALUE lineno, VALUE path,
54
+ VALUE qualified_method_name,
55
+ VALUE path_is_synthetic, VALUE debug);
56
+ static VALUE frame_to_location(const raw_location *raw_loc,
57
+ const raw_location *prev_ruby_loc);
58
+ static VALUE debug_raw_location(const raw_location *the_location);
52
59
  static VALUE debug_frame(VALUE frame);
60
+ static VALUE cfunc_function_info(const raw_location *the_location);
53
61
  static inline VALUE to_boolean(bool value);
54
62
 
63
+ BACKTRACIE_API
55
64
  void Init_backtracie_native_extension(void) {
56
- main_object_instance = rb_funcall(rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING")), rb_intern("eval"), 1, rb_str_new2("self"));
65
+ backtracie_main_object_instance =
66
+ rb_funcall(rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING")),
67
+ rb_intern("eval"), 1, rb_str_new2("self"));
57
68
  ensure_object_is_thread_id = rb_intern("ensure_object_is_thread");
58
69
  to_s_id = rb_intern("to_s");
59
70
 
60
71
  backtracie_module = rb_const_get(rb_cObject, rb_intern("Backtracie"));
61
72
  rb_global_variable(&backtracie_module);
62
73
 
63
- rb_define_module_function(backtracie_module, "backtrace_locations", primitive_backtrace_locations, 1);
74
+ rb_define_module_function(backtracie_module, "backtrace_locations",
75
+ primitive_backtrace_locations, 1);
64
76
 
65
- backtracie_location_class = rb_const_get(backtracie_module, rb_intern("Location"));
77
+ backtracie_location_class =
78
+ rb_const_get(backtracie_module, rb_intern("Location"));
66
79
  rb_global_variable(&backtracie_location_class);
67
80
 
68
- VALUE backtracie_primitive_module = rb_define_module_under(backtracie_module, "Primitive");
81
+ VALUE backtracie_primitive_module =
82
+ rb_define_module_under(backtracie_module, "Primitive");
69
83
 
70
- rb_define_module_function(backtracie_primitive_module, "caller_locations", primitive_caller_locations, 0);
71
- }
84
+ rb_define_module_function(backtracie_primitive_module, "caller_locations",
85
+ primitive_caller_locations, 0);
72
86
 
73
- // Get array of Backtracie::Locations for a given thread; if thread is nil, returns for the current thread
74
- static VALUE collect_backtrace_locations(VALUE self, VALUE thread, int ignored_stack_top_frames) {
75
- int stack_depth = 0;
76
- raw_location raw_locations[MAX_STACK_DEPTH];
87
+ backtracie_frame_wrapper_class =
88
+ rb_define_class_under(backtracie_module, "FrameWrapper", rb_cObject);
89
+ // this class should only be instantiated via backtracie_frame_wrapper_new
90
+ rb_undef_alloc_func(backtracie_frame_wrapper_class);
77
91
 
78
- if (thread == Qnil) {
79
- // Get for own thread
80
- stack_depth = backtracie_rb_profile_frames(MAX_STACK_DEPTH, raw_locations);
81
- } else {
82
- stack_depth = backtracie_rb_profile_frames_for_thread(thread, MAX_STACK_DEPTH, raw_locations);
83
- if (stack_depth == 0 && !backtracie_is_thread_alive(thread)) return Qnil;
92
+ // Create some classes which are used to simulate interesting scenarios in
93
+ // tests
94
+ backtracie_init_c_test_helpers(backtracie_module);
95
+ }
96
+
97
+ // Get array of Backtracie::Locations for a given thread; if thread is nil,
98
+ // returns for the current thread
99
+ static VALUE collect_backtrace_locations(VALUE self, VALUE thread,
100
+ int ignored_stack_top_frames) {
101
+ if (!RTEST(thread)) {
102
+ thread = rb_thread_current();
84
103
  }
85
104
 
86
- VALUE locations = rb_ary_new_capa(stack_depth - ignored_stack_top_frames);
105
+ // To maintain compatability with the Ruby thread backtrace behavior, if a
106
+ // thread is dead, then return nil.
107
+ if (!backtracie_is_thread_alive(thread)) {
108
+ return Qnil;
109
+ }
87
110
 
88
- // MRI does not give us the path or line number for frames implemented using C code. The convention in
89
- // Kernel#caller_locations is to instead use the path and line number of the last Ruby frame seen.
90
- // Thus, we keep that frame here to able to replicate that behavior.
91
- // (This is why we also iterate the frames array backwards below -- so that it's easier to keep the last_ruby_frame)
92
- raw_location *last_ruby_location = 0;
111
+ int raw_frame_count = backtracie_frame_count_for_thread(thread);
93
112
 
94
- for (int i = stack_depth - 1; i >= ignored_stack_top_frames; i--) {
95
- VALUE location = Qnil;
113
+ // Allocate memory for the raw_locations, and keep track of it on the Ruby
114
+ // heap so it will be GC'd even if we raise.
115
+ // Zero the frame array so our mark function doesn't get confused too.
116
+ VALUE frame_wrapper = backtracie_frame_wrapper_new(raw_frame_count);
117
+ raw_location *raw_frames = backtracie_frame_wrapper_frames(frame_wrapper);
118
+ int *raw_frames_len = backtracie_frame_wrapper_len(frame_wrapper);
96
119
 
97
- if (raw_locations[i].is_ruby_frame) {
98
- last_ruby_location = &raw_locations[i];
99
- location = ruby_frame_to_location(&raw_locations[i]);
100
- } else {
101
- location = cfunc_frame_to_location(&raw_locations[i], last_ruby_location);
120
+ for (int i = ignored_stack_top_frames; i < raw_frame_count; i++) {
121
+ bool valid_frame = backtracie_capture_frame_for_thread(
122
+ thread, i, &raw_frames[*raw_frames_len]);
123
+ if (valid_frame) {
124
+ (*raw_frames_len)++;
102
125
  }
126
+ }
103
127
 
104
- rb_ary_store(locations, i - ignored_stack_top_frames, location);
128
+ VALUE rb_locations = rb_ary_new_capa(*raw_frames_len);
129
+ // Iterate _backwards_ through the frames, so we can keep track of the
130
+ // previous ruby frame for a C frame. This is required because C frames don't
131
+ // have filenames or line numbers; we must instead use the filename/lineno of
132
+ // the _caller_ of the function.
133
+ raw_location *prev_ruby_loc = NULL;
134
+ for (int i = *raw_frames_len - 1; i >= 0; i--) {
135
+ if (raw_frames[i].is_ruby_frame) {
136
+ prev_ruby_loc = &raw_frames[i];
137
+ }
138
+ VALUE rb_loc = frame_to_location(&raw_frames[i], prev_ruby_loc);
139
+ rb_ary_store(rb_locations, i, rb_loc);
105
140
  }
106
141
 
107
- return locations;
142
+ RB_GC_GUARD(frame_wrapper);
143
+ return rb_locations;
108
144
  }
109
145
 
110
146
  static VALUE primitive_caller_locations(VALUE self) {
111
147
  // Ignore:
112
148
  // * the current stack frame (native)
113
149
  // * the Backtracie.caller_locations that called us
114
- // * the frame from the caller itself (since we're replicating the semantics of Kernel#caller_locations)
150
+ // * the frame from the caller itself (since we're replicating the semantics
151
+ // of Kernel#caller_locations)
115
152
  int ignored_stack_top_frames = 3;
116
153
 
117
154
  return collect_backtrace_locations(self, Qnil, ignored_stack_top_frames);
@@ -125,221 +162,121 @@ static VALUE primitive_backtrace_locations(VALUE self, VALUE thread) {
125
162
  return collect_backtrace_locations(self, thread, ignored_stack_top_frames);
126
163
  }
127
164
 
128
- inline static VALUE new_location(
129
- VALUE absolute_path,
130
- VALUE base_label,
131
- VALUE label,
132
- VALUE lineno,
133
- VALUE path,
134
- VALUE qualified_method_name,
135
- VALUE debug
136
- ) {
137
- VALUE arguments[] = { absolute_path, base_label, label, lineno, path, qualified_method_name, debug };
138
- return rb_class_new_instance(VALUE_COUNT(arguments), arguments, backtracie_location_class);
139
- }
140
-
141
- static VALUE ruby_frame_to_location(raw_location *the_location) {
142
- VALUE frame = frame_from_location(the_location);
143
-
144
- return new_location(
145
- rb_profile_frame_absolute_path(frame),
146
- rb_profile_frame_base_label(frame),
147
- rb_profile_frame_label(the_location->iseq),
148
- INT2FIX(the_location->line_number),
149
- rb_profile_frame_path(frame),
150
- qualified_method_name_for_location(the_location),
151
- debug_raw_location(the_location)
152
- );
153
- }
154
-
155
- static VALUE qualified_method_name_for_location(raw_location *the_location) {
156
- VALUE frame = frame_from_location(the_location);
157
- VALUE defined_class = backtracie_defined_class(the_location);
158
- VALUE qualified_method_name = Qnil;
159
-
160
- if (is_defined_class_a_refinement(the_location)) {
161
- qualified_method_name = backtracie_refinement_name(the_location);
162
- rb_str_concat(qualified_method_name, rb_str_new2("#"));
163
- rb_str_concat(qualified_method_name, rb_profile_frame_label(frame));
164
- } else if (is_self_class_singleton(the_location)) {
165
- qualified_method_name = qualified_method_name_from_self(the_location);
166
- } else if (backtracie_iseq_is_block(the_location) || backtracie_iseq_is_eval(the_location)) {
167
- qualified_method_name = qualified_method_name_for_block(the_location);
168
- } else if (defined_class != Qnil && rb_mod_name(defined_class) == Qnil) {
169
- // Instance of an anonymous class. Let's find it a name
170
- VALUE superclass = defined_class;
171
- VALUE superclass_name = Qnil;
172
- do {
173
- superclass = RCLASS_SUPER(superclass);
174
- superclass_name = rb_mod_name(superclass);
175
- } while (superclass_name == Qnil);
176
-
177
- qualified_method_name = rb_str_new2("");
178
- rb_str_concat(qualified_method_name, superclass_name);
179
- rb_str_concat(qualified_method_name, rb_str_new2("$anonymous#"));
180
- rb_str_concat(qualified_method_name, rb_profile_frame_base_label(frame_from_location(the_location)));
181
- } else {
182
- qualified_method_name = backtracie_rb_profile_frame_qualified_method_name(frame);
183
-
184
- if (qualified_method_name == Qnil) {
185
- qualified_method_name = qualified_method_name_from_self(the_location);
186
- }
187
- }
188
-
189
- if (backtracie_iseq_is_block(the_location) || backtracie_iseq_is_eval(the_location)) {
190
- rb_str_concat(qualified_method_name, rb_str_new2("{block}"));
191
- }
192
-
193
- return qualified_method_name;
194
- }
195
-
196
- static VALUE cfunc_frame_to_location(raw_location *the_location, raw_location *last_ruby_location) {
197
- VALUE last_ruby_frame =
198
- last_ruby_location != 0 ? frame_from_location(last_ruby_location) : Qnil;
199
-
200
- // Replaces label and base_label in cfuncs
201
- VALUE method_name = backtracie_rb_profile_frame_method_name(the_location->callable_method_entry);
202
-
203
- return new_location(
204
- SAFE_NAVIGATION(rb_profile_frame_absolute_path, last_ruby_frame),
205
- method_name,
206
- method_name,
207
- last_ruby_location != 0 ? INT2FIX(last_ruby_location->line_number) : Qnil,
208
- SAFE_NAVIGATION(rb_profile_frame_path, last_ruby_frame),
209
- backtracie_rb_profile_frame_qualified_method_name(the_location->callable_method_entry),
210
- debug_raw_location(the_location)
211
- );
212
- }
213
-
214
- static VALUE frame_from_location(raw_location *the_location) {
215
- return \
216
- the_location->should_use_iseq ||
217
- // This one is somewhat weird, but the regular MRI Ruby APIs seem to pick the iseq for evals as well
218
- backtracie_iseq_is_eval(the_location) ?
219
- the_location->iseq : the_location->callable_method_entry;
220
- }
221
-
222
- static VALUE qualified_method_name_for_block(raw_location *the_location) {
223
- VALUE class_name = backtracie_rb_profile_frame_classpath(the_location->callable_method_entry);
224
- VALUE method_name = backtracie_called_id(the_location);
225
- VALUE is_singleton_method = rb_profile_frame_singleton_method_p(the_location->iseq);
226
-
227
- VALUE name = rb_str_new2("");
228
- rb_str_concat(name, class_name);
229
- rb_str_concat(name, is_singleton_method ? rb_str_new2(".") : rb_str_new2("#"));
230
- rb_str_concat(name, rb_sym2str(method_name));
231
-
232
- return name;
165
+ inline static VALUE new_location(VALUE absolute_path, VALUE base_label,
166
+ VALUE label, VALUE lineno, VALUE path,
167
+ VALUE qualified_method_name,
168
+ VALUE path_is_synthetic, VALUE debug) {
169
+ VALUE arguments[] = {
170
+ absolute_path, base_label, label, lineno, path,
171
+ qualified_method_name, path_is_synthetic, debug};
172
+ return rb_class_new_instance(VALUE_COUNT(arguments), arguments,
173
+ backtracie_location_class);
233
174
  }
234
175
 
235
- static VALUE qualified_method_name_from_self(raw_location *the_location) {
236
- if (the_location->self == Qnil) return Qnil;
237
-
238
- VALUE self_class = rb_class_of(the_location->self);
239
- bool is_self_class_singleton = FL_TEST(self_class, FL_SINGLETON);
240
-
241
- VALUE name = rb_str_new2("");
242
- if (is_self_class_singleton) {
243
- if (the_location->self == main_object_instance) {
244
- rb_str_concat(name, rb_str_new2("Object$<main>#"));
245
- } else {
246
- // Crawl up the hierarchy to find a real class
247
- VALUE the_class = rb_class_real(self_class);
248
-
249
- // This is similar to BetterBacktraceHelper's self_class_module_or_class?
250
- // If the real class of this object is the actual class Class or the class Module,
251
- // it means that this is a method being directly called on a given Class/Module,
252
- // e.g. Kernel.puts or BasicObject.name. In this case, Ruby already sets the name
253
- // correctly, so we just delegate.
254
- if (the_class == rb_cModule || the_class == rb_cClass) {
255
- // Is the class/module is anonymous?
256
- if (rb_mod_name(the_location->self) == Qnil) {
257
- rb_str_concat(name, rb_funcall(the_class, to_s_id, 0));
258
- rb_str_concat(name, rb_str_new2("$singleton."));
259
- } else {
260
- return SAFE_NAVIGATION(backtracie_rb_profile_frame_qualified_method_name, the_location->callable_method_entry);
261
- }
262
- } else {
263
- rb_str_concat(name, rb_funcall(the_class, to_s_id, 0));
264
- rb_str_concat(name, rb_str_new2("$singleton#"));
265
- }
266
- }
267
- } else {
268
- // Not very sure if this branch of the if is ever reached, and if it would be for a instance or static call, so
269
- // let's just have these defaults and revisit as needed
270
- rb_str_concat(name, rb_funcall(self_class, to_s_id, 0));
271
- rb_str_concat(name, rb_str_new2("#"));
272
- }
176
+ static VALUE frame_to_location(const raw_location *raw_loc,
177
+ const raw_location *prev_ruby_loc) {
178
+ // If raw_loc != prev_ruby_loc, that means this location is a cfunc, and not a
179
+ // ruby frame; so, it doesn't _actually_ have a path. For compatability with
180
+ // Thread#backtrace et. al., we return the frame of the previous
181
+ // actually-a-ruby-frame location. When we do that, we set a flag
182
+ // path_is_synthetic on the location so that interested callers can know if
183
+ // that's the case.
184
+ VALUE filename_abs;
185
+ VALUE filename_rel;
186
+ VALUE line_number;
187
+ VALUE path_is_synthetic;
188
+ if (prev_ruby_loc) {
189
+ filename_abs = backtracie_frame_filename_rbstr(prev_ruby_loc, true);
190
+ filename_rel = backtracie_frame_filename_rbstr(prev_ruby_loc, false);
191
+ line_number = INT2NUM(backtracie_frame_line_number(prev_ruby_loc));
192
+ path_is_synthetic = (raw_loc != prev_ruby_loc) ? Qtrue : Qfalse;
273
193
 
274
- if (backtracie_iseq_is_block(the_location) || backtracie_iseq_is_eval(the_location)) {
275
- // Nothing to do, {block} will be appended in qualified_method_name_for_location which called us
276
194
  } else {
277
- rb_str_concat(name, rb_profile_frame_base_label(frame_from_location(the_location)));
195
+ filename_abs = rb_str_new2("(in native code)");
196
+ filename_rel = rb_str_dup(filename_abs);
197
+ line_number = INT2NUM(0);
198
+ path_is_synthetic = Qtrue;
278
199
  }
279
200
 
280
- return name;
281
- }
282
-
283
- static bool is_self_class_singleton(raw_location *the_location) {
284
- return the_location->self != Qnil && FL_TEST(rb_class_of(the_location->self), FL_SINGLETON);
201
+ return new_location(filename_abs, backtracie_frame_label_rbstr(raw_loc, true),
202
+ backtracie_frame_label_rbstr(raw_loc, false), line_number,
203
+ filename_rel, backtracie_frame_name_rbstr(raw_loc),
204
+ path_is_synthetic, debug_raw_location(raw_loc));
285
205
  }
286
206
 
287
- static bool is_defined_class_a_refinement(raw_location *the_location) {
288
- VALUE defined_class = backtracie_defined_class(the_location);
207
+ static VALUE debug_raw_location(const raw_location *the_location) {
208
+ VALUE arguments[] = {
209
+ ID2SYM(rb_intern("ruby_frame?")),
210
+ /* => */ to_boolean(the_location->is_ruby_frame),
211
+ ID2SYM(rb_intern("self_is_real_self?")),
212
+ /* => */ to_boolean(the_location->self_is_real_self),
213
+ ID2SYM(rb_intern("rb_profile_frames")),
214
+ /* => */ debug_frame(backtracie_frame_for_rb_profile(the_location)),
215
+ ID2SYM(rb_intern("self_or_self_class")),
216
+ /* => */ the_location->self_or_self_class,
217
+ ID2SYM(rb_intern("pc")),
218
+ /* => */ ULONG2NUM((uintptr_t)the_location->pc),
219
+ ID2SYM(rb_intern("cfunc_function_info")),
220
+ /* => */ cfunc_function_info(the_location)};
289
221
 
290
- return defined_class != Qnil && FL_TEST(rb_class_of(defined_class), RMODULE_IS_REFINEMENT);
222
+ VALUE debug_hash = rb_hash_new();
223
+ for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2)
224
+ rb_hash_aset(debug_hash, arguments[i], arguments[i + 1]);
225
+ return debug_hash;
291
226
  }
292
227
 
293
- static VALUE debug_raw_location(raw_location *the_location) {
294
- VALUE self_class = SAFE_NAVIGATION(rb_class_of, the_location->self);
295
-
296
- VALUE arguments[] = {
297
- ID2SYM(rb_intern("ruby_frame?")) , /* => */ to_boolean(the_location->is_ruby_frame),
298
- ID2SYM(rb_intern("should_use_iseq")), /* => */ to_boolean(the_location->should_use_iseq),
299
- ID2SYM(rb_intern("vm_method_type")), /* => */ INT2FIX(the_location->vm_method_type),
300
- ID2SYM(rb_intern("line_number")), /* => */ INT2FIX(the_location->line_number),
301
- ID2SYM(rb_intern("called_id")), /* => */ backtracie_called_id(the_location),
302
- // TODO: On Ruby < 3.0, running be pry -e 'require "backtracie"; Backtracie.caller_locations' with this being
303
- // exposed causes a VM segfault inside the pretty printing code
304
- // ID2SYM(rb_intern("defined_class")), /* => */ backtracie_defined_class(the_location),
305
- ID2SYM(rb_intern("defined_class_refinement?")), /* => */ to_boolean(is_defined_class_a_refinement(the_location)),
306
- ID2SYM(rb_intern("self_class")), /* => */ self_class,
307
- ID2SYM(rb_intern("real_class")), /* => */ SAFE_NAVIGATION(rb_class_real, self_class),
308
- ID2SYM(rb_intern("self")), /* => */ the_location->self,
309
- ID2SYM(rb_intern("self_class_singleton?")), /* => */ to_boolean(is_self_class_singleton(the_location)),
310
- ID2SYM(rb_intern("iseq_is_block?")), /* => */ to_boolean(backtracie_iseq_is_block(the_location)),
311
- ID2SYM(rb_intern("iseq_is_eval?")), /* => */ to_boolean(backtracie_iseq_is_eval(the_location)),
312
- ID2SYM(rb_intern("iseq")), /* => */ debug_frame(the_location->iseq),
313
- ID2SYM(rb_intern("callable_method_entry")), /* => */ debug_frame(the_location->callable_method_entry),
314
- ID2SYM(rb_intern("original_id")), /* => */ the_location->original_id
315
- };
228
+ static VALUE debug_frame(VALUE frame) {
229
+ if (frame == Qnil)
230
+ return Qnil;
231
+
232
+ VALUE arguments[] = {ID2SYM(rb_intern("path")),
233
+ /* => */ rb_profile_frame_path(frame),
234
+ ID2SYM(rb_intern("absolute_path")),
235
+ /* => */ rb_profile_frame_absolute_path(frame),
236
+ ID2SYM(rb_intern("label")),
237
+ /* => */ rb_profile_frame_label(frame),
238
+ ID2SYM(rb_intern("base_label")),
239
+ /* => */ rb_profile_frame_base_label(frame),
240
+ ID2SYM(rb_intern("full_label")),
241
+ /* => */ rb_profile_frame_full_label(frame),
242
+ ID2SYM(rb_intern("first_lineno")),
243
+ /* => */ rb_profile_frame_first_lineno(frame),
244
+ ID2SYM(rb_intern("classpath")),
245
+ /* => */ rb_profile_frame_classpath(frame),
246
+ ID2SYM(rb_intern("singleton_method_p")),
247
+ /* => */ rb_profile_frame_singleton_method_p(frame),
248
+ ID2SYM(rb_intern("method_name")),
249
+ /* => */ rb_profile_frame_method_name(frame),
250
+ ID2SYM(rb_intern("qualified_method_name")),
251
+ /* => */ rb_profile_frame_qualified_method_name(frame)};
316
252
 
317
253
  VALUE debug_hash = rb_hash_new();
318
- for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(debug_hash, arguments[i], arguments[i+1]);
254
+ for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2)
255
+ rb_hash_aset(debug_hash, arguments[i], arguments[i + 1]);
319
256
  return debug_hash;
320
257
  }
321
258
 
322
- static VALUE debug_frame(VALUE frame) {
323
- if (frame == Qnil) return Qnil;
259
+ static VALUE cfunc_function_info(const raw_location *the_location) {
260
+ return Qnil;
261
+ #if 0 // Disabled until this can be fixed up to not break Windows/macOS
262
+ Dl_info symbol_info;
263
+ struct Elf64_Sym *elf_symbol = 0;
264
+
265
+ if (the_location->cfunc_function == NULL ||
266
+ !dladdr1(the_location->cfunc_function, &symbol_info, (void**) &elf_symbol, RTLD_DL_SYMENT)) return Qnil;
267
+
268
+ VALUE fname = symbol_info.dli_fname == NULL ? Qnil : rb_str_new2(symbol_info.dli_fname);
269
+ VALUE sname = symbol_info.dli_sname == NULL ? Qnil : rb_str_new2(symbol_info.dli_sname);
324
270
 
325
271
  VALUE arguments[] = {
326
- ID2SYM(rb_intern("path")), /* => */ rb_profile_frame_path(frame),
327
- ID2SYM(rb_intern("absolute_path")), /* => */ rb_profile_frame_absolute_path(frame),
328
- ID2SYM(rb_intern("label")), /* => */ rb_profile_frame_label(frame),
329
- ID2SYM(rb_intern("base_label")), /* => */ rb_profile_frame_base_label(frame),
330
- ID2SYM(rb_intern("full_label")), /* => */ rb_profile_frame_full_label(frame),
331
- ID2SYM(rb_intern("first_lineno")), /* => */ rb_profile_frame_first_lineno(frame),
332
- ID2SYM(rb_intern("classpath")), /* => */ backtracie_rb_profile_frame_classpath(frame),
333
- ID2SYM(rb_intern("singleton_method_p")), /* => */ rb_profile_frame_singleton_method_p(frame),
334
- ID2SYM(rb_intern("method_name")), /* => */ rb_profile_frame_method_name(frame),
335
- ID2SYM(rb_intern("qualified_method_name")), /* => */ backtracie_rb_profile_frame_qualified_method_name(frame)
272
+ ID2SYM(rb_intern("dli_fname")), /* => */ fname,
273
+ ID2SYM(rb_intern("dli_sname")), /* => */ sname
336
274
  };
337
275
 
338
276
  VALUE debug_hash = rb_hash_new();
339
277
  for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(debug_hash, arguments[i], arguments[i+1]);
340
278
  return debug_hash;
279
+ #endif
341
280
  }
342
281
 
343
- static inline VALUE to_boolean(bool value) {
344
- return value ? Qtrue : Qfalse;
345
- }
282
+ static inline VALUE to_boolean(bool value) { return value ? Qtrue : Qfalse; }