xref: /aosp_15_r20/external/bcc/examples/tracing/strlen_hist_ifunc.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1#!/usr/bin/python
2#
3# strlen_hist_ifunc.py     Histogram of system-wide strlen return values.
4# This can be used instead of strlen_hist.py if strlen is indirect function.
5
6from __future__ import print_function
7from bcc import BPF
8from bcc.libbcc import lib, bcc_symbol, bcc_symbol_option
9
10import ctypes as ct
11import sys
12import time
13
14NAME = 'c'
15SYMBOL = 'strlen'
16STT_GNU_IFUNC = 1 << 10
17
18HIST_BPF_TEXT = """
19#include <uapi/linux/ptrace.h>
20BPF_HISTOGRAM(dist);
21int count(struct pt_regs *ctx) {
22    dist.increment(bpf_log2l(PT_REGS_RC(ctx)));
23    return 0;
24}
25"""
26
27SUBMIT_FUNC_ADDR_BPF_TEXT = """
28#include <uapi/linux/ptrace.h>
29
30BPF_PERF_OUTPUT(impl_func_addr);
31void submit_impl_func_addr(struct pt_regs *ctx) {
32    u64 addr = PT_REGS_RC(ctx);
33    impl_func_addr.perf_submit(ctx, &addr, sizeof(addr));
34}
35
36
37BPF_PERF_OUTPUT(resolv_func_addr);
38int submit_resolv_func_addr(struct pt_regs *ctx) {
39    u64 rip = PT_REGS_IP(ctx);
40    resolv_func_addr.perf_submit(ctx, &rip, sizeof(rip));
41    return 0;
42}
43"""
44
45
46def get_indirect_function_sym(module, symname):
47    sym = bcc_symbol()
48    sym_op = bcc_symbol_option()
49    sym_op.use_debug_file = 1
50    sym_op.check_debug_file_crc = 1
51    sym_op.lazy_symbolize = 1
52    sym_op.use_symbol_type = STT_GNU_IFUNC
53    if lib.bcc_resolve_symname(
54            module.encode(),
55            symname.encode(),
56            0x0,
57            0,
58            ct.byref(sym_op),
59            ct.byref(sym),
60    ) < 0:
61        return None
62    else:
63        return sym
64
65
66def set_impl_func_addr(cpu, data, size):
67    addr = ct.cast(data, ct.POINTER(ct.c_uint64)).contents.value
68    global impl_func_addr
69    impl_func_addr = addr
70
71
72def set_resolv_func_addr(cpu, data, size):
73    addr = ct.cast(data, ct.POINTER(ct.c_uint64)).contents.value
74    global resolv_func_addr
75    resolv_func_addr = addr
76
77
78def find_impl_func_offset(ifunc_symbol):
79    b = BPF(text=SUBMIT_FUNC_ADDR_BPF_TEXT)
80    b.attach_uprobe(name=NAME, sym=SYMBOL, fn_name=b'submit_resolv_func_addr')
81    b['resolv_func_addr'].open_perf_buffer(set_resolv_func_addr)
82    b.attach_uretprobe(name=NAME, sym=SYMBOL, fn_name=b"submit_impl_func_addr")
83    b['impl_func_addr'].open_perf_buffer(set_impl_func_addr)
84
85    print('wait for the first {} call'.format(SYMBOL))
86    while True:
87        try:
88            if resolv_func_addr and impl_func_addr:
89                b.detach_uprobe(name=NAME, sym=SYMBOL)
90                b.detach_uretprobe(name=NAME, sym=SYMBOL)
91                b.cleanup()
92                break
93            b.perf_buffer_poll()
94        except KeyboardInterrupt:
95            exit()
96    print('IFUNC resolution of {} is performed'.format(SYMBOL))
97    print('resolver function address: {:#x}'.format(resolv_func_addr))
98    print('resolver function offset: {:#x}'.format(ifunc_symbol.offset))
99    print('function implementation address: {:#x}'.format(impl_func_addr))
100    impl_func_offset = impl_func_addr - resolv_func_addr + ifunc_symbol.offset
101    print('function implementation offset: {:#x}'.format(impl_func_offset))
102    return impl_func_offset
103
104
105def main():
106    ifunc_symbol = get_indirect_function_sym(NAME, SYMBOL)
107    if not ifunc_symbol:
108        sys.stderr.write('{} is not an indirect function. abort!\n'.format(SYMBOL))
109        exit(1)
110
111    impl_func_offset = find_impl_func_offset(ifunc_symbol)
112
113    b = BPF(text=HIST_BPF_TEXT)
114    b.attach_uretprobe(name=ct.cast(ifunc_symbol.module, ct.c_char_p).value,
115                       addr=impl_func_offset,
116                       fn_name=b'count')
117    dist = b['dist']
118    try:
119        while True:
120            time.sleep(1)
121            print('%-8s\n' % time.strftime('%H:%M:%S'), end='')
122            dist.print_log2_hist(SYMBOL + ' return:')
123            dist.clear()
124
125    except KeyboardInterrupt:
126        pass
127
128
129resolv_func_addr = 0
130impl_func_addr = 0
131
132main()
133