# 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')