• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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