Commit 2ddbc077 authored by vijunag's avatar vijunag Committed by yonghong-song

Add build_id support for BPF stackmap

A separate build_id stackmap can be created with the help of new macro BPF_STACK_TRACE_BUILDID. The kernel BPF reports stacktrace in the 
structure bpf_stack_build_id. Changes have been made to BPF modules to
support resolving symbols mentioned in the above format.
An example tool is also available in examples/tracing/stack_buildid_example.py. 
Both python and C++ test cases are added.
parent 25a0ef32
#!/usr/bin/python
#
# An example usage of stack_build_id
# Most of the code here is borrowed from tools/profile.py
#
# Steps for using this code
# 1) Start ping program in one terminal eg invocation: ping google.com -i0.001
# 2) Change the path of libc specified in b.add_module() below
# 3) Invoke the script as 'python stack_buildid_example.py'
# 4) o/p of the tool is as shown below
# python example/tracing/stack_buildid_example.py
# sendto
# - ping (5232)
# 2
#
# REQUIRES: Linux 4.17+ (BPF_BUILD_ID support)
# Licensed under the Apache License, Version 2.0 (the "License")
# 03-Jan-2019 Vijay Nag
from __future__ import print_function
from bcc import BPF, PerfType, PerfSWConfig
from sys import stderr
from time import sleep
import argparse
import signal
import os
import subprocess
import errno
import multiprocessing
import ctypes as ct
def Get_libc_path():
# A small helper function that returns full path
# of libc in the system
cmd = 'cat /proc/self/maps | grep libc | awk \'{print $6}\' | uniq'
output = subprocess.check_output(cmd, shell=True)
if not isinstance(output, str):
output = output.decode()
return output.split('\n')[0]
bpf_text = """
#include <uapi/linux/ptrace.h>
#include <uapi/linux/bpf_perf_event.h>
#include <linux/sched.h>
struct key_t {
u32 pid;
int user_stack_id;
char name[TASK_COMM_LEN];
};
BPF_HASH(counts, struct key_t);
BPF_STACK_TRACE_BUILDID(stack_traces, 128);
int do_perf_event(struct bpf_perf_event_data *ctx) {
u32 pid = bpf_get_current_pid_tgid() >> 32;
// create map key
struct key_t key = {.pid = pid};
bpf_get_current_comm(&key.name, sizeof(key.name));
key.user_stack_id = stack_traces.get_stackid(&ctx->regs, BPF_F_USER_STACK);
if (key.user_stack_id >= 0) {
counts.increment(key);
}
return 0;
}
"""
b = BPF(text=bpf_text)
b.attach_perf_event(ev_type=PerfType.SOFTWARE,
ev_config=PerfSWConfig.CPU_CLOCK, fn_name="do_perf_event",
sample_period=0, sample_freq=49, cpu=0)
# Add the list of libraries/executables to the build sym cache for sym resolution
# Change the libc path if it is different on a different machine.
# libc.so and ping are added here so that any symbols pertaining to
# libc or ping are resolved. More executables/libraries can be added here.
b.add_module(Get_libc_path())
b.add_module("/usr/sbin/sshd")
b.add_module("/bin/ping")
counts = b.get_table("counts")
stack_traces = b.get_table("stack_traces")
duration = 2
def signal_handler(signal, frame):
print()
try:
sleep(duration)
except KeyboardInterrupt:
# as cleanup can take some time, trap Ctrl-C:
signal.signal(signal.SIGINT, signal_ignore)
user_stack=[]
for k,v in sorted(counts.items(), key=lambda counts: counts[1].value):
user_stack = [] if k.user_stack_id < 0 else \
stack_traces.walk(k.user_stack_id)
user_stack=list(user_stack)
for addr in user_stack:
print(" %s" % b.sym(addr, k.pid).decode('utf-8', 'replace'))
print(" %-16s %s (%d)" % ("-", k.name.decode('utf-8', 'replace'), k.pid))
print(" %d\n" % v.value)
......@@ -86,6 +86,8 @@ BPF::~BPF() {
if (res.code() != 0)
std::cerr << "Failed to detach all probes on destruction: " << std::endl
<< res.msg() << std::endl;
bcc_free_buildsymcache(bsymcache_);
bsymcache_ = NULL;
}
StatusTuple BPF::detach_all() {
......@@ -650,6 +652,21 @@ BPFStackTable BPF::get_stack_table(const std::string& name, bool use_debug_file,
return BPFStackTable({}, use_debug_file, check_debug_file_crc);
}
BPFStackBuildIdTable BPF::get_stackbuildid_table(const std::string &name, bool use_debug_file,
bool check_debug_file_crc) {
TableStorage::iterator it;
if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
return BPFStackBuildIdTable(it->second, use_debug_file, check_debug_file_crc, get_bsymcache());
return BPFStackBuildIdTable({}, use_debug_file, check_debug_file_crc, get_bsymcache());
}
bool BPF::add_module(std::string module)
{
return bcc_buildsymcache_add_module(get_bsymcache(), module.c_str()) != 0 ?
false : true;
}
std::string BPF::get_uprobe_event(const std::string& binary_path,
uint64_t offset, bpf_probe_attach_type type,
pid_t pid) {
......
......@@ -49,6 +49,7 @@ class BPF {
explicit BPF(unsigned int flag = 0, TableStorage* ts = nullptr,
bool rw_engine_enabled = bpf_module_rw_engine_enabled(), const std::string &maps_ns = "")
: flag_(flag),
bsymcache_(NULL),
bpf_module_(new BPFModule(flag, ts, rw_engine_enabled, maps_ns)) {}
StatusTuple init(const std::string& bpf_program,
const std::vector<std::string>& cflags = {},
......@@ -137,6 +138,13 @@ class BPF {
return BPFPercpuHashTable<KeyType, ValueType>({});
}
void* get_bsymcache(void) {
if (bsymcache_ == NULL) {
bsymcache_ = bcc_buildsymcache_new();
}
return bsymcache_;
}
BPFProgTable get_prog_table(const std::string& name);
BPFCgroupArray get_cgroup_array(const std::string& name);
......@@ -147,6 +155,12 @@ class BPF {
bool use_debug_file = true,
bool check_debug_file_crc = true);
BPFStackBuildIdTable get_stackbuildid_table(const std::string &name,
bool use_debug_file = true,
bool check_debug_file_crc = true);
bool add_module(std::string module);
StatusTuple open_perf_event(const std::string& name, uint32_t type,
uint64_t config);
......@@ -225,6 +239,8 @@ class BPF {
int flag_;
void *bsymcache_;
std::unique_ptr<std::string> syscall_prefix_;
std::unique_ptr<BPFModule> bpf_module_;
......
......@@ -316,6 +316,69 @@ std::vector<std::string> BPFStackTable::get_stack_symbol(int stack_id,
return res;
}
BPFStackBuildIdTable::BPFStackBuildIdTable(const TableDesc& desc, bool use_debug_file,
bool check_debug_file_crc,
void *bsymcache)
: BPFTableBase<int, stacktrace_buildid_t>(desc),
bsymcache_(bsymcache) {
if (desc.type != BPF_MAP_TYPE_STACK_TRACE)
throw std::invalid_argument("Table '" + desc.name +
"' is not a stack table");
symbol_option_ = {.use_debug_file = use_debug_file,
.check_debug_file_crc = check_debug_file_crc,
.use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC)};
}
void BPFStackBuildIdTable::clear_table_non_atomic() {
for (int i = 0; size_t(i) < capacity(); i++) {
remove(&i);
}
}
std::vector<bpf_stack_build_id> BPFStackBuildIdTable::get_stack_addr(int stack_id) {
std::vector<bpf_stack_build_id> res;
struct stacktrace_buildid_t stack;
if (stack_id < 0)
return res;
if (!lookup(&stack_id, &stack))
return res;
for (int i = 0; (i < BPF_MAX_STACK_DEPTH) && \
(stack.trace[i].status == BPF_STACK_BUILD_ID_VALID);
i++) {
/* End of stack marker is BCC_STACK_BUILD_ID_EMPTY or
* BCC_STACK_BUILD_IP(fallback) mechanism.
* We do not support fallback mechanism
*/
res.push_back(stack.trace[i]);
}
return res;
}
std::vector<std::string> BPFStackBuildIdTable::get_stack_symbol(int stack_id)
{
auto addresses = get_stack_addr(stack_id);
std::vector<std::string> res;
if (addresses.empty())
return res;
res.reserve(addresses.size());
bcc_symbol symbol;
struct bpf_stack_build_id trace;
for (auto addr : addresses) {
memcpy(trace.build_id, addr.build_id, sizeof(trace.build_id));
trace.status = addr.status;
trace.offset = addr.offset;
if (bcc_buildsymcache_resolve(bsymcache_,&trace,&symbol) != 0) {
res.emplace_back("[UNKNOWN]");
} else {
res.push_back(symbol.name);
bcc_symbol_free_demangle_name(&symbol);
}
}
return res;
}
BPFPerfBuffer::BPFPerfBuffer(const TableDesc& desc)
: BPFTableBase<int, int>(desc), epfd_(-1) {
if (desc.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
......
......@@ -315,6 +315,26 @@ class BPFStackTable : public BPFTableBase<int, stacktrace_t> {
std::map<int, void*> pid_sym_;
};
// from src/cc/export/helpers.h
struct stacktrace_buildid_t {
struct bpf_stack_build_id trace[BPF_MAX_STACK_DEPTH];
};
class BPFStackBuildIdTable : public BPFTableBase<int, stacktrace_buildid_t> {
public:
BPFStackBuildIdTable(const TableDesc& desc, bool use_debug_file,
bool check_debug_file_crc, void *bsymcache);
~BPFStackBuildIdTable() = default;
void clear_table_non_atomic();
std::vector<bpf_stack_build_id> get_stack_addr(int stack_id);
std::vector<std::string> get_stack_symbol(int stack_id);
private:
void *bsymcache_;
bcc_symbol_option symbol_option_;
};
class BPFPerfBuffer : public BPFTableBase<int, int> {
public:
BPFPerfBuffer(const TableDesc& desc);
......
......@@ -882,6 +882,20 @@ int bcc_free_memory() {
return err;
}
int bcc_elf_get_buildid(const char *path, char *buildid)
{
Elf *e;
int fd;
if (openelf(path, &e, &fd) < 0)
return -1;
if (!find_buildid(e, buildid))
return -1;
return 0;
}
#if 0
#include <stdio.h>
......
......@@ -69,6 +69,7 @@ int bcc_elf_is_shared_obj(const char *path);
int bcc_elf_is_exe(const char *path);
int bcc_elf_is_vdso(const char *name);
int bcc_free_memory();
int bcc_elf_get_buildid(const char *path, char *buildid);
#ifdef __cplusplus
}
......
......@@ -378,6 +378,93 @@ bool ProcSyms::Module::find_addr(uint64_t offset, struct bcc_symbol *sym) {
return false;
}
bool BuildSyms::Module::load_sym_table()
{
if (loaded_)
return true;
symbol_option_ = {
.use_debug_file = 1,
.check_debug_file_crc = 1,
.use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC)
};
bcc_elf_foreach_sym(module_name_.c_str(), _add_symbol, &symbol_option_, this);
std::sort(syms_.begin(), syms_.end());
for(std::vector<Symbol>::iterator it = syms_.begin();
it != syms_.end(); ++it++) {
}
loaded_ = true;
return true;
}
int BuildSyms::Module::_add_symbol(const char *symname, uint64_t start,
uint64_t size, void *p)
{
BuildSyms::Module *m = static_cast<BuildSyms::Module *> (p);
auto res = m->symnames_.emplace(symname);
m->syms_.emplace_back(&*(res.first), start, size);
return 0;
}
bool BuildSyms::Module::resolve_addr(uint64_t offset, struct bcc_symbol* sym,
bool demangle)
{
std::vector<Symbol>::iterator it;
load_sym_table();
if (syms_.empty())
goto unknown_symbol;
it = std::upper_bound(syms_.begin(), syms_.end(), Symbol(nullptr, offset, 0));
if (it != syms_.begin()) {
it--;
sym->name = (*it).name->c_str();
if (demangle)
sym->demangle_name = sym->name;
sym->offset = (*it).start;
sym->module = module_name_.c_str();
return true;
}
unknown_symbol:
memset(sym, 0, sizeof(struct bcc_symbol));
return false;
}
bool BuildSyms::add_module(const std::string module_name)
{
struct stat s;
char buildid[BPF_BUILD_ID_SIZE*2+1];
if (stat(module_name.c_str(), &s) < 0)
return false;
if (bcc_elf_get_buildid(module_name.c_str(), buildid) < 0)
return false;
std::string elf_buildid(buildid);
std::unique_ptr<BuildSyms::Module> ptr(new BuildSyms::Module(module_name.c_str()));
buildmap_[elf_buildid] = std::move(ptr);
return true;
}
bool BuildSyms::resolve_addr(std::string build_id, uint64_t offset,
struct bcc_symbol *sym, bool demangle)
{
std::unordered_map<std::string,std::unique_ptr<BuildSyms::Module> >::iterator it;
it = buildmap_.find(build_id);
if (it == buildmap_.end())
/*build-id not added to the BuildSym*/
return false;
BuildSyms::Module *mod = it->second.get();
return mod->resolve_addr(offset, sym, demangle);
}
extern "C" {
void *bcc_symcache_new(int pid, struct bcc_symbol_option *option) {
......@@ -421,6 +508,45 @@ void bcc_symcache_refresh(void *resolver) {
cache->refresh();
}
void *bcc_buildsymcache_new(void) {
return static_cast<void *>(new BuildSyms());
}
void bcc_free_buildsymcache(void *symcache) {
delete static_cast<BuildSyms*>(symcache);
}
int bcc_buildsymcache_add_module(void *resolver, const char *module_name)
{
BuildSyms *bsym = static_cast<BuildSyms *>(resolver);
return bsym->add_module(module_name) ? 0 : -1;
}
int bcc_buildsymcache_resolve(void *resolver,
struct bpf_stack_build_id *trace,
struct bcc_symbol *sym)
{
std::string build_id;
unsigned char *c = &trace->build_id[0];
int idx = 0;
/*cannot resolve in case of fallback*/
if (trace->status == BPF_STACK_BUILD_ID_EMPTY ||
trace->status == BPF_STACK_BUILD_ID_IP)
return 0;
while( idx < 20) {
int nib1 = (c[idx]&0xf0)>>4;
int nib2 = (c[idx]&0x0f);
build_id += "0123456789abcdef"[nib1];
build_id += "0123456789abcdef"[nib2];
idx++;
}
BuildSyms *bsym = static_cast<BuildSyms *>(resolver);
return bsym->resolve_addr(build_id, trace->offset, sym) ? 0 : -1;
}
struct mod_st {
const char *name;
uint64_t start;
......
......@@ -21,6 +21,7 @@ extern "C" {
#endif
#include <stdint.h>
#include <compat/linux/bpf.h>
struct bcc_symbol {
const char *name;
......@@ -67,6 +68,13 @@ void bcc_symcache_refresh(void *resolver);
int bcc_resolve_global_addr(int pid, const char *module, const uint64_t address,
uint64_t *global);
/*bcc APIs for build_id stackmap support*/
void *bcc_buildsymcache_new(void);
void bcc_free_buildsymcache(void *symcache);
int bcc_buildsymcache_add_module(void *resolver, const char *module_name);
int bcc_buildsymcache_resolve(void *resolver,
struct bpf_stack_build_id *trace,
struct bcc_symbol *sym);
// Call cb on every function symbol in the specified module. Uses simpler
// SYM_CB callback mainly for easier to use in Python API.
// Will prefer use debug file and check debug file CRC when reading the module.
......
......@@ -216,9 +216,16 @@ struct bpf_stacktrace {
u64 ip[BPF_MAX_STACK_DEPTH];
};
struct bpf_stacktrace_buildid {
struct bpf_stack_build_id trace[BPF_MAX_STACK_DEPTH];
};
#define BPF_STACK_TRACE(_name, _max_entries) \
BPF_TABLE("stacktrace", int, struct bpf_stacktrace, _name, roundup_pow_of_two(_max_entries))
#define BPF_STACK_TRACE_BUILDID(_name, _max_entries) \
BPF_F_TABLE("stacktrace", int, struct bpf_stacktrace_buildid, _name, roundup_pow_of_two(_max_entries), BPF_F_STACK_BUILD_ID)
#define BPF_PROG_ARRAY(_name, _max_entries) \
BPF_TABLE("prog", u32, u32, _name, _max_entries)
......
......@@ -147,3 +147,42 @@ public:
virtual bool resolve_name(const char *module, const char *name,
uint64_t *addr);
};
class BuildSyms {
struct Symbol {
Symbol(const std::string *name, uint64_t start, uint64_t size)
:name(name), start(start), size(size) {}
const std::string *name;
uint64_t start;
uint64_t size;
bool operator<(const struct Symbol &rhs) const {
return start < rhs.start;
}
};
struct Module {
Module(const char *module_name):
module_name_(module_name),
loaded_(false) {}
const std::string module_name_;
const std::string build_id_;
bool loaded_;
std::unordered_set<std::string> symnames_;
std::vector<Symbol> syms_;
bcc_symbol_option symbol_option_;
bool load_sym_table();
static int _add_symbol(const char *symname, uint64_t start, uint64_t size,
void *p);
bool resolve_addr(uint64_t offset, struct bcc_symbol*, bool demangle=true);
};
std::unordered_map<std::string, std::unique_ptr<Module> > buildmap_;
public:
BuildSyms() {}
virtual ~BuildSyms() = default;
virtual bool add_module(const std::string module_name);
virtual bool resolve_addr(std::string build_id, uint64_t offset, struct bcc_symbol *sym, bool demangle = true);
};
......@@ -24,7 +24,7 @@ import errno
import sys
basestring = (unicode if sys.version_info[0] < 3 else str)
from .libbcc import lib, bcc_symbol, bcc_symbol_option, _SYM_CB_TYPE
from .libbcc import lib, bcc_symbol, bcc_symbol_option, bcc_stacktrace_build_id, _SYM_CB_TYPE
from .table import Table, PerfEventArray
from .perf import Perf
from .syscall import syscall_name
......@@ -69,6 +69,7 @@ class SymbolCache(object):
module. If we don't even know the module, return the absolute
address as the offset.
"""
sym = bcc_symbol()
if demangle:
res = lib.bcc_symcache_resolve(self.cache, addr, ct.byref(sym))
......@@ -96,6 +97,7 @@ class SymbolCache(object):
return -1
return addr.value
class PerfType:
# From perf_type_id in uapi/linux/perf_event.h
HARDWARE = 0
......@@ -158,6 +160,7 @@ class BPF(object):
_probe_repl = re.compile(b"[^a-zA-Z0-9_]")
_sym_caches = {}
_bsymcache = lib.bcc_buildsymcache_new()
_auto_includes = {
"linux/time.h": ["time"],
......@@ -1189,7 +1192,31 @@ class BPF(object):
Example output when both show_module and show_offset are False:
"start_thread"
"""
name, offset, module = BPF._sym_cache(pid).resolve(addr, demangle)
#addr is of type stacktrace_build_id
#so invoke the bsym address resolver
typeofaddr = str(type(addr))
if typeofaddr.find('bpf_stack_build_id') != -1:
sym = bcc_symbol()
b = bcc_stacktrace_build_id()
b.status = addr.status
b.build_id = addr.build_id
b.u.offset = addr.offset;
res = lib.bcc_buildsymcache_resolve(BPF._bsymcache,
ct.byref(b),
ct.byref(sym))
if res < 0:
if sym.module and sym.offset:
name,offset,module = (None, sym.offset,
ct.cast(sym.module, ct.c_char_p).value)
else:
name, offset, module = (None, addr, None)
else:
name, offset, module = (sym.name, sym.offset,
ct.cast(sym.module, ct.c_char_p).value)
else:
name, offset, module = BPF._sym_cache(pid).resolve(addr, demangle)
offset = b"+0x%x" % offset if show_offset and name is not None else b""
name = name or b"[unknown]"
name = name + offset
......@@ -1262,6 +1289,17 @@ class BPF(object):
def free_bcc_memory(self):
return lib.bcc_free_memory()
@staticmethod
def add_module(modname):
"""add_module(modname)
Add a library or exe to buildsym cache
"""
try:
lib.bcc_buildsymcache_add_module(BPF._bsymcache, modname.encode())
except Exception as e:
print("Error adding module to build sym cache"+str(e))
def donothing(self):
"""the do nothing exit handler"""
......
......@@ -133,6 +133,19 @@ class bcc_symbol(ct.Structure):
('offset', ct.c_ulonglong),
]
class bcc_ip_offset_union(ct.Union):
_fields_ = [
('offset', ct.c_uint64),
('ip', ct.c_uint64)
]
class bcc_stacktrace_build_id(ct.Structure):
_fields_ = [
('status', ct.c_uint32),
('build_id',ct.c_ubyte*20),
('u',bcc_ip_offset_union)
]
class bcc_symbol_option(ct.Structure):
_fields_ = [
('use_debug_file', ct.c_int),
......@@ -161,6 +174,18 @@ lib.bcc_symcache_new.argtypes = [ct.c_int, ct.POINTER(bcc_symbol_option)]
lib.bcc_free_symcache.restype = ct.c_void_p
lib.bcc_free_symcache.argtypes = [ct.c_void_p, ct.c_int]
lib.bcc_buildsymcache_new.restype = ct.c_void_p
lib.bcc_buildsymcache_new.argtypes = None
lib.bcc_free_buildsymcache.restype = None
lib.bcc_free_buildsymcache.argtypes = [ct.c_void_p]
lib.bcc_buildsymcache_add_module.restype = ct.c_int
lib.bcc_buildsymcache_add_module.argtypes = [ct.c_void_p, ct.c_char_p]
lib.bcc_buildsymcache_resolve.restype = ct.c_int
lib.bcc_buildsymcache_resolve.argtypes = [ct.c_void_p, ct.POINTER(bcc_stacktrace_build_id), ct.POINTER(bcc_symbol)]
lib.bcc_symbol_free_demangle_name.restype = ct.c_void_p
lib.bcc_symbol_free_demangle_name.argtypes = [ct.POINTER(bcc_symbol)]
......
......@@ -736,15 +736,20 @@ class LpmTrie(TableBase):
class StackTrace(TableBase):
MAX_DEPTH = 127
BPF_F_STACK_BUILD_ID = (1<<5)
BPF_STACK_BUILD_ID_EMPTY = 0 #can't get stacktrace
BPF_STACK_BUILD_ID_VALID = 1 #valid build-id,ip
BPF_STACK_BUILD_ID_IP = 2 #fallback to ip
def __init__(self, *args, **kwargs):
super(StackTrace, self).__init__(*args, **kwargs)
class StackWalker(object):
def __init__(self, stack, resolve=None):
def __init__(self, stack, flags, resolve=None):
self.stack = stack
self.n = -1
self.resolve = resolve
self.flags = flags
def __iter__(self):
return self
......@@ -757,14 +762,21 @@ class StackTrace(TableBase):
if self.n == StackTrace.MAX_DEPTH:
raise StopIteration()
addr = self.stack.ip[self.n]
if self.flags & StackTrace.BPF_F_STACK_BUILD_ID:
addr = self.stack.trace[self.n]
if addr.status == StackTrace.BPF_STACK_BUILD_ID_IP or \
addr.status == StackTrace.BPF_STACK_BUILD_ID_EMPTY:
raise StopIteration()
else:
addr = self.stack.ip[self.n]
if addr == 0 :
raise StopIteration()
return self.resolve(addr) if self.resolve else addr
def walk(self, stack_id, resolve=None):
return StackTrace.StackWalker(self[self.Key(stack_id)], resolve)
return StackTrace.StackWalker(self[self.Key(stack_id)], self.flags, resolve)
def __len__(self):
i = 0
......
......@@ -222,3 +222,59 @@ TEST_CASE("test bpf stack table", "[bpf_stack_table]") {
REQUIRE(addrs.size() == 0);
#endif
}
TEST_CASE("test bpf stack_id table", "[bpf_stack_table]") {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
const std::string BPF_PROGRAM = R"(
BPF_HASH(id, int, int, 1);
BPF_STACK_TRACE_BUILDID(stack_traces, 8);
int on_sys_getuid(void *ctx) {
int stack_id = stack_traces.get_stackid(ctx, BPF_F_USER_STACK);
int zero = 0, *val;
val = id.lookup_or_init(&zero, &stack_id);
(*val) = stack_id;
return 0;
}
)";
ebpf::BPF bpf;
ebpf::StatusTuple res(0);
res = bpf.init(BPF_PROGRAM);
REQUIRE(res.code() == 0);
std::string getuid_fnname = bpf.get_syscall_fnname("getuid");
res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid");
REQUIRE(res.code() == 0);
REQUIRE(getuid() >= 0);
res = bpf.detach_kprobe(getuid_fnname);
REQUIRE(res.code() == 0);
auto id = bpf.get_hash_table<int, int>("id");
auto stack_traces = bpf.get_stackbuildid_table("stack_traces");
/* libc locations on different distributions are added below*/
bpf.add_module("/lib/x86_64-linux-gnu/libc.so.6"); //Location of libc in ubuntu
bpf.add_module("/lib64/libc.so.6"); //Location of libc fedora machine
int stack_id = id[0];
REQUIRE(stack_id >= 0);
auto addrs = stack_traces.get_stack_addr(stack_id);
auto symbols = stack_traces.get_stack_symbol(stack_id);
REQUIRE(addrs.size() > 0);
REQUIRE(addrs.size() == symbols.size());
bool found = false;
for (const auto &symbol : symbols) {
if (symbol.find("getuid") != std::string::npos) {
found = true;
break;
}
}
REQUIRE(found);
stack_traces.clear_table_non_atomic();
addrs = stack_traces.get_stack_addr(stack_id);
REQUIRE(addrs.size()==0);
#endif
}
......@@ -6,6 +6,7 @@ import bcc
import distutils.version
import os
import unittest
import subprocess
def kernel_version_ge(major, minor):
# True if running kernel is >= X.Y
......@@ -49,6 +50,44 @@ int kprobe__htab_map_lookup_elem(struct pt_regs *ctx, struct bpf_map *map, u64 *
stack = stack_traces[stackid].ip
self.assertEqual(b.ksym(stack[0]), b"htab_map_lookup_elem")
def Get_libc_path():
cmd = 'cat /proc/self/maps | grep libc | awk \'{print $6}\' | uniq'
output = subprocess.check_output(cmd, shell=True)
if not isinstance(output, str):
output = output.decode()
return output.split('\n')[0]
@unittest.skipUnless(kernel_version_ge(4,17), "requires kernel >= 4.17")
class TestStackBuildid(unittest.TestCase):
def test_simple(self):
b = bcc.BPF(text="""
#include <uapi/linux/ptrace.h>
struct bpf_map;
BPF_STACK_TRACE_BUILDID(stack_traces, 10240);
BPF_HASH(stack_entries, int, int);
BPF_HASH(stub);
int kprobe__sys_getuid(struct pt_regs *ctx, struct bpf_map *map, u64 *k) {
int id = stack_traces.get_stackid(ctx, BPF_F_USER_STACK);
if (id < 0)
return 0;
int key = 1;
stack_entries.update(&key, &id);
return 0;
}
""")
os.getuid()
stub = b["stub"]
stack_traces = b["stack_traces"]
stack_entries = b["stack_entries"]
b.add_module(Get_libc_path())
try: x = stub[stub.Key(1)]
except: pass
k = stack_entries.Key(1)
self.assertIn(k, stack_entries)
stackid = stack_entries[k]
self.assertIsNotNone(stackid)
stack = stack_traces[stackid]
self.assertTrue(b.sym(stack.trace[0], -1).find(b"getuid")!=-1)
if __name__ == "__main__":
unittest.main()
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