From dd7ec5a3a8a03872ec7aea6fc54d2397e56cdf78 Mon Sep 17 00:00:00 2001 From: Sasha Goldshtein Date: Tue, 25 Oct 2016 07:18:24 -0700 Subject: [PATCH] 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. --- tools/funccount.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tools/funccount.py b/tools/funccount.py index cd92baae7153..71aa0dd4c8b4 100755 --- a/tools/funccount.py +++ b/tools/funccount.py @@ -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 -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 @@ 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): @@ -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: