gc_tracer 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +27 -1
- data/bin/objspace_recorder_convert.rb +34 -1
- data/ext/gc_tracer/gc_tracer.c +111 -45
- data/gc_tracer.gemspec +1 -1
- data/lib/gc_tracer/version.rb +2 -2
- data/lib/gc_tracer/viewer.html.erb +80 -0
- data/spec/gc_tracer_spec.rb +2 -0
- metadata +3 -3
- data/public/viewer.html +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ad7a07f89e6474de38728bba4e183173133c727
|
4
|
+
data.tar.gz: 1be0ad94f2746c7fee8880725827a386cde7ef75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3410157161b046b7870da5caae27c0be60968c825f38908a18907e7ee8941effa28240e32f05ce769a5ad459064ec9784b0ec42746c989d486b2b2a65805c45
|
7
|
+
data.tar.gz: a168bf9cd168fbc5d4c80eacfb44d4b7dcc52053f5ba89bf821bb7a372dd874e9b06ed65073c21a02eda04b34321d6ee484a06513d5d42dac8b4cb95b053200c
|
data/README.md
CHANGED
@@ -72,10 +72,36 @@ If you have netpbm package and pnmtopng command,
|
|
72
72
|
bin/objspace_recorder_convert.rb converts all ppm images into png files.
|
73
73
|
Converted png images stored into dirname/png/.
|
74
74
|
|
75
|
-
|
75
|
+
To view converted images, "dirname/viewer.html" is created.
|
76
|
+
You can view all converted png images with "dirname/viewer.html" file in animation.
|
76
77
|
|
77
78
|
This feature is supported only latest Ruby versions (2.2, and later).
|
78
79
|
|
80
|
+
#### Examaple
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
require 'gc_tracer'
|
84
|
+
GC::Tracer.start_objspace_recording("objspace_recorded_type_example", :type){
|
85
|
+
n = 1_000
|
86
|
+
m = 10_000
|
87
|
+
|
88
|
+
n.times{
|
89
|
+
ary = []
|
90
|
+
m.times{
|
91
|
+
ary << ''
|
92
|
+
}
|
93
|
+
}
|
94
|
+
}
|
95
|
+
```
|
96
|
+
|
97
|
+
This program takes all snapshot of type information at each GC events.
|
98
|
+
|
99
|
+
- :age (default) - take snapshots of age information (empty/young/old/shady)
|
100
|
+
- :type - take snapshots of type information (T_???)
|
101
|
+
|
102
|
+
You can see an [age example] (http://www.atdot.net/~ko1/gc_tracer/objspace_recorded_age_example/viewer.html) and
|
103
|
+
a [type example] (http://www.atdot.net/~ko1/gc_tracer/objspace_recorded_type_example/viewer.html).
|
104
|
+
|
79
105
|
## Contributing
|
80
106
|
|
81
107
|
1. Fork it ( http://github.com/ko1/gc_tracer/fork )
|
@@ -1,7 +1,40 @@
|
|
1
|
-
|
1
|
+
require 'erb'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
dir = ARGV.shift || 'objspace_records'
|
2
5
|
Dir.mkdir("#{dir}/png") unless File.directory?("#{dir}/png")
|
6
|
+
first_gc_count = nil
|
7
|
+
last_gc_count = 0
|
3
8
|
|
4
9
|
Dir.glob("#{dir}/ppm/*"){|file|
|
5
10
|
cmd = "pnmtopng #{file} > #{dir}/png/#{File.basename(file)}.png"
|
6
11
|
system(cmd)
|
12
|
+
if /(\d{8})\.\d/ =~ file
|
13
|
+
c = $1.to_i
|
14
|
+
first_gc_count = c if first_gc_count == nil || first_gc_count > c
|
15
|
+
last_gc_count = c if last_gc_count < c
|
16
|
+
end
|
17
|
+
}
|
18
|
+
|
19
|
+
color_description = {}
|
20
|
+
File.read("#{dir}/color_description.txt").each_line{|line|
|
21
|
+
desc, color = *line.chomp.split(/\t/)
|
22
|
+
color_description[desc] = color
|
23
|
+
}
|
24
|
+
|
25
|
+
color_description_js = color_description.map{|(k, v)|
|
26
|
+
" {desc: \"#{k}\", \t\"color\": \"#{v}\"}"
|
27
|
+
}.join(",\n")
|
28
|
+
|
29
|
+
html_file = "#{dir}/viewer.html"
|
30
|
+
|
31
|
+
open(html_file, 'w'){|f|
|
32
|
+
f.puts ERB.new(File.read(File.join(__dir__, "../lib/gc_tracer/viewer.html.erb"))).result(binding)
|
7
33
|
}
|
34
|
+
|
35
|
+
unless File.exist?("#{dir}/jquery-2.1.0.min.js")
|
36
|
+
FileUtils.cp(File.join(__dir__, "../public/jquery-2.1.0.min.js"), "#{dir}/jquery-2.1.0.min.js")
|
37
|
+
end
|
38
|
+
|
39
|
+
puts "Success: see #{html_file}"
|
40
|
+
|
data/ext/gc_tracer/gc_tracer.c
CHANGED
@@ -478,84 +478,127 @@ struct objspace_recording_data {
|
|
478
478
|
int width, height;
|
479
479
|
};
|
480
480
|
|
481
|
+
struct picker_description {
|
482
|
+
const char *name;
|
483
|
+
const int color;
|
484
|
+
};
|
485
|
+
|
481
486
|
static void
|
482
487
|
set_color(unsigned char *buff, int color)
|
483
488
|
{
|
484
|
-
buff[
|
489
|
+
buff[2] = (color >> 0) & 0xff;
|
485
490
|
buff[1] = (color >> 8) & 0xff;
|
486
|
-
buff[
|
487
|
-
}
|
491
|
+
buff[0] = (color >> 16) & 0xff;
|
492
|
+
}
|
493
|
+
|
494
|
+
const int categorical_color10_colors[] = {
|
495
|
+
0x1f77b4,
|
496
|
+
0xff7f0e,
|
497
|
+
0x2ca02c,
|
498
|
+
0xd62728,
|
499
|
+
0x9467bd,
|
500
|
+
0x8c564b,
|
501
|
+
0xe377c2,
|
502
|
+
0x7f7f7f,
|
503
|
+
0xbcbd22,
|
504
|
+
0x17becf
|
505
|
+
};
|
488
506
|
|
489
507
|
static int
|
490
508
|
categorical_color10(int n)
|
491
509
|
{
|
492
|
-
const int colors[] = {
|
493
|
-
0x1f77b4,
|
494
|
-
0xff7f0e,
|
495
|
-
0x2ca02c,
|
496
|
-
0xd62728,
|
497
|
-
0x9467bd,
|
498
|
-
0x8c564b,
|
499
|
-
0xe377c2,
|
500
|
-
0x7f7f7f,
|
501
|
-
0xbcbd22,
|
502
|
-
0x17becf};
|
503
510
|
assert(n < 10);
|
504
|
-
return
|
505
|
-
}
|
511
|
+
return categorical_color10_colors[n];
|
512
|
+
}
|
513
|
+
|
514
|
+
const int categorical_color20_colors[] = {
|
515
|
+
0x1f77b4,
|
516
|
+
0xaec7e8,
|
517
|
+
0xff7f0e,
|
518
|
+
0xffbb78,
|
519
|
+
0x2ca02c,
|
520
|
+
0x98df8a,
|
521
|
+
0xd62728,
|
522
|
+
0xff9896,
|
523
|
+
0x9467bd,
|
524
|
+
0xc5b0d5,
|
525
|
+
0x8c564b,
|
526
|
+
0xc49c94,
|
527
|
+
0xe377c2,
|
528
|
+
0xf7b6d2,
|
529
|
+
0x7f7f7f,
|
530
|
+
0xc7c7c7,
|
531
|
+
0xbcbd22,
|
532
|
+
0xdbdb8d,
|
533
|
+
0x17becf,
|
534
|
+
0x9edae5,
|
535
|
+
};
|
506
536
|
|
507
537
|
static int
|
508
538
|
categorical_color20(int n)
|
509
539
|
{
|
510
|
-
const int colors[] = {
|
511
|
-
0x1f77b4,
|
512
|
-
0xaec7e8,
|
513
|
-
0xff7f0e,
|
514
|
-
0xffbb78,
|
515
|
-
0x2ca02c,
|
516
|
-
0x98df8a,
|
517
|
-
0xd62728,
|
518
|
-
0xff9896,
|
519
|
-
0x9467bd,
|
520
|
-
0xc5b0d5,
|
521
|
-
0x8c564b,
|
522
|
-
0xc49c94,
|
523
|
-
0xe377c2,
|
524
|
-
0xf7b6d2,
|
525
|
-
0x7f7f7f,
|
526
|
-
0xc7c7c7,
|
527
|
-
0xbcbd22,
|
528
|
-
0xdbdb8d,
|
529
|
-
0x17becf,
|
530
|
-
0x9edae5,
|
531
|
-
};
|
532
540
|
assert(n < 20);
|
533
|
-
return
|
541
|
+
return categorical_color20_colors[n];
|
534
542
|
}
|
535
543
|
|
544
|
+
static struct picker_description object_age_picker_description[] = {
|
545
|
+
{"empty slot", 0},
|
546
|
+
{"old slot", 0x1f77b4},
|
547
|
+
{"young slot", 0xff7f0e},
|
548
|
+
{"shady slot", 0x2ca02c}
|
549
|
+
};
|
550
|
+
|
536
551
|
static int
|
537
552
|
object_age_picker(VALUE v) {
|
538
553
|
if (RB_TYPE_P(v, T_NONE)) {
|
539
|
-
return
|
554
|
+
return 0;
|
540
555
|
}
|
541
556
|
else {
|
542
557
|
if (OBJ_PROMOTED(v)) {
|
543
558
|
/* old */
|
544
|
-
return categorical_color10(
|
559
|
+
return categorical_color10(0);
|
545
560
|
}
|
546
561
|
else {
|
547
|
-
|
548
|
-
|
562
|
+
if (OBJ_WB_PROTECTED(v)) {
|
563
|
+
/* young */
|
564
|
+
return categorical_color10(1);
|
565
|
+
}
|
566
|
+
else {
|
567
|
+
return categorical_color10(2);
|
568
|
+
}
|
549
569
|
}
|
550
570
|
}
|
551
571
|
}
|
552
572
|
|
573
|
+
static struct picker_description object_type_picker_description[] = {
|
574
|
+
{"RUBY_T_NONE", 0},
|
575
|
+
{"RUBY_T_OBJECT", 0x1f77b4},
|
576
|
+
{"RUBY_T_CLASS", 0xaec7e8},
|
577
|
+
{"RUBY_T_MODULE", 0xff7f0e},
|
578
|
+
{"RUBY_T_FLOAT", 0xffbb78},
|
579
|
+
{"RUBY_T_STRING", 0x2ca02c},
|
580
|
+
{"RUBY_T_REGEXP", 0x98df8a},
|
581
|
+
{"RUBY_T_ARRAY", 0xd62728},
|
582
|
+
{"RUBY_T_HASH", 0xff9896},
|
583
|
+
{"RUBY_T_STRUCT", 0x9467bd},
|
584
|
+
{"RUBY_T_BIGNUM", 0xc5b0d5},
|
585
|
+
{"RUBY_T_FILE", 0x8c564b},
|
586
|
+
{"RUBY_T_DATA", 0xc49c94},
|
587
|
+
{"RUBY_T_MATCH", 0xe377c2},
|
588
|
+
{"RUBY_T_COMPLEX", 0xf7b6d2},
|
589
|
+
{"RUBY_T_RATIONAL", 0x7f7f7f},
|
590
|
+
{"RUBY_T_NODE", 0xc7c7c7},
|
591
|
+
{"RUBY_T_ICLASS", 0xbcbd22},
|
592
|
+
{"RUBY_T_ZOMBIE", 0xdbdb8d},
|
593
|
+
};
|
594
|
+
|
553
595
|
static int
|
554
596
|
object_type_picker(VALUE v) {
|
555
597
|
int type = BUILTIN_TYPE(v);
|
556
598
|
int color = 0;
|
557
599
|
switch (type) {
|
558
600
|
case RUBY_T_NONE:
|
601
|
+
return 0;
|
559
602
|
case RUBY_T_OBJECT:
|
560
603
|
case RUBY_T_CLASS:
|
561
604
|
case RUBY_T_MODULE:
|
@@ -571,12 +614,12 @@ object_type_picker(VALUE v) {
|
|
571
614
|
case RUBY_T_MATCH:
|
572
615
|
case RUBY_T_COMPLEX:
|
573
616
|
case RUBY_T_RATIONAL: /* 0x0f */
|
574
|
-
color = type;
|
617
|
+
color = type - 1;
|
575
618
|
break;
|
576
619
|
case RUBY_T_NODE:
|
577
620
|
case RUBY_T_ICLASS:
|
578
621
|
case RUBY_T_ZOMBIE:
|
579
|
-
color = type -
|
622
|
+
color = type - 12;
|
580
623
|
break;
|
581
624
|
default:
|
582
625
|
rb_bug("object_type_picker: unreachable (type: %d)", type);
|
@@ -650,6 +693,27 @@ gc_tracer_stop_objspace_recording(VALUE self)
|
|
650
693
|
return Qnil;
|
651
694
|
}
|
652
695
|
|
696
|
+
static void
|
697
|
+
puts_color_description(VALUE dirname, struct picker_description desc[], int n)
|
698
|
+
{
|
699
|
+
char buff[0x200];
|
700
|
+
FILE *fp;
|
701
|
+
int i;
|
702
|
+
|
703
|
+
snprintf(buff, 0x200, "%s/color_description.txt", RSTRING_PTR(dirname));
|
704
|
+
|
705
|
+
if ((fp = fopen(buff, "w")) == NULL) {
|
706
|
+
rb_raise(rb_eRuntimeError, "puts_color_description: failed to open file");
|
707
|
+
}
|
708
|
+
|
709
|
+
for (i=0; i<n; i++) {
|
710
|
+
fprintf(fp, "%s\t#%06x\n", desc[i].name, desc[i].color);
|
711
|
+
}
|
712
|
+
|
713
|
+
fclose(fp);
|
714
|
+
}
|
715
|
+
|
716
|
+
|
653
717
|
static VALUE
|
654
718
|
gc_tracer_start_objspace_recording(int argc, VALUE *argv, VALUE self)
|
655
719
|
{
|
@@ -679,9 +743,11 @@ gc_tracer_start_objspace_recording(int argc, VALUE *argv, VALUE self)
|
|
679
743
|
|
680
744
|
if (picker_type == ID2SYM(rb_intern("age"))) {
|
681
745
|
objspace_recorder_color_picker = object_age_picker;
|
746
|
+
puts_color_description(dirname, &object_age_picker_description[0], sizeof(object_age_picker_description) / sizeof(struct picker_description));
|
682
747
|
}
|
683
748
|
else if (picker_type == ID2SYM(rb_intern("type"))) {
|
684
749
|
objspace_recorder_color_picker = object_type_picker;
|
750
|
+
puts_color_description(dirname, &object_type_picker_description[0], sizeof(object_type_picker_description) / sizeof(struct picker_description));
|
685
751
|
}
|
686
752
|
else {
|
687
753
|
rb_raise(rb_eArgError, "unsupported picker type: %s", rb_id2name(SYM2ID(picker_type)));
|
data/gc_tracer.gemspec
CHANGED
@@ -5,7 +5,7 @@ require 'gc_tracer/version'
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "gc_tracer"
|
8
|
-
spec.version =
|
8
|
+
spec.version = GC::Tracer::VERSION
|
9
9
|
spec.authors = ["Koichi Sasada"]
|
10
10
|
spec.email = ["ko1@atdot.net"]
|
11
11
|
spec.summary = %q{gc_tracer gem adds GC::Tracer module.}
|
data/lib/gc_tracer/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION = "0.1.
|
1
|
+
module GC::Tracer
|
2
|
+
VERSION = "0.1.2"
|
3
3
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta charset="utf-8">
|
4
|
+
<script src="./jquery-2.1.0.min.js"></script>
|
5
|
+
|
6
|
+
<script>
|
7
|
+
|
8
|
+
function startAnimation(){
|
9
|
+
var start = Number($("#start").val());
|
10
|
+
var end = Number($("#end").val());
|
11
|
+
var sleep_time = Number($("#wait").val());
|
12
|
+
var i = start, j = 0;
|
13
|
+
|
14
|
+
var timer_func = function(){
|
15
|
+
image_path = "./png/" + ("00000000"+i).slice(-8) + "." + j + ".ppm.png";
|
16
|
+
$("#view").attr({
|
17
|
+
src: image_path
|
18
|
+
});
|
19
|
+
$("#status").text(image_path);
|
20
|
+
|
21
|
+
if (j < 2) {
|
22
|
+
j += 1;
|
23
|
+
}
|
24
|
+
else {
|
25
|
+
if (i < end) {
|
26
|
+
i += 1;
|
27
|
+
j = 0;
|
28
|
+
}
|
29
|
+
else{
|
30
|
+
return false;
|
31
|
+
}
|
32
|
+
}
|
33
|
+
setTimeout(timer_func, sleep_time);
|
34
|
+
}
|
35
|
+
|
36
|
+
timer_func();
|
37
|
+
}
|
38
|
+
|
39
|
+
var color_description = [
|
40
|
+
<%= color_description_js %>
|
41
|
+
];
|
42
|
+
|
43
|
+
function reloadConfig() {
|
44
|
+
$("#color_desc_table").remove();
|
45
|
+
$("#config").after("<table id='color_desc_table'></table>");
|
46
|
+
|
47
|
+
for (var i = 0; i < color_description.length; i++) {
|
48
|
+
$("#color_desc_table").before(
|
49
|
+
"<tr>" +
|
50
|
+
"<td style='background-color: " + color_description[i].color + "' hspace='1em' width='100px'>" +
|
51
|
+
"<td> " + color_description[i].desc + "</td>" +
|
52
|
+
"</tr>");
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
$(function(){
|
57
|
+
reloadConfig();
|
58
|
+
$("#submit").click(
|
59
|
+
function(){
|
60
|
+
startAnimation();
|
61
|
+
});
|
62
|
+
});
|
63
|
+
|
64
|
+
</script>
|
65
|
+
</head>
|
66
|
+
<body>
|
67
|
+
<div>
|
68
|
+
<div id='config'>
|
69
|
+
start: <input type='text' id='start' value='<%= first_gc_count %>' readonly>,
|
70
|
+
end: <input type='text' id='end' value='<%= last_gc_count %>' readonly>
|
71
|
+
</div>
|
72
|
+
</div>
|
73
|
+
<p>
|
74
|
+
<input type='submit' id='submit' value='start!'>
|
75
|
+
wait: <input type='text' id='wait' value='100'>
|
76
|
+
<span id='status'></span>
|
77
|
+
</p>
|
78
|
+
<img id='view'>
|
79
|
+
</body>
|
80
|
+
</html>
|
data/spec/gc_tracer_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gc_tracer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Koichi Sasada
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -86,8 +86,8 @@ files:
|
|
86
86
|
- gc_tracer.gemspec
|
87
87
|
- lib/gc_tracer.rb
|
88
88
|
- lib/gc_tracer/version.rb
|
89
|
+
- lib/gc_tracer/viewer.html.erb
|
89
90
|
- public/jquery-2.1.0.min.js
|
90
|
-
- public/viewer.html
|
91
91
|
- spec/gc_tracer_spec.rb
|
92
92
|
- spec/spec_helper.rb
|
93
93
|
homepage: https://github.com/ko1/gc_tracer
|
data/public/viewer.html
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
<html>
|
2
|
-
<head>
|
3
|
-
<meta charset="utf-8">
|
4
|
-
<script src="./jquery-2.1.0.min.js"></script>
|
5
|
-
|
6
|
-
<script>
|
7
|
-
|
8
|
-
function startAnimation(){
|
9
|
-
var dir = $("#dir").val();
|
10
|
-
var start = Number($("#start").val());
|
11
|
-
var end = Number($("#end").val());
|
12
|
-
var sleep_time = Number($("#wait").val());
|
13
|
-
var i = start, j = 0;
|
14
|
-
|
15
|
-
var timer_func = function(){
|
16
|
-
image_path = dir + "/png/" + ("00000000"+i).slice(-8) + "." + j + ".ppm.png";
|
17
|
-
$("#view").attr({
|
18
|
-
src: image_path
|
19
|
-
});
|
20
|
-
$("#status").text(image_path);
|
21
|
-
|
22
|
-
if (j < 2) {
|
23
|
-
j += 1;
|
24
|
-
}
|
25
|
-
else {
|
26
|
-
if (i < end) {
|
27
|
-
i += 1;
|
28
|
-
j = 0;
|
29
|
-
}
|
30
|
-
else{
|
31
|
-
return false;
|
32
|
-
}
|
33
|
-
}
|
34
|
-
setTimeout(timer_func, sleep_time);
|
35
|
-
}
|
36
|
-
|
37
|
-
timer_func();
|
38
|
-
}
|
39
|
-
|
40
|
-
$(function(){
|
41
|
-
$("#submit").click(
|
42
|
-
function(){
|
43
|
-
startAnimation();
|
44
|
-
});
|
45
|
-
});
|
46
|
-
|
47
|
-
</script>
|
48
|
-
</head>
|
49
|
-
<body>
|
50
|
-
<p>
|
51
|
-
dir: <input type='text' id='dir' value='test'>,
|
52
|
-
start: <input type='text' id='start' value='5'>,
|
53
|
-
end: <input type='text' id='end' value='100'>
|
54
|
-
wait: <input type='text' id='wait' value='300'>,
|
55
|
-
</p>
|
56
|
-
<p>
|
57
|
-
<input type='submit' id='submit' value='start!'><span id='status'></span>
|
58
|
-
</p>
|
59
|
-
<img id='view'>
|
60
|
-
</body>
|
61
|
-
</html>
|