Index: include/lldb/API/SBValue.h =================================================================== --- include/lldb/API/SBValue.h +++ include/lldb/API/SBValue.h @@ -432,6 +432,27 @@ //------------------------------------------------------------------ lldb::ValueObjectSP GetSP () const; + + //------------------------------------------------------------------ + /// Returns the number of memory history threads (i.e. recorded malloc/free stack + /// traces) associated with this value object. The list of history threads is + /// cached, so the MemoryHistory plugin is queried only once for an SBValue. + /// + /// @return + /// The number of associated history threads for this SBValue. + //------------------------------------------------------------------ + uint32_t + GetNumMemoryHistoryThreads (); + + //------------------------------------------------------------------ + /// Retrieves the memory history thread by its index. + /// + /// @return + /// An SBThread for the history thread with the specified index or an invalid + /// SBThread object if the index is out of bounds. + //------------------------------------------------------------------ + lldb::SBThread + GetMemoryHistoryThreadAtIndex (uint32_t idx); protected: friend class SBBlock; Index: scripts/Python/interface/SBValue.i =================================================================== --- scripts/Python/interface/SBValue.i +++ scripts/Python/interface/SBValue.i @@ -410,6 +410,12 @@ ) GetExpressionPath; bool GetExpressionPath (lldb::SBStream &description, bool qualify_cxx_base_classes); + + uint32_t + GetNumMemoryHistoryThreads (); + + lldb::SBThread + GetMemoryHistoryThreadAtIndex (uint32_t idx); %pythoncode %{ def __get_dynamic__ (self): Index: source/API/SBValue.cpp =================================================================== --- source/API/SBValue.cpp +++ source/API/SBValue.cpp @@ -37,6 +37,7 @@ #include "lldb/Symbol/Variable.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/MemoryHistory.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" @@ -52,6 +53,8 @@ using namespace lldb; using namespace lldb_private; +typedef std::shared_ptr HistoryThreadsSP; + class ValueImpl { public: @@ -66,7 +69,8 @@ m_valobj_sp(in_valobj_sp), m_use_dynamic(use_dynamic), m_use_synthetic(use_synthetic), - m_name (name) + m_name (name), + m_history_threads() { if (!m_name.IsEmpty() && m_valobj_sp) m_valobj_sp->SetName(m_name); @@ -76,7 +80,8 @@ m_valobj_sp(rhs.m_valobj_sp), m_use_dynamic(rhs.m_use_dynamic), m_use_synthetic(rhs.m_use_synthetic), - m_name (rhs.m_name) + m_name (rhs.m_name), + m_history_threads() { } @@ -89,6 +94,7 @@ m_use_dynamic = rhs.m_use_dynamic; m_use_synthetic = rhs.m_use_synthetic; m_name = rhs.m_name; + m_history_threads = rhs.m_history_threads; } return *this; } @@ -227,12 +233,42 @@ else return StackFrameSP(); } + + HistoryThreadsSP + GetHistoryThreads() + { + if (! m_history_threads) + { + m_history_threads.reset(new HistoryThreads()); + + if (! m_valobj_sp) { + return m_history_threads; + } + + ProcessSP process_sp = m_valobj_sp->GetProcessSP(); + if (! process_sp.get()) { + return m_history_threads; + } + + const MemoryHistorySP &memory_history = MemoryHistory::FindPlugin(process_sp); + + if (! memory_history.get()) { + return m_history_threads; + } + addr_t pointer_value = m_valobj_sp->GetPointerValue(); + m_history_threads.reset(new HistoryThreads(memory_history->GetHistoryThreads(pointer_value))); + } + + return m_history_threads; + } + private: lldb::ValueObjectSP m_valobj_sp; lldb::DynamicValueType m_use_dynamic; bool m_use_synthetic; ConstString m_name; + HistoryThreadsSP m_history_threads; }; class ValueLocker @@ -1836,3 +1872,29 @@ sb_watchpoint = Dereference().Watch (resolve_location, read, write, error); return sb_watchpoint; } + +uint32_t +SBValue::GetNumMemoryHistoryThreads () +{ + if (! IsValid()) + return 0; + HistoryThreadsSP history_threads = m_opaque_sp->GetHistoryThreads(); + if (! history_threads.get()) + return 0; + + return history_threads->size(); +} + +lldb::SBThread +SBValue::GetMemoryHistoryThreadAtIndex (uint32_t idx) +{ + if (! IsValid()) + return SBThread(); + HistoryThreadsSP history_threads = m_opaque_sp->GetHistoryThreads(); + if (! history_threads.get()) + return SBThread(); + if (idx >= history_threads->size()) + return SBThread(); + + return (*history_threads)[idx]; +} Index: test/functionalities/asan/TestAsan.py =================================================================== --- test/functionalities/asan/TestAsan.py +++ test/functionalities/asan/TestAsan.py @@ -70,6 +70,22 @@ 'Memory allocated at', 'a.out`f1', 'main.c:%d' % self.line_malloc, 'Memory deallocated at', 'a.out`f2', 'main.c:%d' % self.line_free]) + # do the same using SB API + process = self.dbg.GetSelectedTarget().process + val = process.GetSelectedThread().GetSelectedFrame().EvaluateExpression("pointer") + self.assertEqual(val.GetNumMemoryHistoryThreads(), 2) + + history_thread = val.GetMemoryHistoryThreadAtIndex(0) + self.assertTrue(history_thread.num_frames >= 2) + self.assertEqual(history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), "main.c") + self.assertEqual(history_thread.frames[1].GetLineEntry().GetLine(), self.line_malloc) + + history_thread = val.GetMemoryHistoryThreadAtIndex(1) + self.assertTrue(history_thread.num_frames >= 2) + self.assertEqual(history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), "main.c") + self.assertEqual(history_thread.frames[1].GetLineEntry().GetLine(), self.line_free) + + # now let's break when an ASan report occurs and try the API then self.runCmd("breakpoint set -n __asan_report_error") self.runCmd("continue")