FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/tests/checkasm/checkasm.c
Date: 2025-08-19 23:55:23
Exec Total Coverage
Lines: 201 355 56.6%
Functions: 29 43 67.4%
Branches: 177 456 38.8%

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 #endif
311 #if CONFIG_AVUTIL
312 { "aes", checkasm_check_aes },
313 { "fixed_dsp", checkasm_check_fixed_dsp },
314 { "float_dsp", checkasm_check_float_dsp },
315 { "lls", checkasm_check_lls },
316 { "av_tx", checkasm_check_av_tx },
317 #endif
318 { NULL }
319 };
320
321 /* List of cpu flags to check */
322 static const struct {
323 const char *name;
324 const char *suffix;
325 int flag;
326 } cpus[] = {
327 #if ARCH_AARCH64
328 { "ARMV8", "armv8", AV_CPU_FLAG_ARMV8 },
329 { "NEON", "neon", AV_CPU_FLAG_NEON },
330 { "DOTPROD", "dotprod", AV_CPU_FLAG_DOTPROD },
331 { "I8MM", "i8mm", AV_CPU_FLAG_I8MM },
332 { "SVE", "sve", AV_CPU_FLAG_SVE },
333 { "SVE2", "sve2", AV_CPU_FLAG_SVE2 },
334 #elif ARCH_ARM
335 { "ARMV5TE", "armv5te", AV_CPU_FLAG_ARMV5TE },
336 { "ARMV6", "armv6", AV_CPU_FLAG_ARMV6 },
337 { "ARMV6T2", "armv6t2", AV_CPU_FLAG_ARMV6T2 },
338 { "VFP", "vfp", AV_CPU_FLAG_VFP },
339 { "VFP_VM", "vfp_vm", AV_CPU_FLAG_VFP_VM },
340 { "VFPV3", "vfp3", AV_CPU_FLAG_VFPV3 },
341 { "NEON", "neon", AV_CPU_FLAG_NEON },
342 #elif ARCH_PPC
343 { "ALTIVEC", "altivec", AV_CPU_FLAG_ALTIVEC },
344 { "VSX", "vsx", AV_CPU_FLAG_VSX },
345 { "POWER8", "power8", AV_CPU_FLAG_POWER8 },
346 #elif ARCH_RISCV
347 { "RVI", "rvi", AV_CPU_FLAG_RVI },
348 { "misaligned", "misaligned", AV_CPU_FLAG_RV_MISALIGNED },
349 { "RV_zbb", "rvb_b", AV_CPU_FLAG_RVB_BASIC },
350 { "RVB", "rvb", AV_CPU_FLAG_RVB },
351 { "RV_zve32x","rvv_i32", AV_CPU_FLAG_RVV_I32 },
352 { "RV_zve32f","rvv_f32", AV_CPU_FLAG_RVV_F32 },
353 { "RV_zve64x","rvv_i64", AV_CPU_FLAG_RVV_I64 },
354 { "RV_zve64d","rvv_f64", AV_CPU_FLAG_RVV_F64 },
355 { "RV_zvbb", "rv_zvbb", AV_CPU_FLAG_RV_ZVBB },
356 #elif ARCH_MIPS
357 { "MMI", "mmi", AV_CPU_FLAG_MMI },
358 { "MSA", "msa", AV_CPU_FLAG_MSA },
359 #elif ARCH_X86
360 { "MMX", "mmx", AV_CPU_FLAG_MMX|AV_CPU_FLAG_CMOV },
361 { "MMXEXT", "mmxext", AV_CPU_FLAG_MMXEXT },
362 { "3DNOW", "3dnow", AV_CPU_FLAG_3DNOW },
363 { "3DNOWEXT", "3dnowext", AV_CPU_FLAG_3DNOWEXT },
364 { "SSE", "sse", AV_CPU_FLAG_SSE },
365 { "SSE2", "sse2", AV_CPU_FLAG_SSE2|AV_CPU_FLAG_SSE2SLOW },
366 { "SSE3", "sse3", AV_CPU_FLAG_SSE3|AV_CPU_FLAG_SSE3SLOW },
367 { "SSSE3", "ssse3", AV_CPU_FLAG_SSSE3|AV_CPU_FLAG_ATOM },
368 { "SSE4.1", "sse4", AV_CPU_FLAG_SSE4 },
369 { "SSE4.2", "sse42", AV_CPU_FLAG_SSE42 },
370 { "AES-NI", "aesni", AV_CPU_FLAG_AESNI },
371 { "AVX", "avx", AV_CPU_FLAG_AVX },
372 { "XOP", "xop", AV_CPU_FLAG_XOP },
373 { "FMA3", "fma3", AV_CPU_FLAG_FMA3 },
374 { "FMA4", "fma4", AV_CPU_FLAG_FMA4 },
375 { "AVX2", "avx2", AV_CPU_FLAG_AVX2 },
376 { "AVX-512", "avx512", AV_CPU_FLAG_AVX512 },
377 { "AVX-512ICL", "avx512icl", AV_CPU_FLAG_AVX512ICL },
378 #elif ARCH_LOONGARCH
379 { "LSX", "lsx", AV_CPU_FLAG_LSX },
380 { "LASX", "lasx", AV_CPU_FLAG_LASX },
381 #elif ARCH_WASM
382 { "SIMD128", "simd128", AV_CPU_FLAG_SIMD128 },
383 #endif
384 { NULL }
385 };
386
387 typedef struct CheckasmFuncVersion {
388 struct CheckasmFuncVersion *next;
389 void *func;
390 int ok;
391 int cpu;
392 CheckasmPerf perf;
393 } CheckasmFuncVersion;
394
395 /* Binary search tree node */
396 typedef struct CheckasmFunc {
397 struct CheckasmFunc *child[2];
398 CheckasmFuncVersion versions;
399 uint8_t color; /* 0 = red, 1 = black */
400 char name[1];
401 } CheckasmFunc;
402
403 /* Internal state */
404 static struct {
405 CheckasmFunc *funcs;
406 CheckasmFunc *current_func;
407 CheckasmFuncVersion *current_func_ver;
408 const char *current_test_name;
409 const char *bench_pattern;
410 int bench_pattern_len;
411 int num_checked;
412 int num_failed;
413
414 /* perf */
415 int nop_time;
416 int sysfd;
417
418 int cpu_flag;
419 const char *cpu_flag_name;
420 const char *test_pattern;
421 int verbose;
422 int csv;
423 int tsv;
424 volatile sig_atomic_t catch_signals;
425 } state;
426
427 /* PRNG state */
428 AVLFG checkasm_lfg;
429
430 /* float compare support code */
431 9286280 static int is_negative(union av_intfloat32 u)
432 {
433 9286280 return u.i >> 31;
434 }
435
436 4643140 int float_near_ulp(float a, float b, unsigned max_ulp)
437 {
438 union av_intfloat32 x, y;
439
440 4643140 x.f = a;
441 4643140 y.f = b;
442
443
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4643140 times.
4643140 if (is_negative(x) != is_negative(y)) {
444 // handle -0.0 == +0.0
445 return a == b;
446 }
447
448
1/2
✓ Branch 0 taken 4643140 times.
✗ Branch 1 not taken.
4643140 if (llabs((int64_t)x.i - y.i) <= max_ulp)
449 4643140 return 1;
450
451 return 0;
452 }
453
454 5211 int float_near_ulp_array(const float *a, const float *b, unsigned max_ulp,
455 unsigned len)
456 {
457 unsigned i;
458
459
2/2
✓ Branch 0 taken 4638020 times.
✓ Branch 1 taken 5211 times.
4643231 for (i = 0; i < len; i++) {
460
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4638020 times.
4638020 if (!float_near_ulp(a[i], b[i], max_ulp))
461 return 0;
462 }
463 5211 return 1;
464 }
465
466 882133 int float_near_abs_eps(float a, float b, float eps)
467 {
468 882133 float abs_diff = fabsf(a - b);
469
1/2
✓ Branch 0 taken 882133 times.
✗ Branch 1 not taken.
882133 if (abs_diff < eps)
470 882133 return 1;
471
472 fprintf(stderr, "test failed comparing %g with %g (abs diff=%g with EPS=%g)\n", a, b, abs_diff, eps);
473
474 return 0;
475 }
476
477 189 int float_near_abs_eps_array(const float *a, const float *b, float eps,
478 unsigned len)
479 {
480 unsigned i;
481
482
2/2
✓ Branch 0 taken 872138 times.
✓ Branch 1 taken 189 times.
872327 for (i = 0; i < len; i++) {
483
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 872138 times.
872138 if (!float_near_abs_eps(a[i], b[i], eps))
484 return 0;
485 }
486 189 return 1;
487 }
488
489 5120 int float_near_abs_eps_ulp(float a, float b, float eps, unsigned max_ulp)
490 {
491
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 5120 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
5120 return float_near_ulp(a, b, max_ulp) || float_near_abs_eps(a, b, eps);
492 }
493
494 int float_near_abs_eps_array_ulp(const float *a, const float *b, float eps,
495 unsigned max_ulp, unsigned len)
496 {
497 unsigned i;
498
499 for (i = 0; i < len; i++) {
500 if (!float_near_abs_eps_ulp(a[i], b[i], eps, max_ulp))
501 return 0;
502 }
503 return 1;
504 }
505
506 65167 int double_near_abs_eps(double a, double b, double eps)
507 {
508 65167 double abs_diff = fabs(a - b);
509
510 65167 return abs_diff < eps;
511 }
512
513 11 int double_near_abs_eps_array(const double *a, const double *b, double eps,
514 unsigned len)
515 {
516 unsigned i;
517
518
2/2
✓ Branch 0 taken 41068 times.
✓ Branch 1 taken 11 times.
41079 for (i = 0; i < len; i++) {
519
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 41068 times.
41068 if (!double_near_abs_eps(a[i], b[i], eps))
520 return 0;
521 }
522 11 return 1;
523 }
524
525 /* Print colored text to stderr if the terminal supports it */
526 617 static void color_printf(int color, const char *fmt, ...)
527 {
528 static int use_color = -1;
529 va_list arg;
530
531 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
532 static HANDLE con;
533 static WORD org_attributes;
534
535 if (use_color < 0) {
536 CONSOLE_SCREEN_BUFFER_INFO con_info;
537 con = GetStdHandle(STD_ERROR_HANDLE);
538 if (con && con != INVALID_HANDLE_VALUE && GetConsoleScreenBufferInfo(con, &con_info)) {
539 org_attributes = con_info.wAttributes;
540 use_color = 1;
541 } else
542 use_color = 0;
543 }
544 if (use_color)
545 SetConsoleTextAttribute(con, (org_attributes & 0xfff0) | (color & 0x0f));
546 #else
547
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 545 times.
617 if (use_color < 0) {
548 72 const char *term = getenv("TERM");
549
3/6
✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 72 times.
72 use_color = term && strcmp(term, "dumb") && isatty(2);
550 }
551
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 617 times.
617 if (use_color)
552 fprintf(stderr, "\x1b[%d;3%dm", (color & 0x08) >> 3, color & 0x07);
553 #endif
554
555 617 va_start(arg, fmt);
556 617 vfprintf(stderr, fmt, arg);
557 617 va_end(arg);
558
559
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 617 times.
617 if (use_color) {
560 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
561 SetConsoleTextAttribute(con, org_attributes);
562 #else
563 fprintf(stderr, "\x1b[0m");
564 #endif
565 }
566 617 }
567
568 /* Deallocate a tree */
569 23987 static void destroy_func_tree(CheckasmFunc *f)
570 {
571
2/2
✓ Branch 0 taken 11956 times.
✓ Branch 1 taken 12031 times.
23987 if (f) {
572 11956 CheckasmFuncVersion *v = f->versions.next;
573
2/2
✓ Branch 0 taken 14437 times.
✓ Branch 1 taken 11956 times.
26393 while (v) {
574 14437 CheckasmFuncVersion *next = v->next;
575 14437 free(v);
576 14437 v = next;
577 }
578
579 11956 destroy_func_tree(f->child[0]);
580 11956 destroy_func_tree(f->child[1]);
581 11956 free(f);
582 }
583 23987 }
584
585 /* Allocate a zero-initialized block, clean up and exit on failure */
586 26393 static void *checkasm_malloc(size_t size)
587 {
588 26393 void *ptr = calloc(1, size);
589
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26393 times.
26393 if (!ptr) {
590 fprintf(stderr, "checkasm: malloc failed\n");
591 destroy_func_tree(state.funcs);
592 exit(1);
593 }
594 26393 return ptr;
595 }
596
597 /* Get the suffix of the specified cpu flag */
598 static const char *cpu_suffix(int cpu)
599 {
600 int i = FF_ARRAY_ELEMS(cpus);
601
602 while (--i >= 0)
603 if (cpu & cpus[i].flag)
604 return cpus[i].suffix;
605
606 return "c";
607 }
608
609 static int cmp_nop(const void *a, const void *b)
610 {
611 return *(const uint16_t*)a - *(const uint16_t*)b;
612 }
613
614 /* Measure the overhead of the timing code (in decicycles) */
615 static int measure_nop_time(void)
616 {
617 uint16_t nops[10000];
618 int i, nop_sum = 0;
619 av_unused const int sysfd = state.sysfd;
620
621 uint64_t t = 0;
622 for (i = 0; i < 10000; i++) {
623 PERF_START(t);
624 PERF_STOP(t);
625 nops[i] = t;
626 }
627
628 qsort(nops, 10000, sizeof(uint16_t), cmp_nop);
629 for (i = 2500; i < 7500; i++)
630 nop_sum += nops[i];
631
632 return nop_sum / 500;
633 }
634
635 static inline double avg_cycles_per_call(const CheckasmPerf *const p)
636 {
637 if (p->iterations) {
638 const double cycles = (double)(10 * p->cycles) / p->iterations - state.nop_time;
639 if (cycles > 0.0)
640 return cycles / 4.0; /* 4 calls per iteration */
641 }
642 return 0.0;
643 }
644
645 /* Print benchmark results */
646 static void print_benchs(CheckasmFunc *f)
647 {
648 if (f) {
649 CheckasmFuncVersion *v = &f->versions;
650 const CheckasmPerf *p = &v->perf;
651 const double baseline = avg_cycles_per_call(p);
652 double decicycles;
653
654 print_benchs(f->child[0]);
655
656 do {
657 if (p->iterations) {
658 p = &v->perf;
659 decicycles = avg_cycles_per_call(p);
660 if (state.csv || state.tsv) {
661 const char sep = state.csv ? ',' : '\t';
662 printf("%s%c%s%c%.1f\n", f->name, sep,
663 cpu_suffix(v->cpu), sep,
664 decicycles / 10.0);
665 } else {
666 const int pad_length = 10 + 50 -
667 printf("%s_%s:", f->name, cpu_suffix(v->cpu));
668 const double ratio = decicycles ?
669 baseline / decicycles : 0.0;
670 printf("%*.1f (%5.2fx)\n", FFMAX(pad_length, 0),
671 decicycles / 10.0, ratio);
672 }
673 }
674 } while ((v = v->next));
675
676 print_benchs(f->child[1]);
677 }
678 }
679
680 /* ASCIIbetical sort except preserving natural order for numbers */
681 1545978 static int cmp_func_names(const char *a, const char *b)
682 {
683 1545978 const char *start = a;
684 int ascii_diff, digit_diff;
685
686
4/4
✓ Branch 0 taken 26149451 times.
✓ Branch 1 taken 1396305 times.
✓ Branch 2 taken 25999778 times.
✓ Branch 3 taken 149673 times.
27545756 for (; !(ascii_diff = *(const unsigned char*)a - *(const unsigned char*)b) && *a; a++, b++);
687
4/4
✓ Branch 0 taken 1597282 times.
✓ Branch 1 taken 1289460 times.
✓ Branch 2 taken 1340764 times.
✓ Branch 3 taken 256518 times.
2886742 for (; av_isdigit(*a) && av_isdigit(*b); a++, b++);
688
689
6/6
✓ Branch 0 taken 1488277 times.
✓ Branch 1 taken 57701 times.
✓ Branch 2 taken 1080911 times.
✓ Branch 3 taken 407366 times.
✓ Branch 4 taken 389140 times.
✓ Branch 5 taken 691771 times.
1545978 if (a > start && av_isdigit(a[-1]) && (digit_diff = av_isdigit(*a) - av_isdigit(*b)))
690 389140 return digit_diff;
691
692 1156838 return ascii_diff;
693 }
694
695 /* Perform a tree rotation in the specified direction and return the new root */
696 11194 static CheckasmFunc *rotate_tree(CheckasmFunc *f, int dir)
697 {
698 11194 CheckasmFunc *r = f->child[dir^1];
699 11194 f->child[dir^1] = r->child[dir];
700 11194 r->child[dir] = f;
701 11194 r->color = f->color;
702 11194 f->color = 0;
703 11194 return r;
704 }
705
706 #define is_red(f) ((f) && !(f)->color)
707
708 /* Balance a left-leaning red-black tree at the specified node */
709 111200 static void balance_tree(CheckasmFunc **root)
710 {
711 111200 CheckasmFunc *f = *root;
712
713
8/8
✓ Branch 0 taken 105662 times.
✓ Branch 1 taken 5538 times.
✓ Branch 2 taken 44072 times.
✓ Branch 3 taken 61590 times.
✓ Branch 4 taken 39881 times.
✓ Branch 5 taken 4191 times.
✓ Branch 6 taken 8511 times.
✓ Branch 7 taken 31370 times.
111200 if (is_red(f->child[0]) && is_red(f->child[1])) {
714 8511 f->color ^= 1;
715 8511 f->child[0]->color = f->child[1]->color = 1;
716 }
717
718
7/8
✓ Branch 0 taken 105662 times.
✓ Branch 1 taken 5538 times.
✓ Branch 2 taken 70101 times.
✓ Branch 3 taken 35561 times.
✓ Branch 4 taken 75639 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8324 times.
✓ Branch 7 taken 67315 times.
111200 if (!is_red(f->child[0]) && is_red(f->child[1]))
719 8324 *root = rotate_tree(f, 0); /* Rotate left */
720
7/8
✓ Branch 0 taken 102876 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35561 times.
✓ Branch 3 taken 67315 times.
✓ Branch 4 taken 32763 times.
✓ Branch 5 taken 2798 times.
✓ Branch 6 taken 2870 times.
✓ Branch 7 taken 29893 times.
102876 else if (is_red(f->child[0]) && is_red(f->child[0]->child[0]))
721 2870 *root = rotate_tree(f, 1); /* Rotate right */
722 111200 }
723
724 /* Get a node with the specified name, creating it if it doesn't exist */
725 1557934 static CheckasmFunc *get_func(CheckasmFunc **root, const char *name)
726 {
727 1557934 CheckasmFunc *f = *root;
728
729
2/2
✓ Branch 0 taken 1545978 times.
✓ Branch 1 taken 11956 times.
1557934 if (f) {
730 /* Search the tree for a matching node */
731 1545978 int cmp = cmp_func_names(name, f->name);
732
2/2
✓ Branch 0 taken 1396305 times.
✓ Branch 1 taken 149673 times.
1545978 if (cmp) {
733 1396305 f = get_func(&f->child[cmp > 0], name);
734
735 /* Rebalance the tree on the way up if a new node was inserted */
736
2/2
✓ Branch 0 taken 111200 times.
✓ Branch 1 taken 1285105 times.
1396305 if (!f->versions.func)
737 111200 balance_tree(root);
738 }
739 } else {
740 /* Allocate and insert a new node into the tree */
741 11956 int name_length = strlen(name);
742 11956 f = *root = checkasm_malloc(sizeof(CheckasmFunc) + name_length);
743 11956 memcpy(f->name, name, name_length + 1);
744 }
745
746 1557934 return f;
747 }
748
749 checkasm_context checkasm_context_buf;
750
751 /* Crash handling: attempt to catch crashes and handle them
752 * gracefully instead of just aborting abruptly. */
753 #ifdef _WIN32
754 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
755 static LONG NTAPI signal_handler(EXCEPTION_POINTERS *e) {
756 int s;
757
758 if (!state.catch_signals)
759 return EXCEPTION_CONTINUE_SEARCH;
760
761 switch (e->ExceptionRecord->ExceptionCode) {
762 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
763 case EXCEPTION_INT_DIVIDE_BY_ZERO:
764 s = SIGFPE;
765 break;
766 case EXCEPTION_ILLEGAL_INSTRUCTION:
767 case EXCEPTION_PRIV_INSTRUCTION:
768 s = SIGILL;
769 break;
770 case EXCEPTION_ACCESS_VIOLATION:
771 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
772 case EXCEPTION_DATATYPE_MISALIGNMENT:
773 case EXCEPTION_STACK_OVERFLOW:
774 s = SIGSEGV;
775 break;
776 case EXCEPTION_IN_PAGE_ERROR:
777 s = SIGBUS;
778 break;
779 default:
780 return EXCEPTION_CONTINUE_SEARCH;
781 }
782 state.catch_signals = 0;
783 checkasm_load_context(s);
784 return EXCEPTION_CONTINUE_EXECUTION; /* never reached, but shuts up gcc */
785 }
786 #endif
787 #elif !defined(_WASI_EMULATED_SIGNAL)
788 static void signal_handler(int s);
789
790 static const struct sigaction signal_handler_act = {
791 .sa_handler = signal_handler,
792 .sa_flags = SA_RESETHAND,
793 };
794
795 static void signal_handler(int s) {
796 if (state.catch_signals) {
797 state.catch_signals = 0;
798 sigaction(s, &signal_handler_act, NULL);
799 checkasm_load_context(s);
800 }
801 }
802 #endif
803
804 /* Compares a string with a wildcard pattern. */
805 74100 static int wildstrcmp(const char *str, const char *pattern)
806 {
807 74100 const char *wild = strchr(pattern, '*');
808
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74100 times.
74100 if (wild) {
809 const size_t len = wild - pattern;
810 if (strncmp(str, pattern, len)) return 1;
811 while (*++wild == '*');
812 if (!*wild) return 0;
813 str += len;
814 while (*str && wildstrcmp(str, wild)) str++;
815 return !*str;
816 }
817 74100 return strcmp(str, pattern);
818 }
819
820 /* Perform tests and benchmarks for the specified cpu flag if supported by the host */
821 1425 static void check_cpu_flag(const char *name, int flag)
822 {
823 1425 int old_cpu_flag = state.cpu_flag;
824
825 1425 flag |= old_cpu_flag;
826 1425 av_force_cpu_flags(-1);
827 1425 state.cpu_flag = flag & av_get_cpu_flags();
828 1425 av_force_cpu_flags(state.cpu_flag);
829
830
4/4
✓ Branch 0 taken 1350 times.
✓ Branch 1 taken 75 times.
✓ Branch 2 taken 900 times.
✓ Branch 3 taken 450 times.
1425 if (!flag || state.cpu_flag != old_cpu_flag) {
831 int i;
832
833 975 state.cpu_flag_name = name;
834
2/2
✓ Branch 0 taken 74100 times.
✓ Branch 1 taken 975 times.
75075 for (i = 0; tests[i].func; i++) {
835
3/4
✓ Branch 0 taken 74100 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 73125 times.
✓ Branch 4 taken 975 times.
74100 if (state.test_pattern && wildstrcmp(tests[i].name, state.test_pattern))
836 73125 continue;
837 975 state.current_test_name = tests[i].name;
838 975 tests[i].func();
839 }
840 }
841 1425 }
842
843 /* Print the name of the current CPU flag, but only do it once */
844 415 static void print_cpu_name(void)
845 {
846
2/2
✓ Branch 0 taken 202 times.
✓ Branch 1 taken 213 times.
415 if (state.cpu_flag_name) {
847 202 color_printf(COLOR_YELLOW, "%s:\n", state.cpu_flag_name);
848 202 state.cpu_flag_name = NULL;
849 }
850 415 }
851
852 #if CONFIG_LINUX_PERF
853 static int bench_init_linux(void)
854 {
855 struct perf_event_attr attr = {
856 .type = PERF_TYPE_HARDWARE,
857 .size = sizeof(struct perf_event_attr),
858 .config = PERF_COUNT_HW_CPU_CYCLES,
859 .disabled = 1, // start counting only on demand
860 .exclude_kernel = 1,
861 .exclude_hv = 1,
862 #if !ARCH_X86
863 .exclude_guest = 1,
864 #endif
865 };
866
867 fprintf(stderr, "benchmarking with Linux Perf Monitoring API\n");
868
869 state.sysfd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
870 if (state.sysfd == -1) {
871 perror("perf_event_open");
872 return -1;
873 }
874 return 0;
875 }
876 #elif CONFIG_MACOS_KPERF
877 static int bench_init_kperf(void)
878 {
879 ff_kperf_init();
880 return 0;
881 }
882 #else
883 static int bench_init_ffmpeg(void)
884 {
885 #ifdef AV_READ_TIME
886 if (!checkasm_save_context()) {
887 checkasm_set_signal_handler_state(1);
888 AV_READ_TIME();
889 checkasm_set_signal_handler_state(0);
890 } else {
891 fprintf(stderr, "checkasm: unable to execute platform specific timer\n");
892 return -1;
893 }
894 fprintf(stderr, "benchmarking with native FFmpeg timers\n");
895 return 0;
896 #else
897 fprintf(stderr, "checkasm: --bench is not supported on your system\n");
898 return -1;
899 #endif
900 }
901 #endif
902
903 static int bench_init(void)
904 {
905 #if CONFIG_LINUX_PERF
906 int ret = bench_init_linux();
907 #elif CONFIG_MACOS_KPERF
908 int ret = bench_init_kperf();
909 #else
910 int ret = bench_init_ffmpeg();
911 #endif
912 if (ret < 0)
913 return ret;
914
915 state.nop_time = measure_nop_time();
916 fprintf(stderr, "nop: %d.%d\n", state.nop_time/10, state.nop_time%10);
917 return 0;
918 }
919
920 75 static void bench_uninit(void)
921 {
922 #if CONFIG_LINUX_PERF
923 close(state.sysfd);
924 #endif
925 75 }
926
927 static int usage(const char *path)
928 {
929 fprintf(stderr,
930 "Usage: %s [options...] [seed]\n"
931 " --test=<pattern> Run specific test.\n"
932 " --bench Run benchmark.\n"
933 " --csv, --tsv Output results in rows of comma or tab separated values.\n"
934 " --runs=<ptwo> Manual number of benchmark iterations to run 2**<ptwo>.\n"
935 " --verbose Increase verbosity.\n",
936 path);
937 return 1;
938 }
939
940 75 int main(int argc, char *argv[])
941 {
942 75 unsigned int seed = av_get_random_seed();
943 75 int i, ret = 0;
944 75 char arch_info_buf[50] = "";
945
946 #ifdef _WIN32
947 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
948 AddVectoredExceptionHandler(0, signal_handler);
949 #endif
950 #elif !defined(_WASI_EMULATED_SIGNAL)
951 75 sigaction(SIGBUS, &signal_handler_act, NULL);
952 75 sigaction(SIGFPE, &signal_handler_act, NULL);
953 75 sigaction(SIGILL, &signal_handler_act, NULL);
954 75 sigaction(SIGSEGV, &signal_handler_act, NULL);
955 #endif
956 #if HAVE_PRCTL && defined(PR_SET_UNALIGN)
957 75 prctl(PR_SET_UNALIGN, PR_UNALIGN_SIGBUS);
958 #endif
959 #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
960 if (have_vfp(av_get_cpu_flags()) || have_neon(av_get_cpu_flags()))
961 checkasm_checked_call = checkasm_checked_call_vfp;
962 #endif
963
964
2/4
✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 75 times.
75 if (!tests[0].func || !cpus[0].flag) {
965 fprintf(stderr, "checkasm: no tests to perform\n");
966 return 0;
967 }
968
969
2/2
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 75 times.
150 for (i = 1; i < argc; i++) {
970 75 const char *arg = argv[i];
971 unsigned long l;
972 char *end;
973
974
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 if (!strncmp(arg, "--bench", 7)) {
975 if (bench_init() < 0)
976 return 1;
977 if (arg[7] == '=') {
978 state.bench_pattern = arg + 8;
979 state.bench_pattern_len = strlen(state.bench_pattern);
980 } else
981 state.bench_pattern = "*";
982
1/2
✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
75 } else if (!strncmp(arg, "--test=", 7)) {
983 75 state.test_pattern = arg + 7;
984 } else if (!strcmp(arg, "--csv")) {
985 state.csv = 1; state.tsv = 0;
986 } else if (!strcmp(arg, "--tsv")) {
987 state.csv = 0; state.tsv = 1;
988 } else if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
989 state.verbose = 1;
990 } else if (!strncmp(arg, "--runs=", 7)) {
991 l = strtoul(arg + 7, &end, 10);
992 if (*end == '\0') {
993 if (l > 30) {
994 fprintf(stderr, "checkasm: error: runs exponent must be within the range 0 <= 30\n");
995 usage(argv[0]);
996 }
997 bench_runs = 1U << l;
998 } else {
999 return usage(argv[0]);
1000 }
1001 } else if ((l = strtoul(arg, &end, 10)) <= UINT_MAX &&
1002 *end == '\0') {
1003 seed = l;
1004 } else {
1005 return usage(argv[0]);
1006 }
1007 }
1008
1009 #if ARCH_AARCH64 && HAVE_SVE
1010 if (have_sve(av_get_cpu_flags()))
1011 snprintf(arch_info_buf, sizeof(arch_info_buf),
1012 "SVE %d bits, ", 8 * ff_aarch64_sve_length());
1013 #elif ARCH_RISCV && HAVE_RVV
1014 if (av_get_cpu_flags() & AV_CPU_FLAG_RVV_I32)
1015 snprintf(arch_info_buf, sizeof (arch_info_buf),
1016 "%zu-bit vectors, ", 8 * ff_get_rv_vlenb());
1017 #endif
1018 75 fprintf(stderr, "checkasm: %susing random seed %u\n", arch_info_buf, seed);
1019 75 av_lfg_init(&checkasm_lfg, seed);
1020
1021
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 if (state.bench_pattern)
1022 fprintf(stderr, "checkasm: bench runs %" PRIu64 " (1 << %i)\n", bench_runs, av_log2(bench_runs));
1023
1024 75 check_cpu_flag(NULL, 0);
1025
2/2
✓ Branch 0 taken 1350 times.
✓ Branch 1 taken 75 times.
1425 for (i = 0; cpus[i].flag; i++)
1026 1350 check_cpu_flag(cpus[i].name, cpus[i].flag);
1027
1028
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 if (state.num_failed) {
1029 fprintf(stderr, "checkasm: %d of %d tests have failed\n", state.num_failed, state.num_checked);
1030 ret = 1;
1031 } else {
1032 75 fprintf(stderr, "checkasm: all %d tests passed\n", state.num_checked);
1033
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 if (state.bench_pattern) {
1034 print_benchs(state.funcs);
1035 }
1036 }
1037
1038 75 destroy_func_tree(state.funcs);
1039 75 bench_uninit();
1040 75 return ret;
1041 }
1042
1043 /* Decide whether or not the specified function needs to be tested and
1044 * allocate/initialize data structures if needed. Returns a pointer to a
1045 * reference function if the function should be tested, otherwise NULL */
1046 161837 void *checkasm_check_func(void *func, const char *name, ...)
1047 {
1048 char name_buf[256];
1049 161837 void *ref = func;
1050 CheckasmFuncVersion *v;
1051 int name_length;
1052 va_list arg;
1053
1054 161837 va_start(arg, name);
1055 161837 name_length = vsnprintf(name_buf, sizeof(name_buf), name, arg);
1056 161837 va_end(arg);
1057
1058
4/6
✓ Branch 0 taken 161629 times.
✓ Branch 1 taken 208 times.
✓ Branch 2 taken 161629 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 161629 times.
161837 if (!func || name_length <= 0 || name_length >= sizeof(name_buf))
1059 208 return NULL;
1060
1061 161629 state.current_func = get_func(&state.funcs, name_buf);
1062 161629 state.funcs->color = 1;
1063 161629 v = &state.current_func->versions;
1064
1065
2/2
✓ Branch 0 taken 149673 times.
✓ Branch 1 taken 11956 times.
161629 if (v->func) {
1066 CheckasmFuncVersion *prev;
1067 do {
1068 /* Only test functions that haven't already been tested */
1069
2/2
✓ Branch 0 taken 135236 times.
✓ Branch 1 taken 65958 times.
201194 if (v->func == func)
1070 135236 return NULL;
1071
1072
1/2
✓ Branch 0 taken 65958 times.
✗ Branch 1 not taken.
65958 if (v->ok)
1073 65958 ref = v->func;
1074
1075 65958 prev = v;
1076
2/2
✓ Branch 0 taken 51521 times.
✓ Branch 1 taken 14437 times.
65958 } while ((v = v->next));
1077
1078 14437 v = prev->next = checkasm_malloc(sizeof(CheckasmFuncVersion));
1079 }
1080
1081 26393 v->func = func;
1082 26393 v->ok = 1;
1083 26393 v->cpu = state.cpu_flag;
1084 26393 state.current_func_ver = v;
1085
1086
2/2
✓ Branch 0 taken 14437 times.
✓ Branch 1 taken 11956 times.
26393 if (state.cpu_flag)
1087 14437 state.num_checked++;
1088
1089 26393 return ref;
1090 }
1091
1092 /* Decide whether or not the current function needs to be benchmarked */
1093 39480 int checkasm_bench_func(void)
1094 {
1095
2/6
✓ Branch 0 taken 39480 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 39480 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
39480 return !state.num_failed && state.bench_pattern &&
1096 !wildstrcmp(state.current_func->name, state.bench_pattern);
1097 }
1098
1099 /* Indicate that the current test has failed, return whether verbose printing
1100 * is requested. */
1101 int checkasm_fail_func(const char *msg, ...)
1102 {
1103 if (state.current_func_ver && state.current_func_ver->cpu &&
1104 state.current_func_ver->ok)
1105 {
1106 va_list arg;
1107
1108 print_cpu_name();
1109 fprintf(stderr, " %s_%s (", state.current_func->name, cpu_suffix(state.current_func_ver->cpu));
1110 va_start(arg, msg);
1111 vfprintf(stderr, msg, arg);
1112 va_end(arg);
1113 fprintf(stderr, ")\n");
1114
1115 state.current_func_ver->ok = 0;
1116 state.num_failed++;
1117 }
1118 return state.verbose;
1119 }
1120
1121 313860 void checkasm_set_signal_handler_state(int enabled) {
1122 313860 state.catch_signals = enabled;
1123 313860 }
1124
1125 161837 int checkasm_handle_signal(int s) {
1126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 161837 times.
161837 if (s) {
1127 #ifdef __GLIBC__
1128 checkasm_fail_func("fatal signal %d: %s", s, strsignal(s));
1129 #else
1130 checkasm_fail_func(s == SIGFPE ? "fatal arithmetic error" :
1131 s == SIGILL ? "illegal instruction" :
1132 s == SIGBUS ? "bus error" :
1133 "segmentation fault");
1134 #endif
1135 }
1136 161837 return s;
1137 }
1138
1139 /* Get the benchmark context of the current function */
1140 CheckasmPerf *checkasm_get_perf_context(void)
1141 {
1142 CheckasmPerf *perf = &state.current_func_ver->perf;
1143 memset(perf, 0, sizeof(*perf));
1144 perf->sysfd = state.sysfd;
1145 return perf;
1146 }
1147
1148 /* Print the outcome of all tests performed since the last time this function was called */
1149 2930 void checkasm_report(const char *name, ...)
1150 {
1151 static int prev_checked, prev_failed, max_length;
1152
1153
2/2
✓ Branch 0 taken 415 times.
✓ Branch 1 taken 2515 times.
2930 if (state.num_checked > prev_checked) {
1154 415 int pad_length = max_length + 4;
1155 va_list arg;
1156
1157 415 print_cpu_name();
1158 415 pad_length -= fprintf(stderr, " - %s.", state.current_test_name);
1159 415 va_start(arg, name);
1160 415 pad_length -= vfprintf(stderr, name, arg);
1161 415 va_end(arg);
1162 415 fprintf(stderr, "%*c", FFMAX(pad_length, 0) + 2, '[');
1163
1164
1/2
✓ Branch 0 taken 415 times.
✗ Branch 1 not taken.
415 if (state.num_failed == prev_failed)
1165 415 color_printf(COLOR_GREEN, "OK");
1166 else
1167 color_printf(COLOR_RED, "FAILED");
1168 415 fprintf(stderr, "]\n");
1169
1170 415 prev_checked = state.num_checked;
1171 415 prev_failed = state.num_failed;
1172
2/2
✓ Branch 0 taken 230 times.
✓ Branch 1 taken 2285 times.
2515 } else if (!state.cpu_flag) {
1173 /* Calculate the amount of padding required to make the output vertically aligned */
1174 230 int length = strlen(state.current_test_name);
1175 va_list arg;
1176
1177 230 va_start(arg, name);
1178 230 length += vsnprintf(NULL, 0, name, arg);
1179 230 va_end(arg);
1180
1181
2/2
✓ Branch 0 taken 133 times.
✓ Branch 1 taken 97 times.
230 if (length > max_length)
1182 133 max_length = length;
1183 }
1184 2930 }
1185
1186 static int check_err(const char *file, int line,
1187 const char *name, int w, int h,
1188 int *err)
1189 {
1190 if (*err)
1191 return 0;
1192 if (!checkasm_fail_func("%s:%d", file, line))
1193 return 1;
1194 *err = 1;
1195 fprintf(stderr, "%s (%dx%d):\n", name, w, h);
1196 return 0;
1197 }
1198
1199 #define DEF_CHECKASM_CHECK_FUNC(type, fmt) \
1200 int checkasm_check_##type(const char *file, int line, \
1201 const type *buf1, ptrdiff_t stride1, \
1202 const type *buf2, ptrdiff_t stride2, \
1203 int w, int h, const char *name, \
1204 int align_w, int align_h, \
1205 int padding) \
1206 { \
1207 int64_t aligned_w = (w - 1LL + align_w) & ~(align_w - 1); \
1208 int64_t aligned_h = (h - 1LL + align_h) & ~(align_h - 1); \
1209 int err = 0; \
1210 int y = 0; \
1211 av_assert0(aligned_w == (int32_t)aligned_w);\
1212 av_assert0(aligned_h == (int32_t)aligned_h);\
1213 stride1 /= sizeof(*buf1); \
1214 stride2 /= sizeof(*buf2); \
1215 for (y = 0; y < h; y++) \
1216 if (memcmp(&buf1[y*stride1], &buf2[y*stride2], w*sizeof(*buf1))) \
1217 break; \
1218 if (y != h) { \
1219 if (check_err(file, line, name, w, h, &err)) \
1220 return 1; \
1221 for (y = 0; y < h; y++) { \
1222 for (int x = 0; x < w; x++) \
1223 fprintf(stderr, " " fmt, buf1[x]); \
1224 fprintf(stderr, " "); \
1225 for (int x = 0; x < w; x++) \
1226 fprintf(stderr, " " fmt, buf2[x]); \
1227 fprintf(stderr, " "); \
1228 for (int x = 0; x < w; x++) \
1229 fprintf(stderr, "%c", buf1[x] != buf2[x] ? 'x' : '.'); \
1230 buf1 += stride1; \
1231 buf2 += stride2; \
1232 fprintf(stderr, "\n"); \
1233 } \
1234 buf1 -= h*stride1; \
1235 buf2 -= h*stride2; \
1236 } \
1237 for (y = -padding; y < 0; y++) \
1238 if (memcmp(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
1239 (w + 2*padding)*sizeof(*buf1))) { \
1240 if (check_err(file, line, name, w, h, &err)) \
1241 return 1; \
1242 fprintf(stderr, " overwrite above\n"); \
1243 break; \
1244 } \
1245 for (y = aligned_h; y < aligned_h + padding; y++) \
1246 if (memcmp(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
1247 (w + 2*padding)*sizeof(*buf1))) { \
1248 if (check_err(file, line, name, w, h, &err)) \
1249 return 1; \
1250 fprintf(stderr, " overwrite below\n"); \
1251 break; \
1252 } \
1253 for (y = 0; y < h; y++) \
1254 if (memcmp(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
1255 padding*sizeof(*buf1))) { \
1256 if (check_err(file, line, name, w, h, &err)) \
1257 return 1; \
1258 fprintf(stderr, " overwrite left\n"); \
1259 break; \
1260 } \
1261 for (y = 0; y < h; y++) \
1262 if (memcmp(&buf1[y*stride1 + aligned_w], &buf2[y*stride2 + aligned_w], \
1263 padding*sizeof(*buf1))) { \
1264 if (check_err(file, line, name, w, h, &err)) \
1265 return 1; \
1266 fprintf(stderr, " overwrite right\n"); \
1267 break; \
1268 } \
1269 return err; \
1270 }
1271
1272
18/46
✗ Branch 0 not taken.
✓ Branch 1 taken 7614 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7614 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 230205 times.
✓ Branch 10 taken 230205 times.
✓ Branch 11 taken 7614 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 7614 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 7614 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 7614 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 230205 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 230205 times.
✓ Branch 56 taken 7614 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 230205 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 230205 times.
✓ Branch 64 taken 7614 times.
776821 DEF_CHECKASM_CHECK_FUNC(uint8_t, "%02x")
1273
18/46
✗ Branch 0 not taken.
✓ Branch 1 taken 18496 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 18496 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 554210 times.
✓ Branch 10 taken 554210 times.
✓ Branch 11 taken 18496 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 18496 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 18496 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 18496 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 554210 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 554210 times.
✓ Branch 56 taken 18496 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 554210 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 554210 times.
✓ Branch 64 taken 18496 times.
1912006 DEF_CHECKASM_CHECK_FUNC(uint16_t, "%04x")
1274 DEF_CHECKASM_CHECK_FUNC(uint32_t, "%08x")
1275
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")
1276
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")
1277