Fixed texturing in Gallium.
[cairo/gpu.git] / perf / cairo-perf-report.c
bloba73538bb61cb5d320b97c9850c6f450445d1bb4d
1 /*
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
14 * warranty.
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
23 * SOFTWARE.
25 * Authors: Carl Worth <cworth@cworth.org>
28 #include "cairo-perf.h"
29 #include "cairo-stats.h"
31 /* We use _GNU_SOURCE for getline and strndup if available. */
32 #ifndef _GNU_SOURCE
33 # define _GNU_SOURCE
34 #endif
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <ctype.h>
40 #include <math.h>
41 #include <assert.h>
42 #ifdef HAVE_LIBGEN_H
43 #include <libgen.h>
44 #endif
46 /* 'ssize_t' does not exist in the C standard on win32.
47 * We use 'ptrdiff_t', which is nearly equivalent. */
48 #ifdef _MSC_VER
49 typedef ptrdiff_t ssize_t;
50 #endif
52 #ifndef __USE_GNU
53 static ssize_t
54 getline (char **lineptr, size_t *n, FILE *stream);
56 static char *
57 strndup (const char *s, size_t n);
58 #endif
60 #ifdef _MSC_VER
61 static long long
62 strtoll(const char *nptr, char **endptr, int base);
64 static char *
65 basename(char *path);
66 #endif
68 /* Ad-hoc parsing, macros with a strong dependence on the calling
69 * context, and plenty of other ugliness is here. But at least it's
70 * not perl... */
71 #define parse_error(...) fprintf(stderr, __VA_ARGS__); return TEST_REPORT_STATUS_ERROR;
72 #define skip_char(c) \
73 do { \
74 if (*s && *s == (c)) { \
75 s++; \
76 } else { \
77 parse_error ("expected '%c' but found '%c'", c, *s); \
78 } \
79 } while (0)
80 #define skip_space() while (*s && (*s == ' ' || *s == '\t')) s++;
81 #define parse_int(result) \
82 do { \
83 (result) = strtol (s, &end, 10); \
84 if (*s && end != s) { \
85 s = end; \
86 } else { \
87 parse_error("expected integer but found %s", s); \
88 } \
89 } while (0)
90 #define parse_long_long(result) \
91 do { \
92 (result) = strtoll (s, &end, 10); \
93 if (*s && end != s) { \
94 s = end; \
95 } else { \
96 parse_error("expected integer but found %s", s); \
97 } \
98 } while (0)
99 #define parse_double(result) \
100 do { \
101 (result) = strtod (s, &end); \
102 if (*s && end != s) { \
103 s = end; \
104 } else { \
105 parse_error("expected floating-point value but found %s", s); \
107 } while (0)
108 /* Here a string is simply a sequence of non-whitespace */
109 #define parse_string(result) \
110 do { \
111 for (end = s; *end; end++) \
112 if (isspace (*end)) \
113 break; \
114 (result) = strndup (s, end - s); \
115 if ((result) == NULL) { \
116 fprintf (stderr, "Out of memory.\n"); \
117 exit (1); \
119 s = end; \
120 } while (0)
122 static test_report_status_t
123 test_report_parse (test_report_t *report, char *line, char *configuration)
125 char *end;
126 char *s = line;
127 cairo_bool_t is_raw = FALSE;
128 double min_time, median_time;
130 /* The code here looks funny unless you understand that these are
131 * all macro calls, (and then the code just looks sick). */
132 if (*s == '\n')
133 return TEST_REPORT_STATUS_COMMENT;
135 skip_char ('[');
136 skip_space ();
137 if (*s == '#')
138 return TEST_REPORT_STATUS_COMMENT;
139 if (*s == '*') {
140 s++;
141 is_raw = TRUE;
142 } else {
143 parse_int (report->id);
145 skip_char (']');
147 skip_space ();
149 report->configuration = configuration;
150 parse_string (report->backend);
151 end = strrchr (report->backend, '.');
152 if (*end)
153 *end++ = '\0';
154 report->content = end;
156 skip_space ();
158 parse_string (report->name);
159 end = strrchr (report->name, '.');
160 if (*end)
161 *end++ = '\0';
162 report->size = atoi (end);
164 skip_space ();
166 report->samples = NULL;
167 report->samples_size = 0;
168 report->samples_count = 0;
170 if (is_raw) {
171 parse_double (report->stats.ticks_per_ms);
172 skip_space ();
174 report->samples_size = 5;
175 report->samples = xmalloc (report->samples_size * sizeof (cairo_perf_ticks_t));
176 report->stats.min_ticks = 0;
177 do {
178 if (report->samples_count == report->samples_size) {
179 report->samples_size *= 2;
180 report->samples = xrealloc (report->samples,
181 report->samples_size * sizeof (cairo_perf_ticks_t));
183 parse_long_long (report->samples[report->samples_count]);
184 if (report->samples_count == 0) {
185 report->stats.min_ticks =
186 report->samples[report->samples_count];
187 } else if (report->stats.min_ticks >
188 report->samples[report->samples_count]){
189 report->stats.min_ticks =
190 report->samples[report->samples_count];
192 report->samples_count++;
193 skip_space ();
194 } while (*s && *s != '\n');
195 report->stats.iterations = 0;
196 skip_char ('\n');
197 } else {
198 parse_double (report->stats.min_ticks);
199 skip_space ();
201 parse_double (min_time);
202 report->stats.ticks_per_ms = report->stats.min_ticks / min_time;
204 skip_space ();
206 parse_double (median_time);
207 report->stats.median_ticks = median_time * report->stats.ticks_per_ms;
209 skip_space ();
211 parse_double (report->stats.std_dev);
212 report->stats.std_dev /= 100.0;
213 skip_char ('%');
215 skip_space ();
217 parse_int (report->stats.iterations);
219 skip_space ();
220 skip_char ('\n');
223 return TEST_REPORT_STATUS_SUCCESS;
226 /* We conditionally provide a custom implementation of getline and strndup
227 * as needed. These aren't necessary full-fledged general purpose
228 * implementations. They just get the job done for our purposes.
230 #ifndef __USE_GNU
231 #define POORMANS_GETLINE_BUFFER_SIZE (65536)
232 static ssize_t
233 getline (char **lineptr, size_t *n, FILE *stream)
235 if (!*lineptr)
237 *n = POORMANS_GETLINE_BUFFER_SIZE;
238 *lineptr = (char *) malloc (*n);
241 if (!fgets (*lineptr, *n, stream))
242 return -1;
244 if (!feof (stream) && !strchr (*lineptr, '\n'))
246 fprintf (stderr, "The poor man's implementation of getline in "
247 __FILE__ " needs a bigger buffer. Perhaps it's "
248 "time for a complete implementation of getline.\n");
249 exit (0);
252 return strlen (*lineptr);
254 #undef POORMANS_GETLINE_BUFFER_SIZE
256 static char *
257 strndup (const char *s, size_t n)
259 size_t len;
260 char *sdup;
262 if (!s)
263 return NULL;
265 len = strlen (s);
266 len = (n < len ? n : len);
267 sdup = (char *) malloc (len + 1);
268 if (sdup)
270 memcpy (sdup, s, len);
271 sdup[len] = '\0';
274 return sdup;
276 #endif /* ifndef __USE_GNU */
278 /* We provide hereafter a win32 implementation of the basename
279 * and strtoll functions which are not available otherwise.
280 * The basename function is fully compliant to its GNU specs.
282 #ifdef _MSC_VER
283 long long
284 strtoll(const char *nptr, char **endptr, int base)
286 return _atoi64(nptr);
289 static char *
290 basename(char *path)
292 char *end, *s;
294 end = (path + strlen(path) - 1);
295 while (end && (end >= path + 1) && (*end == '/')) {
296 *end = '\0';
297 end--;
300 s = strrchr(path, '/');
301 if (s) {
302 if (s == end) {
303 return s;
304 } else {
305 return s+1;
307 } else {
308 return path;
311 #endif /* ifndef _MSC_VER */
314 test_report_cmp_backend_then_name (const void *a, const void *b)
316 const test_report_t *a_test = a;
317 const test_report_t *b_test = b;
319 int cmp;
321 cmp = strcmp (a_test->backend, b_test->backend);
322 if (cmp)
323 return cmp;
325 cmp = strcmp (a_test->content, b_test->content);
326 if (cmp)
327 return cmp;
329 /* A NULL name is a list-termination marker, so force it last. */
330 if (a_test->name == NULL)
331 if (b_test->name == NULL)
332 return 0;
333 else
334 return 1;
335 else if (b_test->name == NULL)
336 return -1;
338 cmp = strcmp (a_test->name, b_test->name);
339 if (cmp)
340 return cmp;
342 if (a_test->size < b_test->size)
343 return -1;
344 if (a_test->size > b_test->size)
345 return 1;
347 return 0;
351 test_report_cmp_name (const void *a, const void *b)
353 const test_report_t *a_test = a;
354 const test_report_t *b_test = b;
356 int cmp;
358 /* A NULL name is a list-termination marker, so force it last. */
359 if (a_test->name == NULL)
360 if (b_test->name == NULL)
361 return 0;
362 else
363 return 1;
364 else if (b_test->name == NULL)
365 return -1;
367 cmp = strcmp (a_test->name, b_test->name);
368 if (cmp)
369 return cmp;
371 if (a_test->size < b_test->size)
372 return -1;
373 if (a_test->size > b_test->size)
374 return 1;
376 return 0;
379 void
380 cairo_perf_report_sort_and_compute_stats (cairo_perf_report_t *report,
381 int (*cmp) (const void*, const void*))
383 test_report_t *base, *next, *last, *t;
385 if (cmp == NULL)
386 cmp = test_report_cmp_backend_then_name;
388 /* First we sort, since the diff needs both lists in the same
389 * order */
390 qsort (report->tests, report->tests_count, sizeof (test_report_t), cmp);
392 /* The sorting also brings all related raw reports together so we
393 * can condense them and compute the stats.
395 base = &report->tests[0];
396 last = &report->tests[report->tests_count - 1];
397 while (base <= last) {
398 next = base+1;
399 if (next <= last) {
400 while (next <= last &&
401 test_report_cmp_backend_then_name (base, next) == 0)
403 next++;
405 if (next != base) {
406 unsigned int new_samples_count = base->samples_count;
407 for (t = base + 1; t < next; t++)
408 new_samples_count += t->samples_count;
409 if (new_samples_count > base->samples_size) {
410 base->samples_size = new_samples_count;
411 base->samples = xrealloc (base->samples,
412 base->samples_size * sizeof (cairo_perf_ticks_t));
414 for (t = base + 1; t < next; t++) {
415 memcpy (&base->samples[base->samples_count], t->samples,
416 t->samples_count * sizeof (cairo_perf_ticks_t));
417 base->samples_count += t->samples_count;
421 if (base->samples)
422 _cairo_stats_compute (&base->stats, base->samples, base->samples_count);
423 base = next;
427 void
428 cairo_perf_report_load (cairo_perf_report_t *report,
429 const char *filename,
430 int (*cmp) (const void *, const void *))
432 FILE *file;
433 test_report_status_t status;
434 int line_number = 0;
435 char *line = NULL;
436 size_t line_size = 0;
437 char *configuration;
438 char *dot;
439 char *baseName;
441 configuration = xmalloc (strlen (filename) * sizeof (char) + 1);
442 strcpy (configuration, filename);
443 baseName = basename (configuration);
444 report->configuration = xmalloc (strlen (baseName) * sizeof (char) + 1);
445 strcpy (report->configuration, baseName);
446 free (configuration);
448 dot = strrchr (report->configuration, '.');
449 if (dot)
450 *dot = '\0';
452 report->name = filename;
453 report->tests_size = 16;
454 report->tests = xmalloc (report->tests_size * sizeof (test_report_t));
455 report->tests_count = 0;
457 file = fopen (filename, "r");
458 if (file == NULL) {
459 fprintf (stderr, "Failed to open %s: %s\n",
460 filename, strerror (errno));
461 exit (1);
464 while (1) {
465 if (report->tests_count == report->tests_size) {
466 report->tests_size *= 2;
467 report->tests = xrealloc (report->tests,
468 report->tests_size * sizeof (test_report_t));
471 line_number++;
472 if (getline (&line, &line_size, file) == -1)
473 break;
475 status = test_report_parse (&report->tests[report->tests_count],
476 line, report->configuration);
477 if (status == TEST_REPORT_STATUS_ERROR)
478 fprintf (stderr, "Ignoring unrecognized line %d of %s:\n%s",
479 line_number, filename, line);
480 if (status == TEST_REPORT_STATUS_SUCCESS)
481 report->tests_count++;
482 /* Do nothing on TEST_REPORT_STATUS_COMMENT */
485 if (line)
486 free (line);
488 fclose (file);
490 cairo_perf_report_sort_and_compute_stats (report, cmp);
492 /* Add one final report with a NULL name to terminate the list. */
493 if (report->tests_count == report->tests_size) {
494 report->tests_size *= 2;
495 report->tests = xrealloc (report->tests,
496 report->tests_size * sizeof (test_report_t));
498 report->tests[report->tests_count].name = NULL;