backtracie 0.3.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8ef71cf1cfd9ca005e0ecb0fe7434a9c8121bc46fc70c34209c74ecad1c705c
4
- data.tar.gz: 723d3c66fb919432481428609a8316243e160805ff4142757f9ec65cf254f2d8
3
+ metadata.gz: c21fb6f1864cd78c05807151502cbd12e3dfb8a1d5517d05389e2ac29297a69b
4
+ data.tar.gz: 126edf9c0e78607a9a6029f08e8a1ae0e4d1a02496ac56a0f67aae349f65ca05
5
5
  SHA512:
6
- metadata.gz: ceffbd048f44b6563298f301bde8dd498c1ea92be244e90a42390f4d387d58ef3837d936afbd4c63376ebfa2a20984989a2f8e1426ab9cccc30fc8c844c78a68
7
- data.tar.gz: cc6e34b726934f01ae92bf089954bace7063619ce8e3d71b1d1eea610d153f10641257399388ef70bcd7730b1aea89039d4f7e6e2b960f2b09e937fd4d802969
6
+ metadata.gz: fbda4c7a47305e4896ff0d39da7f5cf997f766f55e66ea5db0c7c50b2506bb103397f01f58fb2ee3f9b0ea89a975f6d1fdf5b9db91b934cca0c66e51adf96265
7
+ data.tar.gz: af7c5c55fa5339a60a10a49f69aac02ad35b5e99a14e1de33e7988178e976f382e2c8c9260924741d218c44b030058bbf07264d5a9fd1612195752b56264d21a
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; }