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 +4 -4
- data/README.adoc +3 -0
- data/ext/backtracie_native_extension/backtracie.c +182 -245
- data/ext/backtracie_native_extension/backtracie_frames.c +941 -0
- data/ext/backtracie_native_extension/backtracie_private.h +20 -0
- data/ext/backtracie_native_extension/c_test_helpers.c +62 -0
- data/ext/backtracie_native_extension/extconf.rb +11 -17
- data/ext/backtracie_native_extension/public/backtracie.h +268 -0
- data/ext/backtracie_native_extension/strbuilder.c +123 -0
- data/ext/backtracie_native_extension/strbuilder.h +23 -0
- data/lib/backtracie/location.rb +6 -1
- data/lib/backtracie/version.rb +1 -1
- metadata +9 -5
- data/ext/backtracie_native_extension/ruby_shards.c +0 -533
- data/ext/backtracie_native_extension/ruby_shards.h +0 -170
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c21fb6f1864cd78c05807151502cbd12e3dfb8a1d5517d05389e2ac29297a69b
|
4
|
+
data.tar.gz: 126edf9c0e78607a9a6029f08e8a1ae0e4d1a02496ac56a0f67aae349f65ca05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
#
|
21
|
+
#if 0
|
22
|
+
// Disabled until this is fixed to not break Windows
|
23
|
+
#include <dlfcn.h>
|
24
|
+
#endif
|
27
25
|
|
28
|
-
#
|
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)
|
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,
|
42
|
-
|
43
|
-
static VALUE
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
static VALUE
|
48
|
-
|
49
|
-
static
|
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
|
-
|
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",
|
74
|
+
rb_define_module_function(backtracie_module, "backtrace_locations",
|
75
|
+
primitive_backtrace_locations, 1);
|
64
76
|
|
65
|
-
backtracie_location_class =
|
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 =
|
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",
|
71
|
-
|
84
|
+
rb_define_module_function(backtracie_primitive_module, "caller_locations",
|
85
|
+
primitive_caller_locations, 0);
|
72
86
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
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
|
-
|
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
|
95
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
130
|
-
|
131
|
-
|
132
|
-
VALUE
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
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
|
-
|
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
|
281
|
-
|
282
|
-
|
283
|
-
|
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
|
288
|
-
VALUE
|
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
|
-
|
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
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
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)
|
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
|
323
|
-
|
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("
|
327
|
-
ID2SYM(rb_intern("
|
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; }
|