1 //===-- BreakpointResolver.cpp --------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Breakpoint/BreakpointResolver.h"
11 #include "lldb/Breakpoint/Breakpoint.h"
12 #include "lldb/Breakpoint/BreakpointLocation.h"
13 // Have to include the other breakpoint resolver types here so the static
14 // create from StructuredData can call them.
15 #include "lldb/Breakpoint/BreakpointResolverAddress.h"
16 #include "lldb/Breakpoint/BreakpointResolverFileLine.h"
17 #include "lldb/Breakpoint/BreakpointResolverFileRegex.h"
18 #include "lldb/Breakpoint/BreakpointResolverName.h"
19 #include "lldb/Breakpoint/BreakpointResolverScripted.h"
20 #include "lldb/Core/Address.h"
21 #include "lldb/Core/ModuleList.h"
22 #include "lldb/Core/SearchFilter.h"
23 #include "lldb/Symbol/CompileUnit.h"
24 #include "lldb/Symbol/Function.h"
25 #include "lldb/Symbol/SymbolContext.h"
26 #include "lldb/Target/Target.h"
27 #include "lldb/Utility/LLDBLog.h"
28 #include "lldb/Utility/Log.h"
29 #include "lldb/Utility/Stream.h"
30 #include "lldb/Utility/StreamString.h"
33 using namespace lldb_private
;
36 // BreakpointResolver:
37 const char *BreakpointResolver::g_ty_to_name
[] = {"FileAndLine", "Address",
38 "SymbolName", "SourceRegex",
39 "Python", "Exception",
42 const char *BreakpointResolver::g_option_names
[static_cast<uint32_t>(
43 BreakpointResolver::OptionNames::LastOptionName
)] = {
44 "AddressOffset", "Exact", "FileName", "Inlines", "Language",
45 "LineNumber", "Column", "ModuleName", "NameMask", "Offset",
46 "PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth",
47 "SkipPrologue", "SymbolNames"};
49 const char *BreakpointResolver::ResolverTyToName(enum ResolverTy type
) {
50 if (type
> LastKnownResolverType
)
51 return g_ty_to_name
[UnknownResolver
];
53 return g_ty_to_name
[type
];
56 BreakpointResolver::ResolverTy
57 BreakpointResolver::NameToResolverTy(llvm::StringRef name
) {
58 for (size_t i
= 0; i
< LastKnownResolverType
; i
++) {
59 if (name
== g_ty_to_name
[i
])
62 return UnknownResolver
;
65 BreakpointResolver::BreakpointResolver(const BreakpointSP
&bkpt
,
66 const unsigned char resolverTy
,
68 : m_breakpoint(bkpt
), m_offset(offset
), SubclassID(resolverTy
) {}
70 BreakpointResolver::~BreakpointResolver() = default;
72 BreakpointResolverSP
BreakpointResolver::CreateFromStructuredData(
73 const StructuredData::Dictionary
&resolver_dict
, Status
&error
) {
74 BreakpointResolverSP result_sp
;
75 if (!resolver_dict
.IsValid()) {
76 error
.SetErrorString("Can't deserialize from an invalid data object.");
80 llvm::StringRef subclass_name
;
82 bool success
= resolver_dict
.GetValueForKeyAsString(
83 GetSerializationSubclassKey(), subclass_name
);
86 error
.SetErrorString("Resolver data missing subclass resolver key");
90 ResolverTy resolver_type
= NameToResolverTy(subclass_name
);
91 if (resolver_type
== UnknownResolver
) {
92 error
.SetErrorStringWithFormatv("Unknown resolver type: {0}.",
97 StructuredData::Dictionary
*subclass_options
= nullptr;
98 success
= resolver_dict
.GetValueForKeyAsDictionary(
99 GetSerializationSubclassOptionsKey(), subclass_options
);
100 if (!success
|| !subclass_options
|| !subclass_options
->IsValid()) {
101 error
.SetErrorString("Resolver data missing subclass options key.");
105 lldb::offset_t offset
;
106 success
= subclass_options
->GetValueForKeyAsInteger(
107 GetKey(OptionNames::Offset
), offset
);
109 error
.SetErrorString("Resolver data missing offset options key.");
113 BreakpointResolver
*resolver
= nullptr;
114 switch (resolver_type
) {
115 case FileLineResolver
:
116 resolver
= BreakpointResolverFileLine::CreateFromStructuredData(
117 nullptr, *subclass_options
, error
);
119 case AddressResolver
:
120 resolver
= BreakpointResolverAddress::CreateFromStructuredData(
121 nullptr, *subclass_options
, error
);
124 resolver
= BreakpointResolverName::CreateFromStructuredData(
125 nullptr, *subclass_options
, error
);
127 case FileRegexResolver
:
128 resolver
= BreakpointResolverFileRegex::CreateFromStructuredData(
129 nullptr, *subclass_options
, error
);
132 resolver
= BreakpointResolverScripted::CreateFromStructuredData(
133 nullptr, *subclass_options
, error
);
135 case ExceptionResolver
:
136 error
.SetErrorString("Exception resolvers are hard.");
139 llvm_unreachable("Should never get an unresolvable resolver type.");
142 if (!resolver
|| error
.Fail())
145 // Add on the global offset option:
146 resolver
->SetOffset(offset
);
147 return BreakpointResolverSP(resolver
);
150 StructuredData::DictionarySP
BreakpointResolver::WrapOptionsDict(
151 StructuredData::DictionarySP options_dict_sp
) {
152 if (!options_dict_sp
|| !options_dict_sp
->IsValid())
153 return StructuredData::DictionarySP();
155 StructuredData::DictionarySP
type_dict_sp(new StructuredData::Dictionary());
156 type_dict_sp
->AddStringItem(GetSerializationSubclassKey(), GetResolverName());
157 type_dict_sp
->AddItem(GetSerializationSubclassOptionsKey(), options_dict_sp
);
159 // Add the m_offset to the dictionary:
160 options_dict_sp
->AddIntegerItem(GetKey(OptionNames::Offset
), m_offset
);
165 void BreakpointResolver::SetBreakpoint(const BreakpointSP
&bkpt
) {
168 NotifyBreakpointSet();
171 void BreakpointResolver::ResolveBreakpointInModules(SearchFilter
&filter
,
172 ModuleList
&modules
) {
173 filter
.SearchInModuleList(*this, modules
);
176 void BreakpointResolver::ResolveBreakpoint(SearchFilter
&filter
) {
177 filter
.Search(*this);
182 uint32_t line
= UINT32_MAX
;
184 SourceLoc(uint32_t l
, std::optional
<uint16_t> c
)
185 : line(l
), column(c
? *c
: LLDB_INVALID_COLUMN_NUMBER
) {}
186 SourceLoc(const SymbolContext
&sc
)
187 : line(sc
.line_entry
.line
),
188 column(sc
.line_entry
.column
? sc
.line_entry
.column
189 : LLDB_INVALID_COLUMN_NUMBER
) {}
192 bool operator<(const SourceLoc lhs
, const SourceLoc rhs
) {
193 if (lhs
.line
< rhs
.line
)
195 if (lhs
.line
> rhs
.line
)
197 // uint32_t a_col = lhs.column ? lhs.column : LLDB_INVALID_COLUMN_NUMBER;
198 // uint32_t b_col = rhs.column ? rhs.column : LLDB_INVALID_COLUMN_NUMBER;
199 return lhs
.column
< rhs
.column
;
203 void BreakpointResolver::SetSCMatchesByLine(
204 SearchFilter
&filter
, SymbolContextList
&sc_list
, bool skip_prologue
,
205 llvm::StringRef log_ident
, uint32_t line
, std::optional
<uint16_t> column
) {
206 llvm::SmallVector
<SymbolContext
, 16> all_scs
;
207 for (uint32_t i
= 0; i
< sc_list
.GetSize(); ++i
)
208 all_scs
.push_back(sc_list
[i
]);
210 while (all_scs
.size()) {
211 uint32_t closest_line
= UINT32_MAX
;
213 // Move all the elements with a matching file spec to the end.
214 auto &match
= all_scs
[0];
215 auto worklist_begin
= std::partition(
216 all_scs
.begin(), all_scs
.end(), [&](const SymbolContext
&sc
) {
217 if (sc
.line_entry
.file
== match
.line_entry
.file
||
218 sc
.line_entry
.original_file
== match
.line_entry
.original_file
) {
219 // When a match is found, keep track of the smallest line number.
220 closest_line
= std::min(closest_line
, sc
.line_entry
.line
);
226 // (worklist_begin, worklist_end) now contains all entries for one filespec.
227 auto worklist_end
= all_scs
.end();
230 // If a column was requested, do a more precise match and only
231 // return the first location that comes before or at the
232 // requested location.
233 SourceLoc
requested(line
, *column
);
234 // First, filter out all entries left of the requested column.
235 worklist_end
= std::remove_if(
236 worklist_begin
, worklist_end
,
237 [&](const SymbolContext
&sc
) { return requested
< SourceLoc(sc
); });
238 // Sort the remaining entries by (line, column).
239 llvm::sort(worklist_begin
, worklist_end
,
240 [](const SymbolContext
&a
, const SymbolContext
&b
) {
241 return SourceLoc(a
) < SourceLoc(b
);
244 // Filter out all locations with a source location after the closest match.
245 if (worklist_begin
!= worklist_end
)
246 worklist_end
= std::remove_if(
247 worklist_begin
, worklist_end
, [&](const SymbolContext
&sc
) {
248 return SourceLoc(*worklist_begin
) < SourceLoc(sc
);
251 // Remove all entries with a larger line number.
252 // ResolveSymbolContext will always return a number that is >=
253 // the line number you pass in. So the smaller line number is
255 worklist_end
= std::remove_if(worklist_begin
, worklist_end
,
256 [&](const SymbolContext
&sc
) {
257 return closest_line
!= sc
.line_entry
.line
;
261 // Sort by file address.
262 llvm::sort(worklist_begin
, worklist_end
,
263 [](const SymbolContext
&a
, const SymbolContext
&b
) {
264 return a
.line_entry
.range
.GetBaseAddress().GetFileAddress() <
265 b
.line_entry
.range
.GetBaseAddress().GetFileAddress();
268 // Go through and see if there are line table entries that are
269 // contiguous, and if so keep only the first of the contiguous range.
270 // We do this by picking the first location in each lexical block.
271 llvm::SmallDenseSet
<Block
*, 8> blocks_with_breakpoints
;
272 for (auto first
= worklist_begin
; first
!= worklist_end
; ++first
) {
273 assert(!blocks_with_breakpoints
.count(first
->block
));
274 blocks_with_breakpoints
.insert(first
->block
);
276 std::remove_if(std::next(first
), worklist_end
,
277 [&](const SymbolContext
&sc
) {
278 return blocks_with_breakpoints
.count(sc
.block
);
282 // Make breakpoints out of the closest line number match.
283 for (auto &sc
: llvm::make_range(worklist_begin
, worklist_end
))
284 AddLocation(filter
, sc
, skip_prologue
, log_ident
);
286 // Remove all contexts processed by this iteration.
287 all_scs
.erase(worklist_begin
, all_scs
.end());
291 void BreakpointResolver::AddLocation(SearchFilter
&filter
,
292 const SymbolContext
&sc
,
294 llvm::StringRef log_ident
) {
295 Log
*log
= GetLog(LLDBLog::Breakpoints
);
296 Address line_start
= sc
.line_entry
.range
.GetBaseAddress();
297 if (!line_start
.IsValid()) {
299 "error: Unable to set breakpoint %s at file address "
301 log_ident
.str().c_str(), line_start
.GetFileAddress());
305 if (!filter
.AddressPasses(line_start
)) {
307 "Breakpoint %s at file address 0x%" PRIx64
308 " didn't pass the filter.\n",
309 log_ident
.str().c_str(), line_start
.GetFileAddress());
312 // If the line number is before the prologue end, move it there...
313 bool skipped_prologue
= false;
314 if (skip_prologue
&& sc
.function
) {
315 Address
prologue_addr(sc
.function
->GetAddressRange().GetBaseAddress());
316 if (prologue_addr
.IsValid() && (line_start
== prologue_addr
)) {
317 const uint32_t prologue_byte_size
= sc
.function
->GetPrologueByteSize();
318 if (prologue_byte_size
) {
319 prologue_addr
.Slide(prologue_byte_size
);
321 if (filter
.AddressPasses(prologue_addr
)) {
322 skipped_prologue
= true;
323 line_start
= prologue_addr
;
329 BreakpointLocationSP
bp_loc_sp(AddLocation(line_start
));
330 if (log
&& bp_loc_sp
&& !GetBreakpoint()->IsInternal()) {
332 bp_loc_sp
->GetDescription(&s
, lldb::eDescriptionLevelVerbose
);
333 LLDB_LOGF(log
, "Added location (skipped prologue: %s): %s \n",
334 skipped_prologue
? "yes" : "no", s
.GetData());
338 BreakpointLocationSP
BreakpointResolver::AddLocation(Address loc_addr
,
339 bool *new_location
) {
340 loc_addr
.Slide(m_offset
);
341 return GetBreakpoint()->AddLocation(loc_addr
, new_location
);
344 void BreakpointResolver::SetOffset(lldb::addr_t offset
) {
345 // There may already be an offset, so we are actually adjusting location
346 // addresses by the difference.
347 // lldb::addr_t slide = offset - m_offset;
348 // FIXME: We should go fix up all the already set locations for the new