Commit 6f5b3fa2 authored by Brendan Gregg's avatar Brendan Gregg

add ustack and usym() functionality

parent c1ce7156
......@@ -37,7 +37,10 @@ void CodegenLLVM::visit(Builtin &builtin)
}
else if (builtin.ident == "stack" || builtin.ident == "ustack")
{
expr_ = b_.CreateGetStackId(ctx_, builtin.ident == "ustack");
// pack uint64_t with: (uint32_t)stack_id, (uint32_t)pid
Value *pidhigh = b_.CreateShl(b_.CreateGetPidTgid(), 32);
Value *stackid = b_.CreateGetStackId(ctx_, builtin.ident == "ustack");
expr_ = b_.CreateOr(stackid, pidhigh);
}
else if (builtin.ident == "pid" || builtin.ident == "tid")
{
......@@ -321,11 +324,24 @@ void CodegenLLVM::visit(Call &call)
b_.SetInsertPoint(zero);
expr_ = nullptr;
}
else if (call.func == "sym" || call.func == "usym")
else if (call.func == "sym")
{
// We want expr_ to just pass through from the child node - don't set it here
call.vargs->front()->accept(*this);
}
else if (call.func == "usym")
{
// store uint64_t[2] with: [0]: (uint64_t)addr, [1]: (uint64_t)pid
AllocaInst *buf = b_.CreateAllocaBPF(call.type, "usym");
b_.CreateMemSet(buf, b_.getInt8(0), call.type.size, 1);
Value *pid = b_.CreateLShr(b_.CreateGetPidTgid(), 32);
Value *addr_offset = b_.CreateGEP(buf, b_.getInt64(0));
Value *pid_offset = b_.CreateGEP(buf, {b_.getInt64(0), b_.getInt64(8)});
call.vargs->front()->accept(*this);
b_.CreateStore(expr_, addr_offset);
b_.CreateStore(pid, pid_offset);
expr_ = buf;
}
else if (call.func == "reg")
{
auto &reg_name = static_cast<String&>(*call.vargs->at(0)).str;
......@@ -370,7 +386,7 @@ void CodegenLLVM::visit(Call &call)
Expression &arg = *call.vargs->at(i);
arg.accept(*this);
Value *offset = b_.CreateGEP(printf_args, {b_.getInt32(0), b_.getInt32(i)});
if (arg.type.type == Type::string)
if (arg.type.type == Type::string || arg.type.type == Type::usym)
b_.CreateMemCpy(offset, expr_, arg.type.size, 1);
else
b_.CreateStore(expr_, offset);
......@@ -807,7 +823,7 @@ AllocaInst *CodegenLLVM::getMapKey(Map &map)
for (Expression *expr : *map.vargs) {
expr->accept(*this);
Value *offset_val = b_.CreateGEP(key, {b_.getInt64(0), b_.getInt64(offset)});
if (expr->type.type == Type::string)
if (expr->type.type == Type::string || expr->type.type == Type::usym)
b_.CreateMemCpy(offset_val, expr_, expr->type.size, 1);
else
b_.CreateStore(expr_, offset_val);
......@@ -837,7 +853,7 @@ AllocaInst *CodegenLLVM::getHistMapKey(Map &map, Value *log2)
for (Expression *expr : *map.vargs) {
expr->accept(*this);
Value *offset_val = b_.CreateGEP(key, {b_.getInt64(0), b_.getInt64(offset)});
if (expr->type.type == Type::string)
if (expr->type.type == Type::string || expr->type.type == Type::usym)
b_.CreateMemCpy(offset_val, expr_, expr->type.size, 1);
else
b_.CreateStore(expr_, offset_val);
......
......@@ -25,7 +25,7 @@ IRBuilderBPF::IRBuilderBPF(LLVMContext &context,
&module_);
}
AllocaInst *IRBuilderBPF::CreateAllocaBPF(llvm::Type *ty, const std::string &name)
AllocaInst *IRBuilderBPF::CreateAllocaBPF(llvm::Type *ty, llvm::Value *arraysize, const std::string &name)
{
Function *parent = GetInsertBlock()->getParent();
BasicBlock &entry_block = parent->getEntryBlock();
......@@ -35,17 +35,28 @@ AllocaInst *IRBuilderBPF::CreateAllocaBPF(llvm::Type *ty, const std::string &nam
SetInsertPoint(&entry_block);
else
SetInsertPoint(&entry_block.front());
AllocaInst *alloca = CreateAlloca(ty, nullptr, name); // TODO dodgy
AllocaInst *alloca = CreateAlloca(ty, arraysize, name); // TODO dodgy
restoreIP(ip);
CreateLifetimeStart(alloca);
return alloca;
}
AllocaInst *IRBuilderBPF::CreateAllocaBPF(llvm::Type *ty, const std::string &name)
{
return CreateAllocaBPF(ty, nullptr, name);
}
AllocaInst *IRBuilderBPF::CreateAllocaBPF(const SizedType &stype, const std::string &name)
{
llvm::Type *ty = GetType(stype);
return CreateAllocaBPF(ty, name);
return CreateAllocaBPF(ty, nullptr, name);
}
AllocaInst *IRBuilderBPF::CreateAllocaBPF(const SizedType &stype, llvm::Value *arraysize, const std::string &name)
{
llvm::Type *ty = GetType(stype);
return CreateAllocaBPF(ty, arraysize, name);
}
AllocaInst *IRBuilderBPF::CreateAllocaMapKey(int bytes, const std::string &name)
......@@ -57,7 +68,7 @@ AllocaInst *IRBuilderBPF::CreateAllocaMapKey(int bytes, const std::string &name)
llvm::Type *IRBuilderBPF::GetType(const SizedType &stype)
{
llvm::Type *ty;
if (stype.type == Type::string || stype.type == Type::cast)
if (stype.type == Type::string || stype.type == Type::cast || stype.type == Type::usym)
{
ty = ArrayType::get(getInt8Ty(), stype.size);
}
......
......@@ -20,6 +20,8 @@ public:
AllocaInst *CreateAllocaBPF(llvm::Type *ty, const std::string &name="");
AllocaInst *CreateAllocaBPF(const SizedType &stype, const std::string &name="");
AllocaInst *CreateAllocaBPF(llvm::Type *ty, llvm::Value *arraysize, const std::string &name="");
AllocaInst *CreateAllocaBPF(const SizedType &stype, llvm::Value *arraysize, const std::string &name="");
AllocaInst *CreateAllocaMapKey(int bytes, const std::string &name="");
llvm::Type *GetType(const SizedType &stype);
CallInst *CreateBpfPseudoCall(int mapfd);
......
......@@ -55,7 +55,7 @@ void SemanticAnalyser::visit(Builtin &builtin)
type == ProbeType::tracepoint)
builtin.type = SizedType(Type::sym, 8);
else if (type == ProbeType::uprobe || type == ProbeType::uretprobe)
builtin.type = SizedType(Type::usym, 8);
builtin.type = SizedType(Type::usym, 16);
else
err_ << "The func builtin can not be used with '" << attach_point->provider
<< "' probes" << std::endl;
......@@ -182,7 +182,7 @@ void SemanticAnalyser::visit(Call &call)
else if (call.func == "sym")
call.type = SizedType(Type::sym, 8);
else if (call.func == "usym")
call.type = SizedType(Type::usym, 8);
call.type = SizedType(Type::usym, 16);
}
else if (call.func == "join") {
check_assignment(call, false, false);
......
......@@ -224,7 +224,7 @@ void perf_event_printer(void *cb_cookie, void *data, int size)
break;
case Type::usym:
resolved_symbols.emplace_back(strdup(
bpftrace->resolve_usym(*(uint64_t*)arg_data).c_str()));
bpftrace->resolve_usym(*(uint64_t*)arg_data, *(uint64_t*)(arg_data + 8)).c_str()));
arg_values.push_back((uint64_t)resolved_symbols.back().get());
break;
case Type::name:
......@@ -630,13 +630,13 @@ int BPFtrace::print_map(IMap &map, uint32_t top, uint32_t div)
std::cout << map.name_ << map.key_.argument_value_list(*this, key) << ": ";
if (map.type_.type == Type::stack)
std::cout << get_stack(*(uint32_t*)value.data(), false, 8);
std::cout << get_stack(*(uint64_t*)value.data(), false, 8);
else if (map.type_.type == Type::ustack)
std::cout << get_stack(*(uint32_t*)value.data(), true, 8);
std::cout << get_stack(*(uint64_t*)value.data(), true, 8);
else if (map.type_.type == Type::sym)
std::cout << resolve_sym(*(uintptr_t*)value.data());
else if (map.type_.type == Type::usym)
std::cout << resolve_usym(*(uintptr_t*)value.data());
std::cout << resolve_usym(*(uintptr_t*)value.data(), *(uint64_t*)(value.data() + 8));
else if (map.type_.type == Type::string)
std::cout << value.data() << std::endl;
else if (map.type_.type == Type::count || map.type_.type == Type::sum)
......@@ -1014,13 +1014,15 @@ std::vector<uint8_t> BPFtrace::find_empty_key(IMap &map, size_t size) const
throw std::runtime_error("Could not find empty key");
}
std::string BPFtrace::get_stack(uint32_t stackid, bool ustack, int indent)
std::string BPFtrace::get_stack(uint64_t stackidpid, bool ustack, int indent)
{
uint32_t stackid = stackidpid & 0xffffffff;
int pid = stackidpid >> 32;
auto stack_trace = std::vector<uint64_t>(MAX_STACK_SIZE);
int err = bpf_lookup_elem(stackid_map_->mapfd_, &stackid, stack_trace.data());
if (err)
{
std::cerr << "Error looking up stack id " << stackid << ": " << err << std::endl;
std::cerr << "Error looking up stack id " << stackid << " (pid " << pid << "): " << err << std::endl;
return "";
}
......@@ -1035,7 +1037,7 @@ std::string BPFtrace::get_stack(uint32_t stackid, bool ustack, int indent)
if (!ustack)
stack << padding << resolve_sym(addr, true) << std::endl;
else
stack << padding << resolve_usym(addr) << std::endl;
stack << padding << resolve_usym(addr, pid, true) << std::endl;
}
return stack.str();
......@@ -1060,11 +1062,42 @@ std::string BPFtrace::resolve_sym(uintptr_t addr, bool show_offset)
return symbol.str();
}
std::string BPFtrace::resolve_usym(uintptr_t addr) const
std::string BPFtrace::resolve_usym(uintptr_t addr, int pid, bool show_offset)
{
// TODO
struct bcc_symbol sym;
std::ostringstream symbol;
symbol << (void*)addr;
struct bcc_symbol_option symopts;
void *psyms;
// TODO: deal with these:
symopts = {.use_debug_file = false,
.check_debug_file_crc = false,
.use_symbol_type = BCC_SYM_ALL_TYPES};
if (pid_sym_.find(pid) == pid_sym_.end())
{
// not cached, create new ProcSyms cache
psyms = bcc_symcache_new(pid, &symopts);
pid_sym_[pid] = psyms;
}
else
{
psyms = pid_sym_[pid];
}
if (((ProcSyms *)psyms)->resolve_addr(addr, &sym))
{
symbol << sym.name;
if (show_offset)
symbol << "+" << sym.offset;
}
else
{
symbol << (void*)addr;
}
// TODO: deal with process exit and clearing its psyms entry
return symbol.str();
}
......
......@@ -34,9 +34,9 @@ public:
int print_map_ident(const std::string &ident, uint32_t top, uint32_t div);
int clear_map_ident(const std::string &ident);
int zero_map_ident(const std::string &ident);
std::string get_stack(uint32_t stackid, bool ustack, int indent=0);
std::string get_stack(uint64_t stackidpid, bool ustack, int indent=0);
std::string resolve_sym(uintptr_t addr, bool show_offset=false);
std::string resolve_usym(uintptr_t addr) const;
std::string resolve_usym(uintptr_t addr, int pid, bool show_offset=false);
std::string resolve_name(uint64_t name_id);
int pid_;
......@@ -63,6 +63,7 @@ private:
std::vector<std::unique_ptr<AttachedProbe>> attached_probes_;
std::vector<std::unique_ptr<AttachedProbe>> special_attached_probes_;
KSyms ksyms_;
std::map<int, void *> pid_sym_;
int ncpus_;
int online_cpus_;
......
......@@ -55,6 +55,7 @@ std::string MapKey::argument_value(BPFtrace &bpftrace,
const SizedType &arg,
const void *data)
{
auto arg_data = static_cast<const uint8_t*>(data);
switch (arg.type)
{
case Type::integer:
......@@ -66,13 +67,13 @@ std::string MapKey::argument_value(BPFtrace &bpftrace,
return std::to_string(*(int32_t*)data);
}
case Type::stack:
return bpftrace.get_stack(*(uint32_t*)data, false);
return bpftrace.get_stack(*(uint64_t*)data, false);
case Type::ustack:
return bpftrace.get_stack(*(uint32_t*)data, true);
return bpftrace.get_stack(*(uint64_t*)data, true);
case Type::sym:
return bpftrace.resolve_sym(*(uint64_t*)data);
case Type::usym:
return bpftrace.resolve_usym(*(uint64_t*)data);
return bpftrace.resolve_usym(*(uint64_t*)data, *(uint64_t*)(arg_data + 8));
case Type::name:
return bpftrace.name_ids_[*(uint64_t*)data];
case Type::string:
......
......@@ -285,18 +285,21 @@ define i64 @"kprobe:f"(i8*) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@x_val" = alloca i64, align 8
%"@x_key" = alloca i64, align 8
%get_pid_tgid = tail call i64 inttoptr (i64 14 to i64 ()*)()
%1 = shl i64 %get_pid_tgid, 32
%pseudo = tail call i64 @llvm.bpf.pseudo(i64 1, i64 2)
%get_stackid = tail call i64 inttoptr (i64 27 to i64 (i8*, i8*, i64)*)(i8* %0, i64 %pseudo, i64 0)
%1 = bitcast i64* %"@x_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
%2 = or i64 %get_stackid, %1
%3 = bitcast i64* %"@x_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %3)
store i64 0, i64* %"@x_key", align 8
%2 = bitcast i64* %"@x_val" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %2)
store i64 %get_stackid, i64* %"@x_val", align 8
%4 = bitcast i64* %"@x_val" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %4)
store i64 %2, i64* %"@x_val", align 8
%pseudo1 = tail call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo1, i64* nonnull %"@x_key", i64* nonnull %"@x_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %2)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %3)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %4)
ret i64 0
}
......@@ -322,18 +325,21 @@ define i64 @"kprobe:f"(i8*) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@x_val" = alloca i64, align 8
%"@x_key" = alloca i64, align 8
%get_pid_tgid = tail call i64 inttoptr (i64 14 to i64 ()*)()
%1 = shl i64 %get_pid_tgid, 32
%pseudo = tail call i64 @llvm.bpf.pseudo(i64 1, i64 2)
%get_stackid = tail call i64 inttoptr (i64 27 to i64 (i8*, i8*, i64)*)(i8* %0, i64 %pseudo, i64 256)
%1 = bitcast i64* %"@x_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
%2 = or i64 %get_stackid, %1
%3 = bitcast i64* %"@x_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %3)
store i64 0, i64* %"@x_key", align 8
%2 = bitcast i64* %"@x_val" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %2)
store i64 %get_stackid, i64* %"@x_val", align 8
%4 = bitcast i64* %"@x_val" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %4)
store i64 %2, i64* %"@x_val", align 8
%pseudo1 = tail call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo1, i64* nonnull %"@x_key", i64* nonnull %"@x_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %2)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %3)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %4)
ret i64 0
}
......@@ -821,6 +827,59 @@ attributes #1 = { argmemonly nounwind }
)EXPECTED");
}
TEST(codegen, call_usym_key)
{
test("kprobe:f { @x[usym(0)] = count() }",
R"EXPECTED(; 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* nocapture readnone) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@x_val" = alloca i64, align 8
%"@x_key" = alloca [16 x i8], align 8
%1 = getelementptr inbounds [16 x i8], [16 x i8]* %"@x_key", i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
%get_pid_tgid = tail call i64 inttoptr (i64 14 to i64 ()*)()
%2 = lshr i64 %get_pid_tgid, 32
%usym.sroa.0.0..sroa_cast = bitcast [16 x i8]* %"@x_key" to i64*
store i64 0, i64* %usym.sroa.0.0..sroa_cast, align 8
%usym.sroa.4.0..sroa_idx = getelementptr inbounds [16 x i8], [16 x i8]* %"@x_key", i64 0, i64 8
%usym.sroa.4.0..sroa_cast = bitcast i8* %usym.sroa.4.0..sroa_idx to i64*
store i64 %2, i64* %usym.sroa.4.0..sroa_cast, align 8
%pseudo = tail call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%lookup_elem = call i8* inttoptr (i64 1 to i8* (i8*, i8*)*)(i64 %pseudo, [16 x i8]* nonnull %"@x_key")
%map_lookup_cond = icmp eq i8* %lookup_elem, null
br i1 %map_lookup_cond, label %lookup_merge, label %lookup_success
lookup_success: ; preds = %entry
%3 = load i64, i8* %lookup_elem, align 8
%phitmp = add i64 %3, 1
br label %lookup_merge
lookup_merge: ; preds = %entry, %lookup_success
%lookup_elem_val.0 = phi i64 [ %phitmp, %lookup_success ], [ 1, %entry ]
%4 = bitcast i64* %"@x_val" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %4)
store i64 %lookup_elem_val.0, i64* %"@x_val", align 8
%pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo1, [16 x i8]* nonnull %"@x_key", i64* nonnull %"@x_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %4)
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_hist)
{
test("kprobe:f { @x = hist(pid) }",
......
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