pf2 0.9.0 → 1.0.0.alpha1
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.md +9 -4
- data/Rakefile +3 -9
- data/doc/development.md +6 -0
- data/ext/pf2/debug.h +12 -0
- data/ext/pf2/extconf.rb +23 -6
- data/ext/{pf2c → pf2}/sample.c +6 -0
- data/ext/{pf2c → pf2}/sample.h +4 -0
- data/ext/{pf2c → pf2}/serializer.c +1 -1
- data/ext/{pf2c → pf2}/session.c +70 -20
- data/ext/{pf2c → pf2}/session.h +5 -0
- data/lib/pf2/cli.rb +3 -11
- data/lib/pf2/reporter/firefox_profiler_ser2.rb +17 -13
- data/lib/pf2/reporter/stack_weaver.rb +8 -0
- data/lib/pf2/reporter.rb +0 -1
- data/lib/pf2/version.rb +1 -1
- data/lib/pf2.rb +1 -1
- metadata +18 -135
- data/Cargo.lock +0 -630
- data/Cargo.toml +0 -3
- data/crates/backtrace-sys2/.gitignore +0 -1
- data/crates/backtrace-sys2/Cargo.toml +0 -9
- data/crates/backtrace-sys2/build.rs +0 -45
- data/crates/backtrace-sys2/src/lib.rs +0 -5
- data/crates/backtrace-sys2/src/libbacktrace/.gitignore +0 -15
- data/crates/backtrace-sys2/src/libbacktrace/Isaac.Newton-Opticks.txt +0 -9286
- data/crates/backtrace-sys2/src/libbacktrace/LICENSE +0 -29
- data/crates/backtrace-sys2/src/libbacktrace/Makefile.am +0 -708
- data/crates/backtrace-sys2/src/libbacktrace/Makefile.in +0 -2820
- data/crates/backtrace-sys2/src/libbacktrace/README.md +0 -46
- data/crates/backtrace-sys2/src/libbacktrace/aclocal.m4 +0 -864
- data/crates/backtrace-sys2/src/libbacktrace/alloc.c +0 -167
- data/crates/backtrace-sys2/src/libbacktrace/allocfail.c +0 -136
- data/crates/backtrace-sys2/src/libbacktrace/allocfail.sh +0 -104
- data/crates/backtrace-sys2/src/libbacktrace/atomic.c +0 -113
- data/crates/backtrace-sys2/src/libbacktrace/backtrace-supported.h.in +0 -66
- data/crates/backtrace-sys2/src/libbacktrace/backtrace.c +0 -129
- data/crates/backtrace-sys2/src/libbacktrace/backtrace.h +0 -189
- data/crates/backtrace-sys2/src/libbacktrace/btest.c +0 -517
- data/crates/backtrace-sys2/src/libbacktrace/compile +0 -348
- data/crates/backtrace-sys2/src/libbacktrace/config/enable.m4 +0 -38
- data/crates/backtrace-sys2/src/libbacktrace/config/lead-dot.m4 +0 -31
- data/crates/backtrace-sys2/src/libbacktrace/config/libtool.m4 +0 -7545
- data/crates/backtrace-sys2/src/libbacktrace/config/ltoptions.m4 +0 -369
- data/crates/backtrace-sys2/src/libbacktrace/config/ltsugar.m4 +0 -123
- data/crates/backtrace-sys2/src/libbacktrace/config/ltversion.m4 +0 -23
- data/crates/backtrace-sys2/src/libbacktrace/config/lt~obsolete.m4 +0 -98
- data/crates/backtrace-sys2/src/libbacktrace/config/multi.m4 +0 -68
- data/crates/backtrace-sys2/src/libbacktrace/config/override.m4 +0 -117
- data/crates/backtrace-sys2/src/libbacktrace/config/unwind_ipinfo.m4 +0 -37
- data/crates/backtrace-sys2/src/libbacktrace/config/warnings.m4 +0 -227
- data/crates/backtrace-sys2/src/libbacktrace/config.guess +0 -1700
- data/crates/backtrace-sys2/src/libbacktrace/config.h.in +0 -185
- data/crates/backtrace-sys2/src/libbacktrace/config.sub +0 -1885
- data/crates/backtrace-sys2/src/libbacktrace/configure +0 -15929
- data/crates/backtrace-sys2/src/libbacktrace/configure.ac +0 -632
- data/crates/backtrace-sys2/src/libbacktrace/dwarf.c +0 -4409
- data/crates/backtrace-sys2/src/libbacktrace/edtest.c +0 -120
- data/crates/backtrace-sys2/src/libbacktrace/edtest2.c +0 -43
- data/crates/backtrace-sys2/src/libbacktrace/elf.c +0 -7465
- data/crates/backtrace-sys2/src/libbacktrace/fileline.c +0 -407
- data/crates/backtrace-sys2/src/libbacktrace/filenames.h +0 -52
- data/crates/backtrace-sys2/src/libbacktrace/filetype.awk +0 -13
- data/crates/backtrace-sys2/src/libbacktrace/install-debuginfo-for-buildid.sh.in +0 -65
- data/crates/backtrace-sys2/src/libbacktrace/install-sh +0 -501
- data/crates/backtrace-sys2/src/libbacktrace/instrumented_alloc.c +0 -114
- data/crates/backtrace-sys2/src/libbacktrace/internal.h +0 -428
- data/crates/backtrace-sys2/src/libbacktrace/ltmain.sh +0 -8636
- data/crates/backtrace-sys2/src/libbacktrace/macho.c +0 -1361
- data/crates/backtrace-sys2/src/libbacktrace/missing +0 -215
- data/crates/backtrace-sys2/src/libbacktrace/mmap.c +0 -331
- data/crates/backtrace-sys2/src/libbacktrace/mmapio.c +0 -110
- data/crates/backtrace-sys2/src/libbacktrace/move-if-change +0 -83
- data/crates/backtrace-sys2/src/libbacktrace/mtest.c +0 -410
- data/crates/backtrace-sys2/src/libbacktrace/nounwind.c +0 -66
- data/crates/backtrace-sys2/src/libbacktrace/pecoff.c +0 -1123
- data/crates/backtrace-sys2/src/libbacktrace/posix.c +0 -104
- data/crates/backtrace-sys2/src/libbacktrace/print.c +0 -117
- data/crates/backtrace-sys2/src/libbacktrace/read.c +0 -110
- data/crates/backtrace-sys2/src/libbacktrace/simple.c +0 -108
- data/crates/backtrace-sys2/src/libbacktrace/sort.c +0 -108
- data/crates/backtrace-sys2/src/libbacktrace/state.c +0 -72
- data/crates/backtrace-sys2/src/libbacktrace/stest.c +0 -137
- data/crates/backtrace-sys2/src/libbacktrace/test-driver +0 -148
- data/crates/backtrace-sys2/src/libbacktrace/test_format.c +0 -55
- data/crates/backtrace-sys2/src/libbacktrace/testlib.c +0 -234
- data/crates/backtrace-sys2/src/libbacktrace/testlib.h +0 -110
- data/crates/backtrace-sys2/src/libbacktrace/ttest.c +0 -161
- data/crates/backtrace-sys2/src/libbacktrace/unittest.c +0 -92
- data/crates/backtrace-sys2/src/libbacktrace/unknown.c +0 -65
- data/crates/backtrace-sys2/src/libbacktrace/xcoff.c +0 -1617
- data/crates/backtrace-sys2/src/libbacktrace/xztest.c +0 -508
- data/crates/backtrace-sys2/src/libbacktrace/zstdtest.c +0 -523
- data/crates/backtrace-sys2/src/libbacktrace/ztest.c +0 -541
- data/ext/pf2/Cargo.toml +0 -25
- data/ext/pf2/build.rs +0 -10
- data/ext/pf2/src/backtrace.rs +0 -127
- data/ext/pf2/src/lib.rs +0 -22
- data/ext/pf2/src/profile.rs +0 -69
- data/ext/pf2/src/profile_serializer.rs +0 -241
- data/ext/pf2/src/ringbuffer.rs +0 -150
- data/ext/pf2/src/ruby_c_api_helper.c +0 -6
- data/ext/pf2/src/ruby_init.rs +0 -40
- data/ext/pf2/src/ruby_internal_apis.rs +0 -77
- data/ext/pf2/src/sample.rs +0 -67
- data/ext/pf2/src/scheduler.rs +0 -10
- data/ext/pf2/src/serialization/profile.rs +0 -48
- data/ext/pf2/src/serialization/serializer.rs +0 -329
- data/ext/pf2/src/serialization.rs +0 -2
- data/ext/pf2/src/session/configuration.rs +0 -114
- data/ext/pf2/src/session/new_thread_watcher.rs +0 -80
- data/ext/pf2/src/session/ruby_object.rs +0 -90
- data/ext/pf2/src/session.rs +0 -248
- data/ext/pf2/src/siginfo_t.c +0 -5
- data/ext/pf2/src/signal_scheduler.rs +0 -201
- data/ext/pf2/src/signal_scheduler_unsupported_platform.rs +0 -39
- data/ext/pf2/src/timer_thread_scheduler.rs +0 -179
- data/ext/pf2/src/util.rs +0 -31
- data/ext/pf2c/extconf.rb +0 -21
- data/lib/pf2/reporter/firefox_profiler.rb +0 -397
- data/rust-toolchain.toml +0 -2
- data/rustfmt.toml +0 -1
- /data/ext/{pf2c → pf2}/backtrace_state.c +0 -0
- /data/ext/{pf2c → pf2}/backtrace_state.h +0 -0
- /data/ext/{pf2c → pf2}/configuration.c +0 -0
- /data/ext/{pf2c → pf2}/configuration.h +0 -0
- /data/ext/{pf2c → pf2}/pf2.c +0 -0
- /data/ext/{pf2c → pf2}/pf2.h +0 -0
- /data/ext/{pf2c → pf2}/ringbuffer.c +0 -0
- /data/ext/{pf2c → pf2}/ringbuffer.h +0 -0
- /data/ext/{pf2c → pf2}/serializer.h +0 -0
@@ -1,48 +0,0 @@
|
|
1
|
-
#[derive(Clone, Deserialize, Serialize)]
|
2
|
-
pub struct Profile {
|
3
|
-
pub samples: Vec<Sample>,
|
4
|
-
pub locations: Vec<Location>,
|
5
|
-
pub functions: Vec<Function>,
|
6
|
-
pub start_timestamp_ns: u128,
|
7
|
-
pub duration_ns: u128,
|
8
|
-
}
|
9
|
-
|
10
|
-
pub type LocationIndex = usize;
|
11
|
-
pub type FunctionIndex = usize;
|
12
|
-
|
13
|
-
/// Sample
|
14
|
-
#[derive(Clone, Serialize, Deserialize)]
|
15
|
-
pub struct Sample {
|
16
|
-
/// The stack leading to this sample.
|
17
|
-
/// The leaf node will be stored at `stack[0]`.
|
18
|
-
pub stack: Vec<LocationIndex>,
|
19
|
-
pub native_stack: Vec<LocationIndex>,
|
20
|
-
pub ruby_thread_id: Option<u64>,
|
21
|
-
pub elapsed_ns: u64,
|
22
|
-
}
|
23
|
-
|
24
|
-
/// Location represents a location (line) in the source code when a sample was captured.
|
25
|
-
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
26
|
-
pub struct Location {
|
27
|
-
pub function_index: FunctionIndex,
|
28
|
-
pub lineno: i32,
|
29
|
-
pub address: Option<usize>,
|
30
|
-
}
|
31
|
-
|
32
|
-
/// Function represents a Ruby method or a C function in the profile.
|
33
|
-
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
34
|
-
pub struct Function {
|
35
|
-
pub implementation: FunctionImplementation,
|
36
|
-
pub name: Option<String>, // unique key
|
37
|
-
pub filename: Option<String>,
|
38
|
-
/// The first line number in the method/function definition.
|
39
|
-
/// For the actual location (line) which was hit during sample capture, refer to `Location.lineno`.
|
40
|
-
pub start_lineno: Option<i32>,
|
41
|
-
pub start_address: Option<usize>,
|
42
|
-
}
|
43
|
-
|
44
|
-
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
45
|
-
pub enum FunctionImplementation {
|
46
|
-
Ruby,
|
47
|
-
Native,
|
48
|
-
}
|
@@ -1,329 +0,0 @@
|
|
1
|
-
use std::ffi::{c_char, CStr, CString};
|
2
|
-
|
3
|
-
use rb_sys::*;
|
4
|
-
|
5
|
-
use super::profile::{
|
6
|
-
Function, FunctionImplementation, FunctionIndex, Location, LocationIndex, Profile, Sample,
|
7
|
-
};
|
8
|
-
use crate::backtrace::Backtrace;
|
9
|
-
use crate::util::{cstr, RTEST};
|
10
|
-
|
11
|
-
pub struct ProfileSerializer2 {
|
12
|
-
profile: Profile,
|
13
|
-
}
|
14
|
-
|
15
|
-
impl ProfileSerializer2 {
|
16
|
-
pub fn new() -> ProfileSerializer2 {
|
17
|
-
ProfileSerializer2 {
|
18
|
-
profile: Profile {
|
19
|
-
start_timestamp_ns: 0,
|
20
|
-
duration_ns: 0,
|
21
|
-
samples: vec![],
|
22
|
-
locations: vec![],
|
23
|
-
functions: vec![],
|
24
|
-
},
|
25
|
-
}
|
26
|
-
}
|
27
|
-
|
28
|
-
pub fn serialize(&mut self, source: &crate::profile::Profile) {
|
29
|
-
// Fill in meta fields
|
30
|
-
self.profile.start_timestamp_ns =
|
31
|
-
source.start_timestamp.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos();
|
32
|
-
self.profile.duration_ns =
|
33
|
-
source.end_instant.unwrap().duration_since(source.start_instant).as_nanos();
|
34
|
-
|
35
|
-
// Create a Sample for each sample collected
|
36
|
-
for sample in source.samples.iter() {
|
37
|
-
// Iterate over the Ruby stack
|
38
|
-
let mut stack: Vec<LocationIndex> = vec![];
|
39
|
-
let ruby_stack_depth = sample.line_count;
|
40
|
-
for i in 0..ruby_stack_depth {
|
41
|
-
let frame: VALUE = sample.frames[i as usize];
|
42
|
-
let lineno: i32 = sample.linenos[i as usize];
|
43
|
-
let function = Self::extract_function_from_ruby_frame(frame);
|
44
|
-
|
45
|
-
let function_index = self.function_index_for(function);
|
46
|
-
let location_index = self.location_index_for(function_index, lineno);
|
47
|
-
stack.push(location_index);
|
48
|
-
}
|
49
|
-
|
50
|
-
// Iterate over the native stack
|
51
|
-
let mut native_stack: Vec<LocationIndex> = vec![];
|
52
|
-
let native_stack_depth = sample.c_backtrace_pcs[0];
|
53
|
-
for i in 1..(native_stack_depth - 1) {
|
54
|
-
let pc = sample.c_backtrace_pcs[i];
|
55
|
-
let function = Self::extract_function_from_native_pc(pc, source);
|
56
|
-
|
57
|
-
let function_index = self.function_index_for(function);
|
58
|
-
let location_index = self.location_index_for(function_index, 0);
|
59
|
-
native_stack.push(location_index);
|
60
|
-
}
|
61
|
-
|
62
|
-
self.profile.samples.push(Sample {
|
63
|
-
stack,
|
64
|
-
native_stack,
|
65
|
-
ruby_thread_id: Some(sample.ruby_thread),
|
66
|
-
elapsed_ns: sample.timestamp.duration_since(source.start_instant).as_nanos() as u64,
|
67
|
-
});
|
68
|
-
}
|
69
|
-
}
|
70
|
-
|
71
|
-
/// Returns the index of the function in `functions`.
|
72
|
-
/// Calling this method will modify `self.profile` in place.
|
73
|
-
fn function_index_for(&mut self, function: Function) -> FunctionIndex {
|
74
|
-
match self.profile.functions.iter_mut().position(|f| *f == function) {
|
75
|
-
Some(index) => index,
|
76
|
-
None => {
|
77
|
-
self.profile.functions.push(function);
|
78
|
-
self.profile.functions.len() - 1
|
79
|
-
}
|
80
|
-
}
|
81
|
-
}
|
82
|
-
|
83
|
-
/// Returns the index of the location in `locations`.
|
84
|
-
/// Calling this method will modify `self.profile` in place.
|
85
|
-
fn location_index_for(&mut self, function_index: FunctionIndex, lineno: i32) -> LocationIndex {
|
86
|
-
// Build a Location based on (1) the Function and (2) the actual line hit during sampling.
|
87
|
-
let location = Location { function_index, lineno, address: None };
|
88
|
-
match self.profile.locations.iter_mut().position(|l| *l == location) {
|
89
|
-
Some(index) => index,
|
90
|
-
None => {
|
91
|
-
self.profile.locations.push(location);
|
92
|
-
self.profile.locations.len() - 1
|
93
|
-
}
|
94
|
-
}
|
95
|
-
}
|
96
|
-
|
97
|
-
/// Build a Function from a Ruby frame.
|
98
|
-
fn extract_function_from_ruby_frame(frame: VALUE) -> Function {
|
99
|
-
unsafe {
|
100
|
-
let mut frame_full_label: VALUE = rb_profile_frame_full_label(frame);
|
101
|
-
let frame_full_label: Option<String> = if RTEST(frame_full_label) {
|
102
|
-
Some(
|
103
|
-
CStr::from_ptr(rb_string_value_cstr(&mut frame_full_label))
|
104
|
-
.to_str()
|
105
|
-
.unwrap()
|
106
|
-
.to_owned(),
|
107
|
-
)
|
108
|
-
} else {
|
109
|
-
None
|
110
|
-
};
|
111
|
-
|
112
|
-
let mut frame_path: VALUE = rb_profile_frame_path(frame);
|
113
|
-
let frame_path: Option<String> = if RTEST(frame_path) {
|
114
|
-
Some(
|
115
|
-
CStr::from_ptr(rb_string_value_cstr(&mut frame_path))
|
116
|
-
.to_str()
|
117
|
-
.unwrap()
|
118
|
-
.to_owned(),
|
119
|
-
)
|
120
|
-
} else {
|
121
|
-
None
|
122
|
-
};
|
123
|
-
|
124
|
-
let frame_first_lineno: VALUE = rb_profile_frame_first_lineno(frame);
|
125
|
-
let frame_first_lineno: Option<i32> = if RTEST(frame_first_lineno) {
|
126
|
-
Some(rb_num2int(frame_first_lineno).try_into().unwrap())
|
127
|
-
} else {
|
128
|
-
None
|
129
|
-
};
|
130
|
-
|
131
|
-
let start_address = Self::get_underlying_c_function_address(frame);
|
132
|
-
|
133
|
-
Function {
|
134
|
-
implementation: FunctionImplementation::Ruby,
|
135
|
-
name: frame_full_label,
|
136
|
-
filename: frame_path,
|
137
|
-
start_lineno: frame_first_lineno,
|
138
|
-
start_address,
|
139
|
-
}
|
140
|
-
}
|
141
|
-
}
|
142
|
-
|
143
|
-
fn get_underlying_c_function_address(frame: VALUE) -> Option<usize> {
|
144
|
-
unsafe {
|
145
|
-
let cme = frame as *mut crate::ruby_internal_apis::rb_callable_method_entry_struct;
|
146
|
-
let cme = &*cme; // *mut to reference
|
147
|
-
|
148
|
-
if (*(cme.def)).type_ == 1 {
|
149
|
-
// The cme is a Cfunc
|
150
|
-
Some((*(cme.def)).cfunc.func as usize)
|
151
|
-
} else {
|
152
|
-
// The cme is an ISeq (Ruby code) or some other type
|
153
|
-
None
|
154
|
-
}
|
155
|
-
}
|
156
|
-
}
|
157
|
-
|
158
|
-
/// Build a Function from a PC (program counter) obtained by libbacktrace.
|
159
|
-
fn extract_function_from_native_pc(pc: usize, source: &crate::profile::Profile) -> Function {
|
160
|
-
// Obtain the function name and address using libbacktrace
|
161
|
-
let mut function: Option<Function> = None;
|
162
|
-
Backtrace::backtrace_syminfo(
|
163
|
-
&source.backtrace_state,
|
164
|
-
pc,
|
165
|
-
|_pc: usize, symname: *const c_char, symval: usize, _symsize: usize| unsafe {
|
166
|
-
function = Some(Function {
|
167
|
-
implementation: FunctionImplementation::Native,
|
168
|
-
name: if symname.is_null() {
|
169
|
-
None
|
170
|
-
} else {
|
171
|
-
Some(CStr::from_ptr(symname).to_str().unwrap().to_owned())
|
172
|
-
},
|
173
|
-
filename: None,
|
174
|
-
start_lineno: None,
|
175
|
-
start_address: Some(symval),
|
176
|
-
});
|
177
|
-
},
|
178
|
-
Some(Backtrace::backtrace_error_callback),
|
179
|
-
);
|
180
|
-
function.unwrap()
|
181
|
-
}
|
182
|
-
|
183
|
-
pub fn to_ruby_hash(&self) -> VALUE {
|
184
|
-
unsafe {
|
185
|
-
let hash: VALUE = rb_hash_new();
|
186
|
-
|
187
|
-
// profile[:start_timestamp_ns]
|
188
|
-
rb_hash_aset(
|
189
|
-
hash,
|
190
|
-
rb_id2sym(rb_intern(cstr!("start_timestamp_ns"))),
|
191
|
-
rb_int2inum(self.profile.start_timestamp_ns as isize),
|
192
|
-
);
|
193
|
-
// profile[:duration_ns]
|
194
|
-
rb_hash_aset(
|
195
|
-
hash,
|
196
|
-
rb_id2sym(rb_intern(cstr!("duration_ns"))),
|
197
|
-
rb_int2inum(self.profile.duration_ns as isize),
|
198
|
-
);
|
199
|
-
|
200
|
-
// profile[:samples]
|
201
|
-
let samples: VALUE = rb_ary_new();
|
202
|
-
for sample in self.profile.samples.iter() {
|
203
|
-
// sample[:stack]
|
204
|
-
let stack: VALUE = rb_ary_new();
|
205
|
-
for &location_index in sample.stack.iter() {
|
206
|
-
rb_ary_push(stack, rb_int2inum(location_index as isize));
|
207
|
-
}
|
208
|
-
// sample[:native_stack]
|
209
|
-
let native_stack: VALUE = rb_ary_new();
|
210
|
-
for &location_index in sample.native_stack.iter() {
|
211
|
-
rb_ary_push(native_stack, rb_int2inum(location_index as isize));
|
212
|
-
}
|
213
|
-
// sample[:ruby_thread_id]
|
214
|
-
let ruby_thread_id = if let Some(ruby_thread_id) = sample.ruby_thread_id {
|
215
|
-
rb_int2inum(ruby_thread_id as isize)
|
216
|
-
} else {
|
217
|
-
Qnil as VALUE
|
218
|
-
};
|
219
|
-
// sample[:elapsed_ns]
|
220
|
-
let elapsed_ns = rb_ull2inum(sample.elapsed_ns);
|
221
|
-
|
222
|
-
let sample_hash: VALUE = rb_hash_new();
|
223
|
-
rb_hash_aset(sample_hash, rb_id2sym(rb_intern(cstr!("stack"))), stack);
|
224
|
-
rb_hash_aset(
|
225
|
-
sample_hash,
|
226
|
-
rb_id2sym(rb_intern(cstr!("native_stack"))),
|
227
|
-
native_stack,
|
228
|
-
);
|
229
|
-
rb_hash_aset(
|
230
|
-
sample_hash,
|
231
|
-
rb_id2sym(rb_intern(cstr!("ruby_thread_id"))),
|
232
|
-
ruby_thread_id,
|
233
|
-
);
|
234
|
-
rb_hash_aset(sample_hash, rb_id2sym(rb_intern(cstr!("elapsed_ns"))), elapsed_ns);
|
235
|
-
|
236
|
-
rb_ary_push(samples, sample_hash);
|
237
|
-
}
|
238
|
-
rb_hash_aset(hash, rb_id2sym(rb_intern(cstr!("samples"))), samples);
|
239
|
-
|
240
|
-
// profile[:locations]
|
241
|
-
let locations = rb_ary_new();
|
242
|
-
for location in self.profile.locations.iter() {
|
243
|
-
let location_hash: VALUE = rb_hash_new();
|
244
|
-
// location[:function_index]
|
245
|
-
rb_hash_aset(
|
246
|
-
location_hash,
|
247
|
-
rb_id2sym(rb_intern(cstr!("function_index"))),
|
248
|
-
rb_int2inum(location.function_index as isize),
|
249
|
-
);
|
250
|
-
// location[:lineno]
|
251
|
-
rb_hash_aset(
|
252
|
-
location_hash,
|
253
|
-
rb_id2sym(rb_intern(cstr!("lineno"))),
|
254
|
-
rb_int2inum(location.lineno as isize),
|
255
|
-
);
|
256
|
-
// location[:address]
|
257
|
-
rb_hash_aset(
|
258
|
-
location_hash,
|
259
|
-
rb_id2sym(rb_intern(cstr!("address"))),
|
260
|
-
if let Some(address) = location.address {
|
261
|
-
rb_int2inum(address as isize)
|
262
|
-
} else {
|
263
|
-
Qnil as VALUE
|
264
|
-
},
|
265
|
-
);
|
266
|
-
rb_ary_push(locations, location_hash);
|
267
|
-
}
|
268
|
-
rb_hash_aset(hash, rb_id2sym(rb_intern(cstr!("locations"))), locations);
|
269
|
-
|
270
|
-
// profile[:functions]
|
271
|
-
let functions = rb_ary_new();
|
272
|
-
for function in self.profile.functions.iter() {
|
273
|
-
let function_hash: VALUE = rb_hash_new();
|
274
|
-
// function[:implementation]
|
275
|
-
rb_hash_aset(
|
276
|
-
function_hash,
|
277
|
-
rb_id2sym(rb_intern(cstr!("implementation"))),
|
278
|
-
match function.implementation {
|
279
|
-
FunctionImplementation::Ruby => rb_id2sym(rb_intern(cstr!("ruby"))),
|
280
|
-
FunctionImplementation::Native => rb_id2sym(rb_intern(cstr!("native"))),
|
281
|
-
},
|
282
|
-
);
|
283
|
-
|
284
|
-
// function[:name]
|
285
|
-
let name: VALUE = match &function.name {
|
286
|
-
Some(name) => {
|
287
|
-
let cstring = CString::new(name.as_str()).unwrap();
|
288
|
-
rb_str_new_cstr(cstring.as_ptr())
|
289
|
-
}
|
290
|
-
None => Qnil as VALUE,
|
291
|
-
};
|
292
|
-
rb_hash_aset(function_hash, rb_id2sym(rb_intern(cstr!("name"))), name);
|
293
|
-
// function[:filename]
|
294
|
-
let filename: VALUE = match &function.filename {
|
295
|
-
Some(filename) => {
|
296
|
-
let cstring = CString::new(filename.as_str()).unwrap();
|
297
|
-
rb_str_new_cstr(cstring.as_ptr())
|
298
|
-
}
|
299
|
-
None => Qnil as VALUE,
|
300
|
-
};
|
301
|
-
rb_hash_aset(function_hash, rb_id2sym(rb_intern(cstr!("filename"))), filename);
|
302
|
-
// function[:start_lineno]
|
303
|
-
rb_hash_aset(
|
304
|
-
function_hash,
|
305
|
-
rb_id2sym(rb_intern(cstr!("start_lineno"))),
|
306
|
-
if let Some(start_lineno) = function.start_lineno {
|
307
|
-
rb_int2inum(start_lineno as isize)
|
308
|
-
} else {
|
309
|
-
Qnil as VALUE
|
310
|
-
},
|
311
|
-
);
|
312
|
-
// function[:start_address]
|
313
|
-
rb_hash_aset(
|
314
|
-
function_hash,
|
315
|
-
rb_id2sym(rb_intern(cstr!("start_address"))),
|
316
|
-
if let Some(start_address) = function.start_address {
|
317
|
-
rb_int2inum(start_address as isize)
|
318
|
-
} else {
|
319
|
-
Qnil as VALUE
|
320
|
-
},
|
321
|
-
);
|
322
|
-
rb_ary_push(functions, function_hash);
|
323
|
-
}
|
324
|
-
rb_hash_aset(hash, rb_id2sym(rb_intern(cstr!("functions"))), functions);
|
325
|
-
|
326
|
-
hash
|
327
|
-
}
|
328
|
-
}
|
329
|
-
}
|
@@ -1,114 +0,0 @@
|
|
1
|
-
use std::collections::HashSet;
|
2
|
-
use std::str::FromStr;
|
3
|
-
use std::time::Duration;
|
4
|
-
|
5
|
-
use rb_sys::*;
|
6
|
-
|
7
|
-
use crate::util::cstr;
|
8
|
-
|
9
|
-
#[cfg(target_os = "linux")]
|
10
|
-
pub const DEFAULT_SCHEDULER: Scheduler = Scheduler::Signal;
|
11
|
-
#[cfg(target_os = "linux")]
|
12
|
-
pub const DEFAULT_TIME_MODE: TimeMode = TimeMode::CpuTime;
|
13
|
-
#[cfg(not(target_os = "linux"))]
|
14
|
-
pub const DEFAULT_SCHEDULER: Scheduler = Scheduler::TimerThread;
|
15
|
-
#[cfg(not(target_os = "linux"))]
|
16
|
-
pub const DEFAULT_TIME_MODE: TimeMode = TimeMode::WallTime;
|
17
|
-
|
18
|
-
pub const DEFAULT_INTERVAL: Duration = Duration::from_millis(9);
|
19
|
-
|
20
|
-
#[derive(Clone, Debug)]
|
21
|
-
pub struct Configuration {
|
22
|
-
pub scheduler: Scheduler,
|
23
|
-
pub interval: Duration,
|
24
|
-
pub time_mode: TimeMode,
|
25
|
-
pub target_ruby_threads: Threads,
|
26
|
-
pub use_experimental_serializer: bool,
|
27
|
-
}
|
28
|
-
|
29
|
-
#[derive(Clone, Debug, PartialEq)]
|
30
|
-
pub enum Scheduler {
|
31
|
-
Signal,
|
32
|
-
TimerThread,
|
33
|
-
}
|
34
|
-
|
35
|
-
impl FromStr for Scheduler {
|
36
|
-
type Err = ();
|
37
|
-
|
38
|
-
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
39
|
-
match s {
|
40
|
-
"signal" => Ok(Self::Signal),
|
41
|
-
"timer_thread" => Ok(Self::TimerThread),
|
42
|
-
_ => Err(()),
|
43
|
-
}
|
44
|
-
}
|
45
|
-
}
|
46
|
-
|
47
|
-
#[derive(Clone, Debug, PartialEq)]
|
48
|
-
pub enum TimeMode {
|
49
|
-
CpuTime,
|
50
|
-
WallTime,
|
51
|
-
}
|
52
|
-
|
53
|
-
impl FromStr for TimeMode {
|
54
|
-
type Err = ();
|
55
|
-
|
56
|
-
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
57
|
-
match s {
|
58
|
-
"cpu" => Ok(Self::CpuTime),
|
59
|
-
"wall" => Ok(Self::WallTime),
|
60
|
-
_ => Err(()),
|
61
|
-
}
|
62
|
-
}
|
63
|
-
}
|
64
|
-
|
65
|
-
#[derive(Clone, Debug, PartialEq)]
|
66
|
-
pub enum Threads {
|
67
|
-
All,
|
68
|
-
Targeted(HashSet<VALUE>),
|
69
|
-
}
|
70
|
-
|
71
|
-
impl Configuration {
|
72
|
-
pub fn validate(&self) -> Result<(), String> {
|
73
|
-
if self.scheduler == Scheduler::TimerThread && self.time_mode == TimeMode::CpuTime {
|
74
|
-
return Err("TimerThread scheduler does not support `time_mode: :cpu`.".to_owned());
|
75
|
-
}
|
76
|
-
if self.scheduler == Scheduler::TimerThread && self.target_ruby_threads == Threads::All {
|
77
|
-
return Err(concat!(
|
78
|
-
"TimerThread scheduler does not support `threads: :all` at the moment. ",
|
79
|
-
"Consider using `threads: Thread.list` for watching all threads at profiler start."
|
80
|
-
)
|
81
|
-
.to_owned());
|
82
|
-
}
|
83
|
-
|
84
|
-
Ok(())
|
85
|
-
}
|
86
|
-
|
87
|
-
pub fn to_rb_hash(&self) -> VALUE {
|
88
|
-
let hash: VALUE = unsafe { rb_hash_new() };
|
89
|
-
unsafe {
|
90
|
-
rb_hash_aset(
|
91
|
-
hash,
|
92
|
-
rb_id2sym(rb_intern(cstr!("scheduler"))),
|
93
|
-
rb_id2sym(rb_intern(match self.scheduler {
|
94
|
-
Scheduler::Signal => cstr!("signal"),
|
95
|
-
Scheduler::TimerThread => cstr!("timer_thread"),
|
96
|
-
})),
|
97
|
-
);
|
98
|
-
rb_hash_aset(
|
99
|
-
hash,
|
100
|
-
rb_id2sym(rb_intern(cstr!("interval_ms"))),
|
101
|
-
rb_int2inum(self.interval.as_millis().try_into().unwrap()),
|
102
|
-
);
|
103
|
-
rb_hash_aset(
|
104
|
-
hash,
|
105
|
-
rb_id2sym(rb_intern(cstr!("time_mode"))),
|
106
|
-
rb_id2sym(rb_intern(match self.time_mode {
|
107
|
-
TimeMode::CpuTime => cstr!("cpu"),
|
108
|
-
TimeMode::WallTime => cstr!("wall"),
|
109
|
-
})),
|
110
|
-
);
|
111
|
-
}
|
112
|
-
hash
|
113
|
-
}
|
114
|
-
}
|
@@ -1,80 +0,0 @@
|
|
1
|
-
use std::collections::HashSet;
|
2
|
-
use std::ffi::c_void;
|
3
|
-
use std::mem::ManuallyDrop;
|
4
|
-
use std::ptr::null_mut;
|
5
|
-
use std::rc::Rc;
|
6
|
-
use std::sync::Mutex;
|
7
|
-
|
8
|
-
use rb_sys::*;
|
9
|
-
|
10
|
-
/// A helper to watch new Ruby threads.
|
11
|
-
///
|
12
|
-
/// `NewThreadWatcher` operates on the Events Hooks API.
|
13
|
-
/// Instead of relying on the `THREAD_EVENT_STARTED` event, it combines the
|
14
|
-
/// `THREAD_EVENT_RESUMED` event and an internal _known-threads_ record.
|
15
|
-
///
|
16
|
-
/// This is to support operations requiring the underlying pthread. Ruby Threads
|
17
|
-
/// are not guaranteed to be fully initialized at the time
|
18
|
-
/// `THREAD_EVENT_STARTED` is triggered; i.e. the underlying pthread has not
|
19
|
-
/// been created yet and `Thread#native_thread_id` returns `nil`.
|
20
|
-
pub struct NewThreadWatcher {
|
21
|
-
inner: Rc<Mutex<Inner>>,
|
22
|
-
event_hook: *mut rb_internal_thread_event_hook_t,
|
23
|
-
}
|
24
|
-
|
25
|
-
struct Inner {
|
26
|
-
known_threads: HashSet<VALUE>,
|
27
|
-
on_new_thread: Box<dyn Fn(VALUE)>,
|
28
|
-
}
|
29
|
-
|
30
|
-
impl NewThreadWatcher {
|
31
|
-
pub fn watch<F>(callback: F) -> Self
|
32
|
-
where
|
33
|
-
F: Fn(VALUE) + 'static,
|
34
|
-
{
|
35
|
-
let mut watcher = Self {
|
36
|
-
inner: Rc::new(Mutex::new(Inner {
|
37
|
-
known_threads: HashSet::new(),
|
38
|
-
on_new_thread: Box::new(callback),
|
39
|
-
})),
|
40
|
-
event_hook: null_mut(),
|
41
|
-
};
|
42
|
-
|
43
|
-
let inner_ptr = Rc::into_raw(Rc::clone(&watcher.inner));
|
44
|
-
unsafe {
|
45
|
-
watcher.event_hook = rb_internal_thread_add_event_hook(
|
46
|
-
Some(Self::on_thread_resume),
|
47
|
-
RUBY_INTERNAL_THREAD_EVENT_RESUMED,
|
48
|
-
inner_ptr as *mut c_void,
|
49
|
-
);
|
50
|
-
};
|
51
|
-
|
52
|
-
watcher
|
53
|
-
}
|
54
|
-
|
55
|
-
unsafe extern "C" fn on_thread_resume(
|
56
|
-
_flag: rb_event_flag_t,
|
57
|
-
data: *const rb_internal_thread_event_data_t,
|
58
|
-
custom_data: *mut c_void,
|
59
|
-
) {
|
60
|
-
let ruby_thread: VALUE = unsafe { (*data).thread };
|
61
|
-
|
62
|
-
// A pointer to Box<Inner> is passed as custom_data
|
63
|
-
let inner = unsafe { ManuallyDrop::new(Box::from_raw(custom_data as *mut Mutex<Inner>)) };
|
64
|
-
let mut inner = inner.lock().unwrap();
|
65
|
-
|
66
|
-
if !inner.known_threads.contains(&ruby_thread) {
|
67
|
-
inner.known_threads.insert(ruby_thread);
|
68
|
-
(inner.on_new_thread)(ruby_thread);
|
69
|
-
}
|
70
|
-
}
|
71
|
-
}
|
72
|
-
|
73
|
-
impl Drop for NewThreadWatcher {
|
74
|
-
fn drop(&mut self) {
|
75
|
-
log::trace!("Cleaning up event hook");
|
76
|
-
unsafe {
|
77
|
-
rb_internal_thread_remove_event_hook(self.event_hook);
|
78
|
-
}
|
79
|
-
}
|
80
|
-
}
|
@@ -1,90 +0,0 @@
|
|
1
|
-
use std::ffi::{c_int, c_void};
|
2
|
-
use std::mem;
|
3
|
-
use std::mem::ManuallyDrop;
|
4
|
-
use std::ptr::{addr_of, null_mut};
|
5
|
-
|
6
|
-
use rb_sys::*;
|
7
|
-
|
8
|
-
use crate::util::cstr;
|
9
|
-
|
10
|
-
use super::Session;
|
11
|
-
|
12
|
-
pub struct SessionRubyObject {
|
13
|
-
session: Option<Session>,
|
14
|
-
}
|
15
|
-
|
16
|
-
impl SessionRubyObject {
|
17
|
-
pub unsafe extern "C" fn rb_initialize(
|
18
|
-
argc: c_int,
|
19
|
-
argv: *const VALUE,
|
20
|
-
rbself: VALUE,
|
21
|
-
) -> VALUE {
|
22
|
-
let mut obj = unsafe { Self::get_struct_from(rbself) };
|
23
|
-
obj.session = Some(Session::new_from_rb_initialize(argc, argv, rbself));
|
24
|
-
Qnil.into()
|
25
|
-
}
|
26
|
-
|
27
|
-
pub unsafe extern "C" fn rb_start(rbself: VALUE) -> VALUE {
|
28
|
-
let mut obj = Self::get_struct_from(rbself);
|
29
|
-
match &mut obj.session {
|
30
|
-
Some(session) => session.start(),
|
31
|
-
None => panic!("Session is not initialized"),
|
32
|
-
}
|
33
|
-
}
|
34
|
-
|
35
|
-
pub unsafe extern "C" fn rb_stop(rbself: VALUE) -> VALUE {
|
36
|
-
let mut obj = Self::get_struct_from(rbself);
|
37
|
-
match &mut obj.session {
|
38
|
-
Some(session) => session.stop(),
|
39
|
-
None => panic!("Session is not initialized"),
|
40
|
-
}
|
41
|
-
}
|
42
|
-
|
43
|
-
// Extract the SessionRubyObject struct from a Ruby object
|
44
|
-
unsafe fn get_struct_from(obj: VALUE) -> ManuallyDrop<Box<Self>> {
|
45
|
-
unsafe {
|
46
|
-
let ptr = rb_check_typeddata(obj, addr_of!(RBDATA));
|
47
|
-
ManuallyDrop::new(Box::from_raw(ptr as *mut SessionRubyObject))
|
48
|
-
}
|
49
|
-
}
|
50
|
-
|
51
|
-
#[allow(non_snake_case)]
|
52
|
-
pub unsafe extern "C" fn rb_alloc(_rbself: VALUE) -> VALUE {
|
53
|
-
let obj = Box::new(SessionRubyObject { session: None });
|
54
|
-
|
55
|
-
let rb_mPf2: VALUE = rb_define_module(cstr!("Pf2"));
|
56
|
-
let rb_cSession = rb_define_class_under(rb_mPf2, cstr!("Session"), rb_cObject);
|
57
|
-
// Wrap the struct into a Ruby object
|
58
|
-
rb_data_typed_object_wrap(rb_cSession, Box::into_raw(obj) as *mut c_void, addr_of!(RBDATA))
|
59
|
-
}
|
60
|
-
|
61
|
-
unsafe extern "C" fn dmark(ptr: *mut c_void) {
|
62
|
-
let obj = ManuallyDrop::new(Box::from_raw(ptr as *mut SessionRubyObject));
|
63
|
-
if let Some(session) = &obj.session {
|
64
|
-
session.dmark()
|
65
|
-
}
|
66
|
-
}
|
67
|
-
|
68
|
-
unsafe extern "C" fn dfree(ptr: *mut c_void) {
|
69
|
-
drop(Box::from_raw(ptr as *mut SessionRubyObject));
|
70
|
-
}
|
71
|
-
|
72
|
-
unsafe extern "C" fn dsize(_: *const c_void) -> size_t {
|
73
|
-
// FIXME: Report something better
|
74
|
-
mem::size_of::<SessionRubyObject>() as size_t
|
75
|
-
}
|
76
|
-
}
|
77
|
-
|
78
|
-
static mut RBDATA: rb_data_type_t = rb_data_type_t {
|
79
|
-
wrap_struct_name: cstr!("SessionRubyObject"),
|
80
|
-
function: rb_data_type_struct__bindgen_ty_1 {
|
81
|
-
dmark: Some(SessionRubyObject::dmark),
|
82
|
-
dfree: Some(SessionRubyObject::dfree),
|
83
|
-
dsize: Some(SessionRubyObject::dsize),
|
84
|
-
dcompact: None,
|
85
|
-
reserved: [null_mut(); 1],
|
86
|
-
},
|
87
|
-
parent: null_mut(),
|
88
|
-
data: null_mut(),
|
89
|
-
flags: 0,
|
90
|
-
};
|