"""
OProfile is a system-wide profiler for Linux systems,
capable of profiling all running code at low overhead.
OProfile is released under the GNU GPL.

It consists of a kernel driver and a daemon for collecting sample data,
and several post-profiling tools for turning data into information.

More Info: http://oprofile.sourceforge.net/
Will need some libaries to compile. Do 'apt-get build-dep oprofile'
"""
import logging
import os
import time

from autotest.client import utils, profiler
from autotest.client.shared import error


class oprofile(profiler.profiler):
    version = 7
    setup_done = False

# Notes on whether to use the local copy or the builtin from source:
# local = None
#      Try to use source copy if it works, else use local
# local = False
#       Force use of the source copy
# local = True
#       Force use of the local copy

# http://prdownloads.sourceforge.net/oprofile/oprofile-0.9.4.tar.gz
    def setup(self, tarball='oprofile-0.9.4.tar.bz2', local=None,
              *args, **dargs):
        if local is True:
            return

        try:
            self.tarball = utils.unmap_url(self.bindir, tarball,
                                           self.tmpdir)
            utils.extract_tarball_to_dir(self.tarball, self.srcdir)
            os.chdir(self.srcdir)

            patch = os.path.join(self.bindir, "oprofile-69455.patch")
            utils.system('patch -p1 < %s' % patch)
            utils.configure('--with-kernel-support --prefix=' +
                            self.srcdir)
            utils.make('-j %d' % utils.count_cpus())
            utils.make('install')
        except Exception:
            # Build from source failed.
            # But maybe can still use the local copy
            local_opcontrol = os.path.exists('/usr/bin/opcontrol')
            local_opreport = os.path.exists('/usr/bin/opreport')
            if local is False or not local_opcontrol or not local_opreport:
                raise error.AutotestError('No oprofile available')
        else:
            # if we managed to build, try again to pick binaries
            self._pick_binaries(True)

    def _setup_oprofile(self):
        setup = ' --setup'
        if not self.vmlinux:
            setup += ' --no-vmlinux'
        else:
            setup += ' --vmlinux=%s' % self.vmlinux
        for e in self.events:
            setup += ' --event=%s' % e
        if self.others:
            setup += ' ' + self.others

        utils.system(self.opcontrol + setup)
        self.setup_done = True

    def _pick_binaries(self, after_setup):
        src_opreport = os.path.join(self.srcdir, 'bin/opreport')
        src_opcontrol = os.path.join(self.srcdir, 'bin/opcontrol')

        if (self.local is False and after_setup) or (
                (self.local in (None, False) and
                 os.path.exists(src_opreport) and
                 os.path.exists(src_opcontrol))):
            print "Using source-built copy of oprofile"
            self.opreport = src_opreport
            self.opcontrol = src_opcontrol
            perform_setup = True
        elif not self.local and not after_setup:
            # if we are neither forced to use the local versions and
            # we're not running after setup() then delay the decision
            return
        else:
            print "Using machine local copy of oprofile"
            self.opreport = '/usr/bin/opreport'
            self.opcontrol = '/usr/bin/opcontrol'

        self._setup_oprofile()

    def initialize(self, vmlinux=None, events=[], others=None, local=None, **dargs):
        self.job.require_gcc()

        if not vmlinux:
            self.vmlinux = utils.get_vmlinux()
        else:
            self.vmlinux = vmlinux
        if not len(events):
            self.events = ['default']
        else:
            self.events = events
        self.others = others
        self.local = local

        # If there is existing setup file, oprofile may fail to start with default parameters.
        if os.path.isfile('/root/.oprofile/daemonrc'):
            os.rename('/root/.oprofile/daemonrc', '/root/.oprofile/daemonrc.org')

        self._pick_binaries(False)

    def start(self, test):
        if not self.setup_done:
            self._pick_binaries(True)

        self.start_time = time.ctime()
        utils.system(self.opcontrol + ' --shutdown')
        utils.system(self.opcontrol + ' --reset')
        utils.system(self.opcontrol + ' --start')

    def stop(self, test):
        self.stop_time = time.ctime()
        utils.system(self.opcontrol + ' --stop')
        utils.system(self.opcontrol + ' --dump')

    def report(self, test):
        # Output kernel per-symbol profile report
        reportfile = test.profdir + '/oprofile.kernel'
        if self.vmlinux:
            report = self.opreport + ' -l ' + self.vmlinux
            if os.path.exists(utils.get_modules_dir()):
                report += ' -p ' + utils.get_modules_dir()
            logging.info('Starting oprofile: %s' % self.start_time)
            utils.system(report + ' > ' + reportfile)
            logging.info('Ending oprofile: %s' % self.stop_time)

        else:
            utils.system("echo 'no vmlinux found.' > %s" % reportfile)

        # output profile summary report
        reportfile = test.profdir + '/oprofile.user'
        logging.info('Starting oprofile: %s' % self.start_time)
        utils.system(self.opreport + ' --long-filenames ' + ' >> ' + reportfile)
        logging.info('Ending oprofile: %s' % self.stop_time)

        utils.system(self.opcontrol + ' --shutdown')
