pf2 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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