1 | //===-- BreakpointSite.cpp ------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include <cinttypes> |
10 | |
11 | #include "lldb/Breakpoint/BreakpointSite.h" |
12 | |
13 | #include "lldb/Breakpoint/Breakpoint.h" |
14 | #include "lldb/Breakpoint/BreakpointLocation.h" |
15 | #include "lldb/Utility/Stream.h" |
16 | |
17 | using namespace lldb; |
18 | using namespace lldb_private; |
19 | |
20 | BreakpointSite::BreakpointSite(const BreakpointLocationSP &constituent, |
21 | lldb::addr_t addr, bool use_hardware) |
22 | : StoppointSite(GetNextID(), addr, 0, use_hardware), |
23 | m_type(eSoftware), // Process subclasses need to set this correctly using |
24 | // SetType() |
25 | m_saved_opcode(), m_trap_opcode(), |
26 | m_enabled(false) // Need to create it disabled, so the first enable turns |
27 | // it on. |
28 | { |
29 | m_constituents.Add(bp_loc_sp: constituent); |
30 | } |
31 | |
32 | BreakpointSite::~BreakpointSite() { |
33 | BreakpointLocationSP bp_loc_sp; |
34 | const size_t constituent_count = m_constituents.GetSize(); |
35 | for (size_t i = 0; i < constituent_count; i++) { |
36 | m_constituents.GetByIndex(i)->ClearBreakpointSite(); |
37 | } |
38 | } |
39 | |
40 | break_id_t BreakpointSite::GetNextID() { |
41 | static break_id_t g_next_id = 0; |
42 | return ++g_next_id; |
43 | } |
44 | |
45 | // RETURNS - true if we should stop at this breakpoint, false if we |
46 | // should continue. |
47 | |
48 | bool BreakpointSite::ShouldStop(StoppointCallbackContext *context) { |
49 | m_hit_counter.Increment(); |
50 | // ShouldStop can do a lot of work, and might even come back and hit |
51 | // this breakpoint site again. So don't hold the m_constituents_mutex the |
52 | // whole while. Instead make a local copy of the collection and call |
53 | // ShouldStop on the copy. |
54 | BreakpointLocationCollection constituents_copy; |
55 | { |
56 | std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); |
57 | constituents_copy = m_constituents; |
58 | } |
59 | return constituents_copy.ShouldStop(context); |
60 | } |
61 | |
62 | bool BreakpointSite::IsBreakpointAtThisSite(lldb::break_id_t bp_id) { |
63 | std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); |
64 | const size_t constituent_count = m_constituents.GetSize(); |
65 | for (size_t i = 0; i < constituent_count; i++) { |
66 | if (m_constituents.GetByIndex(i)->GetBreakpoint().GetID() == bp_id) |
67 | return true; |
68 | } |
69 | return false; |
70 | } |
71 | |
72 | void BreakpointSite::Dump(Stream *s) const { |
73 | if (s == nullptr) |
74 | return; |
75 | |
76 | s->Printf(format: "BreakpointSite %u: addr = 0x%8.8" PRIx64 |
77 | " type = %s breakpoint hit_count = %-4u" , |
78 | GetID(), (uint64_t)m_addr, IsHardware() ? "hardware" : "software" , |
79 | GetHitCount()); |
80 | } |
81 | |
82 | void BreakpointSite::GetDescription(Stream *s, lldb::DescriptionLevel level) { |
83 | std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); |
84 | if (level != lldb::eDescriptionLevelBrief) |
85 | s->Printf(format: "breakpoint site: %d at 0x%8.8" PRIx64, GetID(), |
86 | GetLoadAddress()); |
87 | m_constituents.GetDescription(s, level); |
88 | } |
89 | |
90 | bool BreakpointSite::IsInternal() const { return m_constituents.IsInternal(); } |
91 | |
92 | uint8_t *BreakpointSite::GetTrapOpcodeBytes() { return &m_trap_opcode[0]; } |
93 | |
94 | const uint8_t *BreakpointSite::GetTrapOpcodeBytes() const { |
95 | return &m_trap_opcode[0]; |
96 | } |
97 | |
98 | size_t BreakpointSite::GetTrapOpcodeMaxByteSize() const { |
99 | return sizeof(m_trap_opcode); |
100 | } |
101 | |
102 | bool BreakpointSite::SetTrapOpcode(const uint8_t *trap_opcode, |
103 | uint32_t trap_opcode_size) { |
104 | if (trap_opcode_size > 0 && trap_opcode_size <= sizeof(m_trap_opcode)) { |
105 | m_byte_size = trap_opcode_size; |
106 | ::memcpy(dest: m_trap_opcode, src: trap_opcode, n: trap_opcode_size); |
107 | return true; |
108 | } |
109 | m_byte_size = 0; |
110 | return false; |
111 | } |
112 | |
113 | uint8_t *BreakpointSite::GetSavedOpcodeBytes() { return &m_saved_opcode[0]; } |
114 | |
115 | const uint8_t *BreakpointSite::GetSavedOpcodeBytes() const { |
116 | return &m_saved_opcode[0]; |
117 | } |
118 | |
119 | bool BreakpointSite::IsEnabled() const { return m_enabled; } |
120 | |
121 | void BreakpointSite::SetEnabled(bool enabled) { m_enabled = enabled; } |
122 | |
123 | void BreakpointSite::AddConstituent(const BreakpointLocationSP &constituent) { |
124 | std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); |
125 | m_constituents.Add(bp_loc_sp: constituent); |
126 | } |
127 | |
128 | size_t BreakpointSite::RemoveConstituent(lldb::break_id_t break_id, |
129 | lldb::break_id_t break_loc_id) { |
130 | std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); |
131 | m_constituents.Remove(break_id, break_loc_id); |
132 | return m_constituents.GetSize(); |
133 | } |
134 | |
135 | size_t BreakpointSite::GetNumberOfConstituents() { |
136 | std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); |
137 | return m_constituents.GetSize(); |
138 | } |
139 | |
140 | BreakpointLocationSP BreakpointSite::GetConstituentAtIndex(size_t index) { |
141 | std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); |
142 | return m_constituents.GetByIndex(i: index); |
143 | } |
144 | |
145 | bool BreakpointSite::ValidForThisThread(Thread &thread) { |
146 | std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); |
147 | return m_constituents.ValidForThisThread(thread); |
148 | } |
149 | |
150 | void BreakpointSite::BumpHitCounts() { |
151 | std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); |
152 | for (BreakpointLocationSP loc_sp : m_constituents.BreakpointLocations()) { |
153 | loc_sp->BumpHitCount(); |
154 | } |
155 | } |
156 | |
157 | bool BreakpointSite::IntersectsRange(lldb::addr_t addr, size_t size, |
158 | lldb::addr_t *intersect_addr, |
159 | size_t *intersect_size, |
160 | size_t *opcode_offset) const { |
161 | // The function should be called only for software breakpoints. |
162 | lldbassert(GetType() == Type::eSoftware); |
163 | |
164 | if (m_byte_size == 0) |
165 | return false; |
166 | |
167 | const lldb::addr_t bp_end_addr = m_addr + m_byte_size; |
168 | const lldb::addr_t end_addr = addr + size; |
169 | // Is the breakpoint end address before the passed in start address? |
170 | if (bp_end_addr <= addr) |
171 | return false; |
172 | |
173 | // Is the breakpoint start address after passed in end address? |
174 | if (end_addr <= m_addr) |
175 | return false; |
176 | |
177 | if (intersect_addr || intersect_size || opcode_offset) { |
178 | if (m_addr < addr) { |
179 | if (intersect_addr) |
180 | *intersect_addr = addr; |
181 | if (intersect_size) |
182 | *intersect_size = |
183 | std::min<lldb::addr_t>(a: bp_end_addr, b: end_addr) - addr; |
184 | if (opcode_offset) |
185 | *opcode_offset = addr - m_addr; |
186 | } else { |
187 | if (intersect_addr) |
188 | *intersect_addr = m_addr; |
189 | if (intersect_size) |
190 | *intersect_size = |
191 | std::min<lldb::addr_t>(a: bp_end_addr, b: end_addr) - m_addr; |
192 | if (opcode_offset) |
193 | *opcode_offset = 0; |
194 | } |
195 | } |
196 | return true; |
197 | } |
198 | |
199 | size_t BreakpointSite::CopyConstituentsList( |
200 | BreakpointLocationCollection &out_collection) { |
201 | std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); |
202 | for (BreakpointLocationSP loc_sp : m_constituents.BreakpointLocations()) { |
203 | out_collection.Add(bp_loc_sp: loc_sp); |
204 | } |
205 | return out_collection.GetSize(); |
206 | } |
207 | |