• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/"],
50          "opt": ["power/clock_set_rate/", "clk/clk_set_rate/"]
51    },
52    "irq": {
53          "desc": "CPU IRQS and IPIS",
54          "req": ["irq/"],
55          "opt": ["ipi/"]
56    },
57    "workq": {
58          "desc": "Kernel workqueues",
59          "req": ["workqueue/"]
60    },
61    "memreclaim": {
62          "desc": "Kernel Memory Reclaim",
63          "req": ["vmscan/mm_vmscan_direct_reclaim_begin/",
64                  "vmscan/mm_vmscan_direct_reclaim_end/",
65                  "vmscan/mm_vmscan_kswapd_wake/",
66                  "vmscan/mm_vmscan_kswapd_sleep/"]
67    },
68    "idle": {
69          "desc": "CPU Idle",
70          "req": ["power/cpu_idle/"]
71    },
72    "regulators": {
73          "desc": "Voltage and Current Regulators",
74          "req": ["regulator/"]
75    },
76    "disk": {
77          "desc": "Disk I/O",
78          "req": ["block/block_rq_issue/",
79                  "block/block_rq_complete/"],
80          "opt": ["f2fs/f2fs_sync_file_enter/",
81                  "f2fs/f2fs_sync_file_exit/",
82                  "f2fs/f2fs_write_begin/",
83                  "f2fs/f2fs_write_end/",
84                  "ext4/ext4_da_write_begin/",
85                  "ext4/ext4_da_write_end/",
86                  "ext4/ext4_sync_file_enter/",
87                  "ext4/ext4_sync_file_exit/"]
88    }
89}
90
91
92def try_create_agent(config):
93  if config.target != 'linux':
94    return None
95  return FtraceAgent(FtraceAgentIo)
96
97
98def list_categories(_):
99  agent = FtraceAgent(FtraceAgentIo)
100  agent._print_avail_categories()
101
102
103class FtraceConfig(tracing_agents.TracingConfig):
104  def __init__(self, ftrace_categories, target, trace_buf_size):
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
110
111def add_options(parser):
112  options = optparse.OptionGroup(parser, 'Ftrace options')
113  options.add_option('--ftrace-categories', dest='ftrace_categories',
114                     help='Select ftrace categories with a comma-delimited '
115                     'list, e.g. --ftrace-categories=cat1,cat2,cat3')
116  return options
117
118
119def get_config(options):
120  return FtraceConfig(options.ftrace_categories, options.target,
121                      options.trace_buf_size)
122
123
124class FtraceAgent(tracing_agents.TracingAgent):
125
126  def __init__(self, fio=FtraceAgentIo):
127    """Initialize a systrace agent.
128
129    Args:
130      config: The command-line config.
131      categories: The trace categories to capture.
132    """
133    super(FtraceAgent, self).__init__()
134    self._fio = fio
135    self._config = None
136    self._categories = None
137
138  def _get_trace_buffer_size(self):
139    buffer_size = 4096
140    if ((self._config.trace_buf_size is not None)
141        and (self._config.trace_buf_size > 0)):
142      buffer_size = self._config.trace_buf_size
143    return buffer_size
144
145  def _fix_categories(self, categories):
146    """
147    Applies the default category (sched) if there are no categories
148    in the list and removes unavailable categories from the list.
149    Args:
150        categories: List of categories.
151    """
152    if not categories:
153      categories = ["sched"]
154    return [x for x in categories
155            if self._is_category_available(x)]
156
157  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
158  def StartAgentTracing(self, config, timeout=None):
159    """Start tracing.
160    """
161    self._config = config
162    categories = self._fix_categories(config.ftrace_categories)
163    self._fio.writeFile(FT_BUFFER_SIZE,
164                        str(self._get_trace_buffer_size()))
165    self._fio.writeFile(FT_CLOCK, 'global')
166    self._fio.writeFile(FT_TRACER, 'nop')
167    self._fio.writeFile(FT_OVERWRITE, "0")
168
169    # TODO: riandrews to push necessary patches for TGID option to upstream
170    # linux kernel
171    # self._fio.writeFile(FT_PRINT_TGID, '1')
172
173    for category in categories:
174      self._category_enable(category)
175
176    self._categories = categories # need to store list of categories to disable
177    print 'starting tracing.'
178
179    self._fio.writeFile(FT_TRACE, '')
180    self._fio.writeFile(FT_TRACE_ON, '1')
181    return True
182
183  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
184  def StopAgentTracing(self, timeout=None):
185    """Collect the result of tracing.
186
187    This function will block while collecting the result. For sync mode, it
188    reads the data, e.g., from stdout, until it finishes. For async mode, it
189    blocks until the agent is stopped and the data is ready.
190    """
191    self._fio.writeFile(FT_TRACE_ON, '0')
192    for category in self._categories:
193      self._category_disable(category)
194    return True
195
196  @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
197  def GetResults(self, timeout=None):
198    # get the output
199    d = self._fio.readFile(FT_TRACE)
200    self._fio.writeFile(FT_BUFFER_SIZE, "1")
201    return trace_result.TraceResult('trace-data', d)
202
203  def SupportsExplicitClockSync(self):
204    return False
205
206  def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
207    # No implementation, but need to have this to support the API
208    # pylint: disable=unused-argument
209    return False
210
211  def _is_category_available(self, category):
212    if category not in all_categories:
213      return False
214    events_dir = FT_DIR + "events/"
215    req_events = all_categories[category]["req"]
216    for event in req_events:
217      event_full_path = events_dir + event + "enable"
218      if not self._fio.haveWritePermissions(event_full_path):
219        return False
220    return True
221
222  def _avail_categories(self):
223    ret = []
224    for event in all_categories:
225      if self._is_category_available(event):
226        ret.append(event)
227    return ret
228
229  def _print_avail_categories(self):
230    avail = self._avail_categories()
231    if len(avail):
232      print "tracing config:"
233      for category in self._avail_categories():
234        desc = all_categories[category]["desc"]
235        print "{0: <16}".format(category), ": ", desc
236    else:
237      print "No tracing categories available - perhaps you need root?"
238
239  def _category_enable_paths(self, category):
240    events_dir = FT_DIR + "events/"
241    req_events = all_categories[category]["req"]
242    for event in req_events:
243      event_full_path = events_dir + event + "enable"
244      yield event_full_path
245    if "opt" in all_categories[category]:
246      opt_events = all_categories[category]["opt"]
247      for event in opt_events:
248        event_full_path = events_dir + event + "enable"
249        if self._fio.haveWritePermissions(event_full_path):
250          yield event_full_path
251
252  def _category_enable(self, category):
253    for path in self._category_enable_paths(category):
254      self._fio.writeFile(path, "1")
255
256  def _category_disable(self, category):
257    for path in self._category_enable_paths(category):
258      self._fio.writeFile(path, "0")
259