• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2
3# Copyright 2016 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7'''Tracing controller class. This class manages
8multiple tracing agents and collects data from all of them. It also
9manages the clock sync process.
10'''
11
12import ast
13import sys
14import py_utils
15import tempfile
16import uuid
17
18from systrace import trace_result
19from systrace import tracing_agents
20from py_trace_event import trace_event
21
22
23TRACE_DATA_CONTROLLER_NAME = 'systraceController'
24
25
26def ControllerAgentClockSync(issue_ts, name):
27  """Record the clock sync marker for controller tracing agent.
28
29  Unlike with the other tracing agents, the tracing controller should not
30  call this directly. Rather, it is called via callback from the other
31  tracing agents when they write a trace.
32  """
33  trace_event.clock_sync(name, issue_ts=issue_ts)
34
35
36class TracingControllerAgent(tracing_agents.TracingAgent):
37  def __init__(self):
38    super(TracingControllerAgent, self).__init__()
39    self._log_path = None
40
41  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
42  def StartAgentTracing(self, config, timeout=None):
43    """Start tracing for the controller tracing agent.
44
45    Start tracing for the controller tracing agent. Note that
46    the tracing controller records the "controller side"
47    of the clock sync records, and nothing else.
48    """
49    del config
50    if not trace_event.trace_can_enable():
51      raise RuntimeError, ('Cannot enable trace_event;'
52                           ' ensure py_utils is in PYTHONPATH')
53
54    controller_log_file = tempfile.NamedTemporaryFile(delete=False)
55    self._log_path = controller_log_file.name
56    controller_log_file.close()
57    trace_event.trace_enable(self._log_path)
58    return True
59
60  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
61  def StopAgentTracing(self, timeout=None):
62    """Stops tracing for the controller tracing agent.
63    """
64    # pylint: disable=no-self-use
65    # This function doesn't use self, but making it a member function
66    # for consistency with the other TracingAgents
67    trace_event.trace_disable()
68    return True
69
70  @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
71  def GetResults(self, timeout=None):
72    """Gets the log output from the controller tracing agent.
73
74    This output only contains the "controller side" of the clock sync records.
75    """
76    with open(self._log_path, 'r') as outfile:
77      result = outfile.read() + ']'
78    return trace_result.TraceResult(TRACE_DATA_CONTROLLER_NAME,
79                                    ast.literal_eval(result))
80
81  def SupportsExplicitClockSync(self):
82    """Returns whether this supports explicit clock sync.
83    Although the tracing controller conceptually supports explicit clock
84    sync, it is not an agent controlled by other controllers so it does not
85    define RecordClockSyncMarker (rather, the recording of the "controller
86    side" of the clock sync marker is done in _IssueClockSyncMarker). Thus,
87    SupportsExplicitClockSync must return false.
88    """
89    return False
90
91  # pylint: disable=unused-argument
92  def RecordClockSyncMarker(self, sync_id, callback):
93    raise NotImplementedError
94
95class TracingController(object):
96  def __init__(self, agents_with_config, controller_config):
97    """Create tracing controller.
98
99    Create a tracing controller object. Note that the tracing
100    controller is also a tracing agent.
101
102    Args:
103       agents_with_config: List of tracing agents for this controller with the
104                           corresponding tracing configuration objects.
105       controller_config:  Configuration options for the tracing controller.
106    """
107    self._child_agents = None
108    self._child_agents_with_config = agents_with_config
109    self._controller_agent = TracingControllerAgent()
110    self._controller_config = controller_config
111    self._trace_in_progress = False
112    self.all_results = None
113
114  @property
115  def get_child_agents(self):
116    return self._child_agents
117
118  def StartTracing(self):
119    """Start tracing for all tracing agents.
120
121    This function starts tracing for both the controller tracing agent
122    and the child tracing agents.
123
124    Returns:
125        Boolean indicating whether or not the start tracing succeeded.
126        Start tracing is considered successful if at least the
127        controller tracing agent was started.
128    """
129    assert not self._trace_in_progress, 'Trace already in progress.'
130    self._trace_in_progress = True
131
132    # Start the controller tracing agents. Controller tracing agent
133    # must be started successfully to proceed.
134    if not self._controller_agent.StartAgentTracing(
135        self._controller_config,
136        timeout=self._controller_config.timeout):
137      print 'Unable to start controller tracing agent.'
138      return False
139
140    # Start the child tracing agents.
141    succ_agents = []
142    for agent_and_config in self._child_agents_with_config:
143      agent = agent_and_config.agent
144      config = agent_and_config.config
145      if agent.StartAgentTracing(config,
146                                 timeout=self._controller_config.timeout):
147        succ_agents.append(agent)
148      else:
149        print 'Agent %s not started.' % str(agent)
150
151    # Print warning if all agents not started.
152    na = len(self._child_agents_with_config)
153    ns = len(succ_agents)
154    if ns < na:
155      print 'Warning: Only %d of %d tracing agents started.' % (ns, na)
156    self._child_agents = succ_agents
157    return True
158
159  def StopTracing(self):
160    """Issue clock sync marker and stop tracing for all tracing agents.
161
162    This function stops both the controller tracing agent
163    and the child tracing agents. It issues a clock sync marker prior
164    to stopping tracing.
165
166    Returns:
167        Boolean indicating whether or not the stop tracing succeeded
168        for all agents.
169    """
170    assert self._trace_in_progress, 'No trace in progress.'
171    self._trace_in_progress = False
172
173    # Issue the clock sync marker and stop the child tracing agents.
174    self._IssueClockSyncMarker()
175    succ_agents = []
176    for agent in self._child_agents:
177      if agent.StopAgentTracing(timeout=self._controller_config.timeout):
178        succ_agents.append(agent)
179      else:
180        print 'Agent %s not stopped.' % str(agent)
181
182    # Stop the controller tracing agent. Controller tracing agent
183    # must be stopped successfully to proceed.
184    if not self._controller_agent.StopAgentTracing(
185        timeout=self._controller_config.timeout):
186      print 'Unable to stop controller tracing agent.'
187      return False
188
189    # Print warning if all agents not stopped.
190    na = len(self._child_agents)
191    ns = len(succ_agents)
192    if ns < na:
193      print 'Warning: Only %d of %d tracing agents stopped.' % (ns, na)
194      self._child_agents = succ_agents
195
196    # Collect the results from all the stopped tracing agents.
197    all_results = []
198    for agent in self._child_agents + [self._controller_agent]:
199      try:
200        result = agent.GetResults(
201            timeout=self._controller_config.collection_timeout)
202        if not result:
203          print 'Warning: Timeout when getting results from %s.' % str(agent)
204          continue
205        if result.source_name in [r.source_name for r in all_results]:
206          print ('Warning: Duplicate tracing agents named %s.' %
207                 result.source_name)
208        all_results.append(result)
209      # Check for exceptions. If any exceptions are seen, reraise and abort.
210      # Note that a timeout exception will be swalloed by the timeout
211      # mechanism and will not get to that point (it will return False instead
212      # of the trace result, which will be dealt with above)
213      except:
214        print 'Warning: Exception getting results from %s:' % str(agent)
215        print sys.exc_info()[0]
216        raise
217    self.all_results = all_results
218    return all_results
219
220  def GetTraceType(self):
221    """Return a string representing the child agents that are being traced."""
222    sorted_agents = sorted(map(str, self._child_agents))
223    return ' + '.join(sorted_agents)
224
225  def _IssueClockSyncMarker(self):
226    """Issue clock sync markers to all the child tracing agents."""
227    for agent in self._child_agents:
228      if agent.SupportsExplicitClockSync():
229        sync_id = GetUniqueSyncID()
230        agent.RecordClockSyncMarker(sync_id, ControllerAgentClockSync)
231
232def GetUniqueSyncID():
233  """Get a unique sync ID.
234
235  Gets a unique sync ID by generating a UUID and converting it to a string
236  (since UUIDs are not JSON serializable)
237  """
238  return str(uuid.uuid4())
239
240
241class AgentWithConfig(object):
242  def __init__(self, agent, config):
243    self.agent = agent
244    self.config = config
245
246
247def CreateAgentsWithConfig(options, modules):
248  """Create tracing agents.
249
250  This function will determine which tracing agents are valid given the
251  options and create those agents along with their corresponding configuration
252  object.
253  Args:
254    options: The command-line options.
255    modules: The modules for either Systrace or profile_chrome.
256             TODO(washingtonp): After all profile_chrome agents are in
257             Systrace, this parameter will no longer be valid.
258  Returns:
259    A list of AgentWithConfig options containing agents and their corresponding
260    configuration object.
261  """
262  result = []
263  for module in modules:
264    config = module.get_config(options)
265    agent = module.try_create_agent(config)
266    if agent and config:
267      result.append(AgentWithConfig(agent, config))
268  return [x for x in result if x and x.agent]
269
270
271class TracingControllerConfig(tracing_agents.TracingConfig):
272  def __init__(self, output_file, trace_time, write_json,
273               link_assets, asset_dir, timeout, collection_timeout,
274               device_serial_number, target):
275    tracing_agents.TracingConfig.__init__(self)
276    self.output_file = output_file
277    self.trace_time = trace_time
278    self.write_json = write_json
279    self.link_assets = link_assets
280    self.asset_dir = asset_dir
281    self.timeout = timeout
282    self.collection_timeout = collection_timeout
283    self.device_serial_number = device_serial_number
284    self.target = target
285
286
287def GetControllerConfig(options):
288  return TracingControllerConfig(options.output_file, options.trace_time,
289                                 options.write_json,
290                                 options.link_assets, options.asset_dir,
291                                 options.timeout, options.collection_timeout,
292                                 options.device_serial_number, options.target)
293
294def GetChromeStartupControllerConfig(options):
295  return TracingControllerConfig(None, options.trace_time,
296                                 options.write_json, None, None, None, None,
297                                 None, None)
298