cmCTestMultiProcessHandler.cxx 49 KB

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