Index: include/lldb/API/SBDebugger.h =================================================================== --- include/lldb/API/SBDebugger.h +++ include/lldb/API/SBDebugger.h @@ -13,6 +13,7 @@ #include #include "lldb/API/SBDefines.h" +#include "lldb/API/SBInitializerOptions.h" #include "lldb/API/SBPlatform.h" namespace lldb { @@ -45,6 +46,7 @@ lldb::SBDebugger &operator=(const lldb::SBDebugger &rhs); static void Initialize(); + static void Initialize(SBInitializerOptions &options); static void Terminate(); @@ -228,8 +230,6 @@ const char *GetReproducerPath() const; - lldb::SBError ReplayReproducer(const char *path); - lldb::ScriptLanguage GetScriptLanguage() const; void SetScriptLanguage(lldb::ScriptLanguage script_lang); Index: include/lldb/API/SBDefines.h =================================================================== --- include/lldb/API/SBDefines.h +++ include/lldb/API/SBDefines.h @@ -51,6 +51,7 @@ class LLDB_API SBFrame; class LLDB_API SBFunction; class LLDB_API SBHostOS; +class LLDB_API SBInitializerOptions; class LLDB_API SBInstruction; class LLDB_API SBInstructionList; class LLDB_API SBLanguageRuntime; Index: include/lldb/API/SBFileSpec.h =================================================================== --- include/lldb/API/SBFileSpec.h +++ include/lldb/API/SBFileSpec.h @@ -59,6 +59,7 @@ friend class SBDeclaration; friend class SBFileSpecList; friend class SBHostOS; + friend class SBInitializerOptions; friend class SBLaunchInfo; friend class SBLineEntry; friend class SBModule; @@ -67,8 +68,8 @@ friend class SBProcess; friend class SBProcessInfo; friend class SBSourceManager; - friend class SBThread; friend class SBTarget; + friend class SBThread; SBFileSpec(const lldb_private::FileSpec &fspec); Index: include/lldb/API/SBInitializerOptions.h =================================================================== --- /dev/null +++ include/lldb/API/SBInitializerOptions.h @@ -0,0 +1,43 @@ +//===-- SBInitializerOptions.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBInitializerOptuions_h_ +#define LLDB_SBInitializerOptuions_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBFileSpec.h" + +namespace lldb_private { +struct InitializerOptions; +} + +namespace lldb { + +class LLDB_API SBInitializerOptions { +public: + SBInitializerOptions(); + SBInitializerOptions(const lldb::SBInitializerOptions &rhs); + ~SBInitializerOptions(); + const SBInitializerOptions &operator=(const lldb::SBInitializerOptions &rhs); + + void SetCaptureReproducer(bool b); + void SetReplayReproducer(bool b); + void SetReproducerPath(const char *path); + + lldb_private::InitializerOptions &ref() const; + +private: + friend class SBDebugger; + + std::unique_ptr m_opaque_up; +}; + +} // namespace lldb + +#endif // LLDB_SBInitializerOptuions_h_ Index: include/lldb/Core/Debugger.h =================================================================== --- include/lldb/Core/Debugger.h +++ include/lldb/Core/Debugger.h @@ -263,11 +263,6 @@ llvm::StringRef GetReproducerPath() const; - llvm::Error SetReproducerReplay(llvm::StringRef p); - llvm::Error SetReproducerReplay(const char *) = delete; - - llvm::Error SetReproducerCapture(bool b); - bool GetUseExternalEditor() const; bool SetUseExternalEditor(bool use_external_editor_p); Index: include/lldb/Host/HostInfoBase.h =================================================================== --- include/lldb/Host/HostInfoBase.h +++ include/lldb/Host/HostInfoBase.h @@ -93,12 +93,6 @@ /// FileSpec is filled in. static FileSpec GetGlobalTempDir(); - /// Returns the reproducer temporary directory. This directory will **not** - /// be automatically cleaned up when this process exits, but might be removed - /// by the reproducer generator. Only the directory member of the FileSpec is - /// filled in. - static FileSpec GetReproducerTempDir(); - //--------------------------------------------------------------------------- /// If the triple does not specify the vendor, os, and environment parts, we /// "augment" these using information from the host and return the resulting @@ -111,7 +105,6 @@ static bool ComputeSupportExeDirectory(FileSpec &file_spec); static bool ComputeProcessTempFileDirectory(FileSpec &file_spec); static bool ComputeGlobalTempFileDirectory(FileSpec &file_spec); - static bool ComputeReproducerTempFileDirectory(FileSpec &file_spec); static bool ComputeTempFileBaseDirectory(FileSpec &file_spec); static bool ComputeHeaderDirectory(FileSpec &file_spec); static bool ComputeSystemPluginsDirectory(FileSpec &file_spec); Index: include/lldb/Initialization/SystemInitializer.h =================================================================== --- include/lldb/Initialization/SystemInitializer.h +++ include/lldb/Initialization/SystemInitializer.h @@ -10,13 +10,22 @@ #ifndef LLDB_INITIALIZATION_SYSTEM_INITIALIZER_H #define LLDB_INITIALIZATION_SYSTEM_INITIALIZER_H +#include + namespace lldb_private { + +struct InitializerOptions { + bool reproducer_capture = false; + bool reproducer_replay = false; + std::string reproducer_path; +}; + class SystemInitializer { public: SystemInitializer(); virtual ~SystemInitializer(); - virtual void Initialize() = 0; + virtual void Initialize(const InitializerOptions &options) = 0; virtual void Terminate() = 0; }; } Index: include/lldb/Initialization/SystemInitializerCommon.h =================================================================== --- include/lldb/Initialization/SystemInitializerCommon.h +++ include/lldb/Initialization/SystemInitializerCommon.h @@ -28,7 +28,7 @@ SystemInitializerCommon(); ~SystemInitializerCommon() override; - void Initialize() override; + void Initialize(const InitializerOptions &options) override; void Terminate() override; }; Index: include/lldb/Initialization/SystemLifetimeManager.h =================================================================== --- include/lldb/Initialization/SystemLifetimeManager.h +++ include/lldb/Initialization/SystemLifetimeManager.h @@ -10,13 +10,13 @@ #ifndef LLDB_INITIALIZATION_SYSTEM_LIFETIME_MANAGER_H #define LLDB_INITIALIZATION_SYSTEM_LIFETIME_MANAGER_H +#include "lldb/Initialization/SystemInitializer.h" #include "lldb/lldb-private-types.h" #include #include namespace lldb_private { -class SystemInitializer; class SystemLifetimeManager { public: @@ -24,6 +24,7 @@ ~SystemLifetimeManager(); void Initialize(std::unique_ptr initializer, + const InitializerOptions &options, LoadPluginCallbackType plugin_callback); void Terminate(); Index: include/lldb/Utility/Reproducer.h =================================================================== --- include/lldb/Utility/Reproducer.h +++ include/lldb/Utility/Reproducer.h @@ -25,6 +25,12 @@ class Reproducer; +enum class ReproducerMode { + Capture, + Replay, + Off, +}; + /// Abstraction for information associated with a provider. This information /// is serialized into an index which is used by the loader. struct ProviderInfo { @@ -159,10 +165,13 @@ /// The reproducer enables clients to obtain access to the Generator and /// Loader. -class Reproducer final { - +class Reproducer { public: static Reproducer &Instance(); + static void Initialize(ReproducerMode mode, llvm::Optional root); + static void Terminate(); + + Reproducer(ReproducerMode mode, llvm::Optional root); Generator *GetGenerator(); Loader *GetLoader(); @@ -170,12 +179,16 @@ const Generator *GetGenerator() const; const Loader *GetLoader() const; + FileSpec GetReproducerPath() const; + +protected: + Reproducer() = default; llvm::Error SetCapture(llvm::Optional root); llvm::Error SetReplay(llvm::Optional root); - FileSpec GetReproducerPath() const; - private: + static llvm::Optional &InstanceImpl(); + llvm::Optional m_generator; llvm::Optional m_loader; Index: lit/Reproducer/Inputs/GDBRemoteCapture.in =================================================================== --- /dev/null +++ lit/Reproducer/Inputs/GDBRemoteCapture.in @@ -0,0 +1,6 @@ +breakpoint set -f simple.c -l 13 +run +bt +cont +reproducer status +reproducer generate Index: lit/Reproducer/Inputs/GDBRemoteReplay.in =================================================================== --- /dev/null +++ lit/Reproducer/Inputs/GDBRemoteReplay.in @@ -0,0 +1,5 @@ +reproducer status +breakpoint set -f simple.c -l 13 +run +bt +cont Index: lit/Reproducer/Inputs/simple.c =================================================================== --- /dev/null +++ lit/Reproducer/Inputs/simple.c @@ -0,0 +1,19 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +void foo() { + printf("testing\n"); +} + +int main (int argc, char const *argv[]) { + foo(); + return 0; +} Index: lit/Reproducer/TestGDBRemoteRepro.test =================================================================== --- /dev/null +++ lit/Reproducer/TestGDBRemoteRepro.test @@ -0,0 +1,26 @@ +# REQUIRES: system-darwin + +# This tests the replaying of GDB remote packets. +# +# We issue the same commands and ensure the output is identical to the original +# process. To ensure we're not actually running the original binary we check +# that the string "testing" is not printed. + +# RUN: %clang %S/Inputs/simple.c -g -o %t.out +# RUN: %lldb -x -b -s %S/Inputs/GDBRemoteCapture.in --capture %T/reproducer -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE +# RUN: %lldb -x -b -s %S/Inputs/GDBRemoteReplay.in --replay %T/reproducer -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix REPLAY + +# CHECK: Breakpoint 1 +# CHECK: Process {{.*}} stopped +# CHECK: Process {{.*}} launched +# CHECK: thread {{.*}} stop reason = breakpoint +# CHECK: frame {{.*}} simple.c + +# CAPTURE: testing +# REPLAY-NOT: testing + +# CHECK: Process {{.*}} resuming +# CHECK: Process {{.*}} exited + +# CAPTURE: Reproducer is in capture mode. +# CAPTURE: Reproducer written Index: packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/Makefile =================================================================== --- packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -LEVEL = ../../../make - -C_SOURCES := main.c - -include $(LEVEL)/Makefile.rules Index: packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/TestGdbRemoteReproducer.py =================================================================== --- packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/TestGdbRemoteReproducer.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Test the GDB remote reproducer. -""" - -from __future__ import print_function - -import os -import lldb -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -from lldbsuite.test import lldbutil - - -class TestGdbRemoteReproducer(TestBase): - - mydir = TestBase.compute_mydir(__file__) - NO_DEBUG_INFO_TESTCASE = True - - def test(self): - """Test record and replay of gdb-remote packets.""" - self.build() - - # Create temp directory for the reproducer. - exe = self.getBuildArtifact("a.out") - - # First capture a regular debugging session. - self.runCmd("reproducer capture enable") - - reproducer_path = self.dbg.GetReproducerPath() - - self.runCmd("file {}".format(exe)) - self.runCmd("breakpoint set -f main.c -l 13") - self.runCmd("run") - self.runCmd("bt") - self.runCmd("cont") - - # Generate the reproducer and stop capturing. - self.runCmd("reproducer generate") - self.runCmd("reproducer capture disable") - - # Replay the session from the reproducer. - self.runCmd("reproducer replay {}".format(reproducer_path)) - - # We have to issue the same commands. - self.runCmd("file {}".format(exe)) - self.runCmd("breakpoint set -f main.c -l 13") - self.runCmd("run") - self.runCmd("bt") - self.expect("cont") Index: scripts/interface/SBDebugger.i =================================================================== --- scripts/interface/SBDebugger.i +++ scripts/interface/SBDebugger.i @@ -123,6 +123,9 @@ static void Initialize(); + static void + Initialize(lldb::SBInitializerOptions& options); + static void Terminate(); @@ -376,9 +379,6 @@ const char * GetReproducerPath() const; - lldb::SBError - ReplayReproducer (const char *path); - lldb::ScriptLanguage GetScriptLanguage() const; Index: scripts/interface/SBInitializerOptions.i =================================================================== --- /dev/null +++ scripts/interface/SBInitializerOptions.i @@ -0,0 +1,24 @@ +//===-- SWIG Interface for SBInitializerOptions -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBInitializerOptions +{ +public: + SBInitializerOptions (); + SBInitializerOptions (const lldb::SBInitializerOptions &rhs); + ~SBInitializerOptions(); + + void SetCaptureReproducer(bool b); + void SetReplayReproducer(bool b); + void SetReproducerPath(const char* path); +}; + +} // namespace lldb Index: scripts/lldb.swig =================================================================== --- scripts/lldb.swig +++ scripts/lldb.swig @@ -187,6 +187,7 @@ %include "./interface/SBFrame.i" %include "./interface/SBFunction.i" %include "./interface/SBHostOS.i" +%include "./interface/SBInitializerOptions.i" %include "./interface/SBInstruction.i" %include "./interface/SBInstructionList.i" %include "./interface/SBLanguageRuntime.i" Index: source/API/CMakeLists.txt =================================================================== --- source/API/CMakeLists.txt +++ source/API/CMakeLists.txt @@ -29,6 +29,7 @@ SBFrame.cpp SBFunction.cpp SBHostOS.cpp + SBInitializerOptions.cpp SBInstruction.cpp SBInstructionList.cpp SBLanguageRuntime.cpp Index: source/API/SBDebugger.cpp =================================================================== --- source/API/SBDebugger.cpp +++ source/API/SBDebugger.cpp @@ -125,13 +125,18 @@ } void SBDebugger::Initialize() { + SBInitializerOptions options; + SBDebugger::Initialize(options); +} + +void SBDebugger::Initialize(SBInitializerOptions &options) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBDebugger::Initialize ()"); g_debugger_lifetime->Initialize(llvm::make_unique(), - LoadPlugin); + *options.m_opaque_up, LoadPlugin); } void SBDebugger::Terminate() { g_debugger_lifetime->Terminate(); } @@ -1057,17 +1062,6 @@ : nullptr); } -SBError SBDebugger::ReplayReproducer(const char *p) { - SBError sb_error; - if (m_opaque_sp) { - auto error = - m_opaque_sp->SetReproducerReplay(llvm::StringRef::withNullAsEmpty(p)); - std::string error_str = llvm::toString(std::move(error)); - sb_error.ref().SetErrorString(error_str); - } - return sb_error; -} - ScriptLanguage SBDebugger::GetScriptLanguage() const { return (m_opaque_sp ? m_opaque_sp->GetScriptLanguage() : eScriptLanguageNone); } Index: source/API/SBInitializerOptions.cpp =================================================================== --- /dev/null +++ source/API/SBInitializerOptions.cpp @@ -0,0 +1,49 @@ +//===-- SBInitializerOptions.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBInitializerOptions.h" +#include "lldb/Initialization/SystemInitializer.h" + +using namespace lldb; +using namespace lldb_private; + +SBInitializerOptions::SBInitializerOptions(const SBInitializerOptions &rhs) { + m_opaque_up.reset(new InitializerOptions()); + *(m_opaque_up.get()) = rhs.ref(); +} + +const SBInitializerOptions &SBInitializerOptions:: +operator=(const SBInitializerOptions &rhs) { + if (this != &rhs) { + this->ref() = rhs.ref(); + } + return *this; +} + +SBInitializerOptions::~SBInitializerOptions() {} + +SBInitializerOptions::SBInitializerOptions() { + m_opaque_up.reset(new InitializerOptions()); +} + +void SBInitializerOptions::SetCaptureReproducer(bool b) { + m_opaque_up->reproducer_capture = b; +} + +void SBInitializerOptions::SetReplayReproducer(bool b) { + m_opaque_up->reproducer_replay = b; +} + +void SBInitializerOptions::SetReproducerPath(const char *path) { + m_opaque_up->reproducer_path = path; +} + +InitializerOptions &SBInitializerOptions::ref() const { + return *(m_opaque_up.get()); +} Index: source/API/SystemInitializerFull.h =================================================================== --- source/API/SystemInitializerFull.h +++ source/API/SystemInitializerFull.h @@ -26,7 +26,7 @@ SystemInitializerFull(); ~SystemInitializerFull() override; - void Initialize() override; + void Initialize(const InitializerOptions &options) override; void Terminate() override; private: Index: source/API/SystemInitializerFull.cpp =================================================================== --- source/API/SystemInitializerFull.cpp +++ source/API/SystemInitializerFull.cpp @@ -263,8 +263,8 @@ SystemInitializerFull::~SystemInitializerFull() {} -void SystemInitializerFull::Initialize() { - SystemInitializerCommon::Initialize(); +void SystemInitializerFull::Initialize(const InitializerOptions &options) { + SystemInitializerCommon::Initialize(options); ObjectFileELF::Initialize(); ObjectFileMachO::Initialize(); Index: source/Commands/CommandObjectReproducer.cpp =================================================================== --- source/Commands/CommandObjectReproducer.cpp +++ source/Commands/CommandObjectReproducer.cpp @@ -19,65 +19,6 @@ using namespace lldb; using namespace lldb_private; -static void AppendErrorToResult(llvm::Error e, CommandReturnObject &result) { - std::string error_str = llvm::toString(std::move(e)); - result.AppendErrorWithFormat("%s", error_str.c_str()); -} - -class CommandObjectReproducerCaptureEnable : public CommandObjectParsed { -public: - CommandObjectReproducerCaptureEnable(CommandInterpreter &interpreter) - : CommandObjectParsed(interpreter, "reproducer capture enable", - "Enable gathering information for reproducer", - nullptr) {} - - ~CommandObjectReproducerCaptureEnable() override = default; - -protected: - bool DoExecute(Args &command, CommandReturnObject &result) override { - if (!command.empty()) { - result.AppendErrorWithFormat("'%s' takes no arguments", - m_cmd_name.c_str()); - return false; - } - - if (auto e = m_interpreter.GetDebugger().SetReproducerCapture(true)) { - AppendErrorToResult(std::move(e), result); - return false; - } - - result.SetStatus(eReturnStatusSuccessFinishNoResult); - return result.Succeeded(); - } -}; - -class CommandObjectReproducerCaptureDisable : public CommandObjectParsed { -public: - CommandObjectReproducerCaptureDisable(CommandInterpreter &interpreter) - : CommandObjectParsed(interpreter, "reproducer capture enable", - "Disable gathering information for reproducer", - nullptr) {} - - ~CommandObjectReproducerCaptureDisable() override = default; - -protected: - bool DoExecute(Args &command, CommandReturnObject &result) override { - if (!command.empty()) { - result.AppendErrorWithFormat("'%s' takes no arguments", - m_cmd_name.c_str()); - return false; - } - - if (auto e = m_interpreter.GetDebugger().SetReproducerCapture(false)) { - AppendErrorToResult(std::move(e), result); - return false; - } - - result.SetStatus(eReturnStatusSuccessFinishNoResult); - return result.Succeeded(); - } -}; - class CommandObjectReproducerGenerate : public CommandObjectParsed { public: CommandObjectReproducerGenerate(CommandInterpreter &interpreter) @@ -110,82 +51,47 @@ } }; -class CommandObjectReproducerReplay : public CommandObjectParsed { +class CommandObjectReproducerStatus : public CommandObjectParsed { public: - CommandObjectReproducerReplay(CommandInterpreter &interpreter) - : CommandObjectParsed(interpreter, "reproducer replay", - "Replay a reproducer.", nullptr) { - CommandArgumentEntry arg1; - CommandArgumentData path_arg; - - // Define the first (and only) variant of this arg. - path_arg.arg_type = eArgTypePath; - path_arg.arg_repetition = eArgRepeatPlain; - - // There is only one variant this argument could be; put it into the - // argument entry. - arg1.push_back(path_arg); - - // Push the data for the first argument into the m_arguments vector. - m_arguments.push_back(arg1); - } + CommandObjectReproducerStatus(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "reproducer status", + "Show the current reproducer status.", nullptr) {} - ~CommandObjectReproducerReplay() override = default; + ~CommandObjectReproducerStatus() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { - if (command.empty()) { - result.AppendErrorWithFormat( - "'%s' takes a single argument: the reproducer path", - m_cmd_name.c_str()); + if (!command.empty()) { + result.AppendErrorWithFormat("'%s' takes no arguments", + m_cmd_name.c_str()); return false; } auto &r = repro::Reproducer::Instance(); + if (auto generator = r.GetGenerator()) { + result.GetOutputStream() << "Reproducer is in capture mode.\n"; + } else if (auto generator = r.GetLoader()) { + result.GetOutputStream() << "Reproducer is in replay mode.\n"; + } else { - const char *repro_path = command.GetArgumentAtIndex(0); - if (auto e = r.SetReplay(FileSpec(repro_path))) { - std::string error_str = llvm::toString(std::move(e)); - result.AppendErrorWithFormat("%s", error_str.c_str()); - return false; + result.GetOutputStream() << "Reproducer is off.\n"; } - result.SetStatus(eReturnStatusSuccessFinishNoResult); + result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } }; -class CommandObjectReproducerCapture : public CommandObjectMultiword { -private: -public: - CommandObjectReproducerCapture(CommandInterpreter &interpreter) - : CommandObjectMultiword( - interpreter, "reproducer capture", - "Manage gathering of information needed to generate a reproducer.", - NULL) { - LoadSubCommand( - "enable", - CommandObjectSP(new CommandObjectReproducerCaptureEnable(interpreter))); - LoadSubCommand("disable", - CommandObjectSP( - new CommandObjectReproducerCaptureDisable(interpreter))); - } - - ~CommandObjectReproducerCapture() {} -}; - CommandObjectReproducer::CommandObjectReproducer( CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "reproducer", "Commands controlling LLDB reproducers.", "log []") { - LoadSubCommand("capture", CommandObjectSP(new CommandObjectReproducerCapture( - interpreter))); LoadSubCommand( "generate", CommandObjectSP(new CommandObjectReproducerGenerate(interpreter))); - LoadSubCommand("replay", CommandObjectSP( - new CommandObjectReproducerReplay(interpreter))); + LoadSubCommand("status", CommandObjectSP( + new CommandObjectReproducerStatus(interpreter))); } CommandObjectReproducer::~CommandObjectReproducer() = default; Index: source/Core/Debugger.cpp =================================================================== --- source/Core/Debugger.cpp +++ source/Core/Debugger.cpp @@ -418,24 +418,6 @@ return r.GetReproducerPath().GetCString(); } -llvm::Error Debugger::SetReproducerReplay(llvm::StringRef p) { - llvm::Optional arg = llvm::None; - - if (!p.empty()) - arg = FileSpec(p); - - return repro::Reproducer::Instance().SetReplay(arg); -} - -llvm::Error Debugger::SetReproducerCapture(bool b) { - llvm::Optional arg = llvm::None; - - if (b) - arg = HostInfo::GetReproducerTempDir(); - - return repro::Reproducer::Instance().SetCapture(arg); -} - const FormatEntity::Entry *Debugger::GetThreadFormat() const { const uint32_t idx = ePropertyThreadFormat; return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx); Index: source/Host/common/HostInfoBase.cpp =================================================================== --- source/Host/common/HostInfoBase.cpp +++ source/Host/common/HostInfoBase.cpp @@ -194,19 +194,6 @@ return success ? g_fields->m_lldb_global_tmp_dir : FileSpec(); } -FileSpec HostInfoBase::GetReproducerTempDir() { - static llvm::once_flag g_once_flag; - static bool success = false; - llvm::call_once(g_once_flag, []() { - success = HostInfo::ComputeReproducerTempFileDirectory( - g_fields->m_lldb_global_tmp_dir); - Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); - LLDB_LOG(log, "reproducer temp dir -> `{0}`", - g_fields->m_lldb_global_tmp_dir); - }); - return success ? g_fields->m_lldb_global_tmp_dir : FileSpec(); -} - ArchSpec HostInfoBase::GetAugmentedArchSpec(llvm::StringRef triple) { if (triple.empty()) return ArchSpec(); @@ -289,26 +276,6 @@ return true; } -bool HostInfoBase::ComputeReproducerTempFileDirectory(FileSpec &file_spec) { - file_spec.Clear(); - - FileSpec temp_file_spec; - if (!HostInfo::ComputeTempFileBaseDirectory(temp_file_spec)) - return false; - - temp_file_spec.AppendPathComponent("reproducer"); - if (llvm::sys::fs::create_directory(temp_file_spec.GetPath())) - return false; - - std::string pid_str{llvm::to_string(Host::GetCurrentProcessID())}; - temp_file_spec.AppendPathComponent(pid_str); - if (llvm::sys::fs::create_directory(temp_file_spec.GetPath())) - return false; - - file_spec.GetDirectory().SetCString(temp_file_spec.GetCString()); - return true; -} - bool HostInfoBase::ComputeHeaderDirectory(FileSpec &file_spec) { // TODO(zturner): Figure out how to compute the header directory for all // platforms. Index: source/Initialization/SystemInitializerCommon.cpp =================================================================== --- source/Initialization/SystemInitializerCommon.cpp +++ source/Initialization/SystemInitializerCommon.cpp @@ -19,6 +19,7 @@ #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/Reproducer.h" #include "lldb/Utility/Timer.h" #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) @@ -35,12 +36,13 @@ #include using namespace lldb_private; +using namespace lldb_private::repro; SystemInitializerCommon::SystemInitializerCommon() {} SystemInitializerCommon::~SystemInitializerCommon() {} -void SystemInitializerCommon::Initialize() { +void SystemInitializerCommon::Initialize(const InitializerOptions &options) { #if defined(_MSC_VER) const char *disable_crash_dialog_var = getenv("LLDB_DISABLE_CRASH_DIALOG"); if (disable_crash_dialog_var && @@ -63,6 +65,13 @@ } #endif + ReproducerMode mode = ReproducerMode::Off; + if (options.reproducer_capture) + mode = ReproducerMode::Capture; + if (options.reproducer_replay) + mode = ReproducerMode::Replay; + + Reproducer::Initialize(mode, FileSpec(options.reproducer_path)); FileSystem::Initialize(); Log::Initialize(); HostInfo::Initialize(); @@ -109,4 +118,5 @@ HostInfo::Terminate(); Log::DisableAllLogChannels(); FileSystem::Terminate(); + Reproducer::Terminate(); } Index: source/Initialization/SystemLifetimeManager.cpp =================================================================== --- source/Initialization/SystemLifetimeManager.cpp +++ source/Initialization/SystemLifetimeManager.cpp @@ -26,7 +26,7 @@ void SystemLifetimeManager::Initialize( std::unique_ptr initializer, - LoadPluginCallbackType plugin_callback) { + const InitializerOptions &options, LoadPluginCallbackType plugin_callback) { std::lock_guard guard(m_mutex); if (!m_initialized) { assert(!m_initializer && "Attempting to call " @@ -35,7 +35,7 @@ m_initialized = true; m_initializer = std::move(initializer); - m_initializer->Initialize(); + m_initializer->Initialize(options); Debugger::Initialize(plugin_callback); } } Index: source/Utility/Reproducer.cpp =================================================================== --- source/Utility/Reproducer.cpp +++ source/Utility/Reproducer.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Utility/Reproducer.h" +#include "lldb/Utility/LLDBAssert.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Threading.h" @@ -18,11 +19,50 @@ using namespace llvm; using namespace llvm::yaml; -Reproducer &Reproducer::Instance() { - static Reproducer g_reproducer; +Reproducer &Reproducer::Instance() { return *InstanceImpl(); } + +void Reproducer::Initialize(ReproducerMode mode, + llvm::Optional root) { + lldbassert(!InstanceImpl() && "Already initialized."); + InstanceImpl().emplace(mode, root); +} + +void Reproducer::Terminate() { + lldbassert(InstanceImpl() && "Already terminated."); + InstanceImpl().reset(); +} + +Optional &Reproducer::InstanceImpl() { + static Optional g_reproducer; return g_reproducer; } +Reproducer::Reproducer(ReproducerMode mode, llvm::Optional root) { + // It's unfortunate that we have to do so much I/O here that can fail. The + // best we can do is not initialize the reproducer. + switch (mode) { + case ReproducerMode::Capture: { + if (!root) { + llvm::SmallString<128> repro_dir; + auto ec = llvm::sys::fs::createUniqueDirectory("reproducer", repro_dir); + if (ec) + return; + root.emplace(repro_dir); + } else { + auto ec = llvm::sys::fs::create_directories(root->GetPath()); + if (ec) + return; + } + llvm::cantFail(SetCapture(root)); + } break; + case ReproducerMode::Replay: + llvm::cantFail(SetReplay(root)); + break; + case ReproducerMode::Off: + break; + }; +} + const Generator *Reproducer::GetGenerator() const { std::lock_guard guard(m_mutex); if (m_generator) @@ -59,11 +99,12 @@ "cannot generate a reproducer when replay one", inconvertibleErrorCode()); - if (root) - m_generator.emplace(*root); - else + if (!root) { m_generator.reset(); + return Error::success(); + } + m_generator.emplace(*root); return Error::success(); } @@ -75,14 +116,15 @@ "cannot replay a reproducer when generating one", inconvertibleErrorCode()); - if (root) { - m_loader.emplace(*root); - if (auto e = m_loader->LoadIndex()) - return e; - } else { + if (!root) { m_loader.reset(); + return Error::success(); } + m_loader.emplace(*root); + if (auto e = m_loader->LoadIndex()) + return e; + return Error::success(); } Index: tools/driver/Driver.cpp =================================================================== --- tools/driver/Driver.cpp +++ tools/driver/Driver.cpp @@ -367,21 +367,6 @@ m_option_data.m_debug_mode = true; } - if (auto *arg = args.getLastArg(OPT_reproducer)) { - auto arg_value = arg->getValue(); - SBFileSpec file(arg_value); - if (file.Exists()) { - SBError repro_error = m_debugger.ReplayReproducer(arg_value); - if (repro_error.Fail()) - return repro_error; - } else { - error.SetErrorStringWithFormat("file specified in --reproducer " - "(-z) option doesn't exist: '%s'", - arg_value); - return error; - } - } - if (args.hasArg(OPT_no_use_colors)) { m_debugger.SetUseColor(false); } @@ -942,7 +927,21 @@ << '\n'; } - SBDebugger::Initialize(); + SBInitializerOptions options; + + if (auto *arg = input_args.getLastArg(OPT_capture)) { + auto arg_value = arg->getValue(); + options.SetReproducerPath(arg_value); + options.SetCaptureReproducer(true); + } + + if (auto *arg = input_args.getLastArg(OPT_replay)) { + auto arg_value = arg->getValue(); + options.SetReplayReproducer(true); + options.SetReproducerPath(arg_value); + } + + SBDebugger::Initialize(options); SBHostOS::ThreadCreated(""); signal(SIGINT, sigint_handler); Index: tools/driver/Options.td =================================================================== --- tools/driver/Options.td +++ tools/driver/Options.td @@ -209,11 +209,11 @@ Alias, HelpText<"Alias for --debug">; -def reproducer: Separate<["--", "-"], "reproducer">, +def capture: Separate<["--", "-"], "capture">, MetaVarName<"">, - HelpText<"Tells the debugger to use the fullpath to as a reproducer.">; -def: Separate<["-"], "z">, - Alias, - HelpText<"Alias for --reproducer">; + HelpText<"Tells the debugger to capture a reproducer to .">; +def replay: Separate<["--", "-"], "replay">, + MetaVarName<"">, + HelpText<"Tells the debugger to replay a reproducer from .">; def REM : R<["--"], "">; Index: tools/lldb-server/SystemInitializerLLGS.h =================================================================== --- tools/lldb-server/SystemInitializerLLGS.h +++ tools/lldb-server/SystemInitializerLLGS.h @@ -10,11 +10,12 @@ #ifndef LLDB_SYSTEMINITIALIZERLLGS_H #define LLDB_SYSTEMINITIALIZERLLGS_H +#include "lldb/Initialization/SystemInitializer.h" #include "lldb/Initialization/SystemInitializerCommon.h" class SystemInitializerLLGS : public lldb_private::SystemInitializerCommon { public: - void Initialize() override; + void Initialize(const lldb_private::InitializerOptions &options) override; void Terminate() override; }; Index: tools/lldb-server/SystemInitializerLLGS.cpp =================================================================== --- tools/lldb-server/SystemInitializerLLGS.cpp +++ tools/lldb-server/SystemInitializerLLGS.cpp @@ -22,8 +22,8 @@ using namespace lldb_private; -void SystemInitializerLLGS::Initialize() { - SystemInitializerCommon::Initialize(); +void SystemInitializerLLGS::Initialize(const InitializerOptions &options) { + SystemInitializerCommon::Initialize(options); HostObjectFile::Initialize(); } Index: tools/lldb-server/lldb-server.cpp =================================================================== --- tools/lldb-server/lldb-server.cpp +++ tools/lldb-server/lldb-server.cpp @@ -39,7 +39,7 @@ static void initialize() { g_debugger_lifetime->Initialize(llvm::make_unique(), - nullptr); + {}, nullptr); } static void terminate() { g_debugger_lifetime->Terminate(); } Index: tools/lldb-test/SystemInitializerTest.h =================================================================== --- tools/lldb-test/SystemInitializerTest.h +++ tools/lldb-test/SystemInitializerTest.h @@ -26,7 +26,7 @@ SystemInitializerTest(); ~SystemInitializerTest() override; - void Initialize() override; + void Initialize(const InitializerOptions &options) override; void Terminate() override; }; Index: tools/lldb-test/SystemInitializerTest.cpp =================================================================== --- tools/lldb-test/SystemInitializerTest.cpp +++ tools/lldb-test/SystemInitializerTest.cpp @@ -111,8 +111,8 @@ SystemInitializerTest::~SystemInitializerTest() {} -void SystemInitializerTest::Initialize() { - SystemInitializerCommon::Initialize(); +void SystemInitializerTest::Initialize(const InitializerOptions &options) { + SystemInitializerCommon::Initialize(options); ObjectFileELF::Initialize(); ObjectFileMachO::Initialize(); Index: tools/lldb-test/lldb-test.cpp =================================================================== --- tools/lldb-test/lldb-test.cpp +++ tools/lldb-test/lldb-test.cpp @@ -934,7 +934,7 @@ cl::ParseCommandLineOptions(argc, argv, "LLDB Testing Utility\n"); SystemLifetimeManager DebuggerLifetime; - DebuggerLifetime.Initialize(llvm::make_unique(), + DebuggerLifetime.Initialize(llvm::make_unique(), {}, nullptr); CleanUp TerminateDebugger([&] { DebuggerLifetime.Terminate(); }); Index: unittests/Utility/ReproducerTest.cpp =================================================================== --- unittests/Utility/ReproducerTest.cpp +++ unittests/Utility/ReproducerTest.cpp @@ -32,10 +32,18 @@ static char ID; }; +class DummyReproducer : public Reproducer { +public: + DummyReproducer() : Reproducer(){}; + + using Reproducer::SetCapture; + using Reproducer::SetReplay; +}; + char DummyProvider::ID = 0; TEST(ReproducerTest, SetCapture) { - Reproducer reproducer; + DummyReproducer reproducer; // Initially both generator and loader are unset. EXPECT_EQ(nullptr, reproducer.GetGenerator()); @@ -59,7 +67,7 @@ } TEST(ReproducerTest, SetReplay) { - Reproducer reproducer; + DummyReproducer reproducer; // Initially both generator and loader are unset. EXPECT_EQ(nullptr, reproducer.GetGenerator()); @@ -80,7 +88,7 @@ } TEST(GeneratorTest, Create) { - Reproducer reproducer; + DummyReproducer reproducer; EXPECT_THAT_ERROR(reproducer.SetCapture(FileSpec("/bogus/path")), Succeeded()); @@ -95,7 +103,7 @@ } TEST(GeneratorTest, Get) { - Reproducer reproducer; + DummyReproducer reproducer; EXPECT_THAT_ERROR(reproducer.SetCapture(FileSpec("/bogus/path")), Succeeded()); @@ -109,7 +117,7 @@ } TEST(GeneratorTest, GetOrCreate) { - Reproducer reproducer; + DummyReproducer reproducer; EXPECT_THAT_ERROR(reproducer.SetCapture(FileSpec("/bogus/path")), Succeeded());