Commit 92218a24 authored by Alastair Robertson's avatar Alastair Robertson

Merge branch 'master' into codegen

parents 8cabf980 492e1f86
......@@ -12,7 +12,7 @@ CONFIG_BPF_EVENTS=y
To use some BPFtrace features, minimum kernel versions are required:
- 4.1+ - kprobes
- 4.3+ - uprobes
- 4.6+ - stack traces, count and quantize builtins (use PERCPU maps for accuracy and efficiency)
- 4.6+ - stack traces, count and hist builtins (use PERCPU maps for accuracy and efficiency)
- 4.7+ - tracepoints
- 4.9+ - timers/profiling
......
......@@ -4,7 +4,7 @@
BPFtrace is a high-level tracing language for Linux enhanced Berkeley Packet Filter (eBPF) available in recent Linux kernels (4.x). BPFtrace uses LLVM as a backend to compile scripts to BPF-bytecode and makes use of [BCC](https://github.com/iovisor/bcc) for interacting with the Linux BPF system, as well as existing Linux tracing capabilities: kernel dynamic tracing (kprobes), user-level dynamic tracing (uprobes), and tracepoints. The BPFtrace language is inspired by awk and C, and predecessor tracers such as DTrace and SystemTap.
For instructions on building BPFtrace, see [INSTALL.md](INSTALL.md)
For instructions on building BPFtrace, see [INSTALL.md](INSTALL.md). There is also a [Reference Guide](docs/reference_guide.md) and [One-Liner Tutorial](docs/tutorial_one_liners.md).
## Examples
......@@ -40,7 +40,7 @@ kprobe:sys_read
kretprobe:sys_read / @start[tid] /
{
@times = quantize(nsecs - @start[tid]);
@times = hist(nsecs - @start[tid]);
delete(@start[tid]);
}
```
......@@ -157,8 +157,20 @@ Attach script to a statically defined tracepoint in the kernel:
Tracepoints are guaranteed to be stable between kernel versions, unlike kprobes.
### timers
Run the script at specified time intervals:
### software
Attach script to kernel software events, executing once every provided count or use a default:
`software:faults:100`
`software:faults:`
### hardware
Attach script to hardware events (PMCs), executing once every provided count or use a default:
`hardware:cache-references:1000000`
`hardware:cache-references:`
### profile
Run the script on all CPUs at specified time intervals:
`profile:hz:99 { ... }`
......@@ -168,6 +180,13 @@ Run the script at specified time intervals:
`profile:us:1500 { ... }`
### interval
Run the script once per interval, for printing interval output:
`interval:s:1 { ... }`
`interval:ms:20 { ... }`
### Multiple attachment points
A single probe can be attached to multiple events:
......@@ -199,13 +218,28 @@ Variables:
- `arg0`, `arg1`, ... etc. - Arguments to the function being traced
- `retval` - Return value from function being traced
- `func` - Name of the function currently being traced
- `curtask` - Current task_struct as a u64.
- `rand` - Random number of type u32.
Functions:
- `quantize(int n)` - Produce a log2 histogram of values of `n`
- `hist(int n)` - Produce a log2 histogram of values of `n`
- `lhist(int n, int min, int max, int step)` - Produce a linear histogram of values of `n`
- `count()` - Count the number of times this function is called
- `sum(int n)` - Sum this value
- `min(int n)` - Record the minimum value seen
- `max(int n)` - Record the maximum value seen
- `avg(int n)` - Average this value
- `stats(int n)` - Return the count, average, and total for this value
- `delete(@x)` - Delete the map element passed in as an argument
- `str(char *s)` - Returns the string pointed to by `s`
- `printf(char *fmt, ...)` - Write to stdout
- `printf(char *fmt, ...)` - Print formatted to stdout
- `print(@x[, int top [, int div]])` - Print a map, with optional top entry count and divisor
- `clear(@x)` - Delet all key/values from a map
- `sym(void *p)` - Resolve kernel address
- `usym(void *p)` - Resolve user space address (incomplete)
- `reg(char *name)` - Returns the value stored in the named register
- `join(char *arr[])` - Prints the string array
- `time(char *fmt)` - Print the current time
- `exit()` - Quit bpftrace
See the [Reference Guide](docs/reference_guide.md) for more detail.
This diff is collapsed.
This diff is collapsed.
......@@ -9,6 +9,7 @@ add_executable(bpftrace
mapkey.cpp
printf.cpp
types.cpp
list.cpp
)
target_link_libraries(bpftrace arch ast parser resources)
......
......@@ -36,6 +36,10 @@ void Unop::accept(Visitor &v) {
v.visit(*this);
}
void Ternary::accept(Visitor &v) {
v.visit(*this);
}
void FieldAccess::accept(Visitor &v) {
v.visit(*this);
}
......
......@@ -49,6 +49,7 @@ class Builtin : public Expression {
public:
explicit Builtin(std::string ident) : ident(ident) { }
std::string ident;
int name_id;
void accept(Visitor &v) override;
};
......@@ -161,6 +162,14 @@ public:
void accept(Visitor &v) override;
};
class Ternary : public Expression {
public:
Ternary(Expression *cond, Expression *left, Expression *right) : cond(cond), left(left), right(right) { }
Expression *cond, *left, *right;
void accept(Visitor &v) override;
};
class AttachPoint : public Node {
public:
explicit AttachPoint(const std::string &provider)
......@@ -198,6 +207,7 @@ public:
void accept(Visitor &v) override;
std::string name() const;
bool need_expansion = false; // must build a BPF program per wildcard match
};
using ProbeList = std::vector<Probe *>;
......@@ -222,6 +232,7 @@ public:
virtual void visit(Variable &var) = 0;
virtual void visit(Binop &binop) = 0;
virtual void visit(Unop &unop) = 0;
virtual void visit(Ternary &ternary) = 0;
virtual void visit(FieldAccess &acc) = 0;
virtual void visit(Cast &cast) = 0;
virtual void visit(ExprStatement &expr) = 0;
......
This diff is collapsed.
......@@ -35,6 +35,7 @@ public:
void visit(Variable &var) override;
void visit(Binop &binop) override;
void visit(Unop &unop) override;
void visit(Ternary &ternary) override;
void visit(FieldAccess &acc) override;
void visit(Cast &cast) override;
void visit(ExprStatement &expr) override;
......@@ -45,11 +46,12 @@ public:
void visit(Probe &probe) override;
void visit(Program &program) override;
AllocaInst *getMapKey(Map &map);
AllocaInst *getQuantizeMapKey(Map &map, Value *log2);
AllocaInst *getHistMapKey(Map &map, Value *log2);
Value *createLogicalAnd(Binop &binop);
Value *createLogicalOr(Binop &binop);
void createLog2Function();
void createLinearFunction();
void createStrcmpFunction();
std::unique_ptr<BpfOrc> compile(bool debug=false, std::ostream &out=std::cerr);
......@@ -63,6 +65,7 @@ private:
Value *expr_ = nullptr;
Value *ctx_;
BPFtrace &bpftrace_;
std::string probefull_;
std::map<std::string, Value *> variables_;
int printf_id_ = 0;
......
......@@ -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::CreateAllocaBPF(int bytes, const std::string &name)
......@@ -57,7 +68,7 @@ AllocaInst *IRBuilderBPF::CreateAllocaBPF(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 && !stype.is_pointer))
if (stype.type == Type::string || stype.type == Type::usym || (stype.type == Type::cast && !stype.is_pointer))
{
ty = ArrayType::get(getInt8Ty(), stype.size);
}
......@@ -96,6 +107,26 @@ CallInst *IRBuilderBPF::CreateBpfPseudoCall(Map &map)
return CreateBpfPseudoCall(mapfd);
}
CallInst *IRBuilderBPF::CreateGetJoinMap(Value *ctx)
{
Value *map_ptr = CreateBpfPseudoCall(bpftrace_.join_map_->mapfd_);
AllocaInst *key = CreateAllocaBPF(getInt32Ty(), "key");
Value *keyv = getInt32(0);
CreateStore(keyv, key);
FunctionType *lookup_func_type = FunctionType::get(
getInt8PtrTy(),
{getInt8PtrTy(), getInt8PtrTy()},
false);
PointerType *lookup_func_ptr_type = PointerType::get(lookup_func_type, 0);
Constant *lookup_func = ConstantExpr::getCast(
Instruction::IntToPtr,
getInt64(BPF_FUNC_map_lookup_elem),
lookup_func_ptr_type);
CallInst *call = CreateCall(lookup_func, {map_ptr, key}, "join_elem");
return call;
}
Value *IRBuilderBPF::CreateMapLookupElem(Map &map, AllocaInst *key)
{
Value *map_ptr = CreateBpfPseudoCall(map);
......@@ -201,7 +232,22 @@ void IRBuilderBPF::CreateProbeRead(AllocaInst *dst, size_t size, Value *src)
CallInst *call = CreateCall(proberead_func, {dst, getInt64(size), src}, "probe_read");
}
void IRBuilderBPF::CreateProbeReadStr(AllocaInst *dst, size_t size, Value *src)
CallInst *IRBuilderBPF::CreateProbeReadStr(AllocaInst *dst, size_t size, Value *src)
{
// int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr)
FunctionType *probereadstr_func_type = FunctionType::get(
getInt64Ty(),
{getInt8PtrTy(), getInt64Ty(), getInt8PtrTy()},
false);
PointerType *probereadstr_func_ptr_type = PointerType::get(probereadstr_func_type, 0);
Constant *probereadstr_func = ConstantExpr::getCast(
Instruction::IntToPtr,
getInt64(BPF_FUNC_probe_read_str),
probereadstr_func_ptr_type);
return CreateCall(probereadstr_func, {dst, getInt64(size), src}, "probe_read_str");
}
CallInst *IRBuilderBPF::CreateProbeReadStr(Value *dst, size_t size, Value *src)
{
// int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr)
FunctionType *probereadstr_func_type = FunctionType::get(
......@@ -213,7 +259,7 @@ void IRBuilderBPF::CreateProbeReadStr(AllocaInst *dst, size_t size, Value *src)
Instruction::IntToPtr,
getInt64(BPF_FUNC_probe_read_str),
probereadstr_func_ptr_type);
CallInst *call = CreateCall(probereadstr_func, {dst, getInt64(size), src}, "probe_read_str");
return CreateCall(probereadstr_func, {dst, getInt64(size), src}, "map_read_str");
}
CallInst *IRBuilderBPF::CreateGetNs()
......@@ -268,6 +314,32 @@ CallInst *IRBuilderBPF::CreateGetCpuId()
return CreateCall(getcpuid_func, {}, "get_cpu_id");
}
CallInst *IRBuilderBPF::CreateGetCurrentTask()
{
// u64 bpf_get_current_task(void)
// Return: current task_struct
FunctionType *getcurtask_func_type = FunctionType::get(getInt64Ty(), false);
PointerType *getcurtask_func_ptr_type = PointerType::get(getcurtask_func_type, 0);
Constant *getcurtask_func = ConstantExpr::getCast(
Instruction::IntToPtr,
getInt64(BPF_FUNC_get_current_task),
getcurtask_func_ptr_type);
return CreateCall(getcurtask_func, {}, "get_cur_task");
}
CallInst *IRBuilderBPF::CreateGetRandom()
{
// u64 bpf_get_prandom_u32(void)
// Return: random
FunctionType *getrandom_func_type = FunctionType::get(getInt64Ty(), false);
PointerType *getrandom_func_ptr_type = PointerType::get(getrandom_func_type, 0);
Constant *getrandom_func = ConstantExpr::getCast(
Instruction::IntToPtr,
getInt64(BPF_FUNC_get_prandom_u32),
getrandom_func_ptr_type);
return CreateCall(getrandom_func, {}, "get_random");
}
CallInst *IRBuilderBPF::CreateGetStackId(Value *ctx, bool ustack)
{
Value *map_ptr = CreateBpfPseudoCall(bpftrace_.stackid_map_->mapfd_);
......
......@@ -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 *CreateAllocaBPF(int bytes, const std::string &name="");
llvm::Type *GetType(const SizedType &stype);
CallInst *CreateBpfPseudoCall(int mapfd);
......@@ -28,12 +30,16 @@ public:
void CreateMapUpdateElem(Map &map, AllocaInst *key, Value *val);
void CreateMapDeleteElem(Map &map, AllocaInst *key);
void CreateProbeRead(AllocaInst *dst, size_t size, Value *src);
void CreateProbeReadStr(AllocaInst *dst, size_t size, Value *src);
CallInst *CreateProbeReadStr(AllocaInst *dst, size_t size, Value *src);
CallInst *CreateProbeReadStr(Value *dst, size_t size, Value *src);
CallInst *CreateGetNs();
CallInst *CreateGetPidTgid();
CallInst *CreateGetUidGid();
CallInst *CreateGetCpuId();
CallInst *CreateGetCurrentTask();
CallInst *CreateGetRandom();
CallInst *CreateGetStackId(Value *ctx, bool ustack);
CallInst *CreateGetJoinMap(Value *ctx);
void CreateGetCurrentComm(AllocaInst *buf, size_t size);
void CreatePerfEventOutput(Value *ctx, Value *data, size_t size);
......
......@@ -86,6 +86,18 @@ void Printer::visit(Unop &unop)
--depth_;
}
void Printer::visit(Ternary &ternary)
{
std::string indent(depth_, ' ');
out_ << indent << "?:" << std::endl;
++depth_;
ternary.cond->accept(*this);
ternary.left->accept(*this);
ternary.right->accept(*this);
--depth_;
}
void Printer::visit(FieldAccess &acc)
{
std::string indent(depth_, ' ');
......
......@@ -18,6 +18,7 @@ public:
void visit(Variable &var) override;
void visit(Binop &binop) override;
void visit(Unop &unop) override;
void visit(Ternary &ternary) override;
void visit(FieldAccess &acc) override;
void visit(Cast &cast) override;
void visit(ExprStatement &expr) override;
......
This diff is collapsed.
......@@ -26,6 +26,7 @@ public:
void visit(Variable &var) override;
void visit(Binop &binop) override;
void visit(Unop &unop) override;
void visit(Ternary &ternary) override;
void visit(FieldAccess &acc) override;
void visit(Cast &cast) override;
void visit(ExprStatement &expr) override;
......@@ -59,7 +60,9 @@ private:
std::map<std::string, SizedType> variable_val_;
std::map<std::string, SizedType> map_val_;
std::map<std::string, MapKey> map_key_;
std::map<std::string, ExpressionList> map_args_;
bool needs_stackid_map_ = false;
bool needs_join_map_ = false;
bool has_begin_probe_ = false;
bool has_end_probe_ = false;
};
......
This diff is collapsed.
......@@ -13,7 +13,8 @@ class AttachedProbe
{
public:
AttachedProbe(Probe &probe, std::tuple<uint8_t *, uintptr_t> func);
virtual ~AttachedProbe();
AttachedProbe(Probe &probe, std::tuple<uint8_t *, uintptr_t> func, int pid);
~AttachedProbe();
AttachedProbe(const AttachedProbe &) = delete;
AttachedProbe& operator=(const AttachedProbe &) = delete;
......@@ -25,8 +26,12 @@ private:
void load_prog();
void attach_kprobe();
void attach_uprobe();
void attach_usdt(int pid);
void attach_tracepoint();
void attach_profile();
void attach_interval();
void attach_software();
void attach_hardware();
Probe &probe_;
std::tuple<uint8_t *, uintptr_t> func_;
......
This diff is collapsed.
......@@ -18,6 +18,10 @@ namespace bpftrace {
class BpfOrc;
// globals
extern bool bt_debug;
extern bool bt_verbose;
class BPFtrace
{
public:
......@@ -27,21 +31,31 @@ public:
int num_probes() const;
int run(std::unique_ptr<BpfOrc> bpforc);
int print_maps();
std::string get_stack(uint32_t stackid, bool ustack, int indent=0);
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(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_;
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::string> time_args_;
std::unique_ptr<IMap> stackid_map_;
std::unique_ptr<IMap> join_map_;
std::unique_ptr<IMap> perf_event_map_;
std::vector<std::string> name_ids_;
int join_argnum_;
int join_argsize_;
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 &attach_point, const std::string &file_name);
protected:
virtual std::set<std::string> find_wildcard_matches(const std::string &prefix, const std::string &attach_point, const std::string &file_name);
std::vector<Probe> probes_;
std::vector<Probe> special_probes_;
......@@ -49,17 +63,25 @@ 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_;
std::unique_ptr<AttachedProbe> attach_probe(Probe &probe, const BpfOrc &bpforc);
int setup_perf_events();
void poll_perf_events(int epollfd, int timeout=-1);
int print_map(IMap &map);
int print_map_quantize(IMap &map);
int print_quantize(const std::vector<uint64_t> &values) const;
int clear_map(IMap &map);
int zero_map(IMap &map);
int print_map(IMap &map, uint32_t top, uint32_t div);
int print_map_hist(IMap &map, uint32_t top, uint32_t div);
int print_map_lhist(IMap &map);
int print_map_stats(IMap &map);
int print_hist(const std::vector<uint64_t> &values, uint32_t div) const;
int print_lhist(const std::vector<uint64_t> &values, int min, int max, int step) const;
static uint64_t reduce_value(const std::vector<uint8_t> &value, int ncpus);
static std::string quantize_index_label(int power);
static uint64_t min_value(const std::vector<uint8_t> &value, int ncpus);
static uint64_t max_value(const std::vector<uint8_t> &value, int ncpus);
static std::string hist_index_label(int power);
std::vector<uint8_t> find_empty_key(IMap &map, size_t size) const;
};
......
......@@ -20,6 +20,11 @@ public:
std::string name_;
SizedType type_;
MapKey key_;
// used by lhist(). TODO: move to separate Map object.
int lqmin;
int lqmax;
int lqstep;
};
} // namespace bpftrace
......@@ -28,6 +28,7 @@ space {hspace}|{vspace}
path :(\\.|[_\-\./a-zA-Z0-9])*:
%x STR
%x STRUCT
%x COMMENT
%%
......@@ -37,9 +38,15 @@ path :(\\.|[_\-\./a-zA-Z0-9])*:
{hspace}+ { loc.step(); }
{vspace}+ { loc.lines(yyleng); loc.step(); }
"//".*$ // Comments
pid|tid|uid|gid|nsecs|cpu|comm|stack|ustack|arg[0-9]|retval|func {
"//".*$ // single-line comments
"/*" BEGIN(COMMENT); // multi-line comments
<COMMENT>"/*" driver.error(loc, std::string("nested comments unsupported"));
<COMMENT>"*/" BEGIN(INITIAL);
<COMMENT>"EOF" driver.error(loc, std::string("end of file during comment"));
<COMMENT>.|"\n" ;
pid|tid|uid|gid|nsecs|cpu|comm|stack|ustack|arg[0-9]|retval|func|name|curtask|rand {
return Parser::make_BUILTIN(yytext, loc); }
{path} { return Parser::make_PATH(yytext, loc); }
{map} { return Parser::make_MAP(yytext, loc); }
......@@ -77,6 +84,7 @@ pid|tid|uid|gid|nsecs|cpu|comm|stack|ustack|arg[0-9]|retval|func {
"." { return Parser::make_DOT(loc); }
"->" { return Parser::make_PTR(loc); }
"#".* { return Parser::make_CPREPROC(yytext, loc); }
"?" { return Parser::make_QUES(loc); }
\" { BEGIN(STR); buffer.clear(); }
<STR>\" { BEGIN(INITIAL); return Parser::make_STRING(buffer, loc); }
......
#include <sys/types.h>
#include <dirent.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <regex>
#include <vector>
#include <string>
#include "list.h"
#include "bpftrace.h"
namespace bpftrace {
const std::string kprobe_path = "/sys/kernel/debug/tracing/available_filter_functions";
const std::string tp_path = "/sys/kernel/debug/tracing/events";
bool search_probe(const std::string &probe, const std::string search)
{
std::string s = search;
char remove[] = "*.?";
unsigned int i;
// TODO: glob searching instead of discarding wildcards
for (i = 0; i < strlen(remove); ++i)
{
s.erase(std::remove(s.begin(), s.end(), remove[i]), s.end());
}
if (probe.find(s) == std::string::npos)
return true;
return false;
}
void list_dir(const std::string path, std::vector<std::string> &files)
{
// yes, I know about std::filesystem::directory_iterator, but no, it wasn't available
DIR *dp;
struct dirent *dep;
if ((dp = opendir(path.c_str())) == NULL)
return;
while ((dep = readdir(dp)) != NULL)
files.push_back(std::string(dep->d_name));
}
void list_probes(const std::string &search)
{
unsigned int i, j;
std::string line, probe;
// software
// TODO: add here
// hardware
// TODO: add here
// tracepoints
std::vector<std::string> cats = std::vector<std::string>();
list_dir(tp_path, cats);
for (i = 0; i < cats.size(); i++)
{
if (cats[i] == "." || cats[i] == ".." || cats[i] == "enable" || cats[i] == "filter")
continue;
std::vector<std::string> events = std::vector<std::string>();
list_dir(tp_path + "/" + cats[i], events);
for (j = 0; j < events.size(); j++)
{
if (events[j] == "." || events[j] == ".." || events[j] == "enable" || events[j] == "filter")
continue;
probe = "tracepoint:" + cats[i] + ":" + events[j];
if (search_probe(probe, search))
continue;
std::cout << probe << std::endl;
}
}
// kprobes
std::cout << std::endl;
std::ifstream file(kprobe_path);
if (file.fail())
{
std::cerr << strerror(errno) << ": " << kprobe_path << std::endl;
return;
}
std::set<std::string> matches;
size_t loc;
while (std::getline(file, line))
{
loc = line.find_first_of(" ");
if (loc == std::string::npos)
probe = "kprobe:" + line;
else
probe = "kprobe:" + line.substr(0, loc);
if (!search.empty())
{
if (search_probe(probe, search))
continue;
}
std::cout << probe << std::endl;
}
}
void list_probes()
{
const std::string search = "";
list_probes(search);
}
} // namespace bpftrace
#include <sstream>
namespace bpftrace {
void list_probes(const std::string &search);
void list_probes();
} // namespace bpftrace
......@@ -8,40 +8,85 @@
#include "driver.h"
#include "printer.h"
#include "semantic_analyser.h"
#include "list.h"
using namespace bpftrace;
void usage()
{
std::cerr << "Usage:" << std::endl;
std::cerr << " bpftrace filename" << std::endl;
std::cerr << " bpftrace -e 'script'" << std::endl;
std::cerr << "USAGE:" << std::endl;
std::cerr << " bpftrace [options] filename" << std::endl;
std::cerr << " bpftrace [options] -e 'program'" << std::endl << std::endl;
std::cerr << "OPTIONS:" << std::endl;
std::cerr << " -l [search] list probes" << std::endl;
std::cerr << " -e 'program' execute this program" << std::endl;
std::cerr << " -p PID PID for enabling USDT probes" << std::endl;
std::cerr << " -v verbose messages" << std::endl;
std::cerr << " -d debug info dry run" << std::endl << std::endl;
std::cerr << "EXAMPLES:" << std::endl;
std::cerr << "bpftrace -l '*sleep*'" << std::endl;
std::cerr << " list probes containing \"sleep\"" << std::endl;
std::cerr << "bpftrace -e 'kprobe:do_nanosleep { printf(\"PID %d sleeping...\\n\", pid); }'" << std::endl;
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;
}
int main(int argc, char *argv[])
{
int err;
Driver driver;
char *pid_str = NULL;
bool listing = false;
std::string script;
bool debug = false;
std::string script, search;
int c;
while ((c = getopt(argc, argv, "de:")) != -1)
while ((c = getopt(argc, argv, "de:lp:v")) != -1)
{
switch (c)
{
case 'd':
debug = true;
bt_debug = true;
break;
case 'v':
bt_verbose = true;
break;
case 'e':
script = optarg;
break;
case 'p':
pid_str = optarg;
break;
case 'l':
listing = true;
break;
default:
usage();
return 1;
}
}
if (bt_verbose && bt_debug)
{
// TODO: allow both
std::cerr << "USAGE: Use either -v or -d." << std::endl;
return 1;
}
// Listing probes
if (listing)
{
if (optind == argc-1)
list_probes(argv[optind]);
else if (optind == argc)
list_probes();
else
{
usage();
}
return 0;
}
if (script.empty())
{
// There should only be 1 non-option argument (the script file)
......@@ -69,10 +114,22 @@ int main(int argc, char *argv[])
BPFtrace bpftrace;
if (debug)
// defaults
bpftrace.join_argnum_ = 16;
bpftrace.join_argsize_ = 1024;
// 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.
bpftrace.pid_ = 0;
if (pid_str)
bpftrace.pid_ = atoi(pid_str);
if (bt_debug)
{
ast::Printer p(std::cout);
driver.root_->accept(p);
std::cout << std::endl;
}
ClangParser clang;
......@@ -83,14 +140,14 @@ int main(int argc, char *argv[])
if (err)
return err;
err = semantics.create_maps(debug);
err = semantics.create_maps(bt_debug);
if (err)
return err;
ast::CodegenLLVM llvm(driver.root_, bpftrace);
auto bpforc = llvm.compile(debug);
auto bpforc = llvm.compile(bt_debug);
if (debug)
if (bt_debug)
return 0;
// Empty signal handler for cleanly terminating the program
......
......@@ -9,29 +9,42 @@
namespace bpftrace {
Map::Map(const std::string &name, const SizedType &type, const MapKey &key)
Map::Map(const std::string &name, const SizedType &type, const MapKey &key, int min, int max, int step)
{
name_ = name;
type_ = type;
key_ = key;
// for lhist maps:
lqmin = min;
lqmax = max;
lqstep = step;
int key_size = key.size();
if (type.type == Type::quantize)
if (type.type == Type::hist || type.type == Type::lhist ||
type.type == Type::avg || type.type == Type::stats)
key_size += 8;
if (key_size == 0)
key_size = 8;
int max_entries = 128;
enum bpf_map_type map_type;
if ((type.type == Type::quantize || type.type == Type::count) &&
if ((type.type == Type::hist || type.type == Type::lhist || type.type == Type::count ||
type.type == Type::sum || type.type == Type::min || type.type == Type::max ||
type.type == Type::avg || type.type == Type::stats) &&
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)))
{
map_type = BPF_MAP_TYPE_PERCPU_HASH;
}
else if (type.type == Type::join)
{
map_type = BPF_MAP_TYPE_PERCPU_ARRAY;
max_entries = 1;
key_size = 4;
}
else
map_type = BPF_MAP_TYPE_HASH;
int value_size = type.size;
int max_entries = 128;
int flags = 0;
mapfd_ = bpf_create_map(map_type, name.c_str(), key_size, value_size, max_entries, flags);
if (mapfd_ < 0)
......
......@@ -6,7 +6,9 @@ namespace bpftrace {
class Map : public IMap {
public:
Map(const std::string &name, const SizedType &type, const MapKey &key);
Map(const std::string &name, const SizedType &type, const MapKey &key)
: Map(name, type, key, 0, 0, 0) {};
Map(const std::string &name, const SizedType &type, const MapKey &key, int min, int max, int step);
Map(enum bpf_map_type map_type);
virtual ~Map() override;
};
......
......@@ -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,15 @@ 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:
return std::string((char*)data);
}
......
......@@ -44,6 +44,7 @@ void yyerror(bpftrace::Driver &driver, const char *s);
RBRACKET "]"
LPAREN "("
RPAREN ")"
QUES "?"
ENDPRED "end predicate"
COMMA ","
ASSIGN "="
......@@ -83,6 +84,7 @@ void yyerror(bpftrace::Driver &driver, const char *s);
%type <ast::ProbeList *> probes
%type <ast::Probe *> probe
%type <ast::Predicate *> pred
%type <ast::Ternary *> ternary
%type <ast::StatementList *> block stmts
%type <ast::Statement *> stmt
%type <ast::Expression *> expr
......@@ -96,6 +98,7 @@ void yyerror(bpftrace::Driver &driver, const char *s);
%type <std::string> ident
%right ASSIGN
%left QUES COLON
%left LOR
%left LAND
%left BOR
......@@ -148,6 +151,9 @@ pred : DIV expr ENDPRED { $$ = new ast::Predicate($2); }
| { $$ = nullptr; }
;
ternary : expr QUES expr COLON expr { $$ = new ast::Ternary($1, $3, $5); }
;
block : "{" stmts "}" { $$ = $2; }
| "{" stmts ";" "}" { $$ = $2; }
;
......@@ -164,6 +170,7 @@ stmt : expr { $$ = new ast::ExprStatement($1); }
expr : INT { $$ = new ast::Integer($1); }
| STRING { $$ = new ast::String($1); }
| BUILTIN { $$ = new ast::Builtin($1); }
| ternary { $$ = $1; }
| map { $$ = $1; }
| var { $$ = $1; }
| call { $$ = $1; }
......
......@@ -33,7 +33,7 @@ std::string verify_format_string(const std::string &fmt, std::vector<Field> args
for (int i=0; i<num_args; i++, token_iter++)
{
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::name)
arg_type = Type::string; // Symbols should be printed as strings
int offset = 1;
......
......@@ -29,14 +29,21 @@ std::string typestr(Type t)
{
case Type::none: return "none"; break;
case Type::integer: return "integer"; break;
case Type::quantize: return "quantize"; break;
case Type::hist: return "hist"; break;
case Type::lhist: return "lhist"; break;
case Type::count: return "count"; break;
case Type::sum: return "sum"; break;
case Type::min: return "min"; break;
case Type::max: return "max"; break;
case Type::avg: return "avg"; break;
case Type::stats: return "stats"; break;
case Type::stack: return "stack"; break;
case Type::ustack: return "ustack"; break;
case Type::string: return "string"; break;
case Type::sym: return "sym"; break;
case Type::usym: return "usym"; break;
case Type::cast: return "cast"; break;
case Type::name: return "name"; break;
default: abort();
}
}
......@@ -51,6 +58,8 @@ ProbeType probetype(const std::string &type)
return ProbeType::uprobe;
else if (type == "uretprobe")
return ProbeType::uretprobe;
else if (type == "usdt")
return ProbeType::usdt;
else if (type == "BEGIN")
return ProbeType::uprobe;
else if (type == "END")
......@@ -59,7 +68,18 @@ ProbeType probetype(const std::string &type)
return ProbeType::tracepoint;
else if (type == "profile")
return ProbeType::profile;
else if (type == "interval")
return ProbeType::interval;
else if (type == "software")
return ProbeType::software;
else if (type == "hardware")
return ProbeType::hardware;
abort();
}
uint64_t asyncactionint(AsyncAction a)
{
return (uint64_t)a;
}
} // namespace bpftrace
......@@ -15,14 +15,22 @@ enum class Type
{
none,
integer,
quantize,
hist,
lhist,
count,
sum,
min,
max,
avg,
stats,
stack,
ustack,
string,
sym,
usym,
cast,
join,
name,
};
std::ostream &operator<<(std::ostream &os, Type type);
......@@ -52,8 +60,12 @@ enum class ProbeType
kretprobe,
uprobe,
uretprobe,
usdt,
tracepoint,
profile,
interval,
software,
hardware,
};
std::string typestr(Type t);
......@@ -63,11 +75,26 @@ class Probe
{
public:
ProbeType type;
std::string path;
std::string attach_point;
std::string prog_name;
std::string name;
std::string path; // file path if used
std::string attach_point; // probe name (last component)
std::string orig_name; // original full probe name,
// before wildcard expansion
std::string name; // full probe name
uint64_t loc; // for USDT probes
int freq;
};
enum class AsyncAction
{
// printf reserves 0-9999 for printf_ids
exit = 10000,
print,
clear,
zero,
time,
join,
};
uint64_t asyncactionint(AsyncAction a);
} // namespace bpftrace
......@@ -48,6 +48,19 @@ TEST(ast, probe_name_uprobe)
EXPECT_EQ(uprobe2.name(), "uprobe:/bin/sh:readline,uprobe:/bin/sh:somefunc");
}
TEST(ast, probe_name_usdt)
{
AttachPoint ap1("usdt", "/bin/sh", "probe1");
AttachPointList attach_points1 = { &ap1 };
Probe usdt1(&attach_points1, nullptr, nullptr);
EXPECT_EQ(usdt1.name(), "usdt:/bin/sh:probe1");
AttachPoint ap2("usdt", "/bin/sh", "probe2");
AttachPointList attach_points2 = { &ap1, &ap2 };
Probe usdt2(&attach_points2, nullptr, nullptr);
EXPECT_EQ(usdt2.name(), "usdt:/bin/sh:probe1,usdt:/bin/sh:probe2");
}
TEST(ast, attach_point_name)
{
AttachPoint ap1("kprobe", "sys_read");
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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