[gtk/wip/chergert/gdkprofiler-gtk3] gdk: backport GdkProfiler



commit ef67eb0b3cc76f54c328bcf6dceafc9a4571a212
Author: Christian Hergert <chergert redhat com>
Date:   Thu Jun 6 16:19:36 2019 -0700

    gdk: backport GdkProfiler
    
    This is a backport of the GdkProfiler from master. It does not include
    the pixel bandwidth numbers that come from gdkdrawcontext.c since there
    does not seem to be an analog in 3.x.
    
    Additionally, this implements the recent changes for SYsprof's D-Bus
    profiler API which adds a Capabilities property and an options hash-table
    to the D-Bus interface for forward portability.

 config.h.meson                  |   3 +
 configure.ac                    |  24 ++++-
 gdk/Makefile.am                 |   2 +
 gdk/gdk-private.c               |   4 +
 gdk/gdk-private.h               |   4 +
 gdk/gdk.c                       |   5 +
 gdk/gdkframeclock.c             | 112 +++++++++++++++++++
 gdk/gdkframeclockidle.c         |  32 +++++-
 gdk/gdkframeclockprivate.h      |   2 +
 gdk/gdkprofiler.c               | 230 ++++++++++++++++++++++++++++++++++++++++
 gdk/gdkprofilerprivate.h        |  46 ++++++++
 gdk/meson.build                 |   7 ++
 gdk/wayland/gdkwindow-wayland.c |   4 +
 gdk/x11/gdkdisplay-x11.c        |   4 +
 gtk/gtkapplication.c            | 155 +++++++++++++++++++++++++++
 meson.build                     |  11 ++
 meson_options.txt               |   2 +
 17 files changed, 643 insertions(+), 4 deletions(-)
---
diff --git a/config.h.meson b/config.h.meson
index ff17836eca..d929f00c14 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -149,6 +149,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
 
diff --git a/configure.ac b/configure.ac
index afb02faea6..323ca92125 100644
--- a/configure.ac
+++ b/configure.ac
@@ -66,6 +66,7 @@ m4_define([mirclient_required_version], [0.22.0])
 m4_define([mircookie_required_version], [0.17.0])
 m4_define([epoxy_required_version], [1.4])
 m4_define([cloudproviders_required_version], [0.2.5])
+m4_define([sysprof_required_version], [3.33.2])
 GLIB_REQUIRED_VERSION=glib_required_version
 PANGO_REQUIRED_VERSION=pango_required_version
 ATK_REQUIRED_VERSION=atk_required_version
@@ -353,6 +354,11 @@ AC_ARG_ENABLE(cloudproviders,
                               [enable libcloudproviders integration])],
                               [cloudproviders_set=yes])
 
+AC_ARG_ENABLE(profiler,
+              [AS_HELP_STRING([--enable-profiler],
+                              [enable profiler integration])],
+                              [profiler_set=yes])
+
 if test -z "$backend_set"; then
   if test "$platform_win32" = yes; then
     enable_win32_backend=yes
@@ -1334,11 +1340,26 @@ if test "x$cloudproviders_set" = "xyes"; then
   fi
 fi
 
+# Check for profiler support
+
+PROFILER_PACKAGES=""
+if test "x$profiler_set" = "xyes"; then
+  PROFILER_PACKAGES="sysprof-capture-3 >= sysprof_required_version"
+  if $PKG_CONFIG --exists $PROFILER_PACKAGES; then
+    AC_DEFINE(HAVE_SYSPROF_CAPTURE, [1],
+              [Define if sysprof-capture-3 is available]
+              )
+  else
+    AC_MSG_ERROR([
+*** sysprof-capture-3 not found.])
+  fi
+fi
+
 CFLAGS="$saved_cflags"
 LDFLAGS="$saved_ldflags"
 
 GDK_PACKAGES="$PANGO_PACKAGES gdk-pixbuf-2.0 >= gdk_pixbuf_required_version cairo >= cairo_required_version 
