1#!/usr/bin/env vpython
2# Copyright 2017 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
6"""Tests for process_profiles.py."""
7
8import collections
9import unittest
10
11import process_profiles
12
13from test_utils import (ProfileFile,
14                        SimpleTestSymbol,
15                        TestSymbolOffsetProcessor,
16                        TestProfileManager)
17
18class ProcessProfilesTestCase(unittest.TestCase):
19  START_SYMBOL = 'linker_script_start_of_text'
20
21  def setUp(self):
22    self.symbol_0 = SimpleTestSymbol(self.START_SYMBOL, 0, 0)
23    self.symbol_1 = SimpleTestSymbol('1', 6, 16)
24    self.symbol_2 = SimpleTestSymbol('2', 32, 8)
25    self.symbol_3 = SimpleTestSymbol('3', 40, 12)
26    self.offset_to_symbol_info = (
27        [None] * 3 + [self.symbol_1] * 8 + [None] * 5 + [self.symbol_2] * 4 +
28        [self.symbol_3] * 6)
29    self.symbol_infos = [self.symbol_0, self.symbol_1,
30                         self.symbol_2, self.symbol_3]
31    self._file_counter = 0
32
33  def MakeAnnotatedOffset(self, offset, counts):
34    ao = process_profiles.ProfileManager.AnnotatedOffset(offset)
35    ao._count = counts
36    return ao
37
38  def testGetOffsetToSymbolInfo(self):
39    processor = TestSymbolOffsetProcessor(self.symbol_infos)
40    self.assertListEqual(self.offset_to_symbol_info,
41                         processor.GetDumpOffsetToSymbolInfo())
42
43  def testOverlappingSymbols(self):
44    symbol_1 = SimpleTestSymbol(self.START_SYMBOL, 6, 8)
45    symbol_2 = SimpleTestSymbol('2', 10, 10)
46    processor = TestSymbolOffsetProcessor([symbol_1, symbol_2])
47    self.assertListEqual([symbol_1] * 4 + [symbol_2] * 3,
48                         processor.GetDumpOffsetToSymbolInfo())
49
50  def testSymbolsBeforeStart(self):
51    self.symbol_infos = [SimpleTestSymbol(s.name, s.offset + 8, s.size)
52                         for s in self.symbol_infos]
53    self.symbol_infos.append(SimpleTestSymbol('early', 0, 4))
54    processor = TestSymbolOffsetProcessor(self.symbol_infos)
55    self.assertRaises(AssertionError, processor.GetDumpOffsetToSymbolInfo)
56
57  def testGetReachedOffsetsFromDump(self):
58    processor = TestSymbolOffsetProcessor(self.symbol_infos)
59    # 2 hits for symbol_1, 0 for symbol_2, 1 for symbol_3
60    dump = [8, 12, 48]
61    reached = processor.GetReachedOffsetsFromDump(dump)
62    self.assertListEqual([self.symbol_1.offset, self.symbol_3.offset], reached)
63    # Ordering matters, no repetitions
64    dump = [48, 12, 8, 12, 8, 16]
65    reached = processor.GetReachedOffsetsFromDump(dump)
66    self.assertListEqual([self.symbol_3.offset, self.symbol_1.offset], reached)
67
68  def testSymbolNameToPrimary(self):
69    symbol_infos = [SimpleTestSymbol('1', 8, 16),
70                    SimpleTestSymbol('AnAlias', 8, 16),
71                    SimpleTestSymbol('Another', 40, 16)]
72    processor = TestSymbolOffsetProcessor(symbol_infos)
73    self.assertDictEqual({8: symbol_infos[0],
74                          40: symbol_infos[2]}, processor.OffsetToPrimaryMap())
75
76  def testGetOrderedSymbols(self):
77    processor = TestSymbolOffsetProcessor(self.symbol_infos)
78    self.assertListEqual(['1', '3', self.START_SYMBOL],
79                         processor.GetOrderedSymbols([7, 41, 5, 0]))
80
81  def testOffsetToSymbolsMap(self):
82    symbol_infos = [SimpleTestSymbol('1', 8, 16),
83                    SimpleTestSymbol('AnAlias', 8, 16),
84                    SimpleTestSymbol('Another', 40, 16)]
85    processor = TestSymbolOffsetProcessor(symbol_infos)
86    self.assertDictEqual({8: [symbol_infos[0], symbol_infos[1]],
87                          40: [symbol_infos[2]]},
88                         processor.OffsetToSymbolsMap())
89
90  def testPrimarySizeMismatch(self):
91    symbol_infos = [SimpleTestSymbol('1', 8, 16),
92                    SimpleTestSymbol('AnAlias', 8, 32)]
93    processor = TestSymbolOffsetProcessor(symbol_infos)
94    self.assertRaises(AssertionError, processor.OffsetToPrimaryMap)
95    symbol_infos = [SimpleTestSymbol('1', 8, 0),
96                    SimpleTestSymbol('2', 8, 32),
97                    SimpleTestSymbol('3', 8, 32),
98                    SimpleTestSymbol('4', 8, 0),]
99    processor = TestSymbolOffsetProcessor(symbol_infos)
100    self.assertDictEqual({8: symbol_infos[1]}, processor.OffsetToPrimaryMap())
101
102  def testMatchSymbols(self):
103    symbols = [SimpleTestSymbol('W', 30, 10),
104               SimpleTestSymbol('Y', 60, 5),
105               SimpleTestSymbol('X', 100, 10)]
106    processor = TestSymbolOffsetProcessor(symbols)
107    self.assertListEqual(symbols[1:3],
108                         processor.MatchSymbolNames(['Y', 'X']))
109
110  def testSymbolsSize(self):
111    symbols = [SimpleTestSymbol('W', 10, 1),
112               SimpleTestSymbol('X', 20, 2),
113               SimpleTestSymbol('Y', 30, 4),
114               SimpleTestSymbol('Z', 40, 8)]
115    processor = TestSymbolOffsetProcessor(symbols)
116    self.assertEqual(13, processor.SymbolsSize(['W', 'Y', 'Z']))
117
118  def testMedian(self):
119    self.assertEquals(None, process_profiles._Median([]))
120    self.assertEquals(5, process_profiles._Median([5]))
121    self.assertEquals(5, process_profiles._Median([1, 5, 20]))
122    self.assertEquals(5, process_profiles._Median([4, 6]))
123    self.assertEquals(5, process_profiles._Median([1, 4, 6, 100]))
124    self.assertEquals(5, process_profiles._Median([1, 4, 5, 6, 100]))
125
126  def testRunGroups(self):
127    files = [ProfileFile(40, 0), ProfileFile(100, 0),
128             ProfileFile(200, 1), ProfileFile(35, 1),
129             ProfileFile(42, 0), ProfileFile(95, 0)]
130    mgr = process_profiles.ProfileManager(files)
131    mgr._ComputeRunGroups()
132    self.assertEquals(3, len(mgr._run_groups))
133    self.assertEquals(3, len(mgr._run_groups[0].Filenames()))
134    self.assertEquals(2, len(mgr._run_groups[1].Filenames()))
135    self.assertEquals(1, len(mgr._run_groups[2].Filenames()))
136    self.assertTrue(files[0] in mgr._run_groups[0].Filenames())
137    self.assertTrue(files[3] in mgr._run_groups[0].Filenames())
138    self.assertTrue(files[4] in mgr._run_groups[0].Filenames())
139    self.assertTrue(files[1] in mgr._run_groups[1].Filenames())
140    self.assertTrue(files[5] in mgr._run_groups[1].Filenames())
141    self.assertTrue(files[2] in mgr._run_groups[2].Filenames())
142
143  def testRunGroupSanity(self):
144    files = []
145    # Generate 20 sets of files in groups separated by 60s.
146    for ts_base in xrange(0, 20):
147      ts = ts_base * 60
148      files.extend([ProfileFile(ts, 0, 'browser'),
149                    ProfileFile(ts + 1, 0, 'renderer'),
150                    ProfileFile(ts + 2, 1, 'browser'),
151                    ProfileFile(ts + 3, 0, 'gpu'),
152                    ProfileFile(ts + 2, 1, 'renderer'),
153                    ProfileFile(ts + 5, 1, 'gpu')])
154    # The following call should not assert.
155    process_profiles.ProfileManager(files)._ComputeRunGroups()
156
157    files.extend([ProfileFile(20 * 60, 0, 'browser'),
158                  ProfileFile(20 * 60 + 2, 1, 'renderer'),
159                  ProfileFile(21 * 60, 0, 'browser')] +
160                 [ProfileFile(22 * 60, 0, 'renderer')
161                  for _ in xrange(0, 10)])
162
163    self.assertRaises(AssertionError,
164                      process_profiles.ProfileManager(files)._ComputeRunGroups)
165
166  def testReadOffsets(self):
167    mgr = TestProfileManager({
168        ProfileFile(30, 0): [1, 3, 5, 7],
169        ProfileFile(40, 1): [8, 10],
170        ProfileFile(50, 0): [13, 15]})
171    self.assertListEqual([1, 3, 5, 7, 8, 10, 13, 15],
172                         mgr.GetMergedOffsets())
173    self.assertListEqual([8, 10], mgr.GetMergedOffsets(1))
174    self.assertListEqual([], mgr.GetMergedOffsets(2))
175
176  def testRunGroupOffsets(self):
177    mgr = TestProfileManager({
178        ProfileFile(30, 0): [1, 2, 3, 4],
179        ProfileFile(150, 0): [9, 11, 13],
180        ProfileFile(40, 1): [5, 6, 7]})
181    offsets_list = mgr.GetRunGroupOffsets()
182    self.assertEquals(2, len(offsets_list))
183    self.assertListEqual([1, 2, 3, 4, 5, 6, 7], offsets_list[0])
184    self.assertListEqual([9, 11, 13], offsets_list[1])
185    offsets_list = mgr.GetRunGroupOffsets(0)
186    self.assertEquals(2, len(offsets_list))
187    self.assertListEqual([1, 2, 3, 4], offsets_list[0])
188    self.assertListEqual([9, 11, 13], offsets_list[1])
189    offsets_list = mgr.GetRunGroupOffsets(1)
190    self.assertEquals(2, len(offsets_list))
191    self.assertListEqual([5, 6, 7], offsets_list[0])
192    self.assertListEqual([], offsets_list[1])
193
194  def testSorted(self):
195    # The fact that the ProfileManager sorts by filename is implicit in the
196    # other tests. It is tested explicitly here.
197    mgr = TestProfileManager({
198        ProfileFile(40, 0): [1, 2, 3, 4],
199        ProfileFile(150, 0): [9, 11, 13],
200        ProfileFile(30, 1): [5, 6, 7]})
201    offsets_list = mgr.GetRunGroupOffsets()
202    self.assertEquals(2, len(offsets_list))
203    self.assertListEqual([5, 6, 7, 1, 2, 3, 4], offsets_list[0])
204
205  def testPhases(self):
206    mgr = TestProfileManager({
207        ProfileFile(40, 0): [],
208        ProfileFile(150, 0): [],
209        ProfileFile(30, 1): [],
210        ProfileFile(30, 2): [],
211        ProfileFile(30, 0): []})
212    self.assertEquals(set([0,1,2]), mgr.GetPhases())
213
214  def testGetAnnotatedOffsets(self):
215    mgr = TestProfileManager({
216        ProfileFile(40, 0, ''): [1, 2, 3],
217        ProfileFile(50, 1, ''): [3, 4, 5],
218        ProfileFile(51, 0, 'renderer'): [2, 3, 6],
219        ProfileFile(51, 1, 'gpu-process'): [6, 7],
220        ProfileFile(70, 0, ''): [2, 8, 9],
221        ProfileFile(70, 1, ''): [9]})
222    offsets = mgr.GetAnnotatedOffsets()
223    self.assertListEqual([
224        self.MakeAnnotatedOffset(1, {(0, 'browser'): 1}),
225        self.MakeAnnotatedOffset(2, {(0, 'browser'): 2,
226                                     (0, 'renderer'): 1}),
227        self.MakeAnnotatedOffset(3, {(0, 'browser'): 1,
228                                     (1, 'browser'): 1,
229                                     (0, 'renderer'): 1}),
230        self.MakeAnnotatedOffset(4, {(1, 'browser'): 1}),
231        self.MakeAnnotatedOffset(5, {(1, 'browser'): 1}),
232        self.MakeAnnotatedOffset(6, {(0, 'renderer'): 1,
233                                     (1, 'gpu-process'): 1}),
234        self.MakeAnnotatedOffset(7, {(1, 'gpu-process'): 1}),
235        self.MakeAnnotatedOffset(8, {(0, 'browser'): 1}),
236        self.MakeAnnotatedOffset(9, {(0, 'browser'): 1,
237                                     (1, 'browser'): 1})],
238                         offsets)
239    self.assertListEqual(['browser', 'renderer'],
240                         sorted(offsets[1].Processes()))
241    self.assertListEqual(['browser'], list(offsets[0].Processes()))
242    self.assertListEqual([0], list(offsets[1].Phases()))
243    self.assertListEqual([0, 1], sorted(offsets[2].Phases()))
244    self.assertListEqual([0, 1], sorted(mgr.GetPhases()))
245
246
247if __name__ == '__main__':
248  unittest.main()
249