Skip to content

Commit

Permalink
Allow wildcards for tracepoint categories
Browse files Browse the repository at this point in the history
It is now possible to run e.g. this:

    bpftrace -e 'tracepoint:kvm*:* { @[probe] = count(); }'

Changes were done to wildcard matching: prefix is no longer used (since
no probe type uses a fixed prefix), the tracepoint category is made a
part of the function pattern. Also, find_wildcard_matches now returns
tracepoint matches in the form "category:event", which must be handled
appropriately in the calling code.

New unit tests for codegen and bpftrace are added.
  • Loading branch information
viktormalik committed Jul 30, 2020
1 parent a44618a commit 512a43e
Show file tree
Hide file tree
Showing 13 changed files with 291 additions and 49 deletions.
12 changes: 9 additions & 3 deletions src/ast/ast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,11 +449,12 @@ std::string opstr(Unop &unop)
}
}

std::string AttachPoint::name(const std::string &attach_point) const
std::string AttachPoint::name(const std::string &attach_target,
const std::string &attach_point) const
{
std::string n = provider;
if (target != "")
n += ":" + target;
if (attach_target != "")
n += ":" + attach_target;
if (ns != "")
n += ":" + ns;
if (attach_point != "")
Expand All @@ -473,6 +474,11 @@ std::string AttachPoint::name(const std::string &attach_point) const
return n;
}

std::string AttachPoint::name(const std::string &attach_point) const
{
return name(target, attach_point);
}

