forked from AuroraMiddleware/gtk
c22af88235
meson seems somewhat weak when it comes to handling test output. We need to get the output from different test runs into different locations, and the only way to communicate from a test setup with the actual test code seems the environment, so use that. Make all tests that produce output in files respect a TEST_OUTPUT_SUBDIR environment variable which specifies the name of a subdirectory to use. This is combined with the existing --output argument, which specifies a per-test location. Affected tests are reftests, css performance tests and gsk compare tests.
221 lines
6.1 KiB
C
221 lines
6.1 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 *mark;
|
|
const char *detail;
|
|
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->mark) == 0 &&
|
|
(data->detail == NULL || strcmp (mark->message, data->detail) == 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_detail;
|
|
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" },
|
|
{ "detail", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_detail, "Detail of the mark", "DETAIL" },
|
|
{ "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;
|
|
const char *subdir;
|
|
|
|
file = g_file_new_for_commandline_arg (opt_output);
|
|
|
|
subdir = g_getenv ("TEST_OUTPUT_SUBDIR");
|
|
if (subdir)
|
|
{
|
|
GFile *child = g_file_get_child (file, subdir);
|
|
g_object_unref (file);
|
|
file = child;
|
|
}
|
|
|
|
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.mark = opt_mark ? opt_mark : "css validation";
|
|
data.detail = opt_detail ? opt_detail : NULL;
|
|
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));
|
|
}
|