profiler: port GdkProfiler to sysprof-3

This uses the new sysprof-3 ABI to implement the capture writer. It also
uses the statically linked libsysprof-capture-3.a that is provided with
Sysprof for the capture writing to ensure that we do not leak any symbols
nor depend on any additional libraries.

The GTK_TRACE_FD can be used to pass a FD for tracing into Gtk. Sysprof
uses this when the Gtk instrument is selected for recording.
This commit is contained in:
Christian Hergert 2019-05-29 19:02:30 -07:00
parent 853e832702
commit 3a0beeadc5
11 changed files with 56 additions and 1627 deletions

View File

@ -130,6 +130,9 @@
/* Define to 1 if you have the <sys/param.h> header file. */
#mesondefine HAVE_SYS_PARAM_H
#
/* Have the sysprof-capture library */
#mesondefine HAVE_SYSPROF_CAPTURE
/* Define to 1 if you have the <sys/stat.h> header file. */
#mesondefine HAVE_SYS_STAT_H

View File

@ -1,224 +0,0 @@
/* sp-capture-types.h
*
* Copyright © 2016 Christian Hergert <chergert@redhat.com>
*
* This file is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This file 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 Lesser 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/>.
*/
#ifndef SP_CAPTURE_FORMAT_H
#define SP_CAPTURE_FORMAT_H
#include <glib.h>
#ifndef SP_DISABLE_GOBJECT
# include <glib-object.h>
#endif
#include "sp-clock.h"
G_BEGIN_DECLS
#define SP_CAPTURE_MAGIC (GUINT32_TO_LE(0xFDCA975E))
#define SP_CAPTURE_ALIGN (sizeof(SpCaptureAddress))
#if __WORDSIZE == 64
# define SP_CAPTURE_JITMAP_MARK G_GUINT64_CONSTANT(0xE000000000000000)
# define SP_CAPTURE_ADDRESS_FORMAT "0x%016lx"
#else
# define SP_CAPTURE_JITMAP_MARK G_GUINT64_CONSTANT(0xE0000000)
# define SP_CAPTURE_ADDRESS_FORMAT "0x%016llx"
#endif
#define SP_CAPTURE_CURRENT_TIME (sp_clock_get_current_time())
#define SP_CAPTURE_COUNTER_INT64 0
#define SP_CAPTURE_COUNTER_DOUBLE 1
typedef struct _SpCaptureReader SpCaptureReader;
typedef struct _SpCaptureWriter SpCaptureWriter;
typedef struct _SpCaptureCursor SpCaptureCursor;
typedef struct _SpCaptureCondition SpCaptureCondition;
typedef guint64 SpCaptureAddress;
typedef union
{
gint64 v64;
gdouble vdbl;
} SpCaptureCounterValue;
typedef enum
{
SP_CAPTURE_FRAME_TIMESTAMP = 1,
SP_CAPTURE_FRAME_SAMPLE = 2,
SP_CAPTURE_FRAME_MAP = 3,
SP_CAPTURE_FRAME_PROCESS = 4,
SP_CAPTURE_FRAME_FORK = 5,
SP_CAPTURE_FRAME_EXIT = 6,
SP_CAPTURE_FRAME_JITMAP = 7,
SP_CAPTURE_FRAME_CTRDEF = 8,
SP_CAPTURE_FRAME_CTRSET = 9,
SP_CAPTURE_FRAME_MARK = 10,
} SpCaptureFrameType;
#pragma pack(push, 1)
typedef struct
{
guint32 magic;
guint8 version;
guint32 little_endian : 1;
guint32 padding : 23;
gchar capture_time[64];
gint64 time;
gint64 end_time;
gchar suffix[168];
} SpCaptureFileHeader;
typedef struct
{
guint16 len;
gint16 cpu;
gint32 pid;
gint64 time;
guint8 type;
guint64 padding : 56;
guint8 data[0];
} SpCaptureFrame;
typedef struct
{
SpCaptureFrame frame;
guint64 start;
guint64 end;
guint64 offset;
guint64 inode;
gchar filename[0];
} SpCaptureMap;
typedef struct
{
SpCaptureFrame frame;
guint32 n_jitmaps;
guint8 data[0];
} SpCaptureJitmap;
typedef struct
{
SpCaptureFrame frame;
gchar cmdline[0];
} SpCaptureProcess;
typedef struct
{
SpCaptureFrame frame;
guint16 n_addrs;
guint64 padding : 48;
SpCaptureAddress addrs[0];
} SpCaptureSample;
typedef struct
{
SpCaptureFrame frame;
GPid child_pid;
} SpCaptureFork;
typedef struct
{
SpCaptureFrame frame;
} SpCaptureExit;
typedef struct
{
SpCaptureFrame frame;
} SpCaptureTimestamp;
typedef struct
{
gchar category[32];
gchar name[32];
gchar description[52];
guint32 id : 24;
guint8 type;
SpCaptureCounterValue value;
} SpCaptureCounter;
typedef struct
{
SpCaptureFrame frame;
guint16 n_counters;
guint64 padding : 48;
SpCaptureCounter counters[0];
} SpCaptureFrameCounterDefine;
typedef struct
{
/*
* 96 bytes might seem a bit odd, but the counter frame header is 32
* bytes. So this makes a nice 2-cacheline aligned size which is
* useful when the number of counters is rather small.
*/
guint32 ids[8];
SpCaptureCounterValue values[8];
} SpCaptureCounterValues;
typedef struct
{
SpCaptureFrame frame;
guint16 n_values;
guint64 padding : 48;
SpCaptureCounterValues values[0];
} SpCaptureFrameCounterSet;
typedef struct
{
SpCaptureFrame frame;
gint64 duration;
gchar group[24];
gchar name[40];
gchar message[0];
} SpCaptureMark;
#pragma pack(pop)
G_STATIC_ASSERT (sizeof (SpCaptureFileHeader) == 256);
G_STATIC_ASSERT (sizeof (SpCaptureFrame) == 24);
G_STATIC_ASSERT (sizeof (SpCaptureMap) == 56);
G_STATIC_ASSERT (sizeof (SpCaptureJitmap) == 28);
G_STATIC_ASSERT (sizeof (SpCaptureProcess) == 24);
G_STATIC_ASSERT (sizeof (SpCaptureSample) == 32);
G_STATIC_ASSERT (sizeof (SpCaptureFork) == 28);
G_STATIC_ASSERT (sizeof (SpCaptureExit) == 24);
G_STATIC_ASSERT (sizeof (SpCaptureTimestamp) == 24);
G_STATIC_ASSERT (sizeof (SpCaptureCounter) == 128);
G_STATIC_ASSERT (sizeof (SpCaptureCounterValues) == 96);
G_STATIC_ASSERT (sizeof (SpCaptureFrameCounterDefine) == 32);
G_STATIC_ASSERT (sizeof (SpCaptureFrameCounterSet) == 32);
G_STATIC_ASSERT (sizeof (SpCaptureMark) == 96);
static inline gint
sp_capture_address_compare (SpCaptureAddress a,
SpCaptureAddress b)
{
if (a < b)
return -1;
if (a > b)
return 1;
else
return 0;
}
G_END_DECLS
#endif /* SP_CAPTURE_FORMAT_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,132 +0,0 @@
/* sp-capture-writer.h
*
* Copyright © 2016 Christian Hergert <chergert@redhat.com>
*
* This file is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This file 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 Lesser 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/>.
*/
#ifndef SP_CAPTURE_WRITER_H
#define SP_CAPTURE_WRITER_H
#include "capture/sp-capture-types.h"
G_BEGIN_DECLS
typedef struct _SpCaptureWriter SpCaptureWriter;
typedef struct
{
/*
* The number of frames indexed by SpCaptureFrameType
*/
gsize frame_count[16];
/*
* Padding for future expansion.
*/
gsize padding[48];
} SpCaptureStat;
SpCaptureWriter *sp_capture_writer_new (const gchar *filename,
gsize buffer_size);
SpCaptureWriter *sp_capture_writer_new_from_fd (int fd,
gsize buffer_size);
SpCaptureWriter *sp_capture_writer_ref (SpCaptureWriter *self);
void sp_capture_writer_unref (SpCaptureWriter *self);
void sp_capture_writer_stat (SpCaptureWriter *self,
SpCaptureStat *stat);
gboolean sp_capture_writer_add_map (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
guint64 start,
guint64 end,
guint64 offset,
guint64 inode,
const gchar *filename);
gboolean sp_capture_writer_add_mark (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
guint64 duration,
const gchar *group,
const gchar *name,
const gchar *message);
guint64 sp_capture_writer_add_jitmap (SpCaptureWriter *self,
const gchar *name);
gboolean sp_capture_writer_add_process (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
const gchar *cmdline);
gboolean sp_capture_writer_add_sample (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
const SpCaptureAddress *addrs,
guint n_addrs);
gboolean sp_capture_writer_add_fork (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
GPid child_pid);
gboolean sp_capture_writer_add_exit (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid);
gboolean sp_capture_writer_add_timestamp (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid);
gboolean sp_capture_writer_define_counters (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
const SpCaptureCounter *counters,
guint n_counters);
gboolean sp_capture_writer_set_counters (SpCaptureWriter *self,
gint64 time,
gint cpu,
GPid pid,
const guint *counters_ids,
const SpCaptureCounterValue *values,
guint n_counters);
gboolean sp_capture_writer_flush (SpCaptureWriter *self);
gboolean sp_capture_writer_save_as (SpCaptureWriter *self,
const gchar *filename,
GError **error);
gint sp_capture_writer_request_counter (SpCaptureWriter *self,
guint n_counters);
SpCaptureReader *sp_capture_writer_create_reader (SpCaptureWriter *self,
GError **error);
gboolean sp_capture_writer_splice (SpCaptureWriter *self,
SpCaptureWriter *dest,
GError **error);
gboolean _sp_capture_writer_splice_from_fd (SpCaptureWriter *self,
int fd,
GError **error) G_GNUC_INTERNAL;
#ifndef SP_DISABLE_GOBJECT
# define SP_TYPE_CAPTURE_WRITER (sp_capture_writer_get_type())
GType sp_capture_writer_get_type (void);
#endif
#if GLIB_CHECK_VERSION(2, 44, 0)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpCaptureWriter, sp_capture_writer_unref)
#endif
G_END_DECLS
#endif /* SP_CAPTURE_WRITER_H */

