mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
memprof: add memory profiling using LD_PRELOAD
This brings over some of the techniques from the old memprof design. Sysprof and memprof shared a lot of code, so it is pretty natural to bring back the same callgraph view based on memory allocations. This reuses the StackStash just like it did in memprof. While it would be nice to reuse some existing tools out there, the fit of memprof with sysprof is so naturally aligned, it's not really a big deal to bring back the LD_PRELOAD. The value really comes from seeing all this stuff together instead of multiple apps. There are plenty of things we can implement on top of this that we are not doing yet such as temporary allocations, cross-thread frees, graphing the heap, and graphing differences between the heap at to points in time. I'd like all of these things, given enough time to make them useful. This is still a bit slow though due to the global lock we take to access the writer. To improve the speed here we need to get rid of that lock and head towards a design that allows a thread to request a new writer from Sysprof and save it in TLS (to be destroyed when the thread exits).
This commit is contained in:
@ -19,6 +19,7 @@
|
||||
<file preprocess="xml-stripblanks">sysprof-failed-state-view.ui</file>
|
||||
<file preprocess="xml-stripblanks">sysprof-logs-page.ui</file>
|
||||
<file preprocess="xml-stripblanks">sysprof-marks-page.ui</file>
|
||||
<file preprocess="xml-stripblanks">sysprof-memprof-page.ui</file>
|
||||
<file preprocess="xml-stripblanks">sysprof-process-model-row.ui</file>
|
||||
<file preprocess="xml-stripblanks">sysprof-profiler-assistant.ui</file>
|
||||
<file preprocess="xml-stripblanks">sysprof-recording-state-view.ui</file>
|
||||
|
||||
@ -45,6 +45,9 @@ libsysprof_ui_private_sources = [
|
||||
'sysprof-marks-page.c',
|
||||
'sysprof-mark-visualizer.c',
|
||||
'sysprof-memory-aid.c',
|
||||
'sysprof-memprof-aid.c',
|
||||
'sysprof-memprof-page.c',
|
||||
'sysprof-memprof-visualizer.c',
|
||||
'sysprof-netdev-aid.c',
|
||||
'sysprof-procs-visualizer.c',
|
||||
'sysprof-profiler-assistant.c',
|
||||
|
||||
@ -42,6 +42,8 @@
|
||||
#include "sysprof-diskstat-aid.h"
|
||||
#include "sysprof-logs-aid.h"
|
||||
#include "sysprof-marks-aid.h"
|
||||
#include "sysprof-memory-aid.h"
|
||||
#include "sysprof-memprof-aid.h"
|
||||
#include "sysprof-netdev-aid.h"
|
||||
#include "sysprof-rapl-aid.h"
|
||||
|
||||
@ -654,6 +656,8 @@ sysprof_display_present_async (SysprofDisplay *self,
|
||||
g_ptr_array_add (aids, sysprof_diskstat_aid_new ());
|
||||
g_ptr_array_add (aids, sysprof_logs_aid_new ());
|
||||
g_ptr_array_add (aids, sysprof_marks_aid_new ());
|
||||
g_ptr_array_add (aids, sysprof_memory_aid_new ());
|
||||
g_ptr_array_add (aids, sysprof_memprof_aid_new ());
|
||||
g_ptr_array_add (aids, sysprof_netdev_aid_new ());
|
||||
g_ptr_array_add (aids, sysprof_rapl_aid_new ());
|
||||
|
||||
|
||||
@ -33,15 +33,6 @@ struct _SysprofMemoryAid
|
||||
|
||||
G_DEFINE_TYPE (SysprofMemoryAid, sysprof_memory_aid, SYSPROF_TYPE_AID)
|
||||
|
||||
/**
|
||||
* sysprof_memory_aid_new:
|
||||
*
|
||||
* Create a new #SysprofMemoryAid.
|
||||
*
|
||||
* Returns: (transfer full): a newly created #SysprofMemoryAid
|
||||
*
|
||||
* Since: 3.34
|
||||
*/
|
||||
SysprofAid *
|
||||
sysprof_memory_aid_new (void)
|
||||
{
|
||||
@ -50,7 +41,7 @@ sysprof_memory_aid_new (void)
|
||||
|
||||
static void
|
||||
sysprof_memory_aid_prepare (SysprofAid *self,
|
||||
SysprofProfiler *profiler)
|
||||
SysprofProfiler *profiler)
|
||||
{
|
||||
#ifdef __linux__
|
||||
g_autoptr(SysprofSource) source = NULL;
|
||||
|
||||
226
src/libsysprof-ui/sysprof-memprof-aid.c
Normal file
226
src/libsysprof-ui/sysprof-memprof-aid.c
Normal file
@ -0,0 +1,226 @@
|
||||
/* sysprof-memprof-aid.c
|
||||
*
|
||||
* Copyright 2019 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sysprof-memprof-aid"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "sysprof-memprof-aid.h"
|
||||
#include "sysprof-memprof-page.h"
|
||||
#include "sysprof-memprof-source.h"
|
||||
#include "sysprof-memprof-visualizer.h"
|
||||
|
||||
struct _SysprofMemprofAid
|
||||
{
|
||||
SysprofAid parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (SysprofMemprofAid, sysprof_memprof_aid, SYSPROF_TYPE_AID)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SysprofCaptureCursor *cursor;
|
||||
SysprofDisplay *display;
|
||||
guint has_allocs : 1;
|
||||
} Present;
|
||||
|
||||
static void
|
||||
present_free (gpointer data)
|
||||
{
|
||||
Present *p = data;
|
||||
|
||||
g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
|
||||
g_clear_object (&p->display);
|
||||
g_slice_free (Present, p);
|
||||
}
|
||||
|
||||
static void
|
||||
on_group_activated_cb (SysprofVisualizerGroup *group,
|
||||
SysprofPage *page)
|
||||
{
|
||||
SysprofDisplay *display;
|
||||
|
||||
g_assert (SYSPROF_IS_VISUALIZER_GROUP (group));
|
||||
g_assert (SYSPROF_IS_PAGE (page));
|
||||
|
||||
display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY));
|
||||
sysprof_display_set_visible_page (display, page);
|
||||
}
|
||||
|
||||
SysprofAid *
|
||||
sysprof_memprof_aid_new (void)
|
||||
{
|
||||
return g_object_new (SYSPROF_TYPE_MEMPROF_AID, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_memprof_aid_prepare (SysprofAid *self,
|
||||
SysprofProfiler *profiler)
|
||||
{
|
||||
#ifdef __linux__
|
||||
g_autoptr(SysprofSource) source = NULL;
|
||||
|
||||
g_assert (SYSPROF_IS_MEMPROF_AID (self));
|
||||
g_assert (SYSPROF_IS_PROFILER (profiler));
|
||||
|
||||
source = sysprof_memprof_source_new ();
|
||||
sysprof_profiler_add_source (profiler, source);
|
||||
#endif
|
||||
}
|
||||
|
||||
static gboolean
|
||||
discover_samples_cb (const SysprofCaptureFrame *frame,
|
||||
gpointer user_data)
|
||||
{
|
||||
Present *p = user_data;
|
||||
|
||||
g_assert (frame != NULL);
|
||||
g_assert (p != NULL);
|
||||
|
||||
if (frame->type == SYSPROF_CAPTURE_FRAME_ALLOCATION)
|
||||
{
|
||||
p->has_allocs = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_memprof_aid_present_worker (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
Present *p = task_data;
|
||||
|
||||
g_assert (G_IS_TASK (task));
|
||||
g_assert (SYSPROF_IS_MEMPROF_AID (source_object));
|
||||
g_assert (p != NULL);
|
||||
g_assert (p->cursor != NULL);
|
||||
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
|
||||
|
||||
sysprof_capture_cursor_foreach (p->cursor, discover_samples_cb, p);
|
||||
g_task_return_boolean (task, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_memprof_aid_present_async (SysprofAid *aid,
|
||||
SysprofCaptureReader *reader,
|
||||
SysprofDisplay *display,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_ALLOCATION };
|
||||
g_autoptr(SysprofCaptureCondition) condition = NULL;
|
||||
g_autoptr(SysprofCaptureCursor) cursor = NULL;
|
||||
g_autoptr(GTask) task = NULL;
|
||||
Present present;
|
||||
|
||||
g_assert (SYSPROF_IS_MEMPROF_AID (aid));
|
||||
g_assert (reader != NULL);
|
||||
g_assert (SYSPROF_IS_DISPLAY (display));
|
||||
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
|
||||
|
||||
condition = sysprof_capture_condition_new_where_type_in (1, types);
|
||||
cursor = sysprof_capture_cursor_new (reader);
|
||||
sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
|
||||
|
||||
present.cursor = g_steal_pointer (&cursor);
|
||||
present.display = g_object_ref (display);
|
||||
|
||||
task = g_task_new (aid, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, sysprof_memprof_aid_present_async);
|
||||
g_task_set_task_data (task,
|
||||
g_slice_dup (Present, &present),
|
||||
present_free);
|
||||
g_task_run_in_thread (task, sysprof_memprof_aid_present_worker);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sysprof_memprof_aid_present_finish (SysprofAid *aid,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
Present *p;
|
||||
|
||||
g_assert (SYSPROF_IS_MEMPROF_AID (aid));
|
||||
g_assert (G_IS_TASK (result));
|
||||
|
||||
p = g_task_get_task_data (G_TASK (result));
|
||||
|
||||
if (p->has_allocs)
|
||||
{
|
||||
SysprofVisualizerGroup *group;
|
||||
SysprofVisualizer *row;
|
||||
SysprofPage *page;
|
||||
|
||||
group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
|
||||
"can-focus", TRUE,
|
||||
"has-page", TRUE,
|
||||
"priority", -300,
|
||||
"title", _("Memory"),
|
||||
"visible", TRUE,
|
||||
NULL);
|
||||
|
||||
row = sysprof_memprof_visualizer_new (FALSE);
|
||||
sysprof_visualizer_group_insert (group, row, 0, FALSE);
|
||||
|
||||
row = sysprof_memprof_visualizer_new (TRUE);
|
||||
sysprof_visualizer_group_insert (group, row, 1, FALSE);
|
||||
|
||||
page = g_object_new (SYSPROF_TYPE_MEMPROF_PAGE,
|
||||
"title", _("Memory Allocations"),
|
||||
"vexpand", TRUE,
|
||||
"visible", TRUE,
|
||||
NULL);
|
||||
sysprof_display_add_page (p->display, page);
|
||||
|
||||
g_signal_connect_object (group,
|
||||
"group-activated",
|
||||
G_CALLBACK (on_group_activated_cb),
|
||||
page,
|
||||
0);
|
||||
|
||||
sysprof_display_add_group (p->display, group);
|
||||
}
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_memprof_aid_class_init (SysprofMemprofAidClass *klass)
|
||||
{
|
||||
SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
|
||||
|
||||
aid_class->prepare = sysprof_memprof_aid_prepare;
|
||||
aid_class->present_async = sysprof_memprof_aid_present_async;
|
||||
aid_class->present_finish = sysprof_memprof_aid_present_finish;
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_memprof_aid_init (SysprofMemprofAid *self)
|
||||
{
|
||||
sysprof_aid_set_display_name (SYSPROF_AID (self), _("Track Allocations"));
|
||||
sysprof_aid_set_icon_name (SYSPROF_AID (self), "org.gnome.Sysprof-symbolic");
|
||||
}
|
||||
33
src/libsysprof-ui/sysprof-memprof-aid.h
Normal file
33
src/libsysprof-ui/sysprof-memprof-aid.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* sysprof-memprof-aid.h
|
||||
*
|
||||
* Copyright 2019 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sysprof-aid.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SYSPROF_TYPE_MEMPROF_AID (sysprof_memprof_aid_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SysprofMemprofAid, sysprof_memprof_aid, SYSPROF, MEMPROF_AID, SysprofAid)
|
||||
|
||||
SysprofAid *sysprof_memprof_aid_new (void);
|
||||
|
||||
G_END_DECLS
|
||||
1319
src/libsysprof-ui/sysprof-memprof-page.c
Normal file
1319
src/libsysprof-ui/sysprof-memprof-page.c
Normal file
File diff suppressed because it is too large
Load Diff
51
src/libsysprof-ui/sysprof-memprof-page.h
Normal file
51
src/libsysprof-ui/sysprof-memprof-page.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* sysprof-memprof-page.h
|
||||
*
|
||||
* Copyright 2016-2019 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <sysprof.h>
|
||||
|
||||
#include "sysprof-page.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SYSPROF_TYPE_MEMPROF_PAGE (sysprof_memprof_page_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SysprofMemprofPage, sysprof_memprof_page, SYSPROF, MEMPROF_PAGE, SysprofPage)
|
||||
|
||||
struct _SysprofMemprofPageClass
|
||||
{
|
||||
SysprofPageClass parent_class;
|
||||
|
||||
void (*go_previous) (SysprofMemprofPage *self);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _reserved[16];
|
||||
};
|
||||
|
||||
GtkWidget *sysprof_memprof_page_new (void);
|
||||
SysprofMemprofProfile *sysprof_memprof_page_get_profile (SysprofMemprofPage *self);
|
||||
void sysprof_memprof_page_set_profile (SysprofMemprofPage *self,
|
||||
SysprofMemprofProfile *profile);
|
||||
gchar *sysprof_memprof_page_screenshot (SysprofMemprofPage *self);
|
||||
guint sysprof_memprof_page_get_n_functions (SysprofMemprofPage *self);
|
||||
|
||||
G_END_DECLS
|
||||
232
src/libsysprof-ui/sysprof-memprof-page.ui
Normal file
232
src/libsysprof-ui/sysprof-memprof-page.ui
Normal file
@ -0,0 +1,232 @@
|
||||
<interface>
|
||||
<template class="SysprofMemprofPage" parent="SysprofPage">
|
||||
<child>
|
||||
<object class="GtkStack" id="stack">
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkPaned">
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="position">450</property>
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkPaned">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="functions_view">
|
||||
<property name="fixed-height-mode">true</property>
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="function_name_column">
|
||||
<property name="expand">true</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="sort-column-id">0</property>
|
||||
<property name="title" translatable="yes">Functions</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText">
|
||||
<property name="ellipsize">middle</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="function_self_column">
|
||||
<property name="expand">false</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="sort-column-id">1</property>
|
||||
<property name="title" translatable="yes">Self</property>
|
||||
<child>
|
||||
<object class="SysprofCellRendererPercent">
|
||||
<property name="width">65</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="percent">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="function_total_column">
|
||||
<property name="expand">false</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="sort-column-id">2</property>
|
||||
<property name="title" translatable="yes">Total</property>
|
||||
<child>
|
||||
<object class="SysprofCellRendererPercent">
|
||||
<property name="width">65</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="percent">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">true</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="callers_view">
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="callers_name_column">
|
||||
<property name="expand">true</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="sort-column-id">0</property>
|
||||
<property name="title" translatable="yes">Callers</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText">
|
||||
<property name="ellipsize">middle</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="callers_self_column">
|
||||
<property name="expand">false</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="sort-column-id">1</property>
|
||||
<property name="title" translatable="yes">Self</property>
|
||||
<child>
|
||||
<object class="SysprofCellRendererPercent">
|
||||
<property name="width">65</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="percent">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="callers_total_column">
|
||||
<property name="expand">false</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="sort-column-id">2</property>
|
||||
<property name="title" translatable="yes">Total</property>
|
||||
<child>
|
||||
<object class="SysprofCellRendererPercent">
|
||||
<property name="width">65</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="percent">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">true</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="descendants_view">
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="descendants_name_column">
|
||||
<property name="expand">true</property>
|
||||
<property name="sizing">autosize</property>
|
||||
<property name="sort-column-id">0</property>
|
||||
<property name="title" translatable="yes">Descendants</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="descendants_self_column">
|
||||
<property name="expand">false</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="sort-column-id">1</property>
|
||||
<property name="title" translatable="yes">Self</property>
|
||||
<child>
|
||||
<object class="SysprofCellRendererPercent">
|
||||
<property name="width">65</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="percent">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="descendants_total_column">
|
||||
<property name="expand">false</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="sort-column-id">2</property>
|
||||
<property name="title" translatable="yes">Total</property>
|
||||
<child>
|
||||
<object class="SysprofCellRendererPercent">
|
||||
<property name="width">65</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="percent">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="function_size_column">
|
||||
<property name="expand">false</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="title" translatable="yes">Size</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="function_size_cell">
|
||||
<property name="xalign">1.0</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">callgraph</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="DzlEmptyState">
|
||||
<property name="icon-name">content-loading-symbolic</property>
|
||||
<property name="title" translatable="yes">Generating Callgraph</property>
|
||||
<property name="subtitle" translatable="yes">Sysprof is busy creating the selected callgraph.</property>
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">loading</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="DzlEmptyState">
|
||||
<property name="icon-name">computer-fail-symbolic</property>
|
||||
<property name="title" translatable="yes">Not Enough Samples</property>
|
||||
<property name="subtitle" translatable="yes">More samples are necessary to display a callgraph.</property>
|
||||
<property name="visible">false</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">empty-state</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
614
src/libsysprof-ui/sysprof-memprof-visualizer.c
Normal file
614
src/libsysprof-ui/sysprof-memprof-visualizer.c
Normal file
@ -0,0 +1,614 @@
|
||||
/* sysprof-memprof-visualizer.c
|
||||
*
|
||||
* Copyright 2020 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define G_LOG_DOMAIN "sysprof-memprof-visualizer"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "rax.h"
|
||||
|
||||
#include "sysprof-memprof-visualizer.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
SysprofCaptureReader *reader;
|
||||
rax *rax;
|
||||
GtkAllocation alloc;
|
||||
gint64 begin_time;
|
||||
gint64 duration;
|
||||
gint64 total_alloc;
|
||||
gint64 max_alloc;
|
||||
GdkRGBA fg;
|
||||
GdkRGBA fg2;
|
||||
guint scale;
|
||||
} DrawContext;
|
||||
|
||||
struct _SysprofMemprofVisualizer
|
||||
{
|
||||
SysprofVisualizer parent_instance;
|
||||
|
||||
SysprofCaptureReader *reader;
|
||||
GCancellable *cancellable;
|
||||
|
||||
cairo_surface_t *surface;
|
||||
gint surface_w;
|
||||
gint surface_h;
|
||||
|
||||
guint queued_draw;
|
||||
|
||||
gint64 begin_time;
|
||||
gint64 duration;
|
||||
|
||||
gint64 cached_total_alloc;
|
||||
gint64 cached_max_alloc;
|
||||
|
||||
guint mode : 1;
|
||||
};
|
||||
|
||||
enum {
|
||||
MODE_ALLOCS,
|
||||
MODE_TOTAL,
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (SysprofMemprofVisualizer, sysprof_memprof_visualizer, SYSPROF_TYPE_VISUALIZER)
|
||||
|
||||
static void
|
||||
draw_context_free (DrawContext *draw)
|
||||
{
|
||||
g_clear_pointer (&draw->reader, sysprof_capture_reader_unref);
|
||||
g_clear_pointer (&draw->surface, cairo_surface_destroy);
|
||||
g_clear_pointer (&draw->rax, raxFree);
|
||||
g_slice_free (DrawContext, draw);
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_memprof_visualizer_set_reader (SysprofVisualizer *visualizer,
|
||||
SysprofCaptureReader *reader)
|
||||
{
|
||||
SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)visualizer;
|
||||
|
||||
g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self));
|
||||
|
||||
if (reader == self->reader)
|
||||
return;
|
||||
|
||||
g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
|
||||
|
||||
self->reader = sysprof_capture_reader_ref (reader);
|
||||
self->begin_time = sysprof_capture_reader_get_start_time (reader);
|
||||
self->duration = sysprof_capture_reader_get_end_time (reader)
|
||||
- sysprof_capture_reader_get_start_time (reader);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
SysprofVisualizer *
|
||||
sysprof_memprof_visualizer_new (gboolean total_allocs)
|
||||
{
|
||||
SysprofMemprofVisualizer *self;
|
||||
|
||||
self = g_object_new (SYSPROF_TYPE_MEMPROF_VISUALIZER,
|
||||
"title", total_allocs ? _("Memory Used") : _("Memory Allocations"),
|
||||
"height-request", 35,
|
||||
"visible", TRUE,
|
||||
NULL);
|
||||
|
||||
if (total_allocs)
|
||||
self->mode = MODE_TOTAL;
|
||||
else
|
||||
self->mode = MODE_ALLOCS;
|
||||
|
||||
return SYSPROF_VISUALIZER (self);
|
||||
}
|
||||
|
||||
static guint64
|
||||
get_max_alloc (SysprofCaptureReader *reader)
|
||||
{
|
||||
SysprofCaptureFrameType type;
|
||||
gint64 ret = 0;
|
||||
|
||||
while (sysprof_capture_reader_peek_type (reader, &type))
|
||||
{
|
||||
const SysprofCaptureAllocation *ev;
|
||||
|
||||
if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION)
|
||||
{
|
||||
if (!(ev = sysprof_capture_reader_read_allocation (reader)))
|
||||
break;
|
||||
|
||||
if (ev->alloc_size > ret)
|
||||
ret = ev->alloc_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sysprof_capture_reader_skip (reader))
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
sysprof_capture_reader_reset (reader);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static guint64
|
||||
get_total_alloc (SysprofCaptureReader *reader)
|
||||
{
|
||||
SysprofCaptureFrameType type;
|
||||
guint64 total = 0;
|
||||
guint64 max = 0;
|
||||
rax *r;
|
||||
|
||||
r = raxNew ();
|
||||
|
||||
while (sysprof_capture_reader_peek_type (reader, &type))
|
||||
{
|
||||
const SysprofCaptureAllocation *ev;
|
||||
|
||||
if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION)
|
||||
{
|
||||
if (!(ev = sysprof_capture_reader_read_allocation (reader)))
|
||||
break;
|
||||
|
||||
if (ev->alloc_size > 0)
|
||||
{
|
||||
raxInsert (r,
|
||||
(guint8 *)&ev->alloc_addr,
|
||||
sizeof ev->alloc_addr,
|
||||
GSIZE_TO_POINTER (ev->alloc_size),
|
||||
NULL);
|
||||
|
||||
total += ev->alloc_size;
|
||||
|
||||
if (total > max)
|
||||
max = total;
|
||||
}
|
||||
else
|
||||
{
|
||||
gpointer res = raxFind (r, (guint8 *)&ev->alloc_addr, sizeof ev->alloc_addr);
|
||||
|
||||
if (res != raxNotFound)
|
||||
{
|
||||
total -= GPOINTER_TO_SIZE (res);
|
||||
raxRemove (r,
|
||||
(guint8 *)&ev->alloc_addr,
|
||||
sizeof ev->alloc_addr,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sysprof_capture_reader_skip (reader))
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
sysprof_capture_reader_reset (reader);
|
||||
raxFree (r);
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
static void
|
||||
draw_total_worker (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
SysprofCaptureFrameType type;
|
||||
DrawContext *draw = task_data;
|
||||
gint64 total = 0;
|
||||
cairo_t *cr;
|
||||
rax *r;
|
||||
gint x = 0;
|
||||
|
||||
g_assert (G_IS_TASK (task));
|
||||
g_assert (draw != NULL);
|
||||
g_assert (draw->surface != NULL);
|
||||
g_assert (draw->reader != NULL);
|
||||
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
|
||||
|
||||
if (draw->total_alloc == 0)
|
||||
draw->total_alloc = get_total_alloc (draw->reader);
|
||||
|
||||
r = raxNew ();
|
||||
|
||||
/* To avoid sorting, this code assums that all allocation information
|
||||
* is sorted and in order. Generally this is the case, but a crafted
|
||||
* syscap file could break it on purpose if they tried.
|
||||
*/
|
||||
|
||||
cr = cairo_create (draw->surface);
|
||||
cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
|
||||
cairo_set_source_rgb (cr, 0, 0, 0);
|
||||
|
||||
while (sysprof_capture_reader_peek_type (draw->reader, &type))
|
||||
{
|
||||
const SysprofCaptureAllocation *ev;
|
||||
gint y;
|
||||
|
||||
if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION)
|
||||
{
|
||||
if (!(ev = sysprof_capture_reader_read_allocation (draw->reader)))
|
||||
break;
|
||||
|
||||
if (ev->alloc_size > 0)
|
||||
{
|
||||
raxInsert (r,
|
||||
(guint8 *)&ev->alloc_addr,
|
||||
sizeof ev->alloc_addr,
|
||||
GSIZE_TO_POINTER (ev->alloc_size),
|
||||
NULL);
|
||||
|
||||
total += ev->alloc_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
gpointer res = raxFind (r, (guint8 *)&ev->alloc_addr, sizeof ev->alloc_addr);
|
||||
|
||||
if (res != raxNotFound)
|
||||
{
|
||||
total -= GPOINTER_TO_SIZE (res);
|
||||
raxRemove (r,
|
||||
(guint8 *)&ev->alloc_addr,
|
||||
sizeof ev->alloc_addr,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sysprof_capture_reader_skip (draw->reader))
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
x = (ev->frame.time - draw->begin_time) / (gdouble)draw->duration * draw->alloc.width;
|
||||
y = draw->alloc.height - ((gdouble)total / (gdouble)draw->total_alloc * (gdouble)draw->alloc.height);
|
||||
|
||||
cairo_rectangle (cr, x, y, 1, 1);
|
||||
cairo_fill (cr);
|
||||
}
|
||||
|
||||
cairo_destroy (cr);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
|
||||
raxFree (r);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_alloc_worker (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
static const gdouble dashes[] = { 1.0, 2.0 };
|
||||
DrawContext *draw = task_data;
|
||||
SysprofCaptureFrameType type;
|
||||
GdkRGBA *last;
|
||||
GdkRGBA mid;
|
||||
cairo_t *cr;
|
||||
guint counter = 0;
|
||||
gint midpt;
|
||||
gdouble log_max;
|
||||
|
||||
g_assert (G_IS_TASK (task));
|
||||
g_assert (draw != NULL);
|
||||
g_assert (draw->surface != NULL);
|
||||
g_assert (draw->reader != NULL);
|
||||
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
|
||||
|
||||
if (draw->max_alloc == 0)
|
||||
draw->max_alloc = get_max_alloc (draw->reader);
|
||||
|
||||
log_max = log10 (draw->max_alloc);
|
||||
midpt = draw->alloc.height / 2;
|
||||
|
||||
cr = cairo_create (draw->surface);
|
||||
|
||||
/* Draw mid-point line */
|
||||
mid = draw->fg;
|
||||
mid.alpha *= 0.4;
|
||||
cairo_set_line_width (cr, 1.0);
|
||||
gdk_cairo_set_source_rgba (cr, &mid);
|
||||
cairo_move_to (cr, 0, midpt);
|
||||
cairo_line_to (cr, draw->alloc.width, midpt);
|
||||
cairo_set_dash (cr, dashes, G_N_ELEMENTS (dashes), 0);
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
|
||||
gdk_cairo_set_source_rgba (cr, &draw->fg);
|
||||
last = &draw->fg;
|
||||
|
||||
/* Now draw data points */
|
||||
while (sysprof_capture_reader_peek_type (draw->reader, &type))
|
||||
{
|
||||
const SysprofCaptureAllocation *ev;
|
||||
gint64 size;
|
||||
gdouble l;
|
||||
gint x;
|
||||
gint y;
|
||||
|
||||
/* Cancellation check every 1000 frames */
|
||||
if G_UNLIKELY (++counter == 1000)
|
||||
{
|
||||
if (g_task_return_error_if_cancelled (task))
|
||||
{
|
||||
cairo_destroy (cr);
|
||||
return;
|
||||
}
|
||||
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
/* We only care about memory frames here */
|
||||
if (type != SYSPROF_CAPTURE_FRAME_ALLOCATION)
|
||||
{
|
||||
if (!sysprof_capture_reader_skip (draw->reader))
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(ev = sysprof_capture_reader_read_allocation (draw->reader)))
|
||||
break;
|
||||
|
||||
if (ev->alloc_size > 0)
|
||||
{
|
||||
size = ev->alloc_size;
|
||||
raxInsert (draw->rax, (guint8 *)&ev->alloc_addr, sizeof ev->alloc_addr, GSIZE_TO_POINTER (size), NULL);
|
||||
|
||||
if (last != &draw->fg)
|
||||
{
|
||||
gdk_cairo_set_source_rgba (cr, &draw->fg);
|
||||
last = &draw->fg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size = GPOINTER_TO_SIZE (raxFind (draw->rax, (guint8 *)&ev->alloc_addr, sizeof ev->alloc_addr));
|
||||
if (size)
|
||||
raxRemove (draw->rax, (guint8 *)&ev->alloc_addr, sizeof ev->alloc_addr, NULL);
|
||||
|
||||
if (last != &draw->fg2)
|
||||
{
|
||||
gdk_cairo_set_source_rgba (cr, &draw->fg2);
|
||||
last = &draw->fg2;
|
||||
}
|
||||
}
|
||||
|
||||
l = log10 (size);
|
||||
|
||||
x = (ev->frame.time - draw->begin_time) / (gdouble)draw->duration * draw->alloc.width;
|
||||
|
||||
if (ev->alloc_size > 0)
|
||||
y = midpt - ((l / log_max) * midpt);
|
||||
else
|
||||
y = midpt + ((l / log_max) * midpt);
|
||||
|
||||
/* Fill immediately instead of batching draws so that
|
||||
* we don't take a lot of memory to hold on to the
|
||||
* path while drawing.
|
||||
*/
|
||||
cairo_rectangle (cr, x, y, 1, 1);
|
||||
cairo_fill (cr);
|
||||
}
|
||||
|
||||
cairo_destroy (cr);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_finished (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(SysprofMemprofVisualizer) self = user_data;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
g_assert (object == NULL);
|
||||
g_assert (G_IS_TASK (result));
|
||||
g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self));
|
||||
|
||||
if (g_task_propagate_boolean (G_TASK (result), &error))
|
||||
{
|
||||
DrawContext *draw = g_task_get_task_data (G_TASK (result));
|
||||
|
||||
g_clear_pointer (&self->surface, cairo_surface_destroy);
|
||||
|
||||
self->surface = g_steal_pointer (&draw->surface);
|
||||
self->surface_w = draw->alloc.width;
|
||||
self->surface_h = draw->alloc.height;
|
||||
self->cached_max_alloc = draw->max_alloc;
|
||||
self->cached_total_alloc = draw->total_alloc;
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sysprof_memprof_visualizer_begin_draw (SysprofMemprofVisualizer *self)
|
||||
{
|
||||
g_autoptr(GTask) task = NULL;
|
||||
GtkAllocation alloc;
|
||||
DrawContext *draw;
|
||||
|
||||
g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self));
|
||||
|
||||
self->queued_draw = 0;
|
||||
|
||||
/* Make sure we even need to draw */
|
||||
gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
|
||||
if (self->reader == NULL ||
|
||||
!gtk_widget_get_visible (GTK_WIDGET (self)) ||
|
||||
!gtk_widget_get_mapped (GTK_WIDGET (self)) ||
|
||||
alloc.width == 0 || alloc.height == 0)
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
/* Some GPUs (Intel) cannot deal with graphics textures larger than
|
||||
* 8000x8000. So here we are going to cheat a bit and just use that as our
|
||||
* max, and scale when drawing. The biggest issue here is that long term we
|
||||
* need a tiling solution that lets us render lots of tiles and then draw
|
||||
* them as necessary.
|
||||
*/
|
||||
if (alloc.width > 8000)
|
||||
alloc.width = 8000;
|
||||
|
||||
draw = g_slice_new0 (DrawContext);
|
||||
draw->rax = raxNew ();
|
||||
draw->alloc.width = alloc.width;
|
||||
draw->alloc.height = alloc.height;
|
||||
draw->reader = sysprof_capture_reader_copy (self->reader);
|
||||
draw->begin_time = self->begin_time;
|
||||
draw->duration = self->duration;
|
||||
draw->scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
|
||||
draw->max_alloc = self->cached_max_alloc;
|
||||
draw->total_alloc = self->cached_total_alloc;
|
||||
|
||||
gdk_rgba_parse (&draw->fg, "rgba(246,97,81,1)");
|
||||
gdk_rgba_parse (&draw->fg2, "rgba(245,194,17,1)");
|
||||
|
||||
draw->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
||||
alloc.width * draw->scale,
|
||||
alloc.height * draw->scale);
|
||||
cairo_surface_set_device_scale (draw->surface, draw->scale, draw->scale);
|
||||
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_clear_object (&self->cancellable);
|
||||
self->cancellable = g_cancellable_new ();
|
||||
|
||||
task = g_task_new (NULL, self->cancellable, draw_finished, g_object_ref (self));
|
||||
g_task_set_source_tag (task, sysprof_memprof_visualizer_begin_draw);
|
||||
g_task_set_task_data (task, g_steal_pointer (&draw), (GDestroyNotify)draw_context_free);
|
||||
|
||||
if (self->mode == MODE_ALLOCS)
|
||||
g_task_run_in_thread (task, draw_alloc_worker);
|
||||
else
|
||||
g_task_run_in_thread (task, draw_total_worker);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_memprof_visualizer_queue_redraw (SysprofMemprofVisualizer *self)
|
||||
{
|
||||
g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self));
|
||||
|
||||
if (self->queued_draw == 0)
|
||||
self->queued_draw = g_idle_add_full (G_PRIORITY_HIGH_IDLE,
|
||||
(GSourceFunc) sysprof_memprof_visualizer_begin_draw,
|
||||
g_object_ref (self),
|
||||
g_object_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_memprof_visualizer_size_allocate (GtkWidget *widget,
|
||||
GtkAllocation *alloc)
|
||||
{
|
||||
SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)widget;
|
||||
|
||||
g_assert (GTK_IS_WIDGET (widget));
|
||||
g_assert (alloc != NULL);
|
||||
|
||||
GTK_WIDGET_CLASS (sysprof_memprof_visualizer_parent_class)->size_allocate (widget, alloc);
|
||||
|
||||
sysprof_memprof_visualizer_queue_redraw (self);
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_memprof_visualizer_destroy (GtkWidget *widget)
|
||||
{
|
||||
SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)widget;
|
||||
|
||||
g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
|
||||
g_clear_pointer (&self->surface, cairo_surface_destroy);
|
||||
g_clear_handle_id (&self->queued_draw, g_source_remove);
|
||||
|
||||
GTK_WIDGET_CLASS (sysprof_memprof_visualizer_parent_class)->destroy (widget);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sysprof_memprof_visualizer_draw (GtkWidget *widget,
|
||||
cairo_t *cr)
|
||||
{
|
||||
SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)widget;
|
||||
gboolean ret;
|
||||
|
||||
g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self));
|
||||
g_assert (cr != NULL);
|
||||
|
||||
ret = GTK_WIDGET_CLASS (sysprof_memprof_visualizer_parent_class)->draw (widget, cr);
|
||||
|
||||
if (self->surface != NULL)
|
||||
{
|
||||
GtkAllocation alloc;
|
||||
|
||||
gtk_widget_get_allocation (widget, &alloc);
|
||||
|
||||
cairo_save (cr);
|
||||
cairo_rectangle (cr, 0, 0, alloc.width, alloc.height);
|
||||
|
||||
/* We might be drawing an updated image in the background, and this
|
||||
* will take our current surface (which is the wrong size) and draw
|
||||
* it stretched to fit the allocation. That gives us *something* that
|
||||
* represents the end result even if it is a bit blurry in the mean
|
||||
* time. Allocators take a while to render anyway.
|
||||
*/
|
||||
if (self->surface_w != alloc.width || self->surface_h != alloc.height)
|
||||
{
|
||||
cairo_scale (cr,
|
||||
(gdouble)alloc.width / (gdouble)self->surface_w,
|
||||
(gdouble)alloc.height / (gdouble)self->surface_h);
|
||||
}
|
||||
|
||||
cairo_set_source_surface (cr, self->surface, 0, 0);
|
||||
cairo_paint (cr);
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_memprof_visualizer_class_init (SysprofMemprofVisualizerClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass);
|
||||
|
||||
widget_class->destroy = sysprof_memprof_visualizer_destroy;
|
||||
widget_class->draw = sysprof_memprof_visualizer_draw;
|
||||
widget_class->size_allocate = sysprof_memprof_visualizer_size_allocate;
|
||||
|
||||
visualizer_class->set_reader = sysprof_memprof_visualizer_set_reader;
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_memprof_visualizer_init (SysprofMemprofVisualizer *self)
|
||||
{
|
||||
}
|
||||
33
src/libsysprof-ui/sysprof-memprof-visualizer.h
Normal file
33
src/libsysprof-ui/sysprof-memprof-visualizer.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* sysprof-memprof-visualizer.h
|
||||
*
|
||||
* Copyright 2020 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sysprof-visualizer.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SYSPROF_TYPE_MEMPROF_VISUALIZER (sysprof_memprof_visualizer_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SysprofMemprofVisualizer, sysprof_memprof_visualizer, SYSPROF, MEMPROF_VISUALIZER, SysprofVisualizer)
|
||||
|
||||
SysprofVisualizer *sysprof_memprof_visualizer_new (gboolean total_allocs);
|
||||
|
||||
G_END_DECLS
|
||||
@ -41,6 +41,7 @@
|
||||
#include "sysprof-callgraph-aid.h"
|
||||
#include "sysprof-cpu-aid.h"
|
||||
#include "sysprof-memory-aid.h"
|
||||
#include "sysprof-memprof-aid.h"
|
||||
#include "sysprof-netdev-aid.h"
|
||||
#include "sysprof-proxy-aid.h"
|
||||
#include "sysprof-rapl-aid.h"
|
||||
@ -397,6 +398,7 @@ sysprof_profiler_assistant_class_init (SysprofProfilerAssistantClass *klass)
|
||||
g_type_ensure (SYSPROF_TYPE_DISKSTAT_SOURCE);
|
||||
g_type_ensure (SYSPROF_TYPE_ENVIRON_EDITOR);
|
||||
g_type_ensure (SYSPROF_TYPE_MEMORY_AID);
|
||||
g_type_ensure (SYSPROF_TYPE_MEMPROF_AID);
|
||||
g_type_ensure (SYSPROF_TYPE_NETDEV_AID);
|
||||
g_type_ensure (SYSPROF_TYPE_PROXY_AID);
|
||||
g_type_ensure (SYSPROF_TYPE_RAPL_AID);
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<object class="SysprofCpuAid" id="cpu_aid"/>
|
||||
<object class="SysprofMemoryAid" id="memory_aid"/>
|
||||
<object class="SysprofMemprofAid" id="memprof_aid"/>
|
||||
<object class="SysprofCallgraphAid" id="callgraph_aid"/>
|
||||
<object class="SysprofNetdevAid" id="network_aid"/>
|
||||
<object class="SysprofRaplAid" id="rapl_aid"/>
|
||||
@ -178,6 +179,14 @@
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="SysprofAidIcon">
|
||||
<property name="tooltip-text" translatable="yes">Track application memory allocations (Sysprof must launch target application)</property>
|
||||
<property name="aid">memprof_aid</property>
|
||||
<property name="selected">false</property>
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@ -29,6 +29,9 @@ G_BEGIN_DECLS
|
||||
void _sysprof_callgraph_page_set_failed (SysprofCallgraphPage *self);
|
||||
void _sysprof_callgraph_page_set_loading (SysprofCallgraphPage *self,
|
||||
gboolean loading);
|
||||
void _sysprof_memory_page_set_failed (SysprofCallgraphPage *self);
|
||||
void _sysprof_memory_page_set_loading (SysprofCallgraphPage *self,
|
||||
gboolean loading);
|
||||
void _sysprof_display_focus_record (SysprofDisplay *self);
|
||||
void _sysprof_profiler_assistant_focus_record (SysprofProfilerAssistant *self);
|
||||
gchar *_sysprof_format_duration (gint64 duration);
|
||||
|
||||
Reference in New Issue
Block a user