cairo-gobject >= cairo_required_version"
-GDK_PRIVATE_PACKAGES="$GDK_GIO_PACKAGE $X_PACKAGES $WAYLAND_PACKAGES $MIR_PACKAGES $cairo_backends epoxy >= 
epoxy_required_version $CLOUDPROVIDER_PACKAGES fribidi >= fribidi_required_version"
+GDK_PRIVATE_PACKAGES="$GDK_GIO_PACKAGE $X_PACKAGES $WAYLAND_PACKAGES $MIR_PACKAGES $cairo_backends epoxy >= 
epoxy_required_version $CLOUDPROVIDER_PACKAGES $PROFILER_PACKAGES fribidi >= fribidi_required_version"
 
 PKG_CHECK_MODULES(GDK_DEP, $GDK_PACKAGES $GDK_PRIVATE_PACKAGES)
 GDK_DEP_LIBS="$GDK_EXTRA_LIBS $GDK_DEP_LIBS $MATH_LIB"
@@ -2012,3 +2033,4 @@ echo "        colord support:       $have_colord"
 echo "        Introspection:        $found_introspection"
 echo "        Debugging:            $enable_debug"
 echo "        Documentation:        $enable_gtk_doc"
+echo "        Profiler:             $enable_profiler"
diff --git a/gdk/Makefile.am b/gdk/Makefile.am
index b1083fe837..6373e26a26 100644
--- a/gdk/Makefile.am
+++ b/gdk/Makefile.am
@@ -129,6 +129,7 @@ gdk_private_headers =                               \
        gdkframeclockprivate.h                  \
        gdkglcontextprivate.h                   \
        gdkmonitorprivate.h                     \
+       gdkprofilerprivate.h                    \
        gdkscreenprivate.h                      \
        gdkseatprivate.h                        \
        gdkseatdefaultprivate.h                 \
@@ -170,6 +171,7 @@ gdk_c_sources =                             \
        gdkframeclockidle.c                     \
        gdkpango.c                              \
        gdkpixbuf-drawable.c                    \
+       gdkprofiler.c                           \
        gdkproperty.c                           \
        gdkrectangle.c                          \
        gdkrgba.c                               \
diff --git a/gdk/gdk-private.c b/gdk/gdk-private.c
index cf679b0c7e..750edcd374 100644
--- a/gdk/gdk-private.c
+++ b/gdk/gdk-private.c
@@ -1,5 +1,6 @@
 #include "config.h"
 #include "gdk-private.h"
+#include "gdkprofilerprivate.h"
 
 GdkPrivateVTable *
 gdk__private__ (void)
@@ -19,6 +20,9 @@ gdk__private__ (void)
     gdk_display_set_debug_updates,
     gdk_get_desktop_startup_id,
     gdk_get_desktop_autostart_id,
+    gdk_profiler_is_running,
+    gdk_profiler_start,
+    gdk_profiler_stop
   };
 
   return &table;
diff --git a/gdk/gdk-private.h b/gdk/gdk-private.h
index e38d7b1548..c71abe4634 100644
--- a/gdk/gdk-private.h
+++ b/gdk/gdk-private.h
@@ -62,6 +62,10 @@ typedef struct {
 
   const gchar * (* gdk_get_desktop_startup_id)   (void);
   const gchar * (* gdk_get_desktop_autostart_id) (void);
+
+  gboolean (* gdk_profiler_is_running) (void);
+  void     (* gdk_profiler_start)      (int fd);
+  void     (* gdk_profiler_stop)       (void);
 } GdkPrivateVTable;
 
 GDK_AVAILABLE_IN_ALL
diff --git a/gdk/gdk.c b/gdk/gdk.c
index 38da23aa21..12a1d1c585 100644
--- a/gdk/gdk.c
+++ b/gdk/gdk.c
@@ -27,6 +27,7 @@
 #include "gdkversionmacros.h"
 #include "gdkmain.h"
 
+#include "gdkprofilerprivate.h"
 #include "gdkinternals.h"
 #include "gdkintl.h"
 
@@ -315,6 +316,10 @@ gdk_pre_parse (void)
       _gdk_debug_flags = g_parse_debug_string (debug_string,
                                               (GDebugKey *) gdk_debug_keys,
                                               G_N_ELEMENTS (gdk_debug_keys));
+    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);
   }
 #endif  /* G_ENABLE_DEBUG */
 
