Commit 6e4ff5a0 authored by Brendan Gregg's avatar Brendan Gregg Committed by GitHub

Merge pull request #113 from iovisor/tp-format-parser

Tracepoint format parser
parents 8422e64b a0fab2e1
......@@ -8,6 +8,7 @@ add_executable(bpftrace
map.cpp
mapkey.cpp
printf.cpp
tracepoint_format_parser.cpp
types.cpp
list.cpp
)
......
......@@ -121,6 +121,10 @@ void CodegenLLVM::visit(Builtin &builtin)
name_id++;
expr_ = b_.getInt64(builtin.name_id);
}
else if (builtin.ident == "args")
{
expr_ = ctx_;
}
else if (builtin.ident == "ctx")
{
// undocumented builtin: for debugging
......
......@@ -3,6 +3,7 @@
#include "fake_map.h"
#include "parser.tab.hh"
#include "printf.h"
#include "tracepoint_format_parser.h"
#include "arch/arch.h"
#include <sys/stat.h>
#include <regex>
......@@ -67,6 +68,12 @@ void SemanticAnalyser::visit(Builtin &builtin)
}
else if (!builtin.ident.compare(0, 3, "arg") && builtin.ident.size() == 4 &&
builtin.ident.at(3) >= '0' && builtin.ident.at(3) <= '9') {
for (auto &attach_point : *probe_->attach_points)
{
ProbeType type = probetype(attach_point->provider);
if (type == ProbeType::tracepoint)
err_ << "The " << builtin.ident << " builtin can not be used with tracepoint probes" << std::endl;
}
int arg_num = atoi(builtin.ident.substr(3).c_str());
if (arg_num > arch::max_arg())
err_ << arch::name() << " doesn't support " << builtin.ident << std::endl;
......@@ -79,6 +86,20 @@ void SemanticAnalyser::visit(Builtin &builtin)
else if (builtin.ident == "username") {
builtin.type = SizedType(Type::username, 8);
}
else if (builtin.ident == "args") {
if (probe_->attach_points->size() > 1)
err_ << "The args builtin only works for probes with 1 attach point" << std::endl;
auto &attach_point = probe_->attach_points->at(0);
ProbeType type = probetype(attach_point->provider);
if (type != ProbeType::tracepoint)
err_ << "The args builtin can only be used with tracepoint probes"
<< "(" << attach_point->provider << " used here)" << std::endl;
std::string tracepoint_struct = TracepointFormatParser::get_struct_name(attach_point->target, attach_point->func);
Struct &cstruct = bpftrace_.structs_[tracepoint_struct];
builtin.type = SizedType(Type::cast, cstruct.size, tracepoint_struct);
builtin.type.is_pointer = true;
}
else {
builtin.type = SizedType(Type::none, 0);
err_ << "Unknown builtin variable: '" << builtin.ident << "'" << std::endl;
......
#include <clang-c/Index.h>
#include <iostream>
#include <string.h>
#include <sys/utsname.h>
#include "ast.h"
#include "bpftrace.h"
......@@ -142,10 +143,15 @@ void ClangParser::parse(ast::Program *program, StructMap &structs)
},
};
struct utsname utsname;
uname(&utsname);
std::string kernel_header_include_flag = std::string("/lib/modules/") + utsname.release + "/build/include";
CXIndex index = clang_createIndex(1, 1);
CXTranslationUnit translation_unit;
const char * const args[] = {
"-I", "/bpftrace/include",
"-I", kernel_header_include_flag.c_str(),
};
CXErrorCode error = clang_parseTranslationUnit2(
index,
......
......@@ -5,7 +5,6 @@
namespace bpftrace {
namespace ast { class Program; }
class BPFtrace;
using StructMap = std::map<std::string, Struct>;
......
......@@ -48,7 +48,7 @@ path :(\\.|[_\-\./a-zA-Z0-9])*:
<COMMENT>"*/" BEGIN(INITIAL);
<COMMENT>"EOF" driver.error(loc, std::string("end of file during comment"));
pid|tid|uid|gid|nsecs|cpu|comm|stack|ustack|arg[0-9]|retval|func|name|curtask|rand|ctx|username {
pid|tid|uid|gid|nsecs|cpu|comm|stack|ustack|arg[0-9]|retval|func|name|curtask|rand|ctx|username|args {
return Parser::make_BUILTIN(yytext, loc); }
{path} { return Parser::make_PATH(yytext, loc); }
{map} { return Parser::make_MAP(yytext, loc); }
......
......@@ -6,9 +6,10 @@
#include "clang_parser.h"
#include "codegen_llvm.h"
#include "driver.h"
#include "list.h"
#include "printer.h"
#include "semantic_analyser.h"
#include "list.h"
#include "tracepoint_format_parser.h"
using namespace bpftrace;
......@@ -130,6 +131,8 @@ int main(int argc, char *argv[])
if (pid_str)
bpftrace.pid_ = atoi(pid_str);
TracepointFormatParser::parse(driver.root_);
if (bt_debug != DebugLevel::kNone)
{
ast::Printer p(std::cout);
......
#include <fstream>
#include <iostream>
#include <string.h>
#include "ast.h"
#include "struct.h"
#include "tracepoint_format_parser.h"
namespace bpftrace {
void TracepointFormatParser::parse(ast::Program *program)
{
bool has_tracepoint_probes = false;
for (ast::Probe *probe : *program->probes)
for (ast::AttachPoint *ap : *probe->attach_points)
if (ap->provider == "tracepoint")
has_tracepoint_probes = true;
if (!has_tracepoint_probes)
return;
program->c_definitions += "#include <linux/types.h>\n";
for (ast::Probe *probe : *program->probes)
{
for (ast::AttachPoint *ap : *probe->attach_points)
{
if (ap->provider == "tracepoint")
{
std::string &category = ap->target;
std::string &event_name = ap->func;
std::string format_file_path = "/sys/kernel/debug/tracing/events/" + category + "/" + event_name + "/format";
std::ifstream format_file(format_file_path.c_str());
if (format_file.fail())
{
std::cerr << strerror(errno) << ": " << format_file_path << std::endl;
return;
}
program->c_definitions += get_tracepoint_struct(format_file, category, event_name);
}
}
}
}
std::string TracepointFormatParser::get_struct_name(const std::string &category, const std::string &event_name)
{
return "_tracepoint_" + category + "_" + event_name;
}
std::string TracepointFormatParser::parse_field(const std::string &line)
{
auto field_pos = line.find("field:");
if (field_pos == std::string::npos)
return "";
auto field_semi_pos = line.find(';', field_pos);
if (field_semi_pos == std::string::npos)
return "";
auto offset_pos = line.find("offset:", field_semi_pos);
if (offset_pos == std::string::npos)
return "";
auto offset_semi_pos = line.find(';', offset_pos);
if (offset_semi_pos == std::string::npos)
return "";
auto size_pos = line.find("size:", offset_semi_pos);
if (size_pos == std::string::npos)
return "";
auto size_semi_pos = line.find(';', size_pos);
if (size_semi_pos == std::string::npos)
return "";
int size = std::stoi(line.substr(size_pos + 5, size_semi_pos - size_pos - 5));
std::string field = line.substr(field_pos + 6, field_semi_pos - field_pos - 6);
auto field_type_end_pos = field.find_last_of("\t ");
if (field_type_end_pos == std::string::npos)
return "";
std::string field_type = field.substr(0, field_type_end_pos);
std::string field_name = field.substr(field_type_end_pos+1);
if (field_type.find("__data_loc") != std::string::npos)
{
field_type = "int";
field_name = "data_loc_" + field_name;
}
// Only adjust field types for non-arrays
if (field_name.find("[") == std::string::npos)
field_type = adjust_integer_types(field_type, size);
return " " + field_type + " " + field_name + ";\n";
}
std::string TracepointFormatParser::adjust_integer_types(const std::string &field_type, int size)
{
std::string new_type = field_type;
// Adjust integer fields to correctly sized types
if (size == 2)
{
if (new_type == "char" || new_type == "int8_t")
new_type = "s16";
if (new_type == "unsigned char" || new_type == "uint8_t")
new_type = "u16";
} else if (size == 4)
{
if (new_type == "char" || new_type == "short" ||
new_type == "int8_t" || new_type == "int16_t")
new_type = "s32";
if (new_type == "unsigned char" || new_type == "unsigned short" ||
new_type == "uint8_t" || new_type == "uint16_t")
new_type = "u32";
} else if (size == 8)
{
if (new_type == "char" || new_type == "short" || new_type == "int" ||
new_type == "int8_t" || new_type == "int16_t" ||
new_type == "int32_t")
new_type = "s64";
if (new_type == "unsigned char" || new_type == "unsigned short" ||
new_type == "unsigned int" || new_type == "uint8_t" ||
new_type == "uint16_t" || new_type == "uint32_t")
new_type = "u64";
}
return new_type;
}
std::string TracepointFormatParser::get_tracepoint_struct(std::istream &format_file, const std::string &category, const std::string &event_name)
{
std::string format_struct = "struct " + get_struct_name(category, event_name) + "\n{\n";
for (std::string line; getline(format_file, line); )
{
format_struct += parse_field(line);
}
format_struct += "};\n";
return format_struct;
}
} // namespace bpftrace
#pragma once
#include <istream>
namespace bpftrace {
namespace ast { class Program; }
class TracepointFormatParser
{
public:
static void parse(ast::Program *program);
static std::string get_struct_name(const std::string &category, const std::string &event_name);
private:
static std::string parse_field(const std::string &line);
static std::string adjust_integer_types(const std::string &field_type, int size);
protected:
static std::string get_tracepoint_struct(std::istream &format_file, const std::string &category, const std::string &event_name);
};
} // namespace bpftrace
......@@ -10,6 +10,7 @@ add_executable(bpftrace_test
main.cpp
parser.cpp
semantic_analyser.cpp
tracepoint_format_parser.cpp
utils.cpp
${CMAKE_SOURCE_DIR}/src/attached_probe.cpp
${CMAKE_SOURCE_DIR}/src/bpftrace.cpp
......@@ -19,6 +20,7 @@ add_executable(bpftrace_test
${CMAKE_SOURCE_DIR}/src/map.cpp
${CMAKE_SOURCE_DIR}/src/mapkey.cpp
${CMAKE_SOURCE_DIR}/src/printf.cpp
${CMAKE_SOURCE_DIR}/src/tracepoint_format_parser.cpp
${CMAKE_SOURCE_DIR}/src/types.cpp
)
......
......@@ -40,6 +40,7 @@ TEST(Parser, builtin_variables)
test("kprobe:f { retval }", "Program\n kprobe:f\n builtin: retval\n");
test("kprobe:f { func }", "Program\n kprobe:f\n builtin: func\n");
test("kprobe:f { name }", "Program\n kprobe:f\n builtin: name\n");
test("kprobe:f { args }", "Program\n kprobe:f\n builtin: args\n");
}
TEST(Parser, map_assign)
......
......@@ -63,6 +63,7 @@ TEST(semantic_analyser, builtin_variables)
test("kprobe:f { retval }", 0);
test("kprobe:f { func }", 0);
test("kprobe:f { name }", 0);
test("tracepoint:a:b { args }", 0);
// test("kprobe:f { fake }", 1);
}
......
#include "gtest/gtest.h"
#include "tracepoint_format_parser.h"
namespace bpftrace {
namespace test {
namespace tracepoint_format_parser {
class MockTracepointFormatParser : public TracepointFormatParser
{
public:
static std::string get_tracepoint_struct_public(std::istream &format_file, const std::string &category, const std::string &event_name)
{
return get_tracepoint_struct(format_file, category, event_name);
}
};
TEST(tracepoint_format_parser, tracepoint_struct)
{
std::string input =
"name: sys_enter_read\n"
"ID: 650\n"
"format:\n"
" field:unsigned short common_type; offset:0; size:2; signed:0;\n"
" field:unsigned char common_flags; offset:2; size:1; signed:0;\n"
" field:unsigned char common_preempt_count; offset:3; size:1; signed:0;\n"
" field:int common_pid; offset:4; size:4; signed:1;\n"
"\n"
" field:int __syscall_nr; offset:8; size:4; signed:1;\n"
" field:unsigned int fd; offset:16; size:8; signed:0;\n"
" field:char * buf; offset:24; size:8; signed:0;\n"
" field:size_t count; offset:32; size:8; signed:0;\n"
"\n"
"print fmt: \"fd: 0x%08lx, buf: 0x%08lx, count: 0x%08lx\", ((unsigned long)(REC->fd)), ((unsigned long)(REC->buf)), ((unsigned long)(REC->count))\n";
std::string expected =
"struct _tracepoint_syscalls_sys_enter_read\n"
"{\n"
" unsigned short common_type;\n"
" unsigned char common_flags;\n"
" unsigned char common_preempt_count;\n"
" int common_pid;\n"
" int __syscall_nr;\n"
" u64 fd;\n"
" char * buf;\n"
" size_t count;\n"
"};\n";
std::istringstream format_file(input);
std::string result = MockTracepointFormatParser::get_tracepoint_struct_public(format_file, "syscalls", "sys_enter_read");
EXPECT_EQ(expected, result);
}
TEST(tracepoint_format_parser, array)
{
std::string input =
" field:char char_array[8]; offset:0; size:8; signed:1;\n"
" field:int int_array[2]; offset:8; size:8; signed:1;\n";
std::string expected =
"struct _tracepoint_syscalls_sys_enter_read\n"
"{\n"
" char char_array[8];\n"
" int int_array[2];\n"
"};\n";
std::istringstream format_file(input);
std::string result = MockTracepointFormatParser::get_tracepoint_struct_public(format_file, "syscalls", "sys_enter_read");
EXPECT_EQ(expected, result);
}
TEST(tracepoint_format_parser, data_loc)
{
std::string input = " field:__data_loc char[] msg; offset:8; size:4; signed:1;";
std::string expected =
"struct _tracepoint_syscalls_sys_enter_read\n"
"{\n"
" int data_loc_msg;\n"
"};\n";
std::istringstream format_file(input);
std::string result = MockTracepointFormatParser::get_tracepoint_struct_public(format_file, "syscalls", "sys_enter_read");
EXPECT_EQ(expected, result);
}
} // namespace tracepoint_format_parser
} // namespace test
} // namespace bpftrace
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