pf2 0.2.0 → 0.4.0
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/CHANGELOG.md +31 -2
- data/Cargo.lock +186 -17
- data/Cargo.toml +1 -1
- data/README.md +18 -6
- data/Rakefile +8 -0
- data/crates/backtrace-sys2/.gitignore +1 -0
- data/crates/backtrace-sys2/Cargo.toml +9 -0
- data/crates/backtrace-sys2/build.rs +48 -0
- data/crates/backtrace-sys2/src/lib.rs +5 -0
- data/crates/backtrace-sys2/src/libbacktrace/.gitignore +15 -0
- data/crates/backtrace-sys2/src/libbacktrace/Isaac.Newton-Opticks.txt +9286 -0
- data/crates/backtrace-sys2/src/libbacktrace/LICENSE +29 -0
- data/crates/backtrace-sys2/src/libbacktrace/Makefile.am +623 -0
- data/crates/backtrace-sys2/src/libbacktrace/Makefile.in +2666 -0
- data/crates/backtrace-sys2/src/libbacktrace/README.md +36 -0
- data/crates/backtrace-sys2/src/libbacktrace/aclocal.m4 +864 -0
- data/crates/backtrace-sys2/src/libbacktrace/alloc.c +167 -0
- data/crates/backtrace-sys2/src/libbacktrace/allocfail.c +136 -0
- data/crates/backtrace-sys2/src/libbacktrace/allocfail.sh +104 -0
- data/crates/backtrace-sys2/src/libbacktrace/atomic.c +113 -0
- data/crates/backtrace-sys2/src/libbacktrace/backtrace-supported.h.in +66 -0
- data/crates/backtrace-sys2/src/libbacktrace/backtrace.c +129 -0
- data/crates/backtrace-sys2/src/libbacktrace/backtrace.h +189 -0
- data/crates/backtrace-sys2/src/libbacktrace/btest.c +501 -0
- data/crates/backtrace-sys2/src/libbacktrace/compile +348 -0
- data/crates/backtrace-sys2/src/libbacktrace/config/enable.m4 +38 -0
- data/crates/backtrace-sys2/src/libbacktrace/config/lead-dot.m4 +31 -0
- data/crates/backtrace-sys2/src/libbacktrace/config/libtool.m4 +7436 -0
- data/crates/backtrace-sys2/src/libbacktrace/config/ltoptions.m4 +369 -0
- data/crates/backtrace-sys2/src/libbacktrace/config/ltsugar.m4 +123 -0
- data/crates/backtrace-sys2/src/libbacktrace/config/ltversion.m4 +23 -0
- data/crates/backtrace-sys2/src/libbacktrace/config/lt~obsolete.m4 +98 -0
- data/crates/backtrace-sys2/src/libbacktrace/config/multi.m4 +68 -0
- data/crates/backtrace-sys2/src/libbacktrace/config/override.m4 +117 -0
- data/crates/backtrace-sys2/src/libbacktrace/config/unwind_ipinfo.m4 +37 -0
- data/crates/backtrace-sys2/src/libbacktrace/config/warnings.m4 +227 -0
- data/crates/backtrace-sys2/src/libbacktrace/config.guess +1700 -0
- data/crates/backtrace-sys2/src/libbacktrace/config.h.in +182 -0
- data/crates/backtrace-sys2/src/libbacktrace/config.sub +1885 -0
- data/crates/backtrace-sys2/src/libbacktrace/configure +15740 -0
- data/crates/backtrace-sys2/src/libbacktrace/configure.ac +613 -0
- data/crates/backtrace-sys2/src/libbacktrace/dwarf.c +4402 -0
- data/crates/backtrace-sys2/src/libbacktrace/edtest.c +120 -0
- data/crates/backtrace-sys2/src/libbacktrace/edtest2.c +43 -0
- data/crates/backtrace-sys2/src/libbacktrace/elf.c +7443 -0
- data/crates/backtrace-sys2/src/libbacktrace/fileline.c +407 -0
- data/crates/backtrace-sys2/src/libbacktrace/filenames.h +52 -0
- data/crates/backtrace-sys2/src/libbacktrace/filetype.awk +13 -0
- data/crates/backtrace-sys2/src/libbacktrace/install-debuginfo-for-buildid.sh.in +65 -0
- data/crates/backtrace-sys2/src/libbacktrace/install-sh +501 -0
- data/crates/backtrace-sys2/src/libbacktrace/instrumented_alloc.c +114 -0
- data/crates/backtrace-sys2/src/libbacktrace/internal.h +389 -0
- data/crates/backtrace-sys2/src/libbacktrace/libtool.m4 +7436 -0
- data/crates/backtrace-sys2/src/libbacktrace/ltmain.sh +8636 -0
- data/crates/backtrace-sys2/src/libbacktrace/ltoptions.m4 +369 -0
- data/crates/backtrace-sys2/src/libbacktrace/ltsugar.m4 +123 -0
- data/crates/backtrace-sys2/src/libbacktrace/ltversion.m4 +23 -0
- data/crates/backtrace-sys2/src/libbacktrace/lt~obsolete.m4 +98 -0
- data/crates/backtrace-sys2/src/libbacktrace/macho.c +1355 -0
- data/crates/backtrace-sys2/src/libbacktrace/missing +215 -0
- data/crates/backtrace-sys2/src/libbacktrace/mmap.c +331 -0
- data/crates/backtrace-sys2/src/libbacktrace/mmapio.c +110 -0
- data/crates/backtrace-sys2/src/libbacktrace/move-if-change +83 -0
- data/crates/backtrace-sys2/src/libbacktrace/mtest.c +410 -0
- data/crates/backtrace-sys2/src/libbacktrace/nounwind.c +66 -0
- data/crates/backtrace-sys2/src/libbacktrace/pecoff.c +957 -0
- data/crates/backtrace-sys2/src/libbacktrace/posix.c +104 -0
- data/crates/backtrace-sys2/src/libbacktrace/print.c +92 -0
- data/crates/backtrace-sys2/src/libbacktrace/read.c +110 -0
- data/crates/backtrace-sys2/src/libbacktrace/simple.c +108 -0
- data/crates/backtrace-sys2/src/libbacktrace/sort.c +108 -0
- data/crates/backtrace-sys2/src/libbacktrace/state.c +72 -0
- data/crates/backtrace-sys2/src/libbacktrace/stest.c +137 -0
- data/crates/backtrace-sys2/src/libbacktrace/test-driver +148 -0
- data/crates/backtrace-sys2/src/libbacktrace/test_format.c +55 -0
- data/crates/backtrace-sys2/src/libbacktrace/testlib.c +234 -0
- data/crates/backtrace-sys2/src/libbacktrace/testlib.h +110 -0
- data/crates/backtrace-sys2/src/libbacktrace/ttest.c +161 -0
- data/crates/backtrace-sys2/src/libbacktrace/unittest.c +92 -0
- data/crates/backtrace-sys2/src/libbacktrace/unknown.c +65 -0
- data/crates/backtrace-sys2/src/libbacktrace/xcoff.c +1606 -0
- data/crates/backtrace-sys2/src/libbacktrace/xztest.c +508 -0
- data/crates/backtrace-sys2/src/libbacktrace/zstdtest.c +523 -0
- data/crates/backtrace-sys2/src/libbacktrace/ztest.c +541 -0
- data/ext/pf2/Cargo.toml +1 -0
- data/ext/pf2/src/backtrace.rs +127 -0
- data/ext/pf2/src/lib.rs +3 -0
- data/ext/pf2/src/profile.rs +16 -1
- data/ext/pf2/src/profile_serializer.rs +95 -21
- data/ext/pf2/src/ringbuffer.rs +7 -0
- data/ext/pf2/src/ruby_init.rs +18 -6
- data/ext/pf2/src/ruby_internal_apis.rs +47 -0
- data/ext/pf2/src/sample.rs +22 -1
- data/ext/pf2/src/signal_scheduler/configuration.rs +7 -0
- data/ext/pf2/src/signal_scheduler/timer_installer.rs +79 -126
- data/ext/pf2/src/signal_scheduler.rs +95 -26
- data/ext/pf2/src/timer_thread_scheduler.rs +88 -12
- data/ext/pf2/src/util.rs +2 -2
- data/lib/pf2/reporter.rb +12 -5
- data/lib/pf2/version.rb +1 -1
- data/lib/pf2.rb +3 -6
- metadata +97 -6
data/ext/pf2/src/profile.rs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
use std::collections::HashSet;
|
|
2
1
|
use std::time::Instant;
|
|
2
|
+
use std::{collections::HashSet, ptr::null_mut};
|
|
3
3
|
|
|
4
4
|
use rb_sys::*;
|
|
5
5
|
|
|
6
|
+
use backtrace_sys2::backtrace_create_state;
|
|
7
|
+
|
|
8
|
+
use super::backtrace::{Backtrace, BacktraceState};
|
|
6
9
|
use super::ringbuffer::Ringbuffer;
|
|
7
10
|
use super::sample::Sample;
|
|
8
11
|
|
|
@@ -15,15 +18,27 @@ pub struct Profile {
|
|
|
15
18
|
pub start_timestamp: Instant,
|
|
16
19
|
pub samples: Vec<Sample>,
|
|
17
20
|
pub temporary_sample_buffer: Ringbuffer,
|
|
21
|
+
pub backtrace_state: BacktraceState,
|
|
18
22
|
known_values: HashSet<VALUE>,
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
impl Profile {
|
|
22
26
|
pub fn new() -> Self {
|
|
27
|
+
let backtrace_state = unsafe {
|
|
28
|
+
let ptr = backtrace_create_state(
|
|
29
|
+
null_mut(),
|
|
30
|
+
1,
|
|
31
|
+
Some(Backtrace::backtrace_error_callback),
|
|
32
|
+
null_mut(),
|
|
33
|
+
);
|
|
34
|
+
BacktraceState::new(ptr)
|
|
35
|
+
};
|
|
36
|
+
|
|
23
37
|
Self {
|
|
24
38
|
start_timestamp: Instant::now(),
|
|
25
39
|
samples: vec![],
|
|
26
40
|
temporary_sample_buffer: Ringbuffer::new(DEFAULT_RINGBUFFER_CAPACITY),
|
|
41
|
+
backtrace_state,
|
|
27
42
|
known_values: HashSet::new(),
|
|
28
43
|
}
|
|
29
44
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
use std::
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
use std::ffi::{c_char, CStr};
|
|
3
|
+
use std::hash::Hasher;
|
|
2
4
|
|
|
3
5
|
use rb_sys::*;
|
|
4
6
|
|
|
7
|
+
use crate::backtrace::Backtrace;
|
|
5
8
|
use crate::profile::Profile;
|
|
6
9
|
|
|
7
10
|
#[derive(Debug, Deserialize, Serialize)]
|
|
@@ -56,9 +59,17 @@ struct StackTreeNode {
|
|
|
56
59
|
|
|
57
60
|
#[derive(Debug, Deserialize, Serialize)]
|
|
58
61
|
struct FrameTableEntry {
|
|
62
|
+
id: FrameTableId,
|
|
63
|
+
entry_type: FrameTableEntryType,
|
|
59
64
|
full_label: String,
|
|
60
65
|
}
|
|
61
66
|
|
|
67
|
+
#[derive(Debug, Deserialize, Serialize)]
|
|
68
|
+
enum FrameTableEntryType {
|
|
69
|
+
Ruby,
|
|
70
|
+
Native,
|
|
71
|
+
}
|
|
72
|
+
|
|
62
73
|
// Represents leaf (末端)
|
|
63
74
|
#[derive(Debug, Deserialize, Serialize)]
|
|
64
75
|
struct ProfileSample {
|
|
@@ -77,6 +88,73 @@ impl ProfileSerializer {
|
|
|
77
88
|
unsafe {
|
|
78
89
|
// Process each sample
|
|
79
90
|
for sample in profile.samples.iter() {
|
|
91
|
+
let mut merged_stack: Vec<FrameTableEntry> = vec![];
|
|
92
|
+
|
|
93
|
+
// Process C-level stack
|
|
94
|
+
|
|
95
|
+
// A vec to keep the "programmer's" C stack trace.
|
|
96
|
+
// A single PC may be mapped to multiple inlined frames,
|
|
97
|
+
// so we keep the expanded stack frame in this Vec.
|
|
98
|
+
let mut c_stack: Vec<String> = vec![];
|
|
99
|
+
for i in 0..sample.c_backtrace_pcs[0] {
|
|
100
|
+
let pc = sample.c_backtrace_pcs[i + 1];
|
|
101
|
+
Backtrace::backtrace_syminfo(
|
|
102
|
+
&profile.backtrace_state,
|
|
103
|
+
pc,
|
|
104
|
+
|_pc: usize, symname: *const c_char, _symval: usize, _symsize: usize| {
|
|
105
|
+
if symname.is_null() {
|
|
106
|
+
c_stack.push("(no symbol information)".to_owned());
|
|
107
|
+
} else {
|
|
108
|
+
c_stack.push(CStr::from_ptr(symname).to_str().unwrap().to_owned());
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
Some(Backtrace::backtrace_error_callback),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Strip the C stack trace:
|
|
116
|
+
// - Remove Pf2-related frames which are always captured
|
|
117
|
+
// - Remove frames below rb_vm_exec
|
|
118
|
+
let mut reached_ruby = false;
|
|
119
|
+
c_stack.retain(|frame| {
|
|
120
|
+
if reached_ruby {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if frame.contains("pf2") {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if frame.contains("rb_vm_exec") || frame.contains("vm_call_cfunc_with_frame") {
|
|
127
|
+
reached_ruby = true;
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
true
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
for frame in c_stack.iter() {
|
|
134
|
+
merged_stack.push(FrameTableEntry {
|
|
135
|
+
id: calculate_id_for_c_frame(frame),
|
|
136
|
+
entry_type: FrameTableEntryType::Native,
|
|
137
|
+
full_label: frame.to_string(),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Process Ruby-level stack
|
|
142
|
+
|
|
143
|
+
let ruby_stack_depth = sample.line_count;
|
|
144
|
+
for i in 0..ruby_stack_depth {
|
|
145
|
+
let frame: VALUE = sample.frames[i as usize];
|
|
146
|
+
merged_stack.push(FrameTableEntry {
|
|
147
|
+
id: frame,
|
|
148
|
+
entry_type: FrameTableEntryType::Ruby,
|
|
149
|
+
full_label: CStr::from_ptr(rb_string_value_cstr(
|
|
150
|
+
&mut rb_profile_frame_full_label(frame),
|
|
151
|
+
))
|
|
152
|
+
.to_str()
|
|
153
|
+
.unwrap()
|
|
154
|
+
.to_owned(),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
80
158
|
// Find the Thread profile for this sample
|
|
81
159
|
let thread_serializer = serializer
|
|
82
160
|
.threads
|
|
@@ -86,34 +164,18 @@ impl ProfileSerializer {
|
|
|
86
164
|
// Stack frames, shallow to deep
|
|
87
165
|
let mut stack_tree = &mut thread_serializer.stack_tree;
|
|
88
166
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// Register frame metadata to frame table, if not registered yet
|
|
93
|
-
let frame_table_id: FrameTableId = frame;
|
|
94
|
-
thread_serializer
|
|
95
|
-
.frame_table
|
|
96
|
-
.entry(frame_table_id)
|
|
97
|
-
.or_insert(FrameTableEntry {
|
|
98
|
-
full_label: CStr::from_ptr(rb_string_value_cstr(
|
|
99
|
-
&mut rb_profile_frame_full_label(frame),
|
|
100
|
-
))
|
|
101
|
-
.to_str()
|
|
102
|
-
.unwrap()
|
|
103
|
-
.to_string(),
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
stack_tree = stack_tree.children.entry(frame_table_id).or_insert({
|
|
167
|
+
while let Some(frame_table_entry) = merged_stack.pop() {
|
|
168
|
+
stack_tree = stack_tree.children.entry(frame_table_entry.id).or_insert({
|
|
107
169
|
let node = StackTreeNode {
|
|
108
170
|
children: HashMap::new(),
|
|
109
171
|
node_id: sequence,
|
|
110
|
-
frame_id:
|
|
172
|
+
frame_id: frame_table_entry.id,
|
|
111
173
|
};
|
|
112
174
|
sequence += 1;
|
|
113
175
|
node
|
|
114
176
|
});
|
|
115
177
|
|
|
116
|
-
if
|
|
178
|
+
if merged_stack.is_empty() {
|
|
117
179
|
// This is the leaf node, record a Sample
|
|
118
180
|
let elapsed_ns = (sample.timestamp - profile.start_timestamp).as_nanos();
|
|
119
181
|
thread_serializer.samples.push(ProfileSample {
|
|
@@ -121,6 +183,12 @@ impl ProfileSerializer {
|
|
|
121
183
|
stack_tree_id: stack_tree.node_id,
|
|
122
184
|
});
|
|
123
185
|
}
|
|
186
|
+
|
|
187
|
+
// Register frame metadata to frame table, if not registered yet
|
|
188
|
+
thread_serializer
|
|
189
|
+
.frame_table
|
|
190
|
+
.entry(frame_table_entry.id)
|
|
191
|
+
.or_insert(frame_table_entry);
|
|
124
192
|
}
|
|
125
193
|
}
|
|
126
194
|
}
|
|
@@ -128,3 +196,9 @@ impl ProfileSerializer {
|
|
|
128
196
|
serde_json::to_string(&serializer).unwrap()
|
|
129
197
|
}
|
|
130
198
|
}
|
|
199
|
+
|
|
200
|
+
fn calculate_id_for_c_frame<T: std::hash::Hash>(t: &T) -> FrameTableId {
|
|
201
|
+
let mut s = std::collections::hash_map::DefaultHasher::new();
|
|
202
|
+
t.hash(&mut s);
|
|
203
|
+
s.finish()
|
|
204
|
+
}
|
data/ext/pf2/src/ringbuffer.rs
CHANGED
|
@@ -71,6 +71,7 @@ mod tests {
|
|
|
71
71
|
line_count: 0,
|
|
72
72
|
frames: [0; 500],
|
|
73
73
|
linenos: [0; 500],
|
|
74
|
+
c_backtrace_pcs: [0; 1001],
|
|
74
75
|
};
|
|
75
76
|
let sample2 = Sample {
|
|
76
77
|
ruby_thread: 2,
|
|
@@ -78,6 +79,7 @@ mod tests {
|
|
|
78
79
|
line_count: 0,
|
|
79
80
|
frames: [0; 500],
|
|
80
81
|
linenos: [0; 500],
|
|
82
|
+
c_backtrace_pcs: [0; 1001],
|
|
81
83
|
};
|
|
82
84
|
|
|
83
85
|
ringbuffer.push(sample1).unwrap();
|
|
@@ -97,6 +99,7 @@ mod tests {
|
|
|
97
99
|
line_count: 0,
|
|
98
100
|
frames: [0; 500],
|
|
99
101
|
linenos: [0; 500],
|
|
102
|
+
c_backtrace_pcs: [0; 1001],
|
|
100
103
|
};
|
|
101
104
|
let sample2 = Sample {
|
|
102
105
|
ruby_thread: 2,
|
|
@@ -104,6 +107,7 @@ mod tests {
|
|
|
104
107
|
line_count: 0,
|
|
105
108
|
frames: [0; 500],
|
|
106
109
|
linenos: [0; 500],
|
|
110
|
+
c_backtrace_pcs: [0; 1001],
|
|
107
111
|
};
|
|
108
112
|
|
|
109
113
|
ringbuffer.push(sample1).unwrap();
|
|
@@ -119,6 +123,7 @@ mod tests {
|
|
|
119
123
|
line_count: 0,
|
|
120
124
|
frames: [0; 500],
|
|
121
125
|
linenos: [0; 500],
|
|
126
|
+
c_backtrace_pcs: [0; 1001],
|
|
122
127
|
};
|
|
123
128
|
let sample2 = Sample {
|
|
124
129
|
ruby_thread: 2,
|
|
@@ -126,6 +131,7 @@ mod tests {
|
|
|
126
131
|
line_count: 0,
|
|
127
132
|
frames: [0; 500],
|
|
128
133
|
linenos: [0; 500],
|
|
134
|
+
c_backtrace_pcs: [0; 1001],
|
|
129
135
|
};
|
|
130
136
|
let sample3 = Sample {
|
|
131
137
|
ruby_thread: 3,
|
|
@@ -133,6 +139,7 @@ mod tests {
|
|
|
133
139
|
line_count: 0,
|
|
134
140
|
frames: [0; 500],
|
|
135
141
|
linenos: [0; 500],
|
|
142
|
+
c_backtrace_pcs: [0; 1001],
|
|
136
143
|
};
|
|
137
144
|
|
|
138
145
|
ringbuffer.push(sample1).unwrap();
|
data/ext/pf2/src/ruby_init.rs
CHANGED
|
@@ -26,16 +26,22 @@ extern "C" fn Init_pf2() {
|
|
|
26
26
|
let rb_mPf2_SignalScheduler =
|
|
27
27
|
rb_define_class_under(rb_mPf2, cstr!("SignalScheduler"), rb_cObject);
|
|
28
28
|
rb_define_alloc_func(rb_mPf2_SignalScheduler, Some(SignalScheduler::rb_alloc));
|
|
29
|
+
rb_define_method(
|
|
30
|
+
rb_mPf2_SignalScheduler,
|
|
31
|
+
cstr!("initialize"),
|
|
32
|
+
Some(to_ruby_cfunc_with_args(SignalScheduler::rb_initialize)),
|
|
33
|
+
-1,
|
|
34
|
+
);
|
|
29
35
|
rb_define_method(
|
|
30
36
|
rb_mPf2_SignalScheduler,
|
|
31
37
|
cstr!("start"),
|
|
32
|
-
Some(
|
|
33
|
-
|
|
38
|
+
Some(to_ruby_cfunc_with_no_args(SignalScheduler::rb_start)),
|
|
39
|
+
0,
|
|
34
40
|
);
|
|
35
41
|
rb_define_method(
|
|
36
42
|
rb_mPf2_SignalScheduler,
|
|
37
43
|
cstr!("stop"),
|
|
38
|
-
Some(
|
|
44
|
+
Some(to_ruby_cfunc_with_no_args(SignalScheduler::rb_stop)),
|
|
39
45
|
0,
|
|
40
46
|
);
|
|
41
47
|
}
|
|
@@ -46,16 +52,22 @@ extern "C" fn Init_pf2() {
|
|
|
46
52
|
rb_mPf2_TimerThreadScheduler,
|
|
47
53
|
Some(TimerThreadScheduler::rb_alloc),
|
|
48
54
|
);
|
|
55
|
+
rb_define_method(
|
|
56
|
+
rb_mPf2_TimerThreadScheduler,
|
|
57
|
+
cstr!("initialize"),
|
|
58
|
+
Some(to_ruby_cfunc_with_args(TimerThreadScheduler::rb_initialize)),
|
|
59
|
+
-1,
|
|
60
|
+
);
|
|
49
61
|
rb_define_method(
|
|
50
62
|
rb_mPf2_TimerThreadScheduler,
|
|
51
63
|
cstr!("start"),
|
|
52
|
-
Some(
|
|
53
|
-
|
|
64
|
+
Some(to_ruby_cfunc_with_no_args(TimerThreadScheduler::rb_start)),
|
|
65
|
+
0,
|
|
54
66
|
);
|
|
55
67
|
rb_define_method(
|
|
56
68
|
rb_mPf2_TimerThreadScheduler,
|
|
57
69
|
cstr!("stop"),
|
|
58
|
-
Some(
|
|
70
|
+
Some(to_ruby_cfunc_with_no_args(TimerThreadScheduler::rb_stop)),
|
|
59
71
|
0,
|
|
60
72
|
);
|
|
61
73
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#![allow(non_snake_case)]
|
|
2
|
+
#![allow(non_camel_case_types)]
|
|
3
|
+
|
|
4
|
+
use libc::{clockid_t, pthread_getcpuclockid, pthread_t};
|
|
5
|
+
use rb_sys::{rb_check_typeddata, rb_data_type_struct, RTypedData, VALUE};
|
|
6
|
+
use std::ffi::{c_char, c_int};
|
|
7
|
+
use std::mem::MaybeUninit;
|
|
8
|
+
|
|
9
|
+
// Types and structs from Ruby 3.4.0.
|
|
10
|
+
|
|
11
|
+
type rb_nativethread_id_t = libc::pthread_t;
|
|
12
|
+
|
|
13
|
+
#[repr(C)]
|
|
14
|
+
struct rb_native_thread {
|
|
15
|
+
_padding_serial: [c_char; 4], // rb_atomic_t
|
|
16
|
+
_padding_vm: *mut c_int, // struct rb_vm_struct
|
|
17
|
+
thread_id: rb_nativethread_id_t,
|
|
18
|
+
// ...
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#[repr(C)]
|
|
22
|
+
struct rb_thread_struct {
|
|
23
|
+
_padding_lt_node: [c_char; 16], // struct ccan_list_node
|
|
24
|
+
_padding_self: VALUE,
|
|
25
|
+
_padding_ractor: *mut c_int, // rb_ractor_t
|
|
26
|
+
_padding_vm: *mut c_int, // rb_vm_t
|
|
27
|
+
nt: *mut rb_native_thread,
|
|
28
|
+
// ...
|
|
29
|
+
}
|
|
30
|
+
type rb_thread_t = rb_thread_struct;
|
|
31
|
+
|
|
32
|
+
/// Reimplementation of the internal RTYPEDDATA_TYPE macro.
|
|
33
|
+
unsafe fn RTYPEDDATA_TYPE(obj: VALUE) -> *const rb_data_type_struct {
|
|
34
|
+
let typed: *mut RTypedData = obj as *mut RTypedData;
|
|
35
|
+
(*typed).type_
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
unsafe fn rb_thread_ptr(thread: VALUE) -> *mut rb_thread_t {
|
|
39
|
+
unsafe { rb_check_typeddata(thread, RTYPEDDATA_TYPE(thread)) as *mut rb_thread_t }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pub unsafe fn rb_thread_getcpuclockid(thread: VALUE) -> clockid_t {
|
|
43
|
+
let mut cid: clockid_t = MaybeUninit::zeroed().assume_init();
|
|
44
|
+
let pthread_id: pthread_t = (*(*rb_thread_ptr(thread)).nt).thread_id;
|
|
45
|
+
pthread_getcpuclockid(pthread_id, &mut cid as *mut clockid_t);
|
|
46
|
+
cid
|
|
47
|
+
}
|
data/ext/pf2/src/sample.rs
CHANGED
|
@@ -2,7 +2,10 @@ use std::time::Instant;
|
|
|
2
2
|
|
|
3
3
|
use rb_sys::*;
|
|
4
4
|
|
|
5
|
+
use crate::backtrace::{Backtrace, BacktraceState};
|
|
6
|
+
|
|
5
7
|
const MAX_STACK_DEPTH: usize = 500;
|
|
8
|
+
const MAX_C_STACK_DEPTH: usize = 1000;
|
|
6
9
|
|
|
7
10
|
#[derive(Debug, PartialEq)]
|
|
8
11
|
pub struct Sample {
|
|
@@ -11,18 +14,36 @@ pub struct Sample {
|
|
|
11
14
|
pub line_count: i32,
|
|
12
15
|
pub frames: [VALUE; MAX_STACK_DEPTH],
|
|
13
16
|
pub linenos: [i32; MAX_STACK_DEPTH],
|
|
17
|
+
pub c_backtrace_pcs: [usize; MAX_C_STACK_DEPTH + 1],
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
impl Sample {
|
|
17
21
|
// Nearly async-signal-safe
|
|
18
22
|
// (rb_profile_thread_frames isn't defined as a-s-s)
|
|
19
|
-
pub fn capture(ruby_thread: VALUE) -> Self {
|
|
23
|
+
pub fn capture(ruby_thread: VALUE, backtrace_state: &BacktraceState) -> Self {
|
|
24
|
+
let mut c_backtrace_pcs = [0; MAX_C_STACK_DEPTH + 1];
|
|
25
|
+
|
|
26
|
+
Backtrace::backtrace_simple(
|
|
27
|
+
backtrace_state,
|
|
28
|
+
0,
|
|
29
|
+
|pc: usize| -> i32 {
|
|
30
|
+
if c_backtrace_pcs[0] >= MAX_C_STACK_DEPTH {
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
c_backtrace_pcs[0] += 1;
|
|
34
|
+
c_backtrace_pcs[c_backtrace_pcs[0]] = pc;
|
|
35
|
+
0
|
|
36
|
+
},
|
|
37
|
+
Some(Backtrace::backtrace_error_callback),
|
|
38
|
+
);
|
|
39
|
+
|
|
20
40
|
let mut sample = Sample {
|
|
21
41
|
ruby_thread,
|
|
22
42
|
timestamp: Instant::now(),
|
|
23
43
|
line_count: 0,
|
|
24
44
|
frames: [0; MAX_STACK_DEPTH],
|
|
25
45
|
linenos: [0; MAX_STACK_DEPTH],
|
|
46
|
+
c_backtrace_pcs,
|
|
26
47
|
};
|
|
27
48
|
unsafe {
|
|
28
49
|
sample.line_count = rb_profile_thread_frames(
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
use std::collections::HashSet;
|
|
1
2
|
use std::str::FromStr;
|
|
3
|
+
use std::time::Duration;
|
|
4
|
+
|
|
5
|
+
use rb_sys::VALUE;
|
|
2
6
|
|
|
3
7
|
#[derive(Clone, Debug)]
|
|
4
8
|
pub struct Configuration {
|
|
9
|
+
pub interval: Duration,
|
|
5
10
|
pub time_mode: TimeMode,
|
|
11
|
+
pub target_ruby_threads: HashSet<VALUE>,
|
|
12
|
+
pub track_all_threads: bool,
|
|
6
13
|
}
|
|
7
14
|
|
|
8
15
|
#[derive(Clone, Debug)]
|