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

Merge pull request #102 from iovisor/syscall

add system() call
parents 23a53937 3b0b5372
......@@ -430,6 +430,55 @@ void CodegenLLVM::visit(Call &call)
b_.CreateLifetimeEnd(printf_args);
expr_ = nullptr;
}
else if (call.func == "system")
{
/*
* perf event output has: uint64_t system_id, vargs
* The system_id maps to bpftrace_.system_args_, and is a way to define the
* types and offsets of each of the arguments, and share that between BPF and
* user-space for printing.
*/
ArrayType *string_type = ArrayType::get(b_.getInt8Ty(), STRING_SIZE);
std::vector<llvm::Type *> elements = { b_.getInt64Ty() }; // system ID
String &fmt = static_cast<String&>(*call.vargs->at(0));
auto &args = std::get<1>(bpftrace_.system_args_.at(system_id_));
for (Field &arg : args)
{
llvm::Type *ty = b_.GetType(arg.type);
elements.push_back(ty);
}
StructType *printf_struct = StructType::create(elements, "system_t", false);
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 system_id field
}
AllocaInst *system_args = b_.CreateAllocaBPF(printf_struct, "system_args");
b_.CreateMemSet(system_args, b_.getInt8(0), struct_size, 1);
// system_id_ has an offset to avoid be confused with printf
b_.CreateStore(b_.getInt64(system_id_ + asyncactionint(AsyncAction::syscall)), system_args);
for (int i=1; i<call.vargs->size(); i++)
{
Expression &arg = *call.vargs->at(i);
arg.accept(*this);
Value *offset = b_.CreateGEP(system_args, {b_.getInt32(0), b_.getInt32(i)});
if (arg.type.type == Type::string || arg.type.type == Type::usym)
b_.CreateMemCpy(offset, expr_, arg.type.size, 1);
else
b_.CreateStore(expr_, offset);
}
system_id_++;
b_.CreatePerfEventOutput(ctx_, system_args, struct_size);
b_.CreateLifetimeEnd(system_args);
expr_ = nullptr;
}
else if (call.func == "exit")
{
/*
......
......@@ -74,6 +74,7 @@ private:
std::map<std::string, Value *> variables_;
int printf_id_ = 0;
int time_id_ = 0;
int system_id_ = 0;
};
} // namespace ast
......
......@@ -232,7 +232,7 @@ void SemanticAnalyser::visit(Call &call)
}
call.type = SizedType(Type::integer, 8);
}
else if (call.func == "printf") {
else if (call.func == "printf" || call.func == "system") {
check_assignment(call, false, false);
if (check_varargs(call, 1, 7)) {
check_arg(call, Type::string, 0, true);
......@@ -245,7 +245,10 @@ void SemanticAnalyser::visit(Call &call)
}
err_ << verify_format_string(fmt.str, args);
bpftrace_.printf_args_.push_back(std::make_tuple(fmt.str, args));
if (call.func == "printf")
bpftrace_.printf_args_.push_back(std::make_tuple(fmt.str, args));
else
bpftrace_.system_args_.push_back(std::make_tuple(fmt.str, args));
}
}
......
......@@ -208,10 +208,92 @@ void perf_event_printer(void *cb_cookie, void *data, int size)
printf("\n");
return;
}
else if ( printf_id >= asyncactionint(AsyncAction::syscall))
{
auto id = printf_id - asyncactionint(AsyncAction::syscall);
auto fmt = std::get<0>(bpftrace->system_args_[id]).c_str();
auto args = std::get<1>(bpftrace->system_args_[id]);
std::vector<uint64_t> arg_values = bpftrace->get_arg_values(args, arg_data);
char buffer [255];
switch (args.size())
{
case 0:
system(fmt);
break;
case 1:
snprintf(buffer, 255, fmt, arg_values.at(0));
system(buffer);
break;
case 2:
snprintf(buffer, 255, fmt, arg_values.at(0), arg_values.at(1));
system(buffer);
break;
case 3:
snprintf(buffer, 255, fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2));
system(buffer);
break;
case 4:
snprintf(buffer, 255, fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2),
arg_values.at(3));
system(buffer);
break;
case 5:
snprintf(buffer, 255, fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2),
arg_values.at(3), arg_values.at(4));
system(buffer);
break;
case 6:
snprintf(buffer, 255, fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2),
arg_values.at(3), arg_values.at(4), arg_values.at(5));
system(buffer);
break;
default:
abort();
}
return;
}
// printf
auto fmt = std::get<0>(bpftrace->printf_args_[printf_id]).c_str();
auto args = std::get<1>(bpftrace->printf_args_[printf_id]);
std::vector<uint64_t> arg_values = bpftrace->get_arg_values(args, arg_data);
switch (args.size())
{
case 0:
printf(fmt);
break;
case 1:
printf(fmt, arg_values.at(0));
break;
case 2:
printf(fmt, arg_values.at(0), arg_values.at(1));
break;
case 3:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2));
break;
case 4:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2),
arg_values.at(3));
break;
case 5:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2),
arg_values.at(3), arg_values.at(4));
break;
case 6:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2),
arg_values.at(3), arg_values.at(4), arg_values.at(5));
break;
default:
abort();
}
}
std::vector<uint64_t> BPFtrace::get_arg_values(std::vector<Field> args, uint8_t* arg_data)
{
std::vector<uint64_t> arg_values;
std::vector<std::unique_ptr<char>> resolved_symbols;
std::vector<std::unique_ptr<char>> resolved_usernames;
......@@ -245,21 +327,21 @@ void perf_event_printer(void *cb_cookie, void *data, int size)
break;
case Type::sym:
resolved_symbols.emplace_back(strdup(
bpftrace->resolve_sym(*(uint64_t*)(arg_data+arg.offset)).c_str()));
resolve_sym(*(uint64_t*)(arg_data+arg.offset)).c_str()));
arg_values.push_back((uint64_t)resolved_symbols.back().get());
break;
case Type::usym:
resolved_symbols.emplace_back(strdup(
bpftrace->resolve_usym(*(uint64_t*)(arg_data+arg.offset), *(uint64_t*)(arg_data+arg.offset + 8)).c_str()));
resolve_usym(*(uint64_t*)(arg_data+arg.offset), *(uint64_t*)(arg_data+arg.offset + 8)).c_str()));
arg_values.push_back((uint64_t)resolved_symbols.back().get());
break;
case Type::username:
resolved_usernames.emplace_back(strdup(
bpftrace->resolve_uid(*(uint64_t*)(arg_data+arg.offset)).c_str()));
resolve_uid(*(uint64_t*)(arg_data+arg.offset)).c_str()));
arg_values.push_back((uint64_t)resolved_usernames.back().get());
break;
case Type::name:
name = strdup(bpftrace->resolve_name(*(uint64_t*)(arg_data+arg.offset)).c_str());
name = strdup(resolve_name(*(uint64_t*)(arg_data+arg.offset)).c_str());
arg_values.push_back((uint64_t)name);
break;
default:
......@@ -267,35 +349,7 @@ void perf_event_printer(void *cb_cookie, void *data, int size)
}
}
switch (args.size())
{
case 0:
printf(fmt);
break;
case 1:
printf(fmt, arg_values.at(0));
break;
case 2:
printf(fmt, arg_values.at(0), arg_values.at(1));
break;
case 3:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2));
break;
case 4:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2),
arg_values.at(3));
break;
case 5:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2),
arg_values.at(3), arg_values.at(4));
break;
case 6:
printf(fmt, arg_values.at(0), arg_values.at(1), arg_values.at(2),
arg_values.at(3), arg_values.at(4), arg_values.at(5));
break;
default:
abort();
}
return arg_values;
}
void perf_event_lost(void *cb_cookie, uint64_t lost)
......
......@@ -41,11 +41,13 @@ public:
uint64_t resolve_kname(const char *name);
uint64_t resolve_uname(const char *name, const char *path);
std::string resolve_name(uint64_t name_id);
std::vector<uint64_t> get_arg_values(std::vector<Field> args, uint8_t* arg_data);
int pid_;
std::map<std::string, std::unique_ptr<IMap>> maps_;
std::map<std::string, Struct> structs_;
std::vector<std::tuple<std::string, std::vector<Field>>> printf_args_;
std::vector<std::tuple<std::string, std::vector<Field>>> system_args_;
std::vector<std::string> time_args_;
std::unique_ptr<IMap> stackid_map_;
std::unique_ptr<IMap> join_map_;
......
......@@ -90,7 +90,8 @@ public:
enum class AsyncAction
{
// printf reserves 0-9999 for printf_ids
exit = 10000,
syscall = 10000, // system reserves 10000-19999 for printf_ids
exit = 20000,
print,
clear,
zero,
......
......@@ -1742,6 +1742,41 @@ attributes #1 = { argmemonly nounwind }
)EXPECTED");
}
TEST(codegen, call_system)
{
test(" kprobe:f { system(\"echo %d\", 100) }",
R"EXPECTED(%system_t = type { i64, i64 }
; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8*) local_unnamed_addr section "s_kprobe:f" {
entry:
%system_args = alloca %system_t, align 8
%1 = bitcast %system_t* %system_args to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
store i64 10000, %system_t* %system_args, align 8
%2 = getelementptr inbounds %system_t, %system_t* %system_args, i64 0, i32 1
store i64 100, i64* %2, align 8
%pseudo = tail call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%get_cpu_id = tail 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, %system_t* nonnull %system_args, i64 16)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
ret i64 0
}
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
)EXPECTED");
}
TEST(codegen, call_exit)
{
test("kprobe:f { exit() }",
......@@ -1757,7 +1792,7 @@ entry:
%perfdata = alloca [8 x i8], align 8
%1 = getelementptr inbounds [8 x i8], [8 x i8]* %perfdata, i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
store i64 10000, [8 x i8]* %perfdata, align 8
store i64 20000, [8 x i8]* %perfdata, align 8
%pseudo = tail call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%get_cpu_id = tail 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, [8 x i8]* nonnull %perfdata, i64 8)
......@@ -1808,7 +1843,7 @@ entry:
%perfdata = alloca [27 x i8], align 8
%1 = getelementptr inbounds [27 x i8], [27 x i8]* %perfdata, i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
store i64 10001, [27 x i8]* %perfdata, align 8
store i64 20001, [27 x i8]* %perfdata, align 8
%2 = getelementptr inbounds [27 x i8], [27 x i8]* %perfdata, i64 0, i64 8
%str.sroa.0.0..sroa_idx = getelementptr inbounds [27 x i8], [27 x i8]* %perfdata, i64 0, i64 24
call void @llvm.memset.p0i8.i64(i8* nonnull %2, i8 0, i64 16, i32 8, i1 false)
......@@ -1867,7 +1902,7 @@ entry:
%perfdata = alloca [11 x i8], align 8
%1 = getelementptr inbounds [11 x i8], [11 x i8]* %perfdata, i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
store i64 10002, [11 x i8]* %perfdata, align 8
store i64 20002, [11 x i8]* %perfdata, align 8
%str.sroa.0.0..sroa_idx = getelementptr inbounds [11 x i8], [11 x i8]* %perfdata, i64 0, i64 8
store i8 64, i8* %str.sroa.0.0..sroa_idx, align 8
%str.sroa.4.0..sroa_idx = getelementptr inbounds [11 x i8], [11 x i8]* %perfdata, i64 0, i64 9
......@@ -1921,7 +1956,7 @@ entry:
%perfdata = alloca [11 x i8], align 8
%1 = getelementptr inbounds [11 x i8], [11 x i8]* %perfdata, i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
store i64 10003, [11 x i8]* %perfdata, align 8
store i64 20003, [11 x i8]* %perfdata, align 8
%str.sroa.0.0..sroa_idx = getelementptr inbounds [11 x i8], [11 x i8]* %perfdata, i64 0, i64 8
store i8 64, i8* %str.sroa.0.0..sroa_idx, align 8
%str.sroa.4.0..sroa_idx = getelementptr inbounds [11 x i8], [11 x i8]* %perfdata, i64 0, i64 9
......@@ -1955,7 +1990,7 @@ entry:
%perfdata = alloca [16 x i8], align 8
%1 = getelementptr inbounds [16 x i8], [16 x i8]* %perfdata, i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
store i64 10004, [16 x i8]* %perfdata, align 8
store i64 20004, [16 x i8]* %perfdata, align 8
%2 = getelementptr inbounds [16 x i8], [16 x i8]* %perfdata, i64 0, i64 8
store i64 0, i8* %2, align 8
%pseudo = tail call i64 @llvm.bpf.pseudo(i64 1, i64 1)
......
......@@ -83,6 +83,7 @@ TEST(semantic_analyser, builtin_functions)
test("kprobe:f { exit() }", 0);
test("kprobe:f { str(0xffff) }", 0);
test("kprobe:f { printf(\"hello\\n\") }", 0);
test("kprobe:f { system(\"ls\\n\") }", 0);
test("kprobe:f { join(0) }", 0);
test("kprobe:f { sym(0xffff) }", 0);
test("kprobe:f { usym(0xffff) }", 0);
......@@ -357,6 +358,14 @@ TEST(semantic_analyser, printf)
test("kprobe:f { $x = printf(\"hi\") }", 1);
}
TEST(semantic_analyser, system)
{
test("kprobe:f { system(\"ls\") }", 0);
test("kprobe:f { system(1234) }", 1);
test("kprobe:f { system() }", 1);
test("kprobe:f { $fmt = \"mystring\"; system($fmt) }", 1);
}
TEST(semantic_analyser, printf_format_int)
{
test("kprobe:f { printf(\"int: %d\", 1234) }", 0);
......
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