backtracie 0.2.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
metadata CHANGED
@@ -1,15 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backtracie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
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: 2021-07-01 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2022-08-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: debase-ruby_core_source
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.10'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.10.12
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.10'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.10.12
13
33
  description: Ruby gem for beautiful backtraces
14
34
  email:
15
35
  - ivo@ivoanjo.me
@@ -25,9 +45,13 @@ files:
25
45
  - README.adoc
26
46
  - backtracie.gemspec
27
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
28
51
  - ext/backtracie_native_extension/extconf.rb
29
- - ext/backtracie_native_extension/ruby_shards.c
30
- - ext/backtracie_native_extension/ruby_shards.h
52
+ - ext/backtracie_native_extension/public/backtracie.h
53
+ - ext/backtracie_native_extension/strbuilder.c
54
+ - ext/backtracie_native_extension/strbuilder.h
31
55
  - lib/backtracie.rb
32
56
  - lib/backtracie/location.rb
33
57
  - lib/backtracie/version.rb
@@ -35,7 +59,7 @@ homepage: https://github.com/ivoanjo/backtracie
35
59
  licenses:
36
60
  - LGPL-3.0+
37
61
  metadata: {}
38
- post_install_message:
62
+ post_install_message:
39
63
  rdoc_options: []
40
64
  require_paths:
41
65
  - lib
@@ -51,8 +75,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
51
75
  - !ruby/object:Gem::Version
52
76
  version: '0'
53
77
  requirements: []
54
- rubygems_version: 3.1.4
55
- signing_key:
78
+ rubygems_version: 3.3.7
79
+ signing_key:
56
80
  specification_version: 4
57
81
  summary: Ruby gem for beautiful backtraces
58
82
  test_files: []
