diff --git a/src/bpftrace.cpp b/src/bpftrace.cpp index 4a972727766..ce21686b6b9 100644 --- a/src/bpftrace.cpp +++ b/src/bpftrace.cpp @@ -153,25 +153,31 @@ std::set BPFtrace::find_wildcard_matches(const std::string &prefix, { if (!has_wildcard(func)) return std::set({func}); - // Turn glob into a regex - auto regex_str = "(" + std::regex_replace(func, std::regex("\\*"), "[^\\s]*") + ")"; - if (prefix != "") - regex_str = prefix + ":" + regex_str; - regex_str = "^" + regex_str; - std::regex func_regex(regex_str); - std::smatch match; + bool start_wildcard = func[0] == '*'; + bool end_wildcard = func[func.length() - 1] == '*'; + + std::vector tokens = split_string(func, '*'); + tokens.erase(std::remove(tokens.begin(), tokens.end(), ""), tokens.end()); std::string line; std::set matches; + std::string full_prefix = prefix.empty() ? "" : (prefix + ":"); while (std::getline(symbol_name_stream, line)) { - if (std::regex_search(line, match, func_regex)) - { - assert(match.size() == 2); - // skip the ".part.N" kprobe variants, as they can't be traced: - if (std::strstr(match.str(1).c_str(), ".part.") == NULL) - matches.insert(match[1]); + 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)) + continue; + + // skip the ".part.N" kprobe variants, as they can't be traced: + if (line.find(".part.") != std::string::npos) + continue; + + matches.insert(line); } return matches; } @@ -1499,17 +1505,6 @@ std::string BPFtrace::resolve_uid(uintptr_t addr) return username; } -std::vector BPFtrace::split_string(std::string &str, char split_by) -{ - std::vector elems; - std::stringstream ss(str); - std::string value; - while(std::getline(ss, value, split_by)) { - elems.push_back(value); - } - return elems; -} - std::string BPFtrace::resolve_ksym(uintptr_t addr, bool show_offset) { struct bcc_symbol ksym; diff --git a/src/bpftrace.h b/src/bpftrace.h index 248c54c2de8..69ab3c7fd35 100644 --- a/src/bpftrace.h +++ b/src/bpftrace.h @@ -136,7 +136,6 @@ class BPFtrace static uint64_t read_address_from_output(std::string output); static std::string hist_index_label(int power); static std::string lhist_index_label(int number); - static std::vector split_string(std::string &str, char split_by); std::vector find_empty_key(IMap &map, size_t size) const; static int spawn_child(const std::vector& args); static bool is_pid_alive(int pid); diff --git a/src/utils.cpp b/src/utils.cpp index b3f0241d46a..7ad0fef3a5b 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -76,6 +77,38 @@ bool has_wildcard(const std::string &str) str.find("]") != std::string::npos); } +std::vector split_string(const std::string &str, char delimiter) { + std::vector elems; + std::stringstream ss(str); + std::string value; + while(std::getline(ss, value, delimiter)) { + elems.push_back(value); + } + return elems; +} + +bool wildcard_match(const std::string &str, std::vector &tokens, bool start_wildcard, bool end_wildcard) { + size_t next = 0; + + if (!start_wildcard) + if (str.find(tokens[0], next) != next) + return false; + + for (std::string token : tokens) { + size_t found = str.find(token, next); + if (found == std::string::npos) + return false; + + next = found + token.length(); + } + + if (!end_wildcard) + if (str.length() != next) + return false; + + return true; +} + std::vector get_online_cpus() { return read_cpu_range("/sys/devices/system/cpu/online"); diff --git a/src/utils.h b/src/utils.h index 892675b8e4e..3d9f1d6d5d0 100644 --- a/src/utils.h +++ b/src/utils.h @@ -30,6 +30,8 @@ static std::vector DEPRECATED_LIST = bool has_wildcard(const std::string &str); +std::vector split_string(const std::string &str, char delimiter); +bool wildcard_match(const std::string &str, std::vector &tokens, bool start_wildcard, bool end_wildcard); std::vector get_online_cpus(); std::vector get_possible_cpus(); std::vector get_kernel_cflags( diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b7f8904d852..ffe85b72ba2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(bpftrace_test probe.cpp semantic_analyser.cpp tracepoint_format_parser.cpp + utils.cpp ${CMAKE_SOURCE_DIR}/src/attached_probe.cpp ${CMAKE_SOURCE_DIR}/src/bpftrace.cpp diff --git a/tests/utils.cpp b/tests/utils.cpp new file mode 100644 index 00000000000..c70e3db3eeb --- /dev/null +++ b/tests/utils.cpp @@ -0,0 +1,77 @@ +#include "gtest/gtest.h" +#include "utils.h" + +namespace bpftrace { +namespace test { +namespace utils { + +TEST(utils, split_string) +{ + std::vector tokens_empty = {}; + std::vector tokens_one_empty = {""}; + std::vector tokens_two_empty = {"", ""}; + std::vector tokens_f = {"", "f"}; + std::vector tokens_foo_bar = {"foo", "bar"}; + std::vector tokens_empty_foo_bar = {"", "foo", "bar"}; + std::vector tokens_empty_foo_empty_bar = {"", "foo", "", "bar"}; + std::vector tokens_empty_foo_bar_biz = {"", "foo", "bar", "biz"}; + + EXPECT_EQ(split_string("", '-'), tokens_empty); + EXPECT_EQ(split_string("-", '-'), tokens_one_empty); + EXPECT_EQ(split_string("--", '-'), tokens_two_empty); + EXPECT_EQ(split_string("-f-", '-'), tokens_f); + EXPECT_EQ(split_string("-foo-bar-", '-'), tokens_empty_foo_bar); + EXPECT_EQ(split_string("-foo--bar-", '-'), tokens_empty_foo_empty_bar); + EXPECT_EQ(split_string("-foo-bar-biz-", '-'), tokens_empty_foo_bar_biz); + EXPECT_EQ(split_string("-foo-bar", '-'), tokens_empty_foo_bar); + EXPECT_EQ(split_string("foo-bar-", '-'), tokens_foo_bar); + EXPECT_EQ(split_string("foo-bar", '-'), tokens_foo_bar); +} + +TEST(utils, wildcard_match) +{ + std::vector tokens_not = {"not"}; + std::vector tokens_bar = {"bar"}; + std::vector tokens_bar_not = {"bar", "not"}; + std::vector tokens_foo = {"foo"}; + std::vector tokens_biz = {"biz"}; + std::vector tokens_foo_biz = {"foo", "biz"}; + + + // start: true, end: true + EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, true, true), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, true, true), true); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, true, true), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, true, true), true); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, true, true), true); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, true, true), true); + + // start: false, end: true + EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, false, true), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, false, true), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, false, true), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, false, true), true); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, false, true), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, false, true), true); + + // start: true, end: false + EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, true, false), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, true, false), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, true, false), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, true, false), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, true, false), true); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, true, false), true); + + // start: false, end: false + EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, false, false), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, false, false), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, false, false), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, false, false), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, false, false), false); + EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, false, false), true); +} + +} // namespace ast +} // namespace test +} // namespace bpftrace +