backtracie 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +18 -0
- data/.gitignore +33 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.standard.yml +14 -0
- data/CODE_OF_CONDUCT.adoc +134 -0
- data/COPYING +674 -0
- data/COPYING.LESSER +165 -0
- data/DEVELOPMENT_NOTES.adoc +399 -0
- data/README.adoc +66 -0
- data/Rakefile +29 -0
- data/backtracie.gemspec +43 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/ext/backtracie_native_extension/backtracie.c +185 -0
- data/ext/backtracie_native_extension/extconf.rb +28 -0
- data/ext/backtracie_native_extension/ruby_3.0.0.c +196 -0
- data/ext/backtracie_native_extension/ruby_3.0.0.h +7 -0
- data/gems.rb +15 -0
- data/lib/backtracie.rb +43 -0
- data/lib/backtracie/location.rb +51 -0
- data/lib/backtracie/version.rb +23 -0
- metadata +66 -0
data/README.adoc
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
= `backtracie`
|
2
|
+
:toc:
|
3
|
+
:toc-placement: macro
|
4
|
+
:toclevels: 4
|
5
|
+
:toc-title:
|
6
|
+
|
7
|
+
image:https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg["Contributor Covenant", link="CODE_OF_CONDUCT.adoc"]
|
8
|
+
image:https://badge.fury.io/rb/backtracie.svg["Gem Version", link="https://badge.fury.io/rb/backtracie"]
|
9
|
+
|
10
|
+
[discrete]
|
11
|
+
== Contents
|
12
|
+
|
13
|
+
toc::[]
|
14
|
+
|
15
|
+
== Installation
|
16
|
+
|
17
|
+
Add this line to your application's `gems.rb` or `Gemfile`:
|
18
|
+
|
19
|
+
[source,ruby]
|
20
|
+
----
|
21
|
+
gem 'backtracie'
|
22
|
+
----
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
[source,bash]
|
27
|
+
----
|
28
|
+
$ bundle install
|
29
|
+
----
|
30
|
+
|
31
|
+
Or install it yourself as:
|
32
|
+
|
33
|
+
[source,bash]
|
34
|
+
----
|
35
|
+
$ gem install backtracie
|
36
|
+
----
|
37
|
+
|
38
|
+
This gem is versioned according to http://semver.org/spec/v2.0.0.html[Semantic Versioning].
|
39
|
+
|
40
|
+
== Usage
|
41
|
+
|
42
|
+
TODO
|
43
|
+
|
44
|
+
== Development
|
45
|
+
|
46
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run `rake spec` to run the tests.
|
47
|
+
|
48
|
+
To open a console with the gem loaded, run `bundle console`.
|
49
|
+
|
50
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to https://rubygems.org[rubygems.org].
|
51
|
+
|
52
|
+
== Feedback and success stories
|
53
|
+
|
54
|
+
Your feedback is welcome!
|
55
|
+
|
56
|
+
== Contributing
|
57
|
+
|
58
|
+
Bug reports and pull requests are welcome on GitLab at https://github.com/ivoanjo/backtracie.
|
59
|
+
|
60
|
+
This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the http://contributor-covenant.org[Contributor Covenant] code of conduct.
|
61
|
+
|
62
|
+
Maintained with ❤️ by https://ivoanjo.me/[Ivo Anjo].
|
63
|
+
|
64
|
+
== Code of Conduct
|
65
|
+
|
66
|
+
Everyone interacting in the backtracie project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the link:CODE_OF_CONDUCT.adoc[code of conduct].
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# backtracie: Ruby gem for beautiful backtraces
|
4
|
+
# Copyright (C) 2021 Ivo Anjo <ivo@ivoanjo.me>
|
5
|
+
#
|
6
|
+
# This file is part of backtracie.
|
7
|
+
#
|
8
|
+
# backtracie is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
11
|
+
# (at your option) any later version.
|
12
|
+
#
|
13
|
+
# backtracie is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Lesser General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU Lesser General Public License
|
19
|
+
# along with backtracie. If not, see <http://www.gnu.org/licenses/>.
|
20
|
+
|
21
|
+
require "bundler/gem_tasks"
|
22
|
+
require "rspec/core/rake_task"
|
23
|
+
require "standard/rake"
|
24
|
+
require "rake/extensiontask"
|
25
|
+
|
26
|
+
RSpec::Core::RakeTask.new(:spec)
|
27
|
+
Rake::ExtensionTask.new("backtracie_native_extension")
|
28
|
+
|
29
|
+
task default: [:compile, :spec, :'standard:fix']
|
data/backtracie.gemspec
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# backtracie: Ruby gem for beautiful backtraces
|
4
|
+
# Copyright (C) 2021 Ivo Anjo <ivo@ivoanjo.me>
|
5
|
+
#
|
6
|
+
# This file is part of backtracie.
|
7
|
+
#
|
8
|
+
# backtracie is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
11
|
+
# (at your option) any later version.
|
12
|
+
#
|
13
|
+
# backtracie is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Lesser General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU Lesser General Public License
|
19
|
+
# along with backtracie. If not, see <http://www.gnu.org/licenses/>.
|
20
|
+
|
21
|
+
lib = File.expand_path("../lib", __FILE__)
|
22
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
23
|
+
require "backtracie/version"
|
24
|
+
|
25
|
+
Gem::Specification.new do |spec|
|
26
|
+
spec.name = "backtracie"
|
27
|
+
spec.version = Backtracie::VERSION
|
28
|
+
spec.authors = ["Ivo Anjo"]
|
29
|
+
spec.email = ["ivo@ivoanjo.me"]
|
30
|
+
|
31
|
+
spec.summary = "Ruby gem for beautiful backtraces"
|
32
|
+
spec.description = "Ruby gem for beautiful backtraces"
|
33
|
+
spec.homepage = "https://github.com/ivoanjo/backtracie"
|
34
|
+
spec.license = "LGPL-3.0+"
|
35
|
+
spec.required_ruby_version = ">= 3.0.0"
|
36
|
+
|
37
|
+
# Specify which files should be added to the gem when it is released.
|
38
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
39
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
40
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
41
|
+
end
|
42
|
+
spec.require_paths = ["lib", "ext"]
|
43
|
+
end
|
data/bin/console
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,185 @@
|
|
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
|
+
#include "ruby/ruby.h"
|
20
|
+
#include "ruby/debug.h"
|
21
|
+
|
22
|
+
#include "extconf.h"
|
23
|
+
|
24
|
+
#include "ruby_3.0.0.h"
|
25
|
+
|
26
|
+
// Constants
|
27
|
+
|
28
|
+
#define MAX_STACK_DEPTH 2000 // FIXME: Need to handle when this is not enough
|
29
|
+
|
30
|
+
// Globals
|
31
|
+
|
32
|
+
static VALUE backtracie_module = Qnil;
|
33
|
+
static VALUE backtracie_location_class = Qnil;
|
34
|
+
|
35
|
+
// Function headers
|
36
|
+
|
37
|
+
static VALUE primitive_caller_locations(VALUE self);
|
38
|
+
static VALUE primitive_backtrace_locations(VALUE self, VALUE thread);
|
39
|
+
static VALUE caller_locations(VALUE self, VALUE thread, int ignored_stack_top_frames);
|
40
|
+
inline static VALUE new_location(VALUE absolute_path, VALUE base_label, VALUE label, VALUE lineno, VALUE path, VALUE debug);
|
41
|
+
static bool is_ruby_frame(VALUE ruby_frame);
|
42
|
+
static VALUE ruby_frame_to_location(VALUE frame, VALUE last_ruby_line, VALUE correct_label);
|
43
|
+
static VALUE cfunc_frame_to_location(VALUE frame, VALUE last_ruby_frame, VALUE last_ruby_line);
|
44
|
+
static VALUE debug_frame(VALUE frame, VALUE type);
|
45
|
+
|
46
|
+
// Macros
|
47
|
+
|
48
|
+
#define VALUE_COUNT(array) (sizeof(array) / sizeof(VALUE))
|
49
|
+
|
50
|
+
void Init_backtracie_native_extension(void) {
|
51
|
+
backtracie_module = rb_const_get(rb_cObject, rb_intern("Backtracie"));
|
52
|
+
rb_global_variable(&backtracie_module);
|
53
|
+
|
54
|
+
rb_define_module_function(backtracie_module, "backtrace_locations", primitive_backtrace_locations, 1);
|
55
|
+
|
56
|
+
// We need to keep a reference to Backtracie::Locations around, to create new instances
|
57
|
+
backtracie_location_class = rb_const_get(backtracie_module, rb_intern("Location"));
|
58
|
+
rb_global_variable(&backtracie_location_class);
|
59
|
+
|
60
|
+
VALUE backtracie_primitive_module = rb_define_module_under(backtracie_module, "Primitive");
|
61
|
+
|
62
|
+
rb_define_module_function(backtracie_primitive_module, "caller_locations", primitive_caller_locations, 0);
|
63
|
+
}
|
64
|
+
|
65
|
+
// Get array of Backtracie::Locations for a given thread; if thread is nil, returns for the current thread
|
66
|
+
static VALUE caller_locations(VALUE self, VALUE thread, int ignored_stack_top_frames) {
|
67
|
+
int stack_depth = 0;
|
68
|
+
VALUE frames[MAX_STACK_DEPTH];
|
69
|
+
VALUE correct_labels[MAX_STACK_DEPTH];
|
70
|
+
int lines[MAX_STACK_DEPTH];
|
71
|
+
|
72
|
+
if (thread == Qnil) {
|
73
|
+
// Get for own thread
|
74
|
+
stack_depth = modified_rb_profile_frames(0, MAX_STACK_DEPTH, frames, correct_labels, lines);
|
75
|
+
} else {
|
76
|
+
stack_depth = modified_rb_profile_frames_for_thread(thread, 0, MAX_STACK_DEPTH, frames, correct_labels, lines);
|
77
|
+
}
|
78
|
+
|
79
|
+
// Ignore the last frame -- seems to be an uninteresting VM frame. MRI itself seems to ignore the last frame in
|
80
|
+
// the implementation of backtrace_collect()
|
81
|
+
int ignored_stack_bottom_frames = 1;
|
82
|
+
|
83
|
+
stack_depth -= ignored_stack_bottom_frames;
|
84
|
+
|
85
|
+
VALUE locations = rb_ary_new_capa(stack_depth - ignored_stack_top_frames);
|
86
|
+
|
87
|
+
// MRI does not give us the path or line number for frames implemented using C code. The convention in
|
88
|
+
// Kernel#caller_locations is to instead use the path and line number of the last Ruby frame seen.
|
89
|
+
// Thus, we keep that frame here to able to replicate that behavior.
|
90
|
+
// (This is why we also iterate the frames array backwards below -- so that it's easier to keep the last_ruby_frame)
|
91
|
+
VALUE last_ruby_frame = Qnil;
|
92
|
+
VALUE last_ruby_line = Qnil;
|
93
|
+
|
94
|
+
for (int i = stack_depth - 1; i >= ignored_stack_top_frames; i--) {
|
95
|
+
VALUE frame = frames[i];
|
96
|
+
int line = lines[i];
|
97
|
+
|
98
|
+
VALUE location = Qnil;
|
99
|
+
|
100
|
+
if (is_ruby_frame(frame)) {
|
101
|
+
last_ruby_frame = frame;
|
102
|
+
last_ruby_line = INT2FIX(line);
|
103
|
+
|
104
|
+
location = ruby_frame_to_location(frame, last_ruby_line, correct_labels[i]);
|
105
|
+
} else {
|
106
|
+
location = cfunc_frame_to_location(frame, last_ruby_frame, last_ruby_line);
|
107
|
+
}
|
108
|
+
|
109
|
+
rb_ary_store(locations, i - ignored_stack_top_frames, location);
|
110
|
+
}
|
111
|
+
|
112
|
+
return locations;
|
113
|
+
}
|
114
|
+
|
115
|
+
static VALUE primitive_caller_locations(VALUE self) {
|
116
|
+
// Ignore:
|
117
|
+
// * the current stack frame (native)
|
118
|
+
// * the Backtracie.caller_locations that called us
|
119
|
+
// * the frame from the caller itself (since we're replicating the semantics of Kernel#caller_locations)
|
120
|
+
int ignored_stack_top_frames = 3;
|
121
|
+
|
122
|
+
return caller_locations(self, Qnil, ignored_stack_top_frames);
|
123
|
+
}
|
124
|
+
|
125
|
+
static VALUE primitive_backtrace_locations(VALUE self, VALUE thread) {
|
126
|
+
rb_funcall(backtracie_module, rb_intern("ensure_object_is_thread"), 1, thread);
|
127
|
+
|
128
|
+
int ignored_stack_top_frames = 0;
|
129
|
+
|
130
|
+
return caller_locations(self, thread, ignored_stack_top_frames);
|
131
|
+
}
|
132
|
+
|
133
|
+
inline static VALUE new_location(VALUE absolute_path, VALUE base_label, VALUE label, VALUE lineno, VALUE path, VALUE debug) {
|
134
|
+
VALUE arguments[] = { absolute_path, base_label, label, lineno, path, debug };
|
135
|
+
return rb_class_new_instance(VALUE_COUNT(arguments), arguments, backtracie_location_class);
|
136
|
+
}
|
137
|
+
|
138
|
+
static bool is_ruby_frame(VALUE frame) {
|
139
|
+
VALUE absolute_path = rb_profile_frame_absolute_path(frame);
|
140
|
+
|
141
|
+
return (rb_profile_frame_path(frame) != Qnil || absolute_path != Qnil) &&
|
142
|
+
(rb_funcall(absolute_path, rb_intern("=="), 1, rb_str_new2("<cfunc>")) == Qfalse);
|
143
|
+
}
|
144
|
+
|
145
|
+
static VALUE ruby_frame_to_location(VALUE frame, VALUE last_ruby_line, VALUE correct_label) {
|
146
|
+
return new_location(
|
147
|
+
rb_profile_frame_absolute_path(frame),
|
148
|
+
rb_profile_frame_base_label(frame),
|
149
|
+
rb_profile_frame_label(correct_label),
|
150
|
+
last_ruby_line,
|
151
|
+
rb_profile_frame_path(frame),
|
152
|
+
debug_frame(frame, rb_str_new2("ruby_frame"))
|
153
|
+
);
|
154
|
+
}
|
155
|
+
|
156
|
+
static VALUE cfunc_frame_to_location(VALUE frame, VALUE last_ruby_frame, VALUE last_ruby_line) {
|
157
|
+
VALUE method_name = rb_profile_frame_method_name(frame); // Replaces label and base_label in cfuncs
|
158
|
+
|
159
|
+
return new_location(
|
160
|
+
last_ruby_frame != Qnil ? rb_profile_frame_absolute_path(last_ruby_frame) : Qnil,
|
161
|
+
method_name,
|
162
|
+
method_name,
|
163
|
+
last_ruby_line,
|
164
|
+
last_ruby_frame != Qnil ? rb_profile_frame_path(last_ruby_frame) : Qnil,
|
165
|
+
debug_frame(frame, rb_str_new2("cfunc_frame"))
|
166
|
+
);
|
167
|
+
}
|
168
|
+
|
169
|
+
// Used to dump all the things we get from the rb_profile_frames API, for debugging
|
170
|
+
static VALUE debug_frame(VALUE frame, VALUE type) {
|
171
|
+
VALUE arguments[] = {
|
172
|
+
rb_profile_frame_path(frame),
|
173
|
+
rb_profile_frame_absolute_path(frame),
|
174
|
+
rb_profile_frame_label(frame),
|
175
|
+
rb_profile_frame_base_label(frame),
|
176
|
+
rb_profile_frame_full_label(frame),
|
177
|
+
rb_profile_frame_first_lineno(frame),
|
178
|
+
rb_profile_frame_classpath(frame),
|
179
|
+
rb_profile_frame_singleton_method_p(frame),
|
180
|
+
rb_profile_frame_method_name(frame),
|
181
|
+
rb_profile_frame_qualified_method_name(frame),
|
182
|
+
type
|
183
|
+
};
|
184
|
+
return rb_ary_new_from_values(VALUE_COUNT(arguments), arguments);
|
185
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# backtracie: Ruby gem for beautiful backtraces
|
4
|
+
# Copyright (C) 2021 Ivo Anjo <ivo@ivoanjo.me>
|
5
|
+
#
|
6
|
+
# This file is part of backtracie.
|
7
|
+
#
|
8
|
+
# backtracie is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
11
|
+
# (at your option) any later version.
|
12
|
+
#
|
13
|
+
# backtracie is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Lesser General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU Lesser General Public License
|
19
|
+
# along with backtracie. If not, see <http://www.gnu.org/licenses/>.
|
20
|
+
|
21
|
+
require "mkmf"
|
22
|
+
|
23
|
+
# This warning gets really annoying when we include the Ruby mjit header file,
|
24
|
+
# let's omit it
|
25
|
+
$CFLAGS << " " << "-Wno-unused-function"
|
26
|
+
|
27
|
+
create_header
|
28
|
+
create_makefile "backtracie_native_extension"
|
@@ -0,0 +1,196 @@
|
|
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
|
+
#include "rb_mjit_min_header-3.0.0.h"
|
82
|
+
|
83
|
+
#include "ruby_3.0.0.h"
|
84
|
+
|
85
|
+
/**********************************************************************
|
86
|
+
|
87
|
+
vm_backtrace.c -
|
88
|
+
|
89
|
+
$Author: ko1 $
|
90
|
+
created at: Sun Jun 03 00:14:20 2012
|
91
|
+
|
92
|
+
Copyright (C) 1993-2012 Yukihiro Matsumoto
|
93
|
+
|
94
|
+
**********************************************************************/
|
95
|
+
|
96
|
+
inline static int
|
97
|
+
calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
98
|
+
{
|
99
|
+
VM_ASSERT(iseq);
|
100
|
+
VM_ASSERT(iseq->body);
|
101
|
+
VM_ASSERT(iseq->body->iseq_encoded);
|
102
|
+
VM_ASSERT(iseq->body->iseq_size);
|
103
|
+
if (! pc) {
|
104
|
+
/* This can happen during VM bootup. */
|
105
|
+
VM_ASSERT(iseq->body->type == ISEQ_TYPE_TOP);
|
106
|
+
VM_ASSERT(! iseq->body->local_table);
|
107
|
+
VM_ASSERT(! iseq->body->local_table_size);
|
108
|
+
return 0;
|
109
|
+
}
|
110
|
+
else {
|
111
|
+
ptrdiff_t n = pc - iseq->body->iseq_encoded;
|
112
|
+
VM_ASSERT(n <= iseq->body->iseq_size);
|
113
|
+
VM_ASSERT(n >= 0);
|
114
|
+
ASSUME(n >= 0);
|
115
|
+
size_t pos = n; /* no overflow */
|
116
|
+
if (LIKELY(pos)) {
|
117
|
+
/* use pos-1 because PC points next instruction at the beginning of instruction */
|
118
|
+
pos--;
|
119
|
+
}
|
120
|
+
#if VMDEBUG && defined(HAVE_BUILTIN___BUILTIN_TRAP)
|
121
|
+
else {
|
122
|
+
/* SDR() is not possible; that causes infinite loop. */
|
123
|
+
rb_print_backtrace();
|
124
|
+
__builtin_trap();
|
125
|
+
}
|
126
|
+
#endif
|
127
|
+
return rb_iseq_line_no(iseq, pos);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
int modified_rb_profile_frames_for_execution_context(
|
132
|
+
rb_execution_context_t *ec,
|
133
|
+
int start,
|
134
|
+
int limit,
|
135
|
+
VALUE *buff,
|
136
|
+
VALUE *correct_labels,
|
137
|
+
int *lines
|
138
|
+
) {
|
139
|
+
int i;
|
140
|
+
const rb_control_frame_t *cfp = ec->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
|
141
|
+
const rb_callable_method_entry_t *cme;
|
142
|
+
|
143
|
+
for (i=0; i<limit && cfp != end_cfp;) {
|
144
|
+
if (VM_FRAME_RUBYFRAME_P(cfp)) {
|
145
|
+
if (start > 0) {
|
146
|
+
start--;
|
147
|
+
continue;
|
148
|
+
}
|
149
|
+
|
150
|
+
// Stash the iseq so we can use it for the label. Otherwise ./spec/unit/backtracie_spec.rb:53 used to fail with
|
151
|
+
// expected: "block (3 levels) in fetch_or_store"
|
152
|
+
// got: "fetch_or_store"
|
153
|
+
// ...but works with this hack.
|
154
|
+
correct_labels[i] = (VALUE) cfp->iseq;
|
155
|
+
|
156
|
+
/* record frame info */
|
157
|
+
cme = rb_vm_frame_method_entry(cfp);
|
158
|
+
if (cme && cme->def->type == VM_METHOD_TYPE_ISEQ) {
|
159
|
+
buff[i] = (VALUE)cme;
|
160
|
+
}
|
161
|
+
else {
|
162
|
+
buff[i] = (VALUE)cfp->iseq;
|
163
|
+
}
|
164
|
+
|
165
|
+
if (lines) lines[i] = calc_lineno(cfp->iseq, cfp->pc);
|
166
|
+
|
167
|
+
i++;
|
168
|
+
}
|
169
|
+
else {
|
170
|
+
cme = rb_vm_frame_method_entry(cfp);
|
171
|
+
if (cme && cme->def->type == VM_METHOD_TYPE_CFUNC) {
|
172
|
+
buff[i] = (VALUE)cme;
|
173
|
+
if (lines) lines[i] = 0;
|
174
|
+
i++;
|
175
|
+
}
|
176
|
+
}
|
177
|
+
|
178
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
179
|
+
}
|
180
|
+
|
181
|
+
return i;
|
182
|
+
}
|
183
|
+
|
184
|
+
int modified_rb_profile_frames(int start, int limit, VALUE *buff, VALUE *correct_labels, int *lines) {
|
185
|
+
return modified_rb_profile_frames_for_execution_context(GET_EC(), start, limit, buff, correct_labels, lines);
|
186
|
+
}
|
187
|
+
|
188
|
+
int modified_rb_profile_frames_for_thread(VALUE thread, int start, int limit, VALUE *buff, VALUE *correct_labels, int *lines) {
|
189
|
+
// In here we're assuming that what we got is really a Thread or its subclass. This assumption NEEDS to be verified by
|
190
|
+
// the caller, otherwise I see a segfault in your future.
|
191
|
+
rb_thread_t *thread_pointer = (rb_thread_t*) DATA_PTR(thread);
|
192
|
+
|
193
|
+
if (thread_pointer->to_kill || thread_pointer->status == THREAD_KILLED) return Qnil;
|
194
|
+
|
195
|
+
return modified_rb_profile_frames_for_execution_context(thread_pointer->ec, start, limit, buff, correct_labels, lines);
|
196
|
+
}
|