summaryrefslogtreecommitdiff
path: root/stackstash.c
diff options
context:
space:
mode:
authorSøren Sandmann Pedersen <ssp@src.gnome.org>2004-04-27 11:08:55 +0000
committerSøren Sandmann Pedersen <ssp@src.gnome.org>2004-04-27 11:08:55 +0000
commit43dddf31acbd8556ea08eb1eb714b7848c5988ac (patch)
tree7eb6cd35ac409080079d94b47398debc5e0ed75f /stackstash.c
Initial revision
Diffstat (limited to 'stackstash.c')
-rw-r--r--stackstash.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/stackstash.c b/stackstash.c
new file mode 100644
index 0000000..5f88ea1
--- /dev/null
+++ b/stackstash.c
@@ -0,0 +1,195 @@
+#include "stackstash.h"
+
+typedef struct StackNode StackNode;
+
+struct StackNode
+{
+ StackNode * parent;
+ gpointer address;
+ StackNode * siblings;
+ StackNode * children;
+ StackNode * next; /* next leaf with the same pid */
+ int size;
+};
+
+struct StackStash
+{
+ StackNode *root;
+ GHashTable *leaves_by_process;
+};
+
+static StackNode *
+stack_node_new (void)
+{
+ StackNode *node = g_new (StackNode, 1);
+ node->siblings = NULL;
+ node->children = NULL;
+ node->address = NULL;
+ node->parent = NULL;
+ node->next = NULL;
+ node->size = 0;
+ return node;
+}
+
+static void
+stack_node_destroy (gpointer p)
+{
+ StackNode *node = p;
+ if (node)
+ {
+ stack_node_destroy (node->siblings);
+ stack_node_destroy (node->children);
+ g_free (node);
+ }
+}
+
+/* Stach */
+StackStash *
+stack_stash_new (void)
+{
+ StackStash *stash = g_new (StackStash, 1);
+
+ stash->leaves_by_process =
+ g_hash_table_new (g_direct_hash, g_direct_equal);
+ stash->root = NULL;
+ return stash;
+}
+
+static StackNode *
+stack_node_add_trace (StackNode *node,
+ GList *bottom,
+ gint size,
+ StackNode **leaf)
+{
+ StackNode *match;
+ StackNode *n;
+
+ if (!bottom)
+ {
+ *leaf = NULL;
+ return node;
+ }
+
+ if (!bottom->next)
+ {
+ /* A leaf must always be separate, so pids can
+ * point to them
+ */
+ match = NULL;
+ }
+ else
+ {
+ for (match = node; match != NULL; match = match->siblings)
+ {
+ if (match->address == bottom->data)
+ break;
+ }
+ }
+
+ if (!match)
+ {
+ match = stack_node_new ();
+ match->address = bottom->data;
+ match->siblings = node;
+ node = match;
+ }
+
+ match->children =
+ stack_node_add_trace (match->children, bottom->next, size, leaf);
+
+ for (n = match->children; n; n = n->siblings)
+ n->parent = match;
+
+ if (!bottom->next)
+ {
+ match->size += size;
+ *leaf = match;
+ }
+
+ return node;
+}
+
+void
+stack_stash_add_trace (StackStash *stash,
+ Process *process,
+ gulong *addrs,
+ int n_addrs,
+ int size)
+{
+ GList *trace;
+ StackNode *leaf;
+ int i;
+
+ trace = NULL;
+ for (i = 0; i < n_addrs; ++i)
+ trace = g_list_prepend (trace, GINT_TO_POINTER (addrs[i]));
+
+ stash->root = stack_node_add_trace (stash->root, trace, size, &leaf);
+
+ leaf->next = g_hash_table_lookup (
+ stash->leaves_by_process, process);
+ g_hash_table_insert (
+ stash->leaves_by_process, process, leaf);
+
+ g_list_free (trace);
+}
+
+typedef struct CallbackInfo
+{
+ StackFunction func;
+ gpointer data;
+} CallbackInfo;
+
+static void
+do_callback (gpointer key, gpointer value, gpointer data)
+{
+ CallbackInfo *info = data;
+ Process *process = key;
+ StackNode *n;
+ StackNode *leaf = value;
+ while (leaf)
+ {
+ GSList *trace;
+
+ trace = NULL;
+ for (n = leaf; n; n = n->parent)
+ trace = g_slist_prepend (trace, n->address);
+
+ info->func (process, trace, leaf->size, info->data);
+
+ g_slist_free (trace);
+
+ leaf = leaf->next;
+ }
+}
+
+void
+stack_stash_foreach (StackStash *stash,
+ StackFunction stack_func,
+ gpointer data)
+{
+ CallbackInfo info;
+ info.func = stack_func;
+ info.data = data;
+
+ g_hash_table_foreach (stash->leaves_by_process, do_callback, &info);
+}
+
+static void
+stack_node_free (StackNode *node)
+{
+ if (!node)
+ return;
+
+ stack_node_free (node->siblings);
+ stack_node_free (node->children);
+
+ g_free (node);
+}
+
+void
+stack_stash_free (StackStash *stash)
+{
+ stack_node_free (stash->root);
+ g_hash_table_destroy (stash->leaves_by_process);
+}