pf2 0.1.0 → 0.2.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.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pf2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daisuke Aritomo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-10-04 00:00:00.000000000 Z
11
+ date: 2024-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rb_sys
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.63
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.9.63
27
41
  description:
28
42
  email:
29
43
  - osyoyu@osyoyu.com
@@ -34,12 +48,27 @@ extensions:
34
48
  extra_rdoc_files: []
35
49
  files:
36
50
  - CHANGELOG.md
51
+ - Cargo.lock
52
+ - Cargo.toml
37
53
  - LICENSE.txt
38
54
  - README.md
39
55
  - Rakefile
40
56
  - exe/pf2
57
+ - ext/pf2/Cargo.toml
58
+ - ext/pf2/build.rs
41
59
  - ext/pf2/extconf.rb
42
- - ext/pf2/pf2.c
60
+ - ext/pf2/src/lib.rs
61
+ - ext/pf2/src/profile.rs
62
+ - ext/pf2/src/profile_serializer.rs
63
+ - ext/pf2/src/ringbuffer.rs
64
+ - ext/pf2/src/ruby_init.rs
65
+ - ext/pf2/src/sample.rs
66
+ - ext/pf2/src/siginfo_t.c
67
+ - ext/pf2/src/signal_scheduler.rs
68
+ - ext/pf2/src/signal_scheduler/configuration.rs
69
+ - ext/pf2/src/signal_scheduler/timer_installer.rs
70
+ - ext/pf2/src/timer_thread_scheduler.rs
71
+ - ext/pf2/src/util.rs
43
72
  - lib/pf2.rb
44
73
  - lib/pf2/cli.rb
45
74
  - lib/pf2/reporter.rb
@@ -60,14 +89,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
89
  requirements:
61
90
  - - ">="
62
91
  - !ruby/object:Gem::Version
63
- version: 3.3.0.dev
92
+ version: 3.3.0
64
93
  required_rubygems_version: !ruby/object:Gem::Requirement
65
94
  requirements:
66
95
  - - ">="
67
96
  - !ruby/object:Gem::Version
68
97
  version: '0'
69
98
  requirements: []
70
- rubygems_version: 3.5.0.dev
99
+ rubygems_version: 3.6.0.dev
71
100
  signing_key:
72
101
  specification_version: 4
73
102
  summary: Yet another Ruby profiler
