• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5import os, subprocess
6
7from autotest_lib.client.bin import test
8from autotest_lib.client.bin import utils
9from autotest_lib.client.common_lib import error
10
11def 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
18class 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