diff options
author | sanine <sanine.not@pm.me> | 2022-10-12 12:03:23 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-10-12 12:03:23 -0500 |
commit | 530ffd0b7d3c39757b20f00716e486b5caf89aff (patch) | |
tree | 76b35fdf57317038acf6b828871f6ae25fce2ebe /libs/cairo-1.16.0/perf/cairo-analyse-trace.c | |
parent | 3dbe9332e47c143a237db12440f134caebd1cfbe (diff) |
add cairo
Diffstat (limited to 'libs/cairo-1.16.0/perf/cairo-analyse-trace.c')
-rw-r--r-- | libs/cairo-1.16.0/perf/cairo-analyse-trace.c | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/libs/cairo-1.16.0/perf/cairo-analyse-trace.c b/libs/cairo-1.16.0/perf/cairo-analyse-trace.c new file mode 100644 index 0000000..9941486 --- /dev/null +++ b/libs/cairo-1.16.0/perf/cairo-analyse-trace.c @@ -0,0 +1,592 @@ +/* -*- 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 + * Copyright © 2011 Intel Corporation + * + * 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> + * Carl Worth <cworth@cworth.org> + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#define _GNU_SOURCE 1 /* for sched_getaffinity() and getline() */ + +#include "../cairo-version.h" /* for the real version */ + +#include "cairo-perf.h" +#include "cairo-stats.h" + +#include "cairo-boilerplate-getopt.h" +#include <cairo-script-interpreter.h> +#include "cairo-missing.h" + +/* rudely reuse bits of the library... */ +#include "../src/cairo-error-private.h" + +/* For basename */ +#ifdef HAVE_LIBGEN_H +#include <libgen.h> +#endif +#include <ctype.h> /* isspace() */ + +#include <sys/types.h> +#include <sys/stat.h> + +#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 +#include <dirent.h> + +static char * +basename_no_ext (char *path) +{ + char *dot, *name; + + name = basename (path); + + dot = strchr (name, '.'); + if (dot) + *dot = '\0'; + + return name; +} + +#endif + +#if HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <signal.h> + +#if HAVE_FCFINI +#include <fontconfig/fontconfig.h> +#endif + +struct trace { + const cairo_boilerplate_target_t *target; + void *closure; + cairo_surface_t *surface; +}; + +cairo_bool_t +cairo_perf_can_run (cairo_perf_t *perf, + const char *name, + cairo_bool_t *is_explicit) +{ + unsigned int i; + char *copy, *dot; + cairo_bool_t ret; + + if (is_explicit) + *is_explicit = FALSE; + + if (perf->exact_names) { + if (is_explicit) + *is_explicit = TRUE; + return TRUE; + } + + if (perf->num_names == 0 && perf->num_exclude_names == 0) + return TRUE; + + copy = xstrdup (name); + dot = strchr (copy, '.'); + if (dot != NULL) + *dot = '\0'; + + if (perf->num_names) { + ret = TRUE; + for (i = 0; i < perf->num_names; i++) + if (strstr (copy, perf->names[i])) { + if (is_explicit) + *is_explicit = strcmp (copy, perf->names[i]) == 0; + goto check_exclude; + } + + ret = FALSE; + goto done; + } + +check_exclude: + if (perf->num_exclude_names) { + ret = FALSE; + for (i = 0; i < perf->num_exclude_names; i++) + if (strstr (copy, perf->exclude_names[i])) { + if (is_explicit) + *is_explicit = strcmp (copy, perf->exclude_names[i]) == 0; + goto done; + } + + ret = TRUE; + goto done; + } + +done: + free (copy); + + return ret; +} + +static cairo_surface_t * +surface_create (void *closure, + cairo_content_t content, + double width, + double height, + long uid) +{ + struct trace *args = closure; + return cairo_surface_create_similar (args->surface, content, width, height); +} + +static int user_interrupt; + +static void +interrupt (int sig) +{ + if (user_interrupt) { + signal (sig, SIG_DFL); + raise (sig); + } + + user_interrupt = 1; +} + +static void +describe (cairo_perf_t *perf, + void *closure) +{ + char *description = NULL; + + if (perf->has_described_backend) + return; + perf->has_described_backend = TRUE; + + if (perf->target->describe) + description = perf->target->describe (closure); + + if (description == NULL) + return; + + free (description); +} + +static void +execute (cairo_perf_t *perf, + struct trace *args, + const char *trace) +{ + char *trace_cpy, *name; + const cairo_script_interpreter_hooks_t hooks = { + .closure = args, + .surface_create = surface_create, + }; + + trace_cpy = xstrdup (trace); + name = basename_no_ext (trace_cpy); + + if (perf->list_only) { + printf ("%s\n", name); + free (trace_cpy); + return; + } + + describe (perf, args->closure); + + { + cairo_script_interpreter_t *csi; + cairo_status_t status; + unsigned int line_no; + + csi = cairo_script_interpreter_create (); + cairo_script_interpreter_install_hooks (csi, &hooks); + + cairo_script_interpreter_run (csi, trace); + + cairo_script_interpreter_finish (csi); + + line_no = cairo_script_interpreter_get_line_number (csi); + status = cairo_script_interpreter_destroy (csi); + if (status) { + /* XXXX cairo_status_to_string is just wrong! */ + fprintf (stderr, "Error during replay, line %d: %s\n", + line_no, cairo_status_to_string (status)); + } + } + user_interrupt = 0; + + free (trace_cpy); +} + +static void +usage (const char *argv0) +{ + fprintf (stderr, +"Usage: %s [-l] [-i iterations] [-x exclude-file] [test-names ... | traces ...]\n" +"\n" +"Run the cairo trace analysis suite over the given tests (all by default)\n" +"The command-line arguments are interpreted as follows:\n" +"\n" +" -i iterations; specify the number of iterations per test case\n" +" -l list only; just list selected test case names without executing\n" +" -x exclude; specify a file to read a list of traces to exclude\n" +"\n" +"If test names are given they are used as sub-string matches so a command\n" +"such as \"%s firefox\" can be used to run all firefox traces.\n" +"Alternatively, you can specify a list of filenames to execute.\n", + argv0, argv0); +} + +static cairo_bool_t +read_excludes (cairo_perf_t *perf, + const char *filename) +{ + 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; + while (*s != '\0' && isspace (*s)) + s++; + + t = s; + while (*t != '\0' && ! isspace (*t)) + 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++; + } + } + free (line); + + fclose (file); + + return TRUE; +} + +static void +parse_options (cairo_perf_t *perf, + int argc, + char *argv[]) +{ + char *end; + int c; + + perf->list_only = FALSE; + perf->names = NULL; + perf->num_names = 0; + perf->exclude_names = NULL; + perf->num_exclude_names = 0; + + while (1) { + c = _cairo_getopt (argc, argv, "i:lx:"); + if (c == -1) + break; + + switch (c) { + 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 'x': + if (! read_excludes (perf, optarg)) { + fprintf (stderr, "Invalid argument for -x (not readable file): %s\n", + optarg); + exit (1); + } + break; + default: + fprintf (stderr, "Internal error: unhandled option: %c\n", c); + /* fall-through */ + case '?': + usage (argv[0]); + exit (1); + } + } + + if (optind < argc) { + perf->names = &argv[optind]; + perf->num_names = argc - optind; + } +} + +static void +cairo_perf_fini (cairo_perf_t *perf) +{ + cairo_boilerplate_free_targets (perf->targets); + cairo_boilerplate_fini (); + + 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; + +#if HAVE_UNISTD_H + for (i = 0; i < perf->num_names; i++) + if (access (perf->names[i], R_OK) == 0) + return TRUE; +#endif + + return FALSE; +} + +static cairo_status_t +print (void *closure, const unsigned char *data, unsigned int length) +{ + fwrite (data, length, 1, closure); + return CAIRO_STATUS_SUCCESS; +} + +static void +cairo_perf_trace (cairo_perf_t *perf, + const cairo_boilerplate_target_t *target, + const char *trace) +{ + struct trace args; + cairo_surface_t *real; + + args.target = target; + real = target->create_surface (NULL, + CAIRO_CONTENT_COLOR_ALPHA, + 1, 1, + 1, 1, + CAIRO_BOILERPLATE_MODE_PERF, + &args.closure); + args.surface = + cairo_surface_create_observer (real, + CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS); + cairo_surface_destroy (real); + if (cairo_surface_status (args.surface)) { + fprintf (stderr, + "Error: Failed to create target surface: %s\n", + target->name); + return; + } + + printf ("Observing '%s'...", trace); + fflush (stdout); + + execute (perf, &args, trace); + + printf ("\n"); + cairo_device_observer_print (cairo_surface_get_device (args.surface), + print, stdout); + fflush (stdout); + + cairo_surface_destroy (args.surface); + + if (target->cleanup) + target->cleanup (args.closure); +} + +static void +warn_no_traces (const char *message, + const char *trace_dir) +{ + fprintf (stderr, +"Error: %s '%s'.\n" +"Have you cloned the cairo-traces repository and uncompressed the traces?\n" +" git clone git://anongit.freedesktop.org/cairo-traces\n" +" cd cairo-traces && make\n" +"Or set the env.var CAIRO_TRACE_DIR to point to your traces?\n", + message, trace_dir); +} + +static int +cairo_perf_trace_dir (cairo_perf_t *perf, + const cairo_boilerplate_target_t *target, + const char *dirname) +{ + DIR *dir; + struct dirent *de; + int num_traces = 0; + cairo_bool_t force; + cairo_bool_t is_explicit; + + dir = opendir (dirname); + if (dir == NULL) + return 0; + + force = FALSE; + if (cairo_perf_can_run (perf, dirname, &is_explicit)) + force = is_explicit; + + 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++; + if (!force && ! cairo_perf_can_run (perf, de->d_name, NULL)) + goto next; + + cairo_perf_trace (perf, target, trace); + } +next: + free (trace); + + } + closedir (dir); + + return num_traces; +} + +int +main (int argc, + char *argv[]) +{ + cairo_perf_t perf; + const char *trace_dir = "cairo-traces:/usr/src/cairo-traces:/usr/share/cairo-traces"; + unsigned int n; + int i; + + parse_options (&perf, argc, argv); + + signal (SIGINT, interrupt); + + if (getenv ("CAIRO_TRACE_DIR") != NULL) + trace_dir = getenv ("CAIRO_TRACE_DIR"); + + perf.targets = cairo_boilerplate_get_targets (&perf.num_targets, NULL); + + /* do we have a list of filenames? */ + perf.exact_names = have_trace_filenames (&perf); + + for (i = 0; i < perf.num_targets; i++) { + const cairo_boilerplate_target_t *target = perf.targets[i]; + + if (! perf.list_only && ! target->is_measurable) + continue; + + perf.target = target; + perf.has_described_backend = FALSE; + + if (perf.exact_names) { + for (n = 0; n < perf.num_names; n++) { + struct stat st; + + 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]); + } + } + } else { + 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) { + warn_no_traces ("Found no traces in", trace_dir); + return 1; + } + } + + if (perf.list_only) + break; + } + + cairo_perf_fini (&perf); + + return 0; +} |