xref: /openbsd-src/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick //===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick //
9061da546Spatrick //  Created by Greg Clayton on 6/29/07.
10061da546Spatrick //
11061da546Spatrick //===----------------------------------------------------------------------===//
12061da546Spatrick 
13061da546Spatrick #include "DNBBreakpoint.h"
14061da546Spatrick #include "DNBLog.h"
15061da546Spatrick #include "MachProcess.h"
16061da546Spatrick #include <algorithm>
17be691f3bSpatrick #include <cassert>
18be691f3bSpatrick #include <cinttypes>
19061da546Spatrick 
20061da546Spatrick #pragma mark-- DNBBreakpoint
DNBBreakpoint(nub_addr_t addr,nub_size_t byte_size,bool hardware)21061da546Spatrick DNBBreakpoint::DNBBreakpoint(nub_addr_t addr, nub_size_t byte_size,
22061da546Spatrick                              bool hardware)
23061da546Spatrick     : m_retain_count(1), m_byte_size(static_cast<uint32_t>(byte_size)),
24061da546Spatrick       m_opcode(), m_addr(addr), m_enabled(0), m_hw_preferred(hardware),
25061da546Spatrick       m_is_watchpoint(0), m_watch_read(0), m_watch_write(0),
26061da546Spatrick       m_hw_index(INVALID_NUB_HW_INDEX) {}
27061da546Spatrick 
28*f6aab3d8Srobert DNBBreakpoint::~DNBBreakpoint() = default;
29061da546Spatrick 
Dump() const30061da546Spatrick void DNBBreakpoint::Dump() const {
31061da546Spatrick   if (IsBreakpoint()) {
32061da546Spatrick     DNBLog("DNBBreakpoint addr = 0x%llx  state = %s  type = %s breakpoint  "
33061da546Spatrick            "hw_index = %i",
34061da546Spatrick            (uint64_t)m_addr, m_enabled ? "enabled " : "disabled",
35061da546Spatrick            IsHardware() ? "hardware" : "software", GetHardwareIndex());
36061da546Spatrick   } else {
37061da546Spatrick     DNBLog("DNBBreakpoint addr = 0x%llx  size = %llu  state = %s  type = %s "
38061da546Spatrick            "watchpoint (%s%s)  hw_index = %i",
39061da546Spatrick            (uint64_t)m_addr, (uint64_t)m_byte_size,
40061da546Spatrick            m_enabled ? "enabled " : "disabled",
41061da546Spatrick            IsHardware() ? "hardware" : "software", m_watch_read ? "r" : "",
42061da546Spatrick            m_watch_write ? "w" : "", GetHardwareIndex());
43061da546Spatrick   }
44061da546Spatrick }
45061da546Spatrick 
46061da546Spatrick #pragma mark-- DNBBreakpointList
47061da546Spatrick 
48*f6aab3d8Srobert DNBBreakpointList::DNBBreakpointList() = default;
49061da546Spatrick 
50*f6aab3d8Srobert DNBBreakpointList::~DNBBreakpointList() = default;
51061da546Spatrick 
Add(nub_addr_t addr,nub_size_t length,bool hardware)52061da546Spatrick DNBBreakpoint *DNBBreakpointList::Add(nub_addr_t addr, nub_size_t length,
53061da546Spatrick                                       bool hardware) {
54061da546Spatrick   m_breakpoints.insert(
55061da546Spatrick       std::make_pair(addr, DNBBreakpoint(addr, length, hardware)));
56061da546Spatrick   iterator pos = m_breakpoints.find(addr);
57061da546Spatrick   return &pos->second;
58061da546Spatrick }
59061da546Spatrick 
Remove(nub_addr_t addr)60061da546Spatrick bool DNBBreakpointList::Remove(nub_addr_t addr) {
61061da546Spatrick   iterator pos = m_breakpoints.find(addr);
62061da546Spatrick   if (pos != m_breakpoints.end()) {
63061da546Spatrick     m_breakpoints.erase(pos);
64061da546Spatrick     return true;
65061da546Spatrick   }
66061da546Spatrick   return false;
67061da546Spatrick }
68061da546Spatrick 
FindByAddress(nub_addr_t addr)69061da546Spatrick DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) {
70061da546Spatrick   iterator pos = m_breakpoints.find(addr);
71061da546Spatrick   if (pos != m_breakpoints.end())
72061da546Spatrick     return &pos->second;
73061da546Spatrick 
74061da546Spatrick   return NULL;
75061da546Spatrick }
76061da546Spatrick 
FindByAddress(nub_addr_t addr) const77061da546Spatrick const DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) const {
78061da546Spatrick   const_iterator pos = m_breakpoints.find(addr);
79061da546Spatrick   if (pos != m_breakpoints.end())
80061da546Spatrick     return &pos->second;
81061da546Spatrick 
82061da546Spatrick   return NULL;
83061da546Spatrick }
84061da546Spatrick 
85061da546Spatrick // 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)86061da546Spatrick size_t DNBBreakpointList::FindBreakpointsThatOverlapRange(
87061da546Spatrick     nub_addr_t addr, nub_addr_t size, std::vector<DNBBreakpoint *> &bps) {
88061da546Spatrick   bps.clear();
89061da546Spatrick   iterator end = m_breakpoints.end();
90061da546Spatrick   // Find the first breakpoint with an address >= to "addr"
91061da546Spatrick   iterator pos = m_breakpoints.lower_bound(addr);
92061da546Spatrick   if (pos != end) {
93061da546Spatrick     if (pos != m_breakpoints.begin()) {
94061da546Spatrick       // Watch out for a breakpoint at an address less than "addr" that might
95061da546Spatrick       // still overlap
96061da546Spatrick       iterator prev_pos = pos;
97061da546Spatrick       --prev_pos;
98061da546Spatrick       if (prev_pos->second.IntersectsRange(addr, size, NULL, NULL, NULL))
99061da546Spatrick         bps.push_back(&pos->second);
100061da546Spatrick     }
101061da546Spatrick 
102061da546Spatrick     while (pos != end) {
103061da546Spatrick       // When we hit a breakpoint whose start address is greater than "addr +
104061da546Spatrick       // size" we are done.
105061da546Spatrick       // Do the math in a way that doesn't risk unsigned overflow with bad
106061da546Spatrick       // input.
107061da546Spatrick       if ((pos->second.Address() - addr) >= size)
108061da546Spatrick         break;
109061da546Spatrick 
110061da546Spatrick       // Check if this breakpoint overlaps, and if it does, add it to the list
111061da546Spatrick       if (pos->second.IntersectsRange(addr, size, NULL, NULL, NULL)) {
112061da546Spatrick         bps.push_back(&pos->second);
113061da546Spatrick         ++pos;
114061da546Spatrick       }
115061da546Spatrick     }
116061da546Spatrick   }
117061da546Spatrick   return bps.size();
118061da546Spatrick }
119061da546Spatrick 
Dump() const120061da546Spatrick void DNBBreakpointList::Dump() const {
121061da546Spatrick   const_iterator pos;
122061da546Spatrick   const_iterator end = m_breakpoints.end();
123061da546Spatrick   for (pos = m_breakpoints.begin(); pos != end; ++pos)
124061da546Spatrick     pos->second.Dump();
125061da546Spatrick }
126061da546Spatrick 
DisableAll()127061da546Spatrick void DNBBreakpointList::DisableAll() {
128061da546Spatrick   iterator pos, end = m_breakpoints.end();
129061da546Spatrick   for (pos = m_breakpoints.begin(); pos != end; ++pos)
130061da546Spatrick     pos->second.SetEnabled(false);
131061da546Spatrick }
132061da546Spatrick 
RemoveTrapsFromBuffer(nub_addr_t addr,nub_size_t size,void * p) const133061da546Spatrick void DNBBreakpointList::RemoveTrapsFromBuffer(nub_addr_t addr, nub_size_t size,
134061da546Spatrick                                               void *p) const {
135061da546Spatrick   uint8_t *buf = (uint8_t *)p;
136061da546Spatrick   const_iterator end = m_breakpoints.end();
137061da546Spatrick   const_iterator pos = m_breakpoints.lower_bound(addr);
138061da546Spatrick   while (pos != end && (pos->first < (addr + size))) {
139061da546Spatrick     nub_addr_t intersect_addr;
140061da546Spatrick     nub_size_t intersect_size;
141061da546Spatrick     nub_size_t opcode_offset;
142061da546Spatrick     const DNBBreakpoint &bp = pos->second;
143061da546Spatrick     if (bp.IntersectsRange(addr, size, &intersect_addr, &intersect_size,
144061da546Spatrick                            &opcode_offset)) {
145061da546Spatrick       assert(addr <= intersect_addr && intersect_addr < addr + size);
146061da546Spatrick       assert(addr < intersect_addr + intersect_size &&
147061da546Spatrick              intersect_addr + intersect_size <= addr + size);
148061da546Spatrick       assert(opcode_offset + intersect_size <= bp.ByteSize());
149061da546Spatrick       nub_size_t buf_offset = intersect_addr - addr;
150061da546Spatrick       ::memcpy(buf + buf_offset, bp.SavedOpcodeBytes() + opcode_offset,
151061da546Spatrick                intersect_size);
152061da546Spatrick     }
153061da546Spatrick     ++pos;
154061da546Spatrick   }
155061da546Spatrick }
156061da546Spatrick 
DisableAllBreakpoints(MachProcess * process)157061da546Spatrick void DNBBreakpointList::DisableAllBreakpoints(MachProcess *process) {
158061da546Spatrick   iterator pos, end = m_breakpoints.end();
159061da546Spatrick   for (pos = m_breakpoints.begin(); pos != end; ++pos)
160061da546Spatrick     process->DisableBreakpoint(pos->second.Address(), false);
161061da546Spatrick }
162061da546Spatrick 
DisableAllWatchpoints(MachProcess * process)163061da546Spatrick void DNBBreakpointList::DisableAllWatchpoints(MachProcess *process) {
164061da546Spatrick   iterator pos, end = m_breakpoints.end();
165061da546Spatrick   for (pos = m_breakpoints.begin(); pos != end; ++pos)
166061da546Spatrick     process->DisableWatchpoint(pos->second.Address(), false);
167061da546Spatrick }
168061da546Spatrick 
RemoveDisabled()169061da546Spatrick void DNBBreakpointList::RemoveDisabled() {
170061da546Spatrick   iterator pos = m_breakpoints.begin();
171061da546Spatrick   while (pos != m_breakpoints.end()) {
172061da546Spatrick     if (!pos->second.IsEnabled())
173061da546Spatrick       pos = m_breakpoints.erase(pos);
174061da546Spatrick     else
175061da546Spatrick       ++pos;
176061da546Spatrick   }
177061da546Spatrick }
178