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:
### Wildcards
Some probe types allow wildcards to be used when attaching a probe:
`uprobe:/bin/bash:read* { ... }`
`kprobe:vfs_* { ... }`
### Predicates
......
......@@ -1114,22 +1114,32 @@ void CodegenLLVM::visit(Probe &probe)
for (auto &attach_point : *probe.attach_points) {
current_attach_point_ = attach_point;
std::string file_name;
std::set<std::string> matches;
switch (probetype(attach_point->provider))
{
case ProbeType::kprobe:
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;
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:
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;
default:
std::cerr << "Wildcard matches aren't available on probe type '"
<< attach_point->provider << "'" << std::endl;
return;
}
auto matches = bpftrace_.find_wildcard_matches(attach_point->target, attach_point->func, file_name);
for (auto &match : matches) {
printf_id_ = starting_printf_id_;
time_id_ = starting_time_id_;
......
......@@ -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))
{
std::string file_name;
std::set<std::string> matches;
switch (probetype(attach_point->provider))
{
case ProbeType::kprobe:
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;
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:
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;
default:
std::cerr << "Wildcard matches aren't available on probe type '"
<< attach_point->provider << "'" << std::endl;
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());
}
else
......@@ -126,7 +135,7 @@ int BPFtrace::add_probe(ast::Probe &p)
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
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,
std::regex func_regex(regex_str);
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::set<std::string> matches;
while (std::getline(file, line))
while (std::getline(symbol_name_stream, line))
{
if (std::regex_search(line, match, func_regex))
{
......@@ -158,6 +160,26 @@ std::set<std::string> BPFtrace::find_wildcard_matches(const std::string &prefix,
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
{
return special_probes_.size() + probes_.size();
......@@ -1476,6 +1498,16 @@ uint64_t BPFtrace::resolve_uname(const std::string &name, const std::string &pat
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::array<char, 128> buffer;
......
......@@ -65,6 +65,7 @@ public:
std::string resolve_uid(uintptr_t addr);
uint64_t resolve_kname(const std::string &name);
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);
uint64_t resolve_cgroupid(const std::string &path);
std::vector<uint64_t> get_arg_values(std::vector<Field> args, uint8_t* arg_data);
......@@ -85,6 +86,7 @@ public:
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);
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);
protected:
......
......@@ -267,6 +267,19 @@ TEST(bpftrace, add_probes_usdt)
}
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::AttachPointList attach_points = { &a };
......@@ -274,7 +287,7 @@ TEST(bpftrace, add_probes_uprobe_wildcard)
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_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