pf2 0.11.2 → 0.12.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -3
- data/README.md +2 -0
- data/Rakefile +1 -0
- data/ext/patches/libbacktrace/0001-Support-MACH_O_MH_BUNDLE.patch +32 -0
- data/ext/pf2/configuration.c +14 -0
- data/ext/pf2/configuration.h +3 -0
- data/ext/pf2/extconf.rb +17 -2
- data/ext/pf2/sample.h +2 -2
- data/ext/pf2/serializer.c +10 -0
- data/ext/pf2/serializer.h +2 -0
- data/ext/pf2/session.c +92 -74
- data/ext/pf2/session.h +3 -0
- data/lib/pf2/cli.rb +1 -1
- data/lib/pf2/version.rb +1 -1
- data/lib/pf2.rb +10 -2
- data/vendor/libbacktrace/atomic.c +1 -1
- data/vendor/libbacktrace/configure +12 -4
- data/vendor/libbacktrace/configure.ac +6 -1
- data/vendor/libbacktrace/elf.c +4 -4
- data/vendor/libbacktrace/fileline.c +35 -1
- data/vendor/libbacktrace/filetype.awk +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ef67bdd8a362c1f5cd3dd37a47e13b64f5ee0663a204908d0f075d41acc94da3
|
|
4
|
+
data.tar.gz: 535d1fe8efb9a408ce47218910d6ad3ddfe88c2a4e4f42ac42c01ab8dceceacd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 15a65711f8c1a804b06d8b2195a9e620646972b7905a714fdab67561f9d796d7c92a1f332f9169ffa27c14b6b91d9f33ba254d84d47c281f66f7a069cc660d58
|
|
7
|
+
data.tar.gz: 411bdf85ef4db94bed73840224132498621f389e4f6092a45e222bf5834dd5d1e6aa6940cafcb56cdc86148163d1b9bda4eee45501cc9c307ce729894954613d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
-
## [0.
|
|
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.
|
|
4
9
|
|
|
5
|
-
|
|
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
|
|
6
31
|
|
|
7
|
-
|
|
32
|
+
0.11.1 was yanked since it was accidentally published without libbacktrace vendored. Use 0.11.2.
|
|
8
33
|
|
|
9
34
|
### Fixed
|
|
10
35
|
|
data/README.md
CHANGED
data/Rakefile
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
|
data/ext/pf2/configuration.c
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#include <ruby.h>
|
|
2
2
|
#include <stdlib.h>
|
|
3
|
+
#include <stdbool.h>
|
|
3
4
|
|
|
4
5
|
#include "configuration.h"
|
|
5
6
|
|
|
6
7
|
static int extract_interval_ms(VALUE options_hash);
|
|
7
8
|
static enum pf2_time_mode extract_time_mode(VALUE options_hash);
|
|
9
|
+
static bool extract__test_no_install_timer(VALUE options_hash);
|
|
8
10
|
|
|
9
11
|
struct pf2_configuration *
|
|
10
12
|
pf2_configuration_new_from_options_hash(VALUE options_hash)
|
|
@@ -16,6 +18,7 @@ pf2_configuration_new_from_options_hash(VALUE options_hash)
|
|
|
16
18
|
|
|
17
19
|
config->interval_ms = extract_interval_ms(options_hash);
|
|
18
20
|
config->time_mode = extract_time_mode(options_hash);
|
|
21
|
+
config->_test_no_install_timer = extract__test_no_install_timer(options_hash);
|
|
19
22
|
|
|
20
23
|
return config;
|
|
21
24
|
}
|
|
@@ -57,6 +60,17 @@ extract_time_mode(VALUE options_hash)
|
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
|
|
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
|
+
|
|
60
74
|
void
|
|
61
75
|
pf2_configuration_free(struct pf2_configuration *config)
|
|
62
76
|
{
|
data/ext/pf2/configuration.h
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#define PF2_CONFIGURATION_H
|
|
3
3
|
|
|
4
4
|
#include <ruby.h>
|
|
5
|
+
#include <stdbool.h>
|
|
5
6
|
|
|
6
7
|
enum pf2_time_mode {
|
|
7
8
|
PF2_TIME_MODE_CPU_TIME,
|
|
@@ -11,10 +12,12 @@ enum pf2_time_mode {
|
|
|
11
12
|
struct pf2_configuration {
|
|
12
13
|
int interval_ms;
|
|
13
14
|
enum pf2_time_mode time_mode;
|
|
15
|
+
bool _test_no_install_timer; // for testing only
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
#define PF2_DEFAULT_INTERVAL_MS 9
|
|
17
19
|
#define PF2_DEFAULT_TIME_MODE PF2_TIME_MODE_CPU_TIME
|
|
20
|
+
#define PF2_DEFAULT__TEST_NO_INSTALL_TIMER false
|
|
18
21
|
|
|
19
22
|
struct pf2_configuration *pf2_configuration_new_from_options_hash(VALUE options_hash);
|
|
20
23
|
void pf2_configuration_free(struct pf2_configuration *config);
|
data/ext/pf2/extconf.rb
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
require 'mkmf'
|
|
2
2
|
require 'mini_portile2'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
gem_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
|
3
6
|
|
|
4
7
|
libbacktrace = MiniPortile.new('libbacktrace', '1.0.0')
|
|
5
|
-
libbacktrace.source_directory = File.
|
|
8
|
+
libbacktrace.source_directory = File.join(gem_root, 'vendor', 'libbacktrace')
|
|
9
|
+
libbacktrace.patch_files = Dir.glob(File.join(gem_root, 'ext', 'patches', 'libbacktrace', '*.patch'))
|
|
6
10
|
libbacktrace.configure_options << 'CFLAGS=-fPIC'
|
|
7
|
-
|
|
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
|
+
|
|
8
23
|
libbacktrace.mkmf_config
|
|
9
24
|
|
|
10
25
|
if !have_func('backtrace_full', 'backtrace.h')
|
data/ext/pf2/sample.h
CHANGED
data/ext/pf2/serializer.c
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include <time.h>
|
|
2
2
|
#include <stdint.h>
|
|
3
3
|
#include <string.h>
|
|
4
|
+
#include <stdatomic.h>
|
|
4
5
|
|
|
5
6
|
#include <ruby.h>
|
|
6
7
|
#include <ruby/debug.h>
|
|
@@ -29,6 +30,9 @@ pf2_ser_new(void) {
|
|
|
29
30
|
ser->start_timestamp_ns = 0;
|
|
30
31
|
ser->duration_ns = 0;
|
|
31
32
|
|
|
33
|
+
ser->collected_sample_count = 0;
|
|
34
|
+
ser->dropped_sample_count = 0;
|
|
35
|
+
|
|
32
36
|
ser->samples = NULL;
|
|
33
37
|
ser->samples_count = 0;
|
|
34
38
|
ser->samples_capacity = 0;
|
|
@@ -76,6 +80,10 @@ pf2_ser_prepare(struct pf2_ser *serializer, struct pf2_session *session) {
|
|
|
76
80
|
(uint64_t)session->start_time_realtime.tv_sec * 1000000000ULL +
|
|
77
81
|
(uint64_t)session->start_time_realtime.tv_nsec;
|
|
78
82
|
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);
|
|
79
87
|
|
|
80
88
|
// Process samples
|
|
81
89
|
for (size_t i = 0; i < session->samples_index; i++) {
|
|
@@ -127,6 +135,8 @@ pf2_ser_to_ruby_hash(struct pf2_ser *serializer) {
|
|
|
127
135
|
// Add metadata
|
|
128
136
|
rb_hash_aset(hash, ID2SYM(rb_intern("start_timestamp_ns")), ULL2NUM(serializer->start_timestamp_ns));
|
|
129
137
|
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));
|
|
130
140
|
|
|
131
141
|
// Add samples
|
|
132
142
|
VALUE samples = rb_ary_new_capa(serializer->samples_count);
|
data/ext/pf2/serializer.h
CHANGED
data/ext/pf2/session.c
CHANGED
|
@@ -19,12 +19,11 @@
|
|
|
19
19
|
#include "session.h"
|
|
20
20
|
#include "serializer.h"
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
// Global session pointer for setitimer fallback
|
|
22
|
+
// Pointer to current active session, for access from signal handlers
|
|
24
23
|
static struct pf2_session *global_current_session = NULL;
|
|
25
|
-
#endif
|
|
26
24
|
|
|
27
25
|
static void *sample_collector_thread(void *arg);
|
|
26
|
+
static void drain_ringbuffer(struct pf2_session *session);
|
|
28
27
|
static void sigprof_handler(int sig, siginfo_t *info, void *ucontext);
|
|
29
28
|
bool ensure_sample_capacity(struct pf2_session *session);
|
|
30
29
|
static void pf2_session_stop(struct pf2_session *session);
|
|
@@ -40,10 +39,11 @@ rb_pf2_session_initialize(int argc, VALUE *argv, VALUE self)
|
|
|
40
39
|
rb_scan_args(argc, argv, ":", &kwargs);
|
|
41
40
|
ID kwarg_labels[] = {
|
|
42
41
|
rb_intern("interval_ms"),
|
|
43
|
-
rb_intern("time_mode")
|
|
42
|
+
rb_intern("time_mode"),
|
|
43
|
+
rb_intern("_test_no_install_timer")
|
|
44
44
|
};
|
|
45
45
|
VALUE *kwarg_values = NULL;
|
|
46
|
-
rb_get_kwargs(kwargs, kwarg_labels, 0,
|
|
46
|
+
rb_get_kwargs(kwargs, kwarg_labels, 0, 3, kwarg_values);
|
|
47
47
|
|
|
48
48
|
session->configuration = pf2_configuration_new_from_options_hash(kwargs);
|
|
49
49
|
|
|
@@ -56,6 +56,9 @@ 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
|
+
|
|
59
62
|
session->is_running = true;
|
|
60
63
|
|
|
61
64
|
// Record start time
|
|
@@ -87,58 +90,60 @@ rb_pf2_session_start(VALUE self)
|
|
|
87
90
|
}
|
|
88
91
|
#endif
|
|
89
92
|
|
|
90
|
-
#ifdef HAVE_TIMER_CREATE
|
|
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
|
-
}
|
|
118
|
-
#else
|
|
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
93
|
global_current_session = session;
|
|
123
94
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
95
|
+
if (!session->configuration->_test_no_install_timer) {
|
|
96
|
+
#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
|
+
}
|
|
123
|
+
#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
|
+
}
|
|
141
145
|
#endif
|
|
146
|
+
} // if !__test_no_install_timer
|
|
142
147
|
|
|
143
148
|
return Qtrue;
|
|
144
149
|
}
|
|
@@ -150,17 +155,7 @@ sample_collector_thread(void *arg)
|
|
|
150
155
|
|
|
151
156
|
while (session->is_running == true) {
|
|
152
157
|
// Take samples from the ring buffer
|
|
153
|
-
|
|
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
|
-
}
|
|
158
|
+
drain_ringbuffer(session);
|
|
164
159
|
|
|
165
160
|
// Sleep for 100 ms
|
|
166
161
|
// TODO: Replace with high watermark callback
|
|
@@ -171,6 +166,24 @@ sample_collector_thread(void *arg)
|
|
|
171
166
|
return NULL;
|
|
172
167
|
}
|
|
173
168
|
|
|
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
|
+
|
|
174
187
|
// async-signal-safe
|
|
175
188
|
static void
|
|
176
189
|
sigprof_handler(int sig, siginfo_t *info, void *ucontext)
|
|
@@ -180,16 +193,12 @@ sigprof_handler(int sig, siginfo_t *info, void *ucontext)
|
|
|
180
193
|
clock_gettime(CLOCK_MONOTONIC, &sig_start_time);
|
|
181
194
|
#endif
|
|
182
195
|
|
|
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
|
|
196
|
+
struct pf2_session *session = global_current_session;
|
|
189
197
|
|
|
190
198
|
// If garbage collection is in progress, don't collect samples.
|
|
191
199
|
if (atomic_load_explicit(&session->is_marking, memory_order_acquire)) {
|
|
192
200
|
PF2_DEBUG_LOG("Dropping sample: Garbage collection is in progress\n");
|
|
201
|
+
atomic_fetch_add_explicit(&session->dropped_sample_count, 1, memory_order_relaxed);
|
|
193
202
|
return;
|
|
194
203
|
}
|
|
195
204
|
|
|
@@ -197,6 +206,7 @@ sigprof_handler(int sig, siginfo_t *info, void *ucontext)
|
|
|
197
206
|
|
|
198
207
|
if (pf2_sample_capture(&sample) == false) {
|
|
199
208
|
PF2_DEBUG_LOG("Dropping sample: Failed to capture sample\n");
|
|
209
|
+
atomic_fetch_add_explicit(&session->dropped_sample_count, 1, memory_order_relaxed);
|
|
200
210
|
return;
|
|
201
211
|
}
|
|
202
212
|
|
|
@@ -204,6 +214,7 @@ sigprof_handler(int sig, siginfo_t *info, void *ucontext)
|
|
|
204
214
|
if (pf2_ringbuffer_push(session->rbuf, &sample) == false) {
|
|
205
215
|
// Copy failed. The sample buffer is full.
|
|
206
216
|
PF2_DEBUG_LOG("Dropping sample: Sample buffer is full\n");
|
|
217
|
+
atomic_fetch_add_explicit(&session->dropped_sample_count, 1, memory_order_relaxed);
|
|
207
218
|
return;
|
|
208
219
|
}
|
|
209
220
|
|
|
@@ -274,8 +285,10 @@ pf2_session_stop(struct pf2_session *session)
|
|
|
274
285
|
|
|
275
286
|
// Disarm and delete the timer.
|
|
276
287
|
#ifdef HAVE_TIMER_CREATE
|
|
277
|
-
if (
|
|
278
|
-
|
|
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
|
+
}
|
|
279
292
|
}
|
|
280
293
|
#else
|
|
281
294
|
struct itimerval zero_timer = {{0, 0}, {0, 0}};
|
|
@@ -291,6 +304,7 @@ pf2_session_stop(struct pf2_session *session)
|
|
|
291
304
|
// Terminate the collector thread
|
|
292
305
|
session->is_running = false;
|
|
293
306
|
pthread_join(*session->collector_thread, NULL);
|
|
307
|
+
drain_ringbuffer(session);
|
|
294
308
|
}
|
|
295
309
|
|
|
296
310
|
VALUE
|
|
@@ -306,7 +320,7 @@ pf2_session_alloc(VALUE self)
|
|
|
306
320
|
{
|
|
307
321
|
// Initialize state for libbacktrace
|
|
308
322
|
if (global_backtrace_state == NULL) {
|
|
309
|
-
global_backtrace_state = backtrace_create_state(
|
|
323
|
+
global_backtrace_state = backtrace_create_state(NULL, 1, pf2_backtrace_print_error, NULL);
|
|
310
324
|
if (global_backtrace_state == NULL) {
|
|
311
325
|
rb_raise(rb_eRuntimeError, "Failed to initialize libbacktrace");
|
|
312
326
|
}
|
|
@@ -350,6 +364,10 @@ pf2_session_alloc(VALUE self)
|
|
|
350
364
|
rb_raise(rb_eNoMemError, "Failed to allocate memory");
|
|
351
365
|
}
|
|
352
366
|
|
|
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
|
+
|
|
353
371
|
// start_time_realtime, start_time
|
|
354
372
|
session->start_time_realtime = (struct timespec){0};
|
|
355
373
|
session->start_time = (struct timespec){0};
|
data/ext/pf2/session.h
CHANGED
|
@@ -30,6 +30,9 @@ 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
|
+
|
|
33
36
|
struct pf2_configuration *configuration;
|
|
34
37
|
};
|
|
35
38
|
|
data/lib/pf2/cli.rb
CHANGED
data/lib/pf2/version.rb
CHANGED
data/lib/pf2.rb
CHANGED
|
@@ -15,9 +15,17 @@ module Pf2
|
|
|
15
15
|
@@session.stop
|
|
16
16
|
end
|
|
17
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
27
|
raise ArgumentError, "block required" unless block_given?
|
|
20
|
-
start(
|
|
28
|
+
start(**kwargs)
|
|
21
29
|
yield
|
|
22
30
|
result = stop
|
|
23
31
|
@@session = nil # let GC clean up the session
|
|
@@ -812,6 +812,7 @@ enable_darwin_at_rpath
|
|
|
812
812
|
enable_largefile
|
|
813
813
|
enable_werror
|
|
814
814
|
with_system_libunwind
|
|
815
|
+
enable_host_pie
|
|
815
816
|
enable_host_shared
|
|
816
817
|
'
|
|
817
818
|
ac_precious_vars='build_alias
|
|
@@ -1461,6 +1462,7 @@ Optional Features:
|
|
|
1461
1462
|
rpaths to be added to executables
|
|
1462
1463
|
--disable-largefile omit support for large files
|
|
1463
1464
|
--disable-werror disable building with -Werror
|
|
1465
|
+
--enable-host-pie build host code as PIE
|
|
1464
1466
|
--enable-host-shared build host code as shared libraries
|
|
1465
1467
|
|
|
1466
1468
|
Optional Packages:
|
|
@@ -11395,7 +11397,7 @@ else
|
|
|
11395
11397
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
|
11396
11398
|
lt_status=$lt_dlunknown
|
|
11397
11399
|
cat > conftest.$ac_ext <<_LT_EOF
|
|
11398
|
-
#line
|
|
11400
|
+
#line 11400 "configure"
|
|
11399
11401
|
#include "confdefs.h"
|
|
11400
11402
|
|
|
11401
11403
|
#if HAVE_DLFCN_H
|
|
@@ -11501,7 +11503,7 @@ else
|
|
|
11501
11503
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
|
11502
11504
|
lt_status=$lt_dlunknown
|
|
11503
11505
|
cat > conftest.$ac_ext <<_LT_EOF
|
|
11504
|
-
#line
|
|
11506
|
+
#line 11506 "configure"
|
|
11505
11507
|
#include "confdefs.h"
|
|
11506
11508
|
|
|
11507
11509
|
#if HAVE_DLFCN_H
|
|
@@ -12190,12 +12192,18 @@ $as_echo "#define HAVE_GETIPINFO 1" >>confdefs.h
|
|
|
12190
12192
|
fi
|
|
12191
12193
|
fi
|
|
12192
12194
|
|
|
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
|
+
|
|
12193
12203
|
# Enable --enable-host-shared.
|
|
12194
12204
|
# Check whether --enable-host-shared was given.
|
|
12195
12205
|
if test "${enable_host_shared+set}" = set; then :
|
|
12196
12206
|
enableval=$enable_host_shared; PIC_FLAG=-fPIC
|
|
12197
|
-
else
|
|
12198
|
-
PIC_FLAG=
|
|
12199
12207
|
fi
|
|
12200
12208
|
|
|
12201
12209
|
|
|
@@ -176,11 +176,16 @@ 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=])
|
|
179
184
|
# Enable --enable-host-shared.
|
|
180
185
|
AC_ARG_ENABLE(host-shared,
|
|
181
186
|
[AS_HELP_STRING([--enable-host-shared],
|
|
182
187
|
[build host code as shared libraries])],
|
|
183
|
-
[PIC_FLAG=-fPIC]
|
|
188
|
+
[PIC_FLAG=-fPIC])
|
|
184
189
|
AC_SUBST(PIC_FLAG)
|
|
185
190
|
|
|
186
191
|
# Test for __sync support.
|
data/vendor/libbacktrace/elf.c
CHANGED
|
@@ -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
|
|
164
|
-
#undef
|
|
165
|
-
#undef
|
|
166
|
-
#undef
|
|
163
|
+
#undef ELFMAG0
|
|
164
|
+
#undef ELFMAG1
|
|
165
|
+
#undef ELFMAG2
|
|
166
|
+
#undef ELFMAG3
|
|
167
167
|
#undef ELFCLASS32
|
|
168
168
|
#undef ELFCLASS64
|
|
169
169
|
#undef ELFDATA2LSB
|
|
@@ -47,6 +47,10 @@ 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
|
+
|
|
50
54
|
#ifdef HAVE_WINDOWS_H
|
|
51
55
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
52
56
|
#define WIN32_LEAN_AND_MEAN
|
|
@@ -66,6 +70,33 @@ POSSIBILITY OF SUCH DAMAGE. */
|
|
|
66
70
|
#define getexecname() NULL
|
|
67
71
|
#endif
|
|
68
72
|
|
|
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
|
+
|
|
69
100
|
#if !defined (HAVE_KERN_PROC_ARGS) && !defined (HAVE_KERN_PROC)
|
|
70
101
|
|
|
71
102
|
#define sysctl_exec_name1(state, error_callback, data) NULL
|
|
@@ -245,7 +276,7 @@ fileline_initialize (struct backtrace_state *state,
|
|
|
245
276
|
|
|
246
277
|
descriptor = -1;
|
|
247
278
|
called_error_callback = 0;
|
|
248
|
-
for (pass = 0; pass <
|
|
279
|
+
for (pass = 0; pass < 11; ++pass)
|
|
249
280
|
{
|
|
250
281
|
int does_not_exist;
|
|
251
282
|
|
|
@@ -285,6 +316,9 @@ fileline_initialize (struct backtrace_state *state,
|
|
|
285
316
|
case 9:
|
|
286
317
|
filename = windows_get_executable_path (buf, error_callback, data);
|
|
287
318
|
break;
|
|
319
|
+
case 10:
|
|
320
|
+
filename = hpux_get_executable_path (state, error_callback, data);
|
|
321
|
+
break;
|
|
288
322
|
default:
|
|
289
323
|
abort ();
|
|
290
324
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
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 } }
|
|
6
7
|
/^\001\337/ { if (NR == 1) { print "xcoff32"; exit } }
|
|
7
8
|
/^\001\367/ { if (NR == 1) { print "xcoff64"; exit } }
|
|
8
9
|
/^\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.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daisuke Aritomo
|
|
@@ -125,6 +125,7 @@ files:
|
|
|
125
125
|
- examples/mandelbrot.rb
|
|
126
126
|
- examples/mandelbrot_ractor.rb
|
|
127
127
|
- exe/pf2
|
|
128
|
+
- ext/patches/libbacktrace/0001-Support-MACH_O_MH_BUNDLE.patch
|
|
128
129
|
- ext/pf2/backtrace_state.c
|
|
129
130
|
- ext/pf2/backtrace_state.h
|
|
130
131
|
- ext/pf2/configuration.c
|