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