View File

@ -1,52 +0,0 @@
/* sp-clock.c
*
* Copyright © 2016 Christian Hergert <chergert@redhat.com>
*
* This file is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This file 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 Lesser 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/>.
*/
#include "sp-clock.h"
gint sp_clock = -1;
void
sp_clock_init (void)
{
static const gint clock_ids[] = {
CLOCK_MONOTONIC,
CLOCK_MONOTONIC_RAW,
CLOCK_MONOTONIC_COARSE,
CLOCK_REALTIME_COARSE,
CLOCK_REALTIME,
};
guint i;
if (sp_clock != -1)
return;
for (i = 0; i < G_N_ELEMENTS (clock_ids); i++)
{
struct timespec ts;
int clock_id = clock_ids [i];
if (0 == clock_gettime (clock_id, &ts))
{
sp_clock = clock_id;
return;
}
}
g_assert_not_reached ();
}

View File

@ -1,55 +0,0 @@
/* sp-clock.h
*
* Copyright © 2016 Christian Hergert <chergert@redhat.com>
*
* This file is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This file 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 Lesser 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/>.
*/
#ifndef SP_CLOCK_H
#define SP_CLOCK_H
#include <glib.h>
#include <time.h>
G_BEGIN_DECLS
typedef gint SpClock;
typedef gint64 SpTimeStamp;
typedef gint32 SpTimeSpan;
extern SpClock sp_clock;
static inline SpTimeStamp
sp_clock_get_current_time (void)
{
struct timespec ts;
clock_gettime (sp_clock, &ts);
return (ts.tv_sec * G_GINT64_CONSTANT (1000000000)) + ts.tv_nsec;
}
static inline SpTimeSpan
sp_clock_get_relative_time (SpTimeStamp epoch)
{
return sp_clock_get_current_time () - epoch;
}
void sp_clock_init (void);
G_END_DECLS
#endif /* SP_CLOCK_H */

