/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return oss.str();
}
+std::string GetWChan(int pid)
+{
+ std::ostringstream procwchan;
+ procwchan << "/proc/" << pid << "/wchan";
+ std::ifstream ifs;
+ ifs.open(procwchan.str(), std::ifstream::in);
+ std::string line;
+ std::getline(ifs, line);
+ ifs.close();
+ return line;
+}
+
std::string ReadAndEscape(std::string filename)
{
std::ostringstream os;
basename(processName),
numPasses + numFailures,
numPasses,
- (float)numPasses * 100.0f / (numPasses + numFailures),
+ numPasses > 0 ? (float)numPasses * 100.0f / (numPasses + numFailures) : 0.0f,
numFailures,
- (float)numFailures * 100.0f / (numPasses + numFailures));
+ numPasses > 0 ? (float)numFailures * 100.0f / (numPasses + numFailures) : 0.0f);
fclose(fp);
}
}
return testResult;
}
-int32_t RunAll(const char* processName, ::testcase tc_array[], bool quiet)
+int32_t RunAll(const char* processName, ::testcase tc_array[], std::string match, bool quiet)
{
int32_t numFailures = 0;
int32_t numPasses = 0;
std::string startTime(buffer);
TestCase testCase(i, &tc_array[i]);
- testCase.result = RunTestCaseInChildProcess(testCase, quiet);
-
- tt = system_clock::to_time_t(system_clock::now());
- strftime(buffer, BUFSIZE, "%c", localtime(&tt));
- std::string endTime(buffer);
-
- if(testCase.result == 0)
- {
- numPasses++;
- }
- else
+ bool run = true;
+ if(!match.empty())
{
- numFailures++;
+ if(match.compare(0, match.size(), testCase.name, match.size()))
+ {
+ run = false;
+ }
}
- if(!quiet)
+
+ if(run)
{
- OutputTestResult(ofs, processName, moduleName, testCase, startTime, endTime);
+ testCase.result = RunTestCaseInChildProcess(testCase, quiet);
+
+ tt = system_clock::to_time_t(system_clock::now());
+ strftime(buffer, BUFSIZE, "%c", localtime(&tt));
+ std::string endTime(buffer);
+
+ if(testCase.result == 0)
+ {
+ numPasses++;
+ }
+ else
+ {
+ numFailures++;
+ }
+ if(!quiet)
+ {
+ OutputTestResult(ofs, processName, moduleName, testCase, startTime, endTime);
+ }
}
}
ofs.close();
}
// Constantly runs up to MAX_NUM_CHILDREN processes
-int32_t RunAllInParallel(const char* processName, ::testcase tc_array[], bool reRunFailed, bool quiet)
+int32_t RunAllInParallel(const char* processName, ::testcase tc_array[], std::string match, bool reRunFailed, bool quiet)
{
int32_t numFailures = 0;
int32_t numPasses = 0;
// Create more children (up to the max number or til the end of the array)
while(numRunningChildren < MAX_NUM_CHILDREN && tc_array[nextTestCase].name)
{
- int32_t pid = fork();
- if(pid == 0) // Child process
+ bool run = true;
+ if(!match.empty())
{
- TestCase testCase(nextTestCase, &tc_array[nextTestCase]);
- int status = RunTestCaseRedirectOutput(testCase, quiet);
- exit(status);
+ if(match.compare(0, match.size(), tc_array[nextTestCase].name, match.size()))
+ {
+ run = false;
+ }
}
- else if(pid == -1)
+ if(run)
{
- perror("fork");
- exit(EXIT_STATUS_FORK_FAILED);
+ int32_t pid = fork();
+ if(pid == 0) // Child process
+ {
+ TestCase testCase(nextTestCase, &tc_array[nextTestCase]);
+ int status = RunTestCaseRedirectOutput(testCase, quiet);
+ exit(status);
+ }
+ else if(pid == -1)
+ {
+ perror("fork");
+ exit(EXIT_STATUS_FORK_FAILED);
+ }
+ else // Parent process
+ {
+ TestCase tc(nextTestCase, tc_array[nextTestCase].name);
+ tc.startTime = steady_clock::now();
+ tc.startSystemTime = system_clock::now();
+ tc.childPid = pid;
+
+ children[pid] = tc;
+ nextTestCase++;
+ numRunningChildren++;
+ }
}
- else // Parent process
+ else
{
- TestCase tc(nextTestCase, tc_array[nextTestCase].name);
- tc.startTime = steady_clock::now();
- tc.startSystemTime = system_clock::now();
- tc.childPid = pid;
-
- children[pid] = tc;
nextTestCase++;
- numRunningChildren++;
}
}
- // Check to see if any children have finished yet
int32_t status = 0;
- int32_t childPid = waitpid(-1, &status, WNOHANG);
+ int32_t childPid = 0;
+ if(numRunningChildren > 0) // Only wait if there are running children.
+ {
+ // Check to see if any children have finished yet
+ childPid = waitpid(-1, &status, WNOHANG);
+ }
+
if(childPid == 0)
{
// No children have finished.
for(auto& tc : children)
{
std::chrono::steady_clock::duration timeSpan = endTime - tc.second.startTime;
- std::chrono::duration<double> seconds = std::chrono::duration_cast<std::chrono::duration<double>>(timeSpan);
- if(seconds.count() > MAXIMUM_CHILD_LIFETIME)
+ double seconds = double(timeSpan.count()) * std::chrono::steady_clock::period::num / std::chrono::steady_clock::period::den;
+
+ if(4.9999 < seconds && seconds < 5 && !tc.second.finished)
+ {
+ printf("Child process %s is delayed: WCHAN:%s\n", tc.second.name, GetWChan(tc.first).c_str());
+ }
+ else if(seconds > MAXIMUM_CHILD_LIFETIME)
{
// Kill the child process. A subsequent call to waitpid will process signal result below.
- kill(tc.first, SIGKILL);
+ if(!tc.second.finished)
+ {
+ printf("Child process %s WCHAN:%s\n", tc.second.name, GetWChan(tc.first).c_str());
+ kill(tc.first, SIGKILL);
+ tc.second.finished = true; // Only send kill signal once.
+ }
}
}
}
{
if(WIFEXITED(status))
{
- auto& testCase = children[childPid];
- testCase.result = WEXITSTATUS(status);
+ auto& testCase = children[childPid];
+ testCase.result = WEXITSTATUS(status);
+ testCase.finished = true;
if(testCase.result)
{
printf("Test case %s failed: %d\n", testCase.name, testCase.result);
RunningTestCases::iterator iter = children.find(childPid);
if(iter != children.end())
{
+ iter->second.finished = true;
printf("Test case %s exited with signal %s\n", iter->second.name, strsignal(status));
iter->second.result = 1;
failedTestCases.push_back(iter->second.testCase);
int RunTests(int argc, char* const argv[], ::testcase tc_array[])
{
int result = TestHarness::EXIT_STATUS_BAD_ARGUMENT;
- const char* optString = "sfq";
+ const char* optString = "sfqm:";
bool optRerunFailed(true);
bool optRunSerially(false);
bool optQuiet(false);
+ std::string optMatch;
int nextOpt = 0;
do
case 'q':
optQuiet = true;
break;
+ case 'm':
+ optMatch = optarg;
+ break;
case '?':
TestHarness::Usage(argv[0]);
exit(TestHarness::EXIT_STATUS_BAD_ARGUMENT);
{
if(optRunSerially)
{
- result = TestHarness::RunAll(argv[0], tc_array, optQuiet);
+ result = TestHarness::RunAll(argv[0], tc_array, optMatch, optQuiet);
}
else
{
- result = TestHarness::RunAllInParallel(argv[0], tc_array, optRerunFailed, optQuiet);
+ result = TestHarness::RunAllInParallel(argv[0], tc_array, optMatch, optRerunFailed, optQuiet);
}
}
else