Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp
39587 views
1
//===-- BreakpointResolverFileLine.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 "lldb/Breakpoint/BreakpointResolverFileLine.h"
10
11
#include "lldb/Breakpoint/BreakpointLocation.h"
12
#include "lldb/Core/Module.h"
13
#include "lldb/Symbol/CompileUnit.h"
14
#include "lldb/Symbol/Function.h"
15
#include "lldb/Target/Target.h"
16
#include "lldb/Utility/LLDBLog.h"
17
#include "lldb/Utility/Log.h"
18
#include "lldb/Utility/StreamString.h"
19
#include <optional>
20
21
using namespace lldb;
22
using namespace lldb_private;
23
24
// BreakpointResolverFileLine:
25
BreakpointResolverFileLine::BreakpointResolverFileLine(
26
const BreakpointSP &bkpt, lldb::addr_t offset, bool skip_prologue,
27
const SourceLocationSpec &location_spec,
28
std::optional<llvm::StringRef> removed_prefix_opt)
29
: BreakpointResolver(bkpt, BreakpointResolver::FileLineResolver, offset),
30
m_location_spec(location_spec), m_skip_prologue(skip_prologue),
31
m_removed_prefix_opt(removed_prefix_opt) {}
32
33
BreakpointResolverSP BreakpointResolverFileLine::CreateFromStructuredData(
34
const StructuredData::Dictionary &options_dict, Status &error) {
35
llvm::StringRef filename;
36
uint32_t line;
37
uint16_t column;
38
bool check_inlines;
39
bool skip_prologue;
40
bool exact_match;
41
bool success;
42
43
lldb::addr_t offset = 0;
44
45
success = options_dict.GetValueForKeyAsString(GetKey(OptionNames::FileName),
46
filename);
47
if (!success) {
48
error.SetErrorString("BRFL::CFSD: Couldn't find filename entry.");
49
return nullptr;
50
}
51
52
success = options_dict.GetValueForKeyAsInteger(
53
GetKey(OptionNames::LineNumber), line);
54
if (!success) {
55
error.SetErrorString("BRFL::CFSD: Couldn't find line number entry.");
56
return nullptr;
57
}
58
59
success =
60
options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Column), column);
61
if (!success) {
62
// Backwards compatibility.
63
column = 0;
64
}
65
66
success = options_dict.GetValueForKeyAsBoolean(GetKey(OptionNames::Inlines),
67
check_inlines);
68
if (!success) {
69
error.SetErrorString("BRFL::CFSD: Couldn't find check inlines entry.");
70
return nullptr;
71
}
72
73
success = options_dict.GetValueForKeyAsBoolean(
74
GetKey(OptionNames::SkipPrologue), skip_prologue);
75
if (!success) {
76
error.SetErrorString("BRFL::CFSD: Couldn't find skip prologue entry.");
77
return nullptr;
78
}
79
80
success = options_dict.GetValueForKeyAsBoolean(
81
GetKey(OptionNames::ExactMatch), exact_match);
82
if (!success) {
83
error.SetErrorString("BRFL::CFSD: Couldn't find exact match entry.");
84
return nullptr;
85
}
86
87
SourceLocationSpec location_spec(FileSpec(filename), line, column,
88
check_inlines, exact_match);
89
if (!location_spec)
90
return nullptr;
91
92
return std::make_shared<BreakpointResolverFileLine>(
93
nullptr, offset, skip_prologue, location_spec);
94
}
95
96
StructuredData::ObjectSP
97
BreakpointResolverFileLine::SerializeToStructuredData() {
98
StructuredData::DictionarySP options_dict_sp(
99
new StructuredData::Dictionary());
100
101
options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue),
102
m_skip_prologue);
103
options_dict_sp->AddStringItem(GetKey(OptionNames::FileName),
104
m_location_spec.GetFileSpec().GetPath());
105
options_dict_sp->AddIntegerItem(GetKey(OptionNames::LineNumber),
106
m_location_spec.GetLine().value_or(0));
107
options_dict_sp->AddIntegerItem(
108
GetKey(OptionNames::Column),
109
m_location_spec.GetColumn().value_or(LLDB_INVALID_COLUMN_NUMBER));
110
options_dict_sp->AddBooleanItem(GetKey(OptionNames::Inlines),
111
m_location_spec.GetCheckInlines());
112
options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch),
113
m_location_spec.GetExactMatch());
114
115
return WrapOptionsDict(options_dict_sp);
116
}
117
118
// Filter the symbol context list to remove contexts where the line number was
119
// moved into a new function. We do this conservatively, so if e.g. we cannot
120
// resolve the function in the context (which can happen in case of line-table-
121
// only debug info), we leave the context as is. The trickiest part here is
122
// handling inlined functions -- in this case we need to make sure we look at
123
// the declaration line of the inlined function, NOT the function it was
124
// inlined into.
125
void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list) {
126
if (m_location_spec.GetExactMatch())
127
return; // Nothing to do. Contexts are precise.
128
129
Log *log = GetLog(LLDBLog::Breakpoints);
130
for(uint32_t i = 0; i < sc_list.GetSize(); ++i) {
131
SymbolContext sc;
132
sc_list.GetContextAtIndex(i, sc);
133
if (!sc.block)
134
continue;
135
136
FileSpec file;
137
uint32_t line;
138
const Block *inline_block = sc.block->GetContainingInlinedBlock();
139
if (inline_block) {
140
const Declaration &inline_declaration = inline_block->GetInlinedFunctionInfo()->GetDeclaration();
141
if (!inline_declaration.IsValid())
142
continue;
143
file = inline_declaration.GetFile();
144
line = inline_declaration.GetLine();
145
} else if (sc.function)
146
sc.function->GetStartLineSourceInfo(file, line);
147
else
148
continue;
149
150
if (file != sc.line_entry.GetFile()) {
151
LLDB_LOG(log, "unexpected symbol context file {0}",
152
sc.line_entry.GetFile());
153
continue;
154
}
155
156
// Compare the requested line number with the line of the function
157
// declaration. In case of a function declared as:
158
//
159
// int
160
// foo()
161
// {
162
// ...
163
//
164
// the compiler will set the declaration line to the "foo" line, which is
165
// the reason why we have -1 here. This can fail in case of two inline
166
// functions defined back-to-back:
167
//
168
// inline int foo1() { ... }
169
// inline int foo2() { ... }
170
//
171
// but that's the best we can do for now.
172
// One complication, if the line number returned from GetStartLineSourceInfo
173
// is 0, then we can't do this calculation. That can happen if
174
// GetStartLineSourceInfo gets an error, or if the first line number in
175
// the function really is 0 - which happens for some languages.
176
177
// But only do this calculation if the line number we found in the SC
178
// was different from the one requested in the source file. If we actually
179
// found an exact match it must be valid.
180
181
if (m_location_spec.GetLine() == sc.line_entry.line)
182
continue;
183
184
const int decl_line_is_too_late_fudge = 1;
185
if (line &&
186
m_location_spec.GetLine() < line - decl_line_is_too_late_fudge) {
187
LLDB_LOG(log, "removing symbol context at {0}:{1}", file, line);
188
sc_list.RemoveContextAtIndex(i);
189
--i;
190
}
191
}
192
}
193
194
void BreakpointResolverFileLine::DeduceSourceMapping(
195
const SymbolContextList &sc_list) {
196
Target &target = GetBreakpoint()->GetTarget();
197
if (!target.GetAutoSourceMapRelative())
198
return;
199
200
Log *log = GetLog(LLDBLog::Breakpoints);
201
// Check if "b" is a suffix of "a".
202
// And return std::nullopt if not or the new path
203
// of "a" after consuming "b" from the back.
204
auto check_suffix =
205
[](llvm::StringRef a, llvm::StringRef b,
206
bool case_sensitive) -> std::optional<llvm::StringRef> {
207
if (case_sensitive ? a.consume_back(b) : a.consume_back_insensitive(b)) {
208
// Note sc_file_dir and request_file_dir below are normalized
209
// and always contain the path separator '/'.
210
if (a.empty() || a.ends_with("/")) {
211
return a;
212
}
213
}
214
return std::nullopt;
215
};
216
217
FileSpec request_file = m_location_spec.GetFileSpec();
218
219
// Only auto deduce source map if breakpoint is full path.
220
// Note: an existing source map reverse mapping (m_removed_prefix_opt has
221
// value) may make request_file relative.
222
if (!m_removed_prefix_opt.has_value() && request_file.IsRelative())
223
return;
224
225
const bool case_sensitive = request_file.IsCaseSensitive();
226
for (const SymbolContext &sc : sc_list) {
227
FileSpec sc_file = sc.line_entry.GetFile();
228
229
if (FileSpec::Equal(sc_file, request_file, /*full*/ true))
230
continue;
231
232
llvm::StringRef sc_file_dir = sc_file.GetDirectory().GetStringRef();
233
llvm::StringRef request_file_dir =
234
request_file.GetDirectory().GetStringRef();
235
236
llvm::StringRef new_mapping_from;
237
llvm::SmallString<256> new_mapping_to;
238
239
// Adding back any potentially reverse mapping stripped prefix.
240
// for new_mapping_to.
241
if (m_removed_prefix_opt.has_value())
242
llvm::sys::path::append(new_mapping_to, *m_removed_prefix_opt);
243
244
std::optional<llvm::StringRef> new_mapping_from_opt =
245
check_suffix(sc_file_dir, request_file_dir, case_sensitive);
246
if (new_mapping_from_opt) {
247
new_mapping_from = *new_mapping_from_opt;
248
if (new_mapping_to.empty())
249
new_mapping_to = ".";
250
} else {
251
std::optional<llvm::StringRef> new_mapping_to_opt =
252
check_suffix(request_file_dir, sc_file_dir, case_sensitive);
253
if (new_mapping_to_opt) {
254
new_mapping_from = ".";
255
llvm::sys::path::append(new_mapping_to, *new_mapping_to_opt);
256
}
257
}
258
259
if (!new_mapping_from.empty() && !new_mapping_to.empty()) {
260
LLDB_LOG(log, "generating auto source map from {0} to {1}",
261
new_mapping_from, new_mapping_to);
262
if (target.GetSourcePathMap().AppendUnique(new_mapping_from,
263
new_mapping_to,
264
/*notify*/ true))
265
target.GetStatistics().IncreaseSourceMapDeduceCount();
266
}
267
}
268
}
269
270
Searcher::CallbackReturn BreakpointResolverFileLine::SearchCallback(
271
SearchFilter &filter, SymbolContext &context, Address *addr) {
272
SymbolContextList sc_list;
273
274
// There is a tricky bit here. You can have two compilation units that
275
// #include the same file, and in one of them the function at m_line_number
276
// is used (and so code and a line entry for it is generated) but in the
277
// other it isn't. If we considered the CU's independently, then in the
278
// second inclusion, we'd move the breakpoint to the next function that
279
// actually generated code in the header file. That would end up being
280
// confusing. So instead, we do the CU iterations by hand here, then scan
281
// through the complete list of matches, and figure out the closest line
282
// number match, and only set breakpoints on that match.
283
284
// Note also that if file_spec only had a file name and not a directory,
285
// there may be many different file spec's in the resultant list. The
286
// closest line match for one will not be right for some totally different
287
// file. So we go through the match list and pull out the sets that have the
288
// same file spec in their line_entry and treat each set separately.
289
290
const uint32_t line = m_location_spec.GetLine().value_or(0);
291
const std::optional<uint16_t> column = m_location_spec.GetColumn();
292
293
const size_t num_comp_units = context.module_sp->GetNumCompileUnits();
294
for (size_t i = 0; i < num_comp_units; i++) {
295
CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i));
296
if (cu_sp) {
297
if (filter.CompUnitPasses(*cu_sp))
298
cu_sp->ResolveSymbolContext(m_location_spec, eSymbolContextEverything,
299
sc_list);
300
}
301
}
302
303
FilterContexts(sc_list);
304
305
DeduceSourceMapping(sc_list);
306
307
StreamString s;
308
s.Printf("for %s:%d ",
309
m_location_spec.GetFileSpec().GetFilename().AsCString("<Unknown>"),
310
line);
311
312
SetSCMatchesByLine(filter, sc_list, m_skip_prologue, s.GetString(), line,
313
column);
314
315
return Searcher::eCallbackReturnContinue;
316
}
317
318
lldb::SearchDepth BreakpointResolverFileLine::GetDepth() {
319
return lldb::eSearchDepthModule;
320
}
321
322
void BreakpointResolverFileLine::GetDescription(Stream *s) {
323
s->Printf("file = '%s', line = %u, ",
324
m_location_spec.GetFileSpec().GetPath().c_str(),
325
m_location_spec.GetLine().value_or(0));
326
auto column = m_location_spec.GetColumn();
327
if (column)
328
s->Printf("column = %u, ", *column);
329
s->Printf("exact_match = %d", m_location_spec.GetExactMatch());
330
}
331
332
void BreakpointResolverFileLine::Dump(Stream *s) const {}
333
334
lldb::BreakpointResolverSP
335
BreakpointResolverFileLine::CopyForBreakpoint(BreakpointSP &breakpoint) {
336
lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine(
337
breakpoint, GetOffset(), m_skip_prologue, m_location_spec));
338
339
return ret_sp;
340
}
341
342