stackprof 0.2.11 → 0.2.12

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
  SHA1:
3
- metadata.gz: 88f62f89ebff2c249b7eaabcb330f12997f09120
4
- data.tar.gz: 629071b6584701d830b827e5d4b9b0951eaa0282
3
+ metadata.gz: 25c29d953e534a2d31065493cbffa762c5f92ee1
4
+ data.tar.gz: f9533f32cd7d5d648f3b9e16fd9d43469181521a
5
5
  SHA512:
6
- metadata.gz: d02684e9bd77e2b561f626a69a897e1ec2c89b870b30ef54709165cc1debb966a56127f3053d7a27e81cc7c3c62fef50865ebd5bcbfd6d2b28630add6280e479
7
- data.tar.gz: 2d5d70aaa53080112d8f794d1d5412cd8225f451f43431f30209e42854b5de8246ff77347ef39496c2b55b447d6cf114116b81dc2eb458adea16a5d9ce03606e
6
+ metadata.gz: 69d486b03120ed37b76b16e65825b98e00fa6fbfb3aa3c484870ebead42cdbfed204e1e4e4362e3d5bdadee9c81ccf4169bcdaf109191c8b0fa580f97a8dfc62
7
+ data.tar.gz: b735b0457b8ee0bbf217d0a891abd81c88ecbfd63a435001d1aaaec9707ac01607e14bea0b82a6c0c6ba827b64b3885d0cbffffdabe0742a3cc51bb402a59fc2
data/.gitignore CHANGED
@@ -2,3 +2,5 @@
2
2
  /lib/stackprof/stackprof.bundle
3
3
  /lib/stackprof/stackprof.so
4
4
  *.sw?
