FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/tests/checkasm/ext/src/checkasm.c
Date: 2026-07-21 08:37:06
Exec Total Coverage
Lines: 210 660 31.8%
Functions: 16 41 39.0%
Branches: 114 374 30.5%

Line Branch Exec Source
1 /*
2 * Copyright © 2025, Niklas Haas
3 * Copyright © 2018, VideoLAN and dav1d authors
4 * Copyright © 2018, Two Orioles, LLC
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "checkasm_config.h"
31
32 #if HAVE_PTHREAD_SETAFFINITY_NP
33 /* _GNU_SOURCE is required for pthread_setaffinity_np and CPU_SET on glibc. */
34 #ifndef _GNU_SOURCE
35 #define _GNU_SOURCE
36 #endif
37 #endif
38
39 #include <assert.h>
40 #include <errno.h>
41 #include <inttypes.h>
42 #include <limits.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #include "checkasm/checkasm.h"
49 #include "checkasm/test.h"
50 #include "cpu.h"
51 #include "function.h"
52 #include "html_data.h"
53 #include "internal.h"
54 #include "stats.h"
55
56 #ifndef _WIN32
57 #if HAVE_PTHREAD_SETAFFINITY_NP
58 #include <pthread.h>
59 #if HAVE_PTHREAD_NP_H
60 #include <pthread_np.h>
61 #endif
62 #endif
63 #endif
64
65 #ifdef _WIN32
66 #include <windows.h>
67 #endif
68
69 #if HAVE_PRCTL
70 #include <sys/prctl.h>
71 #endif
72
73 /* Internal state */
74 static CheckasmConfig cfg;
75 static CheckasmStats stats; /* temporary buffer for function measurements */
76
77 /* Current function/test state, reset after each test run */
78 static struct {
79 CheckasmFuncTree tree;
80
81 /* (Re)set by check_cpu_flag() */
82 const CheckasmCpuInfo *cpu;
83 int cpu_name_printed;
84 CheckasmCpu cpu_flags;
85 int cpu_suffix_length;
86 const char *test_name;
87 int should_fail;
88 int report_idx;
89
90 /* (Re)set per function (check_func, bench_finish) */
91 CheckasmFunc *func;
92 CheckasmFuncVersion *func_ver;
93 char *func_variant;
94 uint64_t cycles;
95
96 /* Overall stats for this test run */
97 int num_funcs; /* known functions */
98 int num_checked; /* checked versions */
99 int num_failed; /* failed versions */
100 int num_benched; /* benched versions */
101 int prev_checked, prev_failed; /* reset by report() */
102 int saved_checked, saved_failed; /* for restoring after a crash */
103 double var_sum, var_max;
104 } current;
105
106 /* Global state for the entire checkasm_run() call */
107 static struct {
108 /* Miscellaneous global state (cosmetic) */
109 int max_function_name_length;
110 int max_report_name_length;
111
112 /* Timing code measurements (aggregated over multiple trials) */
113 CheckasmMeasurement nop_cycles;
114 CheckasmMeasurement perf_scale;
115
116 /* Runtime constants */
117 uint64_t target_cycles;
118 int skip_tests;
119 } state;
120
121 1615 CheckasmCpu checkasm_get_cpu_flags(void)
122 {
123 1615 return current.cpu_flags;
124 }
125
126 1200 const CheckasmCpuInfo *checkasm_get_cpu_info(void)
127 {
128 1200 return current.cpu;
129 }
130
131 /* Get the suffix of the specified cpu flag */
132 1358 static const char *cpu_suffix(const CheckasmCpuInfo *cpu)
133 {
134
2/2
✓ Branch 0 taken 1261 times.
✓ Branch 1 taken 97 times.
1358 return cpu ? cpu->suffix : "c";
135 }
136
137 static const char *ver_suffix(const CheckasmFuncVersion *ver)
138 {
139 return ver->suffix ? ver->suffix : cpu_suffix(ver->cpu);
140 }
141
142 /* Returns the coefficient of variation (CV) */
143 static double relative_error(double lvar)
144 {
145 return sqrt(exp(lvar) - 1.0);
146 }
147
148 static inline char separator(CheckasmFormat format)
149 {
150 switch (format) {
151 case CHECKASM_FORMAT_CSV: return ',';
152 case CHECKASM_FORMAT_TSV: return '\t';
153 default: return 0;
154 }
155 }
156
157 static void json_var(CheckasmJson *json, const char *key, const char *unit,
158 const CheckasmVar var)
159 {
160 if (key)
161 checkasm_json_push(json, key, '{');
162 if (unit)
163 checkasm_json_str(json, "unit", unit);
164 checkasm_json(json, "mode", "%g", checkasm_mode(var));
165 checkasm_json(json, "median", "%g", checkasm_median(var));
166 checkasm_json(json, "mean", "%g", checkasm_mean(var));
167 checkasm_json(json, "lowerCI", "%g", checkasm_sample(var, -1.96));
168 checkasm_json(json, "upperCI", "%g", checkasm_sample(var, 1.96));
169 checkasm_json(json, "stdDev", "%g", checkasm_stddev(var));
170 checkasm_json(json, "logMean", "%g", var.lmean);
171 checkasm_json(json, "logVar", "%g", var.lvar);
172 if (key)
173 checkasm_json_pop(json, '}');
174 }
175
176 static void json_measurement(CheckasmJson *json, const char *key, const char *unit,
177 const CheckasmMeasurement measurement)
178 {
179 const CheckasmVar result = checkasm_measurement_result(measurement);
180 if (key)
181 checkasm_json_push(json, key, '{');
182 json_var(json, NULL, unit, result);
183 checkasm_json(json, "numMeasurements", "%d", measurement.nb_measurements);
184
185 if (measurement.stats.nb_samples) {
186 json_var(json, "regressionSlope", unit,
187 checkasm_stats_estimate(&measurement.stats));
188 checkasm_json_push(json, "rawData", '[');
189 for (int i = 0; i < measurement.stats.nb_samples; i++) {
190 const CheckasmSample s = measurement.stats.samples[i];
191 checkasm_json(json, NULL, "{ \"iters\": %d, \"cycles\": %" PRIu64 " }",
192 s.count, s.sum);
193 }
194 checkasm_json_pop(json, ']');
195 }
196
197 if (key)
198 checkasm_json_pop(json, '}');
199 }
200
201 static void cpu_info_json(void *priv, const char *fmt, ...)
202 {
203 CheckasmJson *json = priv;
204 char buf[128];
205 va_list ap;
206 va_start(ap, fmt);
207 vsnprintf(buf, sizeof(buf), fmt, ap);
208 va_end(ap);
209
210 checkasm_json_str(json, NULL, buf);
211 }
212
213 struct IterState {
214 const char *test;
215 const char *report;
216 CheckasmJson json;
217 };
218
219 static void print_bench_header(struct IterState *const iter)
220 {
221 const CheckasmVar nop_cycles = checkasm_measurement_result(state.nop_cycles);
222 const CheckasmVar perf_scale = checkasm_measurement_result(state.perf_scale);
223 const CheckasmVar nop_time = checkasm_var_mul(nop_cycles, perf_scale);
224 CheckasmJson *const json = &iter->json;
225
226 switch (cfg.format) {
227 case CHECKASM_FORMAT_TSV:
228 case CHECKASM_FORMAT_CSV:
229 if (cfg.verbose) {
230 const char sep = separator(cfg.format);
231 printf("name%csuffix%c%ss%cstddev%cnanoseconds\n", sep, sep,
232 checkasm_perf.unit, sep, sep);
233 printf("nop%c%c%.4f%c%.5f%c%.4f\n", sep, sep, checkasm_mode(nop_cycles), sep,
234 checkasm_stddev(nop_cycles), sep, checkasm_mode(nop_time));
235 }
236 break;
237 case CHECKASM_FORMAT_HTML:
238 printf("<!doctype html>\n"
239 "<html>\n"
240 "<head>\n"
241 " <meta charset=\"utf-8\"/>\n"
242 " <title>checkasm report</title>\n"
243 " <script type=\"module\">\n"
244 " %s"
245 " %s"
246 " </script>\n"
247 " <style>\n"
248 " %s"
249 " </style>\n"
250 " <script type=\"application/json\" id=\"report-data\">\n",
251 checkasm_chart_js, checkasm_js, checkasm_css);
252 FALLTHROUGH;
253 case CHECKASM_FORMAT_JSON:
254 checkasm_json_push(json, NULL, '{');
255 checkasm_json_str(json, "checkasmVersion", CHECKASM_VERSION);
256 checkasm_json(json, "numChecked", "%d", current.num_checked);
257 checkasm_json(json, "numFailed", "%d", current.num_failed);
258 checkasm_json(json, "targetCycles", "%" PRIu64, state.target_cycles);
259 checkasm_json(json, "numBenchmarks", "%d", current.num_benched);
260 checkasm_json_push(json, "config", '{');
261 if (cfg.test_pattern)
262 checkasm_json_str(json, "testPattern", cfg.test_pattern);
263 if (cfg.function_pattern)
264 checkasm_json_str(json, "functionPattern", cfg.function_pattern);
265 checkasm_json(json, "benchUsec", "%u", cfg.bench_usec);
266 checkasm_json(json, "seed", "%u", cfg.seed);
267 checkasm_json(json, "repeat", "%u", cfg.repeat);
268 if (cfg.cpu_affinity_set)
269 checkasm_json(json, "cpuAffinity", "%u", cfg.cpu_affinity);
270 checkasm_json_pop(json, '}'); /* close config */
271 checkasm_json_push(json, "cpuInfo", '[');
272 checkasm_cpu_info(cpu_info_json, json, &cfg);
273 checkasm_json_pop(json, ']');
274 checkasm_json_push(json, "cpuFlags", '{');
275 for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++) {
276 const int available = (cfg.cpu & info->flag) == info->flag;
277 checkasm_json_push(json, info->suffix, '{');
278 checkasm_json_str(json, "name", info->name);
279 checkasm_json(json, "available", available ? "true" : "false");
280 checkasm_json_pop(json, '}');
281 }
282 checkasm_json_pop(json, '}'); /* close cpuFlags */
283 checkasm_json_push(json, "tests", '[');
284 for (const CheckasmTest *test = cfg.tests; test->func; test++)
285 checkasm_json_str(json, NULL, test->name);
286 checkasm_json_pop(json, ']'); /* close tests */
287 char perf_scale_unit[32];
288 snprintf(perf_scale_unit, sizeof(perf_scale_unit), "nsec/%s", checkasm_perf.unit);
289 json_measurement(json, "nopCycles", checkasm_perf.unit, state.nop_cycles);
290 json_measurement(json, "timerScale", perf_scale_unit, state.perf_scale);
291 json_var(json, "nopTime", checkasm_perf.unit, nop_time);
292 checkasm_json(json, "numFunctions", "%d", current.num_funcs);
293 checkasm_json_push(json, "functions", '{');
294 break;
295 case CHECKASM_FORMAT_PRETTY:
296 checkasm_fprintf(stdout, COLOR_YELLOW, "Benchmark results:\n");
297 checkasm_fprintf(stdout, COLOR_GREEN, " name%*ss",
298 5 + state.max_function_name_length, checkasm_perf.unit);
299 if (cfg.verbose) {
300 checkasm_fprintf(stdout, COLOR_GREEN, " +/- stddev %*s", 26,
301 "time (nanoseconds)");
302 }
303 checkasm_fprintf(stdout, COLOR_GREEN, " (vs ref)\n");
304 if (cfg.verbose) {
305 printf(" nop:%*.1f +/- %-7.1f %11.1f ns +/- %-6.1f\n",
306 6 + state.max_function_name_length, checkasm_mode(nop_cycles),
307 checkasm_stddev(nop_cycles), checkasm_mode(nop_time),
308 checkasm_stddev(nop_time));
309 }
310 break;
311 }
312 }
313
314 static void print_bench_footer(struct IterState *const iter)
315 {
316 const double err_rel = relative_error(current.var_sum / current.num_benched);
317 const double err_max = relative_error(current.var_max);
318 CheckasmJson *const json = &iter->json;
319
320 switch (cfg.format) {
321 case CHECKASM_FORMAT_TSV:
322 case CHECKASM_FORMAT_CSV: break;
323 case CHECKASM_FORMAT_PRETTY:
324 if (cfg.verbose) {
325 printf(" - average timing error: %.3f%% across %d benchmarks "
326 "(maximum %.3f%%)\n",
327 100.0 * err_rel, current.num_benched, 100.0 * err_max);
328 }
329 break;
330 case CHECKASM_FORMAT_HTML:
331 case CHECKASM_FORMAT_JSON:
332 checkasm_json_pop(json, '}'); /* close functions */
333 checkasm_json(json, "averageError", "%g", err_rel);
334 checkasm_json(json, "maximumError", "%g", err_max);
335 checkasm_json_pop(json, '}'); /* close root */
336
337 if (cfg.format == CHECKASM_FORMAT_HTML) {
338 printf(" </script>\n"
339 " <meta name=\"viewport\" content=\"width=device-width, "
340 "initial-scale=1\">\n"
341 "</head>\n"
342 "%s"
343 "</html>\n",
344 checkasm_html_body);
345 }
346 break;
347 }
348 }
349
350 static void print_bench_iter(const CheckasmFunc *const f, struct IterState *const iter)
351 {
352 CheckasmJson *const json = &iter->json;
353 const char sep = separator(cfg.format);
354 if (!f)
355 return;
356
357 print_bench_iter(f->child[0], iter);
358
359 const CheckasmFuncVersion *ref = &f->versions;
360 const CheckasmFuncVersion *v = ref;
361 const CheckasmVar nop_cycles = checkasm_measurement_result(state.nop_cycles);
362 const CheckasmVar perf_scale = checkasm_measurement_result(state.perf_scale);
363
364 /* Defer pushing the function header until we know that we have at least one
365 * benchmark to report */
366 int json_func_pushed = 0;
367
368 do {
369 if (v->cycles.nb_measurements) {
370 const CheckasmVar raw = checkasm_measurement_result(v->cycles);
371 const CheckasmVar raw_ref = checkasm_measurement_result(ref->cycles);
372
373 const CheckasmVar cycles = checkasm_var_sub(raw, nop_cycles);
374 const CheckasmVar cycles_ref = checkasm_var_sub(raw_ref, nop_cycles);
375 const CheckasmVar ratio = checkasm_var_div(cycles_ref, cycles);
376 const CheckasmVar raw_time = checkasm_var_mul(raw, perf_scale);
377 const CheckasmVar time = checkasm_var_mul(cycles, perf_scale);
378
379 switch (cfg.format) {
380 case CHECKASM_FORMAT_HTML:
381 case CHECKASM_FORMAT_JSON:
382 if (!json_func_pushed) {
383 checkasm_json_push(json, f->name, '{');
384 checkasm_json_str(json, "testName", f->test_name);
385 if (f->report_name)
386 checkasm_json_str(json, "reportName", f->report_name);
387 checkasm_json_push(json, "versions", '{');
388 json_func_pushed = 1;
389 }
390
391 checkasm_json_push(json, ver_suffix(v), '{');
392 json_measurement(json, "rawCycles", checkasm_perf.unit, v->cycles);
393 json_var(json, "rawTime", "nsec", raw_time);
394 json_var(json, "adjustedCycles", checkasm_perf.unit, cycles);
395 json_var(json, "adjustedTime", "nsec", time);
396 if (v != ref && ref->cycles.nb_measurements)
397 json_var(json, "ratio", NULL, checkasm_var_div(cycles_ref, cycles));
398 checkasm_json_pop(json, '}'); /* close version */
399 break;
400 case CHECKASM_FORMAT_TSV:
401 case CHECKASM_FORMAT_CSV:
402 printf("%s%c%s%c%.4f%c%.5f%c%.4f\n", f->name, sep, ver_suffix(v),
403 sep, checkasm_mode(cycles), sep, checkasm_stddev(cycles), sep,
404 checkasm_mode(time));
405 break;
406 case CHECKASM_FORMAT_PRETTY:;
407 const int pad = 12 + state.max_function_name_length
408 - printf(" %s_%s:", f->name, ver_suffix(v));
409 printf("%*.1f", imax(pad, 0), checkasm_mode(cycles));
410 if (cfg.verbose) {
411 printf(" +/- %-7.1f %11.1f ns +/- %-6.1f", checkasm_stddev(cycles),
412 checkasm_mode(time), checkasm_stddev(time));
413 }
414 if (v != ref && ref->cycles.nb_measurements) {
415 const double ratio_lo = checkasm_sample(ratio, -1.0);
416 const double ratio_hi = checkasm_sample(ratio, 1.0);
417 const int color = ratio_lo >= 10.0 ? COLOR_GREEN
418 : ratio_hi >= 1.1 && ratio_lo >= 1.0 ? COLOR_DEFAULT
419 : ratio_hi >= 1.0 ? COLOR_YELLOW
420 : COLOR_RED;
421 printf(" (");
422 checkasm_fprintf(stdout, color, "%5.2fx", checkasm_mode(ratio));
423 printf(")");
424 }
425 printf("\n");
426 break;
427 }
428 }
429 } while ((v = v->next));
430
431 if (json_func_pushed) {
432 checkasm_json_pop(json, '}'); /* close versions */
433 checkasm_json_pop(json, '}'); /* close function */
434 }
435
436 print_bench_iter(f->child[1], iter);
437 }
438
439 static void print_benchmarks(void)
440 {
441 struct IterState iter = { .json.file = stdout };
442 print_bench_header(&iter);
443 print_bench_iter(current.tree.root, &iter);
444 print_bench_footer(&iter);
445 assert(iter.json.level == 0);
446 }
447
448 /* Decide whether or not the current function needs to be benchmarked */
449 35684 int checkasm_bench_func(void)
450 {
451
2/6
✓ Branch 0 taken 35684 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35684 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
35684 return !current.num_failed && cfg.bench && !checkasm_interrupted;
452 }
453
454 int checkasm_bench_runs(void)
455 {
456 if (checkasm_interrupted)
457 return 0;
458
459 /* This limit should be impossible to hit in practice */
460 if (stats.nb_samples == CHECKASM_STATS_SAMPLES)
461 return 0;
462
463 /* Try and gather at least 30 samples for statistical validity, even if
464 * it means exceeding the time budget */
465 if (current.cycles < state.target_cycles || stats.nb_samples < 30)
466 return stats.next_count;
467 else
468 return 0;
469 }
470
471 /* Update benchmark results of the current function */
472 void checkasm_bench_update(const int iterations, const uint64_t cycles)
473 {
474 checkasm_stats_add(&stats, (CheckasmSample) { cycles, iterations });
475 checkasm_stats_count_grow(&stats, cycles, state.target_cycles);
476 current.cycles += cycles;
477
478 /* Emit this periodically while benchmarking, to avoid the SIMD
479 * units turning on and off during long bench runs of non-SIMD
480 * functions */
481 #if ARCH_X86
482 checkasm_simd_warmup();
483 #endif
484 }
485
486 void checkasm_bench_finish(void)
487 {
488 CheckasmFuncVersion *const v = current.func_ver;
489 if (v && current.cycles) {
490 const CheckasmVar cycles = checkasm_stats_estimate(&stats);
491
492 /* Accumulate multiple bench_new() calls */
493 checkasm_measurement_update(&v->cycles, stats);
494
495 /* Keep track of min/max/avg (log) variance */
496 current.var_sum += cycles.lvar;
497 current.var_max = fmax(current.var_max, cycles.lvar);
498 current.num_benched++;
499 }
500
501 checkasm_stats_reset(&stats);
502 current.cycles = 0;
503 }
504
505 /* Compares a string with a wildcard pattern. */
506 131726 static int wildstrcmp(const char *str, const char *pattern)
507 {
508 131726 const char *wild = strchr(pattern, '*');
509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 131726 times.
131726 if (wild) {
510 const size_t len = wild - pattern;
511 if (strncmp(str, pattern, len))
512 return 1;
513 while (*++wild == '*')
514 ;
515 if (!*wild)
516 return 0;
517 str += len;
518 while (*str && wildstrcmp(str, wild))
519 str++;
520 return !*str;
521 }
522 131726 return strcmp(str, pattern);
523 }
524
525 static void handle_interrupt(void);
526
527 /* Perform tests and benchmarks for the specified
528 * cpu flag if supported by the host */
529 1746 static void check_cpu_flag(const CheckasmCpuInfo *cpu)
530 {
531 1746 const CheckasmCpu prev_cpu_flags = current.cpu_flags;
532
2/2
✓ Branch 0 taken 1649 times.
✓ Branch 1 taken 97 times.
1746 if (cpu) {
533 1649 current.cpu_flags |= cpu->flag & cfg.cpu;
534 } else {
535 /* Also include any CPU flags not related to the CPU flags list */
536 97 current.cpu_flags = cfg.cpu;
537
2/2
✓ Branch 0 taken 1649 times.
✓ Branch 1 taken 97 times.
1746 for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++)
538 1649 current.cpu_flags &= ~info->flag;
539 }
540
541
4/4
✓ Branch 0 taken 1649 times.
✓ Branch 1 taken 97 times.
✓ Branch 2 taken 388 times.
✓ Branch 3 taken 1261 times.
1746 if (cpu && current.cpu_flags == prev_cpu_flags)
542 388 return;
543
544 1358 current.func = NULL;
545 1358 current.report_idx = 1;
546 1358 current.cpu = cpu;
547 1358 current.cpu_name_printed = 0;
548 1358 current.cpu_suffix_length = (int) strlen(cpu_suffix(cpu)) + 1;
549
1/2
✓ Branch 0 taken 1358 times.
✗ Branch 1 not taken.
1358 if (cfg.set_cpu_flags)
550 1358 cfg.set_cpu_flags(current.cpu_flags);
551
552
2/2
✓ Branch 0 taken 131726 times.
✓ Branch 1 taken 1358 times.
133084 for (const CheckasmTest *test = cfg.tests; test->func; test++) {
553
3/4
✓ Branch 0 taken 131726 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 130368 times.
✓ Branch 4 taken 1358 times.
131726 if (cfg.test_pattern && wildstrcmp(test->name, cfg.test_pattern))
554 130368 continue;
555 1358 current.test_name = test->name;
556
557
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1358 times.
1358 if (checkasm_save_context(checkasm_context)) {
558 const char *signal = checkasm_get_last_signal_desc();
559 handle_interrupt();
560 checkasm_fail_func("%s", signal);
561
562 /* We want to associate this (and any prior) failures with the
563 * correct report group, so remember the failure state until we
564 * reach the same position in the test() function again */
565 current.func_ver->state = CHECKASM_FUNC_CRASHED;
566 current.saved_checked = current.num_checked - current.prev_checked;
567 current.saved_failed = current.num_failed - current.prev_failed;
568 current.num_failed = current.prev_failed;
569 current.num_checked = current.prev_checked;
570 current.func = NULL;
571 }
572
573 1358 checkasm_srand(cfg.seed);
574 1358 current.should_fail = 0; // reset between tests
575 1358 test->func();
576 1358 checkasm_report(NULL); // catch any un-reported functions
577
578
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1358 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1358 if (cfg.bench && !state.skip_tests) {
579 /* Measure NOP and perf scale after each test+CPU flag configuration */
580 handle_interrupt();
581 checkasm_measure_nop_cycles(&state.nop_cycles, state.target_cycles);
582 handle_interrupt();
583 checkasm_measure_perf_scale(&state.perf_scale);
584 }
585
586 1358 free(current.func_variant);
587 1358 current.func_variant = NULL;
588 }
589 }
590
591 /* Print the name of the current CPU flag, but only do it once */
592 516 static void print_cpu_name(void)
593 {
594
2/2
✓ Branch 0 taken 227 times.
✓ Branch 1 taken 289 times.
516 if (!current.cpu_name_printed) {
595 227 checkasm_fprintf(stderr, COLOR_YELLOW, "%s:\n",
596
1/2
✓ Branch 0 taken 227 times.
✗ Branch 1 not taken.
227 current.cpu ? current.cpu->name : "C");
597 227 current.cpu_name_printed = 1;
598 }
599 516 }
600
601 int checkasm_run_on_all_cores(void (*func)(void))
602 {
603 #if HAVE_PTHREAD_SETAFFINITY_NP && defined(CPU_SET)
604 cpu_set_t mask;
605 if (pthread_getaffinity_np(pthread_self(), sizeof(mask), &mask))
606 return 1;
607
608 int ret = 0;
609 for (int c = 0; c < CPU_SETSIZE; c++) {
610 if (CPU_ISSET(c, &mask)) {
611 cpu_set_t set;
612 CPU_ZERO(&set);
613 CPU_SET(c, &set);
614 if (pthread_setaffinity_np(pthread_self(), sizeof(set), &set)) {
615 ret = 1;
616 break;
617 }
618 func();
619 }
620 }
621 pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask);
622 return ret;
623 #else
624 return 1;
625 #endif
626 }
627
628 static int set_cpu_affinity(const unsigned affinity)
629 {
630 int affinity_err;
631
632 #ifdef _WIN32
633 HANDLE process = GetCurrentProcess();
634 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
635 BOOL(WINAPI * spdcs)(HANDLE, const ULONG *, ULONG) = (void *) GetProcAddress(
636 GetModuleHandleW(L"kernel32.dll"), "SetProcessDefaultCpuSets");
637 if (spdcs)
638 affinity_err = !spdcs(process, (ULONG[]) { (ULONG) affinity + 256 }, 1);
639 else
640 #endif
641 {
642 if (affinity < sizeof(DWORD_PTR) * 8)
643 affinity_err = !SetProcessAffinityMask(process, (DWORD_PTR) 1 << affinity);
644 else
645 affinity_err = 1;
646 }
647 #elif HAVE_PTHREAD_SETAFFINITY_NP && defined(CPU_SET)
648 cpu_set_t set;
649 CPU_ZERO(&set);
650 CPU_SET(affinity, &set);
651 affinity_err = pthread_setaffinity_np(pthread_self(), sizeof(set), &set);
652 #else
653 (void) affinity;
654 (void) affinity_err;
655 fprintf(stderr, "checkasm: --affinity is not supported on your system\n");
656 return 1;
657 #endif
658
659 if (affinity_err) {
660 fprintf(stderr, "checkasm: invalid cpu affinity (%u)\n", affinity);
661 return 1;
662 } else {
663 fprintf(stderr, "checkasm: running on cpu %u\n", affinity);
664 return 0;
665 }
666 }
667
668 void checkasm_list_cpu_flags(const CheckasmConfig *cfg)
669 {
670 checkasm_setup_fprintf();
671
672 for (const CheckasmCpuInfo *info = cfg->cpu_flags; info->flag; info++) {
673 if ((cfg->cpu & info->flag) == info->flag)
674 checkasm_fprintf(stdout, COLOR_GREEN, "%s", info->suffix);
675 else
676 checkasm_fprintf(stdout, COLOR_RED, "~%s", info->suffix);
677 printf(info[1].flag ? ", " : "\n");
678 }
679 }
680
681 void checkasm_list_tests(const CheckasmConfig *config)
682 {
683 for (const CheckasmTest *test = config->tests; test->func; test++)
684 printf("%s\n", test->name);
685 }
686
687 static void print_functions(const CheckasmFunc *const f)
688 {
689 if (f) {
690 print_functions(f->child[0]);
691 const CheckasmFuncVersion *v = &f->versions;
692 printf("%s (%s", f->name, ver_suffix(v));
693 while ((v = v->next))
694 printf(", %s", ver_suffix(v));
695 printf(")\n");
696 print_functions(f->child[1]);
697 }
698 }
699
700 void checkasm_list_functions(const CheckasmConfig *config)
701 {
702 memset(&state, 0, sizeof(state));
703 memset(&current, 0, sizeof(current));
704 state.skip_tests = 1;
705 cfg = *config;
706
707 check_cpu_flag(NULL);
708 for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++)
709 check_cpu_flag(info);
710
711 print_functions(current.tree.root);
712 checkasm_func_tree_uninit(&current.tree);
713 }
714
715 97 static void cpu_fprintf(void *priv, const char *fmt, ...)
716 {
717 97 FILE *f = priv;
718
719 va_list ap;
720 97 va_start(ap, fmt);
721 97 fprintf(f, " - CPU: ");
722 97 vfprintf(f, fmt, ap);
723 97 fprintf(f, "\n");
724 97 va_end(ap);
725 97 }
726
727 97 static COLD void print_info(void)
728 {
729 97 checkasm_fprintf(stderr, COLOR_YELLOW, "checkasm:\n");
730 97 checkasm_cpu_info(cpu_fprintf, stderr, &cfg);
731
732
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 if (cfg.bench) {
733 fprintf(stderr, " - Timing source: %s\n", checkasm_perf.name);
734 if (cfg.verbose) {
735 const CheckasmVar perf_scale = checkasm_measurement_result(state.perf_scale);
736 const CheckasmVar nop_cycles = checkasm_measurement_result(state.nop_cycles);
737 const CheckasmVar mhz = checkasm_var_div(checkasm_var_const(1e3), perf_scale);
738 fprintf(stderr,
739 " - Timing resolution: %.4f +/- %.3f ns/%s (%.0f +/- %.1f "
740 "MHz) (provisional)\n",
741 checkasm_mode(perf_scale), checkasm_stddev(perf_scale),
742 checkasm_perf.unit, checkasm_mode(mhz), checkasm_stddev(mhz));
743
744 fprintf(stderr,
745 " - No-op overhead: %.2f +/- %.3f %ss per call (provisional)\n",
746 checkasm_mode(nop_cycles), checkasm_stddev(nop_cycles),
747 checkasm_perf.unit);
748 }
749 fprintf(stderr, " - Bench duration: %d µs per function (%" PRIu64 " %ss)\n",
750 cfg.bench_usec, state.target_cycles, checkasm_perf.unit);
751 }
752 97 fprintf(stderr, " - Random seed: %u\n", cfg.seed);
753 97 }
754
755 97 static int print_summary(void)
756 {
757 /* Exclude C/ref versions from count reported to user */
758 97 const int num_checked_asm = current.num_checked - current.num_funcs;
759
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 if (current.num_failed) {
760 fprintf(stderr, "checkasm: %d of %d tests failed\n", current.num_failed,
761 num_checked_asm);
762
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 4 times.
97 } else if (num_checked_asm) {
763 93 fprintf(stderr, "checkasm: all %d tests passed\n", num_checked_asm);
764 } else {
765 4 fprintf(stderr, "checkasm: no tests to perform\n");
766 }
767
768
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
97 if (current.num_benched && !current.num_failed)
769 print_benchmarks();
770
771 97 return current.num_failed > 0;
772 }
773
774 6264 static void handle_interrupt(void)
775 {
776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6264 times.
6264 if (checkasm_interrupted) {
777 fprintf(stderr, "checkasm: interrupted\n");
778 print_summary();
779 exit(128 + checkasm_interrupted);
780 }
781 6264 }
782
783 97 int checkasm_run(const CheckasmConfig *config)
784 {
785 #if !HAVE_HTML_DATA
786
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 if (cfg.format == CHECKASM_FORMAT_HTML) {
787 fprintf(stderr, "checkasm: built without HTML support\n");
788 return 1;
789 }
790 #endif
791
792 97 memset(&state, 0, sizeof(state));
793 97 memset(&current, 0, sizeof(current));
794 97 cfg = *config;
795
796 97 checkasm_set_signal_handlers();
797 #if HAVE_PRCTL && defined(PR_SET_UNALIGN)
798 97 prctl(PR_SET_UNALIGN, PR_UNALIGN_SIGBUS);
799 #endif
800
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 if (cfg.cpu_affinity_set)
801 set_cpu_affinity(cfg.cpu_affinity);
802 97 checkasm_setup_fprintf();
803
804
2/4
✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 97 times.
✗ Branch 3 not taken.
97 if (!cfg.seed && !cfg.seed_set)
805 97 cfg.seed = checkasm_seed();
806
1/2
✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
97 if (!cfg.repeat)
807 97 cfg.repeat = 1;
808
1/2
✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
97 if (!cfg.bench_usec)
809 97 cfg.bench_usec = 1000;
810
811
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 if (cfg.bench) {
812 if (checkasm_perf_init())
813 return 1;
814
815 checkasm_stats_reset(&stats);
816 checkasm_measurement_init(&state.nop_cycles);
817 checkasm_measurement_init(&state.perf_scale);
818 checkasm_measure_perf_scale(&state.perf_scale);
819
820 /* Use the low estimate to compute the number of target cycles, to
821 * ensure we reach the required number of cycles with confidence */
822 const CheckasmVar perf_scale = checkasm_measurement_result(state.perf_scale);
823 const double low_estimate = checkasm_sample(perf_scale, -1.0);
824 if (low_estimate <= 0.0) {
825 fprintf(stderr,
826 "checkasm: cycle counter seems to be non-functional "
827 "(invalid timer scale: %.4f %ss/nsec)\n",
828 checkasm_mode(perf_scale), checkasm_perf.unit);
829 return 1;
830 }
831
832 state.target_cycles = (uint64_t) (1e3 * cfg.bench_usec / low_estimate);
833 checkasm_measure_nop_cycles(&state.nop_cycles, state.target_cycles);
834 }
835
836 97 checkasm_init_cpu();
837
838 97 print_info();
839
840
2/2
✓ Branch 0 taken 97 times.
✓ Branch 1 taken 97 times.
194 for (unsigned i = 0; i < cfg.repeat; i++) {
841
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 if (i > 0) {
842 checkasm_fprintf(stderr, COLOR_YELLOW, "\nTest #%d:\n", i + 1);
843 fprintf(stderr, " - Random seed: %u\n", cfg.seed);
844 }
845
846 97 check_cpu_flag(NULL);
847
2/2
✓ Branch 0 taken 1649 times.
✓ Branch 1 taken 97 times.
1746 for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++)
848 1649 check_cpu_flag(info);
849
850 97 int res = print_summary();
851 97 checkasm_func_tree_uninit(&current.tree);
852
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 if (res)
853 return res;
854
855 97 memset(&current, 0, sizeof(current));
856 97 cfg.seed++;
857 }
858
859 97 return 0;
860 }
861
862 /* Decide whether or not the specified function needs to be tested and
863 * allocate/initialize data structures if needed. Returns a pointer to a
864 * reference function if the function should be tested, otherwise NULL */
865 196760 CheckasmKey checkasm_check_key(const CheckasmKey version, const char *const name, ...)
866 {
867 char name_buf[256];
868 va_list arg;
869
870
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 196760 times.
196760 if (checkasm_interrupted)
871 goto skip;
872
873 196760 va_start(arg, name);
874 196760 int name_length = vsnprintf(name_buf, sizeof(name_buf), name, arg);
875 196760 va_end(arg);
876
877
4/6
✓ Branch 0 taken 194716 times.
✓ Branch 1 taken 2044 times.
✓ Branch 2 taken 194716 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 194716 times.
✗ Branch 5 not taken.
196760 if (!version || name_length <= 0 || (size_t) name_length >= sizeof(name_buf)
878
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 194716 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
194716 || (cfg.function_pattern && wildstrcmp(name_buf, cfg.function_pattern)))
879 2044 goto skip;
880
881 194716 CheckasmFunc *const f = checkasm_func_get(&current.tree, name_buf);
882 194716 CheckasmFuncVersion *v = &f->versions;
883 194716 CheckasmKey ref = version;
884
885
2/2
✓ Branch 0 taken 181362 times.
✓ Branch 1 taken 13354 times.
194716 if (v->key) {
886 CheckasmFuncVersion *prev;
887 do {
888
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 242134 times.
242134 if (v->state == CHECKASM_FUNC_CRASHED) {
889 /* This function threw a signal last time; so restore the
890 * retained test state for the next report() call */
891 v->state = CHECKASM_FUNC_FAILED;
892 current.num_checked += current.saved_checked;
893 current.num_failed += current.saved_failed;
894 current.saved_checked = 0;
895 current.saved_failed = 0;
896 current.func = f;
897 current.func_ver = v;
898 for (CheckasmFunc *fp = f; fp; fp = fp->prev)
899 fp->report_idx = current.report_idx;
900 }
901
902 /* Skip functions without a working reference */
903
3/4
✓ Branch 0 taken 181362 times.
✓ Branch 1 taken 60772 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 181362 times.
242134 if (!v->cpu && v->state != CHECKASM_FUNC_OK)
904 goto skip;
905
906 /* Only test functions that haven't already been tested */
907
2/2
✓ Branch 0 taken 165998 times.
✓ Branch 1 taken 76136 times.
242134 if (v->key == version)
908 165998 goto skip;
909
910 /* Exclude failed or variant functions from being used as ref */
911
3/4
✓ Branch 0 taken 76136 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 76038 times.
✓ Branch 3 taken 98 times.
76136 if (v->state == CHECKASM_FUNC_OK && !v->suffix)
912 76038 ref = v->key;
913
914 76136 prev = v;
915
2/2
✓ Branch 0 taken 60772 times.
✓ Branch 1 taken 15364 times.
76136 } while ((v = v->next));
916
917 15364 v = prev->next = checkasm_mallocz(sizeof(CheckasmFuncVersion));
918 }
919
920
2/2
✓ Branch 0 taken 710 times.
✓ Branch 1 taken 28008 times.
28718 if (current.func_variant) {
921 710 v->suffix = current.func_variant;
922 710 current.func_variant = NULL;
923 710 name_length += (int) strlen(v->suffix) + 1;
924 } else {
925 28008 name_length += current.cpu_suffix_length;
926 }
927
928
2/2
✓ Branch 0 taken 482 times.
✓ Branch 1 taken 28236 times.
28718 if (name_length > state.max_function_name_length)
929 482 state.max_function_name_length = name_length;
930
931 28718 v->key = version;
932 28718 v->state = CHECKASM_FUNC_OK;
933 28718 v->cpu = current.cpu;
934
2/2
✓ Branch 0 taken 13354 times.
✓ Branch 1 taken 15364 times.
28718 if (ref == version)
935 13354 current.num_funcs++;
936
937
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28718 times.
28718 if (state.skip_tests)
938 goto skip;
939
940 /* Associate this function with each other function that was last used
941 * as part of the same report group */
942
2/2
✓ Branch 0 taken 13369 times.
✓ Branch 1 taken 15349 times.
28718 if (f->report_idx < current.report_idx) {
943 13369 f->report_idx = current.report_idx;
944 13369 f->prev = current.func;
945 13369 f->test_name = current.test_name;
946 }
947
948 28718 current.func = f;
949 28718 current.func_ver = v;
950 28718 current.num_checked++;
951 28718 checkasm_srand(cfg.seed);
952
953
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28718 times.
28718 if (cfg.bench) {
954 #if ARCH_X86
955 checkasm_simd_warmup();
956 #endif
957 checkasm_measurement_init(&v->cycles);
958 }
959
960 28718 return ref;
961
962 168042 skip:
963 168042 free(current.func_variant);
964 168042 current.func_variant = NULL;
965 168042 return 0;
966 }
967
968 1200 void checkasm_set_func_variant(const char *id_fmt, ...)
969 {
970 va_list arg;
971 1200 va_start(arg, id_fmt);
972
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1200 times.
1200 assert(!current.func_variant);
973 1200 current.func_variant = checkasm_vasprintf(id_fmt, arg);
974 1200 va_end(arg);
975 1200 }
976
977 /* Indicate that the current test has failed, return whether verbose printing
978 * is requested. */
979 static int fail_internal(const char *const msg, va_list arg)
980 {
981 CheckasmFuncVersion *const v = current.func_ver;
982 if (v && v->state == CHECKASM_FUNC_OK) {
983 if (!current.should_fail) {
984 print_cpu_name();
985 checkasm_fprintf(stderr, COLOR_RED, "FAILURE:");
986 fprintf(stderr, " %s_%s (", current.func->name, ver_suffix(v));
987 vfprintf(stderr, msg, arg);
988 fputs(")\n", stderr);
989 }
990
991 v->state = CHECKASM_FUNC_FAILED;
992 current.num_failed++;
993 }
994 return cfg.verbose && !current.should_fail;
995 }
996
997 int checkasm_fail_func(const char *const msg, ...)
998 {
999 va_list arg;
1000 va_start(arg, msg);
1001 const int ret = fail_internal(msg, arg);
1002 va_end(arg);
1003 return ret;
1004 }
1005
1006 void checkasm_fail_abort(const char *const msg, ...)
1007 {
1008 va_list arg;
1009 va_start(arg, msg);
1010 fail_internal(msg, arg);
1011 va_end(arg);
1012
1013 checkasm_load_context(checkasm_context);
1014 abort(); // in case we don't have a longjmp implementation
1015 }
1016
1017 int checkasm_should_fail(CheckasmCpu cpu_flags)
1018 {
1019 current.should_fail = !!(current.cpu_flags & cpu_flags);
1020
1021 #if CHECKASM_HAVE_LONGJMP
1022 return 1; /* we can catch any crashes */
1023 #else
1024 /* If our signal handler isn't working, we shouldn't run tests that
1025 * are expected to fail, as they may rely on the signal handler. */
1026 return !current.should_fail;
1027 #endif
1028 }
1029
1030 /* Print the outcome of all tests performed since
1031 * the last time this function was called */
1032 6264 void checkasm_report(const char *const name, ...)
1033 {
1034 char report_name[256];
1035
1036 /* Calculate the amount of padding required to make the output vertically aligned */
1037 6264 int length = (int) strlen(current.test_name);
1038
2/2
✓ Branch 0 taken 4906 times.
✓ Branch 1 taken 1358 times.
6264 if (name) {
1039 va_list arg;
1040 4906 va_start(arg, name);
1041 4906 length += 1 + vsnprintf(report_name, sizeof(report_name), name, arg);
1042 4906 va_end(arg);
1043 }
1044
1045
2/2
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 6099 times.
6264 if (length > state.max_report_name_length)
1046 165 state.max_report_name_length = length;
1047
1048 6264 const int new_checked = current.num_checked - current.prev_checked;
1049
2/2
✓ Branch 0 taken 847 times.
✓ Branch 1 taken 5417 times.
6264 if (new_checked) {
1050 847 int pad_length = (int) state.max_report_name_length + 3; // strlen(" - ")
1051
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 847 times.
847 assert(!state.skip_tests);
1052
1053 847 int fails = current.num_failed - current.prev_failed;
1054
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 847 times.
847 if (current.should_fail)
1055 current.num_failed = current.prev_failed + (new_checked - fails);
1056
1057 /* Omit "OK" for non-verbose non-benchmark C function successes */
1058 1694 const int want_print = current.num_failed != current.prev_failed
1059
3/6
✓ Branch 0 taken 847 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 847 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 847 times.
✗ Branch 5 not taken.
847 || current.should_fail || cfg.verbose || cfg.bench
1060
3/4
✓ Branch 0 taken 847 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 516 times.
✓ Branch 3 taken 331 times.
1694 || current.cpu;
1061
1062
2/2
✓ Branch 0 taken 516 times.
✓ Branch 1 taken 331 times.
847 if (want_print) {
1063 516 print_cpu_name();
1064
1/2
✓ Branch 0 taken 516 times.
✗ Branch 1 not taken.
516 if (name) {
1065 516 pad_length -= fprintf(stderr, " - %s.%s", current.test_name, report_name);
1066 } else {
1067 pad_length -= fprintf(stderr, " - %s", current.test_name);
1068 }
1069 516 fprintf(stderr, "%*c", imax(pad_length, 0) + 2, '[');
1070
1071
1/2
✓ Branch 0 taken 516 times.
✗ Branch 1 not taken.
516 if (current.num_failed == current.prev_failed) {
1072 516 checkasm_fprintf(stderr, COLOR_GREEN,
1073
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 516 times.
516 current.should_fail ? "EXPECTED" : "OK");
1074 } else if (!current.should_fail)
1075 checkasm_fprintf(stderr, COLOR_RED, "FAILED");
1076 else
1077 checkasm_fprintf(stderr, COLOR_RED, "%d/%d EXPECTED", fails, new_checked);
1078 516 fprintf(stderr, "]\n");
1079 }
1080
1081 847 current.prev_checked = current.num_checked;
1082 847 current.prev_failed = current.num_failed;
1083 }
1084
1085 /* Store the report name with each function in this report group */
1086 6264 CheckasmFunc *func = current.func;
1087
2/2
✓ Branch 0 taken 34993 times.
✓ Branch 1 taken 6264 times.
41257 while (func) {
1088
3/4
✓ Branch 0 taken 34993 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13354 times.
✓ Branch 3 taken 21639 times.
34993 if (name && !func->report_name)
1089 13354 func->report_name = checkasm_strdup(report_name);
1090 34993 func = func->prev;
1091 }
1092
1093 6264 current.func = NULL; /* reset current function for new report */
1094 6264 current.report_idx++;
1095 6264 handle_interrupt();
1096 6264 }
1097
1098 static void print_usage(const char *const progname)
1099 {
1100 fprintf(stderr,
1101 "Usage: %s [options...] <random seed>\n"
1102 " <random seed> Use fixed value to seed the PRNG\n"
1103 "Options:\n"
1104 " --affinity=<cpu> Run the process on CPU <cpu>\n"
1105 " --bench -b Benchmark the tested functions\n"
1106 " --csv, --tsv, --json, Choose output format for benchmarks\n"
1107 " --html\n"
1108 " --function=<pattern> -f Test only the functions matching "
1109 "<pattern>\n"
1110 " --help -h Print this usage info\n"
1111 " --list-cpu-flags List available cpu flags\n"
1112 " --list-functions List available functions\n"
1113 " --list-tests List available tests\n"
1114 " --duration=<μs> Benchmark duration (per function) in "
1115 "μs\n"
1116 " --repeat[=<N>] Repeat tests N times, on successive seeds\n"
1117 " --test=<pattern> -t Test only <pattern>\n"
1118 " --verbose -v Print verbose timing info and failure "
1119 "data\n",
1120 progname);
1121 }
1122
1123 static int parseu(unsigned *const dst, const char *const str, const int base)
1124 {
1125 unsigned long val;
1126 char *end;
1127 errno = 0;
1128 val = strtoul(str, &end, base);
1129 if (errno || end == str || *end)
1130 return 0;
1131 #if !defined(__SIZEOF_LONG__) || !defined(__SIZEOF_INT__) || __SIZEOF_LONG__ > __SIZEOF_INT__
1132 /* This condition is split out; it can cause -Wtype-limits warnings on
1133 * 32 bit platforms and on Windows:
1134 * warning: result of comparison 'unsigned long' > 4294967295 is always false [-Wtautological-type-limit-compare] */
1135 if (val > (unsigned) -1)
1136 return 0;
1137 #endif
1138 *dst = (unsigned) val;
1139 return 1;
1140 }
1141
1142 97 int checkasm_main(CheckasmConfig *config, int argc, const char *argv[])
1143 {
1144
2/2
✓ Branch 0 taken 97 times.
✓ Branch 1 taken 97 times.
194 while (argc > 1) {
1145
2/4
✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 97 times.
97 if (!strncmp(argv[1], "--help", 6) || !strcmp(argv[1], "-h")) {
1146 print_usage(argv[0]);
1147 return 0;
1148
1/2
✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
97 } else if (!strcmp(argv[1], "--list-cpu-flags")
1149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 || !strcmp(argv[1], "--list-cpuflags")) {
1150 checkasm_list_cpu_flags(config);
1151 return 0;
1152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 } else if (!strcmp(argv[1], "--list-tests")) {
1153 checkasm_list_tests(config);
1154 return 0;
1155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 } else if (!strcmp(argv[1], "--list-functions")) {
1156 checkasm_list_functions(config);
1157 return 0;
1158
2/4
✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 97 times.
97 } else if (!strcmp(argv[1], "--bench") || !strcmp(argv[1], "-b")) {
1159 config->bench = 1;
1160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 } else if (!strncmp(argv[1], "--bench=", 8)) {
1161 config->bench = 1;
1162 config->function_pattern = argv[1] + 8;
1163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 } else if (!strcmp(argv[1], "--csv")) {
1164 config->format = CHECKASM_FORMAT_CSV;
1165
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 } else if (!strcmp(argv[1], "--tsv")) {
1166 config->format = CHECKASM_FORMAT_TSV;
1167
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 } else if (!strcmp(argv[1], "--json")) {
1168 config->format = CHECKASM_FORMAT_JSON;
1169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 } else if (!strcmp(argv[1], "--html")) {
1170 #if HAVE_HTML_DATA
1171 config->format = CHECKASM_FORMAT_HTML;
1172 #else
1173 fprintf(stderr, "checkasm: built without HTML support\n");
1174 return 1;
1175 #endif
1176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 } else if (!strncmp(argv[1], "--duration=", 11)) {
1177 const char *const s = argv[1] + 11;
1178 if (!parseu(&config->bench_usec, s, 10)) {
1179 fprintf(stderr, "checkasm: invalid duration (%s)\n", s);
1180 print_usage(argv[0]);
1181 return 1;
1182 }
1183
1/2
✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
97 } else if (!strncmp(argv[1], "--test=", 7)) {
1184 97 config->test_pattern = argv[1] + 7;
1185 } else if (!strcmp(argv[1], "-t")) {
1186 config->test_pattern = argc > 1 ? argv[2] : "";
1187 argc--;
1188 argv++;
1189 } else if (!strncmp(argv[1], "--function=", 11)) {
1190 config->function_pattern = argv[1] + 11;
1191 } else if (!strcmp(argv[1], "-f")) {
1192 config->function_pattern = argc > 1 ? argv[2] : "";
1193 argc--;
1194 argv++;
1195 } else if (!strcmp(argv[1], "--verbose") || !strcmp(argv[1], "-v")) {
1196 config->verbose = 1;
1197 } else if (!strncmp(argv[1], "--affinity=", 11)) {
1198 const char *const s = argv[1] + 11;
1199 config->cpu_affinity_set = 1;
1200 if (!parseu(&config->cpu_affinity, s, 16)) {
1201 fprintf(stderr, "checkasm: invalid cpu affinity (%s)\n", s);
1202 print_usage(argv[0]);
1203 return 1;
1204 }
1205 } else if (!strncmp(argv[1], "--repeat=", 9)) {
1206 const char *const s = argv[1] + 9;
1207 if (!parseu(&config->repeat, s, 10)) {
1208 fprintf(stderr, "checkasm: invalid number of repetitions (%s)\n", s);
1209 print_usage(argv[0]);
1210 return 1;
1211 }
1212 } else if (!strcmp(argv[1], "--repeat")) {
1213 config->repeat = UINT_MAX;
1214 } else {
1215 config->seed_set = 1;
1216 if (!parseu(&config->seed, argv[1], 10)) {
1217 fprintf(stderr, "checkasm: unknown option (%s)\n", argv[1]);
1218 print_usage(argv[0]);
1219 return 1;
1220 }
1221 }
1222
1223 97 argc--;
1224 97 argv++;
1225 }
1226
1227 97 return checkasm_run(config);
1228 }
1229