pf2 0.8.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.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +20 -4
  4. data/Rakefile +1 -0
  5. data/doc/development.md +17 -0
  6. data/examples/mandelbrot.rb +69 -0
  7. data/examples/mandelbrot_ractor.rb +77 -0
  8. data/ext/pf2/backtrace_state.c +10 -0
  9. data/ext/pf2/backtrace_state.h +10 -0
  10. data/ext/pf2/configuration.c +90 -0
  11. data/ext/pf2/configuration.h +23 -0
  12. data/ext/pf2/debug.h +12 -0
  13. data/ext/pf2/extconf.rb +23 -6
  14. data/ext/pf2/pf2.c +17 -0
  15. data/ext/pf2/pf2.h +8 -0
  16. data/ext/pf2/ringbuffer.c +74 -0
  17. data/ext/pf2/ringbuffer.h +24 -0
  18. data/ext/pf2/sample.c +76 -0
  19. data/ext/pf2/sample.h +26 -0
  20. data/ext/pf2/serializer.c +377 -0
  21. data/ext/pf2/serializer.h +58 -0
  22. data/ext/pf2/session.c +394 -0
  23. data/ext/pf2/session.h +56 -0
  24. data/lib/pf2/cli.rb +25 -11
  25. data/lib/pf2/reporter/annotate.rb +101 -0
  26. data/lib/pf2/reporter/firefox_profiler_ser2.rb +17 -13
  27. data/lib/pf2/reporter/stack_weaver.rb +8 -0
  28. data/lib/pf2/reporter.rb +1 -1
  29. data/lib/pf2/version.rb +1 -1
  30. data/lib/pf2.rb +1 -1
  31. data/vendor/libbacktrace/.gitignore +5 -0
  32. data/{crates/backtrace-sys2/src → vendor}/libbacktrace/README.md +1 -1
  33. data/{crates/backtrace-sys2/src → vendor}/libbacktrace/configure +23 -0
  34. data/{crates/backtrace-sys2/src → vendor}/libbacktrace/configure.ac +10 -0
  35. data/{crates/backtrace-sys2/src → vendor}/libbacktrace/dwarf.c +199 -15
  36. data/{crates/backtrace-sys2/src → vendor}/libbacktrace/elf.c +20 -14
  37. data/{crates/backtrace-sys2/src → vendor}/libbacktrace/fileline.c +2 -2
  38. data/{crates/backtrace-sys2/src → vendor}/libbacktrace/macho.c +2 -2
  39. data/{crates/backtrace-sys2/src → vendor}/libbacktrace/pecoff.c +2 -2
  40. metadata +111 -111
  41. data/Cargo.lock +0 -630
  42. data/Cargo.toml +0 -3
  43. data/crates/backtrace-sys2/.gitignore +0 -1
  44. data/crates/backtrace-sys2/Cargo.toml +0 -9
  45. data/crates/backtrace-sys2/build.rs +0 -45
  46. data/crates/backtrace-sys2/src/lib.rs +0 -5
  47. data/crates/backtrace-sys2/src/libbacktrace/.gitignore +0 -15
  48. data/ext/pf2/Cargo.toml +0 -25
  49. data/ext/pf2/build.rs +0 -10
  50. data/ext/pf2/src/backtrace.rs +0 -127
  51. data/ext/pf2/src/lib.rs +0 -22
  52. data/ext/pf2/src/profile.rs +0 -69
  53. data/ext/pf2/src/profile_serializer.rs +0 -241
  54. data/ext/pf2/src/ringbuffer.rs +0 -150
  55. data/ext/pf2/src/ruby_c_api_helper.c +0 -6
  56. data/ext/pf2/src/ruby_init.rs +0 -40
  57. data/ext/pf2/src/ruby_internal_apis.rs +0 -77
  58. data/ext/pf2/src/sample.rs +0 -67
  59. data/ext/pf2/src/scheduler.rs +0 -10
  60. data/ext/pf2/src/serialization/profile.rs +0 -48
  61. data/ext/pf2/src/serialization/serializer.rs +0 -329
  62. data/ext/pf2/src/serialization.rs +0 -2
  63. data/ext/pf2/src/session/configuration.rs +0 -114
  64. data/ext/pf2/src/session/new_thread_watcher.rs +0 -80
  65. data/ext/pf2/src/session/ruby_object.rs +0 -90
  66. data/ext/pf2/src/session.rs +0 -248
  67. data/ext/pf2/src/siginfo_t.c +0 -5
  68. data/ext/pf2/src/signal_scheduler.rs +0 -201
  69. data/ext/pf2/src/signal_scheduler_unsupported_platform.rs +0 -39
  70. data/ext/pf2/src/timer_thread_scheduler.rs +0 -179
  71. data/ext/pf2/src/util.rs +0 -31
  72. data/lib/pf2/reporter/firefox_profiler.rb +0 -397
  73. data/rust-toolchain.toml +0 -2
  74. data/rustfmt.toml +0 -1
  75. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/Isaac.Newton-Opticks.txt +0 -0
  76. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/LICENSE +0 -0
  77. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/Makefile.am +0 -0
  78. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/Makefile.in +0 -0
  79. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/aclocal.m4 +0 -0
  80. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/alloc.c +0 -0
  81. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/allocfail.c +0 -0
  82. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/allocfail.sh +0 -0
  83. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/atomic.c +0 -0
  84. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/backtrace-supported.h.in +0 -0
  85. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/backtrace.c +0 -0
  86. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/backtrace.h +0 -0
  87. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/btest.c +0 -0
  88. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/compile +0 -0
  89. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config/enable.m4 +0 -0
  90. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config/lead-dot.m4 +0 -0
  91. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config/libtool.m4 +0 -0
  92. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config/ltoptions.m4 +0 -0
  93. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config/ltsugar.m4 +0 -0
  94. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config/ltversion.m4 +0 -0
  95. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config/lt~obsolete.m4 +0 -0
  96. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config/multi.m4 +0 -0
  97. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config/override.m4 +0 -0
  98. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config/unwind_ipinfo.m4 +0 -0
  99. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config/warnings.m4 +0 -0
  100. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config.guess +0 -0
  101. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config.h.in +0 -0
  102. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/config.sub +0 -0
  103. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/edtest.c +0 -0
  104. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/edtest2.c +0 -0
  105. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/filenames.h +0 -0
  106. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/filetype.awk +0 -0
  107. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/install-debuginfo-for-buildid.sh.in +0 -0
  108. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/install-sh +0 -0
  109. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/instrumented_alloc.c +0 -0
  110. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/internal.h +0 -0
  111. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/ltmain.sh +0 -0
  112. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/missing +0 -0
  113. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/mmap.c +0 -0
  114. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/mmapio.c +0 -0
  115. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/move-if-change +0 -0
  116. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/mtest.c +0 -0
  117. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/nounwind.c +0 -0
  118. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/posix.c +0 -0
  119. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/print.c +0 -0
  120. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/read.c +0 -0
  121. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/simple.c +0 -0
  122. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/sort.c +0 -0
  123. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/state.c +0 -0
  124. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/stest.c +0 -0
  125. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/test-driver +0 -0
  126. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/test_format.c +0 -0
  127. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/testlib.c +0 -0
  128. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/testlib.h +0 -0
  129. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/ttest.c +0 -0
  130. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/unittest.c +0 -0
  131. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/unknown.c +0 -0
  132. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/xcoff.c +0 -0
  133. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/xztest.c +0 -0
  134. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/zstdtest.c +0 -0
  135. /data/{crates/backtrace-sys2/src → vendor}/libbacktrace/ztest.c +0 -0
