| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 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 | |
| 5 import os | |
| 6 import sys | |
| 7 import time | |
| 8 | |
| 9 from systrace import systrace_agent | |
| 10 | |
| 11 | |
| 12 class FtraceAgentIo(object): | |
| 13 | |
| 14 @staticmethod | |
| 15 def writeFile(path, data): | |
| 16 with open(path, 'w') as f: | |
| 17 f.write(data) | |
| 18 | |
| 19 @staticmethod | |
| 20 def readFile(path): | |
| 21 with open(path, 'r') as f: | |
| 22 return f.read() | |
| 23 | |
| 24 @staticmethod | |
| 25 def haveWritePermissions(path): | |
| 26 return os.access(path, os.W_OK) | |
| 27 | |
| 28 FT_DIR = "/sys/kernel/debug/tracing/" | |
| 29 FT_CLOCK = FT_DIR + "trace_clock" | |
| 30 FT_BUFFER_SIZE = FT_DIR + "buffer_size_kb" | |
| 31 FT_TRACER = FT_DIR + "current_tracer" | |
| 32 FT_PRINT_TGID = FT_DIR + "options/print-tgid" | |
| 33 FT_TRACE_ON = FT_DIR + "tracing_on" | |
| 34 FT_TRACE = FT_DIR + "trace" | |
| 35 FT_TRACE_MARKER = FT_DIR + "trace_marker" | |
| 36 FT_OVERWRITE = FT_DIR + "options/overwrite" | |
| 37 | |
| 38 all_categories = { | |
| 39 "sched": { | |
| 40 "desc": "CPU Scheduling", | |
| 41 "req": ["sched/sched_switch/", "sched/sched_wakeup/"] | |
| 42 }, | |
| 43 "freq": { | |
| 44 "desc": "CPU Frequency", | |
| 45 "req": ["power/cpu_frequency/", "power/clock_set_rate/"] | |
| 46 }, | |
| 47 "irq": { | |
| 48 "desc": "CPU IRQS and IPIS", | |
| 49 "req": ["irq/"], | |
| 50 "opt": ["ipi/"] | |
| 51 }, | |
| 52 "workq": { | |
| 53 "desc": "Kernel workqueues", | |
| 54 "req": ["workqueue/"] | |
| 55 }, | |
| 56 "memreclaim": { | |
| 57 "desc": "Kernel Memory Reclaim", | |
| 58 "req": ["vmscan/mm_vmscan_direct_reclaim_begin/", | |
| 59 "vmscan/mm_vmscan_direct_reclaim_end/", | |
| 60 "vmscan/mm_vmscan_kswapd_wake/", | |
| 61 "vmscan/mm_vmscan_kswapd_sleep/"] | |
| 62 }, | |
| 63 "idle": { | |
| 64 "desc": "CPU Idle", | |
| 65 "req": ["power/cpu_idle/"] | |
| 66 }, | |
| 67 "regulators": { | |
| 68 "desc": "Voltage and Current Regulators", | |
| 69 "req": ["regulator/"] | |
| 70 }, | |
| 71 "disk": { | |
| 72 "desc": "Disk I/O", | |
| 73 "req": ["block/block_rq_issue/", | |
| 74 "block/block_rq_complete/"], | |
| 75 "opt": ["f2fs/f2fs_sync_file_enter/", | |
| 76 "f2fs/f2fs_sync_file_exit/", | |
| 77 "f2fs/f2fs_write_begin/", | |
| 78 "f2fs/f2fs_write_end/", | |
| 79 "ext4/ext4_da_write_begin/", | |
| 80 "ext4/ext4_da_write_end/", | |
| 81 "ext4/ext4_sync_file_enter/", | |
| 82 "ext4/ext4_sync_file_exit/"] | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 | |
| 87 def try_create_agent(options, categories): | |
| 88 if options.target != 'linux': | |
| 89 return False | |
| 90 return FtraceAgent(options, categories, FtraceAgentIo) | |
| 91 | |
| 92 | |
| 93 class FtraceAgent(systrace_agent.SystraceAgent): | |
| 94 | |
| 95 def __init__(self, options, categories, fio=FtraceAgentIo): | |
| 96 """Initialize a systrace agent. | |
| 97 | |
| 98 Args: | |
| 99 options: The command-line options. | |
| 100 categories: The trace categories to capture. | |
| 101 """ | |
| 102 super(FtraceAgent, self).__init__(options, categories) | |
| 103 if not self._categories: | |
| 104 self._categories = ["sched"] | |
| 105 self._fio = fio | |
| 106 self._categories = [x for x in self._categories | |
| 107 if self._is_category_available(x)] | |
| 108 self._expect_trace = False | |
| 109 | |
| 110 def _get_trace_buffer_size(self): | |
| 111 buffer_size = 4096 | |
| 112 if ((self._options.trace_buf_size is not None) | |
| 113 and (self._options.trace_buf_size > 0)): | |
| 114 buffer_size = self._options.trace_buf_size | |
| 115 return buffer_size | |
| 116 | |
| 117 def _get_trace_time(self): | |
| 118 wait_time = 5 | |
| 119 if ((self._options.trace_time is not None) | |
| 120 and (self._options.trace_time > 0)): | |
| 121 wait_time = self._options.trace_time | |
| 122 return wait_time | |
| 123 | |
| 124 def start(self): | |
| 125 """Start tracing. | |
| 126 """ | |
| 127 if self._options.list_categories or len(self._categories) == 0: | |
| 128 self._expect_trace = False | |
| 129 else: | |
| 130 self._expect_trace = True | |
| 131 | |
| 132 self._fio.writeFile(FT_BUFFER_SIZE, str(self._get_trace_buffer_size())) | |
| 133 | |
| 134 self._fio.writeFile(FT_CLOCK, 'global') | |
| 135 self._fio.writeFile(FT_TRACER, 'nop') | |
| 136 self._fio.writeFile(FT_OVERWRITE, "0") | |
| 137 | |
| 138 # TODO: riandrews to push necessary patches for TGID option to upstream | |
| 139 # linux kernel | |
| 140 # self._fio.writeFile(FT_PRINT_TGID, '1') | |
| 141 | |
| 142 for category in self._categories: | |
| 143 self._category_enable(category) | |
| 144 | |
| 145 self._fio.writeFile(FT_TRACE, '') | |
| 146 | |
| 147 print "starting tracing." | |
| 148 sys.stdout.flush() | |
| 149 | |
| 150 self._fio.writeFile(FT_TRACE_ON, '1') | |
| 151 | |
| 152 def collect_result(self): | |
| 153 """Collect the result of tracing. | |
| 154 | |
| 155 This function will block while collecting the result. For sync mode, it | |
| 156 reads the data, e.g., from stdout, until it finishes. For async mode, it | |
| 157 blocks until the agent is stopped and the data is ready. | |
| 158 """ | |
| 159 if self._options.list_categories or len(self._categories) == 0: | |
| 160 self._print_avail_categories() | |
| 161 else: | |
| 162 try: | |
| 163 time.sleep(self._get_trace_time()) | |
| 164 except KeyboardInterrupt: | |
| 165 pass | |
| 166 self._fio.writeFile(FT_TRACE_ON, '0') | |
| 167 for category in self._categories: | |
| 168 self._category_disable(category) | |
| 169 if self._options.fix_threads: | |
| 170 print "WARN: thread name fixing is not yet supported." | |
| 171 if self._options.fix_tgids: | |
| 172 print "WARN: tgid fixing is not yet supported." | |
| 173 if self._options.fix_circular: | |
| 174 print "WARN: circular buffer fixups are not yet supported." | |
| 175 | |
| 176 def expect_trace(self): | |
| 177 """Check if the agent is returning a trace or not. | |
| 178 | |
| 179 This will be determined in collect_result(). | |
| 180 Returns: | |
| 181 Whether the agent is expecting a trace or not. | |
| 182 """ | |
| 183 return self._expect_trace | |
| 184 | |
| 185 def get_trace_data(self): | |
| 186 """Get the trace data. | |
| 187 | |
| 188 Returns: | |
| 189 The trace data. | |
| 190 """ | |
| 191 d = self._fio.readFile(FT_TRACE) | |
| 192 self._fio.writeFile(FT_BUFFER_SIZE, "1") | |
| 193 return d | |
| 194 | |
| 195 def get_class_name(self): | |
| 196 return 'trace-data' | |
| 197 | |
| 198 def _is_category_available(self, category): | |
| 199 if category not in all_categories: | |
| 200 return False | |
| 201 events_dir = FT_DIR + "events/" | |
| 202 req_events = all_categories[category]["req"] | |
| 203 for event in req_events: | |
| 204 event_full_path = events_dir + event + "enable" | |
| 205 if not self._fio.haveWritePermissions(event_full_path): | |
| 206 return False | |
| 207 return True | |
| 208 | |
| 209 def _avail_categories(self): | |
| 210 ret = [] | |
| 211 for event in all_categories: | |
| 212 if self._is_category_available(event): | |
| 213 ret.append(event) | |
| 214 return ret | |
| 215 | |
| 216 def _print_avail_categories(self): | |
| 217 avail = self._avail_categories() | |
| 218 if len(avail): | |
| 219 print "tracing options:" | |
| 220 for category in self._avail_categories(): | |
| 221 desc = all_categories[category]["desc"] | |
| 222 print "{0: <16}".format(category), ": ", desc | |
| 223 else: | |
| 224 print "No tracing categories available - perhaps you need root?" | |
| 225 | |
| 226 def _category_enable_paths(self, category): | |
| 227 events_dir = FT_DIR + "events/" | |
| 228 req_events = all_categories[category]["req"] | |
| 229 for event in req_events: | |
| 230 event_full_path = events_dir + event + "enable" | |
| 231 yield event_full_path | |
| 232 if "opt" in all_categories[category]: | |
| 233 opt_events = all_categories[category]["opt"] | |
| 234 for event in opt_events: | |
| 235 event_full_path = events_dir + event + "enable" | |
| 236 if self._fio.haveWritePermissions(event_full_path): | |
| 237 yield event_full_path | |
| 238 | |
| 239 def _category_enable(self, category): | |
| 240 for path in self._category_enable_paths(category): | |
| 241 self._fio.writeFile(path, "1") | |
| 242 | |
| 243 def _category_disable(self, category): | |
| 244 for path in self._category_enable_paths(category): | |
| 245 self._fio.writeFile(path, "0") | |
| OLD | NEW |