gc_tracer 0.1.1 → 0.1.2
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 +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>
|