Commit 35b88a83 authored by Adam Jensen's avatar Adam Jensen Committed by Brendan Gregg

Add uprobe/uretprobe wildcard matching (#281)

* Add BPFtrace::extract_func_symbols_from_path function

* Refactor to add find_wildcard_matches overload that takes an istream

* Prepare find_wildcard_matches usage for u(ret)probe types

* Add support for wildcard matches with uprobe and uretprobe types

* Update add_probes_uprobe_wildcard test

* Add test for uprobe wildcard match

* Mention uprobe wildcard in readme

* Add uprobe support to codegen

* Clean up whitespace

* Add TODO comment to remove objdump dependency
parent a0d1bc3b
...@@ -228,6 +228,8 @@ A single probe can be attached to multiple events: ...@@ -228,6 +228,8 @@ A single probe can be attached to multiple events:
### Wildcards ### Wildcards
Some probe types allow wildcards to be used when attaching a probe: Some probe types allow wildcards to be used when attaching a probe:
`uprobe:/bin/bash:read* { ... }`
`kprobe:vfs_* { ... }` `kprobe:vfs_* { ... }`
### Predicates ### Predicates
......
...@@ -1114,22 +1114,32 @@ void CodegenLLVM::visit(Probe &probe) ...@@ -1114,22 +1114,32 @@ void CodegenLLVM::visit(Probe &probe)
for (auto &attach_point : *probe.attach_points) { for (auto &attach_point : *probe.attach_points) {
current_attach_point_ = attach_point; current_attach_point_ = attach_point;
std::string file_name; std::set<std::string> matches;
switch (probetype(attach_point->provider)) switch (probetype(attach_point->provider))
{ {
case ProbeType::kprobe: case ProbeType::kprobe:
case ProbeType::kretprobe: case ProbeType::kretprobe:
file_name = "/sys/kernel/debug/tracing/available_filter_functions"; matches = bpftrace_.find_wildcard_matches(attach_point->target,
attach_point->func,
"/sys/kernel/debug/tracing/available_filter_functions");
break; break;
case ProbeType::uprobe:
case ProbeType::uretprobe:
{
auto symbol_stream = std::istringstream(bpftrace_.extract_func_symbols_from_path(attach_point->target));
matches = bpftrace_.find_wildcard_matches("", attach_point->func, symbol_stream);
break;
}
case ProbeType::tracepoint: case ProbeType::tracepoint:
file_name = "/sys/kernel/debug/tracing/available_events"; matches = bpftrace_.find_wildcard_matches(attach_point->target,
attach_point->func,
"/sys/kernel/debug/tracing/available_events");
break; break;
default: default:
std::cerr << "Wildcard matches aren't available on probe type '" std::cerr << "Wildcard matches aren't available on probe type '"
<< attach_point->provider << "'" << std::endl; << attach_point->provider << "'" << std::endl;
return; return;
} }
auto matches = bpftrace_.find_wildcard_matches(attach_point->target, attach_point->func, file_name);
for (auto &match : matches) { for (auto &match : matches) {
printf_id_ = starting_printf_id_; printf_id_ = starting_printf_id_;
time_id_ = starting_time_id_; time_id_ = starting_time_id_;
......
...@@ -82,24 +82,33 @@ int BPFtrace::add_probe(ast::Probe &p) ...@@ -82,24 +82,33 @@ int BPFtrace::add_probe(ast::Probe &p)
attach_point->func.find("[") != std::string::npos && attach_point->func.find("[") != std::string::npos &&
attach_point->func.find("]") != std::string::npos)) attach_point->func.find("]") != std::string::npos))
{ {
std::string file_name; std::set<std::string> matches;
switch (probetype(attach_point->provider)) switch (probetype(attach_point->provider))
{ {
case ProbeType::kprobe: case ProbeType::kprobe:
case ProbeType::kretprobe: case ProbeType::kretprobe:
file_name = "/sys/kernel/debug/tracing/available_filter_functions"; matches = find_wildcard_matches(attach_point->target,
attach_point->func,
"/sys/kernel/debug/tracing/available_filter_functions");
break; break;
case ProbeType::uprobe:
case ProbeType::uretprobe:
{
auto symbol_stream = std::istringstream(extract_func_symbols_from_path(attach_point->target));
matches = find_wildcard_matches("", attach_point->func, symbol_stream);
break;
}
case ProbeType::tracepoint: case ProbeType::tracepoint:
file_name = "/sys/kernel/debug/tracing/available_events"; matches = find_wildcard_matches(attach_point->target,
attach_point->func,
"/sys/kernel/debug/tracing/available_events");
break; break;
default: default:
std::cerr << "Wildcard matches aren't available on probe type '" std::cerr << "Wildcard matches aren't available on probe type '"
<< attach_point->provider << "'" << std::endl; << attach_point->provider << "'" << std::endl;
return 1; return 1;
} }
auto matches = find_wildcard_matches(attach_point->target,
attach_point->func,
file_name);
attach_funcs.insert(attach_funcs.end(), matches.begin(), matches.end()); attach_funcs.insert(attach_funcs.end(), matches.begin(), matches.end());
} }
else else
...@@ -126,7 +135,7 @@ int BPFtrace::add_probe(ast::Probe &p) ...@@ -126,7 +135,7 @@ int BPFtrace::add_probe(ast::Probe &p)
return 0; return 0;
} }
std::set<std::string> BPFtrace::find_wildcard_matches(const std::string &prefix, const std::string &func, const std::string &file_name) std::set<std::string> BPFtrace::find_wildcard_matches(const std::string &prefix, const std::string &func, std::istream &symbol_name_stream)
{ {
// Turn glob into a regex // Turn glob into a regex
auto regex_str = "(" + std::regex_replace(func, std::regex("\\*"), "[^\\s]*") + ")"; auto regex_str = "(" + std::regex_replace(func, std::regex("\\*"), "[^\\s]*") + ")";
...@@ -136,16 +145,9 @@ std::set<std::string> BPFtrace::find_wildcard_matches(const std::string &prefix, ...@@ -136,16 +145,9 @@ std::set<std::string> BPFtrace::find_wildcard_matches(const std::string &prefix,
std::regex func_regex(regex_str); std::regex func_regex(regex_str);
std::smatch match; std::smatch match;
std::ifstream file(file_name);
if (file.fail())
{
std::cerr << strerror(errno) << ": " << file_name << std::endl;
return std::set<std::string>();
}
std::string line; std::string line;
std::set<std::string> matches; std::set<std::string> matches;
while (std::getline(file, line)) while (std::getline(symbol_name_stream, line))
{ {
if (std::regex_search(line, match, func_regex)) if (std::regex_search(line, match, func_regex))
{ {
...@@ -158,6 +160,26 @@ std::set<std::string> BPFtrace::find_wildcard_matches(const std::string &prefix, ...@@ -158,6 +160,26 @@ std::set<std::string> BPFtrace::find_wildcard_matches(const std::string &prefix,
return matches; return matches;
} }
std::set<std::string> BPFtrace::find_wildcard_matches(const std::string &prefix, const std::string &func, const std::string &file_name)
{
std::ifstream file(file_name);
if (file.fail())
{
throw std::runtime_error("Could not read symbols from \"" + file_name + "\", err=" + std::to_string(errno));
}
std::stringstream symbol_name_stream;
std::string line;
while (file >> line)
{
symbol_name_stream << line << std::endl;
}
file.close();
return find_wildcard_matches(prefix, func, symbol_name_stream);
}
int BPFtrace::num_probes() const int BPFtrace::num_probes() const
{ {
return special_probes_.size() + probes_.size(); return special_probes_.size() + probes_.size();
...@@ -1476,6 +1498,16 @@ uint64_t BPFtrace::resolve_uname(const std::string &name, const std::string &pat ...@@ -1476,6 +1498,16 @@ uint64_t BPFtrace::resolve_uname(const std::string &name, const std::string &pat
return addr; return addr;
} }
std::string BPFtrace::extract_func_symbols_from_path(const std::string &path)
{
// TODO: switch from objdump to library call, perhaps bcc_resolve_symname()
std::string call_str = std::string("objdump -tT ") + path +
+ " | " + "grep \"F .text\" | grep -oE '[^[:space:]]+$'";
const char *call = call_str.c_str();
return exec_system(call);
}
std::string BPFtrace::exec_system(const char* cmd) std::string BPFtrace::exec_system(const char* cmd)
{ {
std::array<char, 128> buffer; std::array<char, 128> buffer;
......
...@@ -65,6 +65,7 @@ public: ...@@ -65,6 +65,7 @@ public:
std::string resolve_uid(uintptr_t addr); std::string resolve_uid(uintptr_t addr);
uint64_t resolve_kname(const std::string &name); uint64_t resolve_kname(const std::string &name);
uint64_t resolve_uname(const std::string &name, const std::string &path); uint64_t resolve_uname(const std::string &name, const std::string &path);
std::string extract_func_symbols_from_path(const std::string &path);
std::string resolve_probe(uint64_t probe_id); std::string resolve_probe(uint64_t probe_id);
uint64_t resolve_cgroupid(const std::string &path); uint64_t resolve_cgroupid(const std::string &path);
std::vector<uint64_t> get_arg_values(std::vector<Field> args, uint8_t* arg_data); std::vector<uint64_t> get_arg_values(std::vector<Field> args, uint8_t* arg_data);
...@@ -85,6 +86,7 @@ public: ...@@ -85,6 +86,7 @@ public:
static void sort_by_key(std::vector<SizedType> key_args, static void sort_by_key(std::vector<SizedType> key_args,
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> &values_by_key); std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> &values_by_key);
virtual std::set<std::string> find_wildcard_matches(const std::string &prefix, const std::string &func, std::istream &symbol_name_stream);
virtual std::set<std::string> find_wildcard_matches(const std::string &prefix, const std::string &attach_point, const std::string &file_name); virtual std::set<std::string> find_wildcard_matches(const std::string &prefix, const std::string &attach_point, const std::string &file_name);
protected: protected:
......
...@@ -267,6 +267,19 @@ TEST(bpftrace, add_probes_usdt) ...@@ -267,6 +267,19 @@ TEST(bpftrace, add_probes_usdt)
} }
TEST(bpftrace, add_probes_uprobe_wildcard) TEST(bpftrace, add_probes_uprobe_wildcard)
{
ast::AttachPoint a("uprobe", "/bin/grep", "*open", true);
ast::AttachPointList attach_points = { &a };
ast::Probe probe(&attach_points, nullptr, nullptr);
StrictMock<MockBPFtrace> bpftrace;
EXPECT_EQ(bpftrace.add_probe(probe), 0);
EXPECT_EQ(1, bpftrace.get_probes().size());
EXPECT_EQ(0, bpftrace.get_special_probes().size());
}
TEST(bpftrace, add_probes_uprobe_wildcard_no_matches)
{ {
ast::AttachPoint a("uprobe", "/bin/sh", "foo*", true); ast::AttachPoint a("uprobe", "/bin/sh", "foo*", true);
ast::AttachPointList attach_points = { &a }; ast::AttachPointList attach_points = { &a };
...@@ -274,7 +287,7 @@ TEST(bpftrace, add_probes_uprobe_wildcard) ...@@ -274,7 +287,7 @@ TEST(bpftrace, add_probes_uprobe_wildcard)
StrictMock<MockBPFtrace> bpftrace; StrictMock<MockBPFtrace> bpftrace;
EXPECT_NE(bpftrace.add_probe(probe), 0); EXPECT_EQ(bpftrace.add_probe(probe), 0);
EXPECT_EQ(0, bpftrace.get_probes().size()); EXPECT_EQ(0, bpftrace.get_probes().size());
EXPECT_EQ(0, bpftrace.get_special_probes().size()); EXPECT_EQ(0, bpftrace.get_special_probes().size());
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment