FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/tests/checkasm/checkasm.c
Date: 2024-05-03 15:42:48
Exec Total Coverage
Lines: 193 306 63.1%
Functions: 27 40 67.5%
Branches: 125 280 44.6%

Line Branch Exec Source
1 /*
2 * Assembly testing and benchmarking tool
3 * Copyright (c) 2015 Henrik Gramner
4 * Copyright (c) 2008 Loren Merritt
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include "config.h"
24 #include "config_components.h"
25
26 #ifndef _GNU_SOURCE
27 # define _GNU_SOURCE // for syscall (performance monitoring API), strsignal()
28 #endif
29
30 #include <signal.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include "checkasm.h"
36 #include "libavutil/common.h"
37 #include "libavutil/cpu.h"
38 #include "libavutil/intfloat.h"
39 #include "libavutil/random_seed.h"
40
41 #if HAVE_IO_H
42 #include <io.h>
43 #endif
44
45 #if defined(_WIN32) && !defined(SIGBUS)
46 /* non-standard, use the same value as mingw-w64 */
47 #define SIGBUS 10
48 #endif
49
50 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
51 #include <windows.h>
52 #define COLOR_RED FOREGROUND_RED
53 #define COLOR_GREEN FOREGROUND_GREEN
54 #define COLOR_YELLOW (FOREGROUND_RED|FOREGROUND_GREEN)
55 #else
56 #define COLOR_RED 1
57 #define COLOR_GREEN 2
58 #define COLOR_YELLOW 3
59 #endif
60
61 #if HAVE_UNISTD_H
62 #include <unistd.h>
63 #endif
64
65 #if !HAVE_ISATTY
66 #define isatty(fd) 1
67 #endif
68
69 #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
70 #include "libavutil/arm/cpu.h"
71
72 void (*checkasm_checked_call)(void *func, int dummy, ...) = checkasm_checked_call_novfp;
73 #endif
74
75 /* List of tests to invoke */
76 static const struct {
77 const char *name;
78 void (*func)(void);
79 } tests[] = {
80 #if CONFIG_AVCODEC
81 #if CONFIG_AAC_DECODER
82 { "aacpsdsp", checkasm_check_aacpsdsp },
83 { "sbrdsp", checkasm_check_sbrdsp },
84 #endif
85 #if CONFIG_AAC_ENCODER
86 { "aacencdsp", checkasm_check_aacencdsp },
87 #endif
88 #if CONFIG_AC3DSP
89 { "ac3dsp", checkasm_check_ac3dsp },
90 #endif
91 #if CONFIG_ALAC_DECODER
92 { "alacdsp", checkasm_check_alacdsp },
93 #endif
94 #if CONFIG_AUDIODSP
95 { "audiodsp", checkasm_check_audiodsp },
96 #endif
97 #if CONFIG_BLOCKDSP
98 { "blockdsp", checkasm_check_blockdsp },
99 #endif
100 #if CONFIG_BSWAPDSP
101 { "bswapdsp", checkasm_check_bswapdsp },
102 #endif
103 #if CONFIG_DCA_DECODER
104 { "synth_filter", checkasm_check_synth_filter },
105 #endif
106 #if CONFIG_EXR_DECODER
107 { "exrdsp", checkasm_check_exrdsp },
108 #endif
109 #if CONFIG_FLAC_DECODER
110 { "flacdsp", checkasm_check_flacdsp },
111 #endif
112 #if CONFIG_FMTCONVERT
113 { "fmtconvert", checkasm_check_fmtconvert },
114 #endif
115 #if CONFIG_G722DSP
116 { "g722dsp", checkasm_check_g722dsp },
117 #endif
118 #if CONFIG_H264CHROMA
119 { "h264chroma", checkasm_check_h264chroma },
120 #endif
121 #if CONFIG_H264DSP
122 { "h264dsp", checkasm_check_h264dsp },
123 #endif
124 #if CONFIG_H264PRED
125 { "h264pred", checkasm_check_h264pred },
126 #endif
127 #if CONFIG_H264QPEL
128 { "h264qpel", checkasm_check_h264qpel },
129 #endif
130 #if CONFIG_HEVC_DECODER
131 { "hevc_add_res", checkasm_check_hevc_add_res },
132 { "hevc_deblock", checkasm_check_hevc_deblock },
133 { "hevc_idct", checkasm_check_hevc_idct },
134 { "hevc_pel", checkasm_check_hevc_pel },
135 { "hevc_sao", checkasm_check_hevc_sao },
136 #endif
137 #if CONFIG_HUFFYUV_DECODER
138 { "huffyuvdsp", checkasm_check_huffyuvdsp },
139 #endif
140 #if CONFIG_IDCTDSP
141 { "idctdsp", checkasm_check_idctdsp },
142 #endif
143 #if CONFIG_JPEG2000_DECODER
144 { "jpeg2000dsp", checkasm_check_jpeg2000dsp },
145 #endif
146 #if CONFIG_LLAUDDSP
147 { "llauddsp", checkasm_check_llauddsp },
148 #endif
149 #if CONFIG_HUFFYUVDSP
150 { "llviddsp", checkasm_check_llviddsp },
151 #endif
152 #if CONFIG_LLVIDENCDSP
153 { "llviddspenc", checkasm_check_llviddspenc },
154 #endif
155 #if CONFIG_LPC
156 { "lpc", checkasm_check_lpc },
157 #endif
158 #if CONFIG_ME_CMP
159 { "motion", checkasm_check_motion },
160 #endif
161 #if CONFIG_OPUS_DECODER
162 { "opusdsp", checkasm_check_opusdsp },
163 #endif
164 #if CONFIG_PIXBLOCKDSP
165 { "pixblockdsp", checkasm_check_pixblockdsp },
166 #endif
167 #if CONFIG_RV34DSP
168 { "rv34dsp", checkasm_check_rv34dsp },
169 #endif
170 #if CONFIG_RV40_DECODER
171 { "rv40dsp", checkasm_check_rv40dsp },
172 #endif
173 #if CONFIG_SVQ1_ENCODER
174 { "svq1enc", checkasm_check_svq1enc },
175 #endif
176 #if CONFIG_TAK_DECODER
177 { "takdsp", checkasm_check_takdsp },
178 #endif
179 #if CONFIG_UTVIDEO_DECODER
180 { "utvideodsp", checkasm_check_utvideodsp },
181 #endif
182 #if CONFIG_V210_DECODER
183 { "v210dec", checkasm_check_v210dec },
184 #endif
185 #if CONFIG_V210_ENCODER
186 { "v210enc", checkasm_check_v210enc },
187 #endif
188 #if CONFIG_VC1DSP
189 { "vc1dsp", checkasm_check_vc1dsp },
190 #endif
191 #if CONFIG_VP8DSP
192 { "vp8dsp", checkasm_check_vp8dsp },
193 #endif
194 #if CONFIG_VP9_DECODER
195 { "vp9dsp", checkasm_check_vp9dsp },
196 #endif
197 #if CONFIG_VIDEODSP
198 { "videodsp", checkasm_check_videodsp },
199 #endif
200 #if CONFIG_VORBIS_DECODER
201 { "vorbisdsp", checkasm_check_vorbisdsp },
202 #endif
203 #if CONFIG_VVC_DECODER
204 { "vvc_mc", checkasm_check_vvc_mc },
205 #endif
206 #endif
207 #if CONFIG_AVFILTER
208 #if CONFIG_AFIR_FILTER
209 { "af_afir", checkasm_check_afir },
210 #endif
211 #if CONFIG_BLEND_FILTER
212 { "vf_blend", checkasm_check_blend },
213 #endif
214 #if CONFIG_BWDIF_FILTER
215 { "vf_bwdif", checkasm_check_vf_bwdif },
216 #endif
217 #if CONFIG_COLORSPACE_FILTER
218 { "vf_colorspace", checkasm_check_colorspace },
219 #endif
220 #if CONFIG_EQ_FILTER
221 { "vf_eq", checkasm_check_vf_eq },
222 #endif
223 #if CONFIG_GBLUR_FILTER
224 { "vf_gblur", checkasm_check_vf_gblur },
225 #endif
226 #if CONFIG_HFLIP_FILTER
227 { "vf_hflip", checkasm_check_vf_hflip },
228 #endif
229 #if CONFIG_NLMEANS_FILTER
230 { "vf_nlmeans", checkasm_check_nlmeans },
231 #endif
232 #if CONFIG_THRESHOLD_FILTER
233 { "vf_threshold", checkasm_check_vf_threshold },
234 #endif
235 #if CONFIG_SOBEL_FILTER
236 { "vf_sobel", checkasm_check_vf_sobel },
237 #endif
238 #endif
239 #if CONFIG_SWSCALE
240 { "sw_gbrp", checkasm_check_sw_gbrp },
241 { "sw_rgb", checkasm_check_sw_rgb },
242 { "sw_scale", checkasm_check_sw_scale },
243 #endif
244 #if CONFIG_AVUTIL
245 { "fixed_dsp", checkasm_check_fixed_dsp },
246 { "float_dsp", checkasm_check_float_dsp },
247 { "av_tx", checkasm_check_av_tx },
248 #endif
249 { NULL }
250 };
251
252 /* List of cpu flags to check */
253 static const struct {
254 const char *name;
255 const char *suffix;
256 int flag;
257 } cpus[] = {
258 #if ARCH_AARCH64
259 { "ARMV8", "armv8", AV_CPU_FLAG_ARMV8 },
260 { "NEON", "neon", AV_CPU_FLAG_NEON },
261 { "DOTPROD", "dotprod", AV_CPU_FLAG_DOTPROD },
262 { "I8MM", "i8mm", AV_CPU_FLAG_I8MM },
263 #elif ARCH_ARM
264 { "ARMV5TE", "armv5te", AV_CPU_FLAG_ARMV5TE },
265 { "ARMV6", "armv6", AV_CPU_FLAG_ARMV6 },
266 { "ARMV6T2", "armv6t2", AV_CPU_FLAG_ARMV6T2 },
267 { "VFP", "vfp", AV_CPU_FLAG_VFP },
268 { "VFP_VM", "vfp_vm", AV_CPU_FLAG_VFP_VM },
269 { "VFPV3", "vfp3", AV_CPU_FLAG_VFPV3 },
270 { "NEON", "neon", AV_CPU_FLAG_NEON },
271 #elif ARCH_PPC
272 { "ALTIVEC", "altivec", AV_CPU_FLAG_ALTIVEC },
273 { "VSX", "vsx", AV_CPU_FLAG_VSX },
274 { "POWER8", "power8", AV_CPU_FLAG_POWER8 },
275 #elif ARCH_RISCV
276 { "RVI", "rvi", AV_CPU_FLAG_RVI },
277 { "RVF", "rvf", AV_CPU_FLAG_RVF },
278 { "RVD", "rvd", AV_CPU_FLAG_RVD },
279 { "RVBaddr", "rvb_a", AV_CPU_FLAG_RVB_ADDR },
280 { "RVBbasic", "rvb_b", AV_CPU_FLAG_RVB_BASIC },
281 { "RVVi32", "rvv_i32", AV_CPU_FLAG_RVV_I32 },
282 { "RVVf32", "rvv_f32", AV_CPU_FLAG_RVV_F32 },
283 { "RVVi64", "rvv_i64", AV_CPU_FLAG_RVV_I64 },
284 { "RVVf64", "rvv_f64", AV_CPU_FLAG_RVV_F64 },
285 #elif ARCH_MIPS
286 { "MMI", "mmi", AV_CPU_FLAG_MMI },
287 { "MSA", "msa", AV_CPU_FLAG_MSA },
288 #elif ARCH_X86
289 { "MMX", "mmx", AV_CPU_FLAG_MMX|AV_CPU_FLAG_CMOV },
290 { "MMXEXT", "mmxext", AV_CPU_FLAG_MMXEXT },
291 { "3DNOW", "3dnow", AV_CPU_FLAG_3DNOW },
292 { "3DNOWEXT", "3dnowext", AV_CPU_FLAG_3DNOWEXT },
293 { "SSE", "sse", AV_CPU_FLAG_SSE },
294 { "SSE2", "sse2", AV_CPU_FLAG_SSE2|AV_CPU_FLAG_SSE2SLOW },
295 { "SSE3", "sse3", AV_CPU_FLAG_SSE3|AV_CPU_FLAG_SSE3SLOW },
296 { "SSSE3", "ssse3", AV_CPU_FLAG_SSSE3|AV_CPU_FLAG_ATOM },
297 { "SSE4.1", "sse4", AV_CPU_FLAG_SSE4 },
298 { "SSE4.2", "sse42", AV_CPU_FLAG_SSE42 },
299 { "AES-NI", "aesni", AV_CPU_FLAG_AESNI },
300 { "AVX", "avx", AV_CPU_FLAG_AVX },
301 { "XOP", "xop", AV_CPU_FLAG_XOP },
302 { "FMA3", "fma3", AV_CPU_FLAG_FMA3 },
303 { "FMA4", "fma4", AV_CPU_FLAG_FMA4 },
304 { "AVX2", "avx2", AV_CPU_FLAG_AVX2 },
305 { "AVX-512", "avx512", AV_CPU_FLAG_AVX512 },
306 { "AVX-512ICL", "avx512icl", AV_CPU_FLAG_AVX512ICL },
307 #elif ARCH_LOONGARCH
308 { "LSX", "lsx", AV_CPU_FLAG_LSX },
309 { "LASX", "lasx", AV_CPU_FLAG_LASX },
310 #endif
311 { NULL }
312 };
313
314 typedef struct CheckasmFuncVersion {
315 struct CheckasmFuncVersion *next;
316 void *func;
317 int ok;
318 int cpu;
319 CheckasmPerf perf;
320 } CheckasmFuncVersion;
321
322 /* Binary search tree node */
323 typedef struct CheckasmFunc {
324 struct CheckasmFunc *child[2];
325 CheckasmFuncVersion versions;
326 uint8_t color; /* 0 = red, 1 = black */
327 char name[1];
328 } CheckasmFunc;
329
330 /* Internal state */
331 static struct {
332 CheckasmFunc *funcs;
333 CheckasmFunc *current_func;
334 CheckasmFuncVersion *current_func_ver;
335 const char *current_test_name;
336 const char *bench_pattern;
337 int bench_pattern_len;
338 int num_checked;
339 int num_failed;
340
341 /* perf */
342 int nop_time;
343 int sysfd;
344
345 int cpu_flag;
346 const char *cpu_flag_name;
347 const char *test_name;
348 int verbose;
349 volatile sig_atomic_t catch_signals;
350 } state;
351
352 /* PRNG state */
353 AVLFG checkasm_lfg;
354
355 /* float compare support code */
356 14112392 static int is_negative(union av_intfloat32 u)
357 {
358 14112392 return u.i >> 31;
359 }
360
361 7056196 int float_near_ulp(float a, float b, unsigned max_ulp)
362 {
363 union av_intfloat32 x, y;
364
365 7056196 x.f = a;
366 7056196 y.f = b;
367
368
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 7056196 times.
7056196 if (is_negative(x) != is_negative(y)) {
369 // handle -0.0 == +0.0
370 return a == b;
371 }
372
373
2/2
✓ Branch 0 taken 7056195 times.
✓ Branch 1 taken 1 times.
7056196 if (llabs((int64_t)x.i - y.i) <= max_ulp)
374 7056195 return 1;
375
376 1 return 0;
377 }
378
379 6363 int float_near_ulp_array(const float *a, const float *b, unsigned max_ulp,
380 unsigned len)
381 {
382 unsigned i;
383
384
2/2
✓ Branch 0 taken 7051076 times.
✓ Branch 1 taken 6363 times.
7057439 for (i = 0; i < len; i++) {
385
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7051076 times.
7051076 if (!float_near_ulp(a[i], b[i], max_ulp))
386 return 0;
387 }
388 6363 return 1;
389 }
390
391 882134 int float_near_abs_eps(float a, float b, float eps)
392 {
393 882134 float abs_diff = fabsf(a - b);
394
1/2
✓ Branch 0 taken 882134 times.
✗ Branch 1 not taken.
882134 if (abs_diff < eps)
395 882134 return 1;
396
397 fprintf(stderr, "test failed comparing %g with %g (abs diff=%g with EPS=%g)\n", a, b, abs_diff, eps);
398
399 return 0;
400 }
401
402 189 int float_near_abs_eps_array(const float *a, const float *b, float eps,
403 unsigned len)
404 {
405 unsigned i;
406
407
2/2
✓ Branch 0 taken 872138 times.
✓ Branch 1 taken 189 times.
872327 for (i = 0; i < len; i++) {
408
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 872138 times.
872138 if (!float_near_abs_eps(a[i], b[i], eps))
409 return 0;
410 }
411 189 return 1;
412 }
413
414 5120 int float_near_abs_eps_ulp(float a, float b, float eps, unsigned max_ulp)
415 {
416
3/4
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5119 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
5120 return float_near_ulp(a, b, max_ulp) || float_near_abs_eps(a, b, eps);
417 }
418
419 int float_near_abs_eps_array_ulp(const float *a, const float *b, float eps,
420 unsigned max_ulp, unsigned len)
421 {
422 unsigned i;
423
424 for (i = 0; i < len; i++) {
425 if (!float_near_abs_eps_ulp(a[i], b[i], eps, max_ulp))
426 return 0;
427 }
428 return 1;
429 }
430
431 54724 int double_near_abs_eps(double a, double b, double eps)
432 {
433 54724 double abs_diff = fabs(a - b);
434
435 54724 return abs_diff < eps;
436 }
437
438 11 int double_near_abs_eps_array(const double *a, const double *b, double eps,
439 unsigned len)
440 {
441 unsigned i;
442
443
2/2
✓ Branch 0 taken 41068 times.
✓ Branch 1 taken 11 times.
41079 for (i = 0; i < len; i++) {
444
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 41068 times.
41068 if (!double_near_abs_eps(a[i], b[i], eps))
445 return 0;
446 }
447 11 return 1;
448 }
449
450 /* Print colored text to stderr if the terminal supports it */
451 517 static void color_printf(int color, const char *fmt, ...)
452 {
453 static int use_color = -1;
454 va_list arg;
455
456 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
457 static HANDLE con;
458 static WORD org_attributes;
459
460 if (use_color < 0) {
461 CONSOLE_SCREEN_BUFFER_INFO con_info;
462 con = GetStdHandle(STD_ERROR_HANDLE);
463 if (con && con != INVALID_HANDLE_VALUE && GetConsoleScreenBufferInfo(con, &con_info)) {
464 org_attributes = con_info.wAttributes;
465 use_color = 1;
466 } else
467 use_color = 0;
468 }
469 if (use_color)
470 SetConsoleTextAttribute(con, (org_attributes & 0xfff0) | (color & 0x0f));
471 #else
472
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 458 times.
517 if (use_color < 0) {
473 59 const char *term = getenv("TERM");
474
3/6
✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 59 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 59 times.
59 use_color = term && strcmp(term, "dumb") && isatty(2);
475 }
476
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 517 times.
517 if (use_color)
477 fprintf(stderr, "\x1b[%d;3%dm", (color & 0x08) >> 3, color & 0x07);
478 #endif
479
480 517 va_start(arg, fmt);
481 517 vfprintf(stderr, fmt, arg);
482 517 va_end(arg);
483
484
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 517 times.
517 if (use_color) {
485 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
486 SetConsoleTextAttribute(con, org_attributes);
487 #else
488 fprintf(stderr, "\x1b[0m");
489 #endif
490 }
491 517 }
492
493 /* Deallocate a tree */
494 13555 static void destroy_func_tree(CheckasmFunc *f)
495 {
496
2/2
✓ Branch 0 taken 6747 times.
✓ Branch 1 taken 6808 times.
13555 if (f) {
497 6747 CheckasmFuncVersion *v = f->versions.next;
498
2/2
✓ Branch 0 taken 9756 times.
✓ Branch 1 taken 6747 times.
16503 while (v) {
499 9756 CheckasmFuncVersion *next = v->next;
500 9756 free(v);
501 9756 v = next;
502 }
503
504 6747 destroy_func_tree(f->child[0]);
505 6747 destroy_func_tree(f->child[1]);
506 6747 free(f);
507 }
508 13555 }
509
510 /* Allocate a zero-initialized block, clean up and exit on failure */
511 16503 static void *checkasm_malloc(size_t size)
512 {
513 16503 void *ptr = calloc(1, size);
514
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16503 times.
16503 if (!ptr) {
515 fprintf(stderr, "checkasm: malloc failed\n");
516 destroy_func_tree(state.funcs);
517 exit(1);
518 }
519 16503 return ptr;
520 }
521
522 /* Get the suffix of the specified cpu flag */
523 static const char *cpu_suffix(int cpu)
524 {
525 int i = FF_ARRAY_ELEMS(cpus);
526
527 while (--i >= 0)
528 if (cpu & cpus[i].flag)
529 return cpus[i].suffix;
530
531 return "c";
532 }
533
534 static int cmp_nop(const void *a, const void *b)
535 {
536 return *(const uint16_t*)a - *(const uint16_t*)b;
537 }
538
539 /* Measure the overhead of the timing code (in decicycles) */
540 static int measure_nop_time(void)
541 {
542 uint16_t nops[10000];
543 int i, nop_sum = 0;
544 av_unused const int sysfd = state.sysfd;
545
546 uint64_t t = 0;
547 for (i = 0; i < 10000; i++) {
548 PERF_START(t);
549 PERF_STOP(t);
550 nops[i] = t;
551 }
552
553 qsort(nops, 10000, sizeof(uint16_t), cmp_nop);
554 for (i = 2500; i < 7500; i++)
555 nop_sum += nops[i];
556
557 return nop_sum / 500;
558 }
559
560 /* Print benchmark results */
561 static void print_benchs(CheckasmFunc *f)
562 {
563 if (f) {
564 print_benchs(f->child[0]);
565
566 /* Only print functions with at least one assembly version */
567 if (f->versions.cpu || f->versions.next) {
568 CheckasmFuncVersion *v = &f->versions;
569 do {
570 CheckasmPerf *p = &v->perf;
571 if (p->iterations) {
572 int decicycles = (10*p->cycles/p->iterations - state.nop_time) / 4;
573 printf("%s_%s: %d.%d\n", f->name, cpu_suffix(v->cpu), decicycles/10, decicycles%10);
574 }
575 } while ((v = v->next));
576 }
577
578 print_benchs(f->child[1]);
579 }
580 }
581
582 /* ASCIIbetical sort except preserving natural order for numbers */
583 839469 static int cmp_func_names(const char *a, const char *b)
584 {
585 839469 const char *start = a;
586 int ascii_diff, digit_diff;
587
588
4/4
✓ Branch 0 taken 12586606 times.
✓ Branch 1 taken 752811 times.
✓ Branch 2 taken 12499948 times.
✓ Branch 3 taken 86658 times.
13339417 for (; !(ascii_diff = *(const unsigned char*)a - *(const unsigned char*)b) && *a; a++, b++);
589
4/4
✓ Branch 0 taken 634579 times.
✓ Branch 1 taken 718755 times.
✓ Branch 2 taken 513865 times.
✓ Branch 3 taken 120714 times.
1353334 for (; av_isdigit(*a) && av_isdigit(*b); a++, b++);
590
591
6/6
✓ Branch 0 taken 802030 times.
✓ Branch 1 taken 37439 times.
✓ Branch 2 taken 476497 times.
✓ Branch 3 taken 325533 times.
✓ Branch 4 taken 183275 times.
✓ Branch 5 taken 293222 times.
839469 if (a > start && av_isdigit(a[-1]) && (digit_diff = av_isdigit(*a) - av_isdigit(*b)))
592 183275 return digit_diff;
593
594 656194 return ascii_diff;
595 }
596
597 /* Perform a tree rotation in the specified direction and return the new root */
598 7053 static CheckasmFunc *rotate_tree(CheckasmFunc *f, int dir)
599 {
600 7053 CheckasmFunc *r = f->child[dir^1];
601 7053 f->child[dir^1] = r->child[dir];
602 7053 r->child[dir] = f;
603 7053 r->color = f->color;
604 7053 f->color = 0;
605 7053 return r;
606 }
607
608 #define is_red(f) ((f) && !(f)->color)
609
610 /* Balance a left-leaning red-black tree at the specified node */
611 60465 static void balance_tree(CheckasmFunc **root)
612 {
613 60465 CheckasmFunc *f = *root;
614
615
8/8
✓ Branch 0 taken 57280 times.
✓ Branch 1 taken 3185 times.
✓ Branch 2 taken 24953 times.
✓ Branch 3 taken 32327 times.
✓ Branch 4 taken 22522 times.
✓ Branch 5 taken 2431 times.
✓ Branch 6 taken 5004 times.
✓ Branch 7 taken 17518 times.
60465 if (is_red(f->child[0]) && is_red(f->child[1])) {
616 5004 f->color ^= 1;
617 5004 f->child[0]->color = f->child[1]->color = 1;
618 }
619
620
7/8
✓ Branch 0 taken 57280 times.
✓ Branch 1 taken 3185 times.
✓ Branch 2 taken 37331 times.
✓ Branch 3 taken 19949 times.
✓ Branch 4 taken 40516 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5053 times.
✓ Branch 7 taken 35463 times.
60465 if (!is_red(f->child[0]) && is_red(f->child[1]))
621 5053 *root = rotate_tree(f, 0); /* Rotate left */
622
7/8
✓ Branch 0 taken 55412 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19949 times.
✓ Branch 3 taken 35463 times.
✓ Branch 4 taken 18446 times.
✓ Branch 5 taken 1503 times.
✓ Branch 6 taken 2000 times.
✓ Branch 7 taken 16446 times.
55412 else if (is_red(f->child[0]) && is_red(f->child[0]->child[0]))
623 2000 *root = rotate_tree(f, 1); /* Rotate right */
624 60465 }
625
626 /* Get a node with the specified name, creating it if it doesn't exist */
627 846216 static CheckasmFunc *get_func(CheckasmFunc **root, const char *name)
628 {
629 846216 CheckasmFunc *f = *root;
630
631
2/2
✓ Branch 0 taken 839469 times.
✓ Branch 1 taken 6747 times.
846216 if (f) {
632 /* Search the tree for a matching node */
633 839469 int cmp = cmp_func_names(name, f->name);
634
2/2
✓ Branch 0 taken 752811 times.
✓ Branch 1 taken 86658 times.
839469 if (cmp) {
635 752811 f = get_func(&f->child[cmp > 0], name);
636
637 /* Rebalance the tree on the way up if a new node was inserted */
638
2/2
✓ Branch 0 taken 60465 times.
✓ Branch 1 taken 692346 times.
752811 if (!f->versions.func)
639 60465 balance_tree(root);
640 }
641 } else {
642 /* Allocate and insert a new node into the tree */
643 6747 int name_length = strlen(name);
644 6747 f = *root = checkasm_malloc(sizeof(CheckasmFunc) + name_length);
645 6747 memcpy(f->name, name, name_length + 1);
646 }
647
648 846216 return f;
649 }
650
651 checkasm_context checkasm_context_buf;
652
653 /* Crash handling: attempt to catch crashes and handle them
654 * gracefully instead of just aborting abruptly. */
655 #ifdef _WIN32
656 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
657 static LONG NTAPI signal_handler(EXCEPTION_POINTERS *e) {
658 int s;
659
660 if (!state.catch_signals)
661 return EXCEPTION_CONTINUE_SEARCH;
662
663 switch (e->ExceptionRecord->ExceptionCode) {
664 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
665 case EXCEPTION_INT_DIVIDE_BY_ZERO:
666 s = SIGFPE;
667 break;
668 case EXCEPTION_ILLEGAL_INSTRUCTION:
669 case EXCEPTION_PRIV_INSTRUCTION:
670 s = SIGILL;
671 break;
672 case EXCEPTION_ACCESS_VIOLATION:
673 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
674 case EXCEPTION_DATATYPE_MISALIGNMENT:
675 case EXCEPTION_STACK_OVERFLOW:
676 s = SIGSEGV;
677 break;
678 case EXCEPTION_IN_PAGE_ERROR:
679 s = SIGBUS;
680 break;
681 default:
682 return EXCEPTION_CONTINUE_SEARCH;
683 }
684 state.catch_signals = 0;
685 checkasm_load_context(s);
686 return EXCEPTION_CONTINUE_EXECUTION; /* never reached, but shuts up gcc */
687 }
688 #endif
689 #else
690 static void signal_handler(int s);
691
692 static const struct sigaction signal_handler_act = {
693 .sa_handler = signal_handler,
694 .sa_flags = SA_RESETHAND,
695 };
696
697 static void signal_handler(int s) {
698 if (state.catch_signals) {
699 state.catch_signals = 0;
700 sigaction(s, &signal_handler_act, NULL);
701 checkasm_load_context(s);
702 }
703 }
704 #endif
705
706 /* Perform tests and benchmarks for the specified cpu flag if supported by the host */
707 1159 static void check_cpu_flag(const char *name, int flag)
708 {
709 1159 int old_cpu_flag = state.cpu_flag;
710
711 1159 flag |= old_cpu_flag;
712 1159 av_force_cpu_flags(-1);
713 1159 state.cpu_flag = flag & av_get_cpu_flags();
714 1159 av_force_cpu_flags(state.cpu_flag);
715
716
4/4
✓ Branch 0 taken 1098 times.
✓ Branch 1 taken 61 times.
✓ Branch 2 taken 732 times.
✓ Branch 3 taken 366 times.
1159 if (!flag || state.cpu_flag != old_cpu_flag) {
717 int i;
718
719 793 state.cpu_flag_name = name;
720
2/2
✓ Branch 0 taken 48373 times.
✓ Branch 1 taken 793 times.
49166 for (i = 0; tests[i].func; i++) {
721
3/4
✓ Branch 0 taken 48373 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 47580 times.
✓ Branch 3 taken 793 times.
48373 if (state.test_name && strcmp(tests[i].name, state.test_name))
722 47580 continue;
723 793 state.current_test_name = tests[i].name;
724 793 tests[i].func();
725 }
726 }
727 1159 }
728
729 /* Print the name of the current CPU flag, but only do it once */
730 337 static void print_cpu_name(void)
731 {
732
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 157 times.
337 if (state.cpu_flag_name) {
733 180 color_printf(COLOR_YELLOW, "%s:\n", state.cpu_flag_name);
734 180 state.cpu_flag_name = NULL;
735 }
736 337 }
737
738 #if CONFIG_LINUX_PERF
739 static int bench_init_linux(void)
740 {
741 struct perf_event_attr attr = {
742 .type = PERF_TYPE_HARDWARE,
743 .size = sizeof(struct perf_event_attr),
744 .config = PERF_COUNT_HW_CPU_CYCLES,
745 .disabled = 1, // start counting only on demand
746 .exclude_kernel = 1,
747 .exclude_hv = 1,
748 #if !ARCH_X86
749 .exclude_guest = 1,
750 #endif
751 };
752
753 printf("benchmarking with Linux Perf Monitoring API\n");
754
755 state.sysfd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
756 if (state.sysfd == -1) {
757 perror("perf_event_open");
758 return -1;
759 }
760 return 0;
761 }
762 #elif CONFIG_MACOS_KPERF
763 static int bench_init_kperf(void)
764 {
765 ff_kperf_init();
766 return 0;
767 }
768 #else
769 static int bench_init_ffmpeg(void)
770 {
771 #ifdef AV_READ_TIME
772 if (!checkasm_save_context()) {
773 checkasm_set_signal_handler_state(1);
774 AV_READ_TIME();
775 checkasm_set_signal_handler_state(0);
776 } else {
777 fprintf(stderr, "checkasm: unable to execute platform specific timer\n");
778 return -1;
779 }
780 printf("benchmarking with native FFmpeg timers\n");
781 return 0;
782 #else
783 fprintf(stderr, "checkasm: --bench is not supported on your system\n");
784 return -1;
785 #endif
786 }
787 #endif
788
789 static int bench_init(void)
790 {
791 #if CONFIG_LINUX_PERF
792 int ret = bench_init_linux();
793 #elif CONFIG_MACOS_KPERF
794 int ret = bench_init_kperf();
795 #else
796 int ret = bench_init_ffmpeg();
797 #endif
798 if (ret < 0)
799 return ret;
800
801 state.nop_time = measure_nop_time();
802 printf("nop: %d.%d\n", state.nop_time/10, state.nop_time%10);
803 return 0;
804 }
805
806 61 static void bench_uninit(void)
807 {
808 #if CONFIG_LINUX_PERF
809 if (state.sysfd > 0)
810 close(state.sysfd);
811 #endif
812 61 }
813
814 static int usage(const char *path)
815 {
816 fprintf(stderr,
817 "Usage: %s [--bench] [--test=<pattern>] [--verbose] [seed]\n",
818 path);
819 return 1;
820 }
821
822 61 int main(int argc, char *argv[])
823 {
824 61 unsigned int seed = av_get_random_seed();
825 61 int i, ret = 0;
826
827 #ifdef _WIN32
828 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
829 AddVectoredExceptionHandler(0, signal_handler);
830 #endif
831 #else
832 61 sigaction(SIGBUS, &signal_handler_act, NULL);
833 61 sigaction(SIGFPE, &signal_handler_act, NULL);
834 61 sigaction(SIGILL, &signal_handler_act, NULL);
835 61 sigaction(SIGSEGV, &signal_handler_act, NULL);
836 #endif
837 #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
838 if (have_vfp(av_get_cpu_flags()) || have_neon(av_get_cpu_flags()))
839 checkasm_checked_call = checkasm_checked_call_vfp;
840 #endif
841
842
2/4
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 61 times.
61 if (!tests[0].func || !cpus[0].flag) {
843 fprintf(stderr, "checkasm: no tests to perform\n");
844 return 0;
845 }
846
847
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 61 times.
122 for (i = 1; i < argc; i++) {
848 61 const char *arg = argv[i];
849 unsigned long l;
850 char *end;
851
852
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
61 if (!strncmp(arg, "--bench", 7)) {
853 if (bench_init() < 0)
854 return 1;
855 if (arg[7] == '=') {
856 state.bench_pattern = arg + 8;
857 state.bench_pattern_len = strlen(state.bench_pattern);
858 } else
859 state.bench_pattern = "";
860
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 } else if (!strncmp(arg, "--test=", 7)) {
861 61 state.test_name = arg + 7;
862 } else if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
863 state.verbose = 1;
864 } else if ((l = strtoul(arg, &end, 10)) <= UINT_MAX &&
865 *end == '\0') {
866 seed = l;
867 } else {
868 return usage(argv[0]);
869 }
870 }
871
872 61 fprintf(stderr, "checkasm: using random seed %u\n", seed);
873 61 av_lfg_init(&checkasm_lfg, seed);
874
875 61 check_cpu_flag(NULL, 0);
876
2/2
✓ Branch 0 taken 1098 times.
✓ Branch 1 taken 61 times.
1159 for (i = 0; cpus[i].flag; i++)
877 1098 check_cpu_flag(cpus[i].name, cpus[i].flag);
878
879
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
61 if (state.num_failed) {
880 fprintf(stderr, "checkasm: %d of %d tests have failed\n", state.num_failed, state.num_checked);
881 ret = 1;
882 } else {
883 61 fprintf(stderr, "checkasm: all %d tests passed\n", state.num_checked);
884
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
61 if (state.bench_pattern) {
885 print_benchs(state.funcs);
886 }
887 }
888
889 61 destroy_func_tree(state.funcs);
890 61 bench_uninit();
891 61 return ret;
892 }
893
894 /* Decide whether or not the specified function needs to be tested and
895 * allocate/initialize data structures if needed. Returns a pointer to a
896 * reference function if the function should be tested, otherwise NULL */
897 93405 void *checkasm_check_func(void *func, const char *name, ...)
898 {
899 char name_buf[256];
900 93405 void *ref = func;
901 CheckasmFuncVersion *v;
902 int name_length;
903 va_list arg;
904
905 93405 va_start(arg, name);
906 93405 name_length = vsnprintf(name_buf, sizeof(name_buf), name, arg);
907 93405 va_end(arg);
908
909
3/6
✓ Branch 0 taken 93405 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 93405 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 93405 times.
93405 if (!func || name_length <= 0 || name_length >= sizeof(name_buf))
910 return NULL;
911
912 93405 state.current_func = get_func(&state.funcs, name_buf);
913 93405 state.funcs->color = 1;
914 93405 v = &state.current_func->versions;
915
916
2/2
✓ Branch 0 taken 86658 times.
✓ Branch 1 taken 6747 times.
93405 if (v->func) {
917 CheckasmFuncVersion *prev;
918 do {
919 /* Only test functions that haven't already been tested */
920
2/2
✓ Branch 0 taken 76902 times.
✓ Branch 1 taken 55016 times.
131918 if (v->func == func)
921 76902 return NULL;
922
923
1/2
✓ Branch 0 taken 55016 times.
✗ Branch 1 not taken.
55016 if (v->ok)
924 55016 ref = v->func;
925
926 55016 prev = v;
927
2/2
✓ Branch 0 taken 45260 times.
✓ Branch 1 taken 9756 times.
55016 } while ((v = v->next));
928
929 9756 v = prev->next = checkasm_malloc(sizeof(CheckasmFuncVersion));
930 }
931
932 16503 v->func = func;
933 16503 v->ok = 1;
934 16503 v->cpu = state.cpu_flag;
935 16503 state.current_func_ver = v;
936
937
2/2
✓ Branch 0 taken 9756 times.
✓ Branch 1 taken 6747 times.
16503 if (state.cpu_flag)
938 9756 state.num_checked++;
939
940 16503 return ref;
941 }
942
943 /* Decide whether or not the current function needs to be benchmarked */
944 37948 int checkasm_bench_func(void)
945 {
946
2/4
✓ Branch 0 taken 37948 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 37948 times.
37948 return !state.num_failed && state.bench_pattern &&
947 !strncmp(state.current_func->name, state.bench_pattern, state.bench_pattern_len);
948 }
949
950 /* Indicate that the current test has failed */
951 void checkasm_fail_func(const char *msg, ...)
952 {
953 if (state.current_func_ver && state.current_func_ver->cpu &&
954 state.current_func_ver->ok)
955 {
956 va_list arg;
957
958 print_cpu_name();
959 fprintf(stderr, " %s_%s (", state.current_func->name, cpu_suffix(state.current_func_ver->cpu));
960 va_start(arg, msg);
961 vfprintf(stderr, msg, arg);
962 va_end(arg);
963 fprintf(stderr, ")\n");
964
965 state.current_func_ver->ok = 0;
966 state.num_failed++;
967 }
968 }
969
970 272580 void checkasm_set_signal_handler_state(int enabled) {
971 272580 state.catch_signals = enabled;
972 272580 }
973
974 93405 int checkasm_handle_signal(int s) {
975
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93405 times.
93405 if (s) {
976 #ifdef __GLIBC__
977 checkasm_fail_func("fatal signal %d: %s", s, strsignal(s));
978 #else
979 checkasm_fail_func(s == SIGFPE ? "fatal arithmetic error" :
980 s == SIGILL ? "illegal instruction" :
981 s == SIGBUS ? "bus error" :
982 "segmentation fault");
983 #endif
984 }
985 93405 return s;
986 }
987
988 /* Get the benchmark context of the current function */
989 CheckasmPerf *checkasm_get_perf_context(void)
990 {
991 CheckasmPerf *perf = &state.current_func_ver->perf;
992 memset(perf, 0, sizeof(*perf));
993 perf->sysfd = state.sysfd;
994 return perf;
995 }
996
997 /* Print the outcome of all tests performed since the last time this function was called */
998 2124 void checkasm_report(const char *name, ...)
999 {
1000 static int prev_checked, prev_failed, max_length;
1001
1002
2/2
✓ Branch 0 taken 337 times.
✓ Branch 1 taken 1787 times.
2124 if (state.num_checked > prev_checked) {
1003 337 int pad_length = max_length + 4;
1004 va_list arg;
1005
1006 337 print_cpu_name();
1007 337 pad_length -= fprintf(stderr, " - %s.", state.current_test_name);
1008 337 va_start(arg, name);
1009 337 pad_length -= vfprintf(stderr, name, arg);
1010 337 va_end(arg);
1011 337 fprintf(stderr, "%*c", FFMAX(pad_length, 0) + 2, '[');
1012
1013
1/2
✓ Branch 0 taken 337 times.
✗ Branch 1 not taken.
337 if (state.num_failed == prev_failed)
1014 337 color_printf(COLOR_GREEN, "OK");
1015 else
1016 color_printf(COLOR_RED, "FAILED");
1017 337 fprintf(stderr, "]\n");
1018
1019 337 prev_checked = state.num_checked;
1020 337 prev_failed = state.num_failed;
1021
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 1619 times.
1787 } else if (!state.cpu_flag) {
1022 /* Calculate the amount of padding required to make the output vertically aligned */
1023 168 int length = strlen(state.current_test_name);
1024 va_list arg;
1025
1026 168 va_start(arg, name);
1027 168 length += vsnprintf(NULL, 0, name, arg);
1028 168 va_end(arg);
1029
1030
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 63 times.
168 if (length > max_length)
1031 105 max_length = length;
1032 }
1033 2124 }
1034
1035 #define DEF_CHECKASM_CHECK_FUNC(type, fmt) \
1036 int checkasm_check_##type(const char *file, int line, \
1037 const type *buf1, ptrdiff_t stride1, \
1038 const type *buf2, ptrdiff_t stride2, \
1039 int w, int h, const char *name) \
1040 { \
1041 int y = 0; \
1042 stride1 /= sizeof(*buf1); \
1043 stride2 /= sizeof(*buf2); \
1044 for (y = 0; y < h; y++) \
1045 if (memcmp(&buf1[y*stride1], &buf2[y*stride2], w*sizeof(*buf1))) \
1046 break; \
1047 if (y == h) \
1048 return 0; \
1049 checkasm_fail_func("%s:%d", file, line); \
1050 if (!state.verbose) \
1051 return 1; \
1052 fprintf(stderr, "%s:\n", name); \
1053 while (h--) { \
1054 for (int x = 0; x < w; x++) \
1055 fprintf(stderr, " " fmt, buf1[x]); \
1056 fprintf(stderr, " "); \
1057 for (int x = 0; x < w; x++) \
1058 fprintf(stderr, " " fmt, buf2[x]); \
1059 fprintf(stderr, " "); \
1060 for (int x = 0; x < w; x++) \
1061 fprintf(stderr, "%c", buf1[x] != buf2[x] ? 'x' : '.'); \
1062 buf1 += stride1; \
1063 buf2 += stride2; \
1064 fprintf(stderr, "\n"); \
1065 } \
1066 return 1; \
1067 }
1068
1069
4/18
✗ Branch 0 not taken.
✓ Branch 1 taken 117227 times.
✓ Branch 2 taken 117227 times.
✓ Branch 3 taken 4744 times.
✓ Branch 4 taken 4744 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
121971 DEF_CHECKASM_CHECK_FUNC(uint8_t, "%02x")
1070
4/18
✗ Branch 0 not taken.
✓ Branch 1 taken 342752 times.
✓ Branch 2 taken 342752 times.
✓ Branch 3 taken 14206 times.
✓ Branch 4 taken 14206 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
356958 DEF_CHECKASM_CHECK_FUNC(uint16_t, "%04x")
1071 DEF_CHECKASM_CHECK_FUNC(uint32_t, "%08x")
1072
4/18
✗ Branch 0 not taken.
✓ Branch 1 taken 14082 times.
✓ Branch 2 taken 14082 times.
✓ Branch 3 taken 548 times.
✓ Branch 4 taken 548 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
14630 DEF_CHECKASM_CHECK_FUNC(int16_t, "%6d")
1073 DEF_CHECKASM_CHECK_FUNC(int32_t, "%9d")
1074