pf2 0.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef67bdd8a362c1f5cd3dd37a47e13b64f5ee0663a204908d0f075d41acc94da3
4
- data.tar.gz: 535d1fe8efb9a408ce47218910d6ad3ddfe88c2a4e4f42ac42c01ab8dceceacd
3
+ metadata.gz: 9f219b8aa5b5281a6ed662a085e00f82636ab52365d2b0f7a8d61a3e06bfc9c6
4
+ data.tar.gz: 162e1eae488afe17e33291f63f807642aa304d7df8069cb165e3a25ce6f10895
5
5
  SHA512:
6
- metadata.gz: 15a65711f8c1a804b06d8b2195a9e620646972b7905a714fdab67561f9d796d7c92a1f332f9169ffa27c14b6b91d9f33ba254d84d47c281f66f7a069cc660d58
7
- data.tar.gz: 411bdf85ef4db94bed73840224132498621f389e4f6092a45e222bf5834dd5d1e6aa6940cafcb56cdc86148163d1b9bda4eee45501cc9c307ce729894954613d
6
+ metadata.gz: 37b6a1aa4f6ab0753d86983d76cbdfae4b3dfbb7464afb0388aa113d728725f09eabcabb2ae7c2ffadc5ab797be75675b5b27b465c20565d5d69f30291d4837f
7
+ data.tar.gz: 7b69aea55c8873cfd6e28fba3bdddd09325e4c73e1e3e44a5be48fcb9ad776b03e811c707fc049ad8ccc1590e7c011032be7f4053e3d15a5ba5b50c4344db656
data/CHANGELOG.md CHANGED
@@ -1,74 +1,5 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.12.0] - 2026-01-09
4
-
5
- ### Added
6
-
7
- - `Pf2.profile` now accepts the same options as `Pf2.start`.
8
- - The resulting profile now has `collected_sample_count` and `dropped_sample_count` fields.
9
-
10
- ### Fixed
11
-
12
- - Samples captured after the collector thread was stopped now get included in the profile.
13
- - This shouldn't matter in practice (this all happens after `Pf2.stop` is called).
14
-
15
- ### Changed
16
-
17
- - Accepted max stack depth is expanded to 1024 for Ruby (was 200) and 512 for native (was 300).
18
- - This is not configurable, but should be sufficient for most use cases. Please open an issue if you need higher limits.
19
- - Pf2.profile now accepts the same parameters as Pf2.start.
20
- - Internal changes
21
- - Updated libbacktrace to the latest version as of 2026/1/8.
22
- - Tests are now much more stabilized.
23
-
24
-
25
- ## [0.11.3] - 2025-12-28
26
-
27
- 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.
28
-
29
-
30
- ## [0.11.2] - 2025-12-28
31
-
32
- 0.11.1 was yanked since it was accidentally published without libbacktrace vendored. Use 0.11.2.
33
-
34
- ### Fixed
35
-
36
- - Fixed issues preventing builds on macOS.
37
-
38
-
39
- ## [0.11.0] - 2025-12-27
40
-
41
- ### Added
42
-
43
- - RDoc documentation is now online - https://osyoyu.github.io/pf2/
44
- - Native stack consolidation now supports LTO-ed binaries (@hanazuki)
45
-
46
- ### Changed
47
-
48
- - `Pf2c` module is now completely removed. `Pf2c::Session` has been merged as `Pf2::Session`.
49
-
50
- ### Fixed
51
-
52
- - Fixed an bug where the program crashes when a `Pf2::Session` is GC'd before profiling starts.
53
- - Fixed an bug where the program crashes when the native stack was more than 200 frames deep.
54
-
55
-
56
- ## [0.10.0] - 2025-12-26
57
-
58
- ### Added
59
-
60
- **This version contains a complete rewrite of the profiler!**
61
-
62
- - The default sample collection backend has been switched to the new C-based backend.
63
- - The previous Rust-based backed has been removed. Use v0.9.0 if you need it.
64
- - macOS / non-Linux platform support!
65
- - 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.
66
-
67
- ### Changed
68
-
69
- - `logger` is now declared as a dependency (Ruby 4.0 compat).
70
-
71
-
72
3
  ## [0.9.0] - 2025-03-22
73
4
 
74
5
  ## Added
@@ -80,7 +11,6 @@ This version is for testing the new release process through [Trusted Publishing]
80
11
 
81
12
  - Set SA_RESTART flag to reduce EINTRs in profiled code
82
13
 
83
-
84
14
  ## [0.8.0] - 2025-01-27
85
15
 
86
16
  ## Added
@@ -89,14 +19,12 @@ This version is for testing the new release process through [Trusted Publishing]
89
19
  - This serializer is more efficient and has a smaller memory footprint than the default serializer.
90
20
  - Ser2 still lacks some features, such as weaving of native stacks.
91
21
 
92
-
93
22
  ## [0.7.1] - 2025-01-02
94
23
 
95
24
  ### Fixed
96
25
 
97
26
  - Reverted Cargo.lock version to 3 to support older versions of Rust (<1.78).
98
27
 
99
-
100
28
  ## [0.7.0] - 2025-01-03
101
29
 
102
30
  ### Changed
data/README.md CHANGED
@@ -3,11 +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
- NOTE: This README contains some outdated information!
10
-
11
6
  Notable Capabilites
12
7
  --------
13
8
 
@@ -18,25 +13,6 @@ Notable Capabilites
18
13
  Usage
19
14
  --------
20
15
 
21
- ### Installation
22
-
23
- You will need a C compiler to build the native extension.
24
-
25
- Add this line to your application's Gemfile:
26
-
27
- ```ruby
28
- gem 'pf2'
29
-
30
- # When using the main branch, specify submodules: true
31
- gem 'pf2', git: 'https://github.com/osyoyu/pf2.git', submodules: true
32
- ```
33
-
34
- Pf2 can be installed as a standalone CLI tool as well.
35
-
36
- ```console
37
- gem install pf2
38
- ```
39
-
40
16
  ### Quickstart
41
17
 
