| """ |
| Function tracer profiler for autotest. |
| |
| @author: David Sharp ([email protected]) |
| """ |
| 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) |