pf2 0.1.0 → 0.3.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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -2
  3. data/Cargo.lock +650 -0
  4. data/Cargo.toml +3 -0
  5. data/README.md +110 -13
  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 +25 -0
  87. data/ext/pf2/build.rs +3 -0
  88. data/ext/pf2/extconf.rb +6 -1
  89. data/ext/pf2/src/backtrace.rs +126 -0
  90. data/ext/pf2/src/lib.rs +15 -0
  91. data/ext/pf2/src/profile.rs +65 -0
  92. data/ext/pf2/src/profile_serializer.rs +204 -0
  93. data/ext/pf2/src/ringbuffer.rs +152 -0
  94. data/ext/pf2/src/ruby_init.rs +74 -0
  95. data/ext/pf2/src/sample.rs +66 -0
  96. data/ext/pf2/src/siginfo_t.c +5 -0
  97. data/ext/pf2/src/signal_scheduler/configuration.rs +31 -0
  98. data/ext/pf2/src/signal_scheduler/timer_installer.rs +199 -0
  99. data/ext/pf2/src/signal_scheduler.rs +311 -0
  100. data/ext/pf2/src/timer_thread_scheduler.rs +319 -0
  101. data/ext/pf2/src/util.rs +30 -0
  102. data/lib/pf2/cli.rb +1 -1
  103. data/lib/pf2/reporter.rb +48 -16
  104. data/lib/pf2/version.rb +1 -1
  105. data/lib/pf2.rb +20 -5
  106. metadata +128 -5
  107. data/ext/pf2/pf2.c +0 -246
data/README.md CHANGED
@@ -1,26 +1,123 @@
1
- # Pf2
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
- ## Installation
6
+ Notable Capabilites
7
+ --------
7
8
 
8
- TBD
9
+ - Can accurately track multiple Ruby Threads' activity
10
+ - Sampling interval can be set based on per-Thread CPU usage
9
11
 
10
- ## Usage
12
+ Usage
13
+ --------
11
14
 
12
- TBD
15
+ ### Profiling
13
16
 
14
- ## Development
17
+ Pf2 will collect samples every 10 ms of wall time by default.
15
18
 
16
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
19
+ ```ruby
20
+ # Threads in `threads` will be tracked
21
+ Pf2.start(threads: [Thread.current])
17
22
 
18
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
23
+ your_code_here
19
24
 
20
- ## Contributing
25
+ # Stop profiling and save the profile for visualization
26
+ profile = Pf2.stop
27
+ File.write("my_program.pf2profile", profile)
28
+ ```
21
29
 
22
- Bug reports and pull requests are welcome on GitHub at https://github.com/osyoyu/pf2.
30
+ Alternatively, you may provide a code block to profile.
23
31
 
24
- ## License
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,9 @@
1
+ [package]
2
+ name = "backtrace-sys2"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
+
8
+ [build-dependencies]
9
+ bindgen = "0.69.2"
@@ -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
+ }
@@ -0,0 +1,5 @@
1
+ #![allow(non_upper_case_globals)]
2
+ #![allow(non_camel_case_types)]
3
+ #![allow(non_snake_case)]
4
+
5
+ include!(concat!(env!("OUT_DIR"), "/backtrace_bindings.rs"));
@@ -0,0 +1,15 @@
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