diff --git a/gdk/gdkframeclock.c b/gdk/gdkframeclock.c
index 99f93838fc..a482354d7e 100644
--- a/gdk/gdkframeclock.c
+++ b/gdk/gdkframeclock.c
@@ -26,6 +26,7 @@
 
 #include "gdkframeclockprivate.h"
 #include "gdkinternals.h"
+#include "gdkprofilerprivate.h"
 
 /**
  * SECTION:gdkframeclock
@@ -80,6 +81,10 @@ enum {
 
 static guint signals[LAST_SIGNAL];
 
+#ifdef G_ENABLE_DEBUG
+static guint fps_counter;
+#endif
+
 #define FRAME_HISTORY_MAX_LENGTH 16
 
 struct _GdkFrameClockPrivate
@@ -238,6 +243,11 @@ gdk_frame_clock_init (GdkFrameClock *clock)
 
   priv->frame_counter = -1;
   priv->current = FRAME_HISTORY_MAX_LENGTH - 1;
+
+#ifdef G_ENABLE_DEBUG
+  if (fps_counter == 0)
+    fps_counter = gdk_profiler_define_counter ("fps", "Frames per Second");
+#endif
 }
 
 /**
@@ -644,3 +654,105 @@ _gdk_frame_clock_emit_resume_events (GdkFrameClock *frame_clock)
 {
   g_signal_emit (frame_clock, signals[RESUME_EVENTS], 0);
 }
+
+#ifdef G_ENABLE_DEBUG
+static gint64
+guess_refresh_interval (GdkFrameClock *frame_clock)
+{
+  gint64 interval;
+  gint64 i;
+
+  interval = G_MAXINT64;
+
+  for (i = gdk_frame_clock_get_history_start (frame_clock);
+       i < gdk_frame_clock_get_frame_counter (frame_clock);
+       i++)
+    {
+      GdkFrameTimings *t, *before;
+      gint64 ts, before_ts;
+
+      t = gdk_frame_clock_get_timings (frame_clock, i);
+      before = gdk_frame_clock_get_timings (frame_clock, i - 1);
+      if (t == NULL || before == NULL)
+        continue;
+
+      ts = gdk_frame_timings_get_frame_time (t);
+      before_ts = gdk_frame_timings_get_frame_time (before);
+      if (ts == 0 || before_ts == 0)
+        continue;
+
+      interval = MIN (interval, ts - before_ts);
+    }
+
+  if (interval == G_MAXINT64)
+    return 0;
+
+  return interval;
+}
+
+static double
+frame_clock_get_fps (GdkFrameClock *frame_clock)
+{
+  GdkFrameTimings *start, *end;
+  gint64 start_counter, end_counter;
+  gint64 start_timestamp, end_timestamp;
+  gint64 interval;
+
+  start_counter = gdk_frame_clock_get_history_start (frame_clock);
+  end_counter = gdk_frame_clock_get_frame_counter (frame_clock);
+  start = gdk_frame_clock_get_timings (frame_clock, start_counter);
+  for (end = gdk_frame_clock_get_timings (frame_clock, end_counter);
+       end_counter > start_counter && end != NULL && !gdk_frame_timings_get_complete (end);
+       end = gdk_frame_clock_get_timings (frame_clock, end_counter))
+    end_counter--;
+  if (end_counter - start_counter < 4)
+    return 0.0;
+
+  start_timestamp = gdk_frame_timings_get_presentation_time (start);
+  end_timestamp = gdk_frame_timings_get_presentation_time (end);
+  if (start_timestamp == 0 || end_timestamp == 0)
+    {
+      start_timestamp = gdk_frame_timings_get_frame_time (start);
+      end_timestamp = gdk_frame_timings_get_frame_time (end);
+    }
+  interval = gdk_frame_timings_get_refresh_interval (end);
+  if (interval == 0)
+    {
+      interval = guess_refresh_interval (frame_clock);
+      if (interval == 0)
+        return 0.0;
+    }   
+    
+  return ((double) end_counter - start_counter) * G_USEC_PER_SEC / (end_timestamp - start_timestamp);
+}
+#endif
+
+void
+_gdk_frame_clock_add_timings_to_profiler (GdkFrameClock   *clock,
+                                          GdkFrameTimings *timings)
+{
+#ifdef G_ENABLE_DEBUG
+  gdk_profiler_add_mark (timings->frame_time * 1000,
+                         (timings->frame_end_time - timings->frame_time) * 1000,
+                         "frame", "");
+
+  if (timings->layout_start_time != 0)
+    gdk_profiler_add_mark (timings->layout_start_time * 1000,
+                           (timings->paint_start_time - timings->layout_start_time) * 1000,
+                            "layout", "");
+
+  if (timings->paint_start_time != 0)
+    gdk_profiler_add_mark (timings->paint_start_time * 1000,
+                           (timings->frame_end_time - timings->paint_start_time) * 1000,
+                            "paint", "");
+
+  if (timings->presentation_time != 0)
+    gdk_profiler_add_mark (timings->presentation_time * 1000,
+                           0,
+                           "presentation", "");
+
+  gdk_profiler_set_counter (fps_counter,
+                            timings->frame_end_time * 1000,
+                            frame_clock_get_fps (clock));
+#endif
+}
diff --git a/gdk/gdkframeclockidle.c b/gdk/gdkframeclockidle.c
index 2ad7da6ddd..8f17d7596f 100644
--- a/gdk/gdkframeclockidle.c
+++ b/gdk/gdkframeclockidle.c
@@ -27,6 +27,7 @@
 #include "gdkinternals.h"
 #include "gdkframeclockprivate.h"
 #include "gdkframeclockidle.h"
+#include "gdkprofilerprivate.h"
 #include "gdk.h"
 
 #ifdef G_OS_WIN32
@@ -40,6 +41,9 @@ struct _GdkFrameClockIdlePrivate
   gint64 frame_time;
   gint64 min_next_frame_time;
   gint64 sleep_serial;
+#ifdef G_ENABLE_DEBUG
+  gint64 freeze_time;
+#endif
 
   guint flush_idle_id;
   guint paint_idle_id;
@@ -402,7 +406,7 @@ gdk_frame_clock_paint_idle (void *data)
             {
              int iter;
 #ifdef G_ENABLE_DEBUG
-              if (GDK_DEBUG_CHECK (FRAMES))
+              if (GDK_DEBUG_CHECK (FRAMES) || gdk_profiler_is_running ())
                 {
                   if (priv->phase != GDK_FRAME_CLOCK_PHASE_LAYOUT &&
                       (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT))
@@ -431,7 +435,7 @@ gdk_frame_clock_paint_idle (void *data)
           if (priv->freeze_count == 0)
             {
 #ifdef G_ENABLE_DEBUG
-              if (GDK_DEBUG_CHECK (FRAMES))
+              if (GDK_DEBUG_CHECK (FRAMES) || gdk_profiler_is_running ())
                 {
                   if (priv->phase != GDK_FRAME_CLOCK_PHASE_PAINT &&
                       (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT))
@@ -457,7 +461,7 @@ gdk_frame_clock_paint_idle (void *data)
               priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
 
 #ifdef G_ENABLE_DEBUG
-              if (GDK_DEBUG_CHECK (FRAMES))
+              if (GDK_DEBUG_CHECK (FRAMES) || gdk_profiler_is_running ())
                 timings->frame_end_time = g_get_monotonic_time ();
 #endif /* G_ENABLE_DEBUG */
             }
