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 llvm::append_range(C&: Args, R: ArrayRef(Argv, Argc));
40 if (!Root.empty())
41 FC = std::make_shared<FileCollector>(args&: Root, args&: Root);
42 VFS = FileCollector::createCollectorVFS(BaseFS: vfs::getRealFileSystem(), Collector: FC);
43}
44
45ReproducerGenerate::~ReproducerGenerate() {
46 if (GenerateOnExit && !Generated)
47 generate();
48 else if (!Generated && !Root.empty())
49 sys::fs::remove_directories(path: Root, /* IgnoreErrors */ true);
50}
51
52void ReproducerGenerate::generate() {
53 if (!FC)
54 return;
55 Generated = true;
56 FC->copyFiles(StopOnError: false);
57 SmallString<128> Mapping(Root);
58 sys::path::append(path&: Mapping, a: "mapping.yaml");
59 FC->writeMapping(MappingFile: Mapping.str());
60 errs() << "********************\n";
61 errs() << "Reproducer written to " << Root << '\n';
62 errs() << "Please include the reproducer and the following invocation in "
63 "your bug report:\n";
64 for (llvm::StringRef Arg : Args)
65 errs() << Arg << ' ';
66 errs() << "--use-reproducer " << Root << '\n';
67 errs() << "********************\n";
68}
69
70ReproducerUse::~ReproducerUse() = default;
71
72ReproducerUse::ReproducerUse(StringRef Root, std::error_code &EC) {
73 SmallString<128> Mapping(Root);
74 sys::path::append(path&: Mapping, a: "mapping.yaml");
75 ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
76 vfs::getRealFileSystem()->getBufferForFile(Name: Mapping.str());
77
78 if (!Buffer) {
79 EC = Buffer.getError();
80 return;
81 }
82
83 VFS = llvm::vfs::getVFSFromYAML(Buffer: std::move(Buffer.get()), DiagHandler: nullptr, YAMLFilePath: Mapping);
84}
85
86llvm::Expected<std::unique_ptr<Reproducer>>
87Reproducer::createReproducer(ReproducerMode Mode, StringRef Root, int Argc,
88 char **Argv) {
89
90 std::error_code EC;
91 std::unique_ptr<Reproducer> Repro;
92 switch (Mode) {
93 case ReproducerMode::GenerateOnExit:
94 Repro = std::make_unique<ReproducerGenerate>(args&: EC, args&: Argc, args&: Argv, args: true);
95 break;
96 case ReproducerMode::GenerateOnCrash:
97 Repro = std::make_unique<ReproducerGenerate>(args&: EC, args&: Argc, args&: Argv, args: false);
98 break;
99 case ReproducerMode::Use:
100 Repro = std::make_unique<ReproducerUse>(args&: Root, args&: EC);
101 break;
102 case ReproducerMode::Off:
103 Repro = std::make_unique<Reproducer>();
104 break;
105 }
106 if (EC)
107 return errorCodeToError(EC);
108 return {std::move(Repro)};
109}
110