Home | History | Annotate | Download | only in cros_perf
      1 # Copyright (c) 2011 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 """
      6 This is a profiler class for the perf profiler in ChromeOS. It differs from
      7 the existing perf profiler in autotset by directly substituting the options
      8 passed to the initialize function into the "perf" command line. It allows one
      9 to specify which perf command to run and thus what type of profile to collect
     10 (e.g. "perf record" or "perf stat"). It also does not produce a perf report
     11 on the client (where there are no debug symbols) but instead copies
     12 the perf.data file back to the server for analysis.
     13 """
     14 
     15 import os, signal, subprocess
     16 import threading, time
     17 import shutil
     18 import logging
     19 from autotest_lib.client.bin import profiler
     20 from autotest_lib.client.common_lib import error
     21 
     22 class cros_perf(profiler.profiler):
     23     """ Profiler for running perf """
     24 
     25     version = 1
     26 
     27     def initialize(self, interval=120, duration=10, profile_type='record'):
     28         """Initializes the cros perf profiler.
     29 
     30         Args:
     31             interval (int): How often to start the profiler
     32             duration (int): How long the profiler will run per interval. Set to
     33                 None to run for the full duration of the test.
     34             profile_type (str): Profile type to run.
     35                 Valid options: record, and stat.
     36         """
     37         self.interval = interval
     38         self.duration = duration
     39         self.profile_type = profile_type
     40 
     41         if self.profile_type not in ['record', 'stat']:
     42             raise error.AutotestError(
     43                     'Unknown profile type: %s' % (profile_type))
     44 
     45     def start(self, test):
     46 
     47         # KASLR makes looking up the symbols from the binary impossible, save
     48         # the running symbols.
     49         shutil.copy('/proc/kallsyms', test.profdir)
     50 
     51         self.thread = MonitorThread(
     52                 interval=self.interval,
     53                 duration=self.duration,
     54                 profile_type=self.profile_type,
     55                 test=test)
     56 
     57         self.thread.start()
     58 
     59     def stop(self, test):
     60         self.thread.stop()
     61 
     62 
     63 class MonitorThread(threading.Thread):
     64     """ Thread that runs perf at the specified interval and duration """
     65 
     66     def __init__(self, interval, duration, profile_type, test):
     67         threading.Thread.__init__(self)
     68         self.stopped = threading.Event()
     69         self.interval = interval
     70         self.duration = duration
     71         self.profile_type = profile_type
     72         self.test = test
     73 
     74     def _get_command(self, timestamp):
     75         file_name = 'perf-%s.data' % (int(timestamp))
     76 
     77         path = os.path.join(self.test.profdir, file_name)
     78 
     79         if self.profile_type == 'record':
     80             return ['perf', 'record', '-e', 'cycles', '-g', '--output', path]
     81         elif self.profile_type == 'stat':
     82             return ['perf', 'stat', 'record', '-a', '--output', path]
     83         else:
     84             raise error.AutotestError(
     85                     'Unknown profile type: %s' % (self.profile_type))
     86 
     87     def run(self):
     88         logging.info("perf thread starting")
     89 
     90         sleep_time = 0
     91 
     92         while not self.stopped.wait(sleep_time):
     93             start_time = time.time()
     94 
     95             cmd = self._get_command(start_time)
     96 
     97             logging.info("Running perf: %s", cmd)
     98             process = subprocess.Popen(cmd, close_fds=True)
     99 
    100             logging.info("Sleeping for %ss", self.duration)
    101             self.stopped.wait(self.duration)
    102 
    103             ret_code = process.poll()
    104 
    105             if ret_code is None:
    106                 logging.info("Stopping perf")
    107                 process.send_signal(signal.SIGINT)
    108                 process.wait()
    109             else:
    110                 logging.info(
    111                         'perf terminated early with return code: %d. '
    112                         'Please check your logs.', ret_code)
    113 
    114             end_time = time.time()
    115 
    116             sleep_time = self.interval - (end_time - start_time)
    117 
    118             if sleep_time < 0:
    119                 sleep_time = 0
    120 
    121     def stop(self):
    122         """ Stops the thread """
    123         logging.info("Stopping perf thread")
    124 
    125         self.stopped.set()
    126 
    127         self.join()
    128 
    129         logging.info("perf thread stopped")
    130