diff --git a/src/python/bcc/__init__.py b/src/python/bcc/__init__.py index a4d767096d1d..52a7a33e9ff2 100644 --- a/src/python/bcc/__init__.py +++ b/src/python/bcc/__init__.py @@ -33,8 +33,8 @@ tracefile = None TRACEFS = "/sys/kernel/debug/tracing" KALLSYMS = "/proc/kallsyms" -ksym_addrs = [] -ksym_names = [] +ksyms = [] +ksym_names = {} ksym_loaded = 0 _kprobe_limit = 1000 @@ -642,7 +642,7 @@ def trace_print(self, fmt=None): @staticmethod def _load_kallsyms(): - global ksym_loaded, ksym_addrs, ksym_names + global ksym_loaded, ksyms, ksym_names if ksym_loaded: return try: @@ -654,19 +654,20 @@ def _load_kallsyms(): cols = line.split() name = cols[2] addr = int(cols[0], 16) - ksym_addrs.append(addr) - ksym_names.append(name) + # keep a mapping of names to ksyms index + ksym_names[name] = len(ksyms) + ksyms.append((name, addr)) syms.close() ksym_loaded = 1 @staticmethod def _ksym_addr2index(addr): - global ksym_addrs + global ksyms start = -1 - end = len(ksym_addrs) + end = len(ksyms) while end != start + 1: mid = int((start + end) / 2) - if addr < ksym_addrs[mid]: + if addr < ksyms[mid][1]: end = mid else: start = mid @@ -679,12 +680,12 @@ def ksym(addr): Translate a kernel memory address into a kernel function name, which is returned. This is a simple translator that uses /proc/kallsyms. """ - global ksym_names + global ksyms BPF._load_kallsyms() idx = BPF._ksym_addr2index(addr) if idx == -1: return "[unknown]" - return ksym_names[idx] + return ksyms[idx][0] @staticmethod def ksymaddr(addr): @@ -694,13 +695,27 @@ def ksymaddr(addr): instruction offset as a hexidecimal number, which is returned as a string. This is a simple translator that uses /proc/kallsyms. """ - global ksym_addrs, ksym_names + global ksyms BPF._load_kallsyms() idx = BPF._ksym_addr2index(addr) if idx == -1: return "[unknown]" - offset = int(addr - ksym_addrs[idx]) - return ksym_names[idx] + hex(offset) + offset = int(addr - ksyms[idx][1]) + return ksyms[idx][0] + hex(offset) + + @staticmethod + def ksymname(name): + """ksymname(name) + + Translate a kernel name into an address. This is the reverse of + ksymaddr. Returns -1 when the function name is unknown.""" + + global ksyms, ksym_names + BPF._load_kallsyms() + idx = ksym_names.get(name, -1) + if idx == -1: + return 0 + return ksyms[idx][1] @staticmethod def num_open_kprobes(): diff --git a/src/python/bcc/table.py b/src/python/bcc/table.py index a364e3266cdc..26fa868d3a21 100644 --- a/src/python/bcc/table.py +++ b/src/python/bcc/table.py @@ -186,6 +186,10 @@ def clear(self): for k in self.keys(): self.__delitem__(k) + def zero(self): + for k in self.keys(): + self[k] = self.Leaf() + def __iter__(self): return TableBase.Iter(self, self.Key) diff --git a/tools/funccount.py b/tools/funccount.py index f41708728d69..427e7c1ae7c9 100755 --- a/tools/funccount.py +++ b/tools/funccount.py @@ -65,9 +65,13 @@ def signal_ignore(signal, frame): int trace_count(struct pt_regs *ctx) { FILTER struct key_t key = {}; - u64 zero = 0, *val; - key.ip = ctx->ip; - val = counts.lookup_or_init(&key, &zero); + u64 *val; + // the kprobe pc is slightly after the function starting address, align + // back to the start (4 byte alignment) in order to match /proc/kallsyms + key.ip = ctx->ip & ~3ull; + val = counts.lookup(&key); + if (!val) + return 0; (*val)++; return 0; } @@ -81,6 +85,16 @@ def signal_ignore(signal, frame): if debug: print(bpf_text) b = BPF(text=bpf_text) +counts = b.get_table("counts") + +# pre-insert the function addresses into the counts table +fns = b._get_kprobe_functions(pattern) +for fn in fns: + addr = b.ksymname(fn) + if addr == -1: + raise Exception("Unknown symbol name %s" % fn) + counts[counts.Key(addr)] = counts.Leaf() + b.attach_kprobe(event_re=pattern, fn_name="trace_count") matched = b.num_open_kprobes() if matched == 0: @@ -106,10 +120,10 @@ def signal_ignore(signal, frame): print("%-8s\n" % strftime("%H:%M:%S"), end="") print("%-16s %-26s %8s" % ("ADDR", "FUNC", "COUNT")) - counts = b.get_table("counts") for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): + if v.value == 0: continue print("%-16x %-26s %8d" % (k.ip, b.ksym(k.ip), v.value)) - counts.clear() + counts.zero() if exiting: print("Detaching...")