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/Language.h"
27 #include "lldb/Target/Target.h"
28 #include "lldb/Utility/LLDBLog.h"
29 #include "lldb/Utility/Log.h"
30 #include "lldb/Utility/Stream.h"
31 #include "lldb/Utility/StreamString.h"
34 using namespace lldb_private
;
37 // BreakpointResolver:
38 const char *BreakpointResolver::g_ty_to_name
[] = {"FileAndLine", "Address",
39 "SymbolName", "SourceRegex",
40 "Python", "Exception",
43 const char *BreakpointResolver::g_option_names
[static_cast<uint32_t>(
44 BreakpointResolver::OptionNames::LastOptionName
)] = {
45 "AddressOffset", "Exact", "FileName", "Inlines", "Language",
46 "LineNumber", "Column", "ModuleName", "NameMask", "Offset",
47 "PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth",
48 "SkipPrologue", "SymbolNames"};
50 const char *BreakpointResolver::ResolverTyToName(enum ResolverTy type
) {
51 if (type
> LastKnownResolverType
)
52 return g_ty_to_name
[UnknownResolver
];
54 return g_ty_to_name
[type
];
57 BreakpointResolver::ResolverTy
58 BreakpointResolver::NameToResolverTy(llvm::StringRef name
) {
59 for (size_t i
= 0; i
< LastKnownResolverType
; i
++) {
60 if (name
== g_ty_to_name
[i
])
63 return UnknownResolver
;
66 BreakpointResolver::BreakpointResolver(const BreakpointSP
&bkpt
,
67 const unsigned char resolverTy
,
69 : m_breakpoint(bkpt
), m_offset(offset
), SubclassID(resolverTy
) {}
71 BreakpointResolver::~BreakpointResolver() = default;
73 BreakpointResolverSP
BreakpointResolver::CreateFromStructuredData(
74 const StructuredData::Dictionary
&resolver_dict
, Status
&error
) {
75 BreakpointResolverSP result_sp
;
76 if (!resolver_dict
.IsValid()) {
77 error
= Status::FromErrorString(
78 "Can't deserialize from an invalid data object.");
82 llvm::StringRef subclass_name
;
84 bool success
= resolver_dict
.GetValueForKeyAsString(
85 GetSerializationSubclassKey(), subclass_name
);
89 Status::FromErrorString("Resolver data missing subclass resolver key");
93 ResolverTy resolver_type
= NameToResolverTy(subclass_name
);
94 if (resolver_type
== UnknownResolver
) {
95 error
= Status::FromErrorStringWithFormatv("Unknown resolver type: {0}.",
100 StructuredData::Dictionary
*subclass_options
= nullptr;
101 success
= resolver_dict
.GetValueForKeyAsDictionary(
102 GetSerializationSubclassOptionsKey(), subclass_options
);
103 if (!success
|| !subclass_options
|| !subclass_options
->IsValid()) {
105 Status::FromErrorString("Resolver data missing subclass options key.");
109 lldb::offset_t offset
;
110 success
= subclass_options
->GetValueForKeyAsInteger(
111 GetKey(OptionNames::Offset
), offset
);
114 Status::FromErrorString("Resolver data missing offset options key.");
118 switch (resolver_type
) {
119 case FileLineResolver
:
120 result_sp
= BreakpointResolverFileLine::CreateFromStructuredData(
121 *subclass_options
, error
);
123 case AddressResolver
:
124 result_sp
= BreakpointResolverAddress::CreateFromStructuredData(
125 *subclass_options
, error
);
128 result_sp
= BreakpointResolverName::CreateFromStructuredData(
129 *subclass_options
, error
);
131 case FileRegexResolver
:
132 result_sp
= BreakpointResolverFileRegex::CreateFromStructuredData(
133 *subclass_options
, error
);
136 result_sp
= BreakpointResolverScripted::CreateFromStructuredData(
137 *subclass_options
, error
);
139 case ExceptionResolver
:
140 error
= Status::FromErrorString("Exception resolvers are hard.");
143 llvm_unreachable("Should never get an unresolvable resolver type.");
146 if (error
.Fail() || !result_sp
)
149 // Add on the global offset option:
150 result_sp
->SetOffset(offset
);
154 StructuredData::DictionarySP
BreakpointResolver::WrapOptionsDict(
155 StructuredData::DictionarySP options_dict_sp
) {
156 if (!options_dict_sp
|| !options_dict_sp
->IsValid())
157 return StructuredData::DictionarySP();
159 StructuredData::DictionarySP
type_dict_sp(new StructuredData::Dictionary());
160 type_dict_sp
->AddStringItem(GetSerializationSubclassKey(), GetResolverName());
161 type_dict_sp
->AddItem(GetSerializationSubclassOptionsKey(), options_dict_sp
);
163 // Add the m_offset to the dictionary:
164 options_dict_sp
->AddIntegerItem(GetKey(OptionNames::Offset
), m_offset
);
169 void BreakpointResolver::SetBreakpoint(const BreakpointSP
&bkpt
) {
172 NotifyBreakpointSet();
175 void BreakpointResolver::ResolveBreakpointInModules(SearchFilter
&filter
,
176 ModuleList
&modules
) {
177 filter
.SearchInModuleList(*this, modules
);
180 void BreakpointResolver::ResolveBreakpoint(SearchFilter
&filter
) {
181 filter
.Search(*this);
186 uint32_t line
= UINT32_MAX
;
188 SourceLoc(uint32_t l
, std::optional
<uint16_t> c
)
189 : line(l
), column(c
? *c
: LLDB_INVALID_COLUMN_NUMBER
) {}
190 SourceLoc(const SymbolContext
&sc
)
191 : line(sc
.line_entry
.line
),
192 column(sc
.line_entry
.column
? sc
.line_entry
.column
193 : LLDB_INVALID_COLUMN_NUMBER
) {}
196 bool operator<(const SourceLoc lhs
, const SourceLoc rhs
) {
197 if (lhs
.line
< rhs
.line
)
199 if (lhs
.line
> rhs
.line
)
201 // uint32_t a_col = lhs.column ? lhs.column : LLDB_INVALID_COLUMN_NUMBER;
202 // uint32_t b_col = rhs.column ? rhs.column : LLDB_INVALID_COLUMN_NUMBER;
203 return lhs
.column
< rhs
.column
;
207 void BreakpointResolver::SetSCMatchesByLine(
208 SearchFilter
&filter
, SymbolContextList
&sc_list
, bool skip_prologue
,
209 llvm::StringRef log_ident
, uint32_t line
, std::optional
<uint16_t> column
) {
210 llvm::SmallVector
<SymbolContext
, 16> all_scs
;
212 for (const auto &sc
: sc_list
) {
213 if (Language::GetGlobalLanguageProperties()
214 .GetEnableFilterForLineBreakpoints())
215 if (Language
*lang
= Language::FindPlugin(sc
.GetLanguage());
216 lang
&& lang
->IgnoreForLineBreakpoints(sc
))
218 all_scs
.push_back(sc
);
221 while (all_scs
.size()) {
222 uint32_t closest_line
= UINT32_MAX
;
224 // Move all the elements with a matching file spec to the end.
225 auto &match
= all_scs
[0];
226 auto worklist_begin
= std::partition(
227 all_scs
.begin(), all_scs
.end(), [&](const SymbolContext
&sc
) {
228 if (sc
.line_entry
.GetFile() == match
.line_entry
.GetFile() ||
229 sc
.line_entry
.original_file_sp
->Equal(
230 *match
.line_entry
.original_file_sp
,
231 SupportFile::eEqualFileSpecAndChecksumIfSet
)) {
232 // When a match is found, keep track of the smallest line number.
233 closest_line
= std::min(closest_line
, sc
.line_entry
.line
);
239 // (worklist_begin, worklist_end) now contains all entries for one filespec.
240 auto worklist_end
= all_scs
.end();
243 // If a column was requested, do a more precise match and only
244 // return the first location that comes before or at the
245 // requested location.
246 SourceLoc
requested(line
, *column
);
247 // First, filter out all entries left of the requested column.
248 worklist_end
= std::remove_if(
249 worklist_begin
, worklist_end
,
250 [&](const SymbolContext
&sc
) { return requested
< SourceLoc(sc
); });
251 // Sort the remaining entries by (line, column).
252 llvm::sort(worklist_begin
, worklist_end
,
253 [](const SymbolContext
&a
, const SymbolContext
&b
) {
254 return SourceLoc(a
) < SourceLoc(b
);
257 // Filter out all locations with a source location after the closest match.
258 if (worklist_begin
!= worklist_end
)
259 worklist_end
= std::remove_if(
260 worklist_begin
, worklist_end
, [&](const SymbolContext
&sc
) {
261 return SourceLoc(*worklist_begin
) < SourceLoc(sc
);
264 // Remove all entries with a larger line number.
265 // ResolveSymbolContext will always return a number that is >=
266 // the line number you pass in. So the smaller line number is
268 worklist_end
= std::remove_if(worklist_begin
, worklist_end
,
269 [&](const SymbolContext
&sc
) {
270 return closest_line
!= sc
.line_entry
.line
;
274 // Sort by file address.
275 llvm::sort(worklist_begin
, worklist_end
,
276 [](const SymbolContext
&a
, const SymbolContext
&b
) {
277 return a
.line_entry
.range
.GetBaseAddress().GetFileAddress() <
278 b
.line_entry
.range
.GetBaseAddress().GetFileAddress();
281 // Go through and see if there are line table entries that are
282 // contiguous, and if so keep only the first of the contiguous range.
283 // We do this by picking the first location in each lexical block.
284 llvm::SmallDenseSet
<Block
*, 8> blocks_with_breakpoints
;
285 for (auto first
= worklist_begin
; first
!= worklist_end
; ++first
) {
286 assert(!blocks_with_breakpoints
.count(first
->block
));
287 blocks_with_breakpoints
.insert(first
->block
);
289 std::remove_if(std::next(first
), worklist_end
,
290 [&](const SymbolContext
&sc
) {
291 return blocks_with_breakpoints
.count(sc
.block
);
295 // Make breakpoints out of the closest line number match.
296 for (auto &sc
: llvm::make_range(worklist_begin
, worklist_end
))
297 AddLocation(filter
, sc
, skip_prologue
, log_ident
);
299 // Remove all contexts processed by this iteration.
300 all_scs
.erase(worklist_begin
, all_scs
.end());
304 void BreakpointResolver::AddLocation(SearchFilter
&filter
,
305 const SymbolContext
&sc
,
307 llvm::StringRef log_ident
) {
308 Log
*log
= GetLog(LLDBLog::Breakpoints
);
309 Address line_start
= sc
.line_entry
.range
.GetBaseAddress();
310 if (!line_start
.IsValid()) {
312 "error: Unable to set breakpoint %s at file address "
314 log_ident
.str().c_str(), line_start
.GetFileAddress());
318 if (!filter
.AddressPasses(line_start
)) {
320 "Breakpoint %s at file address 0x%" PRIx64
321 " didn't pass the filter.\n",
322 log_ident
.str().c_str(), line_start
.GetFileAddress());
325 // If the line number is before the prologue end, move it there...
326 bool skipped_prologue
= false;
327 if (skip_prologue
&& sc
.function
) {
328 Address
prologue_addr(sc
.function
->GetAddressRange().GetBaseAddress());
329 if (prologue_addr
.IsValid() && (line_start
== prologue_addr
)) {
330 const uint32_t prologue_byte_size
= sc
.function
->GetPrologueByteSize();
331 if (prologue_byte_size
) {
332 prologue_addr
.Slide(prologue_byte_size
);
334 if (filter
.AddressPasses(prologue_addr
)) {
335 skipped_prologue
= true;
336 line_start
= prologue_addr
;
342 BreakpointLocationSP
bp_loc_sp(AddLocation(line_start
));
343 // If the address that we resolved the location to returns a different
344 // LineEntry from the one in the incoming SC, we're probably dealing with an
345 // inlined call site, so set that as the preferred LineEntry:
346 LineEntry resolved_entry
;
347 if (!skipped_prologue
&& bp_loc_sp
&&
348 line_start
.CalculateSymbolContextLineEntry(resolved_entry
) &&
349 LineEntry::Compare(resolved_entry
, sc
.line_entry
)) {
350 // FIXME: The function name will also be wrong here. Do we need to record
351 // that as well, or can we figure that out again when we report this
352 // breakpoint location.
353 if (!bp_loc_sp
->SetPreferredLineEntry(sc
.line_entry
)) {
354 LLDB_LOG(log
, "Tried to add a preferred line entry that didn't have the "
355 "same address as this location's address.");
358 if (log
&& bp_loc_sp
&& !GetBreakpoint()->IsInternal()) {
360 bp_loc_sp
->GetDescription(&s
, lldb::eDescriptionLevelVerbose
);
361 LLDB_LOGF(log
, "Added location (skipped prologue: %s): %s \n",
362 skipped_prologue
? "yes" : "no", s
.GetData());
366 BreakpointLocationSP
BreakpointResolver::AddLocation(Address loc_addr
,
367 bool *new_location
) {
368 loc_addr
.Slide(m_offset
);
369 return GetBreakpoint()->AddLocation(loc_addr
, new_location
);
372 void BreakpointResolver::SetOffset(lldb::addr_t offset
) {
373 // There may already be an offset, so we are actually adjusting location
374 // addresses by the difference.
375 // lldb::addr_t slide = offset - m_offset;
376 // FIXME: We should go fix up all the already set locations for the new