2008-10-16 11:56:19 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright © 2008 Chris Wilson
|
|
|
|
|
*
|
|
|
|
|
* Permission to use, copy, modify, distribute, and sell this software
|
|
|
|
|
* and its documentation for any purpose is hereby granted without
|
|
|
|
|
* fee, provided that the above copyright notice appear in all copies
|
|
|
|
|
* and that both that copyright notice and this permission notice
|
|
|
|
|
* appear in supporting documentation, and that the name of the
|
|
|
|
|
* copyright holders not be used in advertising or publicity
|
|
|
|
|
* pertaining to distribution of the software without specific,
|
|
|
|
|
* written prior permission. The copyright holders make no
|
|
|
|
|
* representations about the suitability of this software for any
|
|
|
|
|
* purpose. It is provided "as is" without express or implied
|
|
|
|
|
* warranty.
|
|
|
|
|
*
|
|
|
|
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
|
|
|
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
|
|
|
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
|
|
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
|
|
|
|
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
|
|
|
* SOFTWARE.
|
|
|
|
|
*
|
|
|
|
|
* Authors: Chris Wilson <chris@chris-wilson.co.uk>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "cairo-perf.h"
|
|
|
|
|
#include "cairo-perf-graph.h"
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
|
|
#include <cairo.h>
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
usage (const char *argv0)
|
|
|
|
|
{
|
|
|
|
|
char const *basename = strrchr (argv0, '/');
|
|
|
|
|
basename = basename ? basename+1 : argv0;
|
|
|
|
|
g_printerr ("Usage: %s [options] file1 file2 [...]\n\n", basename);
|
|
|
|
|
g_printerr ("Draws a graph illustrating the change in performance over a series.\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
CASE_SHOWN,
|
|
|
|
|
CASE_INCONSISTENT,
|
|
|
|
|
CASE_BACKEND,
|
|
|
|
|
CASE_CONTENT,
|
|
|
|
|
CASE_NAME,
|
|
|
|
|
CASE_SIZE,
|
|
|
|
|
CASE_FG_COLOR,
|
|
|
|
|
CASE_DATA,
|
|
|
|
|
CASE_NCOLS
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static GtkTreeStore *
|
|
|
|
|
cases_to_store (test_case_t *cases)
|
|
|
|
|
{
|
|
|
|
|
GtkTreeStore *store;
|
|
|
|
|
GtkTreeIter backend_iter;
|
|
|
|
|
GtkTreeIter content_iter;
|
|
|
|
|
const char *backend = NULL;
|
|
|
|
|
const char *content = NULL;
|
|
|
|
|
|
|
|
|
|
store = gtk_tree_store_new (CASE_NCOLS,
|
2010-06-24 14:59:18 +03:00
|
|
|
G_TYPE_BOOLEAN, /* shown */
|
|
|
|
|
G_TYPE_BOOLEAN, /* inconsistent */
|
2008-10-16 11:56:19 +01:00
|
|
|
G_TYPE_STRING, /* backend */
|
|
|
|
|
G_TYPE_STRING, /* content */
|
|
|
|
|
G_TYPE_STRING, /* name */
|
|
|
|
|
G_TYPE_INT, /* size */
|
|
|
|
|
GDK_TYPE_COLOR, /* fg color */
|
|
|
|
|
G_TYPE_POINTER); /* data */
|
|
|
|
|
while (cases->backend != NULL) {
|
|
|
|
|
GtkTreeIter iter;
|
|
|
|
|
|
|
|
|
|
if (backend == NULL || strcmp (backend, cases->backend)) {
|
|
|
|
|
gtk_tree_store_append (store, &backend_iter, NULL);
|
|
|
|
|
gtk_tree_store_set (store, &backend_iter,
|
2010-06-24 14:59:18 +03:00
|
|
|
CASE_SHOWN, TRUE,
|
2008-10-16 11:56:19 +01:00
|
|
|
CASE_BACKEND, cases->backend,
|
|
|
|
|
-1);
|
|
|
|
|
backend = cases->backend;
|
|
|
|
|
content = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (content == NULL || strcmp (content, cases->content)) {
|
|
|
|
|
gtk_tree_store_append (store, &content_iter, &backend_iter);
|
|
|
|
|
gtk_tree_store_set (store, &content_iter,
|
2010-06-24 14:59:18 +03:00
|
|
|
CASE_SHOWN, TRUE,
|
2008-10-16 11:56:19 +01:00
|
|
|
CASE_BACKEND, cases->backend,
|
|
|
|
|
CASE_CONTENT, cases->content,
|
|
|
|
|
-1);
|
|
|
|
|
content = cases->content;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gtk_tree_store_append (store, &iter, &content_iter);
|
|
|
|
|
gtk_tree_store_set (store, &iter,
|
|
|
|
|
CASE_SHOWN, TRUE,
|
|
|
|
|
CASE_BACKEND, cases->backend,
|
|
|
|
|
CASE_CONTENT, cases->content,
|
|
|
|
|
CASE_NAME, cases->name,
|
|
|
|
|
CASE_SIZE, cases->size,
|
|
|
|
|
CASE_FG_COLOR, &cases->color,
|
|
|
|
|
CASE_DATA, cases,
|
|
|
|
|
-1);
|
|
|
|
|
cases++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return store;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct _app_data {
|
|
|
|
|
GtkWidget *window;
|
|
|
|
|
|
|
|
|
|
test_case_t *cases;
|
|
|
|
|
cairo_perf_report_t *reports;
|
|
|
|
|
int num_reports;
|
|
|
|
|
|
|
|
|
|
GtkTreeStore *case_store;
|
|
|
|
|
|
|
|
|
|
GIOChannel *git_io;
|
|
|
|
|
GtkTextBuffer *git_buffer;
|
|
|
|
|
|
|
|
|
|
GtkWidget *gv;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
2010-06-24 14:59:18 +03:00
|
|
|
recurse_set_shown (GtkTreeModel *model,
|
|
|
|
|
GtkTreeIter *parent,
|
|
|
|
|
gboolean shown)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
GtkTreeIter iter;
|
|
|
|
|
|
|
|
|
|
if (gtk_tree_model_iter_children (model, &iter, parent)) do {
|
|
|
|
|
test_case_t *c;
|
|
|
|
|
|
|
|
|
|
gtk_tree_model_get (model, &iter, CASE_DATA, &c, -1);
|
|
|
|
|
if (c == NULL) {
|
|
|
|
|
recurse_set_shown (model, &iter, shown);
|
|
|
|
|
} else if (shown != c->shown) {
|
|
|
|
|
c->shown = shown;
|
|
|
|
|
gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
|
2010-06-24 14:59:18 +03:00
|
|
|
CASE_SHOWN, shown,
|
2008-10-16 11:56:19 +01:00
|
|
|
CASE_INCONSISTENT, FALSE,
|
|
|
|
|
-1);
|
|
|
|
|
}
|
|
|
|
|
} while (gtk_tree_model_iter_next (model, &iter));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2010-06-24 14:59:18 +03:00
|
|
|
children_consistent (GtkTreeModel *model,
|
|
|
|
|
GtkTreeIter *parent)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
GtkTreeIter iter;
|
|
|
|
|
gboolean first = TRUE;
|
|
|
|
|
gboolean first_active;
|
|
|
|
|
|
|
|
|
|
if (gtk_tree_model_iter_children (model, &iter, parent)) do {
|
|
|
|
|
gboolean active, inconsistent;
|
|
|
|
|
|
|
|
|
|
gtk_tree_model_get (model, &iter,
|
2010-06-24 14:59:18 +03:00
|
|
|
CASE_INCONSISTENT, &inconsistent,
|
2008-10-16 11:56:19 +01:00
|
|
|
CASE_SHOWN, &active,
|
|
|
|
|
-1);
|
|
|
|
|
if (inconsistent)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (first) {
|
|
|
|
|
first_active = active;
|
|
|
|
|
first = FALSE;
|
|
|
|
|
} else if (active != first_active)
|
|
|
|
|
return FALSE;
|
|
|
|
|
} while (gtk_tree_model_iter_next (model, &iter));
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-06-24 14:59:18 +03:00
|
|
|
check_consistent (GtkTreeModel *model,
|
|
|
|
|
GtkTreeIter *child)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
GtkTreeIter parent;
|
|
|
|
|
|
|
|
|
|
if (gtk_tree_model_iter_parent (model, &parent, child)) {
|
|
|
|
|
gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
|
2010-06-24 14:59:18 +03:00
|
|
|
CASE_INCONSISTENT,
|
2008-10-16 11:56:19 +01:00
|
|
|
! children_consistent (model, &parent),
|
|
|
|
|
-1);
|
|
|
|
|
check_consistent (model, &parent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
show_case_toggled (GtkCellRendererToggle *cell,
|
2010-06-24 14:59:18 +03:00
|
|
|
gchar *str,
|
|
|
|
|
struct _app_data *app)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
GtkTreeModel *model;
|
|
|
|
|
GtkTreePath *path;
|
|
|
|
|
GtkTreeIter iter;
|
|
|
|
|
test_case_t *c;
|
|
|
|
|
gboolean active;
|
|
|
|
|
|
|
|
|
|
active = ! gtk_cell_renderer_toggle_get_active (cell);
|
|
|
|
|
|
|
|
|
|
model = GTK_TREE_MODEL (app->case_store);
|
|
|
|
|
|
|
|
|
|
path = gtk_tree_path_new_from_string (str);
|
|
|
|
|
gtk_tree_model_get_iter (model, &iter, path);
|
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
|
|
|
|
|
|
gtk_tree_store_set (app->case_store, &iter,
|
|
|
|
|
CASE_SHOWN, active,
|
|
|
|
|
CASE_INCONSISTENT, FALSE,
|
|
|
|
|
-1);
|
|
|
|
|
gtk_tree_model_get (model, &iter, CASE_DATA, &c, -1);
|
|
|
|
|
if (c != NULL) {
|
|
|
|
|
if (active == c->shown)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
c->shown = active;
|
|
|
|
|
} else {
|
|
|
|
|
recurse_set_shown (model, &iter, active);
|
|
|
|
|
}
|
|
|
|
|
check_consistent (model, &iter);
|
|
|
|
|
|
|
|
|
|
graph_view_update_visible ((GraphView *) app->gv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2010-06-24 14:59:18 +03:00
|
|
|
git_read (GIOChannel *io,
|
|
|
|
|
GIOCondition cond,
|
|
|
|
|
struct _app_data *app)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
|
|
fd = g_io_channel_unix_get_fd (io);
|
|
|
|
|
do {
|
|
|
|
|
char buf[4096];
|
|
|
|
|
int len;
|
|
|
|
|
GtkTextIter end;
|
|
|
|
|
|
|
|
|
|
len = read (fd, buf, sizeof (buf));
|
|
|
|
|
if (len <= 0) {
|
|
|
|
|
int err = len ? errno : 0;
|
|
|
|
|
switch (err) {
|
|
|
|
|
case EAGAIN:
|
|
|
|
|
case EINTR:
|
|
|
|
|
return TRUE;
|
|
|
|
|
default:
|
|
|
|
|
g_io_channel_unref (app->git_io);
|
|
|
|
|
app->git_io = NULL;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gtk_text_buffer_get_end_iter (app->git_buffer, &end);
|
|
|
|
|
gtk_text_buffer_insert (app->git_buffer, &end, buf, len);
|
|
|
|
|
} while (TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-06-24 14:59:18 +03:00
|
|
|
do_git (struct _app_data *app,
|
|
|
|
|
char **argv)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
gint output;
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
GtkTextIter start, stop;
|
|
|
|
|
long flags;
|
|
|
|
|
|
|
|
|
|
if (! g_spawn_async_with_pipes (NULL, argv, NULL,
|
|
|
|
|
G_SPAWN_SEARCH_PATH |
|
|
|
|
|
G_SPAWN_STDERR_TO_DEV_NULL |
|
|
|
|
|
G_SPAWN_FILE_AND_ARGV_ZERO,
|
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
|
NULL, &output, NULL,
|
|
|
|
|
&error))
|
|
|
|
|
{
|
|
|
|
|
g_error ("spawn failed: %s", error->message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (app->git_io) {
|
|
|
|
|
g_io_channel_shutdown (app->git_io, FALSE, NULL);
|
|
|
|
|
g_io_channel_unref (app->git_io);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gtk_text_buffer_get_bounds (app->git_buffer, &start, &stop);
|
|
|
|
|
gtk_text_buffer_delete (app->git_buffer, &start, &stop);
|
|
|
|
|
|
|
|
|
|
flags = fcntl (output, F_GETFL);
|
|
|
|
|
if ((flags & O_NONBLOCK) == 0)
|
|
|
|
|
fcntl (output, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
|
|
|
|
|
|
app->git_io = g_io_channel_unix_new (output);
|
|
|
|
|
g_io_add_watch (app->git_io, G_IO_IN | G_IO_HUP, (GIOFunc) git_read, app);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-06-24 14:59:18 +03:00
|
|
|
gv_report_selected (GraphView *gv,
|
|
|
|
|
int i,
|
|
|
|
|
struct _app_data *app)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
cairo_perf_report_t *report;
|
|
|
|
|
char *hyphen;
|
|
|
|
|
|
|
|
|
|
if (i == -1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
report = &app->reports[i];
|
|
|
|
|
hyphen = strchr (report->configuration, '-');
|
|
|
|
|
if (hyphen != NULL) {
|
|
|
|
|
int len = hyphen - report->configuration;
|
|
|
|
|
char *id = g_malloc (len + 1);
|
|
|
|
|
char *argv[5];
|
|
|
|
|
|
|
|
|
|
memcpy (id, report->configuration, len);
|
|
|
|
|
id[len] = '\0';
|
|
|
|
|
|
|
|
|
|
argv[0] = (char *) "git";
|
|
|
|
|
argv[1] = (char *) "git";
|
|
|
|
|
argv[2] = (char *) "show";
|
|
|
|
|
argv[3] = id;
|
|
|
|
|
argv[4] = NULL;
|
|
|
|
|
|
|
|
|
|
do_git (app, argv);
|
2008-12-20 19:14:23 +00:00
|
|
|
g_free (id);
|
2008-10-16 11:56:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GtkWidget *
|
2010-06-24 14:59:18 +03:00
|
|
|
window_create (test_case_t *cases,
|
2008-10-16 11:56:19 +01:00
|
|
|
cairo_perf_report_t *reports,
|
2010-06-24 14:59:18 +03:00
|
|
|
int num_reports)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
GtkWidget *window, *table, *w;
|
|
|
|
|
GtkWidget *tv, *sw;
|
|
|
|
|
GtkTreeStore *store;
|
|
|
|
|
GtkTreeViewColumn *column;
|
|
|
|
|
GtkCellRenderer *renderer;
|
|
|
|
|
struct _app_data *data;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data = g_new0 (struct _app_data, 1);
|
|
|
|
|
data->cases = cases;
|
|
|
|
|
data->reports = reports;
|
|
|
|
|
data->num_reports = num_reports;
|
|
|
|
|
|
|
|
|
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
|
gtk_window_set_title (GTK_WINDOW (window), "Cairo Performance Graph");
|
|
|
|
|
g_object_set_data_full (G_OBJECT (window),
|
2010-06-24 14:59:18 +03:00
|
|
|
"app-data", data, (GDestroyNotify)g_free);
|
2008-10-16 11:56:19 +01:00
|
|
|
|
|
|
|
|
data->window = window;
|
|
|
|
|
|
|
|
|
|
table = gtk_table_new (2, 2, FALSE);
|
|
|
|
|
|
|
|
|
|
/* legend & show/hide lines (categorised) */
|
|
|
|
|
tv = gtk_tree_view_new ();
|
|
|
|
|
store = cases_to_store (cases);
|
|
|
|
|
data->case_store = store;
|
|
|
|
|
gtk_tree_view_set_model (GTK_TREE_VIEW (tv), GTK_TREE_MODEL (store));
|
|
|
|
|
|
|
|
|
|
renderer = gtk_cell_renderer_toggle_new ();
|
|
|
|
|
column = gtk_tree_view_column_new_with_attributes (NULL,
|
|
|
|
|
renderer,
|
|
|
|
|
"active", CASE_SHOWN,
|
|
|
|
|
"inconsistent", CASE_INCONSISTENT,
|
|
|
|
|
NULL);
|
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
|
|
|
|
|
g_signal_connect (renderer, "toggled",
|
2010-06-24 14:59:18 +03:00
|
|
|
G_CALLBACK (show_case_toggled), data);
|
2008-10-16 11:56:19 +01:00
|
|
|
|
|
|
|
|
renderer = gtk_cell_renderer_text_new ();
|
|
|
|
|
column = gtk_tree_view_column_new_with_attributes ("Backend",
|
|
|
|
|
renderer,
|
|
|
|
|
"text", CASE_BACKEND,
|
|
|
|
|
NULL);
|
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
|
|
|
|
|
|
|
|
|
|
renderer = gtk_cell_renderer_text_new ();
|
|
|
|
|
column = gtk_tree_view_column_new_with_attributes ("Content",
|
|
|
|
|
renderer,
|
|
|
|
|
"text", CASE_CONTENT,
|
|
|
|
|
NULL);
|
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
|
|
|
|
|
|
|
|
|
|
renderer = gtk_cell_renderer_text_new ();
|
|
|
|
|
column = gtk_tree_view_column_new_with_attributes ("Test",
|
|
|
|
|
renderer,
|
|
|
|
|
"text", CASE_NAME,
|
|
|
|
|
"foreground-gdk", CASE_FG_COLOR,
|
|
|
|
|
NULL);
|
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
|
|
|
|
|
|
|
|
|
|
renderer = gtk_cell_renderer_text_new ();
|
|
|
|
|
column = gtk_tree_view_column_new_with_attributes ("Size",
|
|
|
|
|
renderer,
|
|
|
|
|
"text", CASE_SIZE,
|
|
|
|
|
NULL);
|
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
|
|
|
|
|
|
|
|
|
|
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tv), TRUE);
|
|
|
|
|
g_object_unref (store);
|
|
|
|
|
|
|
|
|
|
sw = gtk_scrolled_window_new (NULL, NULL);
|
|
|
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
|
2010-06-24 14:59:18 +03:00
|
|
|
GTK_POLICY_NEVER,
|
|
|
|
|
GTK_POLICY_AUTOMATIC);
|
2008-10-16 11:56:19 +01:00
|
|
|
gtk_container_add (GTK_CONTAINER (sw), tv);
|
|
|
|
|
gtk_widget_show (tv);
|
|
|
|
|
gtk_table_attach (GTK_TABLE (table), sw,
|
2010-06-24 14:59:18 +03:00
|
|
|
0, 1, 0, 2,
|
2008-10-16 11:56:19 +01:00
|
|
|
GTK_FILL, GTK_FILL,
|
|
|
|
|
4, 4);
|
|
|
|
|
gtk_widget_show (sw);
|
|
|
|
|
|
|
|
|
|
/* the performance chart */
|
|
|
|
|
w = graph_view_new ();
|
|
|
|
|
data->gv = w;
|
|
|
|
|
g_signal_connect (w, "report-selected",
|
2010-06-24 14:59:18 +03:00
|
|
|
G_CALLBACK (gv_report_selected), data);
|
2008-10-16 11:56:19 +01:00
|
|
|
graph_view_set_reports ((GraphView *)w, cases, reports, num_reports);
|
|
|
|
|
gtk_table_attach (GTK_TABLE (table), w,
|
2010-06-24 14:59:18 +03:00
|
|
|
1, 2, 0, 1,
|
2008-10-16 11:56:19 +01:00
|
|
|
GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND,
|
|
|
|
|
4, 4);
|
|
|
|
|
gtk_widget_show (w);
|
|
|
|
|
|
|
|
|
|
/* interesting information - presumably the commit log */
|
|
|
|
|
w = gtk_text_view_new ();
|
|
|
|
|
data->git_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (w));
|
|
|
|
|
sw = gtk_scrolled_window_new (NULL, NULL);
|
|
|
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
|
2010-06-24 14:59:18 +03:00
|
|
|
GTK_POLICY_NEVER,
|
|
|
|
|
GTK_POLICY_AUTOMATIC);
|
2008-10-16 11:56:19 +01:00
|
|
|
gtk_container_add (GTK_CONTAINER (sw), w);
|
|
|
|
|
gtk_widget_show (w);
|
|
|
|
|
gtk_table_attach (GTK_TABLE (table), sw,
|
2010-06-24 14:59:18 +03:00
|
|
|
1, 2, 1, 2,
|
2008-10-16 11:56:19 +01:00
|
|
|
GTK_FILL, GTK_FILL | GTK_EXPAND,
|
|
|
|
|
4, 4);
|
|
|
|
|
gtk_widget_show (sw);
|
|
|
|
|
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (window), table);
|
|
|
|
|
gtk_widget_show (table);
|
|
|
|
|
|
|
|
|
|
return window;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-06-24 14:59:18 +03:00
|
|
|
name_to_color (const char *name,
|
|
|
|
|
GdkColor *color)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
gint v = g_str_hash (name);
|
|
|
|
|
|
|
|
|
|
color->red = ((v >> 0) & 0xff) / 384. * 0xffff;
|
|
|
|
|
color->green = ((v >> 8) & 0xff) / 384. * 0xffff;
|
|
|
|
|
color->blue = ((v >> 16) & 0xff) / 384. * 0xffff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static test_case_t *
|
|
|
|
|
test_cases_from_reports (cairo_perf_report_t *reports,
|
2010-06-24 14:59:18 +03:00
|
|
|
int num_reports)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
test_case_t *cases, *c;
|
|
|
|
|
test_report_t **tests;
|
|
|
|
|
int i, j;
|
|
|
|
|
int num_tests;
|
|
|
|
|
|
|
|
|
|
num_tests = 0;
|
|
|
|
|
for (i = 0; i < num_reports; i++) {
|
|
|
|
|
for (j = 0; reports[i].tests[j].name != NULL; j++)
|
|
|
|
|
;
|
|
|
|
|
if (j > num_tests)
|
|
|
|
|
num_tests = j;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cases = xcalloc (num_tests+1, sizeof (test_case_t));
|
|
|
|
|
tests = xmalloc (num_reports * sizeof (test_report_t *));
|
|
|
|
|
for (i = 0; i < num_reports; i++)
|
|
|
|
|
tests[i] = reports[i].tests;
|
|
|
|
|
|
|
|
|
|
c = cases;
|
|
|
|
|
while (1) {
|
|
|
|
|
int seen_non_null;
|
|
|
|
|
test_report_t *min_test;
|
|
|
|
|
|
|
|
|
|
/* We expect iterations values of 0 when multiple raw reports
|
|
|
|
|
* for the same test have been condensed into the stats of the
|
|
|
|
|
* first. So we just skip these later reports that have no
|
|
|
|
|
* stats. */
|
|
|
|
|
seen_non_null = 0;
|
|
|
|
|
for (i = 0; i < num_reports; i++) {
|
|
|
|
|
while (tests[i]->name && tests[i]->stats.iterations == 0)
|
|
|
|
|
tests[i]++;
|
|
|
|
|
if (tests[i]->name)
|
|
|
|
|
seen_non_null++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (seen_non_null < 2)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Find the minimum of all current tests, (we have to do this
|
|
|
|
|
* in case some reports don't have a particular test). */
|
|
|
|
|
for (i = 0; i < num_reports; i++) {
|
|
|
|
|
if (tests[i]->name) {
|
|
|
|
|
min_test = tests[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (++i; i < num_reports; i++) {
|
|
|
|
|
if (tests[i]->name &&
|
|
|
|
|
test_report_cmp_backend_then_name (tests[i], min_test) < 0)
|
|
|
|
|
{
|
|
|
|
|
min_test = tests[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c->min_test = min_test;
|
|
|
|
|
c->backend = min_test->backend;
|
|
|
|
|
c->content = min_test->content;
|
|
|
|
|
c->name = min_test->name;
|
|
|
|
|
c->size = min_test->size;
|
|
|
|
|
c->baseline = min_test->stats.min_ticks;
|
|
|
|
|
c->min = c->max = 1.;
|
|
|
|
|
c->shown = TRUE;
|
|
|
|
|
name_to_color (c->name, &c->color);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_reports; i++) {
|
|
|
|
|
if (tests[i]->name &&
|
|
|
|
|
test_report_cmp_backend_then_name (tests[i], min_test) == 0)
|
|
|
|
|
{
|
|
|
|
|
tests[i]++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (++i; i < num_reports; i++) {
|
|
|
|
|
if (tests[i]->name &&
|
|
|
|
|
test_report_cmp_backend_then_name (tests[i], min_test) == 0)
|
|
|
|
|
{
|
|
|
|
|
double v = tests[i]->stats.min_ticks / c->baseline;
|
|
|
|
|
if (v < c->min)
|
|
|
|
|
c->min = v;
|
|
|
|
|
if (v > c->max)
|
|
|
|
|
c->max = v;
|
|
|
|
|
tests[i]++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c++;
|
|
|
|
|
}
|
|
|
|
|
free (tests);
|
|
|
|
|
|
|
|
|
|
return cases;
|
|
|
|
|
}
|
|
|
|
|
int
|
2010-06-24 14:59:18 +03:00
|
|
|
main (int argc,
|
|
|
|
|
char *argv[])
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
cairo_perf_report_t *reports;
|
|
|
|
|
test_case_t *cases;
|
|
|
|
|
test_report_t *t;
|
|
|
|
|
int i;
|
|
|
|
|
GtkWidget *window;
|
|
|
|
|
|
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
|
|
|
|
|
|
if (argc < 3)
|
|
|
|
|
usage (argv[0]);
|
|
|
|
|
|
|
|
|
|
reports = xmalloc ((argc-1) * sizeof (cairo_perf_report_t));
|
|
|
|
|
for (i = 1; i < argc; i++ )
|
2011-11-18 10:04:12 +00:00
|
|
|
cairo_perf_report_load (&reports[i-1], argv[i], i, NULL);
|
2008-10-16 11:56:19 +01:00
|
|
|
|
|
|
|
|
cases = test_cases_from_reports (reports, argc-1);
|
|
|
|
|
|
|
|
|
|
window = window_create (cases, reports, argc-1);
|
|
|
|
|
g_signal_connect (window, "delete-event",
|
2010-06-24 14:59:18 +03:00
|
|
|
G_CALLBACK (gtk_main_quit), NULL);
|
2008-10-16 11:56:19 +01:00
|
|
|
gtk_widget_show (window);
|
|
|
|
|
|
|
|
|
|
gtk_main ();
|
|
|
|
|
|
|
|
|
|
/* Pointless memory cleanup, (would be a great place for talloc) */
|
|
|
|
|
free (cases);
|
|
|
|
|
for (i = 0; i < argc-1; i++) {
|
|
|
|
|
for (t = reports[i].tests; t->name; t++) {
|
|
|
|
|
free (t->samples);
|
|
|
|
|
free (t->backend);
|
|
|
|
|
free (t->name);
|
|
|
|
|
}
|
|
|
|
|
free (reports[i].tests);
|
|
|
|
|
free (reports[i].configuration);
|
|
|
|
|
}
|
|
|
|
|
free (reports);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|