LLDB mainline
BreakpointResolver.cpp
Go to the documentation of this file.
1//===-- BreakpointResolver.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
10
13// Have to include the other breakpoint resolver types here so the static
14// create from StructuredData can call them.
20#include "lldb/Core/Address.h"
27#include "lldb/Target/Target.h"
29#include "lldb/Utility/Log.h"
30#include "lldb/Utility/Stream.h"
32#include <optional>
33
34using namespace lldb_private;
35using namespace lldb;
36
37// BreakpointResolver:
38const char *BreakpointResolver::g_ty_to_name[] = {"FileAndLine", "Address",
39 "SymbolName", "SourceRegex",
40 "Python", "Exception",
41 "Unknown"};
42
43const char *BreakpointResolver::g_option_names[static_cast<uint32_t>(
45 "AddressOffset", "Exact", "FileName", "Inlines", "Language",
46 "LineNumber", "Column", "ModuleName", "NameMask", "Offset",
47 "PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth",
48 "SkipPrologue", "SymbolNames"};
49
51 if (type > LastKnownResolverType)
53
54 return g_ty_to_name[type];
55}
56
59 for (size_t i = 0; i < LastKnownResolverType; i++) {
60 if (name == g_ty_to_name[i])
61 return (ResolverTy)i;
62 }
63 return UnknownResolver;
64}
65
67 if (mask == eResolverUnknown)
68 return false;
69
70 return (mask & MaskForResolverTy()) != 0;
71}
72
74 ResolverTy thisID = GetResolverTy();
75
76 switch (thisID) {
78 return eResolverFileAndLine;
79 case AddressResolver:
80 return eResolverAddress;
81 case NameResolver:
82 return eResolverName;
84 return eResolverFileRegex;
85 case PythonResolver:
86 return eResolverPython;
88 return eResolverException;
89 default:
90 return eResolverUnknown;
91 }
92}
93
94std::string BreakpointResolver::DescribeMask(uint64_t mask) {
95 std::string result;
96 if (mask & eResolverFileAndLine)
97 result.push_back('F');
98 if (mask & eResolverAddress)
99 result.push_back('A');
100 if (mask & eResolverName)
101 result.push_back('N');
102 if (mask & eResolverFileRegex)
103 result.push_back('S');
104 if (mask & eResolverPython)
105 result.push_back('P');
106 if (mask & eResolverException)
107 result.push_back('E');
108 return result;
109}
110
112 if (mask == 0)
113 return false;
114 return (mask & BreakpointResolverAllResolversMask) != 0;
115}
116
118 const unsigned char resolverTy,
119 lldb::addr_t offset,
120 bool offset_is_insn_count)
121 : m_breakpoint(bkpt), m_offset(offset),
122 m_offset_is_insn_count(offset_is_insn_count), SubclassID(resolverTy) {}
123
125
127 const StructuredData::Dictionary &resolver_dict, Status &error) {
128 BreakpointResolverSP result_sp;
129 if (!resolver_dict.IsValid()) {
131 "Can't deserialize from an invalid data object.");
132 return result_sp;
133 }
134
135 llvm::StringRef subclass_name;
136
137 bool success = resolver_dict.GetValueForKeyAsString(
138 GetSerializationSubclassKey(), subclass_name);
139
140 if (!success) {
141 error =
142 Status::FromErrorString("Resolver data missing subclass resolver key");
143 return result_sp;
144 }
145
146 ResolverTy resolver_type = NameToResolverTy(subclass_name);
147 if (resolver_type == UnknownResolver) {
148 error = Status::FromErrorStringWithFormatv("Unknown resolver type: {0}.",
149 subclass_name);
150 return result_sp;
151 }
152
153 StructuredData::Dictionary *subclass_options = nullptr;
154 success = resolver_dict.GetValueForKeyAsDictionary(
155 GetSerializationSubclassOptionsKey(), subclass_options);
156 if (!success || !subclass_options || !subclass_options->IsValid()) {
157 error =
158 Status::FromErrorString("Resolver data missing subclass options key.");
159 return result_sp;
160 }
161
162 lldb::offset_t offset;
163 success = subclass_options->GetValueForKeyAsInteger(
164 GetKey(OptionNames::Offset), offset);
165 if (!success) {
166 error =
167 Status::FromErrorString("Resolver data missing offset options key.");
168 return result_sp;
169 }
170
171 switch (resolver_type) {
172 case FileLineResolver:
174 *subclass_options, error);
175 break;
176 case AddressResolver:
178 *subclass_options, error);
179 break;
180 case NameResolver:
182 *subclass_options, error);
183 break;
186 *subclass_options, error);
187 break;
188 case PythonResolver:
190 *subclass_options, error);
191 break;
193 error = Status::FromErrorString("Exception resolvers are hard.");
194 break;
195 default:
196 llvm_unreachable("Should never get an unresolvable resolver type.");
197 }
198
199 if (error.Fail() || !result_sp)
200 return {};
201
202 // Add on the global offset option:
203 result_sp->SetOffset(offset);
204 return result_sp;
205}
206
208 StructuredData::DictionarySP options_dict_sp) {
209 if (!options_dict_sp || !options_dict_sp->IsValid())
211
213 type_dict_sp->AddStringItem(GetSerializationSubclassKey(), GetResolverName());
214 type_dict_sp->AddItem(GetSerializationSubclassOptionsKey(), options_dict_sp);
215
216 // Add the m_offset to the dictionary:
217 options_dict_sp->AddIntegerItem(GetKey(OptionNames::Offset), m_offset);
218
219 return type_dict_sp;
220}
221
223 assert(bkpt);
224 m_breakpoint = bkpt;
226}
227
229 ModuleList &modules) {
230 filter.SearchInModuleList(*this, modules);
231}
232
234 filter.Search(*this);
235}
236
237namespace {
238struct SourceLoc {
239 uint32_t line = UINT32_MAX;
240 uint16_t column;
241 SourceLoc(uint32_t l, std::optional<uint16_t> c)
242 : line(l), column(c ? *c : LLDB_INVALID_COLUMN_NUMBER) {}
243 SourceLoc(const SymbolContext &sc)
244 : line(sc.line_entry.line),
245 column(sc.line_entry.column ? sc.line_entry.column
247};
248
249bool operator<(const SourceLoc lhs, const SourceLoc rhs) {
250 if (lhs.line < rhs.line)
251 return true;
252 if (lhs.line > rhs.line)
253 return false;
254 // uint32_t a_col = lhs.column ? lhs.column : LLDB_INVALID_COLUMN_NUMBER;
255 // uint32_t b_col = rhs.column ? rhs.column : LLDB_INVALID_COLUMN_NUMBER;
256 return lhs.column < rhs.column;
257}
258} // namespace
259
261 SearchFilter &filter, SymbolContextList &sc_list, bool skip_prologue,
262 llvm::StringRef log_ident, uint32_t line, std::optional<uint16_t> column) {
263 llvm::SmallVector<SymbolContext, 16> all_scs(sc_list.begin(), sc_list.end());
264
265 // Let the language plugin filter `sc_list`. Because all symbol contexts in
266 // sc_list are assumed to belong to the same File, Line and CU, the code below
267 // assumes they have the same language.
269 .GetEnableFilterForLineBreakpoints())
270 if (Language *lang = Language::FindPlugin(sc_list[0].GetLanguage()))
271 lang->FilterForLineBreakpoints(all_scs);
272
273 while (all_scs.size()) {
274 uint32_t closest_line = UINT32_MAX;
275
276 // Move all the elements with a matching file spec to the end.
277 auto &match = all_scs[0];
278 auto worklist_begin = std::partition(
279 all_scs.begin(), all_scs.end(), [&](const SymbolContext &sc) {
280 if (sc.line_entry.GetFile() == match.line_entry.GetFile() ||
281 sc.line_entry.original_file_sp->Equal(
282 *match.line_entry.original_file_sp,
283 SupportFile::eEqualFileSpecAndChecksumIfSet)) {
284 // When a match is found, keep track of the smallest line number.
285 closest_line = std::min(closest_line, sc.line_entry.line);
286 return false;
287 }
288 return true;
289 });
290
291 // (worklist_begin, worklist_end) now contains all entries for one filespec.
292 auto worklist_end = all_scs.end();
293
294 if (column) {
295 // If a column was requested, do a more precise match and only
296 // return the first location that comes before or at the
297 // requested location.
298 SourceLoc requested(line, *column);
299 // First, filter out all entries left of the requested column.
300 worklist_end = std::remove_if(
301 worklist_begin, worklist_end,
302 [&](const SymbolContext &sc) { return requested < SourceLoc(sc); });
303 // Sort the remaining entries by (line, column).
304 llvm::sort(worklist_begin, worklist_end,
305 [](const SymbolContext &a, const SymbolContext &b) {
306 return SourceLoc(a) < SourceLoc(b);
307 });
308
309 // Filter out all locations with a source location after the closest match.
310 if (worklist_begin != worklist_end)
311 worklist_end = std::remove_if(
312 worklist_begin, worklist_end, [&](const SymbolContext &sc) {
313 return SourceLoc(*worklist_begin) < SourceLoc(sc);
314 });
315 } else {
316 // Remove all entries with a larger line number.
317 // ResolveSymbolContext will always return a number that is >=
318 // the line number you pass in. So the smaller line number is
319 // always better.
320 worklist_end = std::remove_if(worklist_begin, worklist_end,
321 [&](const SymbolContext &sc) {
322 return closest_line != sc.line_entry.line;
323 });
324 }
325
326 // Sort by file address.
327 llvm::sort(worklist_begin, worklist_end,
328 [](const SymbolContext &a, const SymbolContext &b) {
331 });
332
333 // Go through and see if there are line table entries that are
334 // contiguous, and if so keep only the first of the contiguous range.
335 // We do this by picking the first location in each lexical block.
336 llvm::SmallDenseSet<Block *, 8> blocks_with_breakpoints;
337 for (auto first = worklist_begin; first != worklist_end; ++first) {
338 assert(!blocks_with_breakpoints.count(first->block));
339 blocks_with_breakpoints.insert(first->block);
340 worklist_end =
341 std::remove_if(std::next(first), worklist_end,
342 [&](const SymbolContext &sc) {
343 return blocks_with_breakpoints.count(sc.block);
344 });
345 }
346
347 // Make breakpoints out of the closest line number match.
348 for (auto &sc : llvm::make_range(worklist_begin, worklist_end))
349 AddLocation(filter, sc, skip_prologue, log_ident);
350
351 // Remove all contexts processed by this iteration.
352 all_scs.erase(worklist_begin, all_scs.end());
353 }
354}
355
357 const SymbolContext &sc,
358 bool skip_prologue,
359 llvm::StringRef log_ident) {
361 Address line_start = sc.line_entry.range.GetBaseAddress();
362 if (!line_start.IsValid()) {
363 LLDB_LOGF(log,
364 "error: Unable to set breakpoint %s at file address "
365 "0x%" PRIx64 "\n",
366 log_ident.str().c_str(), line_start.GetFileAddress());
367 return;
368 }
369
370 if (!filter.AddressPasses(line_start)) {
371 LLDB_LOGF(log,
372 "Breakpoint %s at file address 0x%" PRIx64
373 " didn't pass the filter.\n",
374 log_ident.str().c_str(), line_start.GetFileAddress());
375 }
376
377 // If the line number is before the prologue end, move it there...
378 bool skipped_prologue = false;
379 if (skip_prologue && sc.function) {
380 Address prologue_addr = sc.function->GetAddress();
381 if (prologue_addr.IsValid() && (line_start == prologue_addr)) {
382 const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize();
383 if (prologue_byte_size) {
384 prologue_addr.Slide(prologue_byte_size);
385
386 if (filter.AddressPasses(prologue_addr)) {
387 skipped_prologue = true;
388 line_start = prologue_addr;
389 }
390 }
391 }
392 }
393
394 BreakpointLocationSP bp_loc_sp(AddLocation(line_start));
395 // If the address that we resolved the location to returns a different
396 // LineEntry from the one in the incoming SC, we're probably dealing with an
397 // inlined call site, so set that as the preferred LineEntry:
398 LineEntry resolved_entry;
399 if (!skipped_prologue && bp_loc_sp &&
400 line_start.CalculateSymbolContextLineEntry(resolved_entry) &&
401 LineEntry::Compare(resolved_entry, sc.line_entry)) {
402 // FIXME: The function name will also be wrong here. Do we need to record
403 // that as well, or can we figure that out again when we report this
404 // breakpoint location.
405 if (!bp_loc_sp->SetPreferredLineEntry(sc.line_entry)) {
406 LLDB_LOG(log, "Tried to add a preferred line entry that didn't have the "
407 "same address as this location's address.");
408 }
409 }
410 if (log && bp_loc_sp && !GetBreakpoint()->IsInternal()) {
411 StreamString s;
412 bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
413 LLDB_LOGF(log, "Added location (skipped prologue: %s): %s \n",
414 skipped_prologue ? "yes" : "no", s.GetData());
415 }
416}
417
419 bool *new_location) {
421 Target &target = GetBreakpoint()->GetTarget();
422 llvm::Expected<DisassemblerSP> expected_instructions =
423 target.ReadInstructions(loc_addr, m_offset);
424 if (!expected_instructions) {
426 expected_instructions.takeError(),
427 "error: Unable to read instructions at address 0x{0:x}",
428 loc_addr.GetLoadAddress(&target));
429 return BreakpointLocationSP();
430 }
431
432 const DisassemblerSP instructions = *expected_instructions;
433 if (!instructions ||
434 instructions->GetInstructionList().GetSize() != m_offset) {
436 "error: Unable to read {0} instructions at address 0x{1:x}",
437 m_offset, loc_addr.GetLoadAddress(&target));
438 return BreakpointLocationSP();
439 }
440
441 loc_addr.Slide(instructions->GetInstructionList().GetTotalByteSize());
442 } else {
443 loc_addr.Slide(m_offset);
444 }
445
446 return GetBreakpoint()->AddLocation(loc_addr, new_location);
447}
448
450 // There may already be an offset, so we are actually adjusting location
451 // addresses by the difference.
452 // lldb::addr_t slide = offset - m_offset;
453 // FIXME: We should go fix up all the already set locations for the new
454 // slide.
455
456 m_offset = offset;
457}
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:376
#define LLDB_LOGF(log,...)
Definition Log.h:390
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:406
Address & GetBaseAddress()
Get accessor for the base address of the range.
A section + offset based address class.
Definition Address.h:62
lldb::addr_t GetLoadAddress(Target *target) const
Get the load address.
Definition Address.cpp:301
bool Slide(int64_t offset)
Definition Address.h:446
lldb::addr_t GetFileAddress() const
Get the file address.
Definition Address.cpp:281
bool IsValid() const
Check if the object state is valid.
Definition Address.h:355
bool CalculateSymbolContextLineEntry(LineEntry &line_entry) const
Definition Address.cpp:901
static lldb::BreakpointResolverSP CreateFromStructuredData(const StructuredData::Dictionary &options_dict, Status &error)
static lldb::BreakpointResolverSP CreateFromStructuredData(const StructuredData::Dictionary &data_dict, Status &error)
static lldb::BreakpointResolverSP CreateFromStructuredData(const StructuredData::Dictionary &options_dict, Status &error)
static lldb::BreakpointResolverSP CreateFromStructuredData(const StructuredData::Dictionary &data_dict, Status &error)
static lldb::BreakpointResolverSP CreateFromStructuredData(const StructuredData::Dictionary &options_dict, Status &error)
bool ResolverTyInMask(uint64_t mask)
This checks whether the resolver's type matches the enum lldb::BreakpointResolverType.
void SetBreakpoint(const lldb::BreakpointSP &bkpt)
This sets the breakpoint for this resolver.
~BreakpointResolver() override
The Destructor is virtual, all significant breakpoint resolvers derive from this class.
lldb::BreakpointLocationSP AddLocation(Address loc_addr, bool *new_location=nullptr)
static const char * GetKey(OptionNames enum_value)
static lldb::BreakpointResolverSP CreateFromStructuredData(const StructuredData::Dictionary &resolver_dict, Status &error)
This section handles serializing and deserializing from StructuredData objects.
static ResolverTy NameToResolverTy(llvm::StringRef name)
StructuredData::DictionarySP WrapOptionsDict(StructuredData::DictionarySP options_dict_sp)
ResolverTy
An enumeration for keeping track of the concrete subclass that is actually instantiated.
void SetSCMatchesByLine(SearchFilter &filter, SymbolContextList &sc_list, bool skip_prologue, llvm::StringRef log_ident, uint32_t line=0, std::optional< uint16_t > column=std::nullopt)
Takes a symbol context list of matches which supposedly represent the same file and line number in a ...
lldb::BreakpointSP GetBreakpoint() const
This gets the breakpoint for this resolver.
void SetOffset(lldb::addr_t offset)
This updates the offset for this breakpoint.
static const char * g_option_names[static_cast< uint32_t >(OptionNames::LastOptionName)]
static const char * ResolverTyToName(enum ResolverTy)
static bool TypeMaskIsValid(uint64_t mask)
static std::string DescribeMask(uint64_t mask)
static const char * g_ty_to_name[LastKnownResolverType+2]
static const char * GetSerializationSubclassKey()
virtual void ResolveBreakpointInModules(SearchFilter &filter, ModuleList &modules)
In response to this method the resolver scans the modules in the module list modules,...
static const char * GetSerializationSubclassOptionsKey()
virtual void ResolveBreakpoint(SearchFilter &filter)
In response to this method the resolver scans all the modules in the breakpoint's target,...
BreakpointResolver(const lldb::BreakpointSP &bkpt, unsigned char resolverType, lldb::addr_t offset=0, bool offset_is_insn_count=false)
The breakpoint resolver need to have a breakpoint for "ResolveBreakpoint to make sense.
const Address & GetAddress() const
Return the address of the function (its entry point).
Definition Function.h:429
uint32_t GetPrologueByteSize()
Get the size of the prologue instructions for this function.
Definition Function.cpp:570
static Language * FindPlugin(lldb::LanguageType language)
Definition Language.cpp:84
static LanguageProperties & GetGlobalLanguageProperties()
Definition Language.cpp:40
A collection class for Module objects.
Definition ModuleList.h:125
General Outline: Provides the callback and search depth for the SearchFilter search.
virtual void SearchInModuleList(Searcher &searcher, ModuleList &modules)
Call this method to do the search using the Searcher in the module list modules.
virtual bool AddressPasses(Address &addr)
Call this method with a Address to see if address passes the filter.
virtual void Search(Searcher &searcher)
Call this method to do the search using the Searcher.
An error handling class.
Definition Status.h:118
static Status FromErrorString(const char *str)
Definition Status.h:141
static Status static Status FromErrorStringWithFormatv(const char *format, Args &&...args)
Definition Status.h:151
const char * GetData() const
bool GetValueForKeyAsInteger(llvm::StringRef key, IntType &result) const
bool GetValueForKeyAsString(llvm::StringRef key, llvm::StringRef &result) const
bool GetValueForKeyAsDictionary(llvm::StringRef key, Dictionary *&result) const
std::shared_ptr< Dictionary > DictionarySP
Defines a list of symbol context objects.
const_iterator begin() const
const_iterator end() const
Defines a symbol context baton that can be handed other debug core functions.
Function * function
The Function for a given query.
Block * block
The Block for a given query.
LineEntry line_entry
The LineEntry for a given query.
llvm::Expected< lldb::DisassemblerSP > ReadInstructions(const Address &start_addr, uint32_t count, const char *flavor_string=nullptr)
Definition Target.cpp:3114
#define LLDB_INVALID_COLUMN_NUMBER
#define UINT32_MAX
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:339
bool operator<(const Address &lhs, const Address &rhs)
Definition Address.cpp:973
constexpr unsigned BreakpointResolverAllResolversMask
std::shared_ptr< lldb_private::BreakpointResolver > BreakpointResolverSP
std::shared_ptr< lldb_private::BreakpointLocation > BreakpointLocationSP
@ eDescriptionLevelVerbose
uint64_t offset_t
Definition lldb-types.h:85
std::shared_ptr< lldb_private::Breakpoint > BreakpointSP
std::shared_ptr< lldb_private::Disassembler > DisassemblerSP
uint64_t addr_t
Definition lldb-types.h:80
A line table entry class.
Definition LineEntry.h:21
static int Compare(const LineEntry &lhs, const LineEntry &rhs)
Compare two LineEntry objects.
AddressRange range
The section offset address range for this line entry.
Definition LineEntry.h:137
uint32_t line
The source line number, or LLDB_INVALID_LINE_NUMBER if there is no line number information.
Definition LineEntry.h:151