1 //===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  Created by Greg Clayton on 6/29/07.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "DNBBreakpoint.h"
15 #include "DNBLog.h"
16 #include "MachProcess.h"
17 #include <algorithm>
18 #include <assert.h>
19 #include <inttypes.h>
20 
21 #pragma mark-- DNBBreakpoint
22 DNBBreakpoint::DNBBreakpoint(nub_addr_t addr, nub_size_t byte_size,
23                              bool hardware)
24     : m_retain_count(1), m_byte_size(static_cast<uint32_t>(byte_size)),
25       m_opcode(), m_addr(addr), m_enabled(0), m_hw_preferred(hardware),
26       m_is_watchpoint(0), m_watch_read(0), m_watch_write(0),
27       m_hw_index(INVALID_NUB_HW_INDEX) {}
28 
29 DNBBreakpoint::~DNBBreakpoint() {}
30 
31 void DNBBreakpoint::Dump() const {
32   if (IsBreakpoint()) {
33     DNBLog("DNBBreakpoint addr = 0x%llx  state = %s  type = %s breakpoint  "
34            "hw_index = %i",
35            (uint64_t)m_addr, m_enabled ? "enabled " : "disabled",
36            IsHardware() ? "hardware" : "software", GetHardwareIndex());
37   } else {
38     DNBLog("DNBBreakpoint addr = 0x%llx  size = %llu  state = %s  type = %s "
39            "watchpoint (%s%s)  hw_index = %i",
40            (uint64_t)m_addr, (uint64_t)m_byte_size,
41            m_enabled ? "enabled " : "disabled",
42            IsHardware() ? "hardware" : "software", m_watch_read ? "r" : "",
43            m_watch_write ? "w" : "", GetHardwareIndex());
44   }
45 }
46 
47 #pragma mark-- DNBBreakpointList
48 
49 DNBBreakpointList::DNBBreakpointList() {}
50 
51 DNBBreakpointList::~DNBBreakpointList() {}
52 
53 DNBBreakpoint *DNBBreakpointList::Add(nub_addr_t addr, nub_size_t length,
54                                       bool hardware) {
55   m_breakpoints.insert(
56       std::make_pair(addr, DNBBreakpoint(addr, length, hardware)));
57   iterator pos = m_breakpoints.find(addr);
58   return &pos->second;
59 }
60 
61 bool DNBBreakpointList::Remove(nub_addr_t addr) {
62   iterator pos = m_breakpoints.find(addr);
63   if (pos != m_breakpoints.end()) {
64     m_breakpoints.erase(pos);
65     return true;
66   }
67   return false;
68 }
69 
70 DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) {
71   iterator pos = m_breakpoints.find(addr);
72   if (pos != m_breakpoints.end())
73     return &pos->second;
74 
75   return NULL;
76 }
77 
78 const DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) const {
79   const_iterator pos = m_breakpoints.find(addr);
80   if (pos != m_breakpoints.end())
81     return &pos->second;
82 
83   return NULL;
84 }
85 
86 // Finds the next breakpoint at an address greater than or equal to "addr"
87 size_t DNBBreakpointList::FindBreakpointsThatOverlapRange(
88     nub_addr_t addr, nub_addr_t size, std::vector<DNBBreakpoint *> &bps) {
89   bps.clear();
90   iterator end = m_breakpoints.end();
91   // Find the first breakpoint with an address >= to "addr"
92   iterator pos = m_breakpoints.lower_bound(addr);
93   if (pos != end) {
94     if (pos != m_breakpoints.begin()) {
95       // Watch out for a breakpoint at an address less than "addr" that might
96       // still overlap
97       iterator prev_pos = pos;
98       --prev_pos;
99       if (prev_pos->second.IntersectsRange(addr, size, NULL, NULL, NULL))
100         bps.push_back(&pos->second);
101     }
102 
103     while (pos != end) {
104       // When we hit a breakpoint whose start address is greater than "addr +
105       // size" we are done.
106       // Do the math in a way that doesn't risk unsigned overflow with bad
107       // input.
108       if ((pos->second.Address() - addr) >= size)
109         break;
110 
111       // Check if this breakpoint overlaps, and if it does, add it to the list
112       if (pos->second.IntersectsRange(addr, size, NULL, NULL, NULL)) {
113         bps.push_back(&pos->second);
114         ++pos;
115       }
116     }
117   }
118   return bps.size();
119 }
120 
121 void DNBBreakpointList::Dump() const {
122   const_iterator pos;
123   const_iterator end = m_breakpoints.end();
124   for (pos = m_breakpoints.begin(); pos != end; ++pos)
125     pos->second.Dump();
126 }
127 
128 void DNBBreakpointList::DisableAll() {
129   iterator pos, end = m_breakpoints.end();
130   for (pos = m_breakpoints.begin(); pos != end; ++pos)
131     pos->second.SetEnabled(false);
132 }
133 
134 void DNBBreakpointList::RemoveTrapsFromBuffer(nub_addr_t addr, nub_size_t size,
135                                               void *p) const {
136   uint8_t *buf = (uint8_t *)p;
137   const_iterator end = m_breakpoints.end();
138   const_iterator pos = m_breakpoints.lower_bound(addr);
139   while (pos != end && (pos->first < (addr + size))) {
140     nub_addr_t intersect_addr;
141     nub_size_t intersect_size;
142     nub_size_t opcode_offset;
143     const DNBBreakpoint &bp = pos->second;
144     if (bp.IntersectsRange(addr, size, &intersect_addr, &intersect_size,
145                            &opcode_offset)) {
146       assert(addr <= intersect_addr && intersect_addr < addr + size);
147       assert(addr < intersect_addr + intersect_size &&
148              intersect_addr + intersect_size <= addr + size);
149       assert(opcode_offset + intersect_size <= bp.ByteSize());
150       nub_size_t buf_offset = intersect_addr - addr;
151       ::memcpy(buf + buf_offset, bp.SavedOpcodeBytes() + opcode_offset,
152                intersect_size);
153     }
154     ++pos;
155   }
156 }
157 
158 void DNBBreakpointList::DisableAllBreakpoints(MachProcess *process) {
159   iterator pos, end = m_breakpoints.end();
160   for (pos = m_breakpoints.begin(); pos != end; ++pos)
161     process->DisableBreakpoint(pos->second.Address(), false);
162 }
163 
164 void DNBBreakpointList::DisableAllWatchpoints(MachProcess *process) {
165   iterator pos, end = m_breakpoints.end();
166   for (pos = m_breakpoints.begin(); pos != end; ++pos)
167     process->DisableWatchpoint(pos->second.Address(), false);
168 }
169 
170 void DNBBreakpointList::RemoveDisabled() {
171   iterator pos = m_breakpoints.begin();
172   while (pos != m_breakpoints.end()) {
173     if (!pos->second.IsEnabled())
174       pos = m_breakpoints.erase(pos);
175     else
176       ++pos;
177   }
178 }
179