xref: /aosp_15_r20/external/autotest/client/cros/kernel_trace.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Liimport logging, os, re
6*9c5db199SXin Li
7*9c5db199SXin Lifrom autotest_lib.client.bin import utils
8*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
9*9c5db199SXin Li
10*9c5db199SXin Liclass KernelTrace(object):
11*9c5db199SXin Li    """Allows access and control to Kernel tracing facilities.
12*9c5db199SXin Li
13*9c5db199SXin Li    Example code snippet:
14*9c5db199SXin Li        trace = KernelTrace(events=['mali_dvfs:mali_dvfs_set_clock'])
15*9c5db199SXin Li        results = trace.read(regexp=r'frequency=(\d+)')
16*9c5db199SXin Li
17*9c5db199SXin Li    Public methods:
18*9c5db199SXin Li        on          : Enables tracing
19*9c5db199SXin Li        off         : Disables tracing
20*9c5db199SXin Li        is_tracing  : Returns Boolean of tracing status.
21*9c5db199SXin Li        event_on    : Turns event on.  Returns boolean of success
22*9c5db199SXin Li        event_off   : Turns event off.  Returns boolean of success
23*9c5db199SXin Li        flush       : Flushes trace buffer
24*9c5db199SXin Li        read        : Reads trace buffer returns list of
25*9c5db199SXin Li                      - tuples if regexp provided
26*9c5db199SXin Li                      - else matching string
27*9c5db199SXin Li        uptime_secs : Returns float of current uptime.
28*9c5db199SXin Li
29*9c5db199SXin Li    Private functions:
30*9c5db199SXin Li        _onoff       : Disable/enable tracing
31*9c5db199SXin Li        _onoff_event : Disable/enable events
32*9c5db199SXin Li
33*9c5db199SXin Li    Private attributes:
34*9c5db199SXin Li        _buffer      : list to hold parsed results from trace buffer
35*9c5db199SXin Li        _buffer_ptr  : integer pointing to last byte read
36*9c5db199SXin Li
37*9c5db199SXin Li    TODO(tbroch):  List of potential enhancements
38*9c5db199SXin Li       - currently only supports trace events.  Add other tracers.
39*9c5db199SXin Li    """
40*9c5db199SXin Li    _TRACE_ROOT = '/sys/kernel/debug/tracing'
41*9c5db199SXin Li    _TRACE_ON_PATH = os.path.join(_TRACE_ROOT, 'tracing_on')
42*9c5db199SXin Li
43*9c5db199SXin Li    def __init__(self, flush=True, events=None, on=True):
44*9c5db199SXin Li        """Constructor for KernelTrace class"""
45*9c5db199SXin Li        self._buffer = []
46*9c5db199SXin Li        self._buffer_ptr = 0
47*9c5db199SXin Li        self._events = []
48*9c5db199SXin Li        self._on = on
49*9c5db199SXin Li
50*9c5db199SXin Li        if flush:
51*9c5db199SXin Li            self.flush()
52*9c5db199SXin Li        for event in events:
53*9c5db199SXin Li            if self.event_on(event):
54*9c5db199SXin Li                self._events.append(event)
55*9c5db199SXin Li        if on:
56*9c5db199SXin Li            self.on()
57*9c5db199SXin Li
58*9c5db199SXin Li
59*9c5db199SXin Li    def __del__(self, flush=True, events=None, on=True):
60*9c5db199SXin Li        """Deconstructor for KernelTrace class"""
61*9c5db199SXin Li        for event in self._events:
62*9c5db199SXin Li            self.event_off(event)
63*9c5db199SXin Li        if self._on:
64*9c5db199SXin Li            self.off()
65*9c5db199SXin Li
66*9c5db199SXin Li
67*9c5db199SXin Li    def _onoff(self, val):
68*9c5db199SXin Li        """Turn tracing on or off.
69*9c5db199SXin Li
70*9c5db199SXin Li        Arguments:
71*9c5db199SXin Li            val: integer, 1 for on, 0 for off
72*9c5db199SXin Li
73*9c5db199SXin Li        Raises:
74*9c5db199SXin Li            error.TestFail: If unable to turn tracing on or off.
75*9c5db199SXin Li        """
76*9c5db199SXin Li        utils.write_one_line(self._TRACE_ON_PATH, val)
77*9c5db199SXin Li        result = int(utils.read_one_line(self._TRACE_ON_PATH).strip())
78*9c5db199SXin Li        if not result == val:
79*9c5db199SXin Li            raise error.TestFail("Unable to %sable tracing" %
80*9c5db199SXin Li                                 'en' if val == 1 else 'dis')
81*9c5db199SXin Li
82*9c5db199SXin Li
83*9c5db199SXin Li    def on(self):
84*9c5db199SXin Li        """Enable tracing."""
85*9c5db199SXin Li        return self._onoff(1)
86*9c5db199SXin Li
87*9c5db199SXin Li
88*9c5db199SXin Li    def off(self):
89*9c5db199SXin Li        """Disable tracing."""
90*9c5db199SXin Li        self._onoff(0)
91*9c5db199SXin Li
92*9c5db199SXin Li
93*9c5db199SXin Li    def is_tracing(self):
94*9c5db199SXin Li        """Is tracing on?
95*9c5db199SXin Li
96*9c5db199SXin Li        Returns:
97*9c5db199SXin Li            True if tracing enabled and at least one event is enabled.
98*9c5db199SXin Li        """
99*9c5db199SXin Li        result = int(utils.read_one_line(self._TRACE_ON_PATH).strip())
100*9c5db199SXin Li        if result == 1 and len(self._events) > 0:
101*9c5db199SXin Li            return True
102*9c5db199SXin Li        return False
103*9c5db199SXin Li
104*9c5db199SXin Li
105*9c5db199SXin Li    def _event_onoff(self, event, val):
106*9c5db199SXin Li        """Enable/Disable tracing event.
107*9c5db199SXin Li
108*9c5db199SXin Li        TODO(tbroch) Consider allowing wild card enabling of trace events via
109*9c5db199SXin Li            /sys/kernel/debug/tracing/set_event although it makes filling buffer
110*9c5db199SXin Li            really easy
111*9c5db199SXin Li
112*9c5db199SXin Li        Arguments:
113*9c5db199SXin Li            event: list of events.
114*9c5db199SXin Li                   See kernel(Documentation/trace/events.txt) for formatting.
115*9c5db199SXin Li            val: integer, 1 for on, 0 for off
116*9c5db199SXin Li
117*9c5db199SXin Li         Returns:
118*9c5db199SXin Li            True if success, false otherwise
119*9c5db199SXin Li        """
120*9c5db199SXin Li        logging.debug("event_onoff: event:%s val:%d", event, val)
121*9c5db199SXin Li        event_path = event.replace(':', '/')
122*9c5db199SXin Li        fname = os.path.join(self._TRACE_ROOT, 'events', event_path, 'enable')
123*9c5db199SXin Li
124*9c5db199SXin Li        if not os.path.exists(fname):
125*9c5db199SXin Li            logging.warning("Unable to locate tracing event %s", fname)
126*9c5db199SXin Li            return False
127*9c5db199SXin Li        utils.write_one_line(fname, val)
128*9c5db199SXin Li
129*9c5db199SXin Li        fname = os.path.join(self._TRACE_ROOT, "set_event")
130*9c5db199SXin Li        found = False
131*9c5db199SXin Li        with open(fname) as fd:
132*9c5db199SXin Li            for ln in fd.readlines():
133*9c5db199SXin Li                logging.debug("set_event ln:%s", ln)
134*9c5db199SXin Li                if re.findall(event, ln):
135*9c5db199SXin Li                    found = True
136*9c5db199SXin Li                    break
137*9c5db199SXin Li
138*9c5db199SXin Li        if val == 1 and not found:
139*9c5db199SXin Li            logging.warning("Event %s not enabled", event)
140*9c5db199SXin Li            return False
141*9c5db199SXin Li
142*9c5db199SXin Li        if val == 0 and found:
143*9c5db199SXin Li            logging.warning("Event %s not disabled", event)
144*9c5db199SXin Li            return False
145*9c5db199SXin Li
146*9c5db199SXin Li        return True
147*9c5db199SXin Li
148*9c5db199SXin Li
149*9c5db199SXin Li    def event_on(self, event):
150*9c5db199SXin Li        return self._event_onoff(event, 1)
151*9c5db199SXin Li
152*9c5db199SXin Li
153*9c5db199SXin Li    def event_off(self, event):
154*9c5db199SXin Li        return self._event_onoff(event, 0)
155*9c5db199SXin Li
156*9c5db199SXin Li
157*9c5db199SXin Li    def flush(self):
158*9c5db199SXin Li        """Flush trace buffer.
159*9c5db199SXin Li
160*9c5db199SXin Li        Raises:
161*9c5db199SXin Li            error.TestFail: If unable to flush
162*9c5db199SXin Li        """
163*9c5db199SXin Li        self.off()
164*9c5db199SXin Li        fname = os.path.join(self._TRACE_ROOT, 'free_buffer')
165*9c5db199SXin Li        utils.write_one_line(fname, 1)
166*9c5db199SXin Li        self._buffer_ptr = 0
167*9c5db199SXin Li
168*9c5db199SXin Li        fname = os.path.join(self._TRACE_ROOT, 'buffer_size_kb')
169*9c5db199SXin Li        result = utils.read_one_line(fname).strip()
170*9c5db199SXin Li        if result == '0':
171*9c5db199SXin Li            return True
172*9c5db199SXin Li        return False
173*9c5db199SXin Li
174*9c5db199SXin Li
175*9c5db199SXin Li    def read(self, regexp=None):
176*9c5db199SXin Li        fname = os.path.join(self._TRACE_ROOT, 'trace')
177*9c5db199SXin Li        fd = open(fname)
178*9c5db199SXin Li        fd.seek(self._buffer_ptr)
179*9c5db199SXin Li        for ln in fd.readlines():
180*9c5db199SXin Li            if regexp is None:
181*9c5db199SXin Li                self._buffer.append(ln)
182*9c5db199SXin Li                continue
183*9c5db199SXin Li            results = re.findall(regexp, ln)
184*9c5db199SXin Li            if results:
185*9c5db199SXin Li                logging.debug(ln)
186*9c5db199SXin Li                self._buffer.append(results[0])
187*9c5db199SXin Li        self._buffer_ptr = fd.tell()
188*9c5db199SXin Li        fd.close()
189*9c5db199SXin Li        return self._buffer
190*9c5db199SXin Li
191*9c5db199SXin Li
192*9c5db199SXin Li    @staticmethod
193*9c5db199SXin Li    def uptime_secs():
194*9c5db199SXin Li        results = utils.read_one_line("/proc/uptime")
195*9c5db199SXin Li        return float(results.split()[0])
196