xref: /aosp_15_r20/external/bcc/tests/python/test_uprobes.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) PLUMgrid, Inc.
3*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License")
4*387f9dfdSAndroid Build Coastguard Worker
5*387f9dfdSAndroid Build Coastguard Workerimport bcc
6*387f9dfdSAndroid Build Coastguard Workerimport ctypes
7*387f9dfdSAndroid Build Coastguard Workerimport errno
8*387f9dfdSAndroid Build Coastguard Workerimport os
9*387f9dfdSAndroid Build Coastguard Workerimport subprocess
10*387f9dfdSAndroid Build Coastguard Workerimport shutil
11*387f9dfdSAndroid Build Coastguard Workerimport time
12*387f9dfdSAndroid Build Coastguard Workerimport unittest
13*387f9dfdSAndroid Build Coastguard Worker
14*387f9dfdSAndroid Build Coastguard Workerclass TestUprobes(unittest.TestCase):
15*387f9dfdSAndroid Build Coastguard Worker    def test_simple_library(self):
16*387f9dfdSAndroid Build Coastguard Worker        text = b"""
17*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h>
18*387f9dfdSAndroid Build Coastguard WorkerBPF_ARRAY(stats, u64, 1);
19*387f9dfdSAndroid Build Coastguard Workerstatic void incr(int idx) {
20*387f9dfdSAndroid Build Coastguard Worker    u64 *ptr = stats.lookup(&idx);
21*387f9dfdSAndroid Build Coastguard Worker    if (ptr)
22*387f9dfdSAndroid Build Coastguard Worker        ++(*ptr);
23*387f9dfdSAndroid Build Coastguard Worker}
24*387f9dfdSAndroid Build Coastguard Workerint count(struct pt_regs *ctx) {
25*387f9dfdSAndroid Build Coastguard Worker    bpf_trace_printk("count() uprobe fired");
26*387f9dfdSAndroid Build Coastguard Worker    u32 pid = bpf_get_current_pid_tgid();
27*387f9dfdSAndroid Build Coastguard Worker    if (pid == PID)
28*387f9dfdSAndroid Build Coastguard Worker        incr(0);
29*387f9dfdSAndroid Build Coastguard Worker    return 0;
30*387f9dfdSAndroid Build Coastguard Worker}"""
31*387f9dfdSAndroid Build Coastguard Worker        test_pid = os.getpid()
32*387f9dfdSAndroid Build Coastguard Worker        text = text.replace(b"PID", b"%d" % test_pid)
33*387f9dfdSAndroid Build Coastguard Worker        b = bcc.BPF(text=text)
34*387f9dfdSAndroid Build Coastguard Worker        b.attach_uprobe(name=b"c", sym=b"malloc_stats", fn_name=b"count", pid=test_pid)
35*387f9dfdSAndroid Build Coastguard Worker        b.attach_uretprobe(name=b"c", sym=b"malloc_stats", fn_name=b"count", pid=test_pid)
36*387f9dfdSAndroid Build Coastguard Worker        libc = ctypes.CDLL("libc.so.6")
37*387f9dfdSAndroid Build Coastguard Worker        libc.malloc_stats.restype = None
38*387f9dfdSAndroid Build Coastguard Worker        libc.malloc_stats.argtypes = []
39*387f9dfdSAndroid Build Coastguard Worker        libc.malloc_stats()
40*387f9dfdSAndroid Build Coastguard Worker        self.assertEqual(b[b"stats"][ctypes.c_int(0)].value, 2)
41*387f9dfdSAndroid Build Coastguard Worker        b.detach_uretprobe(name=b"c", sym=b"malloc_stats", pid=test_pid)
42*387f9dfdSAndroid Build Coastguard Worker        b.detach_uprobe(name=b"c", sym=b"malloc_stats", pid=test_pid)
43*387f9dfdSAndroid Build Coastguard Worker
44*387f9dfdSAndroid Build Coastguard Worker    def test_simple_binary(self):
45*387f9dfdSAndroid Build Coastguard Worker        text = b"""
46*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h>
47*387f9dfdSAndroid Build Coastguard WorkerBPF_ARRAY(stats, u64, 1);
48*387f9dfdSAndroid Build Coastguard Workerstatic void incr(int idx) {
49*387f9dfdSAndroid Build Coastguard Worker    u64 *ptr = stats.lookup(&idx);
50*387f9dfdSAndroid Build Coastguard Worker    if (ptr)
51*387f9dfdSAndroid Build Coastguard Worker        ++(*ptr);
52*387f9dfdSAndroid Build Coastguard Worker}
53*387f9dfdSAndroid Build Coastguard Workerint count(struct pt_regs *ctx) {
54*387f9dfdSAndroid Build Coastguard Worker    u32 pid = bpf_get_current_pid_tgid();
55*387f9dfdSAndroid Build Coastguard Worker    incr(0);
56*387f9dfdSAndroid Build Coastguard Worker    return 0;
57*387f9dfdSAndroid Build Coastguard Worker}"""
58*387f9dfdSAndroid Build Coastguard Worker        b = bcc.BPF(text=text)
59*387f9dfdSAndroid Build Coastguard Worker        pythonpath = b"/usr/bin/python3"
60*387f9dfdSAndroid Build Coastguard Worker        symname = b"_start"
61*387f9dfdSAndroid Build Coastguard Worker        b.attach_uprobe(name=pythonpath, sym=symname, fn_name=b"count")
62*387f9dfdSAndroid Build Coastguard Worker        b.attach_uretprobe(name=pythonpath, sym=symname, fn_name=b"count")
63*387f9dfdSAndroid Build Coastguard Worker        with os.popen(pythonpath.decode() + " -V") as f:
64*387f9dfdSAndroid Build Coastguard Worker            pass
65*387f9dfdSAndroid Build Coastguard Worker        self.assertGreater(b[b"stats"][ctypes.c_int(0)].value, 0)
66*387f9dfdSAndroid Build Coastguard Worker        b.detach_uretprobe(name=pythonpath, sym=symname)
67*387f9dfdSAndroid Build Coastguard Worker        b.detach_uprobe(name=pythonpath, sym=symname)
68*387f9dfdSAndroid Build Coastguard Worker
69*387f9dfdSAndroid Build Coastguard Worker    def test_mount_namespace(self):
70*387f9dfdSAndroid Build Coastguard Worker        text = b"""
71*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h>
72*387f9dfdSAndroid Build Coastguard WorkerBPF_TABLE("array", int, u64, stats, 1);
73*387f9dfdSAndroid Build Coastguard Workerstatic void incr(int idx) {
74*387f9dfdSAndroid Build Coastguard Worker    u64 *ptr = stats.lookup(&idx);
75*387f9dfdSAndroid Build Coastguard Worker    if (ptr)
76*387f9dfdSAndroid Build Coastguard Worker        ++(*ptr);
77*387f9dfdSAndroid Build Coastguard Worker}
78*387f9dfdSAndroid Build Coastguard Workerint count(struct pt_regs *ctx) {
79*387f9dfdSAndroid Build Coastguard Worker    bpf_trace_printk("count() uprobe fired");
80*387f9dfdSAndroid Build Coastguard Worker    u32 pid = bpf_get_current_pid_tgid();
81*387f9dfdSAndroid Build Coastguard Worker    if (pid == PID)
82*387f9dfdSAndroid Build Coastguard Worker        incr(0);
83*387f9dfdSAndroid Build Coastguard Worker    return 0;
84*387f9dfdSAndroid Build Coastguard Worker}"""
85*387f9dfdSAndroid Build Coastguard Worker        # Need to import libc from ctypes to access unshare(2)
86*387f9dfdSAndroid Build Coastguard Worker        libc = ctypes.CDLL("libc.so.6", use_errno=True)
87*387f9dfdSAndroid Build Coastguard Worker
88*387f9dfdSAndroid Build Coastguard Worker        # Need to find path to libz.so.1
89*387f9dfdSAndroid Build Coastguard Worker        libz_path = None
90*387f9dfdSAndroid Build Coastguard Worker        p = subprocess.Popen(["ldconfig", "-p"], stdout=subprocess.PIPE)
91*387f9dfdSAndroid Build Coastguard Worker        for l in p.stdout:
92*387f9dfdSAndroid Build Coastguard Worker            n = l.split()
93*387f9dfdSAndroid Build Coastguard Worker            if n[0] == b"libz.so.1":
94*387f9dfdSAndroid Build Coastguard Worker                # if libz was already found, override only if new lib is more
95*387f9dfdSAndroid Build Coastguard Worker                # specific (e.g. libc6,x86-64 vs libc6)
96*387f9dfdSAndroid Build Coastguard Worker                if not libz_path or len(n[1].split(b",")) > 1:
97*387f9dfdSAndroid Build Coastguard Worker                    libz_path = n[-1]
98*387f9dfdSAndroid Build Coastguard Worker        p.wait()
99*387f9dfdSAndroid Build Coastguard Worker        p.stdout.close()
100*387f9dfdSAndroid Build Coastguard Worker        p = None
101*387f9dfdSAndroid Build Coastguard Worker
102*387f9dfdSAndroid Build Coastguard Worker        self.assertIsNotNone(libz_path)
103*387f9dfdSAndroid Build Coastguard Worker
104*387f9dfdSAndroid Build Coastguard Worker        # fork a child that we'll place in a separate mount namespace
105*387f9dfdSAndroid Build Coastguard Worker        child_pid = os.fork()
106*387f9dfdSAndroid Build Coastguard Worker        if child_pid == 0:
107*387f9dfdSAndroid Build Coastguard Worker            # Unshare CLONE_NEWNS
108*387f9dfdSAndroid Build Coastguard Worker            if libc.unshare(0x00020000) == -1:
109*387f9dfdSAndroid Build Coastguard Worker                e = ctypes.get_errno()
110*387f9dfdSAndroid Build Coastguard Worker                raise OSError(e, errno.errorcode[e])
111*387f9dfdSAndroid Build Coastguard Worker
112*387f9dfdSAndroid Build Coastguard Worker            # Remount root MS_REC|MS_PRIVATE
113*387f9dfdSAndroid Build Coastguard Worker            if libc.mount(None, b"/", None, (1<<14)|(1<<18) , None) == -1:
114*387f9dfdSAndroid Build Coastguard Worker                e = ctypes.get_errno()
115*387f9dfdSAndroid Build Coastguard Worker                raise OSError(e, errno.errorcode[e])
116*387f9dfdSAndroid Build Coastguard Worker
117*387f9dfdSAndroid Build Coastguard Worker            if libc.mount(b"tmpfs", b"/tmp", b"tmpfs", 0, None) == -1:
118*387f9dfdSAndroid Build Coastguard Worker                e = ctypes.get_errno()
119*387f9dfdSAndroid Build Coastguard Worker                raise OSError(e, errno.errorcode[e])
120*387f9dfdSAndroid Build Coastguard Worker
121*387f9dfdSAndroid Build Coastguard Worker            shutil.copy(libz_path, b"/tmp")
122*387f9dfdSAndroid Build Coastguard Worker
123*387f9dfdSAndroid Build Coastguard Worker            libz = ctypes.CDLL("/tmp/libz.so.1")
124*387f9dfdSAndroid Build Coastguard Worker            time.sleep(3)
125*387f9dfdSAndroid Build Coastguard Worker            libz.zlibVersion()
126*387f9dfdSAndroid Build Coastguard Worker            time.sleep(5)
127*387f9dfdSAndroid Build Coastguard Worker            os._exit(0)
128*387f9dfdSAndroid Build Coastguard Worker
129*387f9dfdSAndroid Build Coastguard Worker        libname = b"/tmp/libz.so.1"
130*387f9dfdSAndroid Build Coastguard Worker        symname = b"zlibVersion"
131*387f9dfdSAndroid Build Coastguard Worker        text = text.replace(b"PID", b"%d" % child_pid)
132*387f9dfdSAndroid Build Coastguard Worker        b = bcc.BPF(text=text)
133*387f9dfdSAndroid Build Coastguard Worker        b.attach_uprobe(name=libname, sym=symname, fn_name=b"count", pid=child_pid)
134*387f9dfdSAndroid Build Coastguard Worker        b.attach_uretprobe(name=libname, sym=symname, fn_name=b"count", pid=child_pid)
135*387f9dfdSAndroid Build Coastguard Worker        time.sleep(5)
136*387f9dfdSAndroid Build Coastguard Worker        self.assertEqual(b[b"stats"][ctypes.c_int(0)].value, 2)
137*387f9dfdSAndroid Build Coastguard Worker        b.detach_uretprobe(name=libname, sym=symname, pid=child_pid)
138*387f9dfdSAndroid Build Coastguard Worker        b.detach_uprobe(name=libname, sym=symname, pid=child_pid)
139*387f9dfdSAndroid Build Coastguard Worker        os.wait()
140*387f9dfdSAndroid Build Coastguard Worker
141*387f9dfdSAndroid Build Coastguard Workerif __name__ == "__main__":
142*387f9dfdSAndroid Build Coastguard Worker    unittest.main()
143