gc_tracer 1.0.0 → 1.0.1

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: e93e7dfe6c23589a2f0fe11353cffb53dc903632
4
- data.tar.gz: 2cb9278fbf2b4ba77e809b05421e1c5593cb3336
3
+ metadata.gz: 2fd802669a217a30e27978a50adfca22bac7bafd
4
+ data.tar.gz: 02fbf3dbdfe74e7b65a48a0b0c0644e703fb9eda
5
5
  SHA512:
6
- metadata.gz: f9ae74ac0d8577dd8b48b4bfaddaeb85af2aedbf70838b0f796b6eaf65193300d6e41804d28dda93861fb4b211b6bf1c71586850a811b49c390df741b18c0543
7
- data.tar.gz: a1c6d9e8b33f96f664894462750add5481e7ea90ef2420742ecb030b2bd39e5ac9c9dba017e5c58b8ddea99292f9ee9c4b98a2919b23337bc026eec04d0181f3
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
- :user_time and :system_time.
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
 
@@ -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}];"
@@ -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 */
@@ -1,3 +1,3 @@
1
1
  module GC::Tracer
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
data/lib/gc_tracer.rb CHANGED
@@ -8,7 +8,7 @@ module GC
8
8
  # event filter
9
9
  events: %i(start end_mark end_sweep),
10
10
  # tick type (:hw_counter, :time, :user_time, :system_time)
11
- tick_type: :time,
11
+ tick_type: :nano_time,
12
12
  # collect information
13
13
  gc_stat: true,
14
14
  gc_latest_gc_info: true,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gc_tracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koichi Sasada