Commit 61690d65 authored by Alastair Robertson's avatar Alastair Robertson

Printf: Work with non 64-bit integers

parent ebc407e7
...@@ -177,19 +177,26 @@ void CodegenLLVM::visit(Call &call) ...@@ -177,19 +177,26 @@ void CodegenLLVM::visit(Call &call)
std::vector<llvm::Type *> elements = { b_.getInt64Ty() }; // printf ID std::vector<llvm::Type *> elements = { b_.getInt64Ty() }; // printf ID
String &fmt = static_cast<String&>(*call.vargs->at(0)); String &fmt = static_cast<String&>(*call.vargs->at(0));
static int printf_id = 0; auto &args = std::get<1>(bpftrace_.printf_args_.at(printf_id_));
auto args = std::get<1>(bpftrace_.printf_args_.at(printf_id)); for (Field &arg : args)
for (SizedType &t : args)
{ {
llvm::Type *ty = b_.GetType(t); llvm::Type *ty = b_.GetType(arg.type);
elements.push_back(ty); elements.push_back(ty);
} }
StructType *printf_struct = StructType::create(elements, "printf_t", true); StructType *printf_struct = StructType::create(elements, "printf_t", false);
int struct_size = layout_.getTypeAllocSize(printf_struct); int struct_size = layout_.getTypeAllocSize(printf_struct);
auto *struct_layout = layout_.getStructLayout(printf_struct);
for (int i=0; i<args.size(); i++)
{
Field &arg = args[i];
arg.offset = struct_layout->getElementOffset(i+1); // +1 for the printf_id field
}
AllocaInst *printf_args = b_.CreateAllocaBPF(printf_struct, "printf_args"); AllocaInst *printf_args = b_.CreateAllocaBPF(printf_struct, "printf_args");
b_.CreateMemSet(printf_args, b_.getInt8(0), struct_size, 1);
b_.CreateStore(b_.getInt64(printf_id), printf_args); b_.CreateStore(b_.getInt64(printf_id_), printf_args);
for (int i=1; i<call.vargs->size(); i++) for (int i=1; i<call.vargs->size(); i++)
{ {
Expression &arg = *call.vargs->at(i); Expression &arg = *call.vargs->at(i);
...@@ -201,7 +208,7 @@ void CodegenLLVM::visit(Call &call) ...@@ -201,7 +208,7 @@ void CodegenLLVM::visit(Call &call)
b_.CreateStore(expr_, offset); b_.CreateStore(expr_, offset);
} }
printf_id++; printf_id_++;
b_.CreatePerfEventOutput(ctx_, printf_args, struct_size); b_.CreatePerfEventOutput(ctx_, printf_args, struct_size);
b_.CreateLifetimeEnd(printf_args); b_.CreateLifetimeEnd(printf_args);
expr_ = nullptr; expr_ = nullptr;
......
...@@ -65,6 +65,7 @@ private: ...@@ -65,6 +65,7 @@ private:
BPFtrace &bpftrace_; BPFtrace &bpftrace_;
std::map<std::string, Value *> variables_; std::map<std::string, Value *> variables_;
int printf_id_ = 0;
}; };
} // namespace ast } // namespace ast
......
...@@ -137,9 +137,9 @@ void SemanticAnalyser::visit(Call &call) ...@@ -137,9 +137,9 @@ void SemanticAnalyser::visit(Call &call)
if (is_final_pass()) { if (is_final_pass()) {
auto &fmt_arg = *call.vargs->at(0); auto &fmt_arg = *call.vargs->at(0);
String &fmt = static_cast<String&>(fmt_arg); String &fmt = static_cast<String&>(fmt_arg);
std::vector<SizedType> args; std::vector<Field> args;
for (auto iter = call.vargs->begin()+1; iter != call.vargs->end(); iter++) { for (auto iter = call.vargs->begin()+1; iter != call.vargs->end(); iter++) {
args.push_back((*iter)->type); args.push_back({ .type = (*iter)->type });
} }
err_ << verify_format_string(fmt.str, args); err_ << verify_format_string(fmt.str, args);
......
...@@ -128,7 +128,7 @@ void perf_event_printer(void *cb_cookie, void *data, int size) ...@@ -128,7 +128,7 @@ void perf_event_printer(void *cb_cookie, void *data, int size)
{ {
auto bpftrace = static_cast<BPFtrace*>(cb_cookie); auto bpftrace = static_cast<BPFtrace*>(cb_cookie);
auto printf_id = *static_cast<uint64_t*>(data); auto printf_id = *static_cast<uint64_t*>(data);
auto arg_data = static_cast<uint8_t*>(data) + sizeof(uint64_t); auto arg_data = static_cast<uint8_t*>(data);
auto fmt = std::get<0>(bpftrace->printf_args_[printf_id]).c_str(); auto fmt = std::get<0>(bpftrace->printf_args_[printf_id]).c_str();
auto args = std::get<1>(bpftrace->printf_args_[printf_id]); auto args = std::get<1>(bpftrace->printf_args_[printf_id]);
...@@ -136,28 +136,43 @@ void perf_event_printer(void *cb_cookie, void *data, int size) ...@@ -136,28 +136,43 @@ void perf_event_printer(void *cb_cookie, void *data, int size)
std::vector<std::unique_ptr<char>> resolved_symbols; std::vector<std::unique_ptr<char>> resolved_symbols;
for (auto arg : args) for (auto arg : args)
{ {
switch (arg.type) switch (arg.type.type)
{ {
case Type::integer: case Type::integer:
arg_values.push_back(*(uint64_t*)arg_data); switch (arg.type.size)
{
case 8:
arg_values.push_back(*(uint64_t*)(arg_data+arg.offset));
break;
case 4:
arg_values.push_back(*(uint32_t*)(arg_data+arg.offset));
break;
case 2:
arg_values.push_back(*(uint16_t*)(arg_data+arg.offset));
break;
case 1:
arg_values.push_back(*(uint8_t*)(arg_data+arg.offset));
break;
default:
abort();
}
break; break;
case Type::string: case Type::string:
arg_values.push_back((uint64_t)arg_data); arg_values.push_back((uint64_t)(arg_data+arg.offset));
break; break;
case Type::sym: case Type::sym:
resolved_symbols.emplace_back(strdup( resolved_symbols.emplace_back(strdup(
bpftrace->resolve_sym(*(uint64_t*)arg_data).c_str())); bpftrace->resolve_sym(*(uint64_t*)(arg_data+arg.offset)).c_str()));
arg_values.push_back((uint64_t)resolved_symbols.back().get()); arg_values.push_back((uint64_t)resolved_symbols.back().get());
break; break;
case Type::usym: case Type::usym:
resolved_symbols.emplace_back(strdup( resolved_symbols.emplace_back(strdup(
bpftrace->resolve_usym(*(uint64_t*)arg_data).c_str())); bpftrace->resolve_usym(*(uint64_t*)(arg_data+arg.offset)).c_str()));
arg_values.push_back((uint64_t)resolved_symbols.back().get()); arg_values.push_back((uint64_t)resolved_symbols.back().get());
break; break;
default: default:
abort(); abort();
} }
arg_data += arg.size;
} }
switch (args.size()) switch (args.size())
......
...@@ -33,7 +33,7 @@ public: ...@@ -33,7 +33,7 @@ public:
std::map<std::string, std::unique_ptr<IMap>> maps_; std::map<std::string, std::unique_ptr<IMap>> maps_;
std::map<std::string, Struct> structs_; std::map<std::string, Struct> structs_;
std::vector<std::tuple<std::string, std::vector<SizedType>>> printf_args_; std::vector<std::tuple<std::string, std::vector<Field>>> printf_args_;
std::unique_ptr<IMap> stackid_map_; std::unique_ptr<IMap> stackid_map_;
std::unique_ptr<IMap> perf_event_map_; std::unique_ptr<IMap> perf_event_map_;
......
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
#include "printf.h" #include "printf.h"
#include "printf_format_types.h" #include "printf_format_types.h"
#include "struct.h"
namespace bpftrace { namespace bpftrace {
std::string verify_format_string(const std::string &fmt, std::vector<SizedType> args) std::string verify_format_string(const std::string &fmt, std::vector<Field> args)
{ {
std::stringstream message; std::stringstream message;
const std::regex re("%-?[0-9]*[a-zA-Z]+"); const std::regex re("%-?[0-9]*[a-zA-Z]+");
...@@ -31,7 +32,7 @@ std::string verify_format_string(const std::string &fmt, std::vector<SizedType> ...@@ -31,7 +32,7 @@ std::string verify_format_string(const std::string &fmt, std::vector<SizedType>
auto token_iter = tokens_begin; auto token_iter = tokens_begin;
for (int i=0; i<num_args; i++, token_iter++) for (int i=0; i<num_args; i++, token_iter++)
{ {
Type arg_type = args.at(i).type; Type arg_type = args.at(i).type.type;
if (arg_type == Type::sym || arg_type == Type::usym) if (arg_type == Type::sym || arg_type == Type::usym)
arg_type = Type::string; // Symbols should be printed as strings arg_type = Type::string; // Symbols should be printed as strings
int offset = 1; int offset = 1;
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
namespace bpftrace { namespace bpftrace {
std::string verify_format_string(const std::string &fmt, std::vector<SizedType> args); struct Field;
std::string verify_format_string(const std::string &fmt, std::vector<Field> args);
} // namespace bpftrace } // namespace bpftrace
...@@ -24,12 +24,44 @@ TEST(codegen, populate_sections) ...@@ -24,12 +24,44 @@ TEST(codegen, populate_sections)
ASSERT_EQ(semantics.analyse(), 0); ASSERT_EQ(semantics.analyse(), 0);
std::stringstream out; std::stringstream out;
ast::CodegenLLVM codegen(driver.root_, bpftrace); ast::CodegenLLVM codegen(driver.root_, bpftrace);
auto bpforc = codegen.compile(true, out); auto bpforc = codegen.compile();
// Check sections are populated // Check sections are populated
ASSERT_EQ(bpforc->sections_.size(), 2); EXPECT_EQ(bpforc->sections_.size(), 2);
ASSERT_EQ(bpforc->sections_.count("s_kprobe:foo"), 1); EXPECT_EQ(bpforc->sections_.count("s_kprobe:foo"), 1);
ASSERT_EQ(bpforc->sections_.count("s_kprobe:bar"), 1); EXPECT_EQ(bpforc->sections_.count("s_kprobe:bar"), 1);
}
TEST(codegen, printf_offsets)
{
BPFtrace bpftrace;
Driver driver;
ASSERT_EQ(driver.parse_str("struct Foo { char c; int i; } kprobe:f { $foo = (Foo*)0; printf(\"%c %u\\n\", $foo->c, $foo->i) }"), 0);
ClangParser clang;
clang.parse(driver.root_, bpftrace.structs_);
ast::SemanticAnalyser semantics(driver.root_, bpftrace);
ASSERT_EQ(semantics.analyse(), 0);
ASSERT_EQ(semantics.create_maps(true), 0);
std::stringstream out;
ast::CodegenLLVM codegen(driver.root_, bpftrace);
auto bpforc = codegen.compile();
EXPECT_EQ(bpftrace.printf_args_.size(), 1);
auto &fmt = std::get<0>(bpftrace.printf_args_[0]);
auto &args = std::get<1>(bpftrace.printf_args_[0]);
EXPECT_EQ(fmt, "%c %u\n");
EXPECT_EQ(args.size(), 2);
EXPECT_EQ(args[0].type.type, Type::integer);
EXPECT_EQ(args[0].type.size, 1);
EXPECT_EQ(args[0].offset, 8);
EXPECT_EQ(args[1].type.type, Type::integer);
EXPECT_EQ(args[1].type.size, 4);
EXPECT_EQ(args[1].offset, 12);
} }
std::string header = R"HEAD(; ModuleID = 'bpftrace' std::string header = R"HEAD(; ModuleID = 'bpftrace'
...@@ -921,9 +953,9 @@ attributes #1 = { argmemonly nounwind } ...@@ -921,9 +953,9 @@ attributes #1 = { argmemonly nounwind }
TEST(codegen, call_printf) TEST(codegen, call_printf)
{ {
test("kprobe:f { printf(\"hello\\n\") }", test("struct Foo { char c; long l; } kprobe:f { $foo = (Foo*)0; printf(\"%c %lu\\n\", $foo->c, $foo->l) }",
R"EXPECTED(%printf_t = type { i64 } R"EXPECTED(%printf_t = type { i64, i8, i64 }
; Function Attrs: nounwind ; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0 declare i64 @llvm.bpf.pseudo(i64, i64) #0
...@@ -933,17 +965,36 @@ declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1 ...@@ -933,17 +965,36 @@ declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8*) local_unnamed_addr section "s_kprobe:f" { define i64 @"kprobe:f"(i8*) local_unnamed_addr section "s_kprobe:f" {
entry: entry:
%Foo.l = alloca i64, align 8
%Foo.c = alloca i8, align 1
%printf_args = alloca %printf_t, align 8 %printf_args = alloca %printf_t, align 8
%1 = bitcast %printf_t* %printf_args to i8* %1 = bitcast %printf_t* %printf_args to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1) call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
store i64 0, %printf_t* %printf_args, align 8 %2 = bitcast %printf_t* %printf_args to i8*
%pseudo = tail call i64 @llvm.bpf.pseudo(i64 1, i64 1) call void @llvm.memset.p0i8.i64(i8* nonnull %2, i8 0, i64 16, i32 8, i1 false)
%get_cpu_id = tail call i64 inttoptr (i64 8 to i64 ()*)() call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %Foo.c)
%perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i8*, i64, i8*, i64)*)(i8* %0, i64 %pseudo, i64 %get_cpu_id, %printf_t* nonnull %printf_args, i64 8) %probe_read = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)(i8* nonnull %Foo.c, i64 1, i64 0)
%3 = load i8, i8* %Foo.c, align 1
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %Foo.c)
%4 = getelementptr inbounds %printf_t, %printf_t* %printf_args, i64 0, i32 1
store i8 %3, i8* %4, align 8
%5 = bitcast i64* %Foo.l to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %5)
%probe_read1 = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)(i64* nonnull %Foo.l, i64 8, i64 8)
%6 = load i64, i64* %Foo.l, align 8
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %5)
%7 = getelementptr inbounds %printf_t, %printf_t* %printf_args, i64 0, i32 2
store i64 %6, i64* %7, align 8
%pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%get_cpu_id = call i64 inttoptr (i64 8 to i64 ()*)()
%perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i8*, i64, i8*, i64)*)(i8* %0, i64 %pseudo, i64 %get_cpu_id, %printf_t* nonnull %printf_args, i64 20)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1) call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
ret i64 0 ret i64 0
} }
; Function Attrs: argmemonly nounwind
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1) #1
; Function Attrs: argmemonly nounwind ; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1 declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
......
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