From 8058eb13a260db5f00022a29919039d90f459fea Mon Sep 17 00:00:00 2001 From: Sasha Goldshtein Date: Thu, 11 Feb 2016 02:17:22 -0800 Subject: [PATCH] Added ret probes --- tools/gentrace.py | 41 ++++++++++++++++++++++++++++++++--------- tools/gentrace_examples.txt | 42 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/tools/gentrace.py b/tools/gentrace.py index 5eb5df9..f630451 100755 --- a/tools/gentrace.py +++ b/tools/gentrace.py @@ -33,6 +33,9 @@ int PROBENAME(struct pt_regs *ctx SIGNATURE) if len(parts) < 3 or len(parts) > 6: raise ValueError("invalid specifier format") self.type = parts[0] # hist or raw + self.is_ret_probe = self.type.endswith("-ret") + if self.is_ret_probe: + self.type = self.type[:-len("-ret")] if self.type != "hist" and self.type != "raw": raise ValueError("unrecognized probe type") self.library = parts[1] @@ -45,8 +48,11 @@ int PROBENAME(struct pt_regs *ctx SIGNATURE) self.expr_type = parts[3] self.expr = parts[4] else: - self.expr_type = "u64" - self.expr = "1" + self.expr_type = \ + "u64" if not self.is_ret_probe else "int" + self.expr = "1" if not self.is_ret_probe else "@retval" + self.expr = self.expr.replace("@retval", + "(%s)ctx->ax" % self.expr_type) self.filter = None if len(parts) != 6 else parts[5] self.pid = pid self.probe_func_name = self.function + "_probe" @@ -95,13 +101,23 @@ bpf_probe_read(&__key.key, sizeof(__key.key), %s); def attach(self, bpf): self.bpf = bpf if len(self.library) > 0: - bpf.attach_uprobe(name=self.library, - sym=self.function, - fn_name=self.probe_func_name, - pid=self.pid or -1) + if self.is_ret_probe: + bpf.attach_uretprobe(name=self.library, + sym=self.function, + fn_name=self.probe_func_name, + pid=self.pid or -1) + else: + bpf.attach_uprobe(name=self.library, + sym=self.function, + fn_name=self.probe_func_name, + pid=self.pid or -1) else: - bpf.attach_kprobe(event=self.function, - fn_name=self.probe_func_name) + if self.is_ret_probe: + bpf.attach_kretprobe(event=self.function, + fn_name=self.probe_func_name) + else: + bpf.attach_kprobe(event=self.function, + fn_name=self.probe_func_name) def display(self): print(self.raw_spec) @@ -121,9 +137,10 @@ bpf_probe_read(&__key.key, sizeof(__key.key), %s); examples = """ Probe specifier syntax: - :[library]:function(signature)[:type:expr[:filter]] + [-ret]:[library]:function(signature)[:type:expr[:filter]] Where: -- collect raw data or a histogram of values + ret -- probe at function exit, only @retval is accessible library -- the library that contains the function (leave empty for kernel functions) function -- the function name to trace @@ -141,10 +158,16 @@ gentrace.py -p 1005 -s "raw:c:malloc(size_t size):size_t:size:size==16" Print a raw count of how many times process 1005 called malloc with an allocation size of 16 bytes +gentrace.py -s "raw-ret:c:gets():char*:@retval" + Snoop on all strings returned by gets() + gentrace.py -p 1005 -s "raw:c:write(int fd):int:fd" Print raw counts of how many times writes were issued to a particular file descriptor number, in process 1005 +gentrace.py -p 1005 -s "hist-ret:c:read()" + Print a histogram of error codes returned by read() in process 1005 + gentrace.py -s "hist:c:write(int fd, const void *buf, size_t count):size_t:count:fd==1" Print a histogram of buffer sizes passed to write() across all processes, where the file descriptor was 1 (STDOUT) diff --git a/tools/gentrace_examples.txt b/tools/gentrace_examples.txt index 94c59a3..b2019bb 100644 --- a/tools/gentrace_examples.txt +++ b/tools/gentrace_examples.txt @@ -152,11 +152,40 @@ raw:c:puts(char *str):char*:str It looks like the message "Press ENTER to start." was printed twice during the 10 seconds we were tracing. +What about reads? You could trace gets() across the system and print the +strings input by the user: + +# ./gentrace.py 10 1 -s "raw-ret:c:gets():char*:@retval" +[02:12:23] +raw-ret:c:gets():char*:@retval + COUNT EVENT + 1 (char*)ctx->ax = hi there + 1 (char*)ctx->ax = sasha + 1 (char*)ctx->ax = hello + +Similarly, we could get a histogram of the error codes returned by read(): + +# ./gentrace.py 10 1 -s "hist-ret:c:read()" +[02:15:36] +hist-ret:c:read() + (int)ctx->ax : count distribution + 0 -> 1 : 29 |****************************************| + 2 -> 3 : 11 |*************** | + 4 -> 7 : 0 | | + 8 -> 15 : 3 |**** | + 16 -> 31 : 2 |** | + 32 -> 63 : 22 |****************************** | + 64 -> 127 : 5 |****** | + 128 -> 255 : 0 | | + 256 -> 511 : 1 |* | + 512 -> 1023 : 1 |* | + 1024 -> 2047 : 0 | | + 2048 -> 4095 : 2 |** | + USAGE message: # ./gentrace.py -h - usage: gentrace.py [-h] [-p PID] [-z STRING_SIZE] [-s SPECIFIERS [SPECIFIERS ...]] [interval] [count] @@ -176,9 +205,10 @@ optional arguments: the probe specifiers (see examples below) Probe specifier syntax: - :[library]:function(signature)[:type:expr[:filter]] -Where: + [-ret]:[library]:function(signature)[:type:expr[:filter]] +Where: -- collect raw data or a histogram of values + ret -- probe at function exit, only @retval is accessible library -- the library that contains the function (leave empty for kernel functions) function -- the function name to trace @@ -196,10 +226,16 @@ gentrace.py -p 1005 -s "raw:c:malloc(size_t size):size_t:size:size==16" Print a raw count of how many times process 1005 called malloc with an allocation size of 16 bytes +gentrace.py -s "raw-ret:c:gets():char*:@retval" + Snoop on all strings returned by gets() + gentrace.py -p 1005 -s "raw:c:write(int fd):int:fd" Print raw counts of how many times writes were issued to a particular file descriptor number, in process 1005 +gentrace.py -p 1005 -s "hist-ret:c:read()" + Print a histogram of error codes returned by read() in process 1005 + gentrace.py -s "hist:c:write(int fd, const void *buf, size_t count):size_t:count:fd==1" Print a histogram of buffer sizes passed to write() across all processes, where the file descriptor was 1 (STDOUT) -- 2.7.4