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