Skip to content

Commit

Permalink
argdist: STRCMP helper function
Browse files Browse the repository at this point in the history
argdist filter expressions can now use the STRCMP helper
function to compare strings. The first string must be a
compile-time constant literal string, and the second string
can be determined at runtime. This is a workaround until
BPF introduces a kernel builtin for strcmp.

Example:

```
argdist -H 'r:c:open(char *file):u64:$latency:STRCMP("test.txt",file)'
```
  • Loading branch information
goldshtn committed Oct 17, 2016
1 parent f4797b0 commit c8f752f
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 1 deletion.
4 changes: 4 additions & 0 deletions man/man8/argdist.8
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ Only parameter values that pass the filter will be collected. This is any valid
C expression that refers to the parameter values, such as "fd == 1 && length > 16".
The $entry, $retval, and $latency variables can be used here as well, in return
probes.
The filter expression may also use the STRCMP pseudo-function to compare
a predefined string to a string argument. For example: STRCMP("test.txt", file).
The order of arguments is important: the first argument MUST be a quoted
literal string, and the second argument can be a runtime string.
.TP
.B [label]
The label that will be displayed when printing the probed values. By default,
Expand Down
27 changes: 26 additions & 1 deletion tools/argdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

class Probe(object):
next_probe_index = 0
streq_index = 0
aliases = {"$PID": "bpf_get_current_pid_tgid()"}

def _substitute_aliases(self, expr):
Expand Down Expand Up @@ -174,6 +175,7 @@ def _parse_exprs(self, exprs):

def __init__(self, tool, type, specifier):
self.usdt_ctx = None
self.streq_functions = ""
self.pid = tool.args.pid
self.cumulative = tool.args.cumulative or False
self.raw_spec = specifier
Expand Down Expand Up @@ -242,9 +244,32 @@ def _enable_usdt_probe(self):
self.usdt_ctx.enable_probe(
self.function, self.probe_func_name)

def _generate_streq_function(self, string):
fname = "streq_%d" % Probe.streq_index
Probe.streq_index += 1
self.streq_functions += """
static inline bool %s(char const *ignored, char const *str) {
char needle[] = %s;
char haystack[sizeof(needle)];
bpf_probe_read(&haystack, sizeof(haystack), (void *)str);
for (int i = 0; i < sizeof(needle); ++i) {
if (needle[i] != haystack[i]) {
return false;
}
}
return true;
}
""" % (fname, string)
return fname

def _substitute_exprs(self):
def repl(expr):
expr = self._substitute_aliases(expr)
matches = re.finditer('STRCMP\\(("[^"]+\\")', expr)
for match in matches:
string = match.group(1)
fname = self._generate_streq_function(string)
expr = expr.replace("STRCMP", fname, 1)
return expr.replace("$retval", "PT_REGS_RC(ctx)")
for i in range(0, len(self.exprs)):
self.exprs[i] = repl(self.exprs[i])
Expand Down Expand Up @@ -370,7 +395,7 @@ def generate_text(self):
program = program.replace("COLLECT", collect)
program = program.replace("PREFIX", prefix)

return program
return self.streq_functions + program

def _attach_u(self):
libpath = BPF.find_library(self.library)
Expand Down
39 changes: 39 additions & 0 deletions tools/argdist_example.txt
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,45 @@ t:net:net_dev_start_xmit():u16:args->protocol
Note that to discover the format of the net:net_dev_start_xmit tracepoint, you
use the tplist tool (tplist -v net:net_dev_start_xmit).


Occasionally, it is useful to filter certain expressions by string. This is not
trivially supported by BPF, but argdist provides a STRCMP helper you can use in
filter expressions. For example, to get a histogram of latencies opening a
specific file, run this:

# argdist -c -H 'r:c:open(char *file):u64:$latency/1000:STRCMP("test.txt",$entry(file))'
[02:16:38]
[02:16:39]
[02:16:40]
$latency/1000 : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 0 | |
16 -> 31 : 2 |****************************************|
[02:16:41]
$latency/1000 : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 1 |********** |
16 -> 31 : 4 |****************************************|
[02:16:42]
$latency/1000 : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 1 |******** |
16 -> 31 : 5 |****************************************|
[02:16:43]
$latency/1000 : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 1 |******** |
16 -> 31 : 5 |****************************************|


Here's a final example that finds how many write() system calls are performed
by each process on the system:

Expand Down

0 comments on commit c8f752f

Please sign in to comment.