1 //===-- Reproducer.cpp ------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Utility/Reproducer.h"
11 #include "lldb/Host/HostInfo.h"
12 
13 #include "llvm/Support/FileSystem.h"
14 #include "llvm/Support/Threading.h"
15 #include "llvm/Support/raw_ostream.h"
16 
17 using namespace lldb_private;
18 using namespace lldb_private::repro;
19 using namespace llvm;
20 using namespace llvm::yaml;
21 
22 Reproducer &Reproducer::Instance() {
23   static Reproducer g_reproducer;
24   return g_reproducer;
25 }
26 
27 const Generator *Reproducer::GetGenerator() const {
28   std::lock_guard<std::mutex> guard(m_mutex);
29   if (m_generate_reproducer)
30     return &m_generator;
31   return nullptr;
32 }
33 
34 const Loader *Reproducer::GetLoader() const {
35   std::lock_guard<std::mutex> guard(m_mutex);
36   if (m_replay_reproducer)
37     return &m_loader;
38   return nullptr;
39 }
40 
41 Generator *Reproducer::GetGenerator() {
42   std::lock_guard<std::mutex> guard(m_mutex);
43   if (m_generate_reproducer)
44     return &m_generator;
45   return nullptr;
46 }
47 
48 Loader *Reproducer::GetLoader() {
49   std::lock_guard<std::mutex> guard(m_mutex);
50   if (m_replay_reproducer)
51     return &m_loader;
52   return nullptr;
53 }
54 
55 llvm::Error Reproducer::SetGenerateReproducer(bool value) {
56   std::lock_guard<std::mutex> guard(m_mutex);
57 
58   if (value && m_replay_reproducer)
59     return make_error<StringError>(
60         "cannot generate a reproducer when replay one",
61         inconvertibleErrorCode());
62 
63   m_generate_reproducer = value;
64   m_generator.SetEnabled(value);
65 
66   return Error::success();
67 }
68 
69 llvm::Error Reproducer::SetReplayReproducer(bool value) {
70   std::lock_guard<std::mutex> guard(m_mutex);
71 
72   if (value && m_generate_reproducer)
73     return make_error<StringError>(
74         "cannot replay a reproducer when generating one",
75         inconvertibleErrorCode());
76 
77   m_replay_reproducer = value;
78 
79   return Error::success();
80 }
81 
82 llvm::Error Reproducer::SetReproducerPath(const FileSpec &path) {
83   // Setting the path implies using the reproducer.
84   if (auto e = SetReplayReproducer(true))
85     return e;
86 
87   // Tell the reproducer to load the index form the given path.
88   if (auto loader = GetLoader()) {
89     if (auto e = loader->LoadIndex(path))
90       return e;
91   }
92 
93   return make_error<StringError>("unable to get loader",
94                                  inconvertibleErrorCode());
95 }
96 
97 FileSpec Reproducer::GetReproducerPath() const {
98   if (auto g = GetGenerator())
99     return g->GetDirectory();
100   return {};
101 }
102 
103 Generator::Generator() : m_enabled(false), m_done(false) {
104   m_directory = HostInfo::GetReproducerTempDir();
105 }
106 
107 Generator::~Generator() {}
108 
109 Provider &Generator::Register(std::unique_ptr<Provider> provider) {
110   std::lock_guard<std::mutex> lock(m_providers_mutex);
111 
112   AddProviderToIndex(provider->GetInfo());
113 
114   m_providers.push_back(std::move(provider));
115   return *m_providers.back();
116 }
117 
118 void Generator::Keep() {
119   assert(!m_done);
120   m_done = true;
121 
122   if (!m_enabled)
123     return;
124 
125   for (auto &provider : m_providers)
126     provider->Keep();
127 }
128 
129 void Generator::Discard() {
130   assert(!m_done);
131   m_done = true;
132 
133   if (!m_enabled)
134     return;
135 
136   for (auto &provider : m_providers)
137     provider->Discard();
138 
139   llvm::sys::fs::remove_directories(m_directory.GetPath());
140 }
141 
142 void Generator::ChangeDirectory(const FileSpec &directory) {
143   assert(m_providers.empty() && "Changing the directory after providers have "
144                                 "been registered would invalidate the index.");
145   m_directory = directory;
146 }
147 
148 const FileSpec &Generator::GetDirectory() const { return m_directory; }
149 
150 void Generator::AddProviderToIndex(const ProviderInfo &provider_info) {
151   FileSpec index = m_directory;
152   index.AppendPathComponent("index.yaml");
153 
154   std::error_code EC;
155   auto strm = llvm::make_unique<raw_fd_ostream>(index.GetPath(), EC,
156                                                 sys::fs::OpenFlags::F_None);
157   yaml::Output yout(*strm);
158   yout << const_cast<ProviderInfo &>(provider_info);
159 }
160 
161 Loader::Loader() : m_loaded(false) {}
162 
163 llvm::Error Loader::LoadIndex(const FileSpec &directory) {
164   if (m_loaded)
165     return llvm::Error::success();
166 
167   FileSpec index = directory.CopyByAppendingPathComponent("index.yaml");
168 
169   auto error_or_file = MemoryBuffer::getFile(index.GetPath());
170   if (auto err = error_or_file.getError())
171     return errorCodeToError(err);
172 
173   std::vector<ProviderInfo> provider_info;
174   yaml::Input yin((*error_or_file)->getBuffer());
175   yin >> provider_info;
176 
177   if (auto err = yin.error())
178     return errorCodeToError(err);
179 
180   for (auto &info : provider_info)
181     m_provider_info[info.name] = info;
182 
183   m_directory = directory;
184   m_loaded = true;
185 
186   return llvm::Error::success();
187 }
188 
189 llvm::Optional<ProviderInfo> Loader::GetProviderInfo(StringRef name) {
190   assert(m_loaded);
191 
192   auto it = m_provider_info.find(name);
193   if (it == m_provider_info.end())
194     return llvm::None;
195 
196   return it->second;
197 }
198