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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b299eb696cf0c2748e931532afab3f90bab1c94447ff1c844bce0eda878a93c6
4
- data.tar.gz: bd2c389baa8253fc06beda425abc87762b9bca425c6bc5046655610fb9852e79
3
+ metadata.gz: f919a966335bef57faaa01c783e1b204c435a2ba1d35f03ff6bf33302dfe34c9
4
+ data.tar.gz: 679b2bc9c1cc4c9f89638e5ab05c864cb8e9bf10f53e6ae2047cfc835575eb5a
5
5
  SHA512:
6
- metadata.gz: 322e506fc77bd964f39e7b3f0c5584e57efdec8e1dcc432cdea924536e334234d916a1d5e1ff085e2e5210a49751897ca36a4b1bd007468a5b16184151129be3
7
- data.tar.gz: 3234d4159c78119a9238200d1f4516280cbd1e0cfe3affca1c4fa4827b805ecf154e38857c917fafe7aed042d67525bb7e6c5fce85a039e30cde4672ae08f97e
6
+ metadata.gz: fb21acb48e4ea36eed09b5e82bb2780941a30ce973430acb10184df16893179dfd1d8e5092d546f1814b947d0693f10d2dacd526a4383b1b61cd64f75379b3f4
7
+ data.tar.gz: '08112358f745eeacf16b4df2ca4627daccf2ee5fefc31975530859891a57049148025f36f63bd72fed1bb6a1650a0837a9a5a8e4ba6e506c912c5abdd0d9ab38'
@@ -8,7 +8,7 @@ jobs:
8
8
  strategy:
9
9
  fail-fast: false
10
10
  matrix:
11
- ruby: [ ruby-head, '3.1', '3.0', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', truffleruby ]
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
@@ -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
- VALUE *raw_samples;
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 (_stackprof.running)
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 || _stackprof.running)
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 _stackprof.running ? Qtrue : Qfalse;
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
- if (_stackprof.raw_samples[_stackprof.raw_sample_index + 1 + n] != frame)
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.raw_samples[_stackprof.raw_samples_len++] = frame;
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 (!_stackprof.running) return;
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 (!_stackprof.running) return;
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 (!_stackprof.running) return;
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 (!_stackprof.running) return;
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 (!_stackprof.running)
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
- for (int i = 0; i < _stackprof.buffer_count; i++) {
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 (_stackprof.running) {
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 (_stackprof.running) {
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
- StackProf.use_postponed_job!
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.25'
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.25'
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
@@ -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 'mocha/setup'
5
+ require 'tmpdir'
6
6
 
7
- class StackProf::MiddlewareTest < MiniTest::Test
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
- StackProf.stubs(:results).returns({ mode: 'foo' })
25
- FileUtils.expects(:mkdir_p).with('tmp/')
26
- File.expects(:open).with(regexp_matches(/^tmp\/stackprof-foo/), 'wb')
27
-
28
- StackProf::Middleware.save
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(Object.new, { path: 'foo/' })
33
-
34
- StackProf.stubs(:results).returns({ mode: 'foo' })
35
- FileUtils.expects(:mkdir_p).with('foo/')
36
- File.expects(:open).with(regexp_matches(/^foo\/stackprof-foo/), 'wb')
37
-
38
- StackProf::Middleware.save
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 < MiniTest::Test
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 < MiniTest::Test
36
+ class ReportReadTest < Minitest::Test
37
37
  require 'pathname'
38
38
 
39
39
  def test_from_file_read_json
@@ -4,7 +4,7 @@ require 'minitest/autorun'
4
4
  require 'tempfile'
5
5
  require 'pathname'
6
6
 
7
- class StackProfTest < MiniTest::Test
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
- assert_equal gc_frame[:total_samples], profile[:gc_samples]
245
- assert_equal profile[:gc_samples], [gc_frame, marking_frame, sweeping_frame].map{|x| x[:samples] }.inject(:+)
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
@@ -3,7 +3,7 @@ require 'stackprof'
3
3
  require 'minitest/autorun'
4
4
 
5
5
  if RUBY_ENGINE == 'truffleruby'
6
- class StackProfTruffleRubyTest < MiniTest::Test
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.25
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: 2023-04-06 00:00:00.000000000 Z
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.25/CHANGELOG.md
103
- documentation_uri: https://www.rubydoc.info/gems/stackprof/0.2.25
104
- source_code_uri: https://github.com/tmm1/stackprof/tree/v0.2.25
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.3.1
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: []