cmParseGTMCoverage.cxx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. #include "cmStandardIncludes.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include "cmSystemTools.h"
  5. #include "cmParseGTMCoverage.h"
  6. #include <cmsys/Directory.hxx>
  7. #include <cmsys/Glob.hxx>
  8. cmParseGTMCoverage::cmParseGTMCoverage(cmCTestCoverageHandlerContainer& cont,
  9. cmCTest* ctest)
  10. :Coverage(cont), CTest(ctest)
  11. {
  12. }
  13. bool cmParseGTMCoverage::ReadGTMCoverage(const char* file)
  14. {
  15. // Read the gtm_coverage.mcov file, that has two lines of data:
  16. // packages:/full/path/to/Vista/Packages
  17. // coverage_dir:/full/path/to/dir/with/*.mcov
  18. std::ifstream in(file);
  19. if(!in)
  20. {
  21. return false;
  22. }
  23. std::string line;
  24. cmSystemTools::GetLineFromStream(in, line);
  25. std::string::size_type pos = line.find(':', 0);
  26. std::string packages;
  27. if(pos != std::string::npos)
  28. {
  29. packages = line.substr(pos+1);
  30. }
  31. cmSystemTools::GetLineFromStream(in, line);
  32. pos = line.find(':', 0);
  33. std::string coverage_dir;
  34. if(pos != std::string::npos)
  35. {
  36. coverage_dir = line.substr(pos+1);
  37. }
  38. // load the mumps files from the packages directory
  39. this->LoadPackages(packages.c_str());
  40. // now load the *.mcov files from the coverage directory
  41. this->LoadCoverageData(coverage_dir.c_str());
  42. return true;
  43. }
  44. void cmParseGTMCoverage::InitializeFile(std::string& file)
  45. {
  46. // initialize the coverage information for a given mumps file
  47. std::ifstream in(file.c_str());
  48. if(!in)
  49. {
  50. return;
  51. }
  52. std::string line;
  53. cmCTestCoverageHandlerContainer::SingleFileCoverageVector&
  54. coverageVector = this->Coverage.TotalCoverage[file];
  55. if(!cmSystemTools::GetLineFromStream(in, line))
  56. {
  57. return;
  58. }
  59. // first line of a .m file can never be run
  60. coverageVector.push_back(-1);
  61. while( cmSystemTools::GetLineFromStream(in, line) )
  62. {
  63. // putting in a 0 for a line means it is executable code
  64. // putting in a -1 for a line means it is not executable code
  65. int val = -1; // assume line is not executable
  66. bool found = false;
  67. std::string::size_type i = 0;
  68. // (1) Search for the first whitespace or semicolon character on a line.
  69. //This will skip over labels if the line starts with one, or will simply
  70. //be the first character on the line for non-label lines.
  71. for(; i < line.size(); ++i)
  72. {
  73. if(line[i] == ' ' || line[i] == '\t' || line[i] == ';')
  74. {
  75. found = true;
  76. break;
  77. }
  78. }
  79. if(found)
  80. {
  81. // (2) If the first character found above is whitespace then continue the
  82. // search for the first following non-whitespace character.
  83. if(line[i] == ' ' || line[i] == '\t')
  84. {
  85. while(i < line.size() && (line[i] == ' ' || line[i] == '\t'))
  86. {
  87. i++;
  88. }
  89. }
  90. // (3) If the character found is not a semicolon then the line counts for
  91. // coverage.
  92. if(i < line.size() && line[i] != ';')
  93. {
  94. val = 0;
  95. }
  96. }
  97. coverageVector.push_back(val);
  98. }
  99. }
  100. bool cmParseGTMCoverage::LoadPackages(const char* d)
  101. {
  102. cmsys::Glob glob;
  103. glob.RecurseOn();
  104. std::string pat = d;
  105. pat += "/*.m";
  106. glob.FindFiles(pat.c_str());
  107. std::vector<std::string>& files = glob.GetFiles();
  108. std::vector<std::string>::iterator fileIt;
  109. for ( fileIt = files.begin(); fileIt != files.end();
  110. ++ fileIt )
  111. {
  112. std::string name = cmSystemTools::GetFilenameName(*fileIt);
  113. this->RoutineToDirectory[name.substr(0, name.size()-2)] = *fileIt;
  114. // initialze each file, this is left out until CDash is fixed
  115. // to handle large numbers of files
  116. // this->InitializeFile(*fileIt);
  117. }
  118. return true;
  119. }
  120. bool cmParseGTMCoverage::LoadCoverageData(const char* d)
  121. {
  122. // load all the .mcov files in the specified directory
  123. cmsys::Directory dir;
  124. if(!dir.Load(d))
  125. {
  126. return false;
  127. }
  128. size_t numf;
  129. unsigned int i;
  130. numf = dir.GetNumberOfFiles();
  131. for (i = 0; i < numf; i++)
  132. {
  133. std::string file = dir.GetFile(i);
  134. if(file != "." && file != ".."
  135. && !cmSystemTools::FileIsDirectory(file.c_str()))
  136. {
  137. std::string path = d;
  138. path += "/";
  139. path += file;
  140. if(cmSystemTools::GetFilenameLastExtension(path) == ".mcov")
  141. {
  142. if(!this->ReadMCovFile(path.c_str()))
  143. {
  144. return false;
  145. }
  146. }
  147. }
  148. }
  149. return true;
  150. }
  151. bool cmParseGTMCoverage::ParseFile(std::string& filepath,
  152. std::string& function,
  153. int& lineoffset)
  154. {
  155. std::ifstream in(filepath.c_str());
  156. if(!in)
  157. {
  158. return false;
  159. }
  160. std::string line;
  161. int linenum = 0;
  162. while( cmSystemTools::GetLineFromStream(in, line))
  163. {
  164. std::string::size_type pos = line.find(function.c_str());
  165. if(pos == 0)
  166. {
  167. char nextchar = line[function.size()];
  168. if(nextchar == ' ' || nextchar == '(')
  169. {
  170. lineoffset = linenum;
  171. return true;
  172. }
  173. }
  174. if(pos == 1)
  175. {
  176. char prevchar = line[0];
  177. char nextchar = line[function.size()+1];
  178. if(prevchar == '%' && (nextchar == ' ' || nextchar == '('))
  179. {
  180. lineoffset = linenum;
  181. return true;
  182. }
  183. }
  184. linenum++; // move to next line count
  185. }
  186. lineoffset = 0;
  187. cmCTestLog(this->CTest, ERROR_MESSAGE,
  188. "Could not find entry point : "
  189. << function << " in " << filepath << "\n");
  190. return false;
  191. }
  192. bool cmParseGTMCoverage::ParseLine(std::string const& line,
  193. std::string& routine,
  194. std::string& function,
  195. int& linenumber,
  196. int& count)
  197. {
  198. // this method parses lines from the .mcov file
  199. // each line has ^COVERAGE(...) in it, and there
  200. // are several varients of coverage lines:
  201. //
  202. // ^COVERAGE("DIC11","PR1",0)="2:0:0:0"
  203. // ( file , entry, line ) = "number_executed:timing_info"
  204. // ^COVERAGE("%RSEL","SRC")="1:0:0:0"
  205. // ( file , entry ) = "number_executed:timing_info"
  206. // ^COVERAGE("%RSEL","init",8,"FOR_LOOP",1)=1
  207. // ( file , entry, line, IGNORE ) =number_executed
  208. std::vector<cmStdString> args;
  209. std::string::size_type pos = line.find('(', 0);
  210. // if no ( is found, then return line has no coverage
  211. if(pos == std::string::npos)
  212. {
  213. return false;
  214. }
  215. std::string arg;
  216. bool done = false;
  217. // separate out all of the comma separated arguments found
  218. // in the COVERAGE(...) line
  219. while(line[pos] && !done)
  220. {
  221. // save the char we are looking at
  222. char cur = line[pos];
  223. // , or ) means end of argument
  224. if(cur == ',' || cur == ')')
  225. {
  226. // save the argument into the argument vector
  227. args.push_back(arg);
  228. // start on a new argument
  229. arg = "";
  230. // if we are at the end of the ), then finish while loop
  231. if(cur == ')')
  232. {
  233. done = true;
  234. }
  235. }
  236. else
  237. {
  238. // all chars except ", (, and % get stored in the arg string
  239. if(cur != '\"' && cur != '(' && cur != '%')
  240. {
  241. arg.append(1, line[pos]);
  242. }
  243. }
  244. // move to next char
  245. pos++;
  246. }
  247. // now parse the right hand side of the =
  248. pos = line.find('=');
  249. // no = found, this is an error
  250. if(pos == line.npos)
  251. {
  252. return false;
  253. }
  254. pos++; // move past =
  255. // if the next positing is not a ", then this is a
  256. // COVERAGE(..)=count line and turn the rest of the string
  257. // past the = into an integer and set it to count
  258. if(line[pos] != '\"')
  259. {
  260. count = atoi(line.substr(pos).c_str());
  261. }
  262. else
  263. {
  264. // this means line[pos] is a ", and we have a
  265. // COVERAGE(...)="1:0:0:0" type of line
  266. pos++; // move past "
  267. // find the first : past the "
  268. std::string::size_type pos2 = line.find(':', pos);
  269. // turn the string between the " and the first : into an integer
  270. // and set it to count
  271. count = atoi(line.substr(pos, pos2-pos).c_str());
  272. }
  273. // less then two arguments is an error
  274. if(args.size() < 2)
  275. {
  276. cmCTestLog(this->CTest, ERROR_MESSAGE,
  277. "Error parsing mcov line: [" << line << "]\n");
  278. return false;
  279. }
  280. routine = args[0]; // the routine is the first argument
  281. function = args[1]; // the function in the routine is the second
  282. // in the two argument only format
  283. // ^COVERAGE("%RSEL","SRC"), the line offset is 0
  284. if(args.size() == 2)
  285. {
  286. linenumber = 0;
  287. }
  288. else
  289. {
  290. // this is the format for this line
  291. // ^COVERAGE("%RSEL","SRC",count)
  292. linenumber = atoi(args[2].c_str());
  293. }
  294. return true;
  295. }
  296. bool cmParseGTMCoverage::ReadMCovFile(const char* file)
  297. {
  298. std::ifstream in(file);
  299. if(!in)
  300. {
  301. return false;
  302. }
  303. std::string line;
  304. std::string lastfunction;
  305. std::string lastroutine;
  306. std::string lastpath;
  307. int lastoffset = 0;
  308. while( cmSystemTools::GetLineFromStream(in, line))
  309. {
  310. // only look at lines that have coverage data
  311. if(line.find("^COVERAGE") == line.npos)
  312. {
  313. continue;
  314. }
  315. std::string filepath;
  316. std::string function;
  317. std::string routine;
  318. int linenumber = 0;
  319. int count = 0;
  320. this->ParseLine(line, routine, function, linenumber, count);
  321. // skip this one
  322. if(routine == "RSEL")
  323. {
  324. continue;
  325. }
  326. // no need to search the file if we just did it
  327. if(function == lastfunction && lastroutine == routine)
  328. {
  329. this->Coverage.TotalCoverage[lastpath][lastoffset + linenumber] += count;
  330. continue;
  331. }
  332. // Find the full path to the file
  333. std::map<cmStdString, cmStdString>::iterator i =
  334. this->RoutineToDirectory.find(routine);
  335. bool found = false;
  336. if(i != this->RoutineToDirectory.end())
  337. {
  338. filepath = i->second;
  339. found = true;
  340. }
  341. else
  342. {
  343. // try some alternate names
  344. const char* tryname[] = {"GUX", "GTM", "ONT", 0};
  345. for(int k=0; tryname[k] != 0; k++)
  346. {
  347. std::string routine2 = routine + tryname[k];
  348. i = this->RoutineToDirectory.find(routine2);
  349. if(i != this->RoutineToDirectory.end())
  350. {
  351. found = true;
  352. filepath = i->second;
  353. break; // break out of tryname loop if found
  354. }
  355. }
  356. }
  357. if(found)
  358. {
  359. int lineoffset;
  360. if(this->ParseFile(filepath,
  361. function,
  362. lineoffset))
  363. {
  364. // hack, this should be done on every file, but for now
  365. // just do it on the ones that have coverage at all
  366. if( this->Coverage.TotalCoverage[filepath].size() == 0)
  367. {
  368. this->InitializeFile(filepath);
  369. }
  370. cmCTestCoverageHandlerContainer::SingleFileCoverageVector&
  371. coverageVector = this->Coverage.TotalCoverage[filepath];
  372. coverageVector[lineoffset + linenumber] += count;
  373. }
  374. lastoffset = lineoffset;
  375. }
  376. else
  377. {
  378. cmCTestLog(this->CTest, ERROR_MESSAGE,
  379. "Can not find mumps file : "
  380. << routine << " referenced in this line of mcov data:\n"
  381. "[" << line << "]\n");
  382. }
  383. lastfunction = function;
  384. lastroutine = routine;
  385. lastpath = filepath;
  386. }
  387. return true;
  388. }