@@ -559,6 +563,14 @@ gdk_frame_clock_idle_freeze (GdkFrameClock *clock)
   GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
 
+#ifdef G_ENABLE_DEBUG
+  if (priv->freeze_count == 0)
+    {
+      if (gdk_profiler_is_running ())
+        priv->freeze_time = g_get_monotonic_time ();
+    }
+#endif
+
   priv->freeze_count++;
   maybe_stop_idle (clock_idle);
 }
@@ -583,6 +595,20 @@ gdk_frame_clock_idle_thaw (GdkFrameClock *clock)
         priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
 
       priv->sleep_serial = get_sleep_serial ();
+
+#ifdef G_ENABLE_DEBUG
+      if (gdk_profiler_is_running ())
+        {
+          if (priv->freeze_time != 0)
+            {
+              gint64 thaw_time = g_get_monotonic_time ();
+              gdk_profiler_add_mark (priv->freeze_time * 1000,
+                                     (thaw_time - priv->freeze_time) * 1000,
+                                     "freeze", "");
+              priv->freeze_time = 0;
+            }
+        }
+#endif
     }
 }
 
diff --git a/gdk/gdkframeclockprivate.h b/gdk/gdkframeclockprivate.h
index 17e06dfcf7..d568887ba3 100644
--- a/gdk/gdkframeclockprivate.h
+++ b/gdk/gdkframeclockprivate.h
@@ -110,6 +110,8 @@ void _gdk_frame_clock_thaw   (GdkFrameClock *clock);
 void _gdk_frame_clock_begin_frame         (GdkFrameClock   *clock);
 void _gdk_frame_clock_debug_print_timings (GdkFrameClock   *clock,
                                            GdkFrameTimings *timings);
