1//===- Reproducer.cpp -----------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Reproducer.h"
10#include "llvm/Support/Path.h"
11#include "llvm/Support/Process.h"
12
13using namespace llvm;
14using namespace llvm::dsymutil;
15
16static std::string createReproducerDir(std::error_code &EC) {
17 SmallString<128> Root;
18 if (const char *Path = getenv(name: "DSYMUTIL_REPRODUCER_PATH")) {
19 Root.assign(RHS: Path);
20 EC = sys::fs::create_directories(path: Root);
21 } else if (const char *Path = getenv(name: "LLVM_DIAGNOSTIC_DIR")) {
22 Root.assign(RHS: Path);
23 llvm::sys::path::append(
24 path&: Root, a: "dsymutil-" + llvm::Twine(llvm::sys::Process::getProcessId()));
25 EC = sys::fs::create_directories(path: Root);
26 } else {
27 EC = sys::fs::createUniqueDirectory(Prefix: "dsymutil", ResultPath&: Root);
28 }
29 sys::fs::make_absolute(path&: Root);
30 return EC ? "" : std::string(Root);
31}
32
33Reproducer::Reproducer() : VFS(vfs::getRealFileSystem()) {}
34Reproducer::~Reproducer() = default;
35
36ReproducerGenerate::ReproducerGenerate(std::error_code &EC, int Argc,
37 char **Argv, bool GenerateOnExit)
38 : Root(createReproducerDir(EC)), GenerateOnExit(GenerateOnExit) {
39 for (int I = 0; I < Argc; ++I)
40 Args.push_back(Elt: Argv[I]);
41 if (!Root.empty())
42 FC = std::make_shared<FileCollector>(args&: Root, args&: Root);
43 VFS = FileCollector::createCollectorVFS(BaseFS: vfs::getRealFileSystem(), Collector: FC);
44}
45
46ReproducerGenerate::~ReproducerGenerate() {
47 if (GenerateOnExit && !Generated)
48 generate();
49 else if (!Generated && !Root.empty())
50 sys::fs::remove_directories(path: Root, /* IgnoreErrors */ true);
51}
52
53void ReproducerGenerate::generate() {
54 if (!FC)
55 return;
56 Generated = true;
57 FC->copyFiles(StopOnError: false);
58 SmallString<128> Mapping(Root);
59 sys::path::append(path&: Mapping, a: "mapping.yaml");
60 FC->writeMapping(MappingFile: Mapping.str());
61 errs() << "********************\n";
62 errs() << "Reproducer written to " << Root << '\n';
63 errs() << "Please include the reproducer and the following invocation in "
64 "your bug report:\n";
65 for (llvm::StringRef Arg : Args)
66 errs() << Arg << ' ';
67 errs() << "--use-reproducer " << Root << '\n';
68 errs() << "********************\n";
69}
70
71ReproducerUse::~ReproducerUse() = default;
72
73ReproducerUse::ReproducerUse(StringRef Root, std::error_code &EC) {
74 SmallString<128> Mapping(Root);
75 sys::path::append(path&: Mapping, a: "mapping.yaml");
76 ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
77 vfs::getRealFileSystem()->getBufferForFile(Name: Mapping.str());
78
79 if (!Buffer) {
80 EC = Buffer.getError();
81 return;
82 }
83
84 VFS = llvm::vfs::getVFSFromYAML(Buffer: std::move(Buffer.get()), DiagHandler: nullptr, YAMLFilePath: Mapping);
85}
86
87llvm::Expected<std::unique_ptr<Reproducer>>
88Reproducer::createReproducer(ReproducerMode Mode, StringRef Root, int Argc,
89 char **Argv) {
90
91 std::error_code EC;
92 std::unique_ptr<Reproducer> Repro;
93 switch (Mode) {
94 case ReproducerMode::GenerateOnExit:
95 Repro = std::make_unique<ReproducerGenerate>(args&: EC, args&: Argc, args&: Argv, args: true);
96 break;
97 case ReproducerMode::GenerateOnCrash:
98 Repro = std::make_unique<ReproducerGenerate>(args&: EC, args&: Argc, args&: Argv, args: false);
99 break;
100 case ReproducerMode::Use:
101 Repro = std::make_unique<ReproducerUse>(args&: Root, args&: EC);
102 break;
103 case ReproducerMode::Off:
104 Repro = std::make_unique<Reproducer>();
105 break;
106 }
107 if (EC)
108 return errorCodeToError(EC);
109 return {std::move(Repro)};
110}
111