: Opts(createDriverOptTable()), Diags(Diags), VFS(std::move(VFS)),
Mode(GCCMode), SaveTemps(SaveTempsNone), BitcodeEmbed(EmbedNone),
LTOMode(LTOK_None), ClangExecutable(ClangExecutable),
- SysRoot(DEFAULT_SYSROOT),
- DriverTitle("clang LLVM compiler"), CCPrintOptionsFilename(nullptr),
- CCPrintHeadersFilename(nullptr), CCLogDiagnosticsFilename(nullptr),
- CCCPrintBindings(false), CCPrintHeaders(false), CCLogDiagnostics(false),
+ SysRoot(DEFAULT_SYSROOT), DriverTitle("clang LLVM compiler"),
+ CCPrintOptionsFilename(nullptr), CCPrintHeadersFilename(nullptr),
+ CCLogDiagnosticsFilename(nullptr), CCCPrintBindings(false),
+ CCPrintOptions(false), CCPrintHeaders(false), CCLogDiagnostics(false),
CCGenDiagnostics(false), DefaultTargetTriple(DefaultTargetTriple),
- CCCGenericGCCName(""), Saver(Alloc),
- CheckInputsExist(true), CCCUsePCH(true),
- GenReproducer(false), SuppressMissingInputWarning(false) {
+ CCCGenericGCCName(""), Saver(Alloc), CheckInputsExist(true),
+ CCCUsePCH(true), GenReproducer(false),
+ SuppressMissingInputWarning(false) {
// Provide a sane fallback if no VFS is specified.
if (!this->VFS)
// 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) {
+void Driver::generateCompilationDiagnostics(
+ Compilation &C, const Command &FailingCommand,
+ StringRef AdditionalInformation, CompilationDiagnosticReport *Report) {
if (C.getArgs().hasArg(options::OPT_fno_crash_diagnostics))
return;
SmallString<128> ReproCrashFilename;
for (const char *TempFile : TempFiles) {
Diag(clang::diag::note_drv_command_failed_diag_msg) << TempFile;
+ if (Report)
+ Report->TemporaryFiles.push_back(TempFile);
if (ReproCrashFilename.empty()) {
ReproCrashFilename = TempFile;
llvm::sys::path::replace_extension(ReproCrashFilename, ".crash");
ScriptOS << "# Original command: ";
Cmd.Print(ScriptOS, "\n", /*Quote=*/true);
Cmd.Print(ScriptOS, "\n", /*Quote=*/true, &CrashInfo);
+ if (!AdditionalInformation.empty())
+ ScriptOS << "\n# Additional information: " << AdditionalInformation
+ << "\n";
+ if (Report)
+ Report->TemporaryFiles.push_back(Script);
Diag(clang::diag::note_drv_command_failed_diag_msg) << Script;
}
--- /dev/null
+//===-- cc1gen_reproducer_main.cpp - Clang reproducer generator ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This is the entry point to the clang -cc1gen-reproducer functionality, which
+// generates reproducers for invocations for clang-based tools.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+
+namespace {
+
+struct UnsavedFileHash {
+ std::string Name;
+ std::string MD5;
+};
+
+struct ClangInvocationInfo {
+ std::string Toolchain;
+ std::string LibclangOperation;
+ std::string LibclangOptions;
+ std::vector<std::string> Arguments;
+ std::vector<std::string> InvocationArguments;
+ std::vector<UnsavedFileHash> UnsavedFileHashes;
+ bool Dump = false;
+};
+
+} // end anonymous namespace
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash)
+
+namespace llvm {
+namespace yaml {
+
+template <> struct MappingTraits<UnsavedFileHash> {
+ static void mapping(IO &IO, UnsavedFileHash &Info) {
+ IO.mapRequired("name", Info.Name);
+ IO.mapRequired("md5", Info.MD5);
+ }
+};
+
+template <> struct MappingTraits<ClangInvocationInfo> {
+ static void mapping(IO &IO, ClangInvocationInfo &Info) {
+ IO.mapRequired("toolchain", Info.Toolchain);
+ IO.mapOptional("libclang.operation", Info.LibclangOperation);
+ IO.mapOptional("libclang.opts", Info.LibclangOptions);
+ IO.mapRequired("args", Info.Arguments);
+ IO.mapOptional("invocation-args", Info.InvocationArguments);
+ IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes);
+ }
+};
+
+} // end namespace yaml
+} // end namespace llvm
+
+static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) {
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ OS << '{';
+ bool NeedComma = false;
+ auto EmitKey = [&](StringRef Key) {
+ if (NeedComma)
+ OS << ", ";
+ NeedComma = true;
+ OS << '"' << Key << "\": ";
+ };
+ auto EmitStringKey = [&](StringRef Key, StringRef Value) {
+ if (Value.empty())
+ return;
+ EmitKey(Key);
+ OS << '"' << Value << '"';
+ };
+ EmitStringKey("libclang.operation", Info.LibclangOperation);
+ EmitStringKey("libclang.opts", Info.LibclangOptions);
+ if (!Info.InvocationArguments.empty()) {
+ EmitKey("invocation-args");
+ OS << '[';
+ for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) {
+ if (Arg.index())
+ OS << ',';
+ OS << '"' << Arg.value() << '"';
+ }
+ OS << ']';
+ }
+ OS << '}';
+ // FIXME: Compare unsaved file hashes and report mismatch in the reproducer.
+ if (Info.Dump)
+ llvm::outs() << "REPRODUCER METAINFO: " << OS.str() << "\n";
+ return std::move(OS.str());
+}
+
+/// Generates a reproducer for a set of arguments from a specific invocation.
+static llvm::Optional<driver::Driver::CompilationDiagnosticReport>
+generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
+ const ClangInvocationInfo &Info) {
+ using namespace driver;
+ auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);
+
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions;
+
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+ DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer());
+ ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
+ Driver TheDriver(Argv[0], llvm::sys::getDefaultTargetTriple(), Diags);
+ TheDriver.setTargetAndMode(TargetAndMode);
+
+ std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));
+ if (C && !C->containsError()) {
+ for (const auto &J : C->getJobs()) {
+ if (const Command *Cmd = dyn_cast<Command>(&J)) {
+ Driver::CompilationDiagnosticReport Report;
+ TheDriver.generateCompilationDiagnostics(
+ *C, *Cmd, generateReproducerMetaInfo(Info), &Report);
+ return Report;
+ }
+ }
+ }
+
+ return None;
+}
+
+std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes);
+
+static void printReproducerInformation(
+ llvm::raw_ostream &OS, const ClangInvocationInfo &Info,
+ const driver::Driver::CompilationDiagnosticReport &Report) {
+ OS << "REPRODUCER:\n";
+ OS << "{\n";
+ OS << R"("files":[)";
+ for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) {
+ if (File.index())
+ OS << ',';
+ OS << '"' << File.value() << '"';
+ }
+ OS << "]\n}\n";
+}
+
+int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,
+ void *MainAddr) {
+ if (Argv.size() < 1) {
+ llvm::errs() << "error: missing invocation file\n";
+ return 1;
+ }
+ // Parse the invocation descriptor.
+ StringRef Input = Argv[0];
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
+ llvm::MemoryBuffer::getFile(Input);
+ if (!Buffer) {
+ llvm::errs() << "error: failed to read " << Input << ": "
+ << Buffer.getError().message() << "\n";
+ return 1;
+ }
+ llvm::yaml::Input YAML(Buffer.get()->getBuffer());
+ ClangInvocationInfo InvocationInfo;
+ YAML >> InvocationInfo;
+ if (Argv.size() > 1 && Argv[1] == StringRef("-v"))
+ InvocationInfo.Dump = true;
+
+ // Create an invocation that will produce the reproducer.
+ std::vector<const char *> DriverArgs;
+ for (const auto &Arg : InvocationInfo.Arguments)
+ DriverArgs.push_back(Arg.c_str());
+ std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true);
+ DriverArgs[0] = Path.c_str();
+ llvm::Optional<driver::Driver::CompilationDiagnosticReport> Report =
+ generateReproducerForInvocationArguments(DriverArgs, InvocationInfo);
+
+ // Emit the information about the reproduce files to stdout.
+ int Result = 1;
+ if (Report) {
+ printReproducerInformation(llvm::outs(), InvocationInfo, *Report);
+ Result = 0;
+ }
+
+ // Remove the input file.
+ llvm::sys::fs::remove(Input);
+ return Result;
+}