1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* gc.c: added GC::Profiler.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18493 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nari 2008-08-11 09:36:57 +00:00
parent c5579a833d
commit 130e720253
2 changed files with 343 additions and 0 deletions

View file

@ -1,3 +1,7 @@
Mon Aug 11 18:28:02 2008 Narihiro Nakamura <authorNari@gmail.com>
* gc.c: added GC::Profiler.
Mon Aug 11 17:26:16 2008 TAKAO Kouji <kouji@takao7.net>
* ext/readline/README.ja: added API document for

339
gc.c
View file

@ -92,6 +92,132 @@ int ruby_gc_debug_indent = 0;
#undef GC_DEBUG
/* for GC profile */
#define GC_PROFILE_MORE_DETAIL 0
typedef struct gc_profile_record {
double gc_time;
double gc_mark_time;
double gc_sweep_time;
double gc_invoke_time;
size_t heap_use_slots;
size_t heap_live_objects;
size_t heap_free_objects;
size_t heap_total_objects;
size_t heap_use_size;
size_t heap_total_size;
int have_finalize;
size_t allocate_increase;
size_t allocate_limit;
} gc_profile_record;
static double
getrusage_time(void)
{
struct rusage usage;
struct timeval time;
getrusage(RUSAGE_SELF, &usage);
time = usage.ru_utime;
return time.tv_sec + time.tv_usec * 1e-6;
}
#define GC_PROF_TIMER_START do {\
if (objspace->profile.run) {\
if (!objspace->profile.record) {\
objspace->profile.size = 1000;\
objspace->profile.record = malloc(sizeof(gc_profile_record) * objspace->profile.size);\
}\
if (count >= objspace->profile.size) {\
objspace->profile.size += 1000;\
objspace->profile.record = realloc(objspace->profile.record, sizeof(gc_profile_record) * objspace->profile.size);\
}\
if (!objspace->profile.record) {\
rb_bug("gc_profile malloc or realloc miss");\
}\
MEMZERO(&objspace->profile.record[count], gc_profile_record, 1);\
gc_time = getrusage_time();\
objspace->profile.record[count].gc_invoke_time = gc_time - objspace->profile.invoke_time;\
}\
} while(0)
#define GC_PROF_TIMER_STOP do {\
if (objspace->profile.run) {\
gc_time = getrusage_time() - gc_time;\
if(gc_time < 0) gc_time = 0;\
objspace->profile.record[count].gc_time = gc_time;\
objspace->profile.count++;\
}\
} while(0)
#if GC_PROFILE_MORE_DETAIL
#define INIT_GC_PROF_PARAMS double gc_time = 0, mark_time = 0, sweep_time = 0;\
size_t count = objspace->profile.count
#define GC_PROF_MARK_TIMER_START do {\
if (objspace->profile.run) {\
mark_time = getrusage_time();\
}\
} while(0)
#define GC_PROF_MARK_TIMER_STOP do {\
if (objspace->profile.run) {\
mark_time = getrusage_time() - mark_time;\
if (mark_time < 0) mark_time = 0;\
objspace->profile.record[count].gc_mark_time = mark_time;\
}\
} while(0)
#define GC_PROF_SWEEP_TIMER_START do {\
if (objspace->profile.run) {\
sweep_time = getrusage_time();\
}\
} while(0)
#define GC_PROF_SWEEP_TIMER_STOP do {\
if (objspace->profile.run) {\
sweep_time = getrusage_time() - sweep_time;\
if (sweep_time < 0) sweep_time = 0;\
objspace->profile.record[count].gc_sweep_time = sweep_time;\
}\
} while(0)
#define GC_PROF_SET_MALLOC_INFO do {\
if (objspace->profile.run) {\
size_t count = objspace->profile.count;\
objspace->profile.record[count].allocate_increase = malloc_increase;\
objspace->profile.record[count].allocate_limit = malloc_limit; \
}\
} while(0)
#define GC_PROF_SET_HEAP_INFO do {\
if (objspace->profile.run) {\
size_t count = objspace->profile.count;\
objspace->profile.record[count].heap_use_slots = heaps_used;\
objspace->profile.record[count].heap_live_objects = live;\
objspace->profile.record[count].heap_free_objects = freed;\
objspace->profile.record[count].heap_total_objects = heaps_used * HEAP_OBJ_LIMIT;\
objspace->profile.record[count].have_finalize = final_list ? Qtrue : Qfalse;\
objspace->profile.record[count].heap_use_size = live * sizeof(RVALUE);\
objspace->profile.record[count].heap_total_size = heaps_used * (HEAP_OBJ_LIMIT * sizeof(RVALUE));\
}\
} while(0)
#else
#define INIT_GC_PROF_PARAMS double gc_time = 0;\
size_t count = objspace->profile.count
#define GC_PROF_MARK_TIMER_START
#define GC_PROF_MARK_TIMER_STOP
#define GC_PROF_SWEEP_TIMER_START
#define GC_PROF_SWEEP_TIMER_STOP
#define GC_PROF_SET_MALLOC_INFO
#define GC_PROF_SET_HEAP_INFO do {\
if (objspace->profile.run) {\
size_t count = objspace->profile.count;\
objspace->profile.record[count].heap_total_objects = heaps_used * HEAP_OBJ_LIMIT;\
objspace->profile.record[count].heap_use_size = live * sizeof(RVALUE);\
objspace->profile.record[count].heap_total_size = heaps_used * HEAP_SIZE;\
}\
} while(0)
#endif
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
#pragma pack(push, 1) /* magic for reducing sizeof(RVALUE): 24 -> 20 */
#endif
@ -176,6 +302,13 @@ typedef struct rb_objspace {
VALUE *ptr;
int overflow;
} markstack;
struct {
int run;
gc_profile_record *record;
size_t count;
size_t size;
double invoke_time;
} profile;
struct gc_list *global_list;
unsigned int count;
int gc_stress;
@ -309,6 +442,73 @@ gc_stress_set(VALUE self, VALUE bool)
return bool;
}
/*
* call-seq:
* GC::Profiler.enable? => true or false
*
* returns current status of GC profile mode.
*/
static VALUE
gc_profile_enable_get(VALUE self)
{
rb_objspace_t *objspace = &rb_objspace;
return objspace->profile.run;
}
/*
* call-seq:
* GC::Profiler.enable => nil
*
* updates GC profile mode.
* start profiler for GC.
*
*/
static VALUE
gc_profile_enable(void)
{
rb_objspace_t *objspace = &rb_objspace;
objspace->profile.run = Qtrue;
return Qnil;
}
/*
* call-seq:
* GC::Profiler.disable => nil
*
* updates GC profile mode.
* stop profiler for GC.
*
*/
static VALUE
gc_profile_disable(void)
{
rb_objspace_t *objspace = &rb_objspace;
objspace->profile.run = Qfalse;
return Qnil;
}
/*
* call-seq:
* GC::Profiler.clear => nil
*
* clear before profile data.
*
*/
static VALUE
gc_profile_clear(void)
{
rb_objspace_t *objspace = &rb_objspace;
MEMZERO(objspace->profile.record, gc_profile_record, objspace->profile.size);
objspace->profile.count = 0;
return Qnil;
}
static void *
vm_xmalloc(rb_objspace_t *objspace, size_t size)
{
@ -634,6 +834,7 @@ init_heap(rb_objspace_t *objspace)
assign_heap_slot(objspace);
}
heaps_inc = 0;
objspace->profile.invoke_time = getrusage_time();
}
@ -1461,6 +1662,7 @@ gc_sweep(rb_objspace_t *objspace)
freed += n;
}
}
GC_PROF_SET_MALLOC_INFO;
if (malloc_increase > malloc_limit) {
malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed);
if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
@ -1474,11 +1676,13 @@ gc_sweep(rb_objspace_t *objspace)
/* clear finalization list */
if (final_list) {
GC_PROF_SET_HEAP_INFO;
deferred_final_list = final_list;
RUBY_VM_SET_FINALIZER_INTERRUPT(GET_THREAD());
}
else{
free_unused_heaps(objspace);
GC_PROF_SET_HEAP_INFO;
}
}
@ -1706,6 +1910,7 @@ garbage_collect(rb_objspace_t *objspace)
{
struct gc_list *list;
rb_thread_t *th = GET_THREAD();
INIT_GC_PROF_PARAMS;
if (GC_NOTIFY) printf("start garbage_collect()\n");
@ -1725,6 +1930,8 @@ garbage_collect(rb_objspace_t *objspace)
during_gc++;
objspace->count++;
GC_PROF_TIMER_START;
GC_PROF_MARK_TIMER_START;
SET_STACK_END;
init_mark_stack(objspace);
@ -1765,9 +1972,13 @@ garbage_collect(rb_objspace_t *objspace)
gc_mark_rest(objspace);
}
}
GC_PROF_MARK_TIMER_STOP;
GC_PROF_SWEEP_TIMER_START;
gc_sweep(objspace);
GC_PROF_SWEEP_TIMER_STOP;
GC_PROF_TIMER_STOP;
if (GC_NOTIFY) printf("end garbage_collect()\n");
return Qtrue;
}
@ -2354,6 +2565,7 @@ count_objects(int argc, VALUE *argv, VALUE os)
}
rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(freed));
for (i = 0; i <= T_MASK; i++) {
VALUE type;
switch (i) {
@ -2441,6 +2653,124 @@ gc_malloc_allocations(VALUE self)
}
#endif
VALUE
gc_profile_record_get(void)
{
VALUE prof;
VALUE gc_profile = rb_ary_new();
size_t i;
rb_objspace_t *objspace = (&rb_objspace);
if (!objspace->profile.run) {
return Qnil;
}
for (i =0; i < objspace->profile.count; i++) {
prof = rb_hash_new();
rb_hash_aset(prof, ID2SYM(rb_intern("GC_TIME")), DOUBLE2NUM(objspace->profile.record[i].gc_time));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_TIME")), DOUBLE2NUM(objspace->profile.record[i].gc_invoke_time));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SIZE")), rb_uint2inum(objspace->profile.record[i].heap_use_size));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_SIZE")), rb_uint2inum(objspace->profile.record[i].heap_total_size));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_OBJECTS")), rb_uint2inum(objspace->profile.record[i].heap_total_objects));
#if GC_PROFILE_MORE_DETAIL
rb_hash_aset(prof, ID2SYM(rb_intern("GC_MARK_TIME")), DOUBLE2NUM(objspace->profile.record[i].gc_mark_time));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_SWEEP_TIME")), DOUBLE2NUM(objspace->profile.record[i].gc_sweep_time));
rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_INCREASE")), rb_uint2inum(objspace->profile.record[i].allocate_increase));
rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_LIMIT")), rb_uint2inum(objspace->profile.record[i].allocate_limit));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SLOTS")), rb_uint2inum(objspace->profile.record[i].heap_use_slots));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_LIVE_OBJECTS")), rb_uint2inum(objspace->profile.record[i].heap_live_objects));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_FREE_OBJECTS")), rb_uint2inum(objspace->profile.record[i].heap_free_objects));
rb_hash_aset(prof, ID2SYM(rb_intern("HAVE_FINALIZE")), objspace->profile.record[i].have_finalize);
#endif
rb_ary_push(gc_profile, prof);
}
return gc_profile;
}
/*
* call-seq:
* GC::Profiler.result -> string
*
* Report profile data to string.
*
* It returns a string as:
* GC 1 invokes.
* Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC time(ms)
* 1 0.012 159240 212940 10647 0.00000000000001530000
*/
VALUE
gc_profile_result(void)
{
rb_objspace_t *objspace = &rb_objspace;
VALUE record = gc_profile_record_get();
VALUE result = rb_str_new2("");
int i;
char buf[1024];
if(objspace->profile.run && objspace->profile.count) {
sprintf(buf, "GC %1$d invokes.\n", NUM2INT(gc_count(0)));
rb_str_concat(result, rb_str_new2(buf));
rb_str_concat(result, rb_str_new2("Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms)\n"));
for(i = 0; i < (int)RARRAY_LEN(record); i++) {
memset(buf, 0, 1024);
VALUE r = RARRAY_PTR(record)[i];
sprintf(buf, "%1$5d %2$19.3f %3$20d %4$20d %5$20d %6$30.20f\n",
i+1, NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_INVOKE_TIME")))),
NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_USE_SIZE")))),
NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_TOTAL_SIZE")))),
NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_TOTAL_OBJECTS")))),
NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_TIME"))))*100);
rb_str_concat(result, rb_str_new2(buf));
}
#if GC_PROFILE_MORE_DETAIL
rb_str_concat(result, rb_str_new2("\n\n"));
rb_str_concat(result, rb_str_new2("More detail.\n"));
rb_str_concat(result, rb_str_new2("Index Allocate Increase Allocate Limit Use Slot Have Finalize Mark Time(ms) Sweep Time(ms)\n"));
for(i = 0; i < (int)RARRAY_LEN(record); i++) {
memset(buf, 0, 1024);
VALUE r = RARRAY_PTR(record)[i];
sprintf(buf, "%1$5d %2$17d %3$17d %4$9d %5$14s %6$25.20f %7$25.20f\n",
i+1, NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("ALLOCATE_INCREASE")))),
NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("ALLOCATE_LIMIT")))),
NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_USE_SLOTS")))),
rb_hash_aref(r, ID2SYM(rb_intern("HAVE_FINALIZE")))? "true" : "false",
NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_MARK_TIME"))))*100,
NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_SWEEP_TIME"))))*100);
rb_str_concat(result, rb_str_new2(buf));
}
#endif
}
return result;
}
/*
* call-seq:
* GC::Profiler.report
*
* GC::Profiler.result display
*
*/
VALUE
gc_profile_report(int argc, VALUE *argv, VALUE self)
{
VALUE out;
if (argc == 0) {
out = rb_stdout;
}
else {
rb_scan_args(argc, argv, "01", &out);
}
rb_io_write(out, gc_profile_result());
return Qnil;
}
/*
* The <code>GC</code> module provides an interface to Ruby's mark and
* sweep garbage collection mechanism. Some of the underlying methods
@ -2451,6 +2781,7 @@ void
Init_GC(void)
{
VALUE rb_mObSpace;
VALUE rb_mProfiler;
rb_mGC = rb_define_module("GC");
rb_define_singleton_method(rb_mGC, "start", rb_gc_start, 0);
@ -2461,6 +2792,14 @@ Init_GC(void)
rb_define_singleton_method(rb_mGC, "count", gc_count, 0);
rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);
rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler");
rb_define_singleton_method(rb_mProfiler, "enabled?", gc_profile_enable_get, 0);
rb_define_singleton_method(rb_mProfiler, "enable", gc_profile_enable, 0);
rb_define_singleton_method(rb_mProfiler, "disable", gc_profile_disable, 0);
rb_define_singleton_method(rb_mProfiler, "clear", gc_profile_clear, 0);
rb_define_singleton_method(rb_mProfiler, "result", gc_profile_result, 0);
rb_define_singleton_method(rb_mProfiler, "report", gc_profile_report, -1);
rb_mObSpace = rb_define_module("ObjectSpace");
rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0);