//===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Created by Greg Clayton on 6/29/07. // //===----------------------------------------------------------------------===// #include "DNBBreakpoint.h" #include "MachProcess.h" #include #include #include #include "DNBLog.h" #pragma mark -- DNBBreakpoint DNBBreakpoint::DNBBreakpoint(nub_addr_t addr, nub_size_t byte_size, bool hardware) : m_retain_count (1), m_byte_size (static_cast(byte_size)), m_opcode(), m_addr(addr), m_enabled(0), m_hw_preferred(hardware), m_is_watchpoint(0), m_watch_read(0), m_watch_write(0), m_hw_index(INVALID_NUB_HW_INDEX) { } DNBBreakpoint::~DNBBreakpoint() { } void DNBBreakpoint::Dump() const { if (IsBreakpoint()) { DNBLog ("DNBBreakpoint addr = 0x%llx state = %s type = %s breakpoint hw_index = %i", (uint64_t)m_addr, m_enabled ? "enabled " : "disabled", IsHardware() ? "hardware" : "software", GetHardwareIndex()); } else { DNBLog ("DNBBreakpoint addr = 0x%llx size = %llu state = %s type = %s watchpoint (%s%s) hw_index = %i", (uint64_t)m_addr, (uint64_t)m_byte_size, m_enabled ? "enabled " : "disabled", IsHardware() ? "hardware" : "software", m_watch_read ? "r" : "", m_watch_write ? "w" : "", GetHardwareIndex()); } } #pragma mark -- DNBBreakpointList DNBBreakpointList::DNBBreakpointList() { } DNBBreakpointList::~DNBBreakpointList() { } DNBBreakpoint * DNBBreakpointList::Add(nub_addr_t addr, nub_size_t length, bool hardware) { m_breakpoints.insert(std::make_pair(addr, DNBBreakpoint(addr, length, hardware))); iterator pos = m_breakpoints.find (addr); return &pos->second; } bool DNBBreakpointList::Remove (nub_addr_t addr) { iterator pos = m_breakpoints.find(addr); if (pos != m_breakpoints.end()) { m_breakpoints.erase(pos); return true; } return false; } DNBBreakpoint * DNBBreakpointList::FindByAddress (nub_addr_t addr) { iterator pos = m_breakpoints.find(addr); if (pos != m_breakpoints.end()) return &pos->second; return NULL; } const DNBBreakpoint * DNBBreakpointList::FindByAddress (nub_addr_t addr) const { const_iterator pos = m_breakpoints.find(addr); if (pos != m_breakpoints.end()) return &pos->second; return NULL; } // Finds the next breakpoint at an address greater than or equal to "addr" size_t DNBBreakpointList::FindBreakpointsThatOverlapRange (nub_addr_t addr, nub_addr_t size, std::vector &bps) { bps.clear(); iterator end = m_breakpoints.end(); // Find the first breakpoint with an address >= to "addr" iterator pos = m_breakpoints.lower_bound(addr); if (pos != end) { if (pos != m_breakpoints.begin()) { // Watch out for a breakpoint at an address less than "addr" that might still overlap iterator prev_pos = pos; --prev_pos; if (prev_pos->second.IntersectsRange (addr, size, NULL, NULL, NULL)) bps.push_back (&pos->second); } while (pos != end) { // When we hit a breakpoint whose start address is greater than "addr + size" we are done. // Do the math in a way that doesn't risk unsigned overflow with bad input. if ((pos->second.Address() - addr) >= size) break; // Check if this breakpoint overlaps, and if it does, add it to the list if (pos->second.IntersectsRange (addr, size, NULL, NULL, NULL)) { bps.push_back (&pos->second); ++pos; } } } return bps.size(); } void DNBBreakpointList::Dump() const { const_iterator pos; const_iterator end = m_breakpoints.end(); for (pos = m_breakpoints.begin(); pos != end; ++pos) pos->second.Dump(); } void DNBBreakpointList::DisableAll () { iterator pos, end = m_breakpoints.end(); for (pos = m_breakpoints.begin(); pos != end; ++pos) pos->second.SetEnabled(false); } void DNBBreakpointList::RemoveTrapsFromBuffer (nub_addr_t addr, nub_size_t size, void *p) const { uint8_t *buf = (uint8_t *)p; const_iterator end = m_breakpoints.end(); const_iterator pos = m_breakpoints.lower_bound(addr); while (pos != end && (pos->first < (addr + size))) { nub_addr_t intersect_addr; nub_size_t intersect_size; nub_size_t opcode_offset; const DNBBreakpoint &bp = pos->second; if (bp.IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset)) { assert(addr <= intersect_addr && intersect_addr < addr + size); assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); assert(opcode_offset + intersect_size <= bp.ByteSize()); nub_size_t buf_offset = intersect_addr - addr; ::memcpy(buf + buf_offset, bp.SavedOpcodeBytes() + opcode_offset, intersect_size); } ++pos; } } void DNBBreakpointList::DisableAllBreakpoints(MachProcess *process) { iterator pos, end = m_breakpoints.end(); for (pos = m_breakpoints.begin(); pos != end; ++pos) process->DisableBreakpoint(pos->second.Address(), false); } void DNBBreakpointList::DisableAllWatchpoints(MachProcess *process) { iterator pos, end = m_breakpoints.end(); for (pos = m_breakpoints.begin(); pos != end; ++pos) process->DisableWatchpoint(pos->second.Address(), false); } void DNBBreakpointList::RemoveDisabled() { iterator pos = m_breakpoints.begin(); while (pos != m_breakpoints.end()) { if (!pos->second.IsEnabled()) pos = m_breakpoints.erase(pos); else ++pos; } }