1 # Copyright 2015 The Chromium OS Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import os, subprocess 6 7 from autotest_lib.client.bin import test 8 from autotest_lib.client.bin import utils 9 from autotest_lib.client.common_lib import error 10 11 def chain_length(line): 12 """ 13 Return the length of a chain in |line|. 14 E.g. if line is "... chain: nr:5" return 5 15 """ 16 return int(line.split(':')[2]) 17 18 class hardware_PerfCallgraphVerification(test.test): 19 """ 20 Verify perf -g output has a complete call chain in user space. 21 """ 22 version = 1 23 preserve_srcdir = True 24 25 def initialize(self): 26 self.job.require_gcc() 27 28 def setup(self): 29 os.chdir(self.srcdir) 30 utils.make('clean') 31 utils.make('all') 32 33 def report_has_callchain_length_at_least(self, lines, wanted_length): 34 # Look through the output of 'perf report' for the following which 35 # shows a long enough callchain from the test graph program: 36 # ... PERF_RECORD_SAMPLE(IP, 2): 7015/7015: ... 37 # ... chain: nr:5 38 # ..... 0: fffff 39 # ..... 1: 00007 40 # ..... 2: 00007 41 # ..... 3: 00007 42 # ..... 4: f5ee2 43 # ... thread: test.:7015 44 # ...... dso: /tmp/graph.o 45 found_sample = False 46 length = 0 47 for line in lines: 48 if 'PERF_RECORD_SAMPLE' in line: 49 found_sample = True 50 if found_sample and 'chain:' in line: 51 length = chain_length(line) 52 if not length >= wanted_length: 53 found_sample = False 54 if (length >= wanted_length and 'dso:' in line and 55 'src/graph' in line): 56 return True 57 return False 58 59 def run_once(self): 60 """ 61 Collect a perf callchain profile and check the detailed perf report. 62 63 """ 64 # Waiting on ARM/perf support 65 if not utils.get_current_kernel_arch().startswith('x86'): 66 return 67 # These boards are not supported 68 unsupported_boards = ['gizmo'] 69 board = utils.get_board() 70 if board in unsupported_boards: 71 return 72 73 try: 74 graph = os.path.join(self.srcdir, 'graph') 75 perf_file_path = os.tempnam() 76 perf_record_args = ['perf', 'record', '-e', 'cycles', '-g', '-o', 77 perf_file_path, '--', graph] 78 perf_report_args = ['perf', 'report', '-D', '-i', perf_file_path] 79 80 try: 81 subprocess.check_output(perf_record_args, 82 stderr=subprocess.STDOUT) 83 except subprocess.CalledProcessError as cmd_error: 84 raise error.TestFail("Running command [%s] failed: %s" % 85 (' '.join(perf_record_args), 86 cmd_error.output)) 87 88 # Make sure the file still exists. 89 if not os.path.isfile(perf_file_path): 90 raise error.TestFail('Could not find perf output file: ' + 91 perf_file_path) 92 93 p = subprocess.Popen(perf_report_args, stdout=subprocess.PIPE) 94 result = self.report_has_callchain_length_at_least(p.stdout, 3) 95 for _ in p.stdout: 96 pass 97 p.wait() 98 99 finally: 100 os.remove(perf_file_path) 101 102 if not result: 103 raise error.TestFail('Callchain not found') 104 105