Path: blob/21.2-virgl/src/amd/compiler/tests/main.cpp
7099 views
/*1* Copyright © 2020 Valve Corporation2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS20* IN THE SOFTWARE.21*22*/23#include <map>24#include <set>25#include <string>26#include <vector>27#include <stdio.h>28#include <string.h>29#include <getopt.h>30#include <unistd.h>31#include <stdarg.h>32#include <llvm-c/Target.h>33#include "aco_ir.h"34#include "framework.h"3536#include "util/u_cpu_detect.h"3738static const char *help_message =39"Usage: %s [-h] [-l --list] [--no-check] [TEST [TEST ...]]\n"40"\n"41"Run ACO unit test(s). If TEST is not provided, all tests are run.\n"42"\n"43"positional arguments:\n"44" TEST Run TEST. If TEST ends with a '.', run tests with names\n"45" starting with TEST. The test variant (after the '/') can\n"46" be omitted to run all variants\n"47"\n"48"optional arguments:\n"49" -h, --help Show this help message and exit.\n"50" -l --list List unit tests.\n"51" --no-check Print test output instead of checking it.\n";5253std::map<std::string, TestDef> tests;54FILE *output = NULL;5556static TestDef current_test;57static unsigned tests_written = 0;58static FILE *checker_stdin = NULL;59static char *checker_stdin_data = NULL;60static size_t checker_stdin_size = 0;6162static char *output_data = NULL;63static size_t output_size = 0;64static size_t output_offset = 0;6566static char current_variant[64] = {0};67static std::set<std::string> *variant_filter = NULL;6869bool test_failed = false;70bool test_skipped = false;71static char fail_message[256] = {0};7273void write_test()74{75if (!checker_stdin) {76/* not entirely correct, but shouldn't matter */77tests_written++;78return;79}8081fflush(output);82if (output_offset == output_size && !test_skipped && !test_failed)83return;8485char *data = output_data + output_offset;86uint32_t size = output_size - output_offset;8788fwrite("test", 1, 4, checker_stdin);89fwrite(current_test.name, 1, strlen(current_test.name)+1, checker_stdin);90fwrite(current_variant, 1, strlen(current_variant)+1, checker_stdin);91fwrite(current_test.source_file, 1, strlen(current_test.source_file)+1, checker_stdin);92if (test_failed || test_skipped) {93const char *res = test_failed ? "failed" : "skipped";94fwrite("\x01", 1, 1, checker_stdin);95fwrite(res, 1, strlen(res)+1, checker_stdin);96fwrite(fail_message, 1, strlen(fail_message)+1, checker_stdin);97} else {98fwrite("\x00", 1, 1, checker_stdin);99}100fwrite(&size, 4, 1, checker_stdin);101fwrite(data, 1, size, checker_stdin);102103tests_written++;104output_offset += size;105}106107bool set_variant(const char *name)108{109if (variant_filter && !variant_filter->count(name))110return false;111112write_test();113test_failed = false;114test_skipped = false;115strncpy(current_variant, name, sizeof(current_variant) - 1);116117printf("Running '%s/%s'\n", current_test.name, name);118119return true;120}121122void fail_test(const char *fmt, ...)123{124va_list args;125va_start(args, fmt);126127test_failed = true;128vsnprintf(fail_message, sizeof(fail_message), fmt, args);129130va_end(args);131}132133void skip_test(const char *fmt, ...)134{135va_list args;136va_start(args, fmt);137138test_skipped = true;139vsnprintf(fail_message, sizeof(fail_message), fmt, args);140141va_end(args);142}143144void run_test(TestDef def)145{146current_test = def;147output_data = NULL;148output_size = 0;149output_offset = 0;150test_failed = false;151test_skipped = false;152memset(current_variant, 0, sizeof(current_variant));153154if (checker_stdin)155output = open_memstream(&output_data, &output_size);156else157output = stdout;158159current_test.func();160write_test();161162if (checker_stdin)163fclose(output);164free(output_data);165}166167int check_output(char **argv)168{169fflush(stdout);170fflush(stderr);171172fclose(checker_stdin);173174int stdin_pipe[2];175pipe(stdin_pipe);176177pid_t child_pid = fork();178if (child_pid == -1) {179fprintf(stderr, "%s: fork() failed: %s\n", argv[0], strerror(errno));180return 99;181} else if (child_pid != 0) {182/* Evaluate test output externally using Python */183dup2(stdin_pipe[0], STDIN_FILENO);184close(stdin_pipe[0]);185close(stdin_pipe[1]);186187execlp(ACO_TEST_PYTHON_BIN, ACO_TEST_PYTHON_BIN, ACO_TEST_SOURCE_DIR "/check_output.py", NULL);188fprintf(stderr, "%s: execlp() failed: %s\n", argv[0], strerror(errno));189return 99;190} else {191/* Feed input data to the Python process. Writing large streams to192* stdin will block eventually, so this is done in a forked process193* to let the test checker process chunks of data as they arrive */194write(stdin_pipe[1], checker_stdin_data, checker_stdin_size);195close(stdin_pipe[0]);196close(stdin_pipe[1]);197_exit(0);198}199}200201bool match_test(std::string name, std::string pattern)202{203if (name.length() < pattern.length())204return false;205if (pattern.back() == '.')206name.resize(pattern.length());207return name == pattern;208}209210int main(int argc, char **argv)211{212int print_help = 0;213int do_list = 0;214int do_check = 1;215const struct option opts[] = {216{ "help", no_argument, &print_help, 1 },217{ "list", no_argument, &do_list, 1 },218{ "no-check", no_argument, &do_check, 0 },219{ NULL, 0, NULL, 0 }220};221222int c;223while ((c = getopt_long(argc, argv, "hl", opts, NULL)) != -1) {224switch (c) {225case 'h':226print_help = 1;227break;228case 'l':229do_list = 1;230break;231case 0:232break;233case '?':234default:235fprintf(stderr, "%s: Invalid argument\n", argv[0]);236return 99;237}238}239240if (print_help) {241fprintf(stderr, help_message, argv[0]);242return 99;243}244245util_cpu_detect();246247if (do_list) {248for (auto test : tests)249printf("%s\n", test.first.c_str());250return 99;251}252253std::vector<std::pair<std::string, std::string>> names;254for (int i = optind; i < argc; i++) {255std::string name = argv[i];256std::string variant;257size_t pos = name.find('/');258if (pos != std::string::npos) {259variant = name.substr(pos + 1);260name = name.substr(0, pos);261}262names.emplace_back(std::pair<std::string, std::string>(name, variant));263}264265if (do_check)266checker_stdin = open_memstream(&checker_stdin_data, &checker_stdin_size);267268LLVMInitializeAMDGPUTargetInfo();269LLVMInitializeAMDGPUTarget();270LLVMInitializeAMDGPUTargetMC();271LLVMInitializeAMDGPUDisassembler();272273aco::init();274275for (auto pair : tests) {276bool found = names.empty();277bool all_variants = names.empty();278std::set<std::string> variants;279for (const std::pair<std::string, std::string>& name : names) {280if (match_test(pair.first, name.first)) {281found = true;282if (name.second.empty())283all_variants = true;284else285variants.insert(name.second);286}287}288289if (found) {290variant_filter = all_variants ? NULL : &variants;291printf("Running '%s'\n", pair.first.c_str());292run_test(pair.second);293}294}295if (!tests_written) {296fprintf(stderr, "%s: No matching tests\n", argv[0]);297return 99;298}299300if (checker_stdin) {301printf("\n");302return check_output(argv);303} else {304printf("Tests ran\n");305return 99;306}307}308309310