diff options
author | Matthias Clasen <mclasen@redhat.com> | 2020-01-21 13:59:12 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2020-01-21 17:31:48 -0500 |
commit | ea17728ee5f9af0dc1ad96b4d6cbdff5f6d22831 (patch) | |
tree | c516837648c6f60ccd49ca4e5935db0958e0df56 | |
parent | a6f300cefd2edfb7789e75565adb03f82a701530 (diff) | |
download | gtk+-ea17728ee5f9af0dc1ad96b4d6cbdff5f6d22831.tar.gz |
Prototype a sysprof helper
This is an attempt to see how we can use sysprof data
in our tests to extract useful performance numbers.
Use it as a wrapper around any GTK+ process:
./testperf ./gtk4-widget-factory
Currently, it repeatedly runs the given commandline,
extracts the first css validation time from the resulting
syscap file, and prints out the min/max/avg of the runs
at the end.
This relies on the environment variable GTK_DEBUG_AUTO_QUIT
to cause the process to exit soon after launch.
-rw-r--r-- | tests/meson.build | 5 | ||||
-rw-r--r-- | tests/testperf.c | 140 |
2 files changed, 145 insertions, 0 deletions
diff --git a/tests/meson.build b/tests/meson.build index 37d6f0d87f..e7aa88ce87 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -150,4 +150,9 @@ foreach t: gtk_tests dependencies: [libgtk_dep, libm]) endforeach + + +executable('testperf', 'testperf.c', + dependencies: [profiler_dep, platform_gio_dep, libm]) + 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 <stdio.h> +#include <sysprof-capture.h> +#include <gio/gio.h> + +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)); +} |