diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2007-12-04 16:52:52 +0000 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2007-12-04 16:52:52 +0000 |
commit | f3ba4a775af355dae3511470ad46e2111418dde8 (patch) | |
tree | 3a5b4981d1caec06dc50c8c9c90d6f35e94f4277 /src | |
parent | 87ed08fb805a6af242b5563f1a34f5d93c7ca173 (diff) |
Add an initial client summary.
Diffstat (limited to 'src')
-rw-r--r-- | src/allocators-store.c | 488 | ||||
-rw-r--r-- | src/allocators.c | 524 | ||||
-rw-r--r-- | src/allocators.h | 72 | ||||
-rw-r--r-- | src/app.c | 68 | ||||
-rw-r--r-- | src/client.h | 69 | ||||
-rw-r--r-- | src/odin.h | 7 | ||||
-rw-r--r-- | src/summary.c | 106 |
7 files changed, 838 insertions, 496 deletions
diff --git a/src/allocators-store.c b/src/allocators-store.c new file mode 100644 index 0000000..f702bbb --- /dev/null +++ b/src/allocators-store.c @@ -0,0 +1,488 @@ +/* + This file is part of odin, a memory profiler with fragmentation analysis. + + Copyright (C) 2007 Chris Wilson <chris@chris-wilson.co.uk> + + odin is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + odin is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with odin. If not, see <http://www.gnu.org/licenses/>/ + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "odin.h" +#include "allocators.h" + +struct _allocators_store { + GObject object; + + GCompareFunc sum_allocators_cmp; + GPtrArray *allocators; + struct { + guint size; + struct _sum_allocator **nodes; + } ht; + struct _sum_allocator *sums; + + Chunk *sum_allocator_chunks; +}; + +typedef struct _allocators_store_class { + GObjectClass parent_class; +} AllocatorsStoreClass; + +static void +allocators_store_tree_model_init (GtkTreeModelIface *iface); + +static GType +allocators_store_get_type (void); + +G_DEFINE_TYPE_WITH_CODE (AllocatorsStore, allocators_store, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + allocators_store_tree_model_init)) + + +static struct _sum_allocator * +_sum_allocator_alloc (AllocatorsStore *store) +{ + gpointer mem; + gsize size = sizeof (struct _sum_allocator); + +#ifndef G_ENABLE_DEBUG + Chunk *c = store->sum_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->sum_allocator_chunks; + store->sum_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) +{ + const struct _sum_allocator * const *aa = A, * const *bb = B; + const struct _sum_allocator *a = *aa, *b = *bb; + gint cmp; + + if (b->size == 0) + return 1; + if (a->size == 0) + return -1; + + cmp = b->size - a->size; + if (cmp) + return cmp; + + cmp = b->count - a->count; + if (cmp) + return cmp; + + cmp = b->n_pages - a->n_pages; + if (cmp) + return cmp; + + return b->n_pages * 4096 / b->size * b->count - a->n_pages * 4096 / a->size * a->count; +} + +static void +allocators_store_finalize (GObject *obj) +{ + AllocatorsStore *self = (AllocatorsStore *) obj; + Chunk *c; + + c = self->sum_allocator_chunks; + self->sum_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); + + G_OBJECT_CLASS (allocators_store_parent_class)->finalize (obj); +} + +static void +allocators_store_class_init (AllocatorsStoreClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->finalize = allocators_store_finalize; +} + + +/* GtkTreeModelIface */ + +static GtkTreeModelFlags +allocators_store_get_flags (GtkTreeModel *tree_model) +{ + return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY; +} + +static gint +allocators_store_get_n_columns (GtkTreeModel *tree_model) +{ + return ALLOCATORS_N_COLUMNS; +} + +static GType +allocators_store_get_column_type (GtkTreeModel *tree_model, + gint index) +{ + switch (index) { + case ALLOCATORS_FRAME: return G_TYPE_STRING; + case ALLOCATORS_SIZE: return G_TYPE_UINT64; + case ALLOCATORS_COUNT: return G_TYPE_UINT; + case ALLOCATORS_NPAGES: return G_TYPE_UINT; + default: return G_TYPE_INVALID; + } +} + +static gboolean +allocators_store_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + AllocatorsStore *self = (AllocatorsStore *) tree_model; + guint n; + + n = gtk_tree_path_get_depth (path); + g_return_val_if_fail (n == 1, FALSE); + + n = gtk_tree_path_get_indices (path)[0]; + if (n >= self->allocators->len) + return FALSE; + + iter->user_data = g_ptr_array_index (self->allocators, n); + iter->stamp = 1; + return TRUE; +} + +static GtkTreePath * +allocators_store_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreePath *path = gtk_tree_path_new (); + struct _sum_allocator *sum = iter->user_data; + gtk_tree_path_append_index (path, sum->index); + return path; +} + +static void +allocators_store_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + struct _sum_allocator *sum = iter->user_data; + switch (column) { + case ALLOCATORS_FRAME: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, sum->frame); + break; + + case ALLOCATORS_SIZE: + g_value_init (value, G_TYPE_UINT64); + g_value_set_uint64 (value, sum->size); + break; + + case ALLOCATORS_COUNT: + g_value_init (value, G_TYPE_UINT); + g_value_set_uint (value, sum->count); + break; + + case ALLOCATORS_NPAGES: + g_value_init (value, G_TYPE_UINT); + g_value_set_uint (value, sum->n_pages); + break; + } +} + +static gboolean +allocators_store_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + AllocatorsStore *self = (AllocatorsStore *) tree_model; + struct _sum_allocator *sum = iter->user_data; + GPtrArray *array = self->allocators; + guint index = sum->index; + + if (index >= array->len - 1) + return FALSE; + + iter->user_data = g_ptr_array_index (array, index + 1); + return TRUE; +} + +static gboolean +allocators_store_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + return parent == NULL; +} + +static gboolean +allocators_store_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + return FALSE; +} + +static gint +allocators_store_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + return 0; +} + +static gboolean +allocators_store_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) +{ + AllocatorsStore *self = (AllocatorsStore *) tree_model; + GPtrArray *array; + + if (parent != NULL) + return FALSE; + + array = self->allocators; + if ((guint) n >= array->len) + return FALSE; + + iter->stamp = 1; + iter->user_data = g_ptr_array_index (array, n); + return TRUE; +} + +static gboolean +allocators_store_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + return FALSE; +} + +static void +allocators_store_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = allocators_store_get_flags; + iface->get_n_columns = allocators_store_get_n_columns; + iface->get_column_type = allocators_store_get_column_type; + iface->get_iter = allocators_store_get_iter; + iface->get_path = allocators_store_get_path; + iface->get_value = allocators_store_get_value; + iface->iter_next = allocators_store_iter_next; + iface->iter_children = allocators_store_iter_children; + iface->iter_has_child = allocators_store_iter_has_child; + iface->iter_n_children = allocators_store_iter_n_children; + iface->iter_nth_child = allocators_store_iter_nth_child; + iface->iter_parent = allocators_store_iter_parent; +} + +static void +allocators_store_init (AllocatorsStore *self) +{ + self->allocators = g_ptr_array_new (); + self->sum_allocators_cmp = _sum_allocators_cmp_by_size; + + self->ht.size = g_spaced_primes_closest (40000); /* XXX */ + self->ht.nodes = g_new0 (struct _sum_allocator *, self->ht.size); +} + +AllocatorsStore * +allocators_store_new (void) +{ + return g_object_new (allocators_store_get_type (), NULL); +} + +void +allocators_store_set_cmp (AllocatorsStore *store, GCompareFunc cmp) +{ + store->sum_allocators_cmp = cmp; +} + +struct _sum_allocator * +allocators_store_get_sums (AllocatorsStore *store) +{ + return store->sums; +} + +struct _sum_allocator * +allocators_store_get_sum (AllocatorsStore *store, const gchar *fn) +{ + guint index = ((gulong) fn ^ 6017773UL) % store->ht.size; + struct _sum_allocator *sum = store->ht.nodes[index]; + while (sum != NULL && sum->frame != fn) + sum = sum->ht_next; + return sum; +} + +struct _sum_allocator * +allocators_store_new_sum (AllocatorsStore *store, const gchar *fn) +{ + struct _sum_allocator *sum; + guint index; + + sum = _sum_allocator_alloc (store); + sum->next = store->sums; + store->sums = sum; + + sum->index = sum->old_index = -1; + sum->size = 0; + sum->count = 0; + sum->n_pages = 0; + sum->min = (gulong) -1; + sum->max = 0; + sum->sorted = FALSE; + sum->blocks = NULL; + + sum->frame = fn; + + index = ((gulong) fn ^ 6017773UL) % store->ht.size; + sum->ht_next = store->ht.nodes[index]; + store->ht.nodes[index] = sum; + + return sum; +} + +void +allocators_store_reset_sums (AllocatorsStore *store) +{ + struct _sum_allocator *sum; + + for (sum = store->sums; sum != NULL; sum = sum->next) { + sum->old_index = sum->index; + sum->index = -1; + sum->size = 0; + sum->count = 0; + sum->blocks = 0; + sum->n_pages = 0; + sum->min = (gulong) -1; + sum->max = 0; + sum->sorted = FALSE; + } +} + +struct _GtkTreePath { /* XXX */ + gint depth; + gint *indices; +}; + +void +allocators_store_update (AllocatorsStore *store) +{ + GtkTreePath path; + GtkTreeIter iter; + gint index[1]; + guint old_len, n; + gint *new_order; + struct _sum_allocator *sum; + GPtrArray *allocators; + + path.indices = index; + iter.stamp = 1; + + if (store->allocators && store->allocators->len > 1) { + gboolean reordered = FALSE; + + old_len = store->allocators->len; + g_ptr_array_sort (store->allocators, store->sum_allocators_cmp); + new_order = g_newa (gint, old_len); + path.depth = 1; + for (n = 0; n < old_len; n++) { + sum = g_ptr_array_index (store->allocators, n); + sum->index = n; + if (sum->index != sum->old_index) + reordered = TRUE; + + iter.user_data = sum; + path.indices[0] = n; + gtk_tree_model_row_changed ((GtkTreeModel *) store, &path, &iter); + new_order[n] = sum->old_index; + sum->old_index = n; + } + if (reordered) { + path.depth = 0; + gtk_tree_model_rows_reordered ((GtkTreeModel *) store, + &path, NULL, new_order); + } + } + + allocators = g_ptr_array_new (); + path.depth = 1; + for (sum = store->sums; sum != NULL; sum = sum->next) { + if (sum->count) { + g_ptr_array_add (allocators, sum); + } else { + if (sum->old_index != (guint) -1) { + g_assert (store->allocators != NULL); + path.indices[0] = sum->index; + gtk_tree_model_row_deleted ((GtkTreeModel *) store, &path); + for (n = sum->old_index; n < store->allocators->len; n++) { + ((struct _sum_allocator *) g_ptr_array_index (store->allocators, n))->index--; + } + } + } + } + g_ptr_array_free (store->allocators, TRUE); + + g_ptr_array_sort (allocators, store->sum_allocators_cmp); + store->allocators = allocators; + for (n = 0; n < allocators->len; n++) { + sum = g_ptr_array_index (allocators, n); + sum->index = n; + + if (sum->old_index == (guint) -1) { + iter.user_data = sum; + path.indices[0] = n; + gtk_tree_model_row_inserted ((GtkTreeModel *) store, &path, &iter); + } + } +} + +void +allocators_store_update_from_allocators (AllocatorsStore *store, + Allocator *allocators) +{ + Allocator *A; + struct _sum_allocator *sum; + + allocators_store_reset_sums (store); + + for (A = allocators; A != NULL; A = A->next) { + sum = allocators_store_get_sum (store, A->alloc_fn); + + if (sum == NULL) + sum = allocators_store_new_sum (store, A->alloc_fn); + + sum->size += A->time_tail->bytes; + sum->count += A->time_tail->n_allocs; + } + + allocators_store_update (store); +} diff --git a/src/allocators.c b/src/allocators.c index 74af289..7b97235 100644 --- a/src/allocators.c +++ b/src/allocators.c @@ -26,40 +26,11 @@ #include <stdlib.h> #include "odin.h" +#include "allocators.h" #include "block.h" #define _(x) x -struct _sum_allocator { - guint index, old_index; - const gchar *frame; - guint64 size; - guint count; - guint n_pages; - gulong min, max; - Block **blocks; - gboolean sorted; - struct _sum_allocator *next, *ht_next; -}; - -typedef struct _allocators_store { - GObject object; - - GPtrArray *allocators; - struct { - guint size; - struct _sum_allocator **nodes; - } ht; - struct _sum_allocator *sums; - - Chunk *sum_allocator_chunks; -} AllocatorsStore; - -typedef struct _allocators_store_class { - GObjectClass parent_class; -} AllocatorsStoreClass; - - struct _allocators { GtkTreeView tv; @@ -77,81 +48,36 @@ allocators_get_type (void); G_DEFINE_TYPE (Allocators, allocators, GTK_TYPE_TREE_VIEW) -static void -allocators_store_tree_model_init (GtkTreeModelIface *iface); - -static GType -allocators_store_get_type (void); - -G_DEFINE_TYPE_WITH_CODE (AllocatorsStore, allocators_store, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, - allocators_store_tree_model_init)) -enum { - FRAME, - COUNT, - NPAGES, - SIZE, - N_COLUMNS, -}; - enum { PROP_0 = 0, PROP_BLOCKS, }; -static struct _sum_allocator * -_sum_allocator_alloc (AllocatorsStore *store) -{ - gpointer mem; - gsize size = sizeof (struct _sum_allocator); - -#ifndef G_ENABLE_DEBUG - Chunk *c = store->sum_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->sum_allocator_chunks; - store->sum_allocator_chunks = c; - } - - mem = c->mem + c->used; - c->used += size; -#else - mem = g_malloc (size); -#endif - - return mem; -} - -static struct _sum_allocator * -sum_get (AllocatorsStore *store, const gchar *fn) -{ - guint index = ((gulong) fn ^ 6017773) % store->ht.size; - struct _sum_allocator *sum = store->ht.nodes[index]; - while (sum != NULL && sum->frame != fn) - sum = sum->ht_next; - return sum; -} - -static gint -block_cmp (gconstpointer A, gconstpointer B) +static void +_sum_allocators_count_pages (struct _sum_allocator *sum) { - const Block * const *aa = A, * const *bb = B; - const Block *a = *aa, *b = *bb; - const Allocator *Aa = a->allocator, *Ab = b->allocator; - gint cmp; - - cmp = Ab->time_tail->n_allocs - Aa->time_tail->n_allocs; - if (cmp) - return cmp; + guint32 pages[1024]; + guint pagesize = sysconf (_SC_PAGESIZE); + gulong n = ((sum->max - sum->min) / pagesize + 32) & ~31; - cmp = b->size - a->size; - if (cmp) - return cmp; + if (n > 32 * G_N_ELEMENTS (pages)) { + sum->n_pages = block_array_count_pages (sum->blocks, sum->count); + } else { + gulong min = sum->min; - return a->addr - b->addr; + memset (pages, 0, n / 8); + for (n = 0; n < sum->count; n++) { + const Block *block = sum->blocks[n]; + gulong p = (block->addr - min) / pagesize; + gulong last_page = (block->addr + block->size - min) / pagesize; + do { + if ((pages[p / 32] & (1 << (p & 31 ))) == 0) { + pages [p / 32] |= 1 << (p & 31); + sum->n_pages++; + } + } while (++p <= last_page); + } + } } static gint @@ -182,261 +108,13 @@ _sum_allocators_cmp (gconstpointer A, gconstpointer B) } static void -allocators_store_finalize (GObject *obj) -{ - AllocatorsStore *self = (AllocatorsStore *) obj; - Chunk *c; - - c = self->sum_allocator_chunks; - self->sum_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); - - G_OBJECT_CLASS (allocators_store_parent_class)->finalize (obj); -} - -static void -allocators_store_class_init (AllocatorsStoreClass *klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - - object_class->finalize = allocators_store_finalize; -} - - -/* GtkTreeModelIface */ - -static GtkTreeModelFlags -allocators_store_get_flags (GtkTreeModel *tree_model) -{ - return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY; -} - -static gint -allocators_store_get_n_columns (GtkTreeModel *tree_model) -{ - return N_COLUMNS; -} - -static GType -allocators_store_get_column_type (GtkTreeModel *tree_model, - gint index) -{ - switch (index) { - case FRAME: return G_TYPE_STRING; - case SIZE: return G_TYPE_UINT64; - case COUNT: return G_TYPE_UINT; - case NPAGES: return G_TYPE_UINT; - default: return G_TYPE_INVALID; - } -} - -static gboolean -allocators_store_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - AllocatorsStore *self = (AllocatorsStore *) tree_model; - guint n; - - n = gtk_tree_path_get_depth (path); - g_return_val_if_fail (n == 1, FALSE); - - n = gtk_tree_path_get_indices (path)[0]; - if (n >= self->allocators->len) - return FALSE; - - iter->user_data = g_ptr_array_index (self->allocators, n); - iter->stamp = 1; - return TRUE; -} - -static GtkTreePath * -allocators_store_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreePath *path = gtk_tree_path_new (); - struct _sum_allocator *sum = iter->user_data; - gtk_tree_path_append_index (path, sum->index); - return path; -} - -static void -allocators_store_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - gint column, - GValue *value) -{ - struct _sum_allocator *sum = iter->user_data; - switch (column) { - case FRAME: - g_value_init (value, G_TYPE_STRING); - g_value_set_string (value, sum->frame); - break; - - case SIZE: - g_value_init (value, G_TYPE_UINT64); - g_value_set_uint64 (value, sum->size); - break; - - case COUNT: - g_value_init (value, G_TYPE_UINT); - g_value_set_uint (value, sum->count); - break; - - case NPAGES: - g_value_init (value, G_TYPE_UINT); - g_value_set_uint (value, sum->n_pages); - break; - } -} - -static gboolean -allocators_store_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - AllocatorsStore *self = (AllocatorsStore *) tree_model; - struct _sum_allocator *sum = iter->user_data; - GPtrArray *array = self->allocators; - guint index = sum->index; - - if (index >= array->len - 1) - return FALSE; - - iter->user_data = g_ptr_array_index (array, index + 1); - return TRUE; -} - -static gboolean -allocators_store_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - return parent == NULL; -} - -static gboolean -allocators_store_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - return FALSE; -} - -static gint -allocators_store_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - return 0; -} - -static gboolean -allocators_store_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n) -{ - AllocatorsStore *self = (AllocatorsStore *) tree_model; - GPtrArray *array; - - if (parent != NULL) - return FALSE; - - array = self->allocators; - if ((guint) n >= array->len) - return FALSE; - - iter->stamp = 1; - iter->user_data = g_ptr_array_index (array, n); - return TRUE; -} - -static gboolean -allocators_store_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - return FALSE; -} - -static void -allocators_store_tree_model_init (GtkTreeModelIface *iface) -{ - iface->get_flags = allocators_store_get_flags; - iface->get_n_columns = allocators_store_get_n_columns; - iface->get_column_type = allocators_store_get_column_type; - iface->get_iter = allocators_store_get_iter; - iface->get_path = allocators_store_get_path; - iface->get_value = allocators_store_get_value; - iface->iter_next = allocators_store_iter_next; - iface->iter_children = allocators_store_iter_children; - iface->iter_has_child = allocators_store_iter_has_child; - iface->iter_n_children = allocators_store_iter_n_children; - iface->iter_nth_child = allocators_store_iter_nth_child; - iface->iter_parent = allocators_store_iter_parent; -} - -static void -allocators_store_init (AllocatorsStore *self) -{ - self->allocators = g_ptr_array_new (); - - self->ht.size = g_spaced_primes_closest (40000); /* XXX */ - self->ht.nodes = g_new0 (struct _sum_allocator *, self->ht.size); -} - -static void -_sum_allocators_add_to_array (struct _sum_allocator *sum, GPtrArray *array) -{ - guint32 pages[1024]; - guint pagesize = sysconf (_SC_PAGESIZE); - gulong n = ((sum->max - sum->min) / pagesize + 32) & ~31; - - g_ptr_array_add (array, sum); - - if (n > 32 * G_N_ELEMENTS (pages)) { - sum->n_pages = block_array_count_pages (sum->blocks, sum->count); - } else { - gulong min = sum->min; - - memset (pages, 0, n / 8); - for (n = 0; n < sum->count; n++) { - const Block *block = sum->blocks[n]; - gulong p = (block->addr - min) / pagesize; - gulong last_page = (block->addr + block->size - min) / pagesize; - do { - if ((pages[p / 32] & (1 << (p & 31 ))) == 0) { - pages [p / 32] |= 1 << (p & 31); - sum->n_pages++; - } - } while (++p <= last_page); - } - } -} - -struct _GtkTreePath { /* XXX */ - gint depth; - gint *indices; -}; - -static void allocators_set_blocks (Allocators *self, Block *blocks) { Block *b, **block_mem; AllocatorsStore *store; - GtkTreePath path; - GtkTreeIter iter; - gint index[1]; struct _sum_allocator *sum, *sums; gulong pagesize = sysconf (_SC_PAGESIZE); - guint n, count, old_len; - gint *new_order; - GPtrArray *allocators; + guint count; self->blocks = blocks; @@ -444,44 +122,16 @@ allocators_set_blocks (Allocators *self, Block *blocks) return; store = self->store; - sums = self->store->sums; - for (sum = sums; sum != NULL; sum = sum->next) { - sum->old_index = sum->index; - sum->index = -1; - sum->size = 0; - sum->count = 0; - sum->n_pages = 0; - sum->min = (gulong) -1; - sum->max = 0; - sum->sorted = FALSE; - } + allocators_store_reset_sums (store); count = 0; for (b = blocks; b != NULL; b = b->next) { Allocator *A = b->allocator; - sum = sum_get (store, A->alloc_fn); - - if (sum == NULL) { - guint index; - - sum = _sum_allocator_alloc (store); - sum->index = sum->old_index = -1; - sum->size = 0; - sum->count = 0; - sum->n_pages = 0; - sum->min = (gulong) -1; - sum->max = 0; - sum->sorted = FALSE; - sum->next = sums; - sums = sum; - - sum->frame = A->alloc_fn; - - index = ((gulong) A->alloc_fn ^ 6017773) % store->ht.size; - sum->ht_next = store->ht.nodes[index]; - store->ht.nodes[index] = sum; - } + + sum = allocators_store_get_sum (store, A->alloc_fn); + if (sum == NULL) + sum = allocators_store_new_sum (store, A->alloc_fn); if (b->addr < sum->min) sum->min = b->addr; @@ -492,13 +142,18 @@ allocators_set_blocks (Allocators *self, Block *blocks) sum->count++; count++; } - store->sums = sums; - sum->min &= ~(pagesize - 1); - sum->max = (sum->max + pagesize) & ~(pagesize - 1); + + sums = allocators_store_get_sums (store); self->blocks_mem = g_renew (Block *, self->blocks_mem, count); block_mem = self->blocks_mem; for (sum = sums; sum != NULL; sum = sum->next) { + if (sum->count == 0) + continue; + + sum->min &= ~(pagesize - 1); + sum->max = (sum->max + pagesize) & ~(pagesize - 1); + sum->blocks = block_mem; block_mem += sum->count; sum->count = 0; @@ -506,74 +161,18 @@ allocators_set_blocks (Allocators *self, Block *blocks) for (b = blocks; b != NULL; b = b->next) { Allocator *A = b->allocator; - sum = sum_get (store, A->alloc_fn); + sum = allocators_store_get_sum (store, A->alloc_fn); + g_assert (sum != NULL); sum->blocks[sum->count++] = b; } + for (sum = sums; sum != NULL; sum = sum->next) + if (sum->count) + _sum_allocators_count_pages (sum); - path.indices = index; - iter.stamp = 1; - - if (store->allocators && store->allocators->len > 1) { - gboolean reordered = FALSE; - - old_len = store->allocators->len; - g_ptr_array_sort (store->allocators, _sum_allocators_cmp); - new_order = g_newa (gint, old_len); - path.depth = 1; - for (n = 0; n < old_len; n++) { - sum = g_ptr_array_index (store->allocators, n); - sum->index = n; - if (sum->index != sum->old_index) - reordered = TRUE; - - iter.user_data = sum; - path.indices[0] = n; - gtk_tree_model_row_changed ((GtkTreeModel *) store, &path, &iter); - new_order[n] = sum->old_index; - sum->old_index = n; - } - if (reordered) { - path.depth = 0; - gtk_tree_model_rows_reordered ((GtkTreeModel *) store, - &path, NULL, new_order); - } - } - - allocators = g_ptr_array_new (); - path.depth = 1; - for (sum = sums; sum != NULL; sum = sum->next) { - if (sum->count) { - _sum_allocators_add_to_array (sum, allocators); - } else { - if (sum->old_index != (guint) -1) { - g_assert (store->allocators != NULL); - path.indices[0] = sum->index; - gtk_tree_model_row_deleted ((GtkTreeModel *) store, &path); - for (n = sum->old_index; n < store->allocators->len; n++) { - ((struct _sum_allocator *) g_ptr_array_index (store->allocators, n))->index--; - } - } - - } - } - g_ptr_array_free (store->allocators, TRUE); - - g_ptr_array_sort (allocators, _sum_allocators_cmp); - store->allocators = allocators; - for (n = 0; n < allocators->len; n++) { - sum = g_ptr_array_index (allocators, n); - sum->index = n; - - if (sum->old_index == (guint) -1) { - iter.user_data = sum; - path.indices[0] = n; - gtk_tree_model_row_inserted ((GtkTreeModel *) store, &path, &iter); - } - } + allocators_store_update (store); } - static void allocators_set_property (GObject *obj, guint id, const GValue *v, GParamSpec *spec) { @@ -602,6 +201,26 @@ allocators_get_property (GObject *obj, guint id, GValue *v, GParamSpec *spec) } } +static gint +block_cmp (gconstpointer A, gconstpointer B) +{ + const Block * const *aa = A, * const *bb = B; + const Block *a = *aa, *b = *bb; + const Allocator *Aa = a->allocator, *Ab = b->allocator; + gint cmp; + + cmp = Ab->time_tail->n_allocs - Aa->time_tail->n_allocs; + if (cmp) + return cmp; + + cmp = b->size - a->size; + if (cmp) + return cmp; + + return a->addr - b->addr; +} + + static gboolean allocators_query_tooltip (GtkWidget *widget, gint x, @@ -800,28 +419,28 @@ allocators_init (Allocators *self) "width-chars", 50, NULL); column = gtk_tree_view_column_new_with_attributes ("Allocator", - renderer, "text", FRAME, NULL); + renderer, "text", ALLOCATORS_FRAME, NULL); gtk_tree_view_append_column (&self->tv, column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Count", renderer, NULL); gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer, - cell_layout_pretty_print_uint, GUINT_TO_POINTER (COUNT), NULL); + cell_layout_pretty_print_uint, GUINT_TO_POINTER (ALLOCATORS_COUNT), NULL); gtk_tree_view_append_column (&self->tv, column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Size", renderer, NULL); gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer, - cell_layout_pretty_print_uint64, GUINT_TO_POINTER (SIZE), NULL); + cell_layout_pretty_print_uint64, GUINT_TO_POINTER (ALLOCATORS_SIZE), NULL); gtk_tree_view_append_column (&self->tv, column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Pages", renderer, NULL); gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer, - cell_layout_pretty_print_uint, GUINT_TO_POINTER (NPAGES), NULL); + cell_layout_pretty_print_uint, GUINT_TO_POINTER (ALLOCATORS_NPAGES), NULL); gtk_tree_view_append_column (&self->tv, column); gtk_tree_view_set_grid_lines (&self->tv, GTK_TREE_VIEW_GRID_LINES_VERTICAL); @@ -831,7 +450,8 @@ allocators_init (Allocators *self) gtk_widget_set_has_tooltip (GTK_WIDGET (self), TRUE); - self->store = g_object_new (allocators_store_get_type (), NULL); + self->store = allocators_store_new (); + allocators_store_set_cmp (self->store, _sum_allocators_cmp); gtk_tree_view_set_model (&self->tv, GTK_TREE_MODEL (self->store)); } @@ -846,7 +466,8 @@ allocators_reset (Allocators *self) { g_object_unref (self->store); - self->store = g_object_new (allocators_store_get_type (), NULL); + self->store = allocators_store_new (); + allocators_store_set_cmp (self->store, _sum_allocators_cmp); gtk_tree_view_set_model (&self->tv, GTK_TREE_MODEL (self->store)); } @@ -880,7 +501,8 @@ allocators_select_blocks (Allocators *self, GSList *blocks) GtkTreeIter iter; GtkTreePath *path; - iter.user_data = sum_get (self->store, b->allocator->alloc_fn); + iter.user_data = allocators_store_get_sum (self->store, + b->allocator->alloc_fn); iter.stamp = 1; path = gtk_tree_model_get_path (model, &iter); gtk_tree_view_expand_to_path (&self->tv, path); diff --git a/src/allocators.h b/src/allocators.h new file mode 100644 index 0000000..ca73469 --- /dev/null +++ b/src/allocators.h @@ -0,0 +1,72 @@ +/* + This file is part of odin, a memory profiler with fragmentation analysis. + + Copyright (C) 2007 Chris Wilson <chris@chris-wilson.co.uk> + + odin is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + odin is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with odin. If not, see <http://www.gnu.org/licenses/>/ + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef ALLOCATORS_H +#define ALLOCATORS_H + +struct _sum_allocator { + guint index, old_index; + const gchar *frame; + guint64 size; + guint count; + guint n_pages; + gulong min, max; + Block **blocks; + gboolean sorted; + struct _sum_allocator *next, *ht_next; +}; + +typedef struct _allocators_store AllocatorsStore; + +enum { + ALLOCATORS_FRAME, + ALLOCATORS_COUNT, + ALLOCATORS_NPAGES, + ALLOCATORS_SIZE, + ALLOCATORS_N_COLUMNS, +}; + +AllocatorsStore * +allocators_store_new (void); + +void +allocators_store_set_cmp (AllocatorsStore *store, GCompareFunc cmp); + +struct _sum_allocator * +allocators_store_get_sums (AllocatorsStore *store); + +struct _sum_allocator * +allocators_store_get_sum (AllocatorsStore *store, const gchar *fn); + +struct _sum_allocator * +allocators_store_new_sum (AllocatorsStore *store, const gchar *fn); + +void +allocators_store_reset_sums (AllocatorsStore *store); + +void +allocators_store_update (AllocatorsStore *store); + +void +allocators_store_update_from_allocators (AllocatorsStore *store, + Allocator *allocators); + +#endif /* ALLOCATORS_H */ @@ -32,6 +32,7 @@ #include <errno.h> #include "odin.h" +#include "client.h" #include "block.h" #include "callgraph.h" #include "frames.h" @@ -39,47 +40,10 @@ #include "lwp-events.h" -struct _client { - enum { - LWP, - VALGRIND - } type; - gboolean active; - - gchar *name; - GPid pid; - gboolean terminated; - - guint last; - - Allocator *allocators; - struct { - guint size; - guint nnodes; - Allocator **nodes; - } allocator_by_addr; - - Block *blocks; - struct { - guint size; - guint nnodes; - Block **nodes; - } block_by_addr; - - Frames frames; - - CallGraphStore *call_graph; - - GArray *events; - - Chunk *perm_chunks; - Chunk *block_chunks; - Block *block_free_list; -}; - struct _app { GdkCursor *busy_cursor; GtkWidget *window; + GtkWidget *summary; struct { GtkWidget *allocators; GtkWidget *block_map; @@ -667,7 +631,7 @@ _client_update_alloc_fn (Client *client) } static void -app_update_allocators (App *app, guint time) +app_update_allocators (App *app, Client *client, guint time) { _client_update_alloc_fn (&app->client); @@ -679,9 +643,19 @@ app_update_allocators (App *app, guint time) timeline_add_datum ((Timeline *) app->timeline, &app->client, time, app->client.allocators); + summary_update ((Summary *) app->summary, client); + app->client.last = time; } +static void +app_update_client (App *app, Client *client, guint time) +{ + app_update_allocators (app, client, time); + app_set_blocks (app, client->blocks); +} + + static gboolean vg_read (GIOChannel *io, App *app) { @@ -865,9 +839,8 @@ vg_read (GIOChannel *io, App *app) g_array_append_val (app->client.events, ev); } - app_update_allocators (app, time); - app_set_blocks (app, app->client.blocks = blocks); - + app->client.blocks = blocks; + app_update_client (app, &app->client, time); app_update_status (app); return TRUE; @@ -961,6 +934,8 @@ main_window_create (App *app) app->allocations.tree_map = call_graph_tree_map_new (); app->allocations.ring = call_graph_ring_new (); + app->summary = summary_new (); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (app->allocations.call_graph)); g_signal_connect (selection, "changed", G_CALLBACK (call_graph_selection_changed), @@ -1004,6 +979,11 @@ main_window_create (App *app) gtk_box_pack_start (GTK_BOX (vbox), app->statusbar, FALSE, FALSE, 2); gtk_widget_show (app->statusbar); + label = gtk_label_new ("Summary"); + gtk_notebook_append_page (GTK_NOTEBOOK (app->notebook), app->summary, label); + gtk_widget_show (label); + gtk_widget_show (app->summary); + hbox = gtk_hbox_new (FALSE, 2); label = gtk_label_new ("Allocation Map"); gtk_notebook_append_page (GTK_NOTEBOOK (app->notebook), hbox, label); @@ -1601,14 +1581,12 @@ lwp_read (GIOChannel *io, App *app) } } - app_update_allocators (app, time); - app_set_blocks (app, app->client.blocks); - if (app->client.pid) { if (! _get_pid_cmd (app->client.pid, client, G_N_ELEMENTS (client))) strcpy (client, "«unknown»"); _app_set_client_name (app, client); } + app_update_client (app, &app->client, time); app_update_status (app); return TRUE; diff --git a/src/client.h b/src/client.h new file mode 100644 index 0000000..7131b86 --- /dev/null +++ b/src/client.h @@ -0,0 +1,69 @@ +/* + This file is part of odin, a memory profiler with fragmentation analysis. + + Copyright (C) 2007 Chris Wilson <chris@chris-wilson.co.uk> + + odin is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + odin is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with odin. If not, see <http://www.gnu.org/licenses/>/ + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef CLIENT_H +#define CLIENT_H + +#include <gtk/gtk.h> + +#include "frames.h" + +G_BEGIN_DECLS + +struct _client { + enum { + LWP, + VALGRIND + } type; + gboolean active; + + gchar *name; + GPid pid; + gboolean terminated; + + guint last; + + Allocator *allocators; + struct { + guint size; + guint nnodes; + Allocator **nodes; + } allocator_by_addr; + + Block *blocks; + struct { + guint size; + guint nnodes; + Block **nodes; + } block_by_addr; + + Frames frames; + + CallGraphStore *call_graph; + + GArray *events; + + Chunk *perm_chunks; + Chunk *block_chunks; + Block *block_free_list; +}; + +#endif /* CLIENT_H */ @@ -39,6 +39,7 @@ typedef struct _allocators Allocators; typedef struct _block_map BlockMap; typedef struct _call_graph CallGraph; typedef struct _procmap Procmap; +typedef struct _summary Summary; typedef struct _timeline Timeline; typedef struct _call_graph_frame CallGraphFrame; @@ -229,6 +230,12 @@ procmap_new (void); G_CONST_RETURN Event * app_find_prev_event_for_addr_range (App *app, guint time, gulong min, gulong max); +GtkWidget * +summary_new (void); + +void +summary_update (Summary *summary, Client *client); + void pretty_print_number (const char *number, gint len, char *output); diff --git a/src/summary.c b/src/summary.c new file mode 100644 index 0000000..44167fe --- /dev/null +++ b/src/summary.c @@ -0,0 +1,106 @@ +/* + This file is part of odin, a memory profiler with fragmentation analysis. + + Copyright (C) 2007 Chris Wilson <chris@chris-wilson.co.uk> + + odin is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + odin is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with odin. If not, see <http://www.gnu.org/licenses/>/ + + The GNU General Public License is contained in the file COPYING. +*/ + +#include <gtk/gtk.h> + +#include "odin.h" +#include "client.h" +#include "allocators.h" + +struct _summary { + GtkFrame bin; + + AllocatorsStore *store; +}; + +typedef struct _summary_class { + GtkFrameClass parent_class; +} SummaryClass; + +static GType +summary_get_type (void); + +G_DEFINE_TYPE (Summary, summary, GTK_TYPE_FRAME) + +static void +summary_class_init (SummaryClass *klass) +{ +} + +static void +summary_init (Summary *self) +{ + GtkWidget *w, *sw; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + self->store = allocators_store_new (); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (self), sw); + gtk_widget_show (sw); + + w = gtk_tree_view_new_with_model ((GtkTreeModel *) self->store); + gtk_container_add (GTK_CONTAINER (sw), w); + gtk_widget_show (w); + + renderer = gtk_cell_renderer_text_new (); + g_object_set (G_OBJECT (renderer), + "ellipsize", PANGO_ELLIPSIZE_END, + "ellipsize-set", TRUE, + "width-chars", 50, + NULL); + column = gtk_tree_view_column_new_with_attributes ("Allocator", + renderer, "text", ALLOCATORS_FRAME, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (w), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Bytes", + renderer, NULL); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer, + cell_layout_pretty_print_uint64, GUINT_TO_POINTER (ALLOCATORS_SIZE), + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (w), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Count", + renderer, NULL); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer, + cell_layout_pretty_print_uint, GUINT_TO_POINTER (ALLOCATORS_COUNT), + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (w), column); + +} + +GtkWidget * +summary_new (void) +{ + return g_object_new (summary_get_type (), NULL); +} + +void +summary_update (Summary *summary, Client *client) +{ + allocators_store_update_from_allocators (summary->store, + client->allocators); +} |