@@ -1,593 +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
-
109
- #ifndef RUBY_MJIT_HEADER_INCLUDED
110
- #define RUBY_MJIT_HEADER_INCLUDED
111
- #include RUBY_MJIT_HEADER
112
- #endif
113
-
114
- #include "ruby_shards.h"
115
-
116
- /**********************************************************************
117
- vm_backtrace.c -
118
- $Author: ko1 $
119
- created at: Sun Jun 03 00:14:20 2012
120
- Copyright (C) 1993-2012 Yukihiro Matsumoto
121
- **********************************************************************/
122
-
123
- inline static int
124
- calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
125
- {
126
- VM_ASSERT(iseq);
127
- VM_ASSERT(iseq->body);
128
- VM_ASSERT(iseq->body->iseq_encoded);
129
- VM_ASSERT(iseq->body->iseq_size);
130
- if (! pc) {
131
- /* This can happen during VM bootup. */
132
- VM_ASSERT(iseq->body->type == ISEQ_TYPE_TOP);
133
- VM_ASSERT(! iseq->body->local_table);
134
- VM_ASSERT(! iseq->body->local_table_size);
135
- return 0;
136
- }
137
- else {
138
- ptrdiff_t n = pc - iseq->body->iseq_encoded;
139
- VM_ASSERT(n <= iseq->body->iseq_size);
140
- VM_ASSERT(n >= 0);
141
- ASSUME(n >= 0);
142
- size_t pos = n; /* no overflow */
143
- if (LIKELY(pos)) {
144
- /* use pos-1 because PC points next instruction at the beginning of instruction */
145
- pos--;
146
- }
147
- return rb_iseq_line_no(iseq, pos);
148
- }
149
- }
150
-
151
- static VALUE
152
- id2str(ID id)
153
- {
154
- VALUE str = rb_id2str(id);
155
- if (!str) return Qnil;
156
- return str;
157
- }
158
- #define rb_id2str(id) id2str(id)
159
-
160
- // Hacked version of Ruby's rb_profile_frames from Ruby 3.0.0 with the following changes:
161
- // 1. Instead of just using the rb_execution_context_t for the current thread, the context is received as an argument,
162
- // thus allowing the sampling of any thread in the VM, not just the current one.
163
- // 2. It gathers a lot more data: originally you'd get only a VALUE (either iseq or the cme, depending on the case),
164
- // and the line number. The hacked version returns a whole raw_location with a lot more info.
165
- // 3. It correctly ignores the dummy frame at the bottom of the main Ruby thread stack, thus mimicking the behavior of
166
- // Ruby's backtrace_each (which is the function that is used to implement Thread#backtrace and friends)
167
- // 4. Removed the start argument (upstream was broken anyway -- https://github.com/ruby/ruby/pull/2713 -- so we can
168
- // re-add later if needed)
169
- static int backtracie_rb_profile_frames_for_execution_context(
170
- rb_execution_context_t *ec,
171
- int limit,
172
- raw_location *raw_locations
173
- ) {
174
- int i = 0;
175
- const rb_control_frame_t *cfp = ec->cfp;
176
- const rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
177
- const rb_callable_method_entry_t *cme = 0;
178
-
179
- // Hack #3 above: Here we go back one frame in addition to what the original Ruby rb_profile_frames method did.
180
- // Why? According to backtrace_each() in vm_backtrace.c there's two "dummy frames" (what MRI calls them) at the
181
- // bottom of the stack, and we need to skip them both.
182
- // I have no idea why the original rb_profile_frames omits this. Without this, sampling `Thread.main` always
183
- // returned one more frame than the regular MRI APIs (which use the aforementioned backtrace_each internally).
184
- end_cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
185
-
186
- for (i = 0; i < limit && cfp != end_cfp;) {
187
- // Initialize the raw_location, to avoid issues
188
- raw_locations[i].is_ruby_frame = false;
189
- raw_locations[i].should_use_iseq = false;
190
- raw_locations[i].should_use_cfunc_name = false;
191
- raw_locations[i].vm_method_type = 0;
192
- raw_locations[i].line_number = 0;
193
- raw_locations[i].iseq = Qnil;
194
- raw_locations[i].callable_method_entry = Qnil;
195
- raw_locations[i].cfunc_name = Qnil;
196
-
197
- // The current object this is getting called on!
198
- raw_locations[i].self = cfp->self;
199
-
200
- cme = rb_vm_frame_method_entry(cfp);
201
-
202
- if (VM_FRAME_RUBYFRAME_P(cfp)) {
203
- raw_locations[i].is_ruby_frame = true;
204
- raw_locations[i].iseq = (VALUE) cfp->iseq;
205
-
206
- if (cme) {
207
- raw_locations[i].callable_method_entry = (VALUE) cme;
208
- raw_locations[i].vm_method_type = cme->def->type;
209
- }
210
-
211
- if (!(cme && cme->def->type == VM_METHOD_TYPE_ISEQ)) {
212
- // This comes from the original rb_profile_frames logic, which would only return the iseq when the cme
213
- // type is not VM_METHOD_TYPE_ISEQ
214
- raw_locations[i].should_use_iseq = true;
215
- }
216
-
217
- raw_locations[i].line_number = calc_lineno(cfp->iseq, cfp->pc);
218
-
219
- i++;
220
- } else {
221
- if (cme && cme->def->type == VM_METHOD_TYPE_CFUNC) {
222
- raw_locations[i].is_ruby_frame = false;
223
- raw_locations[i].callable_method_entry = (VALUE) cme;
224
- raw_locations[i].vm_method_type = cme->def->type;
225
- raw_locations[i].line_number = 0;
226
-
227
- i++;
228
- }
229
- }
230
-
231
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
232
- }
233
-
234
- return i;
235
- }
236
-
237
- int backtracie_rb_profile_frames(int limit, raw_location *raw_locations) {
238
- return backtracie_rb_profile_frames_for_execution_context(GET_EC(), limit, raw_locations);
239
- }
240
-
241
- bool backtracie_is_thread_alive(VALUE thread) {
242
- // In here we're assuming that what we got is really a Thread or its subclass. This assumption NEEDS to be verified by
243
- // the caller, otherwise I see a segfault in your future.
244
- rb_thread_t *thread_pointer = (rb_thread_t*) DATA_PTR(thread);
245
-
246
- return !(thread_pointer->to_kill || thread_pointer->status == THREAD_KILLED);
247
- }
248
-
249
- int backtracie_rb_profile_frames_for_thread(VALUE thread, int limit, raw_location *raw_locations) {
250
- if (!backtracie_is_thread_alive(thread)) return 0;
251
-
252
- // In here we're assuming that what we got is really a Thread or its subclass. This assumption NEEDS to be verified by
253
- // the caller, otherwise I see a segfault in your future.
254
- rb_thread_t *thread_pointer = (rb_thread_t*) DATA_PTR(thread);
255
-
256
- return backtracie_rb_profile_frames_for_execution_context(thread_pointer->ec, limit, raw_locations);
257
- }
258
-
259
- VALUE backtracie_called_id(raw_location *the_location) {
260
- if (the_location->callable_method_entry == Qnil) return Qnil;
261
-
262
- return ID2SYM(
263
- ((rb_callable_method_entry_t *) the_location->callable_method_entry)
264
- ->called_id
265
- );
266
- }
267
-
268
- VALUE backtracie_defined_class(raw_location *the_location) {
269
- if (the_location->callable_method_entry == Qnil) return Qnil;
270
-
271
- return \
272
- ((rb_callable_method_entry_t *) the_location->callable_method_entry)
273
- ->defined_class;
274
- }
275
-
276
- VALUE backtracie_rb_vm_top_self() {
277
- return GET_VM()->top_self;
278
- }
279
-
280
- bool backtracie_iseq_is_block(raw_location *the_location) {
281
- if (the_location->iseq == Qnil) return false;
282
-
283
- return ((rb_iseq_t *) the_location->iseq)->body->type == ISEQ_TYPE_BLOCK;
284
- }
285
-
286
- bool backtracie_iseq_is_eval(raw_location *the_location) {
287
- if (the_location->iseq == Qnil) return false;
288
-
289
- return ((rb_iseq_t *) the_location->iseq)->body->type == ISEQ_TYPE_EVAL;
290
- }
291
-
292
- VALUE backtracie_refinement_name(raw_location *the_location) {
293
- VALUE defined_class = backtracie_defined_class(the_location);
294
- if (defined_class == Qnil) return Qnil;
295
-
296
- VALUE refinement_module = rb_class_of(defined_class);
297
- if (!FL_TEST(refinement_module, RMODULE_IS_REFINEMENT)) return Qnil;
298
-
299
- // The below bits are inspired by Ruby's rb_mod_to_s(VALUE)
300
- ID id_refined_class;
301
- CONST_ID(id_refined_class, "__refined_class__");
302
- VALUE refined_class = rb_attr_get(refinement_module, id_refined_class);
303
- if (refined_class == Qnil) return Qnil;
304
-
305
- VALUE result = rb_inspect(refined_class);
306
- rb_str_concat(result, rb_str_new2("$refinement@"));
307
- ID id_defined_at;
308
- CONST_ID(id_defined_at, "__defined_at__");
309
- rb_str_concat(result, rb_inspect(rb_attr_get(refinement_module, id_defined_at)));
310
-
311
- return result;
312
- }
313
-
314
- // For more details on the objective of this backport, see the comments on ruby_shards.h
315
- // This is used for Ruby < 3.0.0
316
- #ifdef CFUNC_FRAMES_BACKPORT_NEEDED
317
-
318
- static const rb_callable_method_entry_t *
319
- cframe(VALUE frame)
320
- {
321
- if (frame == Qnil) return NULL;
322
-
323
- if (RB_TYPE_P(frame, T_IMEMO)) {
324
- switch (imemo_type(frame)) {
325
- case imemo_ment:
326
- {
327
- const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
328
- switch (cme->def->type) {
329
- case VM_METHOD_TYPE_CFUNC:
330
- return cme;
331
- default:
332
- return NULL;
333
- }
334
- }
335
- default:
336
- return NULL;
337
- }
338
- }
339
-
340
- return NULL;
341
- }
342
-
343
- static const rb_iseq_t *
344
- frame2iseq(VALUE frame)
345
- {
346
- if (frame == Qnil) return NULL;
347
-
348
- if (RB_TYPE_P(frame, T_IMEMO)) {
349
- switch (imemo_type(frame)) {
350
- case imemo_iseq:
351
- return (const rb_iseq_t *)frame;
352
- case imemo_ment:
353
- {
354
- const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
355
- switch (cme->def->type) {
356
- case VM_METHOD_TYPE_ISEQ:
357
- return cme->def->body.iseq.iseqptr;
358
- default:
359
- return NULL;
360
- }
361
- }
362
- default:
363
- break;
364
- }
365
- }
366
- rb_bug("frame2iseq: unreachable");
367
- }
368
-
369
- VALUE
370
- backported_rb_profile_frame_method_name(VALUE frame)
371
- {
372
- const rb_callable_method_entry_t *cme = cframe(frame);
373
- if (cme) {
374
- ID mid = cme->def->original_id;
375
- return id2str(mid);
376
- }
377
- const rb_iseq_t *iseq = frame2iseq(frame);
378
- return iseq ? rb_iseq_method_name(iseq) : Qnil;
379
- }
380
-
381
- #endif // CFUNC_FRAMES_BACKPORT_NEEDED
382
-
383
- #ifdef CLASSPATH_BACKPORT_NEEDED
384
- static VALUE
385
- frame2klass(VALUE frame)
386
- {
387
- if (frame == Qnil) return Qnil;
388
-
389
- if (RB_TYPE_P(frame, T_IMEMO)) {
390
- const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
391
-
392
- if (imemo_type(frame) == imemo_ment) {
393
- return cme->defined_class;
394
- }
395
- }
396
- return Qnil;
397
- }
398
-
399
- VALUE
400
- backported_rb_profile_frame_classpath(VALUE frame)
401
- {
402
- VALUE klass = frame2klass(frame);
403
-
404
- if (klass && !NIL_P(klass)) {
405
- if (RB_TYPE_P(klass, T_ICLASS)) {
406
- klass = RBASIC(klass)->klass;
407
- }
408
- else if (FL_TEST(klass, FL_SINGLETON)) {
409
- klass = rb_ivar_get(klass, id__attached__);
410
- if (!RB_TYPE_P(klass, T_CLASS) && !RB_TYPE_P(klass, T_MODULE))
411
- return rb_sprintf("#<%s:%p>", rb_class2name(rb_obj_class(klass)), (void*)klass);
412
- }
413
- return rb_class_path(klass);
414
- }
415
- else {
416
- return Qnil;
417
- }
418
- }
419
-
420
- // Oddly enough, this method is on debug.h BUT NOT on the MJIT header. Since I've had
421
- // crashes when trying to combine the MJIT header with the regular Ruby headers, let's
422
- // just supply the declaration for this function, as otherwise the build seems to fail
423
- // on macOS in CI.
424
- VALUE rb_profile_frame_singleton_method_p(VALUE frame);
425
-
426
- static VALUE
427
- qualified_method_name(VALUE frame, VALUE method_name)
428
- {
429
- if (method_name != Qnil) {
430
- VALUE classpath = backported_rb_profile_frame_classpath(frame);
431
- VALUE singleton_p = rb_profile_frame_singleton_method_p(frame);
432
-
433
- if (classpath != Qnil) {
434
- return rb_sprintf("%"PRIsVALUE"%s%"PRIsVALUE,
435
- classpath, singleton_p == Qtrue ? "." : "#", method_name);
436
- }
437
- else {
438
- return method_name;
439
- }
440
- }
441
- else {
442
- return Qnil;
443
- }
444
- }
445
-
446
- VALUE
447
- backported_rb_profile_frame_qualified_method_name(VALUE frame)
448
- {
449
- VALUE method_name = backported_rb_profile_frame_method_name(frame);
450
-
451
- return qualified_method_name(frame, method_name);
452
- }
453
- #endif // CLASSPATH_BACKPORT_NEEDED
454
-
455
- #endif // ifndef PRE_MJIT_RUBY
456
-
457
- // -----------------------------------------------------------------------------
458
-
459
- #ifdef PRE_MJIT_RUBY
460
-
461
- #include <stdbool.h>
462
-
463
- #include "ruby.h"
464
-
465
- #include "ruby_shards.h"
466
-
467
- typedef struct rb_backtrace_location_struct {
468
- enum LOCATION_TYPE {
469
- LOCATION_TYPE_ISEQ = 1,
470
- LOCATION_TYPE_ISEQ_CALCED,
471
- LOCATION_TYPE_CFUNC,
472
- } type;
473
-
474
- union {
475
- struct {
476
- const VALUE iseq; // Originally const rb_iseq_t *
477
- union {
478
- const VALUE *pc;
479
- int lineno;
480
- } lineno;
481
- } iseq;
482
- struct {
483
- ID mid;
484
- struct rb_backtrace_location_struct *prev_loc;
485
- } cfunc;
486
- } body;
487
- } rb_backtrace_location_t;
488
-
489
- struct valued_frame_info {
490
- rb_backtrace_location_t *loc;
491
- VALUE btobj;
492
- };
493
-
494
- // For Ruby < 2.6, we can't rely on the MJIT header. So we need to get... crafty. This alternative to
495
- // backtracie_rb_profile_frames_for_execution_context(...) piggybacks on the actual
496
- // Thread::Backtrace::Locations instances returned by Ruby's Thread#backtrace_locations, and then uses
497
- // knowledge of the VM internal layouts to get the data we need out of them.
498
- //
499
- // Currently, this approach gets us a lot less information than the Ruby >= 2.6 approach (e.g. currently we
500
- // get exactly the same as Thread::Backtrace::Locations offers), so it's a crappier replacement, but it does
501
- // get us support for older Rubies so it's better than nothing (to check the differences in behavior, check which
502
- // tests are disabled on < 2.6).
503
- int backtracie_profile_frames_from_ruby_locations(
504
- VALUE ruby_locations_array,
505
- raw_location *raw_locations
506
- ) {
507
- Check_Type(ruby_locations_array, T_ARRAY);
508
- int ruby_locations_array_size = RARRAY_LEN(ruby_locations_array);
509
-
510
- for (int i = 0; i < ruby_locations_array_size; i++) {
511
- // FIXME: We should validate that what we get out of the array is a Thread::Backtrace::Location instance
512
- VALUE location = rb_ary_entry(ruby_locations_array, i);
513
-
514
- // The leap of faith -- let's get into the data from the Location instance
515
- struct valued_frame_info *location_payload = DATA_PTR(location);
516
-
517
- if (location_payload->loc->type == LOCATION_TYPE_ISEQ) {
518
- // Trigger calculation of the line number; this is calculated lazily when this method is invoked and
519
- // it turns the location type from LOCATION_TYPE_ISEQ to LOCATION_TYPE_ISEQ_CALCED.
520
- rb_funcall(location, rb_intern("lineno"), 0);
521
-
522
- if (location_payload->loc->type == LOCATION_TYPE_ISEQ) {
523
- rb_raise(rb_eRuntimeError, "Internal error: Calling #lineno didn't turn a location into a LOCATION_TYPE_ISEQ_CALCED");
524
- }
525
- }
526
-
527
- if (location_payload->loc->type == LOCATION_TYPE_ISEQ || location_payload->loc->type == LOCATION_TYPE_ISEQ_CALCED) {
528
- // Ruby frame
529
- raw_locations[i].is_ruby_frame = true;
530
- raw_locations[i].should_use_iseq = true;
531
- raw_locations[i].should_use_cfunc_name = false;
532
- raw_locations[i].vm_method_type = -1;
533
- // FIXME: Poke MRI to always get the "calced" version, see location_lineno in vm_backtrace for the context
534
- raw_locations[i].line_number =
535
- location_payload->loc->type == LOCATION_TYPE_ISEQ_CALCED ?
536
- location_payload->loc->body.iseq.lineno.lineno : 0;
537
- raw_locations[i].iseq = (VALUE) location_payload->loc->body.iseq.iseq;
538
- raw_locations[i].callable_method_entry = Qnil;
539
- raw_locations[i].self = Qnil;
540
- raw_locations[i].cfunc_name = Qnil;
541
- } else {
542
- // cfunc frame
543
- raw_locations[i].is_ruby_frame = false;
544
- raw_locations[i].should_use_iseq = false;
545
- raw_locations[i].should_use_cfunc_name = true;
546
- raw_locations[i].vm_method_type = -1;
547
- raw_locations[i].line_number = 0;
548
- raw_locations[i].iseq = Qnil;
549
- raw_locations[i].callable_method_entry = Qnil;
550
- raw_locations[i].self = Qnil;
551
- raw_locations[i].cfunc_name = rb_id2str(location_payload->loc->body.cfunc.mid);
552
- }
553
- }
554
-
555
- return ruby_locations_array_size;
556
- }
557
-
558
- VALUE backtracie_called_id(raw_location *the_location) {
559
- // Not available on PRE_MJIT_RUBY
560
- return Qnil;
561
- }
562
-
563
- VALUE backtracie_defined_class(raw_location *the_location) {
564
- // Not available on PRE_MJIT_RUBY
565
- return Qnil;
566
- }
567
-
568
- VALUE backtracie_rb_vm_top_self() {
569
- // This is only used on gem initialization so... Let's be a bit lazy :)
570
- return rb_eval_string("TOPLEVEL_BINDING.eval('self')");
571
- }
572
-
573
- bool backtracie_iseq_is_block(raw_location *the_location) {
574
- // Not available on PRE_MJIT_RUBY
575
- return false;
576
- }
577
-
578
- bool backtracie_iseq_is_eval(raw_location *the_location) {
579
- // Not available on PRE_MJIT_RUBY
580
- return false;
581
- }
582
-
583
- VALUE backported_rb_profile_frame_method_name(VALUE frame) {
584
- // Not available on PRE_MJIT_RUBY
585
- return Qnil;
586
- }
587
-
588
- VALUE backtracie_refinement_name(raw_location *the_location) {
589
- // Not available on PRE_MJIT_RUBY
590
- return Qnil;
591
- }
592
-
593
- #endif // ifdef PRE_MJIT_RUBY