2 * Copyright © 2006 Red Hat, Inc.
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of the
9 * copyright holders not be used in advertising or publicity
10 * pertaining to distribution of the software without specific,
11 * written prior permission. The copyright holders make no
12 * representations about the suitability of this software for any
13 * purpose. It is provided "as is" without express or implied
16 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
17 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
21 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
22 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
25 * Authors: Carl Worth <cworth@cworth.org>
28 #include "cairo-missing.h"
29 #include "cairo-perf.h"
30 #include "cairo-stats.h"
32 /* We use _GNU_SOURCE for getline and strndup if available. */
49 strtoll (const char *nptr,
54 basename (char *path);
57 /* Ad-hoc parsing, macros with a strong dependence on the calling
58 * context, and plenty of other ugliness is here. But at least it's
60 #define parse_error(...) fprintf(stderr, __VA_ARGS__); return TEST_REPORT_STATUS_ERROR;
61 #define skip_char(c) \
63 if (*s && *s == (c)) { \
66 parse_error ("expected '%c' but found '%c'", c, *s); \
69 #define skip_space() while (*s && (*s == ' ' || *s == '\t')) s++;
70 #define parse_int(result) \
72 (result) = strtol (s, &end, 10); \
73 if (*s && end != s) { \
76 parse_error("expected integer but found %s", s); \
79 #define parse_long_long(result) \
81 (result) = strtoll (s, &end, 10); \
82 if (*s && end != s) { \
85 parse_error("expected integer but found %s", s); \
88 #define parse_double(result) \
90 (result) = strtod (s, &end); \
91 if (*s && end != s) { \
94 parse_error("expected floating-point value but found %s", s); \
97 /* Here a string is simply a sequence of non-whitespace */
98 #define parse_string(result) \
100 for (end = s; *end; end++) \
101 if (isspace (*end)) \
103 (result) = strndup (s, end - s); \
104 if ((result) == NULL) { \
105 fprintf (stderr, "Out of memory.\n"); \
111 static test_report_status_t
112 test_report_parse (test_report_t *report,
119 cairo_bool_t is_raw = FALSE;
120 double min_time, median_time;
122 /* The code here looks funny unless you understand that these are
123 * all macro calls, (and then the code just looks sick). */
125 return TEST_REPORT_STATUS_COMMENT;
130 return TEST_REPORT_STATUS_COMMENT;
135 parse_int (report->id);
141 report->fileno = fileno;
142 report->configuration = configuration;
143 parse_string (report->backend);
144 end = strrchr (report->backend, '.');
147 report->content = end ? end : xstrdup ("???");
151 parse_string (report->name);
152 end = strrchr (report->name, '.');
155 report->size = end ? atoi (end) : 0;
159 report->samples = NULL;
160 report->samples_size = 0;
161 report->samples_count = 0;
164 parse_double (report->stats.ticks_per_ms);
167 report->samples_size = 5;
168 report->samples = xmalloc (report->samples_size * sizeof (cairo_time_t));
169 report->stats.min_ticks = 0;
171 if (report->samples_count == report->samples_size) {
172 report->samples_size *= 2;
173 report->samples = xrealloc (report->samples,
174 report->samples_size * sizeof (cairo_time_t));
176 parse_long_long (report->samples[report->samples_count]);
177 if (report->samples_count == 0) {
178 report->stats.min_ticks =
179 report->samples[report->samples_count];
180 } else if (report->stats.min_ticks >
181 report->samples[report->samples_count]){
182 report->stats.min_ticks =
183 report->samples[report->samples_count];
185 report->samples_count++;
187 } while (*s && *s != '\n');
188 report->stats.iterations = 0;
191 parse_double (report->stats.min_ticks);
194 parse_double (min_time);
195 report->stats.ticks_per_ms = report->stats.min_ticks / min_time;
199 parse_double (median_time);
200 report->stats.median_ticks = median_time * report->stats.ticks_per_ms;
204 parse_double (report->stats.std_dev);
205 report->stats.std_dev /= 100.0;
210 parse_int (report->stats.iterations);
216 return TEST_REPORT_STATUS_SUCCESS;
219 /* We provide hereafter a win32 implementation of the basename
220 * and strtoll functions which are not available otherwise.
221 * The basename function is fully compliant to its GNU specs.
225 strtoll (const char *nptr,
229 return _atoi64(nptr);
233 basename (char *path)
237 end = (path + strlen(path) - 1);
238 while (end && (end >= path + 1) && (*end == '/')) {
243 s = strrchr(path, '/');
254 #endif /* ifndef _MSC_VER */
257 test_report_cmp_backend_then_name (const void *a,
260 const test_report_t *a_test = a;
261 const test_report_t *b_test = b;
265 cmp = strcmp (a_test->backend, b_test->backend);
269 cmp = strcmp (a_test->content, b_test->content);
273 /* A NULL name is a list-termination marker, so force it last. */
274 if (a_test->name == NULL)
275 if (b_test->name == NULL)
279 else if (b_test->name == NULL)
282 cmp = strcmp (a_test->name, b_test->name);
286 if (a_test->size < b_test->size)
288 if (a_test->size > b_test->size)
295 test_report_cmp_name (const void *a,
298 const test_report_t *a_test = a;
299 const test_report_t *b_test = b;
303 /* A NULL name is a list-termination marker, so force it last. */
304 if (a_test->name == NULL)
305 if (b_test->name == NULL)
309 else if (b_test->name == NULL)
312 cmp = strcmp (a_test->name, b_test->name);
316 if (a_test->size < b_test->size)
318 if (a_test->size > b_test->size)
325 cairo_perf_report_sort_and_compute_stats (cairo_perf_report_t *report,
326 int (*cmp) (const void*, const void*))
328 test_report_t *base, *next, *last, *t;
331 cmp = test_report_cmp_backend_then_name;
333 /* First we sort, since the diff needs both lists in the same
335 qsort (report->tests, report->tests_count, sizeof (test_report_t), cmp);
337 /* The sorting also brings all related raw reports together so we
338 * can condense them and compute the stats.
340 base = &report->tests[0];
341 last = &report->tests[report->tests_count - 1];
342 while (base <= last) {
345 while (next <= last &&
346 test_report_cmp_backend_then_name (base, next) == 0)
351 unsigned int new_samples_count = base->samples_count;
352 for (t = base + 1; t < next; t++)
353 new_samples_count += t->samples_count;
354 if (new_samples_count > base->samples_size) {
355 base->samples_size = new_samples_count;
356 base->samples = xrealloc (base->samples,
357 base->samples_size * sizeof (cairo_time_t));
359 for (t = base + 1; t < next; t++) {
360 memcpy (&base->samples[base->samples_count], t->samples,
361 t->samples_count * sizeof (cairo_time_t));
362 base->samples_count += t->samples_count;
367 _cairo_stats_compute (&base->stats, base->samples, base->samples_count);
373 cairo_perf_report_load (cairo_perf_report_t *report,
374 const char *filename, int id,
375 int (*cmp) (const void *, const void *))
378 test_report_status_t status;
381 size_t line_size = 0;
391 configuration = xstrdup (name);
392 baseName = basename (configuration);
393 report->configuration = xstrdup (baseName);
394 free (configuration);
396 dot = strrchr (report->configuration, '.');
401 report->tests_size = 16;
402 report->tests = xmalloc (report->tests_size * sizeof (test_report_t));
403 report->tests_count = 0;
406 if (filename == NULL) {
409 file = fopen (filename, "r");
411 fprintf (stderr, "Failed to open %s: %s\n",
412 filename, strerror (errno));
418 if (report->tests_count == report->tests_size) {
419 report->tests_size *= 2;
420 report->tests = xrealloc (report->tests,
421 report->tests_size * sizeof (test_report_t));
425 if (getline (&line, &line_size, file) == -1)
428 status = test_report_parse (&report->tests[report->tests_count],
429 id, line, report->configuration);
430 if (status == TEST_REPORT_STATUS_ERROR)
431 fprintf (stderr, "Ignoring unrecognized line %d of %s:\n%s",
432 line_number, filename, line);
433 if (status == TEST_REPORT_STATUS_SUCCESS)
434 report->tests_count++;
435 /* Do nothing on TEST_REPORT_STATUS_COMMENT */
440 if (filename != NULL)
443 cairo_perf_report_sort_and_compute_stats (report, cmp);
445 /* Add one final report with a NULL name to terminate the list. */
446 if (report->tests_count == report->tests_size) {
447 report->tests_size *= 2;
448 report->tests = xrealloc (report->tests,
449 report->tests_size * sizeof (test_report_t));
451 report->tests[report->tests_count].name = NULL;