diff --git a/cogl/cogl-config.h.meson b/cogl/cogl-config.h.meson
index 56946b299..f2d46b259 100644
--- a/cogl/cogl-config.h.meson
+++ b/cogl/cogl-config.h.meson
@@ -4,6 +4,9 @@
/* Have GLES 2.0 for rendering */
#mesondefine HAVE_COGL_GLES2
+/* Building with Sysprof profiling suport */
+#mesondefine HAVE_TRACING
+
/* Enable unit tests */
#mesondefine ENABLE_UNIT_TESTS
diff --git a/cogl/cogl/cogl-trace.c b/cogl/cogl/cogl-trace.c
new file mode 100644
index 000000000..1e2d3abcf
--- /dev/null
+++ b/cogl/cogl/cogl-trace.c
@@ -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 .
+ *
+ */
+
+#include "cogl-config.h"
+
+#ifdef HAVE_TRACING
+
+#include "cogl/cogl-trace.h"
+
+#include
+#include
+#include
+#include
+
+#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
+#include
+
+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 */
diff --git a/cogl/cogl/cogl-trace.h b/cogl/cogl/cogl-trace.h
new file mode 100644
index 000000000..ffcd4d240
--- /dev/null
+++ b/cogl/cogl/cogl-trace.h
@@ -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 .
+ *
+ */
+
+#ifndef COGL_TRACE_H
+#define COGL_TRACE_H
+
+#include "cogl-config.h"
+
+#ifdef HAVE_TRACING
+
+#include
+#include
+#include
+#include
+#include
+
+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
+
+#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 */
diff --git a/cogl/cogl/meson.build b/cogl/cogl/meson.build
index a89f6448e..f89b4fb7d 100644
--- a/cogl/cogl/meson.build
+++ b/cogl/cogl/meson.build
@@ -303,6 +303,8 @@ cogl_sources = [
'cogl-blend-string.c',
'cogl-blend-string.h',
'cogl-debug.c',
+ 'cogl-trace.c',
+ 'cogl-trace.h',
'cogl-sub-texture-private.h',
'cogl-texture-private.h',
'cogl-texture-2d-private.h',
diff --git a/cogl/meson.build b/cogl/meson.build
index 585e71697..1cd2092d5 100644
--- a/cogl/meson.build
+++ b/cogl/meson.build
@@ -7,6 +7,7 @@ cogl_includepath = include_directories('.', 'cogl')
cdata = configuration_data()
cdata.set('HAVE_COGL_GL', have_gl)
cdata.set('HAVE_COGL_GLES2', have_gles2)
+cdata.set('HAVE_TRACING', have_profiler)
cdata.set('ENABLE_UNIT_TESTS', have_cogl_tests)
cogl_config_h = configure_file(
@@ -26,6 +27,12 @@ cogl_pkg_private_deps = [
#uprof_dep,
]
+if have_profiler
+ cogl_pkg_private_deps += [
+ sysprof_dep,
+ ]
+endif
+
if have_wayland
cogl_pkg_deps += [
wayland_server_dep,
diff --git a/config.h.meson b/config.h.meson
index 70681d774..7f4f9e176 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -49,6 +49,9 @@
/* Building with startup notification support */
#mesondefine HAVE_STARTUP_NOTIFICATION
+/* Building with Sysprof profiling suport */
+#mesondefine HAVE_TRACING
+
/* Path to Xwayland executable */
#mesondefine XWAYLAND_PATH
diff --git a/meson.build b/meson.build
index a804ff897..d2a39632f 100644
--- a/meson.build
+++ b/meson.build
@@ -264,6 +264,11 @@ if have_tests
have_installed_tests = get_option('installed_tests')
endif
+have_profiler = get_option('profiler')
+if have_profiler
+ sysprof_dep = dependency('sysprof-capture-3')
+endif
+
required_functions = [
'ffs',
'clz',
@@ -346,6 +351,7 @@ cdata.set('HAVE_LIBWACOM', have_libwacom)
cdata.set('HAVE_SM', have_sm)
cdata.set('HAVE_STARTUP_NOTIFICATION', have_startup_notification)
cdata.set('HAVE_INTROSPECTION', have_introspection)
+cdata.set('HAVE_TRACING', have_profiler)
xkb_base = xkeyboard_config_dep.get_pkgconfig_variable('xkb_base')
cdata.set_quoted('XKB_BASE', xkb_base)
@@ -411,6 +417,7 @@ output = [
' SM....................... ' + have_sm.to_string(),
' Startup notification..... ' + have_startup_notification.to_string(),
' Introspection............ ' + have_introspection.to_string(),
+ ' Profiler................. ' + have_profiler.to_string(),
'',
' Tests:',
'',
diff --git a/meson_options.txt b/meson_options.txt
index 1e122300e..73aa7adde 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -123,6 +123,12 @@ option('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',
type: 'boolean',
value: true,