普通文本  |  130行  |  4.12 KB

# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""
This is a profiler class for the perf profiler in ChromeOS. It differs from
the existing perf profiler in autotset by directly substituting the options
passed to the initialize function into the "perf" command line. It allows one
to specify which perf command to run and thus what type of profile to collect
(e.g. "perf record" or "perf stat"). It also does not produce a perf report
on the client (where there are no debug symbols) but instead copies
the perf.data file back to the server for analysis.
"""

import os, signal, subprocess
import threading, time
import shutil
import logging
from autotest_lib.client.bin import profiler
from autotest_lib.client.common_lib import error

class cros_perf(profiler.profiler):
    """ Profiler for running perf """

    version = 1

    def initialize(self, interval=120, duration=10, profile_type='record'):
        """Initializes the cros perf profiler.

        Args:
            interval (int): How often to start the profiler
            duration (int): How long the profiler will run per interval. Set to
                None to run for the full duration of the test.
            profile_type (str): Profile type to run.
                Valid options: record, and stat.
        """
        self.interval = interval
        self.duration = duration
        self.profile_type = profile_type

        if self.profile_type not in ['record', 'stat']:
            raise error.AutotestError(
                    'Unknown profile type: %s' % (profile_type))

    def start(self, test):

        # KASLR makes looking up the symbols from the binary impossible, save
        # the running symbols.
        shutil.copy('/proc/kallsyms', test.profdir)

        self.thread = MonitorThread(
                interval=self.interval,
                duration=self.duration,
                profile_type=self.profile_type,
                test=test)

        self.thread.start()

    def stop(self, test):
        self.thread.stop()


class MonitorThread(threading.Thread):
    """ Thread that runs perf at the specified interval and duration """

    def __init__(self, interval, duration, profile_type, test):
        threading.Thread.__init__(self)
        self.stopped = threading.Event()
        self.interval = interval
        self.duration = duration
        self.profile_type = profile_type
        self.test = test

    def _get_command(self, timestamp):
        file_name = 'perf-%s.data' % (int(timestamp))

        path = os.path.join(self.test.profdir, file_name)

        if self.profile_type == 'record':
            return ['perf', 'record', '-e', 'cycles', '-g', '--output', path]
        elif self.profile_type == 'stat':
            return ['perf', 'stat', 'record', '-a', '--output', path]
        else:
            raise error.AutotestError(
                    'Unknown profile type: %s' % (self.profile_type))

    def run(self):
        logging.info("perf thread starting")

        sleep_time = 0

        while not self.stopped.wait(sleep_time):
            start_time = time.time()

            cmd = self._get_command(start_time)

            logging.info("Running perf: %s", cmd)
            process = subprocess.Popen(cmd, close_fds=True)

            logging.info("Sleeping for %ss", self.duration)
            self.stopped.wait(self.duration)

            ret_code = process.poll()

            if ret_code is None:
                logging.info("Stopping perf")
                process.send_signal(signal.SIGINT)
                process.wait()
            else:
                logging.info(
                        'perf terminated early with return code: %d. '
                        'Please check your logs.', ret_code)

            end_time = time.time()

            sleep_time = self.interval - (end_time - start_time)

            if sleep_time < 0:
                sleep_time = 0

    def stop(self):
        """ Stops the thread """
        logging.info("Stopping perf thread")

        self.stopped.set()

        self.join()

        logging.info("perf thread stopped")