1 # 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 5 import logging, os, re, utils 6 7 from autotest_lib.client.bin import utils 8 from autotest_lib.client.common_lib import error 9 10 class 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 20 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 23 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' 41 _TRACE_EN_PATH = os.path.join(_TRACE_ROOT, 'tracing_enabled') 42 43 def __init__(self, flush=True, events=None, on=True): 44 """Constructor for KernelTrace class""" 45 self._buffer = [] 46 self._buffer_ptr = 0 47 self._events = [] 48 self._on = on 49 50 if flush: 51 self.flush() 52 for event in events: 53 if self.event_on(event): 54 self._events.append(event) 55 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): 68 """Enable/Disable tracing. 69 70 Arguments: 71 val: integer, 1 for on, 0 for off 72 73 Raises: 74 error.TestFail: If unable to enable/disable tracing 75 boolean of tracing on/off status 76 """ 77 utils.write_one_line(self._TRACE_EN_PATH, val) 78 fname = os.path.join(self._TRACE_ROOT, 'tracing_on') 79 result = int(utils.read_one_line(fname).strip()) 80 if not result == val: 81 raise error.TestFail("Unable to %sable tracing" % 82 'en' if val == 1 else 'dis') 83 84 85 def on(self): 86 """Enable tracing.""" 87 return self._onoff(1) 88 89 90 def off(self): 91 """Disable tracing.""" 92 self._onoff(0) 93 94 95 def is_tracing(self): 96 """Is tracing on? 97 98 Returns: 99 True if tracing enabled and at least one event is enabled. 100 """ 101 fname = os.path.join(self._TRACE_ROOT, 'tracing_on') 102 result = int(utils.read_one_line(fname).strip()) 103 if result == 1 and len(self._events) > 0: 104 return True 105 return False 106 107 108 def _event_onoff(self, event, val): 109 """Enable/Disable tracing event. 110 111 TODO(tbroch) Consider allowing wild card enabling of trace events via 112 /sys/kernel/debug/tracing/set_event although it makes filling buffer 113 really easy 114 115 Arguments: 116 event: list of events. 117 See kernel(Documentation/trace/events.txt) for formatting. 118 val: integer, 1 for on, 0 for off 119 120 Returns: 121 True if success, false otherwise 122 """ 123 logging.debug("event_onoff: event:%s val:%d", event, val) 124 event_path = event.replace(':', '/') 125 fname = os.path.join(self._TRACE_ROOT, 'events', event_path, 'enable') 126 127 if not os.path.exists(fname): 128 logging.warning("Unable to locate tracing event %s", fname) 129 return False 130 utils.write_one_line(fname, val) 131 132 fname = os.path.join(self._TRACE_ROOT, "set_event") 133 found = False 134 with open(fname) as fd: 135 for ln in fd.readlines(): 136 logging.debug("set_event ln:%s", ln) 137 if re.findall(event, ln): 138 found = True 139 break 140 141 if val == 1 and not found: 142 logging.warning("Event %s not enabled", event) 143 return False 144 145 if val == 0 and found: 146 logging.warning("Event %s not disabled", event) 147 return False 148 149 return True 150 151 152 def event_on(self, event): 153 return self._event_onoff(event, 1) 154 155 156 def event_off(self, event): 157 return self._event_onoff(event, 0) 158 159 160 def flush(self): 161 """Flush trace buffer. 162 163 Raises: 164 error.TestFail: If unable to flush 165 """ 166 self.off() 167 fname = os.path.join(self._TRACE_ROOT, 'free_buffer') 168 utils.write_one_line(fname, 1) 169 self._buffer_ptr = 0 170 171 fname = os.path.join(self._TRACE_ROOT, 'buffer_size_kb') 172 result = utils.read_one_line(fname).strip() 173 if result is '0': 174 return True 175 return False 176 177 178 def read(self, regexp=None): 179 fname = os.path.join(self._TRACE_ROOT, 'trace') 180 fd = open(fname) 181 fd.seek(self._buffer_ptr) 182 for ln in fd.readlines(): 183 if regexp is None: 184 self._buffer.append(ln) 185 continue 186 results = re.findall(regexp, ln) 187 if results: 188 logging.debug(ln) 189 self._buffer.append(results[0]) 190 self._buffer_ptr = fd.tell() 191 fd.close() 192 return self._buffer 193 194 195 @staticmethod 196 def uptime_secs(): 197 results = utils.read_one_line("/proc/uptime") 198 return float(results.split()[0]) 199