#!/usr/bin/env python3
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import glob
import os
import sys
import unittest
import linker_map_parser
import test_util
_SCRIPT_DIR = os.path.dirname(__file__)
_TEST_DATA_DIR = os.path.join(_SCRIPT_DIR, 'testdata', 'linker_map_parser')
_TEST_MAP_PATH = os.path.join(_TEST_DATA_DIR, 'test_lld-lto_v1.map')
_TEST_CFI_MAP_PATH = os.path.join(_TEST_DATA_DIR, 'test_lld-lto_v1_cfi.map')
def _CompareWithGolden(name=None):
def real_decorator(func):
basename = name
if not basename:
basename = func.__name__.replace('test_', '')
golden_path = os.path.join(_TEST_DATA_DIR, basename + '.golden')
def inner(self):
actual_lines = func(self)
test_util.Golden.CheckOrUpdate(golden_path, actual_lines)
return inner
return real_decorator
def _ReadMapFile(path):
with open(path, 'r') as f:
for line in f:
# Strip blank lines and comments.
stripped_line = line.lstrip()
if not stripped_line or stripped_line.startswith('#'):
continue
yield line
def _RenderSectionSizesAndRawSymbols(section_sizes, raw_symbols):
ret = []
ret.append('******** section_sizes ********')
for k, (address, size) in sorted(section_sizes.items()):
address_text = '@%x' % address
ret.append('%-24s %-9s %d' % (k, address_text, size))
ret.append('')
ret.append('******** raw_symbols ********')
for sym in raw_symbols:
ret.append(repr(sym))
return ret
class LinkerMapParserTest(unittest.TestCase):
@_CompareWithGolden()
def test_Parser(self):
lines = _ReadMapFile(_TEST_MAP_PATH)
section_sizes, raw_symbols, _ = linker_map_parser.ParseLines(lines)
return _RenderSectionSizesAndRawSymbols(section_sizes, raw_symbols)
@_CompareWithGolden()
def test_ParserCfi(self):
lines = _ReadMapFile(_TEST_CFI_MAP_PATH)
section_sizes, raw_symbols, _ = linker_map_parser.ParseLines(lines)
return _RenderSectionSizesAndRawSymbols(section_sizes, raw_symbols)
def test_ParseArmAnnotations(self):
fun = linker_map_parser.MapFileParserLld.ParseArmAnnotations
# Annotations.
self.assertEqual((True, False), fun('$a'))
self.assertEqual((True, False), fun('$a.0'))
self.assertEqual((True, False), fun('$a.137'))
self.assertEqual((True, True), fun('$t'))
self.assertEqual((True, True), fun('$t.42'))
self.assertEqual((True, None), fun('$d'))
self.assertEqual((True, None), fun('$d.7'))
# Annotations that should not appear, but get handled anyway.
self.assertEqual((True, False), fun('$a.'))
self.assertEqual((True, True), fun('$t.'))
self.assertEqual((True, None), fun('$d.'))
self.assertEqual((True, None), fun('$$.'))
# Non-annotations.
self.assertEqual((False, None), fun('$_21::invoke'))
self.assertEqual((False, None), fun('$aa'))
self.assertEqual((False, None), fun('$tt.'))
self.assertEqual((False, None), fun('$'))
self.assertEqual((False, None), fun(''))
self.assertEqual((False, None), fun('void foo()'))
self.assertEqual((False, None), fun('OUTLINED_FUNCTION_'))
self.assertEqual((False, None), fun('abc'))
@_CompareWithGolden()
def test_Tokenize(self):
ret = []
lines = _ReadMapFile(_TEST_MAP_PATH)
parser = linker_map_parser.MapFileParserLld('lld-lto_v1')
tokenizer = parser.Tokenize(lines)
for (_, address, size, level, span, tok) in tokenizer:
ret.append('%8X %8X (%d) %s %s' % (address, size, level, '-' * 8 if
span is None else '%8X' % span, tok))
return ret
def main():
argv = sys.argv
if len(argv) > 1 and argv[1] == '--update':
argv.pop(0)
test_util.Golden.EnableUpdate()
for f in glob.glob(os.path.join(_TEST_DATA_DIR, '*.golden')):
os.unlink(f)
unittest.main(argv=argv, verbosity=2)
if __name__ == '__main__':
main()