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