stackprof 0.2.25 → 0.2.27
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/.github/workflows/ci.yml +1 -1
- data/ext/stackprof/stackprof.c +94 -21
- data/lib/stackprof.rb +6 -2
- data/stackprof.gemspec +1 -2
- data/test/test_middleware.rb +30 -17
- data/test/test_report.rb +2 -2
- data/test/test_stackprof.rb +13 -3
- data/test/test_truffleruby.rb +1 -1
- metadata +6 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f919a966335bef57faaa01c783e1b204c435a2ba1d35f03ff6bf33302dfe34c9
|
4
|
+
data.tar.gz: 679b2bc9c1cc4c9f89638e5ab05c864cb8e9bf10f53e6ae2047cfc835575eb5a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb21acb48e4ea36eed09b5e82bb2780941a30ce973430acb10184df16893179dfd1d8e5092d546f1814b947d0693f10d2dacd526a4383b1b61cd64f75379b3f4
|
7
|
+
data.tar.gz: '08112358f745eeacf16b4df2ca4627daccf2ee5fefc31975530859891a57049148025f36f63bd72fed1bb6a1650a0837a9a5a8e4ba6e506c912c5abdd0d9ab38'
|
data/.github/workflows/ci.yml
CHANGED
@@ -8,7 +8,7 @@ jobs:
|
|
8
8
|
strategy:
|
9
9
|
fail-fast: false
|
10
10
|
matrix:
|
11
|
-
ruby: [ ruby-head, '3.
|
11
|
+
ruby: [ ruby-head, '3.3','3.2', '3.1', '3.0', '2.7', truffleruby ]
|
12
12
|
steps:
|
13
13
|
- name: Checkout
|
14
14
|
uses: actions/checkout@v2
|
data/ext/stackprof/stackprof.c
CHANGED
@@ -91,7 +91,19 @@ typedef struct {
|
|
91
91
|
int64_t delta_usec;
|
92
92
|
} sample_time_t;
|
93
93
|
|
94
|
+
/* We need to ensure that various memory operations are visible across
|
95
|
+
* threads. Ruby doesn't offer a portable way to do this sort of detection
|
96
|
+
* across all the Ruby versions we support, so we use something that casts a
|
97
|
+
* wide net (Clang, along with ICC, defines __GNUC__). */
|
98
|
+
#if defined(__GNUC__) && defined(__ATOMIC_SEQ_CST)
|
99
|
+
#define STACKPROF_HAVE_ATOMICS 1
|
100
|
+
#else
|
101
|
+
#define STACKPROF_HAVE_ATOMICS 0
|
102
|
+
#endif
|
103
|
+
|
94
104
|
static struct {
|
105
|
+
/* Access this field with the `STACKPROF_RUNNING` macro, below, since we
|
106
|
+
* can't properly express that this field has an atomic type. */
|
95
107
|
int running;
|
96
108
|
int raw;
|
97
109
|
int aggregate;
|
@@ -102,7 +114,7 @@ static struct {
|
|
102
114
|
VALUE metadata;
|
103
115
|
int ignore_gc;
|
104
116
|
|
105
|
-
|
117
|
+
uint64_t *raw_samples;
|
106
118
|
size_t raw_samples_len;
|
107
119
|
size_t raw_samples_capa;
|
108
120
|
size_t raw_sample_index;
|
@@ -120,6 +132,8 @@ static struct {
|
|
120
132
|
size_t unrecorded_gc_sweeping_samples;
|
121
133
|
st_table *frames;
|
122
134
|
|
135
|
+
timestamp_t gc_start_timestamp;
|
136
|
+
|
123
137
|
VALUE fake_frame_names[TOTAL_FAKE_FRAMES];
|
124
138
|
VALUE empty_string;
|
125
139
|
|
@@ -131,9 +145,15 @@ static struct {
|
|
131
145
|
pthread_t target_thread;
|
132
146
|
} _stackprof;
|
133
147
|
|
148
|
+
#if STACKPROF_HAVE_ATOMICS
|
149
|
+
#define STACKPROF_RUNNING() __atomic_load_n(&_stackprof.running, __ATOMIC_ACQUIRE)
|
150
|
+
#else
|
151
|
+
#define STACKPROF_RUNNING() _stackprof.running
|
152
|
+
#endif
|
153
|
+
|
134
154
|
static VALUE sym_object, sym_wall, sym_cpu, sym_custom, sym_name, sym_file, sym_line;
|
135
155
|
static VALUE sym_samples, sym_total_samples, sym_missed_samples, sym_edges, sym_lines;
|
136
|
-
static VALUE sym_version, sym_mode, sym_interval, sym_raw, sym_metadata, sym_frames, sym_ignore_gc, sym_out;
|
156
|
+
static VALUE sym_version, sym_mode, sym_interval, sym_raw, sym_raw_lines, sym_metadata, sym_frames, sym_ignore_gc, sym_out;
|
137
157
|
static VALUE sym_aggregate, sym_raw_sample_timestamps, sym_raw_timestamp_deltas, sym_state, sym_marking, sym_sweeping;
|
138
158
|
static VALUE sym_gc_samples, objtracer;
|
139
159
|
static VALUE gc_hook;
|
@@ -152,7 +172,7 @@ stackprof_start(int argc, VALUE *argv, VALUE self)
|
|
152
172
|
int raw = 0, aggregate = 1;
|
153
173
|
VALUE metadata_val;
|
154
174
|
|
155
|
-
if (
|
175
|
+
if (STACKPROF_RUNNING())
|
156
176
|
return Qfalse;
|
157
177
|
|
158
178
|
rb_scan_args(argc, argv, "0:", &opts);
|
@@ -215,7 +235,6 @@ stackprof_start(int argc, VALUE *argv, VALUE self)
|
|
215
235
|
rb_raise(rb_eArgError, "unknown profiler mode");
|
216
236
|
}
|
217
237
|
|
218
|
-
_stackprof.running = 1;
|
219
238
|
_stackprof.raw = raw;
|
220
239
|
_stackprof.aggregate = aggregate;
|
221
240
|
_stackprof.mode = mode;
|
@@ -224,6 +243,13 @@ stackprof_start(int argc, VALUE *argv, VALUE self)
|
|
224
243
|
_stackprof.metadata = metadata;
|
225
244
|
_stackprof.out = out;
|
226
245
|
_stackprof.target_thread = pthread_self();
|
246
|
+
/* We need to ensure previous initialization stores are visible across
|
247
|
+
* threads. */
|
248
|
+
#if STACKPROF_HAVE_ATOMICS
|
249
|
+
__atomic_store_n(&_stackprof.running, 1, __ATOMIC_SEQ_CST);
|
250
|
+
#else
|
251
|
+
_stackprof.running = 1;
|
252
|
+
#endif
|
227
253
|
|
228
254
|
if (raw) {
|
229
255
|
capture_timestamp(&_stackprof.last_sample_at);
|
@@ -238,9 +264,15 @@ stackprof_stop(VALUE self)
|
|
238
264
|
struct sigaction sa;
|
239
265
|
struct itimerval timer;
|
240
266
|
|
267
|
+
#if STACKPROF_HAVE_ATOMICS
|
268
|
+
int was_running = __atomic_exchange_n(&_stackprof.running, 0, __ATOMIC_SEQ_CST);
|
269
|
+
if (!was_running)
|
270
|
+
return Qfalse;
|
271
|
+
#else
|
241
272
|
if (!_stackprof.running)
|
242
273
|
return Qfalse;
|
243
274
|
_stackprof.running = 0;
|
275
|
+
#endif
|
244
276
|
|
245
277
|
if (_stackprof.mode == sym_object) {
|
246
278
|
rb_tracepoint_disable(objtracer);
|
@@ -349,7 +381,7 @@ stackprof_results(int argc, VALUE *argv, VALUE self)
|
|
349
381
|
{
|
350
382
|
VALUE results, frames;
|
351
383
|
|
352
|
-
if (!_stackprof.frames ||
|
384
|
+
if (!_stackprof.frames || STACKPROF_RUNNING())
|
353
385
|
return Qnil;
|
354
386
|
|
355
387
|
results = rb_hash_new();
|
@@ -374,14 +406,23 @@ stackprof_results(int argc, VALUE *argv, VALUE self)
|
|
374
406
|
size_t len, n, o;
|
375
407
|
VALUE raw_sample_timestamps, raw_timestamp_deltas;
|
376
408
|
VALUE raw_samples = rb_ary_new_capa(_stackprof.raw_samples_len);
|
409
|
+
VALUE raw_lines = rb_ary_new_capa(_stackprof.raw_samples_len);
|
377
410
|
|
378
411
|
for (n = 0; n < _stackprof.raw_samples_len; n++) {
|
379
412
|
len = (size_t)_stackprof.raw_samples[n];
|
380
413
|
rb_ary_push(raw_samples, SIZET2NUM(len));
|
414
|
+
rb_ary_push(raw_lines, SIZET2NUM(len));
|
415
|
+
|
416
|
+
for (o = 0, n++; o < len; n++, o++) {
|
417
|
+
// Line is in the upper 16 bits
|
418
|
+
rb_ary_push(raw_lines, INT2NUM(_stackprof.raw_samples[n] >> 48));
|
419
|
+
|
420
|
+
VALUE frame = _stackprof.raw_samples[n] & ~((uint64_t)0xFFFF << 48);
|
421
|
+
rb_ary_push(raw_samples, PTR2NUM(frame));
|
422
|
+
}
|
381
423
|
|
382
|
-
for (o = 0, n++; o < len; n++, o++)
|
383
|
-
rb_ary_push(raw_samples, PTR2NUM(_stackprof.raw_samples[n]));
|
384
424
|
rb_ary_push(raw_samples, SIZET2NUM((size_t)_stackprof.raw_samples[n]));
|
425
|
+
rb_ary_push(raw_lines, SIZET2NUM((size_t)_stackprof.raw_samples[n]));
|
385
426
|
}
|
386
427
|
|
387
428
|
free(_stackprof.raw_samples);
|
@@ -391,6 +432,7 @@ stackprof_results(int argc, VALUE *argv, VALUE self)
|
|
391
432
|
_stackprof.raw_sample_index = 0;
|
392
433
|
|
393
434
|
rb_hash_aset(results, sym_raw, raw_samples);
|
435
|
+
rb_hash_aset(results, sym_raw_lines, raw_lines);
|
394
436
|
|
395
437
|
raw_sample_timestamps = rb_ary_new_capa(_stackprof.raw_sample_times_len);
|
396
438
|
raw_timestamp_deltas = rb_ary_new_capa(_stackprof.raw_sample_times_len);
|
@@ -443,7 +485,7 @@ stackprof_run(int argc, VALUE *argv, VALUE self)
|
|
443
485
|
static VALUE
|
444
486
|
stackprof_running_p(VALUE self)
|
445
487
|
{
|
446
|
-
return
|
488
|
+
return STACKPROF_RUNNING() ? Qtrue : Qfalse;
|
447
489
|
}
|
448
490
|
|
449
491
|
static inline frame_data_t *
|
@@ -520,7 +562,12 @@ stackprof_record_sample_for_stack(int num, uint64_t sample_timestamp, int64_t ti
|
|
520
562
|
* in the frames buffer that came from Ruby. */
|
521
563
|
for (i = num-1, n = 0; i >= 0; i--, n++) {
|
522
564
|
VALUE frame = _stackprof.frames_buffer[i];
|
523
|
-
|
565
|
+
int line = _stackprof.lines_buffer[i];
|
566
|
+
|
567
|
+
// Encode the line in to the upper 16 bits.
|
568
|
+
uint64_t key = ((uint64_t)line << 48) | (uint64_t)frame;
|
569
|
+
|
570
|
+
if (_stackprof.raw_samples[_stackprof.raw_sample_index + 1 + n] != key)
|
524
571
|
break;
|
525
572
|
}
|
526
573
|
if (i == -1) {
|
@@ -538,7 +585,12 @@ stackprof_record_sample_for_stack(int num, uint64_t sample_timestamp, int64_t ti
|
|
538
585
|
_stackprof.raw_samples[_stackprof.raw_samples_len++] = (VALUE)num;
|
539
586
|
for (i = num-1; i >= 0; i--) {
|
540
587
|
VALUE frame = _stackprof.frames_buffer[i];
|
541
|
-
_stackprof.
|
588
|
+
int line = _stackprof.lines_buffer[i];
|
589
|
+
|
590
|
+
// Encode the line in to the upper 16 bits.
|
591
|
+
uint64_t key = ((uint64_t)line << 48) | (uint64_t)frame;
|
592
|
+
|
593
|
+
_stackprof.raw_samples[_stackprof.raw_samples_len++] = key;
|
542
594
|
}
|
543
595
|
_stackprof.raw_samples[_stackprof.raw_samples_len++] = (VALUE)1;
|
544
596
|
}
|
@@ -626,6 +678,7 @@ stackprof_buffer_sample(void)
|
|
626
678
|
_stackprof.buffer_time.delta_usec = timestamp_delta;
|
627
679
|
}
|
628
680
|
|
681
|
+
// Postponed job
|
629
682
|
void
|
630
683
|
stackprof_record_gc_samples(void)
|
631
684
|
{
|
@@ -633,8 +686,7 @@ stackprof_record_gc_samples(void)
|
|
633
686
|
uint64_t start_timestamp = 0;
|
634
687
|
size_t i;
|
635
688
|
if (_stackprof.raw) {
|
636
|
-
struct timestamp_t t;
|
637
|
-
capture_timestamp(&t);
|
689
|
+
struct timestamp_t t = _stackprof.gc_start_timestamp;
|
638
690
|
start_timestamp = timestamp_usec(&t);
|
639
691
|
|
640
692
|
// We don't know when the GC samples were actually marked, so let's
|
@@ -697,7 +749,7 @@ stackprof_sample_and_record(void)
|
|
697
749
|
static void
|
698
750
|
stackprof_job_record_gc(void *data)
|
699
751
|
{
|
700
|
-
if (!
|
752
|
+
if (!STACKPROF_RUNNING()) return;
|
701
753
|
|
702
754
|
stackprof_record_gc_samples();
|
703
755
|
}
|
@@ -705,7 +757,7 @@ stackprof_job_record_gc(void *data)
|
|
705
757
|
static void
|
706
758
|
stackprof_job_sample_and_record(void *data)
|
707
759
|
{
|
708
|
-
if (!
|
760
|
+
if (!STACKPROF_RUNNING()) return;
|
709
761
|
|
710
762
|
stackprof_sample_and_record();
|
711
763
|
}
|
@@ -713,7 +765,7 @@ stackprof_job_sample_and_record(void *data)
|
|
713
765
|
static void
|
714
766
|
stackprof_job_record_buffer(void *data)
|
715
767
|
{
|
716
|
-
if (!
|
768
|
+
if (!STACKPROF_RUNNING()) return;
|
717
769
|
|
718
770
|
stackprof_record_buffer();
|
719
771
|
}
|
@@ -725,7 +777,7 @@ stackprof_signal_handler(int sig, siginfo_t *sinfo, void *ucontext)
|
|
725
777
|
|
726
778
|
_stackprof.overall_signals++;
|
727
779
|
|
728
|
-
if (!
|
780
|
+
if (!STACKPROF_RUNNING()) return;
|
729
781
|
|
730
782
|
// There's a possibility that the signal handler is invoked *after* the Ruby
|
731
783
|
// VM has been shut down (e.g. after ruby_cleanup(0)). In this case, things
|
@@ -756,6 +808,10 @@ stackprof_signal_handler(int sig, siginfo_t *sinfo, void *ucontext)
|
|
756
808
|
} else if (mode == sym_sweeping) {
|
757
809
|
_stackprof.unrecorded_gc_sweeping_samples++;
|
758
810
|
}
|
811
|
+
if(!_stackprof.unrecorded_gc_samples) {
|
812
|
+
// record start
|
813
|
+
capture_timestamp(&_stackprof.gc_start_timestamp);
|
814
|
+
}
|
759
815
|
_stackprof.unrecorded_gc_samples++;
|
760
816
|
rb_postponed_job_register_one(0, stackprof_job_record_gc, (void*)0);
|
761
817
|
} else {
|
@@ -784,7 +840,7 @@ stackprof_newobj_handler(VALUE tpval, void *data)
|
|
784
840
|
static VALUE
|
785
841
|
stackprof_sample(VALUE self)
|
786
842
|
{
|
787
|
-
if (!
|
843
|
+
if (!STACKPROF_RUNNING())
|
788
844
|
return Qfalse;
|
789
845
|
|
790
846
|
_stackprof.overall_signals++;
|
@@ -812,16 +868,23 @@ stackprof_gc_mark(void *data)
|
|
812
868
|
if (_stackprof.frames)
|
813
869
|
st_foreach(_stackprof.frames, frame_mark_i, 0);
|
814
870
|
|
815
|
-
|
871
|
+
int i;
|
872
|
+
for (i = 0; i < _stackprof.buffer_count; i++) {
|
816
873
|
rb_gc_mark(_stackprof.frames_buffer[i]);
|
817
874
|
}
|
818
875
|
}
|
819
876
|
|
877
|
+
static size_t
|
878
|
+
stackprof_memsize(const void *data)
|
879
|
+
{
|
880
|
+
return sizeof(_stackprof);
|
881
|
+
}
|
882
|
+
|
820
883
|
static void
|
821
884
|
stackprof_atfork_prepare(void)
|
822
885
|
{
|
823
886
|
struct itimerval timer;
|
824
|
-
if (
|
887
|
+
if (STACKPROF_RUNNING()) {
|
825
888
|
if (_stackprof.mode == sym_wall || _stackprof.mode == sym_cpu) {
|
826
889
|
memset(&timer, 0, sizeof(timer));
|
827
890
|
setitimer(_stackprof.mode == sym_wall ? ITIMER_REAL : ITIMER_PROF, &timer, 0);
|
@@ -833,7 +896,7 @@ static void
|
|
833
896
|
stackprof_atfork_parent(void)
|
834
897
|
{
|
835
898
|
struct itimerval timer;
|
836
|
-
if (
|
899
|
+
if (STACKPROF_RUNNING()) {
|
837
900
|
if (_stackprof.mode == sym_wall || _stackprof.mode == sym_cpu) {
|
838
901
|
timer.it_interval.tv_sec = 0;
|
839
902
|
timer.it_interval.tv_usec = NUM2LONG(_stackprof.interval);
|
@@ -862,6 +925,15 @@ stackprof_at_exit(ruby_vm_t* vm)
|
|
862
925
|
ruby_vm_running = 0;
|
863
926
|
}
|
864
927
|
|
928
|
+
static const rb_data_type_t stackprof_type = {
|
929
|
+
"StackProf",
|
930
|
+
{
|
931
|
+
stackprof_gc_mark,
|
932
|
+
NULL,
|
933
|
+
stackprof_memsize,
|
934
|
+
}
|
935
|
+
};
|
936
|
+
|
865
937
|
void
|
866
938
|
Init_stackprof(void)
|
867
939
|
{
|
@@ -893,6 +965,7 @@ Init_stackprof(void)
|
|
893
965
|
S(mode);
|
894
966
|
S(interval);
|
895
967
|
S(raw);
|
968
|
+
S(raw_lines);
|
896
969
|
S(raw_sample_timestamps);
|
897
970
|
S(raw_timestamp_deltas);
|
898
971
|
S(out);
|
@@ -908,8 +981,8 @@ Init_stackprof(void)
|
|
908
981
|
/* Need to run this to warm the symbol table before we call this during GC */
|
909
982
|
rb_gc_latest_gc_info(sym_state);
|
910
983
|
|
911
|
-
gc_hook = Data_Wrap_Struct(rb_cObject, stackprof_gc_mark, NULL, &_stackprof);
|
912
984
|
rb_global_variable(&gc_hook);
|
985
|
+
gc_hook = TypedData_Wrap_Struct(rb_cObject, &stackprof_type, &_stackprof);
|
913
986
|
|
914
987
|
_stackprof.raw_samples = NULL;
|
915
988
|
_stackprof.raw_samples_len = 0;
|
data/lib/stackprof.rb
CHANGED
@@ -5,7 +5,11 @@ else
|
|
5
5
|
end
|
6
6
|
|
7
7
|
if defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
|
8
|
-
|
8
|
+
if RUBY_VERSION < "3.3"
|
9
|
+
# On 3.3 we don't need postponed jobs:
|
10
|
+
# https://github.com/ruby/ruby/commit/a1dc1a3de9683daf5a543d6f618e17aabfcb8708
|
11
|
+
StackProf.use_postponed_job!
|
12
|
+
end
|
9
13
|
elsif RUBY_VERSION == "3.2.0"
|
10
14
|
# 3.2.0 crash is the signal is received at the wrong time.
|
11
15
|
# Fixed in https://github.com/ruby/ruby/pull/7116
|
@@ -14,7 +18,7 @@ elsif RUBY_VERSION == "3.2.0"
|
|
14
18
|
end
|
15
19
|
|
16
20
|
module StackProf
|
17
|
-
VERSION = '0.2.
|
21
|
+
VERSION = '0.2.27'
|
18
22
|
end
|
19
23
|
|
20
24
|
StackProf.autoload :Report, "stackprof/report.rb"
|
data/stackprof.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'stackprof'
|
3
|
-
s.version = '0.2.
|
3
|
+
s.version = '0.2.27'
|
4
4
|
s.homepage = 'http://github.com/tmm1/stackprof'
|
5
5
|
|
6
6
|
s.authors = 'Aman Gupta'
|
@@ -29,6 +29,5 @@ Gem::Specification.new do |s|
|
|
29
29
|
s.license = 'MIT'
|
30
30
|
|
31
31
|
s.add_development_dependency 'rake-compiler', '~> 0.9'
|
32
|
-
s.add_development_dependency 'mocha', '~> 0.14'
|
33
32
|
s.add_development_dependency 'minitest', '~> 5.0'
|
34
33
|
end
|
data/test/test_middleware.rb
CHANGED
@@ -2,9 +2,9 @@ $:.unshift File.expand_path('../../lib', __FILE__)
|
|
2
2
|
require 'stackprof'
|
3
3
|
require 'stackprof/middleware'
|
4
4
|
require 'minitest/autorun'
|
5
|
-
require '
|
5
|
+
require 'tmpdir'
|
6
6
|
|
7
|
-
class StackProf::MiddlewareTest <
|
7
|
+
class StackProf::MiddlewareTest < Minitest::Test
|
8
8
|
|
9
9
|
def test_path_default
|
10
10
|
StackProf::Middleware.new(Object.new)
|
@@ -19,23 +19,36 @@ class StackProf::MiddlewareTest < MiniTest::Test
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_save_default
|
22
|
-
StackProf::Middleware.new(Object.new
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
middleware = StackProf::Middleware.new(->(env) { 100.times { Object.new } },
|
23
|
+
save_every: 1,
|
24
|
+
enabled: true)
|
25
|
+
Dir.mktmpdir do |dir|
|
26
|
+
Dir.chdir(dir) { middleware.call({}) }
|
27
|
+
dir = File.join(dir, "tmp")
|
28
|
+
assert File.directory? dir
|
29
|
+
profile = Dir.entries(dir).reject { |x| File.directory?(x) }.first
|
30
|
+
assert profile
|
31
|
+
assert_equal "stackprof", profile.split("-")[0]
|
32
|
+
assert_equal "cpu", profile.split("-")[1]
|
33
|
+
assert_equal Process.pid.to_s, profile.split("-")[2]
|
34
|
+
end
|
29
35
|
end
|
30
36
|
|
31
37
|
def test_save_custom
|
32
|
-
StackProf::Middleware.new(
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
38
|
+
middleware = StackProf::Middleware.new(->(env) { 100.times { Object.new } },
|
39
|
+
path: "foo/",
|
40
|
+
save_every: 1,
|
41
|
+
enabled: true)
|
42
|
+
Dir.mktmpdir do |dir|
|
43
|
+
Dir.chdir(dir) { middleware.call({}) }
|
44
|
+
dir = File.join(dir, "foo")
|
45
|
+
assert File.directory? dir
|
46
|
+
profile = Dir.entries(dir).reject { |x| File.directory?(x) }.first
|
47
|
+
assert profile
|
48
|
+
assert_equal "stackprof", profile.split("-")[0]
|
49
|
+
assert_equal "cpu", profile.split("-")[1]
|
50
|
+
assert_equal Process.pid.to_s, profile.split("-")[2]
|
51
|
+
end
|
39
52
|
end
|
40
53
|
|
41
54
|
def test_enabled_should_use_a_proc_if_passed
|
@@ -70,4 +83,4 @@ class StackProf::MiddlewareTest < MiniTest::Test
|
|
70
83
|
StackProf::Middleware.new(Object.new, metadata: metadata)
|
71
84
|
assert_equal metadata, StackProf::Middleware.metadata
|
72
85
|
end
|
73
|
-
end
|
86
|
+
end unless RUBY_ENGINE == 'truffleruby'
|
data/test/test_report.rb
CHANGED
@@ -2,7 +2,7 @@ $:.unshift File.expand_path('../../lib', __FILE__)
|
|
2
2
|
require 'stackprof'
|
3
3
|
require 'minitest/autorun'
|
4
4
|
|
5
|
-
class ReportDumpTest <
|
5
|
+
class ReportDumpTest < Minitest::Test
|
6
6
|
require 'stringio'
|
7
7
|
|
8
8
|
def test_dump_to_stdout
|
@@ -33,7 +33,7 @@ class ReportDumpTest < MiniTest::Test
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
class ReportReadTest <
|
36
|
+
class ReportReadTest < Minitest::Test
|
37
37
|
require 'pathname'
|
38
38
|
|
39
39
|
def test_from_file_read_json
|
data/test/test_stackprof.rb
CHANGED
@@ -4,7 +4,7 @@ require 'minitest/autorun'
|
|
4
4
|
require 'tempfile'
|
5
5
|
require 'pathname'
|
6
6
|
|
7
|
-
class StackProfTest <
|
7
|
+
class StackProfTest < Minitest::Test
|
8
8
|
def setup
|
9
9
|
Object.new # warm some caches to avoid flakiness
|
10
10
|
end
|
@@ -93,6 +93,7 @@ class StackProfTest < MiniTest::Test
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def test_walltime
|
96
|
+
GC.disable
|
96
97
|
profile = StackProf.run(mode: :wall) do
|
97
98
|
idle
|
98
99
|
end
|
@@ -104,6 +105,8 @@ class StackProfTest < MiniTest::Test
|
|
104
105
|
assert_equal "StackProfTest#idle", frame[:name]
|
105
106
|
end
|
106
107
|
assert_in_delta 200, frame[:samples], 25
|
108
|
+
ensure
|
109
|
+
GC.enable
|
107
110
|
end
|
108
111
|
|
109
112
|
def test_custom
|
@@ -142,10 +145,13 @@ class StackProfTest < MiniTest::Test
|
|
142
145
|
after_monotonic = Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond)
|
143
146
|
|
144
147
|
raw = profile[:raw]
|
148
|
+
raw_lines = profile[:raw_lines]
|
145
149
|
assert_equal 10, raw[-1]
|
146
150
|
assert_equal raw[0] + 2, raw.size
|
151
|
+
assert_equal 10, raw_lines[-1] # seen 10 times
|
147
152
|
|
148
153
|
offset = RUBY_VERSION >= '3' ? -3 : -2
|
154
|
+
assert_equal 140, raw_lines[offset] # sample caller is on 140
|
149
155
|
assert_includes profile[:frames][raw[offset]][:name], 'StackProfTest#test_raw'
|
150
156
|
|
151
157
|
assert_equal 10, profile[:raw_sample_timestamps].size
|
@@ -241,8 +247,12 @@ class StackProfTest < MiniTest::Test
|
|
241
247
|
assert marking_frame
|
242
248
|
assert sweeping_frame
|
243
249
|
|
244
|
-
|
245
|
-
|
250
|
+
# We can't guarantee a certain number of GCs to run, so just assert
|
251
|
+
# that it's within some kind of delta
|
252
|
+
assert_in_delta gc_frame[:total_samples], profile[:gc_samples], 2
|
253
|
+
|
254
|
+
# Lazy marking / sweeping can cause this math to not add up, so also use a delta
|
255
|
+
assert_in_delta profile[:gc_samples], [gc_frame, marking_frame, sweeping_frame].map{|x| x[:samples] }.inject(:+), 2
|
246
256
|
|
247
257
|
assert_operator profile[:gc_samples], :>, 0
|
248
258
|
assert_operator profile[:missed_samples], :<=, 25
|
data/test/test_truffleruby.rb
CHANGED
@@ -3,7 +3,7 @@ require 'stackprof'
|
|
3
3
|
require 'minitest/autorun'
|
4
4
|
|
5
5
|
if RUBY_ENGINE == 'truffleruby'
|
6
|
-
class StackProfTruffleRubyTest <
|
6
|
+
class StackProfTruffleRubyTest < Minitest::Test
|
7
7
|
def test_error
|
8
8
|
error = assert_raises RuntimeError do
|
9
9
|
StackProf.run(mode: :cpu) do
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stackprof
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.27
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aman Gupta
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-01-14 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: rake-compiler
|
@@ -24,20 +23,6 @@ dependencies:
|
|
24
23
|
- - "~>"
|
25
24
|
- !ruby/object:Gem::Version
|
26
25
|
version: '0.9'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: mocha
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0.14'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0.14'
|
41
26
|
- !ruby/object:Gem::Dependency
|
42
27
|
name: minitest
|
43
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -99,10 +84,9 @@ licenses:
|
|
99
84
|
- MIT
|
100
85
|
metadata:
|
101
86
|
bug_tracker_uri: https://github.com/tmm1/stackprof/issues
|
102
|
-
changelog_uri: https://github.com/tmm1/stackprof/blob/v0.2.
|
103
|
-
documentation_uri: https://www.rubydoc.info/gems/stackprof/0.2.
|
104
|
-
source_code_uri: https://github.com/tmm1/stackprof/tree/v0.2.
|
105
|
-
post_install_message:
|
87
|
+
changelog_uri: https://github.com/tmm1/stackprof/blob/v0.2.27/CHANGELOG.md
|
88
|
+
documentation_uri: https://www.rubydoc.info/gems/stackprof/0.2.27
|
89
|
+
source_code_uri: https://github.com/tmm1/stackprof/tree/v0.2.27
|
106
90
|
rdoc_options: []
|
107
91
|
require_paths:
|
108
92
|
- lib
|
@@ -117,8 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
101
|
- !ruby/object:Gem::Version
|
118
102
|
version: '0'
|
119
103
|
requirements: []
|
120
|
-
rubygems_version: 3.0.
|
121
|
-
signing_key:
|
104
|
+
rubygems_version: 3.7.0.dev
|
122
105
|
specification_version: 4
|
123
106
|
summary: sampling callstack-profiler for ruby 2.2+
|
124
107
|
test_files: []
|