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 +4 -4
- data/README.adoc +3 -0
- data/ext/backtracie_native_extension/backtracie.c +182 -245
- data/ext/backtracie_native_extension/backtracie_frames.c +930 -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 +36 -42
- 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 +12 -8
- data/ext/backtracie_native_extension/ruby_shards.c +0 -533
- data/ext/backtracie_native_extension/ruby_shards.h +0 -170
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: backtracie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivo Anjo
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: debase-ruby_core_source
|
@@ -45,9 +45,13 @@ files:
|
|
45
45
|
- README.adoc
|
46
46
|
- backtracie.gemspec
|
47
47
|
- ext/backtracie_native_extension/backtracie.c
|
48
|
+
- ext/backtracie_native_extension/backtracie_frames.c
|
49
|
+
- ext/backtracie_native_extension/backtracie_private.h
|
50
|
+
- ext/backtracie_native_extension/c_test_helpers.c
|
48
51
|
- ext/backtracie_native_extension/extconf.rb
|
49
|
-
- ext/backtracie_native_extension/
|
50
|
-
- ext/backtracie_native_extension/
|
52
|
+
- ext/backtracie_native_extension/public/backtracie.h
|
53
|
+
- ext/backtracie_native_extension/strbuilder.c
|
54
|
+
- ext/backtracie_native_extension/strbuilder.h
|
51
55
|
- lib/backtracie.rb
|
52
56
|
- lib/backtracie/location.rb
|
53
57
|
- lib/backtracie/version.rb
|
@@ -55,7 +59,7 @@ homepage: https://github.com/ivoanjo/backtracie
|
|
55
59
|
licenses:
|
56
60
|
- LGPL-3.0+
|
57
61
|
metadata: {}
|
58
|
-
post_install_message:
|
62
|
+
post_install_message:
|
59
63
|
rdoc_options: []
|
60
64
|
require_paths:
|
61
65
|
- lib
|
@@ -71,8 +75,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
75
|
- !ruby/object:Gem::Version
|
72
76
|
version: '0'
|
73
77
|
requirements: []
|
74
|
-
rubygems_version: 3.
|
75
|
-
signing_key:
|
78
|
+
rubygems_version: 3.3.7
|
79
|
+
signing_key:
|
76
80
|
specification_version: 4
|
77
81
|
summary: Ruby gem for beautiful backtraces
|
78
82
|
test_files: []
|
@@ -1,533 +0,0 @@
|
|
1
|
-
// backtracie: Ruby gem for beautiful backtraces
|
2
|
-
// Copyright (C) 2021 Ivo Anjo <ivo@ivoanjo.me>
|
3
|
-
//
|
4
|
-
// This file is part of backtracie.
|
5
|
-
//
|
6
|
-
// backtracie is free software: you can redistribute it and/or modify
|
7
|
-
// it under the terms of the GNU Lesser General Public License as published by
|
8
|
-
// the Free Software Foundation, either version 3 of the License, or
|
9
|
-
// (at your option) any later version.
|
10
|
-
//
|
11
|
-
// backtracie is distributed in the hope that it will be useful,
|
12
|
-
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
-
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
-
// GNU Lesser General Public License for more details.
|
15
|
-
//
|
16
|
-
// You should have received a copy of the GNU Lesser General Public License
|
17
|
-
// along with backtracie. If not, see <http://www.gnu.org/licenses/>.
|
18
|
-
|
19
|
-
// -----------------------------------------------------------------------------
|
20
|
-
// The file below has modified versions of code extracted from the Ruby project.
|
21
|
-
// The Ruby project copyright and license follow:
|
22
|
-
// -----------------------------------------------------------------------------
|
23
|
-
|
24
|
-
// Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
|
25
|
-
// You can redistribute it and/or modify it under either the terms of the
|
26
|
-
// 2-clause BSDL (see the file BSDL), or the conditions below:
|
27
|
-
|
28
|
-
// 1. You may make and give away verbatim copies of the source form of the
|
29
|
-
// software without restriction, provided that you duplicate all of the
|
30
|
-
// original copyright notices and associated disclaimers.
|
31
|
-
|
32
|
-
// 2. You may modify your copy of the software in any way, provided that
|
33
|
-
// you do at least ONE of the following:
|
34
|
-
|
35
|
-
// a. place your modifications in the Public Domain or otherwise
|
36
|
-
// make them Freely Available, such as by posting said
|
37
|
-
// modifications to Usenet or an equivalent medium, or by allowing
|
38
|
-
// the author to include your modifications in the software.
|
39
|
-
|
40
|
-
// b. use the modified software only within your corporation or
|
41
|
-
// organization.
|
42
|
-
|
43
|
-
// c. give non-standard binaries non-standard names, with
|
44
|
-
// instructions on where to get the original software distribution.
|
45
|
-
|
46
|
-
// d. make other distribution arrangements with the author.
|
47
|
-
|
48
|
-
// 3. You may distribute the software in object code or binary form,
|
49
|
-
// provided that you do at least ONE of the following:
|
50
|
-
|
51
|
-
// a. distribute the binaries and library files of the software,
|
52
|
-
// together with instructions (in the manual page or equivalent)
|
53
|
-
// on where to get the original distribution.
|
54
|
-
|
55
|
-
// b. accompany the distribution with the machine-readable source of
|
56
|
-
// the software.
|
57
|
-
|
58
|
-
// c. give non-standard binaries non-standard names, with
|
59
|
-
// instructions on where to get the original software distribution.
|
60
|
-
|
61
|
-
// d. make other distribution arrangements with the author.
|
62
|
-
|
63
|
-
// 4. You may modify and include the part of the software into any other
|
64
|
-
// software (possibly commercial). But some files in the distribution
|
65
|
-
// are not written by the author, so that they are not under these terms.
|
66
|
-
|
67
|
-
// For the list of those files and their copying conditions, see the
|
68
|
-
// file LEGAL.
|
69
|
-
|
70
|
-
// 5. The scripts and library files supplied as input to or produced as
|
71
|
-
// output from the software do not automatically fall under the
|
72
|
-
// copyright of the software, but belong to whomever generated them,
|
73
|
-
// and may be sold commercially, and may be aggregated with this
|
74
|
-
// software.
|
75
|
-
|
76
|
-
// 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
77
|
-
// IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
78
|
-
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
79
|
-
// PURPOSE.
|
80
|
-
|
81
|
-
// -----------------------------------------------------------------------------
|
82
|
-
|
83
|
-
// ruby_shards.c contains a number of borrowed functions from the MRI Ruby (usually 3.0.0) source tree:
|
84
|
-
// * A few were copy-pasted verbatim, and are dependencies for other functions
|
85
|
-
// * A few were copy-pasted and then changed so we can add features and fixes
|
86
|
-
// * A few were copy-pasted verbatim, with the objective of backporting their 3.0.0 behavior to earlier Ruby versions
|
87
|
-
// `git blame` usually documents which functions were added for what reason.
|
88
|
-
|
89
|
-
// Note that since the RUBY_MJIT_HEADER is a very special header, meant for internal use only, it has a number of quirks:
|
90
|
-
//
|
91
|
-
// 1. I've seen a few segfaults when trying to call back into original Ruby functions. E.g. even if the API is used
|
92
|
-
// correctly, just the mere inclusion of RUBY_MJIT_HEADER causes usage to crash. Thus, as much as possible, it's
|
93
|
-
// better to define functions OUTSIDE this file.
|
94
|
-
//
|
95
|
-
// 2. On Windows, I've observed "multiple definition of `something...'" (such as `rb_vm_ep_local_ep') whenever there
|
96
|
-
// are multiple files in the codebase that include the RUBY_MJIT_HEADER.
|
97
|
-
// It looks like (some?) Windows versions of Ruby define a bunch of functions in the RUBY_MJIT_HEADER itself
|
98
|
-
// without marking them as "static" (e.g. not visible to the outside of the file), and thus the linker then complains
|
99
|
-
// when linking together several files which all have these non-private symbols.
|
100
|
-
// One possible hacky solution suggested by the internets is to use the "-Wl,-allow-multiple-definition" linker
|
101
|
-
// flags to ignore this problem; instead I've chosen to implement all usage of the RUBY_MJIT_HEADER on this file --
|
102
|
-
// no other file in backtracie shall include RUBY_MJIT_HEADER.
|
103
|
-
// It's a simpler approach, and hopefully avoids any problems.
|
104
|
-
|
105
|
-
#include "extconf.h"
|
106
|
-
|
107
|
-
#ifndef PRE_MJIT_RUBY
|
108
|
-
#ifndef RUBY_MJIT_HEADER_INCLUDED
|
109
|
-
#define RUBY_MJIT_HEADER_INCLUDED
|
110
|
-
#include RUBY_MJIT_HEADER
|
111
|
-
#endif
|
112
|
-
#endif
|
113
|
-
|
114
|
-
#include "ruby_shards.h"
|
115
|
-
|
116
|
-
#ifdef PRE_MJIT_RUBY
|
117
|
-
#include <iseq.h>
|
118
|
-
#include <regenc.h>
|
119
|
-
#endif
|
120
|
-
|
121
|
-
#ifdef PRE_EXECUTION_CONTEXT
|
122
|
-
// The thread and its execution context were separated on Ruby 2.5; prior to that, everything was part of the thread
|
123
|
-
#define rb_execution_context_t rb_thread_t
|
124
|
-
#endif
|
125
|
-
|
126
|
-
#ifdef PRE_VM_ENV_RENAMES
|
127
|
-
#define VM_ENV_LOCAL_P VM_EP_LEP_P
|
128
|
-
#define VM_ENV_PREV_EP VM_EP_PREV_EP
|
129
|
-
#define VM_ENV_DATA_INDEX_ME_CREF -1
|
130
|
-
#define VM_FRAME_RUBYFRAME_P(cfp) RUBY_VM_NORMAL_ISEQ_P(cfp->iseq)
|
131
|
-
#endif
|
132
|
-
|
133
|
-
/**********************************************************************
|
134
|
-
vm_backtrace.c -
|
135
|
-
$Author: ko1 $
|
136
|
-
created at: Sun Jun 03 00:14:20 2012
|
137
|
-
Copyright (C) 1993-2012 Yukihiro Matsumoto
|
138
|
-
**********************************************************************/
|
139
|
-
|
140
|
-
inline static int
|
141
|
-
calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
142
|
-
{
|
143
|
-
VM_ASSERT(iseq);
|
144
|
-
VM_ASSERT(iseq->body);
|
145
|
-
VM_ASSERT(iseq->body->iseq_encoded);
|
146
|
-
VM_ASSERT(iseq->body->iseq_size);
|
147
|
-
if (! pc) {
|
148
|
-
/* This can happen during VM bootup. */
|
149
|
-
VM_ASSERT(iseq->body->type == ISEQ_TYPE_TOP);
|
150
|
-
VM_ASSERT(! iseq->body->local_table);
|
151
|
-
VM_ASSERT(! iseq->body->local_table_size);
|
152
|
-
return 0;
|
153
|
-
}
|
154
|
-
else {
|
155
|
-
ptrdiff_t n = pc - iseq->body->iseq_encoded;
|
156
|
-
VM_ASSERT(n <= iseq->body->iseq_size);
|
157
|
-
VM_ASSERT(n >= 0);
|
158
|
-
ASSUME(n >= 0);
|
159
|
-
size_t pos = n; /* no overflow */
|
160
|
-
if (LIKELY(pos)) {
|
161
|
-
/* use pos-1 because PC points next instruction at the beginning of instruction */
|
162
|
-
pos--;
|
163
|
-
}
|
164
|
-
return rb_iseq_line_no(iseq, pos);
|
165
|
-
}
|
166
|
-
}
|
167
|
-
|
168
|
-
static VALUE
|
169
|
-
id2str(ID id)
|
170
|
-
{
|
171
|
-
VALUE str = rb_id2str(id);
|
172
|
-
if (!str) return Qnil;
|
173
|
-
return str;
|
174
|
-
}
|
175
|
-
#define rb_id2str(id) id2str(id)
|
176
|
-
|
177
|
-
// Hacked version of Ruby's rb_profile_frames from Ruby 3.0.0 with the following changes:
|
178
|
-
// 1. Instead of just using the rb_execution_context_t for the current thread, the context is received as an argument,
|
179
|
-
// thus allowing the sampling of any thread in the VM, not just the current one.
|
180
|
-
// 2. It gathers a lot more data: originally you'd get only a VALUE (either iseq or the cme, depending on the case),
|
181
|
-
// and the line number. The hacked version returns a whole raw_location with a lot more info.
|
182
|
-
// 3. It correctly ignores the dummy frame at the bottom of the main Ruby thread stack, thus mimicking the behavior of
|
183
|
-
// Ruby's backtrace_each (which is the function that is used to implement Thread#backtrace and friends)
|
184
|
-
// 4. Removed the start argument (upstream was broken anyway -- https://github.com/ruby/ruby/pull/2713 -- so we can
|
185
|
-
// re-add later if needed)
|
186
|
-
static int backtracie_rb_profile_frames_for_execution_context(
|
187
|
-
rb_execution_context_t *ec,
|
188
|
-
int limit,
|
189
|
-
raw_location *raw_locations
|
190
|
-
) {
|
191
|
-
int i = 0;
|
192
|
-
const rb_control_frame_t *cfp = ec->cfp;
|
193
|
-
const rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
|
194
|
-
const rb_callable_method_entry_t *cme = 0;
|
195
|
-
|
196
|
-
// Hack #3 above: Here we go back one frame in addition to what the original Ruby rb_profile_frames method did.
|
197
|
-
// Why? According to backtrace_each() in vm_backtrace.c there's two "dummy frames" (what MRI calls them) at the
|
198
|
-
// bottom of the stack, and we need to skip them both.
|
199
|
-
// I have no idea why the original rb_profile_frames omits this. Without this, sampling `Thread.main` always
|
200
|
-
// returned one more frame than the regular MRI APIs (which use the aforementioned backtrace_each internally).
|
201
|
-
end_cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
|
202
|
-
|
203
|
-
for (i = 0; i < limit && cfp != end_cfp;) {
|
204
|
-
// Initialize the raw_location, to avoid issues
|
205
|
-
raw_locations[i].is_ruby_frame = false;
|
206
|
-
raw_locations[i].should_use_iseq = false;
|
207
|
-
raw_locations[i].vm_method_type = 0;
|
208
|
-
raw_locations[i].line_number = 0;
|
209
|
-
raw_locations[i].iseq = Qnil;
|
210
|
-
raw_locations[i].callable_method_entry = Qnil;
|
211
|
-
raw_locations[i].original_id = Qnil;
|
212
|
-
|
213
|
-
// The current object this is getting called on!
|
214
|
-
raw_locations[i].self = cfp->self;
|
215
|
-
|
216
|
-
cme = rb_vm_frame_method_entry(cfp);
|
217
|
-
|
218
|
-
if (cfp->iseq && !cfp->pc) {
|
219
|
-
// Do nothing -- this frame should not be used
|
220
|
-
// Bugfix: rb_profile_frames did not do this (skip a frame when there's no pc), but backtrace_each did, and that
|
221
|
-
// caused the "Backtracie.backtrace_locations when sampling a map from an enumerable returns the same number of items as the Ruby API"
|
222
|
-
// test to fail -- Backtracie returned one more frame than Ruby. I suspect that, as usual, this is yet another case where
|
223
|
-
// rb_profile_frames fails us.
|
224
|
-
} else if (VM_FRAME_RUBYFRAME_P(cfp)) {
|
225
|
-
raw_locations[i].is_ruby_frame = true;
|
226
|
-
raw_locations[i].iseq = (VALUE) cfp->iseq;
|
227
|
-
|
228
|
-
if (cme) {
|
229
|
-
raw_locations[i].callable_method_entry = (VALUE) cme;
|
230
|
-
raw_locations[i].vm_method_type = cme->def->type;
|
231
|
-
}
|
232
|
-
|
233
|
-
if (!(cme && cme->def->type == VM_METHOD_TYPE_ISEQ)) {
|
234
|
-
// This comes from the original rb_profile_frames logic, which would only return the iseq when the cme
|
235
|
-
// type is not VM_METHOD_TYPE_ISEQ
|
236
|
-
raw_locations[i].should_use_iseq = true;
|
237
|
-
}
|
238
|
-
|
239
|
-
raw_locations[i].line_number = calc_lineno(cfp->iseq, cfp->pc);
|
240
|
-
|
241
|
-
i++;
|
242
|
-
} else {
|
243
|
-
if (cme && cme->def->type == VM_METHOD_TYPE_CFUNC) {
|
244
|
-
raw_locations[i].is_ruby_frame = false;
|
245
|
-
raw_locations[i].callable_method_entry = (VALUE) cme;
|
246
|
-
raw_locations[i].vm_method_type = cme->def->type;
|
247
|
-
raw_locations[i].line_number = 0;
|
248
|
-
raw_locations[i].original_id = ID2SYM(cme->def->original_id);
|
249
|
-
|
250
|
-
i++;
|
251
|
-
}
|
252
|
-
}
|
253
|
-
|
254
|
-
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
255
|
-
}
|
256
|
-
|
257
|
-
return i;
|
258
|
-
}
|
259
|
-
|
260
|
-
int backtracie_rb_profile_frames(int limit, raw_location *raw_locations) {
|
261
|
-
#ifndef PRE_EXECUTION_CONTEXT
|
262
|
-
return backtracie_rb_profile_frames_for_execution_context(GET_EC(), limit, raw_locations);
|
263
|
-
#else
|
264
|
-
// FIXME: Figure out how to make GET_EC (GET_THREAD) work for Ruby <= 2.4
|
265
|
-
return 0;
|
266
|
-
#endif
|
267
|
-
}
|
268
|
-
|
269
|
-
bool backtracie_is_thread_alive(VALUE thread) {
|
270
|
-
// In here we're assuming that what we got is really a Thread or its subclass. This assumption NEEDS to be verified by
|
271
|
-
// the caller, otherwise I see a segfault in your future.
|
272
|
-
rb_thread_t *thread_pointer = (rb_thread_t*) DATA_PTR(thread);
|
273
|
-
|
274
|
-
return !(thread_pointer->to_kill || thread_pointer->status == THREAD_KILLED);
|
275
|
-
}
|
276
|
-
|
277
|
-
int backtracie_rb_profile_frames_for_thread(VALUE thread, int limit, raw_location *raw_locations) {
|
278
|
-
if (!backtracie_is_thread_alive(thread)) return 0;
|
279
|
-
|
280
|
-
// In here we're assuming that what we got is really a Thread or its subclass. This assumption NEEDS to be verified by
|
281
|
-
// the caller, otherwise I see a segfault in your future.
|
282
|
-
rb_thread_t *thread_pointer = (rb_thread_t*) DATA_PTR(thread);
|
283
|
-
|
284
|
-
#ifndef PRE_EXECUTION_CONTEXT
|
285
|
-
return backtracie_rb_profile_frames_for_execution_context(thread_pointer->ec, limit, raw_locations);
|
286
|
-
#else
|
287
|
-
return backtracie_rb_profile_frames_for_execution_context(thread_pointer, limit, raw_locations);
|
288
|
-
#endif
|
289
|
-
}
|
290
|
-
|
291
|
-
VALUE backtracie_called_id(raw_location *the_location) {
|
292
|
-
if (the_location->callable_method_entry == Qnil) return Qnil;
|
293
|
-
|
294
|
-
return ID2SYM(
|
295
|
-
((rb_callable_method_entry_t *) the_location->callable_method_entry)
|
296
|
-
->called_id
|
297
|
-
);
|
298
|
-
}
|
299
|
-
|
300
|
-
VALUE backtracie_defined_class(raw_location *the_location) {
|
301
|
-
if (the_location->callable_method_entry == Qnil) return Qnil;
|
302
|
-
|
303
|
-
return \
|
304
|
-
((rb_callable_method_entry_t *) the_location->callable_method_entry)
|
305
|
-
->defined_class;
|
306
|
-
}
|
307
|
-
|
308
|
-
bool backtracie_iseq_is_block(raw_location *the_location) {
|
309
|
-
if (the_location->iseq == Qnil) return false;
|
310
|
-
|
311
|
-
return ((rb_iseq_t *) the_location->iseq)->body->type == ISEQ_TYPE_BLOCK;
|
312
|
-
}
|
313
|
-
|
314
|
-
bool backtracie_iseq_is_eval(raw_location *the_location) {
|
315
|
-
if (the_location->iseq == Qnil) return false;
|
316
|
-
|
317
|
-
return ((rb_iseq_t *) the_location->iseq)->body->type == ISEQ_TYPE_EVAL;
|
318
|
-
}
|
319
|
-
|
320
|
-
VALUE backtracie_refinement_name(raw_location *the_location) {
|
321
|
-
VALUE defined_class = backtracie_defined_class(the_location);
|
322
|
-
if (defined_class == Qnil) return Qnil;
|
323
|
-
|
324
|
-
VALUE refinement_module = rb_class_of(defined_class);
|
325
|
-
if (!FL_TEST(refinement_module, RMODULE_IS_REFINEMENT)) return Qnil;
|
326
|
-
|
327
|
-
// The below bits are inspired by Ruby's rb_mod_to_s(VALUE)
|
328
|
-
ID id_refined_class;
|
329
|
-
CONST_ID(id_refined_class, "__refined_class__");
|
330
|
-
VALUE refined_class = rb_attr_get(refinement_module, id_refined_class);
|
331
|
-
if (refined_class == Qnil) return Qnil;
|
332
|
-
|
333
|
-
VALUE result = rb_inspect(refined_class);
|
334
|
-
rb_str_concat(result, rb_str_new2("$refinement@"));
|
335
|
-
ID id_defined_at;
|
336
|
-
CONST_ID(id_defined_at, "__defined_at__");
|
337
|
-
rb_str_concat(result, rb_inspect(rb_attr_get(refinement_module, id_defined_at)));
|
338
|
-
|
339
|
-
return result;
|
340
|
-
}
|
341
|
-
|
342
|
-
// For more details on the objective of this backport, see the comments on ruby_shards.h
|
343
|
-
// This is used for Ruby < 3.0.0
|
344
|
-
#ifdef CFUNC_FRAMES_BACKPORT_NEEDED
|
345
|
-
|
346
|
-
static const rb_callable_method_entry_t *
|
347
|
-
cframe(VALUE frame)
|
348
|
-
{
|
349
|
-
if (frame == Qnil) return NULL;
|
350
|
-
|
351
|
-
if (RB_TYPE_P(frame, T_IMEMO)) {
|
352
|
-
switch (imemo_type(frame)) {
|
353
|
-
case imemo_ment:
|
354
|
-
{
|
355
|
-
const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
|
356
|
-
switch (cme->def->type) {
|
357
|
-
case VM_METHOD_TYPE_CFUNC:
|
358
|
-
return cme;
|
359
|
-
default:
|
360
|
-
return NULL;
|
361
|
-
}
|
362
|
-
}
|
363
|
-
default:
|
364
|
-
return NULL;
|
365
|
-
}
|
366
|
-
}
|
367
|
-
|
368
|
-
return NULL;
|
369
|
-
}
|
370
|
-
|
371
|
-
static const rb_iseq_t *
|
372
|
-
frame2iseq(VALUE frame)
|
373
|
-
{
|
374
|
-
if (frame == Qnil) return NULL;
|
375
|
-
|
376
|
-
if (RB_TYPE_P(frame, T_IMEMO)) {
|
377
|
-
switch (imemo_type(frame)) {
|
378
|
-
case imemo_iseq:
|
379
|
-
return (const rb_iseq_t *)frame;
|
380
|
-
case imemo_ment:
|
381
|
-
{
|
382
|
-
const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
|
383
|
-
switch (cme->def->type) {
|
384
|
-
case VM_METHOD_TYPE_ISEQ:
|
385
|
-
return cme->def->body.iseq.iseqptr;
|
386
|
-
default:
|
387
|
-
return NULL;
|
388
|
-
}
|
389
|
-
}
|
390
|
-
default:
|
391
|
-
break;
|
392
|
-
}
|
393
|
-
}
|
394
|
-
rb_bug("frame2iseq: unreachable");
|
395
|
-
}
|
396
|
-
|
397
|
-
VALUE
|
398
|
-
backported_rb_profile_frame_method_name(VALUE frame)
|
399
|
-
{
|
400
|
-
const rb_callable_method_entry_t *cme = cframe(frame);
|
401
|
-
if (cme) {
|
402
|
-
ID mid = cme->def->original_id;
|
403
|
-
return id2str(mid);
|
404
|
-
}
|
405
|
-
const rb_iseq_t *iseq = frame2iseq(frame);
|
406
|
-
return iseq ? rb_iseq_method_name(iseq) : Qnil;
|
407
|
-
}
|
408
|
-
|
409
|
-
#endif // CFUNC_FRAMES_BACKPORT_NEEDED
|
410
|
-
|
411
|
-
#ifdef CLASSPATH_BACKPORT_NEEDED
|
412
|
-
static VALUE
|
413
|
-
frame2klass(VALUE frame)
|
414
|
-
{
|
415
|
-
if (frame == Qnil) return Qnil;
|
416
|
-
|
417
|
-
if (RB_TYPE_P(frame, T_IMEMO)) {
|
418
|
-
const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
|
419
|
-
|
420
|
-
if (imemo_type(frame) == imemo_ment) {
|
421
|
-
return cme->defined_class;
|
422
|
-
}
|
423
|
-
}
|
424
|
-
return Qnil;
|
425
|
-
}
|
426
|
-
|
427
|
-
VALUE
|
428
|
-
backported_rb_profile_frame_classpath(VALUE frame)
|
429
|
-
{
|
430
|
-
VALUE klass = frame2klass(frame);
|
431
|
-
|
432
|
-
if (klass && !NIL_P(klass)) {
|
433
|
-
if (RB_TYPE_P(klass, T_ICLASS)) {
|
434
|
-
klass = RBASIC(klass)->klass;
|
435
|
-
}
|
436
|
-
else if (FL_TEST(klass, FL_SINGLETON)) {
|
437
|
-
klass = rb_ivar_get(klass, id__attached__);
|
438
|
-
if (!RB_TYPE_P(klass, T_CLASS) && !RB_TYPE_P(klass, T_MODULE))
|
439
|
-
return rb_sprintf("#<%s:%p>", rb_class2name(rb_obj_class(klass)), (void*)klass);
|
440
|
-
}
|
441
|
-
return rb_class_path(klass);
|
442
|
-
}
|
443
|
-
else {
|
444
|
-
return Qnil;
|
445
|
-
}
|
446
|
-
}
|
447
|
-
|
448
|
-
// Oddly enough, this method is on debug.h BUT NOT on the MJIT header. Since I've had
|
449
|
-
// crashes when trying to combine the MJIT header with the regular Ruby headers, let's
|
450
|
-
// just supply the declaration for this function, as otherwise the build seems to fail
|
451
|
-
// on macOS in CI.
|
452
|
-
VALUE rb_profile_frame_singleton_method_p(VALUE frame);
|
453
|
-
|
454
|
-
static VALUE
|
455
|
-
qualified_method_name(VALUE frame, VALUE method_name)
|
456
|
-
{
|
457
|
-
if (method_name != Qnil) {
|
458
|
-
VALUE classpath = backported_rb_profile_frame_classpath(frame);
|
459
|
-
VALUE singleton_p = rb_profile_frame_singleton_method_p(frame);
|
460
|
-
|
461
|
-
if (classpath != Qnil) {
|
462
|
-
return rb_sprintf("%"PRIsVALUE"%s%"PRIsVALUE,
|
463
|
-
classpath, singleton_p == Qtrue ? "." : "#", method_name);
|
464
|
-
}
|
465
|
-
else {
|
466
|
-
return method_name;
|
467
|
-
}
|
468
|
-
}
|
469
|
-
else {
|
470
|
-
return Qnil;
|
471
|
-
}
|
472
|
-
}
|
473
|
-
|
474
|
-
VALUE
|
475
|
-
backported_rb_profile_frame_qualified_method_name(VALUE frame)
|
476
|
-
{
|
477
|
-
VALUE method_name = backported_rb_profile_frame_method_name(frame);
|
478
|
-
|
479
|
-
return qualified_method_name(frame, method_name);
|
480
|
-
}
|
481
|
-
#endif // CLASSPATH_BACKPORT_NEEDED
|
482
|
-
|
483
|
-
#ifdef PRE_MJIT_RUBY
|
484
|
-
|
485
|
-
// A few extra functions copied from the Ruby sources, needed to support Ruby < 2.6
|
486
|
-
|
487
|
-
/**********************************************************************
|
488
|
-
vm_insnhelper.c - instruction helper functions.
|
489
|
-
$Author$
|
490
|
-
Copyright (C) 2007 Koichi Sasada
|
491
|
-
**********************************************************************/
|
492
|
-
|
493
|
-
static rb_callable_method_entry_t *
|
494
|
-
check_method_entry(VALUE obj, int can_be_svar)
|
495
|
-
{
|
496
|
-
if (obj == Qfalse) return NULL;
|
497
|
-
|
498
|
-
#if VM_CHECK_MODE > 0
|
499
|
-
if (!RB_TYPE_P(obj, T_IMEMO)) rb_bug("check_method_entry: unknown type: %s", rb_obj_info(obj));
|
500
|
-
#endif
|
501
|
-
|
502
|
-
switch (imemo_type(obj)) {
|
503
|
-
case imemo_ment:
|
504
|
-
return (rb_callable_method_entry_t *)obj;
|
505
|
-
case imemo_cref:
|
506
|
-
return NULL;
|
507
|
-
case imemo_svar:
|
508
|
-
if (can_be_svar) {
|
509
|
-
return check_method_entry(((struct vm_svar *)obj)->cref_or_me, FALSE);
|
510
|
-
}
|
511
|
-
default:
|
512
|
-
#if VM_CHECK_MODE > 0
|
513
|
-
rb_bug("check_method_entry: svar should not be there:");
|
514
|
-
#endif
|
515
|
-
return NULL;
|
516
|
-
}
|
517
|
-
}
|
518
|
-
|
519
|
-
const rb_callable_method_entry_t *
|
520
|
-
rb_vm_frame_method_entry(const rb_control_frame_t *cfp)
|
521
|
-
{
|
522
|
-
const VALUE *ep = cfp->ep;
|
523
|
-
rb_callable_method_entry_t *me;
|
524
|
-
|
525
|
-
while (!VM_ENV_LOCAL_P(ep)) {
|
526
|
-
if ((me = check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], FALSE)) != NULL) return me;
|
527
|
-
ep = VM_ENV_PREV_EP(ep);
|
528
|
-
}
|
529
|
-
|
530
|
-
return check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], TRUE);
|
531
|
-
}
|
532
|
-
|
533
|
-
#endif
|