summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2009-03-19 16:16:26 +0000
committerChris Wilson <chris@chris-wilson.co.uk>2009-03-19 16:16:37 +0000
commit4b37f6c5ff7976ad42a90499f118d61fe41fc904 (patch)
tree071cc1d5c3269b050db05e8b53023dbc4b975013
parentbc46ac8aaf8378b6399f1e81ec19516cabf020d1 (diff)
mmap.mmap
-rw-r--r--Makefile.am3
-rw-r--r--src/app.c149
-rw-r--r--src/client.h4
-rw-r--r--src/lwp-events.h16
-rw-r--r--src/lwp.c98
-rw-r--r--src/maps-store.c321
-rw-r--r--src/maps.c144
-rw-r--r--src/maps.h49
-rw-r--r--src/odin.h24
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 \
diff --git a/src/app.c b/src/app.c
index cff3f7e..a4977db 100644
--- a/src/app.c
+++ b/src/app.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;
diff --git a/src/lwp.c b/src/lwp.c
index 3986595..a6bc9f5 100644
--- a/src/lwp.c
+++ b/src/lwp.c
@@ -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 */
+
diff --git a/src/odin.h b/src/odin.h
index dac8e55..f2820d4 100644
--- a/src/odin.h
+++ b/src/odin.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 */