Skip to content

Commit

Permalink
tools/funccount: support funccount on specified CPU (iovisor#3059)
Browse files Browse the repository at this point in the history
A typical case of this feature: count timer setting on a x86 server
for each CPU:
 for i in `seq 0 39`;
   do ./funccount.py -i 1 lapic_next_deadline -d 5 -c $i;
 done

Then we can know the timer setting is balanced of not and do some
futher work.

Signed-off-by: zhenwei pi <[email protected]>
  • Loading branch information
pizhenwei committed Aug 21, 2020
1 parent e73e1f6 commit f4e65ac
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 7 deletions.
7 changes: 7 additions & 0 deletions man/man8/funccount.8
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
26 changes: 20 additions & 6 deletions tools/funccount.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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) {
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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:
Expand Down
38 changes: 37 additions & 1 deletion tools/funccount_example.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand All @@ -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

0 comments on commit f4e65ac

Please sign in to comment.