[sysprof] memprof: add section for allocation leaks
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [sysprof] memprof: add section for allocation leaks
- Date: Fri, 5 Nov 2021 04:10:52 +0000 (UTC)
commit 7a040a60328b5ab455a6be937c7c8f98e5c22e7f
Author: Christian Hergert <chergert redhat com>
Date: Thu Nov 4 21:08:23 2021 -0700
memprof: add section for allocation leaks
These are defined by allocation records that do not have a corresponding
release record.
src/libsysprof-ui/sysprof-memprof-page.c | 9 ++
src/libsysprof-ui/sysprof-memprof-page.ui | 9 ++
src/libsysprof/sysprof-memprof-profile.c | 234 ++++++++++++++++++++++++++++++
src/libsysprof/sysprof-memprof-profile.h | 13 ++
4 files changed, 265 insertions(+)
---
diff --git a/src/libsysprof-ui/sysprof-memprof-page.c b/src/libsysprof-ui/sysprof-memprof-page.c
index 9dd42a88..d4764768 100644
--- a/src/libsysprof-ui/sysprof-memprof-page.c
+++ b/src/libsysprof-ui/sysprof-memprof-page.c
@@ -62,6 +62,7 @@ typedef struct
GtkRadioButton *summary;
GtkRadioButton *all_allocs;
GtkRadioButton *temp_allocs;
+ GtkRadioButton *leaked_allocs_button;
GtkLabel *temp_allocs_count;
GtkLabel *num_allocs;
GtkLabel *leaked_allocs;
@@ -1029,6 +1030,8 @@ mode_notify_active (SysprofMemprofPage *self,
do_allocs (self, SYSPROF_MEMPROF_MODE_ALL_ALLOCS);
else if (button == priv->temp_allocs)
do_allocs (self, SYSPROF_MEMPROF_MODE_TEMP_ALLOCS);
+ else if (button == priv->leaked_allocs_button)
+ do_allocs (self, SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS);
}
}
@@ -1147,6 +1150,7 @@ sysprof_memprof_page_class_init (SysprofMemprofPageClass *klass)
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, leaked_allocs_button);
gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, peak_allocs);
bindings = gtk_binding_set_by_class (klass);
@@ -1182,6 +1186,11 @@ sysprof_memprof_page_init (SysprofMemprofPage *self)
G_CALLBACK (mode_notify_active),
self,
G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->leaked_allocs_button,
+ "notify::active",
+ G_CALLBACK (mode_notify_active),
+ self,
+ G_CONNECT_SWAPPED);
g_signal_connect_object (priv->summary,
"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 c123ab3c..0a522b1e 100644
--- a/src/libsysprof-ui/sysprof-memprof-page.ui
+++ b/src/libsysprof-ui/sysprof-memprof-page.ui
@@ -44,6 +44,15 @@
<property name="group">summary</property>
</object>
</child>
+ <child>
+ <object class="GtkRadioButton" id="leaked_allocs_button">
+ <property name="label" translatable="yes">Leaked Allocations</property>
+ <property name="draw-indicator">false</property>
+ <property name="visible">true</property>
+ <property name="active">false</property>
+ <property name="group">summary</property>
+ </object>
+ </child>
</object>
</child>
</object>
diff --git a/src/libsysprof/sysprof-memprof-profile.c b/src/libsysprof/sysprof-memprof-profile.c
index 30984106..33da6bc5 100644
--- a/src/libsysprof/sysprof-memprof-profile.c
+++ b/src/libsysprof/sysprof-memprof-profile.c
@@ -410,6 +410,31 @@ compare_alloc (gconstpointer a,
return 0;
}
+static gint
+compare_alloc_pid_addr_time (gconstpointer a,
+ gconstpointer b)
+{
+ const Alloc *aptr = a;
+ const Alloc *bptr = b;
+
+ if (aptr->pid < bptr->pid)
+ return -1;
+ else if (aptr->pid > bptr->pid)
+ return 1;
+
+ if (aptr->addr < bptr->addr)
+ return -1;
+ else if (aptr->addr > bptr->addr)
+ return 1;
+
+ if (aptr->time < bptr->time)
+ return -1;
+ else if (aptr->time > bptr->time)
+ return 1;
+ else
+ return 0;
+}
+
static guint
get_bucket (gint64 size)
{
@@ -741,6 +766,211 @@ temp_allocs_worker (Generate *g)
}
}
+static void
+leaked_allocs_worker (Generate *g)
+{
+ g_autoptr(GArray) leak_allocs = NULL;
+ g_autoptr(GArray) all_allocs = NULL;
+ StackNode *node;
+ SysprofCaptureFrameType type;
+ guint64 frame_num = 0;
+
+ g_assert (g != NULL);
+ g_assert (g->reader != NULL);
+
+ leak_allocs = g_array_new (FALSE, FALSE, sizeof (Alloc));
+ all_allocs = g_array_new (FALSE, FALSE, sizeof (Alloc));
+
+ sysprof_capture_reader_reset (g->reader);
+
+ while (sysprof_capture_reader_peek_type (g->reader, &type))
+ {
+ if G_UNLIKELY (type == SYSPROF_CAPTURE_FRAME_PROCESS)
+ {
+ const SysprofCaptureProcess *pr;
+
+ if (!(pr = sysprof_capture_reader_read_process (g->reader)))
+ break;
+
+ if (!g_hash_table_contains (g->cmdlines, GINT_TO_POINTER (pr->frame.pid)))
+ {
+ g_autofree gchar *cmdline = g_strdup_printf ("[%s]", pr->cmdline);
+ g_hash_table_insert (g->cmdlines,
+ GINT_TO_POINTER (pr->frame.pid),
+ (gchar *)g_string_chunk_insert_const (g->symbols, cmdline));
+ }
+ }
+ else if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION)
+ {
+ const SysprofCaptureAllocation *ev;
+ Alloc a;
+
+ if (!(ev = sysprof_capture_reader_read_allocation (g->reader)))
+ break;
+
+ frame_num++;
+
+ /* Short-circuit if we don't care about this frame */
+ if (!sysprof_selection_contains (g->selection, ev->frame.time))
+ continue;
+
+ 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 = frame_num;
+
+ g_array_append_val (all_allocs, a);
+ }
+ else
+ {
+ if (!sysprof_capture_reader_skip (g->reader))
+ break;
+ }
+ }
+
+ if (all_allocs->len == 0)
+ return;
+
+ /* Order so we can find frees right after alloc */
+ g_array_sort (all_allocs, compare_alloc_pid_addr_time);
+
+ for (guint i = 0; i < all_allocs->len; i++)
+ {
+ const Alloc *a = &g_array_index (all_allocs, Alloc, i);
+ const Alloc *next;
+
+ /* free()s are <= 0 */
+ if (a->size <= 0)
+ continue;
+
+ if (i + 1 == all_allocs->len)
+ goto leaked;
+
+ next = &g_array_index (all_allocs, Alloc, i+1);
+ if (a->addr == next->addr && a->pid == next->pid && next->size <= 0)
+ continue;
+
+ leaked:
+ g_array_append_vals (leak_allocs, a, 1);
+ }
+
+ if (leak_allocs->len == 0)
+ return;
+
+ /* Now sort by frame number so we can walk the reader and get the stack
+ * for each allocation as we count frames. We can skip frames until we
+ * get to the matching frame_num for the next alloc.
+ *
+ * We sort in reverse so that we can just keep shortening the array as
+ * we match each frame to save having to keep a secondary position
+ * variable.
+ */
+ g_array_sort (leak_allocs, compare_frame_num_reverse);
+ sysprof_capture_reader_reset (g->reader);
+ frame_num = 0;
+ while (sysprof_capture_reader_peek_type (g->reader, &type))
+ {
+ if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION)
+ {
+ const SysprofCaptureAllocation *ev;
+ const Alloc *tail;
+
+ if (!(ev = sysprof_capture_reader_read_allocation (g->reader)))
+ break;
+
+ frame_num++;
+
+ tail = &g_array_index (leak_allocs, Alloc, leak_allocs->len - 1);
+
+ if G_UNLIKELY (tail->frame_num == frame_num)
+ {
+ SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE;
+ const gchar *cmdline;
+ guint len = 5;
+
+ node = stack_stash_add_trace (g->building, ev->addrs, ev->n_addrs, ev->alloc_size);
+
+ for (const StackNode *iter = node; iter != NULL; iter = iter->parent)
+ len++;
+
+ if (G_UNLIKELY (g->resolved->len < len))
+ g_array_set_size (g->resolved, len);
+
+ len = 0;
+
+ for (const StackNode *iter = node; iter != NULL; iter = iter->parent)
+ {
+ SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE;
+ SysprofAddress address = iter->data;
+ const gchar *symbol = NULL;
+
+ if (sysprof_address_is_context_switch (address, &context))
+ {
+ if (last_context)
+ symbol = sysprof_address_context_to_string (last_context);
+ else
+ symbol = NULL;
+
+ last_context = context;
+ }
+ else
+ {
+ for (guint i = 0; i < g->resolvers->len; i++)
+ {
+ SysprofSymbolResolver *resolver = g_ptr_array_index (g->resolvers, i);
+ GQuark tag = 0;
+ gchar *str;
+
+ str = sysprof_symbol_resolver_resolve_with_context (resolver,
+ ev->frame.time,
+ ev->frame.pid,
+ last_context,
+ address,
+ &tag);
+
+ if (str != NULL)
+ {
+ symbol = g_string_chunk_insert_const (g->symbols, str);
+ g_free (str);
+
+ if (tag != 0 && !g_hash_table_contains (g->tags, symbol))
+ g_hash_table_insert (g->tags, (gchar *)symbol, GSIZE_TO_POINTER (tag));
+
+ break;
+ }
+ }
+ }
+
+ if (symbol != NULL)
+ g_array_index (g->resolved, SysprofAddress, len++) = POINTER_TO_U64 (symbol);
+ }
+
+ if ((cmdline = g_hash_table_lookup (g->cmdlines, GINT_TO_POINTER (ev->frame.pid))))
+ g_array_index (g->resolved, guint64, len++) = POINTER_TO_U64 (cmdline);
+
+ g_array_index (g->resolved, guint64, len++) = POINTER_TO_U64 ("[Everything]");
+
+ stack_stash_add_trace (g->stash,
+ (gpointer)g->resolved->data,
+ len,
+ ev->alloc_size);
+
+ g_array_set_size (leak_allocs, leak_allocs->len - 1);
+
+ if (leak_allocs->len == 0)
+ break;
+ }
+ }
+ else
+ {
+ if (!sysprof_capture_reader_skip (g->reader))
+ break;
+ }
+ }
+}
+
static void
sysprof_memprof_profile_generate_worker (GTask *task,
gpointer source_object,
@@ -781,6 +1011,10 @@ sysprof_memprof_profile_generate_worker (GTask *task,
{
summary_worker (g);
}
+ else if (g->mode == SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS)
+ {
+ leaked_allocs_worker (g);
+ }
/* Release some data we don't need anymore */
g_clear_pointer (&g->resolved, g_array_unref);
diff --git a/src/libsysprof/sysprof-memprof-profile.h b/src/libsysprof/sysprof-memprof-profile.h
index 1a5badd3..28e4d915 100644
--- a/src/libsysprof/sysprof-memprof-profile.h
+++ b/src/libsysprof/sysprof-memprof-profile.h
@@ -33,11 +33,24 @@ G_BEGIN_DECLS
#define SYSPROF_TYPE_MEMPROF_PROFILE (sysprof_memprof_profile_get_type())
+/**
+ * SysprofMemprofMode:
+ * @SYSPROF_MEMPROF_MODE_SUMMARY: The summary profile
+ * @SYSPROF_MEMPROF_MODE_ALL_ALLOCS: find all allocations
+ * @SYSPROF_MEMPROF_MODE_TEMP_ALLOCS: find temporary allocations
+ * @SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS: find allocation leaks, Since 3.44
+ *
+ * The memprof profile mode.
+ *
+ * Since 3.44 @SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS is available
+ * to find leaked allocations.
+ */
typedef enum
{
SYSPROF_MEMPROF_MODE_SUMMARY = 0,
SYSPROF_MEMPROF_MODE_ALL_ALLOCS = 1,
SYSPROF_MEMPROF_MODE_TEMP_ALLOCS = 2,
+ SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS = 3,
} SysprofMemprofMode;
typedef struct
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]