gc_tracer 1.0.0 → 1.0.1
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 +2 -2
- data/ext/gc_tracer/extconf.rb +4 -0
- data/ext/gc_tracer/gc_tracer.c +0 -868
- data/lib/gc_tracer/version.rb +1 -1
- data/lib/gc_tracer.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2fd802669a217a30e27978a50adfca22bac7bafd
|
4
|
+
data.tar.gz: 02fbf3dbdfe74e7b65a48a0b0c0644e703fb9eda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a2e58aa4a810b2e6a68d4391e1b9e8a23bba9f4e7e346c880af0c79c04da694e5390371c73985b2695af6f5d3c604059424fab5327db886dac999ece4daad86
|
7
|
+
data.tar.gz: 808239ff54643a9b65ccf516db5d3ca24abd26ca7924a260b6e541a6f2394c34fec5b94c1431bf56123617f7f51b718dc120d193a1c356bec33e39ef55f5e29f
|
data/README.md
CHANGED
@@ -95,8 +95,8 @@ Above example means that no details information are not needed. Default
|
|
95
95
|
setting is "gc_stat: true, gc_latest_gc_info: true, rusage: false".
|
96
96
|
|
97
97
|
You can specify tick (time stamp) type with keyword parameter
|
98
|
-
"tick_type". You can choose one of the tick type in :hw_counter, :time
|
99
|
-
|
98
|
+
"tick_type". You can choose one of the tick type in :hw_counter, :time
|
99
|
+
and :nano_time.
|
100
100
|
|
101
101
|
See lib/gc_tracer.rb for more details.
|
102
102
|
|
data/ext/gc_tracer/extconf.rb
CHANGED
@@ -38,6 +38,10 @@ if have_header('sys/time.h') && have_header('sys/resource.h') && have_func('getr
|
|
38
38
|
}
|
39
39
|
end
|
40
40
|
|
41
|
+
if have_header('time.h')
|
42
|
+
have_func('clock_gettime')
|
43
|
+
end
|
44
|
+
|
41
45
|
open("gc_tracer.h", 'w'){|f|
|
42
46
|
f.puts '#include "ruby/ruby.h"'
|
43
47
|
f.puts "static VALUE sym_gc_stat[#{GC.stat.keys.size}];"
|
data/ext/gc_tracer/gc_tracer.c
CHANGED
@@ -5,874 +5,6 @@
|
|
5
5
|
* created at Wed Feb 26 10:52:59 2014.
|
6
6
|
*/
|
7
7
|
|
8
|
-
#if 0
|
9
|
-
#include "ruby/ruby.h"
|
10
|
-
#include "ruby/debug.h"
|
11
|
-
#include "gc_tracer.h"
|
12
|
-
#include <stdio.h>
|
13
|
-
#include <assert.h>
|
14
|
-
|
15
|
-
#ifdef HAVE_RB_OBJ_GC_FLAGS
|
16
|
-
size_t rb_obj_gc_flags(VALUE obj, ID* flags, size_t max);
|
17
|
-
static ID id_young;
|
18
|
-
#endif
|
19
|
-
|
20
|
-
struct gc_hooks {
|
21
|
-
VALUE hooks[3];
|
22
|
-
VALUE enabled;
|
23
|
-
void (*funcs[3])(FILE *, void *data, int event_index);
|
24
|
-
void *args[3];
|
25
|
-
void *data;
|
26
|
-
FILE *out;
|
27
|
-
};
|
28
|
-
|
29
|
-
static const char * const event_names[] = {
|
30
|
-
"gc_start",
|
31
|
-
"gc_end_m",
|
32
|
-
"gc_end_s",
|
33
|
-
"gc_enter",
|
34
|
-
"gc_exit_"
|
35
|
-
};
|
36
|
-
|
37
|
-
/* common funcs */
|
38
|
-
|
39
|
-
static void
|
40
|
-
gc_start_i(VALUE tpval, void *data)
|
41
|
-
{
|
42
|
-
struct gc_hooks *hooks = (struct gc_hooks *)data;
|
43
|
-
(*hooks->funcs[0])(hooks->out, hooks->args[0], 0);
|
44
|
-
}
|
45
|
-
|
46
|
-
static void
|
47
|
-
gc_end_mark_i(VALUE tpval, void *data)
|
48
|
-
{
|
49
|
-
struct gc_hooks *hooks = (struct gc_hooks *)data;
|
50
|
-
(*hooks->funcs[1])(hooks->out, hooks->args[1], 1);
|
51
|
-
}
|
52
|
-
|
53
|
-
static void
|
54
|
-
gc_end_sweep_i(VALUE tpval, void *data)
|
55
|
-
{
|
56
|
-
struct gc_hooks *hooks = (struct gc_hooks *)data;
|
57
|
-
(*hooks->funcs[2])(hooks->out, hooks->args[2], 2);
|
58
|
-
}
|
59
|
-
|
60
|
-
static void
|
61
|
-
create_gc_hooks(struct gc_hooks *hooks)
|
62
|
-
{
|
63
|
-
if (hooks->hooks[0] == 0) {
|
64
|
-
int i;
|
65
|
-
|
66
|
-
hooks->hooks[0] = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_GC_START, gc_start_i, hooks);
|
67
|
-
hooks->hooks[1] = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_GC_END_MARK, gc_end_mark_i, hooks);
|
68
|
-
hooks->hooks[2] = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_GC_END_SWEEP, gc_end_sweep_i, hooks);
|
69
|
-
|
70
|
-
/* mark for GC */
|
71
|
-
for (i=0; i<3; i++) rb_gc_register_mark_object(hooks->hooks[i]);
|
72
|
-
}
|
73
|
-
}
|
74
|
-
|
75
|
-
static void
|
76
|
-
start_gc_hooks(struct gc_hooks *hooks)
|
77
|
-
{
|
78
|
-
if (hooks->enabled == Qfalse) {
|
79
|
-
int i;
|
80
|
-
hooks->enabled = Qtrue;
|
81
|
-
for (i=0; i<3; i++) {
|
82
|
-
rb_tracepoint_enable(hooks->hooks[i]);
|
83
|
-
}
|
84
|
-
}
|
85
|
-
}
|
86
|
-
|
87
|
-
static void
|
88
|
-
stop_gc_hooks(struct gc_hooks *hooks)
|
89
|
-
{
|
90
|
-
hooks->enabled = Qfalse;
|
91
|
-
|
92
|
-
if (hooks->hooks[0] != 0) {
|
93
|
-
rb_tracepoint_disable(hooks->hooks[0]);
|
94
|
-
rb_tracepoint_disable(hooks->hooks[1]);
|
95
|
-
rb_tracepoint_disable(hooks->hooks[2]);
|
96
|
-
}
|
97
|
-
}
|
98
|
-
|
99
|
-
/* logger */
|
100
|
-
|
101
|
-
struct gc_hooks logger;
|
102
|
-
static VALUE gc_trace_items, gc_trace_items_types;
|
103
|
-
|
104
|
-
#ifdef HAVE_GETRUSAGE
|
105
|
-
#include <sys/time.h>
|
106
|
-
#include <sys/resource.h>
|
107
|
-
#endif
|
108
|
-
|
109
|
-
enum gc_key_type {
|
110
|
-
GC_STAT_KEY,
|
111
|
-
GC_LATEST_GC_INFO_KEY,
|
112
|
-
#ifdef HAVE_GETRUSAGE
|
113
|
-
GC_RUSAGE_TIMEVAL_KEY,
|
114
|
-
GC_RUSAGE_KEY,
|
115
|
-
#endif
|
116
|
-
};
|
117
|
-
|
118
|
-
|
119
|
-
static void
|
120
|
-
out_header(FILE *out, VALUE items, int need_type)
|
121
|
-
{
|
122
|
-
int i;
|
123
|
-
|
124
|
-
out_str(out, "tick");
|
125
|
-
if (need_type) out_str(out, "type");
|
126
|
-
|
127
|
-
for (i=0; i<RARRAY_LEN(items); i++) {
|
128
|
-
out_str(out, rb_id2name(SYM2ID(RARRAY_AREF(items, i))));
|
129
|
-
}
|
130
|
-
|
131
|
-
out_terminate(out);
|
132
|
-
}
|
133
|
-
|
134
|
-
#ifdef HAVE_GETRUSAGE
|
135
|
-
struct rusage_cache {
|
136
|
-
int cached;
|
137
|
-
struct rusage usage;
|
138
|
-
};
|
139
|
-
|
140
|
-
static void
|
141
|
-
getursage_fill(struct rusage_cache *rusage_cache)
|
142
|
-
{
|
143
|
-
if (!rusage_cache->cached) {
|
144
|
-
rusage_cache->cached = 1;
|
145
|
-
getrusage(RUSAGE_SELF, &rusage_cache->usage);
|
146
|
-
}
|
147
|
-
}
|
148
|
-
|
149
|
-
static double
|
150
|
-
timeval2double(struct timeval *tv)
|
151
|
-
{
|
152
|
-
return tv->tv_sec * 1000000 + tv->tv_usec;
|
153
|
-
}
|
154
|
-
|
155
|
-
static double
|
156
|
-
getrusage_timeval(VALUE sym, struct rusage_cache *rusage_cache)
|
157
|
-
{
|
158
|
-
getursage_fill(rusage_cache);
|
159
|
-
|
160
|
-
if (sym == sym_rusage_timeval[0]) {
|
161
|
-
return timeval2double(&rusage_cache->usage.ru_utime);
|
162
|
-
}
|
163
|
-
if (sym == sym_rusage_timeval[1]) {
|
164
|
-
return timeval2double(&rusage_cache->usage.ru_stime);
|
165
|
-
}
|
166
|
-
|
167
|
-
rb_raise(rb_eRuntimeError, "getrusage_timeval: unknown symbol");
|
168
|
-
return 0;
|
169
|
-
}
|
170
|
-
|
171
|
-
static size_t
|
172
|
-
getrusage_sizet(VALUE sym, struct rusage_cache *rusage_cache)
|
173
|
-
{
|
174
|
-
int i = 0;
|
175
|
-
|
176
|
-
getursage_fill(rusage_cache);
|
177
|
-
|
178
|
-
#if HAVE_ST_RU_MAXRSS
|
179
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_maxrss;
|
180
|
-
#endif
|
181
|
-
#if HAVE_ST_RU_IXRSS
|
182
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_ixrss;
|
183
|
-
#endif
|
184
|
-
#if HAVE_ST_RU_IDRSS
|
185
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_idrss;
|
186
|
-
#endif
|
187
|
-
#if HAVE_ST_RU_ISRSS
|
188
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_isrss;
|
189
|
-
#endif
|
190
|
-
#if HAVE_ST_RU_MINFLT
|
191
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_minflt;
|
192
|
-
#endif
|
193
|
-
#if HAVE_ST_RU_MAJFLT
|
194
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_majflt;
|
195
|
-
#endif
|
196
|
-
#if HAVE_ST_RU_NSWAP
|
197
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_nswap;
|
198
|
-
#endif
|
199
|
-
#if HAVE_ST_RU_INBLOCK
|
200
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_inblock;
|
201
|
-
#endif
|
202
|
-
#if HAVE_ST_RU_OUBLOCK
|
203
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_oublock;
|
204
|
-
#endif
|
205
|
-
#if HAVE_ST_RU_MSGSND
|
206
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_msgsnd;
|
207
|
-
#endif
|
208
|
-
#if HAVE_ST_RU_MSGRCV
|
209
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_msgrcv;
|
210
|
-
#endif
|
211
|
-
#if HAVE_ST_RU_NSIGNALS
|
212
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_nsignals;
|
213
|
-
#endif
|
214
|
-
#if HAVE_ST_RU_NVCSW
|
215
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_nvcsw;
|
216
|
-
#endif
|
217
|
-
#if HAVE_ST_RU_NIVCSW
|
218
|
-
if (sym == sym_rusage[i++]) return rusage_cache->usage.ru_nivcsw;
|
219
|
-
#endif
|
220
|
-
|
221
|
-
rb_raise(rb_eRuntimeError, "getrusage_sizet: unknown symbol");
|
222
|
-
return 0;
|
223
|
-
}
|
224
|
-
#endif
|
225
|
-
|
226
|
-
static void
|
227
|
-
out_items(FILE *out, VALUE items, VALUE items_types)
|
228
|
-
{
|
229
|
-
int i;
|
230
|
-
#ifdef HAVE_GETRUSAGE
|
231
|
-
struct rusage_cache rusage_cache = {0};
|
232
|
-
#endif
|
233
|
-
|
234
|
-
for (i=0; i<RARRAY_LEN(items); i++) {
|
235
|
-
enum gc_key_type type = FIX2INT(RARRAY_AREF(items_types, i));
|
236
|
-
VALUE sym = RARRAY_AREF(items, i);
|
237
|
-
|
238
|
-
switch (type) {
|
239
|
-
case GC_STAT_KEY:
|
240
|
-
out_sizet(out, rb_gc_stat(sym));
|
241
|
-
break;
|
242
|
-
case GC_LATEST_GC_INFO_KEY:
|
243
|
-
out_obj(out, rb_gc_latest_gc_info(sym));
|
244
|
-
break;
|
245
|
-
#ifdef HAVE_GETRUSAGE
|
246
|
-
case GC_RUSAGE_TIMEVAL_KEY:
|
247
|
-
out_sizet(out, (size_t)getrusage_timeval(sym, &rusage_cache));
|
248
|
-
break;
|
249
|
-
case GC_RUSAGE_KEY:
|
250
|
-
out_sizet(out, getrusage_sizet(sym, &rusage_cache));
|
251
|
-
break;
|
252
|
-
#endif
|
253
|
-
default:
|
254
|
-
rb_bug("xyzzy");
|
255
|
-
}
|
256
|
-
}
|
257
|
-
}
|
258
|
-
|
259
|
-
static void
|
260
|
-
trace(FILE *out, void *data, int event_index)
|
261
|
-
{
|
262
|
-
const char *type = (const char *)data;
|
263
|
-
|
264
|
-
out_tick(out);
|
265
|
-
out_str(out, type);
|
266
|
-
out_items(out, gc_trace_items, gc_trace_items_types);
|
267
|
-
out_terminate(out);
|
268
|
-
}
|
269
|
-
|
270
|
-
static void
|
271
|
-
close_output(FILE *out)
|
272
|
-
{
|
273
|
-
fflush(out);
|
274
|
-
if (out != stderr) {
|
275
|
-
fclose(out);
|
276
|
-
}
|
277
|
-
}
|
278
|
-
|
279
|
-
static FILE *
|
280
|
-
open_output(int argc, VALUE *argv)
|
281
|
-
{
|
282
|
-
FILE *out = NULL;
|
283
|
-
|
284
|
-
/* setup with args */
|
285
|
-
if (argc == 0) {
|
286
|
-
out = stderr;
|
287
|
-
}
|
288
|
-
else if (argc == 1) {
|
289
|
-
if (RB_TYPE_P(argv[0], T_STRING)) {
|
290
|
-
const char *filename = StringValueCStr(argv[0]);
|
291
|
-
if ((out = fopen(filename, "w")) == NULL) {
|
292
|
-
rb_raise(rb_eRuntimeError, "can not open file: %s\n", filename);
|
293
|
-
}
|
294
|
-
}
|
295
|
-
}
|
296
|
-
else {
|
297
|
-
rb_raise(rb_eArgError, "too many arguments");
|
298
|
-
}
|
299
|
-
|
300
|
-
return out;
|
301
|
-
}
|
302
|
-
|
303
|
-
static VALUE
|
304
|
-
gc_tracer_stop_logging(VALUE self)
|
305
|
-
{
|
306
|
-
if (logger.enabled) {
|
307
|
-
close_output(logger.out);
|
308
|
-
logger.out = NULL;
|
309
|
-
stop_gc_hooks(&logger);
|
310
|
-
}
|
311
|
-
|
312
|
-
return Qnil;
|
313
|
-
}
|
314
|
-
|
315
|
-
static VALUE
|
316
|
-
gc_tracer_start_logging(int argc, VALUE *argv, VALUE self)
|
317
|
-
{
|
318
|
-
if (logger.enabled == Qfalse) {
|
319
|
-
int i;
|
320
|
-
|
321
|
-
logger.out = open_output(argc, argv);
|
322
|
-
out_header(logger.out, gc_trace_items, 1);
|
323
|
-
|
324
|
-
for (i=0; i<3; i++) {
|
325
|
-
logger.funcs[i] = trace;
|
326
|
-
logger.args[i] = (void *)event_names[i];
|
327
|
-
}
|
328
|
-
|
329
|
-
create_gc_hooks(&logger);
|
330
|
-
start_gc_hooks(&logger);
|
331
|
-
|
332
|
-
if (rb_block_given_p()) {
|
333
|
-
rb_ensure(rb_yield, Qnil, gc_tracer_stop_logging, Qnil);
|
334
|
-
}
|
335
|
-
}
|
336
|
-
|
337
|
-
return Qnil;
|
338
|
-
}
|
339
|
-
|
340
|
-
static enum gc_key_type
|
341
|
-
item_type(VALUE sym)
|
342
|
-
{
|
343
|
-
int i;
|
344
|
-
for (i=0; i<(int)(sizeof(sym_gc_stat)/sizeof(VALUE)); i++) {
|
345
|
-
if (sym_gc_stat[i] == sym) return GC_STAT_KEY;
|
346
|
-
}
|
347
|
-
for (i=0; i<(int)(sizeof(sym_latest_gc_info)/sizeof(VALUE)); i++) {
|
348
|
-
if (sym_latest_gc_info[i] == sym) return GC_LATEST_GC_INFO_KEY;
|
349
|
-
}
|
350
|
-
#ifdef HAVE_GETRUSAGE
|
351
|
-
for (i=0; i<(int)(sizeof(sym_rusage_timeval)/sizeof(VALUE)); i++) {
|
352
|
-
if (sym_rusage_timeval[i] == sym) return GC_RUSAGE_TIMEVAL_KEY;
|
353
|
-
}
|
354
|
-
for (i=0; i<(int)(sizeof(sym_rusage)/sizeof(VALUE)); i++) {
|
355
|
-
if (sym_rusage[i] == sym) return GC_RUSAGE_KEY;
|
356
|
-
}
|
357
|
-
#endif
|
358
|
-
rb_raise(rb_eArgError, "Unknown key type");
|
359
|
-
return 0; /* unreachable */
|
360
|
-
}
|
361
|
-
|
362
|
-
static void
|
363
|
-
setup_items(VALUE new_items, VALUE items, VALUE types)
|
364
|
-
{
|
365
|
-
VALUE is = rb_ary_new();
|
366
|
-
VALUE ts = rb_ary_new();
|
367
|
-
int i;
|
368
|
-
|
369
|
-
if (NIL_P(new_items)) {
|
370
|
-
|
371
|
-
#define ADD(syms) for (i=0; i<(int)(sizeof(syms)/sizeof(VALUE));i++) { \
|
372
|
-
rb_ary_push(is, syms[i]); \
|
373
|
-
rb_ary_push(ts, INT2FIX(item_type(syms[i]))); \
|
374
|
-
}
|
375
|
-
ADD(sym_gc_stat);
|
376
|
-
ADD(sym_latest_gc_info);
|
377
|
-
#ifdef HAVE_GETRUSAGE
|
378
|
-
ADD(sym_rusage_timeval);
|
379
|
-
ADD(sym_rusage);
|
380
|
-
#endif
|
381
|
-
}
|
382
|
-
else {
|
383
|
-
if (!RB_TYPE_P(new_items, T_ARRAY)) rb_raise(rb_eArgError, "unsupported argument");
|
384
|
-
|
385
|
-
for (i=0; i<RARRAY_LEN(new_items); i++) {
|
386
|
-
VALUE sym = RARRAY_AREF(new_items, i);
|
387
|
-
enum gc_key_type type;
|
388
|
-
if (!SYMBOL_P(sym)) rb_raise(rb_eArgError, "unsupported type");
|
389
|
-
type = item_type(sym);
|
390
|
-
rb_ary_push(is, sym);
|
391
|
-
rb_ary_push(ts, INT2FIX(type));
|
392
|
-
}
|
393
|
-
|
394
|
-
}
|
395
|
-
|
396
|
-
rb_ary_replace(items, is);
|
397
|
-
rb_ary_replace(types, ts);
|
398
|
-
}
|
399
|
-
|
400
|
-
static VALUE
|
401
|
-
gc_tracer_setup_logging(VALUE self, VALUE ary)
|
402
|
-
{
|
403
|
-
setup_items(Qnil, gc_trace_items, gc_trace_items_types);
|
404
|
-
return Qnil;
|
405
|
-
}
|
406
|
-
|
407
|
-
/* slot logger */
|
408
|
-
|
409
|
-
struct slot_logger {
|
410
|
-
VALUE hook;
|
411
|
-
VALUE items;
|
412
|
-
VALUE items_types;
|
413
|
-
FILE *out;
|
414
|
-
VALUE enabled;
|
415
|
-
} slot_logger;
|
416
|
-
|
417
|
-
static void
|
418
|
-
newobj_i(VALUE tpval, void *data)
|
419
|
-
{
|
420
|
-
struct slot_logger *sl = &slot_logger;
|
421
|
-
|
422
|
-
out_tick(sl->out);
|
423
|
-
out_items(sl->out, sl->items, sl->items_types);
|
424
|
-
out_terminate(sl->out);
|
425
|
-
}
|
426
|
-
|
427
|
-
static void
|
428
|
-
start_newobj_hook(struct slot_logger *sl)
|
429
|
-
{
|
430
|
-
if (sl->hook == 0) {
|
431
|
-
sl->hook = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, NULL);
|
432
|
-
rb_gc_register_mark_object(sl->hook);
|
433
|
-
}
|
434
|
-
rb_tracepoint_enable(sl->hook);
|
435
|
-
}
|
436
|
-
|
437
|
-
static void
|
438
|
-
stop_newobj_hook(struct slot_logger *sl)
|
439
|
-
{
|
440
|
-
rb_tracepoint_disable(sl->hook);
|
441
|
-
}
|
442
|
-
static VALUE
|
443
|
-
gc_tracer_stop_slot_logging(VALUE self)
|
444
|
-
{
|
445
|
-
if (slot_logger.enabled) {
|
446
|
-
close_output(slot_logger.out);
|
447
|
-
slot_logger.out = NULL;
|
448
|
-
stop_newobj_hook(&slot_logger);
|
449
|
-
}
|
450
|
-
return Qnil;
|
451
|
-
}
|
452
|
-
|
453
|
-
static VALUE
|
454
|
-
gc_tracer_start_slot_logging(int argc, VALUE *argv, VALUE self)
|
455
|
-
{
|
456
|
-
if (slot_logger.enabled == Qfalse) {
|
457
|
-
slot_logger.enabled = Qtrue;
|
458
|
-
slot_logger.out = open_output(argc, argv);
|
459
|
-
out_header(slot_logger.out, slot_logger.items, 0);
|
460
|
-
|
461
|
-
start_newobj_hook(&slot_logger);
|
462
|
-
|
463
|
-
if (rb_block_given_p()) {
|
464
|
-
rb_ensure(rb_yield, Qnil, gc_tracer_stop_slot_logging, Qnil);
|
465
|
-
}
|
466
|
-
}
|
467
|
-
|
468
|
-
return Qnil;
|
469
|
-
}
|
470
|
-
|
471
|
-
#ifdef HAVE_RB_OBJSPACE_EACH_OBJECTS_WITHOUT_SETUP
|
472
|
-
/* image logging */
|
473
|
-
|
474
|
-
/* secret API */
|
475
|
-
void rb_objspace_each_objects_without_setup(int (*callback)(void *, void *, size_t, void *), void *data);
|
476
|
-
|
477
|
-
static struct gc_hooks objspace_recorder;
|
478
|
-
static int (*objspace_recorder_color_picker)(VALUE);
|
479
|
-
static int HEAP_OBJ_LIMIT;
|
480
|
-
|
481
|
-
struct objspace_recording_data {
|
482
|
-
FILE *fp;
|
483
|
-
int width, height;
|
484
|
-
};
|
485
|
-
|
486
|
-
struct picker_description {
|
487
|
-
const char *name;
|
488
|
-
const int color;
|
489
|
-
};
|
490
|
-
|
491
|
-
static void
|
492
|
-
set_color(unsigned char *buff, int color)
|
493
|
-
{
|
494
|
-
buff[2] = (color >> 0) & 0xff;
|
495
|
-
buff[1] = (color >> 8) & 0xff;
|
496
|
-
buff[0] = (color >> 16) & 0xff;
|
497
|
-
}
|
498
|
-
|
499
|
-
const int categorical_color10_colors[] = {
|
500
|
-
0x1f77b4,
|
501
|
-
0xff7f0e,
|
502
|
-
0x2ca02c,
|
503
|
-
0xd62728,
|
504
|
-
0x9467bd,
|
505
|
-
0x8c564b,
|
506
|
-
0xe377c2,
|
507
|
-
0x7f7f7f,
|
508
|
-
0xbcbd22,
|
509
|
-
0x17becf
|
510
|
-
};
|
511
|
-
|
512
|
-
static int
|
513
|
-
categorical_color10(int n)
|
514
|
-
{
|
515
|
-
assert(n < 10);
|
516
|
-
return categorical_color10_colors[n];
|
517
|
-
}
|
518
|
-
|
519
|
-
const int categorical_color20_colors[] = {
|
520
|
-
0x1f77b4,
|
521
|
-
0xaec7e8,
|
522
|
-
0xff7f0e,
|
523
|
-
0xffbb78,
|
524
|
-
0x2ca02c,
|
525
|
-
0x98df8a,
|
526
|
-
0xd62728,
|
527
|
-
0xff9896,
|
528
|
-
0x9467bd,
|
529
|
-
0xc5b0d5,
|
530
|
-
0x8c564b,
|
531
|
-
0xc49c94,
|
532
|
-
0xe377c2,
|
533
|
-
0xf7b6d2,
|
534
|
-
0x7f7f7f,
|
535
|
-
0xc7c7c7,
|
536
|
-
0xbcbd22,
|
537
|
-
0xdbdb8d,
|
538
|
-
0x17becf,
|
539
|
-
0x9edae5,
|
540
|
-
};
|
541
|
-
|
542
|
-
static int
|
543
|
-
categorical_color20(int n)
|
544
|
-
{
|
545
|
-
assert(n < 20);
|
546
|
-
return categorical_color20_colors[n];
|
547
|
-
}
|
548
|
-
|
549
|
-
static struct picker_description object_age_picker_description[] = {
|
550
|
-
{"empty slot", 0},
|
551
|
-
{"infant slot", 0x1f77b4},
|
552
|
-
{"young slot", 0xff7f0e},
|
553
|
-
{"old slot", 0x2ca02c},
|
554
|
-
{"shady slot", 0xd62728}
|
555
|
-
};
|
556
|
-
|
557
|
-
static int
|
558
|
-
object_age_picker(VALUE v) {
|
559
|
-
if (RB_TYPE_P(v, T_NONE)) {
|
560
|
-
return 0;
|
561
|
-
}
|
562
|
-
else {
|
563
|
-
if (!OBJ_PROMOTED(v)) {
|
564
|
-
if (OBJ_WB_PROTECTED(v)) {
|
565
|
-
return categorical_color10(0); /* infant */
|
566
|
-
}
|
567
|
-
else {
|
568
|
-
return categorical_color10(3); /* shady */
|
569
|
-
}
|
570
|
-
}
|
571
|
-
else {
|
572
|
-
int is_young = 0;
|
573
|
-
#ifdef HAVE_RB_OBJ_GC_FLAGS
|
574
|
-
ID ids[8];
|
575
|
-
size_t i, count = rb_obj_gc_flags(v, ids, 8);
|
576
|
-
|
577
|
-
for (i=0; i<count; i++) {
|
578
|
-
if (ids[i] == id_young) {
|
579
|
-
is_young = 1;
|
580
|
-
break;
|
581
|
-
}
|
582
|
-
}
|
583
|
-
#endif
|
584
|
-
if (is_young) {
|
585
|
-
return categorical_color10(1); /* young */
|
586
|
-
}
|
587
|
-
else {
|
588
|
-
return categorical_color10(2); /* old */
|
589
|
-
}
|
590
|
-
}
|
591
|
-
}
|
592
|
-
}
|
593
|
-
|
594
|
-
static struct picker_description object_type_picker_description[] = {
|
595
|
-
{"RUBY_T_NONE", 0},
|
596
|
-
{"RUBY_T_OBJECT", 0x1f77b4},
|
597
|
-
{"RUBY_T_CLASS", 0xaec7e8},
|
598
|
-
{"RUBY_T_MODULE", 0xff7f0e},
|
599
|
-
{"RUBY_T_FLOAT", 0xffbb78},
|
600
|
-
{"RUBY_T_STRING", 0x2ca02c},
|
601
|
-
{"RUBY_T_REGEXP", 0x98df8a},
|
602
|
-
{"RUBY_T_ARRAY", 0xd62728},
|
603
|
-
{"RUBY_T_HASH", 0xff9896},
|
604
|
-
{"RUBY_T_STRUCT", 0x9467bd},
|
605
|
-
{"RUBY_T_BIGNUM", 0xc5b0d5},
|
606
|
-
{"RUBY_T_FILE", 0x8c564b},
|
607
|
-
{"RUBY_T_DATA", 0xc49c94},
|
608
|
-
{"RUBY_T_MATCH", 0xe377c2},
|
609
|
-
{"RUBY_T_COMPLEX", 0xf7b6d2},
|
610
|
-
{"RUBY_T_RATIONAL", 0x7f7f7f},
|
611
|
-
{"RUBY_T_NODE", 0xc7c7c7},
|
612
|
-
{"RUBY_T_ICLASS", 0xbcbd22},
|
613
|
-
{"RUBY_T_ZOMBIE", 0xdbdb8d},
|
614
|
-
};
|
615
|
-
|
616
|
-
static int
|
617
|
-
object_type_picker(VALUE v) {
|
618
|
-
int type = BUILTIN_TYPE(v);
|
619
|
-
int color = 0;
|
620
|
-
switch (type) {
|
621
|
-
case RUBY_T_NONE:
|
622
|
-
return 0;
|
623
|
-
case RUBY_T_OBJECT:
|
624
|
-
case RUBY_T_CLASS:
|
625
|
-
case RUBY_T_MODULE:
|
626
|
-
case RUBY_T_FLOAT:
|
627
|
-
case RUBY_T_STRING:
|
628
|
-
case RUBY_T_REGEXP:
|
629
|
-
case RUBY_T_ARRAY:
|
630
|
-
case RUBY_T_HASH:
|
631
|
-
case RUBY_T_STRUCT:
|
632
|
-
case RUBY_T_BIGNUM:
|
633
|
-
case RUBY_T_FILE:
|
634
|
-
case RUBY_T_DATA:
|
635
|
-
case RUBY_T_MATCH:
|
636
|
-
case RUBY_T_COMPLEX:
|
637
|
-
case RUBY_T_RATIONAL: /* 0x0f */
|
638
|
-
color = type - 1;
|
639
|
-
break;
|
640
|
-
case RUBY_T_NODE:
|
641
|
-
case RUBY_T_ICLASS:
|
642
|
-
case RUBY_T_ZOMBIE:
|
643
|
-
color = type - 12;
|
644
|
-
break;
|
645
|
-
default:
|
646
|
-
rb_bug("object_type_picker: unreachable (type: %d)", type);
|
647
|
-
}
|
648
|
-
return categorical_color20(color);
|
649
|
-
}
|
650
|
-
|
651
|
-
static int
|
652
|
-
objspace_recording_i(void *start, void *end, size_t stride, void *data)
|
653
|
-
{
|
654
|
-
struct objspace_recording_data *rd = (struct objspace_recording_data *)data;
|
655
|
-
const int size = ((VALUE)end - (VALUE)start) / stride;
|
656
|
-
unsigned char *buff = ALLOCA_N(unsigned char, size * 3);
|
657
|
-
int i, write_result;
|
658
|
-
|
659
|
-
for (i=0; i<size; i++) {
|
660
|
-
VALUE v = (VALUE)start + i * stride;
|
661
|
-
set_color(&buff[i*3], (*objspace_recorder_color_picker)(v));
|
662
|
-
}
|
663
|
-
for (; i<HEAP_OBJ_LIMIT; i++) {
|
664
|
-
set_color(&buff[i*3], 0);
|
665
|
-
}
|
666
|
-
|
667
|
-
write_result = fwrite(buff, 3, HEAP_OBJ_LIMIT, rd->fp);
|
668
|
-
rd->height++;
|
669
|
-
|
670
|
-
if (0) {
|
671
|
-
fprintf(stderr, "width: %d, write: %d\n", HEAP_OBJ_LIMIT, write_result);
|
672
|
-
}
|
673
|
-
|
674
|
-
return 0;
|
675
|
-
}
|
676
|
-
|
677
|
-
static void
|
678
|
-
objspace_recording(FILE *dmy, void *data, int event_index)
|
679
|
-
{
|
680
|
-
/* const char *event_name = (const char *)data; */
|
681
|
-
const char *dirname = (const char *)objspace_recorder.data;
|
682
|
-
char filename[1024];
|
683
|
-
FILE *fp;
|
684
|
-
struct objspace_recording_data rd;
|
685
|
-
|
686
|
-
snprintf(filename, 1024, "%s/ppm/%08d.%d.ppm", dirname, (int)rb_gc_count(), event_index);
|
687
|
-
|
688
|
-
if ((fp = fopen(filename, "w")) == NULL) {
|
689
|
-
rb_bug("objspace_recording: unable to open file: %s", filename);
|
690
|
-
}
|
691
|
-
rd.fp = fp;
|
692
|
-
|
693
|
-
/* making dummy header */
|
694
|
-
/* MG width heigt dep */
|
695
|
-
fprintf(fp, "P6 wwwww hhhhh 255 ");
|
696
|
-
|
697
|
-
rd.width = HEAP_OBJ_LIMIT;
|
698
|
-
rd.height = 0;
|
699
|
-
rb_objspace_each_objects_without_setup(objspace_recording_i, &rd);
|
700
|
-
|
701
|
-
/* fill width and height */
|
702
|
-
fseek(fp, 3, SEEK_SET);
|
703
|
-
fprintf(fp, "%5d %5d", rd.width, rd.height);
|
704
|
-
fclose(fp);
|
705
|
-
}
|
706
|
-
|
707
|
-
static VALUE
|
708
|
-
gc_tracer_stop_objspace_recording(VALUE self)
|
709
|
-
{
|
710
|
-
if (objspace_recorder.enabled) {
|
711
|
-
ruby_xfree(objspace_recorder.data);
|
712
|
-
stop_gc_hooks(&objspace_recorder);
|
713
|
-
}
|
714
|
-
return Qnil;
|
715
|
-
}
|
716
|
-
|
717
|
-
static void
|
718
|
-
puts_color_description(VALUE dirname, struct picker_description desc[], int n)
|
719
|
-
{
|
720
|
-
char buff[0x200];
|
721
|
-
FILE *fp;
|
722
|
-
int i;
|
723
|
-
|
724
|
-
snprintf(buff, 0x200, "%s/color_description.txt", RSTRING_PTR(dirname));
|
725
|
-
|
726
|
-
if ((fp = fopen(buff, "w")) == NULL) {
|
727
|
-
rb_raise(rb_eRuntimeError, "puts_color_description: failed to open file");
|
728
|
-
}
|
729
|
-
|
730
|
-
for (i=0; i<n; i++) {
|
731
|
-
fprintf(fp, "%s\t#%06x\n", desc[i].name, desc[i].color);
|
732
|
-
}
|
733
|
-
|
734
|
-
fclose(fp);
|
735
|
-
}
|
736
|
-
|
737
|
-
|
738
|
-
static VALUE
|
739
|
-
gc_tracer_start_objspace_recording(int argc, VALUE *argv, VALUE self)
|
740
|
-
{
|
741
|
-
if (objspace_recorder.enabled == Qfalse) {
|
742
|
-
int i;
|
743
|
-
VALUE ppmdir;
|
744
|
-
VALUE dirname;
|
745
|
-
VALUE picker_type = ID2SYM(rb_intern("age"));
|
746
|
-
|
747
|
-
switch (argc) {
|
748
|
-
case 2:
|
749
|
-
picker_type = argv[1];
|
750
|
-
case 1:
|
751
|
-
dirname = argv[0];
|
752
|
-
break;
|
753
|
-
default:
|
754
|
-
rb_raise(rb_eArgError, "expect 1 or 2 arguments, but %d", argc);
|
755
|
-
}
|
756
|
-
|
757
|
-
/* setup */
|
758
|
-
if (rb_funcall(rb_cFile, rb_intern("directory?"), 1, dirname) != Qtrue) {
|
759
|
-
rb_funcall(rb_cDir, rb_intern("mkdir"), 1, dirname);
|
760
|
-
}
|
761
|
-
if (rb_funcall(rb_cFile, rb_intern("directory?"), 1, (ppmdir = rb_str_plus(dirname, rb_str_new2("/ppm")))) != Qtrue) {
|
762
|
-
rb_funcall(rb_cDir, rb_intern("mkdir"), 1, ppmdir);
|
763
|
-
}
|
764
|
-
|
765
|
-
if (picker_type == ID2SYM(rb_intern("age"))) {
|
766
|
-
objspace_recorder_color_picker = object_age_picker;
|
767
|
-
puts_color_description(dirname, &object_age_picker_description[0], sizeof(object_age_picker_description) / sizeof(struct picker_description));
|
768
|
-
}
|
769
|
-
else if (picker_type == ID2SYM(rb_intern("type"))) {
|
770
|
-
objspace_recorder_color_picker = object_type_picker;
|
771
|
-
puts_color_description(dirname, &object_type_picker_description[0], sizeof(object_type_picker_description) / sizeof(struct picker_description));
|
772
|
-
}
|
773
|
-
else {
|
774
|
-
rb_raise(rb_eArgError, "unsupported picker type: %s", rb_id2name(SYM2ID(picker_type)));
|
775
|
-
}
|
776
|
-
|
777
|
-
HEAP_OBJ_LIMIT = FIX2INT(rb_hash_aref(
|
778
|
-
rb_const_get(rb_mGC, rb_intern("INTERNAL_CONSTANTS")),
|
779
|
-
ID2SYM(rb_intern("HEAP_OBJ_LIMIT"))));
|
780
|
-
|
781
|
-
for (i=0; i<3; i++) {
|
782
|
-
objspace_recorder.funcs[i] = objspace_recording;
|
783
|
-
objspace_recorder.args[i] = (void *)event_names[i];
|
784
|
-
}
|
785
|
-
|
786
|
-
objspace_recorder.data = ruby_xmalloc(RSTRING_LEN(dirname) + 1);
|
787
|
-
strcpy((char *)objspace_recorder.data, RSTRING_PTR(dirname));
|
788
|
-
|
789
|
-
create_gc_hooks(&objspace_recorder);
|
790
|
-
start_gc_hooks(&objspace_recorder);
|
791
|
-
|
792
|
-
if (rb_block_given_p()) {
|
793
|
-
rb_ensure(rb_yield, Qnil, gc_tracer_stop_objspace_recording, Qnil);
|
794
|
-
}
|
795
|
-
}
|
796
|
-
else {
|
797
|
-
rb_raise(rb_eRuntimeError, "recursive recording is not permitted");
|
798
|
-
}
|
799
|
-
|
800
|
-
return Qnil;
|
801
|
-
}
|
802
|
-
|
803
|
-
#endif /* HAVE_RB_OBJSPACE_EACH_OBJECTS_WITHOUT_SETUP */
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
/**
|
808
|
-
* GC::Tracer traces GC/ObjectSpace behavior.
|
809
|
-
*
|
810
|
-
* == Logging
|
811
|
-
*
|
812
|
-
* GC::Tracer.start_logging(filename) prints GC/ObjectSpace
|
813
|
-
* information such as GC.stat/GC.latest_gc_info and the result
|
814
|
-
* of getrlimit() (if supported) into specified `filename' on
|
815
|
-
* each GC events.
|
816
|
-
*
|
817
|
-
* GC events are "start marking", "end of marking" and
|
818
|
-
* "end of sweeping". You should need to care about lazy sweep.
|
819
|
-
*
|
820
|
-
* == ObjectSpace recorder
|
821
|
-
*
|
822
|
-
* This feature needs latest Ruby versions (2.2, and later).
|
823
|
-
*/
|
824
|
-
void
|
825
|
-
Init_gc_tracer(void)
|
826
|
-
{
|
827
|
-
VALUE mod = rb_define_module_under(rb_mGC, "Tracer");
|
828
|
-
|
829
|
-
/* logging methods */
|
830
|
-
rb_define_module_function(mod, "start_logging", gc_tracer_start_logging, -1);
|
831
|
-
rb_define_module_function(mod, "stop_logging", gc_tracer_stop_logging, 0);
|
832
|
-
rb_define_module_function(mod, "setup_logging", gc_tracer_setup_logging, 1);
|
833
|
-
|
834
|
-
rb_define_module_function(mod, "start_slot_logging", gc_tracer_start_slot_logging, -1);
|
835
|
-
rb_define_module_function(mod, "stop_slot_logging", gc_tracer_stop_slot_logging, 0);
|
836
|
-
|
837
|
-
/* recording methods */
|
838
|
-
#ifdef HAVE_RB_OBJSPACE_EACH_OBJECTS_WITHOUT_SETUP
|
839
|
-
rb_define_module_function(mod, "start_objspace_recording", gc_tracer_start_objspace_recording, -1);
|
840
|
-
rb_define_module_function(mod, "stop_objspace_recording", gc_tracer_stop_objspace_recording, 0);
|
841
|
-
#endif
|
842
|
-
|
843
|
-
/* setup default banners */
|
844
|
-
setup_gc_trace_symbols();
|
845
|
-
start_tick = tick();
|
846
|
-
|
847
|
-
/* warm up */
|
848
|
-
rb_gc_latest_gc_info(ID2SYM(rb_intern("gc_by")));
|
849
|
-
rb_gc_stat(ID2SYM(rb_intern("count")));
|
850
|
-
#ifdef HAVE_RB_OBJ_GC_FLAGS
|
851
|
-
rb_obj_gc_flags(rb_cObject, NULL, 0);
|
852
|
-
id_young = rb_intern("young");
|
853
|
-
#endif
|
854
|
-
|
855
|
-
gc_trace_items = rb_ary_new();
|
856
|
-
gc_trace_items_types = rb_ary_new();
|
857
|
-
rb_gc_register_mark_object(gc_trace_items);
|
858
|
-
rb_gc_register_mark_object(gc_trace_items_types);
|
859
|
-
setup_items(Qnil, gc_trace_items, gc_trace_items_types);
|
860
|
-
|
861
|
-
/* slot */
|
862
|
-
slot_logger.items = rb_ary_new();
|
863
|
-
slot_logger.items_types = rb_ary_new();
|
864
|
-
rb_gc_register_mark_object(slot_logger.items);
|
865
|
-
rb_gc_register_mark_object(slot_logger.items_types);
|
866
|
-
setup_items(rb_ary_new3(4,
|
867
|
-
ID2SYM(rb_intern("count")),
|
868
|
-
ID2SYM(rb_intern("heap_live_slot")),
|
869
|
-
ID2SYM(rb_intern("heap_free_slot")),
|
870
|
-
ID2SYM(rb_intern("old_object"))),
|
871
|
-
slot_logger.items, slot_logger.items_types);
|
872
|
-
}
|
873
|
-
|
874
|
-
#endif
|
875
|
-
|
876
8
|
#include <ruby/ruby.h>
|
877
9
|
|
878
10
|
void Init_gc_tracer_logging(VALUE m_gc_tracer); /* in gc_logging.c */
|
data/lib/gc_tracer/version.rb
CHANGED
data/lib/gc_tracer.rb
CHANGED