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

Generated by: LCOV version 1.13