pf2 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -2
- data/Cargo.lock +650 -0
- data/Cargo.toml +3 -0
- data/README.md +110 -13
- 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 +25 -0
- data/ext/pf2/build.rs +3 -0
- data/ext/pf2/extconf.rb +6 -1
- data/ext/pf2/src/backtrace.rs +126 -0
- data/ext/pf2/src/lib.rs +15 -0
- data/ext/pf2/src/profile.rs +65 -0
- data/ext/pf2/src/profile_serializer.rs +204 -0
- data/ext/pf2/src/ringbuffer.rs +152 -0
- data/ext/pf2/src/ruby_init.rs +74 -0
- data/ext/pf2/src/sample.rs +66 -0
- data/ext/pf2/src/siginfo_t.c +5 -0
- data/ext/pf2/src/signal_scheduler/configuration.rs +31 -0
- data/ext/pf2/src/signal_scheduler/timer_installer.rs +199 -0
- data/ext/pf2/src/signal_scheduler.rs +311 -0
- data/ext/pf2/src/timer_thread_scheduler.rs +319 -0
- data/ext/pf2/src/util.rs +30 -0
- data/lib/pf2/cli.rb +1 -1
- data/lib/pf2/reporter.rb +48 -16
- data/lib/pf2/version.rb +1 -1
- data/lib/pf2.rb +20 -5
- metadata +128 -5
- data/ext/pf2/pf2.c +0 -246
data/README.md
CHANGED
@@ -1,26 +1,123 @@
|
|
1
|
-
|
1
|
+
Pf2
|
2
|
+
===========
|
2
3
|
|
3
|
-
A sampling-based profiler for Ruby.
|
4
|
-
Works only on a patched version of CRuby (MRI) at the moment.
|
4
|
+
A experimental sampling-based profiler for Ruby 3.3+.
|
5
5
|
|
6
|
-
|
6
|
+
Notable Capabilites
|
7
|
+
--------
|
7
8
|
|
8
|
-
|
9
|
+
- Can accurately track multiple Ruby Threads' activity
|
10
|
+
- Sampling interval can be set based on per-Thread CPU usage
|
9
11
|
|
10
|
-
|
12
|
+
Usage
|
13
|
+
--------
|
11
14
|
|
12
|
-
|
15
|
+
### Profiling
|
13
16
|
|
14
|
-
|
17
|
+
Pf2 will collect samples every 10 ms of wall time by default.
|
15
18
|
|
16
|
-
|
19
|
+
```ruby
|
20
|
+
# Threads in `threads` will be tracked
|
21
|
+
Pf2.start(threads: [Thread.current])
|
17
22
|
|
18
|
-
|
23
|
+
your_code_here
|
19
24
|
|
20
|
-
|
25
|
+
# Stop profiling and save the profile for visualization
|
26
|
+
profile = Pf2.stop
|
27
|
+
File.write("my_program.pf2profile", profile)
|
28
|
+
```
|
21
29
|
|
22
|
-
|
30
|
+
Alternatively, you may provide a code block to profile.
|
23
31
|
|
24
|
-
|
32
|
+
```ruby
|
33
|
+
profile = Pf2.profile do
|
34
|
+
your_code_here() # will be profiled
|
35
|
+
Thread.new { threaded_code() } # will also be profiled
|
36
|
+
end
|
37
|
+
|
38
|
+
# Save the profile for visualization
|
39
|
+
File.write("my_program.pf2profile", profile)
|
40
|
+
```
|
41
|
+
|
42
|
+
### Reporting / Visualization
|
43
|
+
|
44
|
+
Profiles can be visualized using the [Firefox Profiler](https://profiler.firefox.com/).
|
45
|
+
|
46
|
+
```console
|
47
|
+
$ pf2 -o report.json my_program.pf2profile
|
48
|
+
```
|
49
|
+
|
50
|
+
### Configuration
|
51
|
+
|
52
|
+
Pf2 accepts the following configuration keys:
|
53
|
+
|
54
|
+
```rb
|
55
|
+
Pf2.start(
|
56
|
+
interval_ms: 49, # Integer: The sampling interval in milliseconds (default: 49)
|
57
|
+
threads: [], # Array<Thread>: A list of Ruby Threads to be tracked (default: `Thread.list`)
|
58
|
+
time_mode: :cpu, # `:cpu` or `:wall`: The sampling timer's mode
|
59
|
+
# (default: `:cpu` for SignalScheduler, `:wall` for TimerThreadScheduler)
|
60
|
+
track_new_threads: true # Boolean: Whether to automatically track Threads spawned after profiler start
|
61
|
+
# (default: false)
|
62
|
+
)
|
63
|
+
```
|
64
|
+
|
65
|
+
|
66
|
+
Overhead
|
67
|
+
--------
|
68
|
+
|
69
|
+
While Pf2 aims to be non-disturbulent as much as possible, a small overhead still is incured.
|
70
|
+
|
71
|
+
(TBD)
|
72
|
+
|
73
|
+
Limitations
|
74
|
+
--------
|
75
|
+
|
76
|
+
Pf2 cannot properly track program activity in some known cases. I'm working to remove these limtations, so stay tuned.
|
77
|
+
|
78
|
+
- Program execution in forked processes
|
79
|
+
- Workarounds available for Puma
|
80
|
+
- Program execution in Fibers
|
81
|
+
- Program execution when MaNy (`RUBY_MN_THREADS`) is enabled
|
82
|
+
|
83
|
+
Internals
|
84
|
+
--------
|
85
|
+
|
86
|
+
### Sampling
|
87
|
+
|
88
|
+
Pf2 is a _sampling profiler_. This means that Pf2 collects _samples_ of program execution periodically, instead of tracing every action (e.g. method invocations and returns).
|
89
|
+
|
90
|
+
Pf2 uses the `rb_profile_thread_frames()` API for sampling. When to do so is controlled by _Schedulers_, described in the following section.
|
91
|
+
|
92
|
+
### Schedulers
|
93
|
+
|
94
|
+
Schedulers determine when to execute sample collection, based on configuration (time mode and interval). Pf2 has two schedulers available.
|
95
|
+
|
96
|
+
#### SignalScheduler (Linux-only)
|
97
|
+
|
98
|
+
The first is the `SignalScheduler`, based on POSIX timers. Pf2 will use this scheduler when possible. SignalScheduler creates a POSIX timer for each Ruby Thread (the underlying pthread to be more accurate) using `timer_create(3)`. This leaves the actual time-keeping to the OS, which is capable of tracking accurate per-thread CPU time usage.
|
99
|
+
|
100
|
+
When the specified interval has arrived (the timer has _expired_), the OS delivers us a SIGALRM (note: Unlike `setitimer(2)`, `timer_create(3)` allows us to choose which signal to be delivered, and Pf2 uses SIGALRM regardless of time mode). This is why the scheduler is named SignalScheduler.
|
101
|
+
|
102
|
+
Signals are directed to Ruby Threads' underlying pthread, effectively "pausing" the Thread's activity. This routing is done using `SIGEV_THREAD_ID`, which is a Linux-only feature. Sample collection is done in the signal handler, which is expected to be more _accurate_, capturing the paused Thread's activity.
|
103
|
+
|
104
|
+
This scheduler heavily relies on Ruby's 1:N Thread model (1 Ruby Threads is strongly tied to a native pthread). It will not work properly in MaNy (`RUBY_MN_THREADS=1`).
|
105
|
+
|
106
|
+
#### TimerThreadScheduler
|
107
|
+
|
108
|
+
Another scheduler is the `TimerThreadScheduler`, which maintains a time-keeping thread by itself. A new native thread (pthread on Linux/macOS) will be created, and an infinite loop will be run inside. After `sleep(2)`-ing for the specified interval time, sampling will be queued using Ruby's Postponed Job API.
|
109
|
+
|
110
|
+
This scheduler is wall-time only, and does not support CPU-time based profiling.
|
111
|
+
|
112
|
+
Future Plans
|
113
|
+
--------
|
114
|
+
|
115
|
+
- Remove known limitations, if possible
|
116
|
+
- Implement a "tracing" scheduler, using the C TracePoint API
|
117
|
+
- more
|
118
|
+
|
119
|
+
|
120
|
+
License
|
121
|
+
--------
|
25
122
|
|
26
123
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rake/extensiontask'
|
3
|
+
require 'minitest/test_task'
|
3
4
|
|
4
5
|
task default: %i[]
|
5
6
|
|
6
7
|
Rake::ExtensionTask.new 'pf2' do |ext|
|
7
8
|
ext.lib_dir = 'lib/pf2'
|
8
9
|
end
|
10
|
+
|
11
|
+
Minitest::TestTask.create(:test) do |t|
|
12
|
+
t.libs << "test"
|
13
|
+
t.libs << "lib"
|
14
|
+
t.warning = false
|
15
|
+
t.test_globs = ["test/**/*_test.rb"]
|
16
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
/target
|
@@ -0,0 +1,48 @@
|
|
1
|
+
use std::env;
|
2
|
+
use std::path::Path;
|
3
|
+
use std::path::PathBuf;
|
4
|
+
use std::process::Command;
|
5
|
+
|
6
|
+
fn main() {
|
7
|
+
let libbacktrace_src_dir = Path::new("src/libbacktrace").canonicalize().unwrap();
|
8
|
+
|
9
|
+
// Run ./configure
|
10
|
+
let configure_status = Command::new("./configure")
|
11
|
+
.current_dir(&libbacktrace_src_dir)
|
12
|
+
.status()
|
13
|
+
.expect("libbacktrace: ./configure failed");
|
14
|
+
if !configure_status.success() {
|
15
|
+
panic!("libbacktrace: ./configure failed");
|
16
|
+
}
|
17
|
+
|
18
|
+
// Run make
|
19
|
+
let make_status = Command::new("make")
|
20
|
+
.current_dir(&libbacktrace_src_dir)
|
21
|
+
.status()
|
22
|
+
.expect("libbacktrace: make failed");
|
23
|
+
if !make_status.success() {
|
24
|
+
panic!("libbacktrace: make failed");
|
25
|
+
}
|
26
|
+
|
27
|
+
// Generate bindings
|
28
|
+
let bindings = bindgen::Builder::default()
|
29
|
+
.header(format!("{}/backtrace.h", libbacktrace_src_dir.display()))
|
30
|
+
.allowlist_function("backtrace_.*")
|
31
|
+
.generate_comments(true)
|
32
|
+
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
33
|
+
.generate()
|
34
|
+
.expect("Failed to generate bindings");
|
35
|
+
|
36
|
+
// Output bindings to the src directory
|
37
|
+
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
38
|
+
bindings
|
39
|
+
.write_to_file(out_path.join("backtrace_bindings.rs"))
|
40
|
+
.expect("Failed to write bindings");
|
41
|
+
|
42
|
+
println!("cargo:rerun-if-changed=build.rs");
|
43
|
+
println!(
|
44
|
+
"cargo:rustc-link-search=native={}",
|
45
|
+
libbacktrace_src_dir.join(".libs").display()
|
46
|
+
);
|
47
|
+
println!("cargo:rustc-link-lib=static=backtrace");
|
48
|
+
}
|