Home | History | Annotate | Download | only in hardware_PerfCallgraphVerification
      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