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.
9 from systrace
import trace_result
10 from systrace
import tracing_agents
13 class FtraceAgentIo(object):
15 def writeFile(path
, data
):
16 if FtraceAgentIo
.haveWritePermissions(path
):
17 with
open(path
, 'w') as f
:
20 raise IOError('Cannot write to %s; did you forget sudo/root?' % path
)
24 with
open(path
, 'r') as f
:
28 def haveWritePermissions(path
):
29 return os
.access(path
, os
.W_OK
)
32 FT_DIR
= "/sys/kernel/debug/tracing/"
33 FT_CLOCK
= FT_DIR
+ "trace_clock"
34 FT_BUFFER_SIZE
= FT_DIR
+ "buffer_size_kb"
35 FT_TRACER
= FT_DIR
+ "current_tracer"
36 FT_PRINT_TGID
= FT_DIR
+ "options/print-tgid"
37 FT_TRACE_ON
= FT_DIR
+ "tracing_on"
38 FT_TRACE
= FT_DIR
+ "trace"
39 FT_TRACE_MARKER
= FT_DIR
+ "trace_marker"
40 FT_OVERWRITE
= FT_DIR
+ "options/overwrite"
44 "desc": "CPU Scheduling",
45 "req": ["sched/sched_switch/", "sched/sched_wakeup/"]
48 "desc": "CPU Frequency",
49 "req": ["power/cpu_frequency/", "power/clock_set_rate/"]
52 "desc": "CPU IRQS and IPIS",
57 "desc": "Kernel workqueues",
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/"]
69 "req": ["power/cpu_idle/"]
72 "desc": "Voltage and Current Regulators",
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/"]
91 def try_create_agent(config
):
92 if config
.target
!= 'linux':
94 return FtraceAgent(FtraceAgentIo
)
97 def list_categories(_
):
98 agent
= FtraceAgent(FtraceAgentIo
)
99 agent
._print
_avail
_categories
()
102 class FtraceConfig(tracing_agents
.TracingConfig
):
103 def __init__(self
, ftrace_categories
, target
, trace_buf_size
):
104 tracing_agents
.TracingConfig
.__init
__(self
)
105 self
.ftrace_categories
= ftrace_categories
107 self
.trace_buf_size
= trace_buf_size
110 def add_options(parser
):
111 options
= optparse
.OptionGroup(parser
, 'Ftrace options')
112 options
.add_option('--ftrace-categories', dest
='ftrace_categories',
113 help='Select ftrace categories with a comma-delimited '
114 'list, e.g. --ftrace-categories=cat1,cat2,cat3')
118 def get_config(options
):
119 return FtraceConfig(options
.ftrace_categories
, options
.target
,
120 options
.trace_buf_size
)
123 class FtraceAgent(tracing_agents
.TracingAgent
):
125 def __init__(self
, fio
=FtraceAgentIo
):
126 """Initialize a systrace agent.
129 config: The command-line config.
130 categories: The trace categories to capture.
132 super(FtraceAgent
, self
).__init
__()
135 self
._categories
= None
137 def _get_trace_buffer_size(self
):
139 if ((self
._config
.trace_buf_size
is not None)
140 and (self
._config
.trace_buf_size
> 0)):
141 buffer_size
= self
._config
.trace_buf_size
144 def _fix_categories(self
, categories
):
146 Applies the default category (sched) if there are no categories
147 in the list and removes unavailable categories from the list.
149 categories: List of categories.
152 categories
= ["sched"]
153 return [x
for x
in categories
154 if self
._is
_category
_available
(x
)]
156 @py_utils.Timeout(tracing_agents
.START_STOP_TIMEOUT
)
157 def StartAgentTracing(self
, config
, timeout
=None):
160 self
._config
= config
161 categories
= self
._fix
_categories
(config
.ftrace_categories
)
162 self
._fio
.writeFile(FT_BUFFER_SIZE
,
163 str(self
._get
_trace
_buffer
_size
()))
164 self
._fio
.writeFile(FT_CLOCK
, 'global')
165 self
._fio
.writeFile(FT_TRACER
, 'nop')
166 self
._fio
.writeFile(FT_OVERWRITE
, "0")
168 # TODO: riandrews to push necessary patches for TGID option to upstream
170 # self._fio.writeFile(FT_PRINT_TGID, '1')
172 for category
in categories
:
173 self
._category
_enable
(category
)
175 self
._categories
= categories
# need to store list of categories to disable
176 print 'starting tracing.'
178 self
._fio
.writeFile(FT_TRACE
, '')
179 self
._fio
.writeFile(FT_TRACE_ON
, '1')
182 @py_utils.Timeout(tracing_agents
.START_STOP_TIMEOUT
)
183 def StopAgentTracing(self
, timeout
=None):
184 """Collect the result of tracing.
186 This function will block while collecting the result. For sync mode, it
187 reads the data, e.g., from stdout, until it finishes. For async mode, it
188 blocks until the agent is stopped and the data is ready.
190 self
._fio
.writeFile(FT_TRACE_ON
, '0')
191 for category
in self
._categories
:
192 self
._category
_disable
(category
)
195 @py_utils.Timeout(tracing_agents
.GET_RESULTS_TIMEOUT
)
196 def GetResults(self
, timeout
=None):
198 d
= self
._fio
.readFile(FT_TRACE
)
199 self
._fio
.writeFile(FT_BUFFER_SIZE
, "1")
200 return trace_result
.TraceResult('trace-data', d
)
202 def SupportsExplicitClockSync(self
):
205 def RecordClockSyncMarker(self
, sync_id
, did_record_sync_marker_callback
):
206 # No implementation, but need to have this to support the API
207 # pylint: disable=unused-argument
210 def _is_category_available(self
, category
):
211 if category
not in all_categories
:
213 events_dir
= FT_DIR
+ "events/"
214 req_events
= all_categories
[category
]["req"]
215 for event
in req_events
:
216 event_full_path
= events_dir
+ event
+ "enable"
217 if not self
._fio
.haveWritePermissions(event_full_path
):
221 def _avail_categories(self
):
223 for event
in all_categories
:
224 if self
._is
_category
_available
(event
):
228 def _print_avail_categories(self
):
229 avail
= self
._avail
_categories
()
231 print "tracing config:"
232 for category
in self
._avail
_categories
():
233 desc
= all_categories
[category
]["desc"]
234 print "{0: <16}".format(category
), ": ", desc
236 print "No tracing categories available - perhaps you need root?"
238 def _category_enable_paths(self
, category
):
239 events_dir
= FT_DIR
+ "events/"
240 req_events
= all_categories
[category
]["req"]
241 for event
in req_events
:
242 event_full_path
= events_dir
+ event
+ "enable"
243 yield event_full_path
244 if "opt" in all_categories
[category
]:
245 opt_events
= all_categories
[category
]["opt"]
246 for event
in opt_events
:
247 event_full_path
= events_dir
+ event
+ "enable"
248 if self
._fio
.haveWritePermissions(event_full_path
):
249 yield event_full_path
251 def _category_enable(self
, category
):
252 for path
in self
._category
_enable
_paths
(category
):
253 self
._fio
.writeFile(path
, "1")
255 def _category_disable(self
, category
):
256 for path
in self
._category
_enable
_paths
(category
):
257 self
._fio
.writeFile(path
, "0")