""" Function tracer profiler for autotest. @author: David Sharp (dhsharp@google.com) """ import logging, os, signal, time from autotest_lib.client.bin import profiler, utils from autotest_lib.client.common_lib import error class ftrace(profiler.profiler): """ ftrace profiler for autotest. It builds ftrace from souce and runs trace-cmd with configurable parameters. @see: git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git """ version = 1 mountpoint = '/sys/kernel/debug' tracing_dir = os.path.join(mountpoint, 'tracing') @staticmethod def join_command(cmd): """ Shell escape the command for BgJob. grmbl. @param cmd: Command list. """ result = [] for arg in cmd: arg = '"%s"' % utils.sh_escape(arg) result += [arg] return ' '.join(result) def setup(self, tarball='trace-cmd.tar.bz2', **kwargs): """ Build and install trace-cmd from source. The tarball was obtained by checking the git repo at 09-14-2010, removing the Documentation and the .git folders, and compressing it. @param tarball: Path to trace-cmd tarball. @param **kwargs: Dictionary with additional parameters. """ self.tarball = utils.unmap_url(self.bindir, tarball, self.tmpdir) utils.extract_tarball_to_dir(self.tarball, self.srcdir) os.chdir(self.srcdir) utils.make("prefix='%s'" % self.builddir) utils.make("prefix='%s' install" % self.builddir) def initialize(self, tracepoints, buffer_size_kb=1408, **kwargs): """ Initialize ftrace profiler. @param tracepoints: List containing a mix of tracpoint names and (tracepoint name, filter) tuples. Tracepoint names are as accepted by trace-cmd -e, eg "syscalls", or "syscalls:sys_enter_read". Filters are as accepted by trace-cmd -f, eg "((sig >= 10 && sig < 15) || sig == 17)" @param buffer_size_kb: Set the size of the ring buffer (per cpu). """ self.job.require_gcc() self.trace_cmd_args = ['-b', str(buffer_size_kb)] for tracepoint in tracepoints: if isinstance(tracepoint, tuple): tracepoint, event_filter = tracepoint else: event_filter = None self.trace_cmd_args += ['-e', tracepoint] if event_filter: self.trace_cmd_args += ['-f', event_filter] self.builddir = os.path.join(self.bindir, 'build') if not os.path.isdir(self.builddir): os.makedirs(self.builddir) self.trace_cmd = os.path.join(self.builddir, 'bin', 'trace-cmd') def start(self, test): """ Start ftrace profiler @param test: Autotest test in which the profiler will operate on. """ # Make sure debugfs is mounted and tracing disabled. utils.system('%s reset' % self.trace_cmd) output_dir = os.path.join(test.profdir, 'ftrace') if not os.path.isdir(output_dir): os.makedirs(output_dir) self.output = os.path.join(output_dir, 'trace.dat') cmd = [self.trace_cmd, 'record', '-o', self.output] cmd += self.trace_cmd_args self.record_job = utils.BgJob(self.join_command(cmd), stderr_tee=utils.TEE_TO_LOGS) # Wait for tracing to be enabled. If trace-cmd dies before enabling # tracing, then there was a problem. tracing_on = os.path.join(self.tracing_dir, 'tracing_on') while (self.record_job.sp.poll() is None and utils.read_file(tracing_on).strip() != '1'): time.sleep(0.1) if self.record_job.sp.poll() is not None: utils.join_bg_jobs([self.record_job]) raise error.CmdError(self.record_job.command, self.record_job.sp.returncode, 'trace-cmd exited early.') def stop(self, test): """ Stop ftrace profiler. @param test: Autotest test in which the profiler will operate on. """ os.kill(self.record_job.sp.pid, signal.SIGINT) utils.join_bg_jobs([self.record_job]) # shrink the buffer to free memory. utils.system('%s reset -b 1' % self.trace_cmd) #compress output utils.system('bzip2 %s' % self.output) compressed_output = self.output + '.bz2' # if the compressed trace file is large (10MB), just delete it. compressed_output_size = os.path.getsize(compressed_output) if compressed_output_size > 10*1024*1024: logging.warning('Deleting large trace file %s (%d bytes)', compressed_output, compressed_output_size) os.remove(compressed_output) # remove per-cpu files in case trace-cmd died. utils.system('rm -f %s.cpu*' % self.output)