5
+ /ext
6
+ /pkg
@@ -0,0 +1,3 @@
1
+ # 0.2.12
2
+
3
+ * Eliminate one loop over the stack by using `overall_samples` [#98]
data/README.md CHANGED
@@ -1,13 +1,17 @@
1
- ## stackprof
1
+ # Stackprof
2
2
 
3
- a sampling call-stack profiler for ruby 2.1+
3
+ A sampling call-stack profiler for Ruby.
4
4
 
5
- inspired heavily by [gperftools](https://code.google.com/p/gperftools/),
6
- and written as a replacement for [perftools.rb](https://github.com/tmm1/perftools.rb)
5
+ Inspired heavily by [gperftools](https://code.google.com/p/gperftools/), and written as a replacement for [perftools.rb](https://github.com/tmm1/perftools.rb).
7
6
 
8
- ### getting started
7
+ ## Requirements
9
8
 
10
- #### Install
9
+ * Ruby 2.1+
10
+ * Linux-based OS
11
+
12
+ ## Getting Started
13
+
14
+ ### Install
11
15
 
12
16
  In your Gemfile add:
13
17
 
@@ -18,7 +22,7 @@ gem 'stackprof'
18
22
  Then run `$ bundle install`. Alternatively you can run `$ gem install stackprof`.
19
23
 
20
24
 
21
- #### Run
25
+ ### Run
22
26
 
23
27
  in ruby:
24
28
 
@@ -93,7 +97,7 @@ The `--flamegraph-viewer` command will output the exact shell command you need t
93
97
 
94
98
  ![](http://i.imgur.com/EwndrgD.png)
95
99
 
96
- ### sampling
100
+ ## Sampling
97
101
 
98
102
  four sampling modes are supported:
99
103
 
@@ -142,7 +146,7 @@ samples are taken using a combination of three new C-APIs in ruby 2.1:
142
146
  - in allocation mode, samples are taken via `rb_tracepoint_new(RUBY_INTERNAL_EVENT_NEWOBJ)`,
143
147
  which provides a notification every time the VM allocates a new object.
144
148
 
145
- ### Aggregation
149
+ ## Aggregation
146
150
 
147
151
  each sample consists of N stack frames, where a frame looks something like `MyClass#method` or `block in MySingleton.method`.
148
152
  for each of these frames in the sample, the profiler collects a few pieces of metadata:
@@ -175,14 +179,14 @@ this technique builds up an incremental callgraph from the samples. on any given
175
179
  the sum of the outbound edge weights is equal to total samples collected on that frame
176
180
  (`frame.total_samples == frame.edges.values.sum`).
177
181
 
178
- ### reporting
182
+ ## Reporting
179
183
 
180
184
  multiple reporting modes are supported:
181
185
  - text
182
186
  - dotgraph
183
187
  - source annotation
184
188
 
185
- #### `StackProf::Report.new(data).print_text`
189
+ ### `StackProf::Report.new(data).print_text`
186
190
 
187
191
  ```
188
192
  TOTAL (pct) SAMPLES (pct) FRAME
@@ -197,7 +201,7 @@ multiple reporting modes are supported:
197
201
  188 (100.0%) 0 (0.0%) <main>
198
202
  ```
199
203
 
200
- #### `StackProf::Report.new(data).print_graphviz`
204
+ ### `StackProf::Report.new(data).print_graphviz`
201
205
 
202
206
  ![](http://cl.ly/image/2t3l2q0l0B0A/content)
203
207
 
@@ -223,7 +227,7 @@ digraph profile {
223
227
  }
224
228
  ```
225
229
 
226
- #### `StackProf::Report.new(data).print_method(/pow|newobj|math/)`
230
+ ### `StackProf::Report.new(data).print_method(/pow|newobj|math/)`
227
231
 
228
232
  ```
229
233
  A#pow (/Users/tmm1/code/stackprof/sample.rb:11)
@@ -245,7 +249,7 @@ block in A#math (/Users/tmm1/code/stackprof/sample.rb:21)
245
249
  | 23 | end
246
250
  ```
247
251
 
248
- ### usage
252
+ ## Usage
249
253
 
250
254
  the profiler is compiled as a C-extension and exposes a simple api: `StackProf.run(mode: [:cpu|:wall|:object])`.
251
255
  the `run` method takes a block of code and returns a profile as a simple hash.
@@ -295,7 +299,7 @@ above, `A#pow` was involved in 91 samples, and in all cases it was at the top of
295
299
  divided up between its callee edges. all 91 calls to `A#pow` came from `A#initialize`, as seen by the edge numbered
296
300
  `70346498324780`.
297
301
 
298
- ### advanced usage
302
+ ## Advanced usage
299
303
 
300
304
  the profiler can be started and stopped manually. results are accumulated until retrieval, across
301
305
  multiple start/stop invocations.
@@ -307,7 +311,7 @@ StackProf.stop
307
311
  StackProf.results('/tmp/some.file')
308
312
  ```
309
313
 
310
- ### all options
314
+ ## All options
311
315
 
312
316
  `StackProf.run` accepts an options hash. Currently, the following options are recognized:
313
317
 
@@ -320,7 +324,7 @@ Option | Meaning
320
324
  `raw` | defaults `false` - if `true` collects the extra data required by the `--flamegraph` and `--stackcollapse` report types
321
325
  `save_every`| (rack middleware only) write the target file after this many requests
322
326
 
323
- ### todo
327
+ ## Todo
324
328
 
325
329
  * file/iseq blacklist
326
330
  * restore signal handlers on stop
@@ -20,7 +20,7 @@
20
20
  typedef struct {
21
21
  size_t total_samples;
22
22
  size_t caller_samples;
23
- int already_accounted_in_total;
23
+ size_t seen_at_sample_number;
24
24
  st_table *edges;
25
25
  st_table *lines;
26
26
  } frame_data_t;
@@ -268,6 +268,7 @@ stackprof_results(int argc, VALUE *argv, VALUE self)
268
268
 
269
269
  if (_stackprof.raw && _stackprof.raw_samples_len) {
270
270
  size_t len, n, o;
271
+ VALUE raw_timestamp_deltas;
271
272
  VALUE raw_samples = rb_ary_new_capa(_stackprof.raw_samples_len);
272
273
 
273
274
  for (n = 0; n < _stackprof.raw_samples_len; n++) {
@@ -287,7 +288,7 @@ stackprof_results(int argc, VALUE *argv, VALUE self)
287
288
 
288
289
  rb_hash_aset(results, sym_raw, raw_samples);
289
290
 
290
- VALUE raw_timestamp_deltas = rb_ary_new_capa(_stackprof.raw_timestamp_deltas_len);
291
+ raw_timestamp_deltas = rb_ary_new_capa(_stackprof.raw_timestamp_deltas_len);
291
292
 
292
293
  for (n = 0; n < _stackprof.raw_timestamp_deltas_len; n++) {
293
294
  rb_ary_push(raw_timestamp_deltas, INT2FIX(_stackprof.raw_timestamp_deltas[n]));
@@ -432,19 +433,15 @@ stackprof_record_sample_for_stack(int num, int timestamp_delta)
432
433
  _stackprof.raw_timestamp_deltas[_stackprof.raw_timestamp_deltas_len++] = timestamp_delta;
433
434
  }
434
435
 
435
- for (i = 0; i < num; i++) {
436
- VALUE frame = _stackprof.frames_buffer[i];
437
- sample_for(frame)->already_accounted_in_total = 0;
438
- }
439
-
440
436
  for (i = 0; i < num; i++) {
441
437
  int line = _stackprof.lines_buffer[i];
442
438
  VALUE frame = _stackprof.frames_buffer[i];
443
439
  frame_data_t *frame_data = sample_for(frame);
444
440
 
445
- if (!frame_data->already_accounted_in_total)
441
+ if (frame_data->seen_at_sample_number != _stackprof.overall_samples) {
446
442
  frame_data->total_samples++;
447
- frame_data->already_accounted_in_total = 1;
443
+ }
444
+ frame_data->seen_at_sample_number = _stackprof.overall_samples;
448
445
 
449
446
  if (i == 0) {
450
447
  frame_data->caller_samples++;
@@ -455,10 +452,10 @@ stackprof_record_sample_for_stack(int num, int timestamp_delta)
455
452
  }
456
453
 
457
454
  if (_stackprof.aggregate && line > 0) {
458
- if (!frame_data->lines)
459
- frame_data->lines = st_init_numtable();
460
455
  size_t half = (size_t)1<<(8*SIZEOF_SIZE_T/2);
461
456
  size_t increment = i == 0 ? half + 1 : half;
457
+ if (!frame_data->lines)
458
+ frame_data->lines = st_init_numtable();
462
459
  st_numtable_increment(frame_data->lines, (st_data_t)line, increment);
463
460
  }
464
461
 
@@ -474,14 +471,15 @@ void
474
471
  stackprof_record_sample()
475
472
  {
476
473
  int timestamp_delta = 0;
474
+ int num;
477
475
  if (_stackprof.raw) {
478
476
  struct timeval t;
479
- gettimeofday(&t, NULL);
480
477
  struct timeval diff;
478
+ gettimeofday(&t, NULL);
481
479
  timersub(&t, &_stackprof.last_sample_at, &diff);
482
480
  timestamp_delta = (1000 * diff.tv_sec) + diff.tv_usec;
483
481
  }
484
- int num = rb_profile_frames(0, sizeof(_stackprof.frames_buffer) / sizeof(VALUE), _stackprof.frames_buffer, _stackprof.lines_buffer);
482
+ num = rb_profile_frames(0, sizeof(_stackprof.frames_buffer) / sizeof(VALUE), _stackprof.frames_buffer, _stackprof.lines_buffer);
485
483
  stackprof_record_sample_for_stack(num, timestamp_delta);
486
484
  }
487
485
 
@@ -489,10 +487,11 @@ void
489
487
  stackprof_record_gc_samples()
490
488
  {
491
489
  int delta_to_first_unrecorded_gc_sample = 0;
490
+ int i;
492
491
  if (_stackprof.raw) {
493
492
  struct timeval t;
494
- gettimeofday(&t, NULL);
495
493
  struct timeval diff;
494
+ gettimeofday(&t, NULL);
496
495
  timersub(&t, &_stackprof.last_sample_at, &diff);
497
496
 
498
497
  // We don't know when the GC samples were actually marked, so let's
@@ -503,8 +502,6 @@ stackprof_record_gc_samples()
503
502
  }
504
503
  }
505
504
 
506
- int i;
507
-
508
505
  _stackprof.frames_buffer[0] = _stackprof.fake_gc_frame;
509
506
  _stackprof.lines_buffer[0] = 0;
510
507
 
@@ -11,7 +11,8 @@ module StackProf
11
11
  Middleware.interval = options[:interval] || 1000
12
12
  Middleware.raw = options[:raw] || false
13
13
  Middleware.enabled = options[:enabled]
14
- Middleware.path = options[:path] || 'tmp'
14
+ options[:path] = 'tmp/' if options[:path].to_s.empty?
15
+ Middleware.path = options[:path]
15
16
  at_exit{ Middleware.save } if options[:save_at_exit]
16
17
  end
17
18
 
@@ -40,11 +41,20 @@ module StackProf
40
41
  end
41
42
  end
42
43
 
43
- def save(filename = nil)
44
+ def save
44
45
  if results = StackProf.results
45
- FileUtils.mkdir_p(Middleware.path)
46
- filename ||= "stackprof-#{results[:mode]}-#{Process.pid}-#{Time.now.to_i}.dump"
47
- File.open(File.join(Middleware.path, filename), 'wb') do |f|
46
+ path = Middleware.path
47
+ is_directory = path != path.chomp('/')
48
+
49
+ if is_directory
50
+ filename = "stackprof-#{results[:mode]}-#{Process.pid}-#{Time.now.to_i}.dump"
51
+ else
52
+ filename = File.basename(path)
53
+ path = File.dirname(path)
54
+ end
55
+
56
+ FileUtils.mkdir_p(path)
57
+ File.open(File.join(path, filename), 'wb') do |f|
48
58
  f.write Marshal.dump(results)
49
59
  end
50
60
  filename
@@ -55,8 +55,8 @@ module StackProf
55
55
 
56
56
  def add_lines(a, b)
57
57
  return b if a.nil?
58
- return a+b if a.is_a? Fixnum
59
- return [ a[0], a[1]+b ] if b.is_a? Fixnum
58
+ return a+b if a.is_a? Integer
59
+ return [ a[0], a[1]+b ] if b.is_a? Integer
60
60
  [ a[0]+b[0], a[1]+b[1] ]
61
61
  end
62
62
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'stackprof'
3
- s.version = '0.2.11'
3
+ s.version = '0.2.12'
4
4
  s.homepage = 'http://github.com/tmm1/stackprof'
5
5
 
6
6
  s.authors = 'Aman Gupta'
@@ -9,31 +9,31 @@ class StackProf::MiddlewareTest < MiniTest::Test
9
9
  def test_path_default
10
10
  StackProf::Middleware.new(Object.new)
11
11
 
12
- assert_equal 'tmp', StackProf::Middleware.path
12
+ assert_equal 'tmp/', StackProf::Middleware.path
13
13
  end
14
14
 
15
15
  def test_path_custom
16
- StackProf::Middleware.new(Object.new, { path: '/foo' })
16
+ StackProf::Middleware.new(Object.new, { path: 'foo/' })
17
17
 
18
- assert_equal '/foo', StackProf::Middleware.path
18
+ assert_equal 'foo/', StackProf::Middleware.path
19
19
  end
20
20
 
21
21
  def test_save_default
22
22
  StackProf::Middleware.new(Object.new)
23
23
 
24
24
  StackProf.stubs(:results).returns({ mode: 'foo' })
25
- FileUtils.expects(:mkdir_p).with('tmp')
25
+ FileUtils.expects(:mkdir_p).with('tmp/')
26
26
  File.expects(:open).with(regexp_matches(/^tmp\/stackprof-foo/), 'wb')
27
27
 
28
28
  StackProf::Middleware.save
29
29
  end
30
30
 
31
31
  def test_save_custom
32
- StackProf::Middleware.new(Object.new, { path: '/foo' })
32
+ StackProf::Middleware.new(Object.new, { path: 'foo/' })
33
33
 
34
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')
35
+ FileUtils.expects(:mkdir_p).with('foo/')
36
+ File.expects(:open).with(regexp_matches(/^foo\/stackprof-foo/), 'wb')
37
37
 
38
38
  StackProf::Middleware.save
39
39
  end
@@ -119,6 +119,27 @@ class StackProfTest < MiniTest::Test
119
119
  end
120
120
  end
121
121
 
122
+ def foo(n = 10)
123
+ if n == 0
124
+ StackProf.sample
125
+ return
126
+ end
127
+ foo(n - 1)
128
+ end
129
+
130
+ def test_recursive_total_samples
131
+ profile = StackProf.run(mode: :cpu, raw: true) do
132
+ 10.times do
133
+ foo
134
+ end
135
+ end
136
+
137
+ frame = profile[:frames].values.find do |frame|
138
+ frame[:name] == "StackProfTest#foo"
139
+ end
140
+ assert_equal 10, frame[:total_samples]
141
+ end
142
+
122
143
  def test_gc
123
144
  profile = StackProf.run(interval: 100, raw: true) do
124
145
  5.times do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stackprof
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.11
4
+ version: 0.2.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aman Gupta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-22 00:00:00.000000000 Z
11
+ date: 2018-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -65,6 +65,7 @@ extra_rdoc_files: []
65
65
  files:
66
66
  - ".gitignore"
67
67
  - ".travis.yml"
68
+ - CHANGELOG.md
68
69
  - Gemfile
69
70
  - Gemfile.lock
70
71
  - LICENSE
@@ -109,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
110
  version: '0'
110
111
  requirements: []
111
112
  rubyforge_project:
112
- rubygems_version: 2.6.13
113
+ rubygems_version: 2.4.6
113
114
  signing_key:
114
115
  specification_version: 4
115
116
  summary: sampling callstack-profiler for ruby 2.1+