blob: 07b78f07ab29a99348ec957b33e677ba586d5f38 [file] [log] [blame]
Yabin Cui0930ea82015-10-01 17:24:07 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2015 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17"""Simpleperf runtest runner: run simpleperf runtests on host or on device.
18
Yabin Cuid4360f82015-10-08 11:42:55 -070019For a simpleperf runtest like one_function test, it contains following steps:
201. Run simpleperf record command to record simpleperf_runtest_one_function's
21 running samples, which is generated in perf.data.
222. Run simpleperf report command to parse perf.data, generate perf.report.
234. Parse perf.report and see if it matches expectation.
Yabin Cui0930ea82015-10-01 17:24:07 -070024
Yabin Cuid4360f82015-10-08 11:42:55 -070025The information of all runtests is stored in runtest.conf.
Yabin Cui0930ea82015-10-01 17:24:07 -070026"""
27
Yabin Cui2e83a5d2016-07-26 15:57:10 -070028import os
29import os.path
Yabin Cui0930ea82015-10-01 17:24:07 -070030import re
31import subprocess
Yabin Cui4a6c58d2016-01-12 18:10:45 -080032import sys
Yabin Cui0930ea82015-10-01 17:24:07 -070033import xml.etree.ElementTree as ET
34
35
Yabin Cuid4360f82015-10-08 11:42:55 -070036class CallTreeNode(object):
37
38 def __init__(self, name):
39 self.name = name
40 self.children = []
41
42 def add_child(self, child):
43 self.children.append(child)
44
45 def __str__(self):
46 return 'CallTreeNode:\n' + '\n'.join(self._dump(1))
47
48 def _dump(self, indent):
49 indent_str = ' ' * indent
50 strs = [indent_str + self.name]
51 for child in self.children:
52 strs.extend(child._dump(indent + 1))
53 return strs
54
55
Yabin Cui0930ea82015-10-01 17:24:07 -070056class Symbol(object):
57
Yabin Cuid4360f82015-10-08 11:42:55 -070058 def __init__(self, name, comm, overhead, children_overhead):
Yabin Cui0930ea82015-10-01 17:24:07 -070059 self.name = name
60 self.comm = comm
61 self.overhead = overhead
Yabin Cuid4360f82015-10-08 11:42:55 -070062 # children_overhead is the overhead sum of this symbol and functions
63 # called by this symbol.
64 self.children_overhead = children_overhead
65 self.call_tree = None
66
67 def set_call_tree(self, call_tree):
68 self.call_tree = call_tree
Yabin Cui0930ea82015-10-01 17:24:07 -070069
70 def __str__(self):
Yabin Cuid4360f82015-10-08 11:42:55 -070071 strs = []
72 strs.append('Symbol name=%s comm=%s overhead=%f children_overhead=%f' % (
73 self.name, self.comm, self.overhead, self.children_overhead))
74 if self.call_tree:
75 strs.append('\t%s' % self.call_tree)
76 return '\n'.join(strs)
Yabin Cui0930ea82015-10-01 17:24:07 -070077
78
79class SymbolOverheadRequirement(object):
80
Yabin Cui4a6c58d2016-01-12 18:10:45 -080081 def __init__(self, symbol_name=None, comm=None, min_overhead=None,
Yabin Cui0930ea82015-10-01 17:24:07 -070082 max_overhead=None):
83 self.symbol_name = symbol_name
84 self.comm = comm
85 self.min_overhead = min_overhead
86 self.max_overhead = max_overhead
87
88 def __str__(self):
89 strs = []
Yabin Cui4a6c58d2016-01-12 18:10:45 -080090 strs.append('SymbolOverheadRequirement')
91 if self.symbol_name is not None:
92 strs.append('symbol_name=%s' % self.symbol_name)
Yabin Cui0930ea82015-10-01 17:24:07 -070093 if self.comm is not None:
94 strs.append('comm=%s' % self.comm)
95 if self.min_overhead is not None:
96 strs.append('min_overhead=%f' % self.min_overhead)
97 if self.max_overhead is not None:
98 strs.append('max_overhead=%f' % self.max_overhead)
99 return ' '.join(strs)
100
101 def is_match(self, symbol):
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800102 if self.symbol_name is not None:
103 if self.symbol_name != symbol.name:
104 return False
Yabin Cui0930ea82015-10-01 17:24:07 -0700105 if self.comm is not None:
106 if self.comm != symbol.comm:
107 return False
108 return True
109
Yabin Cuid4360f82015-10-08 11:42:55 -0700110 def check_overhead(self, overhead):
Yabin Cui0930ea82015-10-01 17:24:07 -0700111 if self.min_overhead is not None:
Yabin Cuid4360f82015-10-08 11:42:55 -0700112 if self.min_overhead > overhead:
Yabin Cui0930ea82015-10-01 17:24:07 -0700113 return False
114 if self.max_overhead is not None:
Yabin Cuid4360f82015-10-08 11:42:55 -0700115 if self.max_overhead < overhead:
116 return False
117 return True
118
119
120class SymbolRelationRequirement(object):
121
122 def __init__(self, symbol_name, comm=None):
123 self.symbol_name = symbol_name
124 self.comm = comm
125 self.children = []
126
127 def add_child(self, child):
128 self.children.append(child)
129
130 def __str__(self):
131 return 'SymbolRelationRequirement:\n' + '\n'.join(self._dump(1))
132
133 def _dump(self, indent):
134 indent_str = ' ' * indent
135 strs = [indent_str + self.symbol_name +
136 (' ' + self.comm if self.comm else '')]
137 for child in self.children:
138 strs.extend(child._dump(indent + 1))
139 return strs
140
141 def is_match(self, symbol):
142 if symbol.name != self.symbol_name:
143 return False
144 if self.comm is not None:
145 if symbol.comm != self.comm:
146 return False
147 return True
148
149 def check_relation(self, call_tree):
150 if not call_tree:
151 return False
152 if self.symbol_name != call_tree.name:
153 return False
154 for child in self.children:
155 child_matched = False
156 for node in call_tree.children:
157 if child.check_relation(node):
158 child_matched = True
159 break
160 if not child_matched:
Yabin Cui0930ea82015-10-01 17:24:07 -0700161 return False
162 return True
163
164
165class Test(object):
166
Yabin Cuid4360f82015-10-08 11:42:55 -0700167 def __init__(
168 self,
169 test_name,
170 executable_name,
Yabin Cui68b83832017-07-19 17:54:57 -0700171 disable_host,
172 record_options,
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800173 report_options,
Yabin Cuid4360f82015-10-08 11:42:55 -0700174 symbol_overhead_requirements,
175 symbol_children_overhead_requirements,
176 symbol_relation_requirements):
Yabin Cui0930ea82015-10-01 17:24:07 -0700177 self.test_name = test_name
178 self.executable_name = executable_name
Yabin Cui68b83832017-07-19 17:54:57 -0700179 self.disable_host = disable_host
180 self.record_options = record_options
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800181 self.report_options = report_options
Yabin Cui0930ea82015-10-01 17:24:07 -0700182 self.symbol_overhead_requirements = symbol_overhead_requirements
Yabin Cuid4360f82015-10-08 11:42:55 -0700183 self.symbol_children_overhead_requirements = (
184 symbol_children_overhead_requirements)
185 self.symbol_relation_requirements = symbol_relation_requirements
Yabin Cui0930ea82015-10-01 17:24:07 -0700186
187 def __str__(self):
188 strs = []
189 strs.append('Test test_name=%s' % self.test_name)
190 strs.append('\texecutable_name=%s' % self.executable_name)
Yabin Cui68b83832017-07-19 17:54:57 -0700191 strs.append('\tdisable_host=%s' % self.disable_host)
192 strs.append('\trecord_options=%s' % (' '.join(self.record_options)))
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800193 strs.append('\treport_options=%s' % (' '.join(self.report_options)))
Yabin Cuid4360f82015-10-08 11:42:55 -0700194 strs.append('\tsymbol_overhead_requirements:')
195 for req in self.symbol_overhead_requirements:
196 strs.append('\t\t%s' % req)
197 strs.append('\tsymbol_children_overhead_requirements:')
198 for req in self.symbol_children_overhead_requirements:
199 strs.append('\t\t%s' % req)
200 strs.append('\tsymbol_relation_requirements:')
201 for req in self.symbol_relation_requirements:
202 strs.append('\t\t%s' % req)
Yabin Cui0930ea82015-10-01 17:24:07 -0700203 return '\n'.join(strs)
204
205
206def load_config_file(config_file):
207 tests = []
208 tree = ET.parse(config_file)
209 root = tree.getroot()
210 assert root.tag == 'runtests'
211 for test in root:
212 assert test.tag == 'test'
213 test_name = test.attrib['name']
214 executable_name = None
Yabin Cui68b83832017-07-19 17:54:57 -0700215 disable_host = False
216 record_options = []
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800217 report_options = []
Yabin Cui0930ea82015-10-01 17:24:07 -0700218 symbol_overhead_requirements = []
Yabin Cuid4360f82015-10-08 11:42:55 -0700219 symbol_children_overhead_requirements = []
220 symbol_relation_requirements = []
Yabin Cui0930ea82015-10-01 17:24:07 -0700221 for test_item in test:
222 if test_item.tag == 'executable':
223 executable_name = test_item.attrib['name']
Yabin Cui68b83832017-07-19 17:54:57 -0700224 elif test_item.tag == 'disable_host':
225 disable_host = True
226 elif test_item.tag == 'record':
227 record_options = test_item.attrib['option'].split()
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800228 elif test_item.tag == 'report':
229 report_options = test_item.attrib['option'].split()
Yabin Cuid4360f82015-10-08 11:42:55 -0700230 elif (test_item.tag == 'symbol_overhead' or
231 test_item.tag == 'symbol_children_overhead'):
Yabin Cui0930ea82015-10-01 17:24:07 -0700232 for symbol_item in test_item:
233 assert symbol_item.tag == 'symbol'
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800234 symbol_name = None
235 if 'name' in symbol_item.attrib:
236 symbol_name = symbol_item.attrib['name']
Yabin Cui0930ea82015-10-01 17:24:07 -0700237 comm = None
238 if 'comm' in symbol_item.attrib:
239 comm = symbol_item.attrib['comm']
240 overhead_min = None
241 if 'min' in symbol_item.attrib:
242 overhead_min = float(symbol_item.attrib['min'])
243 overhead_max = None
244 if 'max' in symbol_item.attrib:
245 overhead_max = float(symbol_item.attrib['max'])
246
Yabin Cuid4360f82015-10-08 11:42:55 -0700247 if test_item.tag == 'symbol_overhead':
248 symbol_overhead_requirements.append(
249 SymbolOverheadRequirement(
250 symbol_name,
251 comm,
252 overhead_min,
253 overhead_max)
254 )
255 else:
256 symbol_children_overhead_requirements.append(
257 SymbolOverheadRequirement(
258 symbol_name,
259 comm,
260 overhead_min,
261 overhead_max))
262 elif test_item.tag == 'symbol_callgraph_relation':
263 for symbol_item in test_item:
264 req = load_symbol_relation_requirement(symbol_item)
265 symbol_relation_requirements.append(req)
Yabin Cui0930ea82015-10-01 17:24:07 -0700266
267 tests.append(
Yabin Cuid4360f82015-10-08 11:42:55 -0700268 Test(
269 test_name,
270 executable_name,
Yabin Cui68b83832017-07-19 17:54:57 -0700271 disable_host,
272 record_options,
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800273 report_options,
Yabin Cuid4360f82015-10-08 11:42:55 -0700274 symbol_overhead_requirements,
275 symbol_children_overhead_requirements,
276 symbol_relation_requirements))
Yabin Cui0930ea82015-10-01 17:24:07 -0700277 return tests
278
279
Yabin Cuid4360f82015-10-08 11:42:55 -0700280def load_symbol_relation_requirement(symbol_item):
281 symbol_name = symbol_item.attrib['name']
282 comm = None
283 if 'comm' in symbol_item.attrib:
284 comm = symbol_item.attrib['comm']
285 req = SymbolRelationRequirement(symbol_name, comm)
286 for item in symbol_item:
287 child_req = load_symbol_relation_requirement(item)
288 req.add_child(child_req)
289 return req
290
291
Yabin Cui0930ea82015-10-01 17:24:07 -0700292class Runner(object):
293
Yabin Cui26968e62017-01-30 11:34:24 -0800294 def __init__(self, target, perf_path):
295 self.target = target
Yabin Cuib92bae82017-02-10 12:07:29 -0800296 self.is32 = target.endswith('32')
Yabin Cui0930ea82015-10-01 17:24:07 -0700297 self.perf_path = perf_path
Yabin Cui26968e62017-01-30 11:34:24 -0800298 self.use_callgraph = False
299 self.sampler = 'cpu-cycles'
Yabin Cui0930ea82015-10-01 17:24:07 -0700300
Yabin Cuid4360f82015-10-08 11:42:55 -0700301 def record(self, test_executable_name, record_file, additional_options=[]):
Yabin Cui26968e62017-01-30 11:34:24 -0800302 call_args = [self.perf_path, 'record']
Yabin Cuib92bae82017-02-10 12:07:29 -0800303 call_args += ['--duration', '2']
Yabin Cui26968e62017-01-30 11:34:24 -0800304 call_args += ['-e', '%s:u' % self.sampler]
305 if self.use_callgraph:
306 call_args += ['-f', '1000', '-g']
307 call_args += ['-o', record_file]
308 call_args += additional_options
Yabin Cuib92bae82017-02-10 12:07:29 -0800309 test_executable_name += '32' if self.is32 else '64'
Yabin Cui26968e62017-01-30 11:34:24 -0800310 call_args += [test_executable_name]
Yabin Cui0930ea82015-10-01 17:24:07 -0700311 self._call(call_args)
312
Yabin Cuid4360f82015-10-08 11:42:55 -0700313 def report(self, record_file, report_file, additional_options=[]):
Yabin Cui26968e62017-01-30 11:34:24 -0800314 call_args = [self.perf_path, 'report']
315 call_args += ['-i', record_file]
316 if self.use_callgraph:
317 call_args += ['-g', 'callee']
318 call_args += additional_options
Yabin Cui0930ea82015-10-01 17:24:07 -0700319 self._call(call_args, report_file)
320
321 def _call(self, args, output_file=None):
322 pass
323
324
325class HostRunner(Runner):
326
327 """Run perf test on host."""
328
Yabin Cuib92bae82017-02-10 12:07:29 -0800329 def __init__(self, target):
330 perf_path = 'simpleperf32' if target.endswith('32') else 'simpleperf'
331 super(HostRunner, self).__init__(target, perf_path)
Yabin Cui26968e62017-01-30 11:34:24 -0800332
Yabin Cui0930ea82015-10-01 17:24:07 -0700333 def _call(self, args, output_file=None):
334 output_fh = None
335 if output_file is not None:
336 output_fh = open(output_file, 'w')
337 subprocess.check_call(args, stdout=output_fh)
338 if output_fh is not None:
339 output_fh.close()
340
341
342class DeviceRunner(Runner):
343
344 """Run perf test on device."""
345
Yabin Cuib92bae82017-02-10 12:07:29 -0800346 def __init__(self, target):
Yabin Cuid30feeb2016-08-04 11:00:29 -0700347 self.tmpdir = '/data/local/tmp/'
Yabin Cuib92bae82017-02-10 12:07:29 -0800348 perf_path = 'simpleperf32' if target.endswith('32') else 'simpleperf'
349 super(DeviceRunner, self).__init__(target, self.tmpdir + perf_path)
Yabin Cuid30feeb2016-08-04 11:00:29 -0700350 self._download(os.environ['OUT'] + '/system/xbin/' + perf_path, self.tmpdir)
Yabin Cui2e83a5d2016-07-26 15:57:10 -0700351
Yabin Cui0930ea82015-10-01 17:24:07 -0700352 def _call(self, args, output_file=None):
353 output_fh = None
354 if output_file is not None:
355 output_fh = open(output_file, 'w')
356 args_with_adb = ['adb', 'shell']
Yabin Cuib92bae82017-02-10 12:07:29 -0800357 args_with_adb.append('export LD_LIBRARY_PATH=' + self.tmpdir + ' && ' + ' '.join(args))
Yabin Cui0930ea82015-10-01 17:24:07 -0700358 subprocess.check_call(args_with_adb, stdout=output_fh)
359 if output_fh is not None:
360 output_fh.close()
361
Yabin Cui2e83a5d2016-07-26 15:57:10 -0700362 def _download(self, file, to_dir):
363 args = ['adb', 'push', file, to_dir]
364 subprocess.check_call(args)
365
366 def record(self, test_executable_name, record_file, additional_options=[]):
Yabin Cuib92bae82017-02-10 12:07:29 -0800367 self._download(os.environ['OUT'] + '/system/bin/' + test_executable_name +
368 ('32' if self.is32 else '64'), self.tmpdir)
Yabin Cuid30feeb2016-08-04 11:00:29 -0700369 super(DeviceRunner, self).record(self.tmpdir + test_executable_name,
370 self.tmpdir + record_file,
371 additional_options)
372
373 def report(self, record_file, report_file, additional_options=[]):
374 super(DeviceRunner, self).report(self.tmpdir + record_file,
375 report_file,
376 additional_options)
Yabin Cui0930ea82015-10-01 17:24:07 -0700377
378class ReportAnalyzer(object):
379
380 """Check if perf.report matches expectation in Configuration."""
381
Yabin Cuid4360f82015-10-08 11:42:55 -0700382 def _read_report_file(self, report_file, has_callgraph):
383 fh = open(report_file, 'r')
384 lines = fh.readlines()
385 fh.close()
Yabin Cui0930ea82015-10-01 17:24:07 -0700386
Yabin Cuid4360f82015-10-08 11:42:55 -0700387 lines = [x.rstrip() for x in lines]
388 blank_line_index = -1
389 for i in range(len(lines)):
390 if not lines[i]:
391 blank_line_index = i
392 assert blank_line_index != -1
393 assert blank_line_index + 1 < len(lines)
394 title_line = lines[blank_line_index + 1]
395 report_item_lines = lines[blank_line_index + 2:]
396
397 if has_callgraph:
398 assert re.search(r'^Children\s+Self\s+Command.+Symbol$', title_line)
399 else:
400 assert re.search(r'^Overhead\s+Command.+Symbol$', title_line)
401
402 return self._parse_report_items(report_item_lines, has_callgraph)
403
404 def _parse_report_items(self, lines, has_callgraph):
405 symbols = []
406 cur_symbol = None
407 call_tree_stack = {}
408 vertical_columns = []
409 last_node = None
410 last_depth = -1
411
412 for line in lines:
413 if not line:
414 continue
415 if not line[0].isspace():
416 if has_callgraph:
Yabin Cui68b83832017-07-19 17:54:57 -0700417 items = line.split(None, 6)
418 assert len(items) == 7
419 children_overhead = float(items[0][:-1])
420 overhead = float(items[1][:-1])
421 comm = items[2]
422 symbol_name = items[6]
Yabin Cuid4360f82015-10-08 11:42:55 -0700423 cur_symbol = Symbol(symbol_name, comm, overhead, children_overhead)
424 symbols.append(cur_symbol)
425 else:
Yabin Cui68b83832017-07-19 17:54:57 -0700426 items = line.split(None, 5)
427 assert len(items) == 6
428 overhead = float(items[0][:-1])
429 comm = items[1]
430 symbol_name = items[5]
Yabin Cuid4360f82015-10-08 11:42:55 -0700431 cur_symbol = Symbol(symbol_name, comm, overhead, 0)
432 symbols.append(cur_symbol)
433 # Each report item can have different column depths.
434 vertical_columns = []
435 else:
436 for i in range(len(line)):
437 if line[i] == '|':
438 if not vertical_columns or vertical_columns[-1] < i:
439 vertical_columns.append(i)
440
441 if not line.strip('| \t'):
442 continue
443 if line.find('-') == -1:
444 function_name = line.strip('| \t')
445 node = CallTreeNode(function_name)
446 last_node.add_child(node)
447 last_node = node
448 call_tree_stack[last_depth] = node
449 else:
450 pos = line.find('-')
451 depth = -1
452 for i in range(len(vertical_columns)):
453 if pos >= vertical_columns[i]:
454 depth = i
455 assert depth != -1
456
457 line = line.strip('|- \t')
458 m = re.search(r'^[\d\.]+%[-\s]+(.+)$', line)
459 if m:
460 function_name = m.group(1)
461 else:
462 function_name = line
463
464 node = CallTreeNode(function_name)
465 if depth == 0:
466 cur_symbol.set_call_tree(node)
467
468 else:
469 call_tree_stack[depth - 1].add_child(node)
470 call_tree_stack[depth] = node
471 last_node = node
472 last_depth = depth
473
Yabin Cui0930ea82015-10-01 17:24:07 -0700474 return symbols
475
Yabin Cuid4360f82015-10-08 11:42:55 -0700476 def check_report_file(self, test, report_file, has_callgraph):
477 symbols = self._read_report_file(report_file, has_callgraph)
478 if not self._check_symbol_overhead_requirements(test, symbols):
479 return False
480 if has_callgraph:
481 if not self._check_symbol_children_overhead_requirements(test, symbols):
482 return False
483 if not self._check_symbol_relation_requirements(test, symbols):
484 return False
485 return True
Yabin Cui0930ea82015-10-01 17:24:07 -0700486
Yabin Cuid4360f82015-10-08 11:42:55 -0700487 def _check_symbol_overhead_requirements(self, test, symbols):
Yabin Cui0930ea82015-10-01 17:24:07 -0700488 result = True
489 matched = [False] * len(test.symbol_overhead_requirements)
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800490 matched_overhead = [0] * len(test.symbol_overhead_requirements)
Yabin Cui0930ea82015-10-01 17:24:07 -0700491 for symbol in symbols:
492 for i in range(len(test.symbol_overhead_requirements)):
Yabin Cuid4360f82015-10-08 11:42:55 -0700493 req = test.symbol_overhead_requirements[i]
494 if req.is_match(symbol):
Yabin Cui0930ea82015-10-01 17:24:07 -0700495 matched[i] = True
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800496 matched_overhead[i] += symbol.overhead
Yabin Cui0930ea82015-10-01 17:24:07 -0700497 for i in range(len(matched)):
498 if not matched[i]:
499 print 'requirement (%s) has no matched symbol in test %s' % (
500 test.symbol_overhead_requirements[i], test)
501 result = False
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800502 else:
503 fulfilled = req.check_overhead(matched_overhead[i])
504 if not fulfilled:
505 print "Symbol (%s) doesn't match requirement (%s) in test %s" % (
506 symbol, req, test)
507 result = False
Yabin Cui0930ea82015-10-01 17:24:07 -0700508 return result
509
Yabin Cuid4360f82015-10-08 11:42:55 -0700510 def _check_symbol_children_overhead_requirements(self, test, symbols):
511 result = True
512 matched = [False] * len(test.symbol_children_overhead_requirements)
513 for symbol in symbols:
514 for i in range(len(test.symbol_children_overhead_requirements)):
515 req = test.symbol_children_overhead_requirements[i]
516 if req.is_match(symbol):
517 matched[i] = True
518 fulfilled = req.check_overhead(symbol.children_overhead)
519 if not fulfilled:
520 print "Symbol (%s) doesn't match requirement (%s) in test %s" % (
521 symbol, req, test)
522 result = False
523 for i in range(len(matched)):
524 if not matched[i]:
525 print 'requirement (%s) has no matched symbol in test %s' % (
526 test.symbol_children_overhead_requirements[i], test)
527 result = False
528 return result
529
530 def _check_symbol_relation_requirements(self, test, symbols):
531 result = True
532 matched = [False] * len(test.symbol_relation_requirements)
533 for symbol in symbols:
534 for i in range(len(test.symbol_relation_requirements)):
535 req = test.symbol_relation_requirements[i]
536 if req.is_match(symbol):
537 matched[i] = True
538 fulfilled = req.check_relation(symbol.call_tree)
539 if not fulfilled:
540 print "Symbol (%s) doesn't match requirement (%s) in test %s" % (
541 symbol, req, test)
542 result = False
543 for i in range(len(matched)):
544 if not matched[i]:
545 print 'requirement (%s) has no matched symbol in test %s' % (
546 test.symbol_relation_requirements[i], test)
547 result = False
548 return result
549
Yabin Cui0930ea82015-10-01 17:24:07 -0700550
Yabin Cui26968e62017-01-30 11:34:24 -0800551def build_runner(target, use_callgraph, sampler):
Yabin Cuib92bae82017-02-10 12:07:29 -0800552 if target == 'host32' and use_callgraph:
553 print "Current 64bit linux host doesn't support `simpleperf32 record -g`"
554 return None
555 if target.startswith('host'):
556 runner = HostRunner(target)
Yabin Cui26968e62017-01-30 11:34:24 -0800557 else:
Yabin Cuib92bae82017-02-10 12:07:29 -0800558 runner = DeviceRunner(target)
Yabin Cui26968e62017-01-30 11:34:24 -0800559 runner.use_callgraph = use_callgraph
560 runner.sampler = sampler
561 return runner
562
563
564def test_with_runner(runner, tests):
565 report_analyzer = ReportAnalyzer()
566 for test in tests:
Yabin Cui68b83832017-07-19 17:54:57 -0700567 if test.disable_host and runner.target.startswith('host'):
568 print('Skip test %s on %s' % (test.test_name, runner.target))
569 continue
570 runner.record(test.executable_name, 'perf.data', additional_options = test.record_options)
Yabin Cui430c1492020-01-13 15:25:16 -0800571 runner.report('perf.data', 'perf.report', additional_options = test.report_options)
572 result = report_analyzer.check_report_file(test, 'perf.report', runner.use_callgraph)
Yabin Cui26968e62017-01-30 11:34:24 -0800573 str = 'test %s on %s ' % (test.test_name, runner.target)
574 if runner.use_callgraph:
575 str += 'with call graph '
576 str += 'using %s ' % runner.sampler
577 str += ' Succeeded' if result else 'Failed'
578 print str
579 if not result:
580 exit(1)
581
582
583def runtest(target_options, use_callgraph_options, sampler_options, selected_tests):
584 tests = load_config_file(os.path.dirname(os.path.realpath(__file__)) + \
585 '/runtest.conf')
586 if selected_tests is not None:
587 new_tests = []
588 for test in tests:
589 if test.test_name in selected_tests:
590 new_tests.append(test)
591 tests = new_tests
592 for target in target_options:
593 for use_callgraph in use_callgraph_options:
594 for sampler in sampler_options:
595 runner = build_runner(target, use_callgraph, sampler)
Yabin Cuib92bae82017-02-10 12:07:29 -0800596 if runner is not None:
597 test_with_runner(runner, tests)
Yabin Cui26968e62017-01-30 11:34:24 -0800598
599
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800600def main():
Yabin Cuib92bae82017-02-10 12:07:29 -0800601 target_options = ['host64', 'host32', 'device64', 'device32']
Yabin Cui26968e62017-01-30 11:34:24 -0800602 use_callgraph_options = [False, True]
Yabin Cui430c1492020-01-13 15:25:16 -0800603 sampler_options = ['cpu-cycles']
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800604 selected_tests = None
605 i = 1
606 while i < len(sys.argv):
607 if sys.argv[i] == '--host':
Yabin Cuib92bae82017-02-10 12:07:29 -0800608 target_options = ['host64', 'host32']
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800609 elif sys.argv[i] == '--device':
Yabin Cuib92bae82017-02-10 12:07:29 -0800610 target_options = ['device64', 'device32']
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800611 elif sys.argv[i] == '--normal':
Yabin Cui26968e62017-01-30 11:34:24 -0800612 use_callgraph_options = [False]
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800613 elif sys.argv[i] == '--callgraph':
Yabin Cui26968e62017-01-30 11:34:24 -0800614 use_callgraph_options = [True]
Yabin Cui4a6c58d2016-01-12 18:10:45 -0800615 elif sys.argv[i] == '--test':
616 if i < len(sys.argv):
617 i += 1
618 for test in sys.argv[i].split(','):
619 if selected_tests is None:
620 selected_tests = {}
621 selected_tests[test] = True
622 i += 1
Yabin Cui26968e62017-01-30 11:34:24 -0800623 runtest(target_options, use_callgraph_options, sampler_options, selected_tests)
Yabin Cui0930ea82015-10-01 17:24:07 -0700624
625if __name__ == '__main__':
626 main()