42
18
  Run your Ruby program through `pf2 serve`.
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
@@ -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
  {
@@ -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,25 +1,10 @@
1
1
  require 'mkmf'
2
2
  require 'mini_portile2'
3
- require 'fileutils'
4
-
5
- gem_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
6
3
 
7
4
  libbacktrace = MiniPortile.new('libbacktrace', '1.0.0')
8
- libbacktrace.source_directory = File.join(gem_root, 'vendor', 'libbacktrace')
9
- 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'))
10
6
  libbacktrace.configure_options << 'CFLAGS=-fPIC'
11
-
12
- # Expand 'libbacktrace.cook' to call #patch on source_directory files
13
- libbacktrace.prepare_build_directory
14
- # Added: Copy source to build_directory
15
- build_directory = libbacktrace.send(:work_path)
16
- FileUtils.cp_r(File.join(libbacktrace.source_directory, '.'), build_directory)
17
- libbacktrace.patch
18
- libbacktrace.configure unless libbacktrace.configured?
19
- libbacktrace.compile
20
- libbacktrace.install unless libbacktrace.installed?
21
- # END expand 'libbacktrace.cook'
22
-
7
+ libbacktrace.cook
23
8
  libbacktrace.mkmf_config
24
9
 
25
10
  if !have_func('backtrace_full', 'backtrace.h')
data/ext/pf2/pf2.c CHANGED
@@ -2,16 +2,16 @@
2
2
 
3
3
  #include "session.h"
4
4
 
5
- VALUE rb_mPf2;
5
+ VALUE rb_mPf2c;
6
6
 
7
7
  RUBY_FUNC_EXPORTED void
8
8
  Init_pf2(void)
9
9
  {
10
- rb_mPf2 = rb_define_module("Pf2");
11
- VALUE rb_mPf2_cSession = rb_define_class_under(rb_mPf2, "Session", rb_cObject);
12
- rb_define_alloc_func(rb_mPf2_cSession, pf2_session_alloc);
13
- rb_define_method(rb_mPf2_cSession, "initialize", rb_pf2_session_initialize, -1);
14
- rb_define_method(rb_mPf2_cSession, "start", rb_pf2_session_start, 0);
15
- rb_define_method(rb_mPf2_cSession, "stop", rb_pf2_session_stop, 0);
16
- rb_define_method(rb_mPf2_cSession, "configuration", rb_pf2_session_configuration, 0);
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, PF2_SAMPLE_MAX_RUBY_DEPTH, sample->cmes, sample->linenos);
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
- #define PF2_SAMPLE_MAX_RUBY_DEPTH 1024
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[PF2_SAMPLE_MAX_RUBY_DEPTH];
16
- int linenos[PF2_SAMPLE_MAX_RUBY_DEPTH];
14
+ VALUE cmes[200];
15
+ int linenos[200];
17
16
 
18
17
  size_t native_stack_depth;
19
- uintptr_t native_stack[PF2_SAMPLE_MAX_NATIVE_DEPTH];
18
+ uintptr_t native_stack[200];
20
19
 
21
20
  uint64_t consumed_time_ns;
22
21
  uint64_t timestamp_ns;
data/ext/pf2/serializer.c CHANGED
@@ -1,7 +1,6 @@
1
1
  #include <time.h>
2
2
  #include <stdint.h>
3
3
  #include <string.h>
4
- #include <stdatomic.h>
5
4
 
6
5
  #include <ruby.h>
7
6
  #include <ruby/debug.h>
@@ -17,8 +16,8 @@ static struct pf2_ser_function extract_function_from_ruby_frame(VALUE frame);
17
16
  static struct pf2_ser_function extract_function_from_native_pc(uintptr_t pc);
18
17
  // static int backtrace_pcinfo_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function);
19
18
  static void pf2_backtrace_syminfo_callback(void *data, uintptr_t pc, const char *symname, uintptr_t symval, uintptr_t symsize);
20
- static size_t function_index_for(struct pf2_ser *serializer, struct pf2_ser_function *function);
21
- static size_t location_index_for(struct pf2_ser *serializer, size_t function_index, int32_t lineno);
19
+ static int function_index_for(struct pf2_ser *serializer, struct pf2_ser_function *function);
20
+ static int location_index_for(struct pf2_ser *serializer, int function_index, int32_t lineno);
22
21
  static void ensure_samples_capacity(struct pf2_ser *serializer);
23
22
  static void ensure_locations_capacity(struct pf2_ser *serializer);
24
23
  static void ensure_functions_capacity(struct pf2_ser *serializer);
@@ -30,9 +29,6 @@ pf2_ser_new(void) {
30
29
  ser->start_timestamp_ns = 0;
31
30
  ser->duration_ns = 0;
32
31
 
33
- ser->collected_sample_count = 0;
34
- ser->dropped_sample_count = 0;
35
-
36
32
  ser->samples = NULL;
37
33
  ser->samples_count = 0;
38
34
  ser->samples_capacity = 0;
@@ -80,10 +76,6 @@ pf2_ser_prepare(struct pf2_ser *serializer, struct pf2_session *session) {
80
76
  (uint64_t)session->start_time_realtime.tv_sec * 1000000000ULL +
81
77
  (uint64_t)session->start_time_realtime.tv_nsec;
82
78
  serializer->duration_ns = session->duration_ns;
83
- serializer->collected_sample_count =
84
- atomic_load_explicit(&session->collected_sample_count, memory_order_relaxed);
85
- serializer->dropped_sample_count =
86
- atomic_load_explicit(&session->dropped_sample_count, memory_order_relaxed);
87
79
 
88
80
  // Process samples
89
81
  for (size_t i = 0; i < session->samples_index; i++) {
@@ -91,7 +83,7 @@ pf2_ser_prepare(struct pf2_ser *serializer, struct pf2_session *session) {
91
83
  ensure_samples_capacity(serializer);
92
84
 
93
85
  struct pf2_ser_sample *ser_sample = &serializer->samples[serializer->samples_count++];
94
- ser_sample->ruby_thread_id = (uintptr_t)sample->context_pthread;
86
+ ser_sample->ruby_thread_id = sample->context_pthread;
95
87
  ser_sample->elapsed_ns = sample->timestamp_ns - serializer->start_timestamp_ns;
96
88
 
97
89
  // Copy and process Ruby stack frames
@@ -135,8 +127,6 @@ pf2_ser_to_ruby_hash(struct pf2_ser *serializer) {
135
127
  // Add metadata
136
128
  rb_hash_aset(hash, ID2SYM(rb_intern("start_timestamp_ns")), ULL2NUM(serializer->start_timestamp_ns));
137
129
  rb_hash_aset(hash, ID2SYM(rb_intern("duration_ns")), ULL2NUM(serializer->duration_ns));
138
- rb_hash_aset(hash, ID2SYM(rb_intern("collected_sample_count")), ULL2NUM(serializer->collected_sample_count));
139
- rb_hash_aset(hash, ID2SYM(rb_intern("dropped_sample_count")), ULL2NUM(serializer->dropped_sample_count));
140
130
 
141
131
  // Add samples
142
132
  VALUE samples = rb_ary_new_capa(serializer->samples_count);
@@ -155,7 +145,7 @@ pf2_ser_to_ruby_hash(struct pf2_ser *serializer) {
155
145
  VALUE native_stack = rb_ary_new_capa(sample->native_stack_count);
156
146
  if (sample->native_stack != NULL) {
157
147
  for (size_t j = 0; j < sample->native_stack_count; j++) {
158
- rb_ary_push(native_stack, SIZET2NUM(sample->native_stack[j]));
148
+ rb_ary_push(native_stack, ULL2NUM(sample->native_stack[j]));
159
149
  }
160
150
  }
161
151
  rb_hash_aset(sample_hash, ID2SYM(rb_intern("native_stack")), native_stack);
@@ -164,7 +154,7 @@ pf2_ser_to_ruby_hash(struct pf2_ser *serializer) {
164
154
  rb_hash_aset(
165
155
  sample_hash,
166
156
  ID2SYM(rb_intern("ruby_thread_id")),
167
- sample->ruby_thread_id ? ULL2NUM(sample->ruby_thread_id) : Qnil
157
+ sample->ruby_thread_id ? SIZET2NUM(sample->ruby_thread_id) : Qnil
168
158
  );
169
159
  rb_hash_aset(sample_hash, ID2SYM(rb_intern("elapsed_ns")), ULL2NUM(sample->elapsed_ns));
170
160
 
@@ -314,7 +304,7 @@ pf2_backtrace_syminfo_callback(void *data, uintptr_t pc, const char *symname, ui
314
304
 
315
305
  // Returns the index of the function in `functions`.
316
306
  // Calling this method will modify `serializer->profile` in place.
317
- static size_t
307
+ static int
318
308
  function_index_for(struct pf2_ser *serializer, struct pf2_ser_function *function) {
319
309
  for (size_t i = 0; i < serializer->functions_count; i++) {
320
310
  struct pf2_ser_function *existing = &serializer->functions[i];
@@ -342,8 +332,8 @@ function_index_for(struct pf2_ser *serializer, struct pf2_ser_function *function
342
332
 
343
333
  // Returns the index of the location in `locations`.
344
334
  // Calling this method will modify `self.profile` in place.
345
- static size_t
346
- location_index_for(struct pf2_ser *serializer, size_t function_index, int32_t lineno) {
335
+ static int
336
+ location_index_for(struct pf2_ser *serializer, int function_index, int32_t lineno) {
347
337
  for (size_t i = 0; i < serializer->locations_count; i++) {
348
338
  struct pf2_ser_location *existing = &serializer->locations[i];
349
339
  if (existing->function_index == function_index && existing->lineno == lineno) {
data/ext/pf2/serializer.h CHANGED
@@ -1,23 +1,21 @@
1
1
  #ifndef PF2C_SERIALIZER_H
2
2
  #define PF2C_SERIALIZER_H
3
3
 
4
- #include <stdint.h>
5
-
6
4
  #include <ruby.h>
7
5
 
8
6
  #include "session.h"
9
7
 
10
8
  struct pf2_ser_sample {
11
- size_t *stack; // array of location_indexes
9
+ int *stack;
12
10
  size_t stack_count;
13
- size_t *native_stack; // array of location_indexes
11
+ int *native_stack;
14
12
  size_t native_stack_count;
15
- uintptr_t ruby_thread_id;
13
+ size_t ruby_thread_id;
16
14
  uint64_t elapsed_ns;
17
15
  };
18
16
 
19
17
  struct pf2_ser_location {
20
- size_t function_index;
18
+ int function_index;
21
19
  int32_t lineno;
22
20
  size_t address;
23
21
  };
@@ -38,8 +36,6 @@ struct pf2_ser_function {
38
36
  struct pf2_ser {
39
37
  uint64_t start_timestamp_ns;
40
38
  uint64_t duration_ns;
41
- uint64_t collected_sample_count;
42
- uint64_t dropped_sample_count;
43
39
 
44
40
  struct pf2_ser_sample *samples;
45
41
  size_t samples_count;
data/ext/pf2/session.c CHANGED
@@ -1,3 +1,4 @@
1
+ #include <bits/time.h>
1
2
  #include <pthread.h>
2
3
  #include <signal.h>
3
4
  #include <stdatomic.h>
@@ -19,14 +20,14 @@
19
20
  #include "session.h"
20
21
  #include "serializer.h"
21
22
 
22
- // Pointer to current active session, for access from signal handlers
23
+ #ifndef HAVE_TIMER_CREATE
24
+ // Global session pointer for setitimer fallback
23
25
  static struct pf2_session *global_current_session = NULL;
26
+ #endif
24
27
 
25
28
  static void *sample_collector_thread(void *arg);
26
- static void drain_ringbuffer(struct pf2_session *session);
27
29
  static void sigprof_handler(int sig, siginfo_t *info, void *ucontext);
28
30
  bool ensure_sample_capacity(struct pf2_session *session);
29
- static void pf2_session_stop(struct pf2_session *session);
30
31
 
31
32
  VALUE
32
33
  rb_pf2_session_initialize(int argc, VALUE *argv, VALUE self)
@@ -39,11 +40,10 @@ rb_pf2_session_initialize(int argc, VALUE *argv, VALUE self)
39
40
  rb_scan_args(argc, argv, ":", &kwargs);
40
41
  ID kwarg_labels[] = {
41
42
  rb_intern("interval_ms"),
42
- rb_intern("time_mode"),
43
- rb_intern("_test_no_install_timer")
43
+ rb_intern("time_mode")
44
44
  };
45
45
  VALUE *kwarg_values = NULL;
46
- rb_get_kwargs(kwargs, kwarg_labels, 0, 3, kwarg_values);
46
+ rb_get_kwargs(kwargs, kwarg_labels, 0, 2, kwarg_values);
47
47
 
48
48
  session->configuration = pf2_configuration_new_from_options_hash(kwargs);
49
49
 
@@ -56,9 +56,6 @@ rb_pf2_session_start(VALUE self)
56
56
  struct pf2_session *session;
57
57
  TypedData_Get_Struct(self, struct pf2_session, &pf2_session_type, session);
58
58
 
59
- // Store pointer to current session for access from signal handlers
60
- global_current_session = session;
61
-
62
59
  session->is_running = true;
63
60
 
64
61
  // Record start time
@@ -90,60 +87,58 @@ rb_pf2_session_start(VALUE self)
90
87
  }
91
88
  #endif
92
89
 
93
- global_current_session = session;
94
-
95
- if (!session->configuration->_test_no_install_timer) {
96
90
  #ifdef HAVE_TIMER_CREATE
97
- // Configure a kernel timer to send SIGPROF periodically
98
- struct sigevent sev;
99
- sev.sigev_notify = SIGEV_SIGNAL;
100
- sev.sigev_signo = SIGPROF;
101
- if (timer_create(
102
- session->configuration->time_mode == PF2_TIME_MODE_CPU_TIME
103
- ? CLOCK_PROCESS_CPUTIME_ID
104
- : CLOCK_MONOTONIC,
105
- &sev,
106
- &session->timer
107
- ) == -1) {
108
- rb_raise(rb_eRuntimeError, "Failed to create timer");
109
- }
110
- struct itimerspec its = {
111
- .it_value = {
112
- .tv_sec = 0,
113
- .tv_nsec = session->configuration->interval_ms * 1000000,
114
- },
115
- .it_interval = {
116
- .tv_sec = 0,
117
- .tv_nsec = session->configuration->interval_ms * 1000000,
118
- },
119
- };
120
- if (timer_settime(session->timer, 0, &its, NULL) == -1) {
121
- rb_raise(rb_eRuntimeError, "Failed to start timer");
122
- }
91
+ // Configure a kernel timer to send SIGPROF periodically
92
+ struct sigevent sev;
93
+ sev.sigev_notify = SIGEV_SIGNAL;
94
+ sev.sigev_signo = SIGPROF;
95
+ sev.sigev_value.sival_ptr = session; // Passed as info->si_value.sival_ptr
96
+ if (timer_create(
97
+ session->configuration->time_mode == PF2_TIME_MODE_CPU_TIME
98
+ ? CLOCK_PROCESS_CPUTIME_ID
99
+ : CLOCK_MONOTONIC,
100
+ &sev,
101
+ &session->timer
102
+ ) == -1) {
103
+ rb_raise(rb_eRuntimeError, "Failed to create timer");
104
+ }
105
+ struct itimerspec its = {
106
+ .it_value = {
107
+ .tv_sec = 0,
108
+ .tv_nsec = session->configuration->interval_ms * 1000000,
109
+ },
110
+ .it_interval = {
111
+ .tv_sec = 0,
112
+ .tv_nsec = session->configuration->interval_ms * 1000000,
113
+ },
114
+ };
115
+ if (timer_settime(session->timer, 0, &its, NULL) == -1) {
116
+ rb_raise(rb_eRuntimeError, "Failed to start timer");
117
+ }
123
118
  #else
124
- // Use setitimer as fallback
125
- // Some platforms (e.g. macOS) do not have timer_create(3).
126
- // setitimer(3) can be used as a alternative, but has limited functionality.
127
-
128
- struct itimerval itv = {
129
- .it_value = {
130
- .tv_sec = 0,
131
- .tv_usec = session->configuration->interval_ms * 1000,
132
- },
133
- .it_interval = {
134
- .tv_sec = 0,
135
- .tv_usec = session->configuration->interval_ms * 1000,
136
- },
137
- };
138
- int which_timer = session->configuration->time_mode == PF2_TIME_MODE_CPU_TIME
139
- ? ITIMER_PROF // CPU time (sends SIGPROF)
140
- : ITIMER_REAL; // Wall time (sends SIGALRM)
141
-
142
- if (setitimer(which_timer, &itv, NULL) == -1) {
143
- rb_raise(rb_eRuntimeError, "Failed to start timer");
144
- }
119
+ // Use setitimer as fallback
120
+ // Some platforms (e.g. macOS) do not have timer_create(3).
121
+ // setitimer(3) can be used as a alternative, but has limited functionality.
122
+ global_current_session = session;
123
+
124
+ struct itimerval itv = {
125
+ .it_value = {
126
+ .tv_sec = 0,
127
+ .tv_usec = session->configuration->interval_ms * 1000,
128
+ },
129
+ .it_interval = {
130
+ .tv_sec = 0,
131
+ .tv_usec = session->configuration->interval_ms * 1000,
132
+ },
133
+ };
134
+ int which_timer = session->configuration->time_mode == PF2_TIME_MODE_CPU_TIME
135
+ ? ITIMER_PROF // CPU time (sends SIGPROF)
136
+ : ITIMER_REAL; // Wall time (sends SIGALRM)
137
+
138
+ if (setitimer(which_timer, &itv, NULL) == -1) {
139
+ rb_raise(rb_eRuntimeError, "Failed to start timer");
140
+ }
145
141
  #endif
146
- } // if !__test_no_install_timer
147
142
 
148
143
  return Qtrue;
149
144
  }
@@ -155,7 +150,17 @@ sample_collector_thread(void *arg)
155
150
 
156
151
  while (session->is_running == true) {
157
152
  // Take samples from the ring buffer
158
- drain_ringbuffer(session);
153
+ struct pf2_sample sample;
154
+ while (pf2_ringbuffer_pop(session->rbuf, &sample) == true) {
155
+ // Ensure we have capacity before adding a new sample
156
+ if (!ensure_sample_capacity(session)) {
157
+ // Failed to expand buffer
158
+ PF2_DEBUG_LOG("Failed to expand sample buffer. Dropping sample\n");
159
+ break;
160
+ }
161
+
162
+ session->samples[session->samples_index++] = sample;
163
+ }
159
164
 
160
165
  // Sleep for 100 ms
161
166
  // TODO: Replace with high watermark callback
@@ -166,24 +171,6 @@ sample_collector_thread(void *arg)
166
171
  return NULL;
167
172
  }
168
173
 
169
- static void
170
- drain_ringbuffer(struct pf2_session *session)
171
- {
172
- struct pf2_sample sample;
173
- while (pf2_ringbuffer_pop(session->rbuf, &sample) == true) {
174
- // Ensure we have capacity before adding a new sample
175
- if (!ensure_sample_capacity(session)) {
176
- // Failed to expand buffer
177
- atomic_fetch_add_explicit(&session->dropped_sample_count, 1, memory_order_relaxed);
178
- PF2_DEBUG_LOG("Failed to expand sample buffer. Dropping sample\n");
179
- break;
180
- }
181
-
182
- session->samples[session->samples_index++] = sample;
183
- atomic_fetch_add_explicit(&session->collected_sample_count, 1, memory_order_relaxed);
184
- }
185
- }
186
-
187
174
  // async-signal-safe
188
175
  static void
189
176
  sigprof_handler(int sig, siginfo_t *info, void *ucontext)
@@ -193,12 +180,16 @@ sigprof_handler(int sig, siginfo_t *info, void *ucontext)
193
180
  clock_gettime(CLOCK_MONOTONIC, &sig_start_time);
194
181
  #endif
195
182
 
196
- struct pf2_session *session = global_current_session;
183
+ struct pf2_session *session;
184
+ #ifdef HAVE_TIMER_CREATE
185
+ session = info->si_value.sival_ptr;
186
+ #else
187
+ session = global_current_session;
188
+ #endif
197
189
 
198
190
  // If garbage collection is in progress, don't collect samples.
199
191
  if (atomic_load_explicit(&session->is_marking, memory_order_acquire)) {
200
192
  PF2_DEBUG_LOG("Dropping sample: Garbage collection is in progress\n");
201
- atomic_fetch_add_explicit(&session->dropped_sample_count, 1, memory_order_relaxed);
202
193
  return;
203
194
  }
204
195
 
@@ -206,7 +197,6 @@ sigprof_handler(int sig, siginfo_t *info, void *ucontext)
206
197
 
207
198
  if (pf2_sample_capture(&sample) == false) {
208
199
  PF2_DEBUG_LOG("Dropping sample: Failed to capture sample\n");
209
- atomic_fetch_add_explicit(&session->dropped_sample_count, 1, memory_order_relaxed);
210
200
  return;
211
201
  }
212
202
 
@@ -214,7 +204,6 @@ sigprof_handler(int sig, siginfo_t *info, void *ucontext)
214
204
  if (pf2_ringbuffer_push(session->rbuf, &sample) == false) {
215
205
  // Copy failed. The sample buffer is full.
216
206
  PF2_DEBUG_LOG("Dropping sample: Sample buffer is full\n");
217
- atomic_fetch_add_explicit(&session->dropped_sample_count, 1, memory_order_relaxed);
218
207
  return;
219
208
  }
220
209
 
@@ -262,20 +251,6 @@ rb_pf2_session_stop(VALUE self)
262
251
  struct pf2_session *session;
263
252
  TypedData_Get_Struct(self, struct pf2_session, &pf2_session_type, session);
264
253
 
265
- pf2_session_stop(session);
266
-
267
- // Create serializer and serialize
268
- struct pf2_ser *serializer = pf2_ser_new();
269
- pf2_ser_prepare(serializer, session);
270
- VALUE result = pf2_ser_to_ruby_hash(serializer);
271
- pf2_ser_free(serializer);
272
-
273
- return result;
274
- }
275
-
276
- static void
277
- pf2_session_stop(struct pf2_session *session)
278
- {
279
254
  // Calculate duration
280
255
  struct timespec end_time;
281
256
  clock_gettime(CLOCK_MONOTONIC, &end_time);
@@ -285,10 +260,8 @@ pf2_session_stop(struct pf2_session *session)
285
260
 
286
261
  // Disarm and delete the timer.
287
262
  #ifdef HAVE_TIMER_CREATE
288
- if (!session->configuration->_test_no_install_timer) {
289
- if (timer_delete(session->timer) == -1) {
290
- rb_raise(rb_eRuntimeError, "Failed to delete timer");
291
- }
263
+ if (timer_delete(session->timer) == -1) {
264
+ rb_raise(rb_eRuntimeError, "Failed to delete timer");
292
265
  }
293
266
  #else
294
267
  struct itimerval zero_timer = {{0, 0}, {0, 0}};
@@ -304,7 +277,14 @@ pf2_session_stop(struct pf2_session *session)
304
277
  // Terminate the collector thread
305
278
  session->is_running = false;
306
279
  pthread_join(*session->collector_thread, NULL);
307
- drain_ringbuffer(session);
280
+
281
+ // Create serializer and serialize
282
+ struct pf2_ser *serializer = pf2_ser_new();
283
+ pf2_ser_prepare(serializer, session);
284
+ VALUE result = pf2_ser_to_ruby_hash(serializer);
285
+ pf2_ser_free(serializer);
286
+
287
+ return result;
308
288
  }
309
289
 
310
290
  VALUE
@@ -320,7 +300,7 @@ pf2_session_alloc(VALUE self)
320
300
  {
321
301
  // Initialize state for libbacktrace
322
302
  if (global_backtrace_state == NULL) {
323
- global_backtrace_state = backtrace_create_state(NULL, 1, pf2_backtrace_print_error, NULL);
303
+ global_backtrace_state = backtrace_create_state("pf2", 1, pf2_backtrace_print_error, NULL);
324
304
  if (global_backtrace_state == NULL) {
325
305
  rb_raise(rb_eRuntimeError, "Failed to initialize libbacktrace");
326
306
  }
@@ -331,32 +311,19 @@ pf2_session_alloc(VALUE self)
331
311
  rb_raise(rb_eNoMemError, "Failed to allocate memory");
332
312
  }
333
313
 
334
- // is_running
335
- session->is_running = false;
336
-
337
- // timer
338
- #ifdef HAVE_TIMER_CREATE
339
- session->timer = (timer_t)0;
340
- #else
341
- session->timer = (struct itimerval){0};
342
- #endif
343
-
344
- // rbuf
345
314
  session->rbuf = pf2_ringbuffer_new(1000);
346
315
  if (session->rbuf == NULL) {
347
316
  rb_raise(rb_eNoMemError, "Failed to allocate memory");
348
317
  }
349
318
 
350
- // is_marking
351
319
  atomic_store_explicit(&session->is_marking, false, memory_order_relaxed);
352
-
353
- // collector_thread
354
320
  session->collector_thread = malloc(sizeof(pthread_t));
355
321
  if (session->collector_thread == NULL) {
356
322
  rb_raise(rb_eNoMemError, "Failed to allocate memory");
357
323
  }
358
324
 
359
- // samples, samples_index, samples_capacity
325
+ session->duration_ns = 0;
326
+
360
327
  session->samples_index = 0;
361
328
  session->samples_capacity = 500; // 10 seconds worth of samples at 50 Hz
362
329
  session->samples = malloc(sizeof(struct pf2_sample) * session->samples_capacity);
@@ -364,18 +331,6 @@ pf2_session_alloc(VALUE self)
364
331
  rb_raise(rb_eNoMemError, "Failed to allocate memory");
365
332
  }
366
333
 
367
- // collected_sample_count, dropped_sample_count
368
- atomic_store_explicit(&session->collected_sample_count, 0, memory_order_relaxed);
369
- atomic_store_explicit(&session->dropped_sample_count, 0, memory_order_relaxed);
370
-
371
- // start_time_realtime, start_time
372
- session->start_time_realtime = (struct timespec){0};
373
- session->start_time = (struct timespec){0};
374
-
375
- // duration_ns
376
- session->duration_ns = 0;
377
-
378
- // configuration
379
334
  session->configuration = NULL;
380
335
 
381
336
  return TypedData_Wrap_Struct(self, &pf2_session_type, session);
@@ -418,15 +373,8 @@ pf2_session_dmark(void *sess)
418
373
  void
419
374
  pf2_session_dfree(void *sess)
420
375
  {
376
+ // TODO: Ensure the uninstall process is complete before freeing the session
421
377
  struct pf2_session *session = sess;
422
-
423
- assert(session->is_running == false || session->is_running == true);
424
-
425
- // Stop the session if it's still running
426
- if (session->is_running) {
427
- pf2_session_stop(session);
428
- }
429
-
430
378
  pf2_configuration_free(session->configuration);
431
379
  pf2_ringbuffer_free(session->rbuf);
432
380
  free(session->samples);
data/ext/pf2/session.h CHANGED
@@ -30,9 +30,6 @@ struct pf2_session {
30
30
  struct timespec start_time; // When profiling started
31
31
  uint64_t duration_ns; // Duration of profiling in nanoseconds
32
32
 
33
- atomic_uint_fast64_t collected_sample_count; // Number of samples copied out of the ringbuffer
34
- atomic_uint_fast64_t dropped_sample_count; // Number of samples dropped for any reason
35
-
36
33
  struct pf2_configuration *configuration;
37
34
  };
38
35
 
@@ -46,14 +43,14 @@ void pf2_session_dfree(void *sess);
46
43
  size_t pf2_session_dsize(const void *sess);
47
44
 
48
45
  static const rb_data_type_t pf2_session_type = {
49
- .wrap_struct_name = "Pf2::Session",
46
+ .wrap_struct_name = "Pf2c::Session",
50
47
  .function = {
51
48
  .dmark = pf2_session_dmark,
52
49
  .dfree = pf2_session_dfree,
53
50
  .dsize = pf2_session_dsize,
54
51
  },
55
52
  .data = NULL,
56
- .flags = 0,
53
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
57
54
  };
58
55
 
59
56
  #endif // PF2_SESSION_H
data/lib/pf2/cli.rb CHANGED
@@ -26,7 +26,7 @@ module Pf2
26
26
  when 'version'
27
27
  puts VERSION
28
28
  return 0
29
- when nil, '--help'
29
+ when '--help'
30
30
  STDERR.puts <<~__EOS__
31
31
  Usage: #{program_name} COMMAND [options]
32
32
 
@@ -82,7 +82,7 @@ module Pf2
82
82
 
83
83
  # If the next function is a vm_exec_core() (= VM_EXEC in vm_exec.h),
84
84
  # we switch to the Ruby stack.
85
- function[:name]&.match?(/\Avm_exec_core(?:\.lto_priv\.\d+)?\z/)
85
+ function[:name] == 'vm_exec_core'
86
86
  end
87
87
  end
88
88
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pf2
4
+ class Session
5
+ attr_reader :configuration
6
+
7
+ # Implementation is in Rust code.
8
+ end
9
+ end
data/lib/pf2/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pf2
4
- VERSION = '0.12.0'
4
+ VERSION = '1.0.0.alpha1'
5
5
  end
data/lib/pf2.rb CHANGED
@@ -1,13 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'pf2/pf2'
4
+ require_relative 'pf2/session'
4
5
  require_relative 'pf2/version'
5
6
 
6
7
  module Pf2
7
8
  class Error < StandardError; end
8
9
 
9
10
  def self.start(...)
10
- @@session = Session.new(...)
11
+ @@session = Pf2c::Session.new(...)
11
12
  @@session.start
12
13
  end
13
14
 
@@ -15,17 +16,9 @@ module Pf2
15
16
  @@session.stop
16
17
  end
17
18
 
18
- # Profiles the given block of code.
19
- #
20
- # Example:
21
- #
22
- # profile = Pf2.profile(interval_ms: 42) do
23
- # your_code_here
24
- # end
25
- #
26
- def self.profile(**kwargs, &block)
19
+ def self.profile(&block)
27
20
  raise ArgumentError, "block required" unless block_given?
28
- start(**kwargs)
21
+ start(threads: Thread.list)
29
22
  yield
30
23
  result = stop
31
24
  @@session = nil # let GC clean up the session
@@ -103,7 +103,7 @@ backtrace_atomic_store_size_t (size_t *p, size_t v)
103
103
  void
104
104
  backtrace_atomic_store_int (int *p, int v)
105
105
  {
106
- int old;
106
+ size_t old;
107
107
 
108
108
  old = *p;
109
109
  while (!__sync_bool_compare_and_swap (p, old, v))
@@ -812,7 +812,6 @@ enable_darwin_at_rpath
812
812
  enable_largefile
813
813
  enable_werror
814
814
  with_system_libunwind
815
- enable_host_pie
816
815
  enable_host_shared
817
816
  '
818
817
  ac_precious_vars='build_alias
@@ -1462,7 +1461,6 @@ Optional Features:
1462
1461
  rpaths to be added to executables
1463
1462
  --disable-largefile omit support for large files
1464
1463
  --disable-werror disable building with -Werror
1465
- --enable-host-pie build host code as PIE
1466
1464
  --enable-host-shared build host code as shared libraries
1467
1465
 
1468
1466
  Optional Packages:
@@ -11397,7 +11395,7 @@ else
11397
11395
  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
11398
11396
  lt_status=$lt_dlunknown
11399
11397
  cat > conftest.$ac_ext <<_LT_EOF
11400
- #line 11400 "configure"
11398
+ #line 11398 "configure"
11401
11399
  #include "confdefs.h"
11402
11400
 
11403
11401
  #if HAVE_DLFCN_H
@@ -11503,7 +11501,7 @@ else
11503
11501
  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
11504
11502
  lt_status=$lt_dlunknown
11505
11503
  cat > conftest.$ac_ext <<_LT_EOF
11506
- #line 11506 "configure"
11504
+ #line 11504 "configure"
11507
11505
  #include "confdefs.h"
11508
11506
 
11509
11507
  #if HAVE_DLFCN_H
@@ -12192,18 +12190,12 @@ $as_echo "#define HAVE_GETIPINFO 1" >>confdefs.h
12192
12190
  fi
12193
12191
  fi
12194
12192
 
12195
- # Enable --enable-host-pie.
12196
- # Check whether --enable-host-pie was given.
12197
- if test "${enable_host_pie+set}" = set; then :
12198
- enableval=$enable_host_pie; PIC_FLAG=-fPIE
12199
- else
12200
- PIC_FLAG=
12201
- fi
12202
-
12203
12193
  # Enable --enable-host-shared.
12204
12194
  # Check whether --enable-host-shared was given.
12205
12195
  if test "${enable_host_shared+set}" = set; then :
12206
12196
  enableval=$enable_host_shared; PIC_FLAG=-fPIC
12197
+ else
12198
+ PIC_FLAG=
12207
12199
  fi
12208
12200
 
12209
12201
 
@@ -176,16 +176,11 @@ else
176
176
  fi
177
177
  fi
178
178
 
179
- # Enable --enable-host-pie.
180
- AC_ARG_ENABLE(host-pie,
181
- [AS_HELP_STRING([--enable-host-pie],
182
- [build host code as PIE])],
183
- [PIC_FLAG=-fPIE], [PIC_FLAG=])
184
179
  # Enable --enable-host-shared.
185
180
  AC_ARG_ENABLE(host-shared,
186
181
  [AS_HELP_STRING([--enable-host-shared],
187
182
  [build host code as shared libraries])],
188
- [PIC_FLAG=-fPIC])
183
+ [PIC_FLAG=-fPIC], [PIC_FLAG=])
189
184
  AC_SUBST(PIC_FLAG)
190
185
 
191
186
  # Test for __sync support.
@@ -160,10 +160,10 @@ dl_iterate_phdr (int (*callback) (struct dl_phdr_info *,
160
160
  #undef EI_CLASS
161
161
  #undef EI_DATA
162
162
  #undef EI_VERSION
163
- #undef ELFMAG0
164
- #undef ELFMAG1
165
- #undef ELFMAG2
166
- #undef ELFMAG3
163
+ #undef ELF_MAG0
164
+ #undef ELF_MAG1
165
+ #undef ELF_MAG2
166
+ #undef ELF_MAG3
167
167
  #undef ELFCLASS32
168
168
  #undef ELFCLASS64
169
169
  #undef ELFDATA2LSB
@@ -47,10 +47,6 @@ POSSIBILITY OF SUCH DAMAGE. */
47
47
  #include <mach-o/dyld.h>
48
48
  #endif
49
49
 
50
- #ifdef __hpux__
51
- #include <dl.h>
52
- #endif
53
-
54
50
  #ifdef HAVE_WINDOWS_H
55
51
  #ifndef WIN32_LEAN_AND_MEAN
56
52
  #define WIN32_LEAN_AND_MEAN
@@ -70,33 +66,6 @@ POSSIBILITY OF SUCH DAMAGE. */
70
66
  #define getexecname() NULL
71
67
  #endif
72
68
 
73
- #ifdef __hpux__
74
- static char *
75
- hpux_get_executable_path (struct backtrace_state *state,
76
- backtrace_error_callback error_callback, void *data)
77
- {
78
- struct shl_descriptor *desc;
79
- size_t len = sizeof (struct shl_descriptor);
80
-
81
- desc = backtrace_alloc (state, len, error_callback, data);
82
- if (desc == NULL)
83
- return NULL;
84
-
85
- if (shl_get_r (0, desc) == -1)
86
- {
87
- backtrace_free (state, desc, len, error_callback, data);
88
- return NULL;
89
- }
90
-
91
- return desc->filename;
92
- }
93
-
94
- #else
95
-
96
- #define hpux_get_executable_path(state, error_callback, data) NULL
97
-
98
- #endif
99
-
100
69
  #if !defined (HAVE_KERN_PROC_ARGS) && !defined (HAVE_KERN_PROC)
101
70
 
102
71
  #define sysctl_exec_name1(state, error_callback, data) NULL
@@ -276,7 +245,7 @@ fileline_initialize (struct backtrace_state *state,
276
245
 
277
246
  descriptor = -1;
278
247
  called_error_callback = 0;
279
- for (pass = 0; pass < 11; ++pass)
248
+ for (pass = 0; pass < 10; ++pass)
280
249
  {
281
250
  int does_not_exist;
282
251
 
@@ -316,9 +285,6 @@ fileline_initialize (struct backtrace_state *state,
316
285
  case 9:
317
286
  filename = windows_get_executable_path (buf, error_callback, data);
318
287
  break;
319
- case 10:
320
- filename = hpux_get_executable_path (state, error_callback, data);
321
- break;
322
288
  default:
323
289
  abort ();
324
290
  }
@@ -3,7 +3,6 @@
3
3
  /^\177ELF\002/ { if (NR == 1) { print "elf64"; exit } }
4
4
  /^\114\001/ { if (NR == 1) { print "pecoff"; exit } }
5
5
  /^\144\206/ { if (NR == 1) { print "pecoff"; exit } }
6
- /^\000\000\377\377/ { if (NR == 1) { print "pecoff"; exit } }
7
6
  /^\001\337/ { if (NR == 1) { print "xcoff32"; exit } }
8
7
  /^\001\367/ { if (NR == 1) { print "xcoff64"; exit } }
9
8
  /^\376\355\372\316/ { if (NR == 1) { print "macho"; exit } }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pf2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 1.0.0.alpha1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daisuke Aritomo
@@ -9,20 +9,6 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: logger
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - ">="
17
- - !ruby/object:Gem::Version
18
- version: '0'
19
- type: :runtime
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - ">="
24
- - !ruby/object:Gem::Version
25
- version: '0'
26
12
  - !ruby/object:Gem::Dependency
27
13
  name: rake-compiler
28
14
  requirement: !ruby/object:Gem::Requirement
@@ -93,20 +79,6 @@ dependencies:
93
79
  - - ">="
94
80
  - !ruby/object:Gem::Version
95
81
  version: '0'
96
- - !ruby/object:Gem::Dependency
97
- name: rdoc
98
- requirement: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - ">="
101
- - !ruby/object:Gem::Version
102
- version: '0'
103
- type: :development
104
- prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - ">="
108
- - !ruby/object:Gem::Version
109
- version: '0'
110
82
  email:
111
83
  - osyoyu@osyoyu.com
112
84
  executables:
@@ -115,8 +87,6 @@ extensions:
115
87
  - ext/pf2/extconf.rb
116
88
  extra_rdoc_files: []
117
89
  files:
118
- - ".document"
119
- - ".rdoc_options"
120
90
  - CHANGELOG.md
121
91
  - LICENSE.txt
122
92
  - README.md
@@ -125,7 +95,6 @@ files:
125
95
  - examples/mandelbrot.rb
126
96
  - examples/mandelbrot_ractor.rb
127
97
  - exe/pf2
128
- - ext/patches/libbacktrace/0001-Support-MACH_O_MH_BUNDLE.patch
129
98
  - ext/pf2/backtrace_state.c
130
99
  - ext/pf2/backtrace_state.h
131
100
  - ext/pf2/configuration.c
@@ -149,6 +118,7 @@ files:
149
118
  - lib/pf2/reporter/firefox_profiler_ser2.rb
150
119
  - lib/pf2/reporter/stack_weaver.rb
151
120
  - lib/pf2/serve.rb
121
+ - lib/pf2/session.rb
152
122
  - lib/pf2/version.rb
153
123
  - vendor/libbacktrace/.gitignore
154
124
  - vendor/libbacktrace/Isaac.Newton-Opticks.txt
@@ -242,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
242
212
  - !ruby/object:Gem::Version
243
213
  version: '0'
244
214
  requirements: []
245
- rubygems_version: 4.0.3
215
+ rubygems_version: 3.8.0.dev
246
216
  specification_version: 4
247
217
  summary: Yet another Ruby profiler
248
218
  test_files: []
data/.document DELETED
@@ -1,3 +0,0 @@
1
- ext/
2
- lib/
3
- *.md
data/.rdoc_options DELETED
@@ -1,6 +0,0 @@
1
- title: Pf2
2
- main_page: README.md
3
- encoding: UTF-8
4
-
5
- autolink_excluded_words:
6
- - Pf2
@@ -1,32 +0,0 @@
1
- From 4cd047583bc48ad0617fb6c036174de062573e68 Mon Sep 17 00:00:00 2001
2
- From: Daisuke Aritomo <osyoyu@osyoyu.com>
3
- Date: Wed, 7 Jan 2026 02:50:54 +0900
4
- Subject: [PATCH] Support MACH_O_MH_BUNDLE
5
-
6
- ---
7
- macho.c | 2 ++
8
- 1 file changed, 2 insertions(+)
9
-
10
- diff --git a/macho.c b/macho.c
11
- index 9f8738d..5ea07ae 100644
12
- --- a/macho.c
13
- +++ b/macho.c
14
- @@ -92,6 +92,7 @@ struct macho_header_fat
15
-
16
- #define MACH_O_MH_EXECUTE 0x02
17
- #define MACH_O_MH_DYLIB 0x06
18
- +#define MACH_O_MH_BUNDLE 0x08
19
- #define MACH_O_MH_DSYM 0x0a
20
-
21
- /* A component of a fat file. A fat file starts with a
22
- @@ -1062,6 +1063,7 @@ macho_add (struct backtrace_state *state, const char *filename, int descriptor,
23
- {
24
- case MACH_O_MH_EXECUTE:
25
- case MACH_O_MH_DYLIB:
26
- + case MACH_O_MH_BUNDLE:
27
- case MACH_O_MH_DSYM:
28
- break;
29
- default:
30
- --
31
- 2.39.5 (Apple Git-154)
32
-