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,