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 *TmpNam = CreateTempFile(C, "linker-crash", "tar"); + Command NewLLDInvocation = Cmd; + llvm::opt::ArgStringList ArgList = NewLLDInvocation.getArguments(); + ArgList.push_back(Saver.save(Twine{"--reproduce="} + TmpNam).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) @@ -5162,6 +5188,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, @@ -5221,31 +5276,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/CMakeLists.txt =================================================================== --- clang/test/CMakeLists.txt +++ clang/test/CMakeLists.txt @@ -9,6 +9,12 @@ string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} CLANG_TOOLS_DIR ${LLVM_RUNTIME_OUTPUT_INTDIR}) +if("lld" IN_LIST LLVM_ENABLE_PROJECTS) + set(CLANG_TEST_HAS_LLD On) +else() + set(CLANG_TEST_HAS_LLD Off) +endif() + llvm_canonicalize_cmake_booleans( CLANG_BUILD_EXAMPLES CLANG_DEFAULT_PIE_ON_LINUX @@ -22,6 +28,7 @@ LLVM_ENABLE_PER_TARGET_RUNTIME_DIR LLVM_ENABLE_THREADS LLVM_WITH_Z3 + CLANG_TEST_HAS_LLD ) configure_lit_site_cfg( @@ -143,6 +150,10 @@ if(TARGET llvm-lto) list(APPEND CLANG_TEST_DEPS llvm-lto) endif() + + if("lld" IN_LIST LLVM_ENABLE_PROJECTS) + list(APPEND CLANG_TEST_DEPS lld) + endif() endif() if(CLANG_ENABLE_STATIC_ANALYZER) 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/lit.cfg.py =================================================================== --- clang/test/lit.cfg.py +++ clang/test/lit.cfg.py @@ -131,6 +131,9 @@ if config.clang_default_cxx_stdlib != '': config.available_features.add('default-cxx-stdlib-set') +if config.clang_using_lld: + config.available_features.add('using-lld') + # As of 2011.08, crash-recovery tests still do not pass on FreeBSD. if platform.system() not in ['FreeBSD']: config.available_features.add('crash-recovery') Index: clang/test/lit.site.cfg.py.in =================================================================== --- clang/test/lit.site.cfg.py.in +++ clang/test/lit.site.cfg.py.in @@ -37,6 +37,7 @@ config.has_plugins = @CLANG_PLUGIN_SUPPORT@ config.clang_vendor_uti = "@CLANG_VENDOR_UTI@" config.llvm_external_lit = path(r"@LLVM_EXTERNAL_LIT@") +config.clang_using_lld = @CLANG_TEST_HAS_LLD@ # Support substitution of the tools and libs dirs with user parameters. This is # used when we can't determine the tool dir at configuration time. Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -344,6 +344,9 @@ // 4 for ELF32, 8 for ELF64. int wordsize; + + // Used for testing clang driver crash reproduction. + bool forceCrash = false; }; // The only instance of Configuration struct. Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -128,6 +128,9 @@ driver->linkerMain(args); + if (config->forceCrash) + __builtin_trap(); + return errorCount() == 0; } @@ -1202,6 +1205,7 @@ config->zWxneeded = hasZOption(args, "wxneeded"); setUnresolvedSymbolPolicy(args); config->power10Stubs = args.getLastArgValue(OPT_power10_stubs_eq) != "no"; + config->forceCrash = args.hasArg(OPT_force_crash); if (opt::Arg *arg = args.getLastArg(OPT_eb, OPT_el)) { if (arg->getOption().matches(OPT_eb)) Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -714,3 +714,5 @@ "Perform additional validation of the written dynamic relocations", "Do not perform additional validation of the written dynamic relocations">, Flags<[HelpHidden]>; + +def force_crash: F<"force-crash">;