@@ -1,15 +0,0 @@
1
- *~
2
- *.o
3
- *.lo
4
- *.a
5
- *.la
6
-
7
- .libs
8
- Makefile
9
- backtrace-supported.h
10
- config.h
11
- config.log
12
- config.status
13
- install-debuginfo-for-buildid.sh
14
- libtool
15
- stamp-h1
data/ext/pf2/Cargo.toml DELETED
@@ -1,25 +0,0 @@
1
- [package]
2
- name = "pf2"
3
- version = "0.1.0"
4
- edition = "2021"
5
- authors = ["Daisuke Aritomo <osyoyu@osyoyu.com>"]
6
- publish = false
7
-
8
- [lib]
9
- crate-type = ["cdylib"]
10
-
11
- [dependencies]
12
- backtrace-sys2 = { path = "../../crates/backtrace-sys2" }
13
- env_logger = { version = "0.11.0", optional = true }
14
- libc = "0.2.149"
15
- log = "0.4.20"
16
- rb-sys = { version = "0.9.105", features = ["stable-api"] }
17
- serde = "1.0.189"
18
- serde_derive = "1.0.189"
19
- serde_json = "1.0.107"
20
-
21
- [build-dependencies]
22
- cc = "1.0.83"
23
-
24
- [features]
25
- debug = ["env_logger"]
data/ext/pf2/build.rs DELETED
@@ -1,10 +0,0 @@
1
- use std::env;
2
-
3
- fn main() {
4
- cc::Build::new().file("src/siginfo_t.c").compile("ccode");
5
- cc::Build::new()
6
- .flag(format!("-I{}", env::var("DEP_RB_RBCONFIG_RUBYHDRDIR").unwrap()).as_str())
7
- .flag(format!("-I{}", env::var("DEP_RB_RBCONFIG_RUBYARCHHDRDIR").unwrap()).as_str())
8
- .file("src/ruby_c_api_helper.c")
9
- .compile("rubyhelper");
10
- }
@@ -1,127 +0,0 @@
1
- use std::ffi::{c_char, c_int, CStr};
2
-
3
- use libc::c_void;
4
-
5
- #[derive(Debug)]
6
- pub struct BacktraceState {
7
- ptr: *mut backtrace_sys2::backtrace_state,
8
- }
9
-
10
- unsafe impl Send for BacktraceState {}
11
- unsafe impl Sync for BacktraceState {}
12
-
13
- impl BacktraceState {
14
- pub unsafe fn new(ptr: *mut backtrace_sys2::backtrace_state) -> Self {
15
- Self { ptr }
16
- }
17
-
18
- pub fn as_mut_ptr(&self) -> *mut backtrace_sys2::backtrace_state {
19
- self.ptr
20
- }
21
- }
22
-
23
- pub struct Backtrace {}
24
-
25
- impl Backtrace {
26
- pub fn backtrace_simple<F>(
27
- state: &BacktraceState,
28
- skip: i32,
29
- mut on_ok: F,
30
- on_error: backtrace_sys2::backtrace_error_callback,
31
- ) where
32
- F: FnMut(usize) -> c_int,
33
- {
34
- unsafe {
35
- backtrace_sys2::backtrace_simple(
36
- state.as_mut_ptr(),
37
- skip,
38
- Some(Self::backtrace_simple_trampoline::<F>),
39
- on_error,
40
- &mut on_ok as *mut _ as *mut c_void,
41
- );
42
- }
43
- }
44
-
45
- #[allow(dead_code)] // TODO: Remove this
46
- pub fn backtrace_pcinfo<F>(
47
- state: &BacktraceState,
48
- pc: usize,
49
- mut on_ok: F,
50
- on_error: backtrace_sys2::backtrace_error_callback,
51
- ) where
52
- F: FnMut(usize, *const c_char, c_int, *const c_char) -> c_int,
53
- {
54
- unsafe {
55
- backtrace_sys2::backtrace_pcinfo(
56
- state.as_mut_ptr(),
57
- pc,
58
- Some(Self::backtrace_full_trampoline::<F>),
59
- on_error,
60
- &mut on_ok as *mut _ as *mut c_void,
61
- );
62
- }
63
- }
64
-
65
- pub fn backtrace_syminfo<F>(
66
- state: &BacktraceState,
67
- pc: usize,
68
- mut on_ok: F,
69
- on_error: backtrace_sys2::backtrace_error_callback,
70
- ) where
71
- F: FnMut(usize, *const c_char, usize, usize),
72
- {
73
- unsafe {
74
- backtrace_sys2::backtrace_syminfo(
75
- state.as_mut_ptr(),
76
- pc,
77
- Some(Self::backtrace_syminfo_trampoline::<F>),
78
- on_error,
79
- &mut on_ok as *mut _ as *mut c_void,
80
- );
81
- }
82
- }
83
-
84
- unsafe extern "C" fn backtrace_full_trampoline<F>(
85
- user_data: *mut c_void,
86
- pc: usize,
87
- filename: *const c_char,
88
- lineno: c_int,
89
- function: *const c_char,
90
- ) -> c_int
91
- where
92
- F: FnMut(usize, *const c_char, c_int, *const c_char) -> c_int,
93
- {
94
- let user_data = &mut *(user_data as *mut F);
95
- user_data(pc, filename, lineno, function)
96
- }
97
-
98
- unsafe extern "C" fn backtrace_simple_trampoline<F>(user_data: *mut c_void, pc: usize) -> c_int
99
- where
100
- F: FnMut(usize) -> c_int,
101
- {
102
- let user_data = &mut *(user_data as *mut F);
103
- user_data(pc)
104
- }
105
-
106
- unsafe extern "C" fn backtrace_syminfo_trampoline<F>(
107
- user_data: *mut c_void,
108
- pc: usize,
109
- symname: *const c_char,
110
- symval: usize,
111
- symsize: usize,
112
- ) where
113
- F: FnMut(usize, *const c_char, usize, usize),
114
- {
115
- let user_data = &mut *(user_data as *mut F);
116
- user_data(pc, symname, symval, symsize);
117
- }
118
-
119
- pub unsafe extern "C" fn backtrace_error_callback(
120
- _data: *mut c_void,
121
- msg: *const c_char,
122
- errnum: c_int,
123
- ) {
124
- let msg = unsafe { CStr::from_ptr(msg) };
125
- log::debug!("backtrace error: {:?} ({})", msg, errnum);
126
- }
127
- }
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;
@@ -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
- }
@@ -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
- }
@@ -1,6 +0,0 @@
1
- #include <ruby.h>
2
-
3
- VALUE rb_ull2num(unsigned long long n) {
4
- if (POSFIXABLE(n)) return LONG2FIX((long)n);
5
- return rb_ull2inum(n);
6
- }