1# Copyright 2021 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Tests for pw_symbolizer's llvm-symbolizer based symbolization.""" 15 16import subprocess 17import tempfile 18import unittest 19import json 20from pathlib import Path 21import pw_symbolizer 22 23_MODULE_PY_DIR = Path(__file__).parent.resolve() 24_CPP_TEST_FILE_NAME = 'symbolizer_test.cc' 25 26_COMPILER = 'clang++' 27 28 29class TestSymbolizer(unittest.TestCase): 30 """Unit tests for binary symbolization.""" 31 32 def _test_symbolization_results(self, expected_symbols, symbolizer): 33 for expected_symbol in expected_symbols: 34 result = symbolizer.symbolize(expected_symbol['Address']) 35 self.assertEqual(result.name, expected_symbol['Expected']) 36 self.assertEqual(result.address, expected_symbol['Address']) 37 38 # Objects sometimes don't have a file/line number for some 39 # reason. 40 if not expected_symbol['IsObj']: 41 self.assertEqual(result.file, _CPP_TEST_FILE_NAME) 42 self.assertEqual(result.line, expected_symbol['Line']) 43 44 def test_symbolization(self): 45 """Tests that the symbolizer can symbolize addresses properly.""" 46 with tempfile.TemporaryDirectory() as exe_dir: 47 exe_file = Path(exe_dir) / 'print_expected_symbols' 48 49 # Compiles a binary that prints symbol addresses and expected 50 # results as JSON. 51 cmd = [ 52 _COMPILER, 53 _CPP_TEST_FILE_NAME, 54 '-gfull', 55 f'-ffile-prefix-map={_MODULE_PY_DIR}=', 56 '-std=c++17', 57 '-fno-pic', 58 '-fno-pie', 59 '-nopie', 60 '-o', 61 exe_file, 62 ] 63 64 process = subprocess.run( 65 cmd, 66 stdout=subprocess.PIPE, 67 stderr=subprocess.STDOUT, 68 cwd=_MODULE_PY_DIR, 69 ) 70 self.assertEqual(process.returncode, 0) 71 72 process = subprocess.run( 73 [exe_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT 74 ) 75 self.assertEqual(process.returncode, 0) 76 77 expected_symbols = [ 78 json.loads(line) 79 for line in process.stdout.decode().splitlines() 80 ] 81 82 symbolizer = pw_symbolizer.LlvmSymbolizer(exe_file) 83 self._test_symbolization_results(expected_symbols, symbolizer) 84 85 # Test backwards compatibility with older versions of 86 # llvm-symbolizer. 87 symbolizer = pw_symbolizer.LlvmSymbolizer( 88 exe_file, force_legacy=True 89 ) 90 self._test_symbolization_results(expected_symbols, symbolizer) 91 92 93if __name__ == '__main__': 94 unittest.main() 95