stackprof 0.2.3 → 0.2.4

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
  SHA1:
3
- metadata.gz: d8814b67fbdc027a47b2d087321b62ba3f2c5e0b
4
- data.tar.gz: f5584eca1af8afa88afd407846b019dedbf33d3d
3
+ metadata.gz: f9391ce548ee344fce9ee6e1f2ff86a59f7e6837
4
+ data.tar.gz: 961fb781cadaa07df721ed11d60df39d10bd0680
5
5
  SHA512:
6
- metadata.gz: 50d3070d8a4ae606cb90b8e94afdbe4a047abc449a77731ebb9e198dd7fbb4f5e86965bbdba8761f3fbebe01023fca08376b865f84c25dcc7f51d4e7a9752d4c
7
- data.tar.gz: 4b0bdc52fdd32bb21b1a10a0061371e2d51ae126cd39c01c6898b2a405f6e2a13dd879e439b610d90fde8661d245f995bcf86063bb76570b2b8ddc793a2bdd2b
6
+ metadata.gz: fcefc33f597c6ee462651a4242b0251e7a7b8595e5869df0110f59ab9f9174ad86ac3243378f971c1ed0fe96700e36b382ecc325d65a0304ba542e5344c462d2
7
+ data.tar.gz: d5af5d2e2da5082c32b14b2b255e9d70d10d9958d86605b14ac53b2b91239d617e8f7d29006d06f8513e6facb7cd3904589321908e9d0c6e65cb7e42bcc31966
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stackprof (0.2.2)
4
+ stackprof (0.2.4)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -17,6 +17,6 @@ PLATFORMS
17
17
  ruby
18
18
 
19
19
  DEPENDENCIES
20
- mocha
21
- rake-compiler
20
+ mocha (~> 0.14)
21
+ rake-compiler (~> 0.9)
22
22
  stackprof!
data/bin/stackprof CHANGED
@@ -16,10 +16,11 @@ parser = OptionParser.new(ARGV) do |o|
16
16
  o.on('--limit [num]', Integer, 'Limit --text or --files output to N lines'){ |n| options[:limit] = n }
17
17
  o.on('--sort-total', "Sort --text or --files output on total samples\n\n"){ options[:sort] = true }
18
18
  o.on('--method [grep]', 'Zoom into specified method'){ |f| options[:format] = :method; options[:filter] = f }
19
- o.on('--file [grep]', 'Show annotated code for specified file'){ |f| options[:format] = :file; options[:filter] = f }
20
- o.on('--stackcollapse', 'stackcollapse.pl compatible output (use with flamegraph.pl)'){ options[:format] = :stackcollapse }
21
- o.on('--callgrind', 'Callgrind output (use with kcachegrind, gprof2dot)'){ options[:format] = :callgrind }
22
- o.on('--graphviz', "Graphviz output (use with dot)\n\n"){ options[:format] = :graphviz }
19
+ o.on('--file [grep]', "Show annotated code for specified file\n\n"){ |f| options[:format] = :file; options[:filter] = f }
20
+ o.on('--callgrind', 'Callgrind output (use with kcachegrind, stackprof-gprof2dot.py)'){ options[:format] = :callgrind }
21
+ o.on('--graphviz', "Graphviz output (use with dot)"){ options[:format] = :graphviz }
22
+ o.on('--stackcollapse', 'stackcollapse.pl compatible output (use with stackprof-flamegraph.pl)'){ options[:format] = :stackcollapse }
23
+ o.on('--flamegraph', "timeline-flamegraph output (js)\n\n"){ options[:format] = :flamegraph }
23
24
  o.on('--dump', 'Print marshaled profile dump (combine multiple profiles)'){ options[:format] = :dump }
24
25
  o.on('--debug', 'Pretty print raw profile data'){ options[:format] = :debug }
25
26
  end
@@ -51,6 +52,8 @@ when :graphviz
51
52
  report.print_graphviz
52
53
  when :stackcollapse
53
54
  report.print_stackcollapse
55
+ when :flamegraph
56
+ report.print_flamegraph
54
57
  when :method
