xref: /aosp_15_r20/external/bcc/tests/python/test_debuginfo.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1#!/usr/bin/env python3
2# Copyright (c) Sasha Goldshtein
3# Licensed under the Apache License, Version 2.0 (the "License")
4
5import os
6import subprocess
7from bcc import SymbolCache, BPF
8from unittest import main, TestCase
9from utils import mayFail
10
11class TestKSyms(TestCase):
12    def grab_sym(self):
13        address = ""
14        aliases = []
15
16        # Grab the first symbol in kallsyms that has type 't' or 'T'.
17        # Also, find all aliases of this symbol which are identifiable
18        # by the same address.
19        with open("/proc/kallsyms", "rb") as f:
20            for line in f:
21
22                # Extract the first 3 columns only. The 4th column
23                # containing the module name may not exist for all
24                # symbols.
25                (addr, t, name) = line.strip().split()[:3]
26                if t == b"t" or t == b"T":
27                    if not address:
28                        address = addr
29                    if addr == address:
30                        aliases.append(name)
31
32        # Return all aliases of the first symbol.
33        return (address, aliases)
34
35    def test_ksymname(self):
36        sym = BPF.ksymname(b"__kmalloc")
37        self.assertIsNotNone(sym)
38        self.assertNotEqual(sym, 0)
39
40    def test_ksym(self):
41        (addr, aliases) = self.grab_sym()
42        sym = BPF.ksym(int(addr, 16))
43        found = sym in aliases
44        self.assertTrue(found)
45
46class Harness(TestCase):
47    def setUp(self):
48        self.build_command()
49        subprocess.check_output('objcopy --only-keep-debug dummy dummy.debug'
50                                .split())
51        self.debug_command()
52        subprocess.check_output('strip dummy'.split())
53        self.process = subprocess.Popen('./dummy', stdout=subprocess.PIPE)
54        # The process prints out the address of some symbol, which we then
55        # try to resolve in the test.
56        self.addr = int(self.process.stdout.readline().strip(), 16)
57        self.syms = SymbolCache(self.process.pid)
58
59    def tearDown(self):
60        self.process.kill()
61        self.process.wait()
62        self.process.stdout.close()
63        self.process = None
64
65    def resolve_addr(self):
66        sym, offset, module = self.syms.resolve(self.addr, False)
67        self.assertEqual(sym, self.mangled_name)
68        self.assertEqual(offset, 0)
69        self.assertTrue(module[-5:] == b'dummy')
70        sym, offset, module = self.syms.resolve(self.addr, True)
71        self.assertEqual(sym, b'some_namespace::some_function(int, int)')
72        self.assertEqual(offset, 0)
73        self.assertTrue(module[-5:] == b'dummy')
74
75
76    def resolve_name(self):
77        script_dir = os.path.dirname(os.path.realpath(__file__).encode("utf8"))
78        addr = self.syms.resolve_name(os.path.join(script_dir, b'dummy'),
79                                      self.mangled_name)
80        self.assertEqual(addr, self.addr)
81        pass
82
83class TestDebuglink(Harness):
84    def build_command(self):
85        subprocess.check_output('g++ -o dummy dummy.cc'.split())
86        lines = subprocess.check_output('nm dummy'.split()).splitlines()
87        for line in lines:
88            if b"some_function" in line:
89                self.mangled_name = line.split(b' ')[2]
90                break
91        self.assertTrue(self.mangled_name)
92
93    def debug_command(self):
94        subprocess.check_output('objcopy --add-gnu-debuglink=dummy.debug dummy'
95                                .split())
96
97    def tearDown(self):
98        super(TestDebuglink, self).tearDown()
99        subprocess.check_output('rm dummy dummy.debug'.split())
100
101    def test_resolve_addr(self):
102        self.resolve_addr()
103
104    @mayFail("This fails on github actions environment, and needs to be fixed")
105    def test_resolve_name(self):
106        self.resolve_name()
107
108class TestBuildid(Harness):
109    def build_command(self):
110        subprocess.check_output(('g++ -o dummy -Xlinker ' + \
111               '--build-id=0x123456789abcdef0123456789abcdef012345678 dummy.cc')
112               .split())
113        lines = subprocess.check_output('nm dummy'.split()).splitlines()
114        for line in lines:
115            if b"some_function" in line:
116                self.mangled_name = line.split(b' ')[2]
117                break
118        self.assertTrue(self.mangled_name)
119
120
121    def debug_command(self):
122        subprocess.check_output('mkdir -p /usr/lib/debug/.build-id/12'.split())
123        subprocess.check_output(('mv dummy.debug /usr/lib/debug/.build-id' + \
124            '/12/3456789abcdef0123456789abcdef012345678.debug').split())
125
126    def tearDown(self):
127        super(TestBuildid, self).tearDown()
128        subprocess.check_output('rm dummy'.split())
129        subprocess.check_output(('rm /usr/lib/debug/.build-id/12' +
130            '/3456789abcdef0123456789abcdef012345678.debug').split())
131
132    def test_resolve_name(self):
133        self.resolve_addr()
134
135    @mayFail("This fails on github actions environment, and needs to be fixed")
136    def test_resolve_addr(self):
137        self.resolve_name()
138
139if __name__ == "__main__":
140    main()
141