data/ext/pf2/pf2.c DELETED
@@ -1,246 +0,0 @@
1
- #include <errno.h>
2
- #include <signal.h>
3
- #include <stdbool.h>
4
- #include <stdio.h>
5
- #include <stdlib.h>
6
- #include <unistd.h>
7
-
8
- #include <ruby.h>
9
- #include <ruby/debug.h>
10
- #include <ruby/thread.h>
11
-
12
- #define MAX_BUFFER_SIZE 3000
13
-
14
- struct pf2_buffer_t {
15
- VALUE framebuffer[MAX_BUFFER_SIZE];
16
- int linebuffer[MAX_BUFFER_SIZE];
17
- };
18
-
19
- // Ruby functions
20
- void Init_pf2(void);
21
- VALUE rb_start(VALUE self, VALUE debug);
22
- VALUE rb_stop(VALUE self);
23
-
24
- static void pf2_start(void);
25
- static void pf2_stop(void);
26
- static void pf2_signal_handler(int signo);
27
- static void pf2_postponed_job(void *_);
28
-
29
- static void pf2_record(struct pf2_buffer_t *buffer);
30
- static VALUE find_or_create_thread_results(VALUE results, pid_t thread_id);
31
-
32
- // Buffer to record rb_profile_frames() results
33
- struct pf2_buffer_t buffer;
34
- // The time when the profiler started
35
- struct timespec initial_time;
36
- // Debug print?
37
- bool _debug = false;
38
-
39
- void
40
- Init_pf2(void)
41
- {
42
- VALUE rb_mPf2 = rb_define_module("Pf2");
43
- rb_define_module_function(rb_mPf2, "start", rb_start, 1);
44
- rb_define_module_function(rb_mPf2, "stop", rb_stop, 0);
45
- }
46
-
47
- VALUE
48
- rb_start(VALUE self, VALUE debug) {
49
- _debug = RTEST(debug);
50
-
51
- /**
52
- * {
53
- * sequence: 0,
54
- * threads: {},
55
- * }
56
- */
57
- VALUE results = rb_hash_new();
58
- rb_hash_aset(results, ID2SYM(rb_intern_const("sequence")), INT2FIX(0));
59
- rb_hash_aset(results, ID2SYM(rb_intern_const("threads")), rb_hash_new());
60
-
61
- rb_iv_set(self, "@results", results);
62
-
63
- pf2_start();
64
-
65
- if (_debug) {
66
- rb_funcall(rb_mKernel, rb_intern("puts"), 1, rb_str_new_cstr("[debug] Pf2 started"));
67
- }
68
-
69
- return results;
70
- }
71
-
72
- VALUE
73
- rb_stop(VALUE self) {
74
- pf2_stop();
75
-
76
- if (_debug) {
77
- rb_funcall(rb_mKernel, rb_intern("puts"), 1, rb_str_new_cstr("[debug] Pf2 stopped"));
78
- }
79
-
80
- return rb_iv_get(self, "@results");
81
- }
82
-
83
- static void
84
- pf2_start(void)
85
- {
86
- clock_gettime(CLOCK_MONOTONIC, &initial_time);
87
-
88
- // Configure timer for every 10 ms
89
- // TODO: Make interval configurable
90
- struct itimerval timer;
91
- timer.it_value.tv_sec = 1;
92
- timer.it_value.tv_usec = 0;
93
- timer.it_interval.tv_sec = 0;
94
- timer.it_interval.tv_usec = 10 * 1000; // 10 ms
95
- if (signal(SIGALRM, pf2_signal_handler) == SIG_ERR) {
96
- rb_syserr_fail(errno, "Failed to configure profiling timer");
97
- };
98
- if (setitimer(ITIMER_REAL, &timer, NULL) == -1) {
99
- rb_syserr_fail(errno, "Failed to configure profiling timer");
100
- };
101
- }
102
-
103
- static void
104
- pf2_stop(void)
105
- {
106
- struct itimerval timer = { 0 }; // stop
107
- setitimer(ITIMER_REAL, &timer, NULL);
108
- }
109
-
110
- // async-signal-safe
111
- static void
112
- pf2_signal_handler(int signo)
113
- {
114
- rb_postponed_job_register_one(0, pf2_postponed_job, 0);
115
- }
116
-
117
- static void
118
- pf2_postponed_job(void *_) {
119
- pf2_record(&buffer);
120
- };
121
-
122
- // Buffer structure
123
- static void
124
- pf2_record(struct pf2_buffer_t *buffer)
125
- {
126
- // get the current time
127
- struct timespec ts;
128
- clock_gettime(CLOCK_MONOTONIC, &ts);
129
-
130
- VALUE rb_mPf2 = rb_const_get(rb_cObject, rb_intern("Pf2"));
131
- VALUE results = rb_iv_get(rb_mPf2, "@results");
132
-
133
- // Iterate over all Threads
134
- VALUE threads = rb_iv_get(rb_mPf2, "@@threads");
135
- for (int i = 0; i < RARRAY_LEN(threads); i++) {
136
- VALUE thread = rb_ary_entry(threads, i);
137
- VALUE thread_status = rb_funcall(thread, rb_intern("status"), 0);
138
- if (NIL_P(thread) || thread_status == Qfalse) {
139
- // Thread is dead, just ignore
140
- continue;
141
- }
142
-
143
- pid_t thread_id = NUM2INT(rb_funcall(thread, rb_intern("native_thread_id"), 0));
144
- VALUE thread_results = find_or_create_thread_results(results, thread_id);
145
- assert(!NIL_P(thread_results));
146
-
147
- // The actual querying
148
- int stack_depth = rb_profile_thread_frames(thread, 0, MAX_BUFFER_SIZE, buffer->framebuffer, buffer->linebuffer);
149
-
150
- // TODO: Reimplement Pf2-internal data structures without CRuby
151
- // (which will allow us to release the GVL at this point)
152
- // rb_thread_call_without_gvl(...);
153
-
154
- VALUE frames_table = rb_hash_lookup(thread_results, ID2SYM(rb_intern_const("frames")));
155
- assert(!NIL_P(frames_table));
156
- VALUE samples = rb_hash_lookup(thread_results, ID2SYM(rb_intern_const("samples")));
157
- assert(!NIL_P(samples));
158
-
159
- // Dig down the stack (top of call stack -> bottom (root))
160
- VALUE stack_tree_p = rb_hash_lookup(thread_results, ID2SYM(rb_intern_const("stack_tree")));
161
- for (int i = stack_depth - 1; i >= 0; i--) {
162
- assert(NIL_P(buffer->framebuffer[i]));
163
-
164
- // Collect & record frame information
165
- VALUE frame_obj_id = rb_obj_id(buffer->framebuffer[i]);
166
- VALUE frame_table_entry = rb_hash_aref(frames_table, frame_obj_id);
167
- if (NIL_P(frame_table_entry)) {
168
- frame_table_entry = rb_hash_new();
169
- rb_hash_aset(frame_table_entry, ID2SYM(rb_intern_const("full_label")), rb_profile_frame_full_label(buffer->framebuffer[i]));
170
- rb_hash_aset(frames_table, frame_obj_id, frame_table_entry);
171
- }
172
-
173
- VALUE children = rb_hash_aref(stack_tree_p, ID2SYM(rb_intern_const("children")));
174
- VALUE next_node = rb_hash_lookup(children, frame_obj_id);
175
- // If this is the first time we see this frame, register it to the stack tree
176
- if (NIL_P(next_node)) { // not found
177
- next_node = rb_hash_new();
178
-
179
- // Increment sequence
180
- VALUE next =
181
- rb_funcall(
182
- rb_hash_lookup(results, ID2SYM(rb_intern_const("sequence"))),
183
- rb_intern("+"),
184
- 1,
185
- INT2FIX(1)
186
- );
187
- rb_hash_aset(results, ID2SYM(rb_intern_const("sequence")), next);
188
-
189
- rb_hash_aset(next_node, ID2SYM(rb_intern_const("node_id")), INT2FIX(next));
190
- rb_hash_aset(next_node, ID2SYM(rb_intern_const("frame_id")), frame_obj_id);
191
- rb_hash_aset(next_node, ID2SYM(rb_intern_const("full_label")), rb_profile_frame_full_label(buffer->framebuffer[i]));
192
- rb_hash_aset(next_node, ID2SYM(rb_intern_const("children")), rb_hash_new());
193
-
194
- rb_hash_aset(children, frame_obj_id, next_node);
195
- }
196
-
197
- VALUE stack_tree_id = rb_hash_aref(next_node, ID2SYM(rb_intern_const("node_id")));
198
-
199
- // If on leaf
200
- if (i == 0) {
201
- // Record sample
202
- VALUE sample = rb_hash_new();
203
- rb_hash_aset(sample, ID2SYM(rb_intern_const("stack_tree_id")), stack_tree_id);
204
- unsigned long long nsec = (ts.tv_sec - initial_time.tv_sec) * 1000000000 + ts.tv_nsec - initial_time.tv_nsec;
205
- rb_hash_aset(sample, ID2SYM(rb_intern_const("timestamp")), ULL2NUM(nsec));
206
- rb_ary_push(samples, sample);
207
- }
208
-
209
- stack_tree_p = next_node;
210
- }
211
- }
212
- }
213
-
214
- static VALUE
215
- find_or_create_thread_results(VALUE results, pid_t thread_id) {
216
- assert(!NIL_P(results));
217
- assert(!NIL_P(thread));
218
-
219
- VALUE threads = rb_hash_aref(results, ID2SYM(rb_intern_const("threads")));
220
- VALUE thread_results = rb_hash_aref(threads, INT2NUM(thread_id));
221
- if (NIL_P(thread_results)) {
222
- /**
223
- * {
224
- * thread_id: 1,
225
- * frames: [],
226
- * stack_tree: {
227
- * node_id: ...,
228
- * children: {}
229
- * },
230
- * samples: [],
231
- * }
232
- */
233
- thread_results = rb_hash_new();
234
- rb_hash_aset(thread_results, ID2SYM(rb_intern_const("thread_id")), INT2NUM(thread_id));
235
-
236
- rb_hash_aset(thread_results, ID2SYM(rb_intern_const("frames")), rb_hash_new());
237
- VALUE stack_tree = rb_hash_aset(thread_results, ID2SYM(rb_intern_const("stack_tree")), rb_hash_new());
238
- rb_hash_aset(stack_tree, ID2SYM(rb_intern_const("node_id")), ID2SYM(rb_intern_const("root")));
239
- rb_hash_aset(stack_tree, ID2SYM(rb_intern_const("children")), rb_hash_new());
240
- rb_hash_aset(thread_results, ID2SYM(rb_intern_const("samples")), rb_ary_new());
241
- rb_hash_aset(thread_results, ID2SYM(rb_intern_const("gvl_timings")), rb_ary_new());
242
-
243
- rb_hash_aset(threads, INT2NUM(thread_id), thread_results);
244
- }
245
- return thread_results;
246
- }