View File

@ -213,8 +213,8 @@ gdk_pre_parse (void)
(GDebugKey *) gdk_debug_keys,
G_N_ELEMENTS (gdk_debug_keys));
if (g_getenv ("SYSPROF_TRACE_FD"))
gdk_profiler_start (atoi (g_getenv ("SYSPROF_TRACE_FD")));
if (g_getenv ("GTK_TRACE_FD"))
gdk_profiler_start (atoi (g_getenv ("GTK_TRACE_FD")));
else if (g_getenv ("GTK_TRACE"))
gdk_profiler_start (-1);
}

View File

@ -30,18 +30,18 @@
#include "gdkprofilerprivate.h"
#include "gdkframeclockprivate.h"
#ifndef G_OS_WIN32
#ifdef HAVE_SYSPROF_CAPTURE
#include "capture/sp-capture-writer.h"
#include <sysprof-capture.h>
static SpCaptureWriter *writer = NULL;
static SysprofCaptureWriter *writer = NULL;
static gboolean running = FALSE;
static void
profiler_stop (void)
{
if (writer)
sp_capture_writer_unref (writer);
sysprof_capture_writer_unref (writer);
}
void
@ -50,7 +50,7 @@ gdk_profiler_start (int fd)
if (writer)
return;
sp_clock_init ();
sysprof_clock_init ();
if (fd == -1)
{
@ -58,11 +58,11 @@ gdk_profiler_start (int fd)
filename = g_strdup_printf ("gtk.%d.syscap", getpid ());
g_print ("Writing profiling data to %s\n", filename);
writer = sp_capture_writer_new (filename, 16*1024);
writer = sysprof_capture_writer_new (filename, 16*1024);
g_free (filename);
}
else if (fd > 2)
writer = sp_capture_writer_new_from_fd (fd, 16*1024);
writer = sysprof_capture_writer_new_from_fd (fd, 16*1024);
if (writer)
running = TRUE;
@ -91,11 +91,11 @@ gdk_profiler_add_mark (gint64 start,
if (!running)
return;
sp_capture_writer_add_mark (writer,
start,
-1, getpid (),
duration,
"gtk", name, message);
sysprof_capture_writer_add_mark (writer,
start,
-1, getpid (),
duration,
"gtk", name, message);
}
static guint
@ -103,24 +103,24 @@ define_counter (const char *name,
const char *description,
int type)
{
SpCaptureCounter counter;
SysprofCaptureCounter counter;
if (!writer)
return 0;
counter.id = (guint) sp_capture_writer_request_counter (writer, 1);
counter.id = (guint) sysprof_capture_writer_request_counter (writer, 1);
counter.type = type;
counter.value.vdbl = 0;
g_strlcpy (counter.category, "gtk", sizeof counter.category);
g_strlcpy (counter.name, name, sizeof counter.name);
g_strlcpy (counter.description, description, sizeof counter.name);
sp_capture_writer_define_counters (writer,
SP_CAPTURE_CURRENT_TIME,
-1,
getpid (),
&counter,
1);
sysprof_capture_writer_define_counters (writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
getpid (),
&counter,
1);
return counter.id;
}
@ -129,14 +129,14 @@ guint
gdk_profiler_define_counter (const char *name,
const char *description)
{
return define_counter (name, description, SP_CAPTURE_COUNTER_DOUBLE);
return define_counter (name, description, SYSPROF_CAPTURE_COUNTER_DOUBLE);
}
guint
gdk_profiler_define_int_counter (const char *name,
const char *description)
{
return define_counter (name, description, SP_CAPTURE_COUNTER_INT64);
return define_counter (name, description, SYSPROF_CAPTURE_COUNTER_INT64);
}
void
@ -144,16 +144,16 @@ gdk_profiler_set_counter (guint id,
gint64 time,
double val)
{
SpCaptureCounterValue value;
SysprofCaptureCounterValue value;
if (!running)
return;
value.vdbl = val;
sp_capture_writer_set_counters (writer,
time,
-1, getpid (),
&id, &value, 1);
sysprof_capture_writer_set_counters (writer,
time,
-1, getpid (),
&id, &value, 1);
}
void
@ -161,16 +161,16 @@ gdk_profiler_set_int_counter (guint id,
gint64 time,
gint64 val)
{
SpCaptureCounterValue value;
SysprofCaptureCounterValue value;
if (!running)
return;
value.v64 = val;
sp_capture_writer_set_counters (writer,
time,
-1, getpid (),
&id, &value, 1);
sysprof_capture_writer_set_counters (writer,
time,
-1, getpid (),
&id, &value, 1);
}
#else

