Commit dd7ec5a3 authored by Sasha Goldshtein's avatar Sasha Goldshtein

funccount: Switch to BPF array instead of hash

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 #415, #665, #233
for more discussion.
parent 4e9289b1
......@@ -170,16 +170,18 @@ class Probe(object):
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
......@@ -192,11 +194,22 @@ BPF_HASH(counts, u64, u64); // map location number to number of calls
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):
......@@ -253,18 +266,19 @@ class Tool(object):
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:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment