blob: 738a7175ee6018003348b2edfcb61e170d284dc8 [file] [log] [blame]
Sasha Goldshtein85384852016-02-12 01:29:39 -08001#!/usr/bin/env python
2#
Sasha Goldshtein7df65da2016-02-14 05:12:27 -08003# argdist Trace a function and display a distribution of its
Sasha Goldshteinfd60d552016-03-01 12:15:34 -08004# parameter values as a histogram or frequency count.
Sasha Goldshtein85384852016-02-12 01:29:39 -08005#
Sasha Goldshtein7df65da2016-02-14 05:12:27 -08006# USAGE: argdist [-h] [-p PID] [-z STRING_SIZE] [-i INTERVAL]
Sasha Goldshteinfd60d552016-03-01 12:15:34 -08007# [-n COUNT] [-v] [-T TOP]
8# [-C specifier [specifier ...]]
9# [-H specifier [specifier ...]]
10# [-I header [header ...]]
Sasha Goldshtein85384852016-02-12 01:29:39 -080011#
12# Licensed under the Apache License, Version 2.0 (the "License")
13# Copyright (C) 2016 Sasha Goldshtein.
14
Sasha Goldshteinc08c4312016-03-21 03:52:09 -070015from bcc import BPF, Tracepoint, Perf
Sasha Goldshtein85384852016-02-12 01:29:39 -080016from time import sleep, strftime
17import argparse
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080018import re
Sasha Goldshteinc9551302016-02-21 02:21:46 -080019import traceback
Sasha Goldshteinfd60d552016-03-01 12:15:34 -080020import os
Sasha Goldshteinc9551302016-02-21 02:21:46 -080021import sys
Sasha Goldshtein85384852016-02-12 01:29:39 -080022
23class Specifier(object):
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080024 probe_text = """
Sasha Goldshtein85384852016-02-12 01:29:39 -080025DATA_DECL
26
27int PROBENAME(struct pt_regs *ctx SIGNATURE)
28{
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080029 PREFIX
Sasha Goldshtein85384852016-02-12 01:29:39 -080030 PID_FILTER
Sasha Goldshtein85384852016-02-12 01:29:39 -080031 if (!(FILTER)) return 0;
Sasha Goldshteincc27edf2016-02-14 03:49:01 -080032 KEY_EXPR
Sasha Goldshtein85384852016-02-12 01:29:39 -080033 COLLECT
34 return 0;
35}
36"""
37 next_probe_index = 0
Sasha Goldshtein5e4e1f42016-02-12 06:52:19 -080038 aliases = { "$PID": "bpf_get_current_pid_tgid()" }
Sasha Goldshtein7df65da2016-02-14 05:12:27 -080039 auto_includes = {
Sasha Goldshteinfd60d552016-03-01 12:15:34 -080040 "linux/time.h" : ["time"],
41 "linux/fs.h" : ["fs", "file"],
42 "linux/blkdev.h" : ["bio", "request"],
43 "linux/slab.h" : ["alloc"],
44 "linux/netdevice.h" : ["sk_buff", "net_device"]
Sasha Goldshtein7df65da2016-02-14 05:12:27 -080045 }
46
47 @staticmethod
48 def generate_auto_includes(specifiers):
49 headers = ""
50 for header, keywords in Specifier.auto_includes.items():
51 for keyword in keywords:
Sasha Goldshteinc9551302016-02-21 02:21:46 -080052 for specifier in specifiers:
Sasha Goldshtein7df65da2016-02-14 05:12:27 -080053 if keyword in specifier:
54 headers += "#include <%s>\n" \
55 % header
56 return headers
Sasha Goldshtein5e4e1f42016-02-12 06:52:19 -080057
58 def _substitute_aliases(self, expr):
59 if expr is None:
60 return expr
61 for alias, subst in Specifier.aliases.items():
62 expr = expr.replace(alias, subst)
63 return expr
Sasha Goldshtein85384852016-02-12 01:29:39 -080064
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080065 def _parse_signature(self):
66 params = map(str.strip, self.signature.split(','))
67 self.param_types = {}
68 for param in params:
69 # If the type is a pointer, the * can be next to the
70 # param name. Other complex types like arrays are not
71 # supported right now.
72 index = param.rfind('*')
73 index = index if index != -1 else param.rfind(' ')
74 param_type = param[0:index+1].strip()
75 param_name = param[index+1:].strip()
76 self.param_types[param_name] = param_type
77
78 entry_probe_text = """
79int PROBENAME(struct pt_regs *ctx SIGNATURE)
80{
81 u32 pid = bpf_get_current_pid_tgid();
82 PID_FILTER
83 COLLECT
84 return 0;
85}
86"""
87
88 def _generate_entry(self):
89 self.entry_probe_func = self.probe_func_name + "_entry"
90 text = self.entry_probe_text
91 text = text.replace("PROBENAME", self.entry_probe_func)
92 text = text.replace("SIGNATURE",
93 "" if len(self.signature) == 0 else ", " + self.signature)
94 pid_filter = "" if self.is_user or self.pid is None \
95 else "if (pid != %d) { return 0; }" % self.pid
96 text = text.replace("PID_FILTER", pid_filter)
97 collect = ""
98 for pname in self.args_to_probe:
Sasha Goldshteine3501152016-02-13 03:56:29 -080099 param_hash = self.hashname_prefix + pname
100 if pname == "__latency":
101 collect += """
102u64 __time = bpf_ktime_get_ns();
103%s.update(&pid, &__time);
104""" % param_hash
105 else:
106 collect += "%s.update(&pid, &%s);\n" % \
107 (param_hash, pname)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800108 text = text.replace("COLLECT", collect)
109 return text
110
111 def _generate_entry_probe(self):
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800112 # Any $entry(name) expressions result in saving that argument
113 # when entering the function.
114 self.args_to_probe = set()
115 regex = r"\$entry\((\w+)\)"
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800116 for expr in self.exprs:
117 for arg in re.finditer(regex, expr):
118 self.args_to_probe.add(arg.group(1))
Sasha Goldshteine3501152016-02-13 03:56:29 -0800119 for arg in re.finditer(regex, self.filter):
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800120 self.args_to_probe.add(arg.group(1))
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800121 if any(map(lambda expr: "$latency" in expr, self.exprs)) or \
122 "$latency" in self.filter:
Sasha Goldshteine3501152016-02-13 03:56:29 -0800123 self.args_to_probe.add("__latency")
124 self.param_types["__latency"] = "u64" # nanoseconds
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800125 for pname in self.args_to_probe:
126 if pname not in self.param_types:
127 raise ValueError("$entry(%s): no such param" \
128 % arg)
129
130 self.hashname_prefix = "%s_param_" % self.probe_hash_name
131 text = ""
132 for pname in self.args_to_probe:
133 # Each argument is stored in a separate hash that is
134 # keyed by pid.
135 text += "BPF_HASH(%s, u32, %s);\n" % \
136 (self.hashname_prefix + pname,
137 self.param_types[pname])
138 text += self._generate_entry()
139 return text
140
141 def _generate_retprobe_prefix(self):
142 # After we're done here, there are __%s_val variables for each
143 # argument we needed to probe using $entry(name), and they all
144 # have values (which isn't necessarily the case if we missed
145 # the method entry probe).
146 text = "u32 __pid = bpf_get_current_pid_tgid();\n"
147 self.param_val_names = {}
148 for pname in self.args_to_probe:
149 val_name = "__%s_val" % pname
150 text += "%s *%s = %s.lookup(&__pid);\n" % \
151 (self.param_types[pname], val_name,
152 self.hashname_prefix + pname)
153 text += "if (%s == 0) { return 0 ; }\n" % val_name
154 self.param_val_names[pname] = val_name
155 return text
156
157 def _replace_entry_exprs(self):
158 for pname, vname in self.param_val_names.items():
Sasha Goldshteine3501152016-02-13 03:56:29 -0800159 if pname == "__latency":
160 entry_expr = "$latency"
161 val_expr = "(bpf_ktime_get_ns() - *%s)" % vname
162 else:
163 entry_expr = "$entry(%s)" % pname
164 val_expr = "(*%s)" % vname
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800165 for i in range(0, len(self.exprs)):
166 self.exprs[i] = self.exprs[i].replace(
167 entry_expr, val_expr)
Sasha Goldshteine3501152016-02-13 03:56:29 -0800168 self.filter = self.filter.replace(entry_expr,
169 val_expr)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800170
171 def _attach_entry_probe(self):
172 if self.is_user:
173 self.bpf.attach_uprobe(name=self.library,
174 sym=self.function,
175 fn_name=self.entry_probe_func,
176 pid=self.pid or -1)
177 else:
178 self.bpf.attach_kprobe(event=self.function,
179 fn_name=self.entry_probe_func)
180
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800181 def _bail(self, error):
182 raise ValueError("error parsing probe '%s': %s" %
183 (self.raw_spec, error))
184
185 def _validate_specifier(self):
186 # Everything after '#' is the probe label, ignore it
187 spec = self.raw_spec.split('#')[0]
188 parts = spec.strip().split(':')
189 if len(parts) < 3:
190 self._bail("at least the probe type, library, and " +
191 "function signature must be specified")
192 if len(parts) > 6:
193 self._bail("extraneous ':'-separated parts detected")
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800194 if parts[0] not in ["r", "p", "t"]:
195 self._bail("probe type must be 'p', 'r', or 't', " +
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800196 "but got '%s'" % parts[0])
197 if re.match(r"\w+\(.*\)", parts[2]) is None:
198 self._bail(("function signature '%s' has an invalid " +
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800199 "format") % parts[2])
200
201 def _parse_expr_types(self, expr_types):
202 if len(expr_types) == 0:
203 self._bail("no expr types specified")
204 self.expr_types = expr_types.split(',')
205
206 def _parse_exprs(self, exprs):
207 if len(exprs) == 0:
208 self._bail("no exprs specified")
209 self.exprs = exprs.split(',')
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800210
Sasha Goldshtein85384852016-02-12 01:29:39 -0800211 def __init__(self, type, specifier, pid):
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800212 self.raw_spec = specifier
213 self._validate_specifier()
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800214
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800215 spec_and_label = specifier.split('#')
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800216 self.label = spec_and_label[1] \
217 if len(spec_and_label) == 2 else None
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800218
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800219 parts = spec_and_label[0].strip().split(':')
Sasha Goldshtein85384852016-02-12 01:29:39 -0800220 self.type = type # hist or freq
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800221 self.probe_type = parts[0]
Sasha Goldshtein85384852016-02-12 01:29:39 -0800222 fparts = parts[2].split('(')
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800223 self.function = fparts[0].strip()
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800224 if self.probe_type == "t":
225 self.library = "" # kernel
226 self.tp_category = parts[1]
227 self.tp_event = self.function
Sasha Goldshteinc08c4312016-03-21 03:52:09 -0700228 self.tp = Tracepoint.enable_tracepoint(
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800229 self.tp_category, self.tp_event)
230 self.function = "perf_trace_" + self.function
231 else:
232 self.library = parts[1]
233 self.is_user = len(self.library) > 0
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800234 self.signature = fparts[1].strip()[:-1]
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800235 self._parse_signature()
236
237 # If the user didn't specify an expression to probe, we probe
238 # the retval in a ret probe, or simply the value "1" otherwise.
Sasha Goldshtein85384852016-02-12 01:29:39 -0800239 self.is_default_expr = len(parts) < 5
240 if not self.is_default_expr:
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800241 self._parse_expr_types(parts[3])
242 self._parse_exprs(parts[4])
243 if len(self.exprs) != len(self.expr_types):
244 self._bail("mismatched # of exprs and types")
245 if self.type == "hist" and len(self.expr_types) > 1:
246 self._bail("histograms can only have 1 expr")
Sasha Goldshtein85384852016-02-12 01:29:39 -0800247 else:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800248 if not self.probe_type == "r" and self.type == "hist":
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800249 self._bail("histograms must have expr")
250 self.expr_types = \
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800251 ["u64" if not self.probe_type == "r" else "int"]
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800252 self.exprs = \
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800253 ["1" if not self.probe_type == "r" else "$retval"]
Sasha Goldshteine3501152016-02-13 03:56:29 -0800254 self.filter = "" if len(parts) != 6 else parts[5]
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800255 self._substitute_exprs()
256
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800257 # Do we need to attach an entry probe so that we can collect an
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800258 # argument that is required for an exit (return) probe?
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800259 def check(expr):
260 keywords = ["$entry", "$latency"]
261 return any(map(lambda kw: kw in expr, keywords))
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800262 self.entry_probe_required = self.probe_type == "r" and \
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800263 (any(map(check, self.exprs)) or check(self.filter))
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800264
Sasha Goldshtein85384852016-02-12 01:29:39 -0800265 self.pid = pid
266 self.probe_func_name = "%s_probe%d" % \
267 (self.function, Specifier.next_probe_index)
268 self.probe_hash_name = "%s_hash%d" % \
269 (self.function, Specifier.next_probe_index)
270 Specifier.next_probe_index += 1
271
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800272 def _substitute_exprs(self):
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800273 def repl(expr):
274 expr = self._substitute_aliases(expr)
275 return expr.replace("$retval", "ctx->ax")
276 for i in range(0, len(self.exprs)):
277 self.exprs[i] = repl(self.exprs[i])
278 self.filter = repl(self.filter)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800279
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800280 def _is_string(self, expr_type):
281 return expr_type == "char*" or expr_type == "char *"
Sasha Goldshtein85384852016-02-12 01:29:39 -0800282
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800283 def _generate_hash_field(self, i):
284 if self._is_string(self.expr_types[i]):
285 return "struct __string_t v%d;\n" % i
286 else:
287 return "%s v%d;\n" % (self.expr_types[i], i)
288
289 def _generate_field_assignment(self, i):
290 if self._is_string(self.expr_types[i]):
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800291 return " bpf_probe_read(" + \
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800292 "&__key.v%d.s, sizeof(__key.v%d.s), %s);\n" % \
293 (i, i, self.exprs[i])
294 else:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800295 return " __key.v%d = %s;\n" % (i, self.exprs[i])
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800296
297 def _generate_hash_decl(self):
298 if self.type == "hist":
299 return "BPF_HISTOGRAM(%s, %s);" % \
300 (self.probe_hash_name, self.expr_types[0])
301 else:
302 text = "struct %s_key_t {\n" % self.probe_hash_name
303 for i in range(0, len(self.expr_types)):
304 text += self._generate_hash_field(i)
305 text += "};\n"
306 text += "BPF_HASH(%s, struct %s_key_t, u64);\n" % \
307 (self.probe_hash_name, self.probe_hash_name)
308 return text
309
310 def _generate_key_assignment(self):
311 if self.type == "hist":
312 return "%s __key = %s;\n" % \
313 (self.expr_types[0], self.exprs[0])
314 else:
315 text = "struct %s_key_t __key = {};\n" % \
316 self.probe_hash_name
317 for i in range(0, len(self.exprs)):
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800318 text += self._generate_field_assignment(i)
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800319 return text
320
321 def _generate_hash_update(self):
322 if self.type == "hist":
323 return "%s.increment(bpf_log2l(__key));" % \
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800324 self.probe_hash_name
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800325 else:
326 return "%s.increment(__key);" % self.probe_hash_name
327
328 def _generate_pid_filter(self):
329 # Kernel probes need to explicitly filter pid, because the
330 # attach interface doesn't support pid filtering
331 if self.pid is not None and not self.is_user:
332 return "u32 pid = bpf_get_current_pid_tgid();\n" + \
333 "if (pid != %d) { return 0; }" % self.pid
334 else:
335 return ""
336
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800337 def generate_text(self):
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800338 program = ""
339
340 # If any entry arguments are probed in a ret probe, we need
341 # to generate an entry probe to collect them
342 prefix = ""
343 if self.entry_probe_required:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800344 program += self._generate_entry_probe()
345 prefix += self._generate_retprobe_prefix()
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800346 # Replace $entry(paramname) with a reference to the
347 # value we collected when entering the function:
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800348 self._replace_entry_exprs()
349
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800350 # If this is a tracepoint probe, generate a local variable
351 # that enables access to the tracepoint structure and also
352 # the structure definition itself
353 if self.probe_type == "t":
Sasha Goldshteinc08c4312016-03-21 03:52:09 -0700354 program += self.tp.generate_struct()
355 prefix += self.tp.generate_get_struct()
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800356
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800357 program += self.probe_text.replace("PROBENAME",
358 self.probe_func_name)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800359 signature = "" if len(self.signature) == 0 \
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800360 or self.probe_type == "r" \
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800361 else ", " + self.signature
Sasha Goldshtein85384852016-02-12 01:29:39 -0800362 program = program.replace("SIGNATURE", signature)
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800363 program = program.replace("PID_FILTER",
364 self._generate_pid_filter())
365
366 decl = self._generate_hash_decl()
367 key_expr = self._generate_key_assignment()
368 collect = self._generate_hash_update()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800369 program = program.replace("DATA_DECL", decl)
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800370 program = program.replace("KEY_EXPR", key_expr)
Sasha Goldshteine3501152016-02-13 03:56:29 -0800371 program = program.replace("FILTER",
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800372 "1" if len(self.filter) == 0 else self.filter)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800373 program = program.replace("COLLECT", collect)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800374 program = program.replace("PREFIX", prefix)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800375 return program
376
377 def attach(self, bpf):
378 self.bpf = bpf
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800379 uprobes_start = len(BPF.open_uprobes())
380 kprobes_start = len(BPF.open_kprobes())
Sasha Goldshtein85384852016-02-12 01:29:39 -0800381 if self.is_user:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800382 if self.probe_type == "r":
Sasha Goldshtein85384852016-02-12 01:29:39 -0800383 bpf.attach_uretprobe(name=self.library,
384 sym=self.function,
385 fn_name=self.probe_func_name,
386 pid=self.pid or -1)
387 else:
388 bpf.attach_uprobe(name=self.library,
389 sym=self.function,
390 fn_name=self.probe_func_name,
391 pid=self.pid or -1)
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800392 if len(BPF.open_uprobes()) != uprobes_start + 1:
393 self._bail("error attaching probe")
Sasha Goldshtein85384852016-02-12 01:29:39 -0800394 else:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800395 if self.probe_type == "r" or self.probe_type == "t":
Sasha Goldshtein85384852016-02-12 01:29:39 -0800396 bpf.attach_kretprobe(event=self.function,
397 fn_name=self.probe_func_name)
398 else:
399 bpf.attach_kprobe(event=self.function,
400 fn_name=self.probe_func_name)
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800401 if len(BPF.open_kprobes()) != kprobes_start + 1:
402 self._bail("error attaching probe")
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800403 if self.entry_probe_required:
404 self._attach_entry_probe()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800405
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800406 def _v2s(self, v):
407 # Most fields can be converted with plain str(), but strings
408 # are wrapped in a __string_t which has an .s field
409 if "__string_t" in type(v).__name__:
410 return str(v.s)
411 return str(v)
412
413 def _display_expr(self, i):
414 # Replace ugly latency calculation with $latency
415 expr = self.exprs[i].replace(
416 "(bpf_ktime_get_ns() - *____latency_val)", "$latency")
417 # Replace alias values back with the alias name
418 for alias, subst in Specifier.aliases.items():
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800419 expr = expr.replace(subst, alias)
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800420 # Replace retval expression with $retval
421 expr = expr.replace("ctx->ax", "$retval")
422 # Replace ugly (*__param_val) expressions with param name
423 return re.sub(r"\(\*__(\w+)_val\)", r"\1", expr)
424
425 def _display_key(self, key):
426 if self.is_default_expr:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800427 if not self.probe_type == "r":
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800428 return "total calls"
429 else:
430 return "retval = %s" % str(key.v0)
431 else:
432 # The key object has v0, ..., vk fields containing
433 # the values of the expressions from self.exprs
434 def str_i(i):
435 key_i = self._v2s(getattr(key, "v%d" % i))
436 return "%s = %s" % \
437 (self._display_expr(i), key_i)
438 return ", ".join(map(str_i, range(0, len(self.exprs))))
439
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800440 def display(self, top):
Sasha Goldshtein85384852016-02-12 01:29:39 -0800441 data = self.bpf.get_table(self.probe_hash_name)
442 if self.type == "freq":
Sasha Goldshteine3501152016-02-13 03:56:29 -0800443 print(self.label or self.raw_spec)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800444 print("\t%-10s %s" % ("COUNT", "EVENT"))
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800445 data = sorted(data.items(), key=lambda kv: kv[1].value)
446 if top is not None:
447 data = data[-top:]
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800448 for key, value in data:
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800449 # Print some nice values if the user didn't
450 # specify an expression to probe
Sasha Goldshtein85384852016-02-12 01:29:39 -0800451 if self.is_default_expr:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800452 if not self.probe_type == "r":
Sasha Goldshtein85384852016-02-12 01:29:39 -0800453 key_str = "total calls"
454 else:
455 key_str = "retval = %s" % \
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800456 self._v2s(key.v0)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800457 else:
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800458 key_str = self._display_key(key)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800459 print("\t%-10s %s" % \
460 (str(value.value), key_str))
461 elif self.type == "hist":
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800462 label = self.label or (self._display_expr(0)
463 if not self.is_default_expr else "retval")
Sasha Goldshtein85384852016-02-12 01:29:39 -0800464 data.print_log2_hist(val_type=label)
465
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800466class Tool(object):
467 examples = """
Sasha Goldshtein85384852016-02-12 01:29:39 -0800468Probe specifier syntax:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800469 {p,r,t}:{[library],category}:function(signature)[:type[,type...]:expr[,expr...][:filter]][#label]
Sasha Goldshtein85384852016-02-12 01:29:39 -0800470Where:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800471 p,r,t -- probe at function entry, function exit, or kernel tracepoint
Sasha Goldshteine3501152016-02-13 03:56:29 -0800472 in exit probes: can use $retval, $entry(param), $latency
Sasha Goldshtein85384852016-02-12 01:29:39 -0800473 library -- the library that contains the function
474 (leave empty for kernel functions)
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800475 category -- the category of the kernel tracepoint (e.g. net, sched)
476 function -- the function name to trace (or tracepoint name)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800477 signature -- the function's parameters, as in the C header
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800478 type -- the type of the expression to collect (supports multiple)
479 expr -- the expression to collect (supports multiple)
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800480 filter -- the filter that is applied to collected values
481 label -- the label for this probe in the resulting output
Sasha Goldshtein85384852016-02-12 01:29:39 -0800482
483EXAMPLES:
484
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800485argdist -H 'p::__kmalloc(u64 size):u64:size'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800486 Print a histogram of allocation sizes passed to kmalloc
487
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800488argdist -p 1005 -C 'p:c:malloc(size_t size):size_t:size:size==16'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800489 Print a frequency count of how many times process 1005 called malloc
490 with an allocation size of 16 bytes
491
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800492argdist -C 'r:c:gets():char*:(char*)$retval#snooped strings'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800493 Snoop on all strings returned by gets()
494
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800495argdist -H 'r::__kmalloc(size_t size):u64:$latency/$entry(size)#ns per byte'
Sasha Goldshteine3501152016-02-13 03:56:29 -0800496 Print a histogram of nanoseconds per byte from kmalloc allocations
497
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800498argdist -C 'p::__kmalloc(size_t size, gfp_t flags):size_t:size:flags&GFP_ATOMIC'
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800499 Print frequency count of kmalloc allocation sizes that have GFP_ATOMIC
500
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800501argdist -p 1005 -C 'p:c:write(int fd):int:fd' -T 5
Sasha Goldshtein85384852016-02-12 01:29:39 -0800502 Print frequency counts of how many times writes were issued to a
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800503 particular file descriptor number, in process 1005, but only show
504 the top 5 busiest fds
Sasha Goldshtein85384852016-02-12 01:29:39 -0800505
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800506argdist -p 1005 -H 'r:c:read()'
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800507 Print a histogram of results (sizes) returned by read() in process 1005
Sasha Goldshtein85384852016-02-12 01:29:39 -0800508
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800509argdist -C 'r::__vfs_read():u32:$PID:$latency > 100000'
Sasha Goldshteine3501152016-02-13 03:56:29 -0800510 Print frequency of reads by process where the latency was >0.1ms
511
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800512argdist -H 'r::__vfs_read(void *file, void *buf, size_t count):size_t:$entry(count):$latency > 1000000'
Sasha Goldshteine3501152016-02-13 03:56:29 -0800513 Print a histogram of read sizes that were longer than 1ms
514
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800515argdist -H \\
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800516 'p:c:write(int fd, const void *buf, size_t count):size_t:count:fd==1'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800517 Print a histogram of buffer sizes passed to write() across all
518 processes, where the file descriptor was 1 (STDOUT)
519
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800520argdist -C 'p:c:fork()#fork calls'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800521 Count fork() calls in libc across all processes
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800522 Can also use funccount.py, which is easier and more flexible
Sasha Goldshtein85384852016-02-12 01:29:39 -0800523
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800524argdist -H 't:block:block_rq_complete():u32:tp.nr_sector'
525 Print histogram of number of sectors in completing block I/O requests
526
527argdist -C 't:irq:irq_handler_entry():int:tp.irq'
528 Aggregate interrupts by interrupt request (IRQ)
529
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800530argdist -H \\
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800531 'p:c:sleep(u32 seconds):u32:seconds' \\
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800532 'p:c:nanosleep(struct timespec *req):long:req->tv_nsec'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800533 Print histograms of sleep() and nanosleep() parameter values
534
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800535argdist -p 2780 -z 120 \\
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800536 -C 'p:c:write(int fd, char* buf, size_t len):char*:buf:fd==1'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800537 Spy on writes to STDOUT performed by process 2780, up to a string size
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800538 of 120 characters
Sasha Goldshtein85384852016-02-12 01:29:39 -0800539"""
540
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800541 def __init__(self):
542 parser = argparse.ArgumentParser(description="Trace a " +
543 "function and display a summary of its parameter values.",
544 formatter_class=argparse.RawDescriptionHelpFormatter,
545 epilog=Tool.examples)
546 parser.add_argument("-p", "--pid", type=int,
547 help="id of the process to trace (optional)")
548 parser.add_argument("-z", "--string-size", default=80,
549 type=int,
550 help="maximum string size to read from char* arguments")
551 parser.add_argument("-i", "--interval", default=1, type=int,
552 help="output interval, in seconds")
553 parser.add_argument("-n", "--number", type=int, dest="count",
554 help="number of outputs")
555 parser.add_argument("-v", "--verbose", action="store_true",
556 help="print resulting BPF program code before executing")
557 parser.add_argument("-T", "--top", type=int,
558 help="number of top results to show (not applicable to " +
559 "histograms)")
560 parser.add_argument("-H", "--histogram", nargs="*",
561 dest="histspecifier", metavar="specifier",
562 help="probe specifier to capture histogram of " +
563 "(see examples below)")
564 parser.add_argument("-C", "--count", nargs="*",
565 dest="countspecifier", metavar="specifier",
566 help="probe specifier to capture count of " +
567 "(see examples below)")
568 parser.add_argument("-I", "--include", nargs="*",
569 metavar="header",
570 help="additional header files to include in the BPF program")
571 self.args = parser.parse_args()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800572
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800573 def _create_specifiers(self):
574 self.specifiers = []
575 for specifier in (self.args.countspecifier or []):
576 self.specifiers.append(Specifier(
577 "freq", specifier, self.args.pid))
578 for histspecifier in (self.args.histspecifier or []):
579 self.specifiers.append(
580 Specifier("hist", histspecifier, self.args.pid))
581 if len(self.specifiers) == 0:
582 print("at least one specifier is required")
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800583 exit()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800584
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800585 def _generate_program(self):
586 bpf_source = """
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800587struct __string_t { char s[%d]; };
588
589#include <uapi/linux/ptrace.h>
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800590 """ % self.args.string_size
591 for include in (self.args.include or []):
592 bpf_source += "#include <%s>\n" % include
593 bpf_source += Specifier.generate_auto_includes(
594 map(lambda s: s.raw_spec, self.specifiers))
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800595 bpf_source += Tracepoint.generate_decl()
596 bpf_source += Tracepoint.generate_entry_probe()
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800597 for specifier in self.specifiers:
598 bpf_source += specifier.generate_text()
599 if self.args.verbose:
600 print(bpf_source)
601 self.bpf = BPF(text=bpf_source)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800602
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800603 def _attach(self):
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800604 Tracepoint.attach(self.bpf)
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800605 for specifier in self.specifiers:
606 specifier.attach(self.bpf)
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800607 if self.args.verbose:
608 print("open uprobes: %s" % BPF.open_uprobes())
609 print("open kprobes: %s" % BPF.open_kprobes())
Sasha Goldshtein85384852016-02-12 01:29:39 -0800610
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800611 def _main_loop(self):
612 count_so_far = 0
613 while True:
614 try:
615 sleep(self.args.interval)
616 except KeyboardInterrupt:
617 exit()
618 print("[%s]" % strftime("%H:%M:%S"))
619 for specifier in self.specifiers:
620 specifier.display(self.args.top)
621 count_so_far += 1
622 if self.args.count is not None and \
623 count_so_far >= self.args.count:
624 exit()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800625
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800626 def run(self):
627 try:
628 self._create_specifiers()
629 self._generate_program()
630 self._attach()
631 self._main_loop()
632 except:
633 if self.args.verbose:
634 traceback.print_exc()
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800635 elif sys.exc_type is not SystemExit:
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800636 print(sys.exc_value)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800637
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800638if __name__ == "__main__":
639 Tool().run()