Skip to content

Commit

Permalink
Add basic USDT probe support for powerpc64
Browse files Browse the repository at this point in the history
This adds basic support for parsing USDT arguments having
operands in the powerpc64 format. The following operand
formats are supported for now:
  * iNUM or i-NUM
  * REG or %rREG
  * NUM(REG) or -NUM(REG) or NUM(%rREG) or -NUM(%rREG)
  * REG,REG or %rREG,%rREG

Where NUM represents a numeric constant and REG represents
a general-purpose register.

Signed-off-by: Sandipan Das <[email protected]>
  • Loading branch information
sandip4n committed Sep 26, 2017
1 parent 899d3e9 commit 1f0f7b6
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 44 deletions.
34 changes: 20 additions & 14 deletions src/cc/usdt.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,37 +74,34 @@ class Argument {
const optional<int> deref_offset() const { return deref_offset_; }

friend class ArgumentParser;
friend class ArgumentParser_powerpc64;
friend class ArgumentParser_x64;
};

class ArgumentParser {
protected:
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_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);

void print_error(ssize_t pos);

public:
bool parse(Argument *dest);
virtual bool parse(Argument *dest) = 0;
bool done() { return cur_pos_ < 0 || arg_[cur_pos_] == '\0'; }

ArgumentParser(const char *arg) : arg_(arg), cur_pos_(0) {}
};

class ArgumentParser_powerpc64 : public ArgumentParser {
public:
bool parse(Argument *dest);
ArgumentParser_powerpc64(const char *arg) : ArgumentParser(arg) {}
};

