1 //===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // Created by Greg Clayton on 6/29/07.
11 //===----------------------------------------------------------------------===//
13 #include "DNBBreakpoint.h"
15 #include "MachProcess.h"
20 #pragma mark-- DNBBreakpoint
21 DNBBreakpoint::DNBBreakpoint(nub_addr_t addr
, nub_size_t byte_size
,
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
) {}
28 DNBBreakpoint::~DNBBreakpoint() = default;
30 void DNBBreakpoint::Dump() const {
32 DNBLog("DNBBreakpoint addr = 0x%llx state = %s type = %s breakpoint "
34 (uint64_t)m_addr
, m_enabled
? "enabled " : "disabled",
35 IsHardware() ? "hardware" : "software", GetHardwareIndex());
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());
46 #pragma mark-- DNBBreakpointList
48 DNBBreakpointList::DNBBreakpointList() = default;
50 DNBBreakpointList::~DNBBreakpointList() = default;
52 DNBBreakpoint
*DNBBreakpointList::Add(nub_addr_t addr
, nub_size_t length
,
55 std::make_pair(addr
, DNBBreakpoint(addr
, length
, hardware
)));
56 iterator pos
= m_breakpoints
.find(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
);
69 DNBBreakpoint
*DNBBreakpointList::FindByAddress(nub_addr_t addr
) {
70 iterator pos
= m_breakpoints
.find(addr
);
71 if (pos
!= m_breakpoints
.end())
77 const DNBBreakpoint
*DNBBreakpointList::FindByAddress(nub_addr_t addr
) const {
78 const_iterator pos
= m_breakpoints
.find(addr
);
79 if (pos
!= m_breakpoints
.end())
86 DNBBreakpointList::FindByHardwareIndex(uint32_t idx
) const {
87 for (const auto &pos
: m_breakpoints
)
88 if (pos
.second
.GetHardwareIndex() == idx
)
95 DNBBreakpointList::FindNearestWatchpoint(nub_addr_t addr
) const {
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
)
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
;
124 // Finds the next breakpoint at an address greater than or equal to "addr"
125 size_t DNBBreakpointList::FindBreakpointsThatOverlapRange(
126 nub_addr_t addr
, nub_addr_t size
, std::vector
<DNBBreakpoint
*> &bps
) {
128 iterator end
= m_breakpoints
.end();
129 // Find the first breakpoint with an address >= to "addr"
130 iterator pos
= m_breakpoints
.lower_bound(addr
);
132 if (pos
!= m_breakpoints
.begin()) {
133 // Watch out for a breakpoint at an address less than "addr" that might
135 iterator prev_pos
= pos
;
137 if (prev_pos
->second
.IntersectsRange(addr
, size
, NULL
, NULL
, NULL
))
138 bps
.push_back(&pos
->second
);
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
146 if ((pos
->second
.Address() - addr
) >= size
)
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
);
159 void DNBBreakpointList::Dump() const {
161 const_iterator end
= m_breakpoints
.end();
162 for (pos
= m_breakpoints
.begin(); pos
!= end
; ++pos
)
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);
172 void DNBBreakpointList::RemoveTrapsFromBuffer(nub_addr_t addr
, nub_size_t size
,
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
,
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
,
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);
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);
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
);