LCOV - code coverage report
Current view: top level - tests/checkasm - checkasm.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 176 267 65.9 %
Date: 2017-12-16 01:21:47 Functions: 21 31 67.7 %

          Line data    Source code
       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             : 
      25             : #if CONFIG_LINUX_PERF
      26             : # ifndef _GNU_SOURCE
      27             : #  define _GNU_SOURCE // for syscall (performance monitoring API)
      28             : # endif
      29             : #endif
      30             : 
      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 HAVE_SETCONSOLETEXTATTRIBUTE
      46             : #include <windows.h>
      47             : #define COLOR_RED    FOREGROUND_RED
      48             : #define COLOR_GREEN  FOREGROUND_GREEN
      49             : #define COLOR_YELLOW (FOREGROUND_RED|FOREGROUND_GREEN)
      50             : #else
      51             : #define COLOR_RED    1
      52             : #define COLOR_GREEN  2
      53             : #define COLOR_YELLOW 3
      54             : #endif
      55             : 
      56             : #if HAVE_UNISTD_H
      57             : #include <unistd.h>
      58             : #endif
      59             : 
      60             : #if !HAVE_ISATTY
      61             : #define isatty(fd) 1
      62             : #endif
      63             : 
      64             : #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
      65             : #include "libavutil/arm/cpu.h"
      66             : 
      67             : void (*checkasm_checked_call)(void *func, int dummy, ...) = checkasm_checked_call_novfp;
      68             : #endif
      69             : 
      70             : /* List of tests to invoke */
      71             : static const struct {
      72             :     const char *name;
      73             :     void (*func)(void);
      74             : } tests[] = {
      75             : #if CONFIG_AVCODEC
      76             :     #if CONFIG_AAC_DECODER
      77             :         { "aacpsdsp", checkasm_check_aacpsdsp },
      78             :         { "sbrdsp",   checkasm_check_sbrdsp },
      79             :     #endif
      80             :     #if CONFIG_ALAC_DECODER
      81             :         { "alacdsp", checkasm_check_alacdsp },
      82             :     #endif
      83             :     #if CONFIG_AUDIODSP
      84             :         { "audiodsp", checkasm_check_audiodsp },
      85             :     #endif
      86             :     #if CONFIG_BLOCKDSP
      87             :         { "blockdsp", checkasm_check_blockdsp },
      88             :     #endif
      89             :     #if CONFIG_BSWAPDSP
      90             :         { "bswapdsp", checkasm_check_bswapdsp },
      91             :     #endif
      92             :     #if CONFIG_DCA_DECODER
      93             :         { "synth_filter", checkasm_check_synth_filter },
      94             :     #endif
      95             :     #if CONFIG_EXR_DECODER
      96             :         { "exrdsp", checkasm_check_exrdsp },
      97             :     #endif
      98             :     #if CONFIG_FLACDSP
      99             :         { "flacdsp", checkasm_check_flacdsp },
     100             :     #endif
     101             :     #if CONFIG_FMTCONVERT
     102             :         { "fmtconvert", checkasm_check_fmtconvert },
     103             :     #endif
     104             :     #if CONFIG_G722DSP
     105             :         { "g722dsp", checkasm_check_g722dsp },
     106             :     #endif
     107             :     #if CONFIG_H264DSP
     108             :         { "h264dsp", checkasm_check_h264dsp },
     109             :     #endif
     110             :     #if CONFIG_H264PRED
     111             :         { "h264pred", checkasm_check_h264pred },
     112             :     #endif
     113             :     #if CONFIG_H264QPEL
     114             :         { "h264qpel", checkasm_check_h264qpel },
     115             :     #endif
     116             :     #if CONFIG_HEVC_DECODER
     117             :         { "hevc_add_res", checkasm_check_hevc_add_res },
     118             :         { "hevc_idct", checkasm_check_hevc_idct },
     119             :     #endif
     120             :     #if CONFIG_HUFFYUV_DECODER
     121             :         { "huffyuvdsp", checkasm_check_huffyuvdsp },
     122             :     #endif
     123             :     #if CONFIG_JPEG2000_DECODER
     124             :         { "jpeg2000dsp", checkasm_check_jpeg2000dsp },
     125             :     #endif
     126             :     #if CONFIG_HUFFYUVDSP
     127             :         { "llviddsp", checkasm_check_llviddsp },
     128             :     #endif
     129             :     #if CONFIG_PIXBLOCKDSP
     130             :         { "pixblockdsp", checkasm_check_pixblockdsp },
     131             :     #endif
     132             :     #if CONFIG_UTVIDEO_DECODER
     133             :         { "utvideodsp", checkasm_check_utvideodsp },
     134             :     #endif
     135             :     #if CONFIG_V210_ENCODER
     136             :         { "v210enc", checkasm_check_v210enc },
     137             :     #endif
     138             :     #if CONFIG_VP8DSP
     139             :         { "vp8dsp", checkasm_check_vp8dsp },
     140             :     #endif
     141             :     #if CONFIG_VP9_DECODER
     142             :         { "vp9dsp", checkasm_check_vp9dsp },
     143             :     #endif
     144             :     #if CONFIG_VIDEODSP
     145             :         { "videodsp", checkasm_check_videodsp },
     146             :     #endif
     147             : #endif
     148             : #if CONFIG_AVFILTER
     149             :     #if CONFIG_BLEND_FILTER
     150             :         { "vf_blend", checkasm_check_blend },
     151             :     #endif
     152             :     #if CONFIG_COLORSPACE_FILTER
     153             :         { "vf_colorspace", checkasm_check_colorspace },
     154             :     #endif
     155             :     #if CONFIG_HFLIP_FILTER
     156             :         { "vf_hflip", checkasm_check_vf_hflip },
     157             :     #endif
     158             :     #if CONFIG_THRESHOLD_FILTER
     159             :         { "vf_threshold", checkasm_check_vf_threshold },
     160             :     #endif
     161             : #endif
     162             : #if CONFIG_AVUTIL
     163             :         { "fixed_dsp", checkasm_check_fixed_dsp },
     164             :         { "float_dsp", checkasm_check_float_dsp },
     165             : #endif
     166             :     { NULL }
     167             : };
     168             : 
     169             : /* List of cpu flags to check */
     170             : static const struct {
     171             :     const char *name;
     172             :     const char *suffix;
     173             :     int flag;
     174             : } cpus[] = {
     175             : #if   ARCH_AARCH64
     176             :     { "ARMV8",    "armv8",    AV_CPU_FLAG_ARMV8 },
     177             :     { "NEON",     "neon",     AV_CPU_FLAG_NEON },
     178             : #elif ARCH_ARM
     179             :     { "ARMV5TE",  "armv5te",  AV_CPU_FLAG_ARMV5TE },
     180             :     { "ARMV6",    "armv6",    AV_CPU_FLAG_ARMV6 },
     181             :     { "ARMV6T2",  "armv6t2",  AV_CPU_FLAG_ARMV6T2 },
     182             :     { "VFP",      "vfp",      AV_CPU_FLAG_VFP },
     183             :     { "VFP_VM",   "vfp_vm",   AV_CPU_FLAG_VFP_VM },
     184             :     { "VFPV3",    "vfp3",     AV_CPU_FLAG_VFPV3 },
     185             :     { "NEON",     "neon",     AV_CPU_FLAG_NEON },
     186             : #elif ARCH_PPC
     187             :     { "ALTIVEC",  "altivec",  AV_CPU_FLAG_ALTIVEC },
     188             :     { "VSX",      "vsx",      AV_CPU_FLAG_VSX },
     189             :     { "POWER8",   "power8",   AV_CPU_FLAG_POWER8 },
     190             : #elif ARCH_X86
     191             :     { "MMX",      "mmx",      AV_CPU_FLAG_MMX|AV_CPU_FLAG_CMOV },
     192             :     { "MMXEXT",   "mmxext",   AV_CPU_FLAG_MMXEXT },
     193             :     { "3DNOW",    "3dnow",    AV_CPU_FLAG_3DNOW },
     194             :     { "3DNOWEXT", "3dnowext", AV_CPU_FLAG_3DNOWEXT },
     195             :     { "SSE",      "sse",      AV_CPU_FLAG_SSE },
     196             :     { "SSE2",     "sse2",     AV_CPU_FLAG_SSE2|AV_CPU_FLAG_SSE2SLOW },
     197             :     { "SSE3",     "sse3",     AV_CPU_FLAG_SSE3|AV_CPU_FLAG_SSE3SLOW },
     198             :     { "SSSE3",    "ssse3",    AV_CPU_FLAG_SSSE3|AV_CPU_FLAG_ATOM },
     199             :     { "SSE4.1",   "sse4",     AV_CPU_FLAG_SSE4 },
     200             :     { "SSE4.2",   "sse42",    AV_CPU_FLAG_SSE42 },
     201             :     { "AES-NI",   "aesni",    AV_CPU_FLAG_AESNI },
     202             :     { "AVX",      "avx",      AV_CPU_FLAG_AVX },
     203             :     { "XOP",      "xop",      AV_CPU_FLAG_XOP },
     204             :     { "FMA3",     "fma3",     AV_CPU_FLAG_FMA3 },
     205             :     { "FMA4",     "fma4",     AV_CPU_FLAG_FMA4 },
     206             :     { "AVX2",     "avx2",     AV_CPU_FLAG_AVX2 },
     207             : #endif
     208             :     { NULL }
     209             : };
     210             : 
     211             : typedef struct CheckasmFuncVersion {
     212             :     struct CheckasmFuncVersion *next;
     213             :     void *func;
     214             :     int ok;
     215             :     int cpu;
     216             :     CheckasmPerf perf;
     217             : } CheckasmFuncVersion;
     218             : 
     219             : /* Binary search tree node */
     220             : typedef struct CheckasmFunc {
     221             :     struct CheckasmFunc *child[2];
     222             :     CheckasmFuncVersion versions;
     223             :     uint8_t color; /* 0 = red, 1 = black */
     224             :     char name[1];
     225             : } CheckasmFunc;
     226             : 
     227             : /* Internal state */
     228             : static struct {
     229             :     CheckasmFunc *funcs;
     230             :     CheckasmFunc *current_func;
     231             :     CheckasmFuncVersion *current_func_ver;
     232             :     const char *current_test_name;
     233             :     const char *bench_pattern;
     234             :     int bench_pattern_len;
     235             :     int num_checked;
     236             :     int num_failed;
     237             : 
     238             :     /* perf */
     239             :     int nop_time;
     240             :     int sysfd;
     241             : 
     242             :     int cpu_flag;
     243             :     const char *cpu_flag_name;
     244             :     const char *test_name;
     245             : } state;
     246             : 
     247             : /* PRNG state */
     248             : AVLFG checkasm_lfg;
     249             : 
     250             : /* float compare support code */
     251     6523328 : static int is_negative(union av_intfloat32 u)
     252             : {
     253     6523328 :     return u.i >> 31;
     254             : }
     255             : 
     256     3261664 : int float_near_ulp(float a, float b, unsigned max_ulp)
     257             : {
     258             :     union av_intfloat32 x, y;
     259             : 
     260     3261664 :     x.f = a;
     261     3261664 :     y.f = b;
     262             : 
     263     3261664 :     if (is_negative(x) != is_negative(y)) {
     264             :         // handle -0.0 == +0.0
     265           0 :         return a == b;
     266             :     }
     267             : 
     268     3261664 :     if (llabs((int64_t)x.i - y.i) <= max_ulp)
     269     3261649 :         return 1;
     270             : 
     271          15 :     return 0;
     272             : }
     273             : 
     274        6036 : int float_near_ulp_array(const float *a, const float *b, unsigned max_ulp,
     275             :                          unsigned len)
     276             : {
     277             :     unsigned i;
     278             : 
     279     3262580 :     for (i = 0; i < len; i++) {
     280     3256544 :         if (!float_near_ulp(a[i], b[i], max_ulp))
     281           0 :             return 0;
     282             :     }
     283        6036 :     return 1;
     284             : }
     285             : 
     286      153015 : int float_near_abs_eps(float a, float b, float eps)
     287             : {
     288      153015 :     float abs_diff = fabsf(a - b);
     289             : 
     290      153015 :     return abs_diff < eps;
     291             : }
     292             : 
     293         124 : int float_near_abs_eps_array(const float *a, const float *b, float eps,
     294             :                          unsigned len)
     295             : {
     296             :     unsigned i;
     297             : 
     298      147232 :     for (i = 0; i < len; i++) {
     299      147108 :         if (!float_near_abs_eps(a[i], b[i], eps))
     300           0 :             return 0;
     301             :     }
     302         124 :     return 1;
     303             : }
     304             : 
     305        5120 : int float_near_abs_eps_ulp(float a, float b, float eps, unsigned max_ulp)
     306             : {
     307        5120 :     return float_near_ulp(a, b, max_ulp) || float_near_abs_eps(a, b, eps);
     308             : }
     309             : 
     310           0 : int float_near_abs_eps_array_ulp(const float *a, const float *b, float eps,
     311             :                          unsigned max_ulp, unsigned len)
     312             : {
     313             :     unsigned i;
     314             : 
     315           0 :     for (i = 0; i < len; i++) {
     316           0 :         if (!float_near_abs_eps_ulp(a[i], b[i], eps, max_ulp))
     317           0 :             return 0;
     318             :     }
     319           0 :     return 1;
     320             : }
     321             : 
     322        1792 : int double_near_abs_eps(double a, double b, double eps)
     323             : {
     324        1792 :     double abs_diff = fabs(a - b);
     325             : 
     326        1792 :     return abs_diff < eps;
     327             : }
     328             : 
     329           0 : int double_near_abs_eps_array(const double *a, const double *b, double eps,
     330             :                               unsigned len)
     331             : {
     332             :     unsigned i;
     333             : 
     334           0 :     for (i = 0; i < len; i++) {
     335           0 :         if (!double_near_abs_eps(a[i], b[i], eps))
     336           0 :             return 0;
     337             :     }
     338           0 :     return 1;
     339             : }
     340             : 
     341             : /* Print colored text to stderr if the terminal supports it */
     342         269 : static void color_printf(int color, const char *fmt, ...)
     343             : {
     344             :     static int use_color = -1;
     345             :     va_list arg;
     346             : 
     347             : #if HAVE_SETCONSOLETEXTATTRIBUTE
     348             :     static HANDLE con;
     349             :     static WORD org_attributes;
     350             : 
     351             :     if (use_color < 0) {
     352             :         CONSOLE_SCREEN_BUFFER_INFO con_info;
     353             :         con = GetStdHandle(STD_ERROR_HANDLE);
     354             :         if (con && con != INVALID_HANDLE_VALUE && GetConsoleScreenBufferInfo(con, &con_info)) {
     355             :             org_attributes = con_info.wAttributes;
     356             :             use_color = 1;
     357             :         } else
     358             :             use_color = 0;
     359             :     }
     360             :     if (use_color)
     361             :         SetConsoleTextAttribute(con, (org_attributes & 0xfff0) | (color & 0x0f));
     362             : #else
     363         269 :     if (use_color < 0) {
     364          29 :         const char *term = getenv("TERM");
     365          29 :         use_color = term && strcmp(term, "dumb") && isatty(2);
     366             :     }
     367         269 :     if (use_color)
     368           0 :         fprintf(stderr, "\x1b[%d;3%dm", (color & 0x08) >> 3, color & 0x07);
     369             : #endif
     370             : 
     371         269 :     va_start(arg, fmt);
     372         269 :     vfprintf(stderr, fmt, arg);
     373         269 :     va_end(arg);
     374             : 
     375         269 :     if (use_color) {
     376             : #if HAVE_SETCONSOLETEXTATTRIBUTE
     377             :         SetConsoleTextAttribute(con, org_attributes);
     378             : #else
     379           0 :         fprintf(stderr, "\x1b[0m");
     380             : #endif
     381             :     }
     382         269 : }
     383             : 
     384             : /* Deallocate a tree */
     385        3015 : static void destroy_func_tree(CheckasmFunc *f)
     386             : {
     387        3015 :     if (f) {
     388        1493 :         CheckasmFuncVersion *v = f->versions.next;
     389        4969 :         while (v) {
     390        1983 :             CheckasmFuncVersion *next = v->next;
     391        1983 :             free(v);
     392        1983 :             v = next;
     393             :         }
     394             : 
     395        1493 :         destroy_func_tree(f->child[0]);
     396        1493 :         destroy_func_tree(f->child[1]);
     397        1493 :         free(f);
     398             :     }
     399        3015 : }
     400             : 
     401             : /* Allocate a zero-initialized block, clean up and exit on failure */
     402        3476 : static void *checkasm_malloc(size_t size)
     403             : {
     404        3476 :     void *ptr = calloc(1, size);
     405        3476 :     if (!ptr) {
     406           0 :         fprintf(stderr, "checkasm: malloc failed\n");
     407           0 :         destroy_func_tree(state.funcs);
     408           0 :         exit(1);
     409             :     }
     410        3476 :     return ptr;
     411             : }
     412             : 
     413             : /* Get the suffix of the specified cpu flag */
     414           0 : static const char *cpu_suffix(int cpu)
     415             : {
     416           0 :     int i = FF_ARRAY_ELEMS(cpus);
     417             : 
     418           0 :     while (--i >= 0)
     419           0 :         if (cpu & cpus[i].flag)
     420           0 :             return cpus[i].suffix;
     421             : 
     422           0 :     return "c";
     423             : }
     424             : 
     425           0 : static int cmp_nop(const void *a, const void *b)
     426             : {
     427           0 :     return *(const uint16_t*)a - *(const uint16_t*)b;
     428             : }
     429             : 
     430             : /* Measure the overhead of the timing code (in decicycles) */
     431           0 : static int measure_nop_time(void)
     432             : {
     433             :     uint16_t nops[10000];
     434           0 :     int i, nop_sum = 0;
     435           0 :     av_unused const int sysfd = state.sysfd;
     436             : 
     437           0 :     uint64_t t = 0;
     438           0 :     for (i = 0; i < 10000; i++) {
     439           0 :         PERF_START(t);
     440           0 :         PERF_STOP(t);
     441           0 :         nops[i] = t;
     442             :     }
     443             : 
     444           0 :     qsort(nops, 10000, sizeof(uint16_t), cmp_nop);
     445           0 :     for (i = 2500; i < 7500; i++)
     446           0 :         nop_sum += nops[i];
     447             : 
     448           0 :     return nop_sum / 500;
     449             : }
     450             : 
     451             : /* Print benchmark results */
     452           0 : static void print_benchs(CheckasmFunc *f)
     453             : {
     454           0 :     if (f) {
     455           0 :         print_benchs(f->child[0]);
     456             : 
     457             :         /* Only print functions with at least one assembly version */
     458           0 :         if (f->versions.cpu || f->versions.next) {
     459           0 :             CheckasmFuncVersion *v = &f->versions;
     460             :             do {
     461           0 :                 CheckasmPerf *p = &v->perf;
     462           0 :                 if (p->iterations) {
     463           0 :                     int decicycles = (10*p->cycles/p->iterations - state.nop_time) / 4;
     464           0 :                     printf("%s_%s: %d.%d\n", f->name, cpu_suffix(v->cpu), decicycles/10, decicycles%10);
     465             :                 }
     466           0 :             } while ((v = v->next));
     467             :         }
     468             : 
     469           0 :         print_benchs(f->child[1]);
     470             :     }
     471           0 : }
     472             : 
     473             : /* ASCIIbetical sort except preserving natural order for numbers */
     474      161852 : static int cmp_func_names(const char *a, const char *b)
     475             : {
     476      161852 :     const char *start = a;
     477             :     int ascii_diff, digit_diff;
     478             : 
     479      161852 :     for (; !(ascii_diff = *(const unsigned char*)a - *(const unsigned char*)b) && *a; a++, b++);
     480      161852 :     for (; av_isdigit(*a) && av_isdigit(*b); a++, b++);
     481             : 
     482      161852 :     if (a > start && av_isdigit(a[-1]) && (digit_diff = av_isdigit(*a) - av_isdigit(*b)))
     483       28766 :         return digit_diff;
     484             : 
     485      133086 :     return ascii_diff;
     486             : }
     487             : 
     488             : /* Perform a tree rotation in the specified direction and return the new root */
     489        1436 : static CheckasmFunc *rotate_tree(CheckasmFunc *f, int dir)
     490             : {
     491        1436 :     CheckasmFunc *r = f->child[dir^1];
     492        1436 :     f->child[dir^1] = r->child[dir];
     493        1436 :     r->child[dir] = f;
     494        1436 :     r->color = f->color;
     495        1436 :     f->color = 0;
     496        1436 :     return r;
     497             : }
     498             : 
     499             : #define is_red(f) ((f) && !(f)->color)
     500             : 
     501             : /* Balance a left-leaning red-black tree at the specified node */
     502       10544 : static void balance_tree(CheckasmFunc **root)
     503             : {
     504       10544 :     CheckasmFunc *f = *root;
     505             : 
     506       10544 :     if (is_red(f->child[0]) && is_red(f->child[1])) {
     507         887 :         f->color ^= 1;
     508         887 :         f->child[0]->color = f->child[1]->color = 1;
     509             :     }
     510             : 
     511       10544 :     if (!is_red(f->child[0]) && is_red(f->child[1]))
     512        1020 :         *root = rotate_tree(f, 0); /* Rotate left */
     513        9524 :     else if (is_red(f->child[0]) && is_red(f->child[0]->child[0]))
     514         416 :         *root = rotate_tree(f, 1); /* Rotate right */
     515       10544 : }
     516             : 
     517             : /* Get a node with the specified name, creating it if it doesn't exist */
     518      163345 : static CheckasmFunc *get_func(CheckasmFunc **root, const char *name)
     519             : {
     520      163345 :     CheckasmFunc *f = *root;
     521             : 
     522      163345 :     if (f) {
     523             :         /* Search the tree for a matching node */
     524      161852 :         int cmp = cmp_func_names(name, f->name);
     525      161852 :         if (cmp) {
     526      141596 :             f = get_func(&f->child[cmp > 0], name);
     527             : 
     528             :             /* Rebalance the tree on the way up if a new node was inserted */
     529      141596 :             if (!f->versions.func)
     530       10544 :                 balance_tree(root);
     531             :         }
     532             :     } else {
     533             :         /* Allocate and insert a new node into the tree */
     534        1493 :         int name_length = strlen(name);
     535        1493 :         f = *root = checkasm_malloc(sizeof(CheckasmFunc) + name_length);
     536        1493 :         memcpy(f->name, name, name_length + 1);
     537             :     }
     538             : 
     539      163345 :     return f;
     540             : }
     541             : 
     542             : /* Perform tests and benchmarks for the specified cpu flag if supported by the host */
     543         493 : static void check_cpu_flag(const char *name, int flag)
     544             : {
     545         493 :     int old_cpu_flag = state.cpu_flag;
     546             : 
     547         493 :     flag |= old_cpu_flag;
     548         493 :     av_force_cpu_flags(-1);
     549         493 :     state.cpu_flag = flag & av_get_cpu_flags();
     550         493 :     av_force_cpu_flags(state.cpu_flag);
     551             : 
     552         493 :     if (!flag || state.cpu_flag != old_cpu_flag) {
     553             :         int i;
     554             : 
     555         377 :         state.cpu_flag_name = name;
     556       12064 :         for (i = 0; tests[i].func; i++) {
     557       11687 :             if (state.test_name && strcmp(tests[i].name, state.test_name))
     558       11310 :                 continue;
     559         377 :             state.current_test_name = tests[i].name;
     560         377 :             tests[i].func();
     561             :         }
     562             :     }
     563         493 : }
     564             : 
     565             : /* Print the name of the current CPU flag, but only do it once */
     566         175 : static void print_cpu_name(void)
     567             : {
     568         175 :     if (state.cpu_flag_name) {
     569          94 :         color_printf(COLOR_YELLOW, "%s:\n", state.cpu_flag_name);
     570          94 :         state.cpu_flag_name = NULL;
     571             :     }
     572         175 : }
     573             : 
     574             : #if CONFIG_LINUX_PERF
     575             : static int bench_init_linux(void)
     576             : {
     577             :     struct perf_event_attr attr = {
     578             :         .type           = PERF_TYPE_HARDWARE,
     579             :         .size           = sizeof(struct perf_event_attr),
     580             :         .config         = PERF_COUNT_HW_CPU_CYCLES,
     581             :         .disabled       = 1, // start counting only on demand
     582             :         .exclude_kernel = 1,
     583             :         .exclude_hv     = 1,
     584             :     };
     585             : 
     586             :     printf("benchmarking with Linux Perf Monitoring API\n");
     587             : 
     588             :     state.sysfd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
     589             :     if (state.sysfd == -1) {
     590             :         perror("syscall");
     591             :         return -1;
     592             :     }
     593             :     return 0;
     594             : }
     595             : #endif
     596             : 
     597           0 : static int bench_init_ffmpeg(void)
     598             : {
     599             : #ifdef AV_READ_TIME
     600           0 :     printf("benchmarking with native FFmpeg timers\n");
     601           0 :     return 0;
     602             : #else
     603             :     fprintf(stderr, "checkasm: --bench is not supported on your system\n");
     604             :     return -1;
     605             : #endif
     606             : }
     607             : 
     608           0 : static int bench_init(void)
     609             : {
     610             : #if CONFIG_LINUX_PERF
     611             :     int ret = bench_init_linux();
     612             : #else
     613           0 :     int ret = bench_init_ffmpeg();
     614             : #endif
     615           0 :     if (ret < 0)
     616           0 :         return ret;
     617             : 
     618           0 :     state.nop_time = measure_nop_time();
     619           0 :     printf("nop: %d.%d\n", state.nop_time/10, state.nop_time%10);
     620           0 :     return 0;
     621             : }
     622             : 
     623          29 : static void bench_uninit(void)
     624             : {
     625             : #if CONFIG_LINUX_PERF
     626             :     if (state.sysfd > 0)
     627             :         close(state.sysfd);
     628             : #endif
     629          29 : }
     630             : 
     631          29 : int main(int argc, char *argv[])
     632             : {
     633          29 :     unsigned int seed = av_get_random_seed();
     634          29 :     int i, ret = 0;
     635             : 
     636             : #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
     637             :     if (have_vfp(av_get_cpu_flags()) || have_neon(av_get_cpu_flags()))
     638             :         checkasm_checked_call = checkasm_checked_call_vfp;
     639             : #endif
     640             : 
     641          29 :     if (!tests[0].func || !cpus[0].flag) {
     642           0 :         fprintf(stderr, "checkasm: no tests to perform\n");
     643           0 :         return 0;
     644             :     }
     645             : 
     646          87 :     while (argc > 1) {
     647          29 :         if (!strncmp(argv[1], "--bench", 7)) {
     648           0 :             if (bench_init() < 0)
     649           0 :                 return 1;
     650           0 :             if (argv[1][7] == '=') {
     651           0 :                 state.bench_pattern = argv[1] + 8;
     652           0 :                 state.bench_pattern_len = strlen(state.bench_pattern);
     653             :             } else
     654           0 :                 state.bench_pattern = "";
     655          29 :         } else if (!strncmp(argv[1], "--test=", 7)) {
     656          29 :             state.test_name = argv[1] + 7;
     657             :         } else {
     658           0 :             seed = strtoul(argv[1], NULL, 10);
     659             :         }
     660             : 
     661          29 :         argc--;
     662          29 :         argv++;
     663             :     }
     664             : 
     665          29 :     fprintf(stderr, "checkasm: using random seed %u\n", seed);
     666          29 :     av_lfg_init(&checkasm_lfg, seed);
     667             : 
     668          29 :     check_cpu_flag(NULL, 0);
     669         493 :     for (i = 0; cpus[i].flag; i++)
     670         464 :         check_cpu_flag(cpus[i].name, cpus[i].flag);
     671             : 
     672          29 :     if (state.num_failed) {
     673           0 :         fprintf(stderr, "checkasm: %d of %d tests have failed\n", state.num_failed, state.num_checked);
     674           0 :         ret = 1;
     675             :     } else {
     676          29 :         fprintf(stderr, "checkasm: all %d tests passed\n", state.num_checked);
     677          29 :         if (state.bench_pattern) {
     678           0 :             print_benchs(state.funcs);
     679             :         }
     680             :     }
     681             : 
     682          29 :     destroy_func_tree(state.funcs);
     683          29 :     bench_uninit();
     684          29 :     return ret;
     685             : }
     686             : 
     687             : /* Decide whether or not the specified function needs to be tested and
     688             :  * allocate/initialize data structures if needed. Returns a pointer to a
     689             :  * reference function if the function should be tested, otherwise NULL */
     690       21749 : void *checkasm_check_func(void *func, const char *name, ...)
     691             : {
     692             :     char name_buf[256];
     693       21749 :     void *ref = func;
     694             :     CheckasmFuncVersion *v;
     695             :     int name_length;
     696             :     va_list arg;
     697             : 
     698       21749 :     va_start(arg, name);
     699       21749 :     name_length = vsnprintf(name_buf, sizeof(name_buf), name, arg);
     700       21749 :     va_end(arg);
     701             : 
     702       21749 :     if (!func || name_length <= 0 || name_length >= sizeof(name_buf))
     703           0 :         return NULL;
     704             : 
     705       21749 :     state.current_func = get_func(&state.funcs, name_buf);
     706       21749 :     state.funcs->color = 1;
     707       21749 :     v = &state.current_func->versions;
     708             : 
     709       21749 :     if (v->func) {
     710             :         CheckasmFuncVersion *prev;
     711             :         do {
     712             :             /* Only test functions that haven't already been tested */
     713       35310 :             if (v->func == func)
     714       18273 :                 return NULL;
     715             : 
     716       17037 :             if (v->ok)
     717       17037 :                 ref = v->func;
     718             : 
     719       17037 :             prev = v;
     720       17037 :         } while ((v = v->next));
     721             : 
     722        1983 :         v = prev->next = checkasm_malloc(sizeof(CheckasmFuncVersion));
     723             :     }
     724             : 
     725        3476 :     v->func = func;
     726        3476 :     v->ok = 1;
     727        3476 :     v->cpu = state.cpu_flag;
     728        3476 :     state.current_func_ver = v;
     729             : 
     730        3476 :     if (state.cpu_flag)
     731        1983 :         state.num_checked++;
     732             : 
     733        3476 :     return ref;
     734             : }
     735             : 
     736             : /* Decide whether or not the current function needs to be benchmarked */
     737       10951 : int checkasm_bench_func(void)
     738             : {
     739       10951 :     return !state.num_failed && state.bench_pattern &&
     740           0 :            !strncmp(state.current_func->name, state.bench_pattern, state.bench_pattern_len);
     741             : }
     742             : 
     743             : /* Indicate that the current test has failed */
     744           0 : void checkasm_fail_func(const char *msg, ...)
     745             : {
     746           0 :     if (state.current_func_ver->cpu && state.current_func_ver->ok) {
     747             :         va_list arg;
     748             : 
     749           0 :         print_cpu_name();
     750           0 :         fprintf(stderr, "   %s_%s (", state.current_func->name, cpu_suffix(state.current_func_ver->cpu));
     751           0 :         va_start(arg, msg);
     752           0 :         vfprintf(stderr, msg, arg);
     753           0 :         va_end(arg);
     754           0 :         fprintf(stderr, ")\n");
     755             : 
     756           0 :         state.current_func_ver->ok = 0;
     757           0 :         state.num_failed++;
     758             :     }
     759           0 : }
     760             : 
     761             : /* Get the benchmark context of the current function */
     762           0 : CheckasmPerf *checkasm_get_perf_context(void)
     763             : {
     764           0 :     CheckasmPerf *perf = &state.current_func_ver->perf;
     765           0 :     memset(perf, 0, sizeof(*perf));
     766           0 :     perf->sysfd = state.sysfd;
     767           0 :     return perf;
     768             : }
     769             : 
     770             : /* Print the outcome of all tests performed since the last time this function was called */
     771         962 : void checkasm_report(const char *name, ...)
     772             : {
     773             :     static int prev_checked, prev_failed, max_length;
     774             : 
     775         962 :     if (state.num_checked > prev_checked) {
     776         175 :         int pad_length = max_length + 4;
     777             :         va_list arg;
     778             : 
     779         175 :         print_cpu_name();
     780         175 :         pad_length -= fprintf(stderr, " - %s.", state.current_test_name);
     781         175 :         va_start(arg, name);
     782         175 :         pad_length -= vfprintf(stderr, name, arg);
     783         175 :         va_end(arg);
     784         175 :         fprintf(stderr, "%*c", FFMAX(pad_length, 0) + 2, '[');
     785             : 
     786         175 :         if (state.num_failed == prev_failed)
     787         175 :             color_printf(COLOR_GREEN, "OK");
     788             :         else
     789           0 :             color_printf(COLOR_RED, "FAILED");
     790         175 :         fprintf(stderr, "]\n");
     791             : 
     792         175 :         prev_checked = state.num_checked;
     793         175 :         prev_failed  = state.num_failed;
     794         787 :     } else if (!state.cpu_flag) {
     795             :         /* Calculate the amount of padding required to make the output vertically aligned */
     796          74 :         int length = strlen(state.current_test_name);
     797             :         va_list arg;
     798             : 
     799          74 :         va_start(arg, name);
     800          74 :         length += vsnprintf(NULL, 0, name, arg);
     801          74 :         va_end(arg);
     802             : 
     803          74 :         if (length > max_length)
     804          48 :             max_length = length;
     805             :     }
     806         962 : }

Generated by: LCOV version 1.13