View File

@ -47,13 +47,6 @@ gdk_public_sources = files([
'gdkprofiler.c'
])
if not win32_enabled
gdk_public_sources += files([
'capture/sp-capture-writer.c',
'capture/sp-clock.c'
])
endif
gdk_public_headers = files([
'gdk-autocleanup.h',
'gdk.h',
@ -188,6 +181,12 @@ gdk_deps = [
vulkan_dep,
]
if profiler_enabled
if profiler_dep.found()
gdk_deps += [profiler_dep]
endif
endif
# add generated gdk sources
gdk_sources += [
gdkconfig,

View File

@ -666,6 +666,16 @@ if cloudproviders_enabled
endif
endif
profiler_enabled = get_option('profiler')
if profiler_enabled
profiler_dep = dependency('sysprof-capture-3', static: true, required: true)
if profiler_dep.found()
cdata.set('HAVE_SYSPROF_CAPTURE', profiler_dep.found())
else
error('Profiler support not found, but was explicitly requested.')
endif
endif
graphene_dep_type = graphene_dep.type_name()
if graphene_dep_type == 'pkgconfig'
graphene_has_sse2 = graphene_dep.get_pkgconfig_variable('graphene_has_sse2') == '1'
@ -811,6 +821,7 @@ summary = [
' Vulkan support: @0@'.format(have_vulkan),
' Cloud support: @0@'.format(get_option('cloudproviders')),
' Colord support: @0@'.format(get_option('colord')),
' Profiler: @0@'.format(get_option('profiler')),
' Introspection: @0@'.format(get_option('introspection')),
' Documentation: @0@'.format(get_option('documentation')),
' Man pages: @0@'.format(get_option('man-pages')),

View File

@ -21,6 +21,8 @@ option('xinerama', type: 'combo', choices : ['yes', 'no', 'auto'], value : 'auto
description : 'Enable support for the Xinerama extension')
option('cloudproviders', type: 'boolean', value: false,
description : 'Enable the cloudproviders support')
option('profiler', type: 'boolean', value: false,
description : 'Enable profiler support')
# Print backends
option('print-backends', type : 'string', value : 'cups,file',