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 "lldb/Utility/Reproducer.h" 10 #include "lldb/Utility/LLDBAssert.h" 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 static llvm::Optional<bool> GetEnv(const char *var) { 22 std::string val = llvm::StringRef(getenv(var)).lower(); 23 if (val == "0" || val == "off") 24 return false; 25 if (val == "1" || val == "on") 26 return true; 27 return {}; 28 } 29 30 Reproducer &Reproducer::Instance() { return *InstanceImpl(); } 31 32 llvm::Error Reproducer::Initialize(ReproducerMode mode, 33 llvm::Optional<FileSpec> root) { 34 lldbassert(!InstanceImpl() && "Already initialized."); 35 InstanceImpl().emplace(); 36 37 // The environment can override the capture mode. 38 if (mode != ReproducerMode::Replay) { 39 if (llvm::Optional<bool> override = GetEnv("LLDB_CAPTURE_REPRODUCER")) { 40 if (*override) 41 mode = ReproducerMode::Capture; 42 else 43 mode = ReproducerMode::Off; 44 } 45 } 46 47 switch (mode) { 48 case ReproducerMode::Capture: { 49 if (!root) { 50 SmallString<128> repro_dir; 51 auto ec = sys::fs::createUniqueDirectory("reproducer", repro_dir); 52 if (ec) 53 return make_error<StringError>( 54 "unable to create unique reproducer directory", ec); 55 root.emplace(repro_dir); 56 } else { 57 auto ec = sys::fs::create_directory(root->GetPath()); 58 if (ec) 59 return make_error<StringError>("unable to create reproducer directory", 60 ec); 61 } 62 return Instance().SetCapture(root); 63 } break; 64 case ReproducerMode::Replay: 65 return Instance().SetReplay(root, /*passive*/ false); 66 case ReproducerMode::PassiveReplay: 67 return Instance().SetReplay(root, /*passive*/ true); 68 case ReproducerMode::Off: 69 break; 70 }; 71 72 return Error::success(); 73 } 74 75 bool Reproducer::Initialized() { return InstanceImpl().operator bool(); } 76 77 void Reproducer::Terminate() { 78 lldbassert(InstanceImpl() && "Already terminated."); 79 InstanceImpl().reset(); 80 } 81 82 Optional<Reproducer> &Reproducer::InstanceImpl() { 83 static Optional<Reproducer> g_reproducer; 84 return g_reproducer; 85 } 86 87 const Generator *Reproducer::GetGenerator() const { 88 std::lock_guard<std::mutex> guard(m_mutex); 89 if (m_generator) 90 return &(*m_generator); 91 return nullptr; 92 } 93 94 const Loader *Reproducer::GetLoader() const { 95 std::lock_guard<std::mutex> guard(m_mutex); 96 if (m_loader) 97 return &(*m_loader); 98 return nullptr; 99 } 100 101 Generator *Reproducer::GetGenerator() { 102 std::lock_guard<std::mutex> guard(m_mutex); 103 if (m_generator) 104 return &(*m_generator); 105 return nullptr; 106 } 107 108 Loader *Reproducer::GetLoader() { 109 std::lock_guard<std::mutex> guard(m_mutex); 110 if (m_loader) 111 return &(*m_loader); 112 return nullptr; 113 } 114 115 llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) { 116 std::lock_guard<std::mutex> guard(m_mutex); 117 118 if (root && m_loader) 119 return make_error<StringError>( 120 "cannot generate a reproducer when replay one", 121 inconvertibleErrorCode()); 122 123 if (!root) { 124 m_generator.reset(); 125 return Error::success(); 126 } 127 128 m_generator.emplace(*root); 129 return Error::success(); 130 } 131 132 llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root, bool passive) { 133 std::lock_guard<std::mutex> guard(m_mutex); 134 135 if (root && m_generator) 136 return make_error<StringError>( 137 "cannot replay a reproducer when generating one", 138 inconvertibleErrorCode()); 139 140 if (!root) { 141 m_loader.reset(); 142 return Error::success(); 143 } 144 145 m_loader.emplace(*root, passive); 146 if (auto e = m_loader->LoadIndex()) 147 return e; 148 149 return Error::success(); 150 } 151 152 FileSpec Reproducer::GetReproducerPath() const { 153 if (auto g = GetGenerator()) 154 return g->GetRoot(); 155 if (auto l = GetLoader()) 156 return l->GetRoot(); 157 return {}; 158 } 159 160 static FileSpec MakeAbsolute(const FileSpec &file_spec) { 161 SmallString<128> path; 162 file_spec.GetPath(path, false); 163 llvm::sys::fs::make_absolute(path); 164 return FileSpec(path, file_spec.GetPathStyle()); 165 } 166 167 Generator::Generator(FileSpec root) : m_root(MakeAbsolute(std::move(root))) { 168 GetOrCreate<repro::WorkingDirectoryProvider>(); 169 GetOrCreate<repro::HomeDirectoryProvider>(); 170 } 171 172 Generator::~Generator() { 173 if (!m_done) { 174 if (m_auto_generate) 175 Keep(); 176 else 177 Discard(); 178 } 179 } 180 181 ProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) { 182 std::lock_guard<std::mutex> lock(m_providers_mutex); 183 std::pair<const void *, std::unique_ptr<ProviderBase>> key_value( 184 provider->DynamicClassID(), std::move(provider)); 185 auto e = m_providers.insert(std::move(key_value)); 186 return e.first->getSecond().get(); 187 } 188 189 void Generator::Keep() { 190 assert(!m_done); 191 m_done = true; 192 193 for (auto &provider : m_providers) 194 provider.second->Keep(); 195 196 AddProvidersToIndex(); 197 } 198 199 void Generator::Discard() { 200 assert(!m_done); 201 m_done = true; 202 203 for (auto &provider : m_providers) 204 provider.second->Discard(); 205 206 llvm::sys::fs::remove_directories(m_root.GetPath()); 207 } 208 209 void Generator::SetAutoGenerate(bool b) { m_auto_generate = b; } 210 211 bool Generator::IsAutoGenerate() const { return m_auto_generate; } 212 213 const FileSpec &Generator::GetRoot() const { return m_root; } 214 215 void Generator::AddProvidersToIndex() { 216 FileSpec index = m_root; 217 index.AppendPathComponent("index.yaml"); 218 219 std::error_code EC; 220 auto strm = std::make_unique<raw_fd_ostream>(index.GetPath(), EC, 221 sys::fs::OpenFlags::OF_None); 222 yaml::Output yout(*strm); 223 224 std::vector<std::string> files; 225 files.reserve(m_providers.size()); 226 for (auto &provider : m_providers) { 227 files.emplace_back(provider.second->GetFile()); 228 } 229 230 yout << files; 231 } 232 233 Loader::Loader(FileSpec root, bool passive) 234 : m_root(MakeAbsolute(std::move(root))), m_loaded(false), 235 m_passive_replay(passive) {} 236 237 llvm::Error Loader::LoadIndex() { 238 if (m_loaded) 239 return llvm::Error::success(); 240 241 FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml"); 242 243 auto error_or_file = MemoryBuffer::getFile(index.GetPath()); 244 if (auto err = error_or_file.getError()) 245 return make_error<StringError>("unable to load reproducer index", err); 246 247 yaml::Input yin((*error_or_file)->getBuffer()); 248 yin >> m_files; 249 if (auto err = yin.error()) 250 return make_error<StringError>("unable to read reproducer index", err); 251 252 // Sort files to speed up search. 253 llvm::sort(m_files); 254 255 // Remember that we've loaded the index. 256 m_loaded = true; 257 258 return llvm::Error::success(); 259 } 260 261 bool Loader::HasFile(StringRef file) { 262 assert(m_loaded); 263 auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str()); 264 return (it != m_files.end()) && (*it == file); 265 } 266 267 llvm::Expected<std::unique_ptr<DataRecorder>> 268 DataRecorder::Create(const FileSpec &filename) { 269 std::error_code ec; 270 auto recorder = std::make_unique<DataRecorder>(std::move(filename), ec); 271 if (ec) 272 return llvm::errorCodeToError(ec); 273 return std::move(recorder); 274 } 275 276 llvm::Expected<std::unique_ptr<YamlRecorder>> 277 YamlRecorder::Create(const FileSpec &filename) { 278 std::error_code ec; 279 auto recorder = std::make_unique<YamlRecorder>(std::move(filename), ec); 280 if (ec) 281 return llvm::errorCodeToError(ec); 282 return std::move(recorder); 283 } 284 285 void VersionProvider::Keep() { 286 FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); 287 std::error_code ec; 288 llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); 289 if (ec) 290 return; 291 os << m_version << "\n"; 292 } 293 294 void FileProvider::RecordInterestingDirectory(const llvm::Twine &dir) { 295 if (m_collector) 296 m_collector->addFile(dir); 297 } 298 299 void FileProvider::RecordInterestingDirectoryRecursive(const llvm::Twine &dir) { 300 if (m_collector) 301 m_collector->addDirectory(dir); 302 } 303 304 void ProviderBase::anchor() {} 305 char CommandProvider::ID = 0; 306 char FileProvider::ID = 0; 307 char ProviderBase::ID = 0; 308 char VersionProvider::ID = 0; 309 char WorkingDirectoryProvider::ID = 0; 310 char HomeDirectoryProvider::ID = 0; 311 const char *CommandProvider::Info::file = "command-interpreter.yaml"; 312 const char *CommandProvider::Info::name = "command-interpreter"; 313 const char *FileProvider::Info::file = "files.yaml"; 314 const char *FileProvider::Info::name = "files"; 315 const char *VersionProvider::Info::file = "version.txt"; 316 const char *VersionProvider::Info::name = "version"; 317 const char *WorkingDirectoryProvider::Info::file = "cwd.txt"; 318 const char *WorkingDirectoryProvider::Info::name = "cwd"; 319 const char *HomeDirectoryProvider::Info::file = "home.txt"; 320 const char *HomeDirectoryProvider::Info::name = "home"; 321