1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
3 * Copyright © 2006 Mozilla Corporation
4 * Copyright © 2006 Red Hat, Inc.
5 * Copyright © 2009 Chris Wilson
6 * Copyright © 2011 Intel Corporation
8 * Permission to use, copy, modify, distribute, and sell this software
9 * and its documentation for any purpose is hereby granted without
10 * fee, provided that the above copyright notice appear in all copies
11 * and that both that copyright notice and this permission notice
12 * appear in supporting documentation, and that the name of
13 * the authors not be used in advertising or publicity pertaining to
14 * distribution of the software without specific, written prior
15 * permission. The authors make no representations about the
16 * suitability of this software for any purpose. It is provided "as
17 * is" without express or implied warranty.
19 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
20 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
22 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
23 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
24 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
25 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
27 * Authors: Vladimir Vukicevic <vladimir@pobox.com>
28 * Carl Worth <cworth@cworth.org>
29 * Chris Wilson <chris@chris-wilson.co.uk>
32 #define _GNU_SOURCE 1 /* for sched_getaffinity() and getline() */
34 #include "../cairo-version.h" /* for the real version */
36 #include "cairo-perf.h"
37 #include "cairo-stats.h"
39 #include "cairo-boilerplate-getopt.h"
40 #include <cairo-script-interpreter.h>
41 #include "cairo-missing.h"
43 /* rudely reuse bits of the library... */
44 #include "../src/cairo-error-private.h"
50 #include <ctype.h> /* isspace() */
52 #include <sys/types.h>
56 #include "dirent-win32.h"
59 basename_no_ext (char *path)
61 static char name[_MAX_FNAME + 1];
63 _splitpath (path, NULL, NULL, name, NULL);
65 name[_MAX_FNAME] = '\0';
75 basename_no_ext (char *path)
79 name = basename (path);
81 dot = strchr (name, '.');
97 #include <fontconfig/fontconfig.h>
101 const cairo_boilerplate_target_t *target;
103 cairo_surface_t *surface;
107 cairo_perf_can_run (cairo_perf_t *perf,
109 cairo_bool_t *is_explicit)
116 *is_explicit = FALSE;
118 if (perf->exact_names) {
124 if (perf->num_names == 0 && perf->num_exclude_names == 0)
127 copy = xstrdup (name);
128 dot = strchr (copy, '.');
132 if (perf->num_names) {
134 for (i = 0; i < perf->num_names; i++)
135 if (strstr (copy, perf->names[i])) {
137 *is_explicit = strcmp (copy, perf->names[i]) == 0;
146 if (perf->num_exclude_names) {
148 for (i = 0; i < perf->num_exclude_names; i++)
149 if (strstr (copy, perf->exclude_names[i])) {
151 *is_explicit = strcmp (copy, perf->exclude_names[i]) == 0;
165 static cairo_surface_t *
166 surface_create (void *closure,
167 cairo_content_t content,
172 struct trace *args = closure;
173 return cairo_surface_create_similar (args->surface, content, width, height);
176 static int user_interrupt;
181 if (user_interrupt) {
182 signal (sig, SIG_DFL);
190 describe (cairo_perf_t *perf,
193 char *description = NULL;
195 if (perf->has_described_backend)
197 perf->has_described_backend = TRUE;
199 if (perf->target->describe)
200 description = perf->target->describe (closure);
202 if (description == NULL)
209 execute (cairo_perf_t *perf,
213 char *trace_cpy, *name;
214 const cairo_script_interpreter_hooks_t hooks = {
216 .surface_create = surface_create,
219 trace_cpy = xstrdup (trace);
220 name = basename_no_ext (trace_cpy);
222 if (perf->list_only) {
223 printf ("%s\n", name);
228 describe (perf, args->closure);
231 cairo_script_interpreter_t *csi;
232 cairo_status_t status;
233 unsigned int line_no;
235 csi = cairo_script_interpreter_create ();
236 cairo_script_interpreter_install_hooks (csi, &hooks);
238 cairo_script_interpreter_run (csi, trace);
240 cairo_script_interpreter_finish (csi);
242 line_no = cairo_script_interpreter_get_line_number (csi);
243 status = cairo_script_interpreter_destroy (csi);
245 /* XXXX cairo_status_to_string is just wrong! */
246 fprintf (stderr, "Error during replay, line %d: %s\n",
247 line_no, cairo_status_to_string (status));
256 usage (const char *argv0)
259 "Usage: %s [-l] [-i iterations] [-x exclude-file] [test-names ... | traces ...]\n"
261 "Run the cairo trace analysis suite over the given tests (all by default)\n"
262 "The command-line arguments are interpreted as follows:\n"
264 " -i iterations; specify the number of iterations per test case\n"
265 " -l list only; just list selected test case names without executing\n"
266 " -x exclude; specify a file to read a list of traces to exclude\n"
268 "If test names are given they are used as sub-string matches so a command\n"
269 "such as \"%s firefox\" can be used to run all firefox traces.\n"
270 "Alternatively, you can specify a list of filenames to execute.\n",
275 read_excludes (cairo_perf_t *perf,
276 const char *filename)
280 size_t line_size = 0;
283 file = fopen (filename, "r");
287 while (getline (&line, &line_size, file) != -1) {
288 /* terminate the line at a comment marker '#' */
289 s = strchr (line, '#');
293 /* whitespace delimits */
295 while (*s != '\0' && isspace (*s))
299 while (*t != '\0' && ! isspace (*t))
303 int i = perf->num_exclude_names;
304 perf->exclude_names = xrealloc (perf->exclude_names,
305 sizeof (char *) * (i+1));
306 perf->exclude_names[i] = strndup (s, t-s);
307 perf->num_exclude_names++;
318 parse_options (cairo_perf_t *perf,
325 perf->list_only = FALSE;
328 perf->exclude_names = NULL;
329 perf->num_exclude_names = 0;
332 c = _cairo_getopt (argc, argv, "i:lx:");
338 perf->exact_iterations = TRUE;
339 perf->iterations = strtoul (optarg, &end, 10);
341 fprintf (stderr, "Invalid argument for -i (not an integer): %s\n",
347 perf->list_only = TRUE;
350 if (! read_excludes (perf, optarg)) {
351 fprintf (stderr, "Invalid argument for -x (not readable file): %s\n",
357 fprintf (stderr, "Internal error: unhandled option: %c\n", c);
366 perf->names = &argv[optind];
367 perf->num_names = argc - optind;
372 cairo_perf_fini (cairo_perf_t *perf)
374 cairo_boilerplate_free_targets (perf->targets);
375 cairo_boilerplate_fini ();
377 cairo_debug_reset_static_data ();
384 have_trace_filenames (cairo_perf_t *perf)
388 if (perf->num_names == 0)
392 for (i = 0; i < perf->num_names; i++)
393 if (access (perf->names[i], R_OK) == 0)
400 static cairo_status_t
401 print (void *closure, const unsigned char *data, unsigned int length)
403 fwrite (data, length, 1, closure);
404 return CAIRO_STATUS_SUCCESS;
408 cairo_perf_trace (cairo_perf_t *perf,
409 const cairo_boilerplate_target_t *target,
413 cairo_surface_t *real;
415 args.target = target;
416 real = target->create_surface (NULL,
417 CAIRO_CONTENT_COLOR_ALPHA,
420 CAIRO_BOILERPLATE_MODE_PERF,
423 cairo_surface_create_observer (real,
424 CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS);
425 cairo_surface_destroy (real);
426 if (cairo_surface_status (args.surface)) {
428 "Error: Failed to create target surface: %s\n",
433 printf ("Observing '%s'...", trace);
436 execute (perf, &args, trace);
439 cairo_device_observer_print (cairo_surface_get_device (args.surface),
443 cairo_surface_destroy (args.surface);
446 target->cleanup (args.closure);
450 warn_no_traces (const char *message,
451 const char *trace_dir)
455 "Have you cloned the cairo-traces repository and uncompressed the traces?\n"
456 " git clone git://anongit.freedesktop.org/cairo-traces\n"
457 " cd cairo-traces && make\n"
458 "Or set the env.var CAIRO_TRACE_DIR to point to your traces?\n",
463 cairo_perf_trace_dir (cairo_perf_t *perf,
464 const cairo_boilerplate_target_t *target,
471 cairo_bool_t is_explicit;
473 dir = opendir (dirname);
478 if (cairo_perf_can_run (perf, dirname, &is_explicit))
481 while ((de = readdir (dir)) != NULL) {
485 if (de->d_name[0] == '.')
488 xasprintf (&trace, "%s/%s", dirname, de->d_name);
489 if (stat (trace, &st) != 0)
492 if (S_ISDIR(st.st_mode)) {
493 num_traces += cairo_perf_trace_dir (perf, target, trace);
497 dot = strrchr (de->d_name, '.');
500 if (strcmp (dot, ".trace"))
504 if (!force && ! cairo_perf_can_run (perf, de->d_name, NULL))
507 cairo_perf_trace (perf, target, trace);
523 const char *trace_dir = "cairo-traces:/usr/src/cairo-traces:/usr/share/cairo-traces";
527 parse_options (&perf, argc, argv);
529 signal (SIGINT, interrupt);
531 if (getenv ("CAIRO_TRACE_DIR") != NULL)
532 trace_dir = getenv ("CAIRO_TRACE_DIR");
534 perf.targets = cairo_boilerplate_get_targets (&perf.num_targets, NULL);
536 /* do we have a list of filenames? */
537 perf.exact_names = have_trace_filenames (&perf);
539 for (i = 0; i < perf.num_targets; i++) {
540 const cairo_boilerplate_target_t *target = perf.targets[i];
542 if (! perf.list_only && ! target->is_measurable)
545 perf.target = target;
546 perf.has_described_backend = FALSE;
548 if (perf.exact_names) {
549 for (n = 0; n < perf.num_names; n++) {
552 if (stat (perf.names[n], &st) == 0) {
553 if (S_ISDIR (st.st_mode)) {
554 cairo_perf_trace_dir (&perf, target, perf.names[n]);
556 cairo_perf_trace (&perf, target, perf.names[n]);
566 const char *end = strchr (dir, ':');
568 memcpy (buf, dir, end-dir);
575 num_traces += cairo_perf_trace_dir (&perf, target, dir);
577 } while (dir != NULL);
579 if (num_traces == 0) {
580 warn_no_traces ("Found no traces in", trace_dir);
589 cairo_perf_fini (&perf);