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""" 6This is a profiler class for the perf profiler in ChromeOS. It differs from 7the existing perf profiler in autotset by directly substituting the options 8passed to the initialize function into the "perf" command line. It allows one 9to 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 11on the client (where there are no debug symbols) but instead copies 12the perf.data file back to the server for analysis. 13""" 14 15import os, signal, subprocess 16import threading, time 17import shutil 18import logging 19from autotest_lib.client.bin import profiler 20from autotest_lib.client.common_lib import error 21 22class 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 63class 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