ext/ruby_prof/rp_method.c in ruby-prof-0.18.0 vs ext/ruby_prof/rp_method.c in ruby-prof-1.0.0
- old
+ new
@@ -1,435 +1,347 @@
/* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
Please see the LICENSE file for copyright and distribution information */
-#include "ruby_prof.h"
+#include "rp_allocation.h"
+#include "rp_call_info.h"
+#include "rp_method.h"
-#define RP_REL_GET(r, off) ((r) & (1 << (off)))
-#define RP_REL_SET(r, off) \
-do { \
- r |= (1 << (off)); \
-} while (0)
+VALUE cRpMethodInfo;
-VALUE cMethodInfo;
-
/* ================ Helper Functions =================*/
-static VALUE
-figure_singleton_name(VALUE klass)
+VALUE
+resolve_klass(VALUE klass, unsigned int *klass_flags)
{
- volatile VALUE attached, super;
- volatile VALUE attached_str, super_str;
- volatile VALUE result = Qnil;
+ VALUE result = klass;
- /* We have come across a singleton object. First
- figure out what it is attached to.*/
- attached = rb_iv_get(klass, "__attached__");
-
- /* Is this a singleton class acting as a metaclass? */
- if (BUILTIN_TYPE(attached) == T_CLASS)
+ if (klass == 0 || klass == Qnil)
{
- attached_str = rb_class_name(attached);
- result = rb_str_new2("<Class::");
- rb_str_append(result, attached_str);
- rb_str_cat2(result, ">");
+ result = Qnil;
}
-
- /* Is this for singleton methods on a module? */
- else if (BUILTIN_TYPE(attached) == T_MODULE)
+ else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
{
- attached_str = rb_class_name(attached);
- result = rb_str_new2("<Module::");
- rb_str_append(result, attached_str);
- rb_str_cat2(result, ">");
- }
+ /* We have come across a singleton object. First
+ figure out what it is attached to.*/
+ VALUE attached = rb_iv_get(klass, "__attached__");
- /* 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. */
- super = rb_class_superclass(klass);
- super_str = rb_class_name(super);
- result = rb_str_new2("<Object::");
- rb_str_append(result, super_str);
- rb_str_cat2(result, ">");
+ /* Is this a singleton class acting as a metaclass? */
+ if (BUILTIN_TYPE(attached) == T_CLASS)
+ {
+ *klass_flags |= kClassSingleton;
+ result = attached;
+ }
+ /* Is this for singleton methods on a module? */
+ else if (BUILTIN_TYPE(attached) == T_MODULE)
+ {
+ *klass_flags |= kModuleSingleton;
+ result = attached;
+ }
+ /* Is this for singleton methods on an object? */
+ else if (BUILTIN_TYPE(attached) == T_OBJECT)
+ {
+ *klass_flags |= kObjectSingleton;
+ result = rb_class_superclass(klass);
+ }
+ /* 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
+ {
+ *klass_flags |= kOtherSingleton;
+ result = klass;
+ }
}
-
- /* 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
+ /* Is this an include for a module? If so get the actual
+ module class since we want to combine all profiling
+ results for that module. */
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
{
- result = rb_any_to_s(klass);
+ unsigned int dummy;
+ *klass_flags |= kModuleIncludee;
+ result = resolve_klass(RBASIC(klass)->klass, &dummy);
}
-
return result;
}
-static VALUE
-klass_name(VALUE klass)
+VALUE
+resolve_klass_name(VALUE klass, unsigned int* klass_flags)
{
- volatile VALUE result = Qnil;
+ VALUE result = Qnil;
- if (klass == 0 || klass == Qnil)
+ if (klass == Qnil)
{
result = rb_str_new2("[global]");
}
- else if (BUILTIN_TYPE(klass) == T_MODULE)
+ else if (*klass_flags & kOtherSingleton)
{
- result = rb_class_name(klass);
+ result = rb_any_to_s(klass);
}
- else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
+ else
{
- result = figure_singleton_name(klass);
- }
- else if (BUILTIN_TYPE(klass) == T_CLASS)
- {
result = rb_class_name(klass);
}
- else
- {
- /* Should never happen. */
- result = rb_str_new2("[unknown]");
- }
return result;
}
-static VALUE
-method_name(ID mid)
+st_data_t
+method_key(VALUE klass, VALUE msym)
{
- volatile VALUE name = Qnil;
+ VALUE resolved_klass = klass;
- if (RTEST(mid)) {
- name = rb_id2str(mid);
- return rb_str_dup(name);
- } else {
- return rb_str_new2("[no method]");
+ /* Is this an include for a module? If so get the actual
+ module class since we want to combine all profiling
+ results for that module. */
+ if (klass == 0 || klass == Qnil)
+ {
+ resolved_klass = Qnil;
}
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
+ {
+ resolved_klass = RBASIC(klass)->klass;
+ }
+
+ return (resolved_klass << 4) + (msym);
}
-static VALUE
-full_name(VALUE klass, ID mid)
+/* ====== Allocation Table ====== */
+st_table*
+allocations_table_create()
{
- volatile VALUE klass_str, method_str;
- volatile VALUE result = Qnil;
+ return st_init_numtable();
+}
- klass_str = klass_name(klass);
- method_str = method_name(mid);
-
- result = rb_str_dup(klass_str);
- rb_str_cat2(result, "#");
- rb_str_append(result, method_str);
-
- return result;
+static int
+allocations_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
+{
+ prof_allocation_free((prof_allocation_t*)value);
+ return ST_CONTINUE;
}
-static VALUE
-source_klass_name(VALUE source_klass)
+static int
+prof_method_collect_allocations(st_data_t key, st_data_t value, st_data_t result)
{
- volatile VALUE klass_str;
- volatile VALUE result = Qnil;
-
- if (RTEST(source_klass)) {
- klass_str = rb_class_name(source_klass);
- result = rb_str_dup(klass_str);
- } else {
- result = rb_str_new2("[global]");
- }
-
- return result;
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
+ VALUE arr = (VALUE)result;
+ rb_ary_push(arr, prof_allocation_wrap(allocation));
+ return ST_CONTINUE;
}
-static VALUE
-calltree_name(VALUE source_klass, int relation, ID mid)
+static int
+prof_method_mark_allocations(st_data_t key, st_data_t value, st_data_t data)
{
- volatile VALUE klass_str, klass_path, joiner;
- volatile VALUE method_str;
- volatile VALUE result = Qnil;
-
- klass_str = source_klass_name(source_klass);
- method_str = method_name(mid);
-
- klass_path = rb_str_split(klass_str, "::");
- joiner = rb_str_new2("/");
- result = rb_ary_join(klass_path, joiner);
-
- rb_str_cat2(result, "::");
-
- if (RP_REL_GET(relation, kObjectSingleton)) {
- rb_str_cat2(result, "*");
- }
-
- if (RP_REL_GET(relation, kModuleSingleton)) {
- rb_str_cat2(result, "^");
- }
-
- rb_str_append(result, method_str);
-
- return result;
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
+ prof_allocation_mark(allocation);
+ return ST_CONTINUE;
}
void
-method_key(prof_method_key_t* key, VALUE klass, ID mid)
+allocations_table_free(st_table* table)
{
- /* Is this an include for a module? If so get the actual
- module class since we want to combine all profiling
- results for that module. */
- if (klass != 0 && BUILTIN_TYPE(klass) == T_ICLASS) {
- klass = RBASIC(klass)->klass;
- }
-
- key->klass = klass;
- key->mid = mid;
- key->key = (klass << 4) + (mid << 2);
+ st_foreach(table, allocations_table_free_iterator, 0);
+ st_free_table(table);
}
/* ================ prof_method_t =================*/
+static prof_method_t*
+prof_get_method(VALUE self)
+{
+ /* Can't use Data_Get_Struct because that triggers the event hook
+ ending up in endless recursion. */
+ prof_method_t* result = DATA_PTR(self);
+ if (!result)
+ rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
+
+ return result;
+}
+
prof_method_t*
-prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
+prof_method_create(VALUE klass, VALUE msym, VALUE source_file, int source_line)
{
prof_method_t *result = ALLOC(prof_method_t);
+ result->key = method_key(klass, msym);
+ result->klass_flags = 0;
- result->key = ALLOC(prof_method_key_t);
- method_key(result->key, klass, mid);
+ /* Note we do not call resolve_klass_name now because that causes an object allocation that shows up
+ in the allocation results so we want to avoid it until after the profile run is complete. */
+ result->klass = resolve_klass(klass, &result->klass_flags);
+ result->klass_name = Qnil;
+ result->method_name = msym;
+ result->measurement = prof_measurement_create();
- result->excluded = 0;
- result->recursive = 0;
+ result->root = false;
+ result->excluded = false;
- result->call_infos = prof_call_infos_create();
+ result->parent_call_infos = method_table_create();
+ result->child_call_infos = method_table_create();
+ result->allocations_table = allocations_table_create();
+
result->visits = 0;
+ result->recursive = false;
result->object = Qnil;
- if (source_file != NULL)
- {
- size_t len = strlen(source_file) + 1;
- char *buffer = ALLOC_N(char, len);
+ result->source_file = source_file;
+ result->source_line = source_line;
- MEMCPY(buffer, source_file, char, len);
- result->source_file = buffer;
- } else {
- result->source_file = source_file;
- }
-
- result->source_klass = Qnil;
- result->line = line;
-
- result->resolved = 0;
- result->relation = 0;
-
return result;
}
prof_method_t*
-prof_method_create_excluded(VALUE klass, ID mid)
+prof_method_create_excluded(VALUE klass, VALUE msym)
{
- prof_method_t *result = ALLOC(prof_method_t);
-
- result->key = ALLOC(prof_method_key_t);
- method_key(result->key, klass, mid);
-
- /* Invisible with this flag set. */
+ prof_method_t* result = prof_method_create(klass, msym, Qnil, 0);
result->excluded = 1;
- result->recursive = 0;
+ return result;
+}
- result->call_infos = 0;
- result->visits = 0;
+static int
+prof_method_collect_call_infos(st_data_t key, st_data_t value, st_data_t result)
+{
+ prof_call_info_t* call_info = (prof_call_info_t*)value;
+ VALUE arr = (VALUE)result;
+ rb_ary_push(arr, prof_call_info_wrap(call_info));
+ return ST_CONTINUE;
+}
- result->object = Qnil;
- result->source_klass = Qnil;
- result->line = 0;
+static int
+prof_method_mark_call_infos(st_data_t key, st_data_t value, st_data_t data)
+{
+ prof_call_info_t* call_info = (prof_call_info_t*)value;
+ prof_call_info_mark(call_info);
+ return ST_CONTINUE;
+}
- result->resolved = 0;
- result->relation = 0;
+static int
+call_infos_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
+{
+ prof_call_info_free((prof_call_info_t*)value);
+ return ST_CONTINUE;
+}
- return result;
+void
+call_info_table_free(st_table* table)
+{
+ st_foreach(table, call_infos_free_iterator, 0);
+ st_free_table(table);
}
/* The underlying c structures are freed when the parent profile is freed.
However, on shutdown the Ruby GC frees objects in any will-nilly order.
That means the ruby thread object wrapping the c thread struct may
be freed before the parent profile. Thus we add in a free function
for the garbage collector so that if it does get called will nil
out our Ruby object reference.*/
static void
-prof_method_ruby_gc_free(prof_method_t* method)
+prof_method_ruby_gc_free(void *data)
{
- /* Has this thread object been accessed by Ruby? If
+ prof_method_t* method = (prof_method_t*)data;
+
+ /* Has this thread object been accessed by Ruby? If
yes clean it up so to avoid a segmentation fault. */
if (method->object != Qnil)
{
RDATA(method->object)->data = NULL;
RDATA(method->object)->dfree = NULL;
RDATA(method->object)->dmark = NULL;
}
method->object = Qnil;
+ method->klass_name = Qnil;
+ method->method_name = Qnil;
}
static void
prof_method_free(prof_method_t* method)
{
prof_method_ruby_gc_free(method);
+ allocations_table_free(method->allocations_table);
- if (method->call_infos) {
- prof_call_infos_free(method->call_infos);
- xfree(method->call_infos);
- }
+ /* Remember call infos are referenced by their parent method and child method, so we only want
+ to iterate over one of them to avoid a double freeing */
+ call_info_table_free(method->parent_call_infos);
+ xfree(method->child_call_infos);
- xfree(method->key);
+ xfree(method);
+}
- method->key = NULL;
- method->source_klass = Qnil;
-
- xfree(method);
+size_t
+prof_method_size(const void *data)
+{
+ return sizeof(prof_method_t);
}
void
-prof_method_mark(prof_method_t *method)
+prof_method_mark(void *data)
{
- if (method->key->klass) {
- rb_gc_mark(method->key->klass);
- }
+ prof_method_t* method = (prof_method_t*)data;
+ rb_gc_mark(method->klass_name);
+ rb_gc_mark(method->method_name);
+
+ if (method->klass != Qnil)
+ rb_gc_mark(method->klass);
- if (method->source_klass) {
- rb_gc_mark(method->source_klass);
- }
-
- if (method->object) {
+ if (method->object != Qnil)
rb_gc_mark(method->object);
- }
- if (method->call_infos) {
- prof_call_infos_mark(method->call_infos);
- }
+ prof_measurement_mark(method->measurement);
+
+ st_foreach(method->parent_call_infos, prof_method_mark_call_infos, 0);
+ st_foreach(method->child_call_infos, prof_method_mark_call_infos, 0);
+ st_foreach(method->allocations_table, prof_method_mark_allocations, 0);
}
-static VALUE
-resolve_source_klass(prof_method_t* method)
+static const rb_data_type_t method_info_type =
{
- volatile VALUE klass, next_klass;
- volatile VALUE attached;
- unsigned int relation;
-
- /* We want to group methods according to their source-level
- definitions, not their implementation class. Follow module
- inclusions and singleton classes back to a meaningful root
- while keeping track of these relationships. */
-
- if (method->resolved) {
- return method->source_klass;
- }
-
- klass = method->key->klass;
- relation = 0;
-
- while (1)
+ .wrap_struct_name = "MethodInfo",
+ .function =
{
- /* This is a global/unknown class */
- if (klass == 0 || klass == Qnil)
- break;
+ .dmark = prof_method_mark,
+ .dfree = prof_method_ruby_gc_free,
+ .dsize = prof_method_size,
+ },
+ .data = NULL,
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
+};
- /* Is this a singleton class? (most common case) */
- if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
- {
- /* We have come across a singleton object. First
- figure out what it is attached to.*/
- attached = rb_iv_get(klass, "__attached__");
-
- /* Is this a singleton class acting as a metaclass?
- Or for singleton methods on a module? */
- if (BUILTIN_TYPE(attached) == T_CLASS ||
- BUILTIN_TYPE(attached) == T_MODULE)
- {
- RP_REL_SET(relation, kModuleSingleton);
- klass = attached;
- }
- /* Is this for singleton methods on an object? */
- else if (BUILTIN_TYPE(attached) == T_OBJECT)
- {
- RP_REL_SET(relation, kObjectSingleton);
- next_klass = rb_class_superclass(klass);
- klass = next_klass;
- }
- /* This is a singleton of an instance of a builtin type. */
- else
- {
- RP_REL_SET(relation, kObjectSingleton);
- next_klass = rb_class_superclass(klass);
- klass = next_klass;
- }
- }
- /* Is this an include for a module? If so get the actual
- module class since we want to combine all profiling
- results for that module. */
- else if (BUILTIN_TYPE(klass) == T_ICLASS)
- {
- RP_REL_SET(relation, kModuleIncludee);
- next_klass = RBASIC(klass)->klass;
- klass = next_klass;
- }
- /* No transformations apply; so bail. */
- else
- {
- break;
- }
- }
-
- method->resolved = 1;
- method->relation = relation;
- method->source_klass = klass;
-
- return klass;
+static VALUE
+prof_method_allocate(VALUE klass)
+{
+ prof_method_t* method_data = prof_method_create(Qnil, Qnil, Qnil, 0);
+ method_data->object = prof_method_wrap(method_data);
+ return method_data->object;
}
VALUE
-prof_method_wrap(prof_method_t *result)
+prof_method_wrap(prof_method_t *method)
{
- if (result->object == Qnil) {
- result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_ruby_gc_free, result);
+ if (method->object == Qnil)
+ {
+ method->object = TypedData_Wrap_Struct(cRpMethodInfo, &method_info_type, method);
}
- return result->object;
+ return method->object;
}
-static prof_method_t *
-get_prof_method(VALUE self)
+prof_method_t *
+prof_method_get(VALUE self)
{
/* Can't use Data_Get_Struct because that triggers the event hook
ending up in endless recursion. */
prof_method_t* result = DATA_PTR(self);
- if (!result) {
+ if (!result)
+ {
rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
}
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);
+ return st_init_numtable();
}
static int
method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
{
@@ -443,22 +355,25 @@
st_foreach(table, method_table_free_iterator, 0);
st_free_table(table);
}
size_t
-method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
+method_table_insert(st_table *table, st_data_t key, prof_method_t *val)
{
- return st_insert(table, (st_data_t) key, (st_data_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)
+method_table_lookup(st_table *table, st_data_t key)
{
st_data_t val;
- if (st_lookup(table, (st_data_t)key, &val)) {
+ if (st_lookup(table, (st_data_t)key, &val))
+ {
return (prof_method_t *) val;
- } else {
+ }
+ else
+ {
return NULL;
}
}
/* ================ Method Info =================*/
@@ -470,161 +385,258 @@
created. RubyProf::MethodInfo objects can be accessed via
the RubyProf::Profile object.
*/
/* call-seq:
- line_no -> int
+ callers -> array
- returns the line number of the method */
+Returns an array of call info objects that called this method (ie, parents).*/
static VALUE
-prof_method_line(VALUE self)
+prof_method_callers(VALUE self)
{
- int line = get_prof_method(self)->line;
- return rb_int_new(line);
+ prof_method_t* method = prof_get_method(self);
+ VALUE result = rb_ary_new();
+ st_foreach(method->parent_call_infos, prof_method_collect_call_infos, result);
+ return result;
}
/* call-seq:
- source_file => string
+ callees -> array
-return the source file of the method
-*/
-static VALUE prof_method_source_file(VALUE self)
+Returns an array of call info objects that this method called (ie, children).*/
+static VALUE
+prof_method_callees(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);
- }
+ prof_method_t* method = prof_get_method(self);
+ VALUE result = rb_ary_new();
+ st_foreach(method->child_call_infos, prof_method_collect_call_infos, result);
+ return result;
}
+/* call-seq:
+ allocations -> array
+Returns an array of allocation information.*/
+static VALUE
+prof_method_allocations(VALUE self)
+{
+ prof_method_t* method = prof_get_method(self);
+ VALUE result = rb_ary_new();
+ st_foreach(method->allocations_table, prof_method_collect_allocations, result);
+ return result;
+}
+
/* call-seq:
- method_class -> klass
+ called -> Measurement
-Returns the Ruby klass that owns this method. */
+Returns the measurement associated with this method. */
static VALUE
-prof_method_klass(VALUE self)
+prof_method_measurement(VALUE self)
{
- prof_method_t *result = get_prof_method(self);
- return result->key->klass;
+ prof_method_t* method = prof_get_method(self);
+ return prof_measurement_wrap(method->measurement);
}
/* call-seq:
- method_id -> ID
+ source_file => string
-Returns the id of this method. */
+return the source file of the method
+*/
+static VALUE prof_method_source_file(VALUE self)
+{
+ prof_method_t* method = prof_method_get(self);
+ return method->source_file;
+}
+
+/* call-seq:
+ line_no -> int
+
+ returns the line number of the method */
static VALUE
-prof_method_id(VALUE self)
+prof_method_line(VALUE self)
{
- prof_method_t *result = get_prof_method(self);
- return ID2SYM(result->key->mid);
+ prof_method_t* method = prof_method_get(self);
+ return INT2FIX(method->source_line);
}
/* call-seq:
klass_name -> string
Returns the name of this method's class. Singleton classes
will have the form <Object::Object>. */
static VALUE
-prof_klass_name(VALUE self)
+prof_method_klass_name(VALUE self)
{
- prof_method_t *method = get_prof_method(self);
- return klass_name(method->key->klass);
+ prof_method_t *method = prof_method_get(self);
+ if (method->klass_name == Qnil)
+ method->klass_name = resolve_klass_name(method->klass, &method->klass_flags);
+
+ return method->klass_name;
}
/* call-seq:
- method_name -> string
+ klass_flags -> integer
-Returns the name of this method in the format Object#method. Singletons
-methods will be returned in the format <Object::Object>#method.*/
+Returns the klass flags */
static VALUE
-prof_method_name(VALUE self)
+prof_method_klass_flags(VALUE self)
{
- prof_method_t *method = get_prof_method(self);
- return method_name(method->key->mid);
+ prof_method_t* method = prof_method_get(self);
+ return INT2FIX(method->klass_flags);
}
/* call-seq:
- full_name -> string
+ method_name -> string
-Returns the full name of this method in the format Object#method.*/
+Returns the name of this method in the format Object#method. Singletons
+methods will be returned in the format <Object::Object>#method.*/
static VALUE
-prof_full_name(VALUE self)
+prof_method_name(VALUE self)
{
- prof_method_t *method = get_prof_method(self);
- return full_name(method->key->klass, method->key->mid);
+ prof_method_t *method = prof_method_get(self);
+ return method->method_name;
}
/* call-seq:
- call_infos -> Array of call_info
+ root? -> boolean
-Returns an array of call info objects that contain profiling information
-about the current method.*/
+ Returns the true if this method is at the top of the call stack */
static VALUE
-prof_method_call_infos(VALUE self)
+prof_method_root(VALUE self)
{
- prof_method_t *method = get_prof_method(self);
- if (method->call_infos->object == Qnil) {
- method->call_infos->object = prof_call_infos_wrap(method->call_infos);
- }
- return method->call_infos->object;
+ prof_method_t *method = prof_method_get(self);
+ return method->root ? Qtrue : Qfalse;
}
/* call-seq:
recursive? -> boolean
- Returns the true if this method is recursive */
+ Returns the true if this method is recursively invoked */
static VALUE
prof_method_recursive(VALUE self)
{
- prof_method_t *method = get_prof_method(self);
- return method->recursive ? Qtrue : Qfalse;
+ prof_method_t* method = prof_method_get(self);
+ return method->recursive ? Qtrue : Qfalse;
}
/* call-seq:
- source_klass -> klass
+ excluded? -> boolean
-Returns the Ruby klass of the natural source-level definition. */
+ Returns the true if this method was excluded */
static VALUE
-prof_source_klass(VALUE self)
+prof_method_excluded(VALUE self)
{
- prof_method_t *method = get_prof_method(self);
- return resolve_source_klass(method);
+ prof_method_t* method = prof_method_get(self);
+ return method->excluded ? Qtrue : Qfalse;
}
-/* call-seq:
- calltree_name -> string
+/* :nodoc: */
+static VALUE
+prof_method_dump(VALUE self)
+{
+ prof_method_t* method_data = DATA_PTR(self);
+ VALUE result = rb_hash_new();
-Returns the full name of this method in the calltree format.*/
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_method_klass_name(self));
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(method_data->klass_flags));
+ rb_hash_aset(result, ID2SYM(rb_intern("method_name")), method_data->method_name);
+ rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(method_data->key));
+ rb_hash_aset(result, ID2SYM(rb_intern("root")), prof_method_root(self));
+ rb_hash_aset(result, ID2SYM(rb_intern("recursive")), prof_method_recursive(self));
+ rb_hash_aset(result, ID2SYM(rb_intern("excluded")), prof_method_excluded(self));
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), method_data->source_file);
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(method_data->source_line));
+
+ rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(method_data->measurement));
+
+ rb_hash_aset(result, ID2SYM(rb_intern("callers")), prof_method_callers(self));
+ rb_hash_aset(result, ID2SYM(rb_intern("callees")), prof_method_callees(self));
+
+ rb_hash_aset(result, ID2SYM(rb_intern("allocations")), prof_method_allocations(self));
+
+ return result;
+}
+
+/* :nodoc: */
static VALUE
-prof_calltree_name(VALUE self)
+prof_method_load(VALUE self, VALUE data)
{
- prof_method_t *method = get_prof_method(self);
- volatile VALUE source_klass = resolve_source_klass(method);
- return calltree_name(source_klass, method->relation, method->key->mid);
+ prof_method_t* method_data = RDATA(self)->data;
+ method_data->object = self;
+
+ method_data->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
+ method_data->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
+ method_data->method_name = rb_hash_aref(data, ID2SYM(rb_intern("method_name")));
+ method_data->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
+
+ method_data->root = rb_hash_aref(data, ID2SYM(rb_intern("root"))) == Qtrue ? true : false;
+ method_data->recursive = rb_hash_aref(data, ID2SYM(rb_intern("recursive"))) == Qtrue ? true : false;
+ method_data->excluded = rb_hash_aref(data, ID2SYM(rb_intern("excluded"))) == Qtrue ? true : false;
+
+ method_data->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
+ method_data->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
+
+ VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
+ method_data->measurement = prof_get_measurement(measurement);
+
+ VALUE callers = rb_hash_aref(data, ID2SYM(rb_intern("callers")));
+ for (int i = 0; i < rb_array_len(callers); i++)
+ {
+ VALUE call_info = rb_ary_entry(callers, i);
+ prof_call_info_t *call_info_data = prof_get_call_info(call_info);
+ st_data_t key = call_info_data->parent ? call_info_data->parent->key : method_key(Qnil, 0);
+ call_info_table_insert(method_data->parent_call_infos, key, call_info_data);
+ }
+
+ VALUE callees = rb_hash_aref(data, ID2SYM(rb_intern("callees")));
+ for (int i = 0; i < rb_array_len(callees); i++)
+ {
+ VALUE call_info = rb_ary_entry(callees, i);
+ prof_call_info_t *call_info_data = prof_get_call_info(call_info);
+
+ st_data_t key = call_info_data->method ? call_info_data->method->key : method_key(Qnil, 0);
+ call_info_table_insert(method_data->child_call_infos, key, call_info_data);
+ }
+
+ VALUE allocations = rb_hash_aref(data, ID2SYM(rb_intern("allocations")));
+ for (int i = 0; i < rb_array_len(allocations); i++)
+ {
+ VALUE allocation = rb_ary_entry(allocations, i);
+ prof_allocation_t* allocation_data = prof_allocation_get(allocation);
+
+ st_insert(method_data->allocations_table, allocation_data->key, (st_data_t)allocation_data);
+ }
+ return data;
}
void rp_init_method_info()
{
/* MethodInfo */
- cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
- rb_undef_method(CLASS_OF(cMethodInfo), "new");
+ cRpMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cData);
+ rb_undef_method(CLASS_OF(cRpMethodInfo), "new");
+ rb_define_alloc_func(cRpMethodInfo, prof_method_allocate);
- 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(cRpMethodInfo, "klass_name", prof_method_klass_name, 0);
+ rb_define_method(cRpMethodInfo, "klass_flags", prof_method_klass_flags, 0);
- rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
- rb_define_method(cMethodInfo, "source_klass", prof_source_klass, 0);
- rb_define_method(cMethodInfo, "source_file", prof_method_source_file, 0);
- rb_define_method(cMethodInfo, "line", prof_method_line, 0);
+ rb_define_method(cRpMethodInfo, "method_name", prof_method_name, 0);
+
+ rb_define_method(cRpMethodInfo, "callers", prof_method_callers, 0);
+ rb_define_method(cRpMethodInfo, "callees", prof_method_callees, 0);
+ rb_define_method(cRpMethodInfo, "allocations", prof_method_allocations, 0);
- rb_define_method(cMethodInfo, "recursive?", prof_method_recursive, 0);
- rb_define_method(cMethodInfo, "calltree_name", prof_calltree_name, 0);
+ rb_define_method(cRpMethodInfo, "measurement", prof_method_measurement, 0);
+
+ rb_define_method(cRpMethodInfo, "source_file", prof_method_source_file, 0);
+ rb_define_method(cRpMethodInfo, "line", prof_method_line, 0);
+
+ rb_define_method(cRpMethodInfo, "root?", prof_method_root, 0);
+ rb_define_method(cRpMethodInfo, "recursive?", prof_method_recursive, 0);
+ rb_define_method(cRpMethodInfo, "excluded?", prof_method_excluded, 0);
+
+ rb_define_method(cRpMethodInfo, "_dump_data", prof_method_dump, 0);
+ rb_define_method(cRpMethodInfo, "_load_data", prof_method_load, 1);
}