PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/extern/llvm/tools/clang/tools/scan-build/scan-build

https://bitbucket.org/dwilliamson/clreflect/
Perl | 1436 lines | 1214 code | 137 blank | 85 comment | 110 complexity | b0a5509aea981a37a72a9aa7fc001aee MD5 | raw file
Possible License(s): JSON, BSD-3-Clause
  1. #!/usr/bin/env perl
  2. #
  3. # The LLVM Compiler Infrastructure
  4. #
  5. # This file is distributed under the University of Illinois Open Source
  6. # License. See LICENSE.TXT for details.
  7. #
  8. ##===----------------------------------------------------------------------===##
  9. #
  10. # A script designed to wrap a build so that all calls to gcc are intercepted
  11. # and piped to the static analyzer.
  12. #
  13. ##===----------------------------------------------------------------------===##
  14. use strict;
  15. use warnings;
  16. use FindBin qw($RealBin);
  17. use Digest::MD5;
  18. use File::Basename;
  19. use Term::ANSIColor;
  20. use Term::ANSIColor qw(:constants);
  21. use Cwd qw/ getcwd abs_path /;
  22. use Sys::Hostname;
  23. my $Verbose = 0; # Verbose output from this script.
  24. my $Prog = "scan-build";
  25. my $BuildName;
  26. my $BuildDate;
  27. my $TERM = $ENV{'TERM'};
  28. my $UseColor = (defined $TERM and $TERM eq 'xterm-color' and -t STDOUT
  29. and defined $ENV{'SCAN_BUILD_COLOR'});
  30. my $UserName = HtmlEscape(getpwuid($<) || 'unknown');
  31. my $HostName = HtmlEscape(hostname() || 'unknown');
  32. my $CurrentDir = HtmlEscape(getcwd());
  33. my $CurrentDirSuffix = basename($CurrentDir);
  34. my $CmdArgs;
  35. my $HtmlTitle;
  36. my $Date = localtime();
  37. ##----------------------------------------------------------------------------##
  38. # Diagnostics
  39. ##----------------------------------------------------------------------------##
  40. sub Diag {
  41. if ($UseColor) {
  42. print BOLD, MAGENTA "$Prog: @_";
  43. print RESET;
  44. }
  45. else {
  46. print "$Prog: @_";
  47. }
  48. }
  49. sub DiagCrashes {
  50. my $Dir = shift;
  51. Diag ("The analyzer encountered problems on some source files.\n");
  52. Diag ("Preprocessed versions of these sources were deposited in '$Dir/failures'.\n");
  53. Diag ("Please consider submitting a bug report using these files:\n");
  54. Diag (" http://clang-analyzer.llvm.org/filing_bugs.html\n")
  55. }
  56. sub DieDiag {
  57. if ($UseColor) {
  58. print BOLD, RED "$Prog: ";
  59. print RESET, RED @_;
  60. print RESET;
  61. }
  62. else {
  63. print "$Prog: ", @_;
  64. }
  65. exit(0);
  66. }
  67. ##----------------------------------------------------------------------------##
  68. # Some initial preprocessing of Clang options.
  69. ##----------------------------------------------------------------------------##
  70. # Find 'clang'
  71. my $ClangSB = Cwd::realpath("$RealBin/bin/clang");
  72. if (!defined $ClangSB || ! -x $ClangSB) {
  73. $ClangSB = Cwd::realpath("$RealBin/clang");
  74. }
  75. my $Clang;
  76. if (!defined $ClangSB || ! -x $ClangSB) {
  77. # Default to looking for 'clang' in the path.
  78. $Clang = `which clang`;
  79. chomp $Clang;
  80. if ($Clang eq "") {
  81. DieDiag("No 'clang' executable found in path.");
  82. }
  83. }
  84. else {
  85. $Clang = $ClangSB;
  86. }
  87. my $ClangCXX = $Clang . "++";
  88. ##----------------------------------------------------------------------------##
  89. # GetHTMLRunDir - Construct an HTML directory name for the current sub-run.
  90. ##----------------------------------------------------------------------------##
  91. sub GetHTMLRunDir {
  92. die "Not enough arguments." if (@_ == 0);
  93. my $Dir = shift @_;
  94. my $TmpMode = 0;
  95. if (!defined $Dir) {
  96. if (`uname` =~ /Darwin/) {
  97. $Dir = $ENV{'TMPDIR'};
  98. if (!defined $Dir) { $Dir = "/tmp"; }
  99. }
  100. else {
  101. $Dir = "/tmp";
  102. }
  103. $TmpMode = 1;
  104. }
  105. # Chop off any trailing '/' characters.
  106. while ($Dir =~ /\/$/) { chop $Dir; }
  107. # Get current date and time.
  108. my @CurrentTime = localtime();
  109. my $year = $CurrentTime[5] + 1900;
  110. my $day = $CurrentTime[3];
  111. my $month = $CurrentTime[4] + 1;
  112. my $DateString = sprintf("%d-%02d-%02d", $year, $month, $day);
  113. # Determine the run number.
  114. my $RunNumber;
  115. if (-d $Dir) {
  116. if (! -r $Dir) {
  117. DieDiag("directory '$Dir' exists but is not readable.\n");
  118. }
  119. # Iterate over all files in the specified directory.
  120. my $max = 0;
  121. opendir(DIR, $Dir);
  122. my @FILES = grep { -d "$Dir/$_" } readdir(DIR);
  123. closedir(DIR);
  124. foreach my $f (@FILES) {
  125. # Strip the prefix '$Prog-' if we are dumping files to /tmp.
  126. if ($TmpMode) {
  127. next if (!($f =~ /^$Prog-(.+)/));
  128. $f = $1;
  129. }
  130. my @x = split/-/, $f;
  131. next if (scalar(@x) != 4);
  132. next if ($x[0] != $year);
  133. next if ($x[1] != $month);
  134. next if ($x[2] != $day);
  135. if ($x[3] > $max) {
  136. $max = $x[3];
  137. }
  138. }
  139. $RunNumber = $max + 1;
  140. }
  141. else {
  142. if (-x $Dir) {
  143. DieDiag("'$Dir' exists but is not a directory.\n");
  144. }
  145. if ($TmpMode) {
  146. DieDiag("The directory '/tmp' does not exist or cannot be accessed.\n");
  147. }
  148. # $Dir does not exist. It will be automatically created by the
  149. # clang driver. Set the run number to 1.
  150. $RunNumber = 1;
  151. }
  152. die "RunNumber must be defined!" if (!defined $RunNumber);
  153. # Append the run number.
  154. my $NewDir;
  155. if ($TmpMode) {
  156. $NewDir = "$Dir/$Prog-$DateString-$RunNumber";
  157. }
  158. else {
  159. $NewDir = "$Dir/$DateString-$RunNumber";
  160. }
  161. system 'mkdir','-p',$NewDir;
  162. return $NewDir;
  163. }
  164. sub SetHtmlEnv {
  165. die "Wrong number of arguments." if (scalar(@_) != 2);
  166. my $Args = shift;
  167. my $Dir = shift;
  168. die "No build command." if (scalar(@$Args) == 0);
  169. my $Cmd = $$Args[0];
  170. if ($Cmd =~ /configure/ || $Cmd =~ /autogen/) {
  171. return;
  172. }
  173. if ($Verbose) {
  174. Diag("Emitting reports for this run to '$Dir'.\n");
  175. }
  176. $ENV{'CCC_ANALYZER_HTML'} = $Dir;
  177. }
  178. ##----------------------------------------------------------------------------##
  179. # ComputeDigest - Compute a digest of the specified file.
  180. ##----------------------------------------------------------------------------##
  181. sub ComputeDigest {
  182. my $FName = shift;
  183. DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName);
  184. # Use Digest::MD5. We don't have to be cryptographically secure. We're
  185. # just looking for duplicate files that come from a non-malicious source.
  186. # We use Digest::MD5 because it is a standard Perl module that should
  187. # come bundled on most systems.
  188. open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n");
  189. binmode FILE;
  190. my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest;
  191. close(FILE);
  192. # Return the digest.
  193. return $Result;
  194. }
  195. ##----------------------------------------------------------------------------##
  196. # UpdatePrefix - Compute the common prefix of files.
  197. ##----------------------------------------------------------------------------##
  198. my $Prefix;
  199. sub UpdatePrefix {
  200. my $x = shift;
  201. my $y = basename($x);
  202. $x =~ s/\Q$y\E$//;
  203. if (!defined $Prefix) {
  204. $Prefix = $x;
  205. return;
  206. }
  207. chop $Prefix while (!($x =~ /^\Q$Prefix/));
  208. }
  209. sub GetPrefix {
  210. return $Prefix;
  211. }
  212. ##----------------------------------------------------------------------------##
  213. # UpdateInFilePath - Update the path in the report file.
  214. ##----------------------------------------------------------------------------##
  215. sub UpdateInFilePath {
  216. my $fname = shift;
  217. my $regex = shift;
  218. my $newtext = shift;
  219. open (RIN, $fname) or die "cannot open $fname";
  220. open (ROUT, ">", "$fname.tmp") or die "cannot open $fname.tmp";
  221. while (<RIN>) {
  222. s/$regex/$newtext/;
  223. print ROUT $_;
  224. }
  225. close (ROUT);
  226. close (RIN);
  227. system("mv", "$fname.tmp", $fname);
  228. }
  229. ##----------------------------------------------------------------------------##
  230. # AddStatLine - Decode and insert a statistics line into the database.
  231. ##----------------------------------------------------------------------------##
  232. sub AddStatLine {
  233. my $Line = shift;
  234. my $Stats = shift;
  235. print $Line . "\n";
  236. my $Regex = qr/(.*?)\ :\ (.*?)\ ->\ Total\ CFGBlocks:\ (\d+)\ \|\ Unreachable
  237. \ CFGBlocks:\ (\d+)\ \|\ Exhausted\ Block:\ (yes|no)\ \|\ Empty\ WorkList:
  238. \ (yes|no)/x;
  239. if ($Line !~ $Regex) {
  240. return;
  241. }
  242. # Create a hash of the interesting fields
  243. my $Row = {
  244. Filename => $1,
  245. Function => $2,
  246. Total => $3,
  247. Unreachable => $4,
  248. Aborted => $5,
  249. Empty => $6
  250. };
  251. # Add them to the stats array
  252. push @$Stats, $Row;
  253. }
  254. ##----------------------------------------------------------------------------##
  255. # ScanFile - Scan a report file for various identifying attributes.
  256. ##----------------------------------------------------------------------------##
  257. # Sometimes a source file is scanned more than once, and thus produces
  258. # multiple error reports. We use a cache to solve this problem.
  259. my %AlreadyScanned;
  260. sub ScanFile {
  261. my $Index = shift;
  262. my $Dir = shift;
  263. my $FName = shift;
  264. my $Stats = shift;
  265. # Compute a digest for the report file. Determine if we have already
  266. # scanned a file that looks just like it.
  267. my $digest = ComputeDigest("$Dir/$FName");
  268. if (defined $AlreadyScanned{$digest}) {
  269. # Redundant file. Remove it.
  270. system ("rm", "-f", "$Dir/$FName");
  271. return;
  272. }
  273. $AlreadyScanned{$digest} = 1;
  274. # At this point the report file is not world readable. Make it happen.
  275. system ("chmod", "644", "$Dir/$FName");
  276. # Scan the report file for tags.
  277. open(IN, "$Dir/$FName") or DieDiag("Cannot open '$Dir/$FName'\n");
  278. my $BugType = "";
  279. my $BugFile = "";
  280. my $BugCategory = "";
  281. my $BugDescription = "";
  282. my $BugPathLength = 1;
  283. my $BugLine = 0;
  284. while (<IN>) {
  285. last if (/<!-- BUGMETAEND -->/);
  286. if (/<!-- BUGTYPE (.*) -->$/) {
  287. $BugType = $1;
  288. }
  289. elsif (/<!-- BUGFILE (.*) -->$/) {
  290. $BugFile = abs_path($1);
  291. UpdatePrefix($BugFile);
  292. }
  293. elsif (/<!-- BUGPATHLENGTH (.*) -->$/) {
  294. $BugPathLength = $1;
  295. }
  296. elsif (/<!-- BUGLINE (.*) -->$/) {
  297. $BugLine = $1;
  298. }
  299. elsif (/<!-- BUGCATEGORY (.*) -->$/) {
  300. $BugCategory = $1;
  301. }
  302. elsif (/<!-- BUGDESC (.*) -->$/) {
  303. $BugDescription = $1;
  304. }
  305. }
  306. close(IN);
  307. if (!defined $BugCategory) {
  308. $BugCategory = "Other";
  309. }
  310. # Don't add internal statistics to the bug reports
  311. if ($BugCategory =~ /statistics/i) {
  312. AddStatLine($BugDescription, $Stats);
  313. return;
  314. }
  315. push @$Index,[ $FName, $BugCategory, $BugType, $BugFile, $BugLine,
  316. $BugPathLength ];
  317. }
  318. ##----------------------------------------------------------------------------##
  319. # CopyFiles - Copy resource files to target directory.
  320. ##----------------------------------------------------------------------------##
  321. sub CopyFiles {
  322. my $Dir = shift;
  323. my $JS = Cwd::realpath("$RealBin/sorttable.js");
  324. DieDiag("Cannot find 'sorttable.js'.\n")
  325. if (! -r $JS);
  326. system ("cp", $JS, "$Dir");
  327. DieDiag("Could not copy 'sorttable.js' to '$Dir'.\n")
  328. if (! -r "$Dir/sorttable.js");
  329. my $CSS = Cwd::realpath("$RealBin/scanview.css");
  330. DieDiag("Cannot find 'scanview.css'.\n")
  331. if (! -r $CSS);
  332. system ("cp", $CSS, "$Dir");
  333. DieDiag("Could not copy 'scanview.css' to '$Dir'.\n")
  334. if (! -r $CSS);
  335. }
  336. ##----------------------------------------------------------------------------##
  337. # CalcStats - Calculates visitation statistics and returns the string.
  338. ##----------------------------------------------------------------------------##
  339. sub CalcStats {
  340. my $Stats = shift;
  341. my $TotalBlocks = 0;
  342. my $UnreachedBlocks = 0;
  343. my $TotalFunctions = scalar(@$Stats);
  344. my $BlockAborted = 0;
  345. my $WorkListAborted = 0;
  346. my $Aborted = 0;
  347. # Calculate the unique files
  348. my $FilesHash = {};
  349. foreach my $Row (@$Stats) {
  350. $FilesHash->{$Row->{Filename}} = 1;
  351. $TotalBlocks += $Row->{Total};
  352. $UnreachedBlocks += $Row->{Unreachable};
  353. $BlockAborted++ if $Row->{Aborted} eq 'yes';
  354. $WorkListAborted++ if $Row->{Empty} eq 'no';
  355. $Aborted++ if $Row->{Aborted} eq 'yes' || $Row->{Empty} eq 'no';
  356. }
  357. my $TotalFiles = scalar(keys(%$FilesHash));
  358. # Calculations
  359. my $PercentAborted = sprintf("%.2f", $Aborted / $TotalFunctions * 100);
  360. my $PercentBlockAborted = sprintf("%.2f", $BlockAborted / $TotalFunctions
  361. * 100);
  362. my $PercentWorkListAborted = sprintf("%.2f", $WorkListAborted /
  363. $TotalFunctions * 100);
  364. my $PercentBlocksUnreached = sprintf("%.2f", $UnreachedBlocks / $TotalBlocks
  365. * 100);
  366. my $StatsString = "Analyzed $TotalBlocks blocks in $TotalFunctions functions"
  367. . " in $TotalFiles files\n"
  368. . "$Aborted functions aborted early ($PercentAborted%)\n"
  369. . "$BlockAborted had aborted blocks ($PercentBlockAborted%)\n"
  370. . "$WorkListAborted had unfinished worklists ($PercentWorkListAborted%)\n"
  371. . "$UnreachedBlocks blocks were never reached ($PercentBlocksUnreached%)\n";
  372. return $StatsString;
  373. }
  374. ##----------------------------------------------------------------------------##
  375. # Postprocess - Postprocess the results of an analysis scan.
  376. ##----------------------------------------------------------------------------##
  377. sub Postprocess {
  378. my $Dir = shift;
  379. my $BaseDir = shift;
  380. my $AnalyzerStats = shift;
  381. die "No directory specified." if (!defined $Dir);
  382. if (! -d $Dir) {
  383. Diag("No bugs found.\n");
  384. return 0;
  385. }
  386. opendir(DIR, $Dir);
  387. my @files = grep { /^report-.*\.html$/ } readdir(DIR);
  388. closedir(DIR);
  389. if (scalar(@files) == 0 and ! -e "$Dir/failures") {
  390. Diag("Removing directory '$Dir' because it contains no reports.\n");
  391. system ("rm", "-fR", $Dir);
  392. return 0;
  393. }
  394. # Scan each report file and build an index.
  395. my @Index;
  396. my @Stats;
  397. foreach my $file (@files) { ScanFile(\@Index, $Dir, $file, \@Stats); }
  398. # Scan the failures directory and use the information in the .info files
  399. # to update the common prefix directory.
  400. my @failures;
  401. my @attributes_ignored;
  402. if (-d "$Dir/failures") {
  403. opendir(DIR, "$Dir/failures");
  404. @failures = grep { /[.]info.txt$/ && !/attribute_ignored/; } readdir(DIR);
  405. closedir(DIR);
  406. opendir(DIR, "$Dir/failures");
  407. @attributes_ignored = grep { /^attribute_ignored/; } readdir(DIR);
  408. closedir(DIR);
  409. foreach my $file (@failures) {
  410. open IN, "$Dir/failures/$file" or DieDiag("cannot open $file\n");
  411. my $Path = <IN>;
  412. if (defined $Path) { UpdatePrefix($Path); }
  413. close IN;
  414. }
  415. }
  416. # Generate an index.html file.
  417. my $FName = "$Dir/index.html";
  418. open(OUT, ">", $FName) or DieDiag("Cannot create file '$FName'\n");
  419. # Print out the header.
  420. print OUT <<ENDTEXT;
  421. <html>
  422. <head>
  423. <title>${HtmlTitle}</title>
  424. <link type="text/css" rel="stylesheet" href="scanview.css"/>
  425. <script src="sorttable.js"></script>
  426. <script language='javascript' type="text/javascript">
  427. function SetDisplay(RowClass, DisplayVal)
  428. {
  429. var Rows = document.getElementsByTagName("tr");
  430. for ( var i = 0 ; i < Rows.length; ++i ) {
  431. if (Rows[i].className == RowClass) {
  432. Rows[i].style.display = DisplayVal;
  433. }
  434. }
  435. }
  436. function CopyCheckedStateToCheckButtons(SummaryCheckButton) {
  437. var Inputs = document.getElementsByTagName("input");
  438. for ( var i = 0 ; i < Inputs.length; ++i ) {
  439. if (Inputs[i].type == "checkbox") {
  440. if(Inputs[i] != SummaryCheckButton) {
  441. Inputs[i].checked = SummaryCheckButton.checked;
  442. Inputs[i].onclick();
  443. }
  444. }
  445. }
  446. }
  447. function returnObjById( id ) {
  448. if (document.getElementById)
  449. var returnVar = document.getElementById(id);
  450. else if (document.all)
  451. var returnVar = document.all[id];
  452. else if (document.layers)
  453. var returnVar = document.layers[id];
  454. return returnVar;
  455. }
  456. var NumUnchecked = 0;
  457. function ToggleDisplay(CheckButton, ClassName) {
  458. if (CheckButton.checked) {
  459. SetDisplay(ClassName, "");
  460. if (--NumUnchecked == 0) {
  461. returnObjById("AllBugsCheck").checked = true;
  462. }
  463. }
  464. else {
  465. SetDisplay(ClassName, "none");
  466. NumUnchecked++;
  467. returnObjById("AllBugsCheck").checked = false;
  468. }
  469. }
  470. </script>
  471. <!-- SUMMARYENDHEAD -->
  472. </head>
  473. <body>
  474. <h1>${HtmlTitle}</h1>
  475. <table>
  476. <tr><th>User:</th><td>${UserName}\@${HostName}</td></tr>
  477. <tr><th>Working Directory:</th><td>${CurrentDir}</td></tr>
  478. <tr><th>Command Line:</th><td>${CmdArgs}</td></tr>
  479. <tr><th>Date:</th><td>${Date}</td></tr>
  480. ENDTEXT
  481. print OUT "<tr><th>Version:</th><td>${BuildName} (${BuildDate})</td></tr>\n"
  482. if (defined($BuildName) && defined($BuildDate));
  483. print OUT <<ENDTEXT;
  484. </table>
  485. ENDTEXT
  486. if (scalar(@files)) {
  487. # Print out the summary table.
  488. my %Totals;
  489. for my $row ( @Index ) {
  490. my $bug_type = ($row->[2]);
  491. my $bug_category = ($row->[1]);
  492. my $key = "$bug_category:$bug_type";
  493. if (!defined $Totals{$key}) { $Totals{$key} = [1,$bug_category,$bug_type]; }
  494. else { $Totals{$key}->[0]++; }
  495. }
  496. print OUT "<h2>Bug Summary</h2>";
  497. if (defined $BuildName) {
  498. print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n"
  499. }
  500. my $TotalBugs = scalar(@Index);
  501. print OUT <<ENDTEXT;
  502. <table>
  503. <thead><tr><td>Bug Type</td><td>Quantity</td><td class="sorttable_nosort">Display?</td></tr></thead>
  504. <tr style="font-weight:bold"><td class="SUMM_DESC">All Bugs</td><td class="Q">$TotalBugs</td><td><center><input type="checkbox" id="AllBugsCheck" onClick="CopyCheckedStateToCheckButtons(this);" checked/></center></td></tr>
  505. ENDTEXT
  506. my $last_category;
  507. for my $key (
  508. sort {
  509. my $x = $Totals{$a};
  510. my $y = $Totals{$b};
  511. my $res = $x->[1] cmp $y->[1];
  512. $res = $x->[2] cmp $y->[2] if ($res == 0);
  513. $res
  514. } keys %Totals )
  515. {
  516. my $val = $Totals{$key};
  517. my $category = $val->[1];
  518. if (!defined $last_category or $last_category ne $category) {
  519. $last_category = $category;
  520. print OUT "<tr><th>$category</th><th colspan=2></th></tr>\n";
  521. }
  522. my $x = lc $key;
  523. $x =~ s/[ ,'":\/()]+/_/g;
  524. print OUT "<tr><td class=\"SUMM_DESC\">";
  525. print OUT $val->[2];
  526. print OUT "</td><td class=\"Q\">";
  527. print OUT $val->[0];
  528. print OUT "</td><td><center><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></center></td></tr>\n";
  529. }
  530. # Print out the table of errors.
  531. print OUT <<ENDTEXT;
  532. </table>
  533. <h2>Reports</h2>
  534. <table class="sortable" style="table-layout:automatic">
  535. <thead><tr>
  536. <td>Bug Group</td>
  537. <td class="sorttable_sorted">Bug Type<span id="sorttable_sortfwdind">&nbsp;&#x25BE;</span></td>
  538. <td>File</td>
  539. <td class="Q">Line</td>
  540. <td class="Q">Path Length</td>
  541. <td class="sorttable_nosort"></td>
  542. <!-- REPORTBUGCOL -->
  543. </tr></thead>
  544. <tbody>
  545. ENDTEXT
  546. my $prefix = GetPrefix();
  547. my $regex;
  548. my $InFileRegex;
  549. my $InFilePrefix = "File:</td><td>";
  550. if (defined $prefix) {
  551. $regex = qr/^\Q$prefix\E/is;
  552. $InFileRegex = qr/\Q$InFilePrefix$prefix\E/is;
  553. }
  554. for my $row ( sort { $a->[2] cmp $b->[2] } @Index ) {
  555. my $x = "$row->[1]:$row->[2]";
  556. $x = lc $x;
  557. $x =~ s/[ ,'":\/()]+/_/g;
  558. my $ReportFile = $row->[0];
  559. print OUT "<tr class=\"bt_$x\">";
  560. print OUT "<td class=\"DESC\">";
  561. print OUT $row->[1];
  562. print OUT "</td>";
  563. print OUT "<td class=\"DESC\">";
  564. print OUT $row->[2];
  565. print OUT "</td>";
  566. # Update the file prefix.
  567. my $fname = $row->[3];
  568. if (defined $regex) {
  569. $fname =~ s/$regex//;
  570. UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix)
  571. }
  572. print OUT "<td>";
  573. my @fname = split /\//,$fname;
  574. if ($#fname > 0) {
  575. while ($#fname >= 0) {
  576. my $x = shift @fname;
  577. print OUT $x;
  578. if ($#fname >= 0) {
  579. print OUT "<span class=\"W\"> </span>/";
  580. }
  581. }
  582. }
  583. else {
  584. print OUT $fname;
  585. }
  586. print OUT "</td>";
  587. # Print out the quantities.
  588. for my $j ( 4 .. 5 ) {
  589. print OUT "<td class=\"Q\">$row->[$j]</td>";
  590. }
  591. # Print the rest of the columns.
  592. for (my $j = 6; $j <= $#{$row}; ++$j) {
  593. print OUT "<td>$row->[$j]</td>"
  594. }
  595. # Emit the "View" link.
  596. print OUT "<td><a href=\"$ReportFile#EndPath\">View Report</a></td>";
  597. # Emit REPORTBUG markers.
  598. print OUT "\n<!-- REPORTBUG id=\"$ReportFile\" -->\n";
  599. # End the row.
  600. print OUT "</tr>\n";
  601. }
  602. print OUT "</tbody>\n</table>\n\n";
  603. }
  604. if (scalar (@failures) || scalar(@attributes_ignored)) {
  605. print OUT "<h2>Analyzer Failures</h2>\n";
  606. if (scalar @attributes_ignored) {
  607. print OUT "The analyzer's parser ignored the following attributes:<p>\n";
  608. print OUT "<table>\n";
  609. print OUT "<thead><tr><td>Attribute</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n";
  610. foreach my $file (sort @attributes_ignored) {
  611. die "cannot demangle attribute name\n" if (! ($file =~ /^attribute_ignored_(.+).txt/));
  612. my $attribute = $1;
  613. # Open the attribute file to get the first file that failed.
  614. next if (!open (ATTR, "$Dir/failures/$file"));
  615. my $ppfile = <ATTR>;
  616. chomp $ppfile;
  617. close ATTR;
  618. next if (! -e "$Dir/failures/$ppfile");
  619. # Open the info file and get the name of the source file.
  620. open (INFO, "$Dir/failures/$ppfile.info.txt") or
  621. die "Cannot open $Dir/failures/$ppfile.info.txt\n";
  622. my $srcfile = <INFO>;
  623. chomp $srcfile;
  624. close (INFO);
  625. # Print the information in the table.
  626. my $prefix = GetPrefix();
  627. if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; }
  628. print OUT "<tr><td>$attribute</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n";
  629. my $ppfile_clang = $ppfile;
  630. $ppfile_clang =~ s/[.](.+)$/.clang.$1/;
  631. print OUT " <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n";
  632. }
  633. print OUT "</table>\n";
  634. }
  635. if (scalar @failures) {
  636. print OUT "<p>The analyzer had problems processing the following files:</p>\n";
  637. print OUT "<table>\n";
  638. print OUT "<thead><tr><td>Problem</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n";
  639. foreach my $file (sort @failures) {
  640. $file =~ /(.+).info.txt$/;
  641. # Get the preprocessed file.
  642. my $ppfile = $1;
  643. # Open the info file and get the name of the source file.
  644. open (INFO, "$Dir/failures/$file") or
  645. die "Cannot open $Dir/failures/$file\n";
  646. my $srcfile = <INFO>;
  647. chomp $srcfile;
  648. my $problem = <INFO>;
  649. chomp $problem;
  650. close (INFO);
  651. # Print the information in the table.
  652. my $prefix = GetPrefix();
  653. if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; }
  654. print OUT "<tr><td>$problem</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n";
  655. my $ppfile_clang = $ppfile;
  656. $ppfile_clang =~ s/[.](.+)$/.clang.$1/;
  657. print OUT " <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n";
  658. }
  659. print OUT "</table>\n";
  660. }
  661. print OUT "<p>Please consider submitting preprocessed files as <a href=\"http://clang-analyzer.llvm.org/filing_bugs.html\">bug reports</a>. <!-- REPORTCRASHES --> </p>\n";
  662. }
  663. print OUT "</body></html>\n";
  664. close(OUT);
  665. CopyFiles($Dir);
  666. # Make sure $Dir and $BaseDir are world readable/executable.
  667. system("chmod", "755", $Dir);
  668. if (defined $BaseDir) { system("chmod", "755", $BaseDir); }
  669. # Print statistics
  670. print CalcStats(\@Stats) if $AnalyzerStats;
  671. my $Num = scalar(@Index);
  672. Diag("$Num bugs found.\n");
  673. if ($Num > 0 && -r "$Dir/index.html") {
  674. Diag("Run 'scan-view $Dir' to examine bug reports.\n");
  675. }
  676. DiagCrashes($Dir) if (scalar @failures || scalar @attributes_ignored);
  677. return $Num;
  678. }
  679. ##----------------------------------------------------------------------------##
  680. # RunBuildCommand - Run the build command.
  681. ##----------------------------------------------------------------------------##
  682. sub AddIfNotPresent {
  683. my $Args = shift;
  684. my $Arg = shift;
  685. my $found = 0;
  686. foreach my $k (@$Args) {
  687. if ($k eq $Arg) {
  688. $found = 1;
  689. last;
  690. }
  691. }
  692. if ($found == 0) {
  693. push @$Args, $Arg;
  694. }
  695. }
  696. sub RunBuildCommand {
  697. my $Args = shift;
  698. my $IgnoreErrors = shift;
  699. my $Cmd = $Args->[0];
  700. my $CCAnalyzer = shift;
  701. my $CXXAnalyzer = shift;
  702. # Get only the part of the command after the last '/'.
  703. if ($Cmd =~ /\/([^\/]+)$/) {
  704. $Cmd = $1;
  705. }
  706. if ($Cmd =~ /(.*\/?gcc[^\/]*$)/ or
  707. $Cmd =~ /(.*\/?cc[^\/]*$)/ or
  708. $Cmd =~ /(.*\/?llvm-gcc[^\/]*$)/ or
  709. $Cmd =~ /(.*\/?clang$)/ or
  710. $Cmd =~ /(.*\/?ccc-analyzer[^\/]*$)/) {
  711. if (!($Cmd =~ /ccc-analyzer/) and !defined $ENV{"CCC_CC"}) {
  712. $ENV{"CCC_CC"} = $1;
  713. }
  714. shift @$Args;
  715. unshift @$Args, $CCAnalyzer;
  716. }
  717. elsif ($Cmd =~ /(.*\/?g\+\+[^\/]*$)/ or
  718. $Cmd =~ /(.*\/?c\+\+[^\/]*$)/ or
  719. $Cmd =~ /(.*\/?llvm-g\+\+[^\/]*$)/ or
  720. $Cmd =~ /(.*\/?clang\+\+$)/ or
  721. $Cmd =~ /(.*\/?c\+\+-analyzer[^\/]*$)/) {
  722. if (!($Cmd =~ /c\+\+-analyzer/) and !defined $ENV{"CCC_CXX"}) {
  723. $ENV{"CCC_CXX"} = $1;
  724. }
  725. shift @$Args;
  726. unshift @$Args, $CXXAnalyzer;
  727. }
  728. elsif ($IgnoreErrors) {
  729. if ($Cmd eq "make" or $Cmd eq "gmake") {
  730. AddIfNotPresent($Args, "CC=$CCAnalyzer");
  731. AddIfNotPresent($Args, "CXX=$CXXAnalyzer");
  732. AddIfNotPresent($Args,"-k");
  733. AddIfNotPresent($Args,"-i");
  734. }
  735. elsif ($Cmd eq "xcodebuild") {
  736. AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES");
  737. }
  738. }
  739. if ($Cmd eq "xcodebuild") {
  740. # Check if using iPhone SDK 3.0 (simulator). If so the compiler being
  741. # used should be gcc-4.2.
  742. if (!defined $ENV{"CCC_CC"}) {
  743. for (my $i = 0 ; $i < scalar(@$Args); ++$i) {
  744. if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) {
  745. if (@$Args[$i+1] =~ /^iphonesimulator3/) {
  746. $ENV{"CCC_CC"} = "gcc-4.2";
  747. $ENV{"CCC_CXX"} = "g++-4.2";
  748. }
  749. }
  750. }
  751. }
  752. # Disable PCH files until clang supports them.
  753. AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO");
  754. # When 'CC' is set, xcodebuild uses it to do all linking, even if we are
  755. # linking C++ object files. Set 'LDPLUSPLUS' so that xcodebuild uses 'g++'
  756. # (via c++-analyzer) when linking such files.
  757. $ENV{"LDPLUSPLUS"} = $CXXAnalyzer;
  758. }
  759. return (system(@$Args) >> 8);
  760. }
  761. ##----------------------------------------------------------------------------##
  762. # DisplayHelp - Utility function to display all help options.
  763. ##----------------------------------------------------------------------------##
  764. sub DisplayHelp {
  765. print <<ENDTEXT;
  766. USAGE: $Prog [options] <build command> [build options]
  767. ENDTEXT
  768. if (defined $BuildName) {
  769. print "ANALYZER BUILD: $BuildName ($BuildDate)\n\n";
  770. }
  771. print <<ENDTEXT;
  772. OPTIONS:
  773. -analyze-headers - Also analyze functions in #included files.
  774. -o - Target directory for HTML report files. Subdirectories
  775. will be created as needed to represent separate "runs" of
  776. the analyzer. If this option is not specified, a directory
  777. is created in /tmp (TMPDIR on Mac OS X) to store the reports.
  778. -h - Display this message.
  779. --help
  780. -k - Add a "keep on going" option to the specified build command.
  781. --keep-going This option currently supports make and xcodebuild.
  782. This is a convenience option; one can specify this
  783. behavior directly using build options.
  784. --html-title [title] - Specify the title used on generated HTML pages.
  785. --html-title=[title] If not specified, a default title will be used.
  786. -plist - By default the output of scan-build is a set of HTML files.
  787. This option outputs the results as a set of .plist files.
  788. -plist-html - By default the output of scan-build is a set of HTML files.
  789. This option outputs the results as a set of HTML
  790. and .plist files.
  791. --status-bugs - By default, the exit status of $Prog is the same as the
  792. executed build command. Specifying this option causes the
  793. exit status of $Prog to be 1 if it found potential bugs
  794. and 0 otherwise.
  795. --use-cc [compiler path] - $Prog attempts to guess the default compiler for
  796. --use-cc=[compiler path] your C and Objective-C code. Use this option
  797. to specify an alternate compiler.
  798. --use-c++ [compiler path] - $Prog attempts to guess the default compiler for
  799. --use-c++=[compiler path] your C++ and Objective-C++ code. Use this option
  800. to specify an alternate compiler.
  801. -v - Verbose output from $Prog and the analyzer.
  802. A second and third '-v' increases verbosity.
  803. -V - View analysis results in a web browser when the build
  804. --view completes.
  805. ADVANCED OPTIONS:
  806. -constraints [model] - Specify the contraint engine used by the analyzer.
  807. By default the 'range' model is used. Specifying
  808. 'basic' uses a simpler, less powerful constraint model
  809. used by checker-0.160 and earlier.
  810. -store [model] - Specify the store model used by the analyzer. By default,
  811. the 'region' store model is used. 'region' specifies a field-
  812. sensitive store model. Users can also specify 'basic', which
  813. is far less precise but can more quickly analyze code.
  814. 'basic' was the default store model for checker-0.221 and
  815. earlier.
  816. -no-failure-reports - Do not create a 'failures' subdirectory that includes
  817. analyzer crash reports and preprocessed source files.
  818. -stats - Generates visitation statistics for the project being analyzed.
  819. -maxloop N - specifiy the number of times a block can be visited before giving
  820. up. Default is 4. Increase for more comprehensive coverage at a
  821. cost of speed.
  822. CONTROLLING CHECKERS:
  823. A default group of checkers are always run unless explicitly disabled.
  824. Checkers may be enabled/disabled using the following options:
  825. -enable-checker [checker name]
  826. -disable-checker [checker name]
  827. ENDTEXT
  828. # Query clang for list of checkers that are enabled.
  829. my %EnabledCheckers;
  830. foreach my $lang ("c", "objective-c", "objective-c++", "c++") {
  831. pipe(FROM_CHILD, TO_PARENT);
  832. my $pid = fork();
  833. if ($pid == 0) {
  834. close FROM_CHILD;
  835. open(STDOUT,">&", \*TO_PARENT);
  836. open(STDERR,">&", \*TO_PARENT);
  837. exec $Clang, ('--analyze', '-x', $lang, '-', '-###');
  838. }
  839. close(TO_PARENT);
  840. while(<FROM_CHILD>) {
  841. foreach my $val (split /\s+/) {
  842. $val =~ s/\"//g;
  843. if ($val =~ /-analyzer-checker\=([^\s]+)/) {
  844. $EnabledCheckers{$1} = 1;
  845. }
  846. }
  847. }
  848. waitpid($pid,0);
  849. close(FROM_CHILD);
  850. }
  851. # Query clang for complete list of checkers.
  852. pipe(FROM_CHILD, TO_PARENT);
  853. my $pid = fork();
  854. if ($pid == 0) {
  855. close FROM_CHILD;
  856. open(STDOUT,">&", \*TO_PARENT);
  857. open(STDERR,">&", \*TO_PARENT);
  858. exec $Clang, ('-cc1', '-analyzer-checker-help');
  859. }
  860. close(TO_PARENT);
  861. my $foundCheckers = 0;
  862. while(<FROM_CHILD>) {
  863. if (/CHECKERS:/) {
  864. $foundCheckers = 1;
  865. last;
  866. }
  867. }
  868. if (!$foundCheckers) {
  869. print " *** Could not query Clang for the list of available checkers.";
  870. }
  871. else {
  872. print("\nAVAILABLE CHECKERS:\n\n");
  873. my $skip = 0;
  874. while(<FROM_CHILD>) {
  875. if (/experimental/) {
  876. $skip = 1;
  877. next;
  878. }
  879. if ($skip) {
  880. next if (!/^\s\s[^\s]/);
  881. $skip = 0;
  882. }
  883. s/^\s\s//;
  884. if (/^([^\s]+)/) {
  885. # Is the checker enabled?
  886. my $checker = $1;
  887. my $enabled = 0;
  888. my $aggregate = "";
  889. foreach my $domain (split /\./, $checker) {
  890. $aggregate .= $domain;
  891. if ($EnabledCheckers{$aggregate}) {
  892. $enabled =1;
  893. last;
  894. }
  895. }
  896. if ($enabled) {
  897. print " + ";
  898. }
  899. else {
  900. print " ";
  901. }
  902. }
  903. else {
  904. print " ";
  905. }
  906. print $_;
  907. }
  908. }
  909. waitpid($pid,0);
  910. close(FROM_CHILD);
  911. print <<ENDTEXT
  912. NOTE: "+" indicates that an analysis is enabled by default.
  913. BUILD OPTIONS
  914. You can specify any build option acceptable to the build command.
  915. EXAMPLE
  916. $Prog -o /tmp/myhtmldir make -j4
  917. The above example causes analysis reports to be deposited into
  918. a subdirectory of "/tmp/myhtmldir" and to run "make" with the "-j4" option.
  919. A different subdirectory is created each time $Prog analyzes a project.
  920. The analyzer should support most parallel builds, but not distributed builds.
  921. ENDTEXT
  922. }
  923. ##----------------------------------------------------------------------------##
  924. # HtmlEscape - HTML entity encode characters that are special in HTML
  925. ##----------------------------------------------------------------------------##
  926. sub HtmlEscape {
  927. # copy argument to new variable so we don't clobber the original
  928. my $arg = shift || '';
  929. my $tmp = $arg;
  930. $tmp =~ s/&/&amp;/g;
  931. $tmp =~ s/</&lt;/g;
  932. $tmp =~ s/>/&gt;/g;
  933. return $tmp;
  934. }
  935. ##----------------------------------------------------------------------------##
  936. # ShellEscape - backslash escape characters that are special to the shell
  937. ##----------------------------------------------------------------------------##
  938. sub ShellEscape {
  939. # copy argument to new variable so we don't clobber the original
  940. my $arg = shift || '';
  941. if ($arg =~ /["\s]/) { return "'" . $arg . "'"; }
  942. return $arg;
  943. }
  944. ##----------------------------------------------------------------------------##
  945. # Process command-line arguments.
  946. ##----------------------------------------------------------------------------##
  947. my $AnalyzeHeaders = 0;
  948. my $HtmlDir; # Parent directory to store HTML files.
  949. my $IgnoreErrors = 0; # Ignore build errors.
  950. my $ViewResults = 0; # View results when the build terminates.
  951. my $ExitStatusFoundBugs = 0; # Exit status reflects whether bugs were found
  952. my @AnalysesToRun;
  953. my $StoreModel;
  954. my $ConstraintsModel;
  955. my $OutputFormat = "html";
  956. my $AnalyzerStats = 0;
  957. my $MaxLoop = 0;
  958. if (!@ARGV) {
  959. DisplayHelp();
  960. exit 1;
  961. }
  962. my $displayHelp = 0;
  963. while (@ARGV) {
  964. # Scan for options we recognize.
  965. my $arg = $ARGV[0];
  966. if ($arg eq "-h" or $arg eq "--help") {
  967. $displayHelp = 1;
  968. shift @ARGV;
  969. next;
  970. }
  971. if ($arg eq '-analyze-headers') {
  972. shift @ARGV;
  973. $AnalyzeHeaders = 1;
  974. next;
  975. }
  976. if ($arg eq "-o") {
  977. shift @ARGV;
  978. if (!@ARGV) {
  979. DieDiag("'-o' option requires a target directory name.\n");
  980. }
  981. # Construct an absolute path. Uses the current working directory
  982. # as a base if the original path was not absolute.
  983. $HtmlDir = abs_path(shift @ARGV);
  984. next;
  985. }
  986. if ($arg =~ /^--html-title(=(.+))?$/) {
  987. shift @ARGV;
  988. if (!defined $2 || $2 eq '') {
  989. if (!@ARGV) {
  990. DieDiag("'--html-title' option requires a string.\n");
  991. }
  992. $HtmlTitle = shift @ARGV;
  993. } else {
  994. $HtmlTitle = $2;
  995. }
  996. next;
  997. }
  998. if ($arg eq "-k" or $arg eq "--keep-going") {
  999. shift @ARGV;
  1000. $IgnoreErrors = 1;
  1001. next;
  1002. }
  1003. if ($arg =~ /^--use-cc(=(.+))?$/) {
  1004. shift @ARGV;
  1005. my $cc;
  1006. if (!defined $2 || $2 eq "") {
  1007. if (!@ARGV) {
  1008. DieDiag("'--use-cc' option requires a compiler executable name.\n");
  1009. }
  1010. $cc = shift @ARGV;
  1011. }
  1012. else {
  1013. $cc = $2;
  1014. }
  1015. $ENV{"CCC_CC"} = $cc;
  1016. next;
  1017. }
  1018. if ($arg =~ /^--use-c\+\+(=(.+))?$/) {
  1019. shift @ARGV;
  1020. my $cxx;
  1021. if (!defined $2 || $2 eq "") {
  1022. if (!@ARGV) {
  1023. DieDiag("'--use-c++' option requires a compiler executable name.\n");
  1024. }
  1025. $cxx = shift @ARGV;
  1026. }
  1027. else {
  1028. $cxx = $2;
  1029. }
  1030. $ENV{"CCC_CXX"} = $cxx;
  1031. next;
  1032. }
  1033. if ($arg eq "-v") {
  1034. shift @ARGV;
  1035. $Verbose++;
  1036. next;
  1037. }
  1038. if ($arg eq "-V" or $arg eq "--view") {
  1039. shift @ARGV;
  1040. $ViewResults = 1;
  1041. next;
  1042. }
  1043. if ($arg eq "--status-bugs") {
  1044. shift @ARGV;
  1045. $ExitStatusFoundBugs = 1;
  1046. next;
  1047. }
  1048. if ($arg eq "-store") {
  1049. shift @ARGV;
  1050. $StoreModel = shift @ARGV;
  1051. next;
  1052. }
  1053. if ($arg eq "-constraints") {
  1054. shift @ARGV;
  1055. $ConstraintsModel = shift @ARGV;
  1056. next;
  1057. }
  1058. if ($arg eq "-plist") {
  1059. shift @ARGV;
  1060. $OutputFormat = "plist";
  1061. next;
  1062. }
  1063. if ($arg eq "-plist-html") {
  1064. shift @ARGV;
  1065. $OutputFormat = "plist-html";
  1066. next;
  1067. }
  1068. if ($arg eq "-no-failure-reports") {
  1069. $ENV{"CCC_REPORT_FAILURES"} = 0;
  1070. next;
  1071. }
  1072. if ($arg eq "-stats") {
  1073. shift @ARGV;
  1074. $AnalyzerStats = 1;
  1075. next;
  1076. }
  1077. if ($arg eq "-maxloop") {
  1078. shift @ARGV;
  1079. $MaxLoop = shift @ARGV;
  1080. next;
  1081. }
  1082. if ($arg eq "-enable-checker") {
  1083. shift @ARGV;
  1084. push @AnalysesToRun, "-analyzer-checker", shift @ARGV;
  1085. next;
  1086. }
  1087. if ($arg eq "-disable-checker") {
  1088. shift @ARGV;
  1089. push @AnalysesToRun, "-analyzer-disable-checker", shift @ARGV;
  1090. next;
  1091. }
  1092. DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/);
  1093. last;
  1094. }
  1095. if (!@ARGV and $displayHelp == 0) {
  1096. Diag("No build command specified.\n\n");
  1097. $displayHelp = 1;
  1098. }
  1099. if ($displayHelp) {
  1100. DisplayHelp();
  1101. exit 1;
  1102. }
  1103. # Determine where results go.
  1104. $CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV)));
  1105. $HtmlTitle = "${CurrentDirSuffix} - scan-build results"
  1106. unless (defined($HtmlTitle));
  1107. # Determine the output directory for the HTML reports.
  1108. my $BaseDir = $HtmlDir;
  1109. $HtmlDir = GetHTMLRunDir($HtmlDir);
  1110. # Determine the location of ccc-analyzer.
  1111. my $AbsRealBin = Cwd::realpath($RealBin);
  1112. my $Cmd = "$AbsRealBin/libexec/ccc-analyzer";
  1113. my $CmdCXX = "$AbsRealBin/libexec/c++-analyzer";
  1114. if (!defined $Cmd || ! -x $Cmd) {
  1115. $Cmd = "$AbsRealBin/ccc-analyzer";
  1116. DieDiag("Executable 'ccc-analyzer' does not exist at '$Cmd'\n") if(! -x $Cmd);
  1117. }
  1118. if (!defined $CmdCXX || ! -x $CmdCXX) {
  1119. $CmdCXX = "$AbsRealBin/c++-analyzer";
  1120. DieDiag("Executable 'c++-analyzer' does not exist at '$CmdCXX'\n") if(! -x $CmdCXX);
  1121. }
  1122. if (!defined $ClangSB || ! -x $ClangSB) {
  1123. Diag("'clang' executable not found in '$RealBin/bin'.\n");
  1124. Diag("Using 'clang' from path: $Clang\n");
  1125. }
  1126. # Set the appropriate environment variables.
  1127. SetHtmlEnv(\@ARGV, $HtmlDir);
  1128. $ENV{'CC'} = $Cmd;
  1129. $ENV{'CXX'} = $CmdCXX;
  1130. $ENV{'CLANG'} = $Clang;
  1131. $ENV{'CLANG_CXX'} = $ClangCXX;
  1132. if ($Verbose >= 2) {
  1133. $ENV{'CCC_ANALYZER_VERBOSE'} = 1;
  1134. }
  1135. if ($Verbose >= 3) {
  1136. $ENV{'CCC_ANALYZER_LOG'} = 1;
  1137. }
  1138. if ($AnalyzeHeaders) {
  1139. push @AnalysesToRun,"-analyzer-opt-analyze-headers";
  1140. }
  1141. if ($AnalyzerStats) {
  1142. push @AnalysesToRun, '-analyzer-checker', 'debug.Stats';
  1143. }
  1144. if ($MaxLoop > 0) {
  1145. push @AnalysesToRun, '-analyzer-max-loop ' . $MaxLoop;
  1146. }
  1147. $ENV{'CCC_ANALYZER_ANALYSIS'} = join ' ',@AnalysesToRun;
  1148. if (defined $StoreModel) {
  1149. $ENV{'CCC_ANALYZER_STORE_MODEL'} = $StoreModel;
  1150. }
  1151. if (defined $ConstraintsModel) {
  1152. $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'} = $ConstraintsModel;
  1153. }
  1154. if (defined $OutputFormat) {
  1155. $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'} = $OutputFormat;
  1156. }
  1157. # Run the build.
  1158. my $ExitStatus = RunBuildCommand(\@ARGV, $IgnoreErrors, $Cmd, $CmdCXX);
  1159. if (defined $OutputFormat) {
  1160. if ($OutputFormat =~ /plist/) {
  1161. Diag "Analysis run complete.\n";
  1162. Diag "Analysis results (plist files) deposited in '$HtmlDir'\n";
  1163. }
  1164. elsif ($OutputFormat =~ /html/) {
  1165. # Postprocess the HTML directory.
  1166. my $NumBugs = Postprocess($HtmlDir, $BaseDir, $AnalyzerStats);
  1167. if ($ViewResults and -r "$HtmlDir/index.html") {
  1168. Diag "Analysis run complete.\n";
  1169. Diag "Viewing analysis results in '$HtmlDir' using scan-view.\n";
  1170. my $ScanView = Cwd::realpath("$RealBin/scan-view");
  1171. if (! -x $ScanView) { $ScanView = "scan-view"; }
  1172. exec $ScanView, "$HtmlDir";
  1173. }
  1174. if ($ExitStatusFoundBugs) {
  1175. exit 1 if ($NumBugs > 0);
  1176. exit 0;
  1177. }
  1178. }
  1179. }
  1180. exit $ExitStatus;