diff --git a/man/man8/funccount.8 b/man/man8/funccount.8 index 9039ab33a7b0..16ce4fc08932 100644 --- a/man/man8/funccount.8 +++ b/man/man8/funccount.8 @@ -38,6 +38,9 @@ Use regular expressions for the search pattern. .TP \-D Print the BPF program before starting (for debugging purposes). +.TP +\-c CPU +Trace on this CPU only. .SH EXAMPLES .TP Count kernel functions beginning with "vfs_", until Ctrl-C is hit: @@ -75,6 +78,10 @@ Count all GC USDT probes in the Node process: Count all malloc() calls in libc: # .B funccount c:malloc +.TP +Count kernel functions beginning with "vfs_" on CPU 1 only: +# +.B funccount \-c 1 'vfs_*' .SH FIELDS .TP FUNC diff --git a/tools/funccount.py b/tools/funccount.py index 69dd01c8cada..ef7d9cefd86d 100755 --- a/tools/funccount.py +++ b/tools/funccount.py @@ -4,7 +4,8 @@ # funccount Count functions, tracepoints, and USDT probes. # For Linux, uses BCC, eBPF. # -# USAGE: funccount [-h] [-p PID] [-i INTERVAL] [-d DURATION] [-T] [-r] pattern +# USAGE: funccount [-h] [-p PID] [-i INTERVAL] [-d DURATION] [-T] [-r] +# [-c CPU] pattern # # The pattern is a string with optional '*' wildcards, similar to file # globbing. If you'd prefer to use regular expressions, use the -r option. @@ -34,7 +35,7 @@ def verify_limit(num): (probe_limit, num)) class Probe(object): - def __init__(self, pattern, use_regex=False, pid=None): + def __init__(self, pattern, use_regex=False, pid=None, cpu=None): """Init a new probe. Init the probe from the pattern provided by the user. The supported @@ -79,6 +80,7 @@ def __init__(self, pattern, use_regex=False, pid=None): self.library = libpath self.pid = pid + self.cpu = cpu self.matched = 0 self.trace_functions = {} # map location number to function name @@ -164,7 +166,8 @@ def _generate_functions(self, template): def load(self): trace_count_text = b""" int PROBE_FUNCTION(void *ctx) { - FILTER + FILTERPID + FILTERCPU int loc = LOCATION; u64 *val = counts.lookup(&loc); if (!val) { @@ -182,11 +185,18 @@ def load(self): # We really mean the tgid from the kernel's perspective, which is in # the top 32 bits of bpf_get_current_pid_tgid(). if self.pid: - trace_count_text = trace_count_text.replace(b'FILTER', + trace_count_text = trace_count_text.replace(b'FILTERPID', b"""u32 pid = bpf_get_current_pid_tgid() >> 32; if (pid != %d) { return 0; }""" % self.pid) else: - trace_count_text = trace_count_text.replace(b'FILTER', b'') + trace_count_text = trace_count_text.replace(b'FILTERPID', b'') + + if self.cpu: + trace_count_text = trace_count_text.replace(b'FILTERCPU', + b"""u32 cpu = bpf_get_smp_processor_id(); + if (cpu != %d) { return 0; }""" % int(self.cpu)) + else: + trace_count_text = trace_count_text.replace(b'FILTERCPU', b'') bpf_text += self._generate_functions(trace_count_text) bpf_text = bpf_text.replace(b"NUMLOCATIONS", @@ -224,6 +234,7 @@ def __init__(self): ./funccount go:os.* # count all "os.*" calls in libgo ./funccount -p 185 go:os.* # count all "os.*" calls in libgo, PID 185 ./funccount ./test:read* # count "read*" calls in the ./test binary + ./funccount -c 1 'vfs_*' # count vfs calls on CPU 1 only """ parser = argparse.ArgumentParser( description="Count functions, tracepoints, and USDT probes", @@ -241,13 +252,16 @@ def __init__(self): help="use regular expressions. Default is \"*\" wildcards only.") parser.add_argument("-D", "--debug", action="store_true", help="print BPF program before starting (for debugging purposes)") + parser.add_argument("-c", "--cpu", + help="trace this CPU only") parser.add_argument("pattern", type=ArgString, help="search expression for events") self.args = parser.parse_args() global debug debug = self.args.debug - self.probe = Probe(self.args.pattern, self.args.regexp, self.args.pid) + self.probe = Probe(self.args.pattern, self.args.regexp, self.args.pid, + self.args.cpu) if self.args.duration and not self.args.interval: self.args.interval = self.args.duration if not self.args.interval: diff --git a/tools/funccount_example.txt b/tools/funccount_example.txt index d06cfd98e820..e942b9cb32fa 100644 --- a/tools/funccount_example.txt +++ b/tools/funccount_example.txt @@ -325,11 +325,45 @@ tcp_v4_send_check 30 __tcp_v4_send_check 30 Detaching... +A cpu is specified by "-c CPU", this will only trace the specified CPU. Eg, +trace how many timers setting per sencond of CPU 1 on a x86(Intel) server: + +# funccount.py -i 1 -c 1 lapic_next_deadline +Tracing 1 functions for "lapic_next_deadline"... Hit Ctrl-C to end. + +FUNC COUNT +lapic_next_deadline 3840 + +FUNC COUNT +lapic_next_deadline 3930 + +FUNC COUNT +lapic_next_deadline 4701 + +FUNC COUNT +lapic_next_deadline 5895 + +FUNC COUNT +lapic_next_deadline 5591 + +FUNC COUNT +lapic_next_deadline 4727 + +FUNC COUNT +lapic_next_deadline 5560 + +FUNC COUNT +lapic_next_deadline 5416 +^C +FUNC COUNT +lapic_next_deadline 372 +Detaching... Full USAGE: # ./funccount -h -usage: funccount [-h] [-p PID] [-i INTERVAL] [-d DURATION] [-T] [-r] [-D] +usage: funccount.py [-h] [-p PID] [-i INTERVAL] [-d DURATION] [-T] [-r] [-D] + [-c CPU] pattern Count functions, tracepoints, and USDT probes @@ -349,6 +383,7 @@ optional arguments: only. -D, --debug print BPF program before starting (for debugging purposes) + -c CPU, --cpu CPU trace this CPU only examples: ./funccount 'vfs_*' # count kernel fns starting with "vfs" @@ -362,3 +397,4 @@ examples: ./funccount go:os.* # count all "os.*" calls in libgo ./funccount -p 185 go:os.* # count all "os.*" calls in libgo, PID 185 ./funccount ./test:read* # count "read*" calls in the ./test binary + ./funccount -c 1 'vfs_*' # count vfs calls on CPU 1 only