# Copyright 2015 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import os, subprocess from autotest_lib.client.bin import test from autotest_lib.client.bin import utils from autotest_lib.client.common_lib import error def chain_length(line): """ Return the length of a chain in |line|. E.g. if line is "... chain: nr:5" return 5 """ return int(line.split(':')[2]) class hardware_PerfCallgraphVerification(test.test): """ Verify perf -g output has a complete call chain in user space. """ version = 1 preserve_srcdir = True def initialize(self): self.job.require_gcc() def setup(self): os.chdir(self.srcdir) utils.make('clean') utils.make('all') def report_has_callchain_length_at_least(self, lines, wanted_length): # Look through the output of 'perf report' for the following which # shows a long enough callchain from the test graph program: # ... PERF_RECORD_SAMPLE(IP, 2): 7015/7015: ... # ... chain: nr:5 # ..... 0: fffff # ..... 1: 00007 # ..... 2: 00007 # ..... 3: 00007 # ..... 4: f5ee2 # ... thread: test.:7015 # ...... dso: /tmp/graph.o found_sample = False length = 0 for line in lines: if 'PERF_RECORD_SAMPLE' in line: found_sample = True if found_sample and 'chain:' in line: length = chain_length(line) if not length >= wanted_length: found_sample = False if (length >= wanted_length and 'dso:' in line and 'src/graph' in line): return True return False def run_once(self): """ Collect a perf callchain profile and check the detailed perf report. """ # Waiting on ARM/perf support if not utils.get_current_kernel_arch().startswith('x86'): return # These boards are not supported unsupported_boards = ['gizmo'] board = utils.get_board() if board in unsupported_boards: return try: graph = os.path.join(self.srcdir, 'graph') perf_file_path = os.tempnam() perf_record_args = ['perf', 'record', '-e', 'cycles', '-g', '-o', perf_file_path, '--', graph] perf_report_args = ['perf', 'report', '-D', '-i', perf_file_path] try: subprocess.check_output(perf_record_args, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as cmd_error: raise error.TestFail("Running command [%s] failed: %s" % (' '.join(perf_record_args), cmd_error.output)) # Make sure the file still exists. if not os.path.isfile(perf_file_path): raise error.TestFail('Could not find perf output file: ' + perf_file_path) p = subprocess.Popen(perf_report_args, stdout=subprocess.PIPE) result = self.report_has_callchain_length_at_least(p.stdout, 3) for _ in p.stdout: pass p.wait() finally: os.remove(perf_file_path) if not result: raise error.TestFail('Callchain not found')