[clang] 684c080 - [Clang] Extend -gen-reproducer flag
Alex Brachet via cfe-commits
cfe-commits at lists.llvm.org
Fri May 27 08:50:25 PDT 2022
Author: Alex Brachet
Date: 2022-05-27T15:49:13Z
New Revision: 684c080108766b4f112f172fed4a49059484614d
URL: https://github.com/llvm/llvm-project/commit/684c080108766b4f112f172fed4a49059484614d
DIFF: https://github.com/llvm/llvm-project/commit/684c080108766b4f112f172fed4a49059484614d.diff
LOG: [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
Added:
clang/test/Driver/emit-reproducer.c
Modified:
clang/include/clang/Driver/Driver.h
clang/include/clang/Driver/Options.td
clang/lib/Driver/Driver.cpp
clang/tools/driver/driver.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h
index f0f294a669d9..86eea9375f67 100644
--- a/clang/include/clang/Driver/Driver.h
+++ b/clang/include/clang/Driver/Driver.h
@@ -276,11 +276,6 @@ class Driver {
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 @@ class Driver {
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
/// @{
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index e19baac5860a..2a5e8bf27e35 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -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]>,
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 94925568aec2..aaef2e1ded32 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -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)) {
diff --git a/clang/test/Driver/emit-reproducer.c b/clang/test/Driver/emit-reproducer.c
new file mode 100644
index 000000000000..f50171b7d9a9
--- /dev/null
+++ b/clang/test/Driver/emit-reproducer.c
@@ -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
diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp
index a7bfb07e002b..c9aee57d92cf 100644
--- a/clang/tools/driver/driver.cpp
+++ b/clang/tools/driver/driver.cpp
@@ -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) {
More information about the cfe-commits
mailing list