| //===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | // | 
 | //  Created by Greg Clayton on 6/29/07. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "DNBBreakpoint.h" | 
 | #include "DNBLog.h" | 
 | #include "MachProcess.h" | 
 | #include <algorithm> | 
 | #include <assert.h> | 
 | #include <inttypes.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<uint32_t>(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<DNBBreakpoint *> &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; | 
 |   } | 
 | } |