class ArgumentParser_x64 : public ArgumentParser {
private:
enum Register {
REG_A,
REG_B,
Expand Down Expand Up @@ -133,8 +130,17 @@ class ArgumentParser_x64 : public ArgumentParser {
static const std::unordered_map<std::string, RegInfo> registers_;
bool normalize_register(std::string *reg, int *reg_size);
void reg_to_name(std::string *norm, Register reg);
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_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);

public:
bool parse(Argument *dest);
ArgumentParser_x64(const char *arg) : ArgumentParser(arg) {}
};

Expand Down
8 changes: 7 additions & 1 deletion src/cc/usdt/usdt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@
namespace USDT {

Location::Location(uint64_t addr, const char *arg_fmt) : address_(addr) {
#ifdef __powerpc64__
ArgumentParser_powerpc64 parser(arg_fmt);
#elif defined(__x86_64__)
ArgumentParser_x64 parser(arg_fmt);
#else
#error "bcc does not support this platform yet"
#endif
while (!parser.done()) {
Argument arg;
if (!parser.parse(&arg))
Expand Down Expand Up @@ -173,7 +179,7 @@ bool Probe::usdt_getarg(std::ostream &stream) {
return false;
stream << "\n return 0;\n}\n";
} else {
stream << " switch(ctx->ip) {\n";
stream << " switch(PT_REGS_IP(ctx)) {\n";
for (Location &location : locations_) {
uint64_t global_address;

Expand Down
121 changes: 92 additions & 29 deletions src/cc/usdt/usdt_args.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/
#include <unordered_map>
#include <regex>

#include "syms.h"
#include "usdt.h"
Expand Down Expand Up @@ -112,16 +113,96 @@ bool Argument::assign_to_local(std::ostream &stream,
return false;
}

ssize_t ArgumentParser::parse_number(ssize_t pos, optional<int> *result) {
void ArgumentParser::print_error(ssize_t pos) {
fprintf(stderr, "Parse error:\n %s\n", arg_);
for (ssize_t i = 0; i < pos + 4; ++i) fputc('-', stderr);
fputc('^', stderr);
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_powerpc64::parse(Argument *dest) {
if (done())
return false;

bool matched;
std::smatch matches;
std::string arg_str(&arg_[cur_pos_]);
std::regex arg_n_regex("^(\\-?[1248])\\@");
// Operands with constants of form iNUM or i-NUM
std::regex arg_op_regex_const("^i(\\-?[0-9]+)( +|$)");
// Operands with register only of form REG or %rREG
std::regex arg_op_regex_reg("^(?:%r)?([1-2]?[0-9]|3[0-1])( +|$)");
// Operands with a base register and an offset of form
// NUM(REG) or -NUM(REG) or NUM(%rREG) or -NUM(%rREG)
std::regex arg_op_regex_breg_off(
"^(\\-?[0-9]+)\\((?:%r)?([1-2]?[0-9]|3[0-1])\\)( +|$)");
// Operands with a base register and an index register
// of form REG,REG or %rREG,%rREG
std::regex arg_op_regex_breg_ireg(
"^(?:%r)?([1-2]?[0-9]|3[0-1])\\,(?:%r)?([1-2]?[0-9]|3[0-1])( +|$)");

matched = std::regex_search(arg_str, matches, arg_n_regex);
if (matched) {
dest->arg_size_ = stoi(matches.str(1));
cur_pos_ += matches.length(0);
arg_str = &arg_[cur_pos_];

if (std::regex_search(arg_str, matches, arg_op_regex_const)) {
dest->constant_ = stoi(matches.str(1));
} else if (std::regex_search(arg_str, matches, arg_op_regex_reg)) {
dest->base_register_name_ = "gpr[" + matches.str(1) + "]";
} else if (std::regex_search(arg_str, matches, arg_op_regex_breg_off)) {
dest->deref_offset_ = stoi(matches.str(1));
dest->base_register_name_ = "gpr[" + matches.str(2) + "]";
} else if (std::regex_search(arg_str, matches, arg_op_regex_breg_ireg)) {
dest->deref_offset_ = 0; // In powerpc64, such operands contain a base
// register and an index register which are
// part of an indexed load/store operation.
// Even if no offset value is present, this
// is required by Argument::assign_to_local()
// in order to generate code for reading the
// argument. So, this is set to zero.
dest->base_register_name_ = "gpr[" + matches.str(1) + "]";
dest->index_register_name_ = "gpr[" + matches.str(2) + "]";
dest->scale_ = abs(*dest->arg_size_);
} else {
matched = false;
}
}

if (!matched) {
print_error(cur_pos_);
skip_until_whitespace_from(cur_pos_);
skip_whitespace_from(cur_pos_);
return false;
}

cur_pos_ += matches.length(0);
skip_whitespace_from(cur_pos_);
return true;
}

ssize_t ArgumentParser_x64::parse_number(ssize_t pos, optional<int> *result) {
char *endp;
int number = strtol(arg_ + pos, &endp, 0);
if (endp > arg_ + pos)
*result = number;
return endp - arg_;
}

ssize_t ArgumentParser::parse_identifier(ssize_t pos,
optional<std::string> *result) {
ssize_t ArgumentParser_x64::parse_identifier(ssize_t pos,
optional<std::string> *result) {
if (isalpha(arg_[pos]) || arg_[pos] == '_') {
ssize_t start = pos++;
while (isalnum(arg_[pos]) || arg_[pos] == '_') pos++;
Expand All @@ -131,8 +212,8 @@ ssize_t ArgumentParser::parse_identifier(ssize_t pos,
return pos;
}

ssize_t ArgumentParser::parse_register(ssize_t pos, std::string &name,
int &size) {
ssize_t ArgumentParser_x64::parse_register(ssize_t pos, std::string &name,
int &size) {
ssize_t start = ++pos;
if (arg_[start - 1] != '%')
return -start;
Expand All @@ -147,7 +228,7 @@ ssize_t ArgumentParser::parse_register(ssize_t pos, std::string &name,
return pos;
}

ssize_t ArgumentParser::parse_base_register(ssize_t pos, Argument *dest) {
ssize_t ArgumentParser_x64::parse_base_register(ssize_t pos, Argument *dest) {
int size;
std::string name;
ssize_t res = parse_register(pos, name, size);
Expand All @@ -161,7 +242,7 @@ ssize_t ArgumentParser::parse_base_register(ssize_t pos, Argument *dest) {
return res;
}

ssize_t ArgumentParser::parse_index_register(ssize_t pos, Argument *dest) {
ssize_t ArgumentParser_x64::parse_index_register(ssize_t pos, Argument *dest) {
int size;
std::string name;
ssize_t res = parse_register(pos, name, size);
Expand All @@ -173,11 +254,11 @@ ssize_t ArgumentParser::parse_index_register(ssize_t pos, Argument *dest) {
return res;
}

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

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

Expand Down Expand Up @@ -221,7 +302,7 @@ ssize_t ArgumentParser::parse_expr(ssize_t pos, Argument *dest) {
return (arg_[pos] == ')') ? pos + 1 : -pos;
}

ssize_t ArgumentParser::parse_1(ssize_t pos, Argument *dest) {
ssize_t ArgumentParser_x64::parse_1(ssize_t pos, Argument *dest) {
if (isdigit(arg_[pos]) || arg_[pos] == '-') {
optional<int> asize;
ssize_t m = parse_number(pos, &asize);
Expand All @@ -233,25 +314,7 @@ ssize_t ArgumentParser::parse_1(ssize_t pos, Argument *dest) {
return parse_expr(pos, dest);
}

void ArgumentParser::print_error(ssize_t pos) {
fprintf(stderr, "Parse error:\n %s\n", arg_);
for (ssize_t i = 0; i < pos + 4; ++i) fputc('-', stderr);
fputc('^', stderr);
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) {
bool ArgumentParser_x64::parse(Argument *dest) {
if (done())
return false;

Expand Down

0 comments on commit 1f0f7b6

Please sign in to comment.