Commit b264f79b authored by Vicent Marti's avatar Vicent Marti

cc: Implement USDT argument parsing in C++

parent e38286bc
......@@ -33,12 +33,12 @@ if (CMAKE_COMPILER_IS_GNUCC)
endif()
endif()
add_library(bcc-shared SHARED bpf_common.cc bpf_module.cc libbpf.c perf_reader.c shared_table.cc exported_files.cc bcc_elf.c bcc_proc.c bcc_syms.cc)
add_library(bcc-shared SHARED bpf_common.cc bpf_module.cc libbpf.c perf_reader.c shared_table.cc exported_files.cc bcc_elf.c bcc_proc.c bcc_syms.cc usdt_args.cc)
set_target_properties(bcc-shared PROPERTIES VERSION ${REVISION_LAST} SOVERSION 0)
set_target_properties(bcc-shared PROPERTIES OUTPUT_NAME bcc)
add_library(bcc-loader-static libbpf.c perf_reader.c bcc_elf.c bcc_proc.c)
add_library(bcc-static STATIC bpf_common.cc bpf_module.cc shared_table.cc exported_files.cc bcc_syms.cc)
add_library(bcc-static STATIC bpf_common.cc bpf_module.cc shared_table.cc exported_files.cc bcc_syms.cc usdt_args.cc)
set_target_properties(bcc-static PROPERTIES OUTPUT_NAME bcc)
# BPF is still experimental otherwise it should be available
......
/*
* Copyright (c) 2016 GitHub, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <unordered_map>
#include <string>
namespace USDT {
struct Argument {
int arg_size;
int constant;
int deref_offset;
std::string deref_ident;
std::string register_name;
Argument() : arg_size(0), constant(0), deref_offset(0) {}
};
class ArgumentParser {
const char *arg_;
ssize_t cur_pos_;
protected:
virtual bool validate_register(const std::string &reg, int &reg_size) = 0;
ssize_t parse_number(ssize_t pos, int &number);
ssize_t parse_identifier(ssize_t pos, std::string &ident);
ssize_t parse_register(ssize_t pos, Argument &dest);
ssize_t parse_expr(ssize_t pos, Argument &dest);
ssize_t parse_1(ssize_t pos, Argument &dest);
void print_error(ssize_t pos);
public:
bool parse(Argument &dest);
bool done() { return arg_[cur_pos_] == '\0'; }
ArgumentParser(const char *arg) : arg_(arg), cur_pos_(0) {}
};
class ArgumentParser_x64 : public ArgumentParser {
static const std::unordered_map<std::string, int> registers_;
bool validate_register(const std::string &reg, int &reg_size);
public:
ArgumentParser_x64(const char *arg) : ArgumentParser(arg) {}
};
struct Probe {
std::string _bin_path;
std::string _provider;
std::string _name;
uint64_t _semaphore;
Probe(const char *bin_path, const char *provider, const char *name,
uint64_t semaphore)
: _bin_path(bin_path),
_provider(provider),
_name(name),
_semaphore(semaphore) {}
};
}
/*
* Copyright (c) 2016 GitHub, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <unordered_map>
#include <stdio.h>
#include "usdt.h"
namespace USDT {
ssize_t ArgumentParser::parse_number(ssize_t pos, int &number) {
char *endp;
number = strtol(arg_ + pos, &endp, 0);
return endp - arg_;
}
ssize_t ArgumentParser::parse_identifier(ssize_t pos, std::string &ident) {
if (isalpha(arg_[pos]) || arg_[pos] == '_') {
ssize_t start = pos++;
while (isalnum(arg_[pos]) || arg_[pos] == '_') pos++;
ident.assign(arg_ + start, pos - start);
}
return pos;
}
ssize_t ArgumentParser::parse_register(ssize_t pos, Argument &dest) {
ssize_t start = pos++;
if (arg_[start] != '%')
return -start;
while (isalnum(arg_[pos])) pos++;
dest.register_name.assign(arg_ + start, pos - start);
if (!validate_register(dest.register_name, dest.arg_size))
return -start;
return pos;
}
ssize_t ArgumentParser::parse_expr(ssize_t pos, Argument &dest) {
if (arg_[pos] == '$')
return parse_number(pos + 1, dest.constant);
if (arg_[pos] == '%')
return parse_register(pos, dest);
if (isdigit(arg_[pos]) || arg_[pos] == '-') {
pos = parse_number(pos, dest.deref_offset);
if (arg_[pos] == '+') {
pos = parse_identifier(pos + 1, dest.deref_ident);
if (dest.deref_ident.empty())
return -pos;
}
} else {
pos = parse_identifier(pos, dest.deref_ident);
}
if (arg_[pos] != '(')
return -pos;
pos = parse_register(pos + 1, dest);
if (pos < 0)
return pos;
return (arg_[pos] == ')') ? pos + 1 : -pos;
}
ssize_t ArgumentParser::parse_1(ssize_t pos, Argument &dest) {
if (isdigit(arg_[pos]) || arg_[pos] == '-') {
int asize;
ssize_t m = parse_number(pos, asize);
if (arg_[m] == '@') {
dest.arg_size = asize;
return parse_expr(m + 1, dest);
}
}
return parse_expr(pos, dest);
}
void ArgumentParser::print_error(ssize_t pos) {
fprintf(stderr, "Parse error:\n %s\n", arg_);
for (ssize_t i = 0; i < pos + 4; ++i) fputc('-', stderr);
fputc('^', stderr);
fputc('\n', stderr);
}
bool ArgumentParser::parse(Argument &dest) {
if (done())
return false;
ssize_t res = parse_1(cur_pos_, dest);
if (res < 0) {
print_error(-res);
return false;
}
if (!isspace(arg_[res]) && arg_[res] != '\0') {
print_error(res);
return false;
}
while (isspace(arg_[res])) res++;
cur_pos_ = res;
return true;
}
const std::unordered_map<std::string, int> ArgumentParser_x64::registers_ = {
{"%rax", 8}, {"%rbx", 8}, {"%rcx", 8}, {"%rdx", 8}, {"%rdi", 8},
{"%rsi", 8}, {"%rbp", 8}, {"%rsp", 8}, {"%rip", 8}, {"%r8", 8},
{"%r9", 8}, {"%r10", 8}, {"%r11", 8}, {"%r12", 8}, {"%r13", 8},
{"%r14", 8}, {"%r15", 8},
{"%eax", 4}, {"%ebx", 4}, {"%ecx", 4}, {"%edx", 4}, {"%edi", 4},
{"%esi", 4}, {"%ebp", 4}, {"%esp", 4}, {"%eip", 4},
{"%ax", 2}, {"%bx", 2}, {"%cx", 2}, {"%dx", 2}, {"%di", 2},
{"%si", 2}, {"%bp", 2}, {"%sp", 2}, {"%ip", 2},
{"%al", 1}, {"%bl", 1}, {"%cl", 1}, {"%dl", 1}};
bool ArgumentParser_x64::validate_register(const std::string &reg,
int &reg_size) {
auto it = registers_.find(reg);
if (it == registers_.end())
return false;
if (reg_size == 0)
reg_size = it->second;
return true;
}
}
......@@ -8,6 +8,6 @@ target_link_libraries(test_static bcc-static)
add_test(NAME c_test_static COMMAND ${TEST_WRAPPER} c_test_static sudo ${CMAKE_CURRENT_BINARY_DIR}/test_static)
add_executable(test_libbcc test_libbcc.cc test_c_api.cc)
add_executable(test_libbcc test_libbcc.cc test_c_api.cc test_usdt_args.cc)
target_link_libraries(test_libbcc bcc-shared dl)
add_test(NAME test_libbcc COMMAND ${TEST_WRAPPER} c_test_all sudo ${CMAKE_CURRENT_BINARY_DIR}/test_libbcc)
#include <string>
#include "catch.hpp"
#include "usdt.h"
static void verify_register(USDT::ArgumentParser_x64 &parser, int arg_size,
const std::string &register_name, int constant,
int deref_offset, const std::string &deref_ident) {
USDT::Argument arg;
REQUIRE(parser.parse(arg));
REQUIRE(arg.arg_size == arg_size);
REQUIRE(arg.register_name == register_name);
REQUIRE(arg.constant == constant);
REQUIRE(arg.deref_offset == deref_offset);
REQUIRE(arg.deref_ident == deref_ident);
}
TEST_CASE("test usdt argument parsing", "[usdt]") {
SECTION("argument examples from the Python implementation") {
USDT::ArgumentParser_x64 parser(
"-4@$0 8@$1234 %rdi %rax %rsi "
"-8@%rbx 4@%r12 8@-8(%rbp) 4@(%rax) "
"-4@global_max_action(%rip) "
"8@24+mp_(%rip) ");
verify_register(parser, -4, "", 0, 0, "");
verify_register(parser, 8, "", 1234, 0, "");
verify_register(parser, 8, "%rdi", 0, 0, "");
verify_register(parser, 8, "%rax", 0, 0, "");
verify_register(parser, 8, "%rsi", 0, 0, "");
verify_register(parser, -8, "%rbx", 0, 0, "");
verify_register(parser, 4, "%r12", 0, 0, "");
verify_register(parser, 8, "%rbp", 0, -8, "");
verify_register(parser, 4, "%rax", 0, 0, "");
verify_register(parser, -4, "%rip", 0, 0, "global_max_action");
verify_register(parser, 8, "%rip", 0, 24, "mp_");
REQUIRE(parser.done());
}
}
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