2009-06-02 13:08:25 +01:00
|
|
|
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
|
|
|
|
|
/*
|
|
|
|
|
* Copyright © 2006 Mozilla Corporation
|
|
|
|
|
* Copyright © 2006 Red Hat, Inc.
|
|
|
|
|
* Copyright © 2009 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 authors not be used in advertising or publicity pertaining to
|
|
|
|
|
* distribution of the software without specific, written prior
|
|
|
|
|
* permission. The authors make no representations about the
|
|
|
|
|
* suitability of this software for any purpose. It is provided "as
|
|
|
|
|
* is" without express or implied warranty.
|
|
|
|
|
*
|
|
|
|
|
* THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
|
|
|
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
|
|
|
* FITNESS, IN NO EVENT SHALL THE AUTHORS 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: Vladimir Vukicevic <vladimir@pobox.com>
|
2010-06-24 14:59:18 +03:00
|
|
|
* Carl Worth <cworth@cworth.org>
|
|
|
|
|
* Chris Wilson <chris@chris-wilson.co.uk>
|
2009-06-02 13:08:25 +01:00
|
|
|
*/
|
|
|
|
|
|
2021-05-01 16:58:15 +01:00
|
|
|
#include "config.h"
|
2009-06-02 13:08:25 +01:00
|
|
|
|
2011-08-30 16:16:04 +02:00
|
|
|
#include "cairo-missing.h"
|
2009-06-02 13:08:25 +01:00
|
|
|
#include "cairo-perf.h"
|
|
|
|
|
#include "cairo-stats.h"
|
|
|
|
|
|
|
|
|
|
#include "cairo-boilerplate-getopt.h"
|
|
|
|
|
#include <cairo-script-interpreter.h>
|
2009-06-15 11:58:58 +01:00
|
|
|
#include <cairo-types-private.h> /* for INTERNAL_SURFACE_TYPE */
|
2009-06-02 13:08:25 +01:00
|
|
|
|
2009-11-01 08:40:41 +00:00
|
|
|
/* rudely reuse bits of the library... */
|
|
|
|
|
#include "../src/cairo-hash-private.h"
|
|
|
|
|
#include "../src/cairo-error-private.h"
|
|
|
|
|
|
2009-06-02 13:08:25 +01:00
|
|
|
/* For basename */
|
|
|
|
|
#ifdef HAVE_LIBGEN_H
|
|
|
|
|
#include <libgen.h>
|
|
|
|
|
#endif
|
2009-06-11 12:58:05 +01:00
|
|
|
#include <ctype.h> /* isspace() */
|
2009-06-02 13:08:25 +01:00
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
2009-06-30 20:46:12 +01:00
|
|
|
#include <sys/stat.h>
|
2011-06-21 18:12:29 +02:00
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
#include "dirent-win32.h"
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
basename_no_ext (char *path)
|
|
|
|
|
{
|
|
|
|
|
static char name[_MAX_FNAME + 1];
|
|
|
|
|
|
|
|
|
|
_splitpath (path, NULL, NULL, name, NULL);
|
|
|
|
|
|
|
|
|
|
name[_MAX_FNAME] = '\0';
|
|
|
|
|
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
2009-06-02 13:08:25 +01:00
|
|
|
#include <dirent.h>
|
|
|
|
|
|
2011-06-21 18:12:29 +02:00
|
|
|
static char *
|
|
|
|
|
basename_no_ext (char *path)
|
|
|
|
|
{
|
|
|
|
|
char *dot, *name;
|
|
|
|
|
|
|
|
|
|
name = basename (path);
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
dot = strrchr (name, '.');
|
2011-06-21 18:12:29 +02:00
|
|
|
if (dot)
|
|
|
|
|
*dot = '\0';
|
|
|
|
|
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
2010-03-04 17:11:58 +00:00
|
|
|
#if HAVE_UNISTD_H
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-06-04 10:03:45 +01:00
|
|
|
#include <signal.h>
|
|
|
|
|
|
2009-06-02 13:08:25 +01:00
|
|
|
#if HAVE_FCFINI
|
|
|
|
|
#include <fontconfig/fontconfig.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-06-03 13:26:52 +01:00
|
|
|
#define CAIRO_PERF_ITERATIONS_DEFAULT 15
|
|
|
|
|
#define CAIRO_PERF_LOW_STD_DEV 0.05
|
|
|
|
|
#define CAIRO_PERF_MIN_STD_DEV_COUNT 3
|
2010-06-24 14:59:18 +03:00
|
|
|
#define CAIRO_PERF_STABLE_STD_DEV_COUNT 3
|
2009-06-02 13:08:25 +01:00
|
|
|
|
2011-06-01 23:03:36 +01:00
|
|
|
struct trace {
|
|
|
|
|
const cairo_boilerplate_target_t *target;
|
|
|
|
|
void *closure;
|
|
|
|
|
cairo_surface_t *surface;
|
2011-08-23 14:39:20 +01:00
|
|
|
cairo_bool_t observe;
|
2011-07-30 17:28:21 +01:00
|
|
|
int tile_size;
|
2011-06-01 23:03:36 +01:00
|
|
|
};
|
|
|
|
|
|
2009-06-02 13:08:25 +01:00
|
|
|
cairo_bool_t
|
2010-06-24 14:59:18 +03:00
|
|
|
cairo_perf_can_run (cairo_perf_t *perf,
|
|
|
|
|
const char *name,
|
|
|
|
|
cairo_bool_t *is_explicit)
|
2009-06-02 13:08:25 +01:00
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
char *copy, *dot;
|
2009-06-11 12:58:05 +01:00
|
|
|
cairo_bool_t ret;
|
2009-06-02 13:08:25 +01:00
|
|
|
|
2009-08-22 18:59:01 +01:00
|
|
|
if (is_explicit)
|
|
|
|
|
*is_explicit = FALSE;
|
|
|
|
|
|
|
|
|
|
if (perf->exact_names) {
|
|
|
|
|
if (is_explicit)
|
|
|
|
|
*is_explicit = TRUE;
|
2009-06-30 20:46:12 +01:00
|
|
|
return TRUE;
|
2009-08-22 18:59:01 +01:00
|
|
|
}
|
2009-06-30 20:46:12 +01:00
|
|
|
|
2009-06-11 12:58:05 +01:00
|
|
|
if (perf->num_names == 0 && perf->num_exclude_names == 0)
|
2009-06-02 13:08:25 +01:00
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
copy = xstrdup (name);
|
2011-07-30 17:28:21 +01:00
|
|
|
dot = strrchr (copy, '.');
|
2009-06-02 13:08:25 +01:00
|
|
|
if (dot != NULL)
|
|
|
|
|
*dot = '\0';
|
|
|
|
|
|
2009-06-11 12:58:05 +01:00
|
|
|
if (perf->num_names) {
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
for (i = 0; i < perf->num_names; i++)
|
2009-08-22 18:59:01 +01:00
|
|
|
if (strstr (copy, perf->names[i])) {
|
|
|
|
|
if (is_explicit)
|
|
|
|
|
*is_explicit = strcmp (copy, perf->names[i]) == 0;
|
2009-06-11 12:58:05 +01:00
|
|
|
goto check_exclude;
|
2009-08-22 18:59:01 +01:00
|
|
|
}
|
2009-06-11 12:58:05 +01:00
|
|
|
|
|
|
|
|
ret = FALSE;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
2009-06-02 13:08:25 +01:00
|
|
|
|
2009-06-11 12:58:05 +01:00
|
|
|
check_exclude:
|
|
|
|
|
if (perf->num_exclude_names) {
|
|
|
|
|
ret = FALSE;
|
|
|
|
|
for (i = 0; i < perf->num_exclude_names; i++)
|
2009-08-22 18:59:01 +01:00
|
|
|
if (strstr (copy, perf->exclude_names[i])) {
|
|
|
|
|
if (is_explicit)
|
|
|
|
|
*is_explicit = strcmp (copy, perf->exclude_names[i]) == 0;
|
2009-06-11 12:58:05 +01:00
|
|
|
goto done;
|
2009-08-22 18:59:01 +01:00
|
|
|
}
|
2009-06-11 12:58:05 +01:00
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done:
|
2009-06-02 13:08:25 +01:00
|
|
|
free (copy);
|
|
|
|
|
|
2009-06-11 12:58:05 +01:00
|
|
|
return ret;
|
2009-06-02 13:08:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-01-28 10:03:54 +00:00
|
|
|
fill_surface (cairo_surface_t *surface)
|
2009-06-02 13:08:25 +01:00
|
|
|
{
|
|
|
|
|
cairo_t *cr = cairo_create (surface);
|
2013-01-28 10:03:54 +00:00
|
|
|
/* This needs to be an operation that the backends can't optimise away */
|
|
|
|
|
cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.5);
|
|
|
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
|
2009-06-02 13:08:25 +01:00
|
|
|
cairo_paint (cr);
|
|
|
|
|
cairo_destroy (cr);
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-01 08:40:41 +00:00
|
|
|
struct scache {
|
|
|
|
|
cairo_hash_entry_t entry;
|
|
|
|
|
cairo_content_t content;
|
|
|
|
|
int width, height;
|
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static cairo_hash_table_t *surface_cache;
|
|
|
|
|
static cairo_surface_t *surface_holdovers[16];
|
|
|
|
|
|
|
|
|
|
static cairo_bool_t
|
2010-06-24 14:59:18 +03:00
|
|
|
scache_equal (const void *A,
|
|
|
|
|
const void *B)
|
2009-11-01 08:40:41 +00:00
|
|
|
{
|
|
|
|
|
const struct scache *a = A, *b = B;
|
|
|
|
|
return a->entry.hash == b->entry.hash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
scache_mark_active (cairo_surface_t *surface)
|
|
|
|
|
{
|
|
|
|
|
cairo_surface_t *t0, *t1;
|
|
|
|
|
unsigned n;
|
|
|
|
|
|
|
|
|
|
if (surface_cache == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
t0 = cairo_surface_reference (surface);
|
2014-04-16 17:37:18 -07:00
|
|
|
for (n = 0; n < ARRAY_LENGTH (surface_holdovers); n++) {
|
2009-11-01 08:40:41 +00:00
|
|
|
if (surface_holdovers[n] == surface) {
|
|
|
|
|
surface_holdovers[n] = t0;
|
|
|
|
|
t0 = surface;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t1 = surface_holdovers[n];
|
|
|
|
|
surface_holdovers[n] = t0;
|
|
|
|
|
t0 = t1;
|
|
|
|
|
}
|
|
|
|
|
cairo_surface_destroy (t0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
scache_clear (void)
|
|
|
|
|
{
|
|
|
|
|
unsigned n;
|
|
|
|
|
|
|
|
|
|
if (surface_cache == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-04-16 17:37:18 -07:00
|
|
|
for (n = 0; n < ARRAY_LENGTH (surface_holdovers); n++) {
|
2009-11-01 08:40:41 +00:00
|
|
|
cairo_surface_destroy (surface_holdovers[n]);
|
|
|
|
|
surface_holdovers[n] = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
scache_remove (void *closure)
|
|
|
|
|
{
|
|
|
|
|
_cairo_hash_table_remove (surface_cache, closure);
|
|
|
|
|
free (closure);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-02 13:08:25 +01:00
|
|
|
static cairo_surface_t *
|
2010-06-24 14:59:18 +03:00
|
|
|
_similar_surface_create (void *closure,
|
|
|
|
|
cairo_content_t content,
|
2011-06-01 23:03:36 +01:00
|
|
|
double width,
|
|
|
|
|
double height,
|
2010-06-24 14:59:18 +03:00
|
|
|
long uid)
|
2009-06-02 13:08:25 +01:00
|
|
|
{
|
2011-06-01 23:03:36 +01:00
|
|
|
struct trace *args = closure;
|
2009-11-01 08:40:41 +00:00
|
|
|
cairo_surface_t *surface;
|
|
|
|
|
struct scache skey, *s;
|
|
|
|
|
|
2011-08-23 14:39:20 +01:00
|
|
|
if (args->observe)
|
|
|
|
|
return cairo_surface_create_similar (args->surface,
|
|
|
|
|
content, width, height);
|
|
|
|
|
|
2009-11-01 08:40:41 +00:00
|
|
|
if (uid == 0 || surface_cache == NULL)
|
2011-06-01 23:03:36 +01:00
|
|
|
return args->target->create_similar (args->surface, content, width, height);
|
2009-11-01 08:40:41 +00:00
|
|
|
|
|
|
|
|
skey.entry.hash = uid;
|
|
|
|
|
s = _cairo_hash_table_lookup (surface_cache, &skey.entry);
|
|
|
|
|
if (s != NULL) {
|
|
|
|
|
if (s->content == content &&
|
|
|
|
|
s->width == width &&
|
|
|
|
|
s->height == height)
|
|
|
|
|
{
|
|
|
|
|
return cairo_surface_reference (s->surface);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The surface has been resized, allow the original entry to expire
|
|
|
|
|
* as it becomes inactive.
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-01 23:03:36 +01:00
|
|
|
surface = args->target->create_similar (args->surface, content, width, height);
|
2009-11-01 08:40:41 +00:00
|
|
|
s = malloc (sizeof (struct scache));
|
|
|
|
|
if (s == NULL)
|
|
|
|
|
return surface;
|
|
|
|
|
|
|
|
|
|
s->entry.hash = uid;
|
|
|
|
|
s->content = content;
|
|
|
|
|
s->width = width;
|
|
|
|
|
s->height = height;
|
|
|
|
|
s->surface = surface;
|
|
|
|
|
if (_cairo_hash_table_insert (surface_cache, &s->entry)) {
|
|
|
|
|
free (s);
|
|
|
|
|
} else if (cairo_surface_set_user_data
|
|
|
|
|
(surface,
|
|
|
|
|
(const cairo_user_data_key_t *) &surface_cache,
|
|
|
|
|
s, scache_remove))
|
|
|
|
|
{
|
|
|
|
|
scache_remove (s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return surface;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-08 12:19:33 +00:00
|
|
|
static cairo_surface_t *
|
|
|
|
|
_source_image_create (void *closure,
|
|
|
|
|
cairo_format_t format,
|
|
|
|
|
int width,
|
|
|
|
|
int height,
|
|
|
|
|
long uid)
|
|
|
|
|
{
|
|
|
|
|
struct trace *args = closure;
|
|
|
|
|
|
|
|
|
|
return cairo_surface_create_similar_image (args->surface,
|
|
|
|
|
format, width, height);
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-01 08:40:41 +00:00
|
|
|
static cairo_t *
|
2010-06-24 14:59:18 +03:00
|
|
|
_context_create (void *closure,
|
2009-11-01 08:40:41 +00:00
|
|
|
cairo_surface_t *surface)
|
|
|
|
|
{
|
|
|
|
|
scache_mark_active (surface);
|
|
|
|
|
return cairo_create (surface);
|
2009-06-02 13:08:25 +01:00
|
|
|
}
|
|
|
|
|
|
2009-06-04 10:03:45 +01:00
|
|
|
static int user_interrupt;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
interrupt (int sig)
|
|
|
|
|
{
|
|
|
|
|
if (user_interrupt) {
|
|
|
|
|
signal (sig, SIG_DFL);
|
|
|
|
|
raise (sig);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
user_interrupt = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-03 02:54:55 +02:00
|
|
|
static void
|
|
|
|
|
describe (cairo_perf_t *perf,
|
|
|
|
|
void *closure)
|
|
|
|
|
{
|
|
|
|
|
char *description = NULL;
|
|
|
|
|
|
2010-10-20 14:26:37 +01:00
|
|
|
if (perf->has_described_backend)
|
|
|
|
|
return;
|
|
|
|
|
perf->has_described_backend = TRUE;
|
|
|
|
|
|
2010-07-03 02:54:55 +02:00
|
|
|
if (perf->target->describe)
|
|
|
|
|
description = perf->target->describe (closure);
|
|
|
|
|
|
|
|
|
|
if (description == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (perf->raw) {
|
|
|
|
|
printf ("[ # ] %s: %s\n", perf->target->name, description);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (perf->summary) {
|
|
|
|
|
fprintf (perf->summary,
|
|
|
|
|
"[ # ] %8s: %s\n",
|
|
|
|
|
perf->target->name,
|
|
|
|
|
description);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free (description);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-02 13:08:25 +01:00
|
|
|
static void
|
|
|
|
|
usage (const char *argv0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr,
|
2011-10-02 16:50:37 -07:00
|
|
|
"Usage: %s [-clrsv] [-i iterations] [-t tile-size] [-x exclude-file] [test-names ... | traces ...]\n"
|
2009-06-02 13:08:25 +01:00
|
|
|
"\n"
|
|
|
|
|
"Run the cairo performance test suite over the given tests (all by default)\n"
|
|
|
|
|
"The command-line arguments are interpreted as follows:\n"
|
|
|
|
|
"\n"
|
2011-10-02 16:50:37 -07:00
|
|
|
" -c use surface cache; keep a cache of surfaces to be reused\n"
|
|
|
|
|
" -i iterations; specify the number of iterations per test case\n"
|
|
|
|
|
" -l list only; just list selected test case names without executing\n"
|
2009-06-02 13:08:25 +01:00
|
|
|
" -r raw; display each time measurement instead of summary statistics\n"
|
2018-10-20 14:11:30 -04:00
|
|
|
" -s sync; only sum the elapsed time of the individual operations\n"
|
2011-10-02 16:50:37 -07:00
|
|
|
" -t tile size; draw to tiled surfaces\n"
|
2009-06-02 13:08:25 +01:00
|
|
|
" -v verbose; in raw mode also show the summaries\n"
|
2011-10-02 16:50:37 -07:00
|
|
|
" -x exclude; specify a file to read a list of traces to exclude\n"
|
2009-06-02 13:08:25 +01:00
|
|
|
"\n"
|
|
|
|
|
"If test names are given they are used as sub-string matches so a command\n"
|
2011-10-02 16:50:37 -07:00
|
|
|
"such as \"%s firefox\" can be used to run all firefox traces.\n"
|
2009-06-02 13:08:25 +01:00
|
|
|
"Alternatively, you can specify a list of filenames to execute.\n",
|
|
|
|
|
argv0, argv0);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-11 12:58:05 +01:00
|
|
|
static cairo_bool_t
|
2010-06-24 14:59:18 +03:00
|
|
|
read_excludes (cairo_perf_t *perf,
|
|
|
|
|
const char *filename)
|
2009-06-11 12:58:05 +01:00
|
|
|
{
|
|
|
|
|
FILE *file;
|
|
|
|
|
char *line = NULL;
|
|
|
|
|
size_t line_size = 0;
|
|
|
|
|
char *s, *t;
|
|
|
|
|
|
|
|
|
|
file = fopen (filename, "r");
|
|
|
|
|
if (file == NULL)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
while (getline (&line, &line_size, file) != -1) {
|
|
|
|
|
/* terminate the line at a comment marker '#' */
|
|
|
|
|
s = strchr (line, '#');
|
|
|
|
|
if (s)
|
|
|
|
|
*s = '\0';
|
|
|
|
|
|
|
|
|
|
/* whitespace delimits */
|
|
|
|
|
s = line;
|
Avoid misuse of ctype(3) functions
The ctype(3) character classification and mapping functions have a
peculiarly limited definition (C11, Sec. 7.4 `Character handling
<ctype.h>', p. 200):
`The header <ctype.h> declares several functions useful for
classifying and mapping characters. In all cases the
argument is an int, the value of which shall be
representable as an unsigned char or shall equal the value
of the macro EOF. If the argument has any other value, the
behavior is undefined.'
In other words, in the most common case of 8-bit char and EOF = -1,
the domain of the 257 allowed arguments is:
-1, 0, 1, 2, ..., 254, 255
The ctype(3) functions are designed for use with stdio functions like
getchar and fgetc which return int values in the same domain.
In an ABI where char is signed (e.g., x86 SysV ABI used by most
Unixish operating systems), passing an argument of type char as is
can go wrong in two ways:
1. The value of a non-EOF input octet interpreted as `char' may
coincide, as an integer, with the value of EOF, leading to wrong
answers for some non-EOF inputs.
E.g., if EOF = 1, and an input octet has all bits set, i.e., 255
as an unsigned char, then as a char the value is -1, which will be
confused with EOF. In the ISO-8859-1 locale, the code point 255
is (in Unicode terminology) LATIN SMALL LETTER Y WITH DIAERESIS,
for which isprint, isalpha, &c., are true. But isprint, isalpha,
&c., are false for EOF. So if char *s points to a string with
that character, isprint(*s) will return false when it should
return true.
2. Passing a negative char whose value does not coincide with EOF is
undefined behaviour.
This isn't purely theoretical: often the functions are implemented
by an array lookup, #define isprint(c) (ctypetab[c] & ISPRINT).
If c is out of range (e.g., 192, ISO-8859-1 for LATIN CAPITAL
LETTER A WITH GRAVE, which convers to (signed) char as -64), then
you can get garbage answers by reading uninitialized memory or
application crashes with SIGSEGV if the page preceding the table
is unmapped.
If what you have is an arbitrary char (e.g., from a char * string
pointing at user input), then the only correct way to use the
ctype(3) functions is by converting to unsigned char first -- e.g.,
isprint((unsigned char)*s). (If the functions were defined as macros
that convert to unsigned char first, they would then spuriously
interpret EOF as a non-EOF, so they can't do that themselves.)
It is possible, in some cases, to prove that the input always
actually lies in {0, 1, 2, ..., 127}, so the conversion to unsigned
char is not necessary. I didn't check whether this was the case --
it's safer to just adopt the habit of always casting char to unsigned
char first before using the ctype(3) macros, which satisfies a
compiler warning on some systems designed to detect this class of
application errors at compile-time.
2022-04-05 12:51:33 +00:00
|
|
|
while (*s != '\0' && isspace ((unsigned char)*s))
|
2009-06-11 12:58:05 +01:00
|
|
|
s++;
|
|
|
|
|
|
|
|
|
|
t = s;
|
Avoid misuse of ctype(3) functions
The ctype(3) character classification and mapping functions have a
peculiarly limited definition (C11, Sec. 7.4 `Character handling
<ctype.h>', p. 200):
`The header <ctype.h> declares several functions useful for
classifying and mapping characters. In all cases the
argument is an int, the value of which shall be
representable as an unsigned char or shall equal the value
of the macro EOF. If the argument has any other value, the
behavior is undefined.'
In other words, in the most common case of 8-bit char and EOF = -1,
the domain of the 257 allowed arguments is:
-1, 0, 1, 2, ..., 254, 255
The ctype(3) functions are designed for use with stdio functions like
getchar and fgetc which return int values in the same domain.
In an ABI where char is signed (e.g., x86 SysV ABI used by most
Unixish operating systems), passing an argument of type char as is
can go wrong in two ways:
1. The value of a non-EOF input octet interpreted as `char' may
coincide, as an integer, with the value of EOF, leading to wrong
answers for some non-EOF inputs.
E.g., if EOF = 1, and an input octet has all bits set, i.e., 255
as an unsigned char, then as a char the value is -1, which will be
confused with EOF. In the ISO-8859-1 locale, the code point 255
is (in Unicode terminology) LATIN SMALL LETTER Y WITH DIAERESIS,
for which isprint, isalpha, &c., are true. But isprint, isalpha,
&c., are false for EOF. So if char *s points to a string with
that character, isprint(*s) will return false when it should
return true.
2. Passing a negative char whose value does not coincide with EOF is
undefined behaviour.
This isn't purely theoretical: often the functions are implemented
by an array lookup, #define isprint(c) (ctypetab[c] & ISPRINT).
If c is out of range (e.g., 192, ISO-8859-1 for LATIN CAPITAL
LETTER A WITH GRAVE, which convers to (signed) char as -64), then
you can get garbage answers by reading uninitialized memory or
application crashes with SIGSEGV if the page preceding the table
is unmapped.
If what you have is an arbitrary char (e.g., from a char * string
pointing at user input), then the only correct way to use the
ctype(3) functions is by converting to unsigned char first -- e.g.,
isprint((unsigned char)*s). (If the functions were defined as macros
that convert to unsigned char first, they would then spuriously
interpret EOF as a non-EOF, so they can't do that themselves.)
It is possible, in some cases, to prove that the input always
actually lies in {0, 1, 2, ..., 127}, so the conversion to unsigned
char is not necessary. I didn't check whether this was the case --
it's safer to just adopt the habit of always casting char to unsigned
char first before using the ctype(3) macros, which satisfies a
compiler warning on some systems designed to detect this class of
application errors at compile-time.
2022-04-05 12:51:33 +00:00
|
|
|
while (*t != '\0' && ! isspace ((unsigned char)*t))
|
2009-06-11 12:58:05 +01:00
|
|
|
t++;
|
|
|
|
|
|
|
|
|
|
if (s != t) {
|
|
|
|
|
int i = perf->num_exclude_names;
|
|
|
|
|
perf->exclude_names = xrealloc (perf->exclude_names,
|
|
|
|
|
sizeof (char *) * (i+1));
|
|
|
|
|
perf->exclude_names[i] = strndup (s, t-s);
|
|
|
|
|
perf->num_exclude_names++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-30 11:04:16 +02:00
|
|
|
free (line);
|
2009-06-11 12:58:05 +01:00
|
|
|
|
|
|
|
|
fclose (file);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-02 13:08:25 +01:00
|
|
|
static void
|
2010-06-24 14:59:18 +03:00
|
|
|
parse_options (cairo_perf_t *perf,
|
|
|
|
|
int argc,
|
|
|
|
|
char *argv[])
|
2009-06-02 13:08:25 +01:00
|
|
|
{
|
|
|
|
|
int c;
|
|
|
|
|
const char *iters;
|
|
|
|
|
char *end;
|
|
|
|
|
int verbose = 0;
|
2009-11-01 08:40:41 +00:00
|
|
|
int use_surface_cache = 0;
|
2009-06-02 13:08:25 +01:00
|
|
|
|
|
|
|
|
if ((iters = getenv ("CAIRO_PERF_ITERATIONS")) && *iters)
|
|
|
|
|
perf->iterations = strtol (iters, NULL, 0);
|
|
|
|
|
else
|
|
|
|
|
perf->iterations = CAIRO_PERF_ITERATIONS_DEFAULT;
|
|
|
|
|
perf->exact_iterations = 0;
|
|
|
|
|
|
|
|
|
|
perf->raw = FALSE;
|
2011-08-23 14:39:20 +01:00
|
|
|
perf->observe = FALSE;
|
2009-06-02 13:08:25 +01:00
|
|
|
perf->list_only = FALSE;
|
2011-07-30 17:28:21 +01:00
|
|
|
perf->tile_size = 0;
|
2009-06-02 13:08:25 +01:00
|
|
|
perf->names = NULL;
|
|
|
|
|
perf->num_names = 0;
|
|
|
|
|
perf->summary = stdout;
|
2009-06-04 11:16:48 +01:00
|
|
|
perf->summary_continuous = FALSE;
|
2009-06-11 12:58:05 +01:00
|
|
|
perf->exclude_names = NULL;
|
|
|
|
|
perf->num_exclude_names = 0;
|
2009-06-02 13:08:25 +01:00
|
|
|
|
|
|
|
|
while (1) {
|
2011-10-02 16:15:59 -07:00
|
|
|
c = _cairo_getopt (argc, argv, "ci:lrst:vx:");
|
2009-06-02 13:08:25 +01:00
|
|
|
if (c == -1)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
switch (c) {
|
2011-10-02 16:15:59 -07:00
|
|
|
case 'c':
|
|
|
|
|
use_surface_cache = 1;
|
|
|
|
|
break;
|
2009-06-02 13:08:25 +01:00
|
|
|
case 'i':
|
|
|
|
|
perf->exact_iterations = TRUE;
|
|
|
|
|
perf->iterations = strtoul (optarg, &end, 10);
|
|
|
|
|
if (*end != '\0') {
|
|
|
|
|
fprintf (stderr, "Invalid argument for -i (not an integer): %s\n",
|
|
|
|
|
optarg);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'l':
|
|
|
|
|
perf->list_only = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case 'r':
|
|
|
|
|
perf->raw = TRUE;
|
|
|
|
|
perf->summary = NULL;
|
|
|
|
|
break;
|
2011-08-23 14:39:20 +01:00
|
|
|
case 's':
|
|
|
|
|
perf->observe = TRUE;
|
|
|
|
|
break;
|
2011-10-02 16:15:59 -07:00
|
|
|
case 't':
|
|
|
|
|
perf->tile_size = strtoul (optarg, &end, 10);
|
|
|
|
|
if (*end != '\0') {
|
|
|
|
|
fprintf (stderr, "Invalid argument for -t (not an integer): %s\n",
|
|
|
|
|
optarg);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2009-06-02 13:08:25 +01:00
|
|
|
case 'v':
|
|
|
|
|
verbose = 1;
|
|
|
|
|
break;
|
2009-06-11 12:58:05 +01:00
|
|
|
case 'x':
|
|
|
|
|
if (! read_excludes (perf, optarg)) {
|
|
|
|
|
fprintf (stderr, "Invalid argument for -x (not readable file): %s\n",
|
|
|
|
|
optarg);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2009-06-02 13:08:25 +01:00
|
|
|
default:
|
|
|
|
|
fprintf (stderr, "Internal error: unhandled option: %c\n", c);
|
|
|
|
|
/* fall-through */
|
|
|
|
|
case '?':
|
|
|
|
|
usage (argv[0]);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
if (perf->observe && perf->tile_size) {
|
|
|
|
|
fprintf (stderr, "Can't mix observer and tiling. Sorry.\n");
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-02 13:08:25 +01:00
|
|
|
if (verbose && perf->summary == NULL)
|
|
|
|
|
perf->summary = stderr;
|
2010-03-04 17:11:58 +00:00
|
|
|
#if HAVE_UNISTD_H
|
2009-06-08 00:12:28 +01:00
|
|
|
if (perf->summary && isatty (fileno (perf->summary)))
|
2009-06-04 11:16:48 +01:00
|
|
|
perf->summary_continuous = TRUE;
|
2010-03-04 17:11:58 +00:00
|
|
|
#endif
|
2009-06-02 13:08:25 +01:00
|
|
|
|
|
|
|
|
if (optind < argc) {
|
|
|
|
|
perf->names = &argv[optind];
|
|
|
|
|
perf->num_names = argc - optind;
|
|
|
|
|
}
|
2009-11-01 08:40:41 +00:00
|
|
|
|
|
|
|
|
if (use_surface_cache)
|
|
|
|
|
surface_cache = _cairo_hash_table_create (scache_equal);
|
2009-06-02 13:08:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
cairo_perf_fini (cairo_perf_t *perf)
|
|
|
|
|
{
|
|
|
|
|
cairo_boilerplate_free_targets (perf->targets);
|
2010-03-23 16:49:21 +00:00
|
|
|
cairo_boilerplate_fini ();
|
|
|
|
|
|
2009-06-02 13:08:25 +01:00
|
|
|
free (perf->times);
|
|
|
|
|
cairo_debug_reset_static_data ();
|
|
|
|
|
#if HAVE_FCFINI
|
|
|
|
|
FcFini ();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cairo_bool_t
|
|
|
|
|
have_trace_filenames (cairo_perf_t *perf)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
if (perf->num_names == 0)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2010-03-04 17:11:58 +00:00
|
|
|
#if HAVE_UNISTD_H
|
2009-06-02 13:08:25 +01:00
|
|
|
for (i = 0; i < perf->num_names; i++)
|
|
|
|
|
if (access (perf->names[i], R_OK) == 0)
|
|
|
|
|
return TRUE;
|
2010-03-04 17:11:58 +00:00
|
|
|
#endif
|
2009-06-02 13:08:25 +01:00
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
static void
|
|
|
|
|
_tiling_surface_finish (cairo_surface_t *observer,
|
|
|
|
|
cairo_surface_t *target,
|
|
|
|
|
void *closure)
|
|
|
|
|
{
|
|
|
|
|
struct trace *args = closure;
|
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
|
cairo_content_t content;
|
|
|
|
|
cairo_rectangle_t r;
|
|
|
|
|
int width, height;
|
|
|
|
|
int x, y, w, h;
|
|
|
|
|
|
|
|
|
|
cairo_recording_surface_get_extents (target, &r);
|
|
|
|
|
w = r.width;
|
|
|
|
|
h = r.height;
|
|
|
|
|
|
|
|
|
|
content = cairo_surface_get_content (target);
|
|
|
|
|
|
|
|
|
|
for (y = 0; y < h; y += args->tile_size) {
|
|
|
|
|
height = args->tile_size;
|
|
|
|
|
if (y + height > h)
|
|
|
|
|
height = h - y;
|
|
|
|
|
|
|
|
|
|
for (x = 0; x < w; x += args->tile_size) {
|
|
|
|
|
cairo_t *cr;
|
|
|
|
|
|
|
|
|
|
width = args->tile_size;
|
|
|
|
|
if (x + width > w)
|
|
|
|
|
width = w - x;
|
|
|
|
|
|
|
|
|
|
/* XXX to correctly observe the playback we would need
|
|
|
|
|
* to replay the target onto the observer directly.
|
|
|
|
|
*/
|
|
|
|
|
surface = args->target->create_similar (args->surface,
|
|
|
|
|
content, width, height);
|
|
|
|
|
|
|
|
|
|
cr = cairo_create (surface);
|
|
|
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
|
|
|
|
cairo_set_source_surface (cr, target, -x, -y);
|
|
|
|
|
cairo_paint (cr);
|
|
|
|
|
cairo_destroy (cr);
|
|
|
|
|
|
|
|
|
|
cairo_surface_destroy (surface);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cairo_surface_t *
|
|
|
|
|
_tiling_surface_create (void *closure,
|
|
|
|
|
cairo_content_t content,
|
|
|
|
|
double width,
|
|
|
|
|
double height,
|
|
|
|
|
long uid)
|
|
|
|
|
{
|
|
|
|
|
cairo_rectangle_t r;
|
|
|
|
|
cairo_surface_t *surface, *observer;
|
|
|
|
|
|
|
|
|
|
r.x = r.y = 0;
|
|
|
|
|
r.width = width;
|
|
|
|
|
r.height = height;
|
|
|
|
|
|
|
|
|
|
surface = cairo_recording_surface_create (content, &r);
|
|
|
|
|
observer = cairo_surface_create_observer (surface,
|
|
|
|
|
CAIRO_SURFACE_OBSERVER_NORMAL);
|
|
|
|
|
cairo_surface_destroy (surface);
|
|
|
|
|
|
|
|
|
|
cairo_surface_observer_add_finish_callback (observer,
|
|
|
|
|
_tiling_surface_finish,
|
|
|
|
|
closure);
|
|
|
|
|
|
|
|
|
|
return observer;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-02 13:08:25 +01:00
|
|
|
static void
|
2010-06-24 14:59:18 +03:00
|
|
|
cairo_perf_trace (cairo_perf_t *perf,
|
2009-06-12 12:32:51 +01:00
|
|
|
const cairo_boilerplate_target_t *target,
|
2010-06-24 14:59:18 +03:00
|
|
|
const char *trace)
|
2009-06-02 13:08:25 +01:00
|
|
|
{
|
2011-08-19 21:13:42 +01:00
|
|
|
static cairo_bool_t first_run = TRUE;
|
|
|
|
|
unsigned int i;
|
2011-08-31 17:55:07 +02:00
|
|
|
cairo_time_t *times, *paint, *mask, *fill, *stroke, *glyphs;
|
2011-08-19 21:13:42 +01:00
|
|
|
cairo_stats_t stats = {0.0, 0.0};
|
|
|
|
|
struct trace args = { target };
|
|
|
|
|
int low_std_dev_count;
|
|
|
|
|
char *trace_cpy, *name;
|
|
|
|
|
const cairo_script_interpreter_hooks_t hooks = {
|
|
|
|
|
&args,
|
2011-07-30 17:28:21 +01:00
|
|
|
perf->tile_size ? _tiling_surface_create : _similar_surface_create,
|
2011-08-19 21:13:42 +01:00
|
|
|
NULL, /* surface_destroy */
|
|
|
|
|
_context_create,
|
|
|
|
|
NULL, /* context_destroy */
|
|
|
|
|
NULL, /* show_page */
|
2013-01-08 12:19:33 +00:00
|
|
|
NULL, /* copy_page */
|
|
|
|
|
_source_image_create,
|
2011-08-19 21:13:42 +01:00
|
|
|
};
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
args.tile_size = perf->tile_size;
|
2011-08-23 14:39:20 +01:00
|
|
|
args.observe = perf->observe;
|
|
|
|
|
|
2011-08-19 21:13:42 +01:00
|
|
|
trace_cpy = xstrdup (trace);
|
|
|
|
|
name = basename_no_ext (trace_cpy);
|
|
|
|
|
|
|
|
|
|
if (perf->list_only) {
|
|
|
|
|
printf ("%s\n", name);
|
|
|
|
|
free (trace_cpy);
|
2009-06-02 13:08:25 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-19 21:13:42 +01:00
|
|
|
if (first_run) {
|
|
|
|
|
if (perf->raw) {
|
|
|
|
|
printf ("[ # ] %s.%-s %s %s %s ...\n",
|
|
|
|
|
"backend", "content", "test-size", "ticks-per-ms", "time(ticks)");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (perf->summary) {
|
2011-08-25 18:43:02 +01:00
|
|
|
if (perf->observe) {
|
|
|
|
|
fprintf (perf->summary,
|
|
|
|
|
"[ # ] %8s %28s %9s %9s %9s %9s %9s %9s %5s\n",
|
|
|
|
|
"backend", "test",
|
|
|
|
|
"total(s)", "paint(s)", "mask(s)", "fill(s)", "stroke(s)", "glyphs(s)",
|
|
|
|
|
"count");
|
|
|
|
|
} else {
|
|
|
|
|
fprintf (perf->summary,
|
|
|
|
|
"[ # ] %8s %28s %8s %5s %5s %s\n",
|
|
|
|
|
"backend", "test", "min(s)", "median(s)",
|
|
|
|
|
"stddev.", "count");
|
|
|
|
|
}
|
2011-08-19 21:13:42 +01:00
|
|
|
}
|
|
|
|
|
first_run = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
times = perf->times;
|
2011-08-25 18:43:02 +01:00
|
|
|
paint = times + perf->iterations;
|
|
|
|
|
mask = paint + perf->iterations;
|
|
|
|
|
stroke = mask + perf->iterations;
|
|
|
|
|
fill = stroke + perf->iterations;
|
|
|
|
|
glyphs = fill + perf->iterations;
|
2011-08-19 21:13:42 +01:00
|
|
|
|
|
|
|
|
low_std_dev_count = 0;
|
|
|
|
|
for (i = 0; i < perf->iterations && ! user_interrupt; i++) {
|
|
|
|
|
cairo_script_interpreter_t *csi;
|
|
|
|
|
cairo_status_t status;
|
|
|
|
|
unsigned int line_no;
|
|
|
|
|
|
|
|
|
|
args.surface = target->create_surface (NULL,
|
|
|
|
|
CAIRO_CONTENT_COLOR_ALPHA,
|
|
|
|
|
1, 1,
|
|
|
|
|
1, 1,
|
|
|
|
|
CAIRO_BOILERPLATE_MODE_PERF,
|
|
|
|
|
&args.closure);
|
2013-01-28 10:03:54 +00:00
|
|
|
fill_surface(args.surface); /* remove any clear flags */
|
|
|
|
|
|
2011-08-23 14:39:20 +01:00
|
|
|
if (perf->observe) {
|
|
|
|
|
cairo_surface_t *obs;
|
2011-08-25 18:43:02 +01:00
|
|
|
obs = cairo_surface_create_observer (args.surface,
|
|
|
|
|
CAIRO_SURFACE_OBSERVER_NORMAL);
|
2011-08-23 14:39:20 +01:00
|
|
|
cairo_surface_destroy (args.surface);
|
|
|
|
|
args.surface = obs;
|
|
|
|
|
}
|
2011-08-19 21:13:42 +01:00
|
|
|
if (cairo_surface_status (args.surface)) {
|
|
|
|
|
fprintf (stderr,
|
|
|
|
|
"Error: Failed to create target surface: %s\n",
|
|
|
|
|
target->name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_perf_timer_set_synchronize (target->synchronize, args.closure);
|
|
|
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
describe (perf, args.closure);
|
|
|
|
|
if (perf->summary) {
|
|
|
|
|
fprintf (perf->summary,
|
|
|
|
|
"[%3d] %8s %28s ",
|
|
|
|
|
perf->test_number,
|
|
|
|
|
perf->target->name,
|
|
|
|
|
name);
|
|
|
|
|
fflush (perf->summary);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
csi = cairo_script_interpreter_create ();
|
|
|
|
|
cairo_script_interpreter_install_hooks (csi, &hooks);
|
|
|
|
|
|
2011-08-23 14:39:20 +01:00
|
|
|
if (! perf->observe) {
|
|
|
|
|
cairo_perf_yield ();
|
|
|
|
|
cairo_perf_timer_start ();
|
|
|
|
|
}
|
2011-08-19 21:13:42 +01:00
|
|
|
|
|
|
|
|
cairo_script_interpreter_run (csi, trace);
|
2011-07-30 17:28:21 +01:00
|
|
|
line_no = cairo_script_interpreter_get_line_number (csi);
|
|
|
|
|
|
|
|
|
|
/* Finish before querying timings in case we are using an intermediate
|
|
|
|
|
* target and so need to destroy all surfaces before rendering
|
|
|
|
|
* commences.
|
|
|
|
|
*/
|
|
|
|
|
cairo_script_interpreter_finish (csi);
|
2011-08-19 21:13:42 +01:00
|
|
|
|
2011-08-23 14:39:20 +01:00
|
|
|
if (perf->observe) {
|
2011-08-25 18:43:02 +01:00
|
|
|
cairo_device_t *observer = cairo_surface_get_device (args.surface);
|
2011-07-30 17:28:21 +01:00
|
|
|
times[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_elapsed (observer));
|
|
|
|
|
paint[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_paint_elapsed (observer));
|
|
|
|
|
mask[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_mask_elapsed (observer));
|
|
|
|
|
stroke[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_stroke_elapsed (observer));
|
|
|
|
|
fill[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_fill_elapsed (observer));
|
|
|
|
|
glyphs[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_glyphs_elapsed (observer));
|
2011-08-23 14:39:20 +01:00
|
|
|
} else {
|
2013-01-28 10:03:54 +00:00
|
|
|
fill_surface (args.surface); /* queue a write to the sync'ed surface */
|
2011-08-23 14:39:20 +01:00
|
|
|
cairo_perf_timer_stop ();
|
|
|
|
|
times[i] = cairo_perf_timer_elapsed ();
|
|
|
|
|
}
|
2011-08-19 21:13:42 +01:00
|
|
|
|
|
|
|
|
scache_clear ();
|
|
|
|
|
|
|
|
|
|
cairo_surface_destroy (args.surface);
|
|
|
|
|
|
|
|
|
|
if (target->cleanup)
|
|
|
|
|
target->cleanup (args.closure);
|
|
|
|
|
|
|
|
|
|
status = cairo_script_interpreter_destroy (csi);
|
|
|
|
|
if (status) {
|
|
|
|
|
if (perf->summary) {
|
|
|
|
|
fprintf (perf->summary, "Error during replay, line %d: %s\n",
|
|
|
|
|
line_no,
|
|
|
|
|
cairo_status_to_string (status));
|
|
|
|
|
}
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (perf->raw) {
|
|
|
|
|
if (i == 0)
|
|
|
|
|
printf ("[*] %s.%s %s.%d %g",
|
|
|
|
|
perf->target->name,
|
|
|
|
|
"rgba",
|
|
|
|
|
name,
|
|
|
|
|
0,
|
2011-08-31 17:03:33 +02:00
|
|
|
_cairo_time_to_double (_cairo_time_from_s (1)) / 1000.);
|
2011-08-19 21:13:42 +01:00
|
|
|
printf (" %lld", (long long) times[i]);
|
|
|
|
|
fflush (stdout);
|
|
|
|
|
} else if (! perf->exact_iterations) {
|
|
|
|
|
if (i > CAIRO_PERF_MIN_STD_DEV_COUNT) {
|
|
|
|
|
_cairo_stats_compute (&stats, times, i+1);
|
|
|
|
|
|
|
|
|
|
if (stats.std_dev <= CAIRO_PERF_LOW_STD_DEV) {
|
|
|
|
|
if (++low_std_dev_count >= CAIRO_PERF_STABLE_STD_DEV_COUNT)
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
low_std_dev_count = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (perf->summary && perf->summary_continuous) {
|
|
|
|
|
_cairo_stats_compute (&stats, times, i+1);
|
|
|
|
|
|
|
|
|
|
fprintf (perf->summary,
|
|
|
|
|
"\r[%3d] %8s %28s ",
|
|
|
|
|
perf->test_number,
|
|
|
|
|
perf->target->name,
|
|
|
|
|
name);
|
2011-08-25 18:43:02 +01:00
|
|
|
if (perf->observe) {
|
|
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
|
|
|
|
_cairo_stats_compute (&stats, paint, i+1);
|
|
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
|
|
|
|
_cairo_stats_compute (&stats, mask, i+1);
|
|
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
|
|
|
|
_cairo_stats_compute (&stats, fill, i+1);
|
|
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
|
|
|
|
_cairo_stats_compute (&stats, stroke, i+1);
|
|
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
|
|
|
|
_cairo_stats_compute (&stats, glyphs, i+1);
|
|
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
|
|
|
|
fprintf (perf->summary,
|
|
|
|
|
" %5d", i+1);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf (perf->summary,
|
|
|
|
|
"%#8.3f %#8.3f %#6.2f%% %4d/%d",
|
2011-08-31 17:03:33 +02:00
|
|
|
_cairo_time_to_s (stats.min_ticks),
|
|
|
|
|
_cairo_time_to_s (stats.median_ticks),
|
2011-08-25 18:43:02 +01:00
|
|
|
stats.std_dev * 100.0,
|
|
|
|
|
stats.iterations, i+1);
|
|
|
|
|
}
|
2011-08-19 21:13:42 +01:00
|
|
|
fflush (perf->summary);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
user_interrupt = 0;
|
2009-06-02 13:08:25 +01:00
|
|
|
|
2011-08-19 21:13:42 +01:00
|
|
|
if (perf->summary) {
|
|
|
|
|
_cairo_stats_compute (&stats, times, i);
|
|
|
|
|
if (perf->summary_continuous) {
|
|
|
|
|
fprintf (perf->summary,
|
|
|
|
|
"\r[%3d] %8s %28s ",
|
|
|
|
|
perf->test_number,
|
|
|
|
|
perf->target->name,
|
|
|
|
|
name);
|
|
|
|
|
}
|
2011-08-25 18:43:02 +01:00
|
|
|
if (perf->observe) {
|
|
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
_cairo_stats_compute (&stats, paint, i);
|
2011-08-25 18:43:02 +01:00
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
_cairo_stats_compute (&stats, mask, i);
|
2011-08-25 18:43:02 +01:00
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
_cairo_stats_compute (&stats, fill, i);
|
2011-08-25 18:43:02 +01:00
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
_cairo_stats_compute (&stats, stroke, i);
|
2011-08-25 18:43:02 +01:00
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
_cairo_stats_compute (&stats, glyphs, i);
|
2011-08-25 18:43:02 +01:00
|
|
|
fprintf (perf->summary,
|
2011-08-31 17:03:33 +02:00
|
|
|
" %#9.3f", _cairo_time_to_s (stats.median_ticks));
|
2011-08-25 18:43:02 +01:00
|
|
|
|
|
|
|
|
fprintf (perf->summary,
|
2011-07-30 17:28:21 +01:00
|
|
|
" %5d\n", i);
|
2011-08-25 18:43:02 +01:00
|
|
|
} else {
|
|
|
|
|
fprintf (perf->summary,
|
|
|
|
|
"%#8.3f %#8.3f %#6.2f%% %4d/%d\n",
|
2011-08-31 17:03:33 +02:00
|
|
|
_cairo_time_to_s (stats.min_ticks),
|
|
|
|
|
_cairo_time_to_s (stats.median_ticks),
|
2011-08-25 18:43:02 +01:00
|
|
|
stats.std_dev * 100.0,
|
|
|
|
|
stats.iterations, i);
|
|
|
|
|
}
|
2011-08-19 21:13:42 +01:00
|
|
|
fflush (perf->summary);
|
|
|
|
|
}
|
2009-06-02 13:08:25 +01:00
|
|
|
|
2011-08-19 21:13:42 +01:00
|
|
|
out:
|
|
|
|
|
if (perf->raw) {
|
|
|
|
|
printf ("\n");
|
|
|
|
|
fflush (stdout);
|
|
|
|
|
}
|
2009-06-02 13:08:25 +01:00
|
|
|
|
2011-08-19 21:13:42 +01:00
|
|
|
perf->test_number++;
|
|
|
|
|
free (trace_cpy);
|
2009-06-02 13:08:25 +01:00
|
|
|
}
|
|
|
|
|
|
2009-06-02 21:54:44 +01:00
|
|
|
static void
|
2010-06-24 14:59:18 +03:00
|
|
|
warn_no_traces (const char *message,
|
|
|
|
|
const char *trace_dir)
|
2009-06-02 21:54:44 +01:00
|
|
|
{
|
|
|
|
|
fprintf (stderr,
|
|
|
|
|
"Error: %s '%s'.\n"
|
|
|
|
|
"Have you cloned the cairo-traces repository and uncompressed the traces?\n"
|
2009-06-03 08:57:15 +01:00
|
|
|
" git clone git://anongit.freedesktop.org/cairo-traces\n"
|
2009-06-02 21:54:44 +01:00
|
|
|
" cd cairo-traces && make\n"
|
|
|
|
|
"Or set the env.var CAIRO_TRACE_DIR to point to your traces?\n",
|
|
|
|
|
message, trace_dir);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-30 20:46:12 +01:00
|
|
|
static int
|
2010-06-24 14:59:18 +03:00
|
|
|
cairo_perf_trace_dir (cairo_perf_t *perf,
|
2009-06-30 20:46:12 +01:00
|
|
|
const cairo_boilerplate_target_t *target,
|
2010-06-24 14:59:18 +03:00
|
|
|
const char *dirname)
|
2009-06-30 20:46:12 +01:00
|
|
|
{
|
|
|
|
|
DIR *dir;
|
|
|
|
|
struct dirent *de;
|
|
|
|
|
int num_traces = 0;
|
2009-08-22 18:59:01 +01:00
|
|
|
cairo_bool_t force;
|
|
|
|
|
cairo_bool_t is_explicit;
|
2009-06-30 20:46:12 +01:00
|
|
|
|
|
|
|
|
dir = opendir (dirname);
|
2009-07-29 16:17:36 +01:00
|
|
|
if (dir == NULL)
|
2009-06-30 20:46:12 +01:00
|
|
|
return 0;
|
|
|
|
|
|
2009-08-22 18:59:01 +01:00
|
|
|
force = FALSE;
|
|
|
|
|
if (cairo_perf_can_run (perf, dirname, &is_explicit))
|
|
|
|
|
force = is_explicit;
|
|
|
|
|
|
2009-06-30 20:46:12 +01:00
|
|
|
while ((de = readdir (dir)) != NULL) {
|
|
|
|
|
char *trace;
|
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
|
|
if (de->d_name[0] == '.')
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
xasprintf (&trace, "%s/%s", dirname, de->d_name);
|
|
|
|
|
if (stat (trace, &st) != 0)
|
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
|
|
if (S_ISDIR(st.st_mode)) {
|
|
|
|
|
num_traces += cairo_perf_trace_dir (perf, target, trace);
|
|
|
|
|
} else {
|
|
|
|
|
const char *dot;
|
|
|
|
|
|
|
|
|
|
dot = strrchr (de->d_name, '.');
|
|
|
|
|
if (dot == NULL)
|
|
|
|
|
goto next;
|
|
|
|
|
if (strcmp (dot, ".trace"))
|
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
|
|
num_traces++;
|
2009-08-22 18:59:01 +01:00
|
|
|
if (!force && ! cairo_perf_can_run (perf, de->d_name, NULL))
|
2009-06-30 20:46:12 +01:00
|
|
|
goto next;
|
|
|
|
|
|
|
|
|
|
cairo_perf_trace (perf, target, trace);
|
|
|
|
|
}
|
|
|
|
|
next:
|
|
|
|
|
free (trace);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
closedir (dir);
|
|
|
|
|
|
|
|
|
|
return num_traces;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-02 13:08:25 +01:00
|
|
|
int
|
2010-06-24 14:59:18 +03:00
|
|
|
main (int argc,
|
|
|
|
|
char *argv[])
|
2009-06-02 13:08:25 +01:00
|
|
|
{
|
|
|
|
|
cairo_perf_t perf;
|
2009-07-20 22:17:30 +01:00
|
|
|
const char *trace_dir = "cairo-traces:/usr/src/cairo-traces:/usr/share/cairo-traces";
|
2009-06-02 13:08:25 +01:00
|
|
|
unsigned int n;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
parse_options (&perf, argc, argv);
|
|
|
|
|
|
2009-06-04 10:03:45 +01:00
|
|
|
signal (SIGINT, interrupt);
|
|
|
|
|
|
2009-06-02 13:08:25 +01:00
|
|
|
if (getenv ("CAIRO_TRACE_DIR") != NULL)
|
|
|
|
|
trace_dir = getenv ("CAIRO_TRACE_DIR");
|
|
|
|
|
|
|
|
|
|
perf.targets = cairo_boilerplate_get_targets (&perf.num_targets, NULL);
|
2011-08-31 17:55:07 +02:00
|
|
|
perf.times = xmalloc (6 * perf.iterations * sizeof (cairo_time_t));
|
2009-06-02 13:08:25 +01:00
|
|
|
|
|
|
|
|
/* do we have a list of filenames? */
|
2009-06-30 20:46:12 +01:00
|
|
|
perf.exact_names = have_trace_filenames (&perf);
|
2009-06-02 13:08:25 +01:00
|
|
|
|
|
|
|
|
for (i = 0; i < perf.num_targets; i++) {
|
2010-06-24 14:59:18 +03:00
|
|
|
const cairo_boilerplate_target_t *target = perf.targets[i];
|
2009-06-02 13:08:25 +01:00
|
|
|
|
2010-03-27 21:52:16 +00:00
|
|
|
if (! perf.list_only && ! target->is_measurable)
|
2009-06-02 13:08:25 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
perf.target = target;
|
|
|
|
|
perf.test_number = 0;
|
2010-10-20 14:26:37 +01:00
|
|
|
perf.has_described_backend = FALSE;
|
2009-06-02 13:08:25 +01:00
|
|
|
|
2009-06-30 20:46:12 +01:00
|
|
|
if (perf.exact_names) {
|
2009-06-02 13:08:25 +01:00
|
|
|
for (n = 0; n < perf.num_names; n++) {
|
2009-06-30 20:46:12 +01:00
|
|
|
struct stat st;
|
2009-06-02 13:08:25 +01:00
|
|
|
|
2009-06-30 20:46:12 +01:00
|
|
|
if (stat (perf.names[n], &st) == 0) {
|
|
|
|
|
if (S_ISDIR (st.st_mode)) {
|
|
|
|
|
cairo_perf_trace_dir (&perf, target, perf.names[n]);
|
|
|
|
|
} else
|
|
|
|
|
cairo_perf_trace (&perf, target, perf.names[n]);
|
|
|
|
|
}
|
2009-06-02 13:08:25 +01:00
|
|
|
}
|
2009-06-30 20:46:12 +01:00
|
|
|
} else {
|
2009-07-20 22:17:30 +01:00
|
|
|
int num_traces = 0;
|
|
|
|
|
const char *dir;
|
|
|
|
|
|
|
|
|
|
dir = trace_dir;
|
|
|
|
|
do {
|
|
|
|
|
char buf[1024];
|
|
|
|
|
const char *end = strchr (dir, ':');
|
|
|
|
|
if (end != NULL) {
|
|
|
|
|
memcpy (buf, dir, end-dir);
|
|
|
|
|
buf[end-dir] = '\0';
|
|
|
|
|
end++;
|
|
|
|
|
|
|
|
|
|
dir = buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
num_traces += cairo_perf_trace_dir (&perf, target, dir);
|
|
|
|
|
dir = end;
|
|
|
|
|
} while (dir != NULL);
|
|
|
|
|
|
|
|
|
|
if (num_traces == 0) {
|
2009-06-02 21:54:44 +01:00
|
|
|
warn_no_traces ("Found no traces in", trace_dir);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2009-06-02 13:08:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (perf.list_only)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_perf_fini (&perf);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|