vijunag | 2ddbc07 | 2019-01-15 10:48:38 +0530 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # |
| 3 | # An example usage of stack_build_id |
| 4 | # Most of the code here is borrowed from tools/profile.py |
| 5 | # |
| 6 | # Steps for using this code |
| 7 | # 1) Start ping program in one terminal eg invocation: ping google.com -i0.001 |
| 8 | # 2) Change the path of libc specified in b.add_module() below |
| 9 | # 3) Invoke the script as 'python stack_buildid_example.py' |
| 10 | # 4) o/p of the tool is as shown below |
| 11 | # python example/tracing/stack_buildid_example.py |
| 12 | # sendto |
| 13 | # - ping (5232) |
| 14 | # 2 |
| 15 | # |
| 16 | # REQUIRES: Linux 4.17+ (BPF_BUILD_ID support) |
| 17 | # Licensed under the Apache License, Version 2.0 (the "License") |
| 18 | # 03-Jan-2019 Vijay Nag |
| 19 | |
| 20 | from __future__ import print_function |
| 21 | from bcc import BPF, PerfType, PerfSWConfig |
| 22 | from sys import stderr |
| 23 | from time import sleep |
| 24 | import argparse |
| 25 | import signal |
| 26 | import os |
| 27 | import subprocess |
| 28 | import errno |
| 29 | import multiprocessing |
| 30 | import ctypes as ct |
| 31 | |
| 32 | def Get_libc_path(): |
| 33 | # A small helper function that returns full path |
| 34 | # of libc in the system |
| 35 | cmd = 'cat /proc/self/maps | grep libc | awk \'{print $6}\' | uniq' |
| 36 | output = subprocess.check_output(cmd, shell=True) |
| 37 | if not isinstance(output, str): |
| 38 | output = output.decode() |
| 39 | return output.split('\n')[0] |
| 40 | |
| 41 | bpf_text = """ |
| 42 | #include <uapi/linux/ptrace.h> |
| 43 | #include <uapi/linux/bpf_perf_event.h> |
| 44 | #include <linux/sched.h> |
| 45 | |
| 46 | struct key_t { |
| 47 | u32 pid; |
| 48 | int user_stack_id; |
| 49 | char name[TASK_COMM_LEN]; |
| 50 | }; |
| 51 | BPF_HASH(counts, struct key_t); |
| 52 | BPF_STACK_TRACE_BUILDID(stack_traces, 128); |
| 53 | |
| 54 | int do_perf_event(struct bpf_perf_event_data *ctx) { |
| 55 | u32 pid = bpf_get_current_pid_tgid() >> 32; |
| 56 | |
| 57 | // create map key |
| 58 | struct key_t key = {.pid = pid}; |
| 59 | bpf_get_current_comm(&key.name, sizeof(key.name)); |
| 60 | |
| 61 | key.user_stack_id = stack_traces.get_stackid(&ctx->regs, BPF_F_USER_STACK); |
| 62 | |
| 63 | if (key.user_stack_id >= 0) { |
| 64 | counts.increment(key); |
| 65 | } |
| 66 | return 0; |
| 67 | } |
| 68 | """ |
| 69 | |
| 70 | b = BPF(text=bpf_text) |
| 71 | b.attach_perf_event(ev_type=PerfType.SOFTWARE, |
| 72 | ev_config=PerfSWConfig.CPU_CLOCK, fn_name="do_perf_event", |
| 73 | sample_period=0, sample_freq=49, cpu=0) |
| 74 | |
| 75 | # Add the list of libraries/executables to the build sym cache for sym resolution |
| 76 | # Change the libc path if it is different on a different machine. |
| 77 | # libc.so and ping are added here so that any symbols pertaining to |
| 78 | # libc or ping are resolved. More executables/libraries can be added here. |
| 79 | b.add_module(Get_libc_path()) |
| 80 | b.add_module("/usr/sbin/sshd") |
| 81 | b.add_module("/bin/ping") |
| 82 | counts = b.get_table("counts") |
| 83 | stack_traces = b.get_table("stack_traces") |
| 84 | duration = 2 |
| 85 | |
| 86 | def signal_handler(signal, frame): |
| 87 | print() |
| 88 | |
| 89 | try: |
| 90 | sleep(duration) |
| 91 | except KeyboardInterrupt: |
| 92 | # as cleanup can take some time, trap Ctrl-C: |
| 93 | signal.signal(signal.SIGINT, signal_ignore) |
| 94 | |
| 95 | user_stack=[] |
| 96 | for k,v in sorted(counts.items(), key=lambda counts: counts[1].value): |
| 97 | user_stack = [] if k.user_stack_id < 0 else \ |
| 98 | stack_traces.walk(k.user_stack_id) |
| 99 | |
| 100 | user_stack=list(user_stack) |
| 101 | for addr in user_stack: |
| 102 | print(" %s" % b.sym(addr, k.pid).decode('utf-8', 'replace')) |
| 103 | print(" %-16s %s (%d)" % ("-", k.name.decode('utf-8', 'replace'), k.pid)) |
| 104 | print(" %d\n" % v.value) |
| 105 | |