Index: include/lldb/API/SBReproducer.h =================================================================== --- /dev/null +++ include/lldb/API/SBReproducer.h @@ -0,0 +1,24 @@ +//===-- SBReproducer.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_API_SBREPRODUCER_H +#define LLDB_API_SBREPRODUCER_H + +#include "lldb/lldb-defines.h" + +namespace lldb { + +class LLDB_API SBReproducer { +public: + bool Replay() const; +}; + +} // namespace lldb + +#endif Index: include/lldb/Utility/Reproducer.h =================================================================== --- include/lldb/Utility/Reproducer.h +++ include/lldb/Utility/Reproducer.h @@ -70,6 +70,7 @@ /// Every provider keeps track of its own files. ProviderInfo m_info; + private: /// Every provider knows where to dump its potential files. FileSpec m_root; Index: source/API/CMakeLists.txt =================================================================== --- source/API/CMakeLists.txt +++ source/API/CMakeLists.txt @@ -50,6 +50,7 @@ SBProcessInfo.cpp SBQueue.cpp SBQueueItem.cpp + SBReproducer.cpp SBSection.cpp SBSourceManager.cpp SBStream.cpp Index: source/API/SBCommandInterpreter.cpp =================================================================== --- source/API/SBCommandInterpreter.cpp +++ source/API/SBCommandInterpreter.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "SBReproducerPrivate.h" #include "lldb/lldb-types.h" #include "lldb/Interpreter/CommandInterpreter.h" @@ -22,6 +23,7 @@ #include "lldb/API/SBExecutionContext.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBProcess.h" +#include "lldb/API/SBReproducer.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBTarget.h" @@ -31,6 +33,7 @@ SBCommandInterpreterRunOptions::SBCommandInterpreterRunOptions() { m_opaque_up.reset(new CommandInterpreterRunOptions()); + SB_RECORD_CONSTRUCTOR_NO_ARGS(SBCommandInterpreterRunOptions); } SBCommandInterpreterRunOptions::~SBCommandInterpreterRunOptions() = default; @@ -129,6 +132,8 @@ SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter) : m_opaque_ptr(interpreter) { + SB_RECORD_CONSTRUCTOR(SBCommandInterpreter, (CommandInterpreter *), + interpreter); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) @@ -139,7 +144,10 @@ } SBCommandInterpreter::SBCommandInterpreter(const SBCommandInterpreter &rhs) - : m_opaque_ptr(rhs.m_opaque_ptr) {} + : m_opaque_ptr(rhs.m_opaque_ptr) { + SB_RECORD_CONSTRUCTOR(SBCommandInterpreter, (const SBCommandInterpreter &), + rhs); +} SBCommandInterpreter::~SBCommandInterpreter() = default; @@ -149,7 +157,10 @@ return *this; } -bool SBCommandInterpreter::IsValid() const { return m_opaque_ptr != nullptr; } +bool SBCommandInterpreter::IsValid() const { + SB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreter, IsValid); + return m_opaque_ptr != nullptr; +} bool SBCommandInterpreter::CommandExists(const char *cmd) { return (((cmd != nullptr) && IsValid()) ? m_opaque_ptr->CommandExists(cmd) @@ -407,6 +418,8 @@ } void SBCommandInterpreter::AllowExitCodeOnQuit(bool allow) { + SB_RECORD_METHOD(void, SBCommandInterpreter, AllowExitCodeOnQuit, (bool), + allow); if (m_opaque_ptr) m_opaque_ptr->AllowExitCodeOnQuit(allow); } @@ -419,6 +432,7 @@ } int SBCommandInterpreter::GetQuitStatus() { + SB_RECORD_METHOD_NO_ARGS(int, SBCommandInterpreter, GetQuitStatus); bool exited = false; return (m_opaque_ptr ? m_opaque_ptr->GetQuitExitCode(exited) : 0); } @@ -449,6 +463,8 @@ void SBCommandInterpreter::SourceInitFileInHomeDirectory( SBCommandReturnObject &result) { + SB_RECORD_METHOD(void, SBCommandInterpreter, SourceInitFileInHomeDirectory, + (SBCommandReturnObject &), result); result.Clear(); if (IsValid()) { TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget()); Index: source/API/SBCommandReturnObject.cpp =================================================================== --- source/API/SBCommandReturnObject.cpp +++ source/API/SBCommandReturnObject.cpp @@ -8,7 +8,9 @@ //===----------------------------------------------------------------------===// #include "lldb/API/SBCommandReturnObject.h" +#include "SBReproducerPrivate.h" #include "lldb/API/SBError.h" +#include "lldb/API/SBReproducer.h" #include "lldb/API/SBStream.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -20,7 +22,10 @@ using namespace lldb_private; SBCommandReturnObject::SBCommandReturnObject() - : m_opaque_ap(new CommandReturnObject()) {} + : m_opaque_ap(new CommandReturnObject()) { + + SB_RECORD_CONSTRUCTOR_NO_ARGS(SBCommandReturnObject); +} SBCommandReturnObject::SBCommandReturnObject(const SBCommandReturnObject &rhs) : m_opaque_ap() { Index: source/API/SBDebugger.cpp =================================================================== --- source/API/SBDebugger.cpp +++ source/API/SBDebugger.cpp @@ -7,10 +7,11 @@ // //===----------------------------------------------------------------------===// - +#include "SBReproducerPrivate.h" #include "SystemInitializerFull.h" #include "lldb/API/SBDebugger.h" +#include "lldb/API/SBReproducer.h" #include "lldb/lldb-private.h" @@ -108,14 +109,18 @@ bool SBInputReader::IsActive() const { return false; } -SBDebugger::SBDebugger() = default; +SBDebugger::SBDebugger() { SB_RECORD_CONSTRUCTOR_NO_ARGS(SBDebugger); } SBDebugger::SBDebugger(const lldb::DebuggerSP &debugger_sp) - : m_opaque_sp(debugger_sp) {} + : m_opaque_sp(debugger_sp) { + SB_RECORD_CONSTRUCTOR(SBDebugger, (const DebuggerSP &), debugger_sp); +} -SBDebugger::SBDebugger(const SBDebugger &rhs) : m_opaque_sp(rhs.m_opaque_sp) {} +SBDebugger::SBDebugger(const SBDebugger &rhs) : m_opaque_sp(rhs.m_opaque_sp) { + SB_RECORD_CONSTRUCTOR(SBDebugger, (const SBDebugger &), rhs); +} -SBDebugger::~SBDebugger() = default; +SBDebugger::~SBDebugger() {} SBDebugger &SBDebugger::operator=(const SBDebugger &rhs) { if (this != &rhs) { @@ -160,17 +165,24 @@ } SBDebugger SBDebugger::Create() { - return SBDebugger::Create(false, nullptr, nullptr); + SB_RECORD_STATIC_METHOD_NO_ARGS(SBDebugger, SBDebugger, Create); + return SB_RECORD_RESULT(SBDebugger::Create(false, nullptr, nullptr)); } SBDebugger SBDebugger::Create(bool source_init_files) { - return SBDebugger::Create(source_init_files, nullptr, nullptr); + SB_RECORD_STATIC_METHOD(SBDebugger, SBDebugger, Create, (bool), + source_init_files); + return SB_RECORD_RESULT( + SBDebugger::Create(source_init_files, nullptr, nullptr)); } SBDebugger SBDebugger::Create(bool source_init_files, lldb::LogOutputCallback callback, void *baton) { + SB_RECORD_STATIC_METHOD(SBDebugger, SBDebugger, Create, + (bool, lldb::LogOutputCallback, void *), + source_init_files, callback, baton); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBDebugger debugger; @@ -203,7 +215,7 @@ interp.get()->SkipLLDBInitFiles(true); interp.get()->SkipAppInitFiles(true); } - return debugger; + return SB_RECORD_RESULT(debugger); } void SBDebugger::Destroy(SBDebugger &debugger) { @@ -242,20 +254,24 @@ bool SBDebugger::IsValid() const { return m_opaque_sp.get() != nullptr; } void SBDebugger::SetAsync(bool b) { + SB_RECORD_METHOD(void, SBDebugger, SetAsync, (bool), b); if (m_opaque_sp) m_opaque_sp->SetAsyncExecution(b); } bool SBDebugger::GetAsync() { + SB_RECORD_METHOD_NO_ARGS(bool, SBDebugger, GetAsync); return (m_opaque_sp ? m_opaque_sp->GetAsyncExecution() : false); } void SBDebugger::SkipLLDBInitFiles(bool b) { + SB_RECORD_METHOD(void, SBDebugger, SkipLLDBInitFiles, (bool), b); if (m_opaque_sp) m_opaque_sp->GetCommandInterpreter().SkipLLDBInitFiles(b); } void SBDebugger::SkipAppInitFiles(bool b) { + SB_RECORD_METHOD(void, SBDebugger, SkipAppInitFiles, (bool), b); if (m_opaque_sp) m_opaque_sp->GetCommandInterpreter().SkipAppInitFiles(b); } @@ -264,6 +280,8 @@ // of problems; don't want users trying to switch modes in the middle of a // debugging session. void SBDebugger::SetInputFileHandle(FILE *fh, bool transfer_ownership) { + SB_RECORD_METHOD(void, SBDebugger, SetInputFileHandle, (FILE *, bool), fh, + transfer_ownership); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) @@ -277,6 +295,8 @@ } void SBDebugger::SetOutputFileHandle(FILE *fh, bool transfer_ownership) { + SB_RECORD_METHOD(void, SBDebugger, SetOutputFileHandle, (FILE *, bool), fh, + transfer_ownership); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) @@ -290,6 +310,8 @@ } void SBDebugger::SetErrorFileHandle(FILE *fh, bool transfer_ownership) { + SB_RECORD_METHOD(void, SBDebugger, SetErrorFileHandle, (FILE *, bool), fh, + transfer_ownership); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) @@ -339,6 +361,8 @@ m_opaque_sp->RestoreInputTerminalState(); } SBCommandInterpreter SBDebugger::GetCommandInterpreter() { + SB_RECORD_METHOD_NO_ARGS(SBCommandInterpreter, SBDebugger, + GetCommandInterpreter); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBCommandInterpreter sb_interpreter; @@ -351,7 +375,7 @@ static_cast(m_opaque_sp.get()), static_cast(sb_interpreter.get())); - return sb_interpreter; + return SB_RECORD_RESULT(sb_interpreter); } void SBDebugger::HandleCommand(const char *command) { @@ -943,6 +967,11 @@ bool &stopped_for_crash) { + SB_RECORD_METHOD(void, SBDebugger, RunCommandInterpreter, + (bool, bool, lldb::SBCommandInterpreterRunOptions &, int &, + bool &, bool &), + auto_handle_events, spawn_thread, options, num_errors, + quit_requested, stopped_for_crash); if (m_opaque_sp) { CommandInterpreter &interp = m_opaque_sp->GetCommandInterpreter(); interp.RunCommandInterpreter(auto_handle_events, spawn_thread, Index: source/API/SBFileSpec.cpp =================================================================== --- source/API/SBFileSpec.cpp +++ source/API/SBFileSpec.cpp @@ -10,7 +10,9 @@ #include #include +#include "SBReproducerPrivate.h" #include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBReproducer.h" #include "lldb/API/SBStream.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/PosixApi.h" @@ -23,21 +25,27 @@ using namespace lldb; using namespace lldb_private; -SBFileSpec::SBFileSpec() : m_opaque_ap(new lldb_private::FileSpec()) {} +SBFileSpec::SBFileSpec() : m_opaque_ap(new lldb_private::FileSpec()) { + SB_RECORD_CONSTRUCTOR_NO_ARGS(SBFileSpec); +} SBFileSpec::SBFileSpec(const SBFileSpec &rhs) - : m_opaque_ap(new lldb_private::FileSpec(*rhs.m_opaque_ap)) {} + : m_opaque_ap(new lldb_private::FileSpec(*rhs.m_opaque_ap)) { + SB_RECORD_CONSTRUCTOR(SBFileSpec, (const SBFileSpec &), rhs); +} SBFileSpec::SBFileSpec(const lldb_private::FileSpec &fspec) : m_opaque_ap(new lldb_private::FileSpec(fspec)) {} // Deprecated!!! SBFileSpec::SBFileSpec(const char *path) : m_opaque_ap(new FileSpec(path)) { + SB_RECORD_CONSTRUCTOR(SBFileSpec, (const char *), path); FileSystem::Instance().Resolve(*m_opaque_ap); } SBFileSpec::SBFileSpec(const char *path, bool resolve) : m_opaque_ap(new FileSpec(path)) { + SB_RECORD_CONSTRUCTOR(SBFileSpec, (const char *, bool), path, resolve); if (resolve) FileSystem::Instance().Resolve(*m_opaque_ap); } @@ -45,14 +53,20 @@ SBFileSpec::~SBFileSpec() {} const SBFileSpec &SBFileSpec::operator=(const SBFileSpec &rhs) { + SB_RECORD_METHOD(const SBFileSpec &, + SBFileSpec, operator=,(const SBFileSpec &), rhs); if (this != &rhs) *m_opaque_ap = *rhs.m_opaque_ap; return *this; } -bool SBFileSpec::IsValid() const { return m_opaque_ap->operator bool(); } +bool SBFileSpec::IsValid() const { + SB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFileSpec, IsValid); + return m_opaque_ap->operator bool(); +} bool SBFileSpec::Exists() const { + SB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFileSpec, IsValid); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool result = FileSystem::Instance().Exists(*m_opaque_ap); @@ -66,11 +80,15 @@ } bool SBFileSpec::ResolveExecutableLocation() { + SB_RECORD_METHOD_NO_ARGS(bool, SBFileSpec, ResolveExecutableLocation); return FileSystem::Instance().ResolveExecutableLocation(*m_opaque_ap); } int SBFileSpec::ResolvePath(const char *src_path, char *dst_path, size_t dst_len) { + SB_RECORD_STATIC_METHOD(int, SBFileSpec, ResolvePath, + (const char *, char *, size_t), src_path, dst_path, + dst_len); llvm::SmallString<64> result(src_path); FileSystem::Instance().Resolve(result); ::snprintf(dst_path, dst_len, "%s", result.c_str()); @@ -78,6 +96,7 @@ } const char *SBFileSpec::GetFilename() const { + SB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBFileSpec, GetFilename); const char *s = m_opaque_ap->GetFilename().AsCString(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); @@ -94,6 +113,7 @@ } const char *SBFileSpec::GetDirectory() const { + SB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBFileSpec, GetDirectory); FileSpec directory{*m_opaque_ap}; directory.GetFilename().Clear(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); @@ -110,6 +130,7 @@ } void SBFileSpec::SetFilename(const char *filename) { + SB_RECORD_METHOD(void, SBFileSpec, SetFilename, (const char *), filename); if (filename && filename[0]) m_opaque_ap->GetFilename().SetCString(filename); else @@ -117,6 +138,7 @@ } void SBFileSpec::SetDirectory(const char *directory) { + SB_RECORD_METHOD(void, SBFileSpec, SetDirectory, (const char *), directory); if (directory && directory[0]) m_opaque_ap->GetDirectory().SetCString(directory); else @@ -124,6 +146,8 @@ } uint32_t SBFileSpec::GetPath(char *dst_path, size_t dst_len) const { + SB_RECORD_METHOD_CONST(uint32_t, SBFileSpec, GetPath, (char *, size_t), + dst_path, dst_len); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t result = m_opaque_ap->GetPath(dst_path, dst_len); @@ -158,6 +182,8 @@ } bool SBFileSpec::GetDescription(SBStream &description) const { + SB_RECORD_METHOD_CONST(bool, SBFileSpec, GetDescription, (SBStream &), + description); Stream &strm = description.ref(); char path[PATH_MAX]; if (m_opaque_ap->GetPath(path, sizeof(path))) @@ -166,5 +192,6 @@ } void SBFileSpec::AppendPathComponent(const char *fn) { + SB_RECORD_METHOD(void, SBFileSpec, AppendPathComponent, (const char *), fn); m_opaque_ap->AppendPathComponent(fn); } Index: source/API/SBHostOS.cpp =================================================================== --- source/API/SBHostOS.cpp +++ source/API/SBHostOS.cpp @@ -11,8 +11,10 @@ #include "Plugins/ScriptInterpreter/Python/lldb-python.h" #endif +#include "SBReproducerPrivate.h" #include "lldb/API/SBError.h" #include "lldb/API/SBHostOS.h" +#include "lldb/API/SBReproducer.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" @@ -83,6 +85,7 @@ } SBFileSpec SBHostOS::GetUserHomeDirectory() { + SB_RECORD_STATIC_METHOD_NO_ARGS(SBFileSpec, SBHostOS, GetUserHomeDirectory); SBFileSpec sb_fspec; llvm::SmallString<64> home_dir_path; @@ -91,7 +94,7 @@ FileSystem::Instance().Resolve(homedir); sb_fspec.SetFileSpec(homedir); - return sb_fspec; + return SB_RECORD_RESULT(sb_fspec); } lldb::thread_t SBHostOS::ThreadCreate(const char *name, Index: source/API/SBReproducer.cpp =================================================================== --- /dev/null +++ source/API/SBReproducer.cpp @@ -0,0 +1,215 @@ +//===-- SBReproducer.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SBReproducerPrivate.h" + +#include "lldb/API/LLDB.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBHostOS.h" +#include "lldb/API/SBReproducer.h" + +#include "lldb/Host/FileSystem.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::repro; + +#define REGISTER_CONSTRUCTOR(Class, Signature) \ + Register(&construct::doit, m_id++) + +#define REGISTER_METHOD(Result, Class, Method, Signature) \ + Register(&invoke::method<&Class::Method>::doit, \ + m_id++) +#define REGISTER_METHOD_CONST(Result, Class, Method, Signature) \ + Register(&invoke::method_const<&Class::Method>::doit, \ + m_id++) +#define REGISTER_STATIC_METHOD(Result, Class, Method, Signature) \ + Register(static_cast(&Class::Method), \ + m_id++) + +static FileSpec GetCommandFile() { + repro::Loader *loader = repro::Reproducer::Instance().GetLoader(); + if (!loader) { + return {}; + } + + auto provider_info = loader->GetProviderInfo("command-interpreter"); + if (!provider_info) { + return {}; + } + + if (provider_info->files.empty()) { + return {}; + } + + return loader->GetRoot().CopyByAppendingPathComponent( + provider_info->files.front()); +} + +static void SetInputFileHandleRedirect(SBDebugger *t, FILE *, bool) { + FileSpec fs = GetCommandFile(); + if (!fs) + return; + + FILE *f = FileSystem::Instance().Fopen(fs.GetPath().c_str(), "r"); + if (f == nullptr) + return; + t->SetInputFileHandle(f, true); +} + +static void SetFileHandleRedirect(SBDebugger *, FILE *, bool) { + // Do nothing. +} + +void SBRegistry::Init() { + // SBFileSpec + { + REGISTER_CONSTRUCTOR(SBFileSpec, ()); + REGISTER_CONSTRUCTOR(SBFileSpec, (const lldb::SBFileSpec &)); + REGISTER_CONSTRUCTOR(SBFileSpec, (const char *)); + REGISTER_CONSTRUCTOR(SBFileSpec, (const char *, bool)); + + REGISTER_METHOD_CONST(bool, SBFileSpec, IsValid, ()); + REGISTER_METHOD_CONST(bool, SBFileSpec, Exists, ()); + REGISTER_METHOD_CONST(const char *, SBFileSpec, GetFilename, ()); + REGISTER_METHOD_CONST(const char *, SBFileSpec, GetDirectory, ()); + REGISTER_METHOD_CONST(uint32_t, SBFileSpec, GetPath, (char *, size_t)); + REGISTER_METHOD_CONST(bool, SBFileSpec, GetDescription, (lldb::SBStream &)); + + REGISTER_METHOD(const SBFileSpec &, + SBFileSpec, operator=,(const lldb::SBFileSpec &)); + REGISTER_METHOD(bool, SBFileSpec, ResolveExecutableLocation, ()); + REGISTER_METHOD(void, SBFileSpec, SetFilename, (const char *)); + REGISTER_METHOD(void, SBFileSpec, SetDirectory, (const char *)); + REGISTER_METHOD(void, SBFileSpec, AppendPathComponent, (const char *)); + + REGISTER_STATIC_METHOD(int, SBFileSpec, ResolvePath, + (const char *, char *, size_t)); + } + + // SBHostOS + { REGISTER_STATIC_METHOD(SBFileSpec, SBHostOS, GetUserHomeDirectory, ()); } + + // SBDebugger + { + REGISTER_CONSTRUCTOR(SBDebugger, ()); + REGISTER_CONSTRUCTOR(SBDebugger, (const DebuggerSP &)); + REGISTER_CONSTRUCTOR(SBDebugger, (const SBDebugger &)); + + REGISTER_METHOD(SBCommandInterpreter, SBDebugger, GetCommandInterpreter, + ()); + REGISTER_METHOD(SBDebugger &, SBDebugger, operator=,(const SBDebugger &)); + REGISTER_METHOD(void, SBDebugger, SkipLLDBInitFiles, (bool)); + REGISTER_METHOD(void, SBDebugger, SkipAppInitFiles, (bool)); + REGISTER_METHOD(bool, SBDebugger, GetAsync, ()); + REGISTER_METHOD(void, SBDebugger, SetAsync, (bool)); + REGISTER_METHOD(void, SBDebugger, RunCommandInterpreter, + (bool, bool, lldb::SBCommandInterpreterRunOptions &, int &, + bool &, bool &)); + + // Custom implementation. + Register(&invoke::method<&SBDebugger::SetInputFileHandle>::doit, + m_id++, &SetInputFileHandleRedirect); + Register(&invoke::method<&SBDebugger::SetErrorFileHandle>::doit, + m_id++, &SetFileHandleRedirect); + Register(&invoke::method<&SBDebugger::SetOutputFileHandle>::doit, + m_id++, &SetFileHandleRedirect); + + REGISTER_STATIC_METHOD(SBDebugger, SBDebugger, Create, ()); + REGISTER_STATIC_METHOD(SBDebugger, SBDebugger, Create, (bool)); + + // FIXME: Why is this an issue? +#if 0 + REGISTER_STATIC_METHOD(SBDebugger, SBDebugger, Create, + (bool, lldb::LogOutputCallback, void *)); +#endif + } + + // SBCommandInterpreter + { + REGISTER_CONSTRUCTOR(SBCommandInterpreter, + (lldb_private::CommandInterpreter *)); + REGISTER_CONSTRUCTOR(SBCommandInterpreter, (SBCommandInterpreter &)); + REGISTER_CONSTRUCTOR(SBCommandInterpreter, (const SBCommandInterpreter &)); + + REGISTER_METHOD_CONST(bool, SBCommandInterpreter, IsValid, ()); + + REGISTER_METHOD(void, SBCommandInterpreter, SourceInitFileInHomeDirectory, + (SBCommandReturnObject &)); + REGISTER_METHOD(void, SBCommandInterpreter, AllowExitCodeOnQuit, (bool)); + REGISTER_METHOD(int, SBCommandInterpreter, GetQuitStatus, ()); + } + + // SBCommandInterpreterRunOptions + { + REGISTER_CONSTRUCTOR(SBCommandInterpreterRunOptions, ()); + REGISTER_METHOD(void, SBCommandInterpreterRunOptions, SetStopOnError, + (bool)); + REGISTER_METHOD(void, SBCommandInterpreterRunOptions, SetStopOnCrash, + (bool)); + } + + // SBCommandReturnObject + { REGISTER_CONSTRUCTOR(SBCommandReturnObject, ()); } +} + +bool SBReproducer::Replay() const { + repro::Loader *loader = repro::Reproducer::Instance().GetLoader(); + if (!loader) { + return false; + } + + llvm::Optional info = loader->GetProviderInfo("sbapi"); + if (!info) { + return false; + } + + FileSpec file(loader->GetRoot()); + file.AppendPathComponent(info->files.front()); + + SBRegistry::Instance().Replay(file); + + return true; +} + +bool SBRegistry::Replay(const FileSpec &file) { + auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); + if (auto err = error_or_file.getError()) + return false; + + m_deserializer.LoadBuffer((*error_or_file)->getBuffer()); + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API); + + while (m_deserializer.HasData(1)) { + unsigned id = m_deserializer.Deserialize(); + if (log) + log->Printf("Replaying function #%u", id); + m_ids[id]->operator()(); + } + + return true; +} + +template <> const char *SBDeserializer::Deserialize() { + auto pos = m_buffer.find('\0', m_offset); + if (pos == llvm::StringRef::npos) + return nullptr; + size_t begin = m_offset; + m_offset = pos + 1; + return m_buffer.data() + begin; +} + +std::atomic lldb_private::repro::SBRecorder::g_global_boundary; +char lldb_private::repro::SBProvider::ID = 0; Index: source/API/SBReproducerPrivate.h =================================================================== --- /dev/null +++ source/API/SBReproducerPrivate.h @@ -0,0 +1,730 @@ +//===-- SBReproducerPrivate.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_API_SBREPRODUCER_PRIVATE_H +#define LLDB_API_SBREPRODUCER_PRIVATE_H + +#include "lldb/API/SBReproducer.h" + +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Reproducer.h" + +#include "llvm/ADT/DenseMap.h" + +#include + +namespace lldb_private { +namespace repro { + +/// Mapping between serialized indices and their corresponding objects. +/// +/// This class is used during replay to map indices back to in-memory objects. +/// +/// When objects are constructed, they are added to this mapping using +/// AddObjectForIndex. +/// +/// When an object is passed to a function, its index is deserialized and +/// AddObjectForIndex returns the corresponding object. If there is no object +/// for the given index, a nullptr is returend. The latter is valid when custom +/// replay code is in place and the actual object is ignored. +class SBIndexToObject { +public: + /// Returns an object as a pointer for the given index or nullptr if not + /// present in the map. + template T *GetObjectForIndex(int idx) { + assert(idx != 0 && "Cannot get object for sentinel"); + void *object = GetObjectForIndexImpl(idx); + return static_cast::type *>(object); + } + + /// Adds a pointer to an object to the mapping for the given index. + template void AddObjectForIndex(int idx, T *object) { + AddObjectForIndexImpl( + idx, static_cast( + const_cast::type *>(object))); + } + + /// Adds a reference to an object to the mapping for the given index. + template void AddObjectForIndex(int idx, T &object) { + AddObjectForIndexImpl( + idx, static_cast( + const_cast::type *>(&object))); + } + +private: + /// Helper method that does the actual lookup. The void* result is later cast + /// by the caller. + void *GetObjectForIndexImpl(int idx) { + auto it = m_mapping.find(idx); + if (it == m_mapping.end()) { + return nullptr; + } + return m_mapping[idx]; + } + + /// Helper method that does the actual insertion. + void AddObjectForIndexImpl(int idx, void *object) { + assert(idx != 0 && "Cannot add object for sentinel"); + m_mapping[idx] = object; + } + + /// Keeps a mapping between indices and their corresponding object. + llvm::DenseMap m_mapping; +}; + +/// Base class for tag dispatch used in the SBDeserializer. Different tags are +/// instantiated with different values. +template struct SBTag {}; + +/// We need to differentiate between pointers to fundamental and +/// non-fundamental types. See the corresponding SBDeserializer::Read method +/// for the reason why. +typedef SBTag<0> PointerTag; +typedef SBTag<1> ReferenceTag; +typedef SBTag<2> ValueTag; +typedef SBTag<3> FundamentalPointerTag; +typedef SBTag<4> FundamentalReferenceTag; + +/// Return the deserialization tag for the given type T. +template struct serializer_tag { typedef ValueTag type; }; +template struct serializer_tag { + typedef + typename std::conditional::value, + FundamentalPointerTag, PointerTag>::type type; +}; +template struct serializer_tag { + typedef typename std::conditional::value, + FundamentalReferenceTag, ReferenceTag>::type + type; +}; + +/// Deserializes data from a buffer. It is used to deserialize function indices +/// to replay, their arguments and return values. +/// +/// Fundamental types and strings are read by value. Objects are read by their +/// index, which get translated by the SBIndexToObject mapping maintained in +/// this class. +/// +/// Additional bookkeeping with regards to the SBIndexToObject is required to +/// deserialize objects. When a constructor is run or an object is returned by +/// value, we need to capture the object and add it to the index together with +/// its index. This is the job of HandleReplayResult(Void). +class SBDeserializer { +public: + SBDeserializer(llvm::StringRef buffer = {}) : m_buffer(buffer), m_offset(0) {} + + /// Returns true when the buffer has unread data. + bool HasData(int offset = 0) { return m_offset + offset < m_buffer.size(); } + + /// Deserialize and interpret value as T. + template T Deserialize() { + return Read(typename serializer_tag::type()); + } + + /// Store the returned value in the index-to-object mapping. + template void HandleReplayResult(const T &t) { + unsigned result = Deserialize(); + if (std::is_fundamental::value) + return; + // We need to make a copy as the original object might go out of scope. + m_index_to_object.AddObjectForIndex(result, new T(t)); + } + + /// Store the returned value in the index-to-object mapping. + template void HandleReplayResult(T *t) { + unsigned result = Deserialize(); + if (std::is_fundamental::value) + return; + m_index_to_object.AddObjectForIndex(result, t); + } + + /// All returned types are recorded, even when the function returns a void. + /// The latter requires special handling. + void HandleReplayResultVoid() { + unsigned result = Deserialize(); + assert(result == 0); + } + + // FIXME: We have references to this instance stored in replayer instance. We + // should find a better way to swap out the buffer after this instance has + // been created, but his will have to do for now. + void LoadBuffer(llvm::StringRef buffer) { + m_buffer = buffer; + m_offset = 0; + } + +private: + template T Read(ValueTag) { + T t; + std::memcpy((char *)&t, &m_buffer.data()[m_offset], sizeof(t)); + m_offset += sizeof(t); + return t; + } + + template T Read(PointerTag) { + typedef typename std::remove_pointer::type UnderlyingT; + return m_index_to_object.template GetObjectForIndex( + Deserialize()); + } + + template T Read(ReferenceTag) { + typedef typename std::remove_reference::type UnderlyingT; + // If this is a reference to a fundamental type we just read its value. + return *m_index_to_object.template GetObjectForIndex( + Deserialize()); + } + + /// This method is used to parse references to fundamental types. Because + /// they're not recorded in the object table we have serialized their value. + /// We read its value, allocate a copy on the heap, and return a pointer to + /// the copy. + template T Read(FundamentalPointerTag) { + typedef typename std::remove_pointer::type UnderlyingT; + return new UnderlyingT(Deserialize()); + } + + /// This method is used to parse references to fundamental types. Because + /// they're not recorded in the object table we have serialized their value. + /// We read its value, allocate a copy on the heap, and return a reference to + /// the copy. + template T Read(FundamentalReferenceTag) { + // If this is a reference to a fundamental type we just read its value. + typedef typename std::remove_reference::type UnderlyingT; + return *(new UnderlyingT(Deserialize())); + } + + /// Mapping of indices to objects. + SBIndexToObject m_index_to_object; + + /// Buffer containing the serialized data. + llvm::StringRef m_buffer; + + /// Current offset in the buffer. + uint32_t m_offset; +}; + +/// Partial specialization for C-style strings. We read the string value +/// instead of treating it as pointer. +template <> const char *SBDeserializer::Deserialize(); + +/// Helpers to auto-synthesize function replay code. It deserializes the replay +/// function's arguments one by one and finally calls the corresponding +/// function. +template struct DeserializationHelper; + +template +struct DeserializationHelper { + template struct deserialized { + static Result doit(SBDeserializer &deserializer, + Result (*f)(Deserialized..., Head, Tail...), + Deserialized... d) { + return DeserializationHelper:: + template deserialized::doit( + deserializer, f, d..., deserializer.Deserialize()); + } + }; +}; + +template <> struct DeserializationHelper<> { + template struct deserialized { + static Result doit(SBDeserializer &deserializer, + Result (*f)(Deserialized...), Deserialized... d) { + return f(d...); + } + }; +}; + +/// The replayer interface. +struct SBReplayer { + SBReplayer(SBDeserializer &deserializer) : m_deserializer(deserializer) {} + virtual ~SBReplayer() {} + virtual void operator()() const = 0; + +protected: + SBDeserializer &m_deserializer; +}; + +/// The default replayer deserializes the arguments and calls the function. +template struct DefaultReplayer; +template +struct DefaultReplayer : public SBReplayer { + DefaultReplayer(SBDeserializer &deserializer, Result (*f)(Args...)) + : SBReplayer(deserializer), f(f) {} + + void operator()() const override { + m_deserializer.HandleReplayResult( + DeserializationHelper::template deserialized::doit( + m_deserializer, f)); + } + + Result (*f)(Args...); +}; + +/// Partial specialization for function returning a void type. It ignores the +/// (absent) return value. +template +struct DefaultReplayer : public SBReplayer { + DefaultReplayer(SBDeserializer &deserializer, void (*f)(Args...)) + : SBReplayer(deserializer), f(f) {} + + void operator()() const override { + DeserializationHelper::template deserialized::doit( + m_deserializer, f); + m_deserializer.HandleReplayResultVoid(); + } + + void (*f)(Args...); +}; + +/// The custom replayer is similar to the default replayer but doesn't invoke +/// the original function directly, but a different function with the same +/// interface. The allows us to intercept replay calls and have custom +/// implementation, for example to ignore a particular argument. +template struct CustomReplayer; +template +struct CustomReplayer : public SBReplayer { + CustomReplayer(SBDeserializer &deserializer, Result (*f)(Args...), + Result (*g)(Args...)) + : SBReplayer(deserializer), f(f), g(g) {} + + void operator()() const override { + m_deserializer.HandleReplayResult( + DeserializationHelper::template deserialized::doit( + m_deserializer, f)); + } + + /// The replayed function. + Result (*f)(Args...); + /// A custom function. + Result (*g)(Args...); +}; + +/// Partial specialization for function returning a void type. +template +struct CustomReplayer : public SBReplayer { + CustomReplayer(SBDeserializer &deserializer, void (*f)(Args...), + void (*g)(Args...)) + : SBReplayer(deserializer), f(f), g(g) {} + + void operator()() const override { + DeserializationHelper::template deserialized::doit( + m_deserializer, f); + m_deserializer.HandleReplayResultVoid(); + } + + void (*f)(Args...); + void (*g)(Args...); +}; + +/// The registry contains a unique mapping between functions and their ID. The +/// IDs can be serialized and deserialized to replay a function. Functions need +/// to be registered with the registry for this to work. +class SBRegistry { +public: + static SBRegistry &Instance() { + static SBRegistry g_registry; + return g_registry; + } + + SBRegistry() : m_id(1) { Init(); } + + /// Register a default replayer for a function. + template void Register(Signature *f, unsigned ID) { + DoRegister(uintptr_t(f), new DefaultReplayer(m_deserializer, f), + ID); + } + + /// Register a replayer that invokes a custom function with the same + /// signature as the replayed function. + template + void Register(Signature *f, unsigned ID, Signature *g) { + DoRegister(uintptr_t(f), + new CustomReplayer(m_deserializer, f, g), ID); + } + + /// Replay functions from a file. + bool Replay(const FileSpec &file); + + /// Returns the ID for a given function address. + unsigned GetID(uintptr_t addr) { + unsigned id = m_sbreplayers[addr].second; + assert(id != 0); + return id; + } + +private: + /// Initialize the registry by registering function. + void Init(); + + /// Register the given replayer for a function (and the ID mapping). + void DoRegister(uintptr_t RunID, SBReplayer *replayer, unsigned id) { + m_sbreplayers[RunID] = std::make_pair(replayer, id); + m_ids[id] = replayer; + } + + // The deserializer is tightly coupled with the registry. + SBDeserializer m_deserializer; + + /// Mapping of function addresses to replayers and their ID. + std::map> m_sbreplayers; + + /// Mapping of IDs to replayer instances. + std::map m_ids; + + /// Unique ID for every function registered with the registry. + unsigned m_id; +}; + +/// To be used as the "Runtime ID" of a constructor. It also invokes the +/// constructor when called. +template struct construct; +template struct construct { + static Class *doit(Args... args) { return new Class(args...); } +}; + +/// To be used as the "Runtime ID" of a member function. It also invokes the +/// member function when called. +template struct invoke; +template +struct invoke { + template struct method { + static Result doit(Class *c, Args... args) { return (c->*m)(args...); } + }; +}; + +template +struct invoke { + template struct method_const { + static Result doit(Class *c, Args... args) { return (c->*m)(args...); } + }; +}; + +template +struct invoke { + template struct method { + static void doit(Class *c, Args... args) { (c->*m)(args...); } + }; +}; + +/// Maps an object to an index for serialization. Indices are unique and +/// incremented for every new object. +/// +/// Indices start at 1 in order to differentiate with an invalid index (0) in +/// the serialized buffer. +class SBObjectToIndex { +public: + SBObjectToIndex() : m_index(1) {} + + template unsigned GetIndexForObject(T *t) { + return GetIndexForObjectImpl((void *)t); + } + +private: + unsigned GetIndexForObjectImpl(void *object) { + auto it = m_mapping.find(object); + if (it == m_mapping.end()) + m_mapping[object] = Increment(); + return m_mapping[object]; + } + + unsigned Increment() { + std::lock_guard guard(m_mutex); + return ++m_index; + } + + unsigned m_index; + std::mutex m_mutex; + llvm::DenseMap m_mapping; +}; + +/// Serializes functions, their arguments and their return type to a stream. +class SBSerializer { +public: + SBSerializer(llvm::raw_ostream &stream = llvm::outs()) : m_stream(stream) {} + + /// Recursively serialize all the given arguments. + template + void SerializeAll(const Head &head, const Tail &... tail) { + Serialize(head); + SerializeAll(tail...); + } + + void SerializeAll() {} + +private: + /// Serialize pointers. We need to differentiate between pointers to + /// fundamental types (in which case we serialize its value) and pointer to + /// objects (in which case we serialize their index). + template void Serialize(T *t) { + if (std::is_fundamental::value) { + Serialize(*t); + } else { + int idx = m_tracker.GetIndexForObject(t); + Serialize(idx); + } + } + + /// Serialize references. We need to differentiate between references to + /// fundamental types (in which case we serialize its value) and references + /// to objects (in which case we serialize their index). + template void Serialize(T &t) { + if (std::is_fundamental::value) { + Serialize(t); + } else { + int idx = m_tracker.GetIndexForObject(&t); + Serialize(idx); + } + } + + void Serialize(void *v) { + // Do nothing. + } + + void Serialize(const char *t) { + m_stream << t; + m_stream.write(0x0); + } + +#define SB_SERIALIZER_POD(Type) \ + void Serialize(Type t) { \ + m_stream.write(reinterpret_cast(&t), sizeof(Type)); \ + } + + SB_SERIALIZER_POD(bool); + SB_SERIALIZER_POD(char); + SB_SERIALIZER_POD(double); + SB_SERIALIZER_POD(float); + SB_SERIALIZER_POD(int); + SB_SERIALIZER_POD(long long); + SB_SERIALIZER_POD(long); + SB_SERIALIZER_POD(short); + SB_SERIALIZER_POD(unsigned char); + SB_SERIALIZER_POD(unsigned int); + SB_SERIALIZER_POD(unsigned long long); + SB_SERIALIZER_POD(unsigned long); + SB_SERIALIZER_POD(unsigned short); + + /// Serialization stream. + llvm::raw_ostream &m_stream; + + /// Mapping of objects to indices. + SBObjectToIndex m_tracker; +}; + +class SBProvider : public Provider { +public: + SBProvider(const FileSpec &directory) + : Provider(directory), + m_stream(directory.CopyByAppendingPathComponent("sbapi.bin").GetPath(), + m_ec, llvm::sys::fs::OpenFlags::F_None), + m_serializer(m_stream) { + m_info.name = "sbapi"; + m_info.files.push_back("sbapi.bin"); + } + + SBSerializer &GetSerializer() { return m_serializer; } + static char ID; + +private: + std::error_code m_ec; + llvm::raw_fd_ostream m_stream; + SBSerializer m_serializer; +}; + +/// RAII object that tracks the function invocations and their return value. +/// +/// API calls are only captured when the API boundary is crossed. Once we're in +/// the API layer, and another API function is called, it doesn't need to be +/// recorded. +/// +/// When a call is recored, its result is always recorded as well, even if the +/// function returns a void. For functions that return by value, RecordResult +/// should be used. Otherwise a sentinel value (0) will be serialized. +class SBRecorder { +public: + SBRecorder(llvm::StringRef pretty_func = "") + : m_pretty_func(pretty_func), m_serializer(nullptr), + m_local_boundary(false), m_result_recorded(false) { + if (!g_global_boundary) { + g_global_boundary = true; + m_local_boundary = true; + } + } + + ~SBRecorder() { + UpdateBoundary(); + RecordOmittedResult(); + } + + void SetSerializer(SBSerializer &serializer) { m_serializer = &serializer; } + + /// Records a single function call. + template + void Record(Result (*f)(FArgs...), const RArgs &... args) { + if (!ShouldCapture()) { + return; + } + + unsigned id = SBRegistry::Instance().GetID(uintptr_t(f)); + + if (Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)) + log->Printf("Recording #%u '%s'", id, m_pretty_func.data()); + + m_serializer->SerializeAll(id); + m_serializer->SerializeAll(args...); + } + + /// Records a single function call. + template + void Record(void (*f)(Args...), const Args &... args) { + if (!ShouldCapture()) { + return; + } + + unsigned id = SBRegistry::Instance().GetID(uintptr_t(f)); + + if (Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)) + log->Printf("Recording #%u '%s'", id, m_pretty_func.data()); + + m_serializer->SerializeAll(id); + m_serializer->SerializeAll(args...); + } + + /// Record the result of a function call. + template Result RecordResult(const Result &r) { + UpdateBoundary(); + if (ShouldCapture()) { + m_serializer->SerializeAll(r); + m_result_recorded = true; + } + return r; + } + + /// Serialize an omitted return value. + void RecordOmittedResult() { + if (m_result_recorded) + return; + if (!ShouldCapture()) + return; + + m_serializer->SerializeAll(0); + m_result_recorded = true; + } + +private: + void UpdateBoundary() { + if (m_local_boundary) { + g_global_boundary = false; + } + } + + bool ShouldCapture() { return m_serializer && m_local_boundary; } + + /// Pretty function for logging. + llvm::StringRef m_pretty_func; + + /// The serializer is set from the reproducer framework. If the serializer is + /// not set, we're not in recording mode. + SBSerializer *m_serializer; + + /// Whether this function call was the one crossing the API boundary. + bool m_local_boundary; + + /// Whether the return value was recorded explicitly. + bool m_result_recorded; + + /// Whether we're currently across the API boundary. + static std::atomic g_global_boundary; +}; + +} // namespace repro +} // namespace lldb_private + +#define SB_RECORD_CONSTRUCTOR(Class, Signature, ...) \ + if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) { \ + lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__); \ + sb_recorder.SetSerializer( \ + g->GetOrCreate().GetSerializer()); \ + sb_recorder.Record(&lldb_private::repro::construct::doit, \ + __VA_ARGS__); \ + sb_recorder.RecordResult(this); \ + } + +#define SB_RECORD_CONSTRUCTOR_NO_ARGS(Class) \ + if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) { \ + lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__); \ + sb_recorder.SetSerializer( \ + g->GetOrCreate().GetSerializer()); \ + sb_recorder.Record(&lldb_private::repro::construct::doit); \ + sb_recorder.RecordResult(this); \ + } + +#define SB_RECORD_METHOD(Result, Class, Method, Signature, ...) \ + lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__); \ + if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) { \ + sb_recorder.SetSerializer( \ + g->GetOrCreate().GetSerializer()); \ + sb_recorder.Record(&lldb_private::repro::invoke::method<&Class::Method>::doit, \ + this, __VA_ARGS__); \ + } + +#define SB_RECORD_METHOD_CONST(Result, Class, Method, Signature, ...) \ + lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__); \ + if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) { \ + sb_recorder.SetSerializer( \ + g->GetOrCreate().GetSerializer()); \ + sb_recorder.Record( \ + &lldb_private::repro::invoke::method_const<&Class::Method>::doit, \ + this, __VA_ARGS__); \ + } + +#define SB_RECORD_METHOD_NO_ARGS(Result, Class, Method) \ + lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__); \ + if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) { \ + sb_recorder.SetSerializer( \ + g->GetOrCreate().GetSerializer()); \ + sb_recorder.Record(&lldb_private::repro::invoke::method<&Class::Method>::doit, \ + this); \ + } + +#define SB_RECORD_METHOD_CONST_NO_ARGS(Result, Class, Method) \ + lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__); \ + if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) { \ + sb_recorder.SetSerializer( \ + g->GetOrCreate().GetSerializer()); \ + sb_recorder.Record( \ + &lldb_private::repro::invoke::method_const<&Class::Method>::doit, \ + this); \ + } + +#define SB_RECORD_STATIC_METHOD(Result, Class, Method, Signature, ...) \ + lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__); \ + if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) { \ + sb_recorder.SetSerializer( \ + g->GetOrCreate().GetSerializer()); \ + sb_recorder.Record(static_cast(&Class::Method), \ + __VA_ARGS__); \ + } + +#define SB_RECORD_STATIC_METHOD_NO_ARGS(Result, Class, Method) \ + lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__); \ + if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) { \ + sb_recorder.SetSerializer( \ + g->GetOrCreate().GetSerializer()); \ + sb_recorder.Record(static_cast(&Class::Method)); \ + } + +#define SB_RECORD_RESULT(Result) sb_recorder.RecordResult(Result); + +#endif Index: tools/driver/Driver.cpp =================================================================== --- tools/driver/Driver.cpp +++ tools/driver/Driver.cpp @@ -14,6 +14,7 @@ #include "lldb/API/SBDebugger.h" #include "lldb/API/SBHostOS.h" #include "lldb/API/SBLanguageRuntime.h" +#include "lldb/API/SBReproducer.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" @@ -910,6 +911,11 @@ return 1; } + SBReproducer reproducer; + if (reproducer.Replay()) { + return 0; + } + SBHostOS::ThreadCreated(""); signal(SIGINT, sigint_handler);