Skip to content

Commit

Permalink
funccount: Switch to BPF array instead of hash
Browse files Browse the repository at this point in the history
Because we know the number of probes in advance before
attaching them, we can simply preinitialize a fixed-size
array instead of using a BPF map. This avoids potential
deadlocks/hangs/race conditions with the Python program
and internally in the kernel. See also iovisor#415, iovisor#665, iovisor#233
for more discussion.
  • Loading branch information
goldshtn committed Oct 25, 2016
1 parent 4e9289b commit dd7ec5a
Showing 1 changed file with 20 additions and 6 deletions.
26 changes: 20 additions & 6 deletions tools/funccount.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,16 +170,18 @@ def load(self):
trace_count_text = """
int PROBE_FUNCTION(void *ctx) {
FILTER
u64 loc = LOCATION, zero = 0;
u64 *val = counts.lookup_or_init(&loc, &zero);
int loc = LOCATION;
u64 *val = counts.lookup(&loc);
if (!val) {
return 0; // Should never happen, # of locations is known
}
(*val)++;
return 0;
}
"""
bpf_text = """#include <uapi/linux/ptrace.h>
BPF_HASH(counts, u64, u64); // map location number to number of calls
BPF_TABLE("array", int, u64, counts, NUMLOCATIONS);
"""

# We really mean the tgid from the kernel's perspective, which is in
Expand All @@ -192,11 +194,22 @@ def load(self):
trace_count_text = trace_count_text.replace('FILTER', '')

bpf_text += self._generate_functions(trace_count_text)
bpf_text = bpf_text.replace("NUMLOCATIONS",
str(len(self.trace_functions)))
if debug:
print(bpf_text)

self.bpf = BPF(text=bpf_text,
usdt_contexts=[self.usdt] if self.usdt else [])
self.clear() # Initialize all array items to zero

def counts(self):
return self.bpf["counts"]

def clear(self):
counts = self.bpf["counts"]
for location, _ in list(self.trace_functions.items()):
counts[counts.Key(location)] = counts.Leaf()

class Tool(object):
def __init__(self):
Expand Down Expand Up @@ -253,18 +266,19 @@ def run(self):
print("%-8s\n" % strftime("%H:%M:%S"), end="")

print("%-36s %8s" % ("FUNC", "COUNT"))
counts = self.probe.bpf["counts"]
counts = self.probe.counts()
for k, v in sorted(counts.items(),
key=lambda counts: counts[1].value):
if v.value == 0:
continue
print("%-36s %8d" %
(self.probe.trace_functions[k.value], v.value))
counts.clear()

if exiting:
print("Detaching...")
exit()
else:
self.probe.clear()

if __name__ == "__main__":
try:
Expand Down

0 comments on commit dd7ec5a

Please sign in to comment.