1 //===- Job.cpp - Command to Execute ---------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Driver/Job.h" 10 #include "InputInfo.h" 11 #include "clang/Basic/LLVM.h" 12 #include "clang/Driver/Driver.h" 13 #include "clang/Driver/DriverDiagnostic.h" 14 #include "clang/Driver/Tool.h" 15 #include "clang/Driver/ToolChain.h" 16 #include "llvm/ADT/ArrayRef.h" 17 #include "llvm/ADT/SmallString.h" 18 #include "llvm/ADT/SmallVector.h" 19 #include "llvm/ADT/StringRef.h" 20 #include "llvm/ADT/StringSet.h" 21 #include "llvm/ADT/StringSwitch.h" 22 #include "llvm/Support/CrashRecoveryContext.h" 23 #include "llvm/Support/FileSystem.h" 24 #include "llvm/Support/Path.h" 25 #include "llvm/Support/PrettyStackTrace.h" 26 #include "llvm/Support/Program.h" 27 #include "llvm/Support/raw_ostream.h" 28 #include <algorithm> 29 #include <cassert> 30 #include <cstddef> 31 #include <string> 32 #include <system_error> 33 #include <utility> 34 35 using namespace clang; 36 using namespace driver; 37 38 Command::Command(const Action &Source, const Tool &Creator, 39 const char *Executable, 40 const llvm::opt::ArgStringList &Arguments, 41 ArrayRef<InputInfo> Inputs) 42 : Source(Source), Creator(Creator), Executable(Executable), 43 Arguments(Arguments) { 44 for (const auto &II : Inputs) 45 if (II.isFilename()) 46 InputFilenames.push_back(II.getFilename()); 47 } 48 49 /// Check if the compiler flag in question should be skipped when 50 /// emitting a reproducer. Also track how many arguments it has and if the 51 /// option is some kind of include path. 52 static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum, 53 bool &IsInclude) { 54 SkipNum = 2; 55 // These flags are all of the form -Flag <Arg> and are treated as two 56 // arguments. Therefore, we need to skip the flag and the next argument. 57 bool ShouldSkip = llvm::StringSwitch<bool>(Flag) 58 .Cases("-MF", "-MT", "-MQ", "-serialize-diagnostic-file", true) 59 .Cases("-o", "-dependency-file", true) 60 .Cases("-fdebug-compilation-dir", "-diagnostic-log-file", true) 61 .Cases("-dwarf-debug-flags", "-ivfsoverlay", true) 62 .Default(false); 63 if (ShouldSkip) 64 return true; 65 66 // Some include flags shouldn't be skipped if we have a crash VFS 67 IsInclude = llvm::StringSwitch<bool>(Flag) 68 .Cases("-include", "-header-include-file", true) 69 .Cases("-idirafter", "-internal-isystem", "-iwithprefix", true) 70 .Cases("-internal-externc-isystem", "-iprefix", true) 71 .Cases("-iwithprefixbefore", "-isystem", "-iquote", true) 72 .Cases("-isysroot", "-I", "-F", "-resource-dir", true) 73 .Cases("-iframework", "-include-pch", true) 74 .Default(false); 75 if (IsInclude) 76 return !HaveCrashVFS; 77 78 // The remaining flags are treated as a single argument. 79 80 // These flags are all of the form -Flag and have no second argument. 81 ShouldSkip = llvm::StringSwitch<bool>(Flag) 82 .Cases("-M", "-MM", "-MG", "-MP", "-MD", true) 83 .Case("-MMD", true) 84 .Default(false); 85 86 // Match found. 87 SkipNum = 1; 88 if (ShouldSkip) 89 return true; 90 91 // These flags are treated as a single argument (e.g., -F<Dir>). 92 StringRef FlagRef(Flag); 93 IsInclude = FlagRef.startswith("-F") || FlagRef.startswith("-I"); 94 if (IsInclude) 95 return !HaveCrashVFS; 96 if (FlagRef.startswith("-fmodules-cache-path=")) 97 return true; 98 99 SkipNum = 0; 100 return false; 101 } 102 103 void Command::printArg(raw_ostream &OS, StringRef Arg, bool Quote) { 104 const bool Escape = Arg.find_first_of(" \"\\$") != StringRef::npos; 105 106 if (!Quote && !Escape) { 107 OS << Arg; 108 return; 109 } 110 111 // Quote and escape. This isn't really complete, but good enough. 112 OS << '"'; 113 for (const auto c : Arg) { 114 if (c == '"' || c == '\\' || c == '$') 115 OS << '\\'; 116 OS << c; 117 } 118 OS << '"'; 119 } 120 121 void Command::writeResponseFile(raw_ostream &OS) const { 122 // In a file list, we only write the set of inputs to the response file 123 if (Creator.getResponseFilesSupport() == Tool::RF_FileList) { 124 for (const auto *Arg : InputFileList) { 125 OS << Arg << '\n'; 126 } 127 return; 128 } 129 130 // In regular response files, we send all arguments to the response file. 131 // Wrapping all arguments in double quotes ensures that both Unix tools and 132 // Windows tools understand the response file. 133 for (const auto *Arg : Arguments) { 134 OS << '"'; 135 136 for (; *Arg != '\0'; Arg++) { 137 if (*Arg == '\"' || *Arg == '\\') { 138 OS << '\\'; 139 } 140 OS << *Arg; 141 } 142 143 OS << "\" "; 144 } 145 } 146 147 void Command::buildArgvForResponseFile( 148 llvm::SmallVectorImpl<const char *> &Out) const { 149 // When not a file list, all arguments are sent to the response file. 150 // This leaves us to set the argv to a single parameter, requesting the tool 151 // to read the response file. 152 if (Creator.getResponseFilesSupport() != Tool::RF_FileList) { 153 Out.push_back(Executable); 154 Out.push_back(ResponseFileFlag.c_str()); 155 return; 156 } 157 158 llvm::StringSet<> Inputs; 159 for (const auto *InputName : InputFileList) 160 Inputs.insert(InputName); 161 Out.push_back(Executable); 162 // In a file list, build args vector ignoring parameters that will go in the 163 // response file (elements of the InputFileList vector) 164 bool FirstInput = true; 165 for (const auto *Arg : Arguments) { 166 if (Inputs.count(Arg) == 0) { 167 Out.push_back(Arg); 168 } else if (FirstInput) { 169 FirstInput = false; 170 Out.push_back(Creator.getResponseFileFlag()); 171 Out.push_back(ResponseFile); 172 } 173 } 174 } 175 176 /// Rewrite relative include-like flag paths to absolute ones. 177 static void 178 rewriteIncludes(const llvm::ArrayRef<const char *> &Args, size_t Idx, 179 size_t NumArgs, 180 llvm::SmallVectorImpl<llvm::SmallString<128>> &IncFlags) { 181 using namespace llvm; 182 using namespace sys; 183 184 auto getAbsPath = [](StringRef InInc, SmallVectorImpl<char> &OutInc) -> bool { 185 if (path::is_absolute(InInc)) // Nothing to do here... 186 return false; 187 std::error_code EC = fs::current_path(OutInc); 188 if (EC) 189 return false; 190 path::append(OutInc, InInc); 191 return true; 192 }; 193 194 SmallString<128> NewInc; 195 if (NumArgs == 1) { 196 StringRef FlagRef(Args[Idx + NumArgs - 1]); 197 assert((FlagRef.startswith("-F") || FlagRef.startswith("-I")) && 198 "Expecting -I or -F"); 199 StringRef Inc = FlagRef.slice(2, StringRef::npos); 200 if (getAbsPath(Inc, NewInc)) { 201 SmallString<128> NewArg(FlagRef.slice(0, 2)); 202 NewArg += NewInc; 203 IncFlags.push_back(std::move(NewArg)); 204 } 205 return; 206 } 207 208 assert(NumArgs == 2 && "Not expecting more than two arguments"); 209 StringRef Inc(Args[Idx + NumArgs - 1]); 210 if (!getAbsPath(Inc, NewInc)) 211 return; 212 IncFlags.push_back(SmallString<128>(Args[Idx])); 213 IncFlags.push_back(std::move(NewInc)); 214 } 215 216 void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, 217 CrashReportInfo *CrashInfo) const { 218 // Always quote the exe. 219 OS << ' '; 220 printArg(OS, Executable, /*Quote=*/true); 221 222 ArrayRef<const char *> Args = Arguments; 223 SmallVector<const char *, 128> ArgsRespFile; 224 if (ResponseFile != nullptr) { 225 buildArgvForResponseFile(ArgsRespFile); 226 Args = ArrayRef<const char *>(ArgsRespFile).slice(1); // no executable name 227 } 228 229 bool HaveCrashVFS = CrashInfo && !CrashInfo->VFSPath.empty(); 230 for (size_t i = 0, e = Args.size(); i < e; ++i) { 231 const char *const Arg = Args[i]; 232 233 if (CrashInfo) { 234 int NumArgs = 0; 235 bool IsInclude = false; 236 if (skipArgs(Arg, HaveCrashVFS, NumArgs, IsInclude)) { 237 i += NumArgs - 1; 238 continue; 239 } 240 241 // Relative includes need to be expanded to absolute paths. 242 if (HaveCrashVFS && IsInclude) { 243 SmallVector<SmallString<128>, 2> NewIncFlags; 244 rewriteIncludes(Args, i, NumArgs, NewIncFlags); 245 if (!NewIncFlags.empty()) { 246 for (auto &F : NewIncFlags) { 247 OS << ' '; 248 printArg(OS, F.c_str(), Quote); 249 } 250 i += NumArgs - 1; 251 continue; 252 } 253 } 254 255 auto Found = llvm::find_if(InputFilenames, 256 [&Arg](StringRef IF) { return IF == Arg; }); 257 if (Found != InputFilenames.end() && 258 (i == 0 || StringRef(Args[i - 1]) != "-main-file-name")) { 259 // Replace the input file name with the crashinfo's file name. 260 OS << ' '; 261 StringRef ShortName = llvm::sys::path::filename(CrashInfo->Filename); 262 printArg(OS, ShortName.str(), Quote); 263 continue; 264 } 265 } 266 267 OS << ' '; 268 printArg(OS, Arg, Quote); 269 } 270 271 if (CrashInfo && HaveCrashVFS) { 272 OS << ' '; 273 printArg(OS, "-ivfsoverlay", Quote); 274 OS << ' '; 275 printArg(OS, CrashInfo->VFSPath.str(), Quote); 276 277 // The leftover modules from the crash are stored in 278 // <name>.cache/vfs/modules 279 // Leave it untouched for pcm inspection and provide a clean/empty dir 280 // path to contain the future generated module cache: 281 // <name>.cache/vfs/repro-modules 282 SmallString<128> RelModCacheDir = llvm::sys::path::parent_path( 283 llvm::sys::path::parent_path(CrashInfo->VFSPath)); 284 llvm::sys::path::append(RelModCacheDir, "repro-modules"); 285 286 std::string ModCachePath = "-fmodules-cache-path="; 287 ModCachePath.append(RelModCacheDir.c_str()); 288 289 OS << ' '; 290 printArg(OS, ModCachePath, Quote); 291 } 292 293 if (ResponseFile != nullptr) { 294 OS << "\n Arguments passed via response file:\n"; 295 writeResponseFile(OS); 296 // Avoiding duplicated newline terminator, since FileLists are 297 // newline-separated. 298 if (Creator.getResponseFilesSupport() != Tool::RF_FileList) 299 OS << "\n"; 300 OS << " (end of response file)"; 301 } 302 303 OS << Terminator; 304 } 305 306 void Command::setResponseFile(const char *FileName) { 307 ResponseFile = FileName; 308 ResponseFileFlag = Creator.getResponseFileFlag(); 309 ResponseFileFlag += FileName; 310 } 311 312 void Command::setEnvironment(llvm::ArrayRef<const char *> NewEnvironment) { 313 Environment.reserve(NewEnvironment.size() + 1); 314 Environment.assign(NewEnvironment.begin(), NewEnvironment.end()); 315 Environment.push_back(nullptr); 316 } 317 318 void Command::PrintFileNames() const { 319 if (PrintInputFilenames) { 320 for (const char *Arg : InputFilenames) 321 llvm::outs() << llvm::sys::path::filename(Arg) << "\n"; 322 llvm::outs().flush(); 323 } 324 } 325 326 int Command::Execute(ArrayRef<llvm::Optional<StringRef>> Redirects, 327 std::string *ErrMsg, bool *ExecutionFailed) const { 328 PrintFileNames(); 329 330 SmallVector<const char *, 128> Argv; 331 if (ResponseFile == nullptr) { 332 Argv.push_back(Executable); 333 Argv.append(Arguments.begin(), Arguments.end()); 334 Argv.push_back(nullptr); 335 } else { 336 // If the command is too large, we need to put arguments in a response file. 337 std::string RespContents; 338 llvm::raw_string_ostream SS(RespContents); 339 340 // Write file contents and build the Argv vector 341 writeResponseFile(SS); 342 buildArgvForResponseFile(Argv); 343 Argv.push_back(nullptr); 344 SS.flush(); 345 346 // Save the response file in the appropriate encoding 347 if (std::error_code EC = writeFileWithEncoding( 348 ResponseFile, RespContents, Creator.getResponseFileEncoding())) { 349 if (ErrMsg) 350 *ErrMsg = EC.message(); 351 if (ExecutionFailed) 352 *ExecutionFailed = true; 353 // Return -1 by convention (see llvm/include/llvm/Support/Program.h) to 354 // indicate the requested executable cannot be started. 355 return -1; 356 } 357 } 358 359 Optional<ArrayRef<StringRef>> Env; 360 std::vector<StringRef> ArgvVectorStorage; 361 if (!Environment.empty()) { 362 assert(Environment.back() == nullptr && 363 "Environment vector should be null-terminated by now"); 364 ArgvVectorStorage = llvm::toStringRefArray(Environment.data()); 365 Env = makeArrayRef(ArgvVectorStorage); 366 } 367 368 auto Args = llvm::toStringRefArray(Argv.data()); 369 return llvm::sys::ExecuteAndWait(Executable, Args, Env, Redirects, 370 /*secondsToWait*/ 0, 371 /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); 372 } 373 374 CC1Command::CC1Command(const Action &Source, const Tool &Creator, 375 const char *Executable, 376 const llvm::opt::ArgStringList &Arguments, 377 ArrayRef<InputInfo> Inputs) 378 : Command(Source, Creator, Executable, Arguments, Inputs) { 379 InProcess = true; 380 } 381 382 void CC1Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, 383 CrashReportInfo *CrashInfo) const { 384 if (InProcess) 385 OS << " (in-process)\n"; 386 Command::Print(OS, Terminator, Quote, CrashInfo); 387 } 388 389 int CC1Command::Execute(ArrayRef<llvm::Optional<StringRef>> Redirects, 390 std::string *ErrMsg, bool *ExecutionFailed) const { 391 // FIXME: Currently, if there're more than one job, we disable 392 // -fintegrate-cc1. If we're no longer a integrated-cc1 job, fallback to 393 // out-of-process execution. See discussion in https://reviews.llvm.org/D74447 394 if (!InProcess) 395 return Command::Execute(Redirects, ErrMsg, ExecutionFailed); 396 397 PrintFileNames(); 398 399 SmallVector<const char *, 128> Argv; 400 Argv.push_back(getExecutable()); 401 Argv.append(getArguments().begin(), getArguments().end()); 402 Argv.push_back(nullptr); 403 404 // This flag simply indicates that the program couldn't start, which isn't 405 // applicable here. 406 if (ExecutionFailed) 407 *ExecutionFailed = false; 408 409 llvm::CrashRecoveryContext CRC; 410 CRC.DumpStackAndCleanupOnFailure = true; 411 412 const void *PrettyState = llvm::SavePrettyStackState(); 413 const Driver &D = getCreator().getToolChain().getDriver(); 414 415 int R = 0; 416 // Enter ExecuteCC1Tool() instead of starting up a new process 417 if (!CRC.RunSafely([&]() { R = D.CC1Main(Argv); })) { 418 llvm::RestorePrettyStackState(PrettyState); 419 return CRC.RetCode; 420 } 421 return R; 422 } 423 424 void CC1Command::setEnvironment(llvm::ArrayRef<const char *> NewEnvironment) { 425 // We don't support set a new environment when calling into ExecuteCC1Tool() 426 llvm_unreachable( 427 "The CC1Command doesn't support changing the environment vars!"); 428 } 429 430 FallbackCommand::FallbackCommand(const Action &Source_, const Tool &Creator_, 431 const char *Executable_, 432 const llvm::opt::ArgStringList &Arguments_, 433 ArrayRef<InputInfo> Inputs, 434 std::unique_ptr<Command> Fallback_) 435 : Command(Source_, Creator_, Executable_, Arguments_, Inputs), 436 Fallback(std::move(Fallback_)) {} 437 438 void FallbackCommand::Print(raw_ostream &OS, const char *Terminator, 439 bool Quote, CrashReportInfo *CrashInfo) const { 440 Command::Print(OS, "", Quote, CrashInfo); 441 OS << " ||"; 442 Fallback->Print(OS, Terminator, Quote, CrashInfo); 443 } 444 445 static bool ShouldFallback(int ExitCode) { 446 // FIXME: We really just want to fall back for internal errors, such 447 // as when some symbol cannot be mangled, when we should be able to 448 // parse something but can't, etc. 449 return ExitCode != 0; 450 } 451 452 int FallbackCommand::Execute(ArrayRef<llvm::Optional<StringRef>> Redirects, 453 std::string *ErrMsg, bool *ExecutionFailed) const { 454 int PrimaryStatus = Command::Execute(Redirects, ErrMsg, ExecutionFailed); 455 if (!ShouldFallback(PrimaryStatus)) 456 return PrimaryStatus; 457 458 // Clear ExecutionFailed and ErrMsg before falling back. 459 if (ErrMsg) 460 ErrMsg->clear(); 461 if (ExecutionFailed) 462 *ExecutionFailed = false; 463 464 const Driver &D = getCreator().getToolChain().getDriver(); 465 D.Diag(diag::warn_drv_invoking_fallback) << Fallback->getExecutable(); 466 467 int SecondaryStatus = Fallback->Execute(Redirects, ErrMsg, ExecutionFailed); 468 return SecondaryStatus; 469 } 470 471 ForceSuccessCommand::ForceSuccessCommand( 472 const Action &Source_, const Tool &Creator_, const char *Executable_, 473 const llvm::opt::ArgStringList &Arguments_, ArrayRef<InputInfo> Inputs) 474 : Command(Source_, Creator_, Executable_, Arguments_, Inputs) {} 475 476 void ForceSuccessCommand::Print(raw_ostream &OS, const char *Terminator, 477 bool Quote, CrashReportInfo *CrashInfo) const { 478 Command::Print(OS, "", Quote, CrashInfo); 479 OS << " || (exit 0)" << Terminator; 480 } 481 482 int ForceSuccessCommand::Execute(ArrayRef<llvm::Optional<StringRef>> Redirects, 483 std::string *ErrMsg, 484 bool *ExecutionFailed) const { 485 int Status = Command::Execute(Redirects, ErrMsg, ExecutionFailed); 486 (void)Status; 487 if (ExecutionFailed) 488 *ExecutionFailed = false; 489 return 0; 490 } 491 492 void JobList::Print(raw_ostream &OS, const char *Terminator, bool Quote, 493 CrashReportInfo *CrashInfo) const { 494 for (const auto &Job : *this) 495 Job.Print(OS, Terminator, Quote, CrashInfo); 496 } 497 498 void JobList::clear() { Jobs.clear(); } 499