FFmpeg coverage


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