funccount: Switch to BPF array instead of hash
authorSasha Goldshtein <goldshtn@gmail.com>
Tue, 25 Oct 2016 14:18:24 +0000 (07:18 -0700)
committerSasha Goldshtein <goldshtn@gmail.com>
Tue, 25 Oct 2016 14:18:24 +0000 (07:18 -0700)
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

index cd92baa..71aa0dd 100755 (executable)
@@ -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: