1#!/usr/bin/env vpython
2# Copyright 2015 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6from __future__ import print_function
7
8import collections
9import os
10import re
11import sys
12import unittest
13
14import cyglog_to_orderfile
15import symbol_extractor
16
17import test_utils
18from test_utils import SimpleTestSymbol
19
20
21sys.path.insert(
22    0, os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
23                    'build', 'android'))
24
25import pylib.constants.host_paths as host_paths
26
27
28# Used for fake demangling on bots where c++filt does not exist.
29CTOR_PATTERN = re.compile(r'EEEC[12]Ev$')
30CTOR_REPLACEMENT = 'EEECEv'
31DTOR_PATTERN = re.compile(r'EEED[12]Ev$')
32DTOR_REPLACEMENT = 'EEEDEv'
33
34
35SectionTestSymbol = collections.namedtuple(
36    'SectionTestSymbol', ['name', 'section'])
37
38
39class TestObjectFileProcessor(cyglog_to_orderfile.ObjectFileProcessor):
40  def __init__(self, symbol_to_sections):
41    super(TestObjectFileProcessor, self).__init__(None)
42    self._symbol_to_sections_map = symbol_to_sections
43
44
45class TestCyglogToOrderfile(unittest.TestCase):
46  def setUp(self):
47    self._old_demangle = None
48    if not os.path.exists(host_paths.ToolPath('c++filt', 'arm')):
49      print('Using fake demangling due to missing c++filt binary')
50      self._old_demangle = symbol_extractor.DemangleSymbol
51      symbol_extractor.DemangleSymbol = _FakeDemangle
52
53  def tearDown(self):
54    if self._old_demangle is not None:
55      symbol_extractor.DemangleSymbol = self._old_demangle
56
57  def assertDictWithUnorderedListEqual(self, expected, observed):
58    unexpected = set()
59    missing = set()
60    for i in expected:
61      if i not in observed:
62        missing.add(i)
63      else:
64        try:
65          self.assertListEqual(sorted(expected[i]), sorted(observed[i]))
66        except self.failureException, e:
67          raise self.failureException('For key {}: {}'.format(i, e))
68    for i in observed:
69      # All i that are in expected have already been tested.
70      if i not in expected:
71        unexpected.add(i)
72    failure_items = []
73    if unexpected:
74      failure_items.append('Unexpected keys: {}'.format(' '.join(unexpected)))
75    if missing:
76      failure_items.append('Missing keys: {}'.format(' '.join(missing)))
77    if failure_items:
78      raise self.failureException('\n'.join(failure_items))
79
80  def testWarnAboutDuplicates(self):
81    offsets = [0x1, 0x2, 0x3]
82    self.assertTrue(cyglog_to_orderfile._WarnAboutDuplicates(offsets))
83    offsets.append(0x1)
84    self.assertFalse(cyglog_to_orderfile._WarnAboutDuplicates(offsets))
85
86  def testSymbolsAtOffsetExactMatch(self):
87    symbol_infos = [SimpleTestSymbol('1', 0x10, 0x13)]
88    generator = cyglog_to_orderfile.OffsetOrderfileGenerator(
89        test_utils.TestSymbolOffsetProcessor(symbol_infos), None)
90    syms = generator._SymbolsAtOffset(0x10)
91    self.assertEquals(1, len(syms))
92    self.assertEquals(symbol_infos[0], syms[0])
93
94  def testSymbolsAtOffsetInexectMatch(self):
95    symbol_infos = [SimpleTestSymbol('1', 0x10, 0x13)]
96    generator = cyglog_to_orderfile.OffsetOrderfileGenerator(
97        test_utils.TestSymbolOffsetProcessor(symbol_infos), None)
98    syms = generator._SymbolsAtOffset(0x11)
99    self.assertEquals(1, len(syms))
100    self.assertEquals(symbol_infos[0], syms[0])
101
102  def testSameCtorOrDtorNames(self):
103    same_name = cyglog_to_orderfile.ObjectFileProcessor._SameCtorOrDtorNames
104    self.assertTrue(same_name(
105        '_ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEEC1Ev',
106        '_ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEEC2Ev'))
107    self.assertTrue(same_name(
108        '_ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEED1Ev',
109        '_ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEED2Ev'))
110    self.assertFalse(same_name(
111        '_ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEEC1Ev',
112        '_ZNSt3__119foo_iteratorIcNS_11char_traitsIcEEEC1Ev'))
113    self.assertFalse(same_name(
114        '_ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEE',
115        '_ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEE'))
116
117  def testGetSymbolToSectionsMap(self):
118    processor = cyglog_to_orderfile.ObjectFileProcessor(None)
119    processor._GetAllSymbolInfos = lambda: [
120        SectionTestSymbol('.LTHUNK.foobar', 'unused'),
121        SectionTestSymbol('_Znwj', '.text._Znwj'),
122        SectionTestSymbol(  # Ctor/Dtor same name.
123            '_ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEED1Ev',
124            '.text._ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEED1Ev'),
125        SectionTestSymbol(
126            '_ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEED1Ev',
127            '.text._ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEED2Ev'),
128        SectionTestSymbol( # Ctor/Dtor different name.
129            '_ZNSt3__119istreambuf2_iteratorIcNS_11char_traitsIcEEEC1Ev',
130            ('.text. _ZNSt3__119istreambuf2_iteratorIcNS_11char_'
131             'traitsIcEEEC1Ev')),
132        SectionTestSymbol(
133            '_ZNSt3__119istreambuf2_iteratorIcNS_11char_traitsIcEEEC1Ev',
134            '.text._ZNSt3__119foo_iteratorIcNS_11char_traitsIcEEEC1Ev'),
135        SectionTestSymbol('_AssemblyFunction', '.text'),
136        SectionTestSymbol('_UnknownSection', '.surprise._UnknownSection'),
137        SectionTestSymbol('_Znwj', '.text._another_section_for_Znwj')]
138    self.assertDictWithUnorderedListEqual(
139        {'_Znwj': ['.text._Znwj', '.text._another_section_for_Znwj'],
140         # Ctor/Dtor same name.
141         '_ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEED1Ev':
142         ['.text._ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEED1Ev',
143          '.text._ZNSt3__119istreambuf_iteratorIcNS_11char_traitsIcEEED2Ev'],
144         # Ctor/Dtor different name; a warning is emitted but the sections are
145         # still added to the map.
146         '_ZNSt3__119istreambuf2_iteratorIcNS_11char_traitsIcEEEC1Ev':
147         ['.text. _ZNSt3__119istreambuf2_iteratorIcNS_11char_traitsIcEEEC1Ev',
148          '.text._ZNSt3__119foo_iteratorIcNS_11char_traitsIcEEEC1Ev'],
149        },
150        processor.GetSymbolToSectionsMap())
151
152  def testOutputOrderfile(self):
153    # One symbol not matched, one with an odd address, one regularly matched
154    # And two symbols aliased to the same address
155    symbols = [SimpleTestSymbol('Symbol', 0x10, 0x100),
156               SimpleTestSymbol('Symbol2', 0x12, 0x100),
157               SimpleTestSymbol('Symbol3', 0x16, 0x100),
158               SimpleTestSymbol('Symbol3.2', 0x16, 0x0)]
159    generator = cyglog_to_orderfile.OffsetOrderfileGenerator(
160        test_utils.TestSymbolOffsetProcessor(symbols),
161        TestObjectFileProcessor({
162        'Symbol': ['.text.Symbol'],
163        'Symbol2': ['.text.Symbol2', '.text.hot.Symbol2'],
164        'Symbol3': ['.text.Symbol3'],
165        'Symbol3.2': ['.text.Symbol3.2']}))
166    ordered_sections = generator.GetOrderedSections([0x12, 0x17])
167    self.assertListEqual(
168        ['.text.Symbol2',
169         '.text.hot.Symbol2',
170         '.text.Symbol3',
171         '.text.Symbol3.2'],
172        ordered_sections)
173
174
175def _FakeDemangle(mangled):
176  unmangled = CTOR_PATTERN.sub(CTOR_REPLACEMENT, mangled)
177  unmangled = DTOR_PATTERN.sub(DTOR_REPLACEMENT, unmangled)
178  return unmangled
179
180
181if __name__ == '__main__':
182  unittest.main()
183