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 <algorithm>
16 #include <inttypes.h>
17 #include "DNBLog.h"
18 
19 
20 #pragma mark -- DNBBreakpoint
21 DNBBreakpoint::DNBBreakpoint(nub_addr_t addr, nub_size_t byte_size, nub_thread_t tid, bool hardware) :
22     m_breakID(GetNextID()),
23     m_tid(tid),
24     m_byte_size(byte_size),
25     m_opcode(),
26     m_addr(addr),
27     m_enabled(0),
28     m_hw_preferred(hardware),
29     m_is_watchpoint(0),
30     m_watch_read(0),
31     m_watch_write(0),
32     m_hw_index(INVALID_NUB_HW_INDEX),
33     m_hit_count(0),
34     m_ignore_count(0),
35     m_callback(NULL),
36     m_callback_baton(NULL)
37 {
38 }
39 
40 DNBBreakpoint::~DNBBreakpoint()
41 {
42 }
43 
44 nub_break_t
45 DNBBreakpoint::GetNextID()
46 {
47     static uint32_t g_nextBreakID = 0;
48     return ++g_nextBreakID;
49 }
50 
51 void
52 DNBBreakpoint::SetCallback(DNBCallbackBreakpointHit callback, void *callback_baton)
53 {
54     m_callback = callback;
55     m_callback_baton = callback_baton;
56 }
57 
58 
59 // RETURNS - true if we should stop at this breakpoint, false if we
60 // should continue.
61 
62 bool
63 DNBBreakpoint::BreakpointHit(nub_process_t pid, nub_thread_t tid)
64 {
65     m_hit_count++;
66 
67     if (m_hit_count > m_ignore_count)
68     {
69         if (m_callback)
70             return m_callback(pid, tid, GetID(), m_callback_baton);
71         return true;
72     }
73     return false;
74 }
75 
76 void
77 DNBBreakpoint::Dump() const
78 {
79     if (IsBreakpoint())
80     {
81         DNBLog ("DNBBreakpoint %u: tid = %8.8" PRIx64 "  addr = 0x%llx  state = %s  type = %s breakpoint  hw_index = %i  hit_count = %-4u  ignore_count = %-4u  callback = %p baton = %p",
82                 m_breakID,
83                 m_tid,
84                 (uint64_t)m_addr,
85                 m_enabled ? "enabled " : "disabled",
86                 IsHardware() ? "hardware" : "software",
87                 GetHardwareIndex(),
88                 GetHitCount(),
89                 GetIgnoreCount(),
90                 m_callback,
91                 m_callback_baton);
92     }
93     else
94     {
95         DNBLog ("DNBBreakpoint %u: tid = %8.8" PRIx64 "  addr = 0x%llx  size = %llu  state = %s  type = %s watchpoint (%s%s)  hw_index = %i  hit_count = %-4u  ignore_count = %-4u  callback = %p baton = %p",
96                 m_breakID,
97                 m_tid,
98                 (uint64_t)m_addr,
99                 (uint64_t)m_byte_size,
100                 m_enabled ? "enabled " : "disabled",
101                 IsHardware() ? "hardware" : "software",
102                 m_watch_read ? "r" : "",
103                 m_watch_write ? "w" : "",
104                 GetHardwareIndex(),
105                 GetHitCount(),
106                 GetIgnoreCount(),
107                 m_callback,
108                 m_callback_baton);
109     }
110 }
111 
112 #pragma mark -- DNBBreakpointList
113 
114 DNBBreakpointList::DNBBreakpointList()
115 {
116 }
117 
118 DNBBreakpointList::~DNBBreakpointList()
119 {
120 }
121 
122 
123 nub_break_t
124 DNBBreakpointList::Add(const DNBBreakpoint& bp)
125 {
126     m_breakpoints.push_back(bp);
127     return m_breakpoints.back().GetID();
128 }
129 
130 bool
131 DNBBreakpointList::ShouldStop(nub_process_t pid, nub_thread_t tid, nub_break_t breakID)
132 {
133     DNBBreakpoint *bp = FindByID (breakID);
134     if (bp)
135     {
136         // Let the breakpoint decide if it should stop here (could not have
137         // reached it's target hit count yet, or it could have a callback
138         // that decided it shouldn't stop (shared library loads/unloads).
139         return bp->BreakpointHit(pid, tid);
140     }
141     // We should stop here since this breakpoint isn't valid anymore or it
142     // doesn't exist.
143     return true;
144 }
145 
146 nub_break_t
147 DNBBreakpointList::FindIDByAddress (nub_addr_t addr)
148 {
149     DNBBreakpoint *bp = FindByAddress (addr);
150     if (bp)
151     {
152         DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBBreakpointList::%s ( addr = 0x%16.16llx ) => %u", __FUNCTION__, (uint64_t)addr, bp->GetID());
153         return bp->GetID();
154     }
155     DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBBreakpointList::%s ( addr = 0x%16.16llx ) => NONE", __FUNCTION__, (uint64_t)addr);
156     return INVALID_NUB_BREAK_ID;
157 }
158 
159 bool
160 DNBBreakpointList::Remove (nub_break_t breakID)
161 {
162     iterator pos = GetBreakIDIterator(breakID);    // Predicate
163     if (pos != m_breakpoints.end())
164     {
165         m_breakpoints.erase(pos);
166         return true;
167     }
168     return false;
169 }
170 
171 
172 class BreakpointIDMatches
173 {
174 public:
175     BreakpointIDMatches (nub_break_t breakID) : m_breakID(breakID) {}
176     bool operator() (const DNBBreakpoint& bp) const
177     {
178         return m_breakID == bp.GetID();
179     }
180  private:
181    const nub_break_t m_breakID;
182 };
183 
184 class BreakpointAddressMatches
185 {
186 public:
187     BreakpointAddressMatches (nub_addr_t addr) : m_addr(addr) {}
188     bool operator() (const DNBBreakpoint& bp) const
189     {
190         return m_addr == bp.Address();
191     }
192  private:
193    const nub_addr_t m_addr;
194 };
195 
196 DNBBreakpointList::iterator
197 DNBBreakpointList::GetBreakIDIterator (nub_break_t breakID)
198 {
199     return std::find_if(m_breakpoints.begin(), m_breakpoints.end(), // Search full range
200                         BreakpointIDMatches(breakID));              // Predicate
201 }
202 
203 DNBBreakpointList::const_iterator
204 DNBBreakpointList::GetBreakIDConstIterator (nub_break_t breakID) const
205 {
206     return std::find_if(m_breakpoints.begin(), m_breakpoints.end(), // Search full range
207                         BreakpointIDMatches(breakID));              // Predicate
208 }
209 
210 DNBBreakpoint *
211 DNBBreakpointList::FindByID (nub_break_t breakID)
212 {
213     iterator pos = GetBreakIDIterator(breakID);
214     if (pos != m_breakpoints.end())
215         return &(*pos);
216 
217     return NULL;
218 }
219 
220 const DNBBreakpoint *
221 DNBBreakpointList::FindByID (nub_break_t breakID) const
222 {
223     const_iterator pos = GetBreakIDConstIterator(breakID);
224     if (pos != m_breakpoints.end())
225         return &(*pos);
226 
227     return NULL;
228 }
229 
230 DNBBreakpoint *
231 DNBBreakpointList::FindByAddress (nub_addr_t addr)
232 {
233     iterator end = m_breakpoints.end();
234     iterator pos = std::find_if(m_breakpoints.begin(), end,             // Search full range
235                                 BreakpointAddressMatches(addr));        // Predicate
236     if (pos != end)
237         return &(*pos);
238 
239     return NULL;
240 }
241 
242 const DNBBreakpoint *
243 DNBBreakpointList::FindByAddress (nub_addr_t addr) const
244 {
245     const_iterator end = m_breakpoints.end();
246     const_iterator pos = std::find_if(m_breakpoints.begin(), end,       // Search full range
247                                       BreakpointAddressMatches(addr));  // Predicate
248     if (pos != end)
249         return &(*pos);
250 
251     return NULL;
252 }
253 
254 bool
255 DNBBreakpointList::SetCallback(nub_break_t breakID, DNBCallbackBreakpointHit callback, void *callback_baton)
256 {
257     DNBBreakpoint *bp = FindByID (breakID);
258     if (bp)
259     {
260         bp->SetCallback(callback, callback_baton);
261         return true;
262     }
263     return false;
264 }
265 
266 
267 void
268 DNBBreakpointList::Dump() const
269 {
270     const_iterator pos;
271     const_iterator end = m_breakpoints.end();
272     for (pos = m_breakpoints.begin(); pos != end; ++pos)
273         (*pos).Dump();
274 }
275 
276 
277 DNBBreakpoint *
278 DNBBreakpointList::GetByIndex (uint32_t i)
279 {
280     iterator end = m_breakpoints.end();
281     iterator pos;
282     uint32_t curr_i = 0;
283     for (pos = m_breakpoints.begin(), curr_i = 0; pos != end; ++pos, ++curr_i)
284     {
285         if (curr_i == i)
286             return &(*pos);
287     }
288     return NULL;
289 }
290 
291 void
292 DNBBreakpointList::DisableAll ()
293 {
294     iterator pos, end = m_breakpoints.end();
295     for (pos = m_breakpoints.begin(); pos != end; ++pos)
296         (*pos).SetEnabled(false);
297 }
298 
299 
300 const DNBBreakpoint *
301 DNBBreakpointList::GetByIndex (uint32_t i) const
302 {
303     const_iterator end = m_breakpoints.end();
304     const_iterator pos;
305     uint32_t curr_i = 0;
306     for (pos = m_breakpoints.begin(), curr_i = 0; pos != end; ++pos, ++curr_i)
307     {
308         if (curr_i == i)
309             return &(*pos);
310     }
311     return NULL;
312 }
313 
314