Skip to content

Commit ad2f16b

Browse files
ttreyerjordalgo
authored andcommitted
Add 'symbol_source' config
Let the user decide whether to use DebugInfo to determine uprobe location, or use the Symbol Table. The new symbol_source config takes the following two values: - dwarf: uses the DebugInfo, which yield more accurate stack traces. However, if the DebugInfo was rewritten by a post-linkage optimisation tool (like BOLT or AutoFDO), we might get wrong addresses for the probe location. - symbol_table: uses the target's Symbol Table, which won't suffer from improperly rewritten DebugInfo. This will close bpftrace#3493.
1 parent f0f6d60 commit ad2f16b

File tree

12 files changed

+175
-17
lines changed

12 files changed

+175
-17
lines changed

.github/include/aot_skip.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ aot.variable.variable nested tuple string resize
146146
aot.variable.map nested tuple string resize
147147
aot.variable.map key nested tuple string resize
148148
aot.variable.map key tuple with casted ints
149+
aot.dwarf.uprobe without dwarf
149150
aot.dwarf.uprobe inlined function - func
150151
aot.dwarf.uprobe inlined function - probe
151152
aot.dwarf.uprobe inlined function - ustack

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ and this project adheres to
4646
- [#3515](https://github.com/bpftrace/bpftrace/pull/3515)
4747
- Configuration option to suppress printing maps by default at program exit
4848
- [#3547](https://github.com/bpftrace/bpftrace/pull/3547)
49+
- Add `symbol_source` config to source uprobe locations from either DWARF or the Symbol Table
50+
- [#3504](https://github.com/bpftrace/bpftrace/pull/3504/)
4951
#### Changed
5052
- Merge output into `stdout` when `-lv`
5153
- [#3383](https://github.com/bpftrace/bpftrace/pull/3383)

man/adoc/bpftrace.adoc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3724,6 +3724,20 @@ Default: 1
37243724

37253725
Controls whether maps are printed on exit. Set to `0` in order to change the default behavior and not automatically print maps at program exit.
37263726

3727+
=== symbol_source
3728+
3729+
Default: `dwarf` if `bpftrace` is compiled with LLDB, `symbol_table` otherwise
3730+
3731+
Choose how bpftrace will resolve all `uprobe` symbol locations.
3732+
3733+
Available options:
3734+
3735+
- `dwarf` - locate uprobes using DebugInfo, which yields more accurate stack traces (`ustack`). Fall back to the Symbol Table if it can't locate the probe using DebugInfo.
3736+
- `symbol_table` - don't use DebugInfo and rely on the ELF Symbol Table instead.
3737+
3738+
If the DebugInfo was rewritten by a post-linkage optimisation tool (like BOLT or AutoFDO), it might yield an incorrect address for a probe location.
3739+
This config can force using the Symbol Table, for when the DebugInfo returns invalid addresses.
3740+
37273741
== Environment Variables
37283742

37293743
These are not available as part of the standard set of <<Config Variables>> and can only be set as environment variables.

src/ast/passes/config_analyser.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@ void ConfigAnalyser::set_user_symbol_cache_type_config(
9696
LOG(ERROR, assignment.expr->loc, err_);
9797
}
9898

99+
void ConfigAnalyser::set_symbol_source_config(
100+
AssignConfigVarStatement &assignment)
101+
{
102+
auto &assignTy = assignment.expr->type;
103+
if (!assignTy.IsStringTy()) {
104+
log_type_error(assignTy, Type::string, assignment);
105+
return;
106+
}
107+
108+
auto val = dynamic_cast<String *>(assignment.expr)->str;
109+
if (!config_setter_.set_symbol_source_config(val))
110+
LOG(ERROR, assignment.expr->loc, err_);
111+
}
112+
99113
void ConfigAnalyser::set_missing_probes_config(
100114
AssignConfigVarStatement &assignment)
101115
{
@@ -165,6 +179,9 @@ void ConfigAnalyser::visit(AssignConfigVarStatement &assignment)
165179
[&, this](ConfigKeyUserSymbolCacheType) {
166180
set_user_symbol_cache_type_config(assignment);
167181
},
182+
[&, this](ConfigKeySymbolSource) {
183+
set_symbol_source_config(assignment);
184+
},
168185
[&, this](ConfigKeyMissingProbes) {
169186
set_missing_probes_config(assignment);
170187
} },

src/ast/passes/config_analyser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class ConfigAnalyser : public Visitor {
4747
ConfigKeyString key);
4848
void set_stack_mode_config(AssignConfigVarStatement &assignment);
4949
void set_user_symbol_cache_type_config(AssignConfigVarStatement &assignment);
50+
void set_symbol_source_config(AssignConfigVarStatement &assignment);
5051
void set_missing_probes_config(AssignConfigVarStatement &assignment);
5152

5253
void log_type_error(SizedType &type,

src/attached_probe.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,11 @@ static constexpr std::string_view hint_unsafe =
341341
"\nUse --unsafe to force attachment. WARNING: This option could lead to "
342342
"data corruption in the target process.";
343343

344-
static void check_alignment(std::string &path,
344+
static constexpr std::string_view hint_symbol_source =
345+
"\nUse config 'symbol_source = \"symbol_table\"' in case of bad DebugInfo.";
346+
347+
static void check_alignment(std::string &orig_name,
348+
std::string &path,
345349
std::string &symbol,
346350
uint64_t sym_offset,
347351
uint64_t func_offset,
@@ -357,13 +361,20 @@ static void check_alignment(std::string &path,
357361
case AlignState::Ok:
358362
return;
359363
case AlignState::NotAlign:
360-
if (safe_mode)
361-
throw FatalUserException("Could not add " + probetypeName(type) +
362-
" into middle of instruction: " + tmp +
363-
std::string{ hint_unsafe });
364-
else
364+
if (safe_mode) {
365+
auto msg = "Could not add " + probetypeName(type) +
366+
" into middle of instruction: " + tmp +
367+
std::string{ hint_unsafe };
368+
if (orig_name.find('*') != std::string::npos)
369+
msg += hint_symbol_source;
370+
throw FatalUserException(std::move(msg));
371+
} else {
372+
std::string_view hint;
373+
if (orig_name.find('*') != std::string::npos)
374+
hint = hint_symbol_source;
365375
LOG(WARNING) << "Unsafe " << type
366-
<< " in the middle of the instruction: " << tmp;
376+
<< " in the middle of the instruction: " << tmp << hint;
377+
}
367378
break;
368379

369380
case AlignState::Fail:
@@ -473,8 +484,13 @@ bool AttachedProbe::resolve_offset_uprobe(bool safe_mode, bool has_multiple_aps)
473484
if (func_offset == 0)
474485
return true;
475486

476-
check_alignment(
477-
probe_.path, symbol, sym_offset, func_offset, safe_mode, probe_.type);
487+
check_alignment(probe_.orig_name,
488+
probe_.path,
489+
symbol,
490+
sym_offset,
491+
func_offset,
492+
safe_mode,
493+
probe_.type);
478494
return true;
479495
}
480496

src/bpftrace.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,15 +164,19 @@ int BPFtrace::add_probe(const ast::AttachPoint &ap,
164164
probetype(ap.provider) == ProbeType::kprobe) {
165165
bool locations_from_dwarf = false;
166166

167+
// Don't set the DWARF target when the user wants to use the symbol table.
167168
std::optional<std::string> target;
168-
if (probetype(ap.provider) == ProbeType::uprobe) {
169-
target = probe.path;
170-
} else {
171-
// Only use the DWARF information of the Kernel,
172-
// if the user wants to to probe inlined kprobes.
173-
// Otherwise, fall back to using the symbol table.
174-
if (config_.get(ConfigKeyBool::probe_inline))
175-
target = find_vmlinux();
169+
if (config_.get(ConfigKeySymbolSource::default_) ==
170+
ConfigSymbolSource::dwarf) {
171+
if (probetype(ap.provider) == ProbeType::uprobe) {
172+
target = probe.path;
173+
} else {
174+
// Only use the DWARF information of the Kernel,
175+
// if the user wants to to probe inlined kprobes.
176+
// Otherwise, fall back to using the symbol table.
177+
if (config_.get(ConfigKeyBool::probe_inline))
178+
target = find_vmlinux();
179+
}
176180
}
177181

178182
// If the user specified an address/offset, do not overwrite

src/config.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ Config::Config(bool has_cmd)
2727
{ ConfigKeyInt::perf_rb_pages, { .value = static_cast<uint64_t>(64) } },
2828
{ ConfigKeyStackMode::default_, { .value = StackMode::bpftrace } },
2929
{ ConfigKeyString::str_trunc_trailer, { .value = std::string("..") } },
30+
{ ConfigKeySymbolSource::default_,
31+
{ .value =
32+
#ifdef HAVE_LIBLLDB
33+
ConfigSymbolSource::dwarf
34+
#else
35+
ConfigSymbolSource::symbol_table
36+
#endif
37+
} },
3038
{ ConfigKeyMissingProbes::default_,
3139
{ .value = ConfigMissingProbes::warn } },
3240
// by default, cache user symbols per program if ASLR is disabled on system
@@ -148,6 +156,21 @@ bool ConfigSetter::set_user_symbol_cache_type(const std::string &s)
148156
return config_.set(ConfigKeyUserSymbolCacheType::default_, usct, source_);
149157
}
150158

159+
bool ConfigSetter::set_symbol_source_config(const std::string &s)
160+
{
161+
ConfigSymbolSource source;
162+
if (s == "dwarf") {
163+
source = ConfigSymbolSource::dwarf;
164+
} else if (s == "symbol_table") {
165+
source = ConfigSymbolSource::symbol_table;
166+
} else {
167+
LOG(ERROR) << "Invalid value for symbol_source: valid values are "
168+
"\"dwarf\" and \"symbol_table\".";
169+
return false;
170+
}
171+
return config_.set(ConfigKeySymbolSource::default_, source, source_);
172+
}
173+
151174
bool ConfigSetter::set_missing_probes_config(const std::string &s)
152175
{
153176
ConfigMissingProbes mp;

src/config.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ enum class ConfigKeyUserSymbolCacheType {
5353
default_,
5454
};
5555

56+
enum class ConfigSymbolSource {
57+
symbol_table,
58+
dwarf,
59+
};
60+
61+
enum class ConfigKeySymbolSource {
62+
default_,
63+
};
64+
5665
enum class ConfigMissingProbes {
5766
ignore,
5867
warn,
@@ -68,6 +77,7 @@ typedef std::variant<ConfigKeyBool,
6877
ConfigKeyString,
6978
ConfigKeyStackMode,
7079
ConfigKeyUserSymbolCacheType,
80+
ConfigKeySymbolSource,
7181
ConfigKeyMissingProbes>
7282
ConfigKey;
7383

@@ -89,6 +99,7 @@ const std::map<std::string, ConfigKey> CONFIG_KEY_MAP = {
8999
{ "probe_inline", ConfigKeyBool::probe_inline },
90100
{ "stack_mode", ConfigKeyStackMode::default_ },
91101
{ "str_trunc_trailer", ConfigKeyString::str_trunc_trailer },
102+
{ "symbol_source", ConfigKeySymbolSource::default_ },
92103
{ "missing_probes", ConfigKeyMissingProbes::default_ },
93104
{ "print_maps_on_exit", ConfigKeyBool::print_maps_on_exit },
94105
};
@@ -106,6 +117,7 @@ struct ConfigValue {
106117
std::string,
107118
StackMode,
108119
UserSymbolCacheType,
120+
ConfigSymbolSource,
109121
ConfigMissingProbes>
110122
value;
111123
};
@@ -139,6 +151,11 @@ class Config {
139151
return get<UserSymbolCacheType>(key);
140152
}
141153

154+
ConfigSymbolSource get(ConfigKeySymbolSource key) const
155+
{
156+
return get<ConfigSymbolSource>(key);
157+
}
158+
142159
ConfigMissingProbes get(ConfigKeyMissingProbes key) const
143160
{
144161
return get<ConfigMissingProbes>(key);
@@ -226,6 +243,7 @@ class ConfigSetter {
226243

227244
bool set_stack_mode(const std::string &s);
228245
bool set_user_symbol_cache_type(const std::string &s);
246+
bool set_symbol_source_config(const std::string &s);
229247
bool set_missing_probes_config(const std::string &s);
230248

231249
Config &config_;

tests/bpftrace.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,46 @@ TEST(bpftrace, add_probes_uprobe_no_demangling)
707707
"uprobe:/bin/sh:cpp_mangled");
708708
}
709709

710+
#ifdef HAVE_LIBLLDB
711+
#include "dwarf_common.h"
712+
713+
class bpftrace_dwarf : public test_dwarf {};
714+
715+
TEST_F(bpftrace_dwarf, add_probes_uprobe_symbol_source)
716+
{
717+
auto uprobe = "uprobe:" + std::string(bin_) + ":func_1 {}";
718+
719+
{
720+
BPFtrace bpftrace;
721+
ConfigSetter configs{ bpftrace.config_, ConfigSource::script };
722+
configs.set_symbol_source_config("dwarf");
723+
parse_probe(uprobe, bpftrace);
724+
725+
ASSERT_EQ(bpftrace.resources.probes.size(), 1);
726+
ASSERT_EQ(bpftrace.resources.special_probes.size(), 0);
727+
728+
auto &probe = bpftrace.resources.probes.at(0);
729+
ASSERT_TRUE(probe.attach_point.empty());
730+
ASSERT_NE(probe.address, 0);
731+
}
732+
733+
{
734+
BPFtrace bpftrace;
735+
ConfigSetter configs{ bpftrace.config_, ConfigSource::script };
736+
configs.set_symbol_source_config("symbol_table");
737+
parse_probe(uprobe, bpftrace);
738+
739+
ASSERT_EQ(bpftrace.resources.probes.size(), 1);
740+
ASSERT_EQ(bpftrace.resources.special_probes.size(), 0);
741+
742+
auto &probe = bpftrace.resources.probes.at(0);
743+
ASSERT_FALSE(probe.attach_point.empty());
744+
ASSERT_EQ(probe.address, 0);
745+
}
746+
}
747+
748+
#endif
749+
710750
TEST(bpftrace, add_probes_usdt)
711751
{
712752
StrictMock<MockBPFtrace> bpftrace;

0 commit comments

Comments
 (0)