LLDB  mainline
Reproducer.cpp
Go to the documentation of this file.
1 //===-- Reproducer.cpp ------------------------------------------*- C++ -*-===//
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 
11 
12 #include "llvm/Support/FileSystem.h"
13 #include "llvm/Support/Threading.h"
14 #include "llvm/Support/raw_ostream.h"
15 
16 using namespace lldb_private;
17 using namespace lldb_private::repro;
18 using namespace llvm;
19 using namespace llvm::yaml;
20 
21 Reproducer &Reproducer::Instance() { return *InstanceImpl(); }
22 
24  llvm::Optional<FileSpec> root) {
25  lldbassert(!InstanceImpl() && "Already initialized.");
26  InstanceImpl().emplace();
27 
28  switch (mode) {
30  if (!root) {
31  SmallString<128> repro_dir;
32  auto ec = sys::fs::createUniqueDirectory("reproducer", repro_dir);
33  if (ec)
34  return make_error<StringError>(
35  "unable to create unique reproducer directory", ec);
36  root.emplace(repro_dir);
37  } else {
38  auto ec = sys::fs::create_directory(root->GetPath());
39  if (ec)
40  return make_error<StringError>("unable to create reproducer directory",
41  ec);
42  }
43  return Instance().SetCapture(root);
44  } break;
46  return Instance().SetReplay(root);
48  break;
49  };
50 
51  return Error::success();
52 }
53 
54 bool Reproducer::Initialized() { return InstanceImpl().operator bool(); }
55 
57  lldbassert(InstanceImpl() && "Already terminated.");
58  InstanceImpl().reset();
59 }
60 
61 Optional<Reproducer> &Reproducer::InstanceImpl() {
62  static Optional<Reproducer> g_reproducer;
63  return g_reproducer;
64 }
65 
67  std::lock_guard<std::mutex> guard(m_mutex);
68  if (m_generator)
69  return &(*m_generator);
70  return nullptr;
71 }
72 
73 const Loader *Reproducer::GetLoader() const {
74  std::lock_guard<std::mutex> guard(m_mutex);
75  if (m_loader)
76  return &(*m_loader);
77  return nullptr;
78 }
79 
81  std::lock_guard<std::mutex> guard(m_mutex);
82  if (m_generator)
83  return &(*m_generator);
84  return nullptr;
85 }
86 
88  std::lock_guard<std::mutex> guard(m_mutex);
89  if (m_loader)
90  return &(*m_loader);
91  return nullptr;
92 }
93 
94 llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) {
95  std::lock_guard<std::mutex> guard(m_mutex);
96 
97  if (root && m_loader)
98  return make_error<StringError>(
99  "cannot generate a reproducer when replay one",
100  inconvertibleErrorCode());
101 
102  if (!root) {
103  m_generator.reset();
104  return Error::success();
105  }
106 
107  m_generator.emplace(*root);
108  return Error::success();
109 }
110 
111 llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) {
112  std::lock_guard<std::mutex> guard(m_mutex);
113 
114  if (root && m_generator)
115  return make_error<StringError>(
116  "cannot replay a reproducer when generating one",
117  inconvertibleErrorCode());
118 
119  if (!root) {
120  m_loader.reset();
121  return Error::success();
122  }
123 
124  m_loader.emplace(*root);
125  if (auto e = m_loader->LoadIndex())
126  return e;
127 
128  return Error::success();
129 }
130 
132  if (auto g = GetGenerator())
133  return g->GetRoot();
134  if (auto l = GetLoader())
135  return l->GetRoot();
136  return {};
137 }
138 
139 Generator::Generator(const FileSpec &root) : m_root(root), m_done(false) {}
140 
142 
143 ProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) {
144  std::lock_guard<std::mutex> lock(m_providers_mutex);
145  std::pair<const void *, std::unique_ptr<ProviderBase>> key_value(
146  provider->DynamicClassID(), std::move(provider));
147  auto e = m_providers.insert(std::move(key_value));
148  return e.first->getSecond().get();
149 }
150 
152  assert(!m_done);
153  m_done = true;
154 
155  for (auto &provider : m_providers)
156  provider.second->Keep();
157 
158  AddProvidersToIndex();
159 }
160 
162  assert(!m_done);
163  m_done = true;
164 
165  for (auto &provider : m_providers)
166  provider.second->Discard();
167 
168  llvm::sys::fs::remove_directories(m_root.GetPath());
169 }
170 
171 const FileSpec &Generator::GetRoot() const { return m_root; }
172 
173 void Generator::AddProvidersToIndex() {
174  FileSpec index = m_root;
175  index.AppendPathComponent("index.yaml");
176 
177  std::error_code EC;
178  auto strm = llvm::make_unique<raw_fd_ostream>(index.GetPath(), EC,
179  sys::fs::OpenFlags::F_None);
180  yaml::Output yout(*strm);
181 
182  std::vector<std::string> files;
183  files.reserve(m_providers.size());
184  for (auto &provider : m_providers) {
185  files.emplace_back(provider.second->GetFile());
186  }
187 
188  yout << files;
189 }
190 
191 Loader::Loader(const FileSpec &root) : m_root(root), m_loaded(false) {}
192 
194  if (m_loaded)
195  return llvm::Error::success();
196 
197  FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml");
198 
199  auto error_or_file = MemoryBuffer::getFile(index.GetPath());
200  if (auto err = error_or_file.getError())
201  return make_error<StringError>("unable to load reproducer index", err);
202 
203  yaml::Input yin((*error_or_file)->getBuffer());
204  yin >> m_files;
205  if (auto err = yin.error())
206  return make_error<StringError>("unable to read reproducer index", err);
207 
208  // Sort files to speed up search.
209  llvm::sort(m_files);
210 
211  // Remember that we've loaded the index.
212  m_loaded = true;
213 
214  return llvm::Error::success();
215 }
216 
217 bool Loader::HasFile(StringRef file) {
218  assert(m_loaded);
219  auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str());
220  return (it != m_files.end()) && (*it == file);
221 }
222 
223 llvm::Expected<std::unique_ptr<DataRecorder>>
225  std::error_code ec;
226  auto recorder = llvm::make_unique<DataRecorder>(std::move(filename), ec);
227  if (ec)
228  return llvm::errorCodeToError(ec);
229  return std::move(recorder);
230 }
231 
233  std::size_t i = m_data_recorders.size() + 1;
234  std::string filename = (llvm::Twine(info::name) + llvm::Twine("-") +
235  llvm::Twine(i) + llvm::Twine(".txt"))
236  .str();
237  auto recorder_or_error =
238  DataRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename));
239  if (!recorder_or_error) {
240  llvm::consumeError(recorder_or_error.takeError());
241  return nullptr;
242  }
243 
244  m_data_recorders.push_back(std::move(*recorder_or_error));
245  return m_data_recorders.back().get();
246 }
247 
249  std::vector<std::string> files;
250  for (auto &recorder : m_data_recorders) {
251  recorder->Stop();
252  files.push_back(recorder->GetFilename().GetPath());
253  }
254 
255  FileSpec file = GetRoot().CopyByAppendingPathComponent(info::file);
256  std::error_code ec;
257  llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::F_Text);
258  if (ec)
259  return;
260  yaml::Output yout(os);
261  yout << files;
262 }
263 
264 void CommandProvider::Discard() { m_data_recorders.clear(); }
265 
266 void ProviderBase::anchor() {}
267 char ProviderBase::ID = 0;
268 char FileProvider::ID = 0;
269 char CommandProvider::ID = 0;
270 const char *FileInfo::name = "files";
271 const char *FileInfo::file = "files.yaml";
272 const char *CommandInfo::name = "command-interpreter";
273 const char *CommandInfo::file = "command-interpreter.yaml";
void Keep() override
The Keep method is called when it is decided that we need to keep the data in order to provide a repr...
Definition: Reproducer.cpp:248
Enumerations for broadcasting.
Definition: SBLaunchInfo.h:14
Definition: Debugger.h:71
void Discard() override
The Discard method is called when it is decided that we do not need to keep any information and will ...
Definition: Reproducer.cpp:264
#define lldbassert(x)
Definition: LLDBAssert.h:15
const FileSpec & GetRoot() const
Definition: Reproducer.h:239
llvm::Error Error
static const char * file
Definition: Reproducer.h:87
const FileSpec & GetRoot() const
Definition: Reproducer.cpp:171
void Discard()
Method to indicate we do not want to keep the reproducer.
Definition: Reproducer.cpp:161
A file utility class.
Definition: FileSpec.h:55
llvm::Error SetReplay(llvm::Optional< FileSpec > root)
Definition: Reproducer.cpp:111
The reproducer enables clients to obtain access to the Generator and Loader.
Definition: Reproducer.h:251
llvm::Error SetCapture(llvm::Optional< FileSpec > root)
Definition: Reproducer.cpp:94
Generator(const FileSpec &root)
Definition: Reproducer.cpp:139
static llvm::Error Initialize(ReproducerMode mode, llvm::Optional< FileSpec > root)
Definition: Reproducer.cpp:23
FileSpec CopyByAppendingPathComponent(llvm::StringRef component) const
Definition: FileSpec.cpp:428
static const char * name
Definition: Reproducer.h:145
void AppendPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:463
static const char * file
Definition: Reproducer.h:146
static llvm::Expected< std::unique_ptr< DataRecorder > > Create(FileSpec filename)
Definition: Reproducer.cpp:224
void Keep()
Method to indicate we want to keep the reproducer.
Definition: Reproducer.cpp:151
static Reproducer & Instance()
Definition: Reproducer.cpp:21
The provider defines an interface for generating files needed for reproducing.
Definition: Reproducer.h:38
static const char * name
Definition: Reproducer.h:86
The generator is responsible for the logic needed to generate a reproducer.
Definition: Reproducer.h:169
Loader(const FileSpec &root)
Definition: Reproducer.cpp:191
FileSpec GetReproducerPath() const
Definition: Reproducer.cpp:131
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:376