blob: 264cffc5aa3f30871b3b26249b27a75411b6b953 [file] [log] [blame]
Todd Broch407e8292013-09-05 17:58:33 -07001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Allen Li3e694472017-02-07 13:21:54 -08005import logging, os, re
Todd Broch407e8292013-09-05 17:58:33 -07006
7from autotest_lib.client.bin import utils
8from autotest_lib.client.common_lib import error
9
10class KernelTrace(object):
11 """Allows access and control to Kernel tracing facilities.
12
13 Example code snippet:
14 trace = KernelTrace(events=['mali_dvfs:mali_dvfs_set_clock'])
15 results = trace.read(regexp=r'frequency=(\d+)')
16
17 Public methods:
18 on : Enables tracing
19 off : Disables tracing
Todd Broche4deb722013-09-10 11:37:57 -070020 is_tracing : Returns Boolean of tracing status.
21 event_on : Turns event on. Returns boolean of success
22 event_off : Turns event off. Returns boolean of success
Todd Broch407e8292013-09-05 17:58:33 -070023 flush : Flushes trace buffer
24 read : Reads trace buffer returns list of
25 - tuples if regexp provided
26 - else matching string
27 uptime_secs : Returns float of current uptime.
28
29 Private functions:
30 _onoff : Disable/enable tracing
31 _onoff_event : Disable/enable events
32
33 Private attributes:
34 _buffer : list to hold parsed results from trace buffer
35 _buffer_ptr : integer pointing to last byte read
36
37 TODO(tbroch): List of potential enhancements
38 - currently only supports trace events. Add other tracers.
39 """
40 _TRACE_ROOT = '/sys/kernel/debug/tracing'
Todd Brochf98882c2019-04-16 21:47:33 -070041 _TRACE_ON_PATH = os.path.join(_TRACE_ROOT, 'tracing_on')
Todd Broch407e8292013-09-05 17:58:33 -070042
43 def __init__(self, flush=True, events=None, on=True):
44 """Constructor for KernelTrace class"""
45 self._buffer = []
46 self._buffer_ptr = 0
Todd Broche4deb722013-09-10 11:37:57 -070047 self._events = []
Todd Broch407e8292013-09-05 17:58:33 -070048 self._on = on
49
50 if flush:
51 self.flush()
52 for event in events:
Todd Broche4deb722013-09-10 11:37:57 -070053 if self.event_on(event):
54 self._events.append(event)
Todd Broch407e8292013-09-05 17:58:33 -070055 if on:
56 self.on()
57
58
59 def __del__(self, flush=True, events=None, on=True):
60 """Deconstructor for KernelTrace class"""
61 for event in self._events:
62 self.event_off(event)
63 if self._on:
64 self.off()
65
66
67 def _onoff(self, val):
Todd Brochf98882c2019-04-16 21:47:33 -070068 """Turn tracing on or off.
Todd Broch407e8292013-09-05 17:58:33 -070069
70 Arguments:
71 val: integer, 1 for on, 0 for off
72
73 Raises:
Todd Brochf98882c2019-04-16 21:47:33 -070074 error.TestFail: If unable to turn tracing on or off.
Todd Broch407e8292013-09-05 17:58:33 -070075 """
Todd Brochf98882c2019-04-16 21:47:33 -070076 utils.write_one_line(self._TRACE_ON_PATH, val)
77 result = int(utils.read_one_line(self._TRACE_ON_PATH).strip())
Todd Broch407e8292013-09-05 17:58:33 -070078 if not result == val:
79 raise error.TestFail("Unable to %sable tracing" %
80 'en' if val == 1 else 'dis')
81
82
83 def on(self):
84 """Enable tracing."""
85 return self._onoff(1)
86
87
88 def off(self):
89 """Disable tracing."""
90 self._onoff(0)
91
92
Todd Broche4deb722013-09-10 11:37:57 -070093 def is_tracing(self):
94 """Is tracing on?
95
96 Returns:
97 True if tracing enabled and at least one event is enabled.
98 """
Todd Brochf98882c2019-04-16 21:47:33 -070099 result = int(utils.read_one_line(self._TRACE_ON_PATH).strip())
Todd Broche4deb722013-09-10 11:37:57 -0700100 if result == 1 and len(self._events) > 0:
101 return True
102 return False
103
104
Todd Broch407e8292013-09-05 17:58:33 -0700105 def _event_onoff(self, event, val):
106 """Enable/Disable tracing event.
107
108 TODO(tbroch) Consider allowing wild card enabling of trace events via
109 /sys/kernel/debug/tracing/set_event although it makes filling buffer
110 really easy
111
112 Arguments:
113 event: list of events.
114 See kernel(Documentation/trace/events.txt) for formatting.
115 val: integer, 1 for on, 0 for off
116
Todd Broche4deb722013-09-10 11:37:57 -0700117 Returns:
118 True if success, false otherwise
Todd Broch407e8292013-09-05 17:58:33 -0700119 """
120 logging.debug("event_onoff: event:%s val:%d", event, val)
121 event_path = event.replace(':', '/')
122 fname = os.path.join(self._TRACE_ROOT, 'events', event_path, 'enable')
123
124 if not os.path.exists(fname):
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700125 logging.warning("Unable to locate tracing event %s", fname)
Todd Broche4deb722013-09-10 11:37:57 -0700126 return False
Todd Broch407e8292013-09-05 17:58:33 -0700127 utils.write_one_line(fname, val)
128
129 fname = os.path.join(self._TRACE_ROOT, "set_event")
130 found = False
131 with open(fname) as fd:
132 for ln in fd.readlines():
133 logging.debug("set_event ln:%s", ln)
134 if re.findall(event, ln):
135 found = True
136 break
137
138 if val == 1 and not found:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700139 logging.warning("Event %s not enabled", event)
Todd Broche4deb722013-09-10 11:37:57 -0700140 return False
141
Todd Broch407e8292013-09-05 17:58:33 -0700142 if val == 0 and found:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700143 logging.warning("Event %s not disabled", event)
Todd Broche4deb722013-09-10 11:37:57 -0700144 return False
145
146 return True
Todd Broch407e8292013-09-05 17:58:33 -0700147
148
149 def event_on(self, event):
Todd Broche4deb722013-09-10 11:37:57 -0700150 return self._event_onoff(event, 1)
Todd Broch407e8292013-09-05 17:58:33 -0700151
152
153 def event_off(self, event):
Todd Broche4deb722013-09-10 11:37:57 -0700154 return self._event_onoff(event, 0)
Todd Broch407e8292013-09-05 17:58:33 -0700155
156
157 def flush(self):
158 """Flush trace buffer.
159
160 Raises:
161 error.TestFail: If unable to flush
162 """
Todd Broch407e8292013-09-05 17:58:33 -0700163 self.off()
164 fname = os.path.join(self._TRACE_ROOT, 'free_buffer')
165 utils.write_one_line(fname, 1)
166 self._buffer_ptr = 0
167
168 fname = os.path.join(self._TRACE_ROOT, 'buffer_size_kb')
169 result = utils.read_one_line(fname).strip()
Derek Beckett8affba02020-10-20 14:24:18 -0700170 if result == '0':
Todd Broch407e8292013-09-05 17:58:33 -0700171 return True
172 return False
173
174
175 def read(self, regexp=None):
176 fname = os.path.join(self._TRACE_ROOT, 'trace')
177 fd = open(fname)
178 fd.seek(self._buffer_ptr)
179 for ln in fd.readlines():
180 if regexp is None:
181 self._buffer.append(ln)
182 continue
183 results = re.findall(regexp, ln)
184 if results:
185 logging.debug(ln)
186 self._buffer.append(results[0])
187 self._buffer_ptr = fd.tell()
188 fd.close()
189 return self._buffer
190
191
192 @staticmethod
193 def uptime_secs():
194 results = utils.read_one_line("/proc/uptime")
195 return float(results.split()[0])