FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/tests/checkasm/checkasm.c
Date: 2025-09-01 20:07:09
Exec Total Coverage
Lines: 205 357 57.4%
Functions: 31 44 70.5%
Branches: 208 502 41.4%

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 * Copyright © 2018, VideoLAN and dav1d authors
23 * Copyright © 2018, Two Orioles, LLC
24 * All rights reserved.
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions are met:
28 *
29 * 1. Redistributions of source code must retain the above copyright notice, this
30 * list of conditions and the following disclaimer.
31 *
32 * 2. Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
38 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
40 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
41 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
45 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 */
47
48 #include "config.h"
49 #include "config_components.h"
50
51 #ifndef _GNU_SOURCE
52 # define _GNU_SOURCE // for syscall (performance monitoring API), strsignal()
53 #endif
54
55 #include <signal.h>
56 #include <stdarg.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include "checkasm.h"
61 #include "libavutil/avassert.h"
62 #include "libavutil/common.h"
63 #include "libavutil/cpu.h"
64 #include "libavutil/intfloat.h"
65 #include "libavutil/random_seed.h"
66
67 #if HAVE_IO_H
68 #include <io.h>
69 #endif
70 #if HAVE_PRCTL
71 #include <sys/prctl.h>
72 #endif
73
74 #if defined(_WIN32) && !defined(SIGBUS)
75 /* non-standard, use the same value as mingw-w64 */
76 #define SIGBUS 10
77 #endif
78
79 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
80 #include <windows.h>
81 #define COLOR_RED FOREGROUND_RED
82 #define COLOR_GREEN FOREGROUND_GREEN
83 #define COLOR_YELLOW (FOREGROUND_RED|FOREGROUND_GREEN)
84 #else
85 #define COLOR_RED 1
86 #define COLOR_GREEN 2
87 #define COLOR_YELLOW 3
88 #endif
89
90 #if HAVE_UNISTD_H
91 #include <unistd.h>
92 #endif
93
94 #if !HAVE_ISATTY
95 #define isatty(fd) 1
96 #endif
97
98 #if ARCH_AARCH64
99 #include "libavutil/aarch64/cpu.h"
100 #elif ARCH_RISCV
101 #include "libavutil/riscv/cpu.h"
102 #endif
103
104 #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
105 #include "libavutil/arm/cpu.h"
106
107 void (*checkasm_checked_call)(void *func, int dummy, ...) = checkasm_checked_call_novfp;
108 #endif
109
110 /* Trade-off between speed and accuracy */
111 uint64_t bench_runs = 1U << 10;
112
113 /* List of tests to invoke */
114 static const struct {
115 const char *name;
116 void (*func)(void);
117 } tests[] = {
118 #if CONFIG_AVCODEC
119 #if CONFIG_AAC_DECODER
120 { "aacpsdsp", checkasm_check_aacpsdsp },
121 { "sbrdsp", checkasm_check_sbrdsp },
122 #endif
123 #if CONFIG_AAC_ENCODER
124 { "aacencdsp", checkasm_check_aacencdsp },
125 #endif
126 #if CONFIG_AC3DSP
127 { "ac3dsp", checkasm_check_ac3dsp },
128 #endif
129 #if CONFIG_ALAC_DECODER
130 { "alacdsp", checkasm_check_alacdsp },
131 #endif
132 #if CONFIG_APV_DECODER
133 { "apv_dsp", checkasm_check_apv_dsp },
134 #endif
135 #if CONFIG_AUDIODSP
136 { "audiodsp", checkasm_check_audiodsp },
137 #endif
138 #if CONFIG_BLOCKDSP
139 { "blockdsp", checkasm_check_blockdsp },
140 #endif
141 #if CONFIG_BSWAPDSP
142 { "bswapdsp", checkasm_check_bswapdsp },
143 #endif
144 #if CONFIG_DCA_DECODER
145 { "synth_filter", checkasm_check_synth_filter },
146 #endif
147 #if CONFIG_DIRAC_DECODER
148 { "diracdsp", checkasm_check_diracdsp },
149 #endif
150 #if CONFIG_EXR_DECODER
151 { "exrdsp", checkasm_check_exrdsp },
152 #endif
153 #if CONFIG_FDCTDSP
154 { "fdctdsp", checkasm_check_fdctdsp },
155 #endif
156 #if CONFIG_FLAC_DECODER
157 { "flacdsp", checkasm_check_flacdsp },
158 #endif
159 #if CONFIG_FMTCONVERT
160 { "fmtconvert", checkasm_check_fmtconvert },
161 #endif
162 #if CONFIG_G722DSP
163 { "g722dsp", checkasm_check_g722dsp },
164 #endif
165 #if CONFIG_H263DSP
166 { "h263dsp", checkasm_check_h263dsp },
167 #endif
168 #if CONFIG_H264CHROMA
169 { "h264chroma", checkasm_check_h264chroma },
170 #endif
171 #if CONFIG_H264DSP
172 { "h264dsp", checkasm_check_h264dsp },
173 #endif
174 #if CONFIG_H264PRED
175 { "h264pred", checkasm_check_h264pred },
176 #endif
177 #if CONFIG_H264QPEL
178 { "h264qpel", checkasm_check_h264qpel },
179 #endif
180 #if CONFIG_HEVC_DECODER
181 { "hevc_add_res", checkasm_check_hevc_add_res },
182 { "hevc_deblock", checkasm_check_hevc_deblock },
183 { "hevc_idct", checkasm_check_hevc_idct },
184 { "hevc_pel", checkasm_check_hevc_pel },
185 { "hevc_sao", checkasm_check_hevc_sao },
186 #endif
187 #if CONFIG_HUFFYUV_DECODER
188 { "huffyuvdsp", checkasm_check_huffyuvdsp },
189 #endif
190 #if CONFIG_IDCTDSP
191 { "idctdsp", checkasm_check_idctdsp },
192 #endif
193 #if CONFIG_JPEG2000_DECODER
194 { "jpeg2000dsp", checkasm_check_jpeg2000dsp },
195 #endif
196 #if CONFIG_LLAUDDSP
197 { "llauddsp", checkasm_check_llauddsp },
198 #endif
199 #if CONFIG_HUFFYUVDSP
200 { "llviddsp", checkasm_check_llviddsp },
201 #endif
202 #if CONFIG_LLVIDENCDSP
203 { "llviddspenc", checkasm_check_llviddspenc },
204 #endif
205 #if CONFIG_LPC
206 { "lpc", checkasm_check_lpc },
207 #endif
208 #if CONFIG_ME_CMP
209 { "motion", checkasm_check_motion },
210 #endif
211 #if CONFIG_MPEGVIDEOENCDSP
212 { "mpegvideoencdsp", checkasm_check_mpegvideoencdsp },
213 #endif
214 #if CONFIG_OPUS_DECODER
215 { "opusdsp", checkasm_check_opusdsp },
216 #endif
217 #if CONFIG_PIXBLOCKDSP
218 { "pixblockdsp", checkasm_check_pixblockdsp },
219 #endif
220 #if CONFIG_RV34DSP
221 { "rv34dsp", checkasm_check_rv34dsp },
222 #endif
223 #if CONFIG_RV40_DECODER
224 { "rv40dsp", checkasm_check_rv40dsp },
225 #endif
226 #if CONFIG_SVQ1_ENCODER
227 { "svq1enc", checkasm_check_svq1enc },
228 #endif
229 #if CONFIG_TAK_DECODER
230 { "takdsp", checkasm_check_takdsp },
231 #endif
232 #if CONFIG_UTVIDEO_DECODER
233 { "utvideodsp", checkasm_check_utvideodsp },
234 #endif
235 #if CONFIG_V210_DECODER
236 { "v210dec", checkasm_check_v210dec },
237 #endif
238 #if CONFIG_V210_ENCODER
239 { "v210enc", checkasm_check_v210enc },
240 #endif
241 #if CONFIG_VC1DSP
242 { "vc1dsp", checkasm_check_vc1dsp },
243 #endif
244 #if CONFIG_VP8DSP
245 { "vp8dsp", checkasm_check_vp8dsp },
246 #endif
247 #if CONFIG_VP9_DECODER
248 { "vp9dsp", checkasm_check_vp9dsp },
249 #endif
250 #if CONFIG_VIDEODSP
251 { "videodsp", checkasm_check_videodsp },
252 #endif
253 #if CONFIG_VORBIS_DECODER
254 { "vorbisdsp", checkasm_check_vorbisdsp },
255 #endif
256 #if CONFIG_VVC_DECODER
257 { "vvc_alf", checkasm_check_vvc_alf },
258 { "vvc_mc", checkasm_check_vvc_mc },
259 { "vvc_sao", checkasm_check_vvc_sao },
260 #endif
261 #endif
262 #if CONFIG_AVFILTER
263 #if CONFIG_SCENE_SAD
264 { "scene_sad", checkasm_check_scene_sad },
265 #endif
266 #if CONFIG_AFIR_FILTER
267 { "af_afir", checkasm_check_afir },
268 #endif
269 #if CONFIG_BLACKDETECT_FILTER
270 { "vf_blackdetect", checkasm_check_blackdetect },
271 #endif
272 #if CONFIG_BLEND_FILTER
273 { "vf_blend", checkasm_check_blend },
274 #endif
275 #if CONFIG_BWDIF_FILTER
276 { "vf_bwdif", checkasm_check_vf_bwdif },
277 #endif
278 #if CONFIG_COLORDETECT_FILTER
279 { "vf_colordetect", checkasm_check_colordetect },
280 #endif
281 #if CONFIG_COLORSPACE_FILTER
282 { "vf_colorspace", checkasm_check_colorspace },
283 #endif
284 #if CONFIG_EQ_FILTER
285 { "vf_eq", checkasm_check_vf_eq },
286 #endif
287 #if CONFIG_GBLUR_FILTER
288 { "vf_gblur", checkasm_check_vf_gblur },
289 #endif
290 #if CONFIG_HFLIP_FILTER
291 { "vf_hflip", checkasm_check_vf_hflip },
292 #endif
293 #if CONFIG_NLMEANS_FILTER
294 { "vf_nlmeans", checkasm_check_nlmeans },
295 #endif
296 #if CONFIG_THRESHOLD_FILTER
297 { "vf_threshold", checkasm_check_vf_threshold },
298 #endif
299 #if CONFIG_SOBEL_FILTER
300 { "vf_sobel", checkasm_check_vf_sobel },
301 #endif
302 #endif
303 #if CONFIG_SWSCALE
304 { "sw_gbrp", checkasm_check_sw_gbrp },
305 { "sw_range_convert", checkasm_check_sw_range_convert },
306 { "sw_rgb", checkasm_check_sw_rgb },
307 { "sw_scale", checkasm_check_sw_scale },
308 { "sw_yuv2rgb", checkasm_check_sw_yuv2rgb },
309 { "sw_yuv2yuv", checkasm_check_sw_yuv2yuv },
310 { "sw_ops", checkasm_check_sw_ops },
311 #endif
312 #if CONFIG_AVUTIL
313 { "aes", checkasm_check_aes },
314 { "fixed_dsp", checkasm_check_fixed_dsp },
315 { "float_dsp", checkasm_check_float_dsp },
316 { "lls", checkasm_check_lls },
317 { "av_tx", checkasm_check_av_tx },
318 #endif
319 { NULL }
320 };
321
322 /* List of cpu flags to check */
323 static const struct {
324 const char *name;
325 const char *suffix;
326 int flag;
327 } cpus[] = {
328 #if ARCH_AARCH64
329 { "ARMV8", "armv8", AV_CPU_FLAG_ARMV8 },
330 { "NEON", "neon", AV_CPU_FLAG_NEON },
331 { "DOTPROD", "dotprod", AV_CPU_FLAG_DOTPROD },
332 { "I8MM", "i8mm", AV_CPU_FLAG_I8MM },
333 { "SVE", "sve", AV_CPU_FLAG_SVE },
334 { "SVE2", "sve2", AV_CPU_FLAG_SVE2 },
335 #elif ARCH_ARM
336 { "ARMV5TE", "armv5te", AV_CPU_FLAG_ARMV5TE },
337 { "ARMV6", "armv6", AV_CPU_FLAG_ARMV6 },
338 { "ARMV6T2", "armv6t2", AV_CPU_FLAG_ARMV6T2 },
339 { "VFP", "vfp", AV_CPU_FLAG_VFP },
340 { "VFP_VM", "vfp_vm", AV_CPU_FLAG_VFP_VM },
341 { "VFPV3", "vfp3", AV_CPU_FLAG_VFPV3 },
342 { "NEON", "neon", AV_CPU_FLAG_NEON },
343 #elif ARCH_PPC
344 { "ALTIVEC", "altivec", AV_CPU_FLAG_ALTIVEC },
345 { "VSX", "vsx", AV_CPU_FLAG_VSX },
346 { "POWER8", "power8", AV_CPU_FLAG_POWER8 },
347 #elif ARCH_RISCV
348 { "RVI", "rvi", AV_CPU_FLAG_RVI },
349 { "misaligned", "misaligned", AV_CPU_FLAG_RV_MISALIGNED },
350 { "RV_zbb", "rvb_b", AV_CPU_FLAG_RVB_BASIC },
351 { "RVB", "rvb", AV_CPU_FLAG_RVB },
352 { "RV_zve32x","rvv_i32", AV_CPU_FLAG_RVV_I32 },
353 { "RV_zve32f","rvv_f32", AV_CPU_FLAG_RVV_F32 },
354 { "RV_zve64x","rvv_i64", AV_CPU_FLAG_RVV_I64 },
355 { "RV_zve64d","rvv_f64", AV_CPU_FLAG_RVV_F64 },
356 { "RV_zvbb", "rv_zvbb", AV_CPU_FLAG_RV_ZVBB },
357 #elif ARCH_MIPS
358 { "MMI", "mmi", AV_CPU_FLAG_MMI },
359 { "MSA", "msa", AV_CPU_FLAG_MSA },
360 #elif ARCH_X86
361 { "MMX", "mmx", AV_CPU_FLAG_MMX|AV_CPU_FLAG_CMOV },
362 { "MMXEXT", "mmxext", AV_CPU_FLAG_MMXEXT },
363 { "3DNOW", "3dnow", AV_CPU_FLAG_3DNOW },
364 { "3DNOWEXT", "3dnowext", AV_CPU_FLAG_3DNOWEXT },
365 { "SSE", "sse", AV_CPU_FLAG_SSE },
366 { "SSE2", "sse2", AV_CPU_FLAG_SSE2|AV_CPU_FLAG_SSE2SLOW },
367 { "SSE3", "sse3", AV_CPU_FLAG_SSE3|AV_CPU_FLAG_SSE3SLOW },
368 { "SSSE3", "ssse3", AV_CPU_FLAG_SSSE3|AV_CPU_FLAG_ATOM },
369 { "SSE4.1", "sse4", AV_CPU_FLAG_SSE4 },
370 { "SSE4.2", "sse42", AV_CPU_FLAG_SSE42 },
371 { "AES-NI", "aesni", AV_CPU_FLAG_AESNI },
372 { "AVX", "avx", AV_CPU_FLAG_AVX },
373 { "XOP", "xop", AV_CPU_FLAG_XOP },
374 { "FMA3", "fma3", AV_CPU_FLAG_FMA3 },
375 { "FMA4", "fma4", AV_CPU_FLAG_FMA4 },
376 { "AVX2", "avx2", AV_CPU_FLAG_AVX2 },
377 { "AVX-512", "avx512", AV_CPU_FLAG_AVX512 },
378 { "AVX-512ICL", "avx512icl", AV_CPU_FLAG_AVX512ICL },
379 #elif ARCH_LOONGARCH
380 { "LSX", "lsx", AV_CPU_FLAG_LSX },
381 { "LASX", "lasx", AV_CPU_FLAG_LASX },
382 #elif ARCH_WASM
383 { "SIMD128", "simd128", AV_CPU_FLAG_SIMD128 },
384 #endif
385 { NULL }
386 };
387
388 typedef struct CheckasmFuncVersion {
389 struct CheckasmFuncVersion *next;
390 void *func;
391 int ok;
392 int cpu;
393 CheckasmPerf perf;
394 } CheckasmFuncVersion;
395
396 /* Binary search tree node */
397 typedef struct CheckasmFunc {
398 struct CheckasmFunc *child[2];
399 CheckasmFuncVersion versions;
400 uint8_t color; /* 0 = red, 1 = black */
401 char name[1];
402 } CheckasmFunc;
403
404 /* Internal state */
405 static struct {
406 CheckasmFunc *funcs;
407 CheckasmFunc *current_func;
408 CheckasmFuncVersion *current_func_ver;
409 const char *current_test_name;
410 const char *bench_pattern;
411 int bench_pattern_len;
412 int num_checked;
413 int num_failed;
414
415 /* perf */
416 int nop_time;
417 int sysfd;
418
419 int cpu_flag;
420 const char *cpu_flag_name;
421 const char *test_pattern;
422 int verbose;
423 int csv;
424 int tsv;
425 volatile sig_atomic_t catch_signals;
426 } state;
427
428 /* PRNG state */
429 AVLFG checkasm_lfg;
430
431 /* float compare support code */
432 6316680 static int is_negative(union av_intfloat32 u)
433 {
434 6316680 return u.i >> 31;
435 }
436
437 3158340 int float_near_ulp(float a, float b, unsigned max_ulp)
438 {
439 union av_intfloat32 x, y;
440
441 3158340 x.f = a;
442 3158340 y.f = b;
443
444
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 3158340 times.
3158340 if (is_negative(x) != is_negative(y)) {
445 // handle -0.0 == +0.0
446 return a == b;
447 }
448
449
2/2
✓ Branch 0 taken 3158338 times.
✓ Branch 1 taken 2 times.
3158340 if (llabs((int64_t)x.i - y.i) <= max_ulp)
450 3158338 return 1;
451
452 2 return 0;
453 }
454
455 9639 int float_near_ulp_array(const float *a, const float *b, unsigned max_ulp,
456 unsigned len)
457 {
458 unsigned i;
459
460
2/2
✓ Branch 0 taken 3153220 times.
✓ Branch 1 taken 9639 times.
3162859 for (i = 0; i < len; i++) {
461
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3153220 times.
3153220 if (!float_near_ulp(a[i], b[i], max_ulp))
462 return 0;
463 }
464 9639 return 1;
465 }
466
467 882135 int float_near_abs_eps(float a, float b, float eps)
468 {
469 882135 float abs_diff = fabsf(a - b);
470
1/2
✓ Branch 0 taken 882135 times.
✗ Branch 1 not taken.
882135 if (abs_diff < eps)
471 882135 return 1;
472
473 fprintf(stderr, "test failed comparing %g with %g (abs diff=%g with EPS=%g)\n", a, b, abs_diff, eps);
474
475 return 0;
476 }
477
478 189 int float_near_abs_eps_array(const float *a, const float *b, float eps,
479 unsigned len)
480 {
481 unsigned i;
482
483
2/2
✓ Branch 0 taken 872138 times.
✓ Branch 1 taken 189 times.
872327 for (i = 0; i < len; i++) {
484
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 872138 times.
872138 if (!float_near_abs_eps(a[i], b[i], eps))
485 return 0;
486 }
487 189 return 1;
488 }
489
490 5120 int float_near_abs_eps_ulp(float a, float b, float eps, unsigned max_ulp)
491 {
492
3/4
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 5118 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
5120 return float_near_ulp(a, b, max_ulp) || float_near_abs_eps(a, b, eps);
493 }
494
495 int float_near_abs_eps_array_ulp(const float *a, const float *b, float eps,
496 unsigned max_ulp, unsigned len)
497 {
498 unsigned i;
499
500 for (i = 0; i < len; i++) {
501 if (!float_near_abs_eps_ulp(a[i], b[i], eps, max_ulp))
502 return 0;
503 }
504 return 1;
505 }
506
507 63355 int double_near_abs_eps(double a, double b, double eps)
508 {
509 63355 double abs_diff = fabs(a - b);
510
511 63355 return abs_diff < eps;
512 }
513
514 11 int double_near_abs_eps_array(const double *a, const double *b, double eps,
515 unsigned len)
516 {
517 unsigned i;
518
519
2/2
✓ Branch 0 taken 41068 times.
✓ Branch 1 taken 11 times.
41079 for (i = 0; i < len; i++) {
520
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 41068 times.
41068 if (!double_near_abs_eps(a[i], b[i], eps))
521 return 0;
522 }
523 11 return 1;
524 }
525
526 /* Print colored text to stderr if the terminal supports it */
527 636 static void color_printf(int color, const char *fmt, ...)
528 {
529 static int use_color = -1;
530 va_list arg;
531
532 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
533 static HANDLE con;
534 static WORD org_attributes;
535
536 if (use_color < 0) {
537 CONSOLE_SCREEN_BUFFER_INFO con_info;
538 con = GetStdHandle(STD_ERROR_HANDLE);
539 if (con && con != INVALID_HANDLE_VALUE && GetConsoleScreenBufferInfo(con, &con_info)) {
540 org_attributes = con_info.wAttributes;
541 use_color = 1;
542 } else
543 use_color = 0;
544 }
545 if (use_color)
546 SetConsoleTextAttribute(con, (org_attributes & 0xfff0) | (color & 0x0f));
547 #else
548
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 563 times.
636 if (use_color < 0) {
549 73 const char *term = getenv("TERM");
550
3/6
✓ Branch 0 taken 73 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 73 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 73 times.
73 use_color = term && strcmp(term, "dumb") && isatty(2);
551 }
552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 636 times.
636 if (use_color)
553 fprintf(stderr, "\x1b[%d;3%dm", (color & 0x08) >> 3, color & 0x07);
554 #endif
555
556 636 va_start(arg, fmt);
557 636 vfprintf(stderr, fmt, arg);
558 636 va_end(arg);
559
560
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 636 times.
636 if (use_color) {
561 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
562 SetConsoleTextAttribute(con, org_attributes);
563 #else
564 fprintf(stderr, "\x1b[0m");
565 #endif
566 }
567 636 }
568
569 /* Deallocate a tree */
570 24976 static void destroy_func_tree(CheckasmFunc *f)
571 {
572
2/2
✓ Branch 0 taken 12450 times.
✓ Branch 1 taken 12526 times.
24976 if (f) {
573 12450 CheckasmFuncVersion *v = f->versions.next;
574
2/2
✓ Branch 0 taken 14752 times.
✓ Branch 1 taken 12450 times.
27202 while (v) {
575 14752 CheckasmFuncVersion *next = v->next;
576 14752 free(v);
577 14752 v = next;
578 }
579
580 12450 destroy_func_tree(f->child[0]);
581 12450 destroy_func_tree(f->child[1]);
582 12450 free(f);
583 }
584 24976 }
585
586 /* Allocate a zero-initialized block, clean up and exit on failure */
587 27202 static void *checkasm_malloc(size_t size)
588 {
589 27202 void *ptr = calloc(1, size);
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27202 times.
27202 if (!ptr) {
591 fprintf(stderr, "checkasm: malloc failed\n");
592 destroy_func_tree(state.funcs);
593 exit(1);
594 }
595 27202 return ptr;
596 }
597
598 /* Get the suffix of the specified cpu flag */
599 static const char *cpu_suffix(int cpu)
600 {
601 int i = FF_ARRAY_ELEMS(cpus);
602
603 while (--i >= 0)
604 if (cpu & cpus[i].flag)
605 return cpus[i].suffix;
606
607 return "c";
608 }
609
610 static int cmp_nop(const void *a, const void *b)
611 {
612 return *(const uint16_t*)a - *(const uint16_t*)b;
613 }
614
615 /* Measure the overhead of the timing code (in decicycles) */
616 static int measure_nop_time(void)
617 {
618 uint16_t nops[10000];
619 int i, nop_sum = 0;
620 av_unused const int sysfd = state.sysfd;
621
622 uint64_t t = 0;
623 for (i = 0; i < 10000; i++) {
624 PERF_START(t);
625 PERF_STOP(t);
626 nops[i] = t;
627 }
628
629 qsort(nops, 10000, sizeof(uint16_t), cmp_nop);
630 for (i = 2500; i < 7500; i++)
631 nop_sum += nops[i];
632
633 return nop_sum / 500;
634 }
635
636 static inline double avg_cycles_per_call(const CheckasmPerf *const p)
637 {
638 if (p->iterations) {
639 const double cycles = (double)(10 * p->cycles) / p->iterations - state.nop_time;
640 if (cycles > 0.0)
641 return cycles / 32.0; /* 32 calls per iteration */
642 }
643 return 0.0;
644 }
645
646 /* Print benchmark results */
647 static void print_benchs(CheckasmFunc *f)
648 {
649 if (f) {
650 CheckasmFuncVersion *v = &f->versions;
651 const CheckasmPerf *p = &v->perf;
652 const double baseline = avg_cycles_per_call(p);
653 double decicycles;
654
655 print_benchs(f->child[0]);
656
657 do {
658 if (p->iterations) {
659 p = &v->perf;
660 decicycles = avg_cycles_per_call(p);
661 if (state.csv || state.tsv) {
662 const char sep = state.csv ? ',' : '\t';
663 printf("%s%c%s%c%.1f\n", f->name, sep,
664 cpu_suffix(v->cpu), sep,
665 decicycles / 10.0);
666 } else {
667 const int pad_length = 10 + 50 -
668 printf("%s_%s:", f->name, cpu_suffix(v->cpu));
669 const double ratio = decicycles ?
670 baseline / decicycles : 0.0;
671 printf("%*.1f (%5.2fx)\n", FFMAX(pad_length, 0),
672 decicycles / 10.0, ratio);
673 }
674 }
675 } while ((v = v->next));
676
677 print_benchs(f->child[1]);
678 }
679 }
680
681 /* ASCIIbetical sort except preserving natural order for numbers */
682 1599470 static int cmp_func_names(const char *a, const char *b)
683 {
684 1599470 const char *start = a;
685 int ascii_diff, digit_diff;
686
687
4/4
✓ Branch 0 taken 26507224 times.
✓ Branch 1 taken 1443869 times.
✓ Branch 2 taken 26351623 times.
✓ Branch 3 taken 155601 times.
27951093 for (; !(ascii_diff = *(const unsigned char*)a - *(const unsigned char*)b) && *a; a++, b++);
688
4/4
✓ Branch 0 taken 1640349 times.
✓ Branch 1 taken 1340775 times.
✓ Branch 2 taken 1381654 times.
✓ Branch 3 taken 258695 times.
2981124 for (; av_isdigit(*a) && av_isdigit(*b); a++, b++);
689
690
6/6
✓ Branch 0 taken 1526035 times.
✓ Branch 1 taken 73435 times.
✓ Branch 2 taken 1108853 times.
✓ Branch 3 taken 417182 times.
✓ Branch 4 taken 392601 times.
✓ Branch 5 taken 716252 times.
1599470 if (a > start && av_isdigit(a[-1]) && (digit_diff = av_isdigit(*a) - av_isdigit(*b)))
691 392601 return digit_diff;
692
693 1206869 return ascii_diff;
694 }
695
696 /* Perform a tree rotation in the specified direction and return the new root */
697 11805 static CheckasmFunc *rotate_tree(CheckasmFunc *f, int dir)
698 {
699 11805 CheckasmFunc *r = f->child[dir^1];
700 11805 f->child[dir^1] = r->child[dir];
701 11805 r->child[dir] = f;
702 11805 r->color = f->color;
703 11805 f->color = 0;
704 11805 return r;
705 }
706
707 #define is_red(f) ((f) && !(f)->color)
708
709 /* Balance a left-leaning red-black tree at the specified node */
710 115480 static void balance_tree(CheckasmFunc **root)
711 {
712 115480 CheckasmFunc *f = *root;
713
714
8/8
✓ Branch 0 taken 109715 times.
✓ Branch 1 taken 5765 times.
✓ Branch 2 taken 46032 times.
✓ Branch 3 taken 63683 times.
✓ Branch 4 taken 41544 times.
✓ Branch 5 taken 4488 times.
✓ Branch 6 taken 8882 times.
✓ Branch 7 taken 32662 times.
115480 if (is_red(f->child[0]) && is_red(f->child[1])) {
715 8882 f->color ^= 1;
716 8882 f->child[0]->color = f->child[1]->color = 1;
717 }
718
719
7/8
✓ Branch 0 taken 109715 times.
✓ Branch 1 taken 5765 times.
✓ Branch 2 taken 72565 times.
✓ Branch 3 taken 37150 times.
✓ Branch 4 taken 78330 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8701 times.
✓ Branch 7 taken 69629 times.
115480 if (!is_red(f->child[0]) && is_red(f->child[1]))
720 8701 *root = rotate_tree(f, 0); /* Rotate left */
721
7/8
✓ Branch 0 taken 106779 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 37150 times.
✓ Branch 3 taken 69629 times.
✓ Branch 4 taken 34185 times.
✓ Branch 5 taken 2965 times.
✓ Branch 6 taken 3104 times.
✓ Branch 7 taken 31081 times.
106779 else if (is_red(f->child[0]) && is_red(f->child[0]->child[0]))
722 3104 *root = rotate_tree(f, 1); /* Rotate right */
723 115480 }
724
725 /* Get a node with the specified name, creating it if it doesn't exist */
726 1611920 static CheckasmFunc *get_func(CheckasmFunc **root, const char *name)
727 {
728 1611920 CheckasmFunc *f = *root;
729
730
2/2
✓ Branch 0 taken 1599470 times.
✓ Branch 1 taken 12450 times.
1611920 if (f) {
731 /* Search the tree for a matching node */
732 1599470 int cmp = cmp_func_names(name, f->name);
733
2/2
✓ Branch 0 taken 1443869 times.
✓ Branch 1 taken 155601 times.
1599470 if (cmp) {
734 1443869 f = get_func(&f->child[cmp > 0], name);
735
736 /* Rebalance the tree on the way up if a new node was inserted */
737
2/2
✓ Branch 0 taken 115480 times.
✓ Branch 1 taken 1328389 times.
1443869 if (!f->versions.func)
738 115480 balance_tree(root);
739 }
740 } else {
741 /* Allocate and insert a new node into the tree */
742 12450 int name_length = strlen(name);
743 12450 f = *root = checkasm_malloc(sizeof(CheckasmFunc) + name_length);
744 12450 memcpy(f->name, name, name_length + 1);
745 }
746
747 1611920 return f;
748 }
749
750 checkasm_context checkasm_context_buf;
751
752 /* Crash handling: attempt to catch crashes and handle them
753 * gracefully instead of just aborting abruptly. */
754 #ifdef _WIN32
755 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
756 static LONG NTAPI signal_handler(EXCEPTION_POINTERS *e) {
757 int s;
758
759 if (!state.catch_signals)
760 return EXCEPTION_CONTINUE_SEARCH;
761
762 switch (e->ExceptionRecord->ExceptionCode) {
763 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
764 case EXCEPTION_INT_DIVIDE_BY_ZERO:
765 s = SIGFPE;
766 break;
767 case EXCEPTION_ILLEGAL_INSTRUCTION:
768 case EXCEPTION_PRIV_INSTRUCTION:
769 s = SIGILL;
770 break;
771 case EXCEPTION_ACCESS_VIOLATION:
772 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
773 case EXCEPTION_DATATYPE_MISALIGNMENT:
774 case EXCEPTION_STACK_OVERFLOW:
775 s = SIGSEGV;
776 break;
777 case EXCEPTION_IN_PAGE_ERROR:
778 s = SIGBUS;
779 break;
780 default:
781 return EXCEPTION_CONTINUE_SEARCH;
782 }
783 state.catch_signals = 0;
784 checkasm_load_context(s);
785 return EXCEPTION_CONTINUE_EXECUTION; /* never reached, but shuts up gcc */
786 }
787 #endif
788 #elif !defined(_WASI_EMULATED_SIGNAL)
789 static void signal_handler(int s);
790
791 static const struct sigaction signal_handler_act = {
792 .sa_handler = signal_handler,
793 .sa_flags = SA_RESETHAND,
794 };
795
796 static void signal_handler(int s) {
797 if (state.catch_signals) {
798 state.catch_signals = 0;
799 sigaction(s, &signal_handler_act, NULL);
800 checkasm_load_context(s);
801 }
802 }
803 #endif
804
805 /* Compares a string with a wildcard pattern. */
806 76076 static int wildstrcmp(const char *str, const char *pattern)
807 {
808 76076 const char *wild = strchr(pattern, '*');
809
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76076 times.
76076 if (wild) {
810 const size_t len = wild - pattern;
811 if (strncmp(str, pattern, len)) return 1;
812 while (*++wild == '*');
813 if (!*wild) return 0;
814 str += len;
815 while (*str && wildstrcmp(str, wild)) str++;
816 return !*str;
817 }
818 76076 return strcmp(str, pattern);
819 }
820
821 /* Perform tests and benchmarks for the specified cpu flag if supported by the host */
822 1444 static void check_cpu_flag(const char *name, int flag)
823 {
824 1444 int old_cpu_flag = state.cpu_flag;
825
826 1444 flag |= old_cpu_flag;
827 1444 av_force_cpu_flags(-1);
828 1444 state.cpu_flag = flag & av_get_cpu_flags();
829 1444 av_force_cpu_flags(state.cpu_flag);
830
831
4/4
✓ Branch 0 taken 1368 times.
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 912 times.
✓ Branch 3 taken 456 times.
1444 if (!flag || state.cpu_flag != old_cpu_flag) {
832 int i;
833
834 988 state.cpu_flag_name = name;
835
2/2
✓ Branch 0 taken 76076 times.
✓ Branch 1 taken 988 times.
77064 for (i = 0; tests[i].func; i++) {
836
3/4
✓ Branch 0 taken 76076 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 75088 times.
✓ Branch 4 taken 988 times.
76076 if (state.test_pattern && wildstrcmp(tests[i].name, state.test_pattern))
837 75088 continue;
838 988 state.current_test_name = tests[i].name;
839 988 tests[i].func();
840 }
841 }
842 1444 }
843
844 /* Print the name of the current CPU flag, but only do it once */
845 432 static void print_cpu_name(void)
846 {
847
2/2
✓ Branch 0 taken 204 times.
✓ Branch 1 taken 228 times.
432 if (state.cpu_flag_name) {
848 204 color_printf(COLOR_YELLOW, "%s:\n", state.cpu_flag_name);
849 204 state.cpu_flag_name = NULL;
850 }
851 432 }
852
853 #if CONFIG_LINUX_PERF
854 static int bench_init_linux(void)
855 {
856 struct perf_event_attr attr = {
857 .type = PERF_TYPE_HARDWARE,
858 .size = sizeof(struct perf_event_attr),
859 .config = PERF_COUNT_HW_CPU_CYCLES,
860 .disabled = 1, // start counting only on demand
861 .exclude_kernel = 1,
862 .exclude_hv = 1,
863 #if !ARCH_X86
864 .exclude_guest = 1,
865 #endif
866 };
867
868 fprintf(stderr, "benchmarking with Linux Perf Monitoring API\n");
869
870 state.sysfd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
871 if (state.sysfd == -1) {
872 perror("perf_event_open");
873 return -1;
874 }
875 return 0;
876 }
877 #elif CONFIG_MACOS_KPERF
878 static int bench_init_kperf(void)
879 {
880 ff_kperf_init();
881 return 0;
882 }
883 #else
884 static int bench_init_ffmpeg(void)
885 {
886 #ifdef AV_READ_TIME
887 if (!checkasm_save_context()) {
888 checkasm_set_signal_handler_state(1);
889 AV_READ_TIME();
890 checkasm_set_signal_handler_state(0);
891 } else {
892 fprintf(stderr, "checkasm: unable to execute platform specific timer\n");
893 return -1;
894 }
895 fprintf(stderr, "benchmarking with native FFmpeg timers\n");
896 return 0;
897 #else
898 fprintf(stderr, "checkasm: --bench is not supported on your system\n");
899 return -1;
900 #endif
901 }
902 #endif
903
904 static int bench_init(void)
905 {
906 #if CONFIG_LINUX_PERF
907 int ret = bench_init_linux();
908 #elif CONFIG_MACOS_KPERF
909 int ret = bench_init_kperf();
910 #else
911 int ret = bench_init_ffmpeg();
912 #endif
913 if (ret < 0)
914 return ret;
915
916 state.nop_time = measure_nop_time();
917 fprintf(stderr, "nop: %d.%d\n", state.nop_time/10, state.nop_time%10);
918 return 0;
919 }
920
921 76 static void bench_uninit(void)
922 {
923 #if CONFIG_LINUX_PERF
924 close(state.sysfd);
925 #endif
926 76 }
927
928 static int usage(const char *path)
929 {
930 fprintf(stderr,
931 "Usage: %s [options...] [seed]\n"
932 " --test=<pattern> Run specific test.\n"
933 " --bench Run benchmark.\n"
934 " --csv, --tsv Output results in rows of comma or tab separated values.\n"
935 " --runs=<ptwo> Manual number of benchmark iterations to run 2**<ptwo>.\n"
936 " --verbose Increase verbosity.\n",
937 path);
938 return 1;
939 }
940
941 76 int main(int argc, char *argv[])
942 {
943 76 unsigned int seed = av_get_random_seed();
944 76 int i, ret = 0;
945 76 char arch_info_buf[50] = "";
946
947 #ifdef _WIN32
948 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
949 AddVectoredExceptionHandler(0, signal_handler);
950 #endif
951 #elif !defined(_WASI_EMULATED_SIGNAL)
952 76 sigaction(SIGBUS, &signal_handler_act, NULL);
953 76 sigaction(SIGFPE, &signal_handler_act, NULL);
954 76 sigaction(SIGILL, &signal_handler_act, NULL);
955 76 sigaction(SIGSEGV, &signal_handler_act, NULL);
956 #endif
957 #if HAVE_PRCTL && defined(PR_SET_UNALIGN)
958 76 prctl(PR_SET_UNALIGN, PR_UNALIGN_SIGBUS);
959 #endif
960 #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
961 if (have_vfp(av_get_cpu_flags()) || have_neon(av_get_cpu_flags()))
962 checkasm_checked_call = checkasm_checked_call_vfp;
963 #endif
964
965
2/4
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 76 times.
76 if (!tests[0].func || !cpus[0].flag) {
966 fprintf(stderr, "checkasm: no tests to perform\n");
967 return 0;
968 }
969
970
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 76 times.
152 for (i = 1; i < argc; i++) {
971 76 const char *arg = argv[i];
972 unsigned long l;
973 char *end;
974
975
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if (!strncmp(arg, "--bench", 7)) {
976 if (bench_init() < 0)
977 return 1;
978 if (arg[7] == '=') {
979 state.bench_pattern = arg + 8;
980 state.bench_pattern_len = strlen(state.bench_pattern);
981 } else
982 state.bench_pattern = "*";
983
1/2
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
76 } else if (!strncmp(arg, "--test=", 7)) {
984 76 state.test_pattern = arg + 7;
985 } else if (!strcmp(arg, "--csv")) {
986 state.csv = 1; state.tsv = 0;
987 } else if (!strcmp(arg, "--tsv")) {
988 state.csv = 0; state.tsv = 1;
989 } else if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
990 state.verbose = 1;
991 } else if (!strncmp(arg, "--runs=", 7)) {
992 l = strtoul(arg + 7, &end, 10);
993 if (*end == '\0') {
994 if (l > 30) {
995 fprintf(stderr, "checkasm: error: runs exponent must be within the range 0 <= 30\n");
996 usage(argv[0]);
997 }
998 bench_runs = 1U << l;
999 } else {
1000 return usage(argv[0]);
1001 }
1002 } else if ((l = strtoul(arg, &end, 10)) <= UINT_MAX &&
1003 *end == '\0') {
1004 seed = l;
1005 } else {
1006 return usage(argv[0]);
1007 }
1008 }
1009
1010 #if ARCH_AARCH64 && HAVE_SVE
1011 if (have_sve(av_get_cpu_flags()))
1012 snprintf(arch_info_buf, sizeof(arch_info_buf),
1013 "SVE %d bits, ", 8 * ff_aarch64_sve_length());
1014 #elif ARCH_RISCV && HAVE_RVV
1015 if (av_get_cpu_flags() & AV_CPU_FLAG_RVV_I32)
1016 snprintf(arch_info_buf, sizeof (arch_info_buf),
1017 "%zu-bit vectors, ", 8 * ff_get_rv_vlenb());
1018 #endif
1019 76 fprintf(stderr, "checkasm: %susing random seed %u\n", arch_info_buf, seed);
1020 76 av_lfg_init(&checkasm_lfg, seed);
1021
1022
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if (state.bench_pattern)
1023 fprintf(stderr, "checkasm: bench runs %" PRIu64 " (1 << %i)\n", bench_runs, av_log2(bench_runs));
1024
1025 76 check_cpu_flag(NULL, 0);
1026
2/2
✓ Branch 0 taken 1368 times.
✓ Branch 1 taken 76 times.
1444 for (i = 0; cpus[i].flag; i++)
1027 1368 check_cpu_flag(cpus[i].name, cpus[i].flag);
1028
1029
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if (state.num_failed) {
1030 fprintf(stderr, "checkasm: %d of %d tests have failed\n", state.num_failed, state.num_checked);
1031 ret = 1;
1032 } else {
1033 76 fprintf(stderr, "checkasm: all %d tests passed\n", state.num_checked);
1034
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if (state.bench_pattern) {
1035 print_benchs(state.funcs);
1036 }
1037 }
1038
1039 76 destroy_func_tree(state.funcs);
1040 76 bench_uninit();
1041 76 return ret;
1042 }
1043
1044 /* Decide whether or not the specified function needs to be tested and
1045 * allocate/initialize data structures if needed. Returns a pointer to a
1046 * reference function if the function should be tested, otherwise NULL */
1047 168259 void *checkasm_check_func(void *func, const char *name, ...)
1048 {
1049 char name_buf[256];
1050 168259 void *ref = func;
1051 CheckasmFuncVersion *v;
1052 int name_length;
1053 va_list arg;
1054
1055 168259 va_start(arg, name);
1056 168259 name_length = vsnprintf(name_buf, sizeof(name_buf), name, arg);
1057 168259 va_end(arg);
1058
1059
4/6
✓ Branch 0 taken 168051 times.
✓ Branch 1 taken 208 times.
✓ Branch 2 taken 168051 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 168051 times.
168259 if (!func || name_length <= 0 || name_length >= sizeof(name_buf))
1060 208 return NULL;
1061
1062 168051 state.current_func = get_func(&state.funcs, name_buf);
1063 168051 state.funcs->color = 1;
1064 168051 v = &state.current_func->versions;
1065
1066
2/2
✓ Branch 0 taken 155601 times.
✓ Branch 1 taken 12450 times.
168051 if (v->func) {
1067 CheckasmFuncVersion *prev;
1068 do {
1069 /* Only test functions that haven't already been tested */
1070
2/2
✓ Branch 0 taken 140849 times.
✓ Branch 1 taken 66516 times.
207365 if (v->func == func)
1071 140849 return NULL;
1072
1073
1/2
✓ Branch 0 taken 66516 times.
✗ Branch 1 not taken.
66516 if (v->ok)
1074 66516 ref = v->func;
1075
1076 66516 prev = v;
1077
2/2
✓ Branch 0 taken 51764 times.
✓ Branch 1 taken 14752 times.
66516 } while ((v = v->next));
1078
1079 14752 v = prev->next = checkasm_malloc(sizeof(CheckasmFuncVersion));
1080 }
1081
1082 27202 v->func = func;
1083 27202 v->ok = 1;
1084 27202 v->cpu = state.cpu_flag;
1085 27202 state.current_func_ver = v;
1086
1087
2/2
✓ Branch 0 taken 14752 times.
✓ Branch 1 taken 12450 times.
27202 if (state.cpu_flag)
1088 14752 state.num_checked++;
1089
1090 27202 return ref;
1091 }
1092
1093 /* Decide whether or not the current function needs to be benchmarked */
1094 40289 int checkasm_bench_func(void)
1095 {
1096
2/6
✓ Branch 0 taken 40289 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 40289 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
40289 return !state.num_failed && state.bench_pattern &&
1097 !wildstrcmp(state.current_func->name, state.bench_pattern);
1098 }
1099
1100 /* Indicate that the current test has failed, return whether verbose printing
1101 * is requested. */
1102 int checkasm_fail_func(const char *msg, ...)
1103 {
1104 if (state.current_func_ver && state.current_func_ver->cpu &&
1105 state.current_func_ver->ok)
1106 {
1107 va_list arg;
1108
1109 print_cpu_name();
1110 fprintf(stderr, " %s_%s (", state.current_func->name, cpu_suffix(state.current_func_ver->cpu));
1111 va_start(arg, msg);
1112 vfprintf(stderr, msg, arg);
1113 va_end(arg);
1114 fprintf(stderr, ")\n");
1115
1116 state.current_func_ver->ok = 0;
1117 state.num_failed++;
1118 }
1119 return state.verbose;
1120 }
1121
1122 317096 void checkasm_set_signal_handler_state(int enabled) {
1123 317096 state.catch_signals = enabled;
1124 317096 }
1125
1126 168259 int checkasm_handle_signal(int s) {
1127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 168259 times.
168259 if (s) {
1128 #ifdef __GLIBC__
1129 checkasm_fail_func("fatal signal %d: %s", s, strsignal(s));
1130 #else
1131 checkasm_fail_func(s == SIGFPE ? "fatal arithmetic error" :
1132 s == SIGILL ? "illegal instruction" :
1133 s == SIGBUS ? "bus error" :
1134 "segmentation fault");
1135 #endif
1136 }
1137 168259 return s;
1138 }
1139
1140 /* Get the benchmark context of the current function */
1141 CheckasmPerf *checkasm_get_perf_context(void)
1142 {
1143 CheckasmPerf *perf = &state.current_func_ver->perf;
1144 memset(perf, 0, sizeof(*perf));
1145 perf->sysfd = state.sysfd;
1146 return perf;
1147 }
1148
1149 /* Print the outcome of all tests performed since the last time this function was called */
1150 3073 void checkasm_report(const char *name, ...)
1151 {
1152 static int prev_checked, prev_failed, max_length;
1153
1154
2/2
✓ Branch 0 taken 432 times.
✓ Branch 1 taken 2641 times.
3073 if (state.num_checked > prev_checked) {
1155 432 int pad_length = max_length + 4;
1156 va_list arg;
1157
1158 432 print_cpu_name();
1159 432 pad_length -= fprintf(stderr, " - %s.", state.current_test_name);
1160 432 va_start(arg, name);
1161 432 pad_length -= vfprintf(stderr, name, arg);
1162 432 va_end(arg);
1163 432 fprintf(stderr, "%*c", FFMAX(pad_length, 0) + 2, '[');
1164
1165
1/2
✓ Branch 0 taken 432 times.
✗ Branch 1 not taken.
432 if (state.num_failed == prev_failed)
1166 432 color_printf(COLOR_GREEN, "OK");
1167 else
1168 color_printf(COLOR_RED, "FAILED");
1169 432 fprintf(stderr, "]\n");
1170
1171 432 prev_checked = state.num_checked;
1172 432 prev_failed = state.num_failed;
1173
2/2
✓ Branch 0 taken 241 times.
✓ Branch 1 taken 2400 times.
2641 } else if (!state.cpu_flag) {
1174 /* Calculate the amount of padding required to make the output vertically aligned */
1175 241 int length = strlen(state.current_test_name);
1176 va_list arg;
1177
1178 241 va_start(arg, name);
1179 241 length += vsnprintf(NULL, 0, name, arg);
1180 241 va_end(arg);
1181
1182
2/2
✓ Branch 0 taken 135 times.
✓ Branch 1 taken 106 times.
241 if (length > max_length)
1183 135 max_length = length;
1184 }
1185 3073 }
1186
1187 static int check_err(const char *file, int line,
1188 const char *name, int w, int h,
1189 int *err)
1190 {
1191 if (*err)
1192 return 0;
1193 if (!checkasm_fail_func("%s:%d", file, line))
1194 return 1;
1195 *err = 1;
1196 fprintf(stderr, "%s (%dx%d):\n", name, w, h);
1197 return 0;
1198 }
1199
1200 #define DEF_CHECKASM_CHECK_BODY(compare, type, fmt) \
1201 do { \
1202 int64_t aligned_w = (w - 1LL + align_w) & ~(align_w - 1); \
1203 int64_t aligned_h = (h - 1LL + align_h) & ~(align_h - 1); \
1204 int err = 0; \
1205 int y = 0; \
1206 av_assert0(aligned_w == (int32_t)aligned_w);\
1207 av_assert0(aligned_h == (int32_t)aligned_h);\
1208 stride1 /= sizeof(*buf1); \
1209 stride2 /= sizeof(*buf2); \
1210 for (y = 0; y < h; y++) \
1211 if (!compare(&buf1[y*stride1], &buf2[y*stride2], w)) \
1212 break; \
1213 if (y != h) { \
1214 if (check_err(file, line, name, w, h, &err)) \
1215 return 1; \
1216 for (y = 0; y < h; y++) { \
1217 for (int x = 0; x < w; x++) \
1218 fprintf(stderr, " " fmt, buf1[x]); \
1219 fprintf(stderr, " "); \
1220 for (int x = 0; x < w; x++) \
1221 fprintf(stderr, " " fmt, buf2[x]); \
1222 fprintf(stderr, " "); \
1223 for (int x = 0; x < w; x++) \
1224 fprintf(stderr, "%c", buf1[x] != buf2[x] ? 'x' : '.'); \
1225 buf1 += stride1; \
1226 buf2 += stride2; \
1227 fprintf(stderr, "\n"); \
1228 } \
1229 buf1 -= h*stride1; \
1230 buf2 -= h*stride2; \
1231 } \
1232 for (y = -padding; y < 0; y++) \
1233 if (!compare(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
1234 w + 2*padding)) { \
1235 if (check_err(file, line, name, w, h, &err)) \
1236 return 1; \
1237 fprintf(stderr, " overwrite above\n"); \
1238 break; \
1239 } \
1240 for (y = aligned_h; y < aligned_h + padding; y++) \
1241 if (!compare(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
1242 w + 2*padding)) { \
1243 if (check_err(file, line, name, w, h, &err)) \
1244 return 1; \
1245 fprintf(stderr, " overwrite below\n"); \
1246 break; \
1247 } \
1248 for (y = 0; y < h; y++) \
1249 if (!compare(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
1250 padding)) { \
1251 if (check_err(file, line, name, w, h, &err)) \
1252 return 1; \
1253 fprintf(stderr, " overwrite left\n"); \
1254 break; \
1255 } \
1256 for (y = 0; y < h; y++) \
1257 if (!compare(&buf1[y*stride1 + aligned_w], &buf2[y*stride2 + aligned_w], \
1258 padding)) { \
1259 if (check_err(file, line, name, w, h, &err)) \
1260 return 1; \
1261 fprintf(stderr, " overwrite right\n"); \
1262 break; \
1263 } \
1264 return err; \
1265 } while (0)
1266
1267 #define cmp_int(a, b, len) (!memcmp(a, b, (len) * sizeof(*(a))))
1268 #define DEF_CHECKASM_CHECK_FUNC(type, fmt) \
1269 int checkasm_check_##type(const char *file, int line, \
1270 const type *buf1, ptrdiff_t stride1, \
1271 const type *buf2, ptrdiff_t stride2, \
1272 int w, int h, const char *name, \
1273 int align_w, int align_h, \
1274 int padding) \
1275 { \
1276 DEF_CHECKASM_CHECK_BODY(cmp_int, type, fmt); \
1277 }
1278
1279
18/46
✗ Branch 0 not taken.
✓ Branch 1 taken 8131 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8131 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 232197 times.
✓ Branch 10 taken 232197 times.
✓ Branch 11 taken 8131 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 8131 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✓ Branch 34 taken 39296 times.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✓ Branch 39 taken 39296 times.
✓ Branch 40 taken 8131 times.
✗ Branch 41 not taken.
✓ Branch 42 taken 39296 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✓ Branch 47 taken 39296 times.
✓ Branch 48 taken 8131 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 232197 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 232197 times.
✓ Branch 56 taken 8131 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 232197 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 232197 times.
✓ Branch 64 taken 8131 times.
783314 DEF_CHECKASM_CHECK_FUNC(uint8_t, "%02x")
1280
18/46
✗ Branch 0 not taken.
✓ Branch 1 taken 19626 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19626 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 556470 times.
✓ Branch 10 taken 556470 times.
✓ Branch 11 taken 19626 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 19626 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✓ Branch 34 taken 115440 times.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✓ Branch 39 taken 115440 times.
✓ Branch 40 taken 19626 times.
✗ Branch 41 not taken.
✓ Branch 42 taken 115440 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✓ Branch 47 taken 115440 times.
✓ Branch 48 taken 19626 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 556470 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 556470 times.
✓ Branch 56 taken 19626 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 556470 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 556470 times.
✓ Branch 64 taken 19626 times.
1919916 DEF_CHECKASM_CHECK_FUNC(uint16_t, "%04x")
1281
14/46
✗ Branch 0 not taken.
✓ Branch 1 taken 658 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 658 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1316 times.
✓ Branch 10 taken 1316 times.
✓ Branch 11 taken 658 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 658 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 39 not taken.
✓ Branch 40 taken 658 times.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✓ Branch 48 taken 658 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 1316 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 1316 times.
✓ Branch 56 taken 658 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 1316 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 1316 times.
✓ Branch 64 taken 658 times.
4606 DEF_CHECKASM_CHECK_FUNC(uint32_t, "%08x")
1282
14/46
✗ Branch 0 not taken.
✓ Branch 1 taken 550 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 550 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 14114 times.
✓ Branch 10 taken 14114 times.
✓ Branch 11 taken 550 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 550 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 39 not taken.
✓ Branch 40 taken 550 times.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✓ Branch 48 taken 550 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 14114 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 14114 times.
✓ Branch 56 taken 550 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 14114 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 14114 times.
✓ Branch 64 taken 550 times.
42892 DEF_CHECKASM_CHECK_FUNC(int16_t, "%6d")
1283
14/46
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 64 times.
✓ Branch 10 taken 64 times.
✓ Branch 11 taken 4 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 4 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 39 not taken.
✓ Branch 40 taken 4 times.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✓ Branch 48 taken 4 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 64 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 64 times.
✓ Branch 56 taken 4 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 64 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 64 times.
✓ Branch 64 taken 4 times.
196 DEF_CHECKASM_CHECK_FUNC(int32_t, "%9d")
1284
1285 826 int checkasm_check_float_ulp(const char *file, int line,
1286 const float *buf1, ptrdiff_t stride1,
1287 const float *buf2, ptrdiff_t stride2,
1288 int w, int h, const char *name,
1289 unsigned max_ulp, int align_w, int align_h,
1290 int padding)
1291 {
1292 #define cmp_float(a, b, len) float_near_ulp_array(a, b, max_ulp, len)
1293
14/46
✗ Branch 0 not taken.
✓ Branch 1 taken 826 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 826 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1652 times.
✓ Branch 11 taken 1652 times.
✓ Branch 12 taken 826 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 826 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✓ Branch 42 taken 826 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 50 not taken.
✓ Branch 51 taken 826 times.
✗ Branch 53 not taken.
✓ Branch 54 taken 1652 times.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✓ Branch 59 taken 1652 times.
✓ Branch 60 taken 826 times.
✗ Branch 62 not taken.
✓ Branch 63 taken 1652 times.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✓ Branch 68 taken 1652 times.
✓ Branch 69 taken 826 times.
5782 DEF_CHECKASM_CHECK_BODY(cmp_float, float, "%g");
1294 #undef cmp_float
1295 }
1296