Index: clang/include/clang/Driver/Compilation.h =================================================================== --- clang/include/clang/Driver/Compilation.h +++ clang/include/clang/Driver/Compilation.h @@ -217,6 +217,10 @@ return FailureResultFiles; } + const std::vector> &getRedirects() const { + return Redirects; + } + /// Installs a handler that is executed when a compilation job is finished. /// The arguments of the callback specify the compilation job as an instance /// of class Command and the exit status of the child process executed that Index: clang/include/clang/Driver/Driver.h =================================================================== --- clang/include/clang/Driver/Driver.h +++ clang/include/clang/Driver/Driver.h @@ -466,6 +466,11 @@ StringRef AdditionalInformation = "", CompilationDiagnosticReport *GeneratedReport = nullptr); + /// generateLinkerDiagnostics - Generate linker reproduce tar file. + /// Currently only meaningful for lld it's --reproduce option. + std::string generateLinkerDiagnostics(const Compilation &C, + const Command &FailingLLD); + /// @} /// @name Helper Methods /// @{ Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -1390,6 +1390,31 @@ return false; } +std::string Driver::generateLinkerDiagnostics(const Compilation &C, + const Command &FailingLLD) { + llvm::SmallString<128> TmpName; + // This code is similar to Driver::GetNamedOutputPath, but we can't use it + // because it adds to temporary file list, which get removed. + if (Arg *A = C.getArgs().getLastArg(options::OPT_fcrash_diagnostics_dir)) { + SmallString<128> CrashDirectory{A->getValue()}; + if (!getVFS().exists(CrashDirectory)) + llvm::sys::fs::create_directories(CrashDirectory); + llvm::sys::path::append(CrashDirectory, "linker-crash"); + llvm::sys::fs::createUniqueFile(CrashDirectory + "-%%%%%%.tar", TmpName); + } else { + TmpName = GetTemporaryPath("linker-crash", ".tar"); + } + + Command NewLLDInvocation = FailingLLD; + llvm::opt::ArgStringList ArgList = NewLLDInvocation.getArguments(); + ArgList.push_back(Saver.save(Twine{"--reproduce="} + TmpName).data()); + NewLLDInvocation.replaceArguments(ArgList); + + NewLLDInvocation.Execute(C.getRedirects(), nullptr, nullptr); + + return TmpName.str().str(); +} + // When clang crashes, produce diagnostic information including the fully // preprocessed source file(s). Request that the developer attach the // diagnostic information to a bug report. @@ -1400,13 +1425,30 @@ return; // Don't try to generate diagnostics for link or dsymutil jobs. - if (FailingCommand.getCreator().isLinkJob() || - FailingCommand.getCreator().isDsymutilJob()) + if (FailingCommand.getCreator().isDsymutilJob()) return; + bool IsLLD = false; + if (FailingCommand.getCreator().isLinkJob()) { + C.getDefaultToolChain().GetLinkerPath(&IsLLD); + if (!IsLLD) + return; + } + // Print the version of the compiler. PrintVersion(C, llvm::errs()); + Diag(clang::diag::note_drv_command_failed_diag_msg) + << "\n********************\n\n" + "PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:\n" + "Preprocessed source(s) and associated run script(s) are located at:"; + + // If lld failed, rerun it again with --reproduce. + if (IsLLD) { + std::string ReproFilename = generateLinkerDiagnostics(C, FailingCommand); + Diag(clang::diag::note_drv_command_failed_diag_msg) << ReproFilename; + } + // Suppress driver output and emit preprocessor output to temp file. CCGenDiagnostics = true; @@ -1504,11 +1546,6 @@ return; } - Diag(clang::diag::note_drv_command_failed_diag_msg) - << "\n********************\n\n" - "PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:\n" - "Preprocessed source(s) and associated run script(s) are located at:"; - SmallString<128> VFS; SmallString<128> ReproCrashFilename; for (const char *TempFile : TempFiles) {