1# Copyright 2015 The Chromium 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 5import optparse 6import os 7import py_utils 8 9from systrace import trace_result 10from systrace import tracing_agents 11 12 13class FtraceAgentIo(object): 14 @staticmethod 15 def writeFile(path, data): 16 if FtraceAgentIo.haveWritePermissions(path): 17 with open(path, 'w') as f: 18 f.write(data) 19 else: 20 raise IOError('Cannot write to %s; did you forget sudo/root?' % path) 21 22 @staticmethod 23 def readFile(path): 24 with open(path, 'r') as f: 25 return f.read() 26 27 @staticmethod 28 def haveWritePermissions(path): 29 return os.access(path, os.W_OK) 30 31 32FT_DIR = "/sys/kernel/debug/tracing/" 33FT_CLOCK = FT_DIR + "trace_clock" 34FT_BUFFER_SIZE = FT_DIR + "buffer_size_kb" 35FT_TRACER = FT_DIR + "current_tracer" 36FT_PRINT_TGID = FT_DIR + "options/print-tgid" 37FT_TRACE_ON = FT_DIR + "tracing_on" 38FT_TRACE = FT_DIR + "trace" 39FT_TRACE_MARKER = FT_DIR + "trace_marker" 40FT_OVERWRITE = FT_DIR + "options/overwrite" 41 42all_categories = { 43 "sched": { 44 "desc": "CPU Scheduling", 45 "req": ["sched/sched_switch/", "sched/sched_wakeup/"] 46 }, 47 "freq": { 48 "desc": "CPU Frequency", 49 "req": ["power/cpu_frequency/", "power/clock_set_rate/"] 50 }, 51 "irq": { 52 "desc": "CPU IRQS and IPIS", 53 "req": ["irq/"], 54 "opt": ["ipi/"] 55 }, 56 "workq": { 57 "desc": "Kernel workqueues", 58 "req": ["workqueue/"] 59 }, 60 "memreclaim": { 61 "desc": "Kernel Memory Reclaim", 62 "req": ["vmscan/mm_vmscan_direct_reclaim_begin/", 63 "vmscan/mm_vmscan_direct_reclaim_end/", 64 "vmscan/mm_vmscan_kswapd_wake/", 65 "vmscan/mm_vmscan_kswapd_sleep/"] 66 }, 67 "idle": { 68 "desc": "CPU Idle", 69 "req": ["power/cpu_idle/"] 70 }, 71 "regulators": { 72 "desc": "Voltage and Current Regulators", 73 "req": ["regulator/"] 74 }, 75 "disk": { 76 "desc": "Disk I/O", 77 "req": ["block/block_rq_issue/", 78 "block/block_rq_complete/"], 79 "opt": ["f2fs/f2fs_sync_file_enter/", 80 "f2fs/f2fs_sync_file_exit/", 81 "f2fs/f2fs_write_begin/", 82 "f2fs/f2fs_write_end/", 83 "ext4/ext4_da_write_begin/", 84 "ext4/ext4_da_write_end/", 85 "ext4/ext4_sync_file_enter/", 86 "ext4/ext4_sync_file_exit/"] 87 } 88} 89 90 91def try_create_agent(config): 92 if config.target != 'linux': 93 return None 94 return FtraceAgent(FtraceAgentIo) 95 96 97def list_categories(_): 98 agent = FtraceAgent(FtraceAgentIo) 99 agent._print_avail_categories() 100 101 102class FtraceConfig(tracing_agents.TracingConfig): 103 def __init__(self, ftrace_categories, target, trace_buf_size, fix_threads, 104 fix_tgids, fix_circular): 105 tracing_agents.TracingConfig.__init__(self) 106 self.ftrace_categories = ftrace_categories 107 self.target = target 108 self.trace_buf_size = trace_buf_size 109 self.fix_threads = fix_threads 110 self.fix_tgids = fix_tgids 111 self.fix_circular = fix_circular 112 113 114def add_options(parser): 115 options = optparse.OptionGroup(parser, 'Ftrace options') 116 options.add_option('--ftrace-categories', dest='ftrace_categories', 117 help='Select ftrace categories with a comma-delimited ' 118 'list, e.g. --ftrace-categories=cat1,cat2,cat3') 119 return options 120 121 122def get_config(options): 123 return FtraceConfig(options.ftrace_categories, options.target, 124 options.trace_buf_size, options.fix_threads, 125 options.fix_tgids, options.fix_circular) 126 127 128class FtraceAgent(tracing_agents.TracingAgent): 129 130 def __init__(self, fio=FtraceAgentIo): 131 """Initialize a systrace agent. 132 133 Args: 134 config: The command-line config. 135 categories: The trace categories to capture. 136 """ 137 super(FtraceAgent, self).__init__() 138 self._fio = fio 139 self._config = None 140 self._categories = None 141 142 def _get_trace_buffer_size(self): 143 buffer_size = 4096 144 if ((self._config.trace_buf_size is not None) 145 and (self._config.trace_buf_size > 0)): 146 buffer_size = self._config.trace_buf_size 147 return buffer_size 148 149 def _fix_categories(self, categories): 150 """ 151 Applies the default category (sched) if there are no categories 152 in the list and removes unavailable categories from the list. 153 Args: 154 categories: List of categories. 155 """ 156 if not categories: 157 categories = ["sched"] 158 return [x for x in categories 159 if self._is_category_available(x)] 160 161 @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) 162 def StartAgentTracing(self, config, timeout=None): 163 """Start tracing. 164 """ 165 self._config = config 166 categories = self._fix_categories(config.ftrace_categories) 167 self._fio.writeFile(FT_BUFFER_SIZE, 168 str(self._get_trace_buffer_size())) 169 self._fio.writeFile(FT_CLOCK, 'global') 170 self._fio.writeFile(FT_TRACER, 'nop') 171 self._fio.writeFile(FT_OVERWRITE, "0") 172 173 # TODO: riandrews to push necessary patches for TGID option to upstream 174 # linux kernel 175 # self._fio.writeFile(FT_PRINT_TGID, '1') 176 177 for category in categories: 178 self._category_enable(category) 179 180 self._categories = categories # need to store list of categories to disable 181 print 'starting tracing.' 182 183 self._fio.writeFile(FT_TRACE, '') 184 self._fio.writeFile(FT_TRACE_ON, '1') 185 return True 186 187 @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) 188 def StopAgentTracing(self, timeout=None): 189 """Collect the result of tracing. 190 191 This function will block while collecting the result. For sync mode, it 192 reads the data, e.g., from stdout, until it finishes. For async mode, it 193 blocks until the agent is stopped and the data is ready. 194 """ 195 self._fio.writeFile(FT_TRACE_ON, '0') 196 for category in self._categories: 197 self._category_disable(category) 198 if self._config.fix_threads: 199 print "WARN: thread name fixing is not yet supported." 200 if self._config.fix_tgids: 201 print "WARN: tgid fixing is not yet supported." 202 if self._config.fix_circular: 203 print "WARN: circular buffer fixups are not yet supported." 204 return True 205 206 @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT) 207 def GetResults(self, timeout=None): 208 # get the output 209 d = self._fio.readFile(FT_TRACE) 210 self._fio.writeFile(FT_BUFFER_SIZE, "1") 211 return trace_result.TraceResult('trace-data', d) 212 213 def SupportsExplicitClockSync(self): 214 return False 215 216 def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback): 217 # No implementation, but need to have this to support the API 218 # pylint: disable=unused-argument 219 return False 220 221 def _is_category_available(self, category): 222 if category not in all_categories: 223 return False 224 events_dir = FT_DIR + "events/" 225 req_events = all_categories[category]["req"] 226 for event in req_events: 227 event_full_path = events_dir + event + "enable" 228 if not self._fio.haveWritePermissions(event_full_path): 229 return False 230 return True 231 232 def _avail_categories(self): 233 ret = [] 234 for event in all_categories: 235 if self._is_category_available(event): 236 ret.append(event) 237 return ret 238 239 def _print_avail_categories(self): 240 avail = self._avail_categories() 241 if len(avail): 242 print "tracing config:" 243 for category in self._avail_categories(): 244 desc = all_categories[category]["desc"] 245 print "{0: <16}".format(category), ": ", desc 246 else: 247 print "No tracing categories available - perhaps you need root?" 248 249 def _category_enable_paths(self, category): 250 events_dir = FT_DIR + "events/" 251 req_events = all_categories[category]["req"] 252 for event in req_events: 253 event_full_path = events_dir + event + "enable" 254 yield event_full_path 255 if "opt" in all_categories[category]: 256 opt_events = all_categories[category]["opt"] 257 for event in opt_events: 258 event_full_path = events_dir + event + "enable" 259 if self._fio.haveWritePermissions(event_full_path): 260 yield event_full_path 261 262 def _category_enable(self, category): 263 for path in self._category_enable_paths(category): 264 self._fio.writeFile(path, "1") 265 266 def _category_disable(self, category): 267 for path in self._category_enable_paths(category): 268 self._fio.writeFile(path, "0") 269