diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2008-04-28 16:44:53 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2008-04-28 16:44:53 +0100 |
commit | c3ba72816cee6507e2b8fefcddda2eb2b3e9bbd4 (patch) | |
tree | 456aa23c36440c06b63caf89c88e76df12d8ba6f | |
parent | 9db15748ff771cb9464742d78df56bbfb41f087d (diff) |
Add a pie-chart to allocation callstack tooltip.
Include a pie-chart to visualize the fraction of the calls for each level
of the callstack.
-rw-r--r-- | src/allocators-store.c | 163 | ||||
-rw-r--r-- | src/allocators.c | 4 | ||||
-rw-r--r-- | src/allocators.h | 10 | ||||
-rw-r--r-- | src/summary-chart.c | 29 | ||||
-rw-r--r-- | src/summary-view.c | 181 | ||||
-rw-r--r-- | src/summary.c | 9 | ||||
-rw-r--r-- | src/summary.h | 2 |
7 files changed, 344 insertions, 54 deletions
diff --git a/src/allocators-store.c b/src/allocators-store.c index e7d3a4d..0293c0f 100644 --- a/src/allocators-store.c +++ b/src/allocators-store.c @@ -22,9 +22,16 @@ #include "odin.h" #include "allocators.h" +#include <string.h> + +#ifndef _ +#define _(x) x +#endif + struct _allocators_store { GObject object; + gboolean cumulative; AllocatorsStoreSort sort; GCompareFunc sum_allocators_cmp; GPtrArray *allocators; @@ -38,6 +45,7 @@ struct _allocators_store { GList *filters; Chunk *sum_allocator_chunks; + Chunk *list_allocator_chunks; guint update; }; @@ -59,6 +67,39 @@ G_DEFINE_TYPE_WITH_CODE (AllocatorsStore, allocators_store, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, allocators_store_tree_model_init)) +enum { + PROP_0=0, + PROP_CUMULATIVE +}; + +static void +allocators_store_set_property (GObject *obj, guint id, const GValue *v, GParamSpec *spec) +{ + AllocatorsStore *self = (AllocatorsStore *) obj; + switch (id) { + case PROP_CUMULATIVE: + self->cumulative = g_value_get_boolean (v); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, spec); + break; + } +} + +static void +allocators_store_get_property (GObject *obj, guint id, GValue *v, GParamSpec *spec) +{ + AllocatorsStore *self = (AllocatorsStore *) obj; + switch (id) { + case PROP_CUMULATIVE: + g_value_set_boolean (v, self->cumulative); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, spec); + break; + } +} + static struct _sum_allocator * _sum_allocator_alloc (AllocatorsStore *store) @@ -86,6 +127,32 @@ _sum_allocator_alloc (AllocatorsStore *store) return mem; } +static GSList * +_list_allocator_alloc (AllocatorsStore *store) +{ + gpointer mem; + gsize size = sizeof (GSList); + +#ifndef G_ENABLE_DEBUG + Chunk *c = store->list_allocator_chunks; + if (c == NULL || c->used + size > c->size) { + guint len = size * (32<<10); + c = g_malloc (sizeof (Chunk) + len); + c->size = len; + c->used = 0; + c->next = store->list_allocator_chunks; + store->list_allocator_chunks = c; + } + + mem = c->mem + c->used; + c->used += size; +#else + mem = g_malloc (size); +#endif + + return mem; +} + static gint _sum_allocators_cmp_by_size (gconstpointer A, gconstpointer B) { @@ -160,6 +227,14 @@ allocators_store_finalize (GObject *obj) c = next; } + c = self->list_allocator_chunks; + self->list_allocator_chunks = NULL; + while (c != NULL) { + Chunk *next = c->next; + g_free (c); + c = next; + } + g_ptr_array_free (self->allocators, TRUE); g_free (self->ht.nodes); @@ -171,7 +246,20 @@ allocators_store_class_init (AllocatorsStoreClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; + object_class->set_property = allocators_store_set_property; + object_class->get_property = allocators_store_get_property; object_class->finalize = allocators_store_finalize; + + g_object_class_install_property (object_class, + PROP_CUMULATIVE, + g_param_spec_boolean ("cumulative", + _("Cumulative"), + _("Cumulative"), + TRUE, + G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NAME | + G_PARAM_READWRITE)); } @@ -358,9 +446,11 @@ allocators_store_init (AllocatorsStore *self) } AllocatorsStore * -allocators_store_new (void) +allocators_store_new (gboolean cumulative) { - return g_object_new (allocators_store_get_type (), NULL); + return g_object_new (allocators_store_get_type (), + "cumulative", cumulative, + NULL); } void @@ -390,6 +480,12 @@ allocators_store_set_sort (AllocatorsStore *store, AllocatorsStoreSort sort) allocators_store_update (store); } +gboolean +allocators_store_is_cumulative (AllocatorsStore *store) +{ + return store->cumulative; +} + AllocatorsStoreSort allocators_store_get_sort (AllocatorsStore *store) { @@ -457,6 +553,7 @@ allocators_store_new_sum (AllocatorsStore *store, const gchar *fn) sum->sorted = FALSE; sum->blocks = NULL; sum->largest = NULL; + sum->largest_size = 0; sum->filter_serial = 0; sum->filtered = FALSE; @@ -466,6 +563,8 @@ allocators_store_new_sum (AllocatorsStore *store, const gchar *fn) sum->ht_next = store->ht.nodes[index]; store->ht.nodes[index] = sum; + memset (sum->ht, 0, sizeof (sum->ht)); + return sum; } @@ -473,6 +572,7 @@ void allocators_store_reset_sums (AllocatorsStore *store) { struct _sum_allocator *sum; + Chunk *c; if (store->update) { g_source_remove (store->update); @@ -491,6 +591,17 @@ allocators_store_reset_sums (AllocatorsStore *store) sum->sorted = FALSE; sum->largest = NULL; + sum->largest_size = 0; + + memset (sum->ht, 0, sizeof (sum->ht)); + } + + c = store->list_allocator_chunks; + store->list_allocator_chunks = NULL; + while (c != NULL) { + Chunk *next = c->next; + g_free (c); + c = next; } } @@ -643,10 +754,24 @@ allocators_store_add_filter (AllocatorsStore *store, return TRUE; } +static void +_sum_add_allocator (AllocatorsStore *store, struct _sum_allocator *sum, Allocator *A) +{ + guint index = ((gulong) A ^ 6017773UL) % G_N_ELEMENTS (sum->ht); + GSList *l = sum->ht[index]; + while (l != NULL && l->data != A) + l = l->next; + if (l == NULL) { + l = _list_allocator_alloc (store); + l->data = A; + l->next = sum->ht[index]; + sum->ht[index] = l; + } +} + void allocators_store_update_from_allocators (AllocatorsStore *store, - Allocator *allocators, - gboolean total) + Allocator *allocators) { Allocator *A; struct _sum_allocator *sum; @@ -654,27 +779,37 @@ allocators_store_update_from_allocators (AllocatorsStore *store, allocators_store_reset_sums (store); for (A = allocators; A != NULL; A = A->next) { + gsize size; + guint count; + if (A->time_tail->n_allocs == 0) continue; - if (! total && A->time_tail->n_allocs == A->time_tail->n_frees) + if (! store->cumulative && + A->time_tail->n_allocs == A->time_tail->n_frees) + { continue; + } sum = allocators_store_get_sum (store, A->alloc_fn); if (sum == NULL) sum = allocators_store_new_sum (store, A->alloc_fn); + _sum_add_allocator (store, sum, A); - if (sum->largest == NULL || - A->time_tail->bytes > sum->largest->time_tail->bytes) - sum->largest = A; + size = A->time_tail->bytes; + count = A->time_tail->n_allocs; + if (! store->cumulative) { + size -= A->time_tail->freed; + count -= A->time_tail->n_frees; + } - if (total) { - sum->size += A->time_tail->bytes; - sum->count += A->time_tail->n_allocs; - } else { - sum->size += A->time_tail->bytes - A->time_tail->freed; - sum->count += A->time_tail->n_allocs - A->time_tail->n_frees; + if (sum->largest == NULL || size > sum->largest_size) { + sum->largest = A; + sum->largest_size = size; } + + sum->size += size; + sum->count += count; } _allocators_store_update (store); diff --git a/src/allocators.c b/src/allocators.c index 23be621..c219820 100644 --- a/src/allocators.c +++ b/src/allocators.c @@ -438,7 +438,7 @@ allocators_init (Allocators *self) gtk_widget_set_has_tooltip (GTK_WIDGET (self), TRUE); - self->store = allocators_store_new (); + self->store = allocators_store_new (TRUE); allocators_store_set_cmp (self->store, _sum_allocators_cmp); gtk_tree_view_set_model (&self->tv, GTK_TREE_MODEL (self->store)); } @@ -454,7 +454,7 @@ allocators_reset (Allocators *self) { g_object_unref (self->store); - self->store = allocators_store_new (); + self->store = allocators_store_new (TRUE); allocators_store_set_cmp (self->store, _sum_allocators_cmp); gtk_tree_view_set_model (&self->tv, GTK_TREE_MODEL (self->store)); } diff --git a/src/allocators.h b/src/allocators.h index ad9fe59..ec11972 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -29,12 +29,14 @@ struct _sum_allocator { guint count; guint n_pages; gulong min, max; + guint64 largest_size; Allocator *largest; Block **blocks; guint filter_serial; gboolean sorted; gboolean filtered; struct _sum_allocator *next, *ht_next; + GSList *ht[17]; }; typedef struct _allocators_store AllocatorsStore; @@ -54,7 +56,10 @@ typedef enum { } AllocatorsStoreSort; AllocatorsStore * -allocators_store_new (void); +allocators_store_new (gboolean cumulative); + +gboolean +allocators_store_is_cumulative (AllocatorsStore *store); void allocators_store_set_sort (AllocatorsStore *store, AllocatorsStoreSort sort); @@ -85,8 +90,7 @@ allocators_store_update (AllocatorsStore *store); void allocators_store_update_from_allocators (AllocatorsStore *store, - Allocator *allocators, - gboolean total); + Allocator *allocators); gboolean allocators_store_add_filter (AllocatorsStore *store, diff --git a/src/summary-chart.c b/src/summary-chart.c index b67afb6..193252e 100644 --- a/src/summary-chart.c +++ b/src/summary-chart.c @@ -38,7 +38,6 @@ typedef struct _summary_chart { AllocatorsStore *store; struct _sum_allocator others; - gboolean total; guint dirty; } SummaryChart; @@ -54,8 +53,7 @@ G_DEFINE_TYPE (SummaryChart, summary_chart, GTK_TYPE_WIDGET) enum { PROP_0 = 0, - PROP_MODEL, - PROP_TOTAL + PROP_MODEL }; static gboolean @@ -105,9 +103,6 @@ summary_chart_set_property (GObject *obj, guint id, const GValue *v, GParamSpec case PROP_MODEL: summary_chart_set_model (self, g_value_get_object (v)); break; - case PROP_TOTAL: - self->total = g_value_get_boolean (v); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, spec); break; @@ -122,9 +117,6 @@ summary_chart_get_property (GObject *obj, guint id, GValue *v, GParamSpec *spec) case PROP_MODEL: g_value_set_object (v, self->store); break; - case PROP_TOTAL: - g_value_set_boolean (v, self->total); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, spec); break; @@ -550,8 +542,11 @@ summary_chart_query_tooltip (GtkWidget *widget, guint n, m, depth, n_allocs; n_allocs = A->time_tail->n_allocs; - if (! self->total && A->time_tail->prev) + if (! allocators_store_is_cumulative (self->store) && + A->time_tail->prev) + { n_allocs -= A->time_tail->freed; + } g_string_append (string, "\n\nMost frequent allocation callsite, "); len = g_snprintf (calls + 40, 40, "%u", n_allocs); @@ -624,17 +619,6 @@ summary_chart_class_init (SummaryChartClass *klass) G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NAME | G_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_TOTAL, - g_param_spec_boolean ("total", - _("total"), - _("Total"), - TRUE, - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB | - G_PARAM_STATIC_NAME | - G_PARAM_READWRITE)); } @@ -646,10 +630,9 @@ summary_chart_init (SummaryChart *self) } GtkWidget * -summary_chart_new (AllocatorsStore *store, gboolean total) +summary_chart_new (AllocatorsStore *store) { return g_object_new (summary_chart_get_type (), "model", store, - "total", total, NULL); } diff --git a/src/summary-view.c b/src/summary-view.c index 5e962b8..15f0dd5 100644 --- a/src/summary-view.c +++ b/src/summary-view.c @@ -27,6 +27,10 @@ #include "allocators.h" #include "summary.h" +#ifndef _ +#define _(x) x +#endif + typedef struct _summary_view { GtkTreeView tv; } SummaryView; @@ -109,6 +113,86 @@ summary_view_button_press (GtkWidget *widget, GdkEventButton *ev) return ret; } +#define BULLET "•" + +static void +pie_chart_shape_renderer (cairo_t *cr, + PangoAttrShape *attr, + gboolean do_path, + gpointer data) +{ + gint angle1 = GPOINTER_TO_INT (attr->data) & 1023; + gint angle2 = GPOINTER_TO_INT (attr->data) / 1024; + double x, y; + + if (do_path) + return; + + cairo_get_current_point (cr, &x, &y); + + cairo_new_sub_path (cr); + cairo_arc (cr, + x + 0.5 / PANGO_SCALE * attr->logical_rect.width, + y - 0.5 / PANGO_SCALE * attr->logical_rect.height, + .5 / PANGO_SCALE * MIN (attr->ink_rect.width, attr->ink_rect.height - 1), + 0, 2 * G_PI); + cairo_set_line_width (cr, 1.); + cairo_stroke (cr); + + cairo_move_to (cr, + x + 0.5 / PANGO_SCALE * attr->logical_rect.width, + y - 0.5 / PANGO_SCALE * attr->logical_rect.height); + cairo_arc (cr, + x + 0.5 / PANGO_SCALE * attr->logical_rect.width, + y - 0.5 / PANGO_SCALE * attr->logical_rect.height, + .5 / PANGO_SCALE * MIN (attr->ink_rect.width, attr->ink_rect.height), + -G_PI/2, -G_PI/2 + 2 * G_PI * angle2 / 1024.); + cairo_fill (cr); + + cairo_move_to (cr, + x + 0.5 / PANGO_SCALE * attr->logical_rect.width, + y - 0.5 / PANGO_SCALE * attr->logical_rect.height); + cairo_arc (cr, + x + 0.5 / PANGO_SCALE * attr->logical_rect.width, + y - 0.5 / PANGO_SCALE * attr->logical_rect.height, + .25 / PANGO_SCALE * MIN (attr->ink_rect.width, attr->ink_rect.height), + -G_PI/2, -G_PI/2 + 2 * G_PI * angle1 / 1024.); + cairo_set_source_rgb (cr, .5, .5, .5); + cairo_fill (cr); + +} + +static gboolean +label_show_layout (GtkWidget *widget, GdkEventExpose *ev) +{ + cairo_t *cr; + gint x,y; + + cr = gdk_cairo_create (widget->window); + gdk_cairo_region (cr, ev->region); + cairo_clip (cr); + + gtk_label_get_layout_offsets (GTK_LABEL (widget), &x, &y); + cairo_move_to (cr, x, y); + + pango_cairo_show_layout (cr, gtk_label_get_layout (GTK_LABEL (widget))); + cairo_destroy (cr); + + return TRUE; +} + +static GSList * +_sum_allocators_get_allocators (struct _sum_allocator *sum) +{ + GSList *l, *list = NULL; + guint n; + for (n = 0; n < G_N_ELEMENTS (sum->ht); n++) { + for (l = sum->ht[n]; l != NULL; l = g_slist_next (l)) + list = g_slist_prepend (list, l->data); + } + return list; +} + static gboolean summary_view_query_tooltip (GtkWidget *widget, gint x, @@ -118,17 +202,24 @@ summary_view_query_tooltip (GtkWidget *widget, { SummaryView * self = (SummaryView *) widget; GtkTreeModel *model = gtk_tree_view_get_model (&self->tv); + GtkWidget *label; GtkTreePath *path; GtkTreeViewColumn *column; GtkTreeIter iter; + PangoAttrList *attrs; + PangoRectangle ink_rect, logical_rect; + PangoLayout *layout; gint cell_x, cell_y; struct _sum_allocator *sum; - Allocator *A; + const Allocator *A; + GSList *list; + guint last_allocs; GString *string; const gchar *main_fn; - guint n, m, depth; + guint n, m, i, depth, n_allocs; const gchar *last; - gchar *text; + gint fraction[NUM_CALLERS]; + gchar *text, *p; gchar calls[80]; gint len; @@ -151,31 +242,69 @@ summary_view_query_tooltip (GtkWidget *widget, return FALSE; A = sum->largest; + + n_allocs = A->time_tail->n_allocs; + if (! allocators_store_is_cumulative ((AllocatorsStore *) model) && + A->time_tail->prev) + { + n_allocs -= A->time_tail->freed; + } + string = g_string_new ("Most frequent allocation callsite, "); - len = g_snprintf (calls + 40, 40, "%u", A->time_tail->n_allocs); + len = g_snprintf (calls + 40, 40, "%u", n_allocs); pretty_print_number (calls + 40, len, calls); g_string_append (string, calls); g_string_append_printf (string, " calls (%.1f%%)", - A->time_tail->n_allocs * 100. / sum->count); + n_allocs * 100. / sum->count); for (n = 0; n < A->n_frames; n++) if (A->frames[n]->function == A->alloc_fn) break; depth = n; + list = _sum_allocators_get_allocators (sum); main_fn = app_get_main_function (app_get (widget)); last = NULL; + last_allocs = 0; + i = 0; for (m = n; m < MIN (n + NUM_CALLERS, A->n_frames); m++) { if (A->frames[m]->function_srcloc != last) { + GSList *l, *next, **prev; + guint this_allocs = 0; + for (l = list, prev = &list; l != NULL; l = next) { + const Allocator *AA = l->data; + next = g_slist_next (l); + if (A->frames[m] != AA->frames[m]) { + g_slist_free1 (l); + *prev = next; + } else { + n_allocs = AA->time_tail->n_allocs; + if (! allocators_store_is_cumulative ((AllocatorsStore *) model) && + AA->time_tail->prev) + { + n_allocs -= AA->time_tail->freed; + } + this_allocs += n_allocs; + prev = &l->next; + } + } g_string_append_c (string, '\n'); g_string_append_c (string, '\t'); + g_string_append (string, BULLET); + g_string_append_c (string, '\t'); g_string_append (string, A->frames[m]->function_srcloc); + fraction[i] = (this_allocs * 1024 / sum->count) << 10; + if (last_allocs) + fraction[i] |= this_allocs * 1024 / last_allocs - this_allocs / last_allocs; + i++; last = A->frames[m]->function_srcloc; + last_allocs = this_allocs; } else n++; if (A->frames[m]->function == main_fn) break; } + g_slist_free (list); g_string_append (string, "\n\nAllocation stack:"); last = NULL; @@ -188,8 +317,44 @@ summary_view_query_tooltip (GtkWidget *widget, } } + text = g_string_free (string, FALSE); - gtk_tooltip_set_text (tooltip, text); + label = gtk_label_new (text); + + layout = gtk_widget_create_pango_layout (label, "O"); + pango_layout_get_extents (layout, NULL, &logical_rect); + ink_rect.x = 1; + ink_rect.y = -logical_rect.height + 1; + ink_rect.width = logical_rect.height - 2; + ink_rect.height = logical_rect.height - 2; + logical_rect.x = 0; + logical_rect.y = -logical_rect.height; + logical_rect.width = logical_rect.width; + g_object_unref (layout); + + g_signal_connect (label, "expose-event", + G_CALLBACK (label_show_layout), NULL); + pango_cairo_context_set_shape_renderer ( + gtk_widget_get_pango_context (label), + pie_chart_shape_renderer, + NULL, NULL); + attrs = pango_attr_list_new (); + i = 0; + for (p = text; (p = strstr (p, BULLET)); p += strlen (BULLET)) { + PangoAttribute *attr; + + attr = pango_attr_shape_new_with_data (&ink_rect, + &logical_rect, + GINT_TO_POINTER (fraction[i++]), + NULL, NULL); + attr->start_index = p - text; + attr->end_index = attr->start_index + strlen (BULLET); + + pango_attr_list_insert (attrs, attr); + } + gtk_label_set_attributes (GTK_LABEL (label), attrs); + pango_attr_list_unref (attrs); + gtk_tooltip_set_custom (tooltip, label); g_free (text); return TRUE; @@ -288,5 +453,7 @@ summary_view_init (SummaryView *self) GtkWidget * summary_view_new (AllocatorsStore *store) { - return g_object_new (summary_view_get_type (), "model", store, NULL); + return g_object_new (summary_view_get_type (), + "model", store, + NULL); } diff --git a/src/summary.c b/src/summary.c index 3f341b9..69be7ce 100644 --- a/src/summary.c +++ b/src/summary.c @@ -58,6 +58,8 @@ summary_set_property (GObject *obj, guint id, const GValue *v, GParamSpec *spec) switch (id) { case PROP_TOTAL: self->total = g_value_get_boolean (v); + if (self->store) + g_object_set (self->store, "cumulative", self->total, NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, spec); @@ -156,8 +158,8 @@ summary_init (Summary *self) { GtkWidget *hbox, *vbox, *lbl, *w, *sw; - self->store = allocators_store_new (); self->total = TRUE; + self->store = allocators_store_new (self->total); hbox = gtk_hbox_new (FALSE, 2); gtk_container_add (GTK_CONTAINER (self), hbox); @@ -167,7 +169,7 @@ summary_init (Summary *self) gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 2); gtk_widget_show (vbox); - w = summary_chart_new (self->store, self->total); + w = summary_chart_new (self->store); gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 2); gtk_widget_show (w); @@ -212,8 +214,7 @@ void summary_update (Summary *summary, Client *client) { allocators_store_update_from_allocators (summary->store, - client->allocators, - summary->total); + client->allocators); gtk_widget_queue_draw ((GtkWidget *) summary); /* XXX */ } diff --git a/src/summary.h b/src/summary.h index 2a3d4db..5537d4a 100644 --- a/src/summary.h +++ b/src/summary.h @@ -30,6 +30,6 @@ GtkWidget * summary_view_new (AllocatorsStore *store); GtkWidget * -summary_chart_new (AllocatorsStore *store, gboolean total); +summary_chart_new (AllocatorsStore *store); #endif /* SUMMARY_H */ |