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
23DNBBreakpoint::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
37DNBBreakpoint::~DNBBreakpoint()
38{
39}
40
41void
42DNBBreakpoint::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
67DNBBreakpointList::DNBBreakpointList()
68{
69}
70
71DNBBreakpointList::~DNBBreakpointList()
72{
73}
74
75
76DNBBreakpoint *
77DNBBreakpointList::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
84bool
85DNBBreakpointList::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
96DNBBreakpoint *
97DNBBreakpointList::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
106const DNBBreakpoint *
107DNBBreakpointList::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"
117size_t
118DNBBreakpointList::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
156void
157DNBBreakpointList::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
165void
166DNBBreakpointList::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
174void
175DNBBreakpointList::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
198void
199DNBBreakpointList::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
206void
207DNBBreakpointList::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
214void
215DNBBreakpointList::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