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