text: #!/usr/bin/env python3
  # Copyright (c) Sasha Goldshtein
  # Licensed under the Apache License, Version 2.0 (the "License")
  
  import os
  import subprocess
  from bcc import SymbolCache, BPF
  from unittest import main, TestCase
  from utils import mayFail
  
  class TestKSyms(TestCase):
      def grab_sym(self):
          address = ""
          aliases = []
  
          # Grab the first symbol in kallsyms that has type 't' or 'T'.
          # Also, find all aliases of this symbol which are identifiable
          # by the same address.
          with open("/proc/kallsyms", "rb") as f:
              for line in f:
  
                  # Extract the first 3 columns only. The 4th column
                  # containing the module name may not exist for all
                  # symbols.
                  (addr, t, name) = line.strip().split()[:3]
                  if t == b"t" or t == b"T":
                      if not address:
                          address = addr
                      if addr == address:
                          aliases.append(name)
  
          # Return all aliases of the first symbol.
          return (address, aliases)
  
      def test_ksymname(self):
          sym = BPF.ksymname(b"__kmalloc")
          self.assertIsNotNone(sym)
          self.assertNotEqual(sym, 0)
  
      def test_ksym(self):
          (addr, aliases) = self.grab_sym()
          sym = BPF.ksym(int(addr, 16))
          found = sym in aliases
          self.assertTrue(found)
  
  class Harness(TestCase):
      def setUp(self):
          self.build_command()
          subprocess.check_output('objcopy --only-keep-debug dummy dummy.debug'
                                  .split())
          self.debug_command()
          subprocess.check_output('strip dummy'.split())
          self.process = subprocess.Popen('./dummy', stdout=subprocess.PIPE)
          # The process prints out the address of some symbol, which we then
          # try to resolve in the test.
          self.addr = int(self.process.stdout.readline().strip(), 16)
          self.syms = SymbolCache(self.process.pid)
  
      def tearDown(self):
          self.process.kill()
          self.process.wait()
          self.process.stdout.close()
          self.process = None
  
      def resolve_addr(self):
          sym, offset, module = self.syms.resolve(self.addr, False)
          self.assertEqual(sym, self.mangled_name)
          self.assertEqual(offset, 0)
          self.assertTrue(module[-5:] == b'dummy')
          sym, offset, module = self.syms.resolve(self.addr, True)
          self.assertEqual(sym, b'some_namespace::some_function(int, int)')
          self.assertEqual(offset, 0)
          self.assertTrue(module[-5:] == b'dummy')
  
  
      def resolve_name(self):
          script_dir = os.path.dirname(os.path.realpath(__file__).encode("utf8"))
          addr = self.syms.resolve_name(os.path.join(script_dir, b'dummy'),
                                        self.mangled_name)
          self.assertEqual(addr, self.addr)
          pass
  
  class TestDebuglink(Harness):
      def build_command(self):
          subprocess.check_output('g++ -o dummy dummy.cc'.split())
          lines = subprocess.check_output('nm dummy'.split()).splitlines()
          for line in lines:
              if b"some_function" in line:
                  self.mangled_name = line.split(b' ')[2]
                  break
          self.assertTrue(self.mangled_name)
  
      def debug_command(self):
          subprocess.check_output('objcopy --add-gnu-debuglink=dummy.debug dummy'
                                  .split())
  
      def tearDown(self):
          super(TestDebuglink, self).tearDown()
          subprocess.check_output('rm dummy dummy.debug'.split())
  
      def test_resolve_addr(self):
          self.resolve_addr()
  
      @mayFail("This fails on github actions environment, and needs to be fixed")
      def test_resolve_name(self):
          self.resolve_name()
  
  class TestBuildid(Harness):
      def build_command(self):
          subprocess.check_output(('g++ -o dummy -Xlinker ' + \
                 '--build-id=0x123456789abcdef0123456789abcdef012345678 dummy.cc')
                 .split())
          lines = subprocess.check_output('nm dummy'.split()).splitlines()
          for line in lines:
              if b"some_function" in line:
                  self.mangled_name = line.split(b' ')[2]
                  break
          self.assertTrue(self.mangled_name)
  
  
      def debug_command(self):
          subprocess.check_output('mkdir -p /usr/lib/debug/.build-id/12'.split())
          subprocess.check_output(('mv dummy.debug /usr/lib/debug/.build-id' + \
              '/12/3456789abcdef0123456789abcdef012345678.debug').split())
  
      def tearDown(self):
          super(TestBuildid, self).tearDown()
          subprocess.check_output('rm dummy'.split())
          subprocess.check_output(('rm /usr/lib/debug/.build-id/12' +
              '/3456789abcdef0123456789abcdef012345678.debug').split())
  
      def test_resolve_name(self):
          self.resolve_addr()
  
      @mayFail("This fails on github actions environment, and needs to be fixed")
      def test_resolve_addr(self):
          self.resolve_name()
  
  if __name__ == "__main__":
      main()
  