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