diff options
-rw-r--r-- | src/app.c | 50 | ||||
-rw-r--r-- | src/callgraph-store.c | 247 | ||||
-rw-r--r-- | src/callgraph-treemap.c | 2 | ||||
-rw-r--r-- | src/callgraph.c | 92 | ||||
-rw-r--r-- | src/callgraph.h | 11 |
5 files changed, 250 insertions, 152 deletions
@@ -30,6 +30,7 @@ struct _app { GtkWidget *timeline; GtkWidget *statusbar; + gboolean discard_log; GtkWidget *log_tv; GtkTextBuffer *log; GtkTextMark *log_end; @@ -235,6 +236,7 @@ read_allocator (App *app, int fd, guint time) guint magic; guint last; guint n; + guint total; if (! readn (fd, &key, sizeof (key))) return NULL; @@ -330,11 +332,14 @@ read_allocator (App *app, int fd, guint time) return NULL; } + total = 0; At->max_size_alloc = 0; for (n = 0; n < G_N_ELEMENTS (At->size_allocs); n++) { if (At->size_allocs[n] != 0) At->max_size_alloc = n + 1; + total += At->size_allocs[n]; } + g_assert (total == At->n_allocs); At->faults.next = NULL; if (At->faults.tid != 0) { @@ -751,36 +756,18 @@ refresh_clicked (GtkButton *button, App *app) } static void -tree_map_selection_changed (GtkWidget *w, GtkTreeIter *iter, GtkTreeView *tv) +call_graph_select_iter (GtkWidget *w, GtkTreeIter *iter, GtkTreeView *tv) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreePath *path; - selection = gtk_tree_view_get_selection (tv); - gtk_tree_selection_unselect_all (selection); - model = gtk_tree_view_get_model (tv); - path = gtk_tree_model_get_path (model, iter); - - gtk_tree_view_expand_to_path (tv, path); - gtk_tree_view_scroll_to_cell (tv, path, NULL, FALSE, 0., 0.); - gtk_tree_path_free (path); - - gtk_tree_selection_select_iter (selection, iter); -} - -static void -ring_selection_changed (GtkWidget *w, GtkTreeIter *iter, GtkTreeView *tv) -{ - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreePath *path; + call_graph_store_update_tree_model ((CallGraphStore *) model); selection = gtk_tree_view_get_selection (tv); gtk_tree_selection_unselect_all (selection); - model = gtk_tree_view_get_model (tv); path = gtk_tree_model_get_path (model, iter); gtk_tree_view_expand_to_path (tv, path); @@ -820,11 +807,11 @@ main_window_create (App *app) app); g_signal_connect (app->allocations.tree_map, "selected", - G_CALLBACK (tree_map_selection_changed), + G_CALLBACK (call_graph_select_iter), app->allocations.call_graph); g_signal_connect (app->allocations.ring, "selected", - G_CALLBACK (ring_selection_changed), + G_CALLBACK (call_graph_select_iter), app->allocations.call_graph); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (app->allocation_image.allocators)); @@ -966,6 +953,9 @@ tcp_events_server_cb (GIOChannel *source, procmap_store_new (app->pid)); } + if (app->pid == 0 && app->child_terminated) + call_graph_store_update_tree_model (app->call_graph); + gdk_window_set_cursor (app->window->window, NULL); gnet_tcp_socket_delete (client); @@ -991,6 +981,9 @@ ensure_log (App *app) GtkWidget *w, *sw, *label; GtkTextIter iter; + if (app->discard_log) + return NULL; + if (app->log != NULL) return app->log; @@ -1051,8 +1044,10 @@ tcp_log_client_cb (GIOChannel *source, } } - gtk_text_buffer_get_end_iter (log, &iter); - gtk_text_buffer_insert (log, &iter, buf, ret); + if (log != NULL) { + gtk_text_buffer_get_end_iter (log, &iter); + gtk_text_buffer_insert (log, &iter, buf, ret); + } } while (ret == sizeof (buf)); if (ret <= 0) @@ -1185,6 +1180,7 @@ static void reap_child (GPid pid, gint status, gpointer data) { App *app = data; + gtk_widget_set_sensitive (app->refresh_button, FALSE); app->child_terminated = TRUE; g_spawn_close_pid (pid); @@ -1275,6 +1271,8 @@ int main app.allocators_by_addr.nodes = g_new0 (Allocator *, app.allocators_by_addr.size); + app.discard_log = TRUE; + window = main_window_create (&app); g_signal_connect (window, "delete-event", G_CALLBACK (gtk_main_quit), NULL); gtk_window_present (GTK_WINDOW (window)); @@ -1306,8 +1304,10 @@ int main if (io != NULL) { if (! load_allocators (io, &app)) { g_warning ("Failed to load allocations from '%s'", argv[1]); - } else + } else { + call_graph_store_update_tree_model (app.call_graph); have_allocators = TRUE; + } g_io_channel_unref (io); } if (! have_allocators) { diff --git a/src/callgraph-store.c b/src/callgraph-store.c index 38b0d99..869b0b3 100644 --- a/src/callgraph-store.c +++ b/src/callgraph-store.c @@ -29,6 +29,7 @@ typedef gboolean (*TraverseFunc) (CallGraphStore *, CallGraphFrame *, gpointer); enum { INSERTED = 1 << 0, + HAS_CHILD = 1 << 1, }; static void @@ -361,54 +362,66 @@ _call_graph_frame_key_cmp (gconstpointer A, gconstpointer B) static CallGraphFrame * _call_graph_frame_new (CallGraphStore *store, - CallGraphFrame *parent, - guint ip, - const gchar *srcloc, - const gchar *function) + const Allocator *A, + guint depth, + CallGraphFrame *parent) { CallGraphFrame *frame; + CallGraphFrame *node, **prev; + const AllocatorTime *At = A->time_tail; + guint index; + guint n, max; frame = _call_graph_frame_alloc (store); - frame->ip = ip; - frame->frame = srcloc; - frame->function = function; + + frame->allocator = A; + frame->depth = depth; + + frame->ip = A->ips[depth]; + frame->frame = A->functions_srcloc[depth]; + frame->function = A->functions[depth]; + frame->parent = parent; frame->next = store->frames; store->frames = frame; - if (parent != NULL) { - CallGraphFrame *node, **prev; - guint index; - - parent->children = _tree_insert (parent->children, - &frame->node, - _call_graph_frame_node_cmp); - - index = (ip ^ 6017773) % store->frames_by_ip.size; - prev = &store->frames_by_ip.nodes[index]; - while ((node = *prev) != NULL && node->ip != ip) - prev = &node->ht_next_by_ip; - if (node != NULL) { - frame->next_by_ip = node; - *prev = node->ht_next_by_ip; - node->ht_next_by_ip = NULL; - } - frame->ht_next_by_ip = store->frames_by_ip.nodes[index]; - store->frames_by_ip.nodes[index] = frame; - - index = (GPOINTER_TO_UINT (function) ^ 6017773) % store->frames_by_fn.size; - prev = &store->frames_by_fn.nodes[index]; - while ((node = *prev) != NULL && node->function != function) - prev = &node->ht_next_by_fn; - if (node != NULL) { - frame->next_by_fn = node; - *prev = node->ht_next_by_fn; - node->ht_next_by_fn = NULL; - } - frame->ht_next_by_fn = store->frames_by_fn.nodes[index]; - store->frames_by_fn.nodes[index] = frame; + frame->bytes = At->bytes; + frame->allocs = At->n_allocs; + frame->frees = At->n_frees; + + max = At->max_size_alloc; + for (n = 0; n < max; n++) + frame->size_allocs[n] = At->size_allocs[n]; + + + parent->children = _tree_insert (parent->children, + &frame->node, + _call_graph_frame_node_cmp); + + index = (frame->ip ^ 6017773) % store->frames_by_ip.size; + prev = &store->frames_by_ip.nodes[index]; + while ((node = *prev) != NULL && node->ip != frame->ip) + prev = &node->ht_next_by_ip; + if (node != NULL) { + frame->next_by_ip = node; + *prev = node->ht_next_by_ip; + node->ht_next_by_ip = NULL; } + frame->ht_next_by_ip = store->frames_by_ip.nodes[index]; + store->frames_by_ip.nodes[index] = frame; + + index = (GPOINTER_TO_UINT (frame->function) ^ 6017773) % store->frames_by_fn.size; + prev = &store->frames_by_fn.nodes[index]; + while ((node = *prev) != NULL && node->function != frame->function) + prev = &node->ht_next_by_fn; + if (node != NULL) { + frame->next_by_fn = node; + *prev = node->ht_next_by_fn; + node->ht_next_by_fn = NULL; + } + frame->ht_next_by_fn = store->frames_by_fn.nodes[index]; + store->frames_by_fn.nodes[index] = frame; return frame; } @@ -509,7 +522,7 @@ _call_graph_store_get_iter (GtkTreeModel *tree_model, depth = gtk_tree_path_get_depth (path); i = gtk_tree_path_get_indices (path); - frame = self->root; + frame = &self->root; for (n = 1; n < depth; n++) { if ((guint) i[n] >= frame->n_filter) return FALSE; @@ -611,7 +624,7 @@ _call_graph_store_iter_children (GtkTreeModel *tree_model, iter->user_data = frame->filter[0]; } else - iter->user_data = self->root; + iter->user_data = &self->root; return TRUE; } @@ -650,7 +663,7 @@ _call_graph_store_iter_nth_child (GtkTreeModel *tree_model, if (n >= 1) return FALSE; iter->stamp = self->stamp; - iter->user_data = self->root; + iter->user_data = &self->root; return TRUE; } @@ -707,8 +720,9 @@ simple_hash_table_init (SimpleHashTable *ht, guint size) static void call_graph_store_init (CallGraphStore *self) { - self->root = _call_graph_frame_new (self, NULL, 0, - "Everything", "Everything"); + self->root.frame = self->root.function = "Everything"; + self->root.depth = -1; + self->frames = &self->root; /* XXX */ simple_hash_table_init (&self->frames_by_ip, 20000); @@ -743,35 +757,22 @@ emit_tree_model_reorder (CallGraphFrame *frame, { gint new_order_stack[1024]; gint *new_order; - guint n; - - g_print ("reorder\n"); + guint n, m; if (frame->old_n_filter > G_N_ELEMENTS (new_order_stack)) new_order = g_new (gint, frame->old_n_filter); else new_order = new_order_stack; - for (n = 0; n < frame->old_n_filter; n++) - new_order[n] = frame->old_filter[n]->index; - - /* exclude children we haven't inserted yet */ - if (frame->old_n_filter != frame->n_filter) { - for (n = 0; n < frame->n_filter; n++) { - if (! (frame->filter[n]->flags & INSERTED)) { - guint m; - for (m = 0; m < frame->old_n_filter; m++) - if ((guint) new_order[m] >= frame->filter[n]->index) - new_order[m]--; - } - } - } + for (n = m = 0; n < frame->n_filter; n++) + if (frame->filter[n]->flags & INSERTED) + new_order[m++] = frame->filter[n]->old_index; - /* XXX */ - n = frame->n_filter; - frame->n_filter = frame->old_n_filter; + frame->n_filter = m; - gtk_tree_model_rows_reordered (model, path, iter, new_order); + gtk_tree_model_rows_reordered (model, path, + frame->filter_parent == NULL ? NULL : iter, + new_order); frame->n_filter = n; @@ -785,14 +786,11 @@ emit_tree_model_signals (CallGraphFrame *frame, CallGraphStore *store) GtkTreeModel *model = (GtkTreeModel *) store; GtkTreeIter iter; GtkTreePath *path; + guint n; if (frame->stamp != store->stamp) return; - if (frame->is_alloc_fn) - return _call_graph_frame_foreach_child (frame, - (GFunc) emit_tree_model_signals, store); - iter.stamp = store->stamp; iter.user_data = frame; path = gtk_tree_model_get_path (model, &iter); @@ -807,18 +805,76 @@ emit_tree_model_signals (CallGraphFrame *frame, CallGraphStore *store) } gtk_tree_model_row_changed (model, path, &iter); + + if (! (frame->flags & HAS_CHILD) && frame->children != NULL) { + frame->flags |= HAS_CHILD; + gtk_tree_model_row_has_child_toggled (model, path, &iter); + } } else { - gtk_tree_model_row_inserted (model, path, &iter); - if (frame->children != NULL) + if (frame->parent != NULL) + gtk_tree_model_row_inserted (model, path, &iter); + if (frame->children != NULL) { + frame->flags |= HAS_CHILD; gtk_tree_model_row_has_child_toggled (model, path, &iter); + } frame->flags |= INSERTED; } gtk_tree_path_free (path); - _call_graph_frame_foreach_child (frame, - (GFunc) emit_tree_model_signals, store); + for (n = 0; n < frame->n_filter; n++) + emit_tree_model_signals (frame->filter[n], store); +} + +void +call_graph_store_update_tree_model (CallGraphStore *store) +{ + CallGraphFrame *frame; + + if (store->nodes == NULL) + return; + + emit_tree_model_signals (&store->root, store); + + for (frame = store->frames; frame != NULL; frame = frame->next) { + frame->old_filter = frame->filter; + frame->old_n_filter = frame->n_filter; + frame->old_index = frame->index; + } + + g_free (store->old_nodes); + store->old_nodes = store->nodes; + store->nodes = NULL; +} + +static void +validate_sums (CallGraphFrame *frame) +{ + guint n; + guint allocs = 0, frees = 0; + guint64 bytes = 0; + + for (n = 0; n < frame->n_filter; n++){ + allocs += frame->filter[n]->allocs; + frees += frame->filter[n]->frees; + bytes += frame->filter[n]->bytes; + } + + g_print ("%s\n", frame->frame); + g_print ("f->allocs=%d, f->frees=%d, f->bytes=%" G_GUINT64_FORMAT "\n", + frame->allocs, frame->frees, frame->bytes); + g_print ("allocs=%d, frees=%d, bytes=%" G_GUINT64_FORMAT "\n", + allocs, frees, bytes); + + g_assert (allocs == frame->allocs); + g_assert (frees == frame->frees); + g_assert (bytes == frame->bytes); + + g_assert (allocs >= frees); + + for (n = 0; n < frame->n_filter; n++) + validate_sums (frame->filter[n]); } void @@ -827,13 +883,11 @@ call_graph_store_update (CallGraphStore *store, Allocator *allocators, guint since) { - static AllocatorTime nil; + static const AllocatorTime nil; Allocator *A; CallGraphFrame *frame, *child, *eol; guint n, new, updated; - g_assert (store != NULL); - if (++store->stamp == 0) store->stamp = 1; @@ -853,7 +907,7 @@ call_graph_store_update (CallGraphStore *store, if (At->max_size_alloc > store->max_size_alloc) store->max_size_alloc = At->max_size_alloc; - frame = store->root; + frame = &store->root; _call_graph_frame_accumulate (frame, At, Ap); frame->stamp = store->stamp; for (n = 0; n < A->n_frames; n++) { @@ -863,16 +917,17 @@ call_graph_store_update (CallGraphStore *store, GUINT_TO_POINTER (A->ips[n]), _call_graph_frame_key_cmp); if (child == NULL) { - child = _call_graph_frame_new (store, frame, - A->ips[n], - A->functions_srcloc[n], - A->functions[n]); + child = _call_graph_frame_new (store, A, n, frame); new++; - } + } else + _call_graph_frame_accumulate (child, At, Ap); + child->stamp = store->stamp; + + if (child->allocator == A) + break; + child->allocator = NULL; + frame = child; - g_assert (frame != NULL); - _call_graph_frame_accumulate (frame, At, Ap); - frame->stamp = store->stamp; } updated++; } @@ -880,6 +935,8 @@ call_graph_store_update (CallGraphStore *store, store->n_frames += new; if (updated) call_graph_store_filter (store, app); + + validate_sums (&store->root); } static void @@ -925,9 +982,11 @@ _call_graph_frame_add_children (CallGraphFrame *frame, CallGraphFrame *parent) _call_graph_frame_foreach_child (frame, (GFunc) _call_graph_frame_add_children, frame); +#if 0 qsort (frame->filter, frame->n_filter, sizeof (CallGraphFrame *), _call_graph_frame_cmp); +#endif for (n = 0; n < frame->n_filter; n++) frame->filter[n]->index = n; @@ -952,21 +1011,22 @@ call_graph_store_filter (CallGraphStore *store, App *app) guint serial = app_get_alloc_fns_serial (app); guint n; - root = store->root; + root = &store->root; root->stamp = store->stamp; /* force an update of the root node */ //if (store->alloc_fns_serial != serial) +#if 0 _call_graph_frame_foreach_child (root, (GFunc) _call_graph_frame_is_alloc, app); +#endif store->alloc_fns_serial = serial; - nodes = children = g_new (CallGraphFrame *, 2*store->n_frames); + nodes = store->nodes; + store->nodes = g_new (CallGraphFrame *, 2*store->n_frames); + children = store->nodes; for (frame = store->frames; frame != NULL; frame = frame->next) { - frame->old_filter = frame->filter; - frame->old_n_filter = frame->n_filter; - if (frame->stamp == store->stamp) { frame->n_filter = 0; frame->filter = NULL; @@ -986,24 +1046,23 @@ call_graph_store_filter (CallGraphStore *store, App *app) children += frame->n_filter; } } + g_assert (children == store->nodes + store->n_frames); + g_free (nodes); root->filter = children; g_assert (root->n_filter == 0); _call_graph_frame_foreach_child (root, (GFunc) _call_graph_frame_add_filtered, root); +#if 0 qsort (root->filter, root->n_filter, sizeof (CallGraphFrame *), _call_graph_frame_cmp); +#endif for (n = 0; n < root->n_filter; n++) root->filter[n]->index = n; - g_free (store->old_nodes); - store->old_nodes = store->nodes; - store->nodes = nodes; - - emit_tree_model_signals (store->root, store); g_signal_emit (store, signals[FILTERED], 0); } diff --git a/src/callgraph-treemap.c b/src/callgraph-treemap.c index 8d53cce..a0c6e1a 100644 --- a/src/callgraph-treemap.c +++ b/src/callgraph-treemap.c @@ -447,7 +447,7 @@ call_graph_tree_map_layout (CallGraphTreeMap *self) rect.height = self->widget.allocation.height + 2*BORDER; call_graph_tree_map_layout_subdivide (self, - &self->layout, &rect, self->model->root); + &self->layout, &rect, &self->model->root); call_graph_tree_map_layout_colour (self, &self->layout, 3., .6, .6); } diff --git a/src/callgraph.c b/src/callgraph.c index 1b12cb6..95a78e8 100644 --- a/src/callgraph.c +++ b/src/callgraph.c @@ -51,8 +51,8 @@ call_graph_set_model (CallGraph *self, CallGraphStore *store) GtkTreePath *path; GtkTreeIter iter; - iter.user_data = store->root; - iter.stamp = 1; + iter.user_data = &store->root; + iter.stamp = store->stamp; path = gtk_tree_model_get_path (model, &iter); gtk_tree_view_expand_to_path (&self->tv, path); @@ -243,44 +243,76 @@ call_graph_query_tooltip (GtkWidget *widget, gtk_tooltip_set_custom (tooltip, GTK_WIDGET (w)); } else if (strcmp (title, "Frame") == 0) { GString *string; - CallGraphFrame *child; const gchar *main_fn = app_get_main_function (app_get (widget)); guint n; gchar *text; - if (frame->n_filter == 0) + if (frame->function == main_fn) return FALSE; - string = g_string_new ("Top allocation site "); - - child = frame->filter[0]; n = 0; - do { - if (strcmp (child->frame, child->parent->frame)) { - if (++n == 8) - break; - if (frame->function == main_fn) + if (frame->allocator == NULL) { + CallGraphFrame *child = frame->filter[0]; + gchar calls[40]; + gint len; + + string = g_string_new ("Most frequent allocation site "); + + do { + if (child->frame != child->parent->frame) { + if (++n == 8) + break; + if (frame->function == main_fn) + break; + } + if (child->allocator != NULL) break; - } - child = child->filter[0]; - } while (child->n_filter); - g_string_append_printf (string, "(%.0f%%):", - child->allocs * 100. / frame->allocs); - frame = frame->filter[0]; - n = 0; - do { - if (strcmp (frame->frame, frame->parent->frame)) { - g_string_append_c (string, '\n'); - g_string_append_c (string, '\t'); - g_string_append (string, frame->frame); - if (frame->function == main_fn) - break; - if (++n == 8) - break; - } + child = child->filter[0]; + } while (child->n_filter); + len = g_snprintf (calls, 20, "%d", child->allocs); + pretty_print_number (calls, len, calls + 20); + g_string_append_printf (string, "%s (%.0f%%) calls:", + calls, child->allocs * 100. / frame->allocs); + frame = frame->filter[0]; - } while (frame->n_filter); + n = 0; + do { + if (frame->frame != frame->parent->frame) { + g_string_append_c (string, '\n'); + g_string_append_c (string, '\t'); + g_string_append (string, frame->frame); + if (frame->function == main_fn) + break; + if (++n == 8) + break; + } + if (frame->allocator != NULL) + break; + + frame = frame->filter[0]; + } while (frame->n_filter); + } else + string = g_string_new ("Called from:"); + + if (frame->function != main_fn && n < 8) { + const gchar *last_frame =frame->frame; + const Allocator *A = frame->allocator; + g_assert (A != NULL); + do { + const gchar *str = A->functions_srcloc[n + frame->depth + 1]; + if (str != last_frame) { + g_string_append_c (string, '\n'); + g_string_append_c (string, '\t'); + g_string_append (string, str); + if (str == main_fn) + break; + if (++n == 8) + break; + last_frame = str; + } + } while (TRUE); + } text = g_string_free (string, FALSE); gtk_tooltip_set_text (tooltip, text); diff --git a/src/callgraph.h b/src/callgraph.h index ad07700..7f43823 100644 --- a/src/callgraph.h +++ b/src/callgraph.h @@ -20,9 +20,14 @@ struct _simple_hash_table { struct _call_graph_frame { guint ip; + + const Allocator *allocator; + guint depth; + const gchar *frame; const gchar *function; gboolean is_alloc_fn; + guint64 bytes; guint allocs; guint frees; @@ -33,7 +38,7 @@ struct _call_graph_frame { guint stamp; guint flags; - guint index; + guint index, old_index; CallGraphFrame **filter, **old_filter; guint n_filter, old_n_filter; CallGraphFrame *filter_parent; @@ -50,7 +55,7 @@ struct _call_graph_store { guint max_size_alloc; guint stamp; - CallGraphFrame *root, *frames; + CallGraphFrame root, *frames; SimpleHashTable frames_by_ip; SimpleHashTable frames_by_fn; Chunk *frame_chunks; @@ -81,6 +86,8 @@ call_graph_store_update (CallGraphStore *store, App *app, Allocator *allocators, guint since); +void +call_graph_store_update_tree_model (CallGraphStore *store); void call_graph_store_sort (CallGraphStore *store); |