ccc-analyzer revision 2dd7ad13bbbd43cb940040725a30cb7789bd2774
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 interpose between the build system and gcc. It invokes 11# both gcc and the static analyzer. 12# 13##===----------------------------------------------------------------------===## 14 15use strict; 16use warnings; 17use Cwd; 18use File::Temp qw/ tempfile /; 19use File::Path qw / mkpath /; 20 21##----------------------------------------------------------------------------## 22# Process Clang Crashes. 23##----------------------------------------------------------------------------## 24 25sub GetPPExt { 26 my $Lang = shift; 27 if ($Lang =~ /objective-c/) { return ".mi"; } 28 return ".i"; 29} 30 31sub ProcessClangFailure { 32 my ($Lang, $file, $Args, $HtmlDir, $ErrorType) = @_; 33 my $Dir = "$HtmlDir/crashes"; 34 mkpath $Dir; 35 my ($PPH, $PPFile) = tempfile("clang_crash_XXXXXX", 36 SUFFIX => GetPPExt($Lang), 37 DIR => $Dir); 38 39 system "gcc", @$Args, "-E", "-o", $PPFile; 40 close ($PPH); 41 open (OUT, ">", "$PPFile.info") or die "Cannot open $PPFile.info\n"; 42 print OUT "$file\n"; 43 print OUT "$ErrorType\n"; 44 print OUT "@$Args\n"; 45 close OUT; 46} 47 48##----------------------------------------------------------------------------## 49# Running the analyzer. 50##----------------------------------------------------------------------------## 51 52sub Analyze { 53 my ($Clang, $Args, $Lang, $Output, $Verbose, $HtmlDir, $file, $Analyses) = @_; 54 55 # Skip anything related to C++. 56 return if ($Lang =~ /c[+][+]/); 57 58 my $RunAnalyzer = 0; 59 my $Cmd; 60 my @CmdArgs; 61 my @CmdArgsSansAnalyses; 62 63 if ($Lang =~ /header/) { 64 exit 0 if (!defined ($Output)); 65 $Cmd = 'cp'; 66 push @CmdArgs,$file; 67 # Remove the PCH extension. 68 $Output =~ s/[.]gch$//; 69 push @CmdArgs,$Output; 70 @CmdArgsSansAnalyses = @CmdArgs; 71 } 72 else { 73 $Cmd = $Clang; 74 push @CmdArgs,'-DIBOutlet=__attribute__((iboutlet))'; 75 push @CmdArgs,@$Args; 76 @CmdArgsSansAnalyses = @CmdArgs; 77 push @CmdArgs,(split /\s/,$Analyses); 78 $RunAnalyzer = 1; 79 } 80 81 my @PrintArgs; 82 my $dir; 83 84 if ($Verbose) { 85 $dir = getcwd(); 86 print STDERR "\n[LOCATION]: $dir\n"; 87 push @PrintArgs,"'$Cmd'"; 88 foreach my $arg (@CmdArgs) { push @PrintArgs,"\'$arg\'"; } 89 } 90 91 if ($Verbose == 1) { 92 # We MUST print to stderr. Some clients use the stdout output of 93 # gcc for various purposes. 94 print STDERR join(' ',@PrintArgs); 95 print STDERR "\n"; 96 } 97 elsif ($Verbose == 2) { 98 print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n"; 99 } 100 101 if ($RunAnalyzer and defined($HtmlDir)) { 102 push @CmdArgs,'-o'; 103 push @CmdArgs,$HtmlDir; 104 } 105 106 system $Cmd,@CmdArgs; 107 108 # Did the command die because of a signal? 109 if ($? & 127 and $Cmd eq $Clang and defined $HtmlDir) { 110 ProcessClangFailure($Lang, $file, \@CmdArgsSansAnalyses, $HtmlDir, 111 "Crash"); 112 } 113 elsif ($?) { 114 ProcessClangFailure($Lang, $file, \@CmdArgsSansAnalyses, $HtmlDir, 115 "Parser Rejects"); 116 } 117} 118 119##----------------------------------------------------------------------------## 120# Lookup tables. 121##----------------------------------------------------------------------------## 122 123my %CompileOptionMap = ( 124 '-nostdinc' => 0, 125 '-fobjc-gc-only' => 0, 126 '-fobjc-gc' => 0, 127 '-include' => 1, 128 '-idirafter' => 1, 129 '-iprefix' => 1, 130 '-iquote' => 1, 131 '-isystem' => 1, 132 '-iwithprefix' => 1, 133 '-iwithprefixbefore' => 1 134); 135 136my %LinkerOptionMap = ( 137 '-framework' => 1 138); 139 140my %CompilerLinkerOptionMap = ( 141 '-isysroot' => 1, 142 '-arch' => 1, 143 '-v' => 0 144); 145 146my %IgnoredOptionMap = ( 147 '-MT' => 1, # Ignore these preprocessor options. 148 '-MF' => 1, 149 150 '-fsyntax-only' => 0, 151 '-save-temps' => 0, 152 '-install_name' => 1, 153 '-exported_symbols_list' => 1, 154 '-current_version' => 1, 155 '-compatibility_version' => 1, 156 '-init' => 1, 157 '-e' => 1, 158 '-seg1addr' => 1, 159 '-bundle_loader' => 1, 160 '-multiply_defined' => 1, 161 '-sectorder' => 3, 162 '--param' => 1, 163 '-u' => 1 164); 165 166my %LangMap = ( 167 'c' => 'c', 168 'cpp' => 'c++', 169 'cc' => 'c++', 170 'i' => 'c-cpp-output', 171 'm' => 'objective-c', 172 'mi' => 'objective-c-cpp-output' 173); 174 175##----------------------------------------------------------------------------## 176# Main Logic. 177##----------------------------------------------------------------------------## 178 179my $Action = 'link'; 180my @CompileOpts; 181my @LinkOpts; 182my @Files; 183my $Lang; 184my $Output; 185 186# Forward arguments to gcc. 187my $Status = system("gcc",@ARGV); 188if ($Status) { exit($Status); } 189 190# Get the analysis options. 191my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'}; 192if (!defined($Analyses)) { $Analyses = '-checker-cfref'; } 193 194# Determine the level of verbosity. 195my $Verbose = 0; 196if (defined $ENV{CCC_ANALYZER_VERBOSE}) { $Verbose = 1; } 197if (defined $ENV{CCC_ANALYZER_LOG}) { $Verbose = 2; } 198 199# Determine what clang executable to use. 200my $Clang = $ENV{'CLANG'}; 201if (!defined $Clang) { $Clang = 'clang'; } 202 203# Get the HTML output directory. 204my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'}; 205 206 207# Process the arguments. 208foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { 209 my $Arg = $ARGV[$i]; 210 211 # Modes ccc-analyzer supports 212 if ($Arg eq '-E') { $Action = 'preprocess'; } 213 elsif ($Arg eq '-c') { $Action = 'compile'; } 214 elsif ($Arg =~ /^-print-prog-name/) { exit 0; } 215 216 # Options with possible arguments that should pass through to compiler. 217 if (defined $CompileOptionMap{$Arg}) { 218 my $Cnt = $CompileOptionMap{$Arg}; 219 push @CompileOpts,$Arg; 220 while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; } 221 next; 222 } 223 224 # Options with possible arguments that should pass through to linker. 225 if (defined $LinkerOptionMap{$Arg}) { 226 my $Cnt = $LinkerOptionMap{$Arg}; 227 push @LinkOpts,$Arg; 228 while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; } 229 next; 230 } 231 232 # Options with possible arguments that should pass through to both compiler 233 # and the linker. 234 if (defined $CompilerLinkerOptionMap{$Arg}) { 235 my $Cnt = $CompilerLinkerOptionMap{$Arg}; 236 push @CompileOpts,$Arg; 237 push @LinkOpts,$Arg; 238 while ($Cnt > 0) { 239 ++$i; --$Cnt; 240 push @CompileOpts, $ARGV[$i]; 241 push @LinkOpts, $ARGV[$i]; 242 } 243 next; 244 } 245 246 # Ignored options. 247 if (defined $IgnoredOptionMap{$Arg}) { 248 my $Cnt = $IgnoredOptionMap{$Arg}; 249 while ($Cnt > 0) { 250 ++$i; --$Cnt; 251 } 252 next; 253 } 254 255 # Compile mode flags. 256 if ($Arg =~ /^-[D,I,U](.*)$/) { 257 my $Tmp = $Arg; 258 if ($1 eq '') { 259 # FIXME: Check if we are going off the end. 260 ++$i; 261 $Tmp = $Arg . $ARGV[$i]; 262 } 263 push @CompileOpts,$Tmp; 264 next; 265 } 266 267 # Language. 268 if ($Arg eq '-x') { 269 $Lang = $ARGV[$i+1]; 270 ++$i; next; 271 } 272 273 # Output file. 274 if ($Arg eq '-o') { 275 ++$i; 276 $Output = $ARGV[$i]; 277 next; 278 } 279 280 # Get the link mode. 281 if ($Arg =~ /^-[l,L,O]/) { 282 if ($Arg eq '-O') { push @LinkOpts,'-O1'; } 283 elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; } 284 else { push @LinkOpts,$Arg; } 285 next; 286 } 287 288 if ($Arg =~ /^-std=/) { 289 push @CompileOpts,$Arg; 290 next; 291 } 292 293# if ($Arg =~ /^-f/) { 294# # FIXME: Not sure if the remaining -fxxxx options have no arguments. 295# push @CompileOpts,$Arg; 296# push @LinkOpts,$Arg; # FIXME: Not sure if these are link opts. 297# } 298 299 # Get the compiler/link mode. 300 if ($Arg =~ /^-F(.+)$/) { 301 my $Tmp = $Arg; 302 if ($1 eq '') { 303 # FIXME: Check if we are going off the end. 304 ++$i; 305 $Tmp = $Arg . $ARGV[$i]; 306 } 307 push @CompileOpts,$Tmp; 308 push @LinkOpts,$Tmp; 309 next; 310 } 311 312 # Input files. 313 if ($Arg eq '-filelist') { 314 # FIXME: Make sure we aren't walking off the end. 315 open(IN, $ARGV[$i+1]); 316 while (<IN>) { s/\015?\012//; push @Files,$_; } 317 close(IN); 318 ++$i; next; 319 } 320 321 if (!($Arg =~ /^-/)) { 322 push @Files,$Arg; next; 323 } 324} 325 326if ($Action eq 'compile' or $Action eq 'link') { 327 foreach my $file (@Files) { 328 # Determine the language for the file. 329 my $FileLang = $Lang; 330 331 if (!defined($FileLang)) { 332 # Infer the language from the extension. 333 if ($file =~ /[.]([^.]+)$/) { 334 $FileLang = $LangMap{$1}; 335 } 336 } 337 338 next if (!defined $FileLang); 339 340 my @AnalyzeArgs; 341 342 if ($FileLang ne 'unknown') { 343 push @AnalyzeArgs,'-x'; 344 push @AnalyzeArgs,$FileLang; 345 } 346 347 push @AnalyzeArgs,@CompileOpts; 348 push @AnalyzeArgs,$file; 349 350 Analyze($Clang, \@AnalyzeArgs, $FileLang, $Output, 351 $Verbose, $HtmlDir, $file, $Analyses); 352 } 353} 354 355