Have scan-build/ccc-analyzer generate preprocessed .i/.mi files for sources that clang crashes on.

llvm-svn: 54552
This commit is contained in:
Ted Kremenek 2008-08-08 20:46:42 +00:00
parent c893a67fe3
commit 994c8e393d
2 changed files with 149 additions and 75 deletions

View File

@ -15,6 +15,33 @@
use strict;
use warnings;
use Cwd;
use File::Temp qw/ tempfile /;
use File::Path qw / mkpath /;
##----------------------------------------------------------------------------##
# Process Clang Crashes.
##----------------------------------------------------------------------------##
sub GetPPExt {
my $Lang = shift;
if ($Lang =~ /objective-c/) { return ".mi"; }
return ".i";
}
sub ProcessClangCrash {
my ($Clang, $Lang, $file, $Args, $HtmlDir) = @_;
my $Dir = "$HtmlDir/crashes";
mkpath $Dir;
my ($PPH, $PPFile) = tempfile("clang_crash_XXXXXX",
SUFFIX => GetPPExt($Lang),
DIR => $Dir);
system $Clang, @$Args, "-E", "-o", $PPFile;
close ($PPH);
open (OUT, ">", "$PPFile.info") or die "Cannot open $PPFile.info\n";
print OUT "$file";
close OUT;
}
##----------------------------------------------------------------------------##
# Running the analyzer.
@ -29,6 +56,7 @@ sub Analyze {
my $RunAnalyzer = 0;
my $Cmd;
my @CmdArgs;
my @CmdArgsSansAnalyses;
if ($Lang =~ /header/) {
exit 0 if (!defined ($Output));
@ -37,12 +65,14 @@ sub Analyze {
# Remove the PCH extension.
$Output =~ s/[.]gch$//;
push @CmdArgs,$Output;
@CmdArgsSansAnalyses = @CmdArgs;
}
else {
$Cmd = $Clang;
push @CmdArgs,(split /\s/,$Analyses);
push @CmdArgs,'-DIBOutlet=__attribute__((iboutlet))';
push @CmdArgs,@$Args;
@CmdArgsSansAnalyses = @CmdArgs;
push @CmdArgs,(split /\s/,$Analyses);
$RunAnalyzer = 1;
}
@ -70,8 +100,13 @@ sub Analyze {
push @CmdArgs,'-o';
push @CmdArgs,$HtmlDir;
}
system $Cmd,@CmdArgs;
system $Cmd,@CmdArgs;
# Did the command die because of a signal?
if ($? & 127 and $Cmd eq $Clang and defined $HtmlDir) {
ProcessClangCrash($Clang, $Lang, $file, \@CmdArgsSansAnalyses, $HtmlDir);
}
}
##----------------------------------------------------------------------------##

View File

@ -14,7 +14,6 @@
use strict;
use warnings;
use File::Temp qw/ :mktemp /;
use FindBin qw($RealBin);
use Digest::MD5;
use File::Basename;
@ -43,6 +42,14 @@ sub Diag {
}
}
sub DiagCrashes {
my $Dir = shift;
Diag ("The analyzer crashed on some source files.\n");
Diag ("Preprocessed versions of crashed files were depositied in '$Dir/crashes'.\n");
Diag ("Please consider submitting a bug report using these files:\n");
Diag (" http://clang.llvm.org/StaticAnalysisUsage.html#filingbugs\n")
}
sub DieDiag {
if ($UseColor) {
print BOLD, RED "$Prog: ";
@ -79,7 +86,7 @@ while(<PIPE>) {
if (/Available Source Code Analyses/) {
$FoundAnalysis = 1;
}
next;
}
@ -385,24 +392,21 @@ sub Postprocess {
}
opendir(DIR, $Dir);
my @files = grep(/^report-.*\.html$/,readdir(DIR));
my $Crashes = 0;
my @files = grep { if ($_ eq "crashes") { $Crashes++; }
/^report-.*\.html$/; } readdir(DIR);
closedir(DIR);
if (scalar(@files) == 0) {
if (scalar(@files) == 0 and $Crashes == 0) {
Diag("Removing directory '$Dir' because it contains no reports.\n");
system ("rm", "-fR", $Dir);
# Remove the base directory if it contains no files (don't use '-R').
if (defined $BaseDir) { system ("rm", "-f", $BaseDir); }
Diag("No bugs found.\n");
system ("rm", "-f", $BaseDir) if (defined $BaseDir);
return 0;
}
# Scan each report file and build an index.
my @Index;
# Scan each report file and build an index.
my @Index;
foreach my $file (@files) { ScanFile(\@Index, $Dir, $file); }
# Generate an index.html file.
@ -418,7 +422,7 @@ print OUT <<ENDTEXT;
body { color:#000000; background-color:#ffffff }
body { font-family: Helvetica, sans-serif; font-size:9pt }
h1 { font-size:12pt }
table.sortable thead {
table thead {
background-color:#eee; color:#666666;
font-weight: bold; cursor: default;
text-align:center;
@ -426,8 +430,8 @@ print OUT <<ENDTEXT;
border-bottom: 2px solid #000000;
font-weight: bold; font-family: Verdana
}
table.sortable { border: 1px #000000 solid }
table.sortable { border-collapse: collapse; border-spacing: 0px }
table { border: 1px #000000 solid }
table { border-collapse: collapse; border-spacing: 0px }
td { border-bottom: 1px #000000 dotted }
td { padding:5px; padding-left:8px; padding-right:8px }
td { text-align:left; font-size:9pt }
@ -458,28 +462,23 @@ function ToggleDisplay(CheckButton, ClassName) {
<body>
ENDTEXT
# Print out the summary table.
if (scalar(@files)) {
# Print out the summary table.
my %Totals;
my %Totals;
for my $row ( @Index ) {
for my $row ( @Index ) {
#my $bug_type = lc($row->[1]);
my $bug_type = ($row->[1]);
#my $bug_type = lc($row->[1]);
my $bug_type = ($row->[1]);
if (!defined $Totals{$bug_type}) {
$Totals{$bug_type} = 1;
if (!defined $Totals{$bug_type}) { $Totals{$bug_type} = 1; }
else { $Totals{$bug_type}++; }
}
else {
$Totals{$bug_type}++;
print OUT "<h3>Bug Summary</h3>";
if (defined $BuildName) {
print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n"
}
}
print OUT "<h3>Summary</h3>";
if (defined $BuildName) {
print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n"
}
print OUT <<ENDTEXT;
<table class="sortable">
@ -490,11 +489,11 @@ print OUT <<ENDTEXT;
</tr>
ENDTEXT
for my $key ( sort { $a cmp $b } keys %Totals ) {
my $x = lc($key);
$x =~ s/[ ,'"]+/_/g;
print OUT "<tr><td>$key</td><td>$Totals{$key}</td><td><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></td></tr>\n";
}
for my $key ( sort { $a cmp $b } keys %Totals ) {
my $x = lc($key);
$x =~ s/[ ,'"]+/_/g;
print OUT "<tr><td>$key</td><td>$Totals{$key}</td><td><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></td></tr>\n";
}
# Print out the table of errors.
@ -511,55 +510,93 @@ print OUT <<ENDTEXT;
</tr>
ENDTEXT
my $prefix = GetPrefix();
my $regex;
my $InFileRegex;
my $InFilePrefix = "File:</td><td>";
my $prefix = GetPrefix();
my $regex;
my $InFileRegex;
my $InFilePrefix = "File:</td><td>";
if (defined $prefix) {
$regex = qr/^\Q$prefix\E/is;
$InFileRegex = qr/\Q$InFilePrefix$prefix\E/is;
}
if (defined $prefix) {
$regex = qr/^\Q$prefix\E/is;
$InFileRegex = qr/\Q$InFilePrefix$prefix\E/is;
}
for my $row ( sort { $a->[1] cmp $b->[1] } @Index ) {
for my $row ( sort { $a->[1] cmp $b->[1] } @Index ) {
my $x = lc($row->[1]);
$x =~ s/[ ,'"]+/_/g;
my $x = lc($row->[1]);
$x =~ s/[ ,'"]+/_/g;
print OUT "<tr class=\"bt_$x\">\n";
print OUT "<tr class=\"bt_$x\">\n";
my $ReportFile = $row->[0];
my $ReportFile = $row->[0];
print OUT " <td class=\"DESC\">";
#print OUT lc($row->[1]);
print OUT $row->[1];
print OUT "</td>\n";
print OUT " <td class=\"DESC\">";
#print OUT lc($row->[1]);
print OUT $row->[1];
print OUT "</td>\n";
# Update the file prefix.
# Update the file prefix.
my $fname = $row->[2];
if (defined $regex) {
$fname =~ s/$regex//;
UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix)
}
my $fname = $row->[2];
if (defined $regex) {
$fname =~ s/$regex//;
UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix)
}
print OUT "<td>$fname</td>\n";
print OUT "<td>$fname</td>\n";
# Print the rest of the columns.
for my $j ( 3 .. $#{$row} ) {
print OUT "<td>$row->[$j]</td>\n"
}
# Print the rest of the columns.
for my $j ( 3 .. $#{$row} ) {
print OUT "<td>$row->[$j]</td>\n"
}
# Emit the "View" link.
print OUT " <td class=\"View\"><a href=\"$ReportFile#EndPath\">View</a></td>\n";
# Emit the "View" link.
print OUT " <td class=\"View\"><a href=\"$ReportFile#EndPath\">View</a></td>\n";
# End the row.
print OUT "</tr>\n";
# End the row.
print OUT "</tr>\n";
}
print OUT "</table>\n";
}
if ($Crashes) {
# Read the crash directory for files.
opendir(DIR, "$Dir/crashes");
my @files = grep { /[.]info$/ } readdir(DIR);
closedir(DIR);
if (scalar(@files)) {
print OUT <<ENDTEXT;
<h3>Analyzer Crashes</h3>
<p>The analyzer crashed while processing the following files:</p>
<table>
<thead><tr><td>Source File</td><td>Preprocessed File</td></tr></thead>
ENDTEXT
foreach my $file (sort @files) {
$file =~ /(.+).info$/;
# Get the preprocessed file.
my $ppfile = $1;
# Open the info file and get the name of the source file.
open (INFO, "$Dir/crashes/$file") or
die "Cannot open $Dir/crashes/$file\n";
my $srcfile = <INFO>;
close (INFO);
# Print the information in the table.
print OUT "<tr><td>$srcfile</td><td class=\"View\"><a href=\"crashes/$ppfile\">View</a></td></tr>\n";
}
print OUT <<ENDTEXT;
</table>
<p>Please consider submitting preprocessed files as <a href="http://clang.llvm.org/StaticAnalysisUsage.html#filingbugs">bug reports</a>.</p>
ENDTEXT
}
}
print OUT "</table>\n</body></html>\n";
print OUT "</body></html>\n";
close(OUT);
CopyJS($Dir);
# Make sure $Dir and $BaseDir are world readable/executable.
@ -572,6 +609,8 @@ ENDTEXT
Diag("Open '$Dir/index.html' to examine bug reports.\n");
}
DiagCrashes($Dir) if ($Crashes);
return $Num;
}