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