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.) + * ``compiler`` (Generate diagnostics for compiler crashes (default)) + * ``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/Driver.h =================================================================== --- clang/include/clang/Driver/Driver.h +++ clang/include/clang/Driver/Driver.h @@ -529,6 +529,11 @@ /// Returns the default name for linked images (e.g., "a.out"). const char *getDefaultImageName() const; + // Creates a temp file with $Prefix-%%%%%%.$Suffix + const char *CreateTempFile(Compilation &C, StringRef Prefix, StringRef Suffix, + bool MultipleArchs = false, + StringRef BoundArch = {}) const; + /// GetNamedOutputPath - Return the name to use for the output of /// the action \p JA. The result is appended to the compilation's /// list of temporary or result files, as appropriate. Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -1384,8 +1384,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, compiler, all)">; +def fcrash_diagnostics : Flag<["-"], "fcrash-diagnostics">, Group, Flags<[NoArgumentUnused, CoreOption]>, + HelpText<"Enable crash diagnostic reporting (default)">, Alias, AliasArgs<["compiler"]>; 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 @@ -1396,14 +1396,28 @@ 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("compiler", 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()); @@ -1497,6 +1511,18 @@ return; } + // If lld failed, rerun it again with --reproduce. + if (IsLLD) { + const char *TmpName = CreateTempFile(C, "linker-crash", "tar"); + Command NewLLDInvocation = Cmd; + llvm::opt::ArgStringList ArgList = NewLLDInvocation.getArguments(); + ArgList.push_back(Saver.save(Twine{"--reproduce="} + TmpName).data()); + NewLLDInvocation.replaceArguments(std::move(ArgList)); + + // Redirect stdout/stderr to /dev/null. + NewLLDInvocation.Execute({None, {""}, {""}}, nullptr, nullptr); + } + const ArgStringList &TempFiles = C.getTempFiles(); if (TempFiles.empty()) { Diag(clang::diag::note_drv_command_failed_diag_msg) @@ -5164,6 +5190,35 @@ return false; } +const char *Driver::CreateTempFile(Compilation &C, StringRef Prefix, + StringRef Suffix, bool MultipleArchs, + StringRef BoundArch) const { + SmallString<128> TmpName; + Arg *A = C.getArgs().getLastArg(options::OPT_fcrash_diagnostics_dir); + if (CCGenDiagnostics && A) { + SmallString<128> CrashDirectory(A->getValue()); + if (!getVFS().exists(CrashDirectory)) + llvm::sys::fs::create_directories(CrashDirectory); + llvm::sys::path::append(CrashDirectory, Prefix); + const char *Middle = !Suffix.empty() ? "-%%%%%%." : "-%%%%%%"; + std::error_code EC = llvm::sys::fs::createUniqueFile( + CrashDirectory + Middle + Suffix, TmpName); + if (EC) { + Diag(clang::diag::err_unable_to_make_temp) << EC.message(); + return ""; + } + } else { + if (MultipleArchs && !BoundArch.empty()) { + TmpName = GetTemporaryDirectory(Prefix); + llvm::sys::path::append(TmpName, + Twine(Prefix) + "-" + BoundArch + "." + Suffix); + } else { + TmpName = GetTemporaryPath(Prefix, Suffix); + } + } + return C.addTempFile(C.getArgs().MakeArgString(TmpName)); +} + const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, const char *BaseInput, StringRef OrigBoundArch, bool AtTopLevel, @@ -5223,31 +5278,8 @@ CCGenDiagnostics) { StringRef Name = llvm::sys::path::filename(BaseInput); std::pair Split = Name.split('.'); - SmallString<128> TmpName; const char *Suffix = types::getTypeTempSuffix(JA.getType(), IsCLMode()); - Arg *A = C.getArgs().getLastArg(options::OPT_fcrash_diagnostics_dir); - if (CCGenDiagnostics && A) { - SmallString<128> CrashDirectory(A->getValue()); - if (!getVFS().exists(CrashDirectory)) - llvm::sys::fs::create_directories(CrashDirectory); - llvm::sys::path::append(CrashDirectory, Split.first); - const char *Middle = Suffix ? "-%%%%%%." : "-%%%%%%"; - std::error_code EC = llvm::sys::fs::createUniqueFile( - CrashDirectory + Middle + Suffix, TmpName); - if (EC) { - Diag(clang::diag::err_unable_to_make_temp) << EC.message(); - return ""; - } - } else { - if (MultipleArchs && !BoundArch.empty()) { - TmpName = GetTemporaryDirectory(Split.first); - llvm::sys::path::append(TmpName, - Split.first + "-" + BoundArch + "." + Suffix); - } else { - TmpName = GetTemporaryPath(Split.first, Suffix); - } - } - return C.addTempFile(C.getArgs().MakeArgString(TmpName)); + return CreateTempFile(C, Split.first, Suffix, MultipleArchs, BoundArch); } SmallString<128> BasePath(BaseInput); 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[=[compiler|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=compiler 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 Index: clang/test/Driver/lld-repro.c =================================================================== --- /dev/null +++ clang/test/Driver/lld-repro.c @@ -0,0 +1,21 @@ +// REQUIRES: using-lld + +// -force-crash only implemented in ld.lld +// RUN: echo '--target=x86_64-unknown-linux-gnu -fuse-ld=lld' > %t.rsp + +// RUN: LLD_FORCE_CRASH=1 not %clang %s @%t.rsp -fcrash-diagnostics=all 2>&1 | FileCheck %s + +// CHECK: PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT: +// CHECK-NEXT: Preprocessed source(s) and associated run script(s) are located at: +// CHECK-NEXT: note: diagnostic msg: {{.*}}/lld-repro-{{.*}}.c +// CHECK-NEXT: note: diagnostic msg: {{.*}}/linker-crash-{{.*}}.tar +// CHECK-NEXT: note: diagnostic msg: {{.*}}/lld-repro-{{.*}}.sh +// CHECK-NEXT: note: diagnostic msg: +// CHECK: ******************** + +// RUN: LLD_FORCE_CRASH=1 not %clang %s @%t.rsp -fcrash-diagnostics=compiler 2>&1 | FileCheck %s --check-prefix=NO-LINKER +// RUN: LLD_FORCE_CRASH=1 not %clang %s @%t.rsp 2>&1 | FileCheck %s --check-prefix=NO-LINKER + +// NO-LINKER-NOT: PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT: + +int main() {}