cmCTestMultiProcessHandler.cxx 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmCTestMultiProcessHandler.h"
  4. #include <algorithm>
  5. #include <cassert>
  6. #include <chrono>
  7. #include <cmath>
  8. #include <cstddef> // IWYU pragma: keep
  9. #include <cstdlib>
  10. #include <cstring>
  11. #include <iomanip>
  12. #include <iostream>
  13. #include <list>
  14. #include <sstream>
  15. #include <stack>
  16. #include <unordered_map>
  17. #include <utility>
  18. #include <vector>
  19. #include <cm/memory>
  20. #include <cm/optional>
  21. #include <cmext/algorithm>
  22. #include <cm3p/json/value.h>
  23. #include <cm3p/json/writer.h>
  24. #include <cm3p/uv.h>
  25. #include "cmsys/FStream.hxx"
  26. #include "cmsys/SystemInformation.hxx"
  27. #include "cmAffinity.h"
  28. #include "cmCTest.h"
  29. #include "cmCTestBinPacker.h"
  30. #include "cmCTestRunTest.h"
  31. #include "cmCTestTestHandler.h"
  32. #include "cmDuration.h"
  33. #include "cmJSONState.h"
  34. #include "cmListFileCache.h"
  35. #include "cmRange.h"
  36. #include "cmStringAlgorithms.h"
  37. #include "cmSystemTools.h"
  38. #include "cmUVJobServerClient.h"
  39. #include "cmWorkingDirectory.h"
  40. namespace {
  41. // For unspecified parallelism, limit to the number of processors,
  42. // but with a minimum greater than 1 so there is some parallelism.
  43. constexpr unsigned long kParallelLevelMinimum = 2u;
  44. // For "unbounded" parallelism, limit to a very high value.
  45. // Under a job server, parallelism is effectively limited
  46. // only by available job server tokens.
  47. constexpr unsigned long kParallelLevelUnbounded = 0x10000u;
  48. }
  49. namespace cmsys {
  50. class RegularExpression;
  51. }
  52. class TestComparator
  53. {
  54. public:
  55. TestComparator(cmCTestMultiProcessHandler* handler)
  56. : Handler(handler)
  57. {
  58. }
  59. // Sorts tests in descending order of cost
  60. bool operator()(int index1, int index2) const
  61. {
  62. return this->Handler->Properties[index1]->Cost >
  63. this->Handler->Properties[index2]->Cost;
  64. }
  65. private:
  66. cmCTestMultiProcessHandler* Handler;
  67. };
  68. cmCTestMultiProcessHandler::cmCTestMultiProcessHandler(
  69. cmCTest* ctest, cmCTestTestHandler* handler)
  70. : CTest(ctest)
  71. , TestHandler(handler)
  72. , ProcessorsAvailable(cmAffinity::GetProcessorsAvailable())
  73. , HaveAffinity(this->ProcessorsAvailable.size())
  74. , ParallelLevelDefault(kParallelLevelMinimum)
  75. {
  76. }
  77. cmCTestMultiProcessHandler::~cmCTestMultiProcessHandler() = default;
  78. // Set the tests
  79. bool cmCTestMultiProcessHandler::SetTests(TestMap tests,
  80. PropertiesMap properties)
  81. {
  82. this->PendingTests = std::move(tests);
  83. this->Properties = std::move(properties);
  84. this->Total = this->PendingTests.size();
  85. if (!this->CTest->GetShowOnly()) {
  86. this->ReadCostData();
  87. this->HasCycles = !this->CheckCycles();
  88. this->HasInvalidGeneratedResourceSpec =
  89. !this->CheckGeneratedResourceSpec();
  90. if (this->HasCycles || this->HasInvalidGeneratedResourceSpec) {
  91. return false;
  92. }
  93. this->CreateTestCostList();
  94. }
  95. return true;
  96. }
  97. // Set the max number of tests that can be run at the same time.
  98. void cmCTestMultiProcessHandler::SetParallelLevel(cm::optional<size_t> level)
  99. {
  100. this->ParallelLevel = level;
  101. if (!this->ParallelLevel) {
  102. // '-j' was given with no value. Limit by number of processors.
  103. cmsys::SystemInformation info;
  104. info.RunCPUCheck();
  105. unsigned long processorCount = info.GetNumberOfLogicalCPU();
  106. if (cm::optional<std::string> fakeProcessorCount =
  107. cmSystemTools::GetEnvVar(
  108. "__CTEST_FAKE_PROCESSOR_COUNT_FOR_TESTING")) {
  109. unsigned long pc = 0;
  110. if (cmStrToULong(*fakeProcessorCount, &pc)) {
  111. processorCount = pc;
  112. } else {
  113. cmSystemTools::Error("Failed to parse fake processor count: " +
  114. *fakeProcessorCount);
  115. }
  116. }
  117. this->ParallelLevelDefault =
  118. std::max(kParallelLevelMinimum, processorCount);
  119. }
  120. }
  121. size_t cmCTestMultiProcessHandler::GetParallelLevel() const
  122. {
  123. if ((this->ParallelLevel && *this->ParallelLevel == 0) ||
  124. (!this->ParallelLevel && this->JobServerClient)) {
  125. return kParallelLevelUnbounded;
  126. }
  127. if (this->ParallelLevel) {
  128. return *this->ParallelLevel;
  129. }
  130. return this->ParallelLevelDefault;
  131. }
  132. void cmCTestMultiProcessHandler::SetTestLoad(unsigned long load)
  133. {
  134. this->TestLoad = load;
  135. std::string fake_load_value;
  136. if (cmSystemTools::GetEnv("__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING",
  137. fake_load_value)) {
  138. if (!cmStrToULong(fake_load_value, &this->FakeLoadForTesting)) {
  139. cmSystemTools::Error("Failed to parse fake load value: " +
  140. fake_load_value);
  141. }
  142. }
  143. }
  144. bool cmCTestMultiProcessHandler::Complete()
  145. {
  146. return this->Completed == this->Total;
  147. }
  148. void cmCTestMultiProcessHandler::InitializeLoop()
  149. {
  150. this->Loop.init();
  151. this->StartNextTestsOnIdle_.init(*this->Loop, this);
  152. this->StartNextTestsOnTimer_.init(*this->Loop, this);
  153. this->JobServerClient = cmUVJobServerClient::Connect(
  154. *this->Loop, /*onToken=*/[this]() { this->JobServerReceivedToken(); },
  155. /*onDisconnect=*/nullptr);
  156. if (this->JobServerClient) {
  157. cmCTestLog(this->CTest, OUTPUT,
  158. "Connected to MAKE jobserver" << std::endl);
  159. }
  160. }
  161. void cmCTestMultiProcessHandler::FinalizeLoop()
  162. {
  163. this->JobServerClient.reset();
  164. this->StartNextTestsOnTimer_.reset();
  165. this->StartNextTestsOnIdle_.reset();
  166. this->Loop.reset();
  167. }
  168. void cmCTestMultiProcessHandler::RunTests()
  169. {
  170. this->CheckResume();
  171. if (this->HasCycles || this->HasInvalidGeneratedResourceSpec) {
  172. return;
  173. }
  174. this->TestHandler->SetMaxIndex(this->FindMaxIndex());
  175. this->InitializeLoop();
  176. this->StartNextTestsOnIdle();
  177. uv_run(this->Loop, UV_RUN_DEFAULT);
  178. this->FinalizeLoop();
  179. if (!this->StopTimePassed && !this->CheckStopOnFailure()) {
  180. assert(this->Complete());
  181. assert(this->PendingTests.empty());
  182. }
  183. assert(this->AllResourcesAvailable());
  184. this->MarkFinished();
  185. this->UpdateCostData();
  186. }
  187. void cmCTestMultiProcessHandler::StartTestProcess(int test)
  188. {
  189. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  190. "test " << test << "\n", this->Quiet);
  191. auto testRun = cm::make_unique<cmCTestRunTest>(*this, test);
  192. if (this->RepeatMode != cmCTest::Repeat::Never) {
  193. testRun->SetRepeatMode(this->RepeatMode);
  194. testRun->SetNumberOfRuns(this->RepeatCount);
  195. }
  196. if (this->UseResourceSpec) {
  197. testRun->SetUseAllocatedResources(true);
  198. testRun->SetAllocatedResources(this->AllocatedResources[test]);
  199. }
  200. // Find any failed dependencies for this test. We assume the more common
  201. // scenario has no failed tests, so make it the outer loop.
  202. for (std::string const& f : *this->Failed) {
  203. if (cm::contains(this->Properties[test]->RequireSuccessDepends, f)) {
  204. testRun->AddFailedDependency(f);
  205. }
  206. }
  207. if (!this->ResourceAvailabilityErrors[test].empty()) {
  208. std::ostringstream e;
  209. e << "Insufficient resources for test " << this->Properties[test]->Name
  210. << ":\n\n";
  211. for (auto const& it : this->ResourceAvailabilityErrors[test]) {
  212. switch (it.second) {
  213. case ResourceAvailabilityError::NoResourceType:
  214. e << " Test requested resources of type '" << it.first
  215. << "' which does not exist\n";
  216. break;
  217. case ResourceAvailabilityError::InsufficientResources:
  218. e << " Test requested resources of type '" << it.first
  219. << "' in the following amounts:\n";
  220. for (auto const& group : this->Properties[test]->ResourceGroups) {
  221. for (auto const& requirement : group) {
  222. if (requirement.ResourceType == it.first) {
  223. e << " " << requirement.SlotsNeeded
  224. << (requirement.SlotsNeeded == 1 ? " slot\n" : " slots\n");
  225. }
  226. }
  227. }
  228. e << " but only the following units were available:\n";
  229. for (auto const& res :
  230. this->ResourceAllocator.GetResources().at(it.first)) {
  231. e << " '" << res.first << "': " << res.second.Total
  232. << (res.second.Total == 1 ? " slot\n" : " slots\n");
  233. }
  234. break;
  235. }
  236. e << "\n";
  237. }
  238. e << "Resource spec file:\n\n " << this->ResourceSpecFile;
  239. cmCTestRunTest::StartFailure(std::move(testRun), this->Total, e.str(),
  240. "Insufficient resources");
  241. return;
  242. }
  243. cmWorkingDirectory workdir(this->Properties[test]->Directory);
  244. if (workdir.Failed()) {
  245. cmCTestRunTest::StartFailure(std::move(testRun), this->Total,
  246. "Failed to change working directory to " +
  247. this->Properties[test]->Directory + " : " +
  248. std::strerror(workdir.GetLastResult()),
  249. "Failed to change working directory");
  250. return;
  251. }
  252. // Ownership of 'testRun' has moved to another structure.
  253. // When the test finishes, FinishTestProcess will be called.
  254. cmCTestRunTest::StartTest(std::move(testRun), this->Completed, this->Total);
  255. }
  256. bool cmCTestMultiProcessHandler::AllocateResources(int index)
  257. {
  258. if (!this->UseResourceSpec) {
  259. return true;
  260. }
  261. // If the test needs unavailable resources then do not allocate anything
  262. // because it will never run. We will issue the recorded errors instead.
  263. if (!this->ResourceAvailabilityErrors[index].empty()) {
  264. return true;
  265. }
  266. std::map<std::string, std::vector<cmCTestBinPackerAllocation>> allocations;
  267. if (!this->TryAllocateResources(index, allocations)) {
  268. return false;
  269. }
  270. auto& allocatedResources = this->AllocatedResources[index];
  271. allocatedResources.resize(this->Properties[index]->ResourceGroups.size());
  272. for (auto const& it : allocations) {
  273. for (auto const& alloc : it.second) {
  274. bool result = this->ResourceAllocator.AllocateResource(
  275. it.first, alloc.Id, alloc.SlotsNeeded);
  276. (void)result;
  277. assert(result);
  278. allocatedResources[alloc.ProcessIndex][it.first].push_back(
  279. { alloc.Id, static_cast<unsigned int>(alloc.SlotsNeeded) });
  280. }
  281. }
  282. return true;
  283. }
  284. bool cmCTestMultiProcessHandler::TryAllocateResources(
  285. int index,
  286. std::map<std::string, std::vector<cmCTestBinPackerAllocation>>& allocations,
  287. std::map<std::string, ResourceAvailabilityError>* errors)
  288. {
  289. allocations.clear();
  290. std::size_t processIndex = 0;
  291. for (auto const& process : this->Properties[index]->ResourceGroups) {
  292. for (auto const& requirement : process) {
  293. for (int i = 0; i < requirement.UnitsNeeded; ++i) {
  294. allocations[requirement.ResourceType].push_back(
  295. { processIndex, requirement.SlotsNeeded, "" });
  296. }
  297. }
  298. ++processIndex;
  299. }
  300. bool result = true;
  301. auto const& availableResources = this->ResourceAllocator.GetResources();
  302. for (auto& it : allocations) {
  303. if (!availableResources.count(it.first)) {
  304. if (errors) {
  305. (*errors)[it.first] = ResourceAvailabilityError::NoResourceType;
  306. result = false;
  307. } else {
  308. return false;
  309. }
  310. } else if (!cmAllocateCTestResourcesRoundRobin(
  311. availableResources.at(it.first), it.second)) {
  312. if (errors) {
  313. (*errors)[it.first] = ResourceAvailabilityError::InsufficientResources;
  314. result = false;
  315. } else {
  316. return false;
  317. }
  318. }
  319. }
  320. return result;
  321. }
  322. void cmCTestMultiProcessHandler::DeallocateResources(int index)
  323. {
  324. if (!this->UseResourceSpec) {
  325. return;
  326. }
  327. {
  328. auto& allocatedResources = this->AllocatedResources[index];
  329. for (auto const& processAlloc : allocatedResources) {
  330. for (auto const& it : processAlloc) {
  331. auto resourceType = it.first;
  332. for (auto const& it2 : it.second) {
  333. bool success = this->ResourceAllocator.DeallocateResource(
  334. resourceType, it2.Id, it2.Slots);
  335. (void)success;
  336. assert(success);
  337. }
  338. }
  339. }
  340. }
  341. this->AllocatedResources.erase(index);
  342. }
  343. bool cmCTestMultiProcessHandler::AllResourcesAvailable()
  344. {
  345. for (auto const& it : this->ResourceAllocator.GetResources()) {
  346. for (auto const& it2 : it.second) {
  347. if (it2.second.Locked != 0) {
  348. return false;
  349. }
  350. }
  351. }
  352. return true;
  353. }
  354. void cmCTestMultiProcessHandler::CheckResourceAvailability()
  355. {
  356. if (this->UseResourceSpec) {
  357. for (auto const& t : this->PendingTests) {
  358. std::map<std::string, std::vector<cmCTestBinPackerAllocation>>
  359. allocations;
  360. this->TryAllocateResources(t.first, allocations,
  361. &this->ResourceAvailabilityErrors[t.first]);
  362. }
  363. }
  364. }
  365. bool cmCTestMultiProcessHandler::CheckStopOnFailure()
  366. {
  367. return this->CTest->GetStopOnFailure();
  368. }
  369. bool cmCTestMultiProcessHandler::CheckStopTimePassed()
  370. {
  371. if (!this->StopTimePassed) {
  372. std::chrono::system_clock::time_point stop_time =
  373. this->CTest->GetStopTime();
  374. if (stop_time != std::chrono::system_clock::time_point() &&
  375. stop_time <= std::chrono::system_clock::now()) {
  376. this->SetStopTimePassed();
  377. }
  378. }
  379. return this->StopTimePassed;
  380. }
  381. void cmCTestMultiProcessHandler::SetStopTimePassed()
  382. {
  383. if (!this->StopTimePassed) {
  384. cmCTestLog(this->CTest, ERROR_MESSAGE,
  385. "The stop time has been passed. "
  386. "Stopping all tests."
  387. << std::endl);
  388. this->StopTimePassed = true;
  389. }
  390. }
  391. void cmCTestMultiProcessHandler::LockResources(int index)
  392. {
  393. this->RunningCount += this->GetProcessorsUsed(index);
  394. auto* properties = this->Properties[index];
  395. this->ProjectResourcesLocked.insert(properties->ProjectResources.begin(),
  396. properties->ProjectResources.end());
  397. if (properties->RunSerial) {
  398. this->SerialTestRunning = true;
  399. }
  400. if (this->HaveAffinity && properties->WantAffinity) {
  401. size_t needProcessors = this->GetProcessorsUsed(index);
  402. assert(needProcessors <= this->ProcessorsAvailable.size());
  403. std::vector<size_t> affinity;
  404. affinity.reserve(needProcessors);
  405. for (size_t i = 0; i < needProcessors; ++i) {
  406. auto p = this->ProcessorsAvailable.begin();
  407. affinity.push_back(*p);
  408. this->ProcessorsAvailable.erase(p);
  409. }
  410. properties->Affinity = std::move(affinity);
  411. }
  412. }
  413. void cmCTestMultiProcessHandler::UnlockResources(int index)
  414. {
  415. auto* properties = this->Properties[index];
  416. for (auto p : properties->Affinity) {
  417. this->ProcessorsAvailable.insert(p);
  418. }
  419. properties->Affinity.clear();
  420. for (std::string const& i : properties->ProjectResources) {
  421. this->ProjectResourcesLocked.erase(i);
  422. }
  423. if (properties->RunSerial) {
  424. this->SerialTestRunning = false;
  425. }
  426. this->RunningCount -= this->GetProcessorsUsed(index);
  427. }
  428. inline size_t cmCTestMultiProcessHandler::GetProcessorsUsed(int test)
  429. {
  430. size_t processors = static_cast<int>(this->Properties[test]->Processors);
  431. size_t const parallelLevel = this->GetParallelLevel();
  432. // If processors setting is set higher than the -j
  433. // setting, we default to using all of the process slots.
  434. if (processors > parallelLevel) {
  435. processors = parallelLevel;
  436. }
  437. // Cap tests that want affinity to the maximum affinity available.
  438. if (this->HaveAffinity && processors > this->HaveAffinity &&
  439. this->Properties[test]->WantAffinity) {
  440. processors = this->HaveAffinity;
  441. }
  442. return processors;
  443. }
  444. std::string cmCTestMultiProcessHandler::GetName(int test)
  445. {
  446. return this->Properties[test]->Name;
  447. }
  448. void cmCTestMultiProcessHandler::StartTest(int test)
  449. {
  450. if (this->JobServerClient) {
  451. // There is a job server. Request a token and queue the test to run
  452. // when a token is received. Note that if we do not get a token right
  453. // away it's possible that the system load will be higher when the
  454. // token is received and we may violate the test-load limit. However,
  455. // this is unlikely because if we do not get a token right away, some
  456. // other job that's currently running must finish before we get one.
  457. this->JobServerClient->RequestToken();
  458. this->JobServerQueuedTests.emplace_back(test);
  459. } else {
  460. // There is no job server. Start the test now.
  461. this->StartTestProcess(test);
  462. }
  463. }
  464. void cmCTestMultiProcessHandler::JobServerReceivedToken()
  465. {
  466. assert(!this->JobServerQueuedTests.empty());
  467. int test = this->JobServerQueuedTests.front();
  468. this->JobServerQueuedTests.pop_front();
  469. this->StartTestProcess(test);
  470. }
  471. void cmCTestMultiProcessHandler::StartNextTests()
  472. {
  473. // One or more events may be scheduled to call this method again.
  474. // Since this method has been called they are no longer needed.
  475. this->StartNextTestsOnIdle_.stop();
  476. this->StartNextTestsOnTimer_.stop();
  477. if (this->PendingTests.empty() || this->CheckStopTimePassed() ||
  478. (this->CheckStopOnFailure() && !this->Failed->empty())) {
  479. return;
  480. }
  481. size_t numToStart = 0;
  482. size_t const parallelLevel = this->GetParallelLevel();
  483. if (this->RunningCount < parallelLevel) {
  484. numToStart = parallelLevel - this->RunningCount;
  485. }
  486. if (numToStart == 0) {
  487. return;
  488. }
  489. // Don't start any new tests if one with the RUN_SERIAL property
  490. // is already running.
  491. if (this->SerialTestRunning) {
  492. return;
  493. }
  494. bool allTestsFailedTestLoadCheck = false;
  495. size_t minProcessorsRequired = this->GetParallelLevel();
  496. std::string testWithMinProcessors;
  497. cmsys::SystemInformation info;
  498. unsigned long systemLoad = 0;
  499. size_t spareLoad = 0;
  500. if (this->TestLoad > 0) {
  501. // Activate possible wait.
  502. allTestsFailedTestLoadCheck = true;
  503. // Check for a fake load average value used in testing.
  504. if (this->FakeLoadForTesting > 0) {
  505. systemLoad = this->FakeLoadForTesting;
  506. // Drop the fake load for the next iteration to a value low enough
  507. // that the next iteration will start tests.
  508. this->FakeLoadForTesting = 1;
  509. }
  510. // If it's not set, look up the true load average.
  511. else {
  512. systemLoad = static_cast<unsigned long>(ceil(info.GetLoadAverage()));
  513. }
  514. spareLoad =
  515. (this->TestLoad > systemLoad ? this->TestLoad - systemLoad : 0);
  516. // Don't start more tests than the spare load can support.
  517. if (numToStart > spareLoad) {
  518. numToStart = spareLoad;
  519. }
  520. }
  521. // Start tests in the preferred order, each subject to readiness checks.
  522. auto ti = this->OrderedTests.begin();
  523. while (numToStart > 0 && !this->SerialTestRunning &&
  524. ti != this->OrderedTests.end()) {
  525. // Increment the test iterator now because the current list
  526. // entry may be deleted below.
  527. auto cti = ti++;
  528. int test = *cti;
  529. // We can only start a RUN_SERIAL test if no other tests are also
  530. // running.
  531. if (this->Properties[test]->RunSerial && this->RunningCount > 0) {
  532. continue;
  533. }
  534. // Exclude tests that depend on unfinished tests.
  535. if (!this->PendingTests[test].Depends.empty()) {
  536. continue;
  537. }
  538. size_t processors = this->GetProcessorsUsed(test);
  539. if (this->TestLoad > 0) {
  540. // Exclude tests that are too big to fit in the spare load.
  541. if (processors > spareLoad) {
  542. // Keep track of the smallest excluded test to report in message below.
  543. if (processors <= minProcessorsRequired) {
  544. minProcessorsRequired = processors;
  545. testWithMinProcessors = this->GetName(test);
  546. }
  547. continue;
  548. }
  549. // We found a test that fits in the spare load.
  550. allTestsFailedTestLoadCheck = false;
  551. cmCTestLog(this->CTest, DEBUG,
  552. "OK to run "
  553. << this->GetName(test) << ", it requires " << processors
  554. << " procs & system load is: " << systemLoad << std::endl);
  555. }
  556. // Exclude tests that are too big to fit in the concurrency limit.
  557. if (processors > numToStart) {
  558. continue;
  559. }
  560. // Exclude tests that depend on currently-locked project resources.
  561. for (std::string const& i : this->Properties[test]->ProjectResources) {
  562. if (cm::contains(this->ProjectResourcesLocked, i)) {
  563. continue;
  564. }
  565. }
  566. // Allocate system resources needed by this test.
  567. if (!this->AllocateResources(test)) {
  568. continue;
  569. }
  570. // Lock resources needed by this test.
  571. this->LockResources(test);
  572. // The test is ready to run.
  573. numToStart -= processors;
  574. this->OrderedTests.erase(cti);
  575. this->PendingTests.erase(test);
  576. this->StartTest(test);
  577. }
  578. if (allTestsFailedTestLoadCheck) {
  579. // Find out whether there are any non RUN_SERIAL tests left, so that the
  580. // correct warning may be displayed.
  581. bool onlyRunSerialTestsLeft = true;
  582. for (auto const& t : this->PendingTests) {
  583. if (!this->Properties[t.first]->RunSerial) {
  584. onlyRunSerialTestsLeft = false;
  585. }
  586. }
  587. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "***** WAITING, ");
  588. if (this->SerialTestRunning) {
  589. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  590. "Waiting for RUN_SERIAL test to finish.");
  591. } else if (onlyRunSerialTestsLeft) {
  592. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  593. "Only RUN_SERIAL tests remain, awaiting available slot.");
  594. } else if (!testWithMinProcessors.empty()) {
  595. /* clang-format off */
  596. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  597. "System Load: " << systemLoad << ", "
  598. "Max Allowed Load: " << this->TestLoad << ", "
  599. "Smallest test " << testWithMinProcessors <<
  600. " requires " << minProcessorsRequired);
  601. /* clang-format on */
  602. } else {
  603. /* clang-format off */
  604. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  605. "System Load: " << systemLoad << ", "
  606. "Max Allowed Load: " << this->TestLoad);
  607. /* clang-format on */
  608. }
  609. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "*****" << std::endl);
  610. // Try again later when the load might be lower.
  611. this->StartNextTestsOnTimer();
  612. }
  613. }
  614. void cmCTestMultiProcessHandler::StartNextTestsOnIdle()
  615. {
  616. // Start more tests on the next loop iteration.
  617. this->StartNextTestsOnIdle_.start([](uv_idle_t* idle) {
  618. uv_idle_stop(idle);
  619. auto* self = static_cast<cmCTestMultiProcessHandler*>(idle->data);
  620. self->StartNextTests();
  621. });
  622. }
  623. void cmCTestMultiProcessHandler::StartNextTestsOnTimer()
  624. {
  625. // Wait between 1 and 5 seconds before trying again.
  626. unsigned int const milliseconds = this->FakeLoadForTesting
  627. ? 10
  628. : (cmSystemTools::RandomSeed() % 5 + 1) * 1000;
  629. this->StartNextTestsOnTimer_.start(
  630. [](uv_timer_t* timer) {
  631. uv_timer_stop(timer);
  632. auto* self = static_cast<cmCTestMultiProcessHandler*>(timer->data);
  633. self->StartNextTests();
  634. },
  635. milliseconds, 0);
  636. }
  637. void cmCTestMultiProcessHandler::FinishTestProcess(
  638. std::unique_ptr<cmCTestRunTest> runner, bool started)
  639. {
  640. this->Completed++;
  641. int test = runner->GetIndex();
  642. auto* properties = runner->GetTestProperties();
  643. cmCTestRunTest::EndTestResult testResult =
  644. runner->EndTest(this->Completed, this->Total, started);
  645. if (testResult.StopTimePassed) {
  646. this->SetStopTimePassed();
  647. }
  648. if (started) {
  649. if (!this->StopTimePassed &&
  650. cmCTestRunTest::StartAgain(std::move(runner), this->Completed)) {
  651. this->Completed--; // remove the completed test because run again
  652. return;
  653. }
  654. }
  655. if (testResult.Passed) {
  656. this->Passed->push_back(properties->Name);
  657. } else if (!properties->Disabled) {
  658. this->Failed->push_back(properties->Name);
  659. }
  660. for (auto& t : this->PendingTests) {
  661. t.second.Depends.erase(test);
  662. }
  663. this->WriteCheckpoint(test);
  664. this->DeallocateResources(test);
  665. this->UnlockResources(test);
  666. runner.reset();
  667. if (this->JobServerClient) {
  668. this->JobServerClient->ReleaseToken();
  669. }
  670. this->StartNextTestsOnIdle();
  671. }
  672. void cmCTestMultiProcessHandler::UpdateCostData()
  673. {
  674. std::string fname = this->CTest->GetCostDataFile();
  675. std::string tmpout = fname + ".tmp";
  676. cmsys::ofstream fout;
  677. fout.open(tmpout.c_str());
  678. PropertiesMap temp = this->Properties;
  679. if (cmSystemTools::FileExists(fname)) {
  680. cmsys::ifstream fin;
  681. fin.open(fname.c_str());
  682. std::string line;
  683. while (std::getline(fin, line)) {
  684. if (line == "---") {
  685. break;
  686. }
  687. std::vector<std::string> parts = cmSystemTools::SplitString(line, ' ');
  688. // Format: <name> <previous_runs> <avg_cost>
  689. if (parts.size() < 3) {
  690. break;
  691. }
  692. std::string name = parts[0];
  693. int prev = atoi(parts[1].c_str());
  694. float cost = static_cast<float>(atof(parts[2].c_str()));
  695. int index = this->SearchByName(name);
  696. if (index == -1) {
  697. // This test is not in memory. We just rewrite the entry
  698. fout << name << " " << prev << " " << cost << "\n";
  699. } else {
  700. // Update with our new average cost
  701. fout << name << " " << this->Properties[index]->PreviousRuns << " "
  702. << this->Properties[index]->Cost << "\n";
  703. temp.erase(index);
  704. }
  705. }
  706. fin.close();
  707. cmSystemTools::RemoveFile(fname);
  708. }
  709. // Add all tests not previously listed in the file
  710. for (auto const& i : temp) {
  711. fout << i.second->Name << " " << i.second->PreviousRuns << " "
  712. << i.second->Cost << "\n";
  713. }
  714. // Write list of failed tests
  715. fout << "---\n";
  716. for (std::string const& f : *this->Failed) {
  717. fout << f << "\n";
  718. }
  719. fout.close();
  720. cmSystemTools::RenameFile(tmpout, fname);
  721. }
  722. void cmCTestMultiProcessHandler::ReadCostData()
  723. {
  724. std::string fname = this->CTest->GetCostDataFile();
  725. if (cmSystemTools::FileExists(fname, true)) {
  726. cmsys::ifstream fin;
  727. fin.open(fname.c_str());
  728. std::string line;
  729. while (std::getline(fin, line)) {
  730. if (line == "---") {
  731. break;
  732. }
  733. std::vector<std::string> parts = cmSystemTools::SplitString(line, ' ');
  734. // Probably an older version of the file, will be fixed next run
  735. if (parts.size() < 3) {
  736. fin.close();
  737. return;
  738. }
  739. std::string name = parts[0];
  740. int prev = atoi(parts[1].c_str());
  741. float cost = static_cast<float>(atof(parts[2].c_str()));
  742. int index = this->SearchByName(name);
  743. if (index == -1) {
  744. continue;
  745. }
  746. this->Properties[index]->PreviousRuns = prev;
  747. // When not running in parallel mode, don't use cost data
  748. if (this->GetParallelLevel() > 1 && this->Properties[index] &&
  749. this->Properties[index]->Cost == 0) {
  750. this->Properties[index]->Cost = cost;
  751. }
  752. }
  753. // Next part of the file is the failed tests
  754. while (std::getline(fin, line)) {
  755. if (!line.empty()) {
  756. this->LastTestsFailed.push_back(line);
  757. }
  758. }
  759. fin.close();
  760. }
  761. }
  762. int cmCTestMultiProcessHandler::SearchByName(std::string const& name)
  763. {
  764. int index = -1;
  765. for (auto const& p : this->Properties) {
  766. if (p.second->Name == name) {
  767. index = p.first;
  768. }
  769. }
  770. return index;
  771. }
  772. void cmCTestMultiProcessHandler::CreateTestCostList()
  773. {
  774. if (this->GetParallelLevel() > 1) {
  775. this->CreateParallelTestCostList();
  776. } else {
  777. this->CreateSerialTestCostList();
  778. }
  779. }
  780. void cmCTestMultiProcessHandler::CreateParallelTestCostList()
  781. {
  782. TestSet alreadyOrderedTests;
  783. std::list<TestSet> priorityStack;
  784. priorityStack.emplace_back();
  785. TestSet& topLevel = priorityStack.back();
  786. // In parallel test runs add previously failed tests to the front
  787. // of the cost list and queue other tests for further sorting
  788. for (auto const& t : this->PendingTests) {
  789. if (cm::contains(this->LastTestsFailed, this->Properties[t.first]->Name)) {
  790. // If the test failed last time, it should be run first.
  791. this->OrderedTests.push_back(t.first);
  792. alreadyOrderedTests.insert(t.first);
  793. } else {
  794. topLevel.insert(t.first);
  795. }
  796. }
  797. // In parallel test runs repeatedly move dependencies of the tests on
  798. // the current dependency level to the next level until no
  799. // further dependencies exist.
  800. while (!priorityStack.back().empty()) {
  801. TestSet& previousSet = priorityStack.back();
  802. priorityStack.emplace_back();
  803. TestSet& currentSet = priorityStack.back();
  804. for (auto const& i : previousSet) {
  805. TestSet const& dependencies = this->PendingTests[i].Depends;
  806. currentSet.insert(dependencies.begin(), dependencies.end());
  807. }
  808. for (auto const& i : currentSet) {
  809. previousSet.erase(i);
  810. }
  811. }
  812. // Remove the empty dependency level
  813. priorityStack.pop_back();
  814. // Reverse iterate over the different dependency levels (deepest first).
  815. // Sort tests within each level by COST and append them to the cost list.
  816. for (TestSet const& currentSet : cmReverseRange(priorityStack)) {
  817. TestList sortedCopy;
  818. cm::append(sortedCopy, currentSet);
  819. std::stable_sort(sortedCopy.begin(), sortedCopy.end(),
  820. TestComparator(this));
  821. for (auto const& j : sortedCopy) {
  822. if (!cm::contains(alreadyOrderedTests, j)) {
  823. this->OrderedTests.push_back(j);
  824. alreadyOrderedTests.insert(j);
  825. }
  826. }
  827. }
  828. }
  829. void cmCTestMultiProcessHandler::GetAllTestDependencies(int test,
  830. TestList& dependencies)
  831. {
  832. TestSet const& dependencySet = this->PendingTests[test].Depends;
  833. for (int i : dependencySet) {
  834. this->GetAllTestDependencies(i, dependencies);
  835. dependencies.push_back(i);
  836. }
  837. }
  838. void cmCTestMultiProcessHandler::CreateSerialTestCostList()
  839. {
  840. TestList presortedList;
  841. for (auto const& i : this->PendingTests) {
  842. presortedList.push_back(i.first);
  843. }
  844. std::stable_sort(presortedList.begin(), presortedList.end(),
  845. TestComparator(this));
  846. TestSet alreadyOrderedTests;
  847. for (int test : presortedList) {
  848. if (cm::contains(alreadyOrderedTests, test)) {
  849. continue;
  850. }
  851. TestList dependencies;
  852. this->GetAllTestDependencies(test, dependencies);
  853. for (int testDependency : dependencies) {
  854. if (!cm::contains(alreadyOrderedTests, testDependency)) {
  855. alreadyOrderedTests.insert(testDependency);
  856. this->OrderedTests.push_back(testDependency);
  857. }
  858. }
  859. alreadyOrderedTests.insert(test);
  860. this->OrderedTests.push_back(test);
  861. }
  862. }
  863. void cmCTestMultiProcessHandler::WriteCheckpoint(int index)
  864. {
  865. std::string fname =
  866. this->CTest->GetBinaryDir() + "/Testing/Temporary/CTestCheckpoint.txt";
  867. cmsys::ofstream fout;
  868. fout.open(fname.c_str(), std::ios::app);
  869. fout << index << "\n";
  870. fout.close();
  871. }
  872. void cmCTestMultiProcessHandler::MarkFinished()
  873. {
  874. std::string fname =
  875. this->CTest->GetBinaryDir() + "/Testing/Temporary/CTestCheckpoint.txt";
  876. cmSystemTools::RemoveFile(fname);
  877. }
  878. static Json::Value DumpToJsonArray(const std::set<std::string>& values)
  879. {
  880. Json::Value jsonArray = Json::arrayValue;
  881. for (const auto& it : values) {
  882. jsonArray.append(it);
  883. }
  884. return jsonArray;
  885. }
  886. static Json::Value DumpToJsonArray(const std::vector<std::string>& values)
  887. {
  888. Json::Value jsonArray = Json::arrayValue;
  889. for (const auto& it : values) {
  890. jsonArray.append(it);
  891. }
  892. return jsonArray;
  893. }
  894. static Json::Value DumpRegExToJsonArray(
  895. const std::vector<std::pair<cmsys::RegularExpression, std::string>>& values)
  896. {
  897. Json::Value jsonArray = Json::arrayValue;
  898. for (const auto& it : values) {
  899. jsonArray.append(it.second);
  900. }
  901. return jsonArray;
  902. }
  903. static Json::Value DumpMeasurementToJsonArray(
  904. const std::map<std::string, std::string>& values)
  905. {
  906. Json::Value jsonArray = Json::arrayValue;
  907. for (const auto& it : values) {
  908. Json::Value measurement = Json::objectValue;
  909. measurement["measurement"] = it.first;
  910. measurement["value"] = it.second;
  911. jsonArray.append(measurement);
  912. }
  913. return jsonArray;
  914. }
  915. static Json::Value DumpTimeoutAfterMatch(
  916. cmCTestTestHandler::cmCTestTestProperties& testProperties)
  917. {
  918. Json::Value timeoutAfterMatch = Json::objectValue;
  919. timeoutAfterMatch["timeout"] = testProperties.AlternateTimeout.count();
  920. timeoutAfterMatch["regex"] =
  921. DumpRegExToJsonArray(testProperties.TimeoutRegularExpressions);
  922. return timeoutAfterMatch;
  923. }
  924. static Json::Value DumpResourceGroupsToJsonArray(
  925. const std::vector<
  926. std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>&
  927. resourceGroups)
  928. {
  929. Json::Value jsonResourceGroups = Json::arrayValue;
  930. for (auto const& it : resourceGroups) {
  931. Json::Value jsonResourceGroup = Json::objectValue;
  932. Json::Value requirements = Json::arrayValue;
  933. for (auto const& it2 : it) {
  934. Json::Value res = Json::objectValue;
  935. res[".type"] = it2.ResourceType;
  936. // res[".units"] = it2.UnitsNeeded; // Intentionally commented out
  937. res["slots"] = it2.SlotsNeeded;
  938. requirements.append(res);
  939. }
  940. jsonResourceGroup["requirements"] = requirements;
  941. jsonResourceGroups.append(jsonResourceGroup);
  942. }
  943. return jsonResourceGroups;
  944. }
  945. static Json::Value DumpCTestProperty(std::string const& name,
  946. Json::Value value)
  947. {
  948. Json::Value property = Json::objectValue;
  949. property["name"] = name;
  950. property["value"] = std::move(value);
  951. return property;
  952. }
  953. static Json::Value DumpCTestProperties(
  954. cmCTestTestHandler::cmCTestTestProperties& testProperties)
  955. {
  956. Json::Value properties = Json::arrayValue;
  957. if (!testProperties.AttachOnFail.empty()) {
  958. properties.append(DumpCTestProperty(
  959. "ATTACHED_FILES_ON_FAIL", DumpToJsonArray(testProperties.AttachOnFail)));
  960. }
  961. if (!testProperties.AttachedFiles.empty()) {
  962. properties.append(DumpCTestProperty(
  963. "ATTACHED_FILES", DumpToJsonArray(testProperties.AttachedFiles)));
  964. }
  965. if (testProperties.Cost != 0.0f) {
  966. properties.append(
  967. DumpCTestProperty("COST", static_cast<double>(testProperties.Cost)));
  968. }
  969. if (!testProperties.Depends.empty()) {
  970. properties.append(
  971. DumpCTestProperty("DEPENDS", DumpToJsonArray(testProperties.Depends)));
  972. }
  973. if (testProperties.Disabled) {
  974. properties.append(DumpCTestProperty("DISABLED", testProperties.Disabled));
  975. }
  976. if (!testProperties.Environment.empty()) {
  977. properties.append(DumpCTestProperty(
  978. "ENVIRONMENT", DumpToJsonArray(testProperties.Environment)));
  979. }
  980. if (!testProperties.EnvironmentModification.empty()) {
  981. properties.append(DumpCTestProperty(
  982. "ENVIRONMENT_MODIFICATION",
  983. DumpToJsonArray(testProperties.EnvironmentModification)));
  984. }
  985. if (!testProperties.ErrorRegularExpressions.empty()) {
  986. properties.append(DumpCTestProperty(
  987. "FAIL_REGULAR_EXPRESSION",
  988. DumpRegExToJsonArray(testProperties.ErrorRegularExpressions)));
  989. }
  990. if (!testProperties.SkipRegularExpressions.empty()) {
  991. properties.append(DumpCTestProperty(
  992. "SKIP_REGULAR_EXPRESSION",
  993. DumpRegExToJsonArray(testProperties.SkipRegularExpressions)));
  994. }
  995. if (!testProperties.FixturesCleanup.empty()) {
  996. properties.append(DumpCTestProperty(
  997. "FIXTURES_CLEANUP", DumpToJsonArray(testProperties.FixturesCleanup)));
  998. }
  999. if (!testProperties.FixturesRequired.empty()) {
  1000. properties.append(DumpCTestProperty(
  1001. "FIXTURES_REQUIRED", DumpToJsonArray(testProperties.FixturesRequired)));
  1002. }
  1003. if (!testProperties.FixturesSetup.empty()) {
  1004. properties.append(DumpCTestProperty(
  1005. "FIXTURES_SETUP", DumpToJsonArray(testProperties.FixturesSetup)));
  1006. }
  1007. if (!testProperties.Labels.empty()) {
  1008. properties.append(
  1009. DumpCTestProperty("LABELS", DumpToJsonArray(testProperties.Labels)));
  1010. }
  1011. if (!testProperties.Measurements.empty()) {
  1012. properties.append(DumpCTestProperty(
  1013. "MEASUREMENT", DumpMeasurementToJsonArray(testProperties.Measurements)));
  1014. }
  1015. if (!testProperties.RequiredRegularExpressions.empty()) {
  1016. properties.append(DumpCTestProperty(
  1017. "PASS_REGULAR_EXPRESSION",
  1018. DumpRegExToJsonArray(testProperties.RequiredRegularExpressions)));
  1019. }
  1020. if (!testProperties.ResourceGroups.empty()) {
  1021. properties.append(DumpCTestProperty(
  1022. "RESOURCE_GROUPS",
  1023. DumpResourceGroupsToJsonArray(testProperties.ResourceGroups)));
  1024. }
  1025. if (testProperties.WantAffinity) {
  1026. properties.append(
  1027. DumpCTestProperty("PROCESSOR_AFFINITY", testProperties.WantAffinity));
  1028. }
  1029. if (testProperties.Processors != 1) {
  1030. properties.append(
  1031. DumpCTestProperty("PROCESSORS", testProperties.Processors));
  1032. }
  1033. if (!testProperties.RequiredFiles.empty()) {
  1034. properties.append(DumpCTestProperty(
  1035. "REQUIRED_FILES", DumpToJsonArray(testProperties.RequiredFiles)));
  1036. }
  1037. if (!testProperties.ProjectResources.empty()) {
  1038. properties.append(DumpCTestProperty(
  1039. "RESOURCE_LOCK", DumpToJsonArray(testProperties.ProjectResources)));
  1040. }
  1041. if (testProperties.RunSerial) {
  1042. properties.append(
  1043. DumpCTestProperty("RUN_SERIAL", testProperties.RunSerial));
  1044. }
  1045. if (testProperties.SkipReturnCode != -1) {
  1046. properties.append(
  1047. DumpCTestProperty("SKIP_RETURN_CODE", testProperties.SkipReturnCode));
  1048. }
  1049. if (testProperties.Timeout) {
  1050. properties.append(
  1051. DumpCTestProperty("TIMEOUT", testProperties.Timeout->count()));
  1052. }
  1053. if (!testProperties.TimeoutRegularExpressions.empty()) {
  1054. properties.append(DumpCTestProperty(
  1055. "TIMEOUT_AFTER_MATCH", DumpTimeoutAfterMatch(testProperties)));
  1056. }
  1057. if (testProperties.WillFail) {
  1058. properties.append(DumpCTestProperty("WILL_FAIL", testProperties.WillFail));
  1059. }
  1060. if (!testProperties.Directory.empty()) {
  1061. properties.append(
  1062. DumpCTestProperty("WORKING_DIRECTORY", testProperties.Directory));
  1063. }
  1064. if (!testProperties.CustomProperties.empty()) {
  1065. for (auto const& it : testProperties.CustomProperties) {
  1066. properties.append(DumpCTestProperty(it.first, it.second));
  1067. }
  1068. }
  1069. return properties;
  1070. }
  1071. class BacktraceData
  1072. {
  1073. std::unordered_map<std::string, Json::ArrayIndex> CommandMap;
  1074. std::unordered_map<std::string, Json::ArrayIndex> FileMap;
  1075. std::unordered_map<cmListFileContext const*, Json::ArrayIndex> NodeMap;
  1076. Json::Value Commands = Json::arrayValue;
  1077. Json::Value Files = Json::arrayValue;
  1078. Json::Value Nodes = Json::arrayValue;
  1079. Json::ArrayIndex AddCommand(std::string const& command)
  1080. {
  1081. auto i = this->CommandMap.find(command);
  1082. if (i == this->CommandMap.end()) {
  1083. i = this->CommandMap.emplace(command, this->Commands.size()).first;
  1084. this->Commands.append(command);
  1085. }
  1086. return i->second;
  1087. }
  1088. Json::ArrayIndex AddFile(std::string const& file)
  1089. {
  1090. auto i = this->FileMap.find(file);
  1091. if (i == this->FileMap.end()) {
  1092. i = this->FileMap.emplace(file, this->Files.size()).first;
  1093. this->Files.append(file);
  1094. }
  1095. return i->second;
  1096. }
  1097. public:
  1098. bool Add(cmListFileBacktrace const& bt, Json::ArrayIndex& index);
  1099. Json::Value Dump();
  1100. };
  1101. bool BacktraceData::Add(cmListFileBacktrace const& bt, Json::ArrayIndex& index)
  1102. {
  1103. if (bt.Empty()) {
  1104. return false;
  1105. }
  1106. cmListFileContext const* top = &bt.Top();
  1107. auto found = this->NodeMap.find(top);
  1108. if (found != this->NodeMap.end()) {
  1109. index = found->second;
  1110. return true;
  1111. }
  1112. Json::Value entry = Json::objectValue;
  1113. entry["file"] = this->AddFile(top->FilePath);
  1114. if (top->Line) {
  1115. entry["line"] = static_cast<int>(top->Line);
  1116. }
  1117. if (!top->Name.empty()) {
  1118. entry["command"] = this->AddCommand(top->Name);
  1119. }
  1120. Json::ArrayIndex parent;
  1121. if (this->Add(bt.Pop(), parent)) {
  1122. entry["parent"] = parent;
  1123. }
  1124. index = this->NodeMap[top] = this->Nodes.size();
  1125. this->Nodes.append(std::move(entry)); // NOLINT(*)
  1126. return true;
  1127. }
  1128. Json::Value BacktraceData::Dump()
  1129. {
  1130. Json::Value backtraceGraph;
  1131. this->CommandMap.clear();
  1132. this->FileMap.clear();
  1133. this->NodeMap.clear();
  1134. backtraceGraph["commands"] = std::move(this->Commands);
  1135. backtraceGraph["files"] = std::move(this->Files);
  1136. backtraceGraph["nodes"] = std::move(this->Nodes);
  1137. return backtraceGraph;
  1138. }
  1139. static void AddBacktrace(BacktraceData& backtraceGraph, Json::Value& object,
  1140. cmListFileBacktrace const& bt)
  1141. {
  1142. Json::ArrayIndex backtrace;
  1143. if (backtraceGraph.Add(bt, backtrace)) {
  1144. object["backtrace"] = backtrace;
  1145. }
  1146. }
  1147. static Json::Value DumpCTestInfo(
  1148. cmCTestRunTest& testRun,
  1149. cmCTestTestHandler::cmCTestTestProperties& testProperties,
  1150. BacktraceData& backtraceGraph)
  1151. {
  1152. Json::Value testInfo = Json::objectValue;
  1153. // test name should always be present
  1154. testInfo["name"] = testProperties.Name;
  1155. std::string const& config = testRun.GetCTest()->GetConfigType();
  1156. if (!config.empty()) {
  1157. testInfo["config"] = config;
  1158. }
  1159. std::string const& command = testRun.GetActualCommand();
  1160. if (!command.empty()) {
  1161. std::vector<std::string> commandAndArgs;
  1162. commandAndArgs.push_back(command);
  1163. const std::vector<std::string>& args = testRun.GetArguments();
  1164. if (!args.empty()) {
  1165. commandAndArgs.reserve(args.size() + 1);
  1166. cm::append(commandAndArgs, args);
  1167. }
  1168. testInfo["command"] = DumpToJsonArray(commandAndArgs);
  1169. }
  1170. Json::Value properties = DumpCTestProperties(testProperties);
  1171. if (!properties.empty()) {
  1172. testInfo["properties"] = properties;
  1173. }
  1174. if (!testProperties.Backtrace.Empty()) {
  1175. AddBacktrace(backtraceGraph, testInfo, testProperties.Backtrace);
  1176. }
  1177. return testInfo;
  1178. }
  1179. static Json::Value DumpVersion(int major, int minor)
  1180. {
  1181. Json::Value version = Json::objectValue;
  1182. version["major"] = major;
  1183. version["minor"] = minor;
  1184. return version;
  1185. }
  1186. void cmCTestMultiProcessHandler::PrintOutputAsJson()
  1187. {
  1188. this->TestHandler->SetMaxIndex(this->FindMaxIndex());
  1189. Json::Value result = Json::objectValue;
  1190. result["kind"] = "ctestInfo";
  1191. result["version"] = DumpVersion(1, 0);
  1192. BacktraceData backtraceGraph;
  1193. Json::Value tests = Json::arrayValue;
  1194. for (auto& it : this->Properties) {
  1195. cmCTestTestHandler::cmCTestTestProperties& p = *it.second;
  1196. // Don't worry if this fails, we are only showing the test list, not
  1197. // running the tests
  1198. cmWorkingDirectory workdir(p.Directory);
  1199. cmCTestRunTest testRun(*this, p.Index);
  1200. testRun.ComputeArguments();
  1201. // Skip tests not available in this configuration.
  1202. if (p.Args.size() >= 2 && p.Args[1] == "NOT_AVAILABLE") {
  1203. continue;
  1204. }
  1205. Json::Value testInfo = DumpCTestInfo(testRun, p, backtraceGraph);
  1206. tests.append(testInfo);
  1207. }
  1208. result["backtraceGraph"] = backtraceGraph.Dump();
  1209. result["tests"] = std::move(tests);
  1210. Json::StreamWriterBuilder builder;
  1211. builder["indentation"] = " ";
  1212. std::unique_ptr<Json::StreamWriter> jout(builder.newStreamWriter());
  1213. jout->write(result, &std::cout);
  1214. }
  1215. // For ShowOnly mode
  1216. void cmCTestMultiProcessHandler::PrintTestList()
  1217. {
  1218. if (this->CTest->GetOutputAsJson()) {
  1219. this->PrintOutputAsJson();
  1220. return;
  1221. }
  1222. this->TestHandler->SetMaxIndex(this->FindMaxIndex());
  1223. for (auto& it : this->Properties) {
  1224. cmCTestTestHandler::cmCTestTestProperties& p = *it.second;
  1225. // Don't worry if this fails, we are only showing the test list, not
  1226. // running the tests
  1227. cmWorkingDirectory workdir(p.Directory);
  1228. cmCTestRunTest testRun(*this, p.Index);
  1229. testRun.ComputeArguments(); // logs the command in verbose mode
  1230. if (!p.Labels.empty()) // print the labels
  1231. {
  1232. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1233. "Labels:", this->Quiet);
  1234. }
  1235. for (std::string const& label : p.Labels) {
  1236. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " " << label,
  1237. this->Quiet);
  1238. }
  1239. if (!p.Labels.empty()) // print the labels
  1240. {
  1241. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl,
  1242. this->Quiet);
  1243. }
  1244. if (this->TestHandler->MemCheck) {
  1245. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " Memory Check",
  1246. this->Quiet);
  1247. } else {
  1248. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " Test", this->Quiet);
  1249. }
  1250. std::ostringstream indexStr;
  1251. indexStr << " #" << p.Index << ":";
  1252. cmCTestOptionalLog(
  1253. this->CTest, HANDLER_OUTPUT,
  1254. std::setw(3 + getNumWidth(this->TestHandler->GetMaxIndex()))
  1255. << indexStr.str(),
  1256. this->Quiet);
  1257. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " " << p.Name,
  1258. this->Quiet);
  1259. if (p.Disabled) {
  1260. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " (Disabled)",
  1261. this->Quiet);
  1262. }
  1263. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, std::endl, this->Quiet);
  1264. }
  1265. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1266. std::endl
  1267. << "Total Tests: " << this->Total << std::endl,
  1268. this->Quiet);
  1269. }
  1270. void cmCTestMultiProcessHandler::PrintLabels()
  1271. {
  1272. std::set<std::string> allLabels;
  1273. for (auto& it : this->Properties) {
  1274. cmCTestTestHandler::cmCTestTestProperties& p = *it.second;
  1275. allLabels.insert(p.Labels.begin(), p.Labels.end());
  1276. }
  1277. if (!allLabels.empty()) {
  1278. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "All Labels:" << std::endl,
  1279. this->Quiet);
  1280. } else {
  1281. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1282. "No Labels Exist" << std::endl, this->Quiet);
  1283. }
  1284. for (std::string const& label : allLabels) {
  1285. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " " << label << std::endl,
  1286. this->Quiet);
  1287. }
  1288. }
  1289. void cmCTestMultiProcessHandler::CheckResume()
  1290. {
  1291. std::string fname =
  1292. this->CTest->GetBinaryDir() + "/Testing/Temporary/CTestCheckpoint.txt";
  1293. if (this->CTest->GetFailover()) {
  1294. if (cmSystemTools::FileExists(fname, true)) {
  1295. *this->TestHandler->LogFile
  1296. << "Resuming previously interrupted test set" << std::endl
  1297. << "----------------------------------------------------------"
  1298. << std::endl;
  1299. cmsys::ifstream fin;
  1300. fin.open(fname.c_str());
  1301. std::string line;
  1302. while (std::getline(fin, line)) {
  1303. int index = atoi(line.c_str());
  1304. this->RemoveTest(index);
  1305. }
  1306. fin.close();
  1307. }
  1308. } else if (cmSystemTools::FileExists(fname, true)) {
  1309. cmSystemTools::RemoveFile(fname);
  1310. }
  1311. }
  1312. void cmCTestMultiProcessHandler::RemoveTest(int index)
  1313. {
  1314. this->OrderedTests.erase(
  1315. std::find(this->OrderedTests.begin(), this->OrderedTests.end(), index));
  1316. this->PendingTests.erase(index);
  1317. this->Properties.erase(index);
  1318. this->Completed++;
  1319. }
  1320. int cmCTestMultiProcessHandler::FindMaxIndex()
  1321. {
  1322. int max = 0;
  1323. for (auto const& i : this->PendingTests) {
  1324. if (i.first > max) {
  1325. max = i.first;
  1326. }
  1327. }
  1328. return max;
  1329. }
  1330. // Returns true if no cycles exist in the dependency graph
  1331. bool cmCTestMultiProcessHandler::CheckCycles()
  1332. {
  1333. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1334. "Checking test dependency graph..." << std::endl,
  1335. this->Quiet);
  1336. for (auto const& it : this->PendingTests) {
  1337. // DFS from each element to itself
  1338. int root = it.first;
  1339. std::set<int> visited;
  1340. std::stack<int> s;
  1341. s.push(root);
  1342. while (!s.empty()) {
  1343. int test = s.top();
  1344. s.pop();
  1345. if (visited.insert(test).second) {
  1346. for (auto const& d : this->PendingTests[test].Depends) {
  1347. if (d == root) {
  1348. // cycle exists
  1349. cmCTestLog(
  1350. this->CTest, ERROR_MESSAGE,
  1351. "Error: a cycle exists in the test dependency graph "
  1352. "for the test \""
  1353. << this->Properties[root]->Name
  1354. << "\".\nPlease fix the cycle and run ctest again.\n");
  1355. return false;
  1356. }
  1357. s.push(d);
  1358. }
  1359. }
  1360. }
  1361. }
  1362. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1363. "Checking test dependency graph end" << std::endl,
  1364. this->Quiet);
  1365. return true;
  1366. }
  1367. bool cmCTestMultiProcessHandler::CheckGeneratedResourceSpec()
  1368. {
  1369. for (auto& test : this->Properties) {
  1370. if (!test.second->GeneratedResourceSpecFile.empty()) {
  1371. if (this->ResourceSpecSetupTest) {
  1372. cmCTestLog(
  1373. this->CTest, ERROR_MESSAGE,
  1374. "Only one test may define the GENERATED_RESOURCE_SPEC_FILE property"
  1375. << std::endl);
  1376. return false;
  1377. }
  1378. if (test.second->FixturesSetup.size() != 1) {
  1379. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1380. "Test that defines GENERATED_RESOURCE_SPEC_FILE must have "
  1381. "exactly one FIXTURES_SETUP"
  1382. << std::endl);
  1383. return false;
  1384. }
  1385. if (!cmSystemTools::FileIsFullPath(
  1386. test.second->GeneratedResourceSpecFile)) {
  1387. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1388. "GENERATED_RESOURCE_SPEC_FILE must be an absolute path"
  1389. << std::endl);
  1390. return false;
  1391. }
  1392. this->ResourceSpecSetupTest = test.first;
  1393. this->ResourceSpecSetupFixture = *test.second->FixturesSetup.begin();
  1394. }
  1395. }
  1396. if (!this->ResourceSpecSetupFixture.empty()) {
  1397. for (auto& test : this->Properties) {
  1398. if (!test.second->ResourceGroups.empty() &&
  1399. !test.second->FixturesRequired.count(
  1400. this->ResourceSpecSetupFixture)) {
  1401. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1402. "All tests that have RESOURCE_GROUPS must include the "
  1403. "resource spec generator fixture in their FIXTURES_REQUIRED"
  1404. << std::endl);
  1405. return false;
  1406. }
  1407. }
  1408. }
  1409. if (!this->ResourceSpecFile.empty()) {
  1410. if (this->ResourceSpecSetupTest) {
  1411. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1412. "GENERATED_RESOURCE_SPEC_FILE test property cannot be used "
  1413. "in conjunction with ResourceSpecFile option"
  1414. << std::endl);
  1415. return false;
  1416. }
  1417. std::string error;
  1418. if (!this->InitResourceAllocator(error)) {
  1419. cmCTestLog(this->CTest, ERROR_MESSAGE, error << std::endl);
  1420. return false;
  1421. }
  1422. }
  1423. return true;
  1424. }
  1425. bool cmCTestMultiProcessHandler::InitResourceAllocator(std::string& error)
  1426. {
  1427. if (!this->ResourceSpec.ReadFromJSONFile(this->ResourceSpecFile)) {
  1428. error = cmStrCat("Could not read/parse resource spec file ",
  1429. this->ResourceSpecFile, ": ",
  1430. this->ResourceSpec.parseState.GetErrorMessage());
  1431. return false;
  1432. }
  1433. this->UseResourceSpec = true;
  1434. this->ResourceAllocator.InitializeFromResourceSpec(this->ResourceSpec);
  1435. return true;
  1436. }