+void _gdk_frame_clock_add_timings_to_profiler (GdkFrameClock *frame_clock,
+                                               GdkFrameTimings *timings);
 
 GdkFrameTimings *_gdk_frame_timings_new   (gint64           frame_counter);
 gboolean         _gdk_frame_timings_steal (GdkFrameTimings *timings,
diff --git a/gdk/gdkprofiler.c b/gdk/gdkprofiler.c
new file mode 100644
index 0000000000..93055ac9a8
--- /dev/null
+++ b/gdk/gdkprofiler.c
@@ -0,0 +1,230 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkprofiler.c: A simple profiler
+ *
+ * Copyright © 2018 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "gdkversionmacros.h"
+#include "gdkprofilerprivate.h"
+#include "gdkframeclockprivate.h"
+
+#ifdef HAVE_SYSPROF_CAPTURE
+
+#include <sysprof-capture.h>
+
+static SysprofCaptureWriter *writer = NULL;
+static gboolean running = FALSE;
+
+static void
+profiler_stop (void)
+{
+  if (writer)
+    sysprof_capture_writer_unref (writer);
+}
+
+void
+gdk_profiler_start (int fd)
+{
+  if (writer)
+    return;
+
+  sysprof_clock_init ();
+
+  if (fd == -1)
+    {
+      gchar *filename;
+
+      filename = g_strdup_printf ("gtk.%d.syscap", getpid ());
+      g_print ("Writing profiling data to %s\n", filename);
+      writer = sysprof_capture_writer_new (filename, 16*1024);
+      g_free (filename);
+    }
+  else if (fd > 2)
+    writer = sysprof_capture_writer_new_from_fd (fd, 16*1024);
+
+  if (writer)
+    running = TRUE;
+
+  atexit (profiler_stop);
+}
+
+void
+gdk_profiler_stop (void)
+{
+  running = FALSE;
+}
+
+gboolean
+gdk_profiler_is_running (void)
+{
+  return running;
+}
+
+void
+gdk_profiler_add_mark (gint64      start,
+                       guint64     duration,
+                       const char *name,
+                       const char *message)
+{
+  if (!running)
+    return;
+
+  sysprof_capture_writer_add_mark (writer,
+                                   start,
+                                   -1, getpid (),
+                                   duration,
+                                   "gtk", name, message);
+}
+
+static guint
+define_counter (const char *name,
+                const char *description,
+                int         type)
+{
+  SysprofCaptureCounter counter;
+
+  if (!writer)
+    return 0;
+
+  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);
+
+  sysprof_capture_writer_define_counters (writer,
+                                          SYSPROF_CAPTURE_CURRENT_TIME,
+                                          -1,
+                                          getpid (),
+                                          &counter,
+                                          1);
+
+  return counter.id;
+}
+
+guint
+gdk_profiler_define_counter (const char *name,
+                             const char *description)
+{
+  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, SYSPROF_CAPTURE_COUNTER_INT64);
+}
+
+void
+gdk_profiler_set_counter (guint  id,
+                          gint64 time,
+                          double val)
+{
+  SysprofCaptureCounterValue value;
+
+  if (!running)
+    return;
+
+  value.vdbl = val;
+  sysprof_capture_writer_set_counters (writer,
+                                       time,
+                                       -1, getpid (),
+                                       &id, &value, 1);
+}
+
+void
+gdk_profiler_set_int_counter (guint  id,
+                              gint64 time,
+                              gint64 val)
+{
+  SysprofCaptureCounterValue value;
+
+  if (!running)
+    return;
+
+  value.v64 = val;
+  sysprof_capture_writer_set_counters (writer,
+                                       time,
+                                       -1, getpid (),
+                                       &id, &value, 1);
+}
+
+#else
+
+void
+gdk_profiler_start (int fd)
+{
+}
+
+void
+gdk_profiler_stop (void)
+{
+}
+
+gboolean
+gdk_profiler_is_running (void)
+{
+  return FALSE;
+}
+
+void
+gdk_profiler_add_mark (gint64      start,
+                       guint64     duration,
+                       const char *name,
+                       const char *message)
+{
+}
+
+guint
+gdk_profiler_define_counter (const char *name,
+                             const char *description)
+{
+ return 0;
+}
+
+void
+gdk_profiler_set_counter (guint  id,
+                          gint64 time,
+                          double value)
+{
+}
+
+guint
+gdk_profiler_define_int_counter (const char *name,
+                                 const char *description)
+{
+  return 0;
+}
+
+void
+gdk_profiler_set_int_counter (guint  id,
+                              gint64 time,
+                              gint64 value)
+{
+}
+
+#endif /* G_OS_WIN32 */
diff --git a/gdk/gdkprofilerprivate.h b/gdk/gdkprofilerprivate.h
new file mode 100644
index 0000000000..b4d81e40c4
--- /dev/null
+++ b/gdk/gdkprofilerprivate.h
@@ -0,0 +1,46 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This library 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 library 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 Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_PROFILER_PRIVATE_H__
+#define __GDK_PROFILER_PRIVATE_H__
+
+#include "gdk/gdkframeclock.h"
+#include "gdk/gdkdisplay.h"
+
+G_BEGIN_DECLS
+
+void     gdk_profiler_start              (int         fd);
+void     gdk_profiler_stop               (void);
+gboolean gdk_profiler_is_running         (void);
+void     gdk_profiler_add_mark           (gint64      start,
+                                          guint64     duration,
+                                          const char *name,
+                                          const char *message);
+guint    gdk_profiler_define_counter     (const char *name,
+                                          const char *description);
+void     gdk_profiler_set_counter        (guint       id,
+                                          gint64      time,
+                                          double      value);
+guint    gdk_profiler_define_int_counter (const char *name,
+                                          const char *description);
+void     gdk_profiler_set_int_counter    (guint       id,
+                                          gint64      time,
+                                          gint64      value);
+
+G_END_DECLS
+
+#endif  /* __GDK_PROFILER_PRIVATE_H__ */
diff --git a/gdk/meson.build b/gdk/meson.build
index 40725b25a6..49df660482 100644
--- a/gdk/meson.build
+++ b/gdk/meson.build
@@ -24,6 +24,7 @@ gdk_sources = files(
   'gdkframeclockidle.c',
   'gdkpango.c',
   'gdkpixbuf-drawable.c',
+  'gdkprofiler.c',
   'gdkproperty.c',
   'gdkrectangle.c',
   'gdkrgba.c',
@@ -226,6 +227,12 @@ if win32_enabled
   gdk_sources += gdk_res
 endif
 
+if profiler_enabled
+  if profiler_dep.found()
+    gdk_deps += [profiler_dep]
+  endif
+endif
+
 gdk_sources = [
   # Generated
   gdkconfig,
diff --git a/gdk/wayland/gdkwindow-wayland.c b/gdk/wayland/gdkwindow-wayland.c
index 3c26c2971b..1c62c8f65e 100644
--- a/gdk/wayland/gdkwindow-wayland.c
+++ b/gdk/wayland/gdkwindow-wayland.c
@@ -29,6 +29,7 @@
 #include "gdkglcontext-wayland.h"
 #include "gdkframeclockprivate.h"
 #include "gdkprivate-wayland.h"
+#include "gdkprofilerprivate.h"
 #include "gdkinternals.h"
 #include "gdkdeviceprivate.h"
 #include "gdkprivate-wayland.h"
@@ -560,6 +561,9 @@ frame_callback (void               *data,
 #ifdef G_ENABLE_DEBUG
   if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
     _gdk_frame_clock_debug_print_timings (clock, timings);
+
+  if (gdk_profiler_is_running ())
+    _gdk_frame_clock_add_timings_to_profiler (clock, timings);
 #endif
 }
 
diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c
index 5b95196a54..817944e3a5 100644
--- a/gdk/x11/gdkdisplay-x11.c
+++ b/gdk/x11/gdkdisplay-x11.c
@@ -39,6 +39,7 @@
 #include "gdkscreen-x11.h"
 #include "gdkglcontext-x11.h"
 #include "gdk-private.h"
+#include "gdkprofilerprivate.h"
 
 #include <glib.h>
 #include <glib/gprintf.h>
@@ -1426,6 +1427,9 @@ _gdk_wm_protocols_filter (GdkXEvent *xev,
 #ifdef G_ENABLE_DEBUG
               if (GDK_DEBUG_CHECK (FRAMES))
                 _gdk_frame_clock_debug_print_timings (clock, timings);
+
+              if (gdk_profiler_is_running ())
+                _gdk_frame_clock_add_timings_to_profiler (clock, timings);
 #endif /* G_ENABLE_DEBUG */
             }
         }
diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c
index bbd36b7953..3607b69f5e 100644
--- a/gtk/gtkapplication.c
+++ b/gtk/gtkapplication.c
@@ -21,6 +21,11 @@
 #include "config.h"
 
 #include "gtkapplication.h"
+#include "gdkprivate.h"
+
+#ifdef G_OS_UNIX
+#include <gio/gunixfdlist.h>
+#endif
 
 #include <stdlib.h>
 
@@ -165,6 +170,7 @@ struct _GtkApplicationPrivate
   GtkActionMuxer  *muxer;
   GtkBuilder      *menus_builder;
   gchar           *help_overlay_path;
+  guint            profiler_id;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtkApplication, gtk_application, G_TYPE_APPLICATION)
@@ -593,6 +599,153 @@ gtk_application_finalize (GObject *object)
   G_OBJECT_CLASS (gtk_application_parent_class)->finalize (object);
 }
 
