diff --git a/src/libsysprof-ui/sysprof-memprof-page.c b/src/libsysprof-ui/sysprof-memprof-page.c index 4f61cb77..1d5c72eb 100644 --- a/src/libsysprof-ui/sysprof-memprof-page.c +++ b/src/libsysprof-ui/sysprof-memprof-page.c @@ -62,6 +62,11 @@ typedef struct GtkRadioButton *summary; GtkRadioButton *all_allocs; GtkRadioButton *temp_allocs; + GtkLabel *temp_allocs_count; + GtkLabel *num_allocs; + GtkLabel *leaked_allocs; + GtkLabel *peak_allocs; + GtkListBox *by_size; GCancellable *cancellable; @@ -160,6 +165,117 @@ build_functions_store (StackNode *node, } +static void +update_summary (SysprofMemprofPage *self, + SysprofMemprofProfile *profile) +{ + SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); + SysprofMemprofStats stats; + g_autoptr(GString) str = NULL; + + g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); + g_assert (SYSPROF_IS_MEMPROF_PROFILE (profile)); + + sysprof_memprof_profile_get_stats (profile, &stats); + + str = g_string_new (NULL); + + g_string_append_printf (str, "%"G_GINT64_FORMAT, stats.n_allocs); + gtk_label_set_label (priv->num_allocs, str->str); + g_string_truncate (str, 0); + + g_string_append_printf (str, "%"G_GINT64_FORMAT, stats.leaked_allocs); + gtk_label_set_label (priv->leaked_allocs, str->str); + g_string_truncate (str, 0); + + g_string_append_printf (str, "%"G_GINT64_FORMAT, stats.temp_allocs); + gtk_label_set_label (priv->temp_allocs_count, str->str); + g_string_truncate (str, 0); + + gtk_container_foreach (GTK_CONTAINER (priv->by_size), + (GtkCallback)gtk_widget_destroy, + NULL); + + for (guint i = 0; i < G_N_ELEMENTS (stats.by_size); i++) + { + g_autofree gchar *prevstr = NULL; + g_autofree gchar *sizestr = NULL; + g_autofree gchar *title_str = NULL; + g_autofree gchar *subtitle_str = NULL; + g_autofree gchar *allocstr = NULL; + g_autofree gchar *tempstr = NULL; + g_autofree gchar *leakedstr = NULL; + g_autofree gchar *allstr = NULL; + GtkWidget *row; + GtkWidget *title; + GtkWidget *subtitle; + GtkWidget *prog; + GtkWidget *box; + + if (stats.by_size[i].n_allocs == 0) + continue; + + row = gtk_list_box_row_new (); + title = gtk_label_new (NULL); + subtitle = gtk_label_new (NULL); + prog = gtk_level_bar_new_for_interval (0, stats.n_allocs); + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3); + + sizestr = g_format_size_full (stats.by_size[i].bucket, G_FORMAT_SIZE_IEC_UNITS); + if (i == 0) + { + title_str = g_strdup_printf ("≤ %s", sizestr); + } + else + { + /* translators: %s is replaced with a memory size such as "32 bytes" */ + prevstr = g_format_size_full (stats.by_size[i-1].bucket, G_FORMAT_SIZE_IEC_UNITS); + /* translators: %s is replaced with the the lower and upper bound memory sizes in bytes */ + title_str = g_strdup_printf (_("> %s to %s"), prevstr, sizestr); + } + + gtk_label_set_label (GTK_LABEL (title), title_str); + gtk_label_set_xalign (GTK_LABEL (title), 0); + dzl_gtk_widget_add_style_class (title, "dim-label"); + + gtk_widget_set_margin_start (box, 6); + gtk_widget_set_margin_end (box, 6); + + gtk_widget_set_margin_top (prog, 1); + gtk_widget_set_margin_bottom (prog, 1); + + allocstr = g_strdup_printf ("%"G_GINT64_FORMAT, stats.by_size[i].n_allocs); + tempstr = g_strdup_printf ("%"G_GINT64_FORMAT, stats.by_size[i].temp_allocs); + allstr = g_format_size_full (stats.by_size[i].allocated, + G_FORMAT_SIZE_IEC_UNITS); + subtitle_str = g_strdup_printf ("%s allocations, %s temporary, %s", + allocstr, tempstr, allstr); + + gtk_label_set_label (GTK_LABEL (subtitle), subtitle_str); + gtk_label_set_xalign (GTK_LABEL (subtitle), 0); + +#if 0 + /* TODO: Make this chunked by [temp][rest]... */ + gtk_level_bar_add_offset_value (GTK_LEVEL_BAR (prog), + GTK_LEVEL_BAR_OFFSET_HIGH, + stats.by_size[i].temp_allocs); + gtk_level_bar_add_offset_value (GTK_LEVEL_BAR (prog), + GTK_LEVEL_BAR_OFFSET_LOW, + stats.by_size[i].n_allocs); +#endif + gtk_level_bar_set_value (GTK_LEVEL_BAR (prog), + stats.by_size[i].n_allocs); + + gtk_container_add (GTK_CONTAINER (row), box); + gtk_container_add (GTK_CONTAINER (box), title); + gtk_container_add (GTK_CONTAINER (box), prog); + gtk_container_add (GTK_CONTAINER (box), subtitle); + gtk_container_add (GTK_CONTAINER (priv->by_size), row); + + gtk_widget_show_all (row); + } +} + static void sysprof_memprof_page_load (SysprofMemprofPage *self, SysprofMemprofProfile *profile) @@ -189,8 +305,13 @@ sysprof_memprof_page_load (SysprofMemprofPage *self, if (!g_set_object (&priv->profile, profile)) return; + update_summary (self, profile); + if (sysprof_memprof_profile_is_empty (profile)) - return; + { + gtk_stack_set_visible_child_name (priv->stack, "summary"); + return; + } stash = sysprof_memprof_profile_get_stash (profile); @@ -802,7 +923,6 @@ sysprof_memprof_page_generate_cb (GObject *object, { SysprofProfile *profile = (SysprofProfile *)object; SysprofMemprofPage *self; - SysprofMemprofPagePrivate *priv; g_autoptr(GTask) task = user_data; g_autoptr(GError) error = NULL; @@ -811,15 +931,11 @@ sysprof_memprof_page_generate_cb (GObject *object, g_assert (G_IS_TASK (task)); self = g_task_get_source_object (task); - priv = sysprof_memprof_page_get_instance_private (self); if (!sysprof_profile_generate_finish (profile, result, &error)) g_task_return_error (task, g_error_copy (error)); else sysprof_memprof_page_set_profile (self, SYSPROF_MEMPROF_PROFILE (profile)); - - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - gtk_stack_set_visible_child_name (priv->stack, "callgraph"); } static void @@ -876,19 +992,6 @@ sysprof_memprof_page_load_finish (SysprofPage *page, return g_task_propagate_boolean (G_TASK (result), error); } -static void -do_summary (SysprofMemprofPage *self) -{ -#if 0 - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - - g_cancellable_cancel (priv->cancellable); - gtk_stack_set_visible_child_name (priv->stack, "summary"); -#endif -} - static void do_allocs (SysprofMemprofPage *self, SysprofMemprofMode mode) @@ -914,7 +1017,7 @@ mode_notify_active (SysprofMemprofPage *self, if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) { if (button == priv->summary) - do_summary (self); + do_allocs (self, SYSPROF_MEMPROF_MODE_SUMMARY); else if (button == priv->all_allocs) do_allocs (self, SYSPROF_MEMPROF_MODE_ALL_ALLOCS); else if (button == priv->temp_allocs) @@ -922,6 +1025,19 @@ mode_notify_active (SysprofMemprofPage *self, } } +static void +sep_header_func (GtkListBoxRow *row, + GtkListBoxRow *before, + gpointer user_data) +{ + if (before != NULL) + gtk_list_box_row_set_header (row, + g_object_new (GTK_TYPE_SEPARATOR, + "orientation", GTK_ORIENTATION_HORIZONTAL, + "visible", TRUE, + NULL)); +} + static void sysprof_memprof_page_finalize (GObject *object) { @@ -1010,6 +1126,7 @@ sysprof_memprof_page_class_init (SysprofMemprofPageClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-memprof-page.ui"); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, by_size); gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, callers_view); gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, function_size_cell); gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, function_size_column); @@ -1020,6 +1137,10 @@ sysprof_memprof_page_class_init (SysprofMemprofPageClass *klass) gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, all_allocs); gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, temp_allocs); gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, summary); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, temp_allocs_count); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, num_allocs); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, leaked_allocs); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, peak_allocs); bindings = gtk_binding_set_by_class (klass); gtk_binding_entry_add_signal (bindings, GDK_KEY_Left, GDK_MOD1_MASK, "go-previous", 0); @@ -1042,6 +1163,8 @@ sysprof_memprof_page_init (SysprofMemprofPage *self) gtk_stack_set_visible_child_name (priv->stack, "empty-state"); + gtk_list_box_set_header_func (priv->by_size, sep_header_func, NULL, NULL); + g_signal_connect_object (priv->all_allocs, "notify::active", G_CALLBACK (mode_notify_active), diff --git a/src/libsysprof-ui/sysprof-memprof-page.ui b/src/libsysprof-ui/sysprof-memprof-page.ui index b602a927..c123ab3c 100644 --- a/src/libsysprof-ui/sysprof-memprof-page.ui +++ b/src/libsysprof-ui/sysprof-memprof-page.ui @@ -22,7 +22,7 @@ Summary false - false + true false @@ -57,6 +57,160 @@ true + + + never + true + + + 16 + vertical + true + + + true + 6 + 18 + true + + + Peak Allocation + end + false + + + + left + 0 + + + + + 0 + true + false + + + center + 0 + + + + + Number of Allocations + end + true + + + + left + 1 + + + + + 0 + true + true + + + center + 1 + + + + + Leaked Allocations + end + false + + + + left + 2 + + + + + 0 + true + false + + + center + 2 + + + + + Temporary Allocations + end + true + + + + left + 3 + + + + + 0 + true + 50 + true + + + center + 3 + + + + + Allocations by Size + end + start + true + + + + left + 4 + + + + + true + + + true + + + + + center + 4 + + + + + + + + + summary + + horizontal @@ -265,8 +419,8 @@ content-loading-symbolic - Generating Callgraph - Sysprof is busy creating the selected callgraph. + Analyzing Memory Allocations + Sysprof is busy analyzing memory allocations. true diff --git a/src/libsysprof/sysprof-memprof-profile.c b/src/libsysprof/sysprof-memprof-profile.c index cbe87fd2..e338906a 100644 --- a/src/libsysprof/sysprof-memprof-profile.c +++ b/src/libsysprof/sysprof-memprof-profile.c @@ -56,6 +56,7 @@ typedef struct rax *rax; GArray *resolved; SysprofMemprofMode mode; + SysprofMemprofStats stats; } Generate; struct _SysprofMemprofProfile @@ -397,6 +398,131 @@ compare_alloc (gconstpointer a, return 0; } +static guint +get_bucket (gint64 size) +{ + if (size <= 32) + return 0; + if (size <= 64) + return 1; + if (size <= 128) + return 2; + if (size <= 256) + return 3; + if (size <= 512) + return 4; + if (size <= 1024) + return 5; + if (size <= 4096) + return 6; + if (size <= 4096*4) + return 7; + if (size <= 4096*8) + return 8; + if (size <= 4096*16) + return 9; + if (size <= 4096*32) + return 10; + if (size <= 4096*64) + return 11; + if (size <= 4096*256) + return 12; + return 13; +} + +static void +summary_worker (Generate *g) +{ + g_autoptr(GArray) allocs = NULL; + SysprofCaptureFrameType type; + SysprofCaptureAddress last_addr = 0; + guint last_bucket = 0; + + g_assert (g != NULL); + g_assert (g->reader != NULL); + + allocs = g_array_new (FALSE, FALSE, sizeof (Alloc)); + + sysprof_capture_reader_reset (g->reader); + + g->stats.by_size[0].bucket = 32; + g->stats.by_size[1].bucket = 64; + g->stats.by_size[2].bucket = 128; + g->stats.by_size[3].bucket = 256; + g->stats.by_size[4].bucket = 512; + g->stats.by_size[5].bucket = 1024; + g->stats.by_size[6].bucket = 4096; + g->stats.by_size[7].bucket = 4096*4; + g->stats.by_size[8].bucket = 4096*8; + g->stats.by_size[9].bucket = 4096*16; + g->stats.by_size[10].bucket = 4096*32; + g->stats.by_size[11].bucket = 4096*64; + g->stats.by_size[12].bucket = 4096*256; + g->stats.by_size[13].bucket = 4096*256; + + while (sysprof_capture_reader_peek_type (g->reader, &type)) + { + if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION) + { + const SysprofCaptureAllocation *ev; + Alloc a; + + if (!(ev = sysprof_capture_reader_read_allocation (g->reader))) + break; + + a.pid = ev->frame.pid; + a.tid = ev->tid; + a.time = ev->frame.time; + a.addr = ev->alloc_addr; + a.size = ev->alloc_size; + a.frame_num = 0; + + g_array_append_val (allocs, a); + + if (a.size > 0) + g->stats.n_allocs++; + } + else + { + if (!sysprof_capture_reader_skip (g->reader)) + break; + } + } + + g_array_sort (allocs, compare_alloc); + + for (guint i = 0; i < allocs->len; i++) + { + const Alloc *a = &g_array_index (allocs, Alloc, i); + + if (a->size <= 0) + { + if (last_addr == a->addr) + { + g->stats.temp_allocs++; + g->stats.by_size[last_bucket].temp_allocs++; + } + + g->stats.leaked_allocs--; + + last_addr = 0; + last_bucket = 0; + } + else + { + guint b = get_bucket (a->size); + + g->stats.n_allocs++; + g->stats.leaked_allocs++; + g->stats.by_size[b].n_allocs++; + g->stats.by_size[b].allocated += a->size; + + last_addr = a->addr; + last_bucket = b; + } + } +} + static void temp_allocs_worker (Generate *g) { @@ -639,6 +765,10 @@ sysprof_memprof_profile_generate_worker (GTask *task, { temp_allocs_worker (g); } + else if (g->mode == SYSPROF_MEMPROF_MODE_SUMMARY) + { + summary_worker (g); + } /* Release some data we don't need anymore */ g_clear_pointer (&g->resolved, g_array_unref); @@ -781,3 +911,16 @@ sysprof_memprof_profile_new_with_selection (SysprofSelection *selection) "selection", selection, NULL); } + +void +sysprof_memprof_profile_get_stats (SysprofMemprofProfile *self, + SysprofMemprofStats *stats) +{ + g_return_if_fail (SYSPROF_IS_MEMPROF_PROFILE (self)); + g_return_if_fail (stats != NULL); + + if (self->g != NULL) + *stats = self->g->stats; + else + memset (stats, 0, sizeof *stats); +} diff --git a/src/libsysprof/sysprof-memprof-profile.h b/src/libsysprof/sysprof-memprof-profile.h index c1f45c26..1a5badd3 100644 --- a/src/libsysprof/sysprof-memprof-profile.h +++ b/src/libsysprof/sysprof-memprof-profile.h @@ -35,10 +35,28 @@ G_BEGIN_DECLS typedef enum { + SYSPROF_MEMPROF_MODE_SUMMARY = 0, SYSPROF_MEMPROF_MODE_ALL_ALLOCS = 1, SYSPROF_MEMPROF_MODE_TEMP_ALLOCS = 2, } SysprofMemprofMode; +typedef struct +{ + gint64 n_allocs; + gint64 leaked_allocs; + gint64 leaked_allocs_size; + gint64 temp_allocs; + gint64 temp_allocs_size; + struct { + gint64 bucket; + gint64 n_allocs; + gint64 temp_allocs; + gint64 allocated; + } by_size[14]; + /*< private >*/ + gint64 _reserved[32]; +} SysprofMemprofStats; + SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofMemprofProfile, sysprof_memprof_profile, SYSPROF, MEMPROF_PROFILE, GObject) @@ -47,6 +65,9 @@ SysprofProfile *sysprof_memprof_profile_new (void); SYSPROF_AVAILABLE_IN_3_36 SysprofProfile *sysprof_memprof_profile_new_with_selection (SysprofSelection *selection); SYSPROF_AVAILABLE_IN_3_36 +void sysprof_memprof_profile_get_stats (SysprofMemprofProfile *self, + SysprofMemprofStats *stats); +SYSPROF_AVAILABLE_IN_3_36 SysprofMemprofMode sysprof_memprof_profile_get_mode (SysprofMemprofProfile *self); SYSPROF_AVAILABLE_IN_3_36 void sysprof_memprof_profile_set_mode (SysprofMemprofProfile *self,