Created
October 18, 2021 14:39
-
-
Save sitano/3bd9701d61037d1ac3bb6f7f1de6ca0c to your computer and use it in GitHub Desktop.
Example of using BPFTRACE with UPROBES for picking puts calls in Ruby VM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* For a Ruby program: | |
* | |
* $ cat prog.rb | |
* | |
* Thread.new { | |
* while true | |
* puts "2222222222222222222222222222222222222222222222222222222222222222222222" | |
* sleep 0.2 | |
* end | |
* } | |
* | |
* while true | |
* puts "1" | |
* sleep 0.2 | |
* end | |
* | |
* $ ruby t.rb | |
* | |
* We can trace its internal as in the following example: | |
* | |
* $ sudo bpftrace prog.bt | |
* | |
* | |
**/ | |
// VALUE | |
#define VALUE uintptr_t | |
// RBasic | |
struct RBasic { | |
VALUE flags; | |
const VALUE klass; | |
}; | |
enum ruby_value_type { | |
RUBY_T_NONE = 0x00, | |
RUBY_T_OBJECT = 0x01, | |
RUBY_T_CLASS = 0x02, | |
RUBY_T_MODULE = 0x03, | |
RUBY_T_FLOAT = 0x04, | |
RUBY_T_STRING = 0x05, | |
// ... | |
RUBY_T_MASK = 0x1f | |
}; | |
#define T_STRING RUBY_T_STRING | |
#define RB_BUILTIN_TYPE(x) (int32)(((struct RBasic*)(x))->flags & RUBY_T_MASK) | |
// RString | |
enum { | |
RUBY_FL_USHIFT = 12 | |
}; | |
enum { | |
RUBY_FL_USER0 = 0x1000, | |
RUBY_FL_USER1 = 0x2000, | |
RUBY_FL_USER2 = 0x4000, | |
RUBY_FL_USER3 = 0x8000, | |
RUBY_FL_USER4 = 0x10000, | |
RUBY_FL_USER5 = 0x20000, | |
RUBY_FL_USER6 = 0x40000, | |
RUBY_FL_USER17= 0x20000000 | |
}; | |
enum { | |
RSTRING_NOEMBED = RUBY_FL_USER1, | |
RSTRING_EMBED_LEN_MASK = (RUBY_FL_USER2|RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5|RUBY_FL_USER6), | |
RSTRING_EMBED_LEN_SHIFT = (RUBY_FL_USHIFT+2), | |
RSTRING_EMBED_LEN_MAX = (int)((sizeof(VALUE)*3)/sizeof(char)-1), | |
RSTRING_FSTR = RUBY_FL_USER17, | |
RSTRING_ENUM_END | |
}; | |
struct RString { | |
struct RBasic basic; | |
union { | |
struct { | |
long len; | |
char *ptr; | |
union { | |
long capa; | |
VALUE shared; | |
} aux; | |
} heap; | |
char ary[RSTRING_EMBED_LEN_MAX + 1]; | |
} as; | |
}; | |
// #define RSTRING_EMBED_LEN(str) \ | |
// (long)((RBASIC(str)->flags >> RSTRING_EMBED_LEN_SHIFT) & \ | |
// (RSTRING_EMBED_LEN_MASK >> RSTRING_EMBED_LEN_SHIFT)) | |
// #define RSTRING_LEN(str) \ | |
// (!(RBASIC(str)->flags & RSTRING_NOEMBED) ? \ | |
// RSTRING_EMBED_LEN(str) : \ | |
// RSTRING(str)->as.heap.len) | |
// #define RSTRING_PTR(str) \ | |
// (!(RBASIC(str)->flags & RSTRING_NOEMBED) ? \ | |
// RSTRING(str)->as.ary : \ | |
// RSTRING(str)->as.heap.ptr) | |
// #define RSTRING_END(str) \ | |
// (!(RBASIC(str)->flags & RSTRING_NOEMBED) ? \ | |
// (RSTRING(str)->as.ary + RSTRING_EMBED_LEN(str)) : \ | |
// (RSTRING(str)->as.heap.ptr + RSTRING(str)->as.heap.len)) | |
// #define RSTRING_LENINT(str) rb_long2int(RSTRING_LEN(str)) | |
// #define RSTRING_GETMEM(str, ptrvar, lenvar) \ | |
// (!(RBASIC(str)->flags & RSTRING_NOEMBED) ? \ | |
// ((ptrvar) = RSTRING(str)->as.ary, (lenvar) = RSTRING_EMBED_LEN(str)) : \ | |
// ((ptrvar) = RSTRING(str)->as.heap.ptr, (lenvar) = RSTRING(str)->as.heap.len)) | |
enum { | |
RARRAY_EMBED_LEN_MAX = 3, | |
RARRAY_EMBED_FLAG = RUBY_FL_USER1, | |
/* RUBY_FL_USER2 is for ELTS_SHARED */ | |
RARRAY_EMBED_LEN_MASK = (RUBY_FL_USER4|RUBY_FL_USER3), | |
RARRAY_EMBED_LEN_SHIFT = (RUBY_FL_USHIFT+3), | |
RARRAY_ENUM_END | |
}; | |
uprobe:/home/sitano/.asdf/installs/ruby/2.4.10/lib/libruby.so:rb_io_puts /arg0 > 0/ { | |
// VALUE rb_io_puts(int argc, const VALUE *argv, VALUE out) | |
$val = *(arg1); | |
$flags = ((struct RBasic*)($val))->flags; | |
if (($flags & RUBY_T_MASK) == RUBY_T_STRING) { | |
$noembed = $flags & RSTRING_NOEMBED; | |
if (!$noembed) { | |
// RSTRING_PTR | |
$ptr0 = ((struct RString *)($val)) + sizeof(struct RBasic); | |
$str = (((struct RString *)($val))->as.ary); | |
// RSTRING_LEN | |
$len = ($flags >> RSTRING_EMBED_LEN_SHIFT) & (RSTRING_EMBED_LEN_MASK >> RSTRING_EMBED_LEN_SHIFT); | |
// OMG: https://github.com/iovisor/bpftrace/issues/553 - there is no & op | |
// OMG: char[] and int8 * are completelly diferrent types | |
// printf("[%d][%d]: %p of len %d: %s\n", pid, tid, (int64 *)((struct RString *)($val))->as.ary, $len, $str); | |
printf("[%d][%d]: %p of len %d: %s\n", pid, tid, $ptr0, $len, str($ptr0, $len)); | |
} else { | |
// RSTRING_PTR | |
$ptr = ((struct RString *)($val))->as.heap.ptr; | |
// RSTRING_LEN | |
$len = ((struct RString *)($val))->as.heap.len; | |
printf("[%d][%d]: %p of len %d: %s\n", pid, tid, $ptr, $len, str($ptr, $len)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment