diff options
Diffstat (limited to 'libs/cairo-1.16.0/perf/cairo-perf-report.c')
-rw-r--r-- | libs/cairo-1.16.0/perf/cairo-perf-report.c | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/libs/cairo-1.16.0/perf/cairo-perf-report.c b/libs/cairo-1.16.0/perf/cairo-perf-report.c new file mode 100644 index 0000000..2325f47 --- /dev/null +++ b/libs/cairo-1.16.0/perf/cairo-perf-report.c @@ -0,0 +1,454 @@ +/* + * Copyright © 2006 Red Hat, Inc. + * + * 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: Carl Worth <cworth@cworth.org> + */ + +#define _GETDELIM 1/* for getline() on AIX */ + +#include "cairo-perf.h" +#include "cairo-missing.h" +#include "cairo-stats.h" + +/* We use _GNU_SOURCE for getline and strndup if available. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <math.h> +#include <assert.h> +#ifdef HAVE_LIBGEN_H +#include <libgen.h> +#endif + +#ifdef _MSC_VER +static long long +strtoll (const char *nptr, + char **endptr, + int base); + +static char * +basename (char *path); +#endif + +/* Ad-hoc parsing, macros with a strong dependence on the calling + * context, and plenty of other ugliness is here. But at least it's + * not perl... */ +#define parse_error(...) fprintf(stderr, __VA_ARGS__); return TEST_REPORT_STATUS_ERROR; +#define skip_char(c) \ +do { \ + if (*s && *s == (c)) { \ + s++; \ + } else { \ + parse_error ("expected '%c' but found '%c'", c, *s); \ + } \ +} while (0) +#define skip_space() while (*s && (*s == ' ' || *s == '\t')) s++; +#define parse_int(result) \ +do { \ + (result) = strtol (s, &end, 10); \ + if (*s && end != s) { \ + s = end; \ + } else { \ + parse_error("expected integer but found %s", s); \ + } \ +} while (0) +#define parse_long_long(result) \ +do { \ + (result) = strtoll (s, &end, 10); \ + if (*s && end != s) { \ + s = end; \ + } else { \ + parse_error("expected integer but found %s", s); \ + } \ +} while (0) +#define parse_double(result) \ +do { \ + (result) = strtod (s, &end); \ + if (*s && end != s) { \ + s = end; \ + } else { \ + parse_error("expected floating-point value but found %s", s); \ + } \ +} while (0) +/* Here a string is simply a sequence of non-whitespace */ +#define parse_string(result) \ +do { \ + for (end = s; *end; end++) \ + if (isspace (*end)) \ + break; \ + (result) = strndup (s, end - s); \ + if ((result) == NULL) { \ + fprintf (stderr, "Out of memory.\n"); \ + exit (1); \ + } \ + s = end; \ +} while (0) + +static test_report_status_t +test_report_parse (test_report_t *report, + int fileno, + char *line, + char *configuration) +{ + char *end; + char *s = line; + cairo_bool_t is_raw = FALSE; + double min_time, median_time; + + /* The code here looks funny unless you understand that these are + * all macro calls, (and then the code just looks sick). */ + if (*s == '\n') + return TEST_REPORT_STATUS_COMMENT; + + skip_char ('['); + skip_space (); + if (*s == '#') + return TEST_REPORT_STATUS_COMMENT; + if (*s == '*') { + s++; + is_raw = TRUE; + } else { + parse_int (report->id); + } + skip_char (']'); + + skip_space (); + + report->fileno = fileno; + report->configuration = configuration; + parse_string (report->backend); + end = strrchr (report->backend, '.'); + if (end) + *end++ = '\0'; + report->content = end ? end : xstrdup ("???"); + + skip_space (); + + parse_string (report->name); + end = strrchr (report->name, '.'); + if (end) + *end++ = '\0'; + report->size = end ? atoi (end) : 0; + + skip_space (); + + report->samples = NULL; + report->samples_size = 0; + report->samples_count = 0; + + if (is_raw) { + parse_double (report->stats.ticks_per_ms); + skip_space (); + + report->samples_size = 5; + report->samples = xmalloc (report->samples_size * sizeof (cairo_time_t)); + report->stats.min_ticks = 0; + do { + if (report->samples_count == report->samples_size) { + report->samples_size *= 2; + report->samples = xrealloc (report->samples, + report->samples_size * sizeof (cairo_time_t)); + } + parse_long_long (report->samples[report->samples_count]); + if (report->samples_count == 0) { + report->stats.min_ticks = + report->samples[report->samples_count]; + } else if (report->stats.min_ticks > + report->samples[report->samples_count]){ + report->stats.min_ticks = + report->samples[report->samples_count]; + } + report->samples_count++; + skip_space (); + } while (*s && *s != '\n'); + report->stats.iterations = 0; + if (*s) skip_char ('\n'); + } else { + parse_double (report->stats.min_ticks); + skip_space (); + + parse_double (min_time); + report->stats.ticks_per_ms = report->stats.min_ticks / min_time; + + skip_space (); + + parse_double (median_time); + report->stats.median_ticks = median_time * report->stats.ticks_per_ms; + + skip_space (); + + parse_double (report->stats.std_dev); + report->stats.std_dev /= 100.0; + skip_char ('%'); + + skip_space (); + + parse_int (report->stats.iterations); + + skip_space (); + skip_char ('\n'); + } + + return TEST_REPORT_STATUS_SUCCESS; +} + +/* We provide hereafter a win32 implementation of the basename + * and strtoll functions which are not available otherwise. + * The basename function is fully compliant to its GNU specs. + */ +#ifdef _MSC_VER +long long +strtoll (const char *nptr, + char **endptr, + int base) +{ + return _atoi64(nptr); +} + +static char * +basename (char *path) +{ + char *end, *s; + + end = (path + strlen(path) - 1); + while (end && (end >= path + 1) && (*end == '/')) { + *end = '\0'; + end--; + } + + s = strrchr(path, '/'); + if (s) { + if (s == end) { + return s; + } else { + return s+1; + } + } else { + return path; + } +} +#endif /* ifndef _MSC_VER */ + +int +test_report_cmp_backend_then_name (const void *a, + const void *b) +{ + const test_report_t *a_test = a; + const test_report_t *b_test = b; + + int cmp; + + cmp = strcmp (a_test->backend, b_test->backend); + if (cmp) + return cmp; + + cmp = strcmp (a_test->content, b_test->content); + if (cmp) + return cmp; + + /* A NULL name is a list-termination marker, so force it last. */ + if (a_test->name == NULL) + if (b_test->name == NULL) + return 0; + else + return 1; + else if (b_test->name == NULL) + return -1; + + cmp = strcmp (a_test->name, b_test->name); + if (cmp) + return cmp; + + if (a_test->size < b_test->size) + return -1; + if (a_test->size > b_test->size) + return 1; + + return 0; +} + +int +test_report_cmp_name (const void *a, + const void *b) +{ + const test_report_t *a_test = a; + const test_report_t *b_test = b; + + int cmp; + + /* A NULL name is a list-termination marker, so force it last. */ + if (a_test->name == NULL) + if (b_test->name == NULL) + return 0; + else + return 1; + else if (b_test->name == NULL) + return -1; + + cmp = strcmp (a_test->name, b_test->name); + if (cmp) + return cmp; + + if (a_test->size < b_test->size) + return -1; + if (a_test->size > b_test->size) + return 1; + + return 0; +} + +void +cairo_perf_report_sort_and_compute_stats (cairo_perf_report_t *report, + int (*cmp) (const void*, const void*)) +{ + test_report_t *base, *next, *last, *t; + + if (cmp == NULL) + cmp = test_report_cmp_backend_then_name; + + /* First we sort, since the diff needs both lists in the same + * order */ + qsort (report->tests, report->tests_count, sizeof (test_report_t), cmp); + + /* The sorting also brings all related raw reports together so we + * can condense them and compute the stats. + */ + base = &report->tests[0]; + last = &report->tests[report->tests_count - 1]; + while (base <= last) { + next = base+1; + if (next <= last) { + while (next <= last && + test_report_cmp_backend_then_name (base, next) == 0) + { + next++; + } + if (next != base) { + unsigned int new_samples_count = base->samples_count; + for (t = base + 1; t < next; t++) + new_samples_count += t->samples_count; + if (new_samples_count > base->samples_size) { + base->samples_size = new_samples_count; + base->samples = xrealloc (base->samples, + base->samples_size * sizeof (cairo_time_t)); + } + for (t = base + 1; t < next; t++) { + memcpy (&base->samples[base->samples_count], t->samples, + t->samples_count * sizeof (cairo_time_t)); + base->samples_count += t->samples_count; + } + } + } + if (base->samples) + _cairo_stats_compute (&base->stats, base->samples, base->samples_count); + base = next; + } +} + +void +cairo_perf_report_load (cairo_perf_report_t *report, + const char *filename, int id, + int (*cmp) (const void *, const void *)) +{ + FILE *file; + test_report_status_t status; + int line_number = 0; + char *line = NULL; + size_t line_size = 0; + char *configuration; + char *dot; + char *baseName; + const char *name; + + name = filename; + if (name == NULL) + name = "stdin"; + + configuration = xstrdup (name); + baseName = basename (configuration); + report->configuration = xstrdup (baseName); + free (configuration); + + dot = strrchr (report->configuration, '.'); + if (dot) + *dot = '\0'; + + report->name = name; + report->tests_size = 16; + report->tests = xmalloc (report->tests_size * sizeof (test_report_t)); + report->tests_count = 0; + report->fileno = id; + + if (filename == NULL) { + file = stdin; + } else { + file = fopen (filename, "r"); + if (file == NULL) { + fprintf (stderr, "Failed to open %s: %s\n", + filename, strerror (errno)); + exit (1); + } + } + + while (1) { + if (report->tests_count == report->tests_size) { + report->tests_size *= 2; + report->tests = xrealloc (report->tests, + report->tests_size * sizeof (test_report_t)); + } + + line_number++; + if (getline (&line, &line_size, file) == -1) + break; + + status = test_report_parse (&report->tests[report->tests_count], + id, line, report->configuration); + if (status == TEST_REPORT_STATUS_ERROR) + fprintf (stderr, "Ignoring unrecognized line %d of %s:\n%s", + line_number, filename, line); + if (status == TEST_REPORT_STATUS_SUCCESS) + report->tests_count++; + /* Do nothing on TEST_REPORT_STATUS_COMMENT */ + } + + free (line); + + if (filename != NULL) + fclose (file); + + cairo_perf_report_sort_and_compute_stats (report, cmp); + + /* Add one final report with a NULL name to terminate the list. */ + if (report->tests_count == report->tests_size) { + report->tests_size *= 2; + report->tests = xrealloc (report->tests, + report->tests_size * sizeof (test_report_t)); + } + report->tests[report->tests_count].name = NULL; +} |