| #!/usr/bin/python | |
| # | |
| # An example usage of stack_build_id | |
| # Most of the code here is borrowed from tools/profile.py | |
| # | |
| # Steps for using this code | |
| # 1) Start ping program in one terminal eg invocation: ping google.com -i0.001 | |
| # 2) Change the path of libc specified in b.add_module() below | |
| # 3) Invoke the script as 'python stack_buildid_example.py' | |
| # 4) o/p of the tool is as shown below | |
| # python example/tracing/stack_buildid_example.py | |
| # sendto | |
| # - ping (5232) | |
| # 2 | |
| # | |
| # REQUIRES: Linux 4.17+ (BPF_BUILD_ID support) | |
| # Licensed under the Apache License, Version 2.0 (the "License") | |
| # 03-Jan-2019 Vijay Nag | |
| from __future__ import print_function | |
| from bcc import BPF, PerfType, PerfSWConfig | |
| from sys import stderr | |
| from time import sleep | |
| import argparse | |
| import signal | |
| import os | |
| import subprocess | |
| import errno | |
| import multiprocessing | |
| import ctypes as ct | |
| def Get_libc_path(): | |
| # A small helper function that returns full path | |
| # of libc in the system | |
| cmd = 'cat /proc/self/maps | grep libc | awk \'{print $6}\' | uniq' | |
| output = subprocess.check_output(cmd, shell=True) | |
| if not isinstance(output, str): | |
| output = output.decode() | |
| return output.split('\n')[0] | |
| bpf_text = """ | |
| #include <uapi/linux/ptrace.h> | |
| #include <uapi/linux/bpf_perf_event.h> | |
| #include <linux/sched.h> | |
| struct key_t { | |
| u32 pid; | |
| int user_stack_id; | |
| char name[TASK_COMM_LEN]; | |
| }; | |
| BPF_HASH(counts, struct key_t); | |
| BPF_STACK_TRACE_BUILDID(stack_traces, 128); | |
| int do_perf_event(struct bpf_perf_event_data *ctx) { | |
| u32 pid = bpf_get_current_pid_tgid() >> 32; | |
| // create map key | |
| struct key_t key = {.pid = pid}; | |
| bpf_get_current_comm(&key.name, sizeof(key.name)); | |
| key.user_stack_id = stack_traces.get_stackid(&ctx->regs, BPF_F_USER_STACK); | |
| if (key.user_stack_id >= 0) { | |
| counts.increment(key); | |
| } | |
| return 0; | |
| } | |
| """ | |
| b = BPF(text=bpf_text) | |
| b.attach_perf_event(ev_type=PerfType.SOFTWARE, | |
| ev_config=PerfSWConfig.CPU_CLOCK, fn_name="do_perf_event", | |
| sample_period=0, sample_freq=49, cpu=0) | |
| # Add the list of libraries/executables to the build sym cache for sym resolution | |
| # Change the libc path if it is different on a different machine. | |
| # libc.so and ping are added here so that any symbols pertaining to | |
| # libc or ping are resolved. More executables/libraries can be added here. | |
| b.add_module(Get_libc_path()) | |
| b.add_module("/usr/sbin/sshd") | |
| b.add_module("/bin/ping") | |
| counts = b.get_table("counts") | |
| stack_traces = b.get_table("stack_traces") | |
| duration = 2 | |
| def signal_handler(signal, frame): | |
| print() | |
| try: | |
| sleep(duration) | |
| except KeyboardInterrupt: | |
| # as cleanup can take some time, trap Ctrl-C: | |
| signal.signal(signal.SIGINT, signal_ignore) | |
| user_stack=[] | |
| for k,v in sorted(counts.items(), key=lambda counts: counts[1].value): | |
| user_stack = [] if k.user_stack_id < 0 else \ | |
| stack_traces.walk(k.user_stack_id) | |
| user_stack=list(user_stack) | |
| for addr in user_stack: | |
| print(" %s" % b.sym(addr, k.pid).decode('utf-8', 'replace')) | |
| print(" %-16s %s (%d)" % ("-", k.name.decode('utf-8', 'replace'), k.pid)) | |
| print(" %d\n" % v.value) | |