Index: lldb/include/lldb/API/SBReproducer.h =================================================================== --- lldb/include/lldb/API/SBReproducer.h +++ lldb/include/lldb/API/SBReproducer.h @@ -48,6 +48,7 @@ static const char *Replay(const char *path, bool skip_version_check); static const char *Replay(const char *path, const SBReplayOptions &options); static const char *PassiveReplay(const char *path); + static const char *Finalize(const char *path); static const char *GetPath(); static bool SetAutoGenerate(bool b); static bool Generate(); Index: lldb/include/lldb/Utility/Reproducer.h =================================================================== --- lldb/include/lldb/Utility/Reproducer.h +++ lldb/include/lldb/Utility/Reproducer.h @@ -238,6 +238,15 @@ Loader *m_loader; }; +class Finalizer { +public: + Finalizer(Loader *loader) : m_loader(loader) {} + llvm::Error Finalize() const; + +private: + Loader *m_loader; +}; + struct ReplayOptions { bool verify = true; bool check_version = true; Index: lldb/include/lldb/Utility/ReproducerProvider.h =================================================================== --- lldb/include/lldb/Utility/ReproducerProvider.h +++ lldb/include/lldb/Utility/ReproducerProvider.h @@ -101,20 +101,26 @@ : Provider(directory), m_collector(std::make_shared( directory.CopyByAppendingPathComponent("root").GetPath(), - directory.GetPath())) {} + directory.GetPath())), + m_copy_in_process(false) {} std::shared_ptr GetFileCollector() { return m_collector; } + void SetCopyInProcess(bool b) { m_copy_in_process = b; } + void RecordInterestingDirectory(const llvm::Twine &dir); void RecordInterestingDirectoryRecursive(const llvm::Twine &dir); void Keep() override { auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file); - // Temporary files that are removed during execution can cause copy errors. - if (auto ec = m_collector->copyFiles(/*stop_on_error=*/false)) - return; + if (m_copy_in_process) { + // Temporary files that are removed during execution can cause copy + // errors. + if (auto ec = m_collector->copyFiles(/*stop_on_error=*/false)) + return; + } m_collector->writeMapping(mapping.GetPath()); } @@ -122,6 +128,7 @@ private: std::shared_ptr m_collector; + bool m_copy_in_process; }; /// Provider for the LLDB version number. Index: lldb/source/API/SBReproducer.cpp =================================================================== --- lldb/source/API/SBReproducer.cpp +++ lldb/source/API/SBReproducer.cpp @@ -266,6 +266,28 @@ return nullptr; } +const char *SBReproducer::Finalize(const char *path) { + static std::string error; + if (auto e = Reproducer::Initialize(ReproducerMode::Replay, FileSpec(path))) { + error = llvm::toString(std::move(e)); + return error.c_str(); + } + + repro::Loader *loader = repro::Reproducer::Instance().GetLoader(); + if (!loader) { + error = "unable to get replay loader."; + return error.c_str(); + } + + Finalizer finalizer(loader); + if (auto e = finalizer.Finalize()) { + error = llvm::toString(std::move(e)); + return error.c_str(); + } + + return nullptr; +} + bool SBReproducer::Generate() { auto &r = Reproducer::Instance(); if (auto generator = r.GetGenerator()) { @@ -285,10 +307,11 @@ } const char *SBReproducer::GetPath() { - static std::string path; + ConstString path; auto &r = Reproducer::Instance(); - path = r.GetReproducerPath().GetCString(); - return path.c_str(); + if (FileSpec reproducer_path = Reproducer::Instance().GetReproducerPath()) + path = ConstString(r.GetReproducerPath().GetCString()); + return path.GetCString(); } void SBReproducer::SetWorkingDirectory(const char *path) { Index: lldb/source/Commands/CommandObjectReproducer.cpp =================================================================== --- lldb/source/Commands/CommandObjectReproducer.cpp +++ lldb/source/Commands/CommandObjectReproducer.cpp @@ -191,6 +191,7 @@ auto &r = Reproducer::Instance(); if (auto generator = r.GetGenerator()) { + generator->GetOrCreate().SetCopyInProcess(true); generator->Keep(); } else if (r.IsReplaying()) { // Make this operation a NO-OP in replay mode. Index: lldb/source/Utility/Reproducer.cpp =================================================================== --- lldb/source/Utility/Reproducer.cpp +++ lldb/source/Utility/Reproducer.cpp @@ -176,10 +176,12 @@ Generator::~Generator() { if (!m_done) { - if (m_auto_generate) + if (m_auto_generate) { + GetOrCreate().SetCopyInProcess(true); Keep(); - else + } else { Discard(); + } } } @@ -359,3 +361,58 @@ } } } + +llvm::Error Finalizer::Finalize() const { + FileSpec vfs_mapping = m_loader->GetFile(); + ErrorOr> buffer = + vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath()); + if (!buffer) + return errorCodeToError(buffer.getError()); + + IntrusiveRefCntPtr vfs = vfs::getVFSFromYAML( + std::move(buffer.get()), nullptr, vfs_mapping.GetPath()); + if (!vfs) + return make_error( + "unable to initialize the virtual file system", + inconvertibleErrorCode()); + + Expected working_dir = + GetDirectoryFrom(m_loader); + if (!working_dir) + return working_dir.takeError(); + + if (!vfs->exists(*working_dir)) + return make_error( + "working directory not in the virtual file system", + inconvertibleErrorCode()); + + vfs->setCurrentWorkingDirectory(*working_dir); + + auto &redirecting_vfs = static_cast(*vfs); + redirecting_vfs.setFallthrough(false); + + FileSpec reproducer_root = m_loader->GetRoot(); + + FileCollector collector( + reproducer_root.CopyByAppendingPathComponent("root").GetPath(), + reproducer_root.GetPath()); + + std::vector roots = redirecting_vfs.getRoots(); + for (llvm::StringRef root : roots) { + std::error_code ec; + vfs::recursive_directory_iterator iter(*vfs, root, ec); + vfs::recursive_directory_iterator end; + for (; iter != end && !ec; iter.increment(ec)) { + StringRef path = iter->path(); + collector.addFile(path); + } + } + + FileSpec mapping = + reproducer_root.CopyByAppendingPathComponent(FileProvider::Info::file); + if (auto ec = collector.copyFiles(/*stop_on_error=*/false)) + return errorCodeToError(ec); + collector.writeMapping(mapping.GetPath()); + + return llvm::Error::success(); +} Index: lldb/test/Shell/Reproducer/TestCaptureEnvOverride.test =================================================================== --- lldb/test/Shell/Reproducer/TestCaptureEnvOverride.test +++ lldb/test/Shell/Reproducer/TestCaptureEnvOverride.test @@ -9,8 +9,6 @@ # RUN: LLDB_CAPTURE_REPRODUCER=0 %lldb -b -o 'reproducer status' --capture --capture-path %t.repro | FileCheck %s --check-prefix OFF # RUN: LLDB_CAPTURE_REPRODUCER=0 %lldb -b -o 'reproducer status' --capture | FileCheck %s --check-prefix OFF -# RUN: LLDB_CAPTURE_REPRODUCER=OFF %lldb -b -o 'reproducer status' --capture --capture-path %t.repro | FileCheck %s --check-prefix OFF -# RUN: LLDB_CAPTURE_REPRODUCER=off %lldb -b -o 'reproducer status' --capture | FileCheck %s --check-prefix OFF # RUN: LLDB_CAPTURE_REPRODUCER=bogus %lldb -b -o 'reproducer status' --capture | FileCheck %s --check-prefix CAPTURE # RUN: LLDB_CAPTURE_REPRODUCER=bogus %lldb -b -o 'reproducer status' | FileCheck %s --check-prefix OFF Index: lldb/tools/driver/Driver.cpp =================================================================== --- lldb/tools/driver/Driver.cpp +++ lldb/tools/driver/Driver.cpp @@ -729,25 +729,10 @@ signal(signo, sigcont_handler); } -void reproducer_handler(void *argv0) { +void reproducer_handler(void *cmd) { if (SBReproducer::Generate()) { - auto exe = static_cast(argv0); - llvm::outs() << "********************\n"; - llvm::outs() << "Crash reproducer for "; - llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n'; - llvm::outs() << '\n'; - llvm::outs() << "Reproducer written to '" << SBReproducer::GetPath() - << "'\n"; - llvm::outs() << '\n'; - llvm::outs() << "Before attaching the reproducer to a bug report:\n"; - llvm::outs() << " - Look at the directory to ensure you're willing to " - "share its content.\n"; - llvm::outs() - << " - Make sure the reproducer works by replaying the reproducer.\n"; - llvm::outs() << '\n'; - llvm::outs() << "Replay the reproducer with the following command:\n"; - llvm::outs() << exe << " -replay " << SBReproducer::GetPath() << "\n"; - llvm::outs() << "********************\n"; + std::system(static_cast(cmd)); + fflush(stdout); } } @@ -799,6 +784,31 @@ llvm::Optional InitializeReproducer(llvm::StringRef argv0, opt::InputArgList &input_args) { + if (auto *finalize_path = input_args.getLastArg(OPT_reproducer_finalize)) { + if (const char *error = SBReproducer::Finalize(finalize_path->getValue())) { + WithColor::error() << "reproducer finalization failed: " << error << '\n'; + return 1; + } + + llvm::outs() << "********************\n"; + llvm::outs() << "Crash reproducer for "; + llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n'; + llvm::outs() << '\n'; + llvm::outs() << "Reproducer written to '" << SBReproducer::GetPath() + << "'\n"; + llvm::outs() << '\n'; + llvm::outs() << "Before attaching the reproducer to a bug report:\n"; + llvm::outs() << " - Look at the directory to ensure you're willing to " + "share its content.\n"; + llvm::outs() + << " - Make sure the reproducer works by replaying the reproducer.\n"; + llvm::outs() << '\n'; + llvm::outs() << "Replay the reproducer with the following command:\n"; + llvm::outs() << argv0 << " -replay " << finalize_path->getValue() << "\n"; + llvm::outs() << "********************\n"; + return 0; + } + if (auto *replay_path = input_args.getLastArg(OPT_replay)) { SBReplayOptions replay_options; replay_options.SetCheckVersion(!input_args.hasArg(OPT_no_version_check)); @@ -821,12 +831,6 @@ } if (capture || capture_path) { - // Register the reproducer signal handler. - if (!input_args.hasArg(OPT_no_generate_on_signal)) { - llvm::sys::AddSignalHandler(reproducer_handler, - const_cast(argv0.data())); - } - if (capture_path) { if (!capture) WithColor::warning() << "-capture-path specified without -capture\n"; @@ -843,6 +847,18 @@ } if (generate_on_exit) SBReproducer::SetAutoGenerate(true); + + // Register the reproducer signal handler. + if (!input_args.hasArg(OPT_no_generate_on_signal)) { + if (const char *reproducer_path = SBReproducer::GetPath()) { + // Leaking the string on purpose. + std::string *str = new std::string(argv0); + str->append(" --reproducer-finalize "); + str->append(reproducer_path); + llvm::sys::AddSignalHandler(reproducer_handler, + const_cast(str->c_str())); + } + } } return llvm::None; Index: lldb/tools/driver/Options.td =================================================================== --- lldb/tools/driver/Options.td +++ lldb/tools/driver/Options.td @@ -232,6 +232,9 @@ def replay: Separate<["--", "-"], "replay">, MetaVarName<"">, HelpText<"Tells the debugger to replay a reproducer from .">; +def reproducer_finalize: Separate<["--", "-"], "reproducer-finalize">, + MetaVarName<"">, + HelpText<"Finalize the reproducer by copying files into reproducer root.">; def no_version_check: F<"reproducer-no-version-check">, HelpText<"Disable the reproducer version check.">; def no_verification: F<"reproducer-no-verify">,