/* Copyright (C) 2005-2011 Shugo Maeda and Charlie Savage Please see the LICENSE file for copyright and distribution information */ #include "ruby_prof.h" VALUE cMethodInfo; /* ================ Helper Functions =================*/ static VALUE figure_singleton_name(VALUE klass) { VALUE result = Qnil; /* We have come across a singleton object. First figure out what it is attached to.*/ VALUE attached = rb_iv_get(klass, "__attached__"); /* Is this a singleton class acting as a metaclass? */ if (BUILTIN_TYPE(attached) == T_CLASS) { result = rb_str_new2(""); } /* Is this for singleton methods on a module? */ else if (BUILTIN_TYPE(attached) == T_MODULE) { result = rb_str_new2(""); } /* Is this for singleton methods on an object? */ else if (BUILTIN_TYPE(attached) == T_OBJECT) { /* Make sure to get the super class so that we don't mistakenly grab a T_ICLASS which would lead to unknown method errors. */ #ifdef HAVE_RB_CLASS_SUPERCLASS // 1.9.3 VALUE super = rb_class_superclass(klass); #else # ifdef RCLASS_SUPER VALUE super = rb_class_real(RCLASS_SUPER(klass)); # else VALUE super = rb_class_real(RCLASS(klass)->super); # endif #endif result = rb_str_new2(""); } /* Ok, this could be other things like an array made put onto a singleton object (yeah, it happens, see the singleton objects test case). */ else { result = rb_inspect(klass); } return result; } static VALUE klass_name(VALUE klass) { VALUE result = Qnil; if (klass == 0 || klass == Qnil) { result = rb_str_new2("Global"); } else if (BUILTIN_TYPE(klass) == T_MODULE) { result = rb_inspect(klass); } else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON)) { result = figure_singleton_name(klass); } else if (BUILTIN_TYPE(klass) == T_CLASS) { result = rb_inspect(klass); } else { /* Should never happen. */ result = rb_str_new2("Unknown"); } return result; } static VALUE method_name(ID mid) { VALUE result; if (mid == ID_ALLOCATOR) result = rb_str_new2("allocate"); else if (mid == 0) result = rb_str_new2("[No method]"); else result = rb_String(ID2SYM(mid)); return result; } static VALUE full_name(VALUE klass, ID mid) { VALUE result = klass_name(klass); rb_str_cat2(result, "#"); rb_str_append(result, method_name(mid)); return result; } /* ================ Method Table =================*/ int method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2) { return (key1->klass != key2->klass) || (key1->mid != key2->mid); } st_index_t method_table_hash(prof_method_key_t *key) { return key->key; } struct st_hash_type type_method_hash = { method_table_cmp, method_table_hash }; st_table * method_table_create() { return st_init_table(&type_method_hash); } size_t method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val) { return st_insert(table, (st_data_t) key, (st_data_t) val); } prof_method_t * method_table_lookup(st_table *table, const prof_method_key_t* key) { st_data_t val; if (st_lookup(table, (st_data_t)key, &val)) { return (prof_method_t *) val; } else { return NULL; } } void method_table_free(st_table *table) { /* Don't free the contents since they are wrapped by Ruby objects! */ st_free_table(table); } /* ================ Method Info =================*/ /* Document-class: RubyProf::MethodInfo The RubyProf::MethodInfo class stores profiling data for a method. One instance of the RubyProf::MethodInfo class is created per method called per thread. Thus, if a method is called in two different thread then there will be two RubyProf::MethodInfo objects created. RubyProf::MethodInfo objects can be accessed via the RubyProf::Result object. */ prof_method_t* prof_method_create(prof_method_key_t *key, const char* source_file, int line) { prof_method_t *result = ALLOC(prof_method_t); result->object = Qnil; result->key = ALLOC(prof_method_key_t); method_key(result->key, key->klass, key->mid); result->call_infos = prof_call_infos_create(); if (source_file != NULL) { size_t len = strlen(source_file) + 1; char *buffer = ALLOC_N(char, len); MEMCPY(buffer, source_file, char, len); result->source_file = buffer; } else { result->source_file = source_file; } result->line = line; return result; } void prof_method_mark(prof_method_t *method) { rb_gc_mark(method->call_infos->object); rb_gc_mark(method->key->klass); } static void prof_method_free(prof_method_t *method) { if (method->source_file) { xfree((char*)method->source_file); } prof_call_infos_free(method->call_infos); xfree(method->key); xfree(method); } VALUE prof_method_wrap(prof_method_t *result) { if (result->object == Qnil) { result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_free, result); } return result->object; } static prof_method_t * get_prof_method(VALUE obj) { return (prof_method_t *) DATA_PTR(obj); } /* call-seq: line_no -> int returns the line number of the method */ static VALUE prof_method_line(VALUE self) { return rb_int_new(get_prof_method(self)->line); } /* call-seq: source_file => string return the source file of the method */ static VALUE prof_method_source_file(VALUE self) { const char* sf = get_prof_method(self)->source_file; if(!sf) { return rb_str_new2("ruby_runtime"); } else { return rb_str_new2(sf); } } /* call-seq: method_class -> klass Returns the Ruby klass that owns this method. */ static VALUE prof_method_klass(VALUE self) { prof_method_t *result = get_prof_method(self); return result->key->klass; } /* call-seq: method_id -> ID Returns the id of this method. */ static VALUE prof_method_id(VALUE self) { prof_method_t *result = get_prof_method(self); return ID2SYM(result->key->mid); } /* call-seq: klass_name -> string Returns the name of this method's class. Singleton classes will have the form . */ static VALUE prof_klass_name(VALUE self) { prof_method_t *method = get_prof_method(self); return klass_name(method->key->klass); } /* call-seq: method_name -> string Returns the name of this method in the format Object#method. Singletons methods will be returned in the format #method.*/ static VALUE prof_method_name(VALUE self) { prof_method_t *method = get_prof_method(self); return method_name(method->key->mid); } /* call-seq: full_name -> string Returns the full name of this method in the format Object#method.*/ static VALUE prof_full_name(VALUE self) { prof_method_t *method = get_prof_method(self); return full_name(method->key->klass, method->key->mid); } /* call-seq: call_infos -> Array of call_info Returns an array of call info objects that contain profiling information about the current method.*/ static VALUE prof_method_call_infos(VALUE self) { prof_method_t *method = get_prof_method(self); return prof_call_infos_wrap(method->call_infos); } void rp_init_method_info() { /* MethodInfo */ cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject); rb_undef_method(CLASS_OF(cMethodInfo), "new"); rb_define_method(cMethodInfo, "klass", prof_method_klass, 0); rb_define_method(cMethodInfo, "klass_name", prof_klass_name, 0); rb_define_method(cMethodInfo, "method_name", prof_method_name, 0); rb_define_method(cMethodInfo, "full_name", prof_full_name, 0); rb_define_method(cMethodInfo, "method_id", prof_method_id, 0); rb_define_method(cMethodInfo, "source_file", prof_method_source_file,0); rb_define_method(cMethodInfo, "line", prof_method_line, 0); rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0); }