cogl: Add libsysprof capture based tracing

Add the ability to add tracing instrumentation to the code. When
enabled, trace entries will generate a file with timing information
that will be processable by sysprof for generating visualization of
traces over time.

While enabled by default at compile time, it is possible to disable the
expansion of the macros completely by passing --disable-tracing to
./configure.

Tracing is so far only actually done if actually enabled on explicitly
specified threads.

This will be used by Mutter passing the write end of a pipe, where the
read end is sent to Sysprof itself via the D-Bus method 'Capture()'.

By passing that, we have to detect EPIPE that is sent when Sysprof stops
recording. Fortunately, we already ignore the signal at meta_init(), so
no need to add a custom signal handler.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/197
This commit is contained in:
Jonas Ådahl 2018-05-15 16:31:29 +01:00 committed by Georges Basile Stavracas Neto
parent 1da0355528
commit e5e58f8075
No known key found for this signature in database
GPG Key ID: 886C17EE170D1385
8 changed files with 354 additions and 0 deletions

View File

@ -4,6 +4,9 @@
/* Have GLES 2.0 for rendering */ /* Have GLES 2.0 for rendering */
#mesondefine HAVE_COGL_GLES2 #mesondefine HAVE_COGL_GLES2
/* Building with Sysprof profiling suport */
#mesondefine HAVE_TRACING
/* Enable unit tests */ /* Enable unit tests */
#mesondefine ENABLE_UNIT_TESTS #mesondefine ENABLE_UNIT_TESTS

188
cogl/cogl/cogl-trace.c Normal file
View File

@ -0,0 +1,188 @@
/*
* Copyright 2018 Red Hat, Inc.
*
* 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 2 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/>.
*
*/
#include "cogl-config.h"
#ifdef HAVE_TRACING
#include "cogl/cogl-trace.h"
#include <sysprof-capture.h>
#include <syscall.h>
#include <sys/types.h>
#include <unistd.h>
#define COGL_TRACE_OUTPUT_FILE "cogl-trace-sp-capture.syscap"
#define BUFFER_LENGTH (4096 * 4)
static void cogl_trace_thread_context_free (gpointer data);
GPrivate cogl_trace_thread_data = G_PRIVATE_INIT (cogl_trace_thread_context_free);
CoglTraceContext *cogl_trace_context;
GMutex cogl_trace_mutex;
static CoglTraceContext *
cogl_trace_context_new (int fd)
{
CoglTraceContext *context;
SysprofCaptureWriter *writer;
g_debug ("Initializing trace context with fd=%d", fd);
if (fd == -1)
{
writer = sysprof_capture_writer_new (COGL_TRACE_OUTPUT_FILE,
BUFFER_LENGTH);
}
else
{
writer = sysprof_capture_writer_new_from_fd (fd, BUFFER_LENGTH);
}
context = g_new0 (CoglTraceContext, 1);
context->writer = writer;
return context;
}
static void
ensure_trace_context (int fd)
{
g_mutex_lock (&cogl_trace_mutex);
if (!cogl_trace_context)
cogl_trace_context = cogl_trace_context_new (fd);
g_mutex_unlock (&cogl_trace_mutex);
}
static CoglTraceThreadContext *
cogl_trace_thread_context_new (void)
{
CoglTraceThreadContext *thread_context;
pid_t tid;
tid = (pid_t) syscall (SYS_gettid);
thread_context = g_new0 (CoglTraceThreadContext, 1);
thread_context->cpu_id = -1;
thread_context->pid = getpid ();
thread_context->group = g_strdup_printf ("t:%d", tid);
return thread_context;
}
static gboolean
enable_tracing_idle_callback (gpointer user_data)
{
CoglTraceThreadContext *thread_context =
g_private_get (&cogl_trace_thread_data);
int fd = GPOINTER_TO_INT (user_data);
ensure_trace_context (fd);
if (thread_context)
{
g_warning ("Tracing already enabled");
return G_SOURCE_REMOVE;
}
thread_context = cogl_trace_thread_context_new ();
g_private_set (&cogl_trace_thread_data, thread_context);
return G_SOURCE_REMOVE;
}
static void
cogl_trace_thread_context_free (gpointer data)
{
CoglTraceThreadContext *thread_context = data;
if (!thread_context)
return;
g_free (thread_context->group);
g_free (thread_context);
}
static gboolean
disable_tracing_idle_callback (gpointer user_data)
{
CoglTraceThreadContext *thread_context =
g_private_get (&cogl_trace_thread_data);
CoglTraceContext *trace_context;
if (!thread_context)
{
g_warning ("Tracing not enabled");
return G_SOURCE_REMOVE;
}
g_private_replace (&cogl_trace_thread_data, NULL);
g_mutex_lock (&cogl_trace_mutex);
trace_context = cogl_trace_context;
sysprof_capture_writer_flush (trace_context->writer);
g_mutex_unlock (&cogl_trace_mutex);
return G_SOURCE_REMOVE;
}
void
cogl_set_tracing_enabled_on_thread (GMainContext *main_context,
int fd)
{
GSource *source;
source = g_idle_source_new ();
g_source_set_callback (source, enable_tracing_idle_callback, GINT_TO_POINTER (fd), NULL);
g_source_attach (source, main_context);
g_source_unref (source);
}
void
cogl_set_tracing_disabled_on_thread (GMainContext *main_context)
{
GSource *source;
source = g_idle_source_new ();
g_source_set_callback (source, disable_tracing_idle_callback, NULL, NULL);
g_source_attach (source, main_context);
g_source_unref (source);
}
#else
#include <string.h>
#include <stdio.h>
void
cogl_set_tracing_enabled_on_thread (void *data,
int fd)
{
fprintf (stderr, "Tracing not enabled");
}
void
cogl_set_tracing_disabled_on_thread (void *data)
{
fprintf (stderr, "Tracing not enabled");
}
#endif /* HAVE_TRACING */

