1045c514fSAlex Lorenz //===-- cc1gen_reproducer_main.cpp - Clang reproducer generator ----------===// 2045c514fSAlex Lorenz // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6045c514fSAlex Lorenz // 7045c514fSAlex Lorenz //===----------------------------------------------------------------------===// 8045c514fSAlex Lorenz // 9045c514fSAlex Lorenz // This is the entry point to the clang -cc1gen-reproducer functionality, which 10045c514fSAlex Lorenz // generates reproducers for invocations for clang-based tools. 11045c514fSAlex Lorenz // 12045c514fSAlex Lorenz //===----------------------------------------------------------------------===// 13045c514fSAlex Lorenz 14045c514fSAlex Lorenz #include "clang/Basic/Diagnostic.h" 15045c514fSAlex Lorenz #include "clang/Basic/LLVM.h" 16045c514fSAlex Lorenz #include "clang/Driver/Compilation.h" 17045c514fSAlex Lorenz #include "clang/Driver/Driver.h" 18045c514fSAlex Lorenz #include "llvm/ADT/ArrayRef.h" 19045c514fSAlex Lorenz #include "llvm/ADT/STLExtras.h" 20045c514fSAlex Lorenz #include "llvm/Support/FileSystem.h" 213e57aa30SAlex Brachet #include "llvm/Support/LLVMDriver.h" 22045c514fSAlex Lorenz #include "llvm/Support/TargetSelect.h" 23fc51490bSJonas Devlieghere #include "llvm/Support/VirtualFileSystem.h" 24045c514fSAlex Lorenz #include "llvm/Support/YAMLTraits.h" 25045c514fSAlex Lorenz #include "llvm/Support/raw_ostream.h" 26d768bf99SArchibald Elliott #include "llvm/TargetParser/Host.h" 27a1580d7bSKazu Hirata #include <optional> 28045c514fSAlex Lorenz 29045c514fSAlex Lorenz using namespace clang; 30045c514fSAlex Lorenz 31045c514fSAlex Lorenz namespace { 32045c514fSAlex Lorenz 33045c514fSAlex Lorenz struct UnsavedFileHash { 34045c514fSAlex Lorenz std::string Name; 35045c514fSAlex Lorenz std::string MD5; 36045c514fSAlex Lorenz }; 37045c514fSAlex Lorenz 38045c514fSAlex Lorenz struct ClangInvocationInfo { 39045c514fSAlex Lorenz std::string Toolchain; 40045c514fSAlex Lorenz std::string LibclangOperation; 41045c514fSAlex Lorenz std::string LibclangOptions; 42045c514fSAlex Lorenz std::vector<std::string> Arguments; 43045c514fSAlex Lorenz std::vector<std::string> InvocationArguments; 44045c514fSAlex Lorenz std::vector<UnsavedFileHash> UnsavedFileHashes; 45045c514fSAlex Lorenz bool Dump = false; 46045c514fSAlex Lorenz }; 47045c514fSAlex Lorenz 48045c514fSAlex Lorenz } // end anonymous namespace 49045c514fSAlex Lorenz 50045c514fSAlex Lorenz LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash) 51045c514fSAlex Lorenz 52045c514fSAlex Lorenz namespace llvm { 53045c514fSAlex Lorenz namespace yaml { 54045c514fSAlex Lorenz 55045c514fSAlex Lorenz template <> struct MappingTraits<UnsavedFileHash> { 56045c514fSAlex Lorenz static void mapping(IO &IO, UnsavedFileHash &Info) { 57045c514fSAlex Lorenz IO.mapRequired("name", Info.Name); 58045c514fSAlex Lorenz IO.mapRequired("md5", Info.MD5); 59045c514fSAlex Lorenz } 60045c514fSAlex Lorenz }; 61045c514fSAlex Lorenz 62045c514fSAlex Lorenz template <> struct MappingTraits<ClangInvocationInfo> { 63045c514fSAlex Lorenz static void mapping(IO &IO, ClangInvocationInfo &Info) { 64045c514fSAlex Lorenz IO.mapRequired("toolchain", Info.Toolchain); 65045c514fSAlex Lorenz IO.mapOptional("libclang.operation", Info.LibclangOperation); 66045c514fSAlex Lorenz IO.mapOptional("libclang.opts", Info.LibclangOptions); 67045c514fSAlex Lorenz IO.mapRequired("args", Info.Arguments); 68045c514fSAlex Lorenz IO.mapOptional("invocation-args", Info.InvocationArguments); 69045c514fSAlex Lorenz IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes); 70045c514fSAlex Lorenz } 71045c514fSAlex Lorenz }; 72045c514fSAlex Lorenz 73045c514fSAlex Lorenz } // end namespace yaml 74045c514fSAlex Lorenz } // end namespace llvm 75045c514fSAlex Lorenz 76045c514fSAlex Lorenz static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) { 77045c514fSAlex Lorenz std::string Result; 78045c514fSAlex Lorenz llvm::raw_string_ostream OS(Result); 79045c514fSAlex Lorenz OS << '{'; 80045c514fSAlex Lorenz bool NeedComma = false; 81045c514fSAlex Lorenz auto EmitKey = [&](StringRef Key) { 82045c514fSAlex Lorenz if (NeedComma) 83045c514fSAlex Lorenz OS << ", "; 84045c514fSAlex Lorenz NeedComma = true; 85045c514fSAlex Lorenz OS << '"' << Key << "\": "; 86045c514fSAlex Lorenz }; 87045c514fSAlex Lorenz auto EmitStringKey = [&](StringRef Key, StringRef Value) { 88045c514fSAlex Lorenz if (Value.empty()) 89045c514fSAlex Lorenz return; 90045c514fSAlex Lorenz EmitKey(Key); 91045c514fSAlex Lorenz OS << '"' << Value << '"'; 92045c514fSAlex Lorenz }; 93045c514fSAlex Lorenz EmitStringKey("libclang.operation", Info.LibclangOperation); 94045c514fSAlex Lorenz EmitStringKey("libclang.opts", Info.LibclangOptions); 95045c514fSAlex Lorenz if (!Info.InvocationArguments.empty()) { 96045c514fSAlex Lorenz EmitKey("invocation-args"); 97045c514fSAlex Lorenz OS << '['; 98045c514fSAlex Lorenz for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) { 99045c514fSAlex Lorenz if (Arg.index()) 100045c514fSAlex Lorenz OS << ','; 101045c514fSAlex Lorenz OS << '"' << Arg.value() << '"'; 102045c514fSAlex Lorenz } 103045c514fSAlex Lorenz OS << ']'; 104045c514fSAlex Lorenz } 105045c514fSAlex Lorenz OS << '}'; 106045c514fSAlex Lorenz // FIXME: Compare unsaved file hashes and report mismatch in the reproducer. 107045c514fSAlex Lorenz if (Info.Dump) 108918972bdSJOE1994 llvm::outs() << "REPRODUCER METAINFO: " << Result << "\n"; 109918972bdSJOE1994 return Result; 110045c514fSAlex Lorenz } 111045c514fSAlex Lorenz 112045c514fSAlex Lorenz /// Generates a reproducer for a set of arguments from a specific invocation. 1136ad0788cSKazu Hirata static std::optional<driver::Driver::CompilationDiagnosticReport> 114045c514fSAlex Lorenz generateReproducerForInvocationArguments(ArrayRef<const char *> Argv, 1153e57aa30SAlex Brachet const ClangInvocationInfo &Info, 1163e57aa30SAlex Brachet const llvm::ToolContext &ToolContext) { 117045c514fSAlex Lorenz using namespace driver; 118045c514fSAlex Lorenz auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]); 119045c514fSAlex Lorenz 120045c514fSAlex Lorenz IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions; 121045c514fSAlex Lorenz 122045c514fSAlex Lorenz IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 123045c514fSAlex Lorenz DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer()); 124*5845688eSKadir Cetinkaya auto VFS = llvm::vfs::getRealFileSystem(); 125*5845688eSKadir Cetinkaya ProcessWarningOptions(Diags, *DiagOpts, *VFS, /*ReportDiags=*/false); 126*5845688eSKadir Cetinkaya Driver TheDriver(ToolContext.Path, llvm::sys::getDefaultTargetTriple(), Diags, 127*5845688eSKadir Cetinkaya /*Title=*/"clang LLVM compiler", VFS); 128045c514fSAlex Lorenz TheDriver.setTargetAndMode(TargetAndMode); 1293e57aa30SAlex Brachet if (ToolContext.NeedsPrependArg) 1303e57aa30SAlex Brachet TheDriver.setPrependArg(ToolContext.PrependArg); 131045c514fSAlex Lorenz 132045c514fSAlex Lorenz std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv)); 133045c514fSAlex Lorenz if (C && !C->containsError()) { 134045c514fSAlex Lorenz for (const auto &J : C->getJobs()) { 135045c514fSAlex Lorenz if (const Command *Cmd = dyn_cast<Command>(&J)) { 136045c514fSAlex Lorenz Driver::CompilationDiagnosticReport Report; 137045c514fSAlex Lorenz TheDriver.generateCompilationDiagnostics( 138045c514fSAlex Lorenz *C, *Cmd, generateReproducerMetaInfo(Info), &Report); 139045c514fSAlex Lorenz return Report; 140045c514fSAlex Lorenz } 141045c514fSAlex Lorenz } 142045c514fSAlex Lorenz } 143045c514fSAlex Lorenz 1445891420eSKazu Hirata return std::nullopt; 145045c514fSAlex Lorenz } 146045c514fSAlex Lorenz 147045c514fSAlex Lorenz std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes); 148045c514fSAlex Lorenz 149045c514fSAlex Lorenz static void printReproducerInformation( 150045c514fSAlex Lorenz llvm::raw_ostream &OS, const ClangInvocationInfo &Info, 151045c514fSAlex Lorenz const driver::Driver::CompilationDiagnosticReport &Report) { 152045c514fSAlex Lorenz OS << "REPRODUCER:\n"; 153045c514fSAlex Lorenz OS << "{\n"; 154045c514fSAlex Lorenz OS << R"("files":[)"; 155045c514fSAlex Lorenz for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) { 156045c514fSAlex Lorenz if (File.index()) 157045c514fSAlex Lorenz OS << ','; 158045c514fSAlex Lorenz OS << '"' << File.value() << '"'; 159045c514fSAlex Lorenz } 160045c514fSAlex Lorenz OS << "]\n}\n"; 161045c514fSAlex Lorenz } 162045c514fSAlex Lorenz 163045c514fSAlex Lorenz int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0, 1643e57aa30SAlex Brachet void *MainAddr, 1653e57aa30SAlex Brachet const llvm::ToolContext &ToolContext) { 166045c514fSAlex Lorenz if (Argv.size() < 1) { 167045c514fSAlex Lorenz llvm::errs() << "error: missing invocation file\n"; 168045c514fSAlex Lorenz return 1; 169045c514fSAlex Lorenz } 170045c514fSAlex Lorenz // Parse the invocation descriptor. 171045c514fSAlex Lorenz StringRef Input = Argv[0]; 172045c514fSAlex Lorenz llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = 17305b4babcSAbhina Sreeskantharajan llvm::MemoryBuffer::getFile(Input, /*IsText=*/true); 174045c514fSAlex Lorenz if (!Buffer) { 175045c514fSAlex Lorenz llvm::errs() << "error: failed to read " << Input << ": " 176045c514fSAlex Lorenz << Buffer.getError().message() << "\n"; 177045c514fSAlex Lorenz return 1; 178045c514fSAlex Lorenz } 179045c514fSAlex Lorenz llvm::yaml::Input YAML(Buffer.get()->getBuffer()); 180045c514fSAlex Lorenz ClangInvocationInfo InvocationInfo; 181045c514fSAlex Lorenz YAML >> InvocationInfo; 182045c514fSAlex Lorenz if (Argv.size() > 1 && Argv[1] == StringRef("-v")) 183045c514fSAlex Lorenz InvocationInfo.Dump = true; 184045c514fSAlex Lorenz 185045c514fSAlex Lorenz // Create an invocation that will produce the reproducer. 186045c514fSAlex Lorenz std::vector<const char *> DriverArgs; 187045c514fSAlex Lorenz for (const auto &Arg : InvocationInfo.Arguments) 188045c514fSAlex Lorenz DriverArgs.push_back(Arg.c_str()); 189045c514fSAlex Lorenz std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true); 190045c514fSAlex Lorenz DriverArgs[0] = Path.c_str(); 1916ad0788cSKazu Hirata std::optional<driver::Driver::CompilationDiagnosticReport> Report = 1923e57aa30SAlex Brachet generateReproducerForInvocationArguments(DriverArgs, InvocationInfo, 1933e57aa30SAlex Brachet ToolContext); 194045c514fSAlex Lorenz 195045c514fSAlex Lorenz // Emit the information about the reproduce files to stdout. 196045c514fSAlex Lorenz int Result = 1; 197045c514fSAlex Lorenz if (Report) { 198045c514fSAlex Lorenz printReproducerInformation(llvm::outs(), InvocationInfo, *Report); 199045c514fSAlex Lorenz Result = 0; 200045c514fSAlex Lorenz } 201045c514fSAlex Lorenz 202045c514fSAlex Lorenz // Remove the input file. 203045c514fSAlex Lorenz llvm::sys::fs::remove(Input); 204045c514fSAlex Lorenz return Result; 205045c514fSAlex Lorenz } 206