blob: 61f8e0060f9e839d063616b9b1a81ea8f72468b6 [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 Goldshtein4725a722016-10-18 20:54:47 +03006# USAGE: argdist [-h] [-p PID] [-z STRING_SIZE] [-i INTERVAL] [-n COUNT] [-v]
7# [-c] [-T TOP] [-C specifier] [-H specifier] [-I header]
Sasha Goldshtein85384852016-02-12 01:29:39 -08008#
9# Licensed under the Apache License, Version 2.0 (the "License")
10# Copyright (C) 2016 Sasha Goldshtein.
11
Sasha Goldshtein376ae5c2016-10-04 19:49:57 +030012from bcc import BPF, USDT
Sasha Goldshtein85384852016-02-12 01:29:39 -080013from time import sleep, strftime
14import argparse
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080015import re
Sasha Goldshteinc9551302016-02-21 02:21:46 -080016import traceback
Sasha Goldshteinfd60d552016-03-01 12:15:34 -080017import os
Sasha Goldshteinc9551302016-02-21 02:21:46 -080018import sys
Sasha Goldshtein85384852016-02-12 01:29:39 -080019
Sasha Goldshtein3e39a082016-03-24 08:39:47 -070020class Probe(object):
Sasha Goldshtein85384852016-02-12 01:29:39 -080021 next_probe_index = 0
Sasha Goldshteinc8f752f2016-10-17 02:18:43 -070022 streq_index = 0
Rafael Fonsecaaf236e72017-02-15 17:28:26 +010023 aliases = {"$PID": "(bpf_get_current_pid_tgid() >> 32)"}
Sasha Goldshtein5e4e1f42016-02-12 06:52:19 -080024
25 def _substitute_aliases(self, expr):
26 if expr is None:
27 return expr
Sasha Goldshtein3e39a082016-03-24 08:39:47 -070028 for alias, subst in Probe.aliases.items():
Sasha Goldshtein5e4e1f42016-02-12 06:52:19 -080029 expr = expr.replace(alias, subst)
30 return expr
Sasha Goldshtein85384852016-02-12 01:29:39 -080031
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080032 def _parse_signature(self):
33 params = map(str.strip, self.signature.split(','))
34 self.param_types = {}
35 for param in params:
36 # If the type is a pointer, the * can be next to the
37 # param name. Other complex types like arrays are not
38 # supported right now.
39 index = param.rfind('*')
40 index = index if index != -1 else param.rfind(' ')
Sasha Goldshteinf41ae862016-10-19 01:14:30 +030041 param_type = param[0:index + 1].strip()
42 param_name = param[index + 1:].strip()
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080043 self.param_types[param_name] = param_type
44
Sasha Goldshtein3e39a082016-03-24 08:39:47 -070045 def _generate_entry(self):
46 self.entry_probe_func = self.probe_func_name + "_entry"
47 text = """
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080048int PROBENAME(struct pt_regs *ctx SIGNATURE)
49{
Rafael Fonsecaaf236e72017-02-15 17:28:26 +010050 u64 __pid_tgid = bpf_get_current_pid_tgid();
51 u32 __pid = __pid_tgid; // lower 32 bits
52 u32 __tgid = __pid_tgid >> 32; // upper 32 bits
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080053 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)
Rafael Fonsecaaf236e72017-02-15 17:28:26 +010061 text = text.replace("PID_FILTER", self._generate_pid_filter())
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080062 collect = ""
63 for pname in self.args_to_probe:
Sasha Goldshteine3501152016-02-13 03:56:29 -080064 param_hash = self.hashname_prefix + pname
65 if pname == "__latency":
66 collect += """
67u64 __time = bpf_ktime_get_ns();
Rafael Fonsecaaf236e72017-02-15 17:28:26 +010068%s.update(&__pid, &__time);
Sasha Goldshteinf41ae862016-10-19 01:14:30 +030069 """ % param_hash
Sasha Goldshteine3501152016-02-13 03:56:29 -080070 else:
Rafael Fonsecaaf236e72017-02-15 17:28:26 +010071 collect += "%s.update(&__pid, &%s);\n" % \
Sasha Goldshteine3501152016-02-13 03:56:29 -080072 (param_hash, pname)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080073 text = text.replace("COLLECT", collect)
74 return text
75
76 def _generate_entry_probe(self):
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080077 # Any $entry(name) expressions result in saving that argument
78 # when entering the function.
79 self.args_to_probe = set()
80 regex = r"\$entry\((\w+)\)"
Sasha Goldshteincc27edf2016-02-14 03:49:01 -080081 for expr in self.exprs:
82 for arg in re.finditer(regex, expr):
83 self.args_to_probe.add(arg.group(1))
Sasha Goldshteine3501152016-02-13 03:56:29 -080084 for arg in re.finditer(regex, self.filter):
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080085 self.args_to_probe.add(arg.group(1))
Sasha Goldshteincc27edf2016-02-14 03:49:01 -080086 if any(map(lambda expr: "$latency" in expr, self.exprs)) or \
87 "$latency" in self.filter:
Sasha Goldshteine3501152016-02-13 03:56:29 -080088 self.args_to_probe.add("__latency")
89 self.param_types["__latency"] = "u64" # nanoseconds
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080090 for pname in self.args_to_probe:
91 if pname not in self.param_types:
Sasha Goldshteinf41ae862016-10-19 01:14:30 +030092 raise ValueError("$entry(%s): no such param" %
93 arg)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -080094
95 self.hashname_prefix = "%s_param_" % self.probe_hash_name
96 text = ""
97 for pname in self.args_to_probe:
98 # Each argument is stored in a separate hash that is
99 # keyed by pid.
100 text += "BPF_HASH(%s, u32, %s);\n" % \
101 (self.hashname_prefix + pname,
102 self.param_types[pname])
103 text += self._generate_entry()
104 return text
105
106 def _generate_retprobe_prefix(self):
107 # After we're done here, there are __%s_val variables for each
108 # argument we needed to probe using $entry(name), and they all
109 # have values (which isn't necessarily the case if we missed
110 # the method entry probe).
Rafael Fonsecaaf236e72017-02-15 17:28:26 +0100111 text = ""
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800112 self.param_val_names = {}
113 for pname in self.args_to_probe:
114 val_name = "__%s_val" % pname
115 text += "%s *%s = %s.lookup(&__pid);\n" % \
116 (self.param_types[pname], val_name,
117 self.hashname_prefix + pname)
118 text += "if (%s == 0) { return 0 ; }\n" % val_name
119 self.param_val_names[pname] = val_name
120 return text
121
122 def _replace_entry_exprs(self):
123 for pname, vname in self.param_val_names.items():
Sasha Goldshteine3501152016-02-13 03:56:29 -0800124 if pname == "__latency":
125 entry_expr = "$latency"
126 val_expr = "(bpf_ktime_get_ns() - *%s)" % vname
127 else:
128 entry_expr = "$entry(%s)" % pname
129 val_expr = "(*%s)" % vname
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800130 for i in range(0, len(self.exprs)):
131 self.exprs[i] = self.exprs[i].replace(
132 entry_expr, val_expr)
Sasha Goldshteine3501152016-02-13 03:56:29 -0800133 self.filter = self.filter.replace(entry_expr,
134 val_expr)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800135
136 def _attach_entry_probe(self):
137 if self.is_user:
138 self.bpf.attach_uprobe(name=self.library,
139 sym=self.function,
140 fn_name=self.entry_probe_func,
141 pid=self.pid or -1)
142 else:
143 self.bpf.attach_kprobe(event=self.function,
144 fn_name=self.entry_probe_func)
145
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800146 def _bail(self, error):
147 raise ValueError("error parsing probe '%s': %s" %
148 (self.raw_spec, error))
149
150 def _validate_specifier(self):
151 # Everything after '#' is the probe label, ignore it
152 spec = self.raw_spec.split('#')[0]
153 parts = spec.strip().split(':')
154 if len(parts) < 3:
155 self._bail("at least the probe type, library, and " +
156 "function signature must be specified")
157 if len(parts) > 6:
158 self._bail("extraneous ':'-separated parts detected")
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700159 if parts[0] not in ["r", "p", "t", "u"]:
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300160 self._bail("probe type must be 'p', 'r', 't', or 'u'" +
161 " but got '%s'" % parts[0])
Sasha Goldshtein3fa7ba12017-01-14 11:17:40 +0000162 if re.match(r"\S+\(.*\)", parts[2]) is None:
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800163 self._bail(("function signature '%s' has an invalid " +
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800164 "format") % parts[2])
165
166 def _parse_expr_types(self, expr_types):
167 if len(expr_types) == 0:
168 self._bail("no expr types specified")
169 self.expr_types = expr_types.split(',')
170
171 def _parse_exprs(self, exprs):
172 if len(exprs) == 0:
173 self._bail("no exprs specified")
174 self.exprs = exprs.split(',')
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800175
Sasha Goldshtein3fa7ba12017-01-14 11:17:40 +0000176 def _make_valid_identifier(self, ident):
177 return re.sub(r'[^A-Za-z0-9_]', '_', ident)
178
Sasha Goldshtein69e361a2016-09-27 19:40:00 +0300179 def __init__(self, tool, type, specifier):
180 self.usdt_ctx = None
Sasha Goldshteinc8f752f2016-10-17 02:18:43 -0700181 self.streq_functions = ""
Sasha Goldshtein69e361a2016-09-27 19:40:00 +0300182 self.pid = tool.args.pid
Sasha Goldshteind2f47622016-10-04 18:40:15 +0300183 self.cumulative = tool.args.cumulative or False
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800184 self.raw_spec = specifier
185 self._validate_specifier()
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800186
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800187 spec_and_label = specifier.split('#')
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800188 self.label = spec_and_label[1] \
189 if len(spec_and_label) == 2 else None
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800190
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800191 parts = spec_and_label[0].strip().split(':')
Sasha Goldshtein85384852016-02-12 01:29:39 -0800192 self.type = type # hist or freq
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800193 self.probe_type = parts[0]
Sasha Goldshtein85384852016-02-12 01:29:39 -0800194 fparts = parts[2].split('(')
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800195 self.function = fparts[0].strip()
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800196 if self.probe_type == "t":
197 self.library = "" # kernel
198 self.tp_category = parts[1]
199 self.tp_event = self.function
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700200 elif self.probe_type == "u":
201 self.library = parts[1]
Sasha Goldshtein3fa7ba12017-01-14 11:17:40 +0000202 self.probe_func_name = self._make_valid_identifier(
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100203 "%s_probe%d" %
Sasha Goldshtein3fa7ba12017-01-14 11:17:40 +0000204 (self.function, Probe.next_probe_index))
Sasha Goldshtein69e361a2016-09-27 19:40:00 +0300205 self._enable_usdt_probe()
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800206 else:
207 self.library = parts[1]
208 self.is_user = len(self.library) > 0
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800209 self.signature = fparts[1].strip()[:-1]
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800210 self._parse_signature()
211
212 # If the user didn't specify an expression to probe, we probe
213 # the retval in a ret probe, or simply the value "1" otherwise.
Sasha Goldshtein85384852016-02-12 01:29:39 -0800214 self.is_default_expr = len(parts) < 5
215 if not self.is_default_expr:
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800216 self._parse_expr_types(parts[3])
217 self._parse_exprs(parts[4])
218 if len(self.exprs) != len(self.expr_types):
219 self._bail("mismatched # of exprs and types")
220 if self.type == "hist" and len(self.expr_types) > 1:
221 self._bail("histograms can only have 1 expr")
Sasha Goldshtein85384852016-02-12 01:29:39 -0800222 else:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800223 if not self.probe_type == "r" and self.type == "hist":
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800224 self._bail("histograms must have expr")
225 self.expr_types = \
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800226 ["u64" if not self.probe_type == "r" else "int"]
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800227 self.exprs = \
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800228 ["1" if not self.probe_type == "r" else "$retval"]
Sasha Goldshteine3501152016-02-13 03:56:29 -0800229 self.filter = "" if len(parts) != 6 else parts[5]
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800230 self._substitute_exprs()
231
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800232 # Do we need to attach an entry probe so that we can collect an
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800233 # argument that is required for an exit (return) probe?
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800234 def check(expr):
235 keywords = ["$entry", "$latency"]
236 return any(map(lambda kw: kw in expr, keywords))
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800237 self.entry_probe_required = self.probe_type == "r" and \
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800238 (any(map(check, self.exprs)) or check(self.filter))
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800239
Sasha Goldshtein3fa7ba12017-01-14 11:17:40 +0000240 self.probe_func_name = self._make_valid_identifier(
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100241 "%s_probe%d" %
Sasha Goldshtein3fa7ba12017-01-14 11:17:40 +0000242 (self.function, Probe.next_probe_index))
243 self.probe_hash_name = self._make_valid_identifier(
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100244 "%s_hash%d" %
Sasha Goldshtein3fa7ba12017-01-14 11:17:40 +0000245 (self.function, Probe.next_probe_index))
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700246 Probe.next_probe_index += 1
247
Sasha Goldshtein69e361a2016-09-27 19:40:00 +0300248 def _enable_usdt_probe(self):
249 self.usdt_ctx = USDT(path=self.library, pid=self.pid)
250 self.usdt_ctx.enable_probe(
251 self.function, self.probe_func_name)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800252
Sasha Goldshteinc8f752f2016-10-17 02:18:43 -0700253 def _generate_streq_function(self, string):
254 fname = "streq_%d" % Probe.streq_index
255 Probe.streq_index += 1
256 self.streq_functions += """
257static inline bool %s(char const *ignored, char const *str) {
258 char needle[] = %s;
259 char haystack[sizeof(needle)];
260 bpf_probe_read(&haystack, sizeof(haystack), (void *)str);
Sasha Goldshteindcf16752017-01-17 07:40:57 +0000261 for (int i = 0; i < sizeof(needle) - 1; ++i) {
Sasha Goldshteinc8f752f2016-10-17 02:18:43 -0700262 if (needle[i] != haystack[i]) {
263 return false;
264 }
265 }
266 return true;
267}
268 """ % (fname, string)
269 return fname
270
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800271 def _substitute_exprs(self):
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800272 def repl(expr):
273 expr = self._substitute_aliases(expr)
Sasha Goldshteinc8f752f2016-10-17 02:18:43 -0700274 matches = re.finditer('STRCMP\\(("[^"]+\\")', expr)
275 for match in matches:
276 string = match.group(1)
277 fname = self._generate_streq_function(string)
278 expr = expr.replace("STRCMP", fname, 1)
Naveen N. Rao4afa96a2016-05-03 14:54:21 +0530279 return expr.replace("$retval", "PT_REGS_RC(ctx)")
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800280 for i in range(0, len(self.exprs)):
281 self.exprs[i] = repl(self.exprs[i])
282 self.filter = repl(self.filter)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800283
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800284 def _is_string(self, expr_type):
285 return expr_type == "char*" or expr_type == "char *"
Sasha Goldshtein85384852016-02-12 01:29:39 -0800286
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800287 def _generate_hash_field(self, i):
288 if self._is_string(self.expr_types[i]):
289 return "struct __string_t v%d;\n" % i
290 else:
291 return "%s v%d;\n" % (self.expr_types[i], i)
292
Sasha Goldshtein69e361a2016-09-27 19:40:00 +0300293 def _generate_usdt_arg_assignment(self, i):
294 expr = self.exprs[i]
295 if self.probe_type == "u" and expr[0:3] == "arg":
Sasha Goldshtein3a5256f2017-02-20 15:42:57 +0000296 arg_index = int(expr[3])
297 arg_ctype = self.usdt_ctx.get_probe_arg_ctype(
298 self.function, arg_index - 1)
299 return (" %s %s = 0;\n" +
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300300 " bpf_usdt_readarg(%s, ctx, &%s);\n") \
Sasha Goldshtein3a5256f2017-02-20 15:42:57 +0000301 % (arg_ctype, expr, expr[3], expr)
Sasha Goldshtein69e361a2016-09-27 19:40:00 +0300302 else:
303 return ""
304
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800305 def _generate_field_assignment(self, i):
Sasha Goldshtein69e361a2016-09-27 19:40:00 +0300306 text = self._generate_usdt_arg_assignment(i)
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800307 if self._is_string(self.expr_types[i]):
Brendan Gregg4f88a942016-07-22 17:11:51 -0700308 return (text + " bpf_probe_read(&__key.v%d.s," +
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700309 " sizeof(__key.v%d.s), (void *)%s);\n") % \
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800310 (i, i, self.exprs[i])
311 else:
Brendan Gregg4f88a942016-07-22 17:11:51 -0700312 return text + " __key.v%d = %s;\n" % \
313 (i, self.exprs[i])
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800314
315 def _generate_hash_decl(self):
316 if self.type == "hist":
317 return "BPF_HISTOGRAM(%s, %s);" % \
318 (self.probe_hash_name, self.expr_types[0])
319 else:
320 text = "struct %s_key_t {\n" % self.probe_hash_name
321 for i in range(0, len(self.expr_types)):
322 text += self._generate_hash_field(i)
323 text += "};\n"
324 text += "BPF_HASH(%s, struct %s_key_t, u64);\n" % \
325 (self.probe_hash_name, self.probe_hash_name)
326 return text
327
328 def _generate_key_assignment(self):
329 if self.type == "hist":
Sasha Goldshtein69e361a2016-09-27 19:40:00 +0300330 return self._generate_usdt_arg_assignment(0) + \
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300331 ("%s __key = %s;\n" %
Sasha Goldshtein69e361a2016-09-27 19:40:00 +0300332 (self.expr_types[0], self.exprs[0]))
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800333 else:
334 text = "struct %s_key_t __key = {};\n" % \
335 self.probe_hash_name
336 for i in range(0, len(self.exprs)):
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800337 text += self._generate_field_assignment(i)
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800338 return text
339
340 def _generate_hash_update(self):
341 if self.type == "hist":
342 return "%s.increment(bpf_log2l(__key));" % \
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800343 self.probe_hash_name
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800344 else:
345 return "%s.increment(__key);" % self.probe_hash_name
346
347 def _generate_pid_filter(self):
348 # Kernel probes need to explicitly filter pid, because the
349 # attach interface doesn't support pid filtering
350 if self.pid is not None and not self.is_user:
Rafael Fonsecaaf236e72017-02-15 17:28:26 +0100351 return "if (__tgid != %d) { return 0; }" % self.pid
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800352 else:
353 return ""
354
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800355 def generate_text(self):
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800356 program = ""
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700357 probe_text = """
358DATA_DECL
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300359 """ + (
360 "TRACEPOINT_PROBE(%s, %s)" %
361 (self.tp_category, self.tp_event)
362 if self.probe_type == "t"
363 else "int PROBENAME(struct pt_regs *ctx SIGNATURE)") + """
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700364{
Rafael Fonsecaaf236e72017-02-15 17:28:26 +0100365 u64 __pid_tgid = bpf_get_current_pid_tgid();
366 u32 __pid = __pid_tgid; // lower 32 bits
367 u32 __tgid = __pid_tgid >> 32; // upper 32 bits
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700368 PID_FILTER
369 PREFIX
370 if (!(FILTER)) return 0;
371 KEY_EXPR
372 COLLECT
373 return 0;
374}
375"""
376 prefix = ""
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700377 signature = ""
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800378
379 # If any entry arguments are probed in a ret probe, we need
380 # to generate an entry probe to collect them
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800381 if self.entry_probe_required:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800382 program += self._generate_entry_probe()
383 prefix += self._generate_retprobe_prefix()
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800384 # Replace $entry(paramname) with a reference to the
385 # value we collected when entering the function:
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800386 self._replace_entry_exprs()
387
Sasha Goldshtein376ae5c2016-10-04 19:49:57 +0300388 if self.probe_type == "p" and len(self.signature) > 0:
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700389 # Only entry uprobes/kprobes can have user-specified
390 # signatures. Other probes force it to ().
391 signature = ", " + self.signature
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800392
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300393 program += probe_text.replace("PROBENAME",
394 self.probe_func_name)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800395 program = program.replace("SIGNATURE", signature)
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800396 program = program.replace("PID_FILTER",
397 self._generate_pid_filter())
398
399 decl = self._generate_hash_decl()
400 key_expr = self._generate_key_assignment()
401 collect = self._generate_hash_update()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800402 program = program.replace("DATA_DECL", decl)
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800403 program = program.replace("KEY_EXPR", key_expr)
Sasha Goldshteine3501152016-02-13 03:56:29 -0800404 program = program.replace("FILTER",
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800405 "1" if len(self.filter) == 0 else self.filter)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800406 program = program.replace("COLLECT", collect)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800407 program = program.replace("PREFIX", prefix)
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700408
Sasha Goldshteinc8f752f2016-10-17 02:18:43 -0700409 return self.streq_functions + program
Sasha Goldshtein85384852016-02-12 01:29:39 -0800410
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700411 def _attach_u(self):
412 libpath = BPF.find_library(self.library)
413 if libpath is None:
Sasha Goldshteinec679712016-10-04 18:33:36 +0300414 libpath = BPF.find_exe(self.library)
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700415 if libpath is None or len(libpath) == 0:
Sasha Goldshtein5a1d2e32016-03-30 08:14:44 -0700416 self._bail("unable to find library %s" % self.library)
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700417
Brendan Gregg4f88a942016-07-22 17:11:51 -0700418 if self.probe_type == "r":
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700419 self.bpf.attach_uretprobe(name=libpath,
420 sym=self.function,
421 fn_name=self.probe_func_name,
422 pid=self.pid or -1)
423 else:
424 self.bpf.attach_uprobe(name=libpath,
425 sym=self.function,
426 fn_name=self.probe_func_name,
427 pid=self.pid or -1)
428
429 def _attach_k(self):
Sasha Goldshtein376ae5c2016-10-04 19:49:57 +0300430 if self.probe_type == "t":
431 pass # Nothing to do for tracepoints
432 elif self.probe_type == "r":
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700433 self.bpf.attach_kretprobe(event=self.function,
434 fn_name=self.probe_func_name)
435 else:
436 self.bpf.attach_kprobe(event=self.function,
437 fn_name=self.probe_func_name)
438
Sasha Goldshtein85384852016-02-12 01:29:39 -0800439 def attach(self, bpf):
440 self.bpf = bpf
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300441 if self.probe_type == "u":
442 return
Sasha Goldshtein85384852016-02-12 01:29:39 -0800443 if self.is_user:
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700444 self._attach_u()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800445 else:
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700446 self._attach_k()
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800447 if self.entry_probe_required:
448 self._attach_entry_probe()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800449
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800450 def _v2s(self, v):
451 # Most fields can be converted with plain str(), but strings
452 # are wrapped in a __string_t which has an .s field
453 if "__string_t" in type(v).__name__:
454 return str(v.s)
455 return str(v)
456
457 def _display_expr(self, i):
458 # Replace ugly latency calculation with $latency
459 expr = self.exprs[i].replace(
460 "(bpf_ktime_get_ns() - *____latency_val)", "$latency")
461 # Replace alias values back with the alias name
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700462 for alias, subst in Probe.aliases.items():
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800463 expr = expr.replace(subst, alias)
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800464 # Replace retval expression with $retval
Naveen N. Rao4afa96a2016-05-03 14:54:21 +0530465 expr = expr.replace("PT_REGS_RC(ctx)", "$retval")
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800466 # Replace ugly (*__param_val) expressions with param name
467 return re.sub(r"\(\*__(\w+)_val\)", r"\1", expr)
468
469 def _display_key(self, key):
470 if self.is_default_expr:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800471 if not self.probe_type == "r":
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800472 return "total calls"
473 else:
474 return "retval = %s" % str(key.v0)
475 else:
476 # The key object has v0, ..., vk fields containing
477 # the values of the expressions from self.exprs
478 def str_i(i):
479 key_i = self._v2s(getattr(key, "v%d" % i))
480 return "%s = %s" % \
481 (self._display_expr(i), key_i)
482 return ", ".join(map(str_i, range(0, len(self.exprs))))
483
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800484 def display(self, top):
Sasha Goldshtein85384852016-02-12 01:29:39 -0800485 data = self.bpf.get_table(self.probe_hash_name)
486 if self.type == "freq":
Sasha Goldshteine3501152016-02-13 03:56:29 -0800487 print(self.label or self.raw_spec)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800488 print("\t%-10s %s" % ("COUNT", "EVENT"))
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300489 sdata = sorted(data.items(), key=lambda p: p[1].value)
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800490 if top is not None:
Sasha Goldshteind2f47622016-10-04 18:40:15 +0300491 sdata = sdata[-top:]
492 for key, value in sdata:
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800493 # Print some nice values if the user didn't
494 # specify an expression to probe
Sasha Goldshtein85384852016-02-12 01:29:39 -0800495 if self.is_default_expr:
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800496 if not self.probe_type == "r":
Sasha Goldshtein85384852016-02-12 01:29:39 -0800497 key_str = "total calls"
498 else:
499 key_str = "retval = %s" % \
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800500 self._v2s(key.v0)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800501 else:
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800502 key_str = self._display_key(key)
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300503 print("\t%-10s %s" %
Sasha Goldshtein85384852016-02-12 01:29:39 -0800504 (str(value.value), key_str))
505 elif self.type == "hist":
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800506 label = self.label or (self._display_expr(0)
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300507 if not self.is_default_expr else "retval")
Sasha Goldshtein85384852016-02-12 01:29:39 -0800508 data.print_log2_hist(val_type=label)
Sasha Goldshteind2f47622016-10-04 18:40:15 +0300509 if not self.cumulative:
510 data.clear()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800511
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700512 def __str__(self):
513 return self.label or self.raw_spec
514
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800515class Tool(object):
516 examples = """
Sasha Goldshtein85384852016-02-12 01:29:39 -0800517Probe specifier syntax:
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700518 {p,r,t,u}:{[library],category}:function(signature)[:type[,type...]:expr[,expr...][:filter]][#label]
Sasha Goldshtein85384852016-02-12 01:29:39 -0800519Where:
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300520 p,r,t,u -- probe at function entry, function exit, kernel
521 tracepoint, or USDT probe
Sasha Goldshteine3501152016-02-13 03:56:29 -0800522 in exit probes: can use $retval, $entry(param), $latency
Sasha Goldshtein85384852016-02-12 01:29:39 -0800523 library -- the library that contains the function
524 (leave empty for kernel functions)
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800525 category -- the category of the kernel tracepoint (e.g. net, sched)
526 function -- the function name to trace (or tracepoint name)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800527 signature -- the function's parameters, as in the C header
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800528 type -- the type of the expression to collect (supports multiple)
529 expr -- the expression to collect (supports multiple)
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800530 filter -- the filter that is applied to collected values
531 label -- the label for this probe in the resulting output
Sasha Goldshtein85384852016-02-12 01:29:39 -0800532
533EXAMPLES:
534
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800535argdist -H 'p::__kmalloc(u64 size):u64:size'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800536 Print a histogram of allocation sizes passed to kmalloc
537
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800538argdist -p 1005 -C 'p:c:malloc(size_t size):size_t:size:size==16'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800539 Print a frequency count of how many times process 1005 called malloc
540 with an allocation size of 16 bytes
541
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800542argdist -C 'r:c:gets():char*:(char*)$retval#snooped strings'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800543 Snoop on all strings returned by gets()
544
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800545argdist -H 'r::__kmalloc(size_t size):u64:$latency/$entry(size)#ns per byte'
Sasha Goldshteine3501152016-02-13 03:56:29 -0800546 Print a histogram of nanoseconds per byte from kmalloc allocations
547
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300548argdist -C 'p::__kmalloc(size_t sz, gfp_t flags):size_t:sz:flags&GFP_ATOMIC'
Sasha Goldshtein7983d6b2016-02-13 23:14:18 -0800549 Print frequency count of kmalloc allocation sizes that have GFP_ATOMIC
550
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800551argdist -p 1005 -C 'p:c:write(int fd):int:fd' -T 5
Sasha Goldshtein85384852016-02-12 01:29:39 -0800552 Print frequency counts of how many times writes were issued to a
Sasha Goldshtein392d5c82016-02-12 11:14:20 -0800553 particular file descriptor number, in process 1005, but only show
554 the top 5 busiest fds
Sasha Goldshtein85384852016-02-12 01:29:39 -0800555
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800556argdist -p 1005 -H 'r:c:read()'
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800557 Print a histogram of results (sizes) returned by read() in process 1005
Sasha Goldshtein85384852016-02-12 01:29:39 -0800558
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800559argdist -C 'r::__vfs_read():u32:$PID:$latency > 100000'
Sasha Goldshteine3501152016-02-13 03:56:29 -0800560 Print frequency of reads by process where the latency was >0.1ms
561
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300562argdist -H 'r::__vfs_read(void *file, void *buf, size_t count):size_t
563 $entry(count):$latency > 1000000'
Sasha Goldshteine3501152016-02-13 03:56:29 -0800564 Print a histogram of read sizes that were longer than 1ms
565
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800566argdist -H \\
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800567 'p:c:write(int fd, const void *buf, size_t count):size_t:count:fd==1'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800568 Print a histogram of buffer sizes passed to write() across all
569 processes, where the file descriptor was 1 (STDOUT)
570
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800571argdist -C 'p:c:fork()#fork calls'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800572 Count fork() calls in libc across all processes
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800573 Can also use funccount.py, which is easier and more flexible
Sasha Goldshtein85384852016-02-12 01:29:39 -0800574
Sasha Goldshtein376ae5c2016-10-04 19:49:57 +0300575argdist -H 't:block:block_rq_complete():u32:args->nr_sector'
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800576 Print histogram of number of sectors in completing block I/O requests
577
Sasha Goldshtein376ae5c2016-10-04 19:49:57 +0300578argdist -C 't:irq:irq_handler_entry():int:args->irq'
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800579 Aggregate interrupts by interrupt request (IRQ)
580
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700581argdist -C 'u:pthread:pthread_start():u64:arg2' -p 1337
582 Print frequency of function addresses used as a pthread start function,
583 relying on the USDT pthread_start probe in process 1337
584
Sasha Goldshtein4725a722016-10-18 20:54:47 +0300585argdist -H 'p:c:sleep(u32 seconds):u32:seconds' \\
586 -H 'p:c:nanosleep(struct timespec *req):long:req->tv_nsec'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800587 Print histograms of sleep() and nanosleep() parameter values
588
Sasha Goldshtein7df65da2016-02-14 05:12:27 -0800589argdist -p 2780 -z 120 \\
Sasha Goldshteined21adf2016-02-12 03:04:53 -0800590 -C 'p:c:write(int fd, char* buf, size_t len):char*:buf:fd==1'
Sasha Goldshtein85384852016-02-12 01:29:39 -0800591 Spy on writes to STDOUT performed by process 2780, up to a string size
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800592 of 120 characters
Sasha Goldshtein85384852016-02-12 01:29:39 -0800593"""
594
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800595 def __init__(self):
596 parser = argparse.ArgumentParser(description="Trace a " +
597 "function and display a summary of its parameter values.",
598 formatter_class=argparse.RawDescriptionHelpFormatter,
599 epilog=Tool.examples)
600 parser.add_argument("-p", "--pid", type=int,
601 help="id of the process to trace (optional)")
602 parser.add_argument("-z", "--string-size", default=80,
603 type=int,
604 help="maximum string size to read from char* arguments")
605 parser.add_argument("-i", "--interval", default=1, type=int,
606 help="output interval, in seconds")
607 parser.add_argument("-n", "--number", type=int, dest="count",
608 help="number of outputs")
609 parser.add_argument("-v", "--verbose", action="store_true",
610 help="print resulting BPF program code before executing")
Sasha Goldshteind2f47622016-10-04 18:40:15 +0300611 parser.add_argument("-c", "--cumulative", action="store_true",
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300612 help="do not clear histograms and freq counts at " +
613 "each interval")
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800614 parser.add_argument("-T", "--top", type=int,
615 help="number of top results to show (not applicable to " +
616 "histograms)")
Sasha Goldshtein4725a722016-10-18 20:54:47 +0300617 parser.add_argument("-H", "--histogram", action="append",
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800618 dest="histspecifier", metavar="specifier",
619 help="probe specifier to capture histogram of " +
620 "(see examples below)")
Sasha Goldshtein4725a722016-10-18 20:54:47 +0300621 parser.add_argument("-C", "--count", action="append",
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800622 dest="countspecifier", metavar="specifier",
623 help="probe specifier to capture count of " +
624 "(see examples below)")
Sasha Goldshtein4725a722016-10-18 20:54:47 +0300625 parser.add_argument("-I", "--include", action="append",
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800626 metavar="header",
ShelbyFrancesf5dbbdb2017-02-08 05:56:52 +0300627 help="additional header files to include in the BPF program "
628 "as either full path, or relative to '/usr/include'")
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800629 self.args = parser.parse_args()
Brendan Gregg4f88a942016-07-22 17:11:51 -0700630 self.usdt_ctx = None
Sasha Goldshtein85384852016-02-12 01:29:39 -0800631
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700632 def _create_probes(self):
633 self.probes = []
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800634 for specifier in (self.args.countspecifier or []):
Brendan Gregg4f88a942016-07-22 17:11:51 -0700635 self.probes.append(Probe(self, "freq", specifier))
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800636 for histspecifier in (self.args.histspecifier or []):
Brendan Gregg4f88a942016-07-22 17:11:51 -0700637 self.probes.append(Probe(self, "hist", histspecifier))
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700638 if len(self.probes) == 0:
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800639 print("at least one specifier is required")
Sasha Goldshteinf7ab4432017-02-13 18:46:49 -0500640 exit(1)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800641
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800642 def _generate_program(self):
643 bpf_source = """
Sasha Goldshteincc27edf2016-02-14 03:49:01 -0800644struct __string_t { char s[%d]; };
645
646#include <uapi/linux/ptrace.h>
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800647 """ % self.args.string_size
648 for include in (self.args.include or []):
ShelbyFrancesf5dbbdb2017-02-08 05:56:52 +0300649 if include.startswith((".", "/")):
650 include = os.path.abspath(include)
651 bpf_source += "#include \"%s\"\n" % include
652 else:
653 bpf_source += "#include <%s>\n" % include
654
Sasha Goldshteinb950d6f2016-03-21 04:06:15 -0700655 bpf_source += BPF.generate_auto_includes(
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700656 map(lambda p: p.raw_spec, self.probes))
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700657 for probe in self.probes:
658 bpf_source += probe.generate_text()
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800659 if self.args.verbose:
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300660 for text in [probe.usdt_ctx.get_text()
661 for probe in self.probes
662 if probe.usdt_ctx]:
Sasha Goldshtein69e361a2016-09-27 19:40:00 +0300663 print(text)
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800664 print(bpf_source)
Sasha Goldshtein69e361a2016-09-27 19:40:00 +0300665 usdt_contexts = [probe.usdt_ctx
666 for probe in self.probes if probe.usdt_ctx]
667 self.bpf = BPF(text=bpf_source, usdt_contexts=usdt_contexts)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800668
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800669 def _attach(self):
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700670 for probe in self.probes:
671 probe.attach(self.bpf)
Sasha Goldshteinfd60d552016-03-01 12:15:34 -0800672 if self.args.verbose:
Mark Draytoncb679d72016-07-15 23:55:22 +0100673 print("open uprobes: %s" % self.bpf.open_uprobes)
674 print("open kprobes: %s" % self.bpf.open_kprobes)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800675
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800676 def _main_loop(self):
677 count_so_far = 0
678 while True:
679 try:
680 sleep(self.args.interval)
681 except KeyboardInterrupt:
682 exit()
683 print("[%s]" % strftime("%H:%M:%S"))
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700684 for probe in self.probes:
685 probe.display(self.args.top)
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800686 count_so_far += 1
687 if self.args.count is not None and \
688 count_so_far >= self.args.count:
689 exit()
Sasha Goldshtein85384852016-02-12 01:29:39 -0800690
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800691 def run(self):
692 try:
Sasha Goldshtein3e39a082016-03-24 08:39:47 -0700693 self._create_probes()
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800694 self._generate_program()
695 self._attach()
696 self._main_loop()
697 except:
Sasha Goldshteinf7ab4432017-02-13 18:46:49 -0500698 exc_info = sys.exc_info()
699 sys_exit = exc_info[0] is SystemExit
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800700 if self.args.verbose:
701 traceback.print_exc()
Sasha Goldshteinf7ab4432017-02-13 18:46:49 -0500702 elif not sys_exit:
703 print(exc_info[1])
704 exit(0 if sys_exit else 1)
Sasha Goldshtein85384852016-02-12 01:29:39 -0800705
Sasha Goldshteinc9551302016-02-21 02:21:46 -0800706if __name__ == "__main__":
707 Tool().run()