xref: /aosp_15_r20/external/OpenCL-CTS/test_common/harness/testHarness.cpp (revision 6467f958c7de8070b317fc65bcb0f6472e388d82)
1 //
2 // Copyright (c) 2017-2019 The Khronos Group Inc.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //    http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 #include "testHarness.h"
17 #include "compat.h"
18 #include <algorithm>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <cassert>
23 #include <deque>
24 #include <mutex>
25 #include <stdexcept>
26 #include <thread>
27 #include <vector>
28 #include "errorHelpers.h"
29 #include "kernelHelpers.h"
30 #include "fpcontrol.h"
31 #include "typeWrappers.h"
32 #include "imageHelpers.h"
33 #include "parseParameters.h"
34 
35 #if !defined(_WIN32)
36 #include <sys/utsname.h>
37 #include <unistd.h>
38 #endif
39 
40 #if defined(__APPLE__)
41 #include <sys/sysctl.h>
42 #endif
43 
44 #include <time.h>
45 
46 #if !defined(__APPLE__)
47 #include <CL/cl.h>
48 #endif
49 
50 int gTestsPassed = 0;
51 int gTestsFailed = 0;
52 int gFailCount;
53 int gTestCount;
54 cl_uint gRandomSeed = 0;
55 cl_uint gReSeed = 0;
56 
57 int gFlushDenormsToZero = 0;
58 int gInfNanSupport = 1;
59 int gIsEmbedded = 0;
60 int gHasLong = 1;
61 bool gCoreILProgram = true;
62 
63 #define DEFAULT_NUM_ELEMENTS 0x4000
64 
saveResultsToJson(const char * suiteName,test_definition testList[],unsigned char selectedTestList[],test_status resultTestList[],int testNum)65 static int saveResultsToJson(const char *suiteName, test_definition testList[],
66                              unsigned char selectedTestList[],
67                              test_status resultTestList[], int testNum)
68 {
69     char *fileName = getenv("CL_CONFORMANCE_RESULTS_FILENAME");
70     if (fileName == nullptr)
71     {
72         return EXIT_SUCCESS;
73     }
74 
75     FILE *file = fopen(fileName, "w");
76     if (NULL == file)
77     {
78         log_error("ERROR: Failed to open '%s' for writing results.\n",
79                   fileName);
80         return EXIT_FAILURE;
81     }
82 
83     const char *save_map[] = { "success", "failure" };
84     const char *result_map[] = { "pass", "fail", "skip" };
85     const char *linebreak[] = { "", ",\n" };
86     int add_linebreak = 0;
87 
88     fprintf(file, "{\n");
89     fprintf(file, "\t\"cmd\": \"%s\",\n", suiteName);
90     fprintf(file, "\t\"results\": {\n");
91 
92     for (int i = 0; i < testNum; ++i)
93     {
94         if (selectedTestList[i])
95         {
96             fprintf(file, "%s\t\t\"%s\": \"%s\"", linebreak[add_linebreak],
97                     testList[i].name, result_map[(int)resultTestList[i]]);
98             add_linebreak = 1;
99         }
100     }
101     fprintf(file, "\n");
102 
103     fprintf(file, "\t}\n");
104     fprintf(file, "}\n");
105 
106     int ret = fclose(file) ? EXIT_FAILURE : EXIT_SUCCESS;
107 
108     log_info("Saving results to %s: %s!\n", fileName, save_map[ret]);
109 
110     return ret;
111 }
112 
runTestHarness(int argc,const char * argv[],int testNum,test_definition testList[],int forceNoContextCreation,cl_command_queue_properties queueProps)113 int runTestHarness(int argc, const char *argv[], int testNum,
114                    test_definition testList[], int forceNoContextCreation,
115                    cl_command_queue_properties queueProps)
116 {
117     return runTestHarnessWithCheck(argc, argv, testNum, testList,
118                                    forceNoContextCreation, queueProps, NULL);
119 }
120 
suite_did_not_pass_init(const char * suiteName,test_status status,int testNum,test_definition testList[])121 int suite_did_not_pass_init(const char *suiteName, test_status status,
122                             int testNum, test_definition testList[])
123 {
124     std::vector<unsigned char> selectedTestList(testNum, 1);
125     std::vector<test_status> resultTestList(testNum, status);
126 
127     int ret = saveResultsToJson(suiteName, testList, selectedTestList.data(),
128                                 resultTestList.data(), testNum);
129 
130     log_info("Test %s while initialization\n",
131              status == TEST_SKIP ? "skipped" : "failed");
132     log_info("%s %d of %d tests.\n", status == TEST_SKIP ? "SKIPPED" : "FAILED",
133              testNum, testNum);
134 
135     if (ret != EXIT_SUCCESS)
136     {
137         return ret;
138     }
139 
140     return status == TEST_SKIP ? EXIT_SUCCESS : EXIT_FAILURE;
141 }
142 
version_expected_info(const char * test_name,const char * api_name,const char * expected_version,const char * device_version)143 void version_expected_info(const char *test_name, const char *api_name,
144                            const char *expected_version,
145                            const char *device_version)
146 {
147     log_info("%s skipped (requires at least %s version %s, but the device "
148              "reports %s version %s)\n",
149              test_name, api_name, expected_version, api_name, device_version);
150 }
runTestHarnessWithCheck(int argc,const char * argv[],int testNum,test_definition testList[],int forceNoContextCreation,cl_command_queue_properties queueProps,DeviceCheckFn deviceCheckFn)151 int runTestHarnessWithCheck(int argc, const char *argv[], int testNum,
152                             test_definition testList[],
153                             int forceNoContextCreation,
154                             cl_command_queue_properties queueProps,
155                             DeviceCheckFn deviceCheckFn)
156 {
157     test_start();
158 
159     cl_device_type device_type = CL_DEVICE_TYPE_DEFAULT;
160     cl_uint num_platforms = 0;
161     cl_platform_id *platforms;
162     cl_device_id device;
163     int num_elements = DEFAULT_NUM_ELEMENTS;
164     cl_uint num_devices = 0;
165     cl_device_id *devices = NULL;
166     cl_uint choosen_device_index = 0;
167     cl_uint choosen_platform_index = 0;
168 
169     int err, ret;
170     char *endPtr;
171     int based_on_env_var = 0;
172 
173 
174     /* Check for environment variable to set device type */
175     char *env_mode = getenv("CL_DEVICE_TYPE");
176     if (env_mode != NULL)
177     {
178         based_on_env_var = 1;
179         if (strcmp(env_mode, "gpu") == 0
180             || strcmp(env_mode, "CL_DEVICE_TYPE_GPU") == 0)
181             device_type = CL_DEVICE_TYPE_GPU;
182         else if (strcmp(env_mode, "cpu") == 0
183                  || strcmp(env_mode, "CL_DEVICE_TYPE_CPU") == 0)
184             device_type = CL_DEVICE_TYPE_CPU;
185         else if (strcmp(env_mode, "accelerator") == 0
186                  || strcmp(env_mode, "CL_DEVICE_TYPE_ACCELERATOR") == 0)
187             device_type = CL_DEVICE_TYPE_ACCELERATOR;
188         else if (strcmp(env_mode, "default") == 0
189                  || strcmp(env_mode, "CL_DEVICE_TYPE_DEFAULT") == 0)
190             device_type = CL_DEVICE_TYPE_DEFAULT;
191         else
192         {
193             log_error("Unknown CL_DEVICE_TYPE env variable setting: "
194                       "%s.\nAborting...\n",
195                       env_mode);
196             abort();
197         }
198     }
199 
200 #if defined(__APPLE__)
201     {
202         // report on any unusual library search path indirection
203         char *libSearchPath = getenv("DYLD_LIBRARY_PATH");
204         if (libSearchPath)
205             log_info("*** DYLD_LIBRARY_PATH = \"%s\"\n", libSearchPath);
206 
207         // report on any unusual framework search path indirection
208         char *frameworkSearchPath = getenv("DYLD_FRAMEWORK_PATH");
209         if (libSearchPath)
210             log_info("*** DYLD_FRAMEWORK_PATH = \"%s\"\n", frameworkSearchPath);
211     }
212 #endif
213 
214     env_mode = getenv("CL_DEVICE_INDEX");
215     if (env_mode != NULL)
216     {
217         choosen_device_index = atoi(env_mode);
218     }
219 
220     env_mode = getenv("CL_PLATFORM_INDEX");
221     if (env_mode != NULL)
222     {
223         choosen_platform_index = atoi(env_mode);
224     }
225 
226     /* Process the command line arguments */
227 
228     argc = parseCustomParam(argc, argv);
229     if (argc == -1)
230     {
231         return EXIT_FAILURE;
232     }
233 
234     /* Special case: just list the tests */
235     if ((argc > 1)
236         && (!strcmp(argv[1], "-list") || !strcmp(argv[1], "-h")
237             || !strcmp(argv[1], "--help")))
238     {
239         char *fileName = getenv("CL_CONFORMANCE_RESULTS_FILENAME");
240 
241         log_info(
242             "Usage: %s [<test name>*] [pid<num>] [id<num>] [<device type>]\n",
243             argv[0]);
244         log_info("\t<test name>\tOne or more of: (wildcard character '*') "
245                  "(default *)\n");
246         log_info("\tpid<num>\tIndicates platform at index <num> should be used "
247                  "(default 0).\n");
248         log_info("\tid<num>\t\tIndicates device at index <num> should be used "
249                  "(default 0).\n");
250         log_info("\t<device_type>\tcpu|gpu|accelerator|<CL_DEVICE_TYPE_*> "
251                  "(default CL_DEVICE_TYPE_DEFAULT)\n");
252         log_info("\n");
253         log_info("\tNOTE: You may pass environment variable "
254                  "CL_CONFORMANCE_RESULTS_FILENAME (currently '%s')\n",
255                  fileName != NULL ? fileName : "<undefined>");
256         log_info("\t      to save results to JSON file.\n");
257 
258         log_info("\n");
259         log_info("Test names:\n");
260         for (int i = 0; i < testNum; i++)
261         {
262             log_info("\t%s\n", testList[i].name);
263         }
264         return EXIT_SUCCESS;
265     }
266 
267     /* How are we supposed to seed the random # generators? */
268     if (argc > 1 && strcmp(argv[argc - 1], "randomize") == 0)
269     {
270         gRandomSeed = (cl_uint)time(NULL);
271         log_info("Random seed: %u.\n", gRandomSeed);
272         gReSeed = 1;
273         argc--;
274     }
275     else
276     {
277         log_info(" Initializing random seed to 0.\n");
278     }
279 
280     /* Do we have an integer to specify the number of elements to pass to tests?
281      */
282     if (argc > 1)
283     {
284         ret = (int)strtol(argv[argc - 1], &endPtr, 10);
285         if (endPtr != argv[argc - 1] && *endPtr == 0)
286         {
287             /* By spec, this means the entire string was a valid integer, so we
288              * treat it as a num_elements spec */
289             /* (hence why we stored the result in ret first) */
290             num_elements = ret;
291             log_info("Testing with num_elements of %d\n", num_elements);
292             argc--;
293         }
294     }
295 
296     /* Do we have a CPU/GPU specification? */
297     if (argc > 1)
298     {
299         if (strcmp(argv[argc - 1], "gpu") == 0
300             || strcmp(argv[argc - 1], "CL_DEVICE_TYPE_GPU") == 0)
301         {
302             device_type = CL_DEVICE_TYPE_GPU;
303             argc--;
304         }
305         else if (strcmp(argv[argc - 1], "cpu") == 0
306                  || strcmp(argv[argc - 1], "CL_DEVICE_TYPE_CPU") == 0)
307         {
308             device_type = CL_DEVICE_TYPE_CPU;
309             argc--;
310         }
311         else if (strcmp(argv[argc - 1], "accelerator") == 0
312                  || strcmp(argv[argc - 1], "CL_DEVICE_TYPE_ACCELERATOR") == 0)
313         {
314             device_type = CL_DEVICE_TYPE_ACCELERATOR;
315             argc--;
316         }
317         else if (strcmp(argv[argc - 1], "CL_DEVICE_TYPE_DEFAULT") == 0)
318         {
319             device_type = CL_DEVICE_TYPE_DEFAULT;
320             argc--;
321         }
322     }
323 
324     /* Did we choose a specific device index? */
325     if (argc > 1)
326     {
327         if (strlen(argv[argc - 1]) >= 3 && argv[argc - 1][0] == 'i'
328             && argv[argc - 1][1] == 'd')
329         {
330             choosen_device_index = atoi(&(argv[argc - 1][2]));
331             argc--;
332         }
333     }
334 
335     /* Did we choose a specific platform index? */
336     if (argc > 1)
337     {
338         if (strlen(argv[argc - 1]) >= 3 && argv[argc - 1][0] == 'p'
339             && argv[argc - 1][1] == 'i' && argv[argc - 1][2] == 'd')
340         {
341             choosen_platform_index = atoi(&(argv[argc - 1][3]));
342             argc--;
343         }
344     }
345 
346 
347     switch (device_type)
348     {
349         case CL_DEVICE_TYPE_GPU: log_info("Requesting GPU device "); break;
350         case CL_DEVICE_TYPE_CPU: log_info("Requesting CPU device "); break;
351         case CL_DEVICE_TYPE_ACCELERATOR:
352             log_info("Requesting Accelerator device ");
353             break;
354         case CL_DEVICE_TYPE_DEFAULT:
355             log_info("Requesting Default device ");
356             break;
357         default: log_error("Requesting unknown device "); return EXIT_FAILURE;
358     }
359     log_info(based_on_env_var ? "based on environment variable "
360                               : "based on command line ");
361     log_info("for platform index %d and device index %d\n",
362              choosen_platform_index, choosen_device_index);
363 
364 #if defined(__APPLE__)
365 #if defined(__i386__) || defined(__x86_64__)
366 #define kHasSSE3 0x00000008
367 #define kHasSupplementalSSE3 0x00000100
368 #define kHasSSE4_1 0x00000400
369 #define kHasSSE4_2 0x00000800
370     /* check our environment for a hint to disable SSE variants */
371     {
372         const char *env = getenv("CL_MAX_SSE");
373         if (env)
374         {
375             extern int _cpu_capabilities;
376             int mask = 0;
377             if (0 == strcasecmp(env, "SSE4.1"))
378                 mask = kHasSSE4_2;
379             else if (0 == strcasecmp(env, "SSSE3"))
380                 mask = kHasSSE4_2 | kHasSSE4_1;
381             else if (0 == strcasecmp(env, "SSE3"))
382                 mask = kHasSSE4_2 | kHasSSE4_1 | kHasSupplementalSSE3;
383             else if (0 == strcasecmp(env, "SSE2"))
384                 mask =
385                     kHasSSE4_2 | kHasSSE4_1 | kHasSupplementalSSE3 | kHasSSE3;
386             else
387             {
388                 log_error("Error: Unknown CL_MAX_SSE setting: %s\n", env);
389                 return EXIT_FAILURE;
390             }
391 
392             log_info("*** Environment: CL_MAX_SSE = %s ***\n", env);
393             _cpu_capabilities &= ~mask;
394         }
395     }
396 #endif
397 #endif
398 
399     /* Get the platform */
400     err = clGetPlatformIDs(0, NULL, &num_platforms);
401     if (err)
402     {
403         print_error(err, "clGetPlatformIDs failed");
404         return EXIT_FAILURE;
405     }
406 
407     platforms =
408         (cl_platform_id *)malloc(num_platforms * sizeof(cl_platform_id));
409     if (!platforms || choosen_platform_index >= num_platforms)
410     {
411         log_error("platform index out of range -- choosen_platform_index (%d) "
412                   ">= num_platforms (%d)\n",
413                   choosen_platform_index, num_platforms);
414         return EXIT_FAILURE;
415     }
416     BufferOwningPtr<cl_platform_id> platformsBuf(platforms);
417 
418     err = clGetPlatformIDs(num_platforms, platforms, NULL);
419     if (err)
420     {
421         print_error(err, "clGetPlatformIDs failed");
422         return EXIT_FAILURE;
423     }
424 
425     /* Get the number of requested devices */
426     err = clGetDeviceIDs(platforms[choosen_platform_index], device_type, 0,
427                          NULL, &num_devices);
428     if (err)
429     {
430         print_error(err, "clGetDeviceIDs failed");
431         return EXIT_FAILURE;
432     }
433 
434     devices = (cl_device_id *)malloc(num_devices * sizeof(cl_device_id));
435     if (!devices || choosen_device_index >= num_devices)
436     {
437         log_error("device index out of range -- choosen_device_index (%d) >= "
438                   "num_devices (%d)\n",
439                   choosen_device_index, num_devices);
440         return EXIT_FAILURE;
441     }
442     BufferOwningPtr<cl_device_id> devicesBuf(devices);
443 
444 
445     /* Get the requested device */
446     err = clGetDeviceIDs(platforms[choosen_platform_index], device_type,
447                          num_devices, devices, NULL);
448     if (err)
449     {
450         print_error(err, "clGetDeviceIDs failed");
451         return EXIT_FAILURE;
452     }
453 
454     device = devices[choosen_device_index];
455 
456     err = clGetDeviceInfo(device, CL_DEVICE_TYPE, sizeof(gDeviceType),
457                           &gDeviceType, NULL);
458     if (err)
459     {
460         print_error(err, "Unable to get device type");
461         return TEST_FAIL;
462     }
463 
464     if (printDeviceHeader(device) != CL_SUCCESS)
465     {
466         return EXIT_FAILURE;
467     }
468 
469     cl_device_fp_config fpconfig = 0;
470     err = clGetDeviceInfo(device, CL_DEVICE_SINGLE_FP_CONFIG, sizeof(fpconfig),
471                           &fpconfig, NULL);
472     if (err)
473     {
474         print_error(err,
475                     "clGetDeviceInfo for CL_DEVICE_SINGLE_FP_CONFIG failed");
476         return EXIT_FAILURE;
477     }
478 
479     gFlushDenormsToZero = (0 == (fpconfig & CL_FP_DENORM));
480     log_info("Supports single precision denormals: %s\n",
481              gFlushDenormsToZero ? "NO" : "YES");
482     log_info("sizeof( void*) = %d  (host)\n", (int)sizeof(void *));
483 
484     // detect whether profile of the device is embedded
485     char profile[1024] = "";
486     err = clGetDeviceInfo(device, CL_DEVICE_PROFILE, sizeof(profile), profile,
487                           NULL);
488     if (err)
489     {
490         print_error(err, "clGetDeviceInfo for CL_DEVICE_PROFILE failed\n");
491         return EXIT_FAILURE;
492     }
493     gIsEmbedded = NULL != strstr(profile, "EMBEDDED_PROFILE");
494 
495     // detect the floating point capabilities
496     cl_device_fp_config floatCapabilities = 0;
497     err = clGetDeviceInfo(device, CL_DEVICE_SINGLE_FP_CONFIG,
498                           sizeof(floatCapabilities), &floatCapabilities, NULL);
499     if (err)
500     {
501         print_error(err,
502                     "clGetDeviceInfo for CL_DEVICE_SINGLE_FP_CONFIG failed\n");
503         return EXIT_FAILURE;
504     }
505 
506     // Check for problems that only embedded will have
507     if (gIsEmbedded)
508     {
509         // If the device is embedded, we need to detect if the device supports
510         // Infinity and NaN
511         if ((floatCapabilities & CL_FP_INF_NAN) == 0) gInfNanSupport = 0;
512 
513         // check the extensions list to see if ulong and long are supported
514         if (!is_extension_available(device, "cles_khr_int64")) gHasLong = 0;
515     }
516 
517     cl_uint device_address_bits = 0;
518     if ((err = clGetDeviceInfo(device, CL_DEVICE_ADDRESS_BITS,
519                                sizeof(device_address_bits),
520                                &device_address_bits, NULL)))
521     {
522         print_error(err, "Unable to obtain device address bits");
523         return EXIT_FAILURE;
524     }
525     if (device_address_bits)
526         log_info("sizeof( void*) = %d  (device)\n", device_address_bits / 8);
527     else
528     {
529         log_error("Invalid device address bit size returned by device.\n");
530         return EXIT_FAILURE;
531     }
532     const char *suiteName = argv[0];
533     if (gCompilationMode == kSpir_v)
534     {
535         test_status spirv_readiness = check_spirv_compilation_readiness(device);
536         if (spirv_readiness != TEST_PASS)
537         {
538             switch (spirv_readiness)
539             {
540                 case TEST_PASS: break;
541                 case TEST_FAIL:
542                     return suite_did_not_pass_init(suiteName, TEST_FAIL,
543                                                    testNum, testList);
544                 case TEST_SKIP:
545                     return suite_did_not_pass_init(suiteName, TEST_SKIP,
546                                                    testNum, testList);
547                 case TEST_SKIPPED_ITSELF:
548                     return suite_did_not_pass_init(suiteName, TEST_SKIP,
549                                                    testNum, testList);
550             }
551         }
552     }
553 
554     /* If we have a device checking function, run it */
555     if ((deviceCheckFn != NULL))
556     {
557         test_status status = deviceCheckFn(device);
558         switch (status)
559         {
560             case TEST_PASS: break;
561             case TEST_FAIL:
562                 return suite_did_not_pass_init(suiteName, TEST_FAIL, testNum,
563                                                testList);
564             case TEST_SKIP:
565                 return suite_did_not_pass_init(suiteName, TEST_SKIP, testNum,
566                                                testList);
567             case TEST_SKIPPED_ITSELF:
568                 return suite_did_not_pass_init(suiteName, TEST_SKIP, testNum,
569                                                testList);
570         }
571     }
572 
573     if (num_elements <= 0) num_elements = DEFAULT_NUM_ELEMENTS;
574 
575         // On most platforms which support denorm, default is FTZ off. However,
576         // on some hardware where the reference is computed, default might be
577         // flush denorms to zero e.g. arm. This creates issues in result
578         // verification. Since spec allows the implementation to either flush or
579         // not flush denorms to zero, an implementation may choose not be flush
580         // i.e. return denorm result whereas reference result may be zero
581         // (flushed denorm). Hence we need to disable denorm flushing on host
582         // side where reference is being computed to make sure we get
583         // non-flushed reference result. If implementation returns flushed
584         // result, we correctly take care of that in verification code.
585 #if defined(__APPLE__) && defined(__arm__)
586     FPU_mode_type oldMode;
587     DisableFTZ(&oldMode);
588 #endif
589     extern unsigned gNumWorkerThreads;
590     test_harness_config config = { forceNoContextCreation, num_elements,
591                                    queueProps, gNumWorkerThreads };
592 
593     int error = parseAndCallCommandLineTests(argc, argv, device, testNum,
594                                              testList, config);
595 
596 #if defined(__APPLE__) && defined(__arm__)
597     // Restore the old FP mode before leaving.
598     RestoreFPState(&oldMode);
599 #endif
600 
601     return (error == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
602 }
603 
find_matching_tests(test_definition testList[],unsigned char selectedTestList[],int testNum,const char * argument,bool isWildcard)604 static int find_matching_tests(test_definition testList[],
605                                unsigned char selectedTestList[], int testNum,
606                                const char *argument, bool isWildcard)
607 {
608     int found_tests = 0;
609     size_t wildcard_length = strlen(argument) - 1; /* -1 for the asterisk */
610 
611     for (int i = 0; i < testNum; i++)
612     {
613         if ((!isWildcard && strcmp(testList[i].name, argument) == 0)
614             || (isWildcard
615                 && strncmp(testList[i].name, argument, wildcard_length) == 0))
616         {
617             if (selectedTestList[i])
618             {
619                 log_error("ERROR: Test '%s' has already been selected.\n",
620                           testList[i].name);
621                 return EXIT_FAILURE;
622             }
623             else if (testList[i].func == NULL)
624             {
625                 log_error("ERROR: Test '%s' is missing implementation.\n",
626                           testList[i].name);
627                 return EXIT_FAILURE;
628             }
629             else
630             {
631                 selectedTestList[i] = 1;
632                 found_tests = 1;
633                 if (!isWildcard)
634                 {
635                     break;
636                 }
637             }
638         }
639     }
640 
641     if (!found_tests)
642     {
643         log_error("ERROR: The argument '%s' did not match any test names.\n",
644                   argument);
645         return EXIT_FAILURE;
646     }
647 
648     return EXIT_SUCCESS;
649 }
650 
print_results(int failed,int count,const char * name)651 static void print_results(int failed, int count, const char *name)
652 {
653     if (count < failed)
654     {
655         count = failed;
656     }
657 
658     if (failed == 0)
659     {
660         if (count > 1)
661         {
662             log_info("PASSED %d of %d %ss.\n", count, count, name);
663         }
664         else
665         {
666             log_info("PASSED %s.\n", name);
667         }
668     }
669     else if (failed > 0)
670     {
671         if (count > 1)
672         {
673             log_error("FAILED %d of %d %ss.\n", failed, count, name);
674         }
675         else
676         {
677             log_error("FAILED %s.\n", name);
678         }
679     }
680 }
681 
parseAndCallCommandLineTests(int argc,const char * argv[],cl_device_id device,int testNum,test_definition testList[],const test_harness_config & config)682 int parseAndCallCommandLineTests(int argc, const char *argv[],
683                                  cl_device_id device, int testNum,
684                                  test_definition testList[],
685                                  const test_harness_config &config)
686 {
687     int ret = EXIT_SUCCESS;
688 
689     unsigned char *selectedTestList = (unsigned char *)calloc(testNum, 1);
690 
691     if (argc == 1)
692     {
693         /* No actual arguments, all tests will be run. */
694         memset(selectedTestList, 1, testNum);
695     }
696     else
697     {
698         for (int i = 1; i < argc; i++)
699         {
700             if (strchr(argv[i], '*') != NULL)
701             {
702                 ret = find_matching_tests(testList, selectedTestList, testNum,
703                                           argv[i], true);
704             }
705             else
706             {
707                 if (strcmp(argv[i], "all") == 0)
708                 {
709                     memset(selectedTestList, 1, testNum);
710                     break;
711                 }
712                 else
713                 {
714                     ret = find_matching_tests(testList, selectedTestList,
715                                               testNum, argv[i], false);
716                 }
717             }
718 
719             if (ret == EXIT_FAILURE)
720             {
721                 break;
722             }
723         }
724     }
725 
726     if (ret == EXIT_SUCCESS)
727     {
728         std::vector<test_status> resultTestList(testNum, TEST_PASS);
729 
730         callTestFunctions(testList, selectedTestList, resultTestList.data(),
731                           testNum, device, config);
732 
733         print_results(gFailCount, gTestCount, "sub-test");
734         print_results(gTestsFailed, gTestsFailed + gTestsPassed, "test");
735 
736         ret = saveResultsToJson(argv[0], testList, selectedTestList,
737                                 resultTestList.data(), testNum);
738 
739         if (std::any_of(resultTestList.begin(), resultTestList.end(),
740                         [](test_status result) {
741                             switch (result)
742                             {
743                                 case TEST_PASS:
744                                 case TEST_SKIP: return false;
745                                 case TEST_FAIL:
746                                 default: return true;
747                             };
748                         }))
749         {
750             ret = EXIT_FAILURE;
751         }
752     }
753 
754     free(selectedTestList);
755 
756     return ret;
757 }
758 
759 struct test_harness_state
760 {
761     test_definition *tests;
762     test_status *results;
763     cl_device_id device;
764     test_harness_config config;
765 };
766 
767 static std::deque<int> gTestQueue;
768 static std::mutex gTestStateMutex;
769 
test_function_runner(test_harness_state * state)770 void test_function_runner(test_harness_state *state)
771 {
772     int testID;
773     test_definition test;
774     while (true)
775     {
776         // Attempt to get a test
777         {
778             std::lock_guard<std::mutex> lock(gTestStateMutex);
779 
780             // The queue is empty, we're done
781             if (gTestQueue.size() == 0)
782             {
783                 return;
784             }
785 
786             // Get the test at the front of the queue
787             testID = gTestQueue.front();
788             gTestQueue.pop_front();
789             test = state->tests[testID];
790         }
791 
792         // Execute test
793         auto status =
794             callSingleTestFunction(test, state->device, state->config);
795 
796         // Store result
797         {
798             std::lock_guard<std::mutex> lock(gTestStateMutex);
799             state->results[testID] = status;
800         }
801     }
802 }
803 
callTestFunctions(test_definition testList[],unsigned char selectedTestList[],test_status resultTestList[],int testNum,cl_device_id deviceToUse,const test_harness_config & config)804 void callTestFunctions(test_definition testList[],
805                        unsigned char selectedTestList[],
806                        test_status resultTestList[], int testNum,
807                        cl_device_id deviceToUse,
808                        const test_harness_config &config)
809 {
810     // Execute tests serially
811     if (config.numWorkerThreads == 0)
812     {
813         for (int i = 0; i < testNum; ++i)
814         {
815             if (selectedTestList[i])
816             {
817                 resultTestList[i] =
818                     callSingleTestFunction(testList[i], deviceToUse, config);
819             }
820         }
821         // Execute tests in parallel with the specified number of worker threads
822     }
823     else
824     {
825         // Queue all tests that need to run
826         for (int i = 0; i < testNum; ++i)
827         {
828             if (selectedTestList[i])
829             {
830                 gTestQueue.push_back(i);
831             }
832         }
833 
834         // Spawn thread pool
835         std::vector<std::thread *> threads;
836         test_harness_state state = { testList, resultTestList, deviceToUse,
837                                      config };
838         for (unsigned i = 0; i < config.numWorkerThreads; i++)
839         {
840             log_info("Spawning worker thread %u\n", i);
841             threads.push_back(new std::thread(test_function_runner, &state));
842         }
843 
844         // Wait for all threads to complete
845         for (auto th : threads)
846         {
847             th->join();
848         }
849         assert(gTestQueue.size() == 0);
850     }
851 }
852 
notify_callback(const char * errinfo,const void * private_info,size_t cb,void * user_data)853 void CL_CALLBACK notify_callback(const char *errinfo, const void *private_info,
854                                  size_t cb, void *user_data)
855 {
856     log_info("%s\n", errinfo);
857 }
858 
859 // Actual function execution
callSingleTestFunction(test_definition test,cl_device_id deviceToUse,const test_harness_config & config)860 test_status callSingleTestFunction(test_definition test,
861                                    cl_device_id deviceToUse,
862                                    const test_harness_config &config)
863 {
864     test_status status;
865     cl_int error;
866     cl_context context = NULL;
867     cl_command_queue queue = NULL;
868 
869     log_info("%s...\n", test.name);
870     fflush(stdout);
871 
872     const Version device_version = get_device_cl_version(deviceToUse);
873     if (test.min_version > device_version)
874     {
875         version_expected_info(test.name, "OpenCL",
876                               test.min_version.to_string().c_str(),
877                               device_version.to_string().c_str());
878         return TEST_SKIP;
879     }
880 
881     if (!check_functions_for_offline_compiler(test.name))
882     {
883         log_info("Subtest %s tests is not supported in offline compiler "
884                  "execution path!\n",
885                  test.name);
886         return TEST_SKIP;
887     }
888 
889     /* Create a context to work with, unless we're told not to */
890     if (!config.forceNoContextCreation)
891     {
892         context = clCreateContext(NULL, 1, &deviceToUse, notify_callback, NULL,
893                                   &error);
894         if (!context)
895         {
896             print_error(error, "Unable to create testing context");
897             gFailCount++;
898             gTestsFailed++;
899             return TEST_FAIL;
900         }
901 
902         if (device_version < Version(2, 0))
903         {
904             queue = clCreateCommandQueue(context, deviceToUse,
905                                          config.queueProps, &error);
906         }
907         else
908         {
909             const cl_command_queue_properties cmd_queueProps =
910                 (config.queueProps) ? CL_QUEUE_PROPERTIES : 0;
911             cl_command_queue_properties queueCreateProps[] = {
912                 cmd_queueProps, config.queueProps, 0
913             };
914             queue = clCreateCommandQueueWithProperties(
915                 context, deviceToUse, &queueCreateProps[0], &error);
916         }
917 
918         if (queue == NULL)
919         {
920             print_error(error, "Unable to create testing command queue");
921             clReleaseContext(context);
922             gFailCount++;
923             gTestsFailed++;
924             return TEST_FAIL;
925         }
926     }
927 
928     /* Run the test and print the result */
929     if (test.func == NULL)
930     {
931         // Skip unimplemented test, can happen when all of the tests are
932         // selected
933         log_info("%s test currently not implemented\n", test.name);
934         status = TEST_SKIP;
935     }
936     else
937     {
938         int ret =
939             test.func(deviceToUse, context, queue, config.numElementsToUse);
940         if (ret == TEST_SKIPPED_ITSELF)
941         {
942             /* Tests can also let us know they're not supported by the
943              * implementation */
944             log_info("%s test not supported\n", test.name);
945             status = TEST_SKIP;
946         }
947         else
948         {
949             /* Print result */
950             if (ret == 0)
951             {
952                 log_info("%s passed\n", test.name);
953                 gTestsPassed++;
954                 status = TEST_PASS;
955             }
956             else
957             {
958                 log_error("%s FAILED\n", test.name);
959                 gTestsFailed++;
960                 status = TEST_FAIL;
961             }
962         }
963     }
964 
965     /* Release the context */
966     if (!config.forceNoContextCreation)
967     {
968         int error = clFinish(queue);
969         if (error)
970         {
971             log_error("clFinish failed: %s\n", IGetErrorString(error));
972             gFailCount++;
973             gTestsFailed++;
974             status = TEST_FAIL;
975         }
976         clReleaseCommandQueue(queue);
977         clReleaseContext(context);
978     }
979 
980     return status;
981 }
982 
983 #if !defined(__APPLE__)
memset_pattern4(void * dest,const void * src_pattern,size_t bytes)984 void memset_pattern4(void *dest, const void *src_pattern, size_t bytes)
985 {
986     uint32_t pat = ((uint32_t *)src_pattern)[0];
987     size_t count = bytes / 4;
988     size_t i;
989     uint32_t *d = (uint32_t *)dest;
990 
991     for (i = 0; i < count; i++) d[i] = pat;
992 
993     d += i;
994 
995     bytes &= 3;
996     if (bytes) memcpy(d, src_pattern, bytes);
997 }
998 #endif
999 
GetDeviceType(cl_device_id d)1000 cl_device_type GetDeviceType(cl_device_id d)
1001 {
1002     cl_device_type result = -1;
1003     cl_int err =
1004         clGetDeviceInfo(d, CL_DEVICE_TYPE, sizeof(result), &result, NULL);
1005     if (CL_SUCCESS != err)
1006         log_error("ERROR: Unable to get device type for device %p\n", d);
1007     return result;
1008 }
1009 
1010 
GetOpposingDevice(cl_device_id device)1011 cl_device_id GetOpposingDevice(cl_device_id device)
1012 {
1013     cl_int error;
1014     cl_device_id *otherDevices;
1015     cl_uint actualCount;
1016     cl_platform_id plat;
1017 
1018     // Get the platform of the device to use for getting a list of devices
1019     error =
1020         clGetDeviceInfo(device, CL_DEVICE_PLATFORM, sizeof(plat), &plat, NULL);
1021     if (error != CL_SUCCESS)
1022     {
1023         print_error(error, "Unable to get device's platform");
1024         return NULL;
1025     }
1026 
1027     // Get a list of all devices
1028     error = clGetDeviceIDs(plat, CL_DEVICE_TYPE_ALL, 0, NULL, &actualCount);
1029     if (error != CL_SUCCESS)
1030     {
1031         print_error(error, "Unable to get list of devices size");
1032         return NULL;
1033     }
1034     otherDevices = (cl_device_id *)malloc(actualCount * sizeof(cl_device_id));
1035     if (NULL == otherDevices)
1036     {
1037         print_error(error, "Unable to allocate list of other devices.");
1038         return NULL;
1039     }
1040     BufferOwningPtr<cl_device_id> otherDevicesBuf(otherDevices);
1041 
1042     error = clGetDeviceIDs(plat, CL_DEVICE_TYPE_ALL, actualCount, otherDevices,
1043                            NULL);
1044     if (error != CL_SUCCESS)
1045     {
1046         print_error(error, "Unable to get list of devices");
1047         return NULL;
1048     }
1049 
1050     if (actualCount == 1)
1051     {
1052         return device; // NULL means error, returning self means we couldn't
1053                        // find another one
1054     }
1055 
1056     // Loop and just find one that isn't the one we were given
1057     cl_uint i;
1058     for (i = 0; i < actualCount; i++)
1059     {
1060         if (otherDevices[i] != device)
1061         {
1062             cl_device_type newType;
1063             error = clGetDeviceInfo(otherDevices[i], CL_DEVICE_TYPE,
1064                                     sizeof(newType), &newType, NULL);
1065             if (error != CL_SUCCESS)
1066             {
1067                 print_error(error,
1068                             "Unable to get device type for other device");
1069                 return NULL;
1070             }
1071             cl_device_id result = otherDevices[i];
1072             return result;
1073         }
1074     }
1075 
1076     // Should never get here
1077     return NULL;
1078 }
1079 
get_device_cl_version(cl_device_id device)1080 Version get_device_cl_version(cl_device_id device)
1081 {
1082     size_t str_size;
1083     cl_int err = clGetDeviceInfo(device, CL_DEVICE_VERSION, 0, NULL, &str_size);
1084     ASSERT_SUCCESS(err, "clGetDeviceInfo");
1085 
1086     std::vector<char> str(str_size);
1087     err =
1088         clGetDeviceInfo(device, CL_DEVICE_VERSION, str_size, str.data(), NULL);
1089     ASSERT_SUCCESS(err, "clGetDeviceInfo");
1090 
1091     if (strstr(str.data(), "OpenCL 1.0") != NULL)
1092         return Version(1, 0);
1093     else if (strstr(str.data(), "OpenCL 1.1") != NULL)
1094         return Version(1, 1);
1095     else if (strstr(str.data(), "OpenCL 1.2") != NULL)
1096         return Version(1, 2);
1097     else if (strstr(str.data(), "OpenCL 2.0") != NULL)
1098         return Version(2, 0);
1099     else if (strstr(str.data(), "OpenCL 2.1") != NULL)
1100         return Version(2, 1);
1101     else if (strstr(str.data(), "OpenCL 2.2") != NULL)
1102         return Version(2, 2);
1103     else if (strstr(str.data(), "OpenCL 3.0") != NULL)
1104         return Version(3, 0);
1105 
1106     throw std::runtime_error(std::string("Unknown OpenCL version: ")
1107                              + str.data());
1108 }
1109 
check_device_spirv_version_reported(cl_device_id device)1110 bool check_device_spirv_version_reported(cl_device_id device)
1111 {
1112     size_t str_size;
1113     cl_int err;
1114     std::vector<char> str;
1115     if (gCoreILProgram)
1116     {
1117         err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION, 0, NULL, &str_size);
1118         if (err != CL_SUCCESS)
1119         {
1120             log_error(
1121                 "clGetDeviceInfo: cannot read CL_DEVICE_IL_VERSION size;");
1122             return false;
1123         }
1124 
1125         str.resize(str_size);
1126         err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION, str_size,
1127                               str.data(), NULL);
1128         if (err != CL_SUCCESS)
1129         {
1130             log_error(
1131                 "clGetDeviceInfo: cannot read CL_DEVICE_IL_VERSION value;");
1132             return false;
1133         }
1134     }
1135     else
1136     {
1137         cl_int err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION_KHR, 0, NULL,
1138                                      &str_size);
1139         if (err != CL_SUCCESS)
1140         {
1141             log_error(
1142                 "clGetDeviceInfo: cannot read CL_DEVICE_IL_VERSION_KHR size;");
1143             return false;
1144         }
1145 
1146         str.resize(str_size);
1147         err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION_KHR, str_size,
1148                               str.data(), NULL);
1149         if (err != CL_SUCCESS)
1150         {
1151             log_error(
1152                 "clGetDeviceInfo: cannot read CL_DEVICE_IL_VERSION_KHR value;");
1153             return false;
1154         }
1155     }
1156 
1157     if (strstr(str.data(), "SPIR-V") == NULL)
1158     {
1159         log_info("This device does not support SPIR-V offline compilation.\n");
1160         return false;
1161     }
1162     else
1163     {
1164         Version spirv_version = get_device_spirv_il_version(device);
1165         log_info("This device supports SPIR-V offline compilation. SPIR-V "
1166                  "version is %s\n",
1167                  spirv_version.to_string().c_str());
1168     }
1169     return true;
1170 }
1171 
get_device_spirv_il_version(cl_device_id device)1172 Version get_device_spirv_il_version(cl_device_id device)
1173 {
1174     size_t str_size;
1175     cl_int err;
1176     std::vector<char> str;
1177     if (gCoreILProgram)
1178     {
1179         err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION, 0, NULL, &str_size);
1180         ASSERT_SUCCESS(err, "clGetDeviceInfo");
1181 
1182         str.resize(str_size);
1183         err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION, str_size,
1184                               str.data(), NULL);
1185         ASSERT_SUCCESS(err, "clGetDeviceInfo");
1186     }
1187     else
1188     {
1189         err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION_KHR, 0, NULL,
1190                               &str_size);
1191         ASSERT_SUCCESS(err, "clGetDeviceInfo");
1192 
1193         str.resize(str_size);
1194         err = clGetDeviceInfo(device, CL_DEVICE_IL_VERSION_KHR, str_size,
1195                               str.data(), NULL);
1196         ASSERT_SUCCESS(err, "clGetDeviceInfo");
1197     }
1198 
1199     if (strstr(str.data(), "SPIR-V_1.0") != NULL)
1200         return Version(1, 0);
1201     else if (strstr(str.data(), "SPIR-V_1.1") != NULL)
1202         return Version(1, 1);
1203     else if (strstr(str.data(), "SPIR-V_1.2") != NULL)
1204         return Version(1, 2);
1205     else if (strstr(str.data(), "SPIR-V_1.3") != NULL)
1206         return Version(1, 3);
1207     else if (strstr(str.data(), "SPIR-V_1.4") != NULL)
1208         return Version(1, 4);
1209     else if (strstr(str.data(), "SPIR-V_1.5") != NULL)
1210         return Version(1, 5);
1211 
1212     throw std::runtime_error(std::string("Unknown SPIR-V version: ")
1213                              + str.data());
1214 }
1215 
check_spirv_compilation_readiness(cl_device_id device)1216 test_status check_spirv_compilation_readiness(cl_device_id device)
1217 {
1218     auto ocl_version = get_device_cl_version(device);
1219     auto ocl_expected_min_version = Version(2, 1);
1220 
1221     if (ocl_version < ocl_expected_min_version)
1222     {
1223         if (is_extension_available(device, "cl_khr_il_program"))
1224         {
1225             gCoreILProgram = false;
1226             bool spirv_supported = check_device_spirv_version_reported(device);
1227             if (spirv_supported == false)
1228             {
1229                 log_error("SPIR-V intermediate language not supported !!! "
1230                           "OpenCL %s requires support.\n",
1231                           ocl_version.to_string().c_str());
1232                 return TEST_FAIL;
1233             }
1234             else
1235             {
1236                 return TEST_PASS;
1237             }
1238         }
1239         else
1240         {
1241             log_error("SPIR-V intermediate language support on OpenCL version "
1242                       "%s requires cl_khr_il_program extension.\n",
1243                       ocl_version.to_string().c_str());
1244             return TEST_SKIP;
1245         }
1246     }
1247 
1248     bool spirv_supported = check_device_spirv_version_reported(device);
1249     if (ocl_version >= ocl_expected_min_version && ocl_version <= Version(2, 2))
1250     {
1251         if (spirv_supported == false)
1252         {
1253             log_error("SPIR-V intermediate language not supported !!! OpenCL "
1254                       "%s requires support.\n",
1255                       ocl_version.to_string().c_str());
1256             return TEST_FAIL;
1257         }
1258     }
1259 
1260     if (ocl_version > Version(2, 2))
1261     {
1262         if (spirv_supported == false)
1263         {
1264             log_info("SPIR-V intermediate language not supported in OpenCL %s. "
1265                      "Test skipped.\n",
1266                      ocl_version.to_string().c_str());
1267             return TEST_SKIP;
1268         }
1269     }
1270     return TEST_PASS;
1271 }
1272 
getPlatformFromDevice(cl_device_id deviceID)1273 cl_platform_id getPlatformFromDevice(cl_device_id deviceID)
1274 {
1275     cl_platform_id platform = nullptr;
1276     cl_int err = clGetDeviceInfo(deviceID, CL_DEVICE_PLATFORM, sizeof(platform),
1277                                  &platform, nullptr);
1278     ASSERT_SUCCESS(err, "clGetDeviceInfo");
1279     return platform;
1280 }
1281 
PrintArch(void)1282 void PrintArch(void)
1283 {
1284     vlog("sizeof( void*) = %zu\n", sizeof(void *));
1285 #if defined(__ppc__)
1286     vlog("ARCH:\tppc\n");
1287 #elif defined(__ppc64__)
1288     vlog("ARCH:\tppc64\n");
1289 #elif defined(__PPC__)
1290     vlog("ARCH:\tppc\n");
1291 #elif defined(__i386__)
1292     vlog("ARCH:\ti386\n");
1293 #elif defined(__x86_64__)
1294     vlog("ARCH:\tx86_64\n");
1295 #elif defined(__arm__)
1296     vlog("ARCH:\tarm\n");
1297 #elif defined(__aarch64__)
1298     vlog("ARCH:\taarch64\n");
1299 #elif defined(_WIN32)
1300     vlog("ARCH:\tWindows\n");
1301 #else
1302 #error unknown arch
1303 #endif
1304 
1305 #if defined(__APPLE__)
1306 
1307     int type = 0;
1308     size_t typeSize = sizeof(type);
1309     sysctlbyname("hw.cputype", &type, &typeSize, NULL, 0);
1310     vlog("cpu type:\t%d\n", type);
1311     typeSize = sizeof(type);
1312     sysctlbyname("hw.cpusubtype", &type, &typeSize, NULL, 0);
1313     vlog("cpu subtype:\t%d\n", type);
1314 
1315 #elif defined(__linux__)
1316     struct utsname buffer;
1317 
1318     if (uname(&buffer) != 0)
1319     {
1320         vlog("uname error");
1321     }
1322     else
1323     {
1324         vlog("system name = %s\n", buffer.sysname);
1325         vlog("node name   = %s\n", buffer.nodename);
1326         vlog("release     = %s\n", buffer.release);
1327         vlog("version     = %s\n", buffer.version);
1328         vlog("machine     = %s\n", buffer.machine);
1329     }
1330 #endif
1331 }
1332