pf2 0.14.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +0 -102
- data/README.md +54 -66
- data/Rakefile +0 -6
- data/doc/development.md +0 -6
- data/ext/pf2/configuration.c +0 -14
- data/ext/pf2/configuration.h +0 -3
- data/ext/pf2/extconf.rb +3 -37
- data/ext/pf2/pf2.c +8 -8
- data/ext/pf2/sample.c +3 -1
- data/ext/pf2/sample.h +4 -5
- data/ext/pf2/serializer.c +38 -121
- data/ext/pf2/serializer.h +4 -8
- data/ext/pf2/session.c +116 -299
- data/ext/pf2/session.h +5 -157
- data/lib/pf2/cli.rb +2 -1
- data/lib/pf2/reporter/firefox_profiler_ser2.rb +1 -1
- data/lib/pf2/reporter/stack_weaver.rb +1 -1
- data/lib/pf2/reporter.rb +0 -1
- data/lib/pf2/serve.rb +2 -1
- data/lib/pf2/session.rb +9 -0
- data/lib/pf2/version.rb +1 -1
- data/lib/pf2.rb +5 -62
- data/vendor/libbacktrace/atomic.c +1 -1
- data/vendor/libbacktrace/configure +4 -12
- data/vendor/libbacktrace/configure.ac +1 -6
- data/vendor/libbacktrace/elf.c +4 -4
- data/vendor/libbacktrace/fileline.c +1 -35
- data/vendor/libbacktrace/filetype.awk +0 -1
- metadata +3 -37
- data/.document +0 -3
- data/.rdoc_options +0 -9
- data/THIRD_PARTY_LICENSES.txt +0 -75
- data/ext/patches/libbacktrace/0001-Support-MACH_O_MH_BUNDLE.patch +0 -32
- data/ext/pf2/khashl.h +0 -506
- data/lib/pf2/reporter/pprof.rb +0 -349
- data/vendor/profile.proto +0 -233
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9f219b8aa5b5281a6ed662a085e00f82636ab52365d2b0f7a8d61a3e06bfc9c6
|
|
4
|
+
data.tar.gz: 162e1eae488afe17e33291f63f807642aa304d7df8069cb165e3a25ce6f10895
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37b6a1aa4f6ab0753d86983d76cbdfae4b3dfbb7464afb0388aa113d728725f09eabcabb2ae7c2ffadc5ab797be75675b5b27b465c20565d5d69f30291d4837f
|
|
7
|
+
data.tar.gz: 7b69aea55c8873cfd6e28fba3bdddd09325e4c73e1e3e44a5be48fcb9ad776b03e811c707fc049ad8ccc1590e7c011032be7f4053e3d15a5ba5b50c4344db656
|
data/CHANGELOG.md
CHANGED
|
@@ -1,104 +1,5 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
## [0.14.0] - 2026-01-25
|
|
5
|
-
|
|
6
|
-
### Added
|
|
7
|
-
|
|
8
|
-
- `Pf2.profile` can now directly write a Firefox Profiler-compatible profile into a file or an IO-ish object. Check out `Pf2.profile`'s `out:` and `format:` option.
|
|
9
|
-
- An _experimental_ pprof-compatible recorder has been added. Profiles generated by this reporter can be passed to tools such as `go tool pprof`, or services such as Grafana Pyroscope or Google Cloud's Cloud Profiler.
|
|
10
|
-
|
|
11
|
-
### Changed
|
|
12
|
-
|
|
13
|
-
- `Pf2::Reporter::FirefoxProfilerSer2` now emits a JSON string, not a Hash. Parse the JSON to obtain the original representaion.
|
|
14
|
-
|
|
15
|
-
### Fixed
|
|
16
|
-
|
|
17
|
-
- Fixed a crash when a delayed SIGPROF is received after `Pf2.stop` is called.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
## [0.13.0] - 2026-01-18
|
|
21
|
-
|
|
22
|
-
### Added
|
|
23
|
-
|
|
24
|
-
- Pf2 should now have a dramatically lower memory footprint.
|
|
25
|
-
- Samples are now stored in a compact hashmap internally.
|
|
26
|
-
- See https://github.com/osyoyu/pf2/pull/85 for details.
|
|
27
|
-
|
|
28
|
-
### Fixed
|
|
29
|
-
|
|
30
|
-
- `pf2 serve` command now properly works. (Thanks @hanazuki)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## [0.12.0] - 2026-01-09
|
|
34
|
-
|
|
35
|
-
### Added
|
|
36
|
-
|
|
37
|
-
- `Pf2.profile` now accepts the same options as `Pf2.start`.
|
|
38
|
-
- The resulting profile now has `collected_sample_count` and `dropped_sample_count` fields.
|
|
39
|
-
|
|
40
|
-
### Fixed
|
|
41
|
-
|
|
42
|
-
- Samples captured after the collector thread was stopped now get included in the profile.
|
|
43
|
-
- This shouldn't matter in practice (this all happens after `Pf2.stop` is called).
|
|
44
|
-
|
|
45
|
-
### Changed
|
|
46
|
-
|
|
47
|
-
- Accepted max stack depth is expanded to 1024 for Ruby (was 200) and 512 for native (was 300).
|
|
48
|
-
- This is not configurable, but should be sufficient for most use cases. Please open an issue if you need higher limits.
|
|
49
|
-
- Pf2.profile now accepts the same parameters as Pf2.start.
|
|
50
|
-
- Internal changes
|
|
51
|
-
- Updated libbacktrace to the latest version as of 2026/1/8.
|
|
52
|
-
- Tests are now much more stabilized.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
## [0.11.3] - 2025-12-28
|
|
56
|
-
|
|
57
|
-
This version is for testing the new release process through [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/). All code is identical to 0.11.2.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
## [0.11.2] - 2025-12-28
|
|
61
|
-
|
|
62
|
-
0.11.1 was yanked since it was accidentally published without libbacktrace vendored. Use 0.11.2.
|
|
63
|
-
|
|
64
|
-
### Fixed
|
|
65
|
-
|
|
66
|
-
- Fixed issues preventing builds on macOS.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
## [0.11.0] - 2025-12-27
|
|
70
|
-
|
|
71
|
-
### Added
|
|
72
|
-
|
|
73
|
-
- RDoc documentation is now online - https://osyoyu.github.io/pf2/
|
|
74
|
-
- Native stack consolidation now supports LTO-ed binaries (@hanazuki)
|
|
75
|
-
|
|
76
|
-
### Changed
|
|
77
|
-
|
|
78
|
-
- `Pf2c` module is now completely removed. `Pf2c::Session` has been merged as `Pf2::Session`.
|
|
79
|
-
|
|
80
|
-
### Fixed
|
|
81
|
-
|
|
82
|
-
- Fixed an bug where the program crashes when a `Pf2::Session` is GC'd before profiling starts.
|
|
83
|
-
- Fixed an bug where the program crashes when the native stack was more than 200 frames deep.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
## [0.10.0] - 2025-12-26
|
|
87
|
-
|
|
88
|
-
### Added
|
|
89
|
-
|
|
90
|
-
**This version contains a complete rewrite of the profiler!**
|
|
91
|
-
|
|
92
|
-
- The default sample collection backend has been switched to the new C-based backend.
|
|
93
|
-
- The previous Rust-based backed has been removed. Use v0.9.0 if you need it.
|
|
94
|
-
- macOS / non-Linux platform support!
|
|
95
|
-
- On platforms which lack `timer_create(3)` such as macOS, Pf2 now fall backs to `setitimer(3)` based sampling. This mode does not support per-thread CPU time sampling.
|
|
96
|
-
|
|
97
|
-
### Changed
|
|
98
|
-
|
|
99
|
-
- `logger` is now declared as a dependency (Ruby 4.0 compat).
|
|
100
|
-
|
|
101
|
-
|
|
102
3
|
## [0.9.0] - 2025-03-22
|
|
103
4
|
|
|
104
5
|
## Added
|
|
@@ -110,7 +11,6 @@ This version is for testing the new release process through [Trusted Publishing]
|
|
|
110
11
|
|
|
111
12
|
- Set SA_RESTART flag to reduce EINTRs in profiled code
|
|
112
13
|
|
|
113
|
-
|
|
114
14
|
## [0.8.0] - 2025-01-27
|
|
115
15
|
|
|
116
16
|
## Added
|
|
@@ -119,14 +19,12 @@ This version is for testing the new release process through [Trusted Publishing]
|
|
|
119
19
|
- This serializer is more efficient and has a smaller memory footprint than the default serializer.
|
|
120
20
|
- Ser2 still lacks some features, such as weaving of native stacks.
|
|
121
21
|
|
|
122
|
-
|
|
123
22
|
## [0.7.1] - 2025-01-02
|
|
124
23
|
|
|
125
24
|
### Fixed
|
|
126
25
|
|
|
127
26
|
- Reverted Cargo.lock version to 3 to support older versions of Rust (<1.78).
|
|
128
27
|
|
|
129
|
-
|
|
130
28
|
## [0.7.0] - 2025-01-03
|
|
131
29
|
|
|
132
30
|
### Changed
|
data/README.md
CHANGED
|
@@ -3,9 +3,6 @@ Pf2
|
|
|
3
3
|
|
|
4
4
|
A experimental sampling-based profiler for Ruby 3.3+.
|
|
5
5
|
|
|
6
|
-
- GitHub: https://github.com/osyoyu/pf2
|
|
7
|
-
- Documentation: https://osyoyu.github.io/pf2/
|
|
8
|
-
|
|
9
6
|
Notable Capabilites
|
|
10
7
|
--------
|
|
11
8
|
|
|
@@ -16,48 +13,7 @@ Notable Capabilites
|
|
|
16
13
|
Usage
|
|
17
14
|
--------
|
|
18
15
|
|
|
19
|
-
###
|
|
20
|
-
|
|
21
|
-
You will need a C compiler to build the native extension.
|
|
22
|
-
|
|
23
|
-
Add this line to your application's Gemfile:
|
|
24
|
-
|
|
25
|
-
```ruby
|
|
26
|
-
gem 'pf2'
|
|
27
|
-
|
|
28
|
-
# When using the main branch, specify submodules: true
|
|
29
|
-
gem 'pf2', git: 'https://github.com/osyoyu/pf2.git', submodules: true
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Pf2 can be installed as a standalone CLI tool as well.
|
|
33
|
-
|
|
34
|
-
```console
|
|
35
|
-
gem install pf2
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Profiling
|
|
39
|
-
|
|
40
|
-
Surround and run your code with `Pf2.profile { }`.
|
|
41
|
-
|
|
42
|
-
```ruby
|
|
43
|
-
profile = Pf2.profile(out: 'profile.json') do
|
|
44
|
-
your_code_here() # will be profiled
|
|
45
|
-
Thread.new { threaded_code() } # will also be profiled
|
|
46
|
-
end
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Then drop 'profile.json' into https://profiler.firefox.com/ for nice visualization.
|
|
50
|
-
|
|
51
|
-
Options for `Pf2.profile`:
|
|
52
|
-
|
|
53
|
-
| Option | Type | Description |
|
|
54
|
-
| ----------- | ----------------- | ---------------------------------------------- |
|
|
55
|
-
| interval_ms | Integer | Sampling interval in milliseconds (default: 9) |
|
|
56
|
-
| time_mode | `:cpu` or `:wall` | Sampling timer mode (default: `:cpu`) |
|
|
57
|
-
| out | String or IO-like | Location to write collected profile data |
|
|
58
|
-
| format | `:firefox` or `:pf2prof` | Profile format to be saved |
|
|
59
|
-
|
|
60
|
-
### Direct visualization using 'pf2 serve'
|
|
16
|
+
### Quickstart
|
|
61
17
|
|
|
62
18
|
Run your Ruby program through `pf2 serve`.
|
|
63
19
|
Wait a while until Pf2 collects profiles (or until the target program exits), then open the displayed link for visualization.
|
|
@@ -70,32 +26,39 @@ $ pf2 serve -- ruby target.rb
|
|
|
70
26
|
I'm the target program!
|
|
71
27
|
```
|
|
72
28
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
### More precise control
|
|
29
|
+
### Profiling
|
|
76
30
|
|
|
77
|
-
|
|
31
|
+
Pf2 will collect samples every 10 ms of wall time by default.
|
|
78
32
|
|
|
79
33
|
```ruby
|
|
80
|
-
|
|
34
|
+
# Threads in `threads` will be tracked
|
|
35
|
+
Pf2.start(threads: [Thread.current])
|
|
81
36
|
|
|
82
37
|
your_code_here
|
|
83
38
|
|
|
84
|
-
# Stop profiling
|
|
39
|
+
# Stop profiling and save the profile for visualization
|
|
85
40
|
profile = Pf2.stop
|
|
86
|
-
|
|
87
|
-
# Save file for later visualization using 'pf2 report'
|
|
88
|
-
File.binwrite("my_program.pf2prof", Marshal.dump(profile))
|
|
41
|
+
File.write("my_program.pf2profile", profile)
|
|
89
42
|
```
|
|
90
43
|
|
|
91
|
-
|
|
44
|
+
Alternatively, you may provide a code block to profile.
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
profile = Pf2.profile do
|
|
48
|
+
your_code_here() # will be profiled
|
|
49
|
+
Thread.new { threaded_code() } # will also be profiled
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Save the profile for visualization
|
|
53
|
+
File.write("my_program.pf2profile", profile)
|
|
54
|
+
```
|
|
92
55
|
|
|
93
56
|
### Reporting / Visualization
|
|
94
57
|
|
|
95
58
|
Profiles can be visualized using the [Firefox Profiler](https://profiler.firefox.com/).
|
|
96
59
|
|
|
97
60
|
```console
|
|
98
|
-
$ pf2 report -o report.json my_program.
|
|
61
|
+
$ pf2 report -o report.json my_program.pf2profile
|
|
99
62
|
```
|
|
100
63
|
|
|
101
64
|
Alternatively, `pf2 annotate` can be used to display hit counts side-by-side with source code.
|
|
@@ -104,10 +67,25 @@ Alternatively, `pf2 annotate` can be used to display hit counts side-by-side wit
|
|
|
104
67
|
$ pf2 annotate my_program.pf2prof
|
|
105
68
|
```
|
|
106
69
|
|
|
70
|
+
### Configuration
|
|
71
|
+
|
|
72
|
+
Pf2 accepts the following configuration keys:
|
|
73
|
+
|
|
74
|
+
```rb
|
|
75
|
+
Pf2.start(
|
|
76
|
+
interval_ms: 9, # Integer: The sampling interval in milliseconds (default: 9)
|
|
77
|
+
time_mode: :cpu, # `:cpu` or `:wall`: The sampling timer's mode
|
|
78
|
+
# (default: `:cpu` for SignalScheduler, `:wall` for TimerThreadScheduler)
|
|
79
|
+
threads: [th1, th2], # `Array<Thread>` | `:all`: A list of Ruby Threads to be tracked.
|
|
80
|
+
# When `:all` or unspecified, Pf2 will track all active Threads.
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
|
|
107
85
|
Overhead
|
|
108
86
|
--------
|
|
109
87
|
|
|
110
|
-
While Pf2 aims to be non-disturbulent as much as possible, a small overhead is
|
|
88
|
+
While Pf2 aims to be non-disturbulent as much as possible, a small overhead still is incured.
|
|
111
89
|
|
|
112
90
|
(TBD)
|
|
113
91
|
|
|
@@ -130,35 +108,45 @@ Pf2 is a _sampling profiler_. This means that Pf2 collects _samples_ of program
|
|
|
130
108
|
|
|
131
109
|
Pf2 uses the `rb_profile_thread_frames()` API for sampling. When to do so is controlled by _Schedulers_, described in the following section.
|
|
132
110
|
|
|
133
|
-
###
|
|
111
|
+
### Schedulers
|
|
134
112
|
|
|
135
|
-
Schedulers determine when to execute sample collection, based on configuration (time mode and interval).
|
|
113
|
+
Schedulers determine when to execute sample collection, based on configuration (time mode and interval). Pf2 has two schedulers available.
|
|
136
114
|
|
|
137
|
-
####
|
|
115
|
+
#### SignalScheduler (Linux-only)
|
|
138
116
|
|
|
139
|
-
|
|
117
|
+
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(2)`. This leaves the actual time-keeping to the OS, which is capable of tracking accurate per-thread CPU time usage.
|
|
140
118
|
|
|
141
|
-
When the specified interval has arrived (the timer has _expired_), the OS delivers us a SIGPROF signal.
|
|
119
|
+
When the specified interval has arrived (the timer has _expired_), the OS delivers us a SIGPROF signal. This is why the scheduler is named SignalScheduler.
|
|
142
120
|
|
|
143
121
|
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.
|
|
144
122
|
|
|
145
123
|
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`).
|
|
146
124
|
|
|
147
|
-
####
|
|
148
|
-
|
|
149
|
-
Note: Timer thread-based scheduling has been removed in v0.10.0, when the profiling backend has been rewritten in C. This may come back in the future if needed.
|
|
125
|
+
#### TimerThreadScheduler
|
|
150
126
|
|
|
151
127
|
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.
|
|
152
128
|
|
|
153
129
|
This scheduler is wall-time only, and does not support CPU-time based profiling.
|
|
154
130
|
|
|
131
|
+
#### macOS Support
|
|
132
|
+
|
|
133
|
+
On platforms where `timer_create()` is not supported (namely macOS), Pf2 falls back to `setitimer()`.
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
Wishlist
|
|
137
|
+
--------
|
|
138
|
+
|
|
139
|
+
- [Flame Scopes](https://www.brendangregg.com/flamescope.html)
|
|
140
|
+
- More unit/e2e tests
|
|
141
|
+
- more
|
|
142
|
+
|
|
155
143
|
Development
|
|
156
144
|
--------
|
|
157
145
|
|
|
158
146
|
See [doc/development.md](doc/development.md).
|
|
159
147
|
|
|
148
|
+
|
|
160
149
|
License
|
|
161
150
|
--------
|
|
162
151
|
|
|
163
152
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
164
|
-
See [LICENSE.txt](/LICENSE.txt) and [THIRD_PARTY_LICENSES.txt](/THIRD_PARTY_LICENSES.txt) for details.
|
data/Rakefile
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'bundler/gem_tasks'
|
|
2
2
|
require 'rake/extensiontask'
|
|
3
3
|
require 'minitest/test_task'
|
|
4
|
-
require 'rdoc/task'
|
|
5
4
|
|
|
6
5
|
task default: %i[]
|
|
7
6
|
|
|
@@ -15,9 +14,4 @@ Minitest::TestTask.create(:test) do |t|
|
|
|
15
14
|
t.libs << "lib"
|
|
16
15
|
t.warning = false
|
|
17
16
|
t.test_globs = ["test/**/*_test.rb"]
|
|
18
|
-
t.extra_args << "--verbose"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
RDoc::Task.new do |doc|
|
|
22
|
-
doc.rdoc_dir = "_site" # for GitHub pages
|
|
23
17
|
end
|
data/doc/development.md
CHANGED
|
@@ -15,9 +15,3 @@ Releasing
|
|
|
15
15
|
- Run `bundle install` to update Gemfile.lock
|
|
16
16
|
- Commit changes
|
|
17
17
|
- Run `bundle exec rake release`
|
|
18
|
-
|
|
19
|
-
Hidden options for testing
|
|
20
|
-
--------
|
|
21
|
-
|
|
22
|
-
- Pf2.start
|
|
23
|
-
- _test_no_install_timer: When true, no timer is installed. Manually send a SIGPROF to trigger sampling.
|
data/ext/pf2/configuration.c
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
#include <ruby.h>
|
|
2
2
|
#include <stdlib.h>
|
|
3
|
-
#include <stdbool.h>
|
|
4
3
|
|
|
5
4
|
#include "configuration.h"
|
|
6
5
|
|
|
7
6
|
static int extract_interval_ms(VALUE options_hash);
|
|
8
7
|
static enum pf2_time_mode extract_time_mode(VALUE options_hash);
|
|
9
|
-
static bool extract__test_no_install_timer(VALUE options_hash);
|
|
10
8
|
|
|
11
9
|
struct pf2_configuration *
|
|
12
10
|
pf2_configuration_new_from_options_hash(VALUE options_hash)
|
|
@@ -18,7 +16,6 @@ pf2_configuration_new_from_options_hash(VALUE options_hash)
|
|
|
18
16
|
|
|
19
17
|
config->interval_ms = extract_interval_ms(options_hash);
|
|
20
18
|
config->time_mode = extract_time_mode(options_hash);
|
|
21
|
-
config->_test_no_install_timer = extract__test_no_install_timer(options_hash);
|
|
22
19
|
|
|
23
20
|
return config;
|
|
24
21
|
}
|
|
@@ -60,17 +57,6 @@ extract_time_mode(VALUE options_hash)
|
|
|
60
57
|
}
|
|
61
58
|
}
|
|
62
59
|
|
|
63
|
-
static bool
|
|
64
|
-
extract__test_no_install_timer(VALUE options_hash)
|
|
65
|
-
{
|
|
66
|
-
if (options_hash == Qnil) {
|
|
67
|
-
return PF2_DEFAULT__TEST_NO_INSTALL_TIMER;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
VALUE _test_no_install_timer = rb_hash_aref(options_hash, ID2SYM(rb_intern("_test_no_install_timer")));
|
|
71
|
-
return RTEST(_test_no_install_timer);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
60
|
void
|
|
75
61
|
pf2_configuration_free(struct pf2_configuration *config)
|
|
76
62
|
{
|
data/ext/pf2/configuration.h
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
#define PF2_CONFIGURATION_H
|
|
3
3
|
|
|
4
4
|
#include <ruby.h>
|
|
5
|
-
#include <stdbool.h>
|
|
6
5
|
|
|
7
6
|
enum pf2_time_mode {
|
|
8
7
|
PF2_TIME_MODE_CPU_TIME,
|
|
@@ -12,12 +11,10 @@ enum pf2_time_mode {
|
|
|
12
11
|
struct pf2_configuration {
|
|
13
12
|
int interval_ms;
|
|
14
13
|
enum pf2_time_mode time_mode;
|
|
15
|
-
bool _test_no_install_timer; // for testing only
|
|
16
14
|
};
|
|
17
15
|
|
|
18
16
|
#define PF2_DEFAULT_INTERVAL_MS 9
|
|
19
17
|
#define PF2_DEFAULT_TIME_MODE PF2_TIME_MODE_CPU_TIME
|
|
20
|
-
#define PF2_DEFAULT__TEST_NO_INSTALL_TIMER false
|
|
21
18
|
|
|
22
19
|
struct pf2_configuration *pf2_configuration_new_from_options_hash(VALUE options_hash);
|
|
23
20
|
void pf2_configuration_free(struct pf2_configuration *config);
|
data/ext/pf2/extconf.rb
CHANGED
|
@@ -1,44 +1,10 @@
|
|
|
1
1
|
require 'mkmf'
|
|
2
2
|
require 'mini_portile2'
|
|
3
|
-
require 'fileutils'
|
|
4
|
-
require 'optparse'
|
|
5
|
-
|
|
6
|
-
gem_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
|
7
|
-
|
|
8
|
-
options = {
|
|
9
|
-
debug: ENV['PF2_DEBUG'] == '1',
|
|
10
|
-
}
|
|
11
|
-
option_parser = OptionParser.new do |opts|
|
|
12
|
-
opts.on('--debug[=BOOL]') do |debug|
|
|
13
|
-
options[:debug] =
|
|
14
|
-
case debug
|
|
15
|
-
when nil, "true"
|
|
16
|
-
true
|
|
17
|
-
when "false"
|
|
18
|
-
false
|
|
19
|
-
else
|
|
20
|
-
raise OptionParser::InvalidArgument, "Expected true or false for --debug"
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
option_parser.parse!(ARGV)
|
|
25
3
|
|
|
26
4
|
libbacktrace = MiniPortile.new('libbacktrace', '1.0.0')
|
|
27
|
-
libbacktrace.source_directory = File.join(
|
|
28
|
-
libbacktrace.patch_files = Dir.glob(File.join(gem_root, 'ext', 'patches', 'libbacktrace', '*.patch'))
|
|
5
|
+
libbacktrace.source_directory = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'vendor', 'libbacktrace'))
|
|
29
6
|
libbacktrace.configure_options << 'CFLAGS=-fPIC'
|
|
30
|
-
|
|
31
|
-
# Expand 'libbacktrace.cook' to call #patch on source_directory files
|
|
32
|
-
libbacktrace.prepare_build_directory
|
|
33
|
-
# Added: Copy source to build_directory
|
|
34
|
-
build_directory = libbacktrace.send(:work_path)
|
|
35
|
-
FileUtils.cp_r(File.join(libbacktrace.source_directory, '.'), build_directory)
|
|
36
|
-
libbacktrace.patch
|
|
37
|
-
libbacktrace.configure unless libbacktrace.configured?
|
|
38
|
-
libbacktrace.compile
|
|
39
|
-
libbacktrace.install unless libbacktrace.installed?
|
|
40
|
-
# END expand 'libbacktrace.cook'
|
|
41
|
-
|
|
7
|
+
libbacktrace.cook
|
|
42
8
|
libbacktrace.mkmf_config
|
|
43
9
|
|
|
44
10
|
if !have_func('backtrace_full', 'backtrace.h')
|
|
@@ -47,7 +13,7 @@ end
|
|
|
47
13
|
|
|
48
14
|
append_ldflags('-lrt') # for timer_create
|
|
49
15
|
append_cflags('-fvisibility=hidden')
|
|
50
|
-
append_cflags('-DPF2_DEBUG') if
|
|
16
|
+
append_cflags('-DPF2_DEBUG') if ENV['PF2_DEBUG'] == '1'
|
|
51
17
|
|
|
52
18
|
# Check for timer functions
|
|
53
19
|
have_timer_create = have_func('timer_create')
|
data/ext/pf2/pf2.c
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
#include "session.h"
|
|
4
4
|
|
|
5
|
-
VALUE
|
|
5
|
+
VALUE rb_mPf2c;
|
|
6
6
|
|
|
7
7
|
RUBY_FUNC_EXPORTED void
|
|
8
8
|
Init_pf2(void)
|
|
9
9
|
{
|
|
10
|
-
|
|
11
|
-
VALUE
|
|
12
|
-
rb_define_alloc_func(
|
|
13
|
-
rb_define_method(
|
|
14
|
-
rb_define_method(
|
|
15
|
-
rb_define_method(
|
|
16
|
-
rb_define_method(
|
|
10
|
+
rb_mPf2c = rb_define_module("Pf2c");
|
|
11
|
+
VALUE rb_mPf2c_cSession = rb_define_class_under(rb_mPf2c, "Session", rb_cObject);
|
|
12
|
+
rb_define_alloc_func(rb_mPf2c_cSession, pf2_session_alloc);
|
|
13
|
+
rb_define_method(rb_mPf2c_cSession, "initialize", rb_pf2_session_initialize, -1);
|
|
14
|
+
rb_define_method(rb_mPf2c_cSession, "start", rb_pf2_session_start, 0);
|
|
15
|
+
rb_define_method(rb_mPf2c_cSession, "stop", rb_pf2_session_stop, 0);
|
|
16
|
+
rb_define_method(rb_mPf2c_cSession, "configuration", rb_pf2_session_configuration, 0);
|
|
17
17
|
}
|
data/ext/pf2/sample.c
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
#include "backtrace_state.h"
|
|
10
10
|
#include "sample.h"
|
|
11
11
|
|
|
12
|
+
const int PF2_SAMPLE_MAX_NATIVE_DEPTH = 300;
|
|
13
|
+
|
|
12
14
|
static int capture_native_backtrace(struct pf2_sample *sample);
|
|
13
15
|
static int backtrace_on_ok(void *data, uintptr_t pc);
|
|
14
16
|
|
|
@@ -27,7 +29,7 @@ pf2_sample_capture(struct pf2_sample *sample)
|
|
|
27
29
|
sample->context_pthread = pthread_self();
|
|
28
30
|
|
|
29
31
|
// Obtain the current stack from Ruby
|
|
30
|
-
sample->depth = rb_profile_frames(0,
|
|
32
|
+
sample->depth = rb_profile_frames(0, 200, sample->cmes, sample->linenos);
|
|
31
33
|
|
|
32
34
|
// Capture C-level backtrace
|
|
33
35
|
sample->native_stack_depth = capture_native_backtrace(sample);
|
data/ext/pf2/sample.h
CHANGED
|
@@ -5,18 +5,17 @@
|
|
|
5
5
|
|
|
6
6
|
#include <ruby.h>
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
#define PF2_SAMPLE_MAX_NATIVE_DEPTH 512
|
|
8
|
+
extern const int PF2_SAMPLE_MAX_NATIVE_DEPTH;
|
|
10
9
|
|
|
11
10
|
struct pf2_sample {
|
|
12
11
|
pthread_t context_pthread;
|
|
13
12
|
|
|
14
13
|
int depth;
|
|
15
|
-
VALUE cmes[
|
|
16
|
-
int linenos[
|
|
14
|
+
VALUE cmes[200];
|
|
15
|
+
int linenos[200];
|
|
17
16
|
|
|
18
17
|
size_t native_stack_depth;
|
|
19
|
-
uintptr_t native_stack[
|
|
18
|
+
uintptr_t native_stack[200];
|
|
20
19
|
|
|
21
20
|
uint64_t consumed_time_ns;
|
|
22
21
|
uint64_t timestamp_ns;
|