+#ifdef G_OS_UNIX
+
+static const gchar org_gnome_Sysprof3_Profiler_xml[] =
+  "<node>"
+    "<interface name='org.gnome.Sysprof3.Profiler'>"
+      "<property name='Capabilities' type='a{sv}' access='read'/>"
+      "<method name='Start'>"
+        "<arg type='a{sv}' name='options' direction='in'/>"
+        "<arg type='h' name='fd' direction='in'/>"
+      "</method>"
+      "<method name='Stop'>"
+      "</method>"
+    "</interface>"
+  "</node>";
+
+static GDBusInterfaceInfo *org_gnome_Sysprof3_Profiler;
+
+static void
+sysprof_profiler_method_call (GDBusConnection       *connection,
+                              const gchar           *sender,
+                              const gchar           *object_path,
+                              const gchar           *interface_name,
+                              const gchar           *method_name,
+                              GVariant              *parameters,
+                              GDBusMethodInvocation *invocation,
+                              gpointer               user_data)
+{
+  if (strcmp (method_name, "Start") == 0)
+    {
+      GDBusMessage *message;
+      GUnixFDList *fd_list;
+      GVariant *options;
+      int fd = -1;
+      int idx;
+
+      if (GDK_PRIVATE_CALL (gdk_profiler_is_running) ())
+        {
+          g_dbus_method_invocation_return_error (invocation,
+                                                 G_DBUS_ERROR,
+                                                 G_DBUS_ERROR_FAILED,
+                                                 "Profiler already running");
+          return;
+        }
+
+      g_variant_get (parameters, "(@a{sv}h)", &options, &idx);
+
+      message = g_dbus_method_invocation_get_message (invocation);
+      fd_list = g_dbus_message_get_unix_fd_list (message);
+      if (fd_list)
+        fd = g_unix_fd_list_get (fd_list, idx, NULL);
+
+      GDK_PRIVATE_CALL (gdk_profiler_start) (fd);
+
+      g_variant_unref (options);
+    }
+  else if (strcmp (method_name, "Stop") == 0)
+    {
+      if (!GDK_PRIVATE_CALL (gdk_profiler_is_running) ())
+        {
+          g_dbus_method_invocation_return_error (invocation,
+                                                 G_DBUS_ERROR,
+                                                 G_DBUS_ERROR_FAILED,
+                                                 "Profiler not running");
+          return;
+        }
+
+      GDK_PRIVATE_CALL (gdk_profiler_stop) ();
+    }
+  else
+    {
+      g_dbus_method_invocation_return_error (invocation,
+                                             G_DBUS_ERROR,
+                                             G_DBUS_ERROR_UNKNOWN_METHOD,
+                                             "Unknown method");
+      return;
+    }
+
+  g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static gboolean
+gtk_application_dbus_register (GApplication     *application,
+                               GDBusConnection  *connection,
+                               const char       *obect_path,
+                               GError          **error)
+{
+  GtkApplicationPrivate *priv = gtk_application_get_instance_private (GTK_APPLICATION (application));
+  GDBusInterfaceVTable vtable = {
+    sysprof_profiler_method_call,
+    NULL,
+    NULL
+  };
+
+  if (org_gnome_Sysprof3_Profiler == NULL)
+    {
+      GDBusNodeInfo *info;
+
+      info = g_dbus_node_info_new_for_xml (org_gnome_Sysprof3_Profiler_xml, error);
+      if (info == NULL)
+        return FALSE;
+
+      org_gnome_Sysprof3_Profiler = g_dbus_node_info_lookup_interface (info, "org.gnome.Sysprof3.Profiler");
+      g_dbus_interface_info_ref (org_gnome_Sysprof3_Profiler);
+      g_dbus_node_info_unref (info);
+    }
+
+  priv->profiler_id = g_dbus_connection_register_object (connection,
+                                                         "/org/gtk/Profiler",
+                                                         org_gnome_Sysprof3_Profiler,
+                                                         &vtable,
+                                                         NULL,
+                                                         NULL,
+                                                         error);
+
+  return TRUE;
+}
+
+static void
+gtk_application_dbus_unregister (GApplication     *application,
+                                 GDBusConnection  *connection,
+                                 const char       *obect_path)
+{
+  GtkApplicationPrivate *priv = gtk_application_get_instance_private (GTK_APPLICATION (application));
+
+  g_dbus_connection_unregister_object (connection, priv->profiler_id);
+}
+
+#else
+
+static gboolean
+gtk_application_dbus_register (GApplication     *application,
+                               GDBusConnection  *connection,
+                               const char       *obect_path,
+                               GError          **error)
+{
+  return TRUE;
+}
+
+static void
+gtk_application_dbus_unregister (GApplication     *application,
+                                 GDBusConnection  *connection,
+                                 const char       *obect_path)
+{
+}
+
+#endif
+
 static void
 gtk_application_class_init (GtkApplicationClass *class)
 {
@@ -609,6 +762,8 @@ gtk_application_class_init (GtkApplicationClass *class)
   application_class->after_emit = gtk_application_after_emit;
   application_class->startup = gtk_application_startup;
   application_class->shutdown = gtk_application_shutdown;
+  application_class->dbus_register = gtk_application_dbus_register;
+  application_class->dbus_unregister = gtk_application_dbus_unregister;
 
   class->window_added = gtk_application_window_added;
   class->window_removed = gtk_application_window_removed;
diff --git a/meson.build b/meson.build
index bc554efb3e..f2301aa082 100644
--- a/meson.build
+++ b/meson.build
@@ -798,6 +798,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
+
 build_gir = get_option('introspection')
 subdir('gdk')
 subdir('gtk')
@@ -956,6 +966,7 @@ summary = [
   '    Print backends: @0@'.format(' '.join(print_backends)),
   '     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('gtk_doc')),
   '         Man pages: @0@'.format(get_option('man')),
diff --git a/meson_options.txt b/meson_options.txt
index 5b30c29aa6..811a8f4069 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -15,6 +15,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 : 'auto',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]