130fdc8d8SChris Lattner //===-- SBBreakpoint.cpp ----------------------------------------*- C++ -*-===// 230fdc8d8SChris Lattner // 330fdc8d8SChris Lattner // The LLVM Compiler Infrastructure 430fdc8d8SChris Lattner // 530fdc8d8SChris Lattner // This file is distributed under the University of Illinois Open Source 630fdc8d8SChris Lattner // License. See LICENSE.TXT for details. 730fdc8d8SChris Lattner // 830fdc8d8SChris Lattner //===----------------------------------------------------------------------===// 930fdc8d8SChris Lattner 10dbb0abbfSEugene Zelenko // C Includes 11dbb0abbfSEugene Zelenko // C++ Includes 12dbb0abbfSEugene Zelenko // Other libraries and framework includes 13dbb0abbfSEugene Zelenko // Project includes 1430fdc8d8SChris Lattner #include "lldb/API/SBBreakpoint.h" 1530fdc8d8SChris Lattner #include "lldb/API/SBBreakpointLocation.h" 1630fdc8d8SChris Lattner #include "lldb/API/SBDebugger.h" 179fed0d85SGreg Clayton #include "lldb/API/SBEvent.h" 1830fdc8d8SChris Lattner #include "lldb/API/SBProcess.h" 19dde9cff3SCaroline Tice #include "lldb/API/SBStream.h" 205e09c8c3SJim Ingham #include "lldb/API/SBStringList.h" 2130fdc8d8SChris Lattner #include "lldb/API/SBThread.h" 2230fdc8d8SChris Lattner 2330fdc8d8SChris Lattner #include "lldb/Breakpoint/Breakpoint.h" 2430fdc8d8SChris Lattner #include "lldb/Breakpoint/BreakpointLocation.h" 2530fdc8d8SChris Lattner #include "lldb/Breakpoint/StoppointCallbackContext.h" 2630fdc8d8SChris Lattner #include "lldb/Core/Address.h" 27d80102e4SJim Ingham #include "lldb/Core/Debugger.h" 28ceb6b139SCaroline Tice #include "lldb/Core/Log.h" 2930fdc8d8SChris Lattner #include "lldb/Core/Stream.h" 3030fdc8d8SChris Lattner #include "lldb/Core/StreamFile.h" 31d80102e4SJim Ingham #include "lldb/Interpreter/CommandInterpreter.h" 32d80102e4SJim Ingham #include "lldb/Interpreter/ScriptInterpreter.h" 3330fdc8d8SChris Lattner #include "lldb/Target/Process.h" 34d5944cd1SGreg Clayton #include "lldb/Target/SectionLoadList.h" 3530fdc8d8SChris Lattner #include "lldb/Target/Target.h" 3662b02c61SJim Ingham #include "lldb/Target/Thread.h" 3762b02c61SJim Ingham #include "lldb/Target/ThreadSpec.h" 3830fdc8d8SChris Lattner 3930fdc8d8SChris Lattner #include "lldb/lldb-enumerations.h" 4030fdc8d8SChris Lattner 41*4e4fbe82SZachary Turner #include "llvm/ADT/STLExtras.h" 42*4e4fbe82SZachary Turner 4330fdc8d8SChris Lattner using namespace lldb; 4430fdc8d8SChris Lattner using namespace lldb_private; 4530fdc8d8SChris Lattner 46b9c1b51eSKate Stone struct CallbackData { 4730fdc8d8SChris Lattner SBBreakpoint::BreakpointHitCallback callback; 4830fdc8d8SChris Lattner void *callback_baton; 4930fdc8d8SChris Lattner }; 5030fdc8d8SChris Lattner 51*4e4fbe82SZachary Turner class SBBreakpointCallbackBaton : public TypedBaton<CallbackData> { 5230fdc8d8SChris Lattner public: 53b9c1b51eSKate Stone SBBreakpointCallbackBaton(SBBreakpoint::BreakpointHitCallback callback, 54b9c1b51eSKate Stone void *baton) 55*4e4fbe82SZachary Turner : TypedBaton(llvm::make_unique<CallbackData>()) { 56*4e4fbe82SZachary Turner getItem()->callback = callback; 57*4e4fbe82SZachary Turner getItem()->callback_baton = baton; 5830fdc8d8SChris Lattner } 5930fdc8d8SChris Lattner }; 6030fdc8d8SChris Lattner 61b9c1b51eSKate Stone SBBreakpoint::SBBreakpoint() : m_opaque_sp() {} 6230fdc8d8SChris Lattner 63b9c1b51eSKate Stone SBBreakpoint::SBBreakpoint(const SBBreakpoint &rhs) 64b9c1b51eSKate Stone : m_opaque_sp(rhs.m_opaque_sp) {} 6530fdc8d8SChris Lattner 66b9c1b51eSKate Stone SBBreakpoint::SBBreakpoint(const lldb::BreakpointSP &bp_sp) 67b9c1b51eSKate Stone : m_opaque_sp(bp_sp) {} 6830fdc8d8SChris Lattner 69dbb0abbfSEugene Zelenko SBBreakpoint::~SBBreakpoint() = default; 7030fdc8d8SChris Lattner 71b9c1b51eSKate Stone const SBBreakpoint &SBBreakpoint::operator=(const SBBreakpoint &rhs) { 7230fdc8d8SChris Lattner if (this != &rhs) 736611103cSGreg Clayton m_opaque_sp = rhs.m_opaque_sp; 7430fdc8d8SChris Lattner return *this; 7530fdc8d8SChris Lattner } 7630fdc8d8SChris Lattner 77b9c1b51eSKate Stone bool SBBreakpoint::operator==(const lldb::SBBreakpoint &rhs) { 78ac2eb9b1SGreg Clayton if (m_opaque_sp && rhs.m_opaque_sp) 79ac2eb9b1SGreg Clayton return m_opaque_sp.get() == rhs.m_opaque_sp.get(); 80ac2eb9b1SGreg Clayton return false; 81ac2eb9b1SGreg Clayton } 82ac2eb9b1SGreg Clayton 83b9c1b51eSKate Stone bool SBBreakpoint::operator!=(const lldb::SBBreakpoint &rhs) { 84c3387333SEnrico Granata if (m_opaque_sp && rhs.m_opaque_sp) 85c3387333SEnrico Granata return m_opaque_sp.get() != rhs.m_opaque_sp.get(); 86c3387333SEnrico Granata return (m_opaque_sp && !rhs.m_opaque_sp) || (rhs.m_opaque_sp && !m_opaque_sp); 87c3387333SEnrico Granata } 88c3387333SEnrico Granata 89b9c1b51eSKate Stone break_id_t SBBreakpoint::GetID() const { 905160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 91ceb6b139SCaroline Tice 92af67cecdSGreg Clayton break_id_t break_id = LLDB_INVALID_BREAK_ID; 936611103cSGreg Clayton if (m_opaque_sp) 94af67cecdSGreg Clayton break_id = m_opaque_sp->GetID(); 95af67cecdSGreg Clayton 96b9c1b51eSKate Stone if (log) { 97af67cecdSGreg Clayton if (break_id == LLDB_INVALID_BREAK_ID) 98324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::GetID () => LLDB_INVALID_BREAK_ID", 99324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get())); 100af67cecdSGreg Clayton else 101324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::GetID () => %u", 102324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), break_id); 103ceb6b139SCaroline Tice } 104ceb6b139SCaroline Tice 105af67cecdSGreg Clayton return break_id; 10630fdc8d8SChris Lattner } 10730fdc8d8SChris Lattner 108b9c1b51eSKate Stone bool SBBreakpoint::IsValid() const { 109e029fa57SJim Ingham if (!m_opaque_sp) 110e029fa57SJim Ingham return false; 111e029fa57SJim Ingham else if (m_opaque_sp->GetTarget().GetBreakpointByID(m_opaque_sp->GetID())) 112e029fa57SJim Ingham return true; 113e029fa57SJim Ingham else 114e029fa57SJim Ingham return false; 11530fdc8d8SChris Lattner } 11630fdc8d8SChris Lattner 117b9c1b51eSKate Stone void SBBreakpoint::ClearAllBreakpointSites() { 118b9c1b51eSKate Stone if (m_opaque_sp) { 119b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 120b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 1216611103cSGreg Clayton m_opaque_sp->ClearAllBreakpointSites(); 12230fdc8d8SChris Lattner } 123af67cecdSGreg Clayton } 12430fdc8d8SChris Lattner 125b9c1b51eSKate Stone SBBreakpointLocation SBBreakpoint::FindLocationByAddress(addr_t vm_addr) { 12630fdc8d8SChris Lattner SBBreakpointLocation sb_bp_location; 12730fdc8d8SChris Lattner 128b9c1b51eSKate Stone if (m_opaque_sp) { 129b9c1b51eSKate Stone if (vm_addr != LLDB_INVALID_ADDRESS) { 130b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 131b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 13230fdc8d8SChris Lattner Address address; 133f5e56de0SGreg Clayton Target &target = m_opaque_sp->GetTarget(); 134b9c1b51eSKate Stone if (!target.GetSectionLoadList().ResolveLoadAddress(vm_addr, address)) { 135e72dfb32SGreg Clayton address.SetRawAddress(vm_addr); 13630fdc8d8SChris Lattner } 1376611103cSGreg Clayton sb_bp_location.SetLocation(m_opaque_sp->FindLocationByAddress(address)); 13830fdc8d8SChris Lattner } 13930fdc8d8SChris Lattner } 14030fdc8d8SChris Lattner return sb_bp_location; 14130fdc8d8SChris Lattner } 14230fdc8d8SChris Lattner 143b9c1b51eSKate Stone break_id_t SBBreakpoint::FindLocationIDByAddress(addr_t vm_addr) { 144af67cecdSGreg Clayton break_id_t break_id = LLDB_INVALID_BREAK_ID; 14530fdc8d8SChris Lattner 146b9c1b51eSKate Stone if (m_opaque_sp && vm_addr != LLDB_INVALID_ADDRESS) { 147b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 148b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 14930fdc8d8SChris Lattner Address address; 150f5e56de0SGreg Clayton Target &target = m_opaque_sp->GetTarget(); 151b9c1b51eSKate Stone if (!target.GetSectionLoadList().ResolveLoadAddress(vm_addr, address)) { 152e72dfb32SGreg Clayton address.SetRawAddress(vm_addr); 15330fdc8d8SChris Lattner } 154af67cecdSGreg Clayton break_id = m_opaque_sp->FindLocationIDByAddress(address); 15530fdc8d8SChris Lattner } 15630fdc8d8SChris Lattner 157af67cecdSGreg Clayton return break_id; 15830fdc8d8SChris Lattner } 15930fdc8d8SChris Lattner 160b9c1b51eSKate Stone SBBreakpointLocation SBBreakpoint::FindLocationByID(break_id_t bp_loc_id) { 16130fdc8d8SChris Lattner SBBreakpointLocation sb_bp_location; 16230fdc8d8SChris Lattner 163b9c1b51eSKate Stone if (m_opaque_sp) { 164b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 165b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 1666611103cSGreg Clayton sb_bp_location.SetLocation(m_opaque_sp->FindLocationByID(bp_loc_id)); 167af67cecdSGreg Clayton } 16830fdc8d8SChris Lattner 16930fdc8d8SChris Lattner return sb_bp_location; 17030fdc8d8SChris Lattner } 17130fdc8d8SChris Lattner 172b9c1b51eSKate Stone SBBreakpointLocation SBBreakpoint::GetLocationAtIndex(uint32_t index) { 17330fdc8d8SChris Lattner SBBreakpointLocation sb_bp_location; 17430fdc8d8SChris Lattner 175b9c1b51eSKate Stone if (m_opaque_sp) { 176b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 177b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 1786611103cSGreg Clayton sb_bp_location.SetLocation(m_opaque_sp->GetLocationAtIndex(index)); 179af67cecdSGreg Clayton } 18030fdc8d8SChris Lattner 18130fdc8d8SChris Lattner return sb_bp_location; 18230fdc8d8SChris Lattner } 18330fdc8d8SChris Lattner 184b9c1b51eSKate Stone void SBBreakpoint::SetEnabled(bool enable) { 1855160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 186ceb6b139SCaroline Tice 187ceb6b139SCaroline Tice if (log) 188324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::SetEnabled (enabled=%i)", 189324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), enable); 190ceb6b139SCaroline Tice 191b9c1b51eSKate Stone if (m_opaque_sp) { 192b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 193b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 1946611103cSGreg Clayton m_opaque_sp->SetEnabled(enable); 19530fdc8d8SChris Lattner } 196af67cecdSGreg Clayton } 19730fdc8d8SChris Lattner 198b9c1b51eSKate Stone bool SBBreakpoint::IsEnabled() { 199b9c1b51eSKate Stone if (m_opaque_sp) { 200b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 201b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 2026611103cSGreg Clayton return m_opaque_sp->IsEnabled(); 203b9c1b51eSKate Stone } else 20430fdc8d8SChris Lattner return false; 20530fdc8d8SChris Lattner } 20630fdc8d8SChris Lattner 207b9c1b51eSKate Stone void SBBreakpoint::SetOneShot(bool one_shot) { 2085160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 209ca36cd16SJim Ingham 210ca36cd16SJim Ingham if (log) 211324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::SetOneShot (one_shot=%i)", 212324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), one_shot); 213ca36cd16SJim Ingham 214b9c1b51eSKate Stone if (m_opaque_sp) { 215b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 216b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 217ca36cd16SJim Ingham m_opaque_sp->SetOneShot(one_shot); 218ca36cd16SJim Ingham } 219ca36cd16SJim Ingham } 220ca36cd16SJim Ingham 221b9c1b51eSKate Stone bool SBBreakpoint::IsOneShot() const { 222b9c1b51eSKate Stone if (m_opaque_sp) { 223b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 224b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 225ca36cd16SJim Ingham return m_opaque_sp->IsOneShot(); 226b9c1b51eSKate Stone } else 227ca36cd16SJim Ingham return false; 228ca36cd16SJim Ingham } 229ca36cd16SJim Ingham 230b9c1b51eSKate Stone bool SBBreakpoint::IsInternal() { 231b9c1b51eSKate Stone if (m_opaque_sp) { 232b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 233b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 23411c8108dSJim Ingham return m_opaque_sp->IsInternal(); 235b9c1b51eSKate Stone } else 23611c8108dSJim Ingham return false; 23711c8108dSJim Ingham } 23811c8108dSJim Ingham 239b9c1b51eSKate Stone void SBBreakpoint::SetIgnoreCount(uint32_t count) { 2405160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 241ceb6b139SCaroline Tice 242ceb6b139SCaroline Tice if (log) 243324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::SetIgnoreCount (count=%u)", 244324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), count); 245ceb6b139SCaroline Tice 246b9c1b51eSKate Stone if (m_opaque_sp) { 247b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 248b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 2496611103cSGreg Clayton m_opaque_sp->SetIgnoreCount(count); 25030fdc8d8SChris Lattner } 251af67cecdSGreg Clayton } 25230fdc8d8SChris Lattner 253b9c1b51eSKate Stone void SBBreakpoint::SetCondition(const char *condition) { 254b9c1b51eSKate Stone if (m_opaque_sp) { 255b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 256b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 257041a12fcSJim Ingham m_opaque_sp->SetCondition(condition); 258041a12fcSJim Ingham } 259af67cecdSGreg Clayton } 260041a12fcSJim Ingham 261b9c1b51eSKate Stone const char *SBBreakpoint::GetCondition() { 262b9c1b51eSKate Stone if (m_opaque_sp) { 263b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 264b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 265041a12fcSJim Ingham return m_opaque_sp->GetConditionText(); 266041a12fcSJim Ingham } 267dbb0abbfSEugene Zelenko return nullptr; 268af67cecdSGreg Clayton } 269041a12fcSJim Ingham 270b9c1b51eSKate Stone uint32_t SBBreakpoint::GetHitCount() const { 2714838131bSGreg Clayton uint32_t count = 0; 272b9c1b51eSKate Stone if (m_opaque_sp) { 273b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 274b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 2754838131bSGreg Clayton count = m_opaque_sp->GetHitCount(); 276af67cecdSGreg Clayton } 2774838131bSGreg Clayton 2785160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 279ceb6b139SCaroline Tice if (log) 280324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::GetHitCount () => %u", 281324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), count); 2824838131bSGreg Clayton 2834838131bSGreg Clayton return count; 284ceb6b139SCaroline Tice } 2859fed0d85SGreg Clayton 286b9c1b51eSKate Stone uint32_t SBBreakpoint::GetIgnoreCount() const { 2874838131bSGreg Clayton uint32_t count = 0; 288b9c1b51eSKate Stone if (m_opaque_sp) { 289b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 290b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 2914838131bSGreg Clayton count = m_opaque_sp->GetIgnoreCount(); 292af67cecdSGreg Clayton } 2934838131bSGreg Clayton 2945160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 2954838131bSGreg Clayton if (log) 296324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::GetIgnoreCount () => %u", 297324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), count); 2984838131bSGreg Clayton 2994838131bSGreg Clayton return count; 30030fdc8d8SChris Lattner } 30130fdc8d8SChris Lattner 302b9c1b51eSKate Stone void SBBreakpoint::SetThreadID(tid_t tid) { 303b9c1b51eSKate Stone if (m_opaque_sp) { 304b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 305b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 3064838131bSGreg Clayton m_opaque_sp->SetThreadID(tid); 307af67cecdSGreg Clayton } 3085160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 3094838131bSGreg Clayton if (log) 310324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::SetThreadID (tid=0x%4.4" PRIx64 ")", 311324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), tid); 31230fdc8d8SChris Lattner } 31330fdc8d8SChris Lattner 314b9c1b51eSKate Stone tid_t SBBreakpoint::GetThreadID() { 3154838131bSGreg Clayton tid_t tid = LLDB_INVALID_THREAD_ID; 316b9c1b51eSKate Stone if (m_opaque_sp) { 317b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 318b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 3194838131bSGreg Clayton tid = m_opaque_sp->GetThreadID(); 320af67cecdSGreg Clayton } 32130fdc8d8SChris Lattner 3225160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 3234838131bSGreg Clayton if (log) 324324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::GetThreadID () => 0x%4.4" PRIx64, 325324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), tid); 3264838131bSGreg Clayton return tid; 32730fdc8d8SChris Lattner } 32830fdc8d8SChris Lattner 329b9c1b51eSKate Stone void SBBreakpoint::SetThreadIndex(uint32_t index) { 3305160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 3314838131bSGreg Clayton if (log) 332324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::SetThreadIndex (%u)", 333324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), index); 334b9c1b51eSKate Stone if (m_opaque_sp) { 335b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 336b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 3376611103cSGreg Clayton m_opaque_sp->GetOptions()->GetThreadSpec()->SetIndex(index); 33862b02c61SJim Ingham } 339af67cecdSGreg Clayton } 34062b02c61SJim Ingham 341b9c1b51eSKate Stone uint32_t SBBreakpoint::GetThreadIndex() const { 342bdf4c6acSGreg Clayton uint32_t thread_idx = UINT32_MAX; 343b9c1b51eSKate Stone if (m_opaque_sp) { 344b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 345b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 346b9c1b51eSKate Stone const ThreadSpec *thread_spec = 347b9c1b51eSKate Stone m_opaque_sp->GetOptions()->GetThreadSpecNoCreate(); 348dbb0abbfSEugene Zelenko if (thread_spec != nullptr) 3494838131bSGreg Clayton thread_idx = thread_spec->GetIndex(); 35062b02c61SJim Ingham } 3515160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 3524838131bSGreg Clayton if (log) 353324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::GetThreadIndex () => %u", 354324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), thread_idx); 3554838131bSGreg Clayton 356763d1a17SJohnny Chen return thread_idx; 35762b02c61SJim Ingham } 35862b02c61SJim Ingham 359b9c1b51eSKate Stone void SBBreakpoint::SetThreadName(const char *thread_name) { 3605160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 3614838131bSGreg Clayton if (log) 362324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::SetThreadName (%s)", 363324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), thread_name); 3644838131bSGreg Clayton 365b9c1b51eSKate Stone if (m_opaque_sp) { 366b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 367b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 3686611103cSGreg Clayton m_opaque_sp->GetOptions()->GetThreadSpec()->SetName(thread_name); 36962b02c61SJim Ingham } 370af67cecdSGreg Clayton } 37162b02c61SJim Ingham 372b9c1b51eSKate Stone const char *SBBreakpoint::GetThreadName() const { 373dbb0abbfSEugene Zelenko const char *name = nullptr; 374b9c1b51eSKate Stone if (m_opaque_sp) { 375b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 376b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 377b9c1b51eSKate Stone const ThreadSpec *thread_spec = 378b9c1b51eSKate Stone m_opaque_sp->GetOptions()->GetThreadSpecNoCreate(); 379dbb0abbfSEugene Zelenko if (thread_spec != nullptr) 3804838131bSGreg Clayton name = thread_spec->GetName(); 38162b02c61SJim Ingham } 3825160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 3834838131bSGreg Clayton if (log) 384324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::GetThreadName () => %s", 385324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), name); 3864838131bSGreg Clayton 3874838131bSGreg Clayton return name; 38862b02c61SJim Ingham } 38962b02c61SJim Ingham 390b9c1b51eSKate Stone void SBBreakpoint::SetQueueName(const char *queue_name) { 3915160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 3924838131bSGreg Clayton if (log) 393324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::SetQueueName (%s)", 394324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), queue_name); 395b9c1b51eSKate Stone if (m_opaque_sp) { 396b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 397b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 3986611103cSGreg Clayton m_opaque_sp->GetOptions()->GetThreadSpec()->SetQueueName(queue_name); 39962b02c61SJim Ingham } 400af67cecdSGreg Clayton } 40162b02c61SJim Ingham 402b9c1b51eSKate Stone const char *SBBreakpoint::GetQueueName() const { 403dbb0abbfSEugene Zelenko const char *name = nullptr; 404b9c1b51eSKate Stone if (m_opaque_sp) { 405b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 406b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 407b9c1b51eSKate Stone const ThreadSpec *thread_spec = 408b9c1b51eSKate Stone m_opaque_sp->GetOptions()->GetThreadSpecNoCreate(); 409af67cecdSGreg Clayton if (thread_spec) 4104838131bSGreg Clayton name = thread_spec->GetQueueName(); 41162b02c61SJim Ingham } 4125160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 4134838131bSGreg Clayton if (log) 414324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::GetQueueName () => %s", 415324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), name); 4164838131bSGreg Clayton 4174838131bSGreg Clayton return name; 41862b02c61SJim Ingham } 41962b02c61SJim Ingham 420b9c1b51eSKate Stone size_t SBBreakpoint::GetNumResolvedLocations() const { 4214838131bSGreg Clayton size_t num_resolved = 0; 422b9c1b51eSKate Stone if (m_opaque_sp) { 423b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 424b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 4254838131bSGreg Clayton num_resolved = m_opaque_sp->GetNumResolvedLocations(); 426af67cecdSGreg Clayton } 4275160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 4284838131bSGreg Clayton if (log) 429324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::GetNumResolvedLocations () => %" PRIu64, 430324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), 431324a1036SSaleem Abdulrasool static_cast<uint64_t>(num_resolved)); 4324838131bSGreg Clayton return num_resolved; 43330fdc8d8SChris Lattner } 43430fdc8d8SChris Lattner 435b9c1b51eSKate Stone size_t SBBreakpoint::GetNumLocations() const { 4364838131bSGreg Clayton size_t num_locs = 0; 437b9c1b51eSKate Stone if (m_opaque_sp) { 438b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 439b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 4404838131bSGreg Clayton num_locs = m_opaque_sp->GetNumLocations(); 441af67cecdSGreg Clayton } 4425160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 4434838131bSGreg Clayton if (log) 444324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::GetNumLocations () => %" PRIu64, 445324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), 446324a1036SSaleem Abdulrasool static_cast<uint64_t>(num_locs)); 4474838131bSGreg Clayton return num_locs; 44830fdc8d8SChris Lattner } 44930fdc8d8SChris Lattner 450b9c1b51eSKate Stone bool SBBreakpoint::GetDescription(SBStream &s) { 451b9c1b51eSKate Stone if (m_opaque_sp) { 452b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 453b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 45405faeb71SGreg Clayton s.Printf("SBBreakpoint: id = %i, ", m_opaque_sp->GetID()); 45505faeb71SGreg Clayton m_opaque_sp->GetResolverDescription(s.get()); 45605faeb71SGreg Clayton m_opaque_sp->GetFilterDescription(s.get()); 45705faeb71SGreg Clayton const size_t num_locations = m_opaque_sp->GetNumLocations(); 458d01b2953SDaniel Malea s.Printf(", locations = %" PRIu64, (uint64_t)num_locations); 459dde9cff3SCaroline Tice return true; 460dde9cff3SCaroline Tice } 46105faeb71SGreg Clayton s.Printf("No value"); 46205faeb71SGreg Clayton return false; 46305faeb71SGreg Clayton } 464dde9cff3SCaroline Tice 465b9c1b51eSKate Stone bool SBBreakpoint::PrivateBreakpointHitCallback(void *baton, 46630fdc8d8SChris Lattner StoppointCallbackContext *ctx, 46730fdc8d8SChris Lattner lldb::user_id_t break_id, 468b9c1b51eSKate Stone lldb::user_id_t break_loc_id) { 4691ac04c30SGreg Clayton ExecutionContext exe_ctx(ctx->exe_ctx_ref); 470b9c1b51eSKate Stone BreakpointSP bp_sp( 471b9c1b51eSKate Stone exe_ctx.GetTargetRef().GetBreakpointList().FindBreakpointByID(break_id)); 472b9c1b51eSKate Stone if (baton && bp_sp) { 47330fdc8d8SChris Lattner CallbackData *data = (CallbackData *)baton; 47430fdc8d8SChris Lattner lldb_private::Breakpoint *bp = bp_sp.get(); 475b9c1b51eSKate Stone if (bp && data->callback) { 4761ac04c30SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 477b9c1b51eSKate Stone if (process) { 478e1cd1be6SGreg Clayton SBProcess sb_process(process->shared_from_this()); 47930fdc8d8SChris Lattner SBThread sb_thread; 48030fdc8d8SChris Lattner SBBreakpointLocation sb_location; 48130fdc8d8SChris Lattner assert(bp_sp); 48230fdc8d8SChris Lattner sb_location.SetLocation(bp_sp->FindLocationByID(break_loc_id)); 4831ac04c30SGreg Clayton Thread *thread = exe_ctx.GetThreadPtr(); 484c14ee32dSGreg Clayton if (thread) 485e1cd1be6SGreg Clayton sb_thread.SetThread(thread->shared_from_this()); 48630fdc8d8SChris Lattner 487b9c1b51eSKate Stone return data->callback(data->callback_baton, sb_process, sb_thread, 48830fdc8d8SChris Lattner sb_location); 48930fdc8d8SChris Lattner } 49030fdc8d8SChris Lattner } 49130fdc8d8SChris Lattner } 49230fdc8d8SChris Lattner return true; // Return true if we should stop at this breakpoint 49330fdc8d8SChris Lattner } 49430fdc8d8SChris Lattner 495b9c1b51eSKate Stone void SBBreakpoint::SetCallback(BreakpointHitCallback callback, void *baton) { 4965160ce5cSGreg Clayton Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 497ceb6b139SCaroline Tice 498b9c1b51eSKate Stone if (log) { 499324a1036SSaleem Abdulrasool void *pointer = &callback; 500324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::SetCallback (callback=%p, baton=%p)", 501324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), 502324a1036SSaleem Abdulrasool *static_cast<void **>(&pointer), static_cast<void *>(baton)); 503324a1036SSaleem Abdulrasool } 504ceb6b139SCaroline Tice 505b9c1b51eSKate Stone if (m_opaque_sp) { 506b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 507b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 50830fdc8d8SChris Lattner BatonSP baton_sp(new SBBreakpointCallbackBaton(callback, baton)); 509b9c1b51eSKate Stone m_opaque_sp->SetCallback(SBBreakpoint::PrivateBreakpointHitCallback, 510b9c1b51eSKate Stone baton_sp, false); 51130fdc8d8SChris Lattner } 51230fdc8d8SChris Lattner } 51330fdc8d8SChris Lattner 514b9c1b51eSKate Stone void SBBreakpoint::SetScriptCallbackFunction( 515b9c1b51eSKate Stone const char *callback_function_name) { 516d80102e4SJim Ingham Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 517d80102e4SJim Ingham 518d80102e4SJim Ingham if (log) 519324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::SetScriptCallbackFunction (callback=%s)", 520b9c1b51eSKate Stone static_cast<void *>(m_opaque_sp.get()), callback_function_name); 521d80102e4SJim Ingham 522b9c1b51eSKate Stone if (m_opaque_sp) { 523b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 524b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 525d80102e4SJim Ingham BreakpointOptions *bp_options = m_opaque_sp->GetOptions(); 526b9c1b51eSKate Stone m_opaque_sp->GetTarget() 527b9c1b51eSKate Stone .GetDebugger() 528b9c1b51eSKate Stone .GetCommandInterpreter() 529b9c1b51eSKate Stone .GetScriptInterpreter() 530b9c1b51eSKate Stone ->SetBreakpointCommandCallbackFunction(bp_options, 531d80102e4SJim Ingham callback_function_name); 532d80102e4SJim Ingham } 533d80102e4SJim Ingham } 534d80102e4SJim Ingham 535b9c1b51eSKate Stone SBError SBBreakpoint::SetScriptCallbackBody(const char *callback_body_text) { 536d80102e4SJim Ingham Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 537d80102e4SJim Ingham 538d80102e4SJim Ingham if (log) 539324a1036SSaleem Abdulrasool log->Printf("SBBreakpoint(%p)::SetScriptCallbackBody: callback body:\n%s)", 540324a1036SSaleem Abdulrasool static_cast<void *>(m_opaque_sp.get()), callback_body_text); 541d80102e4SJim Ingham 542d80102e4SJim Ingham SBError sb_error; 543b9c1b51eSKate Stone if (m_opaque_sp) { 544b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 545b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 546d80102e4SJim Ingham BreakpointOptions *bp_options = m_opaque_sp->GetOptions(); 547b9c1b51eSKate Stone Error error = 548b9c1b51eSKate Stone m_opaque_sp->GetTarget() 549b9c1b51eSKate Stone .GetDebugger() 550b9c1b51eSKate Stone .GetCommandInterpreter() 551b9c1b51eSKate Stone .GetScriptInterpreter() 552b9c1b51eSKate Stone ->SetBreakpointCommandCallback(bp_options, callback_body_text); 553d80102e4SJim Ingham sb_error.SetError(error); 554b9c1b51eSKate Stone } else 555d80102e4SJim Ingham sb_error.SetErrorString("invalid breakpoint"); 556d80102e4SJim Ingham 557d80102e4SJim Ingham return sb_error; 558d80102e4SJim Ingham } 55930fdc8d8SChris Lattner 560b9c1b51eSKate Stone bool SBBreakpoint::AddName(const char *new_name) { 5615e09c8c3SJim Ingham Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 5625e09c8c3SJim Ingham 5635e09c8c3SJim Ingham if (log) 5645e09c8c3SJim Ingham log->Printf("SBBreakpoint(%p)::AddName (name=%s)", 565b9c1b51eSKate Stone static_cast<void *>(m_opaque_sp.get()), new_name); 5665e09c8c3SJim Ingham 567b9c1b51eSKate Stone if (m_opaque_sp) { 568b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 569b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 570b9c1b51eSKate Stone Error error; // Think I'm just going to swallow the error here, it's 571b9c1b51eSKate Stone // probably more annoying to have to provide it. 5725e09c8c3SJim Ingham return m_opaque_sp->AddName(new_name, error); 5735e09c8c3SJim Ingham } 5745e09c8c3SJim Ingham 5755e09c8c3SJim Ingham return false; 5765e09c8c3SJim Ingham } 5775e09c8c3SJim Ingham 578b9c1b51eSKate Stone void SBBreakpoint::RemoveName(const char *name_to_remove) { 5795e09c8c3SJim Ingham Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 5805e09c8c3SJim Ingham 5815e09c8c3SJim Ingham if (log) 5825e09c8c3SJim Ingham log->Printf("SBBreakpoint(%p)::RemoveName (name=%s)", 583b9c1b51eSKate Stone static_cast<void *>(m_opaque_sp.get()), name_to_remove); 5845e09c8c3SJim Ingham 585b9c1b51eSKate Stone if (m_opaque_sp) { 586b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 587b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 5885e09c8c3SJim Ingham m_opaque_sp->RemoveName(name_to_remove); 5895e09c8c3SJim Ingham } 5905e09c8c3SJim Ingham } 5915e09c8c3SJim Ingham 592b9c1b51eSKate Stone bool SBBreakpoint::MatchesName(const char *name) { 5935e09c8c3SJim Ingham Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 5945e09c8c3SJim Ingham 5955e09c8c3SJim Ingham if (log) 5965e09c8c3SJim Ingham log->Printf("SBBreakpoint(%p)::MatchesName (name=%s)", 597b9c1b51eSKate Stone static_cast<void *>(m_opaque_sp.get()), name); 5985e09c8c3SJim Ingham 599b9c1b51eSKate Stone if (m_opaque_sp) { 600b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 601b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 6025e09c8c3SJim Ingham return m_opaque_sp->MatchesName(name); 6035e09c8c3SJim Ingham } 6045e09c8c3SJim Ingham 6055e09c8c3SJim Ingham return false; 6065e09c8c3SJim Ingham } 6075e09c8c3SJim Ingham 608b9c1b51eSKate Stone void SBBreakpoint::GetNames(SBStringList &names) { 6095e09c8c3SJim Ingham Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); 6105e09c8c3SJim Ingham 6115e09c8c3SJim Ingham if (log) 6125e09c8c3SJim Ingham log->Printf("SBBreakpoint(%p)::GetNames ()", 6135e09c8c3SJim Ingham static_cast<void *>(m_opaque_sp.get())); 6145e09c8c3SJim Ingham 615b9c1b51eSKate Stone if (m_opaque_sp) { 616b9c1b51eSKate Stone std::lock_guard<std::recursive_mutex> guard( 617b9c1b51eSKate Stone m_opaque_sp->GetTarget().GetAPIMutex()); 6185e09c8c3SJim Ingham std::vector<std::string> names_vec; 6195e09c8c3SJim Ingham m_opaque_sp->GetNames(names_vec); 620b9c1b51eSKate Stone for (std::string name : names_vec) { 6215e09c8c3SJim Ingham names.AppendString(name.c_str()); 6225e09c8c3SJim Ingham } 6235e09c8c3SJim Ingham } 6245e09c8c3SJim Ingham } 6255e09c8c3SJim Ingham 626b9c1b51eSKate Stone lldb_private::Breakpoint *SBBreakpoint::operator->() const { 6276611103cSGreg Clayton return m_opaque_sp.get(); 62830fdc8d8SChris Lattner } 62930fdc8d8SChris Lattner 630b9c1b51eSKate Stone lldb_private::Breakpoint *SBBreakpoint::get() const { 6316611103cSGreg Clayton return m_opaque_sp.get(); 63230fdc8d8SChris Lattner } 63330fdc8d8SChris Lattner 634b9c1b51eSKate Stone lldb::BreakpointSP &SBBreakpoint::operator*() { return m_opaque_sp; } 635b9c1b51eSKate Stone 636b9c1b51eSKate Stone const lldb::BreakpointSP &SBBreakpoint::operator*() const { 6376611103cSGreg Clayton return m_opaque_sp; 63830fdc8d8SChris Lattner } 63930fdc8d8SChris Lattner 640b9c1b51eSKate Stone bool SBBreakpoint::EventIsBreakpointEvent(const lldb::SBEvent &event) { 641b9c1b51eSKate Stone return Breakpoint::BreakpointEventData::GetEventDataFromEvent(event.get()) != 642b9c1b51eSKate Stone nullptr; 643e6bc6cb9SJim Ingham } 644e6bc6cb9SJim Ingham 6459fed0d85SGreg Clayton BreakpointEventType 646b9c1b51eSKate Stone SBBreakpoint::GetBreakpointEventTypeFromEvent(const SBEvent &event) { 6479fed0d85SGreg Clayton if (event.IsValid()) 648b9c1b51eSKate Stone return Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent( 649b9c1b51eSKate Stone event.GetSP()); 6509fed0d85SGreg Clayton return eBreakpointEventTypeInvalidType; 6519fed0d85SGreg Clayton } 6529fed0d85SGreg Clayton 653b9c1b51eSKate Stone SBBreakpoint SBBreakpoint::GetBreakpointFromEvent(const lldb::SBEvent &event) { 6549fed0d85SGreg Clayton SBBreakpoint sb_breakpoint; 6559fed0d85SGreg Clayton if (event.IsValid()) 656b9c1b51eSKate Stone sb_breakpoint.m_opaque_sp = 657b9c1b51eSKate Stone Breakpoint::BreakpointEventData::GetBreakpointFromEvent(event.GetSP()); 6589fed0d85SGreg Clayton return sb_breakpoint; 6599fed0d85SGreg Clayton } 6609fed0d85SGreg Clayton 6619fed0d85SGreg Clayton SBBreakpointLocation 662b9c1b51eSKate Stone SBBreakpoint::GetBreakpointLocationAtIndexFromEvent(const lldb::SBEvent &event, 663b9c1b51eSKate Stone uint32_t loc_idx) { 6649fed0d85SGreg Clayton SBBreakpointLocation sb_breakpoint_loc; 6659fed0d85SGreg Clayton if (event.IsValid()) 666b9c1b51eSKate Stone sb_breakpoint_loc.SetLocation( 667b9c1b51eSKate Stone Breakpoint::BreakpointEventData::GetBreakpointLocationAtIndexFromEvent( 668b9c1b51eSKate Stone event.GetSP(), loc_idx)); 6699fed0d85SGreg Clayton return sb_breakpoint_loc; 6709fed0d85SGreg Clayton } 6719fed0d85SGreg Clayton 672e6bc6cb9SJim Ingham uint32_t 673b9c1b51eSKate Stone SBBreakpoint::GetNumBreakpointLocationsFromEvent(const lldb::SBEvent &event) { 674e6bc6cb9SJim Ingham uint32_t num_locations = 0; 675e6bc6cb9SJim Ingham if (event.IsValid()) 676b9c1b51eSKate Stone num_locations = 677b9c1b51eSKate Stone (Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent( 678b9c1b51eSKate Stone event.GetSP())); 679e6bc6cb9SJim Ingham return num_locations; 680e6bc6cb9SJim Ingham } 681