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
data/ext/pf2/src/lib.rs
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
extern crate serde;
|
2
|
-
#[macro_use]
|
3
|
-
extern crate serde_derive;
|
4
|
-
|
5
|
-
mod ruby_init;
|
6
|
-
|
7
|
-
mod backtrace;
|
8
|
-
mod profile;
|
9
|
-
mod profile_serializer;
|
10
|
-
mod ringbuffer;
|
11
|
-
mod sample;
|
12
|
-
mod scheduler;
|
13
|
-
mod serialization;
|
14
|
-
mod session;
|
15
|
-
#[cfg(target_os = "linux")]
|
16
|
-
mod signal_scheduler;
|
17
|
-
#[cfg(not(target_os = "linux"))]
|
18
|
-
mod signal_scheduler_unsupported_platform;
|
19
|
-
mod timer_thread_scheduler;
|
20
|
-
mod util;
|
21
|
-
|
22
|
-
mod ruby_internal_apis;
|
data/ext/pf2/src/profile.rs
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
use std::time::{Instant, SystemTime};
|
2
|
-
use std::{collections::HashSet, ptr::null_mut};
|
3
|
-
|
4
|
-
use rb_sys::*;
|
5
|
-
|
6
|
-
use backtrace_sys2::backtrace_create_state;
|
7
|
-
|
8
|
-
use super::backtrace::{Backtrace, BacktraceState};
|
9
|
-
use super::ringbuffer::Ringbuffer;
|
10
|
-
use super::sample::Sample;
|
11
|
-
|
12
|
-
// Capacity large enough to hold 1 second worth of samples for 16 threads
|
13
|
-
// 16 threads * 20 samples per second * 1 second = 320
|
14
|
-
const DEFAULT_RINGBUFFER_CAPACITY: usize = 320;
|
15
|
-
|
16
|
-
#[derive(Debug)]
|
17
|
-
pub struct Profile {
|
18
|
-
pub start_timestamp: SystemTime,
|
19
|
-
pub start_instant: Instant,
|
20
|
-
pub end_instant: Option<Instant>,
|
21
|
-
pub samples: Vec<Sample>,
|
22
|
-
pub temporary_sample_buffer: Ringbuffer,
|
23
|
-
pub backtrace_state: BacktraceState,
|
24
|
-
known_values: HashSet<VALUE>,
|
25
|
-
}
|
26
|
-
|
27
|
-
impl Profile {
|
28
|
-
pub fn new() -> Self {
|
29
|
-
let backtrace_state = unsafe {
|
30
|
-
let ptr = backtrace_create_state(
|
31
|
-
null_mut(),
|
32
|
-
1,
|
33
|
-
Some(Backtrace::backtrace_error_callback),
|
34
|
-
null_mut(),
|
35
|
-
);
|
36
|
-
BacktraceState::new(ptr)
|
37
|
-
};
|
38
|
-
|
39
|
-
Self {
|
40
|
-
start_timestamp: SystemTime::now(),
|
41
|
-
start_instant: Instant::now(),
|
42
|
-
end_instant: None,
|
43
|
-
samples: vec![],
|
44
|
-
temporary_sample_buffer: Ringbuffer::new(DEFAULT_RINGBUFFER_CAPACITY),
|
45
|
-
backtrace_state,
|
46
|
-
known_values: HashSet::new(),
|
47
|
-
}
|
48
|
-
}
|
49
|
-
|
50
|
-
pub fn flush_temporary_sample_buffer(&mut self) {
|
51
|
-
while let Some(sample) = self.temporary_sample_buffer.pop() {
|
52
|
-
self.known_values.insert(sample.ruby_thread);
|
53
|
-
for frame in sample.frames.iter() {
|
54
|
-
if frame == &0 {
|
55
|
-
break;
|
56
|
-
}
|
57
|
-
self.known_values.insert(*frame);
|
58
|
-
}
|
59
|
-
self.samples.push(sample);
|
60
|
-
}
|
61
|
-
}
|
62
|
-
|
63
|
-
pub unsafe fn dmark(&self) {
|
64
|
-
for value in self.known_values.iter() {
|
65
|
-
rb_gc_mark(*value);
|
66
|
-
}
|
67
|
-
self.temporary_sample_buffer.dmark();
|
68
|
-
}
|
69
|
-
}
|
@@ -1,241 +0,0 @@
|
|
1
|
-
use std::collections::HashMap;
|
2
|
-
use std::ffi::{c_char, CStr};
|
3
|
-
use std::hash::Hasher;
|
4
|
-
|
5
|
-
use rb_sys::*;
|
6
|
-
|
7
|
-
use crate::backtrace::Backtrace;
|
8
|
-
use crate::profile::Profile;
|
9
|
-
use crate::util::RTEST;
|
10
|
-
|
11
|
-
#[derive(Debug, Deserialize, Serialize)]
|
12
|
-
pub struct ProfileSerializer {
|
13
|
-
threads: HashMap<ThreadId, ThreadProfile>,
|
14
|
-
}
|
15
|
-
|
16
|
-
type ThreadId = VALUE;
|
17
|
-
|
18
|
-
#[derive(Debug, Deserialize, Serialize)]
|
19
|
-
struct ThreadProfile {
|
20
|
-
thread_id: ThreadId,
|
21
|
-
stack_tree: StackTreeNode,
|
22
|
-
#[serde(rename = "frames")]
|
23
|
-
frame_table: HashMap<FrameTableId, FrameTableEntry>,
|
24
|
-
samples: Vec<ProfileSample>,
|
25
|
-
}
|
26
|
-
|
27
|
-
impl ThreadProfile {
|
28
|
-
fn new(thread_id: ThreadId) -> ThreadProfile {
|
29
|
-
ThreadProfile {
|
30
|
-
thread_id,
|
31
|
-
// The root node
|
32
|
-
stack_tree: StackTreeNode { children: HashMap::new(), node_id: 0, frame_id: 0 },
|
33
|
-
frame_table: HashMap::new(),
|
34
|
-
samples: vec![],
|
35
|
-
}
|
36
|
-
}
|
37
|
-
}
|
38
|
-
|
39
|
-
type StackTreeNodeId = i32;
|
40
|
-
|
41
|
-
// Arbitary value which is used inside StackTreeNode.
|
42
|
-
// This VALUE should not be dereferenced as a pointer; we're merely using its pointer as a unique value.
|
43
|
-
// (Probably should be reconsidered)
|
44
|
-
type FrameTableId = VALUE;
|
45
|
-
|
46
|
-
#[derive(Debug, Deserialize, Serialize)]
|
47
|
-
struct StackTreeNode {
|
48
|
-
// TODO: Maybe a Vec<StackTreeNode> is enough?
|
49
|
-
// There's no particular meaning in using FrameTableId as key
|
50
|
-
children: HashMap<FrameTableId, StackTreeNode>,
|
51
|
-
// An arbitary ID (no particular meaning)
|
52
|
-
node_id: StackTreeNodeId,
|
53
|
-
// ?
|
54
|
-
frame_id: FrameTableId,
|
55
|
-
}
|
56
|
-
|
57
|
-
#[derive(Debug, Deserialize, Serialize)]
|
58
|
-
struct FrameTableEntry {
|
59
|
-
id: FrameTableId,
|
60
|
-
entry_type: FrameTableEntryType,
|
61
|
-
full_label: String,
|
62
|
-
file_name: Option<String>,
|
63
|
-
function_first_lineno: Option<i32>,
|
64
|
-
callsite_lineno: Option<i32>,
|
65
|
-
address: Option<usize>,
|
66
|
-
}
|
67
|
-
|
68
|
-
#[derive(Debug, Deserialize, Serialize)]
|
69
|
-
enum FrameTableEntryType {
|
70
|
-
Ruby,
|
71
|
-
Native,
|
72
|
-
}
|
73
|
-
|
74
|
-
// Represents leaf (末端)
|
75
|
-
#[derive(Debug, Deserialize, Serialize)]
|
76
|
-
struct ProfileSample {
|
77
|
-
elapsed_ns: u128,
|
78
|
-
stack_tree_id: StackTreeNodeId,
|
79
|
-
}
|
80
|
-
|
81
|
-
struct NativeFunctionFrame {
|
82
|
-
pub symbol_name: String,
|
83
|
-
pub address: Option<usize>,
|
84
|
-
}
|
85
|
-
|
86
|
-
impl ProfileSerializer {
|
87
|
-
pub fn serialize(profile: &Profile) -> String {
|
88
|
-
let mut sequence = 1;
|
89
|
-
|
90
|
-
let mut serializer = ProfileSerializer { threads: HashMap::new() };
|
91
|
-
|
92
|
-
unsafe {
|
93
|
-
// Process each sample
|
94
|
-
for sample in profile.samples.iter() {
|
95
|
-
let mut merged_stack: Vec<FrameTableEntry> = vec![];
|
96
|
-
|
97
|
-
// Process C-level stack
|
98
|
-
|
99
|
-
let mut c_stack: Vec<NativeFunctionFrame> = vec![];
|
100
|
-
// Rebuild the original backtrace (including inlined functions) from the PC.
|
101
|
-
for i in 0..sample.c_backtrace_pcs[0] {
|
102
|
-
let pc = sample.c_backtrace_pcs[i + 1];
|
103
|
-
Backtrace::backtrace_syminfo(
|
104
|
-
&profile.backtrace_state,
|
105
|
-
pc,
|
106
|
-
|_pc: usize, symname: *const c_char, symval: usize, _symsize: usize| {
|
107
|
-
if symname.is_null() {
|
108
|
-
c_stack.push(NativeFunctionFrame {
|
109
|
-
symbol_name: "(no symbol information)".to_owned(),
|
110
|
-
address: None,
|
111
|
-
});
|
112
|
-
} else {
|
113
|
-
c_stack.push(NativeFunctionFrame {
|
114
|
-
symbol_name: CStr::from_ptr(symname)
|
115
|
-
.to_str()
|
116
|
-
.unwrap()
|
117
|
-
.to_owned(),
|
118
|
-
address: Some(symval),
|
119
|
-
});
|
120
|
-
}
|
121
|
-
},
|
122
|
-
Some(Backtrace::backtrace_error_callback),
|
123
|
-
);
|
124
|
-
}
|
125
|
-
for frame in c_stack.iter() {
|
126
|
-
if frame.symbol_name.contains("pf2") {
|
127
|
-
// Skip Pf2-related frames
|
128
|
-
continue;
|
129
|
-
}
|
130
|
-
|
131
|
-
merged_stack.push(FrameTableEntry {
|
132
|
-
id: calculate_id_for_c_frame(&frame.symbol_name),
|
133
|
-
entry_type: FrameTableEntryType::Native,
|
134
|
-
full_label: frame.symbol_name.clone(),
|
135
|
-
file_name: None,
|
136
|
-
function_first_lineno: None,
|
137
|
-
callsite_lineno: None,
|
138
|
-
address: frame.address,
|
139
|
-
});
|
140
|
-
}
|
141
|
-
|
142
|
-
// Process Ruby-level stack
|
143
|
-
|
144
|
-
let ruby_stack_depth = sample.line_count;
|
145
|
-
for i in 0..ruby_stack_depth {
|
146
|
-
let frame: VALUE = sample.frames[i as usize];
|
147
|
-
let lineno: i32 = sample.linenos[i as usize];
|
148
|
-
let address: Option<usize> = {
|
149
|
-
let cme = frame
|
150
|
-
as *mut crate::ruby_internal_apis::rb_callable_method_entry_struct;
|
151
|
-
let cme = &*cme;
|
152
|
-
|
153
|
-
if (*(cme.def)).type_ == 1 {
|
154
|
-
// The cme is a Cfunc
|
155
|
-
Some((*(cme.def)).cfunc.func as usize)
|
156
|
-
} else {
|
157
|
-
// The cme is an ISeq (Ruby code) or some other type
|
158
|
-
None
|
159
|
-
}
|
160
|
-
};
|
161
|
-
let mut frame_full_label: VALUE = rb_profile_frame_full_label(frame);
|
162
|
-
let frame_full_label: String = if RTEST(frame_full_label) {
|
163
|
-
CStr::from_ptr(rb_string_value_cstr(&mut frame_full_label))
|
164
|
-
.to_str()
|
165
|
-
.unwrap()
|
166
|
-
.to_owned()
|
167
|
-
} else {
|
168
|
-
"(unknown)".to_owned()
|
169
|
-
};
|
170
|
-
let mut frame_path: VALUE = rb_profile_frame_path(frame);
|
171
|
-
let frame_path: String = if RTEST(frame_path) {
|
172
|
-
CStr::from_ptr(rb_string_value_cstr(&mut frame_path))
|
173
|
-
.to_str()
|
174
|
-
.unwrap()
|
175
|
-
.to_owned()
|
176
|
-
} else {
|
177
|
-
"(unknown)".to_owned()
|
178
|
-
};
|
179
|
-
let frame_first_lineno: VALUE = rb_profile_frame_first_lineno(frame);
|
180
|
-
let frame_first_lineno: Option<i32> = if RTEST(frame_first_lineno) {
|
181
|
-
Some(rb_num2int(frame_first_lineno).try_into().unwrap())
|
182
|
-
} else {
|
183
|
-
None
|
184
|
-
};
|
185
|
-
merged_stack.push(FrameTableEntry {
|
186
|
-
id: frame,
|
187
|
-
entry_type: FrameTableEntryType::Ruby,
|
188
|
-
full_label: frame_full_label,
|
189
|
-
file_name: Some(frame_path),
|
190
|
-
function_first_lineno: frame_first_lineno,
|
191
|
-
callsite_lineno: Some(lineno),
|
192
|
-
address,
|
193
|
-
});
|
194
|
-
}
|
195
|
-
|
196
|
-
// Find the Thread profile for this sample
|
197
|
-
let thread_serializer = serializer
|
198
|
-
.threads
|
199
|
-
.entry(sample.ruby_thread)
|
200
|
-
.or_insert(ThreadProfile::new(sample.ruby_thread));
|
201
|
-
|
202
|
-
// Stack frames, shallow to deep
|
203
|
-
let mut stack_tree = &mut thread_serializer.stack_tree;
|
204
|
-
|
205
|
-
while let Some(frame_table_entry) = merged_stack.pop() {
|
206
|
-
stack_tree = stack_tree.children.entry(frame_table_entry.id).or_insert({
|
207
|
-
let node = StackTreeNode {
|
208
|
-
children: HashMap::new(),
|
209
|
-
node_id: sequence,
|
210
|
-
frame_id: frame_table_entry.id,
|
211
|
-
};
|
212
|
-
sequence += 1;
|
213
|
-
node
|
214
|
-
});
|
215
|
-
|
216
|
-
if merged_stack.is_empty() {
|
217
|
-
// This is the leaf node, record a Sample
|
218
|
-
let elapsed_ns = (sample.timestamp - profile.start_instant).as_nanos();
|
219
|
-
thread_serializer
|
220
|
-
.samples
|
221
|
-
.push(ProfileSample { elapsed_ns, stack_tree_id: stack_tree.node_id });
|
222
|
-
}
|
223
|
-
|
224
|
-
// Register frame metadata to frame table, if not registered yet
|
225
|
-
thread_serializer
|
226
|
-
.frame_table
|
227
|
-
.entry(frame_table_entry.id)
|
228
|
-
.or_insert(frame_table_entry);
|
229
|
-
}
|
230
|
-
}
|
231
|
-
}
|
232
|
-
|
233
|
-
serde_json::to_string(&serializer).unwrap()
|
234
|
-
}
|
235
|
-
}
|
236
|
-
|
237
|
-
fn calculate_id_for_c_frame<T: std::hash::Hash>(t: &T) -> FrameTableId {
|
238
|
-
let mut s = std::collections::hash_map::DefaultHasher::new();
|
239
|
-
t.hash(&mut s);
|
240
|
-
s.finish()
|
241
|
-
}
|
data/ext/pf2/src/ringbuffer.rs
DELETED
@@ -1,150 +0,0 @@
|
|
1
|
-
use crate::sample::Sample;
|
2
|
-
|
3
|
-
#[derive(Debug)]
|
4
|
-
pub struct Ringbuffer {
|
5
|
-
capacity: usize,
|
6
|
-
buffer: Vec<Option<Sample>>,
|
7
|
-
read_index: usize,
|
8
|
-
write_index: usize,
|
9
|
-
}
|
10
|
-
|
11
|
-
#[derive(Debug, PartialEq)]
|
12
|
-
pub enum RingbufferError {
|
13
|
-
Full,
|
14
|
-
}
|
15
|
-
|
16
|
-
impl Ringbuffer {
|
17
|
-
pub fn new(capacity: usize) -> Self {
|
18
|
-
Self {
|
19
|
-
capacity,
|
20
|
-
buffer: std::iter::repeat_with(|| None).take(capacity + 1).collect::<Vec<_>>(),
|
21
|
-
read_index: 0,
|
22
|
-
write_index: 0,
|
23
|
-
}
|
24
|
-
}
|
25
|
-
|
26
|
-
// async-signal-safe
|
27
|
-
pub fn push(&mut self, sample: Sample) -> Result<(), RingbufferError> {
|
28
|
-
let next = (self.write_index + 1) % (self.capacity + 1);
|
29
|
-
if next == self.read_index {
|
30
|
-
return Err(RingbufferError::Full);
|
31
|
-
}
|
32
|
-
self.buffer[self.write_index] = Some(sample);
|
33
|
-
self.write_index = next;
|
34
|
-
Ok(())
|
35
|
-
}
|
36
|
-
|
37
|
-
pub fn pop(&mut self) -> Option<Sample> {
|
38
|
-
if self.read_index == self.write_index {
|
39
|
-
return None;
|
40
|
-
}
|
41
|
-
let sample = self.buffer[self.read_index].take();
|
42
|
-
self.read_index = (self.read_index + 1) % (self.capacity + 1);
|
43
|
-
sample
|
44
|
-
}
|
45
|
-
|
46
|
-
// This will call rb_gc_mark() for capacity * Sample::MAX_STACK_DEPTH * 2 times, which is a lot!
|
47
|
-
pub fn dmark(&self) {
|
48
|
-
for sample in self.buffer.iter().flatten() {
|
49
|
-
unsafe {
|
50
|
-
sample.dmark();
|
51
|
-
}
|
52
|
-
}
|
53
|
-
}
|
54
|
-
}
|
55
|
-
|
56
|
-
#[cfg(test)]
|
57
|
-
mod tests {
|
58
|
-
use super::*;
|
59
|
-
use std::time::Instant;
|
60
|
-
|
61
|
-
#[test]
|
62
|
-
fn test_ringbuffer() {
|
63
|
-
let mut ringbuffer = Ringbuffer::new(2);
|
64
|
-
assert_eq!(ringbuffer.pop(), None);
|
65
|
-
|
66
|
-
let sample1 = Sample {
|
67
|
-
ruby_thread: 1,
|
68
|
-
timestamp: Instant::now(),
|
69
|
-
line_count: 0,
|
70
|
-
frames: [0; 500],
|
71
|
-
linenos: [0; 500],
|
72
|
-
c_backtrace_pcs: [0; 1001],
|
73
|
-
};
|
74
|
-
let sample2 = Sample {
|
75
|
-
ruby_thread: 2,
|
76
|
-
timestamp: Instant::now(),
|
77
|
-
line_count: 0,
|
78
|
-
frames: [0; 500],
|
79
|
-
linenos: [0; 500],
|
80
|
-
c_backtrace_pcs: [0; 1001],
|
81
|
-
};
|
82
|
-
|
83
|
-
ringbuffer.push(sample1).unwrap();
|
84
|
-
ringbuffer.push(sample2).unwrap();
|
85
|
-
|
86
|
-
assert_eq!(ringbuffer.pop().unwrap().ruby_thread, 1);
|
87
|
-
assert_eq!(ringbuffer.pop().unwrap().ruby_thread, 2);
|
88
|
-
assert_eq!(ringbuffer.pop(), None);
|
89
|
-
}
|
90
|
-
|
91
|
-
#[test]
|
92
|
-
fn test_ringbuffer_full() {
|
93
|
-
let mut ringbuffer = Ringbuffer::new(1);
|
94
|
-
let sample1 = Sample {
|
95
|
-
ruby_thread: 1,
|
96
|
-
timestamp: Instant::now(),
|
97
|
-
line_count: 0,
|
98
|
-
frames: [0; 500],
|
99
|
-
linenos: [0; 500],
|
100
|
-
c_backtrace_pcs: [0; 1001],
|
101
|
-
};
|
102
|
-
let sample2 = Sample {
|
103
|
-
ruby_thread: 2,
|
104
|
-
timestamp: Instant::now(),
|
105
|
-
line_count: 0,
|
106
|
-
frames: [0; 500],
|
107
|
-
linenos: [0; 500],
|
108
|
-
c_backtrace_pcs: [0; 1001],
|
109
|
-
};
|
110
|
-
|
111
|
-
ringbuffer.push(sample1).unwrap();
|
112
|
-
assert_eq!(ringbuffer.push(sample2), Err(RingbufferError::Full));
|
113
|
-
}
|
114
|
-
|
115
|
-
#[test]
|
116
|
-
fn test_ringbuffer_write_a_lot() {
|
117
|
-
let mut ringbuffer = Ringbuffer::new(2);
|
118
|
-
let sample1 = Sample {
|
119
|
-
ruby_thread: 1,
|
120
|
-
timestamp: Instant::now(),
|
121
|
-
line_count: 0,
|
122
|
-
frames: [0; 500],
|
123
|
-
linenos: [0; 500],
|
124
|
-
c_backtrace_pcs: [0; 1001],
|
125
|
-
};
|
126
|
-
let sample2 = Sample {
|
127
|
-
ruby_thread: 2,
|
128
|
-
timestamp: Instant::now(),
|
129
|
-
line_count: 0,
|
130
|
-
frames: [0; 500],
|
131
|
-
linenos: [0; 500],
|
132
|
-
c_backtrace_pcs: [0; 1001],
|
133
|
-
};
|
134
|
-
let sample3 = Sample {
|
135
|
-
ruby_thread: 3,
|
136
|
-
timestamp: Instant::now(),
|
137
|
-
line_count: 0,
|
138
|
-
frames: [0; 500],
|
139
|
-
linenos: [0; 500],
|
140
|
-
c_backtrace_pcs: [0; 1001],
|
141
|
-
};
|
142
|
-
|
143
|
-
ringbuffer.push(sample1).unwrap();
|
144
|
-
ringbuffer.pop().unwrap();
|
145
|
-
ringbuffer.push(sample2).unwrap();
|
146
|
-
ringbuffer.pop().unwrap();
|
147
|
-
ringbuffer.push(sample3).unwrap();
|
148
|
-
assert_eq!(ringbuffer.pop().unwrap().ruby_thread, 3);
|
149
|
-
}
|
150
|
-
}
|
data/ext/pf2/src/ruby_init.rs
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
#![deny(unsafe_op_in_unsafe_fn)]
|
2
|
-
|
3
|
-
use rb_sys::*;
|
4
|
-
|
5
|
-
use crate::session::ruby_object::SessionRubyObject;
|
6
|
-
use crate::util::*;
|
7
|
-
|
8
|
-
#[allow(non_snake_case)]
|
9
|
-
#[no_mangle]
|
10
|
-
extern "C" fn Init_pf2() {
|
11
|
-
#[cfg(feature = "debug")]
|
12
|
-
{
|
13
|
-
env_logger::builder().format_timestamp(None).format_module_path(false).init();
|
14
|
-
}
|
15
|
-
|
16
|
-
unsafe {
|
17
|
-
let rb_mPf2: VALUE = rb_define_module(cstr!("Pf2"));
|
18
|
-
|
19
|
-
let rb_mPf2_Session = rb_define_class_under(rb_mPf2, cstr!("Session"), rb_cObject);
|
20
|
-
rb_define_alloc_func(rb_mPf2_Session, Some(SessionRubyObject::rb_alloc));
|
21
|
-
rb_define_method(
|
22
|
-
rb_mPf2_Session,
|
23
|
-
cstr!("initialize"),
|
24
|
-
Some(to_ruby_cfunc_with_args(SessionRubyObject::rb_initialize)),
|
25
|
-
-1,
|
26
|
-
);
|
27
|
-
rb_define_method(
|
28
|
-
rb_mPf2_Session,
|
29
|
-
cstr!("start"),
|
30
|
-
Some(to_ruby_cfunc_with_no_args(SessionRubyObject::rb_start)),
|
31
|
-
0,
|
32
|
-
);
|
33
|
-
rb_define_method(
|
34
|
-
rb_mPf2_Session,
|
35
|
-
cstr!("stop"),
|
36
|
-
Some(to_ruby_cfunc_with_no_args(SessionRubyObject::rb_stop)),
|
37
|
-
0,
|
38
|
-
);
|
39
|
-
}
|
40
|
-
}
|
@@ -1,77 +0,0 @@
|
|
1
|
-
#![allow(non_snake_case)]
|
2
|
-
#![allow(non_camel_case_types)]
|
3
|
-
|
4
|
-
use libc::{clockid_t, pthread_t};
|
5
|
-
use rb_sys::{rb_check_typeddata, rb_data_type_struct, RTypedData, VALUE};
|
6
|
-
use std::ffi::{c_char, c_int, c_void};
|
7
|
-
use std::mem::MaybeUninit;
|
8
|
-
|
9
|
-
#[cfg(target_os = "linux")]
|
10
|
-
use libc::pthread_getcpuclockid;
|
11
|
-
|
12
|
-
#[cfg(not(target_os = "linux"))]
|
13
|
-
pub unsafe fn pthread_getcpuclockid(thread: pthread_t, clk_id: *mut clockid_t) -> c_int {
|
14
|
-
unimplemented!()
|
15
|
-
}
|
16
|
-
|
17
|
-
// Types and structs from Ruby 3.4.0.
|
18
|
-
#[repr(C)]
|
19
|
-
pub struct rb_callable_method_entry_struct {
|
20
|
-
/* same fields with rb_method_entry_t */
|
21
|
-
pub flags: VALUE,
|
22
|
-
_padding_defined_class: VALUE,
|
23
|
-
pub def: *mut rb_method_definition_struct,
|
24
|
-
// ...
|
25
|
-
}
|
26
|
-
|
27
|
-
#[repr(C)]
|
28
|
-
pub struct rb_method_definition_struct {
|
29
|
-
pub type_: c_int,
|
30
|
-
_padding: [c_char; 4],
|
31
|
-
pub cfunc: rb_method_cfunc_struct,
|
32
|
-
// ...
|
33
|
-
}
|
34
|
-
|
35
|
-
#[repr(C)]
|
36
|
-
pub struct rb_method_cfunc_struct {
|
37
|
-
pub func: *mut c_void,
|
38
|
-
// ...
|
39
|
-
}
|
40
|
-
|
41
|
-
type rb_nativethread_id_t = libc::pthread_t;
|
42
|
-
|
43
|
-
#[repr(C)]
|
44
|
-
struct rb_native_thread {
|
45
|
-
_padding_serial: [c_char; 4], // rb_atomic_t
|
46
|
-
_padding_vm: *mut c_int, // struct rb_vm_struct
|
47
|
-
thread_id: rb_nativethread_id_t,
|
48
|
-
// ...
|
49
|
-
}
|
50
|
-
|
51
|
-
#[repr(C)]
|
52
|
-
struct rb_thread_struct {
|
53
|
-
_padding_lt_node: [c_char; 16], // struct ccan_list_node
|
54
|
-
_padding_self: VALUE,
|
55
|
-
_padding_ractor: *mut c_int, // rb_ractor_t
|
56
|
-
_padding_vm: *mut c_int, // rb_vm_t
|
57
|
-
nt: *mut rb_native_thread,
|
58
|
-
// ...
|
59
|
-
}
|
60
|
-
type rb_thread_t = rb_thread_struct;
|
61
|
-
|
62
|
-
/// Reimplementation of the internal RTYPEDDATA_TYPE macro.
|
63
|
-
unsafe fn RTYPEDDATA_TYPE(obj: VALUE) -> *const rb_data_type_struct {
|
64
|
-
let typed: *mut RTypedData = obj as *mut RTypedData;
|
65
|
-
(*typed).type_
|
66
|
-
}
|
67
|
-
|
68
|
-
unsafe fn rb_thread_ptr(thread: VALUE) -> *mut rb_thread_t {
|
69
|
-
unsafe { rb_check_typeddata(thread, RTYPEDDATA_TYPE(thread)) as *mut rb_thread_t }
|
70
|
-
}
|
71
|
-
|
72
|
-
pub unsafe fn rb_thread_getcpuclockid(thread: VALUE) -> clockid_t {
|
73
|
-
let mut cid: clockid_t = MaybeUninit::zeroed().assume_init();
|
74
|
-
let pthread_id: pthread_t = (*(*rb_thread_ptr(thread)).nt).thread_id;
|
75
|
-
pthread_getcpuclockid(pthread_id, &mut cid as *mut clockid_t);
|
76
|
-
cid
|
77
|
-
}
|
data/ext/pf2/src/sample.rs
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
use std::time::Instant;
|
2
|
-
|
3
|
-
use rb_sys::*;
|
4
|
-
|
5
|
-
use crate::backtrace::{Backtrace, BacktraceState};
|
6
|
-
|
7
|
-
const MAX_STACK_DEPTH: usize = 500;
|
8
|
-
const MAX_C_STACK_DEPTH: usize = 1000;
|
9
|
-
|
10
|
-
#[derive(Debug, PartialEq)]
|
11
|
-
pub struct Sample {
|
12
|
-
pub ruby_thread: VALUE,
|
13
|
-
pub timestamp: Instant,
|
14
|
-
pub line_count: i32,
|
15
|
-
pub frames: [VALUE; MAX_STACK_DEPTH],
|
16
|
-
pub linenos: [i32; MAX_STACK_DEPTH],
|
17
|
-
/// First element represents the backtrace depth.
|
18
|
-
pub c_backtrace_pcs: [usize; MAX_C_STACK_DEPTH + 1],
|
19
|
-
}
|
20
|
-
|
21
|
-
impl Sample {
|
22
|
-
// Nearly async-signal-safe
|
23
|
-
// (rb_profile_thread_frames isn't defined as a-s-s)
|
24
|
-
pub fn capture(ruby_thread: VALUE, backtrace_state: &BacktraceState) -> Self {
|
25
|
-
let mut c_backtrace_pcs = [0; MAX_C_STACK_DEPTH + 1];
|
26
|
-
|
27
|
-
Backtrace::backtrace_simple(
|
28
|
-
backtrace_state,
|
29
|
-
0,
|
30
|
-
|pc: usize| -> i32 {
|
31
|
-
if c_backtrace_pcs[0] >= MAX_C_STACK_DEPTH {
|
32
|
-
return 1;
|
33
|
-
}
|
34
|
-
c_backtrace_pcs[0] += 1;
|
35
|
-
c_backtrace_pcs[c_backtrace_pcs[0]] = pc;
|
36
|
-
0
|
37
|
-
},
|
38
|
-
Some(Backtrace::backtrace_error_callback),
|
39
|
-
);
|
40
|
-
|
41
|
-
let mut sample = Sample {
|
42
|
-
ruby_thread,
|
43
|
-
timestamp: Instant::now(),
|
44
|
-
line_count: 0,
|
45
|
-
frames: [0; MAX_STACK_DEPTH],
|
46
|
-
linenos: [0; MAX_STACK_DEPTH],
|
47
|
-
c_backtrace_pcs,
|
48
|
-
};
|
49
|
-
unsafe {
|
50
|
-
sample.line_count = rb_profile_thread_frames(
|
51
|
-
ruby_thread,
|
52
|
-
0,
|
53
|
-
2000,
|
54
|
-
sample.frames.as_mut_ptr(),
|
55
|
-
sample.linenos.as_mut_ptr(),
|
56
|
-
);
|
57
|
-
};
|
58
|
-
sample
|
59
|
-
}
|
60
|
-
|
61
|
-
pub unsafe fn dmark(&self) {
|
62
|
-
rb_gc_mark(self.ruby_thread);
|
63
|
-
for frame in self.frames.iter() {
|
64
|
-
rb_gc_mark(*frame);
|
65
|
-
}
|
66
|
-
}
|
67
|
-
}
|
data/ext/pf2/src/scheduler.rs
DELETED