diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 84d250a1b1..dbb0259b78 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ stages: - subprojects/pango/ fedora-x86_64: &fedora-x86_64-defaults - image: registry.gitlab.gnome.org/gnome/gtk/master:v8 + image: registry.gitlab.gnome.org/gnome/gtk/master:v9 stage: build script: - bash -x ./.gitlab-ci/test-docker.sh diff --git a/.gitlab-ci/Dockerfile b/.gitlab-ci/Dockerfile index f30d2cba8a..a86bb1e3ce 100644 --- a/.gitlab-ci/Dockerfile +++ b/.gitlab-ci/Dockerfile @@ -67,6 +67,7 @@ RUN dnf -y install \ python3-wheel \ redhat-rpm-config \ sassc \ + sysprof-devel \ systemtap-sdt-devel \ vulkan-devel \ wayland-devel \ diff --git a/.gitlab-ci/test-docker.sh b/.gitlab-ci/test-docker.sh index 6b5a7301a1..ada8edaba6 100755 --- a/.gitlab-ci/test-docker.sh +++ b/.gitlab-ci/test-docker.sh @@ -16,6 +16,7 @@ meson \ -Dwayland-backend=true \ -Dbroadway-backend=true \ -Dvulkan=yes \ + -Dprofiler=true \ --werror \ ${EXTRA_MESON_FLAGS:-} \ _build $srcdir diff --git a/demos/widget-factory/widget-factory.c b/demos/widget-factory/widget-factory.c index 559fb82c87..d50143deda 100644 --- a/demos/widget-factory/widget-factory.c +++ b/demos/widget-factory/widget-factory.c @@ -2000,6 +2000,13 @@ toggle_action (GSimpleAction *action, g_variant_new_boolean (!g_variant_get_boolean (state))); } +static gboolean +quit_timeout (gpointer data) +{ + exit (0); + return G_SOURCE_REMOVE; +} + int main (int argc, char *argv[]) { @@ -2065,6 +2072,9 @@ main (int argc, char *argv[]) g_application_add_main_option (G_APPLICATION (app), "version", 0, 0, G_OPTION_ARG_NONE, "Show program version", NULL); + if (g_getenv ("GTK_DEBUG_AUTO_QUIT")) + g_timeout_add (500, quit_timeout, NULL); + g_signal_connect (app, "handle-local-options", G_CALLBACK (local_options), NULL); status = g_application_run (G_APPLICATION (app), argc, argv); g_object_unref (app); diff --git a/gdk/gdk.c b/gdk/gdk.c index c3c58b053b..ae456dc3e2 100644 --- a/gdk/gdk.c +++ b/gdk/gdk.c @@ -212,14 +212,14 @@ 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 */ + 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); + #ifndef G_HAS_CONSTRUCTORS stash_desktop_startup_notification_id (); #endif diff --git a/gtk/gtkcssnode.c b/gtk/gtkcssnode.c index 1d4ec82441..a7c9c9e602 100644 --- a/gtk/gtkcssnode.c +++ b/gtk/gtkcssnode.c @@ -123,12 +123,10 @@ gtk_css_node_get_style_provider_or_null (GtkCssNode *cssnode) return GTK_CSS_NODE_GET_CLASS (cssnode)->get_style_provider (cssnode); } -#ifdef G_ENABLE_DEBUG static int invalidated_nodes; static int created_styles; static guint invalidated_nodes_counter; static guint created_styles_counter; -#endif static void gtk_css_node_set_invalid (GtkCssNode *node, @@ -139,10 +137,8 @@ gtk_css_node_set_invalid (GtkCssNode *node, node->invalid = invalid; -#ifdef G_ENABLE_DEBUG if (invalid) invalidated_nodes++; -#endif if (node->visible) { @@ -382,9 +378,7 @@ gtk_css_node_create_style (GtkCssNode *cssnode, if (style) return g_object_ref (style); -#ifdef G_ENABLE_DEBUG created_styles++; -#endif parent = cssnode->parent ? cssnode->parent->style : NULL; @@ -669,13 +663,11 @@ gtk_css_node_class_init (GtkCssNodeClass *klass) g_object_class_install_properties (object_class, NUM_PROPERTIES, cssnode_properties); -#ifdef G_ENABLE_DEBUG if (invalidated_nodes_counter == 0) { invalidated_nodes_counter = gdk_profiler_define_int_counter ("invalidated-nodes", "CSS Node Invalidations"); created_styles_counter = gdk_profiler_define_int_counter ("created-styles", "CSS Style Creations"); } -#endif } static void @@ -1390,15 +1382,12 @@ void gtk_css_node_validate (GtkCssNode *cssnode) { gint64 timestamp; -#ifdef G_ENABLE_DEBUG gint64 before = g_get_monotonic_time (); -#endif timestamp = gtk_css_node_get_timestamp (cssnode); gtk_css_node_validate_internal (cssnode, timestamp); -#ifdef G_ENABLE_DEBUG if (cssnode->parent == NULL) { if (gdk_profiler_is_running ()) @@ -1411,7 +1400,6 @@ gtk_css_node_validate (GtkCssNode *cssnode) created_styles = 0; } } -#endif } gboolean diff --git a/tests/meson.build b/tests/meson.build index 37d6f0d87f..a62924d050 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -150,4 +150,9 @@ foreach t: gtk_tests dependencies: [libgtk_dep, libm]) endforeach +if get_option('profiler') + executable('testperf', 'testperf.c', + dependencies: [profiler_dep, platform_gio_dep, libm]) +endif + subdir('visuals') diff --git a/tests/testperf.c b/tests/testperf.c new file mode 100644 index 0000000000..27b9d623aa --- /dev/null +++ b/tests/testperf.c @@ -0,0 +1,140 @@ +#include +#include +#include + +typedef struct { + const char *group; + gint64 value; +} Data; + +static gboolean +callback (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + Data *data = user_data; + + if (frame->type == SYSPROF_CAPTURE_FRAME_MARK) + { + SysprofCaptureMark *mark = (SysprofCaptureMark *)frame; + if (strcmp (mark->group, "gtk") == 0 && + strcmp (mark->name, data->group) == 0) + { + data->value = mark->duration; + return FALSE; + } + } + + return TRUE; +} + +#define MILLISECONDS(v) ((v) / (1000.0 * G_TIME_SPAN_MILLISECOND)) + +static int opt_rep = 10; + +static GOptionEntry options[] = { + { "runs", 'r', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &opt_rep, "Number of runs", "COUNT" }, + { NULL, } +}; + +int +main (int argc, char *argv[]) +{ + GOptionContext *context; + GError *error = NULL; + Data data; + SysprofCaptureFrameType type; + char fd_str[20]; + gint64 *values; + gint64 min, max, total; + int i; + + context = g_option_context_new ("COMMANDLINE"); + g_option_context_add_main_entries (context, options, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + g_error ("Parsing options: %s", error->message); + + if (argc < 2) + { + g_print ("Usage: testperf [OPTIONS] COMMANDLINE\n"); + exit (1); + } + + if (opt_rep < 1) + g_error ("COUNT must be a positive number"); + + values = g_new (gint64, opt_rep); + + for (i = 0; i < opt_rep; i++) + { + GSubprocessLauncher *launcher; + GSubprocess *subprocess; + int fd; + gchar *name; + SysprofCaptureReader *reader; + SysprofCaptureCursor *cursor; + SysprofCaptureCondition *condition; + + fd = g_file_open_tmp ("gtk.XXXXXX.syscap", &name, &error); + if (error) + g_error ("Create syscap file: %s", error->message); + + launcher = g_subprocess_launcher_new (0); + g_subprocess_launcher_take_fd (launcher, fd, fd); + g_snprintf (fd_str, sizeof (fd_str), "%d", fd); + g_subprocess_launcher_setenv (launcher, "GTK_TRACE_FD", fd_str, TRUE); + g_subprocess_launcher_setenv (launcher, "GTK_DEBUG_AUTO_QUIT", "1", TRUE); + + subprocess = g_subprocess_launcher_spawnv (launcher, (const char *const *)argv + 1, &error); + if (error) + g_error ("Launch child: %s", error->message); + + if (!g_subprocess_wait (subprocess, NULL, &error)) + g_error ("Run child: %s", error->message); + + g_object_unref (subprocess); + g_object_unref (launcher); + + reader = sysprof_capture_reader_new (name, &error); + if (error) + g_error ("Opening syscap file: %s", error->message); + + data.group = "style"; + data.value = 0; + + cursor = sysprof_capture_cursor_new (reader); + + type = SYSPROF_CAPTURE_FRAME_MARK; + condition = sysprof_capture_condition_new_where_type_in (1, &type); + sysprof_capture_cursor_add_condition (cursor, condition); + + sysprof_capture_cursor_foreach (cursor, callback, &data); + + values[i] = data.value; + + sysprof_capture_cursor_unref (cursor); + sysprof_capture_reader_unref (reader); + + remove (name); + + g_free (name); + } + + min = G_MAXINT64; + max = 0; + total = 0; + + for (i = 0; i < opt_rep; i++) + { + if (min > values[i]) + min = values[i]; + if (max < values[i]) + max = values[i]; + total += values[i]; + } + + g_print ("%d runs, min %g, max %g, avg %g\n", + opt_rep, + MILLISECONDS (min), + MILLISECONDS (max), + MILLISECONDS (total / opt_rep)); +} diff --git a/testsuite/css/meson.build b/testsuite/css/meson.build index 98509c29ca..d5c6ab513b 100644 --- a/testsuite/css/meson.build +++ b/testsuite/css/meson.build @@ -2,6 +2,7 @@ subdir('parser') subdir('nodes') subdir('style') subdir('change') +subdir('performance') testexecdir = join_paths(installed_test_bindir, 'css') testdatadir = join_paths(installed_test_datadir, 'css') diff --git a/testsuite/css/performance/meson.build b/testsuite/css/performance/meson.build new file mode 100644 index 0000000000..781b1b3658 --- /dev/null +++ b/testsuite/css/performance/meson.build @@ -0,0 +1,10 @@ +if get_option ('profiler') + test_css_performance = executable('test-css-performance', 'test-css-performance.c', + dependencies: [profiler_dep, platform_gio_dep, libm]) + + test('performance', test_css_performance, + args: [ join_paths(meson.current_build_dir(), '../../../demos/widget-factory/gtk4-widget-factory') ], + env: [ 'GTK_THEME=Adwaita', + 'GSETTINGS_SCHEMA_DIR=@0@'.format(gtk_schema_build_dir) ], + suite: [ 'css' ]) +endif diff --git a/testsuite/css/performance/test-css-performance.c b/testsuite/css/performance/test-css-performance.c new file mode 100644 index 0000000000..40d996ddc2 --- /dev/null +++ b/testsuite/css/performance/test-css-performance.c @@ -0,0 +1,143 @@ +#include +#include +#include + +typedef struct { + const char *group; + gint64 value; +} Data; + +static gboolean +callback (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + Data *data = user_data; + + if (frame->type == SYSPROF_CAPTURE_FRAME_MARK) + { + SysprofCaptureMark *mark = (SysprofCaptureMark *)frame; + if (strcmp (mark->group, "gtk") == 0 && + strcmp (mark->name, data->group) == 0) + { + data->value = mark->duration; + return FALSE; + } + } + + return TRUE; +} + +#define MILLISECONDS(v) ((v) / (1000.0 * G_TIME_SPAN_MILLISECOND)) + +static int opt_rep = 10; + +static GOptionEntry options[] = { + { "runs", 'r', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &opt_rep, "Number of runs", "COUNT" }, + { NULL, } +}; + +int +main (int argc, char *argv[]) +{ + GOptionContext *context; + GError *error = NULL; + Data data; + SysprofCaptureFrameType type; + char fd_str[20]; + gint64 *values; + gint64 min, max, total; + int i; + + context = g_option_context_new ("COMMANDLINE"); + g_option_context_add_main_entries (context, options, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + g_error ("Parsing options: %s", error->message); + + if (argc < 2) + { + g_print ("Usage: testperf [OPTIONS] COMMANDLINE\n"); + exit (1); + } + + if (opt_rep < 1) + g_error ("COUNT must be a positive number"); + + values = g_new (gint64, opt_rep); + + for (i = 0; i < opt_rep; i++) + { + GSubprocessLauncher *launcher; + GSubprocess *subprocess; + int fd; + gchar *name; + SysprofCaptureReader *reader; + SysprofCaptureCursor *cursor; + SysprofCaptureCondition *condition; + + fd = g_file_open_tmp ("gtk.XXXXXX.syscap", &name, &error); + if (error) + g_error ("Create syscap file: %s", error->message); + + launcher = g_subprocess_launcher_new (0); + g_subprocess_launcher_take_fd (launcher, fd, fd); + g_snprintf (fd_str, sizeof (fd_str), "%d", fd); + g_subprocess_launcher_setenv (launcher, "GTK_TRACE_FD", fd_str, TRUE); + g_subprocess_launcher_setenv (launcher, "GTK_DEBUG_AUTO_QUIT", "1", TRUE); + + subprocess = g_subprocess_launcher_spawnv (launcher, (const char *const *)argv + 1, &error); + if (error) + g_error ("Launch child: %s", error->message); + + if (!g_subprocess_wait (subprocess, NULL, &error)) + g_error ("Run child: %s", error->message); + + if (!g_subprocess_get_successful (subprocess)) + g_error ("Child process failed"); + + g_object_unref (subprocess); + g_object_unref (launcher); + + reader = sysprof_capture_reader_new (name, &error); + if (error) + g_error ("Opening syscap file: %s", error->message); + + data.group = "style"; + data.value = 0; + + cursor = sysprof_capture_cursor_new (reader); + + type = SYSPROF_CAPTURE_FRAME_MARK; + condition = sysprof_capture_condition_new_where_type_in (1, &type); + sysprof_capture_cursor_add_condition (cursor, condition); + + sysprof_capture_cursor_foreach (cursor, callback, &data); + + values[i] = data.value; + + sysprof_capture_cursor_unref (cursor); + sysprof_capture_reader_unref (reader); + + remove (name); + + g_free (name); + } + + min = G_MAXINT64; + max = 0; + total = 0; + + for (i = 0; i < opt_rep; i++) + { + if (min > values[i]) + min = values[i]; + if (max < values[i]) + max = values[i]; + total += values[i]; + } + + g_print ("%d runs, min %g, max %g, avg %g\n", + opt_rep, + MILLISECONDS (min), + MILLISECONDS (max), + MILLISECONDS (total / opt_rep)); +}