Index: include/lldb/API/SBReproducer.h =================================================================== --- /dev/null +++ include/lldb/API/SBReproducer.h @@ -0,0 +1,174 @@ +//===-- 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/Utility/FileSpec.h" +#include "lldb/Utility/Reproducer.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/YAMLTraits.h" + +#include +#include +#include + +#define SB_RECORD_NO_ARGS \ + repro::SBRecorder sb_recorder; \ + if (auto *g = repro::Reproducer::Instance().GetGenerator()) { \ + repro::SBProvider &p = g->GetOrCreate(); \ + sb_recorder.SetSerializer(&p.GetSerializer()); \ + sb_recorder.RecordCall(__PRETTY_FUNCTION__); \ + } + +#define SB_RECORD(...) \ + repro::SBRecorder sb_recorder; \ + if (auto *g = repro::Reproducer::Instance().GetGenerator()) { \ + repro::SBProvider &p = g->GetOrCreate(); \ + sb_recorder.SetSerializer(&p.GetSerializer()); \ + sb_recorder.RecordCall(__PRETTY_FUNCTION__, __VA_ARGS__); \ + } + +#define SB_RECORD_RETURN(t) sb_recorder.RecordReturn(t) + +namespace lldb { +void GenerateReproducer(); +bool ReplayReproducer(); +} // namespace lldb + +namespace lldb_private { +namespace repro { + +template <> const char *SBDeserializer::Read(); + +class SBRecorder { +public: + SBRecorder() : m_serializer(nullptr), m_local_boundary(false) { + if (!g_global_boundary) { + g_global_boundary = true; + m_local_boundary = true; + } + } + + ~SBRecorder() { UpdateBoundary(); } + + void UpdateBoundary() { + if (m_local_boundary) { + g_global_boundary = false; + } + } + + template + void RecordCall(const char *name, const Ts &... args) { + if (!ShouldCapture() || !ShouldSerialize()) { + return; + } + m_serializer->Serialize(name); + m_serializer->Serialize(args...); + } + + template const T &RecordReturn(const T &t) { + UpdateBoundary(); + if (!ShouldCapture() || !ShouldSerialize()) { + return t; + } + m_serializer->Serialize(t); + return t; + } + + void SetSerializer(SBSerializer *serializer) { m_serializer = serializer; } + +private: + bool ShouldCapture() { return m_local_boundary; } + bool ShouldSerialize() { return m_serializer != nullptr; } + + SBSerializer *m_serializer; + bool m_local_boundary; + + static thread_local std::atomic g_global_boundary; +}; + +class SBReplayer { +public: + SBReplayer() {} + + void Init(); + + llvm::Error Replay() { + repro::Loader *loader = repro::Reproducer::Instance().GetLoader(); + if (!loader) { + return llvm::make_error( + "Cannot replay when not in replay mode.", + llvm::inconvertibleErrorCode()); + } + + llvm::Optional info = loader->GetProviderInfo("sbapi"); + if (!info) { + return llvm::make_error( + "No SB API provider info available to replay.", + llvm::inconvertibleErrorCode()); + } + + FileSpec file(loader->GetRoot()); + file.AppendPathComponent(info->files.front()); + + auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); + if (auto err = error_or_file.getError()) + return llvm::errorCodeToError(err); + + SBDeserializer deserializer((*error_or_file)->getBuffer()); + while (deserializer.HasData()) { + llvm::StringRef f(deserializer.Read()); + if (f.empty()) + break; + if (m_functions.find(f) == m_functions.end()) { + llvm::outs() << "Could not find function for '" << f << "' (" + << f.size() << ")\n"; + break; + } + m_functions[f](deserializer); + } + + return llvm::Error::success(); + } + +private: + SBIndexToObject m_index_to_object; + llvm::StringMap> m_functions; +}; + +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; +}; + +} // namespace repro +} // namespace lldb_private + +#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; @@ -195,6 +196,115 @@ mutable std::mutex m_mutex; }; +class SBObjectToIndex { +public: + SBObjectToIndex() : m_index(0) {} + + template unsigned GetIndexForObject(T *t) { + return GetIndexForObjectImpl((void *)t); + } + +private: + unsigned GetIndexForObjectImpl(void *object); + unsigned Increment(); + + unsigned m_index; + std::mutex m_mutex; + llvm::DenseMap m_mapping; +}; + +class SBIndexToObject { +public: + template T *GetObjectForIndex(int index) { + void *object = GetObjectForIndexImpl(index); + return static_cast(object); + } + + template void AddObjectForIndex(int index, T *object) { + AddObjectForIndexImpl(index, static_cast(object)); + } + + template void AddObjectForIndex(int index, T &object) { + AddObjectForIndexImpl(index, static_cast(&object)); + } + +private: + void *GetObjectForIndexImpl(int index); + void AddObjectForIndexImpl(int index, void *object); + + llvm::DenseMap m_mapping; +}; + +class SBSerializer { +public: + SBSerializer(llvm::raw_ostream &stream) : m_stream(stream) {} + + void Serialize() {} + + template + void Serialize(const T &t, const Ts &... ts) { + Write(t); + Serialize(ts...); + } + +private: + template void Write(T *t) { + int idx = m_tracker.GetIndexForObject(t); + Write(idx); + } + + template void Write(T &t) { + int idx = m_tracker.GetIndexForObject(&t); + Write(idx); + } + + void Write(std::string t); + void Write(const char *t); + +#define SB_SERIALIZER_POD(Type) \ + void Write(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); + +private: + llvm::raw_ostream &m_stream; + SBObjectToIndex m_tracker; +}; + +class SBDeserializer { +public: + SBDeserializer(llvm::StringRef buffer) : m_buffer(buffer), m_offset(0) {} + + template T Read() { + T t; + std::memcpy((char *)&t, &m_buffer.data()[m_offset], sizeof(t)); + m_offset += sizeof(t); + return t; + } + + bool HasData(int offset = 0); + +private: + llvm::StringRef m_buffer; + uint32_t m_offset; +}; + +template <> const char *SBDeserializer::Read(); + } // namespace repro } // namespace lldb_private Index: source/API/CMakeLists.txt =================================================================== --- source/API/CMakeLists.txt +++ source/API/CMakeLists.txt @@ -77,6 +77,7 @@ SBVariablesOptions.cpp SBWatchpoint.cpp SBUnixSignals.cpp + SBReproducer.cpp SystemInitializerFull.cpp ${lldb_python_wrapper} Index: source/API/SBCommandInterpreter.cpp =================================================================== --- source/API/SBCommandInterpreter.cpp +++ source/API/SBCommandInterpreter.cpp @@ -22,6 +22,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" @@ -30,64 +31,79 @@ using namespace lldb_private; SBCommandInterpreterRunOptions::SBCommandInterpreterRunOptions() { + SB_RECORD(this); m_opaque_up.reset(new CommandInterpreterRunOptions()); } SBCommandInterpreterRunOptions::~SBCommandInterpreterRunOptions() = default; bool SBCommandInterpreterRunOptions::GetStopOnContinue() const { + SB_RECORD(this); return m_opaque_up->GetStopOnContinue(); } void SBCommandInterpreterRunOptions::SetStopOnContinue(bool stop_on_continue) { + SB_RECORD(this, stop_on_continue); m_opaque_up->SetStopOnContinue(stop_on_continue); } bool SBCommandInterpreterRunOptions::GetStopOnError() const { + SB_RECORD(this); return m_opaque_up->GetStopOnError(); } void SBCommandInterpreterRunOptions::SetStopOnError(bool stop_on_error) { + SB_RECORD(this, stop_on_error); m_opaque_up->SetStopOnError(stop_on_error); } bool SBCommandInterpreterRunOptions::GetStopOnCrash() const { + SB_RECORD(this); return m_opaque_up->GetStopOnCrash(); } void SBCommandInterpreterRunOptions::SetStopOnCrash(bool stop_on_crash) { + SB_RECORD(this, stop_on_crash); m_opaque_up->SetStopOnCrash(stop_on_crash); } bool SBCommandInterpreterRunOptions::GetEchoCommands() const { + SB_RECORD(this); return m_opaque_up->GetEchoCommands(); } void SBCommandInterpreterRunOptions::SetEchoCommands(bool echo_commands) { + SB_RECORD(this, echo_commands); m_opaque_up->SetEchoCommands(echo_commands); } bool SBCommandInterpreterRunOptions::GetEchoCommentCommands() const { + SB_RECORD(this); return m_opaque_up->GetEchoCommentCommands(); } void SBCommandInterpreterRunOptions::SetEchoCommentCommands(bool echo) { + SB_RECORD(this, echo); m_opaque_up->SetEchoCommentCommands(echo); } bool SBCommandInterpreterRunOptions::GetPrintResults() const { + SB_RECORD(this); return m_opaque_up->GetPrintResults(); } void SBCommandInterpreterRunOptions::SetPrintResults(bool print_results) { + SB_RECORD(this, print_results); m_opaque_up->SetPrintResults(print_results); } bool SBCommandInterpreterRunOptions::GetAddToHistory() const { + SB_RECORD(this); return m_opaque_up->GetAddToHistory(); } void SBCommandInterpreterRunOptions::SetAddToHistory(bool add_to_history) { + SB_RECORD(this, add_to_history); m_opaque_up->SetAddToHistory(add_to_history); } @@ -129,6 +145,7 @@ SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter) : m_opaque_ptr(interpreter) { + SB_RECORD(this, interpreter); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) @@ -139,37 +156,48 @@ } SBCommandInterpreter::SBCommandInterpreter(const SBCommandInterpreter &rhs) - : m_opaque_ptr(rhs.m_opaque_ptr) {} + : m_opaque_ptr(rhs.m_opaque_ptr) { + SB_RECORD(this, rhs); +} SBCommandInterpreter::~SBCommandInterpreter() = default; const SBCommandInterpreter &SBCommandInterpreter:: operator=(const SBCommandInterpreter &rhs) { + SB_RECORD(this, rhs); m_opaque_ptr = rhs.m_opaque_ptr; return *this; } -bool SBCommandInterpreter::IsValid() const { return m_opaque_ptr != nullptr; } +bool SBCommandInterpreter::IsValid() const { + SB_RECORD(this); + return m_opaque_ptr != nullptr; +} bool SBCommandInterpreter::CommandExists(const char *cmd) { + SB_RECORD(this); return (((cmd != nullptr) && IsValid()) ? m_opaque_ptr->CommandExists(cmd) : false); } bool SBCommandInterpreter::AliasExists(const char *cmd) { + SB_RECORD(this, cmd); return (((cmd != nullptr) && IsValid()) ? m_opaque_ptr->AliasExists(cmd) : false); } bool SBCommandInterpreter::IsActive() { + SB_RECORD(this); return (IsValid() ? m_opaque_ptr->IsActive() : false); } bool SBCommandInterpreter::WasInterrupted() const { + SB_RECORD(this); return (IsValid() ? m_opaque_ptr->WasInterrupted() : false); } const char *SBCommandInterpreter::GetIOHandlerControlSequence(char ch) { + SB_RECORD(this, ch); return (IsValid() ? m_opaque_ptr->GetDebugger() .GetTopIOHandlerControlSequence(ch) @@ -181,6 +209,7 @@ SBCommandInterpreter::HandleCommand(const char *command_line, SBCommandReturnObject &result, bool add_to_history) { + SB_RECORD(this, command_line, result, add_to_history); SBExecutionContext sb_exe_ctx; return HandleCommand(command_line, sb_exe_ctx, result, add_to_history); } @@ -188,6 +217,7 @@ lldb::ReturnStatus SBCommandInterpreter::HandleCommand( const char *command_line, SBExecutionContext &override_context, SBCommandReturnObject &result, bool add_to_history) { + SB_RECORD(this, command_line, result, add_to_history); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) @@ -234,6 +264,7 @@ lldb::SBFileSpec &file, lldb::SBExecutionContext &override_context, lldb::SBCommandInterpreterRunOptions &options, lldb::SBCommandReturnObject result) { + SB_RECORD(this, file, override_context, options, result); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { @@ -273,6 +304,8 @@ int SBCommandInterpreter::HandleCompletion( const char *current_line, const char *cursor, const char *last_char, int match_start_point, int max_return_elements, SBStringList &matches) { + SB_RECORD(this, current_line, cursor, last_char, match_start_point, + max_return_elements, matches); SBStringList dummy_descriptions; return HandleCompletionWithDescriptions( current_line, cursor, last_char, match_start_point, max_return_elements, @@ -332,6 +365,8 @@ const char *current_line, uint32_t cursor_pos, int match_start_point, int max_return_elements, SBStringList &matches, SBStringList &descriptions) { + SB_RECORD(this, current_line, cursor_pos, match_start_point, + max_return_elements, matches); const char *cursor = current_line + cursor_pos; const char *last_char = current_line + strlen(current_line); return HandleCompletionWithDescriptions( @@ -344,6 +379,8 @@ int match_start_point, int max_return_elements, lldb::SBStringList &matches) { + SB_RECORD(this, current_line, cursor_pos, match_start_point, + max_return_elements, matches); const char *cursor = current_line + cursor_pos; const char *last_char = current_line + strlen(current_line); return HandleCompletion(current_line, cursor, last_char, match_start_point, @@ -351,18 +388,22 @@ } bool SBCommandInterpreter::HasCommands() { + SB_RECORD(this); return (IsValid() ? m_opaque_ptr->HasCommands() : false); } bool SBCommandInterpreter::HasAliases() { + SB_RECORD(this); return (IsValid() ? m_opaque_ptr->HasAliases() : false); } bool SBCommandInterpreter::HasAliasOptions() { + SB_RECORD(this); return (IsValid() ? m_opaque_ptr->HasAliasOptions() : false); } SBProcess SBCommandInterpreter::GetProcess() { + SB_RECORD(this); SBProcess sb_process; ProcessSP process_sp; if (IsValid()) { @@ -384,6 +425,7 @@ } SBDebugger SBCommandInterpreter::GetDebugger() { + SB_RECORD(this); SBDebugger sb_debugger; if (IsValid()) sb_debugger.reset(m_opaque_ptr->GetDebugger().shared_from_this()); @@ -398,20 +440,24 @@ } bool SBCommandInterpreter::GetPromptOnQuit() { + SB_RECORD(this); return (IsValid() ? m_opaque_ptr->GetPromptOnQuit() : false); } void SBCommandInterpreter::SetPromptOnQuit(bool b) { + SB_RECORD(this, b); if (IsValid()) m_opaque_ptr->SetPromptOnQuit(b); } void SBCommandInterpreter::AllowExitCodeOnQuit(bool allow) { + SB_RECORD(this, allow); if (m_opaque_ptr) m_opaque_ptr->AllowExitCodeOnQuit(allow); } bool SBCommandInterpreter::HasCustomQuitExitCode() { + SB_RECORD(this); bool exited = false; if (m_opaque_ptr) m_opaque_ptr->GetQuitExitCode(exited); @@ -419,12 +465,14 @@ } int SBCommandInterpreter::GetQuitStatus() { + SB_RECORD(this); bool exited = false; return (m_opaque_ptr ? m_opaque_ptr->GetQuitExitCode(exited) : 0); } void SBCommandInterpreter::ResolveCommand(const char *command_line, SBCommandReturnObject &result) { + SB_RECORD(this, command_line, result); result.Clear(); if (command_line && IsValid()) { m_opaque_ptr->ResolveCommand(command_line, result.ref()); @@ -435,20 +483,26 @@ } } -CommandInterpreter *SBCommandInterpreter::get() { return m_opaque_ptr; } +CommandInterpreter *SBCommandInterpreter::get() { + SB_RECORD(this); + return m_opaque_ptr; +} CommandInterpreter &SBCommandInterpreter::ref() { + SB_RECORD(this); assert(m_opaque_ptr); return *m_opaque_ptr; } void SBCommandInterpreter::reset( lldb_private::CommandInterpreter *interpreter) { + SB_RECORD(this); m_opaque_ptr = interpreter; } void SBCommandInterpreter::SourceInitFileInHomeDirectory( SBCommandReturnObject &result) { + SB_RECORD(this, result); result.Clear(); if (IsValid()) { TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget()); @@ -471,6 +525,7 @@ void SBCommandInterpreter::SourceInitFileInCurrentWorkingDirectory( SBCommandReturnObject &result) { + SB_RECORD(this, result); result.Clear(); if (IsValid()) { TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget()); @@ -492,6 +547,7 @@ } SBBroadcaster SBCommandInterpreter::GetBroadcaster() { + SB_RECORD(this); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBBroadcaster broadcaster(m_opaque_ptr, false); @@ -506,21 +562,25 @@ } const char *SBCommandInterpreter::GetBroadcasterClass() { + SB_RECORD_NO_ARGS; return CommandInterpreter::GetStaticBroadcasterClass().AsCString(); } const char *SBCommandInterpreter::GetArgumentTypeAsCString( const lldb::CommandArgumentType arg_type) { + SB_RECORD(arg_type); return CommandObject::GetArgumentTypeAsCString(arg_type); } const char *SBCommandInterpreter::GetArgumentDescriptionAsCString( const lldb::CommandArgumentType arg_type) { + SB_RECORD(arg_type); return CommandObject::GetArgumentDescriptionAsCString(arg_type); } bool SBCommandInterpreter::EventIsCommandInterpreterEvent( const lldb::SBEvent &event) { + SB_RECORD(event); return event.GetBroadcasterClass() == SBCommandInterpreter::GetBroadcasterClass(); } @@ -528,6 +588,7 @@ bool SBCommandInterpreter::SetCommandOverrideCallback( const char *command_name, lldb::CommandOverrideCallback callback, void *baton) { + SB_RECORD(this, command_name, callback, baton); if (command_name && command_name[0] && IsValid()) { llvm::StringRef command_name_str = command_name; CommandObject *cmd_obj = @@ -543,6 +604,7 @@ lldb::SBCommand SBCommandInterpreter::AddMultiwordCommand(const char *name, const char *help) { + SB_RECORD(this, name, help); CommandObjectMultiword *new_command = new CommandObjectMultiword(*m_opaque_ptr, name, help); new_command->SetRemovable(true); @@ -555,6 +617,7 @@ lldb::SBCommand SBCommandInterpreter::AddCommand( const char *name, lldb::SBCommandPluginInterface *impl, const char *help) { + SB_RECORD(this, name, impl, help); lldb::CommandObjectSP new_command_sp; new_command_sp.reset(new CommandPluginInterfaceImplementation( *m_opaque_ptr, name, impl, help)); @@ -569,6 +632,7 @@ SBCommandInterpreter::AddCommand(const char *name, lldb::SBCommandPluginInterface *impl, const char *help, const char *syntax) { + SB_RECORD(this, name, impl, help, syntax); lldb::CommandObjectSP new_command_sp; new_command_sp.reset(new CommandPluginInterfaceImplementation( *m_opaque_ptr, name, impl, help, syntax)); Index: source/API/SBCommandReturnObject.cpp =================================================================== --- source/API/SBCommandReturnObject.cpp +++ source/API/SBCommandReturnObject.cpp @@ -9,6 +9,7 @@ #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBError.h" +#include "lldb/API/SBReproducer.h" #include "lldb/API/SBStream.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -20,25 +21,32 @@ using namespace lldb_private; SBCommandReturnObject::SBCommandReturnObject() - : m_opaque_ap(new CommandReturnObject()) {} + : m_opaque_ap(new CommandReturnObject()) { + SB_RECORD(this); +} SBCommandReturnObject::SBCommandReturnObject(const SBCommandReturnObject &rhs) : m_opaque_ap() { + SB_RECORD(this, rhs); if (rhs.m_opaque_ap) m_opaque_ap.reset(new CommandReturnObject(*rhs.m_opaque_ap)); } SBCommandReturnObject::SBCommandReturnObject(CommandReturnObject *ptr) - : m_opaque_ap(ptr) {} + : m_opaque_ap(ptr) { + SB_RECORD(this, ptr); +} SBCommandReturnObject::~SBCommandReturnObject() = default; CommandReturnObject *SBCommandReturnObject::Release() { + SB_RECORD(this); return m_opaque_ap.release(); } const SBCommandReturnObject &SBCommandReturnObject:: operator=(const SBCommandReturnObject &rhs) { + SB_RECORD(this, rhs); if (this != &rhs) { if (rhs.m_opaque_ap) m_opaque_ap.reset(new CommandReturnObject(*rhs.m_opaque_ap)); @@ -48,7 +56,10 @@ return *this; } -bool SBCommandReturnObject::IsValid() const { return m_opaque_ap != nullptr; } +bool SBCommandReturnObject::IsValid() const { + SB_RECORD(this); + return m_opaque_ap != nullptr; +} const char *SBCommandReturnObject::GetOutput() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); Index: source/API/SBDebugger.cpp =================================================================== --- source/API/SBDebugger.cpp +++ source/API/SBDebugger.cpp @@ -22,6 +22,7 @@ #include "lldb/API/SBFrame.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBProcess.h" +#include "lldb/API/SBReproducer.h" #include "lldb/API/SBSourceManager.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" @@ -108,16 +109,21 @@ bool SBInputReader::IsActive() const { return false; } -SBDebugger::SBDebugger() = default; +SBDebugger::SBDebugger() { SB_RECORD(this); } SBDebugger::SBDebugger(const lldb::DebuggerSP &debugger_sp) - : m_opaque_sp(debugger_sp) {} + : m_opaque_sp(debugger_sp) { + SB_RECORD(this); +} -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(this, rhs); +} SBDebugger::~SBDebugger() = default; SBDebugger &SBDebugger::operator=(const SBDebugger &rhs) { + SB_RECORD(this, rhs); if (this != &rhs) { m_opaque_sp = rhs.m_opaque_sp; } @@ -160,17 +166,21 @@ } SBDebugger SBDebugger::Create() { - return SBDebugger::Create(false, nullptr, nullptr); + SB_RECORD_NO_ARGS; + return SB_RECORD_RETURN(SBDebugger::Create(false, nullptr, nullptr)); } SBDebugger SBDebugger::Create(bool source_init_files) { - return SBDebugger::Create(source_init_files, nullptr, nullptr); + SB_RECORD(source_init_files); + return SB_RECORD_RETURN( + SBDebugger::Create(source_init_files, nullptr, nullptr)); } SBDebugger SBDebugger::Create(bool source_init_files, lldb::LogOutputCallback callback, void *baton) { + SB_RECORD(source_init_files, callback, baton); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBDebugger debugger; @@ -203,7 +213,7 @@ interp.get()->SkipLLDBInitFiles(true); interp.get()->SkipAppInitFiles(true); } - return debugger; + return SB_RECORD_RETURN(debugger); } void SBDebugger::Destroy(SBDebugger &debugger) { @@ -239,23 +249,30 @@ ModuleList::RemoveOrphanSharedModules(mandatory); } -bool SBDebugger::IsValid() const { return m_opaque_sp.get() != nullptr; } +bool SBDebugger::IsValid() const { + SB_RECORD(this); + return m_opaque_sp.get() != nullptr; +} void SBDebugger::SetAsync(bool b) { + SB_RECORD(this, b); if (m_opaque_sp) m_opaque_sp->SetAsyncExecution(b); } bool SBDebugger::GetAsync() { + SB_RECORD(this); return (m_opaque_sp ? m_opaque_sp->GetAsyncExecution() : false); } void SBDebugger::SkipLLDBInitFiles(bool b) { + SB_RECORD(this, b); if (m_opaque_sp) m_opaque_sp->GetCommandInterpreter().SkipLLDBInitFiles(b); } void SBDebugger::SkipAppInitFiles(bool b) { + SB_RECORD(this, b); if (m_opaque_sp) m_opaque_sp->GetCommandInterpreter().SkipAppInitFiles(b); } @@ -264,6 +281,7 @@ // 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(this, fh, transfer_ownership); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) @@ -277,6 +295,7 @@ } void SBDebugger::SetOutputFileHandle(FILE *fh, bool transfer_ownership) { + SB_RECORD(this, fh, transfer_ownership); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) @@ -290,6 +309,7 @@ } void SBDebugger::SetErrorFileHandle(FILE *fh, bool transfer_ownership) { + SB_RECORD(this, fh, transfer_ownership); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) @@ -303,6 +323,7 @@ } FILE *SBDebugger::GetInputFileHandle() { + SB_RECORD(this); if (m_opaque_sp) { StreamFileSP stream_file_sp(m_opaque_sp->GetInputFile()); if (stream_file_sp) @@ -312,6 +333,7 @@ } FILE *SBDebugger::GetOutputFileHandle() { + SB_RECORD(this); if (m_opaque_sp) { StreamFileSP stream_file_sp(m_opaque_sp->GetOutputFile()); if (stream_file_sp) @@ -321,6 +343,7 @@ } FILE *SBDebugger::GetErrorFileHandle() { + SB_RECORD(this); if (m_opaque_sp) { StreamFileSP stream_file_sp(m_opaque_sp->GetErrorFile()); if (stream_file_sp) @@ -330,15 +353,19 @@ } void SBDebugger::SaveInputTerminalState() { + SB_RECORD(this); if (m_opaque_sp) m_opaque_sp->SaveInputTerminalState(); } void SBDebugger::RestoreInputTerminalState() { + SB_RECORD(this); if (m_opaque_sp) m_opaque_sp->RestoreInputTerminalState(); } + SBCommandInterpreter SBDebugger::GetCommandInterpreter() { + SB_RECORD(this); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBCommandInterpreter sb_interpreter; @@ -351,10 +378,11 @@ static_cast(m_opaque_sp.get()), static_cast(sb_interpreter.get())); - return sb_interpreter; + return SB_RECORD_RETURN(sb_interpreter); } void SBDebugger::HandleCommand(const char *command) { + SB_RECORD(this, command); if (m_opaque_sp) { TargetSP target_sp(m_opaque_sp->GetSelectedTarget()); std::unique_lock lock; @@ -928,6 +956,7 @@ void SBDebugger::RunCommandInterpreter(bool auto_handle_events, bool spawn_thread) { + SB_RECORD(this, auto_handle_events, spawn_thread); if (m_opaque_sp) { CommandInterpreterRunOptions options; @@ -943,6 +972,8 @@ bool &stopped_for_crash) { + SB_RECORD(this, 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 @@ -11,11 +11,13 @@ #include #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" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/Reproducer.h" #include "lldb/Utility/Stream.h" #include "llvm/ADT/SmallString.h" @@ -23,21 +25,29 @@ 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(this); +} 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(this, rhs); +} SBFileSpec::SBFileSpec(const lldb_private::FileSpec &fspec) - : m_opaque_ap(new lldb_private::FileSpec(fspec)) {} + : m_opaque_ap(new lldb_private::FileSpec(fspec)) { + SB_RECORD(this, fspec); +} // Deprecated!!! SBFileSpec::SBFileSpec(const char *path) : m_opaque_ap(new FileSpec(path)) { + SB_RECORD(this, path); FileSystem::Instance().Resolve(*m_opaque_ap); } SBFileSpec::SBFileSpec(const char *path, bool resolve) : m_opaque_ap(new FileSpec(path)) { + SB_RECORD(this, path, resolve); if (resolve) FileSystem::Instance().Resolve(*m_opaque_ap); } @@ -45,14 +55,19 @@ SBFileSpec::~SBFileSpec() {} const SBFileSpec &SBFileSpec::operator=(const SBFileSpec &rhs) { + SB_RECORD(this, rhs); if (this != &rhs) *m_opaque_ap = *rhs.m_opaque_ap; - return *this; + return SB_RECORD_RETURN(*this); } -bool SBFileSpec::IsValid() const { return m_opaque_ap->operator bool(); } +bool SBFileSpec::IsValid() const { + SB_RECORD(this); + return m_opaque_ap->operator bool(); +} bool SBFileSpec::Exists() const { + SB_RECORD(this); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool result = FileSystem::Instance().Exists(*m_opaque_ap); @@ -66,11 +81,13 @@ } bool SBFileSpec::ResolveExecutableLocation() { + SB_RECORD(this); return FileSystem::Instance().ResolveExecutableLocation(*m_opaque_ap); } int SBFileSpec::ResolvePath(const char *src_path, char *dst_path, size_t dst_len) { + SB_RECORD(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 +95,7 @@ } const char *SBFileSpec::GetFilename() const { + SB_RECORD(this); const char *s = m_opaque_ap->GetFilename().AsCString(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); @@ -94,6 +112,7 @@ } const char *SBFileSpec::GetDirectory() const { + SB_RECORD(this); FileSpec directory{*m_opaque_ap}; directory.GetFilename().Clear(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); @@ -110,6 +129,7 @@ } void SBFileSpec::SetFilename(const char *filename) { + SB_RECORD(this, filename); if (filename && filename[0]) m_opaque_ap->GetFilename().SetCString(filename); else @@ -117,6 +137,7 @@ } void SBFileSpec::SetDirectory(const char *directory) { + SB_RECORD(this, directory); if (directory && directory[0]) m_opaque_ap->GetDirectory().SetCString(directory); else @@ -124,6 +145,7 @@ } uint32_t SBFileSpec::GetPath(char *dst_path, size_t dst_len) const { + SB_RECORD(this, dst_path, dst_len); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t result = m_opaque_ap->GetPath(dst_path, dst_len); @@ -140,24 +162,32 @@ } const lldb_private::FileSpec *SBFileSpec::operator->() const { + SB_RECORD(this); return m_opaque_ap.get(); } const lldb_private::FileSpec *SBFileSpec::get() const { + SB_RECORD(this); return m_opaque_ap.get(); } const lldb_private::FileSpec &SBFileSpec::operator*() const { + SB_RECORD(this); return *m_opaque_ap; } -const lldb_private::FileSpec &SBFileSpec::ref() const { return *m_opaque_ap; } +const lldb_private::FileSpec &SBFileSpec::ref() const { + SB_RECORD(this); + return *m_opaque_ap; +} void SBFileSpec::SetFileSpec(const lldb_private::FileSpec &fs) { + SB_RECORD(this, fs); *m_opaque_ap = fs; } bool SBFileSpec::GetDescription(SBStream &description) const { + SB_RECORD(this, description); Stream &strm = description.ref(); char path[PATH_MAX]; if (m_opaque_ap->GetPath(path, sizeof(path))) @@ -166,5 +196,6 @@ } void SBFileSpec::AppendPathComponent(const char *fn) { + SB_RECORD(this, fn); m_opaque_ap->AppendPathComponent(fn); } Index: source/API/SBHostOS.cpp =================================================================== --- source/API/SBHostOS.cpp +++ source/API/SBHostOS.cpp @@ -13,6 +13,7 @@ #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 +84,7 @@ } SBFileSpec SBHostOS::GetUserHomeDirectory() { + SB_RECORD_NO_ARGS; SBFileSpec sb_fspec; llvm::SmallString<64> home_dir_path; @@ -91,7 +93,8 @@ FileSystem::Instance().Resolve(homedir); sb_fspec.SetFileSpec(homedir); - return sb_fspec; + + return SB_RECORD_RETURN(sb_fspec); } lldb::thread_t SBHostOS::ThreadCreate(const char *name, Index: source/API/SBReproducer.cpp =================================================================== --- /dev/null +++ source/API/SBReproducer.cpp @@ -0,0 +1,315 @@ +//===-- 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 "lldb/API/SBReproducer.h" +#include "lldb/API/LLDB.h" + +#include "lldb/Host/FileSystem.h" +#include "lldb/Utility/FileSpec.h" + +using namespace lldb; +using namespace lldb_private::repro; +using namespace lldb_private; +using namespace llvm; + +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()); +} + +template +T *ReadObject(SBDeserializer &s, SBIndexToObject &index_to_object) { + return index_to_object.GetObjectForIndex(s.Read()); +} + +#define SB_REGISTER(function, implementation) \ + { \ + m_functions[function] = [&](SBDeserializer &s) { implementation }; \ + } + +void SBReplayer::Init() { + SB_REGISTER("lldb::SBFileSpec::SBFileSpec(const char *, bool)", { + auto t = s.Read(); // this + auto a = s.Read(); + auto b = s.Read(); + auto x = new SBFileSpec(a, b); + m_index_to_object.AddObjectForIndex(t, x); + }); + + SB_REGISTER("lldb::SBFileSpec::SBFileSpec(const char *)", { + auto t = s.Read(); // this + auto a = s.Read(); + auto x = new SBFileSpec(a); + m_index_to_object.AddObjectForIndex(t, x); + }); + + SB_REGISTER("static lldb::SBFileSpec lldb::SBHostOS::GetUserHomeDirectory()", + { + auto r = s.Read(); // return + auto x = new SBFileSpec(SBHostOS::GetUserHomeDirectory()); + m_index_to_object.AddObjectForIndex(r, x); + }); + + SB_REGISTER("void lldb::SBFileSpec::AppendPathComponent(const char *)", { + auto a = ReadObject(s, m_index_to_object); + auto b = s.Read(); + a->AppendPathComponent(b); + }); + + SB_REGISTER("lldb::SBFileSpec::SBFileSpec(const lldb::SBFileSpec &)", { + auto t = s.Read(); // this + auto a = m_index_to_object.GetObjectForIndex(s.Read()); + auto x = new SBFileSpec(*a); + m_index_to_object.AddObjectForIndex(t, x); + }); + + SB_REGISTER("bool lldb::SBFileSpec::Exists() const", { + auto a = m_index_to_object.GetObjectForIndex(s.Read()); + a->Exists(); + }); + + SB_REGISTER("lldb::SBCommandInterpreterRunOptions::" + "SBCommandInterpreterRunOptions()", + { + auto t = s.Read(); // this + auto x = new SBCommandInterpreterRunOptions(); + m_index_to_object.AddObjectForIndex(t, x); + }); + + SB_REGISTER( + "void lldb::SBCommandInterpreterRunOptions::SetStopOnError(bool)", { + auto t = + m_index_to_object.GetObjectForIndex( + s.Read()); + auto a = s.Read(); + t->SetStopOnError(a); + }); + + SB_REGISTER( + "void lldb::SBCommandInterpreterRunOptions::SetStopOnCrash(bool)", { + auto t = + m_index_to_object.GetObjectForIndex( + s.Read()); + auto a = s.Read(); + t->SetStopOnCrash(a); + }); + + SB_REGISTER("lldb::SBCommandInterpreter::SBCommandInterpreter(lldb_private::" + "CommandInterpreter *)", + { + auto t = s.Read(); + auto a = + m_index_to_object.GetObjectForIndex( + s.Read()); + auto x = new SBCommandInterpreter(a); + m_index_to_object.AddObjectForIndex(t, x); + }); + + SB_REGISTER("bool lldb::SBCommandInterpreter::IsValid() const", { + auto t = m_index_to_object.GetObjectForIndex( + s.Read()); // this + t->IsValid(); + }); + + SB_REGISTER("lldb::SBDebugger::SBDebugger()", { + auto t = s.Read(); + auto x = new SBDebugger(); + m_index_to_object.AddObjectForIndex(t, x); + }); + + SB_REGISTER("static lldb::SBDebugger lldb::SBDebugger::Create(bool)", { + auto a = s.Read(); + auto r = s.Read(); + auto x = new SBDebugger(SBDebugger::Create(a)); + m_index_to_object.AddObjectForIndex(r, x); + }); + + SB_REGISTER("lldb::SBDebugger::SBDebugger(const lldb::SBDebugger &)", { + auto t = s.Read(); // this + auto a = m_index_to_object.GetObjectForIndex(s.Read()); + auto x = new SBDebugger(*a); + m_index_to_object.AddObjectForIndex(t, x); + }); + + SB_REGISTER( + "lldb::SBCommandInterpreter lldb::SBDebugger::GetCommandInterpreter()", { + auto t = m_index_to_object.GetObjectForIndex( + s.Read()); // this + auto r = s.Read(); + auto x = new SBCommandInterpreter(t->GetCommandInterpreter()); + m_index_to_object.AddObjectForIndex(r, x); + }); + + SB_REGISTER("void " + "lldb::SBCommandInterpreter::SourceInitFileInHomeDirectory(lldb::" + "SBCommandReturnObject &)", + { + auto t = + m_index_to_object.GetObjectForIndex( + s.Read()); // this + auto a = + m_index_to_object.GetObjectForIndex( + s.Read()); + t->SourceInitFileInHomeDirectory(*a); + }); + + SB_REGISTER("lldb::SBCommandReturnObject::SBCommandReturnObject()", { + auto t = s.Read(); // this + auto x = new SBCommandReturnObject(); + m_index_to_object.AddObjectForIndex(t, x); + }); + + SB_REGISTER("lldb::SBCommandInterpreter::SBCommandInterpreter(const " + "lldb::SBCommandInterpreter &)", + { + auto t = s.Read(); // this + auto a = + m_index_to_object.GetObjectForIndex( + s.Read()); + auto x = new SBCommandInterpreter(*a); + m_index_to_object.AddObjectForIndex(t, x); + }); + + SB_REGISTER("void lldb::SBCommandInterpreter::AllowExitCodeOnQuit(bool)", { + auto t = m_index_to_object.GetObjectForIndex( + s.Read()); // this + auto a = s.Read(); + t->AllowExitCodeOnQuit(a); + }); + + SB_REGISTER( + "lldb::SBDebugger &lldb::SBDebugger::operator=(const lldb::SBDebugger &)", + { + auto t = s.Read(); // this + auto a = m_index_to_object.GetObjectForIndex(s.Read()); + auto x = new SBDebugger(*a); + m_index_to_object.AddObjectForIndex(t, x); + }); + + SB_REGISTER("int lldb::SBCommandInterpreter::GetQuitStatus()", { + auto t = m_index_to_object.GetObjectForIndex( + s.Read()); // this + t->GetQuitStatus(); + }); + + SB_REGISTER("void lldb::SBDebugger::SkipLLDBInitFiles(bool)", { + auto t = + m_index_to_object.GetObjectForIndex(s.Read()); // this + auto a = s.Read(); + t->SkipLLDBInitFiles(a); + }); + + SB_REGISTER("void lldb::SBDebugger::SkipAppInitFiles(bool)", { + auto t = + m_index_to_object.GetObjectForIndex(s.Read()); // this + auto a = s.Read(); + t->SkipAppInitFiles(a); + }); + + SB_REGISTER("bool lldb::SBDebugger::GetAsync()", { + auto t = + m_index_to_object.GetObjectForIndex(s.Read()); // this + t->GetAsync(); + }); + + SB_REGISTER("void lldb::SBDebugger::SetAsync(bool)", { + auto t = + m_index_to_object.GetObjectForIndex(s.Read()); // this + auto a = s.Read(); + t->SetAsync(a); + }); + + SB_REGISTER( + "void lldb::SBDebugger::RunCommandInterpreter(bool, bool, " + "lldb::SBCommandInterpreterRunOptions &, int &, bool &, bool &)", + { + auto t = m_index_to_object.GetObjectForIndex( + s.Read()); // this + auto a = s.Read(); + auto b = s.Read(); + auto c = + m_index_to_object.GetObjectForIndex( + s.Read()); + auto d = s.Read(); + auto e = s.Read(); + auto f = s.Read(); + t->RunCommandInterpreter(a, b, *c, d, e, f); + }); + + SB_REGISTER("void lldb::SBDebugger::SetErrorFileHandle(FILE *, bool)", { + s.Read(); // this + s.Read(); + s.Read(); + + // Do nothing. + }); + + SB_REGISTER("void lldb::SBDebugger::SetOutputFileHandle(FILE *, bool)", { + s.Read(); // this + s.Read(); + s.Read(); + + // Do nothing. + }); + + SB_REGISTER("void lldb::SBDebugger::SetInputFileHandle(FILE *, bool)", { + auto t = + m_index_to_object.GetObjectForIndex(s.Read()); // this + s.Read(); + s.Read(); + + FileSpec fs = GetCommandFile(); + if (!fs) + return; + + FILE *f = FileSystem::Instance().Fopen(fs.GetPath().c_str(), "r"); + if (f == nullptr) + return; + t->SetInputFileHandle(f, true); + }); + + llvm::outs() << "Registered " << m_functions.size() << " functions\n"; +} + +namespace lldb { +void GenerateReproducer() { + if (auto *g = Reproducer::Instance().GetGenerator()) { + g->Keep(); + } +} + +bool ReplayReproducer() { + if (auto *g = Reproducer::Instance().GetLoader()) { + SBReplayer replayer; + replayer.Init(); + if (auto e = replayer.Replay()) { + llvm::outs() << toString(std::move(e)) << '\n'; + } + return true; + } + return false; +} +} // namespace lldb + +char SBProvider::ID = 0; +thread_local std::atomic SBRecorder::g_global_boundary; Index: source/Utility/Reproducer.cpp =================================================================== --- source/Utility/Reproducer.cpp +++ source/Utility/Reproducer.cpp @@ -221,5 +221,51 @@ return it->second; } +unsigned SBObjectToIndex::GetIndexForObjectImpl(void *object) { + auto it = m_mapping.find(object); + if (it == m_mapping.end()) + m_mapping[object] = Increment(); + return m_mapping[object]; +} + +unsigned SBObjectToIndex::Increment() { + std::lock_guard guard(m_mutex); + return ++m_index; +} + +void *SBIndexToObject::GetObjectForIndexImpl(int index) { + auto it = m_mapping.find(index); + if (it == m_mapping.end()) { + return nullptr; + } + return m_mapping[index]; +} + +void SBIndexToObject::AddObjectForIndexImpl(int index, void *object) { + if (index == -1) + return; + m_mapping[index] = object; +} + +void SBSerializer::Write(std::string t) { Write(t.c_str()); } + +void SBSerializer::Write(const char *t) { + m_stream << t; + m_stream.write(0x0); +} + +bool SBDeserializer::HasData(int offset) { + return m_offset + offset < m_buffer.size(); +} + +template <> const char *SBDeserializer::Read() { + 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; +} + void ProviderBase::anchor() {} char ProviderBase::ID = 0; 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" @@ -271,6 +272,7 @@ auto arg_value = arg->getValue(); SBFileSpec file(arg_value); if (!file.Exists()) { + GenerateReproducer(); error.SetErrorStringWithFormat( "file specified in --core (-c) option doesn't exist: '%s'", arg_value); @@ -919,6 +921,9 @@ signal(SIGCONT, sigcont_handler); #endif + if (ReplayReproducer()) + return 0; + int exit_code = 0; // Create a scope for driver so that the driver object will destroy itself // before SBDebugger::Terminate() is called.