xref: /aosp_15_r20/external/mesa3d/src/amd/compiler/tests/main.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2020 Valve Corporation
3  *
4  * SPDX-License-Identifier: MIT
5  */
6 #include "aco_ir.h"
7 
8 #include <llvm-c/Target.h>
9 
10 #include "framework.h"
11 #include <getopt.h>
12 #include <map>
13 #include <set>
14 #include <stdarg.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <string>
18 #include <unistd.h>
19 #include <vector>
20 
21 static const char* help_message =
22    "Usage: %s [-h] [-l --list] [--no-check] [TEST [TEST ...]]\n"
23    "\n"
24    "Run ACO unit test(s). If TEST is not provided, all tests are run.\n"
25    "\n"
26    "positional arguments:\n"
27    "  TEST        Run TEST. If TEST ends with a '.', run tests with names\n"
28    "              starting with TEST. The test variant (after the '/') can\n"
29    "              be omitted to run all variants\n"
30    "\n"
31    "optional arguments:\n"
32    "  -h, --help  Show this help message and exit.\n"
33    "  -l --list   List unit tests.\n"
34    "  --no-check  Print test output instead of checking it.\n";
35 
36 std::map<std::string, TestDef> *tests = NULL;
37 FILE* output = NULL;
38 
39 static TestDef current_test;
40 static unsigned tests_written = 0;
41 static FILE* checker_stdin = NULL;
42 static char* checker_stdin_data = NULL;
43 static size_t checker_stdin_size = 0;
44 
45 static char* output_data = NULL;
46 static size_t output_size = 0;
47 static size_t output_offset = 0;
48 
49 static char current_variant[64] = {0};
50 static std::set<std::string>* variant_filter = NULL;
51 
52 bool test_failed = false;
53 bool test_skipped = false;
54 static char fail_message[256] = {0};
55 
56 void
write_test()57 write_test()
58 {
59    if (!checker_stdin) {
60       /* not entirely correct, but shouldn't matter */
61       tests_written++;
62       return;
63    }
64 
65    fflush(output);
66    if (output_offset == output_size && !test_skipped && !test_failed)
67       return;
68 
69    char* data = output_data + output_offset;
70    uint32_t size = output_size - output_offset;
71 
72    fwrite("test", 1, 4, checker_stdin);
73    fwrite(current_test.name, 1, strlen(current_test.name) + 1, checker_stdin);
74    fwrite(current_variant, 1, strlen(current_variant) + 1, checker_stdin);
75    fwrite(current_test.source_file, 1, strlen(current_test.source_file) + 1, checker_stdin);
76    if (test_failed || test_skipped) {
77       const char* res = test_failed ? "failed" : "skipped";
78       fwrite("\x01", 1, 1, checker_stdin);
79       fwrite(res, 1, strlen(res) + 1, checker_stdin);
80       fwrite(fail_message, 1, strlen(fail_message) + 1, checker_stdin);
81    } else {
82       fwrite("\x00", 1, 1, checker_stdin);
83    }
84    fwrite(&size, 4, 1, checker_stdin);
85    fwrite(data, 1, size, checker_stdin);
86 
87    tests_written++;
88    output_offset += size;
89 }
90 
91 bool
set_variant(const char * name)92 set_variant(const char* name)
93 {
94    if (variant_filter && !variant_filter->count(name))
95       return false;
96 
97    write_test();
98    test_failed = false;
99    test_skipped = false;
100    strncpy(current_variant, name, sizeof(current_variant) - 1);
101 
102    printf("Running '%s/%s'\n", current_test.name, name);
103 
104    return true;
105 }
106 
107 void
fail_test(const char * fmt,...)108 fail_test(const char* fmt, ...)
109 {
110    va_list args;
111    va_start(args, fmt);
112 
113    test_failed = true;
114    vsnprintf(fail_message, sizeof(fail_message), fmt, args);
115 
116    va_end(args);
117 }
118 
119 void
skip_test(const char * fmt,...)120 skip_test(const char* fmt, ...)
121 {
122    va_list args;
123    va_start(args, fmt);
124 
125    test_skipped = true;
126    vsnprintf(fail_message, sizeof(fail_message), fmt, args);
127 
128    va_end(args);
129 }
130 
131 void
run_test(TestDef def)132 run_test(TestDef def)
133 {
134    current_test = def;
135    output_data = NULL;
136    output_size = 0;
137    output_offset = 0;
138    test_failed = false;
139    test_skipped = false;
140    memset(current_variant, 0, sizeof(current_variant));
141 
142    if (checker_stdin)
143       output = open_memstream(&output_data, &output_size);
144    else
145       output = stdout;
146 
147    current_test.func();
148    write_test();
149 
150    if (checker_stdin)
151       fclose(output);
152    free(output_data);
153 }
154 
155 int
check_output(char ** argv)156 check_output(char** argv)
157 {
158    fflush(stdout);
159    fflush(stderr);
160 
161    fclose(checker_stdin);
162 
163    int stdin_pipe[2];
164    pipe(stdin_pipe);
165 
166    pid_t child_pid = fork();
167    if (child_pid == -1) {
168       fprintf(stderr, "%s: fork() failed: %s\n", argv[0], strerror(errno));
169       return 99;
170    } else if (child_pid != 0) {
171       /* Evaluate test output externally using Python */
172       dup2(stdin_pipe[0], STDIN_FILENO);
173       close(stdin_pipe[0]);
174       close(stdin_pipe[1]);
175 
176       execlp(ACO_TEST_PYTHON_BIN, ACO_TEST_PYTHON_BIN, ACO_TEST_SOURCE_DIR "/check_output.py",
177              NULL);
178       fprintf(stderr, "%s: execlp() failed: %s\n", argv[0], strerror(errno));
179       return 99;
180    } else {
181       /* Feed input data to the Python process. Writing large streams to
182        * stdin will block eventually, so this is done in a forked process
183        * to let the test checker process chunks of data as they arrive */
184       write(stdin_pipe[1], checker_stdin_data, checker_stdin_size);
185       close(stdin_pipe[0]);
186       close(stdin_pipe[1]);
187       _exit(0);
188    }
189 }
190 
191 bool
match_test(std::string name,std::string pattern)192 match_test(std::string name, std::string pattern)
193 {
194    if (name.length() < pattern.length())
195       return false;
196    if (pattern.back() == '.')
197       name.resize(pattern.length());
198    return name == pattern;
199 }
200 
201 int
main(int argc,char ** argv)202 main(int argc, char** argv)
203 {
204    int print_help = 0;
205    int do_list = 0;
206    int do_check = 1;
207    const struct option opts[] = {{"help", no_argument, &print_help, 1},
208                                  {"list", no_argument, &do_list, 1},
209                                  {"no-check", no_argument, &do_check, 0},
210                                  {NULL, 0, NULL, 0}};
211 
212    int c;
213    while ((c = getopt_long(argc, argv, "hl", opts, NULL)) != -1) {
214       switch (c) {
215       case 'h': print_help = 1; break;
216       case 'l': do_list = 1; break;
217       case 0: break;
218       case '?':
219       default: fprintf(stderr, "%s: Invalid argument\n", argv[0]); return 99;
220       }
221    }
222 
223    if (print_help) {
224       fprintf(stderr, help_message, argv[0]);
225       return 99;
226    }
227 
228    if (!tests)
229       tests = new std::map<std::string, TestDef>;
230 
231    if (do_list) {
232       for (auto test : *tests)
233          printf("%s\n", test.first.c_str());
234       return 99;
235    }
236 
237    std::vector<std::pair<std::string, std::string>> names;
238    for (int i = optind; i < argc; i++) {
239       std::string name = argv[i];
240       std::string variant;
241       size_t pos = name.find('/');
242       if (pos != std::string::npos) {
243          variant = name.substr(pos + 1);
244          name = name.substr(0, pos);
245       }
246       names.emplace_back(std::pair<std::string, std::string>(name, variant));
247    }
248 
249    if (do_check)
250       checker_stdin = open_memstream(&checker_stdin_data, &checker_stdin_size);
251 
252    LLVMInitializeAMDGPUTargetInfo();
253    LLVMInitializeAMDGPUTarget();
254    LLVMInitializeAMDGPUTargetMC();
255    LLVMInitializeAMDGPUDisassembler();
256 
257    aco::init();
258 
259    for (auto pair : *tests) {
260       bool found = names.empty();
261       bool all_variants = names.empty();
262       std::set<std::string> variants;
263       for (const std::pair<std::string, std::string>& name : names) {
264          if (match_test(pair.first, name.first)) {
265             found = true;
266             if (name.second.empty())
267                all_variants = true;
268             else
269                variants.insert(name.second);
270          }
271       }
272 
273       if (found) {
274          variant_filter = all_variants ? NULL : &variants;
275          printf("Running '%s'\n", pair.first.c_str());
276          run_test(pair.second);
277       }
278    }
279    if (!tests_written) {
280       fprintf(stderr, "%s: No matching tests\n", argv[0]);
281       return 99;
282    }
283 
284    if (checker_stdin) {
285       printf("\n");
286       return check_output(argv);
287    } else {
288       printf("Tests ran\n");
289       return 99;
290    }
291 }
292