• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
2Function tracer profiler for autotest.
3
4@author: David Sharp (dhsharp@google.com)
5"""
6import logging, os, signal, time
7from autotest_lib.client.bin import profiler, utils
8from autotest_lib.client.common_lib import error
9
10
11class ftrace(profiler.profiler):
12    """
13    ftrace profiler for autotest. It builds ftrace from souce and runs
14    trace-cmd with configurable parameters.
15
16    @see: git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git
17    """
18    version = 1
19
20    mountpoint = '/sys/kernel/debug'
21    tracing_dir = os.path.join(mountpoint, 'tracing')
22
23    @staticmethod
24    def join_command(cmd):
25        """
26        Shell escape the command for BgJob. grmbl.
27
28        @param cmd: Command list.
29        """
30        result = []
31        for arg in cmd:
32            arg = '"%s"' % utils.sh_escape(arg)
33            result += [arg]
34        return ' '.join(result)
35
36
37    def setup(self, tarball='trace-cmd.tar.bz2', **kwargs):
38        """
39        Build and install trace-cmd from source.
40
41        The tarball was obtained by checking the git repo at 09-14-2010,
42        removing the Documentation and the .git folders, and compressing
43        it.
44
45        @param tarball: Path to trace-cmd tarball.
46        @param **kwargs: Dictionary with additional parameters.
47        """
48        self.tarball = utils.unmap_url(self.bindir, tarball, self.tmpdir)
49        utils.extract_tarball_to_dir(self.tarball, self.srcdir)
50        os.chdir(self.srcdir)
51        utils.make("prefix='%s'" % self.builddir)
52        utils.make("prefix='%s' install" % self.builddir)
53
54
55    def initialize(self, tracepoints, buffer_size_kb=1408, **kwargs):
56        """
57        Initialize ftrace profiler.
58
59        @param tracepoints: List containing a mix of tracpoint names and
60                (tracepoint name, filter) tuples. Tracepoint names are as
61                accepted by trace-cmd -e, eg "syscalls", or
62                "syscalls:sys_enter_read". Filters are as accepted by
63                trace-cmd -f, eg "((sig >= 10 && sig < 15) || sig == 17)"
64        @param buffer_size_kb: Set the size of the ring buffer (per cpu).
65        """
66        self.job.require_gcc()
67        self.trace_cmd_args = ['-b', str(buffer_size_kb)]
68        for tracepoint in tracepoints:
69            if isinstance(tracepoint, tuple):
70                tracepoint, event_filter = tracepoint
71            else:
72                event_filter = None
73            self.trace_cmd_args += ['-e', tracepoint]
74            if event_filter:
75                self.trace_cmd_args += ['-f', event_filter]
76
77        self.builddir = os.path.join(self.bindir, 'build')
78        if not os.path.isdir(self.builddir):
79            os.makedirs(self.builddir)
80        self.trace_cmd = os.path.join(self.builddir, 'bin', 'trace-cmd')
81
82
83    def start(self, test):
84        """
85        Start ftrace profiler
86
87        @param test: Autotest test in which the profiler will operate on.
88        """
89        # Make sure debugfs is mounted and tracing disabled.
90        utils.system('%s reset' % self.trace_cmd)
91
92        output_dir = os.path.join(test.profdir, 'ftrace')
93        if not os.path.isdir(output_dir):
94            os.makedirs(output_dir)
95        self.output = os.path.join(output_dir, 'trace.dat')
96        cmd = [self.trace_cmd, 'record', '-o', self.output]
97        cmd += self.trace_cmd_args
98        self.record_job = utils.BgJob(self.join_command(cmd),
99                                      stderr_tee=utils.TEE_TO_LOGS)
100
101        # Wait for tracing to be enabled. If trace-cmd dies before enabling
102        # tracing, then there was a problem.
103        tracing_on = os.path.join(self.tracing_dir, 'tracing_on')
104        while (self.record_job.sp.poll() is None and
105               utils.read_file(tracing_on).strip() != '1'):
106            time.sleep(0.1)
107        if self.record_job.sp.poll() is not None:
108            utils.join_bg_jobs([self.record_job])
109            raise error.CmdError(self.record_job.command,
110                                 self.record_job.sp.returncode,
111                                 'trace-cmd exited early.')
112
113    def stop(self, test):
114        """
115        Stop ftrace profiler.
116
117        @param test: Autotest test in which the profiler will operate on.
118        """
119        os.kill(self.record_job.sp.pid, signal.SIGINT)
120        utils.join_bg_jobs([self.record_job])
121        # shrink the buffer to free memory.
122        utils.system('%s reset -b 1' % self.trace_cmd)
123
124        #compress output
125        utils.system('bzip2 %s' % self.output)
126        compressed_output = self.output + '.bz2'
127        # if the compressed trace file is large (10MB), just delete it.
128        compressed_output_size = os.path.getsize(compressed_output)
129        if compressed_output_size > 10*1024*1024:
130            logging.warning('Deleting large trace file %s (%d bytes)',
131                         compressed_output, compressed_output_size)
132            os.remove(compressed_output)
133        # remove per-cpu files in case trace-cmd died.
134        utils.system('rm -f %s.cpu*' % self.output)
135