00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "ruby/ruby.h"
00017 #include "ruby/debug.h"
00018 #include "objspace.h"
00019 #include "internal.h"
00020
00021 struct traceobj_arg {
00022 int running;
00023 int keep_remains;
00024 VALUE newobj_trace;
00025 VALUE freeobj_trace;
00026 st_table *object_table;
00027 st_table *str_table;
00028 struct traceobj_arg *prev_traceobj_arg;
00029 };
00030
00031 static const char *
00032 make_unique_str(st_table *tbl, const char *str, long len)
00033 {
00034 if (!str) {
00035 return NULL;
00036 }
00037 else {
00038 st_data_t n;
00039 char *result;
00040
00041 if (st_lookup(tbl, (st_data_t)str, &n)) {
00042 st_insert(tbl, (st_data_t)str, n+1);
00043 st_get_key(tbl, (st_data_t)str, (st_data_t *)&result);
00044 }
00045 else {
00046 result = (char *)ruby_xmalloc(len+1);
00047 strncpy(result, str, len);
00048 result[len] = 0;
00049 st_add_direct(tbl, (st_data_t)result, 1);
00050 }
00051 return result;
00052 }
00053 }
00054
00055 static void
00056 delete_unique_str(st_table *tbl, const char *str)
00057 {
00058 if (str) {
00059 st_data_t n;
00060
00061 st_lookup(tbl, (st_data_t)str, &n);
00062 if (n == 1) {
00063 st_delete(tbl, (st_data_t *)&str, 0);
00064 ruby_xfree((char *)str);
00065 }
00066 else {
00067 st_insert(tbl, (st_data_t)str, n-1);
00068 }
00069 }
00070 }
00071
00072 static void
00073 newobj_i(VALUE tpval, void *data)
00074 {
00075 struct traceobj_arg *arg = (struct traceobj_arg *)data;
00076 rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
00077 VALUE obj = rb_tracearg_object(tparg);
00078 VALUE path = rb_tracearg_path(tparg);
00079 VALUE line = rb_tracearg_lineno(tparg);
00080 VALUE mid = rb_tracearg_method_id(tparg);
00081 VALUE klass = rb_tracearg_defined_class(tparg);
00082 struct allocation_info *info;
00083 const char *path_cstr = RTEST(path) ? make_unique_str(arg->str_table, RSTRING_PTR(path), RSTRING_LEN(path)) : 0;
00084 VALUE class_path = (RTEST(klass) && !OBJ_FROZEN(klass)) ? rb_class_path_cached(klass) : Qnil;
00085 const char *class_path_cstr = RTEST(class_path) ? make_unique_str(arg->str_table, RSTRING_PTR(class_path), RSTRING_LEN(class_path)) : 0;
00086
00087 if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
00088 if (arg->keep_remains) {
00089 if (info->living) {
00090
00091 }
00092 }
00093
00094 delete_unique_str(arg->str_table, info->path);
00095 delete_unique_str(arg->str_table, info->class_path);
00096 }
00097 else {
00098 info = (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info));
00099 }
00100 info->living = 1;
00101 info->flags = RBASIC(obj)->flags;
00102 info->klass = RBASIC_CLASS(obj);
00103
00104 info->path = path_cstr;
00105 info->line = NUM2INT(line);
00106 info->mid = mid;
00107 info->class_path = class_path_cstr;
00108 info->generation = rb_gc_count();
00109 st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
00110 }
00111
00112 static void
00113 freeobj_i(VALUE tpval, void *data)
00114 {
00115 struct traceobj_arg *arg = (struct traceobj_arg *)data;
00116 rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
00117 VALUE obj = rb_tracearg_object(tparg);
00118 struct allocation_info *info;
00119
00120 if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
00121 if (arg->keep_remains) {
00122 info->living = 0;
00123 }
00124 else {
00125 st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info);
00126 delete_unique_str(arg->str_table, info->path);
00127 delete_unique_str(arg->str_table, info->class_path);
00128 ruby_xfree(info);
00129 }
00130 }
00131 }
00132
00133 static int
00134 free_keys_i(st_data_t key, st_data_t value, void *data)
00135 {
00136 ruby_xfree((void *)key);
00137 return ST_CONTINUE;
00138 }
00139
00140 static int
00141 free_values_i(st_data_t key, st_data_t value, void *data)
00142 {
00143 ruby_xfree((void *)value);
00144 return ST_CONTINUE;
00145 }
00146
00147 static struct traceobj_arg *tmp_trace_arg;
00148 static int tmp_keep_remains;
00149
00150 static struct traceobj_arg *
00151 get_traceobj_arg(void)
00152 {
00153 if (tmp_trace_arg == 0) {
00154 tmp_trace_arg = ALLOC_N(struct traceobj_arg, 1);
00155 tmp_trace_arg->running = 0;
00156 tmp_trace_arg->keep_remains = tmp_keep_remains;
00157 tmp_trace_arg->newobj_trace = 0;
00158 tmp_trace_arg->freeobj_trace = 0;
00159 tmp_trace_arg->object_table = st_init_numtable();
00160 tmp_trace_arg->str_table = st_init_strtable();
00161 }
00162 return tmp_trace_arg;
00163 }
00164
00165
00166
00167
00168
00169
00170
00171 static VALUE
00172 trace_object_allocations_start(VALUE self)
00173 {
00174 struct traceobj_arg *arg = get_traceobj_arg();
00175
00176 if (arg->running++ > 0) {
00177
00178 }
00179 else {
00180 if (arg->newobj_trace == 0) {
00181 arg->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg);
00182 arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg);
00183 }
00184 rb_tracepoint_enable(arg->newobj_trace);
00185 rb_tracepoint_enable(arg->freeobj_trace);
00186 }
00187
00188 return Qnil;
00189 }
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200 static VALUE
00201 trace_object_allocations_stop(VALUE self)
00202 {
00203 struct traceobj_arg *arg = get_traceobj_arg();
00204
00205 if (arg->running > 0) {
00206 arg->running--;
00207 }
00208
00209 if (arg->running == 0) {
00210 rb_tracepoint_disable(arg->newobj_trace);
00211 rb_tracepoint_disable(arg->freeobj_trace);
00212 arg->newobj_trace = 0;
00213 arg->freeobj_trace = 0;
00214 }
00215
00216 return Qnil;
00217 }
00218
00219
00220
00221
00222
00223
00224
00225 static VALUE
00226 trace_object_allocations_clear(VALUE self)
00227 {
00228 struct traceobj_arg *arg = get_traceobj_arg();
00229
00230
00231 st_foreach(arg->object_table, free_values_i, 0);
00232 st_clear(arg->object_table);
00233 st_foreach(arg->str_table, free_keys_i, 0);
00234 st_clear(arg->str_table);
00235
00236
00237
00238 return Qnil;
00239 }
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269 static VALUE
00270 trace_object_allocations(VALUE self)
00271 {
00272 trace_object_allocations_start(self);
00273 return rb_ensure(rb_yield, Qnil, trace_object_allocations_stop, self);
00274 }
00275
00276 int rb_bug_reporter_add(void (*func)(FILE *, void *), void *data);
00277 static int object_allocations_reporter_registered = 0;
00278
00279 static int
00280 object_allocations_reporter_i(st_data_t key, st_data_t val, st_data_t ptr)
00281 {
00282 FILE *out = (FILE *)ptr;
00283 VALUE obj = (VALUE)key;
00284 struct allocation_info *info = (struct allocation_info *)val;
00285
00286 fprintf(out, "-- %p (%s F: %p, ", (void *)obj, info->living ? "live" : "dead", (void *)info->flags);
00287 if (info->class_path) fprintf(out, "C: %s", info->class_path);
00288 else fprintf(out, "C: %p", (void *)info->klass);
00289 fprintf(out, "@%s:%lu", info->path ? info->path : "", info->line);
00290 if (!NIL_P(info->mid)) fprintf(out, " (%s)", rb_id2name(SYM2ID(info->mid)));
00291 fprintf(out, ")\n");
00292
00293 return ST_CONTINUE;
00294 }
00295
00296 static void
00297 object_allocations_reporter(FILE *out, void *ptr)
00298 {
00299 fprintf(out, "== object_allocations_reporter: START\n");
00300 if (tmp_trace_arg) {
00301 st_foreach(tmp_trace_arg->object_table, object_allocations_reporter_i, (st_data_t)out);
00302 }
00303 fprintf(out, "== object_allocations_reporter: END\n");
00304 }
00305
00306 static VALUE
00307 trace_object_allocations_debug_start(VALUE self)
00308 {
00309 tmp_keep_remains = 1;
00310 if (object_allocations_reporter_registered == 0) {
00311 object_allocations_reporter_registered = 1;
00312 rb_bug_reporter_add(object_allocations_reporter, 0);
00313 }
00314
00315 return trace_object_allocations_start(self);
00316 }
00317
00318 static struct allocation_info *
00319 lookup_allocation_info(VALUE obj)
00320 {
00321 if (tmp_trace_arg) {
00322 struct allocation_info *info;
00323 if (st_lookup(tmp_trace_arg->object_table, obj, (st_data_t *)&info)) {
00324 return info;
00325 }
00326 }
00327 return NULL;
00328 }
00329
00330 struct allocation_info *
00331 objspace_lookup_allocation_info(VALUE obj)
00332 {
00333 return lookup_allocation_info(obj);
00334 }
00335
00336
00337
00338
00339
00340
00341
00342
00343 static VALUE
00344 allocation_sourcefile(VALUE self, VALUE obj)
00345 {
00346 struct allocation_info *info = lookup_allocation_info(obj);
00347
00348 if (info && info->path) {
00349 return rb_str_new2(info->path);
00350 }
00351 else {
00352 return Qnil;
00353 }
00354 }
00355
00356
00357
00358
00359
00360
00361
00362
00363 static VALUE
00364 allocation_sourceline(VALUE self, VALUE obj)
00365 {
00366 struct allocation_info *info = lookup_allocation_info(obj);
00367
00368 if (info) {
00369 return INT2FIX(info->line);
00370 }
00371 else {
00372 return Qnil;
00373 }
00374 }
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394 static VALUE
00395 allocation_class_path(VALUE self, VALUE obj)
00396 {
00397 struct allocation_info *info = lookup_allocation_info(obj);
00398
00399 if (info && info->class_path) {
00400 return rb_str_new2(info->class_path);
00401 }
00402 else {
00403 return Qnil;
00404 }
00405 }
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427 static VALUE
00428 allocation_method_id(VALUE self, VALUE obj)
00429 {
00430 struct allocation_info *info = lookup_allocation_info(obj);
00431 if (info) {
00432 return info->mid;
00433 }
00434 else {
00435 return Qnil;
00436 }
00437 }
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459 static VALUE
00460 allocation_generation(VALUE self, VALUE obj)
00461 {
00462 struct allocation_info *info = lookup_allocation_info(obj);
00463 if (info) {
00464 return SIZET2NUM(info->generation);
00465 }
00466 else {
00467 return Qnil;
00468 }
00469 }
00470
00471 void
00472 Init_object_tracing(VALUE rb_mObjSpace)
00473 {
00474 #if 0
00475 rb_mObjSpace = rb_define_module("ObjectSpace");
00476 #endif
00477
00478 rb_define_module_function(rb_mObjSpace, "trace_object_allocations", trace_object_allocations, 0);
00479 rb_define_module_function(rb_mObjSpace, "trace_object_allocations_start", trace_object_allocations_start, 0);
00480 rb_define_module_function(rb_mObjSpace, "trace_object_allocations_stop", trace_object_allocations_stop, 0);
00481 rb_define_module_function(rb_mObjSpace, "trace_object_allocations_clear", trace_object_allocations_clear, 0);
00482
00483 rb_define_module_function(rb_mObjSpace, "trace_object_allocations_debug_start", trace_object_allocations_debug_start, 0);
00484
00485 rb_define_module_function(rb_mObjSpace, "allocation_sourcefile", allocation_sourcefile, 1);
00486 rb_define_module_function(rb_mObjSpace, "allocation_sourceline", allocation_sourceline, 1);
00487 rb_define_module_function(rb_mObjSpace, "allocation_class_path", allocation_class_path, 1);
00488 rb_define_module_function(rb_mObjSpace, "allocation_method_id", allocation_method_id, 1);
00489 rb_define_module_function(rb_mObjSpace, "allocation_generation", allocation_generation, 1);
00490 }
00491