chromium/tools/binary_size/libsupersize/dwarfdump_test.py

#!/usr/bin/env python3
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import unittest

import dwarfdump


class DwarfDumpTest(unittest.TestCase):
  def _MakeRangeInfoList(self, flat_list):
    out = []
    for item in flat_list:
      assert len(item) == 3
      out.append((dwarfdump._AddressRange(item[0], item[1]), item[2]))
    return out

  def testParseNonContiguousAddressRange(self):
    """Test parsing DW_TAG_compile_unit with non-contiguous address range."""
    lines = [
        'DW_TAG_compile_unit',
        'DW_AT_name ("solution.cc")',
        'DW_AT_low_pc (0x0)',
        'DW_AT_ranges (0x1',
        '[0x10, 0x21)',
        '[0x31, 0x41))',
    ]
    expected_info_list = [(0x10, 0x21, 'solution.cc'),
                          (0x31, 0x41, 'solution.cc')]
    self.assertEqual(self._MakeRangeInfoList(expected_info_list),
                     dwarfdump.ParseDumpOutputForTest(lines))

  def testParseNonContiguousAddressRangeOtherBrackets(self):
    """Test parsing DW_AT_ranges when non-standard brackets are used."""
    lines = [
        'DW_TAG_compile_unit',
        'DW_AT_name ("solution.cc")',
        'DW_AT_low_pc (0x0)',
        'DW_AT_ranges [0x1',
        '(0x10, 0x21)',
        '[0x31, 0x41]]',
    ]
    expected_info_list = [(0x10, 0x21, 'solution.cc'),
                          (0x31, 0x41, 'solution.cc')]
    self.assertEqual(self._MakeRangeInfoList(expected_info_list),
                     dwarfdump.ParseDumpOutputForTest(lines))

  def testParseNonContiguousIgnoreEmptyRanges(self):
    """Test that empty ranges are ignored when parsing DW_AT_ranges."""
    lines = [
        'DW_TAG_compile_unit',
        'DW_AT_name ("solution.cc")',
        'DW_AT_ranges (0x1',
        '[0x1, 0x1)',
        '[0x10, 0x21)',
        '[0x22, 0x22)',
        '[0x31, 0x41)',
        '[0x42, 0x42))',
    ]
    expected_info_list = [(0x10, 0x21, 'solution.cc'),
                          (0x31, 0x41, 'solution.cc')]
    self.assertEqual(self._MakeRangeInfoList(expected_info_list),
                     dwarfdump.ParseDumpOutputForTest(lines))

  def testParseContiguousAddressRange(self):
    """Test parsing DW_TAG_compile_unit with contiguous address range."""
    lines = [
        'DW_TAG_compile_unit',
        'DW_AT_name ("solution.cc")',
        'DW_AT_low_pc (0x1)',
        'DW_AT_high_pc (0x10)',
    ]
    expected_info_list = [
        (0x1, 0x10, 'solution.cc'),
    ]
    self.assertEqual(self._MakeRangeInfoList(expected_info_list),
                     dwarfdump.ParseDumpOutputForTest(lines))

  def testParseSingleAddress(self):
    """Test parsing DW_TAG_compile_unit with single address."""
    lines = [
        'DW_TAG_compile_unit',
        'DW_AT_name ("solution.cc")',
        'DW_AT_low_pc (0x10)',
    ]
    expected_info_list = [
        (0x10, 0x11, 'solution.cc'),
    ]
    self.assertEqual(self._MakeRangeInfoList(expected_info_list),
                     dwarfdump.ParseDumpOutputForTest(lines))

  def testParseEmptyCompileUnit(self):
    """Test parsing empty DW_TAG_compile_unit."""
    lines = ['DW_TAG_compile_unit']
    self.assertEqual([], dwarfdump.ParseDumpOutputForTest(lines))

  def testConsecutiveCompileUnits(self):
    """Test parsing consecutive DW_TAG_compile_units."""
    lines = [
        'DW_TAG_compile_unit',
        'DW_AT_name ("foo.cc")',
        'DW_AT_low_pc (0x1)',
        'DW_AT_high_pc (0x10)',
        'DW_TAG_compile_unit',
        'DW_AT_name ("bar.cc")',
        'DW_AT_low_pc (0x12)',
        'DW_AT_high_pc (0x20)',
    ]
    expected_info_list = [(0x1, 0x10, 'foo.cc'), (0x12, 0x20, 'bar.cc')]
    self.assertEqual(self._MakeRangeInfoList(expected_info_list),
                     dwarfdump.ParseDumpOutputForTest(lines))

  def testTagTerminatedCompileUnit(self):
    """Test parsing DW_TAG_compile_unit where compile unit is followed by a
    non-DW_TAG_compile_unit entry.
    """
    lines = [
        'DW_TAG_compile_unit',
        'DW_AT_name ("foo.cc")',
        'DW_AT_low_pc (0x1)',
        'DW_AT_high_pc (0x10)',
        'DW_TAG_subprogram',
        'DW_AT_name ("bar.cc")',
        'DW_AT_low_pc (0x12)',
        'DW_AT_high_pc (0x20)',
    ]
    expected_info_list = [
        (0x1, 0x10, 'foo.cc'),
    ]
    self.assertEqual(self._MakeRangeInfoList(expected_info_list),
                     dwarfdump.ParseDumpOutputForTest(lines))

  def testHandlePrefixes(self):
    """Test parsing DW_TAG_compile_unit where 'DW_' does not start line in
    DW_TAG_compile_unit entry.
    """
    lines = [
        '0x1 DW_TAG_compile_unit',
        '    DW_AT_language (DW_LANG_C_plus_plus_14)',
        '    DW_AT_name     ("solution.cc")',
        '    DW_AT_stmt_list (0x5)',
        '    DW_AT_low_pc   (0x1)',
        '    DW_AT_high_pc  (0x10)',
    ]
    expected_info_list = [
        (0x1, 0x10, 'solution.cc'),
    ]
    self.assertEqual(self._MakeRangeInfoList(expected_info_list),
                     dwarfdump.ParseDumpOutputForTest(lines))

  def testFindAddress(self):
    """Tests for _SourceMapper.FindSourceForTextAddress()"""
    lines = [
        'DW_TAG_compile_unit',
        'DW_AT_name     ("foo.cc")',
        'DW_AT_low_pc   (0x1)',
        'DW_AT_high_pc  (0x10)',
        'DW_TAG_compile_unit',
        'DW_AT_name     ("bar.cc")',
        'DW_AT_low_pc   (0x21)',
        'DW_AT_high_pc  (0x30)',
        'DW_TAG_compile_unit',
        'DW_AT_name     ("baz.cc")',
        'DW_AT_low_pc   (0x41)',
        'DW_AT_high_pc  (0x50)',
    ]
    source_mapper = dwarfdump.CreateAddressSourceMapperForTest(lines)
    # Address is before first range.
    self.assertEqual('', source_mapper.FindSourceForTextAddress(0x0))
    # Address matches start of first range.
    self.assertEqual('foo.cc', source_mapper.FindSourceForTextAddress(0x1))
    # Address is in the middle of middle range.
    self.assertEqual('bar.cc', source_mapper.FindSourceForTextAddress(0x2a))
    # Address matches end of last range.
    self.assertEqual('baz.cc', source_mapper.FindSourceForTextAddress(0x4f))
    # Address is after lange range.
    self.assertEqual('', source_mapper.FindSourceForTextAddress(0x50))


if __name__ == '__main__':
  unittest.main()