stackprof 0.2.3 → 0.2.4

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: 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