diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2009-03-19 16:16:26 +0000 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2009-03-19 16:16:37 +0000 |
commit | 4b37f6c5ff7976ad42a90499f118d61fe41fc904 (patch) | |
tree | 071cc1d5c3269b050db05e8b53023dbc4b975013 | |
parent | bc46ac8aaf8378b6399f1e81ec19516cabf020d1 (diff) |
mmap.mmap
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | src/app.c | 149 | ||||
-rw-r--r-- | src/client.h | 4 | ||||
-rw-r--r-- | src/lwp-events.h | 16 | ||||
-rw-r--r-- | src/lwp.c | 98 | ||||
-rw-r--r-- | src/maps-store.c | 321 | ||||
-rw-r--r-- | src/maps.c | 144 | ||||
-rw-r--r-- | src/maps.h | 49 | ||||
-rw-r--r-- | src/odin.h | 24 |
9 files changed, 807 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 378be43..58ea729 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,6 +25,9 @@ odin_SOURCES = src/odin.h \ src/callgraph-store.c \ src/callgraph-ring.c \ src/callgraph-treemap.c \ + src/maps.h \ + src/maps.c \ + src/maps-store.c \ src/procmap.h \ src/procmap.c \ src/procmap-store.c \ @@ -39,6 +39,7 @@ #include "block.h" #include "callgraph.h" #include "frames.h" +#include "maps.h" #include "procmap.h" #include "shared-objects.h" @@ -62,6 +63,7 @@ struct _app { GtkWidget *ring; } allocations; GtkWidget *spacetime; + GtkWidget *maps; GtkWidget *procmap; GtkWidget *timeline; GtkWidget *statusbar; @@ -1086,6 +1088,19 @@ main_window_create (App *app) gtk_widget_show (app->spacetime); gtk_widget_show (label); + app->maps = maps_new (app); + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (sw), app->maps); + gtk_widget_show (app->maps); + + label = gtk_label_new ("Mappings"); + gtk_notebook_append_page (GTK_NOTEBOOK (app->notebook), + sw, label); + gtk_widget_show (sw); + gtk_widget_show (label); + return app->window; } @@ -1368,6 +1383,99 @@ _client_move_block (Client *c, Allocator *A, gpointer old_addr, gpointer new_add } } +static Map * +_client_map_alloc (Client *client) +{ + Map *map; + gsize size = sizeof (Map); + +#ifndef G_ENABLE_DEBUG + Chunk *c; + + map = client->map_free_list; + if (map != NULL) { + client->map_free_list = map->next; + memset (map, 0, size); + return map; + } + + c = client->map_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 = client->map_chunks; + client->map_chunks = c; + } + + map = (Map *) (c->mem + c->used); + c->used += size; +#else + map = g_malloc (size); +#endif + + memset (map, 0, size); + return map; +} + +static void +_client_add_map (Client *c, + Allocator *A, + gpointer addr, gsize size, + guint32 time, guint32 max_time) +{ + Map *map, *m; + + map = _client_map_alloc (c); + map->addr = addr; + map->size = size; + map->age = time; + map->allocator = A; + + for (m = c->mappings; m && m->addr < map->addr; m = m->next) + ; + map->next = m; + if (m != NULL) { + map->prev = m->prev; + if (m->prev != NULL) + m->prev->next = map; + else + c->mappings = map; + m->prev = map; + } else { + c->mappings = map; + map->prev = NULL; + } +} + +static void +_client_delete_map (Client *c, gpointer addr, gsize size) +{ + Map *map; + + for (map = c->mappings; map && map->addr < addr; map = map->next) + ; + if (map == NULL || map->addr != addr) { + g_debug ("invalid munmap? %p, %d\n", addr, size); + return; + } + + if (map->prev != NULL) + map->prev->next = map->next; + else + c->mappings = map->next; + if (map->next != NULL) + map->next->prev = map->prev; + +#ifndef G_ENABLE_DEBUG + map->next = c->map_free_list; + c->map_free_list = map; +#else + g_free (map); +#endif +} + static void _client_init (Client *client, App *app) { @@ -1396,9 +1504,13 @@ _client_init (Client *client, App *app) client->block_by_addr.size); client->blocks = NULL; + client->mappings = NULL; + client->perm_chunks = NULL; client->block_chunks = NULL; client->block_free_list = NULL; + client->map_chunks = NULL; + client->map_free_list = NULL; client->call_graph = call_graph_store_new (); @@ -1536,6 +1648,16 @@ lwp_discard (gzFile *file) case LWP_FREE: ok &= discardn (file, sizeof (ev.event.free.addr)); break; + + case LWP_MMAP: + ok &= discardn (file, sizeof (ev.event.mmap.addr)); + ok &= discardn (file, sizeof (ev.event.mmap.size)); + break; + + case LWP_MUNMAP: + ok &= discardn (file, sizeof (ev.event.munmap.addr)); + ok &= discardn (file, sizeof (ev.event.munmap.size)); + break; } if (!ok) @@ -1743,6 +1865,25 @@ lwp_read (gzFile *file, App *app) ev.time, time); break; + + case LWP_MMAP: + readn (file, &ev.event.mmap.addr, sizeof (ev.event.mmap.addr)); + readn (file, &ev.event.mmap.size, sizeof (ev.event.mmap.size)); + _client_add_map (&app->client, + A, + ev.event.mmap.addr, + ev.event.mmap.size, + ev.time, + time); + break; + + case LWP_MUNMAP: + readn (file, &ev.event.munmap.addr, sizeof (ev.event.munmap.addr)); + readn (file, &ev.event.munmap.size, sizeof (ev.event.munmap.size)); + _client_delete_map (&app->client, + ev.event.munmap.addr, + ev.event.munmap.size); + break; } } @@ -1939,6 +2080,8 @@ _update_client (App *app) model = procmap_store_new (app->client.pid); gtk_tree_view_set_model (ensure_procmap (app), model); g_object_unref (model); + + maps_update ((Maps *) app->maps); } else { call_graph_store_update_tree_model (app->client.call_graph); } @@ -2565,3 +2708,9 @@ int main return 0; } + +Client * +app_get_client (App *app) +{ + return &app->client; +} diff --git a/src/client.h b/src/client.h index 5a17840..aa26349 100644 --- a/src/client.h +++ b/src/client.h @@ -65,6 +65,8 @@ struct _client { Block **nodes; } block_by_addr; + Map *mappings; + SharedObjects objects; Frames frames; @@ -80,7 +82,9 @@ struct _client { Chunk *perm_chunks; Chunk *block_chunks; + Chunk *map_chunks; Block *block_free_list; + Map *map_free_list; GSource *timeout; }; diff --git a/src/lwp-events.h b/src/lwp-events.h index ab11141..2478458 100644 --- a/src/lwp-events.h +++ b/src/lwp-events.h @@ -37,6 +37,9 @@ typedef enum { LWP_REALLOC, LWP_FREE, + LWP_MMAP, + LWP_MUNMAP, + LWP_DLOPEN, LWP_DLCLOSE } LWP_EventType; @@ -70,6 +73,16 @@ typedef struct _lwp_event_free { gpointer addr; } LWP_EventFree; +typedef struct _lwp_event_mmap { + gpointer addr; + gsize size; +} LWP_EventMmap; + +typedef struct _lwp_event_munmap { + gpointer addr; + gsize size; +} LWP_EventMunmap; + typedef struct _lwp_event_dlopen { } LWP_EventDlopen; @@ -84,6 +97,9 @@ typedef union _lwp_event { LWP_EventRealloc realloc; LWP_EventFree free; + LWP_EventMmap mmap; + LWP_EventMunmap munmap; + LWP_EventDlopen dlopen; LWP_EventDlclose dlclose; } LWP_Event; @@ -43,6 +43,7 @@ #include <stdlib.h> /* malloc() and friends */ #include <malloc.h> /* memalign() and friends */ #include <string.h> /* memcpy() */ +#include <sys/mman.h> /* mmap() and friends */ #include "lwp-events.h" @@ -131,6 +132,10 @@ DLSYM_DECLARE (memalign); DLSYM_DECLARE (posix_memalign); DLSYM_DECLARE (free); +DLSYM_DECLARE (mmap); +DLSYM_DECLARE (mmap64); +DLSYM_DECLARE (munmap); + DLSYM_DECLARE (dlopen); DLSYM_DECLARE (dlclose); @@ -553,6 +558,22 @@ _lwp_write_events (const LWP_EventRecord *events, gushort n_events) goto CLEAN_FD; } break; + + case LWP_MMAP: + if (! _lwp_writen (fd, &ev->mmap.addr, sizeof (ev->mmap.addr)) || + ! _lwp_writen (fd, &ev->mmap.size, sizeof (ev->mmap.size))) + { + goto CLEAN_FD; + } + break; + + case LWP_MUNMAP: + if (! _lwp_writen (fd, &ev->munmap.addr, sizeof (ev->munmap.addr)) || + ! _lwp_writen (fd, &ev->munmap.size, sizeof (ev->munmap.size))) + { + goto CLEAN_FD; + } + break; } } @@ -773,6 +794,8 @@ _lwp_record_event (LWP_EventType type, const LWP_Event *ev) case LWP_REALLOC: case LWP_MEMALIGN: case LWP_FREE: + case LWP_MMAP: + case LWP_MUNMAP: default: break; } @@ -832,6 +855,8 @@ _lwp_record_event (LWP_EventType type, const LWP_Event *ev) case LWP_MEMALIGN: caller = memalign; break; case LWP_REALLOC: caller = realloc; break; case LWP_FREE: caller = free; break; + case LWP_MMAP: caller = mmap; break; + case LWP_MUNMAP: caller = munmap; break; } events[n_events].allocator = _lwp_add_allocator (&caller, 1); } @@ -1016,6 +1041,74 @@ free (void *ptr) } void * +mmap (void *addr, size_t length, int prot, int flags, int fd, off_t offset) +{ + LWP_Event event; + void *ret; + + if (! _lwp_dlcall_initialized) { + errno = EINVAL; + return MAP_FAILED; + } + + + ret = DLCALL (mmap, addr, length, prot, flags, fd, offset); + if (ret == MAP_FAILED) + return MAP_FAILED; + + event.mmap.addr = ret; + event.mmap.size = length; + _lwp_record_event (LWP_MMAP, &event); + + return ret; +} + +void * +mmap64 (void *addr, size_t length, int prot, int flags, int fd, off64_t offset) +{ + LWP_Event event; + void *ret; + + if (! _lwp_dlcall_initialized) { + errno = EINVAL; + return MAP_FAILED; + } + + + ret = DLCALL (mmap64, addr, length, prot, flags, fd, offset); + if (ret == MAP_FAILED) + return MAP_FAILED; + + event.mmap.addr = ret; + event.mmap.size = length; + _lwp_record_event (LWP_MMAP, &event); + + return ret; +} + +int +munmap (void *addr, size_t length) +{ + LWP_Event event; + int ret; + + if (! _lwp_dlcall_initialized) { + errno = EINVAL; + return -1; + } + + ret = DLCALL (munmap, addr, length); + if (ret < 0) + return ret; + + event.munmap.addr = addr; + event.munmap.size = length; + _lwp_record_event (LWP_MUNMAP, &event); + + return 0; +} + +void * dlopen (const char *filename, int flag) { LWP_Event event; @@ -1072,7 +1165,6 @@ __lwp_init (void) env = getenv ("LWP_PRUNE_RECURSION"); _lwp_enable_recursion_pruning = env != NULL; } - _lwp_enable_recursion_pruning = TRUE; DLSYM_DEFINE (malloc); DLSYM_DEFINE (calloc); @@ -1083,6 +1175,10 @@ __lwp_init (void) DLSYM_DEFINE (posix_memalign); DLSYM_DEFINE (free); + DLSYM_DEFINE (mmap); + DLSYM_DEFINE (mmap64); + DLSYM_DEFINE (munmap); + DLSYM_DEFINE (dlopen); DLSYM_DEFINE (dlclose); diff --git a/src/maps-store.c b/src/maps-store.c new file mode 100644 index 0000000..963f0a9 --- /dev/null +++ b/src/maps-store.c @@ -0,0 +1,321 @@ +/* + This file is part of odin, a memory profiler with fragmentation analysis. + + Copyright (C) 2009 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 <glibtop.h> +#include <glibtop/procmap.h> + +#include "odin.h" +#include "client.h" +#include "maps.h" + +struct _maps_store { + GObject object; + + App *app; + guint max_path; +}; + +typedef struct _maps_store_class { + GObjectClass parent_class; +} MapsStoreClass; + +static void +maps_store_tree_model_init (GtkTreeModelIface *iface); + +static GType +maps_store_get_type (void); + +G_DEFINE_TYPE_WITH_CODE (MapsStore, maps_store, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + maps_store_tree_model_init)) + +static void +maps_store_class_init (MapsStoreClass *klass) +{ +} + + +/* GtkTreeModelIface */ + +static GtkTreeModelFlags +maps_store_get_flags (GtkTreeModel *tree_model) +{ + return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY; +} + +static gint +maps_store_get_n_columns (GtkTreeModel *tree_model) +{ + return MAPS_N_COLUMNS; +} + +static GType +maps_store_get_column_type (GtkTreeModel *tree_model, + gint index) +{ + switch (index) { + case MAPS_FILENAME: return G_TYPE_STRING; + case MAPS_MODE: return G_TYPE_STRING; + case MAPS_ADDR: return G_TYPE_ULONG; + case MAPS_SIZE: return G_TYPE_ULONG; + case MAPS_RSS: return G_TYPE_ULONG; + case MAPS_DIRTY: return G_TYPE_ULONG; + default: return G_TYPE_INVALID; + } +} + +static gboolean +maps_store_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + MapsStore *self = (MapsStore *) tree_model; + Client *client = app_get_client (self->app); + Map *map; + gulong n; + + n = gtk_tree_path_get_depth (path); + g_return_val_if_fail (n == 1, FALSE); + + n = gtk_tree_path_get_indices (path)[0]; + map = client->mappings; + while (map != NULL && n--) + map = map->next; + if (map == NULL) + return FALSE; + + iter->user_data = map; + iter->stamp = 1; + return TRUE; +} + +static GtkTreePath * +maps_store_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + Map *map = iter->user_data; + GtkTreePath *path; + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, map->index); + return path; +} + +static void +maps_store_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + Map *map = iter->user_data; + switch (column) { + case MAPS_FILENAME: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, map->filename ? map->filename : ""); + break; + + case MAPS_MODE: { + gchar perm[6]; + perm [0] = map->perm & GLIBTOP_MAP_PERM_READ ? 'r' : '-'; + perm [1] = map->perm & GLIBTOP_MAP_PERM_WRITE ? 'w' : '-'; + perm [2] = map->perm & GLIBTOP_MAP_PERM_EXECUTE ? 'x' : '-'; + perm [3] = map->perm & GLIBTOP_MAP_PERM_SHARED ? 's' : '-'; + perm [4] = map->perm & GLIBTOP_MAP_PERM_PRIVATE ? 'p' : '-'; + perm [5] = '\0'; + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, perm); + break; + } + + case MAPS_ADDR: + g_value_init (value, G_TYPE_ULONG); + g_value_set_ulong (value, (gulong) map->addr); + break; + + case MAPS_SIZE: + g_value_init (value, G_TYPE_ULONG); + g_value_set_ulong (value, map->size); + break; + + case MAPS_RSS: + g_value_init (value, G_TYPE_ULONG); + g_value_set_ulong (value, map->rss); + break; + + case MAPS_DIRTY: + g_value_init (value, G_TYPE_ULONG); + g_value_set_ulong (value, map->dirty); + break; + } +} + +static gboolean +maps_store_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + Map *map = iter->user_data; + + if (map->next == NULL) + return FALSE; + + iter->user_data = map->next; + return TRUE; +} + +static gboolean +maps_store_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + return parent == NULL; +} + +static gboolean +maps_store_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + return FALSE; +} + +static gint +maps_store_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + return 0; +} + +static gboolean +maps_store_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) +{ + MapsStore *self = (MapsStore *) tree_model; + Client *client = app_get_client (self->app); + Map *map; + + if (parent != NULL) + return FALSE; + + map = client->mappings; + while (--n && map != NULL) + map = map->next; + if (map == NULL) + return FALSE; + + iter->user_data = map; + iter->stamp = 1; + return TRUE; +} + +static gboolean +maps_store_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + return FALSE; +} + +static void +maps_store_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = maps_store_get_flags; + iface->get_n_columns = maps_store_get_n_columns; + iface->get_column_type = maps_store_get_column_type; + iface->get_iter = maps_store_get_iter; + iface->get_path = maps_store_get_path; + iface->get_value = maps_store_get_value; + iface->iter_next = maps_store_iter_next; + iface->iter_children = maps_store_iter_children; + iface->iter_has_child = maps_store_iter_has_child; + iface->iter_n_children = maps_store_iter_n_children; + iface->iter_nth_child = maps_store_iter_nth_child; + iface->iter_parent = maps_store_iter_parent; +} + +static void +maps_store_init (MapsStore *self) +{ +} + +void +maps_store_update (MapsStore *self) +{ + Client *client = app_get_client (self->app); + glibtop_proc_map procmap; + glibtop_map_entry *maps; + Map *map; + guint n; + + while (self->max_path--) { + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices (self->max_path, -1); + gtk_tree_model_row_deleted ((GtkTreeModel *) self, path); + gtk_tree_path_free (path); + } + + maps = glibtop_get_proc_map (&procmap, (pid_t) client->pid); + for (n = 0; n < procmap.number; n++) { + for (map = client->mappings; map != NULL; map = map->next) { + if ((gulong) map->addr == maps[n].start) { + if (map->filename == NULL) { + if (maps[n].flags & (1L << GLIBTOP_MAP_ENTRY_FILENAME)) { + map->filename = client_add_string (client, + maps[n].filename); + } else + map->filename = "???"; + + map->perm = maps[n].perm; + } + map->rss = maps[n].rss; + map->dirty = maps[n].private_dirty; + break; + } + } + } + g_free (maps); + + self->max_path = 0; + for (map = client->mappings; map != NULL; map = map->next) { + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices (self->max_path, -1); + iter.stamp = 1; + iter.user_data = map; + gtk_tree_model_row_inserted ((GtkTreeModel *) self, path, &iter); + gtk_tree_path_free (path); + map->index = self->max_path++; + } +} + +GtkTreeModel * +maps_store_new (App *app) +{ + MapsStore *self; + + self = g_object_new (maps_store_get_type (), NULL); + self->app = app; + + return (GtkTreeModel *) self; +} diff --git a/src/maps.c b/src/maps.c new file mode 100644 index 0000000..05de64f --- /dev/null +++ b/src/maps.c @@ -0,0 +1,144 @@ +/* + 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 "maps.h" + +#define _(x) x + +struct _maps { + GtkTreeView tv; +}; + +typedef struct _maps_class { + GtkTreeViewClass parent_class; +} MapsClass; + +static GType +maps_get_type (void); + +G_DEFINE_TYPE (Maps, maps, GTK_TYPE_TREE_VIEW) + +static void +maps_set_property (GObject *obj, guint id, const GValue *v, GParamSpec *spec) +{ + Maps *self = (Maps *) obj; + switch (id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, spec); + break; + } +} + +static void +maps_get_property (GObject *obj, guint id, GValue *v, GParamSpec *spec) +{ + Maps *self = (Maps *) obj; + switch (id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, spec); + break; + } +} + +static void +maps_class_init (MapsClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + GtkWidgetClass *widget_class = (GtkWidgetClass *) klass; + + object_class->set_property = maps_set_property; + object_class->get_property = maps_get_property; +} + +static void +maps_init (Maps *self) +{ + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + 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 ("Filename", + renderer, "text", MAPS_FILENAME, NULL); + gtk_tree_view_append_column (&self->tv, column); + + renderer = gtk_cell_renderer_text_new (); + g_object_set (G_OBJECT (renderer), + "width-chars", 5, + NULL); + column = gtk_tree_view_column_new_with_attributes ("Mode", + renderer, "text", MAPS_MODE, NULL); + gtk_tree_view_append_column (&self->tv, column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Address", + renderer, "text", MAPS_ADDR, 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_uint, GUINT_TO_POINTER (MAPS_SIZE), + NULL); + gtk_tree_view_append_column (&self->tv, column); + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("RSS", + renderer, NULL); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer, + cell_layout_pretty_print_uint, GUINT_TO_POINTER (MAPS_RSS), + NULL); + gtk_tree_view_append_column (&self->tv, column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Dirty", + renderer, NULL); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer, + cell_layout_pretty_print_uint, GUINT_TO_POINTER (MAPS_DIRTY), + NULL); + gtk_tree_view_append_column (&self->tv, column); + + gtk_tree_view_set_grid_lines (&self->tv, GTK_TREE_VIEW_GRID_LINES_VERTICAL); + + //gtk_widget_set_has_tooltip (GTK_WIDGET (self), TRUE); +} + +GtkWidget * +maps_new (App *app) +{ + return g_object_new (maps_get_type(), + "model", maps_store_new (app), + NULL); +} + +void +maps_update (Maps *self) +{ + GtkTreeModel *model = gtk_tree_view_get_model (&self->tv); + maps_store_update ((MapsStore *) model); +} diff --git a/src/maps.h b/src/maps.h new file mode 100644 index 0000000..df9952f --- /dev/null +++ b/src/maps.h @@ -0,0 +1,49 @@ +/* + This file is part of odin, a memory profiler with fragmentation analysis. + + Copyright (C) 2009 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 MAPS_H +#define MAPS_H + +G_BEGIN_DECLS + +typedef struct _maps_store MapsStore; + +enum { + MAPS_FILENAME, + MAPS_MODE, + MAPS_ADDR, + MAPS_SIZE, + MAPS_RSS, + MAPS_DIRTY, + + MAPS_N_COLUMNS, +}; + +GtkTreeModel * +maps_store_new (App *app); + +void +maps_store_update (MapsStore *self); + +G_END_DECLS + +#endif /* MAPS_H */ + @@ -34,6 +34,7 @@ typedef struct _allocator Allocator; typedef struct _allocator_time AllocatorTime; typedef struct _block Block; typedef struct _chunk Chunk; +typedef struct _map Map; typedef struct _thread_faults ThreadFaults; typedef struct _frame Frame; @@ -45,6 +46,7 @@ typedef struct _shared_objects SharedObjects; typedef struct _allocators Allocators; typedef struct _block_map BlockMap; typedef struct _call_graph CallGraph; +typedef struct _maps Maps; typedef struct _procmap Procmap; typedef struct _summary Summary; typedef struct _timeline Timeline; @@ -77,6 +79,20 @@ struct _thread_faults { ThreadFaults *next; }; +struct _map { + Map *next; + Map *prev; + Allocator *allocator; + gpointer addr; + const char *filename; + guint perm; + gsize size; + gulong rss; + gulong dirty; + guint age; + guint index; +}; + struct _allocator { gulong key; Allocator *next, *ht_next; @@ -243,6 +259,11 @@ call_graph_ring_new (void); GtkWidget * procmap_new (void); +GtkWidget * +maps_new (App *app); +void +maps_update (Maps *self); + G_CONST_RETURN Event * app_find_prev_event_for_addr_range (App *app, guint time, gulong min, gulong max); @@ -275,6 +296,9 @@ hsv_to_rgb (gdouble h, gdouble s, gdouble v, gdouble *rgb); gdouble median_double (gdouble *v, guint n); +Client * +app_get_client (App *app); + G_END_DECLS #endif /* ODIN_H */ |