xref: /llvm-project/lldb/tools/debugserver/source/DNBBreakpoint.cpp (revision e76cfaca70be0b45e62149e52f68d8352fa8ea2f)
1 //===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //  Created by Greg Clayton on 6/29/07.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "DNBBreakpoint.h"
14 #include "DNBLog.h"
15 #include "MachProcess.h"
16 #include <algorithm>
17 #include <cassert>
18 #include <cinttypes>
19 
20 #pragma mark-- DNBBreakpoint
DNBBreakpoint(nub_addr_t addr,nub_size_t byte_size,bool hardware)21 DNBBreakpoint::DNBBreakpoint(nub_addr_t addr, nub_size_t byte_size,
22                              bool hardware)
23     : m_retain_count(1), m_byte_size(static_cast<uint32_t>(byte_size)),
24       m_opcode(), m_addr(addr), m_enabled(0), m_hw_preferred(hardware),
25       m_is_watchpoint(0), m_watch_read(0), m_watch_write(0),
26       m_hw_index(INVALID_NUB_HW_INDEX) {}
27 
28 DNBBreakpoint::~DNBBreakpoint() = default;
29 
Dump() const30 void DNBBreakpoint::Dump() const {
31   if (IsBreakpoint()) {
32     DNBLog("DNBBreakpoint addr = 0x%llx  state = %s  type = %s breakpoint  "
33            "hw_index = %i",
34            (uint64_t)m_addr, m_enabled ? "enabled " : "disabled",
35            IsHardware() ? "hardware" : "software", GetHardwareIndex());
36   } else {
37     DNBLog("DNBBreakpoint addr = 0x%llx  size = %llu  state = %s  type = %s "
38            "watchpoint (%s%s)  hw_index = %i",
39            (uint64_t)m_addr, (uint64_t)m_byte_size,
40            m_enabled ? "enabled " : "disabled",
41            IsHardware() ? "hardware" : "software", m_watch_read ? "r" : "",
42            m_watch_write ? "w" : "", GetHardwareIndex());
43   }
44 }
45 
46 #pragma mark-- DNBBreakpointList
47 
48 DNBBreakpointList::DNBBreakpointList() = default;
49 
50 DNBBreakpointList::~DNBBreakpointList() = default;
51 
Add(nub_addr_t addr,nub_size_t length,bool hardware)52 DNBBreakpoint *DNBBreakpointList::Add(nub_addr_t addr, nub_size_t length,
53                                       bool hardware) {
54   m_breakpoints.insert(
55       std::make_pair(addr, DNBBreakpoint(addr, length, hardware)));
56   iterator pos = m_breakpoints.find(addr);
57   return &pos->second;
58 }
59 
Remove(nub_addr_t addr)60 bool DNBBreakpointList::Remove(nub_addr_t addr) {
61   iterator pos = m_breakpoints.find(addr);
62   if (pos != m_breakpoints.end()) {
63     m_breakpoints.erase(pos);
64     return true;
65   }
66   return false;
67 }
68 
FindByAddress(nub_addr_t addr)69 DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) {
70   iterator pos = m_breakpoints.find(addr);
71   if (pos != m_breakpoints.end())
72     return &pos->second;
73 
74   return NULL;
75 }
76 
FindByAddress(nub_addr_t addr) const77 const DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) const {
78   const_iterator pos = m_breakpoints.find(addr);
79   if (pos != m_breakpoints.end())
80     return &pos->second;
81 
82   return NULL;
83 }
84 
85 const DNBBreakpoint *
FindByHardwareIndex(uint32_t idx) const86 DNBBreakpointList::FindByHardwareIndex(uint32_t idx) const {
87   for (const auto &pos : m_breakpoints)
88     if (pos.second.GetHardwareIndex() == idx)
89       return &pos.second;
90 
91   return nullptr;
92 }
93 
94 const DNBBreakpoint *
FindNearestWatchpoint(nub_addr_t addr) const95 DNBBreakpointList::FindNearestWatchpoint(nub_addr_t addr) const {
96   // Exact match
97   for (const auto &pos : m_breakpoints) {
98     if (pos.second.IsEnabled()) {
99       nub_addr_t start_addr = pos.second.Address();
100       nub_addr_t end_addr = start_addr + pos.second.ByteSize();
101       if (addr >= start_addr && addr <= end_addr)
102         return &pos.second;
103     }
104   }
105 
106   // Find watchpoint nearest to this address
107   // before or after the watched region of memory
108   const DNBBreakpoint *closest = nullptr;
109   uint32_t best_match = UINT32_MAX;
110   for (const auto &pos : m_breakpoints) {
111     if (pos.second.IsEnabled()) {
112       nub_addr_t start_addr = pos.second.Address();
113       nub_addr_t end_addr = start_addr + pos.second.ByteSize();
114       uint32_t delta = addr < start_addr ? start_addr - addr : addr - end_addr;
115       if (delta < best_match) {
116         closest = &pos.second;
117         best_match = delta;
118       }
119     }
120   }
121   return closest;
122 }
123 
124 // Finds the next breakpoint at an address greater than or equal to "addr"
FindBreakpointsThatOverlapRange(nub_addr_t addr,nub_addr_t size,std::vector<DNBBreakpoint * > & bps)125 size_t DNBBreakpointList::FindBreakpointsThatOverlapRange(
126     nub_addr_t addr, nub_addr_t size, std::vector<DNBBreakpoint *> &bps) {
127   bps.clear();
128   iterator end = m_breakpoints.end();
129   // Find the first breakpoint with an address >= to "addr"
130   iterator pos = m_breakpoints.lower_bound(addr);
131   if (pos != end) {
132     if (pos != m_breakpoints.begin()) {
133       // Watch out for a breakpoint at an address less than "addr" that might
134       // still overlap
135       iterator prev_pos = pos;
136       --prev_pos;
137       if (prev_pos->second.IntersectsRange(addr, size, NULL, NULL, NULL))
138         bps.push_back(&pos->second);
139     }
140 
141     while (pos != end) {
142       // When we hit a breakpoint whose start address is greater than "addr +
143       // size" we are done.
144       // Do the math in a way that doesn't risk unsigned overflow with bad
145       // input.
146       if ((pos->second.Address() - addr) >= size)
147         break;
148 
149       // Check if this breakpoint overlaps, and if it does, add it to the list
150       if (pos->second.IntersectsRange(addr, size, NULL, NULL, NULL)) {
151         bps.push_back(&pos->second);
152         ++pos;
153       }
154     }
155   }
156   return bps.size();
157 }
158 
Dump() const159 void DNBBreakpointList::Dump() const {
160   const_iterator pos;
161   const_iterator end = m_breakpoints.end();
162   for (pos = m_breakpoints.begin(); pos != end; ++pos)
163     pos->second.Dump();
164 }
165 
DisableAll()166 void DNBBreakpointList::DisableAll() {
167   iterator pos, end = m_breakpoints.end();
168   for (pos = m_breakpoints.begin(); pos != end; ++pos)
169     pos->second.SetEnabled(false);
170 }
171 
RemoveTrapsFromBuffer(nub_addr_t addr,nub_size_t size,void * p) const172 void DNBBreakpointList::RemoveTrapsFromBuffer(nub_addr_t addr, nub_size_t size,
173                                               void *p) const {
174   uint8_t *buf = (uint8_t *)p;
175   const_iterator end = m_breakpoints.end();
176   const_iterator pos = m_breakpoints.lower_bound(addr);
177   while (pos != end && (pos->first < (addr + size))) {
178     nub_addr_t intersect_addr;
179     nub_size_t intersect_size;
180     nub_size_t opcode_offset;
181     const DNBBreakpoint &bp = pos->second;
182     if (bp.IntersectsRange(addr, size, &intersect_addr, &intersect_size,
183                            &opcode_offset)) {
184       assert(addr <= intersect_addr && intersect_addr < addr + size);
185       assert(addr < intersect_addr + intersect_size &&
186              intersect_addr + intersect_size <= addr + size);
187       assert(opcode_offset + intersect_size <= bp.ByteSize());
188       nub_size_t buf_offset = intersect_addr - addr;
189       ::memcpy(buf + buf_offset, bp.SavedOpcodeBytes() + opcode_offset,
190                intersect_size);
191     }
192     ++pos;
193   }
194 }
195 
DisableAllBreakpoints(MachProcess * process)196 void DNBBreakpointList::DisableAllBreakpoints(MachProcess *process) {
197   iterator pos, end = m_breakpoints.end();
198   for (pos = m_breakpoints.begin(); pos != end; ++pos)
199     process->DisableBreakpoint(pos->second.Address(), false);
200 }
201 
DisableAllWatchpoints(MachProcess * process)202 void DNBBreakpointList::DisableAllWatchpoints(MachProcess *process) {
203   iterator pos, end = m_breakpoints.end();
204   for (pos = m_breakpoints.begin(); pos != end; ++pos)
205     process->DisableWatchpoint(pos->second.Address(), false);
206 }
207 
RemoveDisabled()208 void DNBBreakpointList::RemoveDisabled() {
209   iterator pos = m_breakpoints.begin();
210   while (pos != m_breakpoints.end()) {
211     if (!pos->second.IsEnabled())
212       pos = m_breakpoints.erase(pos);
213     else
214       ++pos;
215   }
216 }
217