stackprof 0.2.25 → 0.2.27

Sign up to get free protection for your applications and to get access to all the features.
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: []