Commit 5def5f7f authored by Alex Birch's avatar Alex Birch

support on-stack strings of size up to about 240

parent 35b88a83
......@@ -329,11 +329,27 @@ void CodegenLLVM::visit(Call &call)
}
else if (call.func == "str")
{
AllocaInst *buf = b_.CreateAllocaBPF(call.type, "str");
b_.CreateMemSet(buf, b_.getInt8(0), call.type.size, 1);
AllocaInst *strlen = b_.CreateAllocaBPF(b_.getInt64Ty(), "strlen");
b_.CreateMemSet(strlen, b_.getInt8(0), sizeof(uint64_t), 1);
if (call.vargs->size() > 1) {
call.vargs->at(1)->accept(*this);
Value *proposed_strlen = b_.CreateAdd(expr_, b_.getInt64(1)); // add 1 to accommodate probe_read_str's null byte
Value *max = b_.getInt64(bpftrace_.strlen_);
CmpInst::Predicate P = CmpInst::ICMP_ULE;
Value *Cmp = b_.CreateICmp(P, proposed_strlen, max, "str.min.cmp");
Value *Select = b_.CreateSelect(Cmp, proposed_strlen, max, "str.min.select");
b_.CreateStore(Select, strlen);
} else {
b_.CreateStore(b_.getInt64(bpftrace_.strlen_), strlen);
}
AllocaInst *buf = b_.CreateAllocaBPF(bpftrace_.strlen_, "str");
b_.CreateMemSet(buf, b_.getInt8(0), bpftrace_.strlen_, 1);
call.vargs->front()->accept(*this);
b_.CreateProbeReadStr(buf, call.type.size, expr_);
b_.CreateProbeReadStr(buf, b_.CreateLoad(strlen), expr_);
b_.CreateLifetimeEnd(strlen);
expr_ = buf;
expr_deleter_ = [this,buf]() { b_.CreateLifetimeEnd(buf); };
}
else if (call.func == "kaddr")
{
......@@ -470,12 +486,16 @@ void CodegenLLVM::visit(Call &call)
for (int i=1; i<call.vargs->size(); i++)
{
Expression &arg = *call.vargs->at(i);
expr_deleter_ = nullptr;
arg.accept(*this);
Value *offset = b_.CreateGEP(printf_args, {b_.getInt32(0), b_.getInt32(i)});
if (arg.type.IsArray())
b_.CREATE_MEMCPY(offset, expr_, arg.type.size, 1);
else
b_.CreateStore(expr_, offset);
if (expr_deleter_)
expr_deleter_();
}
printf_id_++;
......@@ -519,12 +539,16 @@ void CodegenLLVM::visit(Call &call)
for (int i=1; i<call.vargs->size(); i++)
{
Expression &arg = *call.vargs->at(i);
expr_deleter_ = nullptr;
arg.accept(*this);
Value *offset = b_.CreateGEP(system_args, {b_.getInt32(0), b_.getInt32(i)});
if (arg.type.IsArray())
b_.CREATE_MEMCPY(offset, expr_, arg.type.size, 1);
else
b_.CreateStore(expr_, offset);
if (expr_deleter_)
expr_deleter_();
}
system_id_++;
......
......@@ -66,6 +66,7 @@ private:
IRBuilderBPF b_;
DataLayout layout_;
Value *expr_ = nullptr;
std::function<void()> expr_deleter_; // intentionally empty
Value *ctx_;
AttachPoint *current_attach_point_ = nullptr;
BPFtrace &bpftrace_;
......
......@@ -267,6 +267,11 @@ void IRBuilderBPF::CreateProbeRead(AllocaInst *dst, size_t size, Value *src)
}
CallInst *IRBuilderBPF::CreateProbeReadStr(AllocaInst *dst, size_t size, Value *src)
{
return CreateProbeReadStr(dst, getInt64(size), src);
}
CallInst *IRBuilderBPF::CreateProbeReadStr(AllocaInst *dst, llvm::Value *size, Value *src)
{
// int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr)
FunctionType *probereadstr_func_type = FunctionType::get(
......@@ -278,7 +283,7 @@ CallInst *IRBuilderBPF::CreateProbeReadStr(AllocaInst *dst, size_t size, Value *
Instruction::IntToPtr,
getInt64(BPF_FUNC_probe_read_str),
probereadstr_func_ptr_type);
return CreateCall(probereadstr_func, {dst, getInt64(size), src}, "probe_read_str");
return CreateCall(probereadstr_func, {dst, size, src}, "probe_read_str");
}
CallInst *IRBuilderBPF::CreateProbeReadStr(Value *dst, size_t size, Value *src)
......
......@@ -41,6 +41,7 @@ public:
void CreateMapUpdateElem(Map &map, AllocaInst *key, Value *val);
void CreateMapDeleteElem(Map &map, AllocaInst *key);
void CreateProbeRead(AllocaInst *dst, size_t size, Value *src);
CallInst *CreateProbeReadStr(AllocaInst *dst, llvm::Value *size, Value *src);
CallInst *CreateProbeReadStr(AllocaInst *dst, size_t size, Value *src);
CallInst *CreateProbeReadStr(Value *dst, size_t size, Value *src);
Value *CreateUSDTReadArgument(Value *ctx, AttachPoint *attach_point, int arg_name, Builtin &builtin);
......
......@@ -202,13 +202,27 @@ void SemanticAnalyser::visit(Call &call)
call.type = SizedType(Type::none, 0);
}
else if (call.func == "str" || call.func == "sym" || call.func == "usym") {
else if (call.func == "str") {
if (check_varargs(call, 1, 2)) {
check_arg(call, Type::integer, 0);
if (is_final_pass()) {
uint64_t strlen = bpftrace_.strlen_;
if (call.vargs->size() > 1) {
check_arg(call, Type::integer, 1, false);
auto &strlen_arg = *call.vargs->at(1);
if (strlen_arg.is_literal) {
strlen = static_cast<Integer&>(strlen_arg).n;
}
}
call.type = SizedType(Type::string, strlen);
}
}
}
else if (call.func == "sym" || call.func == "usym") {
check_nargs(call, 1);
check_arg(call, Type::integer, 0);
if (call.func == "str")
call.type = SizedType(Type::string, STRING_SIZE);
else if (call.func == "sym")
if (call.func == "sym")
call.type = SizedType(Type::sym, 8);
else if (call.func == "usym")
call.type = SizedType(Type::usym, 16);
......
......@@ -84,6 +84,8 @@ public:
int join_argnum_;
int join_argsize_;
uint64_t strlen_;
static void sort_by_key(std::vector<SizedType> key_args,
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> &values_by_key);
virtual std::set<std::string> find_wildcard_matches(const std::string &prefix, const std::string &func, std::istream &symbol_name_stream);
......
......@@ -29,6 +29,11 @@ void usage()
std::cerr << " -p PID enable USDT probes on PID" << std::endl;
std::cerr << " -c 'CMD' run CMD and enable USDT probes on resulting process" << std::endl;
std::cerr << " -v verbose messages" << std::endl << std::endl;
std::cerr << "ENVIRONMENT:" << std::endl;
std::cerr << " BPFTRACE_STRLEN [default: 64] bytes allocated on BPF stack by str()." << std::endl;
std::cerr << " Tune this to read in larger strings from userspace." << std::endl;
std::cerr << " Beware that BPF stack is small (512 bytes)." << std::endl;
std::cerr << " Beware that printf() and system() allocate strlen again when composing perf event output." << std::endl;
std::cerr << "EXAMPLES:" << std::endl;
std::cerr << "bpftrace -l '*sleep*'" << std::endl;
std::cerr << " list probes containing \"sleep\"" << std::endl;
......@@ -36,6 +41,8 @@ void usage()
std::cerr << " trace processes calling sleep" << std::endl;
std::cerr << "bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'" << std::endl;
std::cerr << " count syscalls by process name" << std::endl;
std::cerr << "BPFTRACE_STRLEN=200 bpftrace -e 'tracepoint:syscalls:sys_enter_write /pid == 25783/ { printf(\"<%s>\\n\", str(args->buf, args->count)); }'" << std::endl;
std::cerr << " trace input & output of terminal with specified PID" << std::endl;
}
static void enforce_infinite_rlimit() {
......@@ -176,6 +183,17 @@ int main(int argc, char *argv[])
bpftrace.join_argnum_ = 16;
bpftrace.join_argsize_ = 1024;
bpftrace.strlen_ = 64;
if(const char* env_p = std::getenv("BPFTRACE_STRLEN")) {
uint64_t proposed;
std::istringstream stringstream(env_p);
if (!(stringstream >> proposed)) {
std::cerr << "Env var 'BPFTRACE_STRLEN' did not contain a valid uint64_t, or was zero-valued." << std::endl;
return 1;
}
bpftrace.strlen_ = proposed;
}
// PID is currently only used for USDT probes that need enabling. Future work:
// - make PID a filter for all probe types: pass to perf_event_open(), etc.
// - provide PID in USDT probe specification as a way to override -p.
......
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