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.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -2
  3. data/Cargo.lock +186 -17
  4. data/Cargo.toml +1 -1
  5. data/README.md +18 -6
  6. data/Rakefile +8 -0
  7. data/crates/backtrace-sys2/.gitignore +1 -0
  8. data/crates/backtrace-sys2/Cargo.toml +9 -0
  9. data/crates/backtrace-sys2/build.rs +48 -0
  10. data/crates/backtrace-sys2/src/lib.rs +5 -0
  11. data/crates/backtrace-sys2/src/libbacktrace/.gitignore +15 -0
  12. data/crates/backtrace-sys2/src/libbacktrace/Isaac.Newton-Opticks.txt +9286 -0
  13. data/crates/backtrace-sys2/src/libbacktrace/LICENSE +29 -0
  14. data/crates/backtrace-sys2/src/libbacktrace/Makefile.am +623 -0
  15. data/crates/backtrace-sys2/src/libbacktrace/Makefile.in +2666 -0
  16. data/crates/backtrace-sys2/src/libbacktrace/README.md +36 -0
  17. data/crates/backtrace-sys2/src/libbacktrace/aclocal.m4 +864 -0
  18. data/crates/backtrace-sys2/src/libbacktrace/alloc.c +167 -0
  19. data/crates/backtrace-sys2/src/libbacktrace/allocfail.c +136 -0
  20. data/crates/backtrace-sys2/src/libbacktrace/allocfail.sh +104 -0
  21. data/crates/backtrace-sys2/src/libbacktrace/atomic.c +113 -0
  22. data/crates/backtrace-sys2/src/libbacktrace/backtrace-supported.h.in +66 -0
  23. data/crates/backtrace-sys2/src/libbacktrace/backtrace.c +129 -0
  24. data/crates/backtrace-sys2/src/libbacktrace/backtrace.h +189 -0
  25. data/crates/backtrace-sys2/src/libbacktrace/btest.c +501 -0
  26. data/crates/backtrace-sys2/src/libbacktrace/compile +348 -0
  27. data/crates/backtrace-sys2/src/libbacktrace/config/enable.m4 +38 -0
  28. data/crates/backtrace-sys2/src/libbacktrace/config/lead-dot.m4 +31 -0
  29. data/crates/backtrace-sys2/src/libbacktrace/config/libtool.m4 +7436 -0
  30. data/crates/backtrace-sys2/src/libbacktrace/config/ltoptions.m4 +369 -0
  31. data/crates/backtrace-sys2/src/libbacktrace/config/ltsugar.m4 +123 -0
  32. data/crates/backtrace-sys2/src/libbacktrace/config/ltversion.m4 +23 -0
  33. data/crates/backtrace-sys2/src/libbacktrace/config/lt~obsolete.m4 +98 -0
  34. data/crates/backtrace-sys2/src/libbacktrace/config/multi.m4 +68 -0
  35. data/crates/backtrace-sys2/src/libbacktrace/config/override.m4 +117 -0
  36. data/crates/backtrace-sys2/src/libbacktrace/config/unwind_ipinfo.m4 +37 -0
  37. data/crates/backtrace-sys2/src/libbacktrace/config/warnings.m4 +227 -0
  38. data/crates/backtrace-sys2/src/libbacktrace/config.guess +1700 -0
  39. data/crates/backtrace-sys2/src/libbacktrace/config.h.in +182 -0
  40. data/crates/backtrace-sys2/src/libbacktrace/config.sub +1885 -0
  41. data/crates/backtrace-sys2/src/libbacktrace/configure +15740 -0
  42. data/crates/backtrace-sys2/src/libbacktrace/configure.ac +613 -0
  43. data/crates/backtrace-sys2/src/libbacktrace/dwarf.c +4402 -0
  44. data/crates/backtrace-sys2/src/libbacktrace/edtest.c +120 -0
  45. data/crates/backtrace-sys2/src/libbacktrace/edtest2.c +43 -0
  46. data/crates/backtrace-sys2/src/libbacktrace/elf.c +7443 -0
  47. data/crates/backtrace-sys2/src/libbacktrace/fileline.c +407 -0
  48. data/crates/backtrace-sys2/src/libbacktrace/filenames.h +52 -0
  49. data/crates/backtrace-sys2/src/libbacktrace/filetype.awk +13 -0
  50. data/crates/backtrace-sys2/src/libbacktrace/install-debuginfo-for-buildid.sh.in +65 -0
  51. data/crates/backtrace-sys2/src/libbacktrace/install-sh +501 -0
  52. data/crates/backtrace-sys2/src/libbacktrace/instrumented_alloc.c +114 -0
  53. data/crates/backtrace-sys2/src/libbacktrace/internal.h +389 -0
  54. data/crates/backtrace-sys2/src/libbacktrace/libtool.m4 +7436 -0
  55. data/crates/backtrace-sys2/src/libbacktrace/ltmain.sh +8636 -0
  56. data/crates/backtrace-sys2/src/libbacktrace/ltoptions.m4 +369 -0
  57. data/crates/backtrace-sys2/src/libbacktrace/ltsugar.m4 +123 -0
  58. data/crates/backtrace-sys2/src/libbacktrace/ltversion.m4 +23 -0
  59. data/crates/backtrace-sys2/src/libbacktrace/lt~obsolete.m4 +98 -0
  60. data/crates/backtrace-sys2/src/libbacktrace/macho.c +1355 -0
  61. data/crates/backtrace-sys2/src/libbacktrace/missing +215 -0
  62. data/crates/backtrace-sys2/src/libbacktrace/mmap.c +331 -0
  63. data/crates/backtrace-sys2/src/libbacktrace/mmapio.c +110 -0
  64. data/crates/backtrace-sys2/src/libbacktrace/move-if-change +83 -0
  65. data/crates/backtrace-sys2/src/libbacktrace/mtest.c +410 -0
  66. data/crates/backtrace-sys2/src/libbacktrace/nounwind.c +66 -0
  67. data/crates/backtrace-sys2/src/libbacktrace/pecoff.c +957 -0
  68. data/crates/backtrace-sys2/src/libbacktrace/posix.c +104 -0
  69. data/crates/backtrace-sys2/src/libbacktrace/print.c +92 -0
  70. data/crates/backtrace-sys2/src/libbacktrace/read.c +110 -0
  71. data/crates/backtrace-sys2/src/libbacktrace/simple.c +108 -0
  72. data/crates/backtrace-sys2/src/libbacktrace/sort.c +108 -0
  73. data/crates/backtrace-sys2/src/libbacktrace/state.c +72 -0
  74. data/crates/backtrace-sys2/src/libbacktrace/stest.c +137 -0
  75. data/crates/backtrace-sys2/src/libbacktrace/test-driver +148 -0
  76. data/crates/backtrace-sys2/src/libbacktrace/test_format.c +55 -0
  77. data/crates/backtrace-sys2/src/libbacktrace/testlib.c +234 -0
  78. data/crates/backtrace-sys2/src/libbacktrace/testlib.h +110 -0
  79. data/crates/backtrace-sys2/src/libbacktrace/ttest.c +161 -0
  80. data/crates/backtrace-sys2/src/libbacktrace/unittest.c +92 -0
  81. data/crates/backtrace-sys2/src/libbacktrace/unknown.c +65 -0
  82. data/crates/backtrace-sys2/src/libbacktrace/xcoff.c +1606 -0
  83. data/crates/backtrace-sys2/src/libbacktrace/xztest.c +508 -0
  84. data/crates/backtrace-sys2/src/libbacktrace/zstdtest.c +523 -0
  85. data/crates/backtrace-sys2/src/libbacktrace/ztest.c +541 -0
  86. data/ext/pf2/Cargo.toml +1 -0
  87. data/ext/pf2/src/backtrace.rs +127 -0
  88. data/ext/pf2/src/lib.rs +3 -0
  89. data/ext/pf2/src/profile.rs +16 -1
  90. data/ext/pf2/src/profile_serializer.rs +95 -21
  91. data/ext/pf2/src/ringbuffer.rs +7 -0
  92. data/ext/pf2/src/ruby_init.rs +18 -6
  93. data/ext/pf2/src/ruby_internal_apis.rs +47 -0
  94. data/ext/pf2/src/sample.rs +22 -1
  95. data/ext/pf2/src/signal_scheduler/configuration.rs +7 -0
  96. data/ext/pf2/src/signal_scheduler/timer_installer.rs +79 -126
  97. data/ext/pf2/src/signal_scheduler.rs +95 -26
  98. data/ext/pf2/src/timer_thread_scheduler.rs +88 -12
  99. data/ext/pf2/src/util.rs +2 -2
  100. data/lib/pf2/reporter.rb +12 -5
  101. data/lib/pf2/version.rb +1 -1
  102. data/lib/pf2.rb +3 -6
  103. metadata +97 -6
@@ -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::{collections::HashMap, ffi::CStr};
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
- for i in (0..(sample.line_count - 1)).rev() {
90
- let frame = sample.frames[i as usize];
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: frame_table_id,
172
+ frame_id: frame_table_entry.id,
111
173
  };
112
174
  sequence += 1;
113
175
  node
114
176
  });
115
177
 
116
- if i == 0 {
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
+ }
@@ -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();
@@ -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(to_ruby_cfunc3(SignalScheduler::rb_start)),
33
- 2,
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(to_ruby_cfunc1(SignalScheduler::rb_stop)),
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(to_ruby_cfunc3(TimerThreadScheduler::rb_start)),
53
- 2,
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(to_ruby_cfunc1(TimerThreadScheduler::rb_stop)),
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
+ }
@@ -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)]