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): ...@@ -170,16 +170,18 @@ class Probe(object):
trace_count_text = """ trace_count_text = """
int PROBE_FUNCTION(void *ctx) { int PROBE_FUNCTION(void *ctx) {
FILTER FILTER
u64 loc = LOCATION, zero = 0; int loc = LOCATION;
u64 *val = counts.lookup_or_init(&loc, &zero); u64 *val = counts.lookup(&loc);
if (!val) {
return 0; // Should never happen, # of locations is known
}
(*val)++; (*val)++;
return 0; return 0;
} }
""" """
bpf_text = """#include <uapi/linux/ptrace.h> 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 # 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 ...@@ -192,11 +194,22 @@ BPF_HASH(counts, u64, u64); // map location number to number of calls
trace_count_text = trace_count_text.replace('FILTER', '') trace_count_text = trace_count_text.replace('FILTER', '')
bpf_text += self._generate_functions(trace_count_text) bpf_text += self._generate_functions(trace_count_text)
bpf_text = bpf_text.replace("NUMLOCATIONS",
str(len(self.trace_functions)))
if debug: if debug:
print(bpf_text) print(bpf_text)
self.bpf = BPF(text=bpf_text, self.bpf = BPF(text=bpf_text,
usdt_contexts=[self.usdt] if self.usdt else []) 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): class Tool(object):
def __init__(self): def __init__(self):
...@@ -253,18 +266,19 @@ class Tool(object): ...@@ -253,18 +266,19 @@ class Tool(object):
print("%-8s\n" % strftime("%H:%M:%S"), end="") print("%-8s\n" % strftime("%H:%M:%S"), end="")
print("%-36s %8s" % ("FUNC", "COUNT")) print("%-36s %8s" % ("FUNC", "COUNT"))
counts = self.probe.bpf["counts"] counts = self.probe.counts()
for k, v in sorted(counts.items(), for k, v in sorted(counts.items(),
key=lambda counts: counts[1].value): key=lambda counts: counts[1].value):
if v.value == 0: if v.value == 0:
continue continue
print("%-36s %8d" % print("%-36s %8d" %
(self.probe.trace_functions[k.value], v.value)) (self.probe.trace_functions[k.value], v.value))
counts.clear()
if exiting: if exiting:
print("Detaching...") print("Detaching...")
exit() exit()
else:
self.probe.clear()
if __name__ == "__main__": if __name__ == "__main__":
try: 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