blob: 36d0425dc473ba6154fa13c03d932cf8d5c2db82 [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
Brendan Gregg4f88a942016-07-22 17:11:51 -070015from bcc import BPF, Tracepoint, Perf, USDT
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
Sasha Goldshtein3e39a082016-03-24 08:39:47 -070023class Probe(object):
Sasha Goldshtein85384852016-02-12 01:29:39 -080024 next_probe_index = 0
Sasha Goldshtein5e4e1f42016-02-12 06:52:19 -080025 aliases = { "$PID": "bpf_get_current_pid_tgid()" }
26
27 def _substitute_aliases(self, expr):
28 if expr is None:
29 return expr
Sasha Goldshtein3e39a082016-03-24 08:39:47 -070030 for alias, subst in Probe.aliases.items():
Sasha Goldshtein5e4e1f42016-02-12 06:52:19 -080031 expr = expr.replace(alias, subst)
32 return expr
Sasha Goldshtein85384852016-02-12 01:29:39 -080033
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080034 def _parse_signature(self):
35 params = map(str.strip, self.signature.split(','))
36 self.param_types = {}
37 for param in params:
38 # If the type is a pointer, the * can be next to the
39 # param name. Other complex types like arrays are not
40 # supported right now.
41 index = param.rfind('*')
42 index = index if index != -1 else param.rfind(' ')
43 param_type = param[0:index+1].strip()
44 param_name = param[index+1:].strip()
45 self.param_types[param_name] = param_type
46
Sasha Goldshtein3e39a082016-03-24 08:39:47 -070047 def _generate_entry(self):
48 self.entry_probe_func = self.probe_func_name + "_entry"
49 text = """
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080050int PROBENAME(struct pt_regs *ctx SIGNATURE)
51{
52 u32 pid = bpf_get_current_pid_tgid();
53 PID_FILTER
54 COLLECT
55 return 0;
56}
57"""
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080058 text = text.replace("PROBENAME", self.entry_probe_func)
59 text = text.replace("SIGNATURE",
60 "" if len(self.signature) == 0 else ", " + self.signature)
61 pid_filter = "" if self.is_user or self.pid is None \
62 else "if (pid != %d) { return 0; }" % self.pid
63 text = text.replace("PID_FILTER", pid_filter)
64 collect = ""
65 for pname in self.args_to_probe:
Sasha Goldshteine3501152016-02-13 03:56:29 -080066 param_hash = self.hashname_prefix + pname
67 if pname == "__latency":
68 collect += """
69u64 __time = bpf_ktime_get_ns();
70%s.update(&pid, &__time);
71""" % param_hash
72 else:
73 collect += "%s.update(&pid, &%s);\n" % \
74 (param_hash, pname)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080075 text = text.replace("COLLECT", collect)
76 return text
77
78 def _generate_entry_probe(self):
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080079 # Any $entry(name) expressions result in saving that argument
80 # when entering the function.
81 self.args_to_probe = set()
82 regex = r"\$entry\((\w+)\)"
Sasha Goldshteincc27edf2016-02-14 03:49:01 -080083 for expr in self.exprs:
84 for arg in re.finditer(regex, expr):
85 self.args_to_probe.add(arg.group(1))
Sasha Goldshteine3501152016-02-13 03:56:29 -080086 for arg in re.finditer(regex, self.filter):
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080087 self.args_to_probe.add(arg.group(1))
Sasha Goldshteincc27edf2016-02-14 03:49:01 -080088 if any(map(lambda expr: "$latency" in expr, self.exprs)) or \
89 "$latency" in self.filter:
Sasha Goldshteine3501152016-02-13 03:56:29 -080090 self.args_to_probe.add("__latency")
91 self.param_types["__latency"] = "u64" # nanoseconds
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080092 for pname in self.args_to_probe:
93 if pname not in self.param_types:
94 raise ValueError("$entry(%s): no such param" \
95 % arg)
96
97 self.hashname_prefix = "%s_param_" % self.probe_hash_name
98 text = ""
99 for pname in self.args_to_probe:
100 # Each argument is stored in a separate hash that is
101 # keyed by pid.
102 text += "BPF_HASH(%s, u32, %s);\n" % \
103 (self.hashname_prefix + pname,
104 self.param_types[pname])
105 text += self._generate_entry()
106 return text
107
108 def _generate_retprobe_prefix(self):
109 # After we're done here, there are __%s_val variables for each
110 # argument we needed to probe using $entry(name), and they all
111 # have values (which isn't necessarily the case if we missed
112 # the method entry probe).
113 text = "u32 __pid = bpf_get_current_pid_tgid();\n"
114 self.param_val_names = {}
115 for pname in self.args_to_probe:
116 val_name = "__%s_val" % pname
117 text += "%s *%s = %s.lookup(&__pid);\n" % \
118 (self.param_types[pname], val_name,
119 self.hashname_prefix + pname)
120 text += "if (%s == 0) { return 0 ; }\n" % val_name
121 self.param_val_names[pname] = val_name
122 return text
123
124 def _replace_entry_exprs(self):
125 for pname, vname in self.param_val_names.items():
Sasha Goldshteine3501152016-02-13 03:56:29 -0800126 if pname == "__latency":
127 entry_expr = "$latency"
128 val_expr = "(bpf_ktime_get_ns() - *%s)" % vname
129 else:
130 entry_expr = "$entry(%s)" % pname
131 val_expr = "(*%s)" % vname
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800132 for i in range(0, len(self.exprs)):
133 self.exprs[i] = self.exprs[i].replace(
134 entry_expr, val_expr)
Sasha Goldshteine3501152016-02-13 03:56:29 -0800135 self.filter = self.filter.replace(entry_expr,
136 val_expr)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800137
138 def _attach_entry_probe(self):
139 if self.is_user:
140 self.bpf.attach_uprobe(name=self.library,
141 sym=self.function,
142 fn_name=self.entry_probe_func,
143 pid=self.pid or -1)
144 else:
145 self.bpf.attach_kprobe(event=self.function,
146 fn_name=self.entry_probe_func)
147
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800148 def _bail(self, error):
149 raise ValueError("error parsing probe '%s': %s" %
150 (self.raw_spec, error))
151
152 def _validate_specifier(self):
153 # Everything after '#' is the probe label, ignore it
154 spec = self.raw_spec.split('#')[0]
155 parts = spec.strip().split(':')
156 if len(parts) < 3:
157 self._bail("at least the probe type, library, and " +
158 "function signature must be specified")
159 if len(parts) > 6:
160 self._bail("extraneous ':'-separated parts detected")
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700161 if parts[0] not in ["r", "p", "t", "u"]:
162 self._bail("probe type must be 'p', 'r', 't', or 'u' " +
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800163 "but got '%s'" % parts[0])
164 if re.match(r"\w+\(.*\)", parts[2]) is None:
165 self._bail(("function signature '%s' has an invalid " +
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800166 "format") % parts[2])
167
168 def _parse_expr_types(self, expr_types):
169 if len(expr_types) == 0:
170 self._bail("no expr types specified")
171 self.expr_types = expr_types.split(',')
172
173 def _parse_exprs(self, exprs):
174 if len(exprs) == 0:
175 self._bail("no exprs specified")
176 self.exprs = exprs.split(',')
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800177
Brendan Gregg4f88a942016-07-22 17:11:51 -0700178 def __init__(self, bpf, type, specifier):
179 self.pid = bpf.args.pid
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800180 self.raw_spec = specifier
181 self._validate_specifier()
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800182
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800183 spec_and_label = specifier.split('#')
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800184 self.label = spec_and_label[1] \
185 if len(spec_and_label) == 2 else None
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800186
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800187 parts = spec_and_label[0].strip().split(':')
Sasha Goldshtein85384852016-02-12 01:29:39 -0800188 self.type = type # hist or freq
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800189 self.probe_type = parts[0]
Sasha Goldshtein85384852016-02-12 01:29:39 -0800190 fparts = parts[2].split('(')
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800191 self.function = fparts[0].strip()
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800192 if self.probe_type == "t":
193 self.library = "" # kernel
194 self.tp_category = parts[1]
195 self.tp_event = self.function
Sasha Goldshteinc08c4312016-03-21 03:52:09 -0700196 self.tp = Tracepoint.enable_tracepoint(
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800197 self.tp_category, self.tp_event)
198 self.function = "perf_trace_" + self.function
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700199 elif self.probe_type == "u":
200 self.library = parts[1]
Brendan Gregg4f88a942016-07-22 17:11:51 -0700201 self.probe_func_name = "%s_probe%d" % \
202 (self.function, Probe.next_probe_index)
203 bpf.enable_usdt_probe(self.function,
204 fn_name=self.probe_func_name)
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800205 else:
206 self.library = parts[1]
207 self.is_user = len(self.library) > 0
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800208 self.signature = fparts[1].strip()[:-1]
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800209 self._parse_signature()
210
211 # If the user didn't specify an expression to probe, we probe
212 # the retval in a ret probe, or simply the value "1" otherwise.
Sasha Goldshtein85384852016-02-12 01:29:39 -0800213 self.is_default_expr = len(parts) < 5
214 if not self.is_default_expr:
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800215 self._parse_expr_types(parts[3])
216 self._parse_exprs(parts[4])
217 if len(self.exprs) != len(self.expr_types):
218 self._bail("mismatched # of exprs and types")
219 if self.type == "hist" and len(self.expr_types) > 1:
220 self._bail("histograms can only have 1 expr")
Sasha Goldshtein85384852016-02-12 01:29:39 -0800221 else:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800222 if not self.probe_type == "r" and self.type == "hist":
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800223 self._bail("histograms must have expr")
224 self.expr_types = \
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800225 ["u64" if not self.probe_type == "r" else "int"]
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800226 self.exprs = \
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800227 ["1" if not self.probe_type == "r" else "$retval"]
Sasha Goldshteine3501152016-02-13 03:56:29 -0800228 self.filter = "" if len(parts) != 6 else parts[5]
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800229 self._substitute_exprs()
230
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800231 # Do we need to attach an entry probe so that we can collect an
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800232 # argument that is required for an exit (return) probe?
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800233 def check(expr):
234 keywords = ["$entry", "$latency"]
235 return any(map(lambda kw: kw in expr, keywords))
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800236 self.entry_probe_required = self.probe_type == "r" and \
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800237 (any(map(check, self.exprs)) or check(self.filter))
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800238
Sasha Goldshtein85384852016-02-12 01:29:39 -0800239 self.probe_func_name = "%s_probe%d" % \
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700240 (self.function, Probe.next_probe_index)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800241 self.probe_hash_name = "%s_hash%d" % \
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700242 (self.function, Probe.next_probe_index)
243 Probe.next_probe_index += 1
244
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700245 def close(self):
Brendan Gregg4f88a942016-07-22 17:11:51 -0700246 pass
Sasha Goldshtein85384852016-02-12 01:29:39 -0800247
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800248 def _substitute_exprs(self):
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800249 def repl(expr):
250 expr = self._substitute_aliases(expr)
Naveen N. Rao4afa96a2016-05-03 14:54:21 +0530251 return expr.replace("$retval", "PT_REGS_RC(ctx)")
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800252 for i in range(0, len(self.exprs)):
253 self.exprs[i] = repl(self.exprs[i])
254 self.filter = repl(self.filter)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800255
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800256 def _is_string(self, expr_type):
257 return expr_type == "char*" or expr_type == "char *"
Sasha Goldshtein85384852016-02-12 01:29:39 -0800258
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800259 def _generate_hash_field(self, i):
260 if self._is_string(self.expr_types[i]):
261 return "struct __string_t v%d;\n" % i
262 else:
263 return "%s v%d;\n" % (self.expr_types[i], i)
264
265 def _generate_field_assignment(self, i):
Brendan Gregg4f88a942016-07-22 17:11:51 -0700266 text = ""
267 if self.probe_type == "u" and self.exprs[i][0:3] == "arg":
268 text = (" u64 %s;\n" +
269 " bpf_usdt_readarg(%s, ctx, &%s);\n") % \
270 (self.exprs[i], self.exprs[i][3], self.exprs[i])
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800271 if self._is_string(self.expr_types[i]):
Brendan Gregg4f88a942016-07-22 17:11:51 -0700272 return (text + " bpf_probe_read(&__key.v%d.s," +
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700273 " sizeof(__key.v%d.s), (void *)%s);\n") % \
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800274 (i, i, self.exprs[i])
275 else:
Brendan Gregg4f88a942016-07-22 17:11:51 -0700276 return text + " __key.v%d = %s;\n" % \
277 (i, self.exprs[i])
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800278
279 def _generate_hash_decl(self):
280 if self.type == "hist":
281 return "BPF_HISTOGRAM(%s, %s);" % \
282 (self.probe_hash_name, self.expr_types[0])
283 else:
284 text = "struct %s_key_t {\n" % self.probe_hash_name
285 for i in range(0, len(self.expr_types)):
286 text += self._generate_hash_field(i)
287 text += "};\n"
288 text += "BPF_HASH(%s, struct %s_key_t, u64);\n" % \
289 (self.probe_hash_name, self.probe_hash_name)
290 return text
291
292 def _generate_key_assignment(self):
293 if self.type == "hist":
294 return "%s __key = %s;\n" % \
295 (self.expr_types[0], self.exprs[0])
296 else:
297 text = "struct %s_key_t __key = {};\n" % \
298 self.probe_hash_name
299 for i in range(0, len(self.exprs)):
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800300 text += self._generate_field_assignment(i)
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800301 return text
302
303 def _generate_hash_update(self):
304 if self.type == "hist":
305 return "%s.increment(bpf_log2l(__key));" % \
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800306 self.probe_hash_name
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800307 else:
308 return "%s.increment(__key);" % self.probe_hash_name
309
310 def _generate_pid_filter(self):
311 # Kernel probes need to explicitly filter pid, because the
312 # attach interface doesn't support pid filtering
313 if self.pid is not None and not self.is_user:
314 return "u32 pid = bpf_get_current_pid_tgid();\n" + \
315 "if (pid != %d) { return 0; }" % self.pid
316 else:
317 return ""
318
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800319 def generate_text(self):
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800320 program = ""
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700321 probe_text = """
322DATA_DECL
323
Brendan Gregg4f88a942016-07-22 17:11:51 -0700324int PROBENAME(struct pt_regs *ctx SIGNATURE)
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700325{
326 PID_FILTER
327 PREFIX
328 if (!(FILTER)) return 0;
329 KEY_EXPR
330 COLLECT
331 return 0;
332}
333"""
334 prefix = ""
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700335 signature = ""
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800336
337 # If any entry arguments are probed in a ret probe, we need
338 # to generate an entry probe to collect them
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800339 if self.entry_probe_required:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800340 program += self._generate_entry_probe()
341 prefix += self._generate_retprobe_prefix()
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800342 # Replace $entry(paramname) with a reference to the
343 # value we collected when entering the function:
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800344 self._replace_entry_exprs()
345
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800346 if self.probe_type == "t":
Sasha Goldshteinc08c4312016-03-21 03:52:09 -0700347 program += self.tp.generate_struct()
348 prefix += self.tp.generate_get_struct()
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700349 elif self.probe_type == "p" and len(self.signature) > 0:
350 # Only entry uprobes/kprobes can have user-specified
351 # signatures. Other probes force it to ().
352 signature = ", " + self.signature
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800353
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700354 program += probe_text.replace("PROBENAME", self.probe_func_name)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800355 program = program.replace("SIGNATURE", signature)
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800356 program = program.replace("PID_FILTER",
357 self._generate_pid_filter())
358
359 decl = self._generate_hash_decl()
360 key_expr = self._generate_key_assignment()
361 collect = self._generate_hash_update()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800362 program = program.replace("DATA_DECL", decl)
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800363 program = program.replace("KEY_EXPR", key_expr)
Sasha Goldshteine3501152016-02-13 03:56:29 -0800364 program = program.replace("FILTER",
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800365 "1" if len(self.filter) == 0 else self.filter)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800366 program = program.replace("COLLECT", collect)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800367 program = program.replace("PREFIX", prefix)
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700368
Sasha Goldshtein85384852016-02-12 01:29:39 -0800369 return program
370
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700371 def _attach_u(self):
372 libpath = BPF.find_library(self.library)
373 if libpath is None:
Sasha Goldshtein5a1d2e32016-03-30 08:14:44 -0700374 libpath = ProcUtils.which(self.library)
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700375 if libpath is None or len(libpath) == 0:
Sasha Goldshtein5a1d2e32016-03-30 08:14:44 -0700376 self._bail("unable to find library %s" % self.library)
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700377
Brendan Gregg4f88a942016-07-22 17:11:51 -0700378 if self.probe_type == "r":
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700379 self.bpf.attach_uretprobe(name=libpath,
380 sym=self.function,
381 fn_name=self.probe_func_name,
382 pid=self.pid or -1)
383 else:
384 self.bpf.attach_uprobe(name=libpath,
385 sym=self.function,
386 fn_name=self.probe_func_name,
387 pid=self.pid or -1)
388
389 def _attach_k(self):
390 if self.probe_type == "r" or self.probe_type == "t":
391 self.bpf.attach_kretprobe(event=self.function,
392 fn_name=self.probe_func_name)
393 else:
394 self.bpf.attach_kprobe(event=self.function,
395 fn_name=self.probe_func_name)
396
Sasha Goldshtein85384852016-02-12 01:29:39 -0800397 def attach(self, bpf):
398 self.bpf = bpf
Brendan Gregg4f88a942016-07-22 17:11:51 -0700399 if self.probe_type == "u": return;
Sasha Goldshtein85384852016-02-12 01:29:39 -0800400 if self.is_user:
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700401 self._attach_u()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800402 else:
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700403 self._attach_k()
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800404 if self.entry_probe_required:
405 self._attach_entry_probe()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800406
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800407 def _v2s(self, v):
408 # Most fields can be converted with plain str(), but strings
409 # are wrapped in a __string_t which has an .s field
410 if "__string_t" in type(v).__name__:
411 return str(v.s)
412 return str(v)
413
414 def _display_expr(self, i):
415 # Replace ugly latency calculation with $latency
416 expr = self.exprs[i].replace(
417 "(bpf_ktime_get_ns() - *____latency_val)", "$latency")
418 # Replace alias values back with the alias name
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700419 for alias, subst in Probe.aliases.items():
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800420 expr = expr.replace(subst, alias)
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800421 # Replace retval expression with $retval
Naveen N. Rao4afa96a2016-05-03 14:54:21 +0530422 expr = expr.replace("PT_REGS_RC(ctx)", "$retval")
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800423 # Replace ugly (*__param_val) expressions with param name
424 return re.sub(r"\(\*__(\w+)_val\)", r"\1", expr)
425
426 def _display_key(self, key):
427 if self.is_default_expr:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800428 if not self.probe_type == "r":
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800429 return "total calls"
430 else:
431 return "retval = %s" % str(key.v0)
432 else:
433 # The key object has v0, ..., vk fields containing
434 # the values of the expressions from self.exprs
435 def str_i(i):
436 key_i = self._v2s(getattr(key, "v%d" % i))
437 return "%s = %s" % \
438 (self._display_expr(i), key_i)
439 return ", ".join(map(str_i, range(0, len(self.exprs))))
440
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800441 def display(self, top):
Sasha Goldshtein85384852016-02-12 01:29:39 -0800442 data = self.bpf.get_table(self.probe_hash_name)
443 if self.type == "freq":
Sasha Goldshteine3501152016-02-13 03:56:29 -0800444 print(self.label or self.raw_spec)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800445 print("\t%-10s %s" % ("COUNT", "EVENT"))
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800446 data = sorted(data.items(), key=lambda kv: kv[1].value)
447 if top is not None:
448 data = data[-top:]
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800449 for key, value in data:
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800450 # Print some nice values if the user didn't
451 # specify an expression to probe
Sasha Goldshtein85384852016-02-12 01:29:39 -0800452 if self.is_default_expr:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800453 if not self.probe_type == "r":
Sasha Goldshtein85384852016-02-12 01:29:39 -0800454 key_str = "total calls"
455 else:
456 key_str = "retval = %s" % \
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800457 self._v2s(key.v0)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800458 else:
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800459 key_str = self._display_key(key)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800460 print("\t%-10s %s" % \
461 (str(value.value), key_str))
462 elif self.type == "hist":
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800463 label = self.label or (self._display_expr(0)
464 if not self.is_default_expr else "retval")
Sasha Goldshtein85384852016-02-12 01:29:39 -0800465 data.print_log2_hist(val_type=label)
466
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700467 def __str__(self):
468 return self.label or self.raw_spec
469
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800470class Tool(object):
471 examples = """
Sasha Goldshtein85384852016-02-12 01:29:39 -0800472Probe specifier syntax:
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700473 {p,r,t,u}:{[library],category}:function(signature)[:type[,type...]:expr[,expr...][:filter]][#label]
Sasha Goldshtein85384852016-02-12 01:29:39 -0800474Where:
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700475 p,r,t,u -- probe at function entry, function exit, kernel tracepoint,
476 or USDT probe
Sasha Goldshteine3501152016-02-13 03:56:29 -0800477 in exit probes: can use $retval, $entry(param), $latency
Sasha Goldshtein85384852016-02-12 01:29:39 -0800478 library -- the library that contains the function
479 (leave empty for kernel functions)
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800480 category -- the category of the kernel tracepoint (e.g. net, sched)
481 function -- the function name to trace (or tracepoint name)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800482 signature -- the function's parameters, as in the C header
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800483 type -- the type of the expression to collect (supports multiple)
484 expr -- the expression to collect (supports multiple)
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800485 filter -- the filter that is applied to collected values
486 label -- the label for this probe in the resulting output
Sasha Goldshtein85384852016-02-12 01:29:39 -0800487
488EXAMPLES:
489
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800490argdist -H 'p::__kmalloc(u64 size):u64:size'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800491 Print a histogram of allocation sizes passed to kmalloc
492
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800493argdist -p 1005 -C 'p:c:malloc(size_t size):size_t:size:size==16'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800494 Print a frequency count of how many times process 1005 called malloc
495 with an allocation size of 16 bytes
496
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800497argdist -C 'r:c:gets():char*:(char*)$retval#snooped strings'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800498 Snoop on all strings returned by gets()
499
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800500argdist -H 'r::__kmalloc(size_t size):u64:$latency/$entry(size)#ns per byte'
Sasha Goldshteine3501152016-02-13 03:56:29 -0800501 Print a histogram of nanoseconds per byte from kmalloc allocations
502
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800503argdist -C 'p::__kmalloc(size_t size, gfp_t flags):size_t:size:flags&GFP_ATOMIC'
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800504 Print frequency count of kmalloc allocation sizes that have GFP_ATOMIC
505
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800506argdist -p 1005 -C 'p:c:write(int fd):int:fd' -T 5
Sasha Goldshtein85384852016-02-12 01:29:39 -0800507 Print frequency counts of how many times writes were issued to a
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800508 particular file descriptor number, in process 1005, but only show
509 the top 5 busiest fds
Sasha Goldshtein85384852016-02-12 01:29:39 -0800510
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800511argdist -p 1005 -H 'r:c:read()'
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800512 Print a histogram of results (sizes) returned by read() in process 1005
Sasha Goldshtein85384852016-02-12 01:29:39 -0800513
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800514argdist -C 'r::__vfs_read():u32:$PID:$latency > 100000'
Sasha Goldshteine3501152016-02-13 03:56:29 -0800515 Print frequency of reads by process where the latency was >0.1ms
516
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800517argdist -H 'r::__vfs_read(void *file, void *buf, size_t count):size_t:$entry(count):$latency > 1000000'
Sasha Goldshteine3501152016-02-13 03:56:29 -0800518 Print a histogram of read sizes that were longer than 1ms
519
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800520argdist -H \\
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800521 'p:c:write(int fd, const void *buf, size_t count):size_t:count:fd==1'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800522 Print a histogram of buffer sizes passed to write() across all
523 processes, where the file descriptor was 1 (STDOUT)
524
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800525argdist -C 'p:c:fork()#fork calls'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800526 Count fork() calls in libc across all processes
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800527 Can also use funccount.py, which is easier and more flexible
Sasha Goldshtein85384852016-02-12 01:29:39 -0800528
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800529argdist -H 't:block:block_rq_complete():u32:tp.nr_sector'
530 Print histogram of number of sectors in completing block I/O requests
531
532argdist -C 't:irq:irq_handler_entry():int:tp.irq'
533 Aggregate interrupts by interrupt request (IRQ)
534
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700535argdist -C 'u:pthread:pthread_start():u64:arg2' -p 1337
536 Print frequency of function addresses used as a pthread start function,
537 relying on the USDT pthread_start probe in process 1337
538
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800539argdist -H \\
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800540 'p:c:sleep(u32 seconds):u32:seconds' \\
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800541 'p:c:nanosleep(struct timespec *req):long:req->tv_nsec'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800542 Print histograms of sleep() and nanosleep() parameter values
543
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800544argdist -p 2780 -z 120 \\
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800545 -C 'p:c:write(int fd, char* buf, size_t len):char*:buf:fd==1'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800546 Spy on writes to STDOUT performed by process 2780, up to a string size
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800547 of 120 characters
Sasha Goldshtein85384852016-02-12 01:29:39 -0800548"""
549
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800550 def __init__(self):
551 parser = argparse.ArgumentParser(description="Trace a " +
552 "function and display a summary of its parameter values.",
553 formatter_class=argparse.RawDescriptionHelpFormatter,
554 epilog=Tool.examples)
555 parser.add_argument("-p", "--pid", type=int,
556 help="id of the process to trace (optional)")
557 parser.add_argument("-z", "--string-size", default=80,
558 type=int,
559 help="maximum string size to read from char* arguments")
560 parser.add_argument("-i", "--interval", default=1, type=int,
561 help="output interval, in seconds")
562 parser.add_argument("-n", "--number", type=int, dest="count",
563 help="number of outputs")
564 parser.add_argument("-v", "--verbose", action="store_true",
565 help="print resulting BPF program code before executing")
566 parser.add_argument("-T", "--top", type=int,
567 help="number of top results to show (not applicable to " +
568 "histograms)")
569 parser.add_argument("-H", "--histogram", nargs="*",
570 dest="histspecifier", metavar="specifier",
571 help="probe specifier to capture histogram of " +
572 "(see examples below)")
573 parser.add_argument("-C", "--count", nargs="*",
574 dest="countspecifier", metavar="specifier",
575 help="probe specifier to capture count of " +
576 "(see examples below)")
577 parser.add_argument("-I", "--include", nargs="*",
578 metavar="header",
579 help="additional header files to include in the BPF program")
580 self.args = parser.parse_args()
Brendan Gregg4f88a942016-07-22 17:11:51 -0700581 self.usdt_ctx = None
Sasha Goldshtein85384852016-02-12 01:29:39 -0800582
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700583 def _create_probes(self):
584 self.probes = []
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800585 for specifier in (self.args.countspecifier or []):
Brendan Gregg4f88a942016-07-22 17:11:51 -0700586 self.probes.append(Probe(self, "freq", specifier))
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800587 for histspecifier in (self.args.histspecifier or []):
Brendan Gregg4f88a942016-07-22 17:11:51 -0700588 self.probes.append(Probe(self, "hist", histspecifier))
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700589 if len(self.probes) == 0:
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800590 print("at least one specifier is required")
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800591 exit()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800592
Brendan Gregg4f88a942016-07-22 17:11:51 -0700593 def enable_usdt_probe(self, probe_name, fn_name):
594 if not self.usdt_ctx:
595 self.usdt_ctx = USDT(pid=self.args.pid)
596 self.usdt_ctx.enable_probe(probe_name, fn_name)
597
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800598 def _generate_program(self):
599 bpf_source = """
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800600struct __string_t { char s[%d]; };
601
602#include <uapi/linux/ptrace.h>
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800603 """ % self.args.string_size
604 for include in (self.args.include or []):
605 bpf_source += "#include <%s>\n" % include
Sasha Goldshteinb950d6f2016-03-21 04:06:15 -0700606 bpf_source += BPF.generate_auto_includes(
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700607 map(lambda p: p.raw_spec, self.probes))
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800608 bpf_source += Tracepoint.generate_decl()
609 bpf_source += Tracepoint.generate_entry_probe()
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700610 for probe in self.probes:
611 bpf_source += probe.generate_text()
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800612 if self.args.verbose:
Brendan Gregg4f88a942016-07-22 17:11:51 -0700613 if self.usdt_ctx: print(self.usdt_ctx.get_text())
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800614 print(bpf_source)
Brendan Gregg4f88a942016-07-22 17:11:51 -0700615 self.bpf = BPF(text=bpf_source, usdt=self.usdt_ctx)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800616
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800617 def _attach(self):
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800618 Tracepoint.attach(self.bpf)
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700619 for probe in self.probes:
620 probe.attach(self.bpf)
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800621 if self.args.verbose:
Mark Draytoncb679d72016-07-15 23:55:22 +0100622 print("open uprobes: %s" % self.bpf.open_uprobes)
623 print("open kprobes: %s" % self.bpf.open_kprobes)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800624
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800625 def _main_loop(self):
626 count_so_far = 0
627 while True:
628 try:
629 sleep(self.args.interval)
630 except KeyboardInterrupt:
631 exit()
632 print("[%s]" % strftime("%H:%M:%S"))
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700633 for probe in self.probes:
634 probe.display(self.args.top)
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800635 count_so_far += 1
636 if self.args.count is not None and \
637 count_so_far >= self.args.count:
638 exit()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800639
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700640 def _close_probes(self):
641 for probe in self.probes:
642 probe.close()
643 if self.args.verbose:
644 print("closed probe: " + str(probe))
645
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800646 def run(self):
647 try:
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700648 self._create_probes()
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800649 self._generate_program()
650 self._attach()
651 self._main_loop()
652 except:
653 if self.args.verbose:
654 traceback.print_exc()
Brenden Blancobc94d4c2016-05-05 12:05:07 -0700655 elif sys.exc_info()[0] is not SystemExit:
656 print(sys.exc_info()[1])
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700657 self._close_probes()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800658
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800659if __name__ == "__main__":
660 Tool().run()