130fdc8d8SChris Lattner //===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===//
230fdc8d8SChris Lattner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
630fdc8d8SChris Lattner //
730fdc8d8SChris Lattner //===----------------------------------------------------------------------===//
830fdc8d8SChris Lattner //
930fdc8d8SChris Lattner // Created by Greg Clayton on 6/29/07.
1030fdc8d8SChris Lattner //
1130fdc8d8SChris Lattner //===----------------------------------------------------------------------===//
1230fdc8d8SChris Lattner
1330fdc8d8SChris Lattner #include "DNBBreakpoint.h"
1430fdc8d8SChris Lattner #include "DNBLog.h"
15b9c1b51eSKate Stone #include "MachProcess.h"
16b9c1b51eSKate Stone #include <algorithm>
1776e47d48SRaphael Isemann #include <cassert>
1876e47d48SRaphael Isemann #include <cinttypes>
1930fdc8d8SChris Lattner
2030fdc8d8SChris Lattner #pragma mark-- DNBBreakpoint
DNBBreakpoint(nub_addr_t addr,nub_size_t byte_size,bool hardware)21b9c1b51eSKate Stone DNBBreakpoint::DNBBreakpoint(nub_addr_t addr, nub_size_t byte_size,
22b9c1b51eSKate Stone bool hardware)
23b9c1b51eSKate Stone : m_retain_count(1), m_byte_size(static_cast<uint32_t>(byte_size)),
24b9c1b51eSKate Stone m_opcode(), m_addr(addr), m_enabled(0), m_hw_preferred(hardware),
25b9c1b51eSKate Stone m_is_watchpoint(0), m_watch_read(0), m_watch_write(0),
26b9c1b51eSKate Stone m_hw_index(INVALID_NUB_HW_INDEX) {}
2730fdc8d8SChris Lattner
28*24f9a2f5SShafik Yaghmour DNBBreakpoint::~DNBBreakpoint() = default;
2930fdc8d8SChris Lattner
Dump() const30b9c1b51eSKate Stone void DNBBreakpoint::Dump() const {
31b9c1b51eSKate Stone if (IsBreakpoint()) {
32b9c1b51eSKate Stone DNBLog("DNBBreakpoint addr = 0x%llx state = %s type = %s breakpoint "
33b9c1b51eSKate Stone "hw_index = %i",
34b9c1b51eSKate Stone (uint64_t)m_addr, m_enabled ? "enabled " : "disabled",
35b9c1b51eSKate Stone IsHardware() ? "hardware" : "software", GetHardwareIndex());
36b9c1b51eSKate Stone } else {
37b9c1b51eSKate Stone DNBLog("DNBBreakpoint addr = 0x%llx size = %llu state = %s type = %s "
38b9c1b51eSKate Stone "watchpoint (%s%s) hw_index = %i",
39b9c1b51eSKate Stone (uint64_t)m_addr, (uint64_t)m_byte_size,
4030fdc8d8SChris Lattner m_enabled ? "enabled " : "disabled",
41b9c1b51eSKate Stone IsHardware() ? "hardware" : "software", m_watch_read ? "r" : "",
42b9c1b51eSKate Stone m_watch_write ? "w" : "", GetHardwareIndex());
4330fdc8d8SChris Lattner }
4430fdc8d8SChris Lattner }
4530fdc8d8SChris Lattner
4630fdc8d8SChris Lattner #pragma mark-- DNBBreakpointList
4730fdc8d8SChris Lattner
48*24f9a2f5SShafik Yaghmour DNBBreakpointList::DNBBreakpointList() = default;
4930fdc8d8SChris Lattner
50*24f9a2f5SShafik Yaghmour DNBBreakpointList::~DNBBreakpointList() = default;
5130fdc8d8SChris Lattner
Add(nub_addr_t addr,nub_size_t length,bool hardware)52b9c1b51eSKate Stone DNBBreakpoint *DNBBreakpointList::Add(nub_addr_t addr, nub_size_t length,
53b9c1b51eSKate Stone bool hardware) {
54b9c1b51eSKate Stone m_breakpoints.insert(
55b9c1b51eSKate Stone std::make_pair(addr, DNBBreakpoint(addr, length, hardware)));
56d8cf1a11SGreg Clayton iterator pos = m_breakpoints.find(addr);
57d8cf1a11SGreg Clayton return &pos->second;
5830fdc8d8SChris Lattner }
5930fdc8d8SChris Lattner
Remove(nub_addr_t addr)60b9c1b51eSKate Stone bool DNBBreakpointList::Remove(nub_addr_t addr) {
61d8cf1a11SGreg Clayton iterator pos = m_breakpoints.find(addr);
62b9c1b51eSKate Stone if (pos != m_breakpoints.end()) {
6330fdc8d8SChris Lattner m_breakpoints.erase(pos);
6430fdc8d8SChris Lattner return true;
6530fdc8d8SChris Lattner }
6630fdc8d8SChris Lattner return false;
6730fdc8d8SChris Lattner }
6830fdc8d8SChris Lattner
FindByAddress(nub_addr_t addr)69b9c1b51eSKate Stone DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) {
70d8cf1a11SGreg Clayton iterator pos = m_breakpoints.find(addr);
71d8cf1a11SGreg Clayton if (pos != m_breakpoints.end())
72d8cf1a11SGreg Clayton return &pos->second;
7330fdc8d8SChris Lattner
7430fdc8d8SChris Lattner return NULL;
7530fdc8d8SChris Lattner }
7630fdc8d8SChris Lattner
FindByAddress(nub_addr_t addr) const77b9c1b51eSKate Stone const DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) const {
78d8cf1a11SGreg Clayton const_iterator pos = m_breakpoints.find(addr);
79d8cf1a11SGreg Clayton if (pos != m_breakpoints.end())
80d8cf1a11SGreg Clayton return &pos->second;
8130fdc8d8SChris Lattner
8230fdc8d8SChris Lattner return NULL;
8330fdc8d8SChris Lattner }
8430fdc8d8SChris Lattner
85d8cf1a11SGreg Clayton // 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)86b9c1b51eSKate Stone size_t DNBBreakpointList::FindBreakpointsThatOverlapRange(
87b9c1b51eSKate Stone nub_addr_t addr, nub_addr_t size, std::vector<DNBBreakpoint *> &bps) {
88d8cf1a11SGreg Clayton bps.clear();
89d8cf1a11SGreg Clayton iterator end = m_breakpoints.end();
90d8cf1a11SGreg Clayton // Find the first breakpoint with an address >= to "addr"
91d8cf1a11SGreg Clayton iterator pos = m_breakpoints.lower_bound(addr);
92b9c1b51eSKate Stone if (pos != end) {
93b9c1b51eSKate Stone if (pos != m_breakpoints.begin()) {
94b9c1b51eSKate Stone // Watch out for a breakpoint at an address less than "addr" that might
95b9c1b51eSKate Stone // still overlap
96d8cf1a11SGreg Clayton iterator prev_pos = pos;
97d8cf1a11SGreg Clayton --prev_pos;
98d8cf1a11SGreg Clayton if (prev_pos->second.IntersectsRange(addr, size, NULL, NULL, NULL))
99d8cf1a11SGreg Clayton bps.push_back(&pos->second);
10030fdc8d8SChris Lattner }
10130fdc8d8SChris Lattner
102b9c1b51eSKate Stone while (pos != end) {
103b9c1b51eSKate Stone // When we hit a breakpoint whose start address is greater than "addr +
104b9c1b51eSKate Stone // size" we are done.
105b9c1b51eSKate Stone // Do the math in a way that doesn't risk unsigned overflow with bad
106b9c1b51eSKate Stone // input.
107d8cf1a11SGreg Clayton if ((pos->second.Address() - addr) >= size)
108d8cf1a11SGreg Clayton break;
109d8cf1a11SGreg Clayton
110d8cf1a11SGreg Clayton // Check if this breakpoint overlaps, and if it does, add it to the list
111b9c1b51eSKate Stone if (pos->second.IntersectsRange(addr, size, NULL, NULL, NULL)) {
112d8cf1a11SGreg Clayton bps.push_back(&pos->second);
113d8cf1a11SGreg Clayton ++pos;
114d8cf1a11SGreg Clayton }
115d8cf1a11SGreg Clayton }
116d8cf1a11SGreg Clayton }
117d8cf1a11SGreg Clayton return bps.size();
118d8cf1a11SGreg Clayton }
11930fdc8d8SChris Lattner
Dump() const120b9c1b51eSKate Stone void DNBBreakpointList::Dump() const {
12130fdc8d8SChris Lattner const_iterator pos;
12230fdc8d8SChris Lattner const_iterator end = m_breakpoints.end();
12330fdc8d8SChris Lattner for (pos = m_breakpoints.begin(); pos != end; ++pos)
124d8cf1a11SGreg Clayton pos->second.Dump();
12530fdc8d8SChris Lattner }
12630fdc8d8SChris Lattner
DisableAll()127b9c1b51eSKate Stone void DNBBreakpointList::DisableAll() {
12815fc2be7SGreg Clayton iterator pos, end = m_breakpoints.end();
12915fc2be7SGreg Clayton for (pos = m_breakpoints.begin(); pos != end; ++pos)
130d8cf1a11SGreg Clayton pos->second.SetEnabled(false);
13115fc2be7SGreg Clayton }
13215fc2be7SGreg Clayton
RemoveTrapsFromBuffer(nub_addr_t addr,nub_size_t size,void * p) const133b9c1b51eSKate Stone void DNBBreakpointList::RemoveTrapsFromBuffer(nub_addr_t addr, nub_size_t size,
134b9c1b51eSKate Stone void *p) const {
135d8cf1a11SGreg Clayton uint8_t *buf = (uint8_t *)p;
13630fdc8d8SChris Lattner const_iterator end = m_breakpoints.end();
137d8cf1a11SGreg Clayton const_iterator pos = m_breakpoints.lower_bound(addr);
138b9c1b51eSKate Stone while (pos != end && (pos->first < (addr + size))) {
139d8cf1a11SGreg Clayton nub_addr_t intersect_addr;
140d8cf1a11SGreg Clayton nub_size_t intersect_size;
141d8cf1a11SGreg Clayton nub_size_t opcode_offset;
142d8cf1a11SGreg Clayton const DNBBreakpoint &bp = pos->second;
143b9c1b51eSKate Stone if (bp.IntersectsRange(addr, size, &intersect_addr, &intersect_size,
144b9c1b51eSKate Stone &opcode_offset)) {
145d8cf1a11SGreg Clayton assert(addr <= intersect_addr && intersect_addr < addr + size);
146b9c1b51eSKate Stone assert(addr < intersect_addr + intersect_size &&
147b9c1b51eSKate Stone intersect_addr + intersect_size <= addr + size);
148d8cf1a11SGreg Clayton assert(opcode_offset + intersect_size <= bp.ByteSize());
149d8cf1a11SGreg Clayton nub_size_t buf_offset = intersect_addr - addr;
150b9c1b51eSKate Stone ::memcpy(buf + buf_offset, bp.SavedOpcodeBytes() + opcode_offset,
151b9c1b51eSKate Stone intersect_size);
15230fdc8d8SChris Lattner }
153d8cf1a11SGreg Clayton ++pos;
154d8cf1a11SGreg Clayton }
15530fdc8d8SChris Lattner }
15630fdc8d8SChris Lattner
DisableAllBreakpoints(MachProcess * process)157b9c1b51eSKate Stone void DNBBreakpointList::DisableAllBreakpoints(MachProcess *process) {
158d8cf1a11SGreg Clayton iterator pos, end = m_breakpoints.end();
159d8cf1a11SGreg Clayton for (pos = m_breakpoints.begin(); pos != end; ++pos)
160d8cf1a11SGreg Clayton process->DisableBreakpoint(pos->second.Address(), false);
161d8cf1a11SGreg Clayton }
162d8cf1a11SGreg Clayton
DisableAllWatchpoints(MachProcess * process)163b9c1b51eSKate Stone void DNBBreakpointList::DisableAllWatchpoints(MachProcess *process) {
164d8cf1a11SGreg Clayton iterator pos, end = m_breakpoints.end();
165d8cf1a11SGreg Clayton for (pos = m_breakpoints.begin(); pos != end; ++pos)
166d8cf1a11SGreg Clayton process->DisableWatchpoint(pos->second.Address(), false);
167d8cf1a11SGreg Clayton }
168d8cf1a11SGreg Clayton
RemoveDisabled()169b9c1b51eSKate Stone void DNBBreakpointList::RemoveDisabled() {
170d8cf1a11SGreg Clayton iterator pos = m_breakpoints.begin();
171b9c1b51eSKate Stone while (pos != m_breakpoints.end()) {
172d8cf1a11SGreg Clayton if (!pos->second.IsEnabled())
173d8cf1a11SGreg Clayton pos = m_breakpoints.erase(pos);
174d8cf1a11SGreg Clayton else
175d8cf1a11SGreg Clayton ++pos;
176d8cf1a11SGreg Clayton }
177d8cf1a11SGreg Clayton }
178