55
58
  report.print_method(options[:filter])
56
59
  when :file
File without changes
data/ext/stackprof.c CHANGED
@@ -26,12 +26,18 @@ typedef struct {
26
26
 
27
27
  static struct {
28
28
  int running;
29
+ int raw;
30
+ int aggregate;
31
+
29
32
  VALUE mode;
30
33
  VALUE interval;
31
- VALUE raw;
32
- size_t raw_sample_index;
33
34
  VALUE out;
34
35
 
36
+ VALUE *raw_samples;
37
+ size_t raw_samples_len;
38
+ size_t raw_samples_capa;
39
+ size_t raw_sample_index;
40
+
35
41
  size_t overall_signals;
36
42
  size_t overall_samples;
37
43
  size_t during_gc;
@@ -43,7 +49,7 @@ static struct {
43
49
 
44
50
  static VALUE sym_object, sym_wall, sym_cpu, sym_custom, sym_name, sym_file, sym_line;
45
51
  static VALUE sym_samples, sym_total_samples, sym_missed_samples, sym_edges, sym_lines;
46
- static VALUE sym_version, sym_mode, sym_interval, sym_raw, sym_frames, sym_out;
52
+ static VALUE sym_version, sym_mode, sym_interval, sym_raw, sym_frames, sym_out, sym_aggregate;
47
53
  static VALUE sym_gc_samples, objtracer;
48
54
  static VALUE gc_hook;
49
55
  static VALUE rb_mStackProf;
@@ -56,7 +62,8 @@ stackprof_start(int argc, VALUE *argv, VALUE self)
56
62
  {
57
63
  struct sigaction sa;
58
64
  struct itimerval timer;
59
- VALUE opts = Qnil, mode = Qnil, interval = Qnil, raw = Qfalse, out = Qfalse;
65
+ VALUE opts = Qnil, mode = Qnil, interval = Qnil, out = Qfalse;
66
+ int raw = 0, aggregate = 1;
60
67
 
61
68
  if (_stackprof.running)
62
69
  return Qfalse;
@@ -69,7 +76,9 @@ stackprof_start(int argc, VALUE *argv, VALUE self)
69
76
  out = rb_hash_aref(opts, sym_out);
70
77
 
71
78
  if (RTEST(rb_hash_aref(opts, sym_raw)))
72
- raw = rb_ary_new();
79
+ raw = 1;
80
+ if (rb_hash_lookup2(opts, sym_aggregate, Qundef) == Qfalse)
81
+ aggregate = 0;
73
82
  }
74
83
  if (!RTEST(mode)) mode = sym_wall;
75
84
 
@@ -106,6 +115,7 @@ stackprof_start(int argc, VALUE *argv, VALUE self)
106
115
 
107
116
  _stackprof.running = 1;
108
117
  _stackprof.raw = raw;
118
+ _stackprof.aggregate = aggregate;
109
119
  _stackprof.mode = mode;
110
120
  _stackprof.interval = interval;
111
121
  _stackprof.out = out;
@@ -234,9 +244,27 @@ stackprof_results(int argc, VALUE *argv, VALUE self)
234
244
  st_free_table(_stackprof.frames);
235
245
  _stackprof.frames = NULL;
236
246
 
237
- if (RTEST(_stackprof.raw)) {
238
- rb_hash_aset(results, sym_raw, _stackprof.raw);
239
- _stackprof.raw = Qfalse;
247
+ if (_stackprof.raw && _stackprof.raw_samples_len) {
248
+ size_t len, n, o;
249
+ VALUE raw_samples = rb_ary_new_capa(_stackprof.raw_samples_len);
250
+
251
+ for (n = 0; n < _stackprof.raw_samples_len; n++) {
252
+ len = (size_t)_stackprof.raw_samples[n];
253
+ rb_ary_push(raw_samples, SIZET2NUM(len));
254
+
255
+ for (o = 0, n++; o < len; n++, o++)
256
+ rb_ary_push(raw_samples, rb_obj_id(_stackprof.raw_samples[n]));
257
+ rb_ary_push(raw_samples, SIZET2NUM((size_t)_stackprof.raw_samples[n]));
258
+ }
259
+
260
+ free(_stackprof.raw_samples);
261
+ _stackprof.raw_samples = NULL;
262
+ _stackprof.raw_samples_len = 0;
263
+ _stackprof.raw_samples_capa = 0;
264
+ _stackprof.raw_sample_index = 0;
265
+ _stackprof.raw = 0;
266
+
267
+ rb_hash_aset(results, sym_raw, raw_samples);
240
268
  }
241
269
 
242
270
  if (argc == 1)
@@ -315,37 +343,44 @@ void
315
343
  stackprof_record_sample()
316
344
  {
317
345
  int num, i, n;
318
- int raw_mode = RTEST(_stackprof.raw);
319
346
  VALUE prev_frame = Qnil;
320
- size_t raw_len;
321
347
 
322
348
  _stackprof.overall_samples++;
323
349
  num = rb_profile_frames(0, sizeof(_stackprof.frames_buffer), _stackprof.frames_buffer, _stackprof.lines_buffer);
324
350
 
325
- if (raw_mode) {
351
+ if (_stackprof.raw) {
326
352
  int found = 0;
327
- raw_len = RARRAY_LEN(_stackprof.raw);
328
353
 
329
- if (RARRAY_LEN(_stackprof.raw) > 0 && RARRAY_AREF(_stackprof.raw, _stackprof.raw_sample_index) == INT2FIX(num)) {
354
+ if (!_stackprof.raw_samples) {
355
+ _stackprof.raw_samples_capa = num * 100;
356
+ _stackprof.raw_samples = malloc(sizeof(VALUE) * _stackprof.raw_samples_capa);
357
+ }
358
+
359
+ if (_stackprof.raw_samples_capa <= _stackprof.raw_samples_len + num) {
360
+ _stackprof.raw_samples_capa *= 2;
361
+ _stackprof.raw_samples = realloc(_stackprof.raw_samples, _stackprof.raw_samples_capa);
362
+ }
363
+
364
+ if (_stackprof.raw_samples_len > 0 && _stackprof.raw_samples[_stackprof.raw_sample_index] == (VALUE)num) {
330
365
  for (i = num-1, n = 0; i >= 0; i--, n++) {
331
366
  VALUE frame = _stackprof.frames_buffer[i];
332
- if (RARRAY_AREF(_stackprof.raw, _stackprof.raw_sample_index + 1 + n) != rb_obj_id(frame))
367
+ if (_stackprof.raw_samples[_stackprof.raw_sample_index + 1 + n] != frame)
333
368
  break;
334
369
  }
335
370
  if (i == -1) {
336
- RARRAY_ASET(_stackprof.raw, raw_len-1, LONG2NUM(NUM2LONG(RARRAY_AREF(_stackprof.raw, raw_len-1))+1));
371
+ _stackprof.raw_samples[_stackprof.raw_samples_len-1] += 1;
337
372
  found = 1;
338
373
  }
339
374
  }
340
375
 
341
376
  if (!found) {
342
- _stackprof.raw_sample_index = raw_len;
343
- rb_ary_push(_stackprof.raw, INT2FIX(num));
377
+ _stackprof.raw_sample_index = _stackprof.raw_samples_len;
378
+ _stackprof.raw_samples[_stackprof.raw_samples_len++] = (VALUE)num;
344
379
  for (i = num-1; i >= 0; i--) {
345
380
  VALUE frame = _stackprof.frames_buffer[i];
346
- rb_ary_push(_stackprof.raw, rb_obj_id(frame));
381
+ _stackprof.raw_samples[_stackprof.raw_samples_len++] = frame;
347
382
  }
348
- rb_ary_push(_stackprof.raw, INT2FIX(1));
383
+ _stackprof.raw_samples[_stackprof.raw_samples_len++] = (VALUE)1;
349
384
  }
350
385
  }
351
386
 
@@ -358,13 +393,13 @@ stackprof_record_sample()
358
393
 
359
394
  if (i == 0) {
360
395
  frame_data->caller_samples++;
361
- } else {
396
+ } else if (_stackprof.aggregate) {
362
397
  if (!frame_data->edges)
363
398
  frame_data->edges = st_init_numtable();
364
399
  st_numtable_increment(frame_data->edges, (st_data_t)prev_frame, 1);
365
400
  }
366
401
 
367
- if (line > 0) {
402
+ if (_stackprof.aggregate && line > 0) {
368
403
  if (!frame_data->lines)
369
404
  frame_data->lines = st_init_numtable();
370
405
  size_t half = (size_t)1<<(8*SIZEOF_SIZE_T/2);
@@ -428,8 +463,6 @@ frame_mark_i(st_data_t key, st_data_t val, st_data_t arg)
428
463
  static void
429
464
  stackprof_gc_mark(void *data)
430
465
  {
431
- if (RTEST(_stackprof.raw))
432
- rb_gc_mark(_stackprof.raw);
433
466
  if (RTEST(_stackprof.out))
434
467
  rb_gc_mark(_stackprof.out);
435
468
 
@@ -472,25 +505,28 @@ stackprof_atfork_child(void)
472
505
  void
473
506
  Init_stackprof(void)
474
507
  {
475
- sym_object = ID2SYM(rb_intern("object"));
476
- sym_custom = ID2SYM(rb_intern("custom"));
477
- sym_wall = ID2SYM(rb_intern("wall"));
478
- sym_cpu = ID2SYM(rb_intern("cpu"));
479
- sym_name = ID2SYM(rb_intern("name"));
480
- sym_file = ID2SYM(rb_intern("file"));
481
- sym_line = ID2SYM(rb_intern("line"));
482
- sym_total_samples = ID2SYM(rb_intern("total_samples"));
483
- sym_gc_samples = ID2SYM(rb_intern("gc_samples"));
484
- sym_missed_samples = ID2SYM(rb_intern("missed_samples"));
485
- sym_samples = ID2SYM(rb_intern("samples"));
486
- sym_edges = ID2SYM(rb_intern("edges"));
487
- sym_lines = ID2SYM(rb_intern("lines"));
488
- sym_version = ID2SYM(rb_intern("version"));
489
- sym_mode = ID2SYM(rb_intern("mode"));
490
- sym_interval = ID2SYM(rb_intern("interval"));
491
- sym_raw = ID2SYM(rb_intern("raw"));
492
- sym_out = ID2SYM(rb_intern("out"));
493
- sym_frames = ID2SYM(rb_intern("frames"));
508
+ #define S(name) sym_##name = ID2SYM(rb_intern(#name));
509
+ S(object);
510
+ S(custom);
511
+ S(wall);
512
+ S(cpu);
513
+ S(name);
514
+ S(file);
515
+ S(line);
516
+ S(total_samples);
517
+ S(gc_samples);
518
+ S(missed_samples);
519
+ S(samples);
520
+ S(edges);
521
+ S(lines);
522
+ S(version);
523
+ S(mode);
524
+ S(interval);
525
+ S(raw);
526
+ S(out);
527
+ S(frames);
528
+ S(aggregate);
529
+ #undef S
494
530
 
495
531
  gc_hook = Data_Wrap_Struct(rb_cObject, stackprof_gc_mark, NULL, NULL);
496
532
  rb_global_variable(&gc_hook);
@@ -79,6 +79,71 @@ module StackProf
79
79
  end
80
80
  end
81
81
 
82
+ def print_flamegraph(f=STDOUT, skip_common=true)
83
+ raise "profile does not include raw samples" unless raw = data[:raw]
84
+
85
+ stacks = []
86
+ max_y = 0
87
+ while len = raw.shift
88
+ max_y = len if len > max_y
89
+ stacks << raw.slice!(0, len+1)
90
+ end
91
+ max_x = stacks.inject(0){ |sum, (*stack, weight)| sum + weight }
92
+
93
+ f.puts 'flamegraph(['
94
+ max_y.times do |y|
95
+ row_prev = nil
96
+ row_width = 0
97
+ x = 0
98
+
99
+ stacks.each do |*stack, weight|
100
+ cell = stack[y]
101
+
102
+ if cell.nil?
103
+ if row_prev
104
+ flamegraph_row(x - row_width, y, row_width, row_prev)
105
+ end
106
+
107
+ row_prev = nil
108
+ x += weight
109
+ next
110
+ end
111
+
112
+ if row_prev.nil? # start new row with this cell
113
+ row_width = weight
114
+ row_prev = cell
115
+ x += weight
116
+
117
+ elsif row_prev == cell # grow current row along x-axis
118
+ row_width += weight
119
+ x += weight
120
+
121
+ else # end current row and start new row
122
+ flamegraph_row(x - row_width, y, row_width, row_prev)
123
+ x += weight
124
+ row_prev = cell
125
+ row_width = weight
126
+ end
127
+
128
+ row_prev = cell
129
+ end
130
+
131
+ if row_prev
132
+ next if skip_common && row_width == max_x
133
+
134
+ flamegraph_row(x - row_width, y, row_width, row_prev)
135
+ end
136
+ end
137
+ f.puts '])'
138
+ end
139
+
140
+ def flamegraph_row(x, y, weight, addr)
141
+ frame = frames[addr]
142
+ print ',' if @rows_started
143
+ @rows_started = true
144
+ f.puts %{{"x":#{x},"y":#{y},"width":#{weight},"frame_id":#{addr},"frame":#{frame[:name].dump},"file":#{frame[:file].dump}}}
145
+ end
146
+
82
147
  def print_graphviz(filter = nil, f = STDOUT)
83
148
  if filter
84
149
  mark_stack = []
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'
3
+ s.version = '0.2.4'
4
4
  s.homepage = 'http://github.com/tmm1/stackprof'
5
5
 
6
6
  s.authors = 'Aman Gupta'
@@ -11,14 +11,14 @@ Gem::Specification.new do |s|
11
11
 
12
12
  s.bindir = 'bin'
13
13
  s.executables << 'stackprof'
14
- s.executables << 'stackprof-flamegraph'
15
- s.executables << 'stackprof-gprof2dot'
14
+ s.executables << 'stackprof-flamegraph.pl'
15
+ s.executables << 'stackprof-gprof2dot.py'
16
16
 
17
17
  s.summary = 'sampling callstack-profiler for ruby 2.1+'
18
18
  s.description = 'stackprof is a fast sampling profiler for ruby code, with cpu, wallclock and object allocation samplers.'
19
19
 
20
20
  s.license = 'MIT'
21
21
 
22
- s.add_development_dependency 'rake-compiler'
23
- s.add_development_dependency 'mocha'
22
+ s.add_development_dependency 'rake-compiler', '~> 0.9'
23
+ s.add_development_dependency 'mocha', '~> 0.14'
24
24
  end
metadata CHANGED
@@ -1,50 +1,50 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stackprof
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aman Gupta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-24 00:00:00.000000000 Z
11
+ date: 2014-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '0.9'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '0.9'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: mocha
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '0.14'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '0.14'
41
41
  description: stackprof is a fast sampling profiler for ruby code, with cpu, wallclock
42
42
  and object allocation samplers.
43
43
  email: aman@tmm1.net
44
44
  executables:
45
45
  - stackprof
46
- - stackprof-flamegraph
47
- - stackprof-gprof2dot
46
+ - stackprof-flamegraph.pl
47
+ - stackprof-gprof2dot.py
48
48
  extensions:
49
49
  - ext/extconf.rb
50
50
  extra_rdoc_files: []
@@ -55,8 +55,8 @@ files:
55
55
  - README.md
56
56
  - Rakefile
57
57
  - bin/stackprof
58
- - bin/stackprof-flamegraph
59
- - bin/stackprof-gprof2dot
58
+ - bin/stackprof-flamegraph.pl
59
+ - bin/stackprof-gprof2dot.py
60
60
  - ext/extconf.rb
61
61
  - ext/stackprof.c
62
62
  - lib/stackprof/middleware.rb