Skip to content

Commit

Permalink
Support base + index * scale addressing for USDT arguments
Browse files Browse the repository at this point in the history
It turns out that some software will have USDT probe arguments
referencing memory using the full `nnn@(%basereg + %idxreg * scale`
syntax. This is represented as `nnn@(%basereg,%idxreg,scale)` in
the `NT_STAPSDT` note, encountered in building a recent version of
PostgreSQL on FC25.

This format is now recognized by the USDT parser, and the correct
BPF code is emitted to retrieve arguments that reference memory
using this full addressing syntax.`
  • Loading branch information
goldshtn committed Feb 19, 2017
1 parent 5f354e5 commit 8698bdb
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 77 deletions.
16 changes: 10 additions & 6 deletions src/cc/bcc_usdt.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,23 @@ struct bcc_usdt_location {
uint64_t address;
};

#define BCC_USDT_ARGUMENT_NONE 0x0
#define BCC_USDT_ARGUMENT_CONSTANT 0x1
#define BCC_USDT_ARGUMENT_DEREF_OFFSET 0x2
#define BCC_USDT_ARGUMENT_DEREF_IDENT 0x4
#define BCC_USDT_ARGUMENT_REGISTER_NAME 0x8
#define BCC_USDT_ARGUMENT_NONE 0x0
#define BCC_USDT_ARGUMENT_CONSTANT 0x1
#define BCC_USDT_ARGUMENT_DEREF_OFFSET 0x2
#define BCC_USDT_ARGUMENT_DEREF_IDENT 0x4
#define BCC_USDT_ARGUMENT_BASE_REGISTER_NAME 0x8
#define BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME 0x10
#define BCC_USDT_ARGUMENT_SCALE 0x20

struct bcc_usdt_argument {
int size;
int valid;
int constant;
int deref_offset;
const char *deref_ident;
const char *register_name;
const char *base_register_name;
const char *index_register_name;
int scale;
};

typedef void (*bcc_usdt_cb)(struct bcc_usdt *);
Expand Down
82 changes: 45 additions & 37 deletions src/cc/usdt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -360,48 +360,56 @@ void bcc_usdt_foreach(void *usdt, bcc_usdt_cb callback) {

int bcc_usdt_get_location(void *usdt, const char *probe_name,
int index, struct bcc_usdt_location *location) {
USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
USDT::Probe *probe = ctx->get(probe_name);
if (!probe)
return -1;
if (index < 0 || (size_t)index >= probe->num_locations())
return -1;
location->address = probe->address(index);
return 0;
USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
USDT::Probe *probe = ctx->get(probe_name);
if (!probe)
return -1;
if (index < 0 || (size_t)index >= probe->num_locations())
return -1;
location->address = probe->address(index);
return 0;
}

int bcc_usdt_get_argument(void *usdt, const char *probe_name,
int location_index, int argument_index,
struct bcc_usdt_argument *argument) {
USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
USDT::Probe *probe = ctx->get(probe_name);
if (!probe)
return -1;
if (argument_index < 0 || (size_t)argument_index >= probe->num_arguments())
return -1;
if (location_index < 0 || (size_t)location_index >= probe->num_locations())
return -1;
auto const &location = probe->location(location_index);
auto const &arg = location.arguments_[argument_index];
argument->size = arg.arg_size();
argument->valid = BCC_USDT_ARGUMENT_NONE;
if (arg.constant()) {
argument->valid |= BCC_USDT_ARGUMENT_CONSTANT;
argument->constant = *(arg.constant());
}
if (arg.deref_offset()) {
argument->valid |= BCC_USDT_ARGUMENT_DEREF_OFFSET;
argument->deref_offset = *(arg.deref_offset());
}
if (arg.deref_ident()) {
argument->valid |= BCC_USDT_ARGUMENT_DEREF_IDENT;
argument->deref_ident = arg.deref_ident()->c_str();
}
if (arg.register_name()) {
argument->valid |= BCC_USDT_ARGUMENT_REGISTER_NAME;
argument->register_name = arg.register_name()->c_str();
}
return 0;
USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
USDT::Probe *probe = ctx->get(probe_name);
if (!probe)
return -1;
if (argument_index < 0 || (size_t)argument_index >= probe->num_arguments())
return -1;
if (location_index < 0 || (size_t)location_index >= probe->num_locations())
return -1;
auto const &location = probe->location(location_index);
auto const &arg = location.arguments_[argument_index];
argument->size = arg.arg_size();
argument->valid = BCC_USDT_ARGUMENT_NONE;
if (arg.constant()) {
argument->valid |= BCC_USDT_ARGUMENT_CONSTANT;
argument->constant = *(arg.constant());
}
if (arg.deref_offset()) {
argument->valid |= BCC_USDT_ARGUMENT_DEREF_OFFSET;
argument->deref_offset = *(arg.deref_offset());
}
if (arg.deref_ident()) {
argument->valid |= BCC_USDT_ARGUMENT_DEREF_IDENT;
argument->deref_ident = arg.deref_ident()->c_str();
}
if (arg.base_register_name()) {
argument->valid |= BCC_USDT_ARGUMENT_BASE_REGISTER_NAME;
argument->base_register_name = arg.base_register_name()->c_str();
}
if (arg.index_register_name()) {
argument->valid |= BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME;
argument->index_register_name = arg.index_register_name()->c_str();
}
if (arg.scale()) {
argument->valid |= BCC_USDT_ARGUMENT_SCALE;
argument->scale = *(arg.scale());
}
return 0;
}

void bcc_usdt_foreach_uprobe(void *usdt, bcc_usdt_uprobe_cb callback) {
Expand Down
20 changes: 17 additions & 3 deletions src/cc/usdt.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ class Argument {
optional<int> constant_;
optional<int> deref_offset_;
optional<std::string> deref_ident_;
optional<std::string> register_name_;
optional<std::string> base_register_name_;
optional<std::string> index_register_name_;
optional<int> scale_;

bool get_global_address(uint64_t *address, const std::string &binpath,
const optional<int> &pid) const;
Expand All @@ -57,7 +59,13 @@ class Argument {
std::string ctype() const;

const optional<std::string> &deref_ident() const { return deref_ident_; }
const optional<std::string> &register_name() const { return register_name_; }
const optional<std::string> &base_register_name() const {
return base_register_name_;
}
const optional<std::string> &index_register_name() const {
return index_register_name_;
}
const optional<int> scale() const { return scale_; }
const optional<int> constant() const { return constant_; }
const optional<int> deref_offset() const { return deref_offset_; }

Expand All @@ -68,12 +76,18 @@ class ArgumentParser {
const char *arg_;
ssize_t cur_pos_;

void skip_whitespace_from(size_t pos);
void skip_until_whitespace_from(size_t pos);

protected:
virtual bool normalize_register(std::string *reg, int *reg_size) = 0;

ssize_t parse_register(ssize_t pos, std::string &name, int &size);
ssize_t parse_number(ssize_t pos, optional<int> *number);
ssize_t parse_identifier(ssize_t pos, optional<std::string> *ident);
ssize_t parse_register(ssize_t pos, Argument *dest);
ssize_t parse_base_register(ssize_t pos, Argument *dest);
ssize_t parse_index_register(ssize_t pos, Argument *dest);
ssize_t parse_scale(ssize_t pos, Argument *dest);
ssize_t parse_expr(ssize_t pos, Argument *dest);
ssize_t parse_1(ssize_t pos, Argument *dest);

Expand Down
90 changes: 73 additions & 17 deletions src/cc/usdt_args.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,28 @@ bool Argument::assign_to_local(std::ostream &stream,

if (!deref_offset_) {
tfm::format(stream, "%s = (%s)ctx->%s;", local_name, ctype(),
*register_name_);
*base_register_name_);
return true;
}

if (deref_offset_ && !deref_ident_) {
tfm::format(stream, "{ u64 __addr = ctx->%s + (%d)",
*base_register_name_, *deref_offset_);
if (index_register_name_) {
int scale = scale_.value_or(1);
tfm::format(stream, " + (ctx->%s * %d);", *index_register_name_, scale);
} else {
tfm::format(stream, ";");
}
tfm::format(stream,
"{ u64 __addr = ctx->%s + (%d); %s __res = 0x0; "
"%s __res = 0x0; "
"bpf_probe_read(&__res, sizeof(__res), (void *)__addr); "
"%s = __res; }",
*register_name_, *deref_offset_, ctype(), local_name);
ctype(), local_name);
return true;
}

if (deref_offset_ && deref_ident_ && *register_name_ == "ip") {
if (deref_offset_ && deref_ident_ && *base_register_name_ == "ip") {
uint64_t global_address;
if (!get_global_address(&global_address, binpath, pid))
return false;
Expand Down Expand Up @@ -109,32 +117,58 @@ ssize_t ArgumentParser::parse_identifier(ssize_t pos,
return pos;
}

ssize_t ArgumentParser::parse_register(ssize_t pos, Argument *dest) {
ssize_t ArgumentParser::parse_register(ssize_t pos, std::string &name,
int &size) {
ssize_t start = ++pos;
if (arg_[start - 1] != '%')
return -start;

while (isalnum(arg_[pos])) pos++;

std::string regname(arg_ + start, pos - start);
int regsize = 0;

if (!normalize_register(&regname, &regsize))
if (!normalize_register(&regname, &size))
return -start;

dest->register_name_ = regname;
name = regname;
return pos;
}

ssize_t ArgumentParser::parse_base_register(ssize_t pos, Argument *dest) {
int size;
std::string name;
ssize_t res = parse_register(pos, name, size);
if (res < 0)
return res;

dest->base_register_name_ = name;
if (!dest->arg_size_)
dest->arg_size_ = regsize;
dest->arg_size_ = size;

return pos;
return res;
}

ssize_t ArgumentParser::parse_index_register(ssize_t pos, Argument *dest) {
int size;
std::string name;
ssize_t res = parse_register(pos, name, size);
if (res < 0)
return res;

dest->index_register_name_ = name;

return res;
}

ssize_t ArgumentParser::parse_scale(ssize_t pos, Argument *dest) {
return parse_number(pos, &dest->scale_);
}

ssize_t ArgumentParser::parse_expr(ssize_t pos, Argument *dest) {
if (arg_[pos] == '$')
return parse_number(pos + 1, &dest->constant_);

if (arg_[pos] == '%')
return parse_register(pos, dest);
return parse_base_register(pos, dest);

if (isdigit(arg_[pos]) || arg_[pos] == '-') {
pos = parse_number(pos, &dest->deref_offset_);
Expand All @@ -154,10 +188,22 @@ ssize_t ArgumentParser::parse_expr(ssize_t pos, Argument *dest) {
if (arg_[pos] != '(')
return -pos;

pos = parse_register(pos + 1, dest);
pos = parse_base_register(pos + 1, dest);
if (pos < 0)
return pos;

if (arg_[pos] == ',') {
pos = parse_index_register(pos + 1, dest);
if (pos < 0)
return pos;

if (arg_[pos] == ',') {
pos = parse_scale(pos + 1, dest);
if (pos < 0)
return pos;
}
}

return (arg_[pos] == ')') ? pos + 1 : -pos;
}

Expand All @@ -180,23 +226,33 @@ void ArgumentParser::print_error(ssize_t pos) {
fputc('\n', stderr);
}

void ArgumentParser::skip_whitespace_from(size_t pos) {
while (isspace(arg_[pos])) pos++;
cur_pos_ = pos;
}

void ArgumentParser::skip_until_whitespace_from(size_t pos) {
while (arg_[pos] != '\0' && !isspace(arg_[pos]))
pos++;
cur_pos_ = pos;
}

bool ArgumentParser::parse(Argument *dest) {
if (done())
return false;

ssize_t res = parse_1(cur_pos_, dest);
if (res < 0) {
print_error(-res);
cur_pos_ = -res;
skip_whitespace_from(-res + 1);
return false;
}
if (!isspace(arg_[res]) && arg_[res] != '\0') {
print_error(res);
cur_pos_ = res;
skip_until_whitespace_from(res);
return false;
}
while (isspace(arg_[res])) res++;
cur_pos_ = res;
skip_whitespace_from(res);
return true;
}

Expand Down
10 changes: 7 additions & 3 deletions src/python/bcc/libbcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
lib.bpf_detach_kprobe.restype = ct.c_int
lib.bpf_detach_kprobe.argtypes = [ct.c_char_p]
lib.bpf_attach_uprobe.restype = ct.c_void_p
lib.bpf_attach_uprobe.argtypes = [ct.c_int, ct.c_int, ct.c_char_p, ct.c_char_p,
lib.bpf_attach_uprobe.argtypes = [ct.c_int, ct.c_int, ct.c_char_p, ct.c_char_p,
ct.c_ulonglong, ct.c_int, ct.c_int, ct.c_int, _CB_TYPE, ct.py_object]
lib.bpf_detach_uprobe.restype = ct.c_int
lib.bpf_detach_uprobe.argtypes = [ct.c_char_p]
Expand Down Expand Up @@ -197,7 +197,9 @@ class BCC_USDT_ARGUMENT_FLAGS(object):
CONSTANT = 0x1
DEREF_OFFSET = 0x2
DEREF_IDENT = 0x4
REGISTER_NAME = 0x8
BASE_REGISTER_NAME = 0x8
INDEX_REGISTER_NAME = 0x10
SCALE = 0x20

class bcc_usdt_argument(ct.Structure):
_fields_ = [
Expand All @@ -206,7 +208,9 @@ class bcc_usdt_argument(ct.Structure):
('constant', ct.c_int),
('deref_offset', ct.c_int),
('deref_ident', ct.c_char_p),
('register_name', ct.c_char_p)
('base_register_name', ct.c_char_p),
('index_register_name', ct.c_char_p),
('scale', ct.c_int)
]

_USDT_CB = ct.CFUNCTYPE(None, ct.POINTER(bcc_usdt))
Expand Down
Loading

0 comments on commit 8698bdb

Please sign in to comment.