cmCTestMemCheckHandler.cxx 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420
  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 "cmCTestMemCheckHandler.h"
  4. #include <algorithm>
  5. #include <chrono>
  6. #include <cstring>
  7. #include <iostream>
  8. #include <iterator>
  9. #include <ratio>
  10. #include <sstream>
  11. #include <utility>
  12. #include <cmext/algorithm>
  13. #include "cmsys/FStream.hxx"
  14. #include "cmsys/Glob.hxx"
  15. #include "cmsys/RegularExpression.hxx"
  16. #include "cmCTest.h"
  17. #include "cmDuration.h"
  18. #include "cmSystemTools.h"
  19. #include "cmXMLParser.h"
  20. #include "cmXMLWriter.h"
  21. struct CatToErrorType
  22. {
  23. const char* ErrorCategory;
  24. int ErrorCode;
  25. };
  26. static CatToErrorType cmCTestMemCheckBoundsChecker[] = {
  27. // Error tags
  28. { "Write Overrun", cmCTestMemCheckHandler::ABW },
  29. { "Read Overrun", cmCTestMemCheckHandler::ABR },
  30. { "Memory Overrun", cmCTestMemCheckHandler::ABW },
  31. { "Allocation Conflict", cmCTestMemCheckHandler::FMM },
  32. { "Bad Pointer Use", cmCTestMemCheckHandler::FMW },
  33. { "Dangling Pointer", cmCTestMemCheckHandler::FMR },
  34. { nullptr, 0 }
  35. };
  36. static void xmlReportError(int line, const char* msg, void* data)
  37. {
  38. cmCTest* ctest = static_cast<cmCTest*>(data);
  39. cmCTestLog(ctest, ERROR_MESSAGE,
  40. "Error parsing XML in stream at line " << line << ": " << msg
  41. << std::endl);
  42. }
  43. // parse the xml file containing the results of last BoundsChecker run
  44. class cmBoundsCheckerParser : public cmXMLParser
  45. {
  46. public:
  47. cmBoundsCheckerParser(cmCTest* c)
  48. {
  49. this->CTest = c;
  50. this->SetErrorCallback(xmlReportError, c);
  51. }
  52. void StartElement(const std::string& name, const char** atts) override
  53. {
  54. if (name == "MemoryLeak" || name == "ResourceLeak") {
  55. this->Errors.push_back(cmCTestMemCheckHandler::MLK);
  56. } else if (name == "Error" || name == "Dangling Pointer") {
  57. this->ParseError(atts);
  58. }
  59. // Create the log
  60. std::ostringstream ostr;
  61. ostr << name << ":\n";
  62. int i = 0;
  63. for (; atts[i]; i += 2) {
  64. ostr << " " << atts[i] << " - " << atts[i + 1] << "\n";
  65. }
  66. ostr << "\n";
  67. this->Log += ostr.str();
  68. }
  69. void EndElement(const std::string& /*name*/) override {}
  70. const char* GetAttribute(const char* name, const char** atts)
  71. {
  72. int i = 0;
  73. for (; atts[i]; ++i) {
  74. if (strcmp(name, atts[i]) == 0) {
  75. return atts[i + 1];
  76. }
  77. }
  78. return nullptr;
  79. }
  80. void ParseError(const char** atts)
  81. {
  82. CatToErrorType* ptr = cmCTestMemCheckBoundsChecker;
  83. const char* cat = this->GetAttribute("ErrorCategory", atts);
  84. if (!cat) {
  85. this->Errors.push_back(cmCTestMemCheckHandler::ABW); // do not know
  86. cmCTestLog(this->CTest, ERROR_MESSAGE,
  87. "No Category found in Bounds checker XML\n");
  88. return;
  89. }
  90. while (ptr->ErrorCategory && cat) {
  91. if (strcmp(ptr->ErrorCategory, cat) == 0) {
  92. this->Errors.push_back(ptr->ErrorCode);
  93. return; // found it we are done
  94. }
  95. ptr++;
  96. }
  97. if (ptr->ErrorCategory) {
  98. this->Errors.push_back(cmCTestMemCheckHandler::ABW); // do not know
  99. cmCTestLog(this->CTest, ERROR_MESSAGE,
  100. "Found unknown Bounds Checker error " << ptr->ErrorCategory
  101. << std::endl);
  102. }
  103. }
  104. cmCTest* CTest;
  105. std::vector<int> Errors;
  106. std::string Log;
  107. };
  108. #define BOUNDS_CHECKER_MARKER \
  109. "******######*****Begin BOUNDS CHECKER XML******######******"
  110. cmCTestMemCheckHandler::cmCTestMemCheckHandler()
  111. {
  112. this->MemCheck = true;
  113. this->CustomMaximumPassedTestOutputSize = 0;
  114. this->CustomMaximumFailedTestOutputSize = 0;
  115. this->LogWithPID = false;
  116. }
  117. void cmCTestMemCheckHandler::Initialize()
  118. {
  119. this->Superclass::Initialize();
  120. this->LogWithPID = false;
  121. this->CustomMaximumPassedTestOutputSize = 0;
  122. this->CustomMaximumFailedTestOutputSize = 0;
  123. this->MemoryTester.clear();
  124. this->MemoryTesterDynamicOptions.clear();
  125. this->MemoryTesterOptions.clear();
  126. this->MemoryTesterStyle = UNKNOWN;
  127. this->MemoryTesterOutputFile.clear();
  128. this->DefectCount = 0;
  129. }
  130. int cmCTestMemCheckHandler::PreProcessHandler()
  131. {
  132. if (!this->InitializeMemoryChecking()) {
  133. return 0;
  134. }
  135. if (!this->ExecuteCommands(this->CustomPreMemCheck)) {
  136. cmCTestLog(this->CTest, ERROR_MESSAGE,
  137. "Problem executing pre-memcheck command(s)." << std::endl);
  138. return 0;
  139. }
  140. return 1;
  141. }
  142. int cmCTestMemCheckHandler::PostProcessHandler()
  143. {
  144. if (!this->ExecuteCommands(this->CustomPostMemCheck)) {
  145. cmCTestLog(this->CTest, ERROR_MESSAGE,
  146. "Problem executing post-memcheck command(s)." << std::endl);
  147. return 0;
  148. }
  149. return 1;
  150. }
  151. void cmCTestMemCheckHandler::GenerateTestCommand(
  152. std::vector<std::string>& args, int test)
  153. {
  154. std::string index = std::to_string(test);
  155. std::string memcheckcommand =
  156. cmSystemTools::ConvertToOutputPath(this->MemoryTester);
  157. std::vector<std::string> dirs;
  158. bool nextArgIsDir = false;
  159. for (std::string arg : this->MemoryTesterDynamicOptions) {
  160. std::string::size_type pos = arg.find("??");
  161. if (pos != std::string::npos) {
  162. arg.replace(pos, 2, index);
  163. }
  164. args.push_back(arg);
  165. memcheckcommand += " \"";
  166. memcheckcommand += arg;
  167. memcheckcommand += "\"";
  168. if (nextArgIsDir) {
  169. nextArgIsDir = false;
  170. dirs.push_back(arg);
  171. }
  172. if (this->MemoryTesterStyle == cmCTestMemCheckHandler::DRMEMORY &&
  173. (arg == "-logdir" || arg == "-symcache_dir")) {
  174. nextArgIsDir = true;
  175. }
  176. }
  177. // Create a copy of the memory tester environment variable.
  178. // This is used for memory testing programs that pass options
  179. // via environment variables.
  180. std::string memTesterEnvironmentVariable =
  181. this->MemoryTesterEnvironmentVariable;
  182. for (std::string const& arg : this->MemoryTesterOptions) {
  183. if (!memTesterEnvironmentVariable.empty()) {
  184. // If we are using env to pass options, append all the options to
  185. // this string with space separation.
  186. memTesterEnvironmentVariable += " " + arg;
  187. }
  188. // for regular options just add them to args and memcheckcommand
  189. // which is just used for display
  190. else {
  191. args.push_back(arg);
  192. memcheckcommand += " \"";
  193. memcheckcommand += arg;
  194. memcheckcommand += "\"";
  195. }
  196. }
  197. // if this is an env option type, then add the env string as a single
  198. // argument.
  199. if (!memTesterEnvironmentVariable.empty()) {
  200. std::string::size_type pos = memTesterEnvironmentVariable.find("??");
  201. if (pos != std::string::npos) {
  202. memTesterEnvironmentVariable.replace(pos, 2, index);
  203. }
  204. memcheckcommand += " " + memTesterEnvironmentVariable;
  205. args.push_back(memTesterEnvironmentVariable);
  206. }
  207. for (std::string const& dir : dirs) {
  208. cmSystemTools::MakeDirectory(dir);
  209. }
  210. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  211. "Memory check command: " << memcheckcommand << std::endl,
  212. this->Quiet);
  213. }
  214. void cmCTestMemCheckHandler::InitializeResultsVectors()
  215. {
  216. // fill these members
  217. // cmsys::vector<std::string> ResultStrings;
  218. // cmsys::vector<std::string> ResultStringsLong;
  219. // cmsys::vector<int> GlobalResults;
  220. this->ResultStringsLong.clear();
  221. this->ResultStrings.clear();
  222. this->GlobalResults.clear();
  223. // If we are working with style checkers that dynamically fill
  224. // the results strings then return.
  225. if (this->MemoryTesterStyle > cmCTestMemCheckHandler::BOUNDS_CHECKER) {
  226. return;
  227. }
  228. // define the standard set of errors
  229. //----------------------------------------------------------------------
  230. static const char* cmCTestMemCheckResultStrings[] = {
  231. "ABR", "ABW", "ABWL", "COR", "EXU", "FFM", "FIM", "FMM",
  232. "FMR", "FMW", "FUM", "IPR", "IPW", "MAF", "MLK", "MPK",
  233. "NPR", "ODS", "PAR", "PLK", "UMC", "UMR", nullptr
  234. };
  235. static const char* cmCTestMemCheckResultLongStrings[] = {
  236. "Threading Problem",
  237. "ABW",
  238. "ABWL",
  239. "COR",
  240. "EXU",
  241. "FFM",
  242. "FIM",
  243. "Mismatched deallocation",
  244. "FMR",
  245. "FMW",
  246. "FUM",
  247. "IPR",
  248. "IPW",
  249. "MAF",
  250. "Memory Leak",
  251. "Potential Memory Leak",
  252. "NPR",
  253. "ODS",
  254. "Invalid syscall param",
  255. "PLK",
  256. "Uninitialized Memory Conditional",
  257. "Uninitialized Memory Read",
  258. nullptr
  259. };
  260. this->GlobalResults.clear();
  261. for (int i = 0; cmCTestMemCheckResultStrings[i]; ++i) {
  262. this->ResultStrings.emplace_back(cmCTestMemCheckResultStrings[i]);
  263. this->ResultStringsLong.emplace_back(cmCTestMemCheckResultLongStrings[i]);
  264. this->GlobalResults.push_back(0);
  265. }
  266. }
  267. void cmCTestMemCheckHandler::PopulateCustomVectors(cmMakefile* mf)
  268. {
  269. this->cmCTestTestHandler::PopulateCustomVectors(mf);
  270. this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_PRE_MEMCHECK",
  271. this->CustomPreMemCheck);
  272. this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_POST_MEMCHECK",
  273. this->CustomPostMemCheck);
  274. this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_MEMCHECK_IGNORE",
  275. this->CustomTestsIgnore);
  276. }
  277. int cmCTestMemCheckHandler::GetDefectCount() const
  278. {
  279. return this->DefectCount;
  280. }
  281. void cmCTestMemCheckHandler::GenerateCTestXML(cmXMLWriter& xml)
  282. {
  283. if (!this->CTest->GetProduceXML()) {
  284. return;
  285. }
  286. this->CTest->StartXML(xml, this->AppendXML);
  287. this->CTest->GenerateSubprojectsOutput(xml);
  288. xml.StartElement("DynamicAnalysis");
  289. switch (this->MemoryTesterStyle) {
  290. case cmCTestMemCheckHandler::VALGRIND:
  291. xml.Attribute("Checker", "Valgrind");
  292. break;
  293. case cmCTestMemCheckHandler::DRMEMORY:
  294. xml.Attribute("Checker", "DrMemory");
  295. break;
  296. case cmCTestMemCheckHandler::PURIFY:
  297. xml.Attribute("Checker", "Purify");
  298. break;
  299. case cmCTestMemCheckHandler::BOUNDS_CHECKER:
  300. xml.Attribute("Checker", "BoundsChecker");
  301. break;
  302. case cmCTestMemCheckHandler::CUDA_SANITIZER:
  303. xml.Attribute("Checker", "CudaSanitizer");
  304. break;
  305. case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
  306. xml.Attribute("Checker", "AddressSanitizer");
  307. break;
  308. case cmCTestMemCheckHandler::LEAK_SANITIZER:
  309. xml.Attribute("Checker", "LeakSanitizer");
  310. break;
  311. case cmCTestMemCheckHandler::THREAD_SANITIZER:
  312. xml.Attribute("Checker", "ThreadSanitizer");
  313. break;
  314. case cmCTestMemCheckHandler::MEMORY_SANITIZER:
  315. xml.Attribute("Checker", "MemorySanitizer");
  316. break;
  317. case cmCTestMemCheckHandler::UB_SANITIZER:
  318. xml.Attribute("Checker", "UndefinedBehaviorSanitizer");
  319. break;
  320. default:
  321. xml.Attribute("Checker", "Unknown");
  322. }
  323. xml.Element("StartDateTime", this->StartTest);
  324. xml.Element("StartTestTime", this->StartTestTime);
  325. xml.StartElement("TestList");
  326. cmCTestMemCheckHandler::TestResultsVector::size_type cc;
  327. for (cmCTestTestResult const& result : this->TestResults) {
  328. std::string testPath = result.Path + "/" + result.Name;
  329. xml.Element("Test", this->CTest->GetShortPathToFile(testPath));
  330. }
  331. xml.EndElement(); // TestList
  332. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  333. "-- Processing memory checking output:\n", this->Quiet);
  334. size_t total = this->TestResults.size();
  335. for (cc = 0; cc < this->TestResults.size(); cc++) {
  336. cmCTestTestResult const& result = this->TestResults[cc];
  337. std::string memcheckstr;
  338. std::vector<int> memcheckresults(this->ResultStrings.size(), 0);
  339. bool res =
  340. this->ProcessMemCheckOutput(result.Output, memcheckstr, memcheckresults);
  341. if (res && result.Status == cmCTestMemCheckHandler::COMPLETED) {
  342. continue;
  343. }
  344. this->CleanTestOutput(
  345. memcheckstr,
  346. static_cast<size_t>(this->CustomMaximumFailedTestOutputSize),
  347. this->TestOutputTruncation);
  348. this->WriteTestResultHeader(xml, result);
  349. xml.StartElement("Results");
  350. int memoryErrors = 0;
  351. for (std::vector<int>::size_type kk = 0; kk < memcheckresults.size();
  352. ++kk) {
  353. if (memcheckresults[kk]) {
  354. xml.StartElement("Defect");
  355. xml.Attribute("type", this->ResultStringsLong[kk]);
  356. xml.Content(memcheckresults[kk]);
  357. memoryErrors += memcheckresults[kk];
  358. xml.EndElement(); // Defect
  359. }
  360. this->GlobalResults[kk] += memcheckresults[kk];
  361. }
  362. xml.EndElement(); // Results
  363. if (memoryErrors > 0) {
  364. const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
  365. std::string outname = result.Name + " ";
  366. outname.resize(maxTestNameWidth + 4, '.');
  367. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  368. cc + 1 << "/" << total << " MemCheck: #"
  369. << result.TestCount << ": " << outname
  370. << " Defects: " << memoryErrors << std::endl,
  371. this->Quiet);
  372. }
  373. xml.StartElement("Log");
  374. if (this->CTest->ShouldCompressTestOutput()) {
  375. this->CTest->CompressString(memcheckstr);
  376. xml.Attribute("compression", "gzip");
  377. xml.Attribute("encoding", "base64");
  378. }
  379. xml.Content(memcheckstr);
  380. xml.EndElement(); // Log
  381. this->WriteTestResultFooter(xml, result);
  382. }
  383. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  384. "MemCheck log files can be found here: "
  385. "(<#> corresponds to test number)"
  386. << std::endl,
  387. this->Quiet);
  388. std::string output = this->MemoryTesterOutputFile;
  389. cmSystemTools::ReplaceString(output, "??", "<#>");
  390. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, output << std::endl,
  391. this->Quiet);
  392. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  393. "Memory checking results:" << std::endl, this->Quiet);
  394. xml.StartElement("DefectList");
  395. for (cc = 0; cc < this->GlobalResults.size(); cc++) {
  396. if (this->GlobalResults[cc]) {
  397. std::cerr.width(35);
  398. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  399. this->ResultStringsLong[cc]
  400. << " - " << this->GlobalResults[cc] << std::endl,
  401. this->Quiet);
  402. xml.StartElement("Defect");
  403. xml.Attribute("Type", this->ResultStringsLong[cc]);
  404. xml.EndElement();
  405. }
  406. }
  407. xml.EndElement(); // DefectList
  408. xml.Element("EndDateTime", this->EndTest);
  409. xml.Element("EndTestTime", this->EndTestTime);
  410. xml.Element(
  411. "ElapsedMinutes",
  412. std::chrono::duration_cast<std::chrono::minutes>(this->ElapsedTestingTime)
  413. .count());
  414. xml.EndElement(); // DynamicAnalysis
  415. this->CTest->EndXML(xml);
  416. }
  417. bool cmCTestMemCheckHandler::InitializeMemoryChecking()
  418. {
  419. this->MemoryTesterEnvironmentVariable.clear();
  420. this->MemoryTester.clear();
  421. // Setup the command
  422. if (cmSystemTools::FileExists(
  423. this->CTest->GetCTestConfiguration("MemoryCheckCommand"))) {
  424. this->MemoryTester =
  425. this->CTest->GetCTestConfiguration("MemoryCheckCommand");
  426. std::string testerName =
  427. cmSystemTools::GetFilenameName(this->MemoryTester);
  428. // determine the checker type
  429. if (testerName.find("valgrind") != std::string::npos ||
  430. this->CTest->GetCTestConfiguration("MemoryCheckType") == "Valgrind") {
  431. this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
  432. } else if (testerName.find("drmemory") != std::string::npos ||
  433. this->CTest->GetCTestConfiguration("MemoryCheckType") ==
  434. "DrMemory") {
  435. this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY;
  436. } else if (testerName.find("purify") != std::string::npos) {
  437. this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
  438. } else if (testerName.find("BC") != std::string::npos) {
  439. this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
  440. } else if (testerName.find("cuda-memcheck") != std::string::npos ||
  441. testerName.find("compute-sanitizer") != std::string::npos) {
  442. this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_SANITIZER;
  443. } else {
  444. this->MemoryTesterStyle = cmCTestMemCheckHandler::UNKNOWN;
  445. }
  446. } else if (cmSystemTools::FileExists(
  447. this->CTest->GetCTestConfiguration("PurifyCommand"))) {
  448. this->MemoryTester = this->CTest->GetCTestConfiguration("PurifyCommand");
  449. this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
  450. } else if (cmSystemTools::FileExists(
  451. this->CTest->GetCTestConfiguration("ValgrindCommand"))) {
  452. this->MemoryTester = this->CTest->GetCTestConfiguration("ValgrindCommand");
  453. this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
  454. } else if (cmSystemTools::FileExists(
  455. this->CTest->GetCTestConfiguration("DrMemoryCommand"))) {
  456. this->MemoryTester = this->CTest->GetCTestConfiguration("DrMemoryCommand");
  457. this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY;
  458. } else if (cmSystemTools::FileExists(
  459. this->CTest->GetCTestConfiguration("BoundsCheckerCommand"))) {
  460. this->MemoryTester =
  461. this->CTest->GetCTestConfiguration("BoundsCheckerCommand");
  462. this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
  463. } else if (cmSystemTools::FileExists(
  464. this->CTest->GetCTestConfiguration("CudaSanitizerCommand"))) {
  465. this->MemoryTester =
  466. this->CTest->GetCTestConfiguration("CudaSanitizerCommand");
  467. this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_SANITIZER;
  468. }
  469. if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
  470. "AddressSanitizer") {
  471. this->MemoryTester = cmSystemTools::GetCMakeCommand();
  472. this->MemoryTesterStyle = cmCTestMemCheckHandler::ADDRESS_SANITIZER;
  473. this->LogWithPID = true; // even if we give the log file the pid is added
  474. }
  475. if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
  476. "LeakSanitizer") {
  477. this->MemoryTester = cmSystemTools::GetCMakeCommand();
  478. this->MemoryTesterStyle = cmCTestMemCheckHandler::LEAK_SANITIZER;
  479. this->LogWithPID = true; // even if we give the log file the pid is added
  480. }
  481. if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
  482. "ThreadSanitizer") {
  483. this->MemoryTester = cmSystemTools::GetCMakeCommand();
  484. this->MemoryTesterStyle = cmCTestMemCheckHandler::THREAD_SANITIZER;
  485. this->LogWithPID = true; // even if we give the log file the pid is added
  486. }
  487. if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
  488. "MemorySanitizer") {
  489. this->MemoryTester = cmSystemTools::GetCMakeCommand();
  490. this->MemoryTesterStyle = cmCTestMemCheckHandler::MEMORY_SANITIZER;
  491. this->LogWithPID = true; // even if we give the log file the pid is added
  492. }
  493. if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
  494. "UndefinedBehaviorSanitizer") {
  495. this->MemoryTester = cmSystemTools::GetCMakeCommand();
  496. this->MemoryTesterStyle = cmCTestMemCheckHandler::UB_SANITIZER;
  497. this->LogWithPID = true; // even if we give the log file the pid is added
  498. }
  499. // Check the MemoryCheckType
  500. if (this->MemoryTesterStyle == cmCTestMemCheckHandler::UNKNOWN) {
  501. std::string checkType =
  502. this->CTest->GetCTestConfiguration("MemoryCheckType");
  503. if (checkType == "Purify") {
  504. this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
  505. } else if (checkType == "BoundsChecker") {
  506. this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
  507. } else if (checkType == "Valgrind") {
  508. this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
  509. } else if (checkType == "DrMemory") {
  510. this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY;
  511. } else if (checkType == "CudaSanitizer") {
  512. this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_SANITIZER;
  513. }
  514. }
  515. if (this->MemoryTester.empty()) {
  516. cmCTestOptionalLog(this->CTest, WARNING,
  517. "Memory checker (MemoryCheckCommand) "
  518. "not set, or cannot find the specified program."
  519. << std::endl,
  520. this->Quiet);
  521. return false;
  522. }
  523. // Setup the options
  524. std::string memoryTesterOptions;
  525. if (!this->CTest->GetCTestConfiguration("MemoryCheckCommandOptions")
  526. .empty()) {
  527. memoryTesterOptions =
  528. this->CTest->GetCTestConfiguration("MemoryCheckCommandOptions");
  529. } else if (!this->CTest->GetCTestConfiguration("ValgrindCommandOptions")
  530. .empty()) {
  531. memoryTesterOptions =
  532. this->CTest->GetCTestConfiguration("ValgrindCommandOptions");
  533. } else if (!this->CTest->GetCTestConfiguration("DrMemoryCommandOptions")
  534. .empty()) {
  535. memoryTesterOptions =
  536. this->CTest->GetCTestConfiguration("DrMemoryCommandOptions");
  537. } else if (!this->CTest->GetCTestConfiguration("CudaSanitizerCommandOptions")
  538. .empty()) {
  539. memoryTesterOptions =
  540. this->CTest->GetCTestConfiguration("CudaSanitizerCommandOptions");
  541. }
  542. this->MemoryTesterOptions =
  543. cmSystemTools::ParseArguments(memoryTesterOptions);
  544. this->MemoryTesterOutputFile =
  545. this->CTest->GetBinaryDir() + "/Testing/Temporary/MemoryChecker.??.log";
  546. switch (this->MemoryTesterStyle) {
  547. case cmCTestMemCheckHandler::VALGRIND: {
  548. if (this->MemoryTesterOptions.empty()) {
  549. this->MemoryTesterOptions.emplace_back("-q");
  550. this->MemoryTesterOptions.emplace_back("--tool=memcheck");
  551. this->MemoryTesterOptions.emplace_back("--leak-check=yes");
  552. this->MemoryTesterOptions.emplace_back("--show-reachable=yes");
  553. this->MemoryTesterOptions.emplace_back("--num-callers=50");
  554. }
  555. if (!this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
  556. .empty()) {
  557. if (!cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
  558. "MemoryCheckSuppressionFile"))) {
  559. cmCTestLog(this->CTest, ERROR_MESSAGE,
  560. "Cannot find memory checker suppression file: "
  561. << this->CTest->GetCTestConfiguration(
  562. "MemoryCheckSuppressionFile")
  563. << std::endl);
  564. return false;
  565. }
  566. this->MemoryTesterOptions.push_back(
  567. "--suppressions=" +
  568. this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile"));
  569. }
  570. this->MemoryTesterDynamicOptions.push_back("--log-file=" +
  571. this->MemoryTesterOutputFile);
  572. break;
  573. }
  574. case cmCTestMemCheckHandler::DRMEMORY: {
  575. std::string tempDrMemoryDir =
  576. this->CTest->GetBinaryDir() + "/Testing/Temporary/DrMemory";
  577. if (!cm::contains(this->MemoryTesterOptions, "-quiet")) {
  578. this->MemoryTesterOptions.emplace_back("-quiet");
  579. }
  580. if (!cm::contains(this->MemoryTesterOptions, "-batch")) {
  581. this->MemoryTesterOptions.emplace_back("-batch");
  582. }
  583. this->MemoryTesterDynamicOptions.emplace_back("-logdir");
  584. auto logdirOption =
  585. std::find(this->MemoryTesterOptions.begin(),
  586. this->MemoryTesterOptions.end(), "-logdir");
  587. if (logdirOption == this->MemoryTesterOptions.end()) {
  588. // No logdir found in memory tester options
  589. std::string drMemoryLogDir = tempDrMemoryDir + "/??";
  590. this->MemoryTesterDynamicOptions.push_back(drMemoryLogDir);
  591. this->MemoryTesterOutputFile = drMemoryLogDir;
  592. } else {
  593. // Use logdir found in memory tester options
  594. auto logdirLocation = std::next(logdirOption);
  595. this->MemoryTesterOutputFile = *logdirLocation;
  596. this->MemoryTesterDynamicOptions.push_back(*logdirLocation);
  597. this->MemoryTesterOptions.erase(logdirOption, logdirLocation + 1);
  598. }
  599. this->MemoryTesterOutputFile += "/*/results.txt";
  600. if (std::find(this->MemoryTesterOptions.begin(),
  601. this->MemoryTesterOptions.end(),
  602. "-symcache_dir") == this->MemoryTesterOptions.end()) {
  603. this->MemoryTesterDynamicOptions.emplace_back("-symcache_dir");
  604. std::string drMemoryCacheDir = tempDrMemoryDir + "/cache";
  605. this->MemoryTesterDynamicOptions.push_back(drMemoryCacheDir);
  606. }
  607. if (!this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
  608. .empty()) {
  609. if (!cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
  610. "MemoryCheckSuppressionFile"))) {
  611. cmCTestLog(this->CTest, ERROR_MESSAGE,
  612. "Cannot find memory checker suppression file: "
  613. << this->CTest->GetCTestConfiguration(
  614. "MemoryCheckSuppressionFile")
  615. << std::endl);
  616. return false;
  617. }
  618. this->MemoryTesterOptions.emplace_back("-suppress");
  619. this->MemoryTesterOptions.push_back(
  620. this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile"));
  621. }
  622. this->MemoryTesterOptions.emplace_back("--");
  623. break;
  624. }
  625. case cmCTestMemCheckHandler::PURIFY: {
  626. std::string outputFile;
  627. #ifdef _WIN32
  628. if (this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
  629. .size()) {
  630. if (!cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
  631. "MemoryCheckSuppressionFile"))) {
  632. cmCTestLog(
  633. this->CTest, ERROR_MESSAGE,
  634. "Cannot find memory checker suppression file: "
  635. << this->CTest
  636. ->GetCTestConfiguration("MemoryCheckSuppressionFile")
  637. .c_str()
  638. << std::endl);
  639. return false;
  640. }
  641. std::string filterFiles = "/FilterFiles=" +
  642. this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile");
  643. this->MemoryTesterOptions.push_back(filterFiles);
  644. }
  645. outputFile = "/SAVETEXTDATA=";
  646. #else
  647. outputFile = "-log-file=";
  648. #endif
  649. outputFile += this->MemoryTesterOutputFile;
  650. this->MemoryTesterDynamicOptions.push_back(outputFile);
  651. break;
  652. }
  653. case cmCTestMemCheckHandler::BOUNDS_CHECKER: {
  654. this->BoundsCheckerXMLFile = this->MemoryTesterOutputFile;
  655. std::string dpbdFile = this->CTest->GetBinaryDir() +
  656. "/Testing/Temporary/MemoryChecker.??.DPbd";
  657. this->BoundsCheckerDPBDFile = dpbdFile;
  658. this->MemoryTesterDynamicOptions.emplace_back("/B");
  659. this->MemoryTesterDynamicOptions.push_back(std::move(dpbdFile));
  660. this->MemoryTesterDynamicOptions.emplace_back("/X");
  661. this->MemoryTesterDynamicOptions.push_back(this->MemoryTesterOutputFile);
  662. this->MemoryTesterOptions.emplace_back("/M");
  663. break;
  664. }
  665. case cmCTestMemCheckHandler::CUDA_SANITIZER: {
  666. // cuda sanitizer separates flags from arguments by spaces
  667. if (this->MemoryTesterOptions.empty()) {
  668. this->MemoryTesterOptions.emplace_back("--tool");
  669. this->MemoryTesterOptions.emplace_back("memcheck");
  670. this->MemoryTesterOptions.emplace_back("--leak-check");
  671. this->MemoryTesterOptions.emplace_back("full");
  672. }
  673. this->MemoryTesterDynamicOptions.emplace_back("--log-file");
  674. this->MemoryTesterDynamicOptions.push_back(this->MemoryTesterOutputFile);
  675. break;
  676. }
  677. // these are almost the same but the env var used is different
  678. case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
  679. case cmCTestMemCheckHandler::LEAK_SANITIZER:
  680. case cmCTestMemCheckHandler::THREAD_SANITIZER:
  681. case cmCTestMemCheckHandler::MEMORY_SANITIZER:
  682. case cmCTestMemCheckHandler::UB_SANITIZER: {
  683. // To pass arguments to ThreadSanitizer the environment variable
  684. // TSAN_OPTIONS is used. This is done with the cmake -E env command.
  685. // The MemoryTesterDynamicOptions is setup with the -E env
  686. // Then the MemoryTesterEnvironmentVariable gets the
  687. // TSAN_OPTIONS string with the log_path in it.
  688. this->MemoryTesterDynamicOptions.emplace_back("-E");
  689. this->MemoryTesterDynamicOptions.emplace_back("env");
  690. std::string envVar;
  691. std::string extraOptions;
  692. std::string suppressionsOption;
  693. if (!this->CTest->GetCTestConfiguration("MemoryCheckSanitizerOptions")
  694. .empty()) {
  695. extraOptions = ":" +
  696. this->CTest->GetCTestConfiguration("MemoryCheckSanitizerOptions");
  697. }
  698. if (!this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
  699. .empty()) {
  700. suppressionsOption = ":suppressions=" +
  701. this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile");
  702. }
  703. if (this->MemoryTesterStyle ==
  704. cmCTestMemCheckHandler::ADDRESS_SANITIZER) {
  705. envVar = "ASAN_OPTIONS";
  706. } else if (this->MemoryTesterStyle ==
  707. cmCTestMemCheckHandler::LEAK_SANITIZER) {
  708. envVar = "LSAN_OPTIONS";
  709. } else if (this->MemoryTesterStyle ==
  710. cmCTestMemCheckHandler::THREAD_SANITIZER) {
  711. envVar = "TSAN_OPTIONS";
  712. } else if (this->MemoryTesterStyle ==
  713. cmCTestMemCheckHandler::MEMORY_SANITIZER) {
  714. envVar = "MSAN_OPTIONS";
  715. } else if (this->MemoryTesterStyle ==
  716. cmCTestMemCheckHandler::UB_SANITIZER) {
  717. envVar = "UBSAN_OPTIONS";
  718. }
  719. // Quote log_path with single quotes; see
  720. // https://bugs.chromium.org/p/chromium/issues/detail?id=467936
  721. std::string outputFile =
  722. envVar + "=log_path='" + this->MemoryTesterOutputFile + "'";
  723. this->MemoryTesterEnvironmentVariable =
  724. outputFile + suppressionsOption + extraOptions;
  725. break;
  726. }
  727. default:
  728. cmCTestLog(this->CTest, ERROR_MESSAGE,
  729. "Do not understand memory checker: " << this->MemoryTester
  730. << std::endl);
  731. return false;
  732. }
  733. this->InitializeResultsVectors();
  734. // std::vector<std::string>::size_type cc;
  735. // for ( cc = 0; cmCTestMemCheckResultStrings[cc]; cc ++ )
  736. // {
  737. // this->MemoryTesterGlobalResults[cc] = 0;
  738. // }
  739. return true;
  740. }
  741. bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
  742. std::string& log,
  743. std::vector<int>& results)
  744. {
  745. switch (this->MemoryTesterStyle) {
  746. case cmCTestMemCheckHandler::VALGRIND:
  747. return this->ProcessMemCheckValgrindOutput(str, log, results);
  748. case cmCTestMemCheckHandler::DRMEMORY:
  749. return this->ProcessMemCheckDrMemoryOutput(str, log, results);
  750. case cmCTestMemCheckHandler::PURIFY:
  751. return this->ProcessMemCheckPurifyOutput(str, log, results);
  752. case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
  753. case cmCTestMemCheckHandler::LEAK_SANITIZER:
  754. case cmCTestMemCheckHandler::THREAD_SANITIZER:
  755. case cmCTestMemCheckHandler::MEMORY_SANITIZER:
  756. case cmCTestMemCheckHandler::UB_SANITIZER:
  757. return this->ProcessMemCheckSanitizerOutput(str, log, results);
  758. case cmCTestMemCheckHandler::BOUNDS_CHECKER:
  759. return this->ProcessMemCheckBoundsCheckerOutput(str, log, results);
  760. case cmCTestMemCheckHandler::CUDA_SANITIZER:
  761. return this->ProcessMemCheckCudaOutput(str, log, results);
  762. default:
  763. log.append("\nMemory checking style used was: ");
  764. log.append("None that I know");
  765. log = str;
  766. return true;
  767. }
  768. }
  769. std::vector<int>::size_type cmCTestMemCheckHandler::FindOrAddWarning(
  770. const std::string& warning)
  771. {
  772. for (std::vector<std::string>::size_type i = 0;
  773. i < this->ResultStrings.size(); ++i) {
  774. if (this->ResultStrings[i] == warning) {
  775. return i;
  776. }
  777. }
  778. this->GlobalResults.push_back(0); // this must stay the same size
  779. this->ResultStrings.push_back(warning);
  780. this->ResultStringsLong.push_back(warning);
  781. return this->ResultStrings.size() - 1;
  782. }
  783. bool cmCTestMemCheckHandler::ProcessMemCheckSanitizerOutput(
  784. const std::string& str, std::string& log, std::vector<int>& result)
  785. {
  786. std::string regex;
  787. switch (this->MemoryTesterStyle) {
  788. case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
  789. regex = "ERROR: AddressSanitizer: (.*) on.*";
  790. break;
  791. case cmCTestMemCheckHandler::LEAK_SANITIZER:
  792. // use leakWarning regex
  793. break;
  794. case cmCTestMemCheckHandler::THREAD_SANITIZER:
  795. regex = "WARNING: ThreadSanitizer: (.*) \\(pid=.*\\)";
  796. break;
  797. case cmCTestMemCheckHandler::MEMORY_SANITIZER:
  798. regex = "WARNING: MemorySanitizer: (.*)";
  799. break;
  800. case cmCTestMemCheckHandler::UB_SANITIZER:
  801. regex = "runtime error: (.*)";
  802. break;
  803. default:
  804. break;
  805. }
  806. cmsys::RegularExpression sanitizerWarning(regex);
  807. cmsys::RegularExpression leakWarning("(Direct|Indirect) leak of .*");
  808. int defects = 0;
  809. std::vector<std::string> lines;
  810. cmsys::SystemTools::Split(str, lines);
  811. std::ostringstream ostr;
  812. log.clear();
  813. for (std::string const& l : lines) {
  814. std::string resultFound;
  815. if (leakWarning.find(l)) {
  816. resultFound = leakWarning.match(1) + " leak";
  817. } else if (sanitizerWarning.find(l)) {
  818. resultFound = sanitizerWarning.match(1);
  819. }
  820. if (!resultFound.empty()) {
  821. std::vector<int>::size_type idx = this->FindOrAddWarning(resultFound);
  822. if (result.empty() || idx > result.size() - 1) {
  823. result.push_back(1);
  824. } else {
  825. result[idx]++;
  826. }
  827. defects++;
  828. ostr << "<b>" << this->ResultStrings[idx] << "</b> ";
  829. }
  830. ostr << l << std::endl;
  831. }
  832. log = ostr.str();
  833. this->DefectCount += defects;
  834. return defects == 0;
  835. }
  836. bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput(
  837. const std::string& str, std::string& log, std::vector<int>& results)
  838. {
  839. std::vector<std::string> lines;
  840. cmsys::SystemTools::Split(str, lines);
  841. std::ostringstream ostr;
  842. log.clear();
  843. cmsys::RegularExpression pfW("^\\[[WEI]\\] ([A-Z][A-Z][A-Z][A-Z]*): ");
  844. int defects = 0;
  845. for (std::string const& l : lines) {
  846. std::vector<int>::size_type failure = this->ResultStrings.size();
  847. if (pfW.find(l)) {
  848. std::vector<int>::size_type cc;
  849. for (cc = 0; cc < this->ResultStrings.size(); cc++) {
  850. if (pfW.match(1) == this->ResultStrings[cc]) {
  851. failure = cc;
  852. break;
  853. }
  854. }
  855. if (cc == this->ResultStrings.size()) {
  856. cmCTestLog(this->CTest, ERROR_MESSAGE,
  857. "Unknown Purify memory fault: " << pfW.match(1)
  858. << std::endl);
  859. ostr << "*** Unknown Purify memory fault: " << pfW.match(1)
  860. << std::endl;
  861. }
  862. }
  863. if (failure != this->ResultStrings.size()) {
  864. ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
  865. results[failure]++;
  866. defects++;
  867. }
  868. ostr << l << std::endl;
  869. }
  870. log = ostr.str();
  871. this->DefectCount += defects;
  872. return defects == 0;
  873. }
  874. bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
  875. const std::string& str, std::string& log, std::vector<int>& results)
  876. {
  877. std::vector<std::string> lines;
  878. cmsys::SystemTools::Split(str, lines);
  879. bool unlimitedOutput = false;
  880. if (str.find("CTEST_FULL_OUTPUT") != std::string::npos ||
  881. this->CustomMaximumFailedTestOutputSize == 0) {
  882. unlimitedOutput = true;
  883. }
  884. std::string::size_type cc;
  885. std::ostringstream ostr;
  886. log.clear();
  887. int defects = 0;
  888. cmsys::RegularExpression valgrindLine("^==[0-9][0-9]*==");
  889. cmsys::RegularExpression vgFIM(
  890. R"(== .*Invalid free\(\) / delete / delete\[\])");
  891. cmsys::RegularExpression vgFMM(
  892. R"(== .*Mismatched free\(\) / delete / delete \[\])");
  893. cmsys::RegularExpression vgMLK1(
  894. "== .*[0-9,]+ bytes in [0-9,]+ blocks are definitely lost"
  895. " in loss record [0-9,]+ of [0-9,]+");
  896. cmsys::RegularExpression vgMLK2(
  897. "== .*[0-9,]+ \\([0-9,]+ direct, [0-9,]+ indirect\\)"
  898. " bytes in [0-9,]+ blocks are definitely lost"
  899. " in loss record [0-9,]+ of [0-9,]+");
  900. cmsys::RegularExpression vgPAR(
  901. "== .*Syscall param .* (contains|points to) unaddressable byte\\(s\\)");
  902. cmsys::RegularExpression vgMPK1(
  903. "== .*[0-9,]+ bytes in [0-9,]+ blocks are possibly lost in"
  904. " loss record [0-9,]+ of [0-9,]+");
  905. cmsys::RegularExpression vgMPK2(
  906. "== .*[0-9,]+ bytes in [0-9,]+ blocks are still reachable"
  907. " in loss record [0-9,]+ of [0-9,]+");
  908. cmsys::RegularExpression vgUMC(
  909. "== .*Conditional jump or move depends on uninitiali[sz]ed value\\(s\\)");
  910. cmsys::RegularExpression vgUMR1(
  911. "== .*Use of uninitiali[sz]ed value of size [0-9,]+");
  912. cmsys::RegularExpression vgUMR2("== .*Invalid read of size [0-9,]+");
  913. cmsys::RegularExpression vgUMR3("== .*Jump to the invalid address ");
  914. cmsys::RegularExpression vgUMR4(
  915. "== .*Syscall param .* contains "
  916. "uninitiali[sz]ed or unaddressable byte\\(s\\)");
  917. cmsys::RegularExpression vgUMR5("== .*Syscall param .* uninitiali[sz]ed");
  918. cmsys::RegularExpression vgIPW("== .*Invalid write of size [0-9,]+");
  919. cmsys::RegularExpression vgABR("== .*pthread_mutex_unlock: mutex is "
  920. "locked by a different thread");
  921. std::vector<std::string::size_type> nonValGrindOutput;
  922. auto sttime = std::chrono::steady_clock::now();
  923. cmCTestOptionalLog(this->CTest, DEBUG,
  924. "Start test: " << lines.size() << std::endl, this->Quiet);
  925. std::string::size_type totalOutputSize = 0;
  926. for (cc = 0; cc < lines.size(); cc++) {
  927. cmCTestOptionalLog(this->CTest, DEBUG,
  928. "test line " << lines[cc] << std::endl, this->Quiet);
  929. if (valgrindLine.find(lines[cc])) {
  930. cmCTestOptionalLog(this->CTest, DEBUG,
  931. "valgrind line " << lines[cc] << std::endl,
  932. this->Quiet);
  933. int failure = cmCTestMemCheckHandler::NO_MEMORY_FAULT;
  934. auto& line = lines[cc];
  935. if (vgFIM.find(line)) {
  936. failure = cmCTestMemCheckHandler::FIM;
  937. } else if (vgFMM.find(line)) {
  938. failure = cmCTestMemCheckHandler::FMM;
  939. } else if (vgMLK1.find(line) || vgMLK2.find(line)) {
  940. failure = cmCTestMemCheckHandler::MLK;
  941. } else if (vgPAR.find(line)) {
  942. failure = cmCTestMemCheckHandler::PAR;
  943. } else if (vgMPK1.find(line) || vgMPK2.find(line)) {
  944. failure = cmCTestMemCheckHandler::MPK;
  945. } else if (vgUMC.find(line)) {
  946. failure = cmCTestMemCheckHandler::UMC;
  947. } else if (vgUMR1.find(line) || vgUMR2.find(line) || vgUMR3.find(line) ||
  948. vgUMR4.find(line) || vgUMR5.find(line)) {
  949. failure = cmCTestMemCheckHandler::UMR;
  950. } else if (vgIPW.find(line)) {
  951. failure = cmCTestMemCheckHandler::IPW;
  952. } else if (vgABR.find(line)) {
  953. failure = cmCTestMemCheckHandler::ABR;
  954. }
  955. if (failure != cmCTestMemCheckHandler::NO_MEMORY_FAULT) {
  956. ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
  957. results[failure]++;
  958. defects++;
  959. }
  960. totalOutputSize += lines[cc].size();
  961. ostr << lines[cc] << std::endl;
  962. } else {
  963. nonValGrindOutput.push_back(cc);
  964. }
  965. }
  966. // Now put all all the non valgrind output into the test output
  967. // This should be last in case it gets truncated by the output
  968. // limiting code
  969. for (std::string::size_type i : nonValGrindOutput) {
  970. totalOutputSize += lines[i].size();
  971. ostr << lines[i] << std::endl;
  972. if (!unlimitedOutput &&
  973. totalOutputSize >
  974. static_cast<size_t>(this->CustomMaximumFailedTestOutputSize)) {
  975. ostr << "....\n";
  976. ostr << "Test Output for this test has been truncated see testing"
  977. " machine logs for full output,\n";
  978. ostr << "or put CTEST_FULL_OUTPUT in the output of "
  979. "this test program.\n";
  980. break; // stop the copy of output if we are full
  981. }
  982. }
  983. cmCTestOptionalLog(this->CTest, DEBUG,
  984. "End test (elapsed: "
  985. << cmDurationTo<unsigned int>(
  986. std::chrono::steady_clock::now() - sttime)
  987. << "s)" << std::endl,
  988. this->Quiet);
  989. log = ostr.str();
  990. this->DefectCount += defects;
  991. return defects == 0;
  992. }
  993. bool cmCTestMemCheckHandler::ProcessMemCheckDrMemoryOutput(
  994. const std::string& str, std::string& log, std::vector<int>& results)
  995. {
  996. std::vector<std::string> lines;
  997. cmsys::SystemTools::Split(str, lines);
  998. cmsys::RegularExpression drMemoryError("^Error #[0-9]+");
  999. cmsys::RegularExpression unaddressableAccess("UNADDRESSABLE ACCESS");
  1000. cmsys::RegularExpression uninitializedRead("UNINITIALIZED READ");
  1001. cmsys::RegularExpression invalidHeapArgument("INVALID HEAP ARGUMENT");
  1002. cmsys::RegularExpression leak("LEAK");
  1003. cmsys::RegularExpression handleLeak("HANDLE LEAK");
  1004. int defects = 0;
  1005. std::ostringstream ostr;
  1006. for (const auto& l : lines) {
  1007. ostr << l << std::endl;
  1008. if (drMemoryError.find(l)) {
  1009. defects++;
  1010. if (unaddressableAccess.find(l) || uninitializedRead.find(l)) {
  1011. results[cmCTestMemCheckHandler::UMR]++;
  1012. } else if (leak.find(l) || handleLeak.find(l)) {
  1013. results[cmCTestMemCheckHandler::MLK]++;
  1014. } else if (invalidHeapArgument.find(l)) {
  1015. results[cmCTestMemCheckHandler::FMM]++;
  1016. }
  1017. }
  1018. }
  1019. log = ostr.str();
  1020. this->DefectCount += defects;
  1021. return defects == 0;
  1022. }
  1023. bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
  1024. const std::string& str, std::string& log, std::vector<int>& results)
  1025. {
  1026. log.clear();
  1027. auto sttime = std::chrono::steady_clock::now();
  1028. std::vector<std::string> lines;
  1029. cmsys::SystemTools::Split(str, lines);
  1030. cmCTestOptionalLog(this->CTest, DEBUG,
  1031. "Start test: " << lines.size() << std::endl, this->Quiet);
  1032. std::vector<std::string>::size_type cc;
  1033. for (cc = 0; cc < lines.size(); cc++) {
  1034. if (lines[cc] == BOUNDS_CHECKER_MARKER) {
  1035. break;
  1036. }
  1037. }
  1038. cmBoundsCheckerParser parser(this->CTest);
  1039. parser.InitializeParser();
  1040. if (cc < lines.size()) {
  1041. for (cc++; cc < lines.size(); ++cc) {
  1042. std::string& theLine = lines[cc];
  1043. // check for command line arguments that are not escaped
  1044. // correctly by BC
  1045. if (theLine.find("TargetArgs=") != std::string::npos) {
  1046. // skip this because BC gets it wrong and we can't parse it
  1047. } else if (!parser.ParseChunk(theLine.c_str(), theLine.size())) {
  1048. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1049. "Error in ParseChunk: " << theLine << std::endl);
  1050. }
  1051. }
  1052. }
  1053. int defects = 0;
  1054. for (int err : parser.Errors) {
  1055. results[err]++;
  1056. defects++;
  1057. }
  1058. cmCTestOptionalLog(this->CTest, DEBUG,
  1059. "End test (elapsed: "
  1060. << cmDurationTo<unsigned int>(
  1061. std::chrono::steady_clock::now() - sttime)
  1062. << "s)" << std::endl,
  1063. this->Quiet);
  1064. if (defects) {
  1065. // only put the output of Bounds Checker if there were
  1066. // errors or leaks detected
  1067. log = parser.Log;
  1068. }
  1069. this->DefectCount += defects;
  1070. return defects == 0;
  1071. }
  1072. bool cmCTestMemCheckHandler::ProcessMemCheckCudaOutput(
  1073. const std::string& str, std::string& log, std::vector<int>& results)
  1074. {
  1075. std::vector<std::string> lines;
  1076. cmsys::SystemTools::Split(str, lines);
  1077. bool unlimitedOutput = false;
  1078. if (str.find("CTEST_FULL_OUTPUT") != std::string::npos ||
  1079. this->CustomMaximumFailedTestOutputSize == 0) {
  1080. unlimitedOutput = true;
  1081. }
  1082. std::string::size_type cc;
  1083. std::ostringstream ostr;
  1084. log.clear();
  1085. int defects = 0;
  1086. cmsys::RegularExpression memcheckLine("^========");
  1087. cmsys::RegularExpression leakExpr("== Leaked [0-9,]+ bytes at");
  1088. // list of matchers for output messages that contain variable content
  1089. // (addresses, sizes, ...) or can be shortened in general. the first match is
  1090. // used as a error name.
  1091. std::vector<cmsys::RegularExpression> matchers{
  1092. // API errors
  1093. "== Malloc/Free error encountered: (.*)",
  1094. "== Program hit error ([^ ]*).* on CUDA API call to",
  1095. "== Program hit ([^ ]*).* on CUDA API call to",
  1096. // memcheck
  1097. "== (Invalid .*) of size [0-9,]+", "== (Fatal UVM [CG]PU fault)",
  1098. // racecheck
  1099. "== .* (Potential .* hazard detected)", "== .* (Race reported)",
  1100. // synccheck
  1101. "== (Barrier error)",
  1102. // initcheck
  1103. "== (Uninitialized .* memory read)", "== (Unused memory)",
  1104. "== (Host API memory access error)",
  1105. // generic error: ignore ERROR SUMMARY, CUDA-MEMCHECK and others
  1106. "== ([A-Z][a-z].*)"
  1107. };
  1108. // matchers for messages that aren't defects, but caught by above matchers
  1109. std::vector<cmsys::RegularExpression> false_positive_matchers{
  1110. "== Error: No attachable process found.*timed-out",
  1111. "== Default timeout can be adjusted with --launch-timeout",
  1112. "== Error: Target application terminated before first instrumented API",
  1113. "== Tracking kernels launched by child processes requires"
  1114. };
  1115. std::vector<std::string::size_type> nonMemcheckOutput;
  1116. auto sttime = std::chrono::steady_clock::now();
  1117. cmCTestOptionalLog(this->CTest, DEBUG,
  1118. "Start test: " << lines.size() << std::endl, this->Quiet);
  1119. std::string::size_type totalOutputSize = 0;
  1120. for (cc = 0; cc < lines.size(); cc++) {
  1121. cmCTestOptionalLog(this->CTest, DEBUG,
  1122. "test line " << lines[cc] << std::endl, this->Quiet);
  1123. if (memcheckLine.find(lines[cc])) {
  1124. cmCTestOptionalLog(this->CTest, DEBUG,
  1125. "cuda sanitizer line " << lines[cc] << std::endl,
  1126. this->Quiet);
  1127. int failure = -1;
  1128. auto& line = lines[cc];
  1129. if (leakExpr.find(line)) {
  1130. failure = static_cast<int>(this->FindOrAddWarning("Memory leak"));
  1131. } else {
  1132. auto match_predicate =
  1133. [&line](cmsys::RegularExpression& matcher) -> bool {
  1134. return matcher.find(line);
  1135. };
  1136. auto const pos_matcher =
  1137. std::find_if(matchers.begin(), matchers.end(), match_predicate);
  1138. if (pos_matcher != matchers.end()) {
  1139. if (!std::any_of(false_positive_matchers.begin(),
  1140. false_positive_matchers.end(), match_predicate)) {
  1141. failure =
  1142. static_cast<int>(this->FindOrAddWarning(pos_matcher->match(1)));
  1143. }
  1144. }
  1145. }
  1146. if (failure >= 0) {
  1147. ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
  1148. if (results.empty() ||
  1149. static_cast<unsigned>(failure) > results.size() - 1) {
  1150. results.push_back(1);
  1151. } else {
  1152. results[failure]++;
  1153. }
  1154. defects++;
  1155. }
  1156. totalOutputSize += lines[cc].size();
  1157. ostr << lines[cc] << std::endl;
  1158. } else {
  1159. nonMemcheckOutput.push_back(cc);
  1160. }
  1161. }
  1162. // Now put all all the non cuda sanitizer output into the test output
  1163. // This should be last in case it gets truncated by the output
  1164. // limiting code
  1165. for (std::string::size_type i : nonMemcheckOutput) {
  1166. totalOutputSize += lines[i].size();
  1167. ostr << lines[i] << std::endl;
  1168. if (!unlimitedOutput &&
  1169. totalOutputSize >
  1170. static_cast<size_t>(this->CustomMaximumFailedTestOutputSize)) {
  1171. ostr << "....\n";
  1172. ostr << "Test Output for this test has been truncated see testing"
  1173. " machine logs for full output,\n";
  1174. ostr << "or put CTEST_FULL_OUTPUT in the output of "
  1175. "this test program.\n";
  1176. break; // stop the copy of output if we are full
  1177. }
  1178. }
  1179. cmCTestOptionalLog(this->CTest, DEBUG,
  1180. "End test (elapsed: "
  1181. << cmDurationTo<unsigned int>(
  1182. std::chrono::steady_clock::now() - sttime)
  1183. << "s)" << std::endl,
  1184. this->Quiet);
  1185. log = ostr.str();
  1186. this->DefectCount += defects;
  1187. return defects == 0;
  1188. }
  1189. // PostProcessTest memcheck results
  1190. void cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res, int test)
  1191. {
  1192. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1193. "PostProcessTest memcheck results for : " << res.Name
  1194. << std::endl,
  1195. this->Quiet);
  1196. if (this->MemoryTesterStyle == cmCTestMemCheckHandler::BOUNDS_CHECKER) {
  1197. this->PostProcessBoundsCheckerTest(res, test);
  1198. } else if (this->MemoryTesterStyle == cmCTestMemCheckHandler::DRMEMORY) {
  1199. this->PostProcessDrMemoryTest(res, test);
  1200. } else {
  1201. std::vector<std::string> files;
  1202. this->TestOutputFileNames(test, files);
  1203. for (std::string const& f : files) {
  1204. this->AppendMemTesterOutput(res, f);
  1205. }
  1206. }
  1207. }
  1208. // This method puts the bounds checker output file into the output
  1209. // for the test
  1210. void cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(
  1211. cmCTestTestResult& res, int test)
  1212. {
  1213. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1214. "PostProcessBoundsCheckerTest for : " << res.Name
  1215. << std::endl,
  1216. this->Quiet);
  1217. std::vector<std::string> files;
  1218. this->TestOutputFileNames(test, files);
  1219. if (files.empty()) {
  1220. return;
  1221. }
  1222. std::string ofile = files[0];
  1223. if (ofile.empty()) {
  1224. return;
  1225. }
  1226. // put a scope around this to close ifs so the file can be removed
  1227. {
  1228. cmsys::ifstream ifs(ofile.c_str());
  1229. if (!ifs) {
  1230. std::string log = "Cannot read memory tester output file: " + ofile;
  1231. cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
  1232. return;
  1233. }
  1234. res.Output += BOUNDS_CHECKER_MARKER;
  1235. res.Output += "\n";
  1236. std::string line;
  1237. while (cmSystemTools::GetLineFromStream(ifs, line)) {
  1238. res.Output += line;
  1239. res.Output += "\n";
  1240. }
  1241. }
  1242. cmSystemTools::Delay(1000);
  1243. cmSystemTools::RemoveFile(this->BoundsCheckerDPBDFile);
  1244. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1245. "Remove: " << this->BoundsCheckerDPBDFile << std::endl,
  1246. this->Quiet);
  1247. cmSystemTools::RemoveFile(this->BoundsCheckerXMLFile);
  1248. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1249. "Remove: " << this->BoundsCheckerXMLFile << std::endl,
  1250. this->Quiet);
  1251. }
  1252. void cmCTestMemCheckHandler::PostProcessDrMemoryTest(
  1253. cmCTestTestHandler::cmCTestTestResult& res, int test)
  1254. {
  1255. std::string drMemoryLogDir = this->MemoryTesterOutputFile.substr(
  1256. 0, this->MemoryTesterOutputFile.find("/*/results.txt"));
  1257. // replace placeholder of test
  1258. std::string::size_type pos = drMemoryLogDir.find("??");
  1259. if (pos != std::string::npos) {
  1260. drMemoryLogDir.replace(pos, 2, std::to_string(test));
  1261. }
  1262. cmsys::Glob g;
  1263. g.FindFiles(drMemoryLogDir + "/resfile.*");
  1264. const std::vector<std::string>& files = g.GetFiles();
  1265. for (const std::string& f : files) {
  1266. cmsys::ifstream ifs(f.c_str());
  1267. if (!ifs) {
  1268. std::string log = "Cannot read memory tester output file: " + f;
  1269. cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
  1270. return;
  1271. }
  1272. std::string resultFileLocation;
  1273. cmSystemTools::GetLineFromStream(ifs, resultFileLocation);
  1274. this->AppendMemTesterOutput(res, resultFileLocation);
  1275. ifs.close();
  1276. cmSystemTools::RemoveFile(f);
  1277. }
  1278. }
  1279. void cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res,
  1280. std::string const& ofile)
  1281. {
  1282. if (ofile.empty()) {
  1283. return;
  1284. }
  1285. // put ifs in scope so file can be deleted if needed
  1286. {
  1287. cmsys::ifstream ifs(ofile.c_str());
  1288. if (!ifs) {
  1289. std::string log = "Cannot read memory tester output file: " + ofile;
  1290. cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
  1291. return;
  1292. }
  1293. std::string line;
  1294. while (cmSystemTools::GetLineFromStream(ifs, line)) {
  1295. res.Output += line;
  1296. res.Output += "\n";
  1297. }
  1298. }
  1299. if (this->LogWithPID) {
  1300. auto pos = ofile.find_last_of('.');
  1301. if (pos != std::string::npos) {
  1302. auto ofileWithoutPid = ofile.substr(0, pos);
  1303. cmSystemTools::RenameFile(ofile, ofileWithoutPid);
  1304. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1305. "Renaming: " << ofile << " to: " << ofileWithoutPid
  1306. << "\n",
  1307. this->Quiet);
  1308. }
  1309. }
  1310. }
  1311. void cmCTestMemCheckHandler::TestOutputFileNames(
  1312. int test, std::vector<std::string>& files)
  1313. {
  1314. std::string index = std::to_string(test);
  1315. std::string ofile = this->MemoryTesterOutputFile;
  1316. std::string::size_type pos = ofile.find("??");
  1317. ofile.replace(pos, 2, index);
  1318. if (this->LogWithPID) {
  1319. ofile += ".*";
  1320. cmsys::Glob g;
  1321. g.FindFiles(ofile);
  1322. if (g.GetFiles().empty()) {
  1323. std::string log = "Cannot find memory tester output file: " + ofile;
  1324. cmCTestLog(this->CTest, WARNING, log << std::endl);
  1325. ofile.clear();
  1326. } else {
  1327. files = g.GetFiles();
  1328. return;
  1329. }
  1330. } else if (!cmSystemTools::FileExists(ofile)) {
  1331. std::string log = "Cannot find memory tester output file: " + ofile;
  1332. cmCTestLog(this->CTest, WARNING, log << std::endl);
  1333. ofile.clear();
  1334. }
  1335. files.push_back(std::move(ofile));
  1336. }