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
21DNBBreakpoint::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
28DNBBreakpoint::~DNBBreakpoint() = default;
29
30void 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
48DNBBreakpointList::DNBBreakpointList() = default;
49
50DNBBreakpointList::~DNBBreakpointList() = default;
51
52DNBBreakpoint *DNBBreakpointList::Add(nub_addr_t addr, nub_size_t length,
53 bool hardware) {
54 m_breakpoints.insert(
55 x: std::make_pair(x&: addr, y: DNBBreakpoint(addr, length, hardware)));
56 iterator pos = m_breakpoints.find(x: addr);
57 return &pos->second;
58}
59
60bool DNBBreakpointList::Remove(nub_addr_t addr) {
61 iterator pos = m_breakpoints.find(x: addr);
62 if (pos != m_breakpoints.end()) {
63 m_breakpoints.erase(position: pos);
64 return true;
65 }
66 return false;
67}
68
69DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) {
70 iterator pos = m_breakpoints.find(x: addr);
71 if (pos != m_breakpoints.end())
72 return &pos->second;
73
74 return NULL;
75}
76
77const DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) const {
78 const_iterator pos = m_breakpoints.find(x: addr);
79 if (pos != m_breakpoints.end())
80 return &pos->second;
81
82 return NULL;
83}
84
85const DNBBreakpoint *
86DNBBreakpointList::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
94const DNBBreakpoint *
95DNBBreakpointList::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"
125size_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(x: 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(x: &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(x: &pos->second);
152 ++pos;
153 }
154 }
155 }
156 return bps.size();
157}
158
159void 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
166void 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
172void 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(x: 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_addr, intersect_size: &intersect_size,
183 opcode_offset: &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
196void 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
202void 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
208void DNBBreakpointList::RemoveDisabled() {
209 iterator pos = m_breakpoints.begin();
210 while (pos != m_breakpoints.end()) {
211 if (!pos->second.IsEnabled())
212 pos = m_breakpoints.erase(position: pos);
213 else
214 ++pos;
215 }
216}
217

source code of lldb/tools/debugserver/source/DNBBreakpoint.cpp