Index: clang/docs/UsersManual.rst =================================================================== --- clang/docs/UsersManual.rst +++ clang/docs/UsersManual.rst @@ -667,6 +667,14 @@ reproducibility of the failure. Below are the command line options to control the crash diagnostics. +.. option:: -fcrash_diagnostics= + + Valid values are: + + * ``off`` (Disable auto-generation of preprocessed source files during a clang crash.) + * ``default`` (Generate diagnostics for compiler crashes) + * ``all`` (Generate diagnostics for all tools which support it) + .. option:: -fno-crash-diagnostics Disable auto-generation of preprocessed source files during a clang crash. 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); + /// Generate linker reproduce tar file. Currently only meaningful on lld + /// with it's --reproduce option. + std::string generateLinkerDiagnostics(const Compilation &C, + const Command &FailingLLD); + /// @} /// @name Helper Methods /// @{ Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -1372,8 +1372,13 @@ MarshallingInfoFlag>; def fconstexpr_backtrace_limit_EQ : Joined<["-"], "fconstexpr-backtrace-limit=">, Group; +def fcrash_diagnostics_EQ : Joined<["-"], "fcrash-diagnostics=">, Group, Flags<[NoArgumentUnused, CoreOption]>, + HelpText<"Set level of crash diagnostic reporting, (option: off, default, all)">; +def fcrash_diagnostics : Flag<["-"], "fcrash-diagnostics">, Group, Flags<[NoArgumentUnused, CoreOption]>, + HelpText<"Enable crash diagnostic reporting (default)">, Alias, AliasArgs<["default"]>; def fno_crash_diagnostics : Flag<["-"], "fno-crash-diagnostics">, Group, Flags<[NoArgumentUnused, CoreOption]>, - HelpText<"Disable auto-generation of preprocessed source files and a script for reproduction during a clang crash">; + HelpText<"Disable auto-generation of preprocessed source files and a script for reproduction during a clang crash">, + Alias, AliasArgs<["off"]>; def fcrash_diagnostics_dir : Joined<["-"], "fcrash-diagnostics-dir=">, Group, Flags<[NoArgumentUnused, CoreOption]>, HelpText<"Put crash-report files in ">, MetaVarName<"">; Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -1390,23 +1390,73 @@ 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. void Driver::generateCompilationDiagnostics( Compilation &C, const Command &FailingCommand, StringRef AdditionalInformation, CompilationDiagnosticReport *Report) { - if (C.getArgs().hasArg(options::OPT_fno_crash_diagnostics)) + unsigned level = 1; + if (Arg *A = C.getArgs().getLastArg(options::OPT_fcrash_diagnostics_EQ)) { + level = llvm::StringSwitch(A->getValue()) + .Case("off", 0) + .Case("default", 1) + .Case("all", 2) + .Default(1); + } + if (!level) 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 || level < 2) + 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 +1554,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) { Index: clang/test/Driver/crash-report.cpp =================================================================== --- clang/test/Driver/crash-report.cpp +++ clang/test/Driver/crash-report.cpp @@ -27,6 +27,29 @@ // RUN: cat %t/crash-report-*.cpp | FileCheck --check-prefix=CHECKSRC %s // RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s +// Test manually specifying -fcrash-diagnostics[=[default|all]] emits +// diagnostics +// RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \ +// RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \ +// RUN: not %clang %s @%t.rsp -DFATAL -fcrash-diagnostics 2>&1 | \ +// RUN: FileCheck %s +// RUN: cat %t/crash-report-*.cpp | FileCheck --check-prefix=CHECKSRC %s +// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s + +// RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \ +// RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \ +// RUN: not %clang %s @%t.rsp -DFATAL -fcrash-diagnostics=default 2>&1 | \ +// RUN: FileCheck %s +// RUN: cat %t/crash-report-*.cpp | FileCheck --check-prefix=CHECKSRC %s +// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s + +// RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \ +// RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \ +// RUN: not %clang %s @%t.rsp -DFATAL -fcrash-diagnostics=all 2>&1 | \ +// RUN: FileCheck %s +// RUN: cat %t/crash-report-*.cpp | FileCheck --check-prefix=CHECKSRC %s +// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s + // REQUIRES: crash-recovery #ifdef PARSER