LCOV - code coverage report
Current view: top level - src/tests/checkasm - checkasm.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 159 235 67.7 %
Date: 2017-01-19 23:52:33 Functions: 18 26 69.2 %

          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 <stdarg.h>
      24             : #include <stdio.h>
      25             : #include <stdlib.h>
      26             : #include <string.h>
      27             : #include "checkasm.h"
      28             : #include "libavutil/common.h"
      29             : #include "libavutil/cpu.h"
      30             : #include "libavutil/intfloat.h"
      31             : #include "libavutil/random_seed.h"
      32             : 
      33             : #if HAVE_IO_H
      34             : #include <io.h>
      35             : #endif
      36             : 
      37             : #if HAVE_SETCONSOLETEXTATTRIBUTE
      38             : #include <windows.h>
      39             : #define COLOR_RED    FOREGROUND_RED
      40             : #define COLOR_GREEN  FOREGROUND_GREEN
      41             : #define COLOR_YELLOW (FOREGROUND_RED|FOREGROUND_GREEN)
      42             : #else
      43             : #define COLOR_RED    1
      44             : #define COLOR_GREEN  2
      45             : #define COLOR_YELLOW 3
      46             : #endif
      47             : 
      48             : #if HAVE_UNISTD_H
      49             : #include <unistd.h>
      50             : #endif
      51             : 
      52             : #if !HAVE_ISATTY
      53             : #define isatty(fd) 1
      54             : #endif
      55             : 
      56             : #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
      57             : #include "libavutil/arm/cpu.h"
      58             : 
      59             : void (*checkasm_checked_call)(void *func, int dummy, ...) = checkasm_checked_call_novfp;
      60             : #endif
      61             : 
      62             : /* List of tests to invoke */
      63             : static const struct {
      64             :     const char *name;
      65             :     void (*func)(void);
      66             : } tests[] = {
      67             : #if CONFIG_AVCODEC
      68             :     #if CONFIG_ALAC_DECODER
      69             :         { "alacdsp", checkasm_check_alacdsp },
      70             :     #endif
      71             :     #if CONFIG_BSWAPDSP
      72             :         { "bswapdsp", checkasm_check_bswapdsp },
      73             :     #endif
      74             :     #if CONFIG_DCA_DECODER
      75             :         { "synth_filter", checkasm_check_synth_filter },
      76             :     #endif
      77             :     #if CONFIG_FLACDSP
      78             :         { "flacdsp", checkasm_check_flacdsp },
      79             :     #endif
      80             :     #if CONFIG_FMTCONVERT
      81             :         { "fmtconvert", checkasm_check_fmtconvert },
      82             :     #endif
      83             :     #if CONFIG_H264DSP
      84             :         { "h264dsp", checkasm_check_h264dsp },
      85             :     #endif
      86             :     #if CONFIG_H264PRED
      87             :         { "h264pred", checkasm_check_h264pred },
      88             :     #endif
      89             :     #if CONFIG_H264QPEL
      90             :         { "h264qpel", checkasm_check_h264qpel },
      91             :     #endif
      92             :     #if CONFIG_JPEG2000_DECODER
      93             :         { "jpeg2000dsp", checkasm_check_jpeg2000dsp },
      94             :     #endif
      95             :     #if CONFIG_PIXBLOCKDSP
      96             :         { "pixblockdsp", checkasm_check_pixblockdsp },
      97             :     #endif
      98             :     #if CONFIG_V210_ENCODER
      99             :         { "v210enc", checkasm_check_v210enc },
     100             :     #endif
     101             :     #if CONFIG_VP8DSP
     102             :         { "vp8dsp", checkasm_check_vp8dsp },
     103             :     #endif
     104             :     #if CONFIG_VP9_DECODER
     105             :         { "vp9dsp", checkasm_check_vp9dsp },
     106             :     #endif
     107             :     #if CONFIG_VIDEODSP
     108             :         { "videodsp", checkasm_check_videodsp },
     109             :     #endif
     110             : #endif
     111             : #if CONFIG_AVFILTER
     112             :     #if CONFIG_BLEND_FILTER
     113             :         { "vf_blend", checkasm_check_blend },
     114             :     #endif
     115             :     #if CONFIG_COLORSPACE_FILTER
     116             :         { "vf_colorspace", checkasm_check_colorspace },
     117             :     #endif
     118             : #endif
     119             :     { NULL }
     120             : };
     121             : 
     122             : /* List of cpu flags to check */
     123             : static const struct {
     124             :     const char *name;
     125             :     const char *suffix;
     126             :     int flag;
     127             : } cpus[] = {
     128             : #if   ARCH_AARCH64
     129             :     { "ARMV8",    "armv8",    AV_CPU_FLAG_ARMV8 },
     130             :     { "NEON",     "neon",     AV_CPU_FLAG_NEON },
     131             : #elif ARCH_ARM
     132             :     { "ARMV5TE",  "armv5te",  AV_CPU_FLAG_ARMV5TE },
     133             :     { "ARMV6",    "armv6",    AV_CPU_FLAG_ARMV6 },
     134             :     { "ARMV6T2",  "armv6t2",  AV_CPU_FLAG_ARMV6T2 },
     135             :     { "VFP",      "vfp",      AV_CPU_FLAG_VFP },
     136             :     { "VFP_VM",   "vfp_vm",   AV_CPU_FLAG_VFP_VM },
     137             :     { "VFPV3",    "vfp3",     AV_CPU_FLAG_VFPV3 },
     138             :     { "NEON",     "neon",     AV_CPU_FLAG_NEON },
     139             : #elif ARCH_PPC
     140             :     { "ALTIVEC",  "altivec",  AV_CPU_FLAG_ALTIVEC },
     141             :     { "VSX",      "vsx",      AV_CPU_FLAG_VSX },
     142             :     { "POWER8",   "power8",   AV_CPU_FLAG_POWER8 },
     143             : #elif ARCH_X86
     144             :     { "MMX",      "mmx",      AV_CPU_FLAG_MMX|AV_CPU_FLAG_CMOV },
     145             :     { "MMXEXT",   "mmxext",   AV_CPU_FLAG_MMXEXT },
     146             :     { "3DNOW",    "3dnow",    AV_CPU_FLAG_3DNOW },
     147             :     { "3DNOWEXT", "3dnowext", AV_CPU_FLAG_3DNOWEXT },
     148             :     { "SSE",      "sse",      AV_CPU_FLAG_SSE },
     149             :     { "SSE2",     "sse2",     AV_CPU_FLAG_SSE2|AV_CPU_FLAG_SSE2SLOW },
     150             :     { "SSE3",     "sse3",     AV_CPU_FLAG_SSE3|AV_CPU_FLAG_SSE3SLOW },
     151             :     { "SSSE3",    "ssse3",    AV_CPU_FLAG_SSSE3|AV_CPU_FLAG_ATOM },
     152             :     { "SSE4.1",   "sse4",     AV_CPU_FLAG_SSE4 },
     153             :     { "SSE4.2",   "sse42",    AV_CPU_FLAG_SSE42 },
     154             :     { "AES-NI",   "aesni",    AV_CPU_FLAG_AESNI },
     155             :     { "AVX",      "avx",      AV_CPU_FLAG_AVX },
     156             :     { "XOP",      "xop",      AV_CPU_FLAG_XOP },
     157             :     { "FMA3",     "fma3",     AV_CPU_FLAG_FMA3 },
     158             :     { "FMA4",     "fma4",     AV_CPU_FLAG_FMA4 },
     159             :     { "AVX2",     "avx2",     AV_CPU_FLAG_AVX2 },
     160             : #endif
     161             :     { NULL }
     162             : };
     163             : 
     164             : typedef struct CheckasmFuncVersion {
     165             :     struct CheckasmFuncVersion *next;
     166             :     void *func;
     167             :     int ok;
     168             :     int cpu;
     169             :     int iterations;
     170             :     uint64_t cycles;
     171             : } CheckasmFuncVersion;
     172             : 
     173             : /* Binary search tree node */
     174             : typedef struct CheckasmFunc {
     175             :     struct CheckasmFunc *child[2];
     176             :     CheckasmFuncVersion versions;
     177             :     uint8_t color; /* 0 = red, 1 = black */
     178             :     char name[1];
     179             : } CheckasmFunc;
     180             : 
     181             : /* Internal state */
     182             : static struct {
     183             :     CheckasmFunc *funcs;
     184             :     CheckasmFunc *current_func;
     185             :     CheckasmFuncVersion *current_func_ver;
     186             :     const char *current_test_name;
     187             :     const char *bench_pattern;
     188             :     int bench_pattern_len;
     189             :     int num_checked;
     190             :     int num_failed;
     191             :     int nop_time;
     192             :     int cpu_flag;
     193             :     const char *cpu_flag_name;
     194             : } state;
     195             : 
     196             : /* PRNG state */
     197             : AVLFG checkasm_lfg;
     198             : 
     199             : /* float compare support code */
     200     2979264 : static int is_negative(union av_intfloat32 u)
     201             : {
     202     2979264 :     return u.i >> 31;
     203             : }
     204             : 
     205     1489632 : int float_near_ulp(float a, float b, unsigned max_ulp)
     206             : {
     207             :     union av_intfloat32 x, y;
     208             : 
     209     1489632 :     x.f = a;
     210     1489632 :     y.f = b;
     211             : 
     212     1489632 :     if (is_negative(x) != is_negative(y)) {
     213             :         // handle -0.0 == +0.0
     214           0 :         return a == b;
     215             :     }
     216             : 
     217     1489632 :     if (abs(x.i - y.i) <= max_ulp)
     218     1489622 :         return 1;
     219             : 
     220          10 :     return 0;
     221             : }
     222             : 
     223        4356 : int float_near_ulp_array(const float *a, const float *b, unsigned max_ulp,
     224             :                          unsigned len)
     225             : {
     226             :     unsigned i;
     227             : 
     228     1490148 :     for (i = 0; i < len; i++) {
     229     1485792 :         if (!float_near_ulp(a[i], b[i], max_ulp))
     230           0 :             return 0;
     231             :     }
     232        4356 :     return 1;
     233             : }
     234             : 
     235          10 : int float_near_abs_eps(float a, float b, float eps)
     236             : {
     237          10 :     float abs_diff = fabsf(a - b);
     238             : 
     239          10 :     return abs_diff < eps;
     240             : }
     241             : 
     242           0 : int float_near_abs_eps_array(const float *a, const float *b, float eps,
     243             :                          unsigned len)
     244             : {
     245             :     unsigned i;
     246             : 
     247           0 :     for (i = 0; i < len; i++) {
     248           0 :         if (!float_near_abs_eps(a[i], b[i], eps))
     249           0 :             return 0;
     250             :     }
     251           0 :     return 1;
     252             : }
     253             : 
     254        3840 : int float_near_abs_eps_ulp(float a, float b, float eps, unsigned max_ulp)
     255             : {
     256        3840 :     return float_near_ulp(a, b, max_ulp) || float_near_abs_eps(a, b, eps);
     257             : }
     258             : 
     259           0 : int float_near_abs_eps_array_ulp(const float *a, const float *b, float eps,
     260             :                          unsigned max_ulp, unsigned len)
     261             : {
     262             :     unsigned i;
     263             : 
     264           0 :     for (i = 0; i < len; i++) {
     265           0 :         if (!float_near_abs_eps_ulp(a[i], b[i], eps, max_ulp))
     266           0 :             return 0;
     267             :     }
     268           0 :     return 1;
     269             : }
     270             : 
     271             : /* Print colored text to stderr if the terminal supports it */
     272          94 : static void color_printf(int color, const char *fmt, ...)
     273             : {
     274             :     static int use_color = -1;
     275             :     va_list arg;
     276             : 
     277             : #if HAVE_SETCONSOLETEXTATTRIBUTE
     278             :     static HANDLE con;
     279             :     static WORD org_attributes;
     280             : 
     281             :     if (use_color < 0) {
     282             :         CONSOLE_SCREEN_BUFFER_INFO con_info;
     283             :         con = GetStdHandle(STD_ERROR_HANDLE);
     284             :         if (con && con != INVALID_HANDLE_VALUE && GetConsoleScreenBufferInfo(con, &con_info)) {
     285             :             org_attributes = con_info.wAttributes;
     286             :             use_color = 1;
     287             :         } else
     288             :             use_color = 0;
     289             :     }
     290             :     if (use_color)
     291             :         SetConsoleTextAttribute(con, (org_attributes & 0xfff0) | (color & 0x0f));
     292             : #else
     293          94 :     if (use_color < 0) {
     294           1 :         const char *term = getenv("TERM");
     295           1 :         use_color = term && strcmp(term, "dumb") && isatty(2);
     296             :     }
     297          94 :     if (use_color)
     298           0 :         fprintf(stderr, "\x1b[%d;3%dm", (color & 0x08) >> 3, color & 0x07);
     299             : #endif
     300             : 
     301          94 :     va_start(arg, fmt);
     302          94 :     vfprintf(stderr, fmt, arg);
     303          94 :     va_end(arg);
     304             : 
     305          94 :     if (use_color) {
     306             : #if HAVE_SETCONSOLETEXTATTRIBUTE
     307             :         SetConsoleTextAttribute(con, org_attributes);
     308             : #else
     309           0 :         fprintf(stderr, "\x1b[0m");
     310             : #endif
     311             :     }
     312          94 : }
     313             : 
     314             : /* Deallocate a tree */
     315        2781 : static void destroy_func_tree(CheckasmFunc *f)
     316             : {
     317        2781 :     if (f) {
     318        1390 :         CheckasmFuncVersion *v = f->versions.next;
     319        4423 :         while (v) {
     320        1643 :             CheckasmFuncVersion *next = v->next;
     321        1643 :             free(v);
     322        1643 :             v = next;
     323             :         }
     324             : 
     325        1390 :         destroy_func_tree(f->child[0]);
     326        1390 :         destroy_func_tree(f->child[1]);
     327        1390 :         free(f);
     328             :     }
     329        2781 : }
     330             : 
     331             : /* Allocate a zero-initialized block, clean up and exit on failure */
     332        3033 : static void *checkasm_malloc(size_t size)
     333             : {
     334        3033 :     void *ptr = calloc(1, size);
     335        3033 :     if (!ptr) {
     336           0 :         fprintf(stderr, "checkasm: malloc failed\n");
     337           0 :         destroy_func_tree(state.funcs);
     338           0 :         exit(1);
     339             :     }
     340        3033 :     return ptr;
     341             : }
     342             : 
     343             : /* Get the suffix of the specified cpu flag */
     344           0 : static const char *cpu_suffix(int cpu)
     345             : {
     346           0 :     int i = FF_ARRAY_ELEMS(cpus);
     347             : 
     348           0 :     while (--i >= 0)
     349           0 :         if (cpu & cpus[i].flag)
     350           0 :             return cpus[i].suffix;
     351             : 
     352           0 :     return "c";
     353             : }
     354             : 
     355             : #ifdef AV_READ_TIME
     356           0 : static int cmp_nop(const void *a, const void *b)
     357             : {
     358           0 :     return *(const uint16_t*)a - *(const uint16_t*)b;
     359             : }
     360             : 
     361             : /* Measure the overhead of the timing code (in decicycles) */
     362           0 : static int measure_nop_time(void)
     363             : {
     364             :     uint16_t nops[10000];
     365           0 :     int i, nop_sum = 0;
     366             : 
     367           0 :     for (i = 0; i < 10000; i++) {
     368           0 :         uint64_t t = AV_READ_TIME();
     369           0 :         nops[i] = AV_READ_TIME() - t;
     370             :     }
     371             : 
     372           0 :     qsort(nops, 10000, sizeof(uint16_t), cmp_nop);
     373           0 :     for (i = 2500; i < 7500; i++)
     374           0 :         nop_sum += nops[i];
     375             : 
     376           0 :     return nop_sum / 500;
     377             : }
     378             : 
     379             : /* Print benchmark results */
     380           0 : static void print_benchs(CheckasmFunc *f)
     381             : {
     382           0 :     if (f) {
     383           0 :         print_benchs(f->child[0]);
     384             : 
     385             :         /* Only print functions with at least one assembly version */
     386           0 :         if (f->versions.cpu || f->versions.next) {
     387           0 :             CheckasmFuncVersion *v = &f->versions;
     388             :             do {
     389           0 :                 if (v->iterations) {
     390           0 :                     int decicycles = (10*v->cycles/v->iterations - state.nop_time) / 4;
     391           0 :                     printf("%s_%s: %d.%d\n", f->name, cpu_suffix(v->cpu), decicycles/10, decicycles%10);
     392             :                 }
     393           0 :             } while ((v = v->next));
     394             :         }
     395             : 
     396           0 :         print_benchs(f->child[1]);
     397             :     }
     398           0 : }
     399             : #endif
     400             : 
     401             : /* ASCIIbetical sort except preserving natural order for numbers */
     402      170529 : static int cmp_func_names(const char *a, const char *b)
     403             : {
     404      170529 :     const char *start = a;
     405             :     int ascii_diff, digit_diff;
     406             : 
     407      170529 :     for (; !(ascii_diff = *(const unsigned char*)a - *(const unsigned char*)b) && *a; a++, b++);
     408      170529 :     for (; av_isdigit(*a) && av_isdigit(*b); a++, b++);
     409             : 
     410      170529 :     if (a > start && av_isdigit(a[-1]) && (digit_diff = av_isdigit(*a) - av_isdigit(*b)))
     411       25823 :         return digit_diff;
     412             : 
     413      144706 :     return ascii_diff;
     414             : }
     415             : 
     416             : /* Perform a tree rotation in the specified direction and return the new root */
     417        1389 : static CheckasmFunc *rotate_tree(CheckasmFunc *f, int dir)
     418             : {
     419        1389 :     CheckasmFunc *r = f->child[dir^1];
     420        1389 :     f->child[dir^1] = r->child[dir];
     421        1389 :     r->child[dir] = f;
     422        1389 :     r->color = f->color;
     423        1389 :     f->color = 0;
     424        1389 :     return r;
     425             : }
     426             : 
     427             : #define is_red(f) ((f) && !(f)->color)
     428             : 
     429             : /* Balance a left-leaning red-black tree at the specified node */
     430       13022 : static void balance_tree(CheckasmFunc **root)
     431             : {
     432       13022 :     CheckasmFunc *f = *root;
     433             : 
     434       13022 :     if (is_red(f->child[0]) && is_red(f->child[1])) {
     435         880 :         f->color ^= 1;
     436         880 :         f->child[0]->color = f->child[1]->color = 1;
     437             :     }
     438             : 
     439       13022 :     if (!is_red(f->child[0]) && is_red(f->child[1]))
     440        1012 :         *root = rotate_tree(f, 0); /* Rotate left */
     441       12010 :     else if (is_red(f->child[0]) && is_red(f->child[0]->child[0]))
     442         377 :         *root = rotate_tree(f, 1); /* Rotate right */
     443       13022 : }
     444             : 
     445             : /* Get a node with the specified name, creating it if it doesn't exist */
     446      171919 : static CheckasmFunc *get_func(CheckasmFunc **root, const char *name)
     447             : {
     448      171919 :     CheckasmFunc *f = *root;
     449             : 
     450      171919 :     if (f) {
     451             :         /* Search the tree for a matching node */
     452      170529 :         int cmp = cmp_func_names(name, f->name);
     453      170529 :         if (cmp) {
     454      154649 :             f = get_func(&f->child[cmp > 0], name);
     455             : 
     456             :             /* Rebalance the tree on the way up if a new node was inserted */
     457      154649 :             if (!f->versions.func)
     458       13022 :                 balance_tree(root);
     459             :         }
     460             :     } else {
     461             :         /* Allocate and insert a new node into the tree */
     462        1390 :         int name_length = strlen(name);
     463        1390 :         f = *root = checkasm_malloc(sizeof(CheckasmFunc) + name_length);
     464        1390 :         memcpy(f->name, name, name_length + 1);
     465             :     }
     466             : 
     467      171919 :     return f;
     468             : }
     469             : 
     470             : /* Perform tests and benchmarks for the specified cpu flag if supported by the host */
     471          17 : static void check_cpu_flag(const char *name, int flag)
     472             : {
     473          17 :     int old_cpu_flag = state.cpu_flag;
     474             : 
     475          17 :     flag |= old_cpu_flag;
     476          17 :     av_force_cpu_flags(-1);
     477          17 :     state.cpu_flag = flag & av_get_cpu_flags();
     478          17 :     av_force_cpu_flags(state.cpu_flag);
     479             : 
     480          17 :     if (!flag || state.cpu_flag != old_cpu_flag) {
     481             :         int i;
     482             : 
     483          11 :         state.cpu_flag_name = name;
     484         187 :         for (i = 0; tests[i].func; i++) {
     485         176 :             state.current_test_name = tests[i].name;
     486         176 :             tests[i].func();
     487             :         }
     488             :     }
     489          17 : }
     490             : 
     491             : /* Print the name of the current CPU flag, but only do it once */
     492          87 : static void print_cpu_name(void)
     493             : {
     494          87 :     if (state.cpu_flag_name) {
     495           7 :         color_printf(COLOR_YELLOW, "%s:\n", state.cpu_flag_name);
     496           7 :         state.cpu_flag_name = NULL;
     497             :     }
     498          87 : }
     499             : 
     500           1 : int main(int argc, char *argv[])
     501             : {
     502           1 :     int i, seed, ret = 0;
     503             : 
     504             : #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
     505             :     if (have_vfp(av_get_cpu_flags()) || have_neon(av_get_cpu_flags()))
     506             :         checkasm_checked_call = checkasm_checked_call_vfp;
     507             : #endif
     508             : 
     509           1 :     if (!tests[0].func || !cpus[0].flag) {
     510           0 :         fprintf(stderr, "checkasm: no tests to perform\n");
     511           0 :         return 0;
     512             :     }
     513             : 
     514           1 :     if (argc > 1 && !strncmp(argv[1], "--bench", 7)) {
     515             : #ifndef AV_READ_TIME
     516             :         fprintf(stderr, "checkasm: --bench is not supported on your system\n");
     517             :         return 1;
     518             : #endif
     519           0 :         if (argv[1][7] == '=') {
     520           0 :             state.bench_pattern = argv[1] + 8;
     521           0 :             state.bench_pattern_len = strlen(state.bench_pattern);
     522             :         } else
     523           0 :             state.bench_pattern = "";
     524             : 
     525           0 :         argc--;
     526           0 :         argv++;
     527             :     }
     528             : 
     529           1 :     seed = (argc > 1) ? atoi(argv[1]) : av_get_random_seed();
     530           1 :     fprintf(stderr, "checkasm: using random seed %u\n", seed);
     531           1 :     av_lfg_init(&checkasm_lfg, seed);
     532             : 
     533           1 :     check_cpu_flag(NULL, 0);
     534          17 :     for (i = 0; cpus[i].flag; i++)
     535          16 :         check_cpu_flag(cpus[i].name, cpus[i].flag);
     536             : 
     537           1 :     if (state.num_failed) {
     538           0 :         fprintf(stderr, "checkasm: %d of %d tests have failed\n", state.num_failed, state.num_checked);
     539           0 :         ret = 1;
     540             :     } else {
     541           1 :         fprintf(stderr, "checkasm: all %d tests passed\n", state.num_checked);
     542             : #ifdef AV_READ_TIME
     543           1 :         if (state.bench_pattern) {
     544           0 :             state.nop_time = measure_nop_time();
     545           0 :             printf("nop: %d.%d\n", state.nop_time/10, state.nop_time%10);
     546           0 :             print_benchs(state.funcs);
     547             :         }
     548             : #endif
     549             :     }
     550             : 
     551           1 :     destroy_func_tree(state.funcs);
     552           1 :     return ret;
     553             : }
     554             : 
     555             : /* Decide whether or not the specified function needs to be tested and
     556             :  * allocate/initialize data structures if needed. Returns a pointer to a
     557             :  * reference function if the function should be tested, otherwise NULL */
     558       17270 : void *checkasm_check_func(void *func, const char *name, ...)
     559             : {
     560             :     char name_buf[256];
     561       17270 :     void *ref = func;
     562             :     CheckasmFuncVersion *v;
     563             :     int name_length;
     564             :     va_list arg;
     565             : 
     566       17270 :     va_start(arg, name);
     567       17270 :     name_length = vsnprintf(name_buf, sizeof(name_buf), name, arg);
     568       17270 :     va_end(arg);
     569             : 
     570       17270 :     if (!func || name_length <= 0 || name_length >= sizeof(name_buf))
     571           0 :         return NULL;
     572             : 
     573       17270 :     state.current_func = get_func(&state.funcs, name_buf);
     574       17270 :     state.funcs->color = 1;
     575       17270 :     v = &state.current_func->versions;
     576             : 
     577       17270 :     if (v->func) {
     578             :         CheckasmFuncVersion *prev;
     579             :         do {
     580             :             /* Only test functions that haven't already been tested */
     581       26175 :             if (v->func == func)
     582       14237 :                 return NULL;
     583             : 
     584       11938 :             if (v->ok)
     585       11938 :                 ref = v->func;
     586             : 
     587       11938 :             prev = v;
     588       11938 :         } while ((v = v->next));
     589             : 
     590        1643 :         v = prev->next = checkasm_malloc(sizeof(CheckasmFuncVersion));
     591             :     }
     592             : 
     593        3033 :     v->func = func;
     594        3033 :     v->ok = 1;
     595        3033 :     v->cpu = state.cpu_flag;
     596        3033 :     state.current_func_ver = v;
     597             : 
     598        3033 :     if (state.cpu_flag)
     599        1643 :         state.num_checked++;
     600             : 
     601        3033 :     return ref;
     602             : }
     603             : 
     604             : /* Decide whether or not the current function needs to be benchmarked */
     605        9538 : int checkasm_bench_func(void)
     606             : {
     607        9538 :     return !state.num_failed && state.bench_pattern &&
     608           0 :            !strncmp(state.current_func->name, state.bench_pattern, state.bench_pattern_len);
     609             : }
     610             : 
     611             : /* Indicate that the current test has failed */
     612           0 : void checkasm_fail_func(const char *msg, ...)
     613             : {
     614           0 :     if (state.current_func_ver->cpu && state.current_func_ver->ok) {
     615             :         va_list arg;
     616             : 
     617           0 :         print_cpu_name();
     618           0 :         fprintf(stderr, "   %s_%s (", state.current_func->name, cpu_suffix(state.current_func_ver->cpu));
     619           0 :         va_start(arg, msg);
     620           0 :         vfprintf(stderr, msg, arg);
     621           0 :         va_end(arg);
     622           0 :         fprintf(stderr, ")\n");
     623             : 
     624           0 :         state.current_func_ver->ok = 0;
     625           0 :         state.num_failed++;
     626             :     }
     627           0 : }
     628             : 
     629             : /* Update benchmark results of the current function */
     630           0 : void checkasm_update_bench(int iterations, uint64_t cycles)
     631             : {
     632           0 :     state.current_func_ver->iterations += iterations;
     633           0 :     state.current_func_ver->cycles += cycles;
     634           0 : }
     635             : 
     636             : /* Print the outcome of all tests performed since the last time this function was called */
     637         330 : void checkasm_report(const char *name, ...)
     638             : {
     639             :     static int prev_checked, prev_failed, max_length;
     640             : 
     641         330 :     if (state.num_checked > prev_checked) {
     642          87 :         int pad_length = max_length + 4;
     643             :         va_list arg;
     644             : 
     645          87 :         print_cpu_name();
     646          87 :         pad_length -= fprintf(stderr, " - %s.", state.current_test_name);
     647          87 :         va_start(arg, name);
     648          87 :         pad_length -= vfprintf(stderr, name, arg);
     649          87 :         va_end(arg);
     650          87 :         fprintf(stderr, "%*c", FFMAX(pad_length, 0) + 2, '[');
     651             : 
     652          87 :         if (state.num_failed == prev_failed)
     653          87 :             color_printf(COLOR_GREEN, "OK");
     654             :         else
     655           0 :             color_printf(COLOR_RED, "FAILED");
     656          87 :         fprintf(stderr, "]\n");
     657             : 
     658          87 :         prev_checked = state.num_checked;
     659          87 :         prev_failed  = state.num_failed;
     660         243 :     } else if (!state.cpu_flag) {
     661             :         /* Calculate the amount of padding required to make the output vertically aligned */
     662          30 :         int length = strlen(state.current_test_name);
     663             :         va_list arg;
     664             : 
     665          30 :         va_start(arg, name);
     666          30 :         length += vsnprintf(NULL, 0, name, arg);
     667          30 :         va_end(arg);
     668             : 
     669          30 :         if (length > max_length)
     670           1 :             max_length = length;
     671             :     }
     672         330 : }

Generated by: LCOV version 1.12