gtk/testsuite/performance/test-performance.c
Matthias Clasen ed742f1d6c testperf: Allow measuring start time
This can be used e.g. to measure how long it takes us
to get the first frame on screen.
2020-02-05 00:46:39 -05:00

206 lines
5.6 KiB
C

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sysprof-capture.h>
#include <gio/gio.h>
typedef struct {
const char *group;
gboolean do_start;
gint64 start_time;
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)
{
if (data->do_start)
data->value = frame->time - data->start_time;
else
data->value = mark->duration;
return FALSE;
}
}
return TRUE;
}
#define MILLISECONDS(v) ((v) / (1000.0 * G_TIME_SPAN_MILLISECOND))
static int opt_rep = 10;
static char *opt_mark;
static char *opt_name;
static char *opt_output;
static gboolean opt_start_time;
static GOptionEntry options[] = {
{ "mark", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_mark, "Name of the mark", "NAME" },
{ "start", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_start_time, "Measure the start time", NULL },
{ "runs", '0', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &opt_rep, "Number of runs", "COUNT" },
{ "name", '0', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_name, "Name of this test", "NAME" },
{ "output", '0', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_output, "Directory to save syscap files", "DIRECTORY" },
{ 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 count;
char *output_dir = NULL;
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: %s [OPTIONS] COMMANDLINE\n", argv[0]);
exit (1);
}
if (opt_rep < 1)
g_error ("COUNT must be a positive number");
if (opt_output)
{
GError *err = NULL;
GFile *file;
file = g_file_new_for_commandline_arg (opt_output);
if (!g_file_make_directory_with_parents (file, NULL, &err))
{
if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_EXISTS))
g_error ("%s", err->message);
}
output_dir = g_file_get_path (file);
g_object_unref (file);
}
opt_rep++;
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 = opt_mark ? opt_mark : "css validation";
data.do_start = opt_start_time;
data.start_time = sysprof_capture_reader_get_start_time (reader);
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);
if (output_dir)
{
GFile *src, *dest;
char * save_to;
save_to = g_strdup_printf ("%s/%s.%d.syscap", output_dir, opt_name ? opt_name : "gtk", i);
src = g_file_new_for_path (name);
dest = g_file_new_for_path (save_to);
if (!g_file_copy (src, dest, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error))
g_error ("%s", error->message);
g_free (save_to);
g_object_unref (src);
g_object_unref (dest);
}
else
remove (name);
g_free (name);
/* A poor mans way to try and isolate the runs from each other */
g_usleep (300000);
}
min = G_MAXINT64;
max = 0;
count = 0;
total = 0;
/* Ignore the first run, to avoid cache effects */
for (i = 1; i < opt_rep; i++)
{
if (min > values[i])
min = values[i];
if (max < values[i])
max = values[i];
count++;
total += values[i];
}
g_print ("%d runs, min %g, max %g, avg %g\n",
count,
MILLISECONDS (min),
MILLISECONDS (max),
MILLISECONDS (total / count));
}