138
cogl/cogl/cogl-trace.h Normal file
View File

@ -0,0 +1,138 @@
/*
* Copyright 2018 Red Hat, Inc.
*
* 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 2 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/>.
*
*/
#ifndef COGL_TRACE_H
#define COGL_TRACE_H
#include "cogl-config.h"
#ifdef HAVE_TRACING
#include <glib.h>
#include <sysprof-capture-writer.h>
#include <sysprof-clock.h>
#include <stdint.h>
#include <errno.h>
typedef struct _CoglTraceContext
{
SysprofCaptureWriter *writer;
} CoglTraceContext;
typedef struct _CoglTraceThreadContext
{
int cpu_id;
GPid pid;
char *group;
} CoglTraceThreadContext;
typedef struct _CoglTraceHead
{
SysprofTimeStamp begin_time;
const char *name;
} CoglTraceHead;
extern GPrivate cogl_trace_thread_data;
extern CoglTraceContext *cogl_trace_context;
extern GMutex cogl_trace_mutex;
void cogl_set_tracing_enabled_on_thread (GMainContext *main_context,
int fd);
void cogl_set_tracing_disabled_on_thread (GMainContext *main_context);
static inline void
cogl_trace_begin (CoglTraceHead *head,
const char *name)
{
head->begin_time = g_get_monotonic_time () * 1000;
head->name = name;
}
static inline void
cogl_trace_end (CoglTraceHead *head)
{
SysprofTimeStamp end_time;
CoglTraceContext *trace_context;
CoglTraceThreadContext *trace_thread_context;
end_time = g_get_monotonic_time () * 1000;
trace_context = cogl_trace_context;
trace_thread_context = g_private_get (&cogl_trace_thread_data);
g_mutex_lock (&cogl_trace_mutex);
if (!sysprof_capture_writer_add_mark (trace_context->writer,
head->begin_time,
trace_thread_context->cpu_id,
trace_thread_context->pid,
(uint64_t) end_time - head->begin_time,
trace_thread_context->group,
head->name,
NULL))
{
/* XXX: g_main_context_get_thread_default() might be wrong, it probably
* needs to store the GMainContext in CoglTraceThreadContext when creating
* and use it here.
*/
if (errno == EPIPE)
cogl_set_tracing_disabled_on_thread (g_main_context_get_thread_default ());
}
g_mutex_unlock (&cogl_trace_mutex);
}
static inline void
cogl_auto_trace_end_helper (CoglTraceHead **head)
{
if (*head)
cogl_trace_end (*head);
}
#define COGL_TRACE_BEGIN(Name) \
CoglTraceHead CoglTrace##Name = { 0 }; \
if (g_private_get (&cogl_trace_thread_data)) \
cogl_trace_begin (&CoglTrace##Name, #Name); \
#define COGL_TRACE_END(Name)\
if (g_private_get (&cogl_trace_thread_data)) \
cogl_trace_end (&CoglTrace##Name);
#define COGL_TRACE_BEGIN_SCOPED(Name) \
CoglTraceHead CoglTrace##Name = { 0 }; \
__attribute__((cleanup (cogl_auto_trace_end_helper))) \
CoglTraceHead *ScopedCoglTrace##Name = NULL; \
if (g_private_get (&cogl_trace_thread_data)) \
{ \
cogl_trace_begin (&CoglTrace##Name, #Name); \
ScopedCoglTrace##Name = &CoglTrace##Name; \
}
#else /* HAVE_TRACING */
#include <stdio.h>
#define COGL_TRACE_BEGIN(Name) (void) 0
#define COGL_TRACE_END(Name) (void) 0
#define COGL_TRACE_BEGIN_SCOPED(Name) (void) 0
void cogl_set_tracing_enabled_on_thread (void *data, int fd);
void cogl_set_tracing_disabled_on_thread (void *data);
#endif /* HAVE_TRACING */
#endif /* COGL_TRACE_H */

View File

@ -303,6 +303,8 @@ cogl_sources = [
'cogl-blend-string.c', 'cogl-blend-string.c',
'cogl-blend-string.h', 'cogl-blend-string.h',
'cogl-debug.c', 'cogl-debug.c',
'cogl-trace.c',
'cogl-trace.h',
'cogl-sub-texture-private.h', 'cogl-sub-texture-private.h',
'cogl-texture-private.h', 'cogl-texture-private.h',
'cogl-texture-2d-private.h', 'cogl-texture-2d-private.h',

View File

@ -7,6 +7,7 @@ cogl_includepath = include_directories('.', 'cogl')
cdata = configuration_data() cdata = configuration_data()
cdata.set('HAVE_COGL_GL', have_gl) cdata.set('HAVE_COGL_GL', have_gl)
cdata.set('HAVE_COGL_GLES2', have_gles2) cdata.set('HAVE_COGL_GLES2', have_gles2)
cdata.set('HAVE_TRACING', have_profiler)
cdata.set('ENABLE_UNIT_TESTS', have_cogl_tests) cdata.set('ENABLE_UNIT_TESTS', have_cogl_tests)
cogl_config_h = configure_file( cogl_config_h = configure_file(
@ -26,6 +27,12 @@ cogl_pkg_private_deps = [
#uprof_dep, #uprof_dep,
] ]
if have_profiler
cogl_pkg_private_deps += [
sysprof_dep,
]
endif
if have_wayland if have_wayland
cogl_pkg_deps += [ cogl_pkg_deps += [
wayland_server_dep, wayland_server_dep,

View File

@ -49,6 +49,9 @@
/* Building with startup notification support */ /* Building with startup notification support */
#mesondefine HAVE_STARTUP_NOTIFICATION #mesondefine HAVE_STARTUP_NOTIFICATION
/* Building with Sysprof profiling suport */
#mesondefine HAVE_TRACING
/* Path to Xwayland executable */ /* Path to Xwayland executable */
#mesondefine XWAYLAND_PATH #mesondefine XWAYLAND_PATH

View File

@ -264,6 +264,11 @@ if have_tests
have_installed_tests = get_option('installed_tests') have_installed_tests = get_option('installed_tests')
endif endif
have_profiler = get_option('profiler')
if have_profiler
sysprof_dep = dependency('sysprof-capture-3')
endif
required_functions = [ required_functions = [
'ffs', 'ffs',
'clz', 'clz',
@ -346,6 +351,7 @@ cdata.set('HAVE_LIBWACOM', have_libwacom)
cdata.set('HAVE_SM', have_sm) cdata.set('HAVE_SM', have_sm)
cdata.set('HAVE_STARTUP_NOTIFICATION', have_startup_notification) cdata.set('HAVE_STARTUP_NOTIFICATION', have_startup_notification)
cdata.set('HAVE_INTROSPECTION', have_introspection) cdata.set('HAVE_INTROSPECTION', have_introspection)
cdata.set('HAVE_TRACING', have_profiler)
xkb_base = xkeyboard_config_dep.get_pkgconfig_variable('xkb_base') xkb_base = xkeyboard_config_dep.get_pkgconfig_variable('xkb_base')
cdata.set_quoted('XKB_BASE', xkb_base) cdata.set_quoted('XKB_BASE', xkb_base)
@ -411,6 +417,7 @@ output = [
' SM....................... ' + have_sm.to_string(), ' SM....................... ' + have_sm.to_string(),
' Startup notification..... ' + have_startup_notification.to_string(), ' Startup notification..... ' + have_startup_notification.to_string(),
' Introspection............ ' + have_introspection.to_string(), ' Introspection............ ' + have_introspection.to_string(),
' Profiler................. ' + have_profiler.to_string(),
'', '',
' Tests:', ' Tests:',
'', '',

View File

@ -123,6 +123,12 @@ option('tests',
description: 'Enable tests globally. Specific test suites can be controlled with core_tests, clutter_tests, and cogl_tests' description: 'Enable tests globally. Specific test suites can be controlled with core_tests, clutter_tests, and cogl_tests'
) )
option('profiler',
type: 'boolean',
value: true,
description: 'Enable Sysprof tracing'
)
option('installed_tests', option('installed_tests',
type: 'boolean', type: 'boolean',
value: true, value: true,