/* * Copyright (c) Advanced Micro Devices, Inc., or its affiliates. * * SPDX-License-Identifier: MIT */ ///////////////////////////////////////////////////////////////////////////// #include #ifdef _WIN32 #include #include "Window.h" typedef HMODULE ModuleHandle; #endif ///////////////////////////////////////////////////////////////////////////// #ifdef __linux__ #include typedef void* ModuleHandle; #endif ///////////////////////////////////////////////////////////////////////////// #include "BaseTestImp.h" #include "Module.h" #include "OCLLog.h" #include "OCLTest.h" #include "OCLTestImp.h" #include "OCLTestList.h" #include "OCLWrapper.h" #include "Timer.h" #include "Worker.h" #include "getopt.h" #include "oclsysinfo.h" #include "pfm.h" //! Including OCLutilities Thread utility #include "OCL/Thread.h" //! Lock that needs to be obtained to access the global //! module variable static OCLutil::Lock moduleLock; #include #include #include #include #include #include ///////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 static LONG WINAPI xFilter(LPEXCEPTION_POINTERS xEP); void serviceStubCall(); #endif #define MAX_DEVICES 16 #undef CHECK_RESULT #define CHECK_RESULT(test, msg) \ if ((test)) { \ printf("\n%s\n", msg); \ exit(1); \ } //! Declaration of a function that find devices of a specific type for the //! chosen platform int findAdapters(unsigned int platformIdx, bool useCPU, cl_platform_id*); //! class App that is used to run the tests on the system class App { public: static bool m_reRunFailed; static const char* m_svcMsg; //! Constructor for App App(unsigned int platform) : m_list(false), m_console(true), m_useCPU(false), m_dump(false), m_perflab(false), m_noSysInfoPrint(false), m_numItr(1), mp_testOrder(NULL), m_rndOrder(false), m_spawned(0), m_threads(1), m_runthread(0), m_width(512), m_height(512), m_window(0), m_platform(platform) { // initialize OCLWrapper reference // m_workers = Set of worker objects that are used to run a subtest from a // module for (unsigned int i = 0; i < 256; i++) m_workers[i] = 0; // Setting the number of devices /* * Force caltst to use 1 thread at a time in Windows * only contextual calls are thread safe currently */ m_numDevices = findAdapters(m_platform, m_useCPU, &mpform_id); m_wrapper = new OCLWrapper(mpform_id); // m_numDevices = 1; // Report structure used to store the results of the tests #if 0 testReport = (Report **)malloc(sizeof(Report *) * m_numDevices); for(unsigned int i = 0; i < m_numDevices; i++) { testReport[i] = new Report; } #else testReport = (Report**)malloc(sizeof(Report*)); testReport[0] = new Report; #endif } //! Destructor for App ~App() { // Deleting the Worker objects for (unsigned int i = 0; i < 256; i++) { if (m_workers[i]) { delete m_workers[i]; m_workers[i] = 0; } } // Deleting the report structures // for(unsigned int i = 0; i < m_numDevices; i++) for (unsigned int i = 0; i < 1; i++) { delete testReport[i]; } free(testReport); m_wrapper->clUnloadPlatformAMD(mpform_id); delete m_wrapper; } //! Function used to create a worker object corresponding to a subtest in a //! module void SetWorker(unsigned int index, OCLWrapper* wrapper, Module* module, TestMethod run, unsigned int id, unsigned int subtest, unsigned int test, bool dump, bool view, bool useCPU, void* window, unsigned int x, unsigned int y, bool perflab, unsigned int deviceId, unsigned int platform) { if (index >= 256) return; if (m_workers[index]) delete m_workers[index]; m_workers[index] = new Worker(wrapper, module, run, id, subtest, test, dump, view, useCPU, window, x, y, perflab, deviceId, platform); assert(m_workers[index] != 0); // oclTestLog(OCLTEST_LOG_ALWAYS, "Worker Device Id = %d\n", // m_workers[index]->getDeviceId()); } //! Function to return the 'index'th m_workers Worker* GetWorker(unsigned int index) { if (index >= 256) return 0; return m_workers[index]; } //! Create a thread to run the subtest void AddThread(unsigned int workerindex, unsigned int usage) { Worker* worker = GetWorker(workerindex); if (worker == 0) { return; } // usage = Whether to use threads or not if (usage != 0) { // Creating a thread // getTestMethod = runSubTest here // which takes a Worker object as an argument m_pool[workerindex].create(worker->getTestMethod(), (void*)(worker)); m_spawned++; } else { // Same as above without using threads TestMethod run = worker->getTestMethod(); if (run) { run(worker); UpdateTestReport(workerindex, worker->getResult()); } } return; } //! Function which waits for all threads to execute and also updates the //! report void WaitAllThreads() { for (unsigned int w = 0; w < m_spawned; w++) { m_pool[w].join(); UpdateTestReport(w, m_workers[w]->getResult()); } m_spawned = 0; } //! Function to add a worker thread so as to run a subtest of a module //! @param run = runSubtest function //! @param index = index of the module in m_modules //! @param subtest = the subtest number to run //! @param usage = whether to use threads or not //! @param test = The test in the module to be executed void AddWorkerThread(unsigned int index, unsigned int subtest, unsigned int test, unsigned int usage, TestMethod run) { if (m_spawned > m_threads) { WaitAllThreads(); } // Creating a worker thread for each device #if 0 for(unsigned int i = 0; i < m_numDevices; i++) { SetWorker(i, m_wrapper, &m_modules[index], run, m_spawned, subtest, test, m_dump, !m_console, m_useCPU, m_window, m_width, m_height, m_perflab, i, m_platform); } #else for (unsigned int i = 0; i < 1; i++) { SetWorker(i, m_wrapper, &m_modules[index], run, m_spawned, subtest, test, m_dump, !m_console, m_useCPU, m_window, m_width, m_height, m_perflab, m_deviceId, m_platform); } #endif // Creating and executing a thread for each device // for(unsigned int i = 0; i < m_numDevices; i++) for (unsigned int i = 0; i < 1; i++) { AddThread(i, usage); } } void printOCLinfo(void); //! Function to process the commandline arguments void CommandLine(unsigned int argc, char** argv); //! Function to scan for the different tests in the module void ScanForTests(); //! Function to run all the specified tests. //! \return true if all requested tests are executed and pass successfully; //! false if any test fails, if no tests are executed, or when //! running in list-only mode. bool RunAllTests(); //! Free memory void CleanUp(); //! Function to set the order in which test are executed. void SetTestRunOrder(int); //! Function to print the test order void PrintTestOrder(int); //! Function to get the number of iterations. int GetNumItr(void) { return m_numItr; } private: typedef std::vector TestIndexList; typedef std::vector StringList; void AddToList(StringList& strlist, const char* str); void LoadList(StringList& strlist, const char* filename); bool TestInList(StringList& strlist, const char* testname); //! Array storing the report for each device Report** testReport; //! Function to update the result of each device void UpdateTestReport(int index, TestResult* result) { if (result != NULL) { if (result->passed) { if (testReport[index]->max->value < result->value) { testReport[index]->max->value = result->value; testReport[index]->max->resultString = result->resultString; } if (testReport[index]->min->value > result->value) { testReport[index]->min->value = result->value; testReport[index]->min->resultString = result->resultString; } } else { testReport[index]->numFailedTests++; testReport[index]->success = false; } } else { testReport[index]->numFailedTests++; testReport[index]->success = false; } } //! Functions used to find the range of the tests to be run void GetTestIndexList(TestIndexList& testIndices, StringList& testList, const char* szModuleTestname, int maxIndex); void PruneTestIndexList(TestIndexList& testIndices, TestIndexList& avoidIndices, TestIndexList& erasedIndices); StringList m_paths; StringList m_tests; StringList m_avoid; std::vector m_modules; bool m_list; bool m_console; bool m_useCPU; bool m_dump; bool m_perflab; bool m_noSysInfoPrint; int m_numItr; int* mp_testOrder; bool m_rndOrder; //! m_pool = Various threads created to execute tests on multiple devices OCLutil::Thread m_pool[256]; Worker* m_workers[256]; //! Number of threads spawned unsigned int m_spawned; //! Upper limit on the number of threads that can be spawned unsigned int m_threads; unsigned int m_runthread; unsigned int m_width; unsigned int m_height; void* m_window; //! which index/platform id from the platforms vector returned by //! cl::Platform::get we should run on unsigned int m_platform; cl_platform_id mpform_id; //! Number of devices on the system unsigned int m_numDevices; // //! Device ID to use on the system unsigned int m_deviceId; // OCLWrapper reference OCLWrapper* m_wrapper; }; void App::printOCLinfo(void) { std::string calinfo; if (!m_noSysInfoPrint) { oclSysInfo(calinfo, m_useCPU, m_deviceId, m_platform); oclTestLog(OCLTEST_LOG_ALWAYS, calinfo.c_str()); } } /*----------------------------------------------------- Function to randomize the order in which tests are executed -------------------------------------------------------*/ #ifdef _WIN32 #include #endif // void App::SetTestRunOrder(int test_count) void App::SetTestRunOrder(int mod_index) { assert(mp_testOrder != NULL); unsigned int test_count = m_modules[mod_index].get_count(); StringList uniqueTests; for (unsigned int i = 0; i < m_tests.size(); ++i) { // see if the tests are being run using indices size_t nFirstBracket = m_tests[i].find("["); // set the test name std::string szTestName = m_tests[i]; // order of execution is set based on base name so get the base name if (nFirstBracket != std::string::npos) szTestName = szTestName.substr(0, nFirstBracket); bool bTestExists = false; for (unsigned int j = 0; j < uniqueTests.size(); ++j) { if (strcmp(szTestName.c_str(), uniqueTests[j].c_str()) == 0) { bTestExists = true; break; } } if (!bTestExists) { AddToList(uniqueTests, szTestName.c_str()); } } for (unsigned int i = 0; i < test_count && i < uniqueTests.size(); i++) { for (unsigned int j = 0; j < test_count; j++) { unsigned int index = i; // add all the prev test indices for (int k = 0; k < mod_index; k++) index += m_modules[k].get_count(); std::string szTestName = uniqueTests[index]; if (strcmp(szTestName.c_str(), m_modules[mod_index].get_name(j)) == 0) { mp_testOrder[i] = j; break; } } } if (m_rndOrder) { srand((unsigned int)time(NULL)); for (unsigned int i = 0; i < test_count; i++) { // find two random indices int index1 = (int)((float)test_count * (rand() / (RAND_MAX + 1.0))); int index2 = (int)((float)test_count * (rand() / (RAND_MAX + 1.0))); // swap the data int tmp = mp_testOrder[index1]; mp_testOrder[index1] = mp_testOrder[index2]; mp_testOrder[index2] = tmp; } } } ///////////////////////////////////////////////////////////////////////////// // Process device string. Returns true if there is a primary ATI Radeon device // adapter, false otherwise static bool procDevString(const char* devString) { // Search for the string "Radeon" inside the device string if (strstr(devString, "Radeon") || strstr(devString, "R600") || strstr(devString, "RV630") || strstr(devString, "RV670") || (strstr(devString, "Stream") && strstr(devString, "Processor"))) { // Ignore if the device is a secondary device, i.e., not an adapter if (strstr(devString, "Secondary")) { return false; } } else { return false; } return true; } //! //! Function to find the total number of adapters on the system //! int findAdapters(unsigned int platformIdx, bool useCPU, cl_platform_id* mpform) { unsigned int numOfAdapters = 0; cl_int error = 0; cl_uint numPlatforms = 0; error = clGetPlatformIDs(0, NULL, &numPlatforms); CHECK_RESULT((error != CL_SUCCESS), "clGetPlatformIDs failed"); CHECK_RESULT((platformIdx >= numPlatforms), "Invalid platform"); cl_platform_id* platforms = new cl_platform_id[numPlatforms]; error = clGetPlatformIDs(numPlatforms, platforms, NULL); CHECK_RESULT(error != CL_SUCCESS, "clGetPlatformIDs failed"); cl_platform_id platform = 0; platform = platforms[platformIdx]; delete[] platforms; cl_device_type devType = CL_DEVICE_TYPE_GPU; if (useCPU) devType = CL_DEVICE_TYPE_CPU; error = clGetDeviceIDs(platform, devType, 0, 0, &numOfAdapters); CHECK_RESULT((error != CL_SUCCESS), "clGetDeviceIDs failed"); if (mpform) { (*mpform) = platform; } return (int)numOfAdapters; } int calibrate(OCLTest* test) { int n = 1; #if 0 while (1) { double timer = run(test, n); if (timer > 2.) { break; } n *= 2; } #endif return n; } void* dummyThread(void* argv) { unsigned int counter = 0; while (counter < 1000000) counter++; return argv; } //! Function used to run the test specified //! It would look something like OCLPerfInputspeed[0] double run(OCLTest* test, int passes) { CPerfCounter counter; counter.Reset(); counter.Start(); int i; for (i = 0; i < passes; i++) { test->run(); } counter.Stop(); double timer = counter.GetElapsedTime(); counter.Reset(); return timer; } //! Function to display the result after a test is finished //! It also stores the result in a TestResult object void report(Worker* w, const char* testname, int testnum, unsigned int crc, const char* errorMsg, float timer, TestResult* tr, const char* testDesc) { unsigned int thread = w->getId(); bool perflab = w->getPerflab(); unsigned int deviceId = w->getDeviceId(); char tmpUnits[256]; if (perflab) { oclTestLog(OCLTEST_LOG_ALWAYS, "%10.3f\n", timer); } else { const char* passedOrFailed[] = {"FAILED", "PASSED"}; // char teststring[256]; // sprintf(teststring, "%s[%d]", testname, testnum); // sprintf(tmpUnits, "Device[%d]:\t%-32s:\t%s\n", deviceId, teststring, // ((tr->passed) ? passedOrFailed[1] : passedOrFailed[0])); // If crc is not 0 or errorMsg is not empty, print the full stats if ((crc != 0) || (errorMsg && (errorMsg[0] != '\0'))) { sprintf(tmpUnits, "%s %s: %s[%d] T[%1d] [%3d], %10.3f %-20s (chksum 0x%08x)\n", testDesc, ((tr->passed) ? passedOrFailed[1] : passedOrFailed[0]), w->isCPUEnabled() ? "CPU" : "GPU", deviceId, thread, testnum, timer, errorMsg, crc); } else { sprintf(tmpUnits, "%s %s: %s[%d] T[%1d] [%3d], %10.3f\n", testDesc, ((tr->passed) ? passedOrFailed[1] : passedOrFailed[0]), w->isCPUEnabled() ? "CPU" : "GPU", deviceId, thread, testnum, timer); } oclTestLog(OCLTEST_LOG_ALWAYS, tmpUnits); tr->value = timer; tr->resultString.assign(tmpUnits); if (App::m_svcMsg && !tr->passed) { char escaped[2 * sizeof(tmpUnits)]; char* ptr = escaped; for (int i = 0; tmpUnits[i] != '\0'; ++i) { switch (tmpUnits[i]) { case '\n': *ptr++ = '|'; *ptr++ = 'n'; break; case '\r': *ptr++ = '|'; *ptr++ = 'r'; break; case '\'': case '|': case ']': case '[': *ptr++ = '|'; default: *ptr++ = tmpUnits[i]; } } *ptr = '\0'; oclTestLog(OCLTEST_LOG_ALWAYS, "##%s[testFailed name='%s.%s.%d' message='FAILED' " "details='%s']\n", App::m_svcMsg, w->getModule()->get_libname(), testname, testnum, escaped); } } } //! Thread Entry point void* runSubtest(void* worker) { char units[256]; double conversion; unsigned int crc = 0; bool second_run = false; // Getting the worker object that is running in this thread Worker* w = (Worker*)worker; if (w == 0) return NULL; unsigned int test = w->getTestIndex(); unsigned int subtest = w->getSubTest(); unsigned int deviceId = w->getDeviceId(); unsigned int platformIndex = w->getPlatformID(); TestResult* result = w->getResult(); RERUN_TEST: // Acquiring lock on the 'module' object common to all threads moduleLock.lock(); Module* m = w->getModule(); if (m == 0 || m->create_test == 0) return NULL; // If we can, used the cached version, // otherwise create the test. OCLTest* pt = (m->cached_test ? m->cached_test[subtest] : NULL); if (!pt) { pt = m->create_test(subtest); if (pt->cache_test() && m->cached_test) { m->cached_test[subtest] = pt; } } pt->clearError(); OCLTestImp* tmp = pt->toOCLTestImp(); if (tmp) { tmp->setOCLWrapper(w->getOCLWrapper()); } std::string subtestName = m->get_name(subtest); moduleLock.unlock(); if (pt == 0) return NULL; pt->resetDescString(); if (App::m_svcMsg) { oclTestLog(OCLTEST_LOG_ALWAYS, "##%s[testStarted name='%s.%s.%d' " "captureStandardOutput='true']\n", App::m_svcMsg, m->get_libname(), subtestName.c_str(), test); } // setting the type to CPU. if (w->isCPUEnabled()) { pt->useCPU(); } // Setting the device according to the worker thread pt->setDeviceId(w->getDeviceId()); pt->setPlatformIndex(w->getPlatformID()); // Opening the 'test'th subtest of 'pt' pt->open(test, units, conversion, deviceId); pt->clearPerfInfo(); char buffer[256]; sprintf(buffer, "%s[%3d]", subtestName.c_str(), test); oclTestLog(OCLTEST_LOG_ALWAYS, "%-32s", buffer); if (pt->hasErrorOccured()) { result->passed = false; report(w, subtestName.c_str(), test, crc, pt->getErrorMsg(), pt->getPerfInfo(), result, pt->testDescString.c_str()); } else { unsigned int n = calibrate(pt); double timer = run(pt, n); crc = pt->close(); if (pt->hasErrorOccured()) { // run second time if the test fails the first time. if (!second_run && App::m_reRunFailed && !App::m_svcMsg) { second_run = true; // Destroying a test object moduleLock.lock(); if (!pt->cache_test()) { m->destroy_test(pt); } moduleLock.unlock(); pt->clearError(); goto RERUN_TEST; } } result->passed = !pt->hasErrorOccured(); /// print conditional pass if it is passes the second time. if (second_run && result->passed) { report(w, subtestName.c_str(), test, crc, "Conditional PASS", pt->getPerfInfo(), result, pt->testDescString.c_str()); } else { report(w, subtestName.c_str(), test, crc, pt->getErrorMsg(), pt->getPerfInfo(), result, pt->testDescString.c_str()); } } if (App::m_svcMsg) { oclTestLog(OCLTEST_LOG_ALWAYS, "##%s[testFinished name='%s.%s.%d']\n", App::m_svcMsg, m->get_libname(), subtestName.c_str(), test); } // Make sure we clear the error after we report that there was an error. pt->clearError(); // Destroying a test object moduleLock.lock(); if (!pt->cache_test()) { m->destroy_test(pt); } moduleLock.unlock(); return NULL; } void App::PrintTestOrder(int mod_index) { oclTestLog(OCLTEST_LOG_ALWAYS, "Module: %s (%d tests)\n", m_modules[mod_index].name.c_str(), m_modules[mod_index].get_count()); for (unsigned int j = 0; j < m_modules[mod_index].get_count(); j++) { oclTestLog(OCLTEST_LOG_ALWAYS, "%s\n", m_modules[mod_index].get_name(mp_testOrder[j])); } } //! Function that runs all the tests specified in the command-line bool App::RunAllTests() { bool status = true; #ifdef _WIN32 if (!m_console) m_window = new Window("Test", 100, 100, m_width, m_height, 0); #endif // // Add all tests to run list if none specified // if (m_tests.size() < 1) { for (unsigned int i = 0; i < m_modules.size(); i++) { for (unsigned int j = 0; j < m_modules[i].get_count(); j++) { AddToList(m_tests, m_modules[i].get_name(j)); } } } unsigned int num_passes = 0; unsigned int num_failures = 0; // // Run each test // for (unsigned int i = 0; i < m_modules.size(); i++) { oclTestLog(OCLTEST_LOG_ALWAYS, "\n-------------------------------------------------\n"); oclTestLog(OCLTEST_LOG_ALWAYS, "The OpenCL Testing Module %s Version = %d \n", m_modules[i].get_libname(), m_modules[i].get_version()); oclTestLog(OCLTEST_LOG_ALWAYS, "-------------------------------------------------\n"); if (App::m_svcMsg) { oclTestLog(OCLTEST_LOG_ALWAYS, "##%s[testSuiteStarted name='ocltst %s']\n", App::m_svcMsg, m_modules[i].get_libname()); } // array to keep track of order of test execution. int test_count = m_modules[i].get_count(); mp_testOrder = new int[test_count]; memset((void*)mp_testOrder, 0, sizeof(*mp_testOrder) * test_count); SetTestRunOrder(i); // // List all tests first if the option was turned on // if (m_list) { PrintTestOrder(i); delete[] mp_testOrder; continue; // return; } for (unsigned int itr_var = 0; itr_var < m_modules[i].get_count(); itr_var++) { // done for random order generation unsigned int subtest = mp_testOrder[itr_var]; const char* name = m_modules[i].get_name(subtest); if (itr_var < m_tests.size() && TestInList(m_tests, name)) { OCLTest* pt = NULL; if (m_modules[i].cached_test) { pt = m_modules[i].cached_test[subtest]; } // Try to use the cached version first! if (!pt) { pt = m_modules[i].create_test(subtest); if (pt->cache_test() && m_modules[i].cached_test) { m_modules[i].cached_test[subtest] = pt; } } int numSubTests = pt->getNumSubTests(); assert(numSubTests > 0); TestIndexList testIndices; GetTestIndexList(testIndices, m_tests, name, numSubTests - 1); TestIndexList avoidIndices; GetTestIndexList(avoidIndices, m_avoid, name, numSubTests - 1); TestIndexList erasedIndices; PruneTestIndexList(testIndices, avoidIndices, erasedIndices); int numTestsRun = 0; for (unsigned int j = 0; j < testIndices.size(); j++) { unsigned int test = testIndices[j]; WaitAllThreads(); AddWorkerThread(i, subtest, test, pt->getThreadUsage(), runSubtest); for (unsigned int thread = 1; (thread < m_threads) && (thread < m_modules.size()); thread++) { AddWorkerThread(thread, subtest, test, pt->getThreadUsage(), dummyThread); } numTestsRun++; } WaitAllThreads(); // Printing the test report // First checking whether the number of subtests is greater than 1. // No point printing report for a one subtest test if (numTestsRun > 0) { if (testReport[0]->success) { num_passes++; } else { num_failures++; } } if (App::m_svcMsg) { for (unsigned int j = 0; j < erasedIndices.size(); j++) { oclTestLog(OCLTEST_LOG_ALWAYS, "##%s[testIgnored name='%s.%s.%d']\n", App::m_svcMsg, m_modules[i].get_libname(), name, erasedIndices[j]); } } // Resetting the values of the test reports // for(unsigned int j = 0; j < m_numDevices; j++) for (unsigned int j = 0; j < 1; j++) { testReport[j]->reset(); } m_modules[i].destroy_test(pt); if (m_modules[i].cached_test) { m_modules[i].cached_test[subtest] = NULL; } } } if (App::m_svcMsg) { oclTestLog(OCLTEST_LOG_ALWAYS, "##%s[testSuiteFinished name='ocltst %s']\n", App::m_svcMsg, m_modules[i].get_libname()); } // print the order in which the test are executed if they are // randomized. if (m_rndOrder) { PrintTestOrder(i); } // deleting the test order delete[] mp_testOrder; } #ifdef _WIN32 if (!m_console && m_window) { ((Window*)m_window)->ConsumeEvents(); } #endif float total_tests = (float)(num_passes + num_failures); float percent_passed = 0.0f; float percent_failed = 0.0f; float percent_total = 0.0f; if (total_tests > 0) { percent_passed = 100.0f * ((float)num_passes / total_tests); percent_failed = 100.0f * ((float)num_failures / total_tests); percent_total = 100.0f * ((float)total_tests / total_tests); } oclTestLog(OCLTEST_LOG_ALWAYS, "\n\n"); oclTestLog(OCLTEST_LOG_ALWAYS, "----------------------------------------\n"); oclTestLog(OCLTEST_LOG_ALWAYS, "Total Passed Tests: %8d (%6.2f%s)\n", num_passes, percent_passed, "%"); oclTestLog(OCLTEST_LOG_ALWAYS, "Total Failed Tests: %8d (%6.2f%s)\n", num_failures, percent_failed, "%"); oclTestLog(OCLTEST_LOG_ALWAYS, "----------------------------------------\n"); oclTestLog(OCLTEST_LOG_ALWAYS, "Total Run Tests: %8d (%6.2f%s)\n", (int)total_tests, percent_total, "%"); oclTestLog(OCLTEST_LOG_ALWAYS, "\n\n"); // fail the ocltst process if there are any failed tests status = (num_failures == 0); return status; } ///////////////////////////////////////////////////////////////////////////// void App::AddToList(StringList& strlist, const char* str) { std::string s(str); strlist.push_back(s); } void App::LoadList(StringList& strlist, const char* filename) { char buffer[1024]; FILE* fp = fopen(filename, "r"); if (fp == NULL) return; while (fgets(buffer, 1000, fp) != NULL) { size_t length = strlen(buffer); if (length > 0) { if (buffer[length - 1] != '\n') { length++; } buffer[length - 1] = 0; AddToList(strlist, buffer); } } fclose(fp); } static void Help(const char* name) { oclTestLog(OCLTEST_LOG_ALWAYS, "%s (-w | -V | -m | -M | -l | -t | -T | -p | -d | -x | -y | -g| " "-o | -n )\n", name); oclTestLog(OCLTEST_LOG_ALWAYS, " -w : enable window mode\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -V : enable TeamCity service messages\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -J : enable Jenkins service messages\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -d : dump test output to portable float map (pfm)\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -m : specify a DLL module with tests\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -M : specify a text file with one DLL module per line\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -l : list test names in DLL modules and exit\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -s : number of threads to spawn\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -t : run test\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -T : specify a text file with one test per line\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -a : specify a test to avoid\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -A : specify a text file of tests to avoid with " "one test per line\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -p : specify a platform to run on, 'amd','nvidia' " ",'intel' or 'ms'\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -h : this help text\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -x : x dimension for debug output image (and window)\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -y : y dimension for debug output image (and window)\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -P : Perflab mode (just print the result without " "any supplementary information)\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -n #number : run the tests specified with -m, -M, -t or -T " "options multiple times\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -r : Option to Randomize the order in which the " "tests are executed.\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -R : Option to ReRun failed tests for conditional " "pass.\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -i : Don't print system information\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -g : GPUid to run the tests on\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -o : dump the output to a specified file\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " -c : Run the test on the CPU device.\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " : \n"); oclTestLog(OCLTEST_LOG_ALWAYS, " : To run only one subtest of a test, append the " "subtest to\n"); oclTestLog(OCLTEST_LOG_ALWAYS, " : the end of the test name in brackets. i.e. test[1]"); oclTestLog(OCLTEST_LOG_ALWAYS, "\n"); exit(0); } unsigned int getPlatformID(const char* str) { std::string strOfCLVendor(str); std::string strOfCLPlatformName; unsigned int platform = 0; // currently, the only input values amd,nvidia and intel are supported if (strOfCLVendor == "amd") { strOfCLPlatformName = "Advanced Micro Devices, Inc."; } else if (strOfCLVendor == "intel") { strOfCLPlatformName = "Intel(R) Corporation"; } else if (strOfCLVendor == "nvidia") { strOfCLPlatformName = "NVIDIA Corporation"; } else if (strOfCLVendor == "ms") { strOfCLPlatformName = "Microsoft"; } else { // fall-back on platform index 0 return platform; } cl_int status; cl_uint numPlatforms = 0; status = clGetPlatformIDs(0, NULL, &numPlatforms); if (status != CL_SUCCESS) { return platform; } cl_platform_id* platforms = new cl_platform_id[numPlatforms]; status = clGetPlatformIDs(numPlatforms, platforms, NULL); if (status == CL_SUCCESS) { unsigned int i; for (i = 0; i < numPlatforms; ++i) { char buff[200]; status = clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(buff), buff, NULL); if (status != CL_SUCCESS) { break; } if (strcmp(buff, strOfCLPlatformName.c_str()) == 0) { platform = i; break; } } } delete[] platforms; return platform; } static const char* supported_options = "dg:lm:M:o:Ps:t:T:a:A:p:v:wxy:in:rcRVJ"; unsigned int parseCommandLineForPlatform(unsigned int argc, char** argv) { int c; unsigned int platform = 0; while ((c = getopt(argc, argv, supported_options)) != -1) { switch (c) { case 'p': platform = getPlatformID(optarg); break; default: break; } } return platform; } void App::CommandLine(unsigned int argc, char** argv) { unsigned int i = 1; int c; bool hasOption = false; unsigned int tmpNumDevices = 0; unsigned int tmpDeviceId = 0; m_deviceId = 0; int tmp; while ((c = getopt(argc, argv, supported_options)) != -1) { switch (c) { case 'c': m_useCPU = true; break; case 'p': break; case 'w': m_console = false; hasOption = true; break; case 'V': m_svcMsg = "teamcity"; break; case 'J': m_svcMsg = "jenkins"; break; case 'd': m_dump = true; hasOption = true; break; case 'm': AddToList(m_paths, optarg); hasOption = true; break; case 'M': LoadList(m_paths, optarg); hasOption = true; break; case 'a': AddToList(m_avoid, optarg); hasOption = true; break; case 'A': LoadList(m_avoid, optarg); hasOption = true; break; case 'l': m_list = true; hasOption = true; break; // command line switch to loop execution of any specified test or tests n // number of times case 'n': m_numItr = atoi(optarg); break; // command line switch to randomize the order of test execution in OCLTest case 'r': m_rndOrder = true; break; // command line switch to rerun the failed tests to see if they pass on // second run case 'R': { m_reRunFailed = true; break; } case 't': AddToList(m_tests, optarg); hasOption = true; break; case 'T': LoadList(m_tests, optarg); hasOption = true; break; case 's': m_threads = atoi(optarg); hasOption = true; break; case 'h': Help(argv[0]); break; case 'x': m_width = atoi(optarg); hasOption = true; break; case 'y': m_height = atoi(optarg); hasOption = true; break; case 'P': m_perflab = true; hasOption = true; break; case 'g': #if 0 tmpNumDevices = (unsigned int)atoi(optarg); if(m_numDevices < tmpNumDevices) { oclTestLog(OCLTEST_LOG_ALWAYS, "Number of Devices(%d) less than specified by the user(%d). Using %d devices.\n", m_numDevices, tmpNumDevices, m_numDevices); } else { m_numDevices = tmpNumDevices; } #else tmpDeviceId = (unsigned int)atoi(optarg); #endif break; case 'v': tmp = atoi(optarg); if (tmp >= 0 && tmp < 100) { oclTestSetLogLevel(atoi(optarg)); } else { oclTestLog(OCLTEST_LOG_ALWAYS, "Invalid verbose level\n"); } break; case 'o': { hasOption = true; oclTestEnableLogToFile(optarg); } break; case 'i': m_noSysInfoPrint = true; break; default: Help(argv[0]); break; } } // Reset devices in case user overrode defaults m_numDevices = findAdapters(m_platform, m_useCPU, &mpform_id); if (m_numDevices < (tmpDeviceId + 1)) { m_deviceId = 0; oclTestLog(OCLTEST_LOG_ALWAYS, "User specified deviceId(%d) exceedes the number of " "Devices(%d). Using device %d.\n", tmpDeviceId, m_numDevices, m_deviceId); } else { m_deviceId = tmpDeviceId; } if (!hasOption) { Help(argv[0]); } } bool App::TestInList(StringList& strlist, const char* szModuleTestname) { if (szModuleTestname == NULL) { return false; } for (unsigned int i = 0; i < strlist.size(); i++) { // check to see if an index is specified for this test name int nIndex = -1; std::string szTestName = strlist[i]; if (szTestName.find("[") != std::string::npos) { size_t nFirstBracket = szTestName.find("["); size_t nLastBracket = szTestName.find("]"); if ((nFirstBracket != std::string::npos) && (nLastBracket != std::string::npos) && (nLastBracket > nFirstBracket)) { szTestName = szTestName.substr(0, nFirstBracket); } } if (strcmp(szModuleTestname, szTestName.c_str()) == 0) { return true; } } return false; } void App::GetTestIndexList(TestIndexList& testIndices, StringList& testList, const char* szModuleTestname, int maxIndex) { for (unsigned int i = 0; i < testList.size(); i++) { IndicesRange nIndex = {0, maxIndex}; // If the test name string ends with [...] parse the text // between the brackets to determine the index range. std::string szTestName = testList[i]; if (szTestName.find("[") != std::string::npos) { size_t nFirstBracket = szTestName.find("["); size_t nLastBracket = szTestName.find("]"); if ((nFirstBracket != std::string::npos) && (nLastBracket != std::string::npos) && (nLastBracket > nFirstBracket)) { // Getting the string between the brackets '[' and ']' // The values can be one of the following:- // [a-b] - Run tests from a to b // [a-] - Run tests from subtest a to subtest total_tests // [-b] - Run tests from subtest 0 to subtest b // a and b are indices of the tests to run std::string nIndexString = szTestName.substr(nFirstBracket + 1, nLastBracket - nFirstBracket - 1); size_t nIntermediateHyphen = szTestName.find("-"); if ((nIntermediateHyphen != std::string::npos) && (nIntermediateHyphen < nLastBracket) && (nIntermediateHyphen > nFirstBracket)) { // Getting the start index if ((nIntermediateHyphen - 1) == nFirstBracket) { nIndex.startIndex = 0; } else { nIndex.startIndex = atoi(szTestName.substr(nFirstBracket + 1, nIntermediateHyphen - nFirstBracket - 1) .c_str()); } // Getting the end index if ((nIntermediateHyphen + 1) == nLastBracket) { nIndex.endIndex = maxIndex; } else { nIndex.endIndex = atoi( szTestName.substr(nIntermediateHyphen + 1, nLastBracket - nIntermediateHyphen - 1) .c_str()); } } else { nIndex.startIndex = atoi(szTestName.substr(nFirstBracket + 1, nLastBracket - nFirstBracket - 1).c_str()); nIndex.endIndex = nIndex.startIndex; } } szTestName = szTestName.substr(0, nFirstBracket); } if (strcmp(szModuleTestname, szTestName.c_str()) == 0) { // If the values are out of order, swap them. if (nIndex.startIndex > nIndex.endIndex) { int tmp = nIndex.startIndex; nIndex.startIndex = nIndex.endIndex; nIndex.endIndex = tmp; } // Add the indices in the specified range to the list. for (int i = nIndex.startIndex; i <= nIndex.endIndex; ++i) { if (i <= maxIndex) { testIndices.push_back(i); } else { oclTestLog(OCLTEST_LOG_ALWAYS, "Error: Invalid test index for subtest: %s!\n", szModuleTestname); } } // Now sort and prune duplicates. std::sort(testIndices.begin(), testIndices.end()); std::unique(testIndices.begin(), testIndices.end()); } } } void App::PruneTestIndexList(TestIndexList& testIndices, TestIndexList& avoidIndices, TestIndexList& erasedIndices) { for (TestIndexList::iterator it = testIndices.begin(); it != testIndices.end();) { unsigned int index = *it; TestIndexList::iterator result = std::find(avoidIndices.begin(), avoidIndices.end(), index); if (result != avoidIndices.end()) { it = testIndices.erase(it); erasedIndices.push_back(index); } else { ++it; } } } void App::ScanForTests() { for (unsigned int i = 0; i < m_paths.size(); i++) { Module mod; #ifdef _WIN32 std::string::iterator myIter; myIter = m_paths[i].end(); myIter--; if (*myIter == 0x0a) m_paths[i].erase(myIter); mod.hmodule = LoadLibrary(m_paths[i].c_str()); #endif #ifdef __linux__ mod.hmodule = dlopen(m_paths[i].c_str(), RTLD_NOW); #endif if (mod.hmodule == NULL) { fprintf(stderr, "Could not load module: %s\n", m_paths[i].c_str()); #ifdef __linux__ fprintf(stderr, "Error : %s\n", dlerror()); #else #endif } else { mod.name = m_paths[i]; #ifdef _WIN32 mod.get_count = (TestCountFuncPtr)GetProcAddress(mod.hmodule, "OCLTestList_TestCount"); mod.get_name = (TestNameFuncPtr)GetProcAddress(mod.hmodule, "OCLTestList_TestName"); mod.create_test = (CreateTestFuncPtr)GetProcAddress(mod.hmodule, "OCLTestList_CreateTest"); mod.destroy_test = (DestroyTestFuncPtr)GetProcAddress(mod.hmodule, "OCLTestList_DestroyTest"); mod.get_version = (TestVersionFuncPtr)GetProcAddress(mod.hmodule, "OCLTestList_TestLibVersion"); mod.get_libname = (TestLibNameFuncPtr)GetProcAddress(mod.hmodule, "OCLTestList_TestLibName"); #endif #ifdef __linux__ mod.get_count = (TestCountFuncPtr)dlsym(mod.hmodule, "OCLTestList_TestCount"); mod.get_name = (TestNameFuncPtr)dlsym(mod.hmodule, "OCLTestList_TestName"); mod.create_test = (CreateTestFuncPtr)dlsym(mod.hmodule, "OCLTestList_CreateTest"); mod.destroy_test = (DestroyTestFuncPtr)dlsym(mod.hmodule, "OCLTestList_DestroyTest"); mod.get_version = (TestVersionFuncPtr)dlsym(mod.hmodule, "OCLTestList_TestLibVersion"); mod.get_libname = (TestLibNameFuncPtr)dlsym(mod.hmodule, "OCLTestList_TestLibName"); #endif mod.cached_test = new OCLTest*[mod.get_count()]; for (int x = 0, y = mod.get_count(); x < y; ++x) { mod.cached_test[x] = NULL; } m_modules.push_back(mod); } } } void App::CleanUp() { for (unsigned int i = 0; i < m_modules.size(); i++) { if (m_modules[i].cached_test) { delete[] m_modules[i].cached_test; } #ifdef _WIN32 FreeLibrary(m_modules[i].hmodule); #endif #ifdef __linux__ dlclose(m_modules[i].hmodule); #endif } #ifdef _WIN32 if (m_window) delete m_window; m_window = 0; #endif } extern int optind; ///////////////////////////////////////////////////////////////////////////// bool App::m_reRunFailed = false; const char* App::m_svcMsg = nullptr; int main(int argc, char** argv) { #if EMU_ENV printf("Built for Emulation Environment\n"); #endif // EMU_ENV unsigned int platform = 0; platform = parseCommandLineForPlatform(argc, argv); // reset optind as we really didn't parse the full command line optind = 1; bool status = true; App app(platform); #ifdef _WIN32 // this function is registers windows service routine when ocltst is launched // by the OS on service initialization. On other scenarios, this function does // nothing. serviceStubCall(); // SetErrorMode(SEM_NOGPFAULTERRORBOX); // const LPTOP_LEVEL_EXCEPTION_FILTER oldFilter = // SetUnhandledExceptionFilter(xFilter); #endif // _WIN32 #ifdef AUTO_REGRESS try { #endif /* AUTO_REGRESS */ app.CommandLine(argc, argv); app.printOCLinfo(); app.ScanForTests(); for (int i = 0; i < app.GetNumItr(); i++) { if (!app.RunAllTests()) { status = false; } } app.CleanUp(); #ifdef AUTO_REGRESS } catch (...) { oclTestLog(OCLTEST_LOG_ALWAYS, "Exiting due to unhandled exception!\n"); return (-1); } #endif /* AUTO_REGRESS */ // return 0 if all tests passed, 1 otherwise return status ? 0 : 1; } #ifdef _WIN32 #include typedef unsigned int uint32; typedef size_t uintp; struct StackEntry { uintp addr; uint32 line; uint32 disp; char symbol[128]; char file[128]; }; static const unsigned int MAX_DEPTH_PER_NODE = 24; struct Info { bool operator==(const Info& b) const { return key == b.key; } uintp key; // pointer, handle, whatever StackEntry stack[MAX_DEPTH_PER_NODE]; }; static void dumpTraceBack(CONTEXT& context) { Info info; oclTestLog(OCLTEST_LOG_ALWAYS, "Exception: exiting!\n"); HANDLE process = GetCurrentProcess(); STACKFRAME64 stackframe; memset(&stackframe, 0, sizeof(STACKFRAME64)); #if defined(_WIN64) stackframe.AddrPC.Offset = context.Rip; stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrStack.Offset = context.Rsp; stackframe.AddrStack.Mode = AddrModeFlat; stackframe.AddrFrame.Offset = context.Rbp; stackframe.AddrFrame.Mode = AddrModeFlat; #else stackframe.AddrPC.Offset = context.Eip; stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrStack.Offset = context.Esp; stackframe.AddrStack.Mode = AddrModeFlat; stackframe.AddrFrame.Offset = context.Ebp; stackframe.AddrFrame.Mode = AddrModeFlat; #endif unsigned int depth = 0; if (SymInitialize(process, NULL, true)) { while ((depth < MAX_DEPTH_PER_NODE) && StackWalk64(IMAGE_FILE_MACHINE_I386, process, GetCurrentThread(), &stackframe, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { if (stackframe.AddrPC.Offset != 0) { // // we don't want to evaluate the names/lines yet // so just record the address // info.stack[depth].addr = (uintp)stackframe.AddrPC.Offset; DWORD64 disp64; DWORD disp; IMAGEHLP_SYMBOL64* symInfo; IMAGEHLP_LINE64 lineInfo; uintp addr = (uintp)stackframe.AddrPC.Offset; char buffer[128]; symInfo = (IMAGEHLP_SYMBOL64*)&buffer[0]; symInfo->SizeOfStruct = sizeof(symInfo); symInfo->MaxNameLength = (sizeof(buffer) - sizeof(IMAGEHLP_SYMBOL64)); lineInfo.SizeOfStruct = sizeof(lineInfo); if (SymGetSymFromAddr64(process, addr, &disp64, symInfo)) { sprintf(info.stack[depth].symbol, "%s", symInfo->Name); info.stack[depth].disp = (uint32)disp64; } else { sprintf(info.stack[depth].symbol, ""); } if (SymGetLineFromAddr64(process, addr, &disp, &lineInfo)) { sprintf(info.stack[depth].file, "%s", lineInfo.FileName); info.stack[depth].line = lineInfo.LineNumber; } else { info.stack[depth].file[0] = '\0'; } depth++; } } } SymCleanup(process); int j = 0; while (j < MAX_DEPTH_PER_NODE && info.stack[j].addr != 0) { oclTestLog(OCLTEST_LOG_ALWAYS, " %s()+%d (0x%.8x) %s:%d\n", info.stack[j].symbol, info.stack[j].disp, info.stack[j].addr, info.stack[j].file, info.stack[j].line); j++; } } static LONG WINAPI xFilter(LPEXCEPTION_POINTERS xEP) { CONTEXT context; CONTEXT* xCtx = &context; memset(xCtx, 0, sizeof(CONTEXT)); context.ContextFlags = CONTEXT_FULL; memcpy(xCtx, xEP->ContextRecord, sizeof(CONTEXT)); dumpTraceBack(context); return (EXCEPTION_EXECUTE_HANDLER); } #undef CHECK_RESULT #endif // WIN_OS /////////////////////////////////////////////////////////////////////////////