forked from OSchip/llvm-project
[Clang] Extend -gen-reproducer flag
-gen-reproducer causes crash reproduction to be emitted even when clang didn't crash, and now can optionally take an argument of never, on-crash (default), on-error and always. Differential revision: https://reviews.llvm.org/D120201
This commit is contained in:
parent
af430944b3
commit
684c080108
|
@ -276,11 +276,6 @@ private:
|
|||
unsigned ProbePrecompiled : 1;
|
||||
|
||||
public:
|
||||
/// Force clang to emit reproducer for driver invocation. This is enabled
|
||||
/// indirectly by setting FORCE_CLANG_DIAGNOSTICS_CRASH environment variable
|
||||
/// or when using the -gen-reproducer driver flag.
|
||||
unsigned GenReproducer : 1;
|
||||
|
||||
// getFinalPhase - Determine which compilation mode we are in and record
|
||||
// which option we used to determine the final phase.
|
||||
// TODO: Much of what getFinalPhase returns are not actually true compiler
|
||||
|
@ -505,6 +500,32 @@ public:
|
|||
StringRef AdditionalInformation = "",
|
||||
CompilationDiagnosticReport *GeneratedReport = nullptr);
|
||||
|
||||
enum class CommandStatus {
|
||||
Crash = 1,
|
||||
Error,
|
||||
Ok,
|
||||
};
|
||||
|
||||
enum class ReproLevel {
|
||||
Off = 0,
|
||||
OnCrash = static_cast<int>(CommandStatus::Crash),
|
||||
OnError = static_cast<int>(CommandStatus::Error),
|
||||
Always = static_cast<int>(CommandStatus::Ok),
|
||||
};
|
||||
|
||||
bool maybeGenerateCompilationDiagnostics(
|
||||
CommandStatus CS, ReproLevel Level, Compilation &C,
|
||||
const Command &FailingCommand, StringRef AdditionalInformation = "",
|
||||
CompilationDiagnosticReport *GeneratedReport = nullptr) {
|
||||
if (static_cast<int>(CS) > static_cast<int>(Level))
|
||||
return false;
|
||||
// Hack to ensure that diagnostic notes get emitted.
|
||||
Diags.setLastDiagnosticIgnored(false);
|
||||
generateCompilationDiagnostics(C, FailingCommand, AdditionalInformation,
|
||||
GeneratedReport);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @}
|
||||
/// @name Helper Methods
|
||||
/// @{
|
||||
|
|
|
@ -558,7 +558,10 @@ def arcmt_migrate_report_output : Separate<["-"], "arcmt-migrate-report-output">
|
|||
def arcmt_migrate_emit_arc_errors : Flag<["-"], "arcmt-migrate-emit-errors">,
|
||||
HelpText<"Emit ARC errors even if the migrator can fix them">, Flags<[CC1Option]>,
|
||||
MarshallingInfoFlag<FrontendOpts<"ARCMTMigrateEmitARCErrors">>;
|
||||
def gen_reproducer_eq: Joined<["-"], "gen-reproducer=">, Flags<[NoArgumentUnused, CoreOption]>,
|
||||
HelpText<"Emit reproducer on (option: off, crash (default), error, always)">;
|
||||
def gen_reproducer: Flag<["-"], "gen-reproducer">, InternalDebugOpt,
|
||||
Alias<gen_reproducer_eq>, AliasArgs<["always"]>,
|
||||
HelpText<"Auto-generates preprocessed source files and a reproduction script">;
|
||||
def gen_cdb_fragment_path: Separate<["-"], "gen-cdb-fragment-path">, InternalDebugOpt,
|
||||
HelpText<"Emit a compilation database fragment to the specified directory">;
|
||||
|
@ -1397,6 +1400,7 @@ def fexperimental_new_constant_interpreter : Flag<["-"], "fexperimental-new-cons
|
|||
def fconstexpr_backtrace_limit_EQ : Joined<["-"], "fconstexpr-backtrace-limit=">,
|
||||
Group<f_Group>;
|
||||
def fno_crash_diagnostics : Flag<["-"], "fno-crash-diagnostics">, Group<f_clang_Group>, Flags<[NoArgumentUnused, CoreOption]>,
|
||||
Alias<gen_reproducer_eq>, AliasArgs<["off"]>,
|
||||
HelpText<"Disable auto-generation of preprocessed source files and a script for reproduction during a clang crash">;
|
||||
def fcrash_diagnostics_dir : Joined<["-"], "fcrash-diagnostics-dir=">,
|
||||
Group<f_clang_Group>, Flags<[NoArgumentUnused, CoreOption]>,
|
||||
|
|
|
@ -198,8 +198,7 @@ Driver::Driver(StringRef ClangExecutable, StringRef TargetTriple,
|
|||
CCPrintOptions(false), CCPrintHeaders(false), CCLogDiagnostics(false),
|
||||
CCGenDiagnostics(false), CCPrintProcessStats(false),
|
||||
TargetTriple(TargetTriple), Saver(Alloc), CheckInputsExist(true),
|
||||
ProbePrecompiled(true), GenReproducer(false),
|
||||
SuppressMissingInputWarning(false) {
|
||||
ProbePrecompiled(true), SuppressMissingInputWarning(false) {
|
||||
// Provide a sane fallback if no VFS is specified.
|
||||
if (!this->VFS)
|
||||
this->VFS = llvm::vfs::getRealFileSystem();
|
||||
|
@ -1217,9 +1216,6 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
|
|||
CCCPrintBindings = Args.hasArg(options::OPT_ccc_print_bindings);
|
||||
if (const Arg *A = Args.getLastArg(options::OPT_ccc_gcc_name))
|
||||
CCCGenericGCCName = A->getValue();
|
||||
GenReproducer = Args.hasFlag(options::OPT_gen_reproducer,
|
||||
options::OPT_fno_crash_diagnostics,
|
||||
!!::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"));
|
||||
|
||||
// Process -fproc-stat-report options.
|
||||
if (const Arg *A = Args.getLastArg(options::OPT_fproc_stat_report_EQ)) {
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// RUN: rm -rf %t && mkdir %t
|
||||
|
||||
// RUN: not %clang -DFATAL %s -fcrash-diagnostics-dir=%t -gen-reproducer=off 2>&1 | FileCheck %s --check-prefix=NOT
|
||||
// RUN: not %clang -DFATAL %s -fcrash-diagnostics-dir=%t -fno-crash-diagnostics 2>&1 | FileCheck %s --check-prefix=NOT
|
||||
// RUN: not %clang -DFATAL %s -fcrash-diagnostics-dir=%t 2>&1 | FileCheck %s
|
||||
// RUN: not %clang -DFATAL %s -fcrash-diagnostics-dir=%t -gen-reproducer=crash 2>&1 | FileCheck %s
|
||||
// RUN: not %clang -DFATAL %s -fcrash-diagnostics-dir=%t -gen-reproducer=error 2>&1 | FileCheck %s
|
||||
// RUN: not %clang -DFATAL %s -fcrash-diagnostics-dir=%t -gen-reproducer=always 2>&1 | FileCheck %s
|
||||
// RUN: not %clang -DFATAL %s -fcrash-diagnostics-dir=%t -gen-reproducer 2>&1 | FileCheck %s
|
||||
|
||||
// RUN: not %clang -DERROR %s -fcrash-diagnostics-dir=%t -gen-reproducer=off 2>&1 | FileCheck %s --check-prefix=NOT
|
||||
// RUN: not %clang -DERROR %s -fcrash-diagnostics-dir=%t -fno-crash-diagnostics 2>&1 | FileCheck %s --check-prefix=NOT
|
||||
// RUN: not %clang -DERROR %s -fcrash-diagnostics-dir=%t 2>&1 | FileCheck %s --check-prefix=NOT
|
||||
// RUN: not %clang -DERROR %s -fcrash-diagnostics-dir=%t -gen-reproducer=crash 2>&1 | FileCheck %s --check-prefix=NOT
|
||||
// RUN: not %clang -DERROR %s -fcrash-diagnostics-dir=%t -gen-reproducer=error 2>&1 | FileCheck %s
|
||||
// RUN: not %clang -DERROR %s -fcrash-diagnostics-dir=%t -gen-reproducer=always 2>&1 | FileCheck %s
|
||||
// RUN: not %clang -DERROR %s -fcrash-diagnostics-dir=%t -gen-reproducer 2>&1 | FileCheck %s
|
||||
|
||||
// RUN: %clang %s -fcrash-diagnostics-dir=%t -gen-reproducer=off 2>&1 | FileCheck %s --check-prefix=NOT --allow-empty
|
||||
// RUN: %clang %s -fcrash-diagnostics-dir=%t -fno-crash-diagnostics 2>&1 | FileCheck %s --check-prefix=NOT --allow-empty
|
||||
// RUN: %clang %s -fcrash-diagnostics-dir=%t 2>&1 | FileCheck %s --check-prefix=NOT --allow-empty
|
||||
// RUN: %clang %s -fcrash-diagnostics-dir=%t -gen-reproducer=crash 2>&1 | FileCheck %s --check-prefix=NOT --allow-empty
|
||||
// RUN: %clang %s -fcrash-diagnostics-dir=%t -gen-reproducer=error 2>&1 | FileCheck %s --check-prefix=NOT --allow-empty
|
||||
// RUN: not %clang %s -fcrash-diagnostics-dir=%t -gen-reproducer=always 2>&1 | FileCheck %s
|
||||
// RUN: not %clang %s -fcrash-diagnostics-dir=%t -gen-reproducer 2>&1 | FileCheck %s
|
||||
|
||||
// RUN: not %clang $s -gen-reproducer=badvalue 2>&1 | FileCheck %s --check-prefix=BAD-VALUE
|
||||
// BAD-VALUE: Unknown value for -gen-reproducer=: 'badvalue'
|
||||
|
||||
// CHECK: note: diagnostic msg: {{.*}}emit-reproducer-{{.*}}.c
|
||||
// NOT-NOT: note: diagnostic msg: {{.*}}emit-reproducer-{{.*}}.c
|
||||
|
||||
#ifdef FATAL
|
||||
#pragma clang __debug crash
|
||||
#elif ERROR
|
||||
int main
|
||||
#else
|
||||
int main() {}
|
||||
#endif
|
|
@ -482,32 +482,37 @@ int main(int Argc, const char **Argv) {
|
|||
}
|
||||
|
||||
std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Args));
|
||||
|
||||
Driver::ReproLevel ReproLevel = Driver::ReproLevel::OnCrash;
|
||||
if (Arg *A = C->getArgs().getLastArg(options::OPT_gen_reproducer_eq)) {
|
||||
auto Level = llvm::StringSwitch<Optional<Driver::ReproLevel>>(A->getValue())
|
||||
.Case("off", Driver::ReproLevel::Off)
|
||||
.Case("crash", Driver::ReproLevel::OnCrash)
|
||||
.Case("error", Driver::ReproLevel::OnError)
|
||||
.Case("always", Driver::ReproLevel::Always)
|
||||
.Default(None);
|
||||
if (!Level) {
|
||||
llvm::errs() << "Unknown value for " << A->getSpelling() << ": '"
|
||||
<< A->getValue() << "'\n";
|
||||
return 1;
|
||||
}
|
||||
ReproLevel = *Level;
|
||||
}
|
||||
if (!!::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"))
|
||||
ReproLevel = Driver::ReproLevel::Always;
|
||||
|
||||
int Res = 1;
|
||||
bool IsCrash = false;
|
||||
Driver::CommandStatus CommandStatus = Driver::CommandStatus::Ok;
|
||||
// Pretend the first command failed if ReproStatus is Always.
|
||||
const Command *FailingCommand = &*C->getJobs().begin();
|
||||
if (C && !C->containsError()) {
|
||||
SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
|
||||
Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
|
||||
|
||||
// Force a crash to test the diagnostics.
|
||||
if (TheDriver.GenReproducer) {
|
||||
Diags.Report(diag::err_drv_force_crash)
|
||||
<< !::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH");
|
||||
|
||||
// Pretend that every command failed.
|
||||
FailingCommands.clear();
|
||||
for (const auto &J : C->getJobs())
|
||||
if (const Command *C = dyn_cast<Command>(&J))
|
||||
FailingCommands.push_back(std::make_pair(-1, C));
|
||||
|
||||
// Print the bug report message that would be printed if we did actually
|
||||
// crash, but only if we're crashing due to FORCE_CLANG_DIAGNOSTICS_CRASH.
|
||||
if (::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"))
|
||||
llvm::dbgs() << llvm::getBugReportMsg();
|
||||
}
|
||||
|
||||
for (const auto &P : FailingCommands) {
|
||||
int CommandRes = P.first;
|
||||
const Command *FailingCommand = P.second;
|
||||
FailingCommand = P.second;
|
||||
if (!Res)
|
||||
Res = CommandRes;
|
||||
|
||||
|
@ -526,13 +531,21 @@ int main(int Argc, const char **Argv) {
|
|||
// https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html
|
||||
IsCrash |= CommandRes > 128;
|
||||
#endif
|
||||
if (IsCrash) {
|
||||
TheDriver.generateCompilationDiagnostics(*C, *FailingCommand);
|
||||
CommandStatus =
|
||||
IsCrash ? Driver::CommandStatus::Crash : Driver::CommandStatus::Error;
|
||||
if (IsCrash)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print the bug report message that would be printed if we did actually
|
||||
// crash, but only if we're crashing due to FORCE_CLANG_DIAGNOSTICS_CRASH.
|
||||
if (::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"))
|
||||
llvm::dbgs() << llvm::getBugReportMsg();
|
||||
if (TheDriver.maybeGenerateCompilationDiagnostics(CommandStatus, ReproLevel,
|
||||
*C, *FailingCommand))
|
||||
Res = 1;
|
||||
|
||||
Diags.getClient()->finish();
|
||||
|
||||
if (!UseNewCC1Process && IsCrash) {
|
||||
|
|
Loading…
Reference in New Issue