int AttachPoint::index(std::string name) {
if (index_.count(name) == 0) return 0;
return index_[name];
Expand Down
2 changes: 2 additions & 0 deletions src/ast/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ class AttachPoint : public Node {

void accept(Visitor &v) override;
std::string name(const std::string &attach_point) const;
std::string name(const std::string &attach_target,
const std::string &attach_point) const;

int index(std::string name);
void set_index(std::string name, int index);
Expand Down
16 changes: 11 additions & 5 deletions src/ast/codegen_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1837,8 +1837,7 @@ void CodegenLLVM::generateProbe(Probe &probe,
// tracepoint wildcard expansion, part 3 of 3. Set tracepoint_struct_ for use
// by args builtin.
if (probetype(current_attach_point_->provider) == ProbeType::tracepoint)
tracepoint_struct_ = TracepointFormatParser::get_struct_name(
current_attach_point_->target, full_func_id);
tracepoint_struct_ = TracepointFormatParser::get_struct_name(full_func_id);
int index = getNextIndexForProbe(probe.name());
if (expansion)
current_attach_point_->set_index(full_func_id, index);
Expand Down Expand Up @@ -1937,9 +1936,7 @@ void CodegenLLVM::visit(Probe &probe)
if (probetype(attach_point->provider) == ProbeType::usdt) {
std::string func_id = match;
std::string orig_ns = attach_point->ns;
std::string ns = func_id.substr(0, func_id.find(":"));

func_id.erase(0, func_id.find(":")+1);
std::string ns = erase_prefix(func_id);

// Ensure that the full probe name used is the resolved one for this probe,
attach_point->ns = ns;
Expand Down Expand Up @@ -1980,6 +1977,15 @@ void CodegenLLVM::visit(Probe &probe)
if (attach_point->provider == "BEGIN" ||
attach_point->provider == "END")
probefull_ = attach_point->provider;
else if (probetype(attach_point->provider) == ProbeType::tracepoint)
{
// Tracepoint probes must specify both a category (target) and
// a function name
std::string func = match;
std::string category = erase_prefix(func);

probefull_ = attach_point->name(category, func);
}
else
probefull_ = attach_point->name(match);

Expand Down
7 changes: 3 additions & 4 deletions src/ast/semantic_analyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ void SemanticAnalyser::builtin_args_tracepoint(AttachPoint *attach_point,
{
auto &match = *matches.begin();
std::string tracepoint_struct = TracepointFormatParser::get_struct_name(
attach_point->target, match);
match);
Struct &cstruct = bpftrace_.structs_[tracepoint_struct];
builtin.type = CreateCTX(cstruct.size, tracepoint_struct);
builtin.type.is_pointer = true;
Expand Down Expand Up @@ -1621,9 +1621,8 @@ void SemanticAnalyser::visit(FieldAccess &acc)

auto matches = bpftrace_.find_wildcard_matches(*attach_point);
for (auto &match : matches) {
std::string tracepoint_struct =
TracepointFormatParser::get_struct_name(attach_point->target,
match);
std::string tracepoint_struct = TracepointFormatParser::get_struct_name(
match);
structs[tracepoint_struct] = bpftrace_.structs_[tracepoint_struct].fields;
}
}
Expand Down
43 changes: 21 additions & 22 deletions src/bpftrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ namespace {
* ehci_disable_ASE [ehci_hcd]
*/
std::set<std::string> find_wildcard_matches_internal(
const std::string &prefix,
const std::string &func,
bool ignore_trailing_module,
std::istream &symbol_stream)
Expand All @@ -72,7 +71,6 @@ std::set<std::string> find_wildcard_matches_internal(

std::string line;
std::set<std::string> matches;
std::string full_prefix = prefix.empty() ? "" : (prefix + ":");
while (std::getline(symbol_stream, line))
{
if (ignore_trailing_module && line.size() && line[line.size() - 1] == ']')
Expand All @@ -81,13 +79,6 @@ std::set<std::string> find_wildcard_matches_internal(
line = line.substr(0, idx);
}

if (!full_prefix.empty())
{
if (line.find(full_prefix, 0) != 0)
continue;
line = line.substr(full_prefix.length());
}

if (!wildcard_match(line, tokens, start_wildcard, end_wildcard))
{
if (symbol_has_cpp_mangled_signature(line))
Expand Down Expand Up @@ -223,7 +214,9 @@ int BPFtrace::add_probe(ast::Probe &p)

std::vector<std::string> attach_funcs;
bool underspecified_usdt_probe = (probetype(attach_point->provider) == ProbeType::usdt && attach_point->ns.empty());
if (attach_point->need_expansion && (has_wildcard(attach_point->func) || underspecified_usdt_probe))
if (attach_point->need_expansion &&
(has_wildcard(attach_point->func) ||
has_wildcard(attach_point->target) || underspecified_usdt_probe))
{
std::set<std::string> matches;
try
Expand Down Expand Up @@ -262,34 +255,43 @@ int BPFtrace::add_probe(ast::Probe &p)
{
if (probetype(attach_point->provider) == ProbeType::usdt && !attach_point->ns.empty())
attach_funcs.push_back(attach_point->ns + ":" + attach_point->func);
else if (probetype(attach_point->provider) == ProbeType::tracepoint)
attach_funcs.push_back(attach_point->target + ":" + attach_point->func);
else
attach_funcs.push_back(attach_point->func);
}

for (const auto &func : attach_funcs)
{
std::string func_id = func;
std::string target = attach_point->target;

// USDT probes must specify both a provider and a function name for full id
// So we will extract out the provider namespace to get just the function name
if (probetype(attach_point->provider) == ProbeType::usdt )
{
std::string ns = func_id.substr(0, func_id.find(":"));
func_id.erase(0, func_id.find(":")+1);
std::string ns = erase_prefix(func_id);
// Set attach_point ns to be a resolved namespace in case of wildcard
attach_point->ns = ns;
// Set the function name to be a resolved function id in case of wildcard
attach_point->func = func_id;
}
else if (probetype(attach_point->provider) == ProbeType::tracepoint)
{
// tracepoint probes must specify both a target and a function name
// We extract the target from func_id so that a resolved target and a
// resolved function name are used in the probe.
target = erase_prefix(func_id);
}

Probe probe;
probe.path = attach_point->target;
probe.path = target;
probe.attach_point = func_id;
probe.type = probetype(attach_point->provider);
probe.log_size = log_size_;
probe.orig_name = p.name();
probe.ns = attach_point->ns;
probe.name = attach_point->name(func_id);
probe.name = attach_point->name(target, func_id);
probe.freq = attach_point->freq;
probe.address = attach_point->address;
probe.func_offset = attach_point->func_offset;
Expand Down Expand Up @@ -329,7 +331,7 @@ std::set<std::string> BPFtrace::find_wildcard_matches(
{
std::unique_ptr<std::istream> symbol_stream;
bool ignore_trailing_module = false;
std::string prefix, func;
std::string func;

switch (probetype(attach_point.provider))
{
Expand All @@ -338,7 +340,6 @@ std::set<std::string> BPFtrace::find_wildcard_matches(
{
symbol_stream = get_symbols_from_file(
"/sys/kernel/debug/tracing/available_filter_functions");
prefix = "";
func = attach_point.func;
ignore_trailing_module = true;
break;
Expand All @@ -348,22 +349,19 @@ std::set<std::string> BPFtrace::find_wildcard_matches(
{
symbol_stream = std::make_unique<std::istringstream>(
extract_func_symbols_from_path(attach_point.target));
prefix = "";
func = attach_point.func;
break;
}
case ProbeType::tracepoint:
{
symbol_stream = get_symbols_from_file(
"/sys/kernel/debug/tracing/available_events");
prefix = attach_point.target;
func = attach_point.func;
func = attach_point.target + ":" + attach_point.func;
break;
}
case ProbeType::usdt:
{
symbol_stream = get_symbols_from_usdt(pid(), attach_point.target);
prefix = "";
if (attach_point.ns == "")
func = "*:" + attach_point.func;
else
Expand All @@ -383,8 +381,9 @@ std::set<std::string> BPFtrace::find_wildcard_matches(
}
}

return find_wildcard_matches_internal(
prefix, func, ignore_trailing_module, *symbol_stream);
return find_wildcard_matches_internal(func,
ignore_trailing_module,
*symbol_stream);
}

std::set<std::string> BPFtrace::find_symbol_matches(
Expand Down
34 changes: 19 additions & 15 deletions src/tracepoint_format_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,7 @@ bool TracepointFormatParser::parse(ast::Program *program, BPFtrace &bpftrace)
std::string format_file_path = "/sys/kernel/debug/tracing/events/" + category + "/" + event_name + "/format";
glob_t glob_result;

if (has_wildcard(category))
{
bpftrace.error(std::cerr,
ap->loc,
"wildcards in tracepoint category is not supported: " +
category);
return false;
}

if (has_wildcard(event_name))
if (has_wildcard(category) || has_wildcard(event_name))
{
// tracepoint wildcard expansion, part 1 of 3. struct definitions.
memset(&glob_result, 0, sizeof(glob_result));
Expand Down Expand Up @@ -88,16 +79,21 @@ bool TracepointFormatParser::parse(ast::Program *program, BPFtrace &bpftrace)
for (size_t i = 0; i < glob_result.gl_pathc; ++i) {
std::string filename(glob_result.gl_pathv[i]);
std::ifstream format_file(filename);
std::string prefix("/sys/kernel/debug/tracing/events/" + category + "/");
std::string real_event = filename.substr(prefix.length(),
filename.length() - std::string("/format").length() - prefix.length());
std::string prefix("/sys/kernel/debug/tracing/events/");
size_t pos = prefix.length();
std::string real_category = filename.substr(
pos, filename.find('/', pos) - pos);
pos = prefix.length() + real_category.length() + 1;
std::string real_event = filename.substr(
pos, filename.length() - std::string("/format").length() - pos);

// Check to avoid adding the same struct more than once to definitions
std::string struct_name = get_struct_name(category, real_event);
std::string struct_name = get_struct_name(real_category,
real_event);
if (!TracepointFormatParser::struct_list.count(struct_name))
{
program->c_definitions += get_tracepoint_struct(
format_file, category, real_event, bpftrace);
format_file, real_category, real_event, bpftrace);
TracepointFormatParser::struct_list.insert(struct_name);
}
}
Expand Down Expand Up @@ -150,6 +146,14 @@ std::string TracepointFormatParser::get_struct_name(const std::string &category,
return "struct _tracepoint_" + category + "_" + event_name;
}

std::string TracepointFormatParser::get_struct_name(const std::string &probe_id)
{
// probe_id has format category:event
std::string event_name = probe_id;
std::string category = erase_prefix(event_name);
return get_struct_name(category, event_name);
}

std::string TracepointFormatParser::parse_field(const std::string &line,
int *last_offset,
BPFtrace &bpftrace)
Expand Down
1 change: 1 addition & 0 deletions src/tracepoint_format_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class TracepointFormatParser
static bool parse(ast::Program *program, BPFtrace &bpftrace);
static std::string get_struct_name(const std::string &category,
const std::string &event_name);
static std::string get_struct_name(const std::string &probe_id);

private:
static std::string parse_field(const std::string &line,
Expand Down
8 changes: 8 additions & 0 deletions src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ std::vector<std::string> split_string(const std::string &str,
return elems;
}

/// Erase prefix up to the first colon (:) from str and return the prefix
std::string erase_prefix(std::string &str)
{
std::string prefix = str.substr(0, str.find(':'));
str.erase(0, prefix.length() + 1);
return prefix;
}

bool wildcard_match(const std::string &str, std::vector<std::string> &tokens, bool start_wildcard, bool end_wildcard) {
size_t next = 0;

Expand Down
1 change: 1 addition & 0 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ bool has_wildcard(const std::string &str);
std::vector<std::string> split_string(const std::string &str,
char delimiter,
bool remove_empty = false);
std::string erase_prefix(std::string &str);
bool wildcard_match(const std::string &str,
std::vector<std::string> &tokens,
bool start_wildcard,
Expand Down
25 changes: 25 additions & 0 deletions tests/bpftrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,31 @@ TEST(bpftrace, add_probes_tracepoint_wildcard)
check_tracepoint(bpftrace->get_probes().at(1), "sched", "sched_two", probe_orig_name);
}

TEST(bpftrace, add_probes_tracepoint_category_wildcard)
{
ast::AttachPoint a("");
a.provider = "tracepoint";
a.target = "sched*";
a.func = "sched_*";
a.need_expansion = true;
ast::AttachPointList attach_points = { &a };
ast::Probe probe(&attach_points, nullptr, nullptr);

auto bpftrace = get_strict_mock_bpftrace();
EXPECT_CALL(*bpftrace,
get_symbols_from_file("/sys/kernel/debug/tracing/available_events"))
.Times(1);

ASSERT_EQ(0, bpftrace->add_probe(probe));
ASSERT_EQ(3U, bpftrace->get_probes().size());
ASSERT_EQ(0U, bpftrace->get_special_probes().size());

std::string probe_orig_name = "tracepoint:sched*:sched_*";
check_tracepoint(bpftrace->get_probes().at(0), "sched", "sched_one", probe_orig_name);
check_tracepoint(bpftrace->get_probes().at(1), "sched", "sched_two", probe_orig_name);
check_tracepoint(bpftrace->get_probes().at(2), "sched_extra", "sched_extra", probe_orig_name);
}

TEST(bpftrace, add_probes_tracepoint_wildcard_no_matches)
{
ast::AttachPoint a("");
Expand Down
22 changes: 22 additions & 0 deletions tests/codegen/args_multiple_tracepoints_category_wild.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "../mocks.h"
#include "common.h"

using ::testing::_;
using ::testing::Return;

namespace bpftrace {
namespace test {
namespace codegen {

TEST(codegen, args_multiple_tracepoints_category_wild)
{
auto bpftrace = get_mock_bpftrace();

test(*bpftrace,
"tracepoint:sched*:sched_* { @[args->common_field] = count(); }",
NAME);
}

} // namespace codegen
} // namespace test
} // namespace bpftrace
Loading

0 comments on commit 512a43e

Please sign in to comment.