diff options
-rw-r--r-- | src/allocators-store.c | 91 | ||||
-rw-r--r-- | src/allocators.c | 2 | ||||
-rw-r--r-- | src/allocators.h | 10 | ||||
-rw-r--r-- | src/app.c | 9 | ||||
-rw-r--r-- | src/blockmap.c | 4 | ||||
-rw-r--r-- | src/callgraph-treemap.c | 2 | ||||
-rw-r--r-- | src/callgraph.c | 8 | ||||
-rw-r--r-- | src/frames.c | 4 | ||||
-rw-r--r-- | src/lwp.c | 4 | ||||
-rw-r--r-- | src/minibfd/binfile.c | 21 | ||||
-rw-r--r-- | src/odin.h | 2 | ||||
-rw-r--r-- | src/spacetime.c | 2 | ||||
-rw-r--r-- | src/summary-chart.c | 90 | ||||
-rw-r--r-- | src/summary-view.c | 22 | ||||
-rw-r--r-- | src/summary.c | 83 | ||||
-rw-r--r-- | src/timeline.c | 513 | ||||
-rw-r--r-- | src/utils.c | 10 |
17 files changed, 635 insertions, 242 deletions
diff --git a/src/allocators-store.c b/src/allocators-store.c index 41e30c1..3625484 100644 --- a/src/allocators-store.c +++ b/src/allocators-store.c @@ -34,7 +34,12 @@ struct _allocators_store { } ht; struct _sum_allocator *sums; + guint filter_serial; + GList *filters; + Chunk *sum_allocator_chunks; + + guint update; }; typedef struct _allocators_store_class { @@ -437,6 +442,8 @@ allocators_store_new_sum (AllocatorsStore *store, const gchar *fn) sum->sorted = FALSE; sum->blocks = NULL; sum->largest = NULL; + sum->filter_serial = 0; + sum->filtered = FALSE; sum->frame = fn; @@ -467,13 +474,36 @@ allocators_store_reset_sums (AllocatorsStore *store) } } +static void +_allocators_store_filter_sum (AllocatorsStore *store, + struct _sum_allocator *sum) +{ + GList *l; + + if (sum->filter_serial == store->filter_serial) + return; + + sum->filter_serial = store->filter_serial; + + if (store->filters == NULL) { + sum->filtered = FALSE; + return; + } + + for (l = store->filters; l != NULL; l = g_list_next (l)) + if (g_regex_match (l->data, sum->frame, 0, NULL)) + break; + + sum->filtered = l == NULL; +} + struct _GtkTreePath { /* XXX */ gint depth; gint *indices; }; -void -allocators_store_update (AllocatorsStore *store) +static gboolean +_allocators_store_update (AllocatorsStore *store) { GtkTreePath path; GtkTreeIter iter; @@ -515,7 +545,9 @@ allocators_store_update (AllocatorsStore *store) allocators = g_ptr_array_new (); path.depth = 1; for (sum = store->sums; sum != NULL; sum = sum->next) { - if (sum->count) { + _allocators_store_filter_sum (store, sum); + + if (sum->count && ! sum->filtered) { g_ptr_array_add (allocators, sum); } else { if (sum->old_index != (guint) -1 && sum->index != (guint) -1) { @@ -542,6 +574,52 @@ allocators_store_update (AllocatorsStore *store) gtk_tree_model_row_inserted ((GtkTreeModel *) store, &path, &iter); } } + + store->update = 0; + return FALSE; +} + +void +allocators_store_update (AllocatorsStore *store) +{ + if (store->update == 0) { + store->update = gdk_threads_add_idle_full ( + G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) _allocators_store_update, + g_object_ref (store), + (GDestroyNotify) g_object_unref); + } +} + +void +allocators_store_reset_filters (AllocatorsStore *store) +{ + if (++store->filter_serial == 0) + store->filter_serial = 1; + + g_list_foreach (store->filters, (GFunc) g_regex_unref, NULL); + g_list_free (store->filters); + store->filters = NULL; + + allocators_store_update (store); +} + +gboolean +allocators_store_add_filter (AllocatorsStore *store, + const gchar *pattern, + GError **error) +{ + GRegex *regex = g_regex_new (pattern, G_REGEX_OPTIMIZE, 0, error); + if (regex == NULL) + return FALSE; + + store->filters = g_list_prepend (store->filters, regex); + + if (++store->filter_serial == 0) + store->filter_serial = 1; + + allocators_store_update (store); + return TRUE; } void @@ -578,5 +656,10 @@ allocators_store_update_from_allocators (AllocatorsStore *store, } } - allocators_store_update (store); + if (store->update) { + g_source_remove (store->update); + store->update = 0; + } + + _allocators_store_update (store); } diff --git a/src/allocators.c b/src/allocators.c index c9ac354..28cec49 100644 --- a/src/allocators.c +++ b/src/allocators.c @@ -278,7 +278,7 @@ allocators_query_tooltip (GtkWidget *widget, main_fn = app_get_main_function (app_get ((GtkWidget *) self)); last = NULL; - for (m = n; m < MIN (n + 8, A->n_frames); m++) { + for (m = n; m < MIN (n + NUM_CALLERS, A->n_frames); m++) { if (A->frames[m]->function_srcloc != last) { g_string_append_c (string, '\n'); g_string_append_c (string, '\t'); diff --git a/src/allocators.h b/src/allocators.h index 839f438..ad9fe59 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -31,7 +31,9 @@ struct _sum_allocator { gulong min, max; Allocator *largest; Block **blocks; + guint filter_serial; gboolean sorted; + gboolean filtered; struct _sum_allocator *next, *ht_next; }; @@ -86,4 +88,12 @@ allocators_store_update_from_allocators (AllocatorsStore *store, Allocator *allocators, gboolean total); +gboolean +allocators_store_add_filter (AllocatorsStore *store, + const gchar *pattern, + GError **error); + +void +allocators_store_reset_filters (AllocatorsStore *store); + #endif /* ALLOCATORS_H */ @@ -1958,7 +1958,10 @@ lwp_events_server_cb (GIOChannel *source, int fd = g_io_channel_unix_get_fd (io); gzFile *file = gzdopen (dup (fd), "r"); - update |= lwp_read (file, app); + if (lwp_read (file, app)) { + update = TRUE; + while (lwp_read (file, app)) ; + } gzclose (file); gnet_unix_socket_delete (client); @@ -1967,9 +1970,7 @@ lwp_events_server_cb (GIOChannel *source, } while (client != NULL); if (update) { - if (app->client.timeout) - _reset_timeout (app->client.timeout, 2000); - else + if (app->client.timeout == NULL) app->client.timeout = _new_timeout (2000, (GSourceFunc) _update_client, app); } } diff --git a/src/blockmap.c b/src/blockmap.c index 8461a7c..1f6eec9 100644 --- a/src/blockmap.c +++ b/src/blockmap.c @@ -676,7 +676,7 @@ block_map_query_tooltip (GtkWidget *widget, break; } last = NULL; - for (m = n + 1; m < MIN (n + 9, A->n_frames); m++) { + for (m = n + 1; m < MIN (n + NUM_CALLERS + 1, A->n_frames); m++) { if (A->frames[m]->function_srcloc != last) { g_string_append_c (string, '\n'); g_string_append_c (string, '\t'); @@ -736,7 +736,7 @@ block_map_query_tooltip (GtkWidget *widget, break; } last = NULL; - for (m = n + 1; m < MIN (n + 9, A->n_frames); m++) { + for (m = n + 1; m < MIN (n + NUM_CALLERS + 1, A->n_frames); m++) { if (A->frames[m]->function_srcloc != last) { g_string_append_c (string, '\n'); g_string_append_c (string, '\t'); diff --git a/src/callgraph-treemap.c b/src/callgraph-treemap.c index a74f44b..13cba3b 100644 --- a/src/callgraph-treemap.c +++ b/src/callgraph-treemap.c @@ -616,7 +616,7 @@ call_graph_tree_map_draw_label (CallGraphTreeMap *self, ctx = gtk_widget_create_pango_context (&self->widget); merge = pango_context_get_font_description (ctx); desc = pango_font_description_new (); - pango_font_description_set_family (desc, "sans"); + pango_font_description_set_family_static (desc, "sans"); pango_font_description_set_size (desc, PANGO_SCALE * 8); pango_font_description_set_weight (desc, PANGO_WEIGHT_NORMAL); pango_font_description_merge (desc, merge, FALSE); diff --git a/src/callgraph.c b/src/callgraph.c index ac8eac5..97d1970 100644 --- a/src/callgraph.c +++ b/src/callgraph.c @@ -327,7 +327,7 @@ call_graph_query_tooltip (GtkWidget *widget, do { if (child->frame != child->parent->frame) { - if (++n == 8) + if (++n == NUM_CALLERS) break; if (frame->frame->function == main_fn) break; @@ -352,7 +352,7 @@ call_graph_query_tooltip (GtkWidget *widget, g_string_append (string, frame->frame->function_srcloc); if (frame->frame->function == main_fn) break; - if (++n == 8) + if (++n == NUM_CALLERS) break; } if (frame->allocator != NULL) @@ -363,7 +363,7 @@ call_graph_query_tooltip (GtkWidget *widget, } else string = g_string_new ("Called from:"); - if (frame->frame->function != main_fn && n < 8) { + if (frame->frame->function != main_fn && n < NUM_CALLERS) { const gchar *last_frame = frame->frame->function_srcloc; const Allocator *A = frame->allocator; guint m = frame->depth + 1; @@ -376,7 +376,7 @@ call_graph_query_tooltip (GtkWidget *widget, g_string_append (string, str); if (A->frames[m]->function == main_fn) break; - if (++n == 8) + if (++n == NUM_CALLERS) break; last_frame = str; } diff --git a/src/frames.c b/src/frames.c index dd281a1..2532617 100644 --- a/src/frames.c +++ b/src/frames.c @@ -242,7 +242,9 @@ _frames_init_alloc_fns (Frames *frames) "([sS]trn?|[mM]em)dup", "ft_.*alloc", "FT_New_Memory", /* FreeType */ "_hb_.*alloc", /* HarfBuzz */ - "X.*alloc" /* xorg */ + "X.*alloc", /* xorg */ + + "^alloc" }; guint n; @@ -836,6 +836,10 @@ _lwp_record_event (LWP_EventType type, const LWP_Event *ev) events[n_events].type = type; events[n_events].time = _lwp_read_time (); events[n_events].event = *ev; + if (n_events) { /* flush the pending events every 10 seconds */ + if (events[n_events].time - events[0].time > 10000) + force_send = TRUE; + } if (G_UNLIKELY (++n_events == G_N_ELEMENTS (events) || force_send)) { _write_failed = ! _lwp_write_events (events, n_events); n_events = 0; diff --git a/src/minibfd/binfile.c b/src/minibfd/binfile.c index f8e38a4..38cc569 100644 --- a/src/minibfd/binfile.c +++ b/src/minibfd/binfile.c @@ -64,7 +64,7 @@ read_inode (const char *filename) } static ElfParser * -separate_debug_file_exists (const char *name, guint32 crc) +separate_debug_file_exists (const char *name, guint32 crc, gboolean force) { ElfParser *parser; @@ -73,8 +73,12 @@ separate_debug_file_exists (const char *name, guint32 crc) return NULL; if (elf_parser_get_crc32 (parser) != crc) { - elf_parser_free (parser); - return NULL; + if (force) { + g_warning ("debug crc mismatch for '%s' (forced load)", name); + } else { + elf_parser_free (parser); + return NULL; + } } return parser; @@ -108,7 +112,16 @@ get_debug_file (ElfParser *elf, const char *filename, char **new_name) tries[3] = g_build_filename (debug_file_directory, dir, basename, NULL); for (i = 0; i < N_TRIES; ++i) { - result = separate_debug_file_exists (tries[i], crc32); + result = separate_debug_file_exists (tries[i], crc32, FALSE); + if (result) { + if (new_name) + *new_name = g_strdup (tries[i]); + break; + } + } + + for (i = 1; i < N_TRIES; ++i) { + result = separate_debug_file_exists (tries[i], crc32, TRUE); if (result) { if (new_name) *new_name = g_strdup (tries[i]); @@ -26,6 +26,8 @@ G_BEGIN_DECLS +#define NUM_CALLERS 12 + typedef struct _app App; typedef struct _client Client; typedef struct _allocator Allocator; diff --git a/src/spacetime.c b/src/spacetime.c index 94a9d0e..830a906 100644 --- a/src/spacetime.c +++ b/src/spacetime.c @@ -442,7 +442,7 @@ spacetime_expose (GtkWidget *widget, GdkEventExpose *ev) ctx = gtk_widget_create_pango_context (&self->widget); merge = pango_context_get_font_description (ctx); desc = pango_font_description_new (); - pango_font_description_set_family (desc, "sans"); + pango_font_description_set_family_static (desc, "sans"); pango_font_description_set_size (desc, PANGO_SCALE * 8); pango_font_description_set_weight (desc, PANGO_WEIGHT_NORMAL); pango_font_description_merge (desc, merge, FALSE); diff --git a/src/summary-chart.c b/src/summary-chart.c index 53751bc..e9a5dc1 100644 --- a/src/summary-chart.c +++ b/src/summary-chart.c @@ -33,7 +33,8 @@ typedef struct _summary_chart { GtkWidget widget; - cairo_surface_t *image; + cairo_pattern_t *image; + int image_width, image_height; AllocatorsStore *store; struct _sum_allocator others; @@ -140,6 +141,45 @@ summary_chart_realize (GtkWidget *widget) gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); } +static void +summary_chart_load_background (SummaryChart *self, cairo_surface_t *target) +{ + static cairo_surface_t *background; + + if (background == NULL) + background = cairo_image_surface_create_from_png (DATADIR G_DIR_SEPARATOR_S "odin.png"); + + if (cairo_surface_status (background) == CAIRO_STATUS_SUCCESS) { + cairo_surface_t *surface; + cairo_t *cr; + cairo_status_t status; + + self->image_width = cairo_image_surface_get_width (background); + self->image_height = cairo_image_surface_get_height (background); + surface = cairo_surface_create_similar (target, + cairo_surface_get_content (background), + self->image_width, self->image_height); + + cr = cairo_create (surface); + cairo_set_source_surface (cr, background, 0, 0); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + status = cairo_status (cr); + cairo_destroy (cr); + + if (status == CAIRO_STATUS_SUCCESS && + cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS) + { + self->image = cairo_pattern_create_for_surface (surface); + if (cairo_pattern_status (self->image)) { + cairo_pattern_destroy (self->image); + self->image = NULL; + } + } + cairo_surface_destroy (surface); + } +} + static gboolean summary_chart_expose (GtkWidget *widget, GdkEventExpose *ev) { @@ -148,9 +188,9 @@ summary_chart_expose (GtkWidget *widget, GdkEventExpose *ev) GPtrArray *allocators; guint64 total; guint min; - guint n, ww, hh; + guint n; gint r; - gdouble theta, theta_offset; + gdouble theta, theta_offset, ww, hh; AllocatorsStoreSort sort; PangoContext *ctx; @@ -161,7 +201,7 @@ summary_chart_expose (GtkWidget *widget, GdkEventExpose *ev) ctx = gtk_widget_create_pango_context (&self->widget); merge = pango_context_get_font_description (ctx); desc = pango_font_description_new (); - pango_font_description_set_family (desc, "sans"); + pango_font_description_set_family_static (desc, "sans"); pango_font_description_set_size (desc, PANGO_SCALE * 8); pango_font_description_set_weight (desc, PANGO_WEIGHT_NORMAL); pango_font_description_merge (desc, merge, FALSE); @@ -199,20 +239,26 @@ summary_chart_expose (GtkWidget *widget, GdkEventExpose *ev) } theta_offset = -G_PI / 2 + (2 * G_PI - theta) / 2.; - ww = widget->allocation.width / 2; - hh = widget->allocation.height / 2; + ww = .5 * widget->allocation.width; + hh = .5 * widget->allocation.height; /* background */ cairo_arc (cr, ww, hh, r, 0, 2 * G_PI); cairo_set_source_rgb (cr, .7, .7, .7); + if (self->image == NULL) + summary_chart_load_background (self, cairo_get_target (cr)); if (self->image != NULL) { + cairo_matrix_t matrix; + cairo_fill_preserve (cr); cairo_clip (cr); - cairo_set_source_surface (cr, self->image, - ww - cairo_image_surface_get_width (self->image) / 2, - hh - cairo_image_surface_get_width (self->image) / 2); + cairo_matrix_init_translate (&matrix, + .5 * self->image_width - ww, + .5 * self->image_height - hh); + cairo_pattern_set_matrix (self->image, &matrix); + cairo_set_source (cr, self->image); cairo_paint (cr); cairo_reset_clip (cr); } else @@ -280,7 +326,7 @@ summary_chart_expose (GtkWidget *widget, GdkEventExpose *ev) max_r = mid_r + logical.width / 2.; arc_height = dtheta * min_r - 4; if (min_r < r/4 + 2 || max_r > r - 2 || logical.height > arc_height) { - pango_layout_set_width (text, r*3./4 - 2*BORDER); + pango_layout_set_width (text, PANGO_SCALE * (r*3./4 - 2*BORDER)); pango_layout_set_ellipsize (text, PANGO_ELLIPSIZE_END); pango_layout_get_pixel_extents (text, NULL, &logical); min_r = mid_r - logical.width / 2.; @@ -303,7 +349,6 @@ summary_chart_expose (GtkWidget *widget, GdkEventExpose *ev) } cairo_set_source_rgb (cr, 0., 0., 0.); pango_cairo_show_layout (cr, text); - cairo_stroke (cr); cairo_restore (cr); } g_object_unref (text); @@ -324,6 +369,19 @@ summary_chart_expose (GtkWidget *widget, GdkEventExpose *ev) return FALSE; } +static void +summary_chart_unrealize (GtkWidget *widget) +{ + SummaryChart *self = (SummaryChart *) widget; + + if (self->image != NULL) { + cairo_pattern_destroy (self->image); + self->image = NULL; + } + + GTK_WIDGET_CLASS (summary_chart_parent_class)->unrealize (widget); +} + static struct _sum_allocator * _summary_chart_get_sum_for_cursor (SummaryChart *self, gint x, gint y) { @@ -461,7 +519,7 @@ summary_chart_query_tooltip (GtkWidget *widget, main_fn = app_get_main_function (app_get (widget)); last = NULL; - for (m = n; m < MIN (n + 8, A->n_frames); m++) { + for (m = n; m < MIN (n + NUM_CALLERS, A->n_frames); m++) { if (A->frames[m]->function_srcloc != last) { g_string_append_c (string, '\n'); g_string_append_c (string, '\t'); @@ -492,8 +550,6 @@ summary_chart_query_tooltip (GtkWidget *widget, return TRUE; } -static cairo_surface_t *background; - static void summary_chart_class_init (SummaryChartClass *klass) { @@ -504,6 +560,7 @@ summary_chart_class_init (SummaryChartClass *klass) object_class->get_property = summary_chart_get_property; widget_class->realize = summary_chart_realize; + widget_class->unrealize = summary_chart_unrealize; widget_class->expose_event = summary_chart_expose; widget_class->query_tooltip = summary_chart_query_tooltip; @@ -518,10 +575,6 @@ summary_chart_class_init (SummaryChartClass *klass) G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NAME | G_PARAM_READWRITE)); - - background = cairo_image_surface_create_from_png (DATADIR G_DIR_SEPARATOR_S "odin.png"); - if (cairo_surface_status (background)) - background = NULL; } @@ -529,7 +582,6 @@ static void summary_chart_init (SummaryChart *self) { self->others.frame = (gchar *) "Others"; - self->image = cairo_surface_reference (background); gtk_widget_set_has_tooltip (&self->widget, TRUE); } diff --git a/src/summary-view.c b/src/summary-view.c index 8c68120..5e962b8 100644 --- a/src/summary-view.c +++ b/src/summary-view.c @@ -165,7 +165,7 @@ summary_view_query_tooltip (GtkWidget *widget, main_fn = app_get_main_function (app_get (widget)); last = NULL; - for (m = n; m < MIN (n + 8, A->n_frames); m++) { + for (m = n; m < MIN (n + NUM_CALLERS, A->n_frames); m++) { if (A->frames[m]->function_srcloc != last) { g_string_append_c (string, '\n'); g_string_append_c (string, '\t'); @@ -220,6 +220,20 @@ summary_view_set_sort_by_count (SummaryView *self) ALLOCATORS_STORE_SORT_BY_COUNT); } +static gboolean +summary_view_search_equal_func (GtkTreeModel *model, + gint column, + const gchar *key, + GtkTreeIter *iter, + gpointer data) +{ + struct _sum_allocator *sum = iter->user_data; + if (sum->filtered) + return FALSE; + return strncmp (sum->frame, key, strlen (key)) != 0; +} + + static void summary_view_init (SummaryView *self) { @@ -262,6 +276,12 @@ summary_view_init (SummaryView *self) self); gtk_tree_view_append_column (&self->tv, column); + gtk_tree_view_set_search_column (&self->tv, ALLOCATORS_FRAME); + gtk_tree_view_set_enable_search (&self->tv, TRUE); + gtk_tree_view_set_search_equal_func (&self->tv, + summary_view_search_equal_func, + NULL, NULL); + gtk_widget_set_has_tooltip (GTK_WIDGET (self), TRUE); } diff --git a/src/summary.c b/src/summary.c index 4f19614..91a7a8b 100644 --- a/src/summary.c +++ b/src/summary.c @@ -101,9 +101,60 @@ summary_class_init (SummaryClass *klass) } static void +do_filter_changed (GtkEntry *entry, Summary *self) +{ + const char *filter; + + allocators_store_reset_filters (self->store); + + filter = gtk_entry_get_text (entry); + if (filter != NULL && *filter != '\0') + allocators_store_add_filter (self->store, filter, NULL); +} + +static void +do_save_allocators (GtkWidget *widget, Summary *self) +{ + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new ("Choose file", + GTK_WINDOW (gtk_widget_get_toplevel (widget)), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER(dialog), TRUE); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename; + FILE *file; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + file = fopen (filename, "w"); + g_free (filename); + + if (file != NULL) { + GPtrArray *allocators; + guint n; + + allocators = allocators_store_get_array (self->store); + for (n = 0; n < allocators->len; n++) { + struct _sum_allocator *sum = g_ptr_array_index (allocators, n); + fprintf (file, "%s\t%d\t%"G_GUINT64_FORMAT"\n", + sum->frame, sum->count, sum->size); + } + + fclose (file); + } + } + + gtk_widget_destroy (dialog); +} + +static void summary_init (Summary *self) { - GtkWidget *hbox, *w, *sw; + GtkWidget *hbox, *vbox, *lbl, *w, *sw; self->store = allocators_store_new (); self->total = TRUE; @@ -112,18 +163,42 @@ summary_init (Summary *self) gtk_container_add (GTK_CONTAINER (self), hbox); gtk_widget_show (hbox); + vbox = gtk_vbox_new (FALSE, 2); + gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 2); + gtk_widget_show (vbox); + + w = summary_chart_new (self->store); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 2); + gtk_widget_show (w); + sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_box_pack_start (GTK_BOX (hbox), sw, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 2); gtk_widget_show (sw); w = summary_view_new (self->store); gtk_container_add (GTK_CONTAINER (sw), w); gtk_widget_show (w); - w = summary_chart_new (self->store); - gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 2); + + hbox = gtk_hbox_new (FALSE, 2); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2); + gtk_widget_show (hbox); + + lbl = gtk_label_new_with_mnemonic ("_Filter"); + gtk_box_pack_start (GTK_BOX (hbox), lbl, FALSE, FALSE, 2); + gtk_widget_show (lbl); + + w = gtk_entry_new (); + g_signal_connect (w, "activate", G_CALLBACK (do_filter_changed), self); + gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 2); + gtk_widget_show (w); + gtk_label_set_mnemonic_widget (GTK_LABEL (lbl), w); + + w = gtk_button_new_from_stock (GTK_STOCK_SAVE); + g_signal_connect (w, "clicked", G_CALLBACK (do_save_allocators), self); + gtk_box_pack_end (GTK_BOX (hbox), w, FALSE, FALSE, 2); gtk_widget_show (w); } diff --git a/src/timeline.c b/src/timeline.c index 912be76..b6e6832 100644 --- a/src/timeline.c +++ b/src/timeline.c @@ -47,6 +47,10 @@ struct _timeline { guint64 max_bytes; guint64 max_active; gint max_blocks; + + cairo_pattern_t *red_yellow_green_pattern; + cairo_pattern_t *red_black_green_pattern; + cairo_pattern_t *transparent_white_pattern; }; static GType @@ -114,6 +118,112 @@ timeline_realize (GtkWidget *widget) } #define BORDER 4 + +static cairo_pattern_t * +_red_yellow_green_pattern (Timeline *self, cairo_t *cr) +{ + cairo_pattern_t *gradient; + cairo_surface_t *surface; + cairo_t *cr2; + + if (self->red_yellow_green_pattern != NULL) + return self->red_yellow_green_pattern; + + surface = cairo_surface_create_similar (cairo_get_target (cr), + CAIRO_CONTENT_COLOR, + 8, self->widget.allocation.height); + cr2 = cairo_create (surface); + + gradient = cairo_pattern_create_linear ( + 0, BORDER, + 0, self->widget.allocation.height - BORDER); + cairo_pattern_add_color_stop_rgb (gradient, 0., 0.76, 0.1, 0); + cairo_pattern_add_color_stop_rgb (gradient, .40, 0.76, 0.1, 0); + cairo_pattern_add_color_stop_rgb (gradient, .45, 0.76, 0.76, 0); + cairo_pattern_add_color_stop_rgb (gradient, .55, 0.76, 0.76, 0.); + cairo_pattern_add_color_stop_rgb (gradient, .60, 0, 0.76, 0.1); + cairo_pattern_add_color_stop_rgb (gradient, 1., 0, 0.76, 0.1); + cairo_set_source (cr2, gradient); + cairo_pattern_destroy (gradient); + cairo_paint (cr2); + cairo_destroy (cr2); + + self->red_yellow_green_pattern = cairo_pattern_create_for_surface (surface); + cairo_surface_destroy (surface); + cairo_pattern_set_extend (self->red_yellow_green_pattern, + CAIRO_EXTEND_REPEAT); + + return self->red_yellow_green_pattern; +} + +static cairo_pattern_t * +_transparent_white_pattern (Timeline *self, cairo_t *cr) +{ + cairo_pattern_t *gradient; + cairo_surface_t *surface; + cairo_t *cr2; + + if (self->transparent_white_pattern != NULL) + return self->transparent_white_pattern; + + surface = cairo_surface_create_similar (cairo_get_target (cr), + CAIRO_CONTENT_COLOR_ALPHA, + 8, self->widget.allocation.height); + cr2 = cairo_create (surface); + + gradient = cairo_pattern_create_linear ( + 0, 0, + 0, self->widget.allocation.height); + cairo_pattern_add_color_stop_rgba (gradient, 0.0, 1, 1, 1, 0); + cairo_pattern_add_color_stop_rgba (gradient, 0.5, 1, 1, 1, 1); + cairo_pattern_add_color_stop_rgba (gradient, 1.0, 1, 1, 1, 0); + cairo_set_source (cr2, gradient); + cairo_pattern_destroy (gradient); + cairo_paint (cr2); + cairo_destroy (cr2); + + self->transparent_white_pattern = cairo_pattern_create_for_surface (surface); + cairo_surface_destroy (surface); + cairo_pattern_set_extend (self->transparent_white_pattern, + CAIRO_EXTEND_REPEAT); + + return self->transparent_white_pattern; +} + +static cairo_pattern_t * +_red_black_green_pattern (Timeline *self, cairo_t *cr) +{ + cairo_pattern_t *gradient; + cairo_surface_t *surface; + cairo_t *cr2; + + if (self->red_black_green_pattern != NULL) + return self->red_black_green_pattern; + + surface = cairo_surface_create_similar (cairo_get_target (cr), + CAIRO_CONTENT_COLOR, + 8, self->widget.allocation.height); + cr2 = cairo_create (surface); + + gradient = cairo_pattern_create_linear ( + 0, 0, + 0, self->widget.allocation.height); + cairo_pattern_add_color_stop_rgb (gradient, 0.0, 1, 0, 0); + cairo_pattern_add_color_stop_rgb (gradient, 0.5, 0, 0, 0); + cairo_pattern_add_color_stop_rgb (gradient, 1.0, 0, 1, 0); + cairo_set_source (cr2, gradient); + cairo_pattern_destroy (gradient); + cairo_paint (cr2); + cairo_destroy (cr2); + + self->red_black_green_pattern = cairo_pattern_create_for_surface (surface); + cairo_surface_destroy (surface); + cairo_pattern_set_extend (self->red_black_green_pattern, + CAIRO_EXTEND_REPEAT); + + return self->red_black_green_pattern; +} + static gboolean timeline_expose (GtkWidget *widget, GdkEventExpose *ev) { @@ -121,7 +231,6 @@ timeline_expose (GtkWidget *widget, GdkEventExpose *ev) TimelineData *data, *first; cairo_t *cr; double dashes[1] = {4.}; - cairo_pattern_t *pattern; guint last_time; guint64 last_value; gdouble last_x; @@ -134,205 +243,173 @@ timeline_expose (GtkWidget *widget, GdkEventExpose *ev) if (self->data != NULL) first = self->data->next; if (first != NULL) { - cairo_save (cr); - cairo_translate (cr, BORDER, widget->allocation.height - BORDER); - cairo_scale (cr, - (widget->allocation.width - 2*BORDER) / (gdouble) self->max_time, - -(widget->allocation.height - 2*BORDER) / (gdouble) (2 * self->max_bytes)); - cairo_translate (cr, 0, self->max_bytes); - - /* bytes allocated */ - cairo_move_to (cr, 0, 0); - cairo_line_to (cr, 0, first->bytes_allocated); - last_time = last_value = 0; - for (data = first; data != NULL; data = data->next) { - guint64 bytes = data->prev->bytes_allocated; - guint64 value = data->bytes_allocated - bytes; - cairo_curve_to (cr, - .5 * (data->time - last_time) + last_time, last_value, - .5 * (data->time - last_time) + last_time, value, - data->time, value); - last_time = data->time; - last_value = value; - } - cairo_line_to (cr, self->max_time, 0); - cairo_close_path (cr); - - cairo_save (cr); - cairo_identity_matrix (cr); - pattern = cairo_pattern_create_linear ( - 0, .5 * self->widget.allocation.height, - 0, 0); - cairo_pattern_add_color_stop_rgba (pattern, 0, 0, 0, 0, 1); - cairo_pattern_add_color_stop_rgba (pattern, 1, 1, 0, 0, 1); - cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); - cairo_fill (cr); - cairo_restore (cr); - - cairo_scale (cr, 1, -1); - - /* bytes freed */ - cairo_move_to (cr, 0, 0); - cairo_line_to (cr, 0, first->bytes_freed); - last_time = last_value = 0; - for (data = first; data != NULL; data = data->next) { - guint64 bytes = data->prev->bytes_freed; - guint64 value = data->bytes_freed - bytes; - cairo_curve_to (cr, - .5 * (data->time - last_time) + last_time, last_value, - .5 * (data->time - last_time) + last_time, value, - data->time, value); - last_time = data->time; - last_value = value; - } - cairo_line_to (cr, self->max_time, 0); - cairo_close_path (cr); - - cairo_save (cr); - cairo_identity_matrix (cr); - pattern = cairo_pattern_create_linear ( - 0, .5 * widget->allocation.height, - 0, widget->allocation.height); - cairo_pattern_add_color_stop_rgb (pattern, 0., 0, 0, 0); - cairo_pattern_add_color_stop_rgb (pattern, 1., 0, 1, 0); - cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); - cairo_fill (cr); - cairo_restore (cr); - - - /* bytes delta */ - cairo_move_to (cr, 0, 0); - cairo_line_to (cr, 0, first->bytes_allocated - first->bytes_freed); - last_time = last_value = 0; - for (data = first; data != NULL; data = data->next) { - gint bytes = data->prev->bytes_allocated - data->prev->bytes_freed; - gint value = data->bytes_allocated - data->bytes_freed - bytes; - if (value < 0) - value = 0; - cairo_curve_to (cr, - .5 * (data->time - last_time) + last_time, last_value, - .5 * (data->time - last_time) + last_time, value, - data->time, value); - last_time = data->time; - last_value = value; - } - cairo_line_to (cr, last_time, 0); - - cairo_scale (cr, 1, -1); - - cairo_move_to (cr, 0, 0); - cairo_line_to (cr, 0, first->bytes_allocated - first->bytes_freed); - last_time = last_value = 0; - for (data = first; data != NULL; data = data->next) { - gint bytes = data->prev->bytes_allocated - data->prev->bytes_freed; - gint value = data->bytes_allocated - data->bytes_freed - bytes; - if (value < 0) - value = 0; - cairo_curve_to (cr, - .5 * (data->time - last_time) + last_time, last_value, - .5 * (data->time - last_time) + last_time, value, - data->time, value); - last_time = data->time; - last_value = value; - } - cairo_line_to (cr, last_time, 0); - cairo_close_path (cr); - - cairo_save (cr); - cairo_identity_matrix (cr); - //cairo_set_source_rgb (cr, 1, 1, 1); - pattern = cairo_pattern_create_linear ( - 0, 0, - 0, self->widget.allocation.height); - cairo_pattern_add_color_stop_rgba (pattern, 0, 1, 1, 1, 0); - cairo_pattern_add_color_stop_rgba (pattern, 0.5, 1, 1, 1, 1); - cairo_pattern_add_color_stop_rgba (pattern, 1, 1, 1, 1, 0); - cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); - cairo_fill (cr); - cairo_restore (cr); - - cairo_restore (cr); - - - pattern = cairo_pattern_create_linear ( - 0, BORDER, - 0, self->widget.allocation.height - BORDER); - cairo_pattern_add_color_stop_rgb (pattern, 0., 0.76, 0.1, 0); - cairo_pattern_add_color_stop_rgb (pattern, .40, 0.76, 0.1, 0); - cairo_pattern_add_color_stop_rgb (pattern, .45, 0.76, 0.76, 0); - cairo_pattern_add_color_stop_rgb (pattern, .55, 0.76, 0.76, 0.); - cairo_pattern_add_color_stop_rgb (pattern, .60, 0, 0.76, 0.1); - cairo_pattern_add_color_stop_rgb (pattern, 1., 0, 0.76, 0.1); - cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); + cairo_save (cr); { + cairo_translate (cr, BORDER, widget->allocation.height - BORDER); + cairo_scale (cr, + (widget->allocation.width - 2*BORDER) / (gdouble) self->max_time, + -(widget->allocation.height - 2*BORDER) / (gdouble) (2 * self->max_bytes)); + cairo_translate (cr, 0, self->max_bytes); + + + /* bytes allocated */ + cairo_move_to (cr, 0, 0); + cairo_line_to (cr, 0, first->bytes_allocated); + last_time = last_value = 0; + for (data = first; data != NULL; data = data->next) { + guint64 bytes = data->prev->bytes_allocated; + guint64 value = data->bytes_allocated - bytes; + cairo_curve_to (cr, + .5 * (data->time - last_time) + last_time, last_value, + .5 * (data->time - last_time) + last_time, value, + data->time, value); + last_time = data->time; + last_value = value; + } + cairo_line_to (cr, self->max_time, 0); + cairo_close_path (cr); + cairo_save (cr); { + cairo_identity_matrix (cr); + cairo_set_source (cr, _red_black_green_pattern (self, cr)); + cairo_fill (cr); + } cairo_restore (cr); + + cairo_scale (cr, 1, -1); + + /* bytes freed */ + cairo_move_to (cr, 0, 0); + cairo_line_to (cr, 0, first->bytes_freed); + last_time = last_value = 0; + for (data = first; data != NULL; data = data->next) { + guint64 bytes = data->prev->bytes_freed; + guint64 value = data->bytes_freed - bytes; + cairo_curve_to (cr, + .5 * (data->time - last_time) + last_time, last_value, + .5 * (data->time - last_time) + last_time, value, + data->time, value); + last_time = data->time; + last_value = value; + } + cairo_line_to (cr, self->max_time, 0); + cairo_close_path (cr); + cairo_save (cr); { + cairo_identity_matrix (cr); + cairo_set_source (cr, _red_black_green_pattern (self, cr)); + cairo_fill (cr); + } cairo_restore (cr); + + /* bytes delta */ + cairo_move_to (cr, 0, 0); + cairo_line_to (cr, 0, first->bytes_allocated - first->bytes_freed); + last_time = last_value = 0; + for (data = first; data != NULL; data = data->next) { + gint bytes = data->prev->bytes_allocated - data->prev->bytes_freed; + gint value = data->bytes_allocated - data->bytes_freed - bytes; + if (value < 0) + value = 0; + cairo_curve_to (cr, + .5 * (data->time - last_time) + last_time, last_value, + .5 * (data->time - last_time) + last_time, value, + data->time, value); + last_time = data->time; + last_value = value; + } + cairo_line_to (cr, last_time, 0); + + cairo_scale (cr, 1, -1); + + cairo_move_to (cr, 0, 0); + cairo_line_to (cr, 0, first->bytes_allocated - first->bytes_freed); + last_time = last_value = 0; + for (data = first; data != NULL; data = data->next) { + gint bytes = data->prev->bytes_allocated - data->prev->bytes_freed; + gint value = data->bytes_allocated - data->bytes_freed - bytes; + if (value < 0) + value = 0; + cairo_curve_to (cr, + .5 * (data->time - last_time) + last_time, last_value, + .5 * (data->time - last_time) + last_time, value, + data->time, value); + last_time = data->time; + last_value = value; + } + cairo_line_to (cr, last_time, 0); + cairo_close_path (cr); + + cairo_save (cr); { + cairo_identity_matrix (cr); + cairo_set_source (cr, _transparent_white_pattern (self, cr)); + cairo_fill (cr); + } cairo_restore (cr); + + } cairo_restore (cr); + + + cairo_set_source (cr, _red_yellow_green_pattern (self, cr)); cairo_set_line_width (cr, 2.0); - cairo_save (cr); - cairo_translate (cr, BORDER, widget->allocation.height - BORDER); - cairo_scale (cr, - (widget->allocation.width - 2*BORDER) / (gdouble) self->max_time, - -(widget->allocation.height - 2*BORDER) / (gdouble) (2 * self->max_blocks)); - cairo_translate (cr, 0, self->max_blocks); - - - /* allocation bias */ - cairo_move_to (cr, 0, first->prev->alloc_bias); - for (data = first; data != NULL; data = data->next) { - gint last_bias = data->prev->alloc_bias; - cairo_line_to (cr, data->time, data->alloc_bias - last_bias); - } - cairo_save (cr); - cairo_identity_matrix (cr); - cairo_stroke (cr); - cairo_restore (cr); - cairo_restore (cr); + cairo_save (cr); { + cairo_translate (cr, BORDER, widget->allocation.height - BORDER); + cairo_scale (cr, + (widget->allocation.width - 2*BORDER) / (gdouble) self->max_time, + -(widget->allocation.height - 2*BORDER) / (gdouble) (2 * self->max_blocks)); + cairo_translate (cr, 0, self->max_blocks); + + + /* allocation bias */ + cairo_move_to (cr, 0, first->prev->alloc_bias); + for (data = first; data != NULL; data = data->next) { + gint last_bias = data->prev->alloc_bias; + cairo_line_to (cr, data->time, data->alloc_bias - last_bias); + } + cairo_save (cr); { + cairo_identity_matrix (cr); + cairo_stroke (cr); + } cairo_restore (cr); + } cairo_restore (cr); /* active bytes */ - cairo_save (cr); - cairo_translate (cr, BORDER, widget->allocation.height - BORDER); - cairo_scale (cr, - (widget->allocation.width - 2*BORDER) / (gdouble) self->max_time, - -(widget->allocation.height - 2*BORDER) / (gdouble) (2 * self->max_active)); - cairo_translate (cr, 0, self->max_active); - - cairo_set_source_rgb (cr, 1, 1, 1); - /* struts */ - last_x = 0; - for (data = first; data->next != NULL; data = data->next) { - gdouble x; - - x = floor ((widget->allocation.width - 2*BORDER) / (gdouble) self->max_time * data->time) - .5; - /* maintain a minimum of 10 pixels between struts */ - if (x - last_x < 10.) - continue; - - last_x = x; - x *= self->max_time / (gdouble) (widget->allocation.width - 2*BORDER); - - cairo_move_to (cr, x, 0); - cairo_line_to (cr, x, data->bytes_allocated - data->bytes_freed); - - cairo_save (cr); + cairo_save (cr); { + cairo_translate (cr, BORDER, widget->allocation.height - BORDER); + cairo_scale (cr, + (widget->allocation.width - 2*BORDER) / (gdouble) self->max_time, + -(widget->allocation.height - 2*BORDER) / (gdouble) (2 * self->max_active)); + cairo_translate (cr, 0, self->max_active); + + cairo_set_source_rgb (cr, 1, 1, 1); + /* struts */ + last_x = 0; + for (data = first; data->next != NULL; data = data->next) { + gdouble x; + + x = floor ((widget->allocation.width - 2*BORDER) / (gdouble) self->max_time * data->time) - .5; + /* maintain a minimum of 10 pixels between struts */ + if (x - last_x < 10.) + continue; + + last_x = x; + x *= self->max_time / (gdouble) (widget->allocation.width - 2*BORDER); + + cairo_move_to (cr, x, 0); + cairo_line_to (cr, x, data->bytes_allocated - data->bytes_freed); + + cairo_save (cr); { + cairo_identity_matrix (cr); + cairo_set_dash (cr, dashes, 1, 0.); + cairo_set_line_width (cr, 1.); + cairo_stroke (cr); + } cairo_restore (cr); + } + + /* bytes allocated */ + cairo_move_to (cr, 0, 0); + for (data = first; data != NULL; data = data->next) { + cairo_line_to (cr, + data->time, + data->bytes_allocated - data->bytes_freed); + } cairo_identity_matrix (cr); - cairo_set_dash (cr, dashes, 1, 0.); - cairo_set_line_width (cr, 1.); cairo_stroke (cr); - cairo_restore (cr); - } - - /* bytes allocated */ - cairo_move_to (cr, 0, 0); - for (data = first; data != NULL; data = data->next) { - cairo_line_to (cr, - data->time, - data->bytes_allocated - data->bytes_freed); - } - cairo_identity_matrix (cr); - cairo_stroke (cr); - cairo_restore (cr); + } cairo_restore (cr); /* draw a fine-line to show zero */ cairo_move_to (cr, @@ -358,6 +435,52 @@ timeline_expose (GtkWidget *widget, GdkEventExpose *ev) } static void +timeline_unrealize (GtkWidget *widget) +{ + Timeline *self = (Timeline *) widget; + + if (self->red_yellow_green_pattern != NULL) { + cairo_pattern_destroy (self->red_yellow_green_pattern); + self->red_yellow_green_pattern = NULL; + } + + if (self->transparent_white_pattern != NULL) { + cairo_pattern_destroy (self->transparent_white_pattern); + self->transparent_white_pattern = NULL; + } + + if (self->red_black_green_pattern != NULL) { + cairo_pattern_destroy (self->red_black_green_pattern); + self->red_black_green_pattern = NULL; + } + + GTK_WIDGET_CLASS (timeline_parent_class)->unrealize (widget); +} + +static void +timeline_size_allocate (GtkWidget *widget, GdkRectangle *allocation) +{ + Timeline *self = (Timeline *) widget; + + if (self->red_yellow_green_pattern != NULL) { + cairo_pattern_destroy (self->red_yellow_green_pattern); + self->red_yellow_green_pattern = NULL; + } + + if (self->transparent_white_pattern != NULL) { + cairo_pattern_destroy (self->transparent_white_pattern); + self->transparent_white_pattern = NULL; + } + + if (self->red_black_green_pattern != NULL) { + cairo_pattern_destroy (self->red_black_green_pattern); + self->red_black_green_pattern = NULL; + } + + GTK_WIDGET_CLASS (timeline_parent_class)->size_allocate (widget, allocation); +} + +static void timeline_class_init (TimelineClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; @@ -367,7 +490,9 @@ timeline_class_init (TimelineClass *klass) object_class->get_property = timeline_get_property; widget_class->realize = timeline_realize; + widget_class->unrealize = timeline_unrealize; widget_class->expose_event = timeline_expose; + widget_class->size_allocate = timeline_size_allocate; } static void diff --git a/src/utils.c b/src/utils.c index ac641f3..3656864 100644 --- a/src/utils.c +++ b/src/utils.c @@ -59,7 +59,10 @@ cell_layout_pretty_print_uint (GtkCellLayout *layout, len = g_snprintf (buf + 40, 40, "%u", v); pretty_print_number (buf + 40, len, buf); - g_object_set (G_OBJECT (cell), "text", buf, NULL); + g_object_set (G_OBJECT (cell), + "text", buf, + "xalign", (gfloat) 1.0, + NULL); } void @@ -78,7 +81,10 @@ cell_layout_pretty_print_uint64 (GtkCellLayout *layout, len = g_snprintf (buf + 80, 80, "%" G_GUINT64_FORMAT, v); pretty_print_number (buf + 80, len, buf); - g_object_set (G_OBJECT (cell), "text", buf, NULL); + g_object_set (G_OBJECT (cell), + "text", buf, + "xalign", (gfloat) 1.0, + NULL); } void |