• 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
7import base64
8import gzip
9import json
10import os
11import StringIO
12
13from systrace import tracing_controller
14from systrace import trace_result
15from tracing.trace_data import trace_data
16
17
18# TODO(alexandermont): Current version of trace viewer does not support
19# the controller tracing agent output. Thus we use this variable to
20# suppress this tracing agent's output. This should be removed once
21# trace viewer is working again.
22OUTPUT_CONTROLLER_TRACE_ = False
23CONTROLLER_TRACE_DATA_KEY = 'controllerTraceDataKey'
24_SYSTRACE_TO_TRACE_DATA_NAME_MAPPING = {
25    'androidProcessDump': trace_data.ANDROID_PROCESS_DATA_PART,
26    'systemTraceEvents': trace_data.ATRACE_PART,
27    'powerTraceAsString': trace_data.BATTOR_TRACE_PART,
28    'systraceController': trace_data.TELEMETRY_PART,
29    'traceEvents': trace_data.CHROME_TRACE_PART,
30    'waltTrace': trace_data.WALT_TRACE_PART,
31}
32_SYSTRACE_HEADER = 'Systrace'
33
34
35def NewGenerateHTMLOutput(trace_results, output_file_name):
36  trace_data_builder = trace_data.TraceDataBuilder()
37  for trace in trace_results:
38    trace_data_part = _SYSTRACE_TO_TRACE_DATA_NAME_MAPPING.get(
39        trace.source_name)
40    trace_data_builder.AddTraceFor(trace_data_part, trace.raw_data)
41  trace_data_builder.AsData().Serialize(output_file_name, _SYSTRACE_HEADER)
42
43
44def GenerateHTMLOutput(trace_results, output_file_name):
45  """Write the results of systrace to an HTML file.
46
47  Args:
48      trace_results: A list of TraceResults.
49      output_file_name: The name of the HTML file that the trace viewer
50          results should be written to.
51  """
52  def _ReadAsset(src_dir, filename):
53    return open(os.path.join(src_dir, filename)).read()
54
55  # TODO(rnephew): The tracing output formatter is able to handle a single
56  # systrace trace just as well as it handles multiple traces. The obvious thing
57  # to do here would be to use it all for all systrace output: however, we want
58  # to continue using the legacy way of formatting systrace output when a single
59  # systrace and the tracing controller trace are present in order to match the
60  # Java verison of systrace. Java systrace is expected to be deleted at a later
61  # date. We should consolidate this logic when that happens.
62
63  if len(trace_results) > 3:
64    NewGenerateHTMLOutput(trace_results, output_file_name)
65    return os.path.abspath(output_file_name)
66
67  systrace_dir = os.path.abspath(os.path.dirname(__file__))
68
69  try:
70    from systrace import update_systrace_trace_viewer
71  except ImportError:
72    pass
73  else:
74    update_systrace_trace_viewer.update()
75
76  trace_viewer_html = _ReadAsset(systrace_dir, 'systrace_trace_viewer.html')
77
78  # Open the file in binary mode to prevent python from changing the
79  # line endings, then write the prefix.
80  systrace_dir = os.path.abspath(os.path.dirname(__file__))
81  html_prefix = _ReadAsset(systrace_dir, 'prefix.html')
82  html_suffix = _ReadAsset(systrace_dir, 'suffix.html')
83  trace_viewer_html = _ReadAsset(systrace_dir,
84                                  'systrace_trace_viewer.html')
85
86  # Open the file in binary mode to prevent python from changing the
87  # line endings, then write the prefix.
88  html_file = open(output_file_name, 'wb')
89  html_file.write(html_prefix.replace('{{SYSTRACE_TRACE_VIEWER_HTML}}',
90                                      trace_viewer_html))
91
92  # Write the trace data itself. There is a separate section of the form
93  # <script class="trace-data" type="application/text"> ... </script>
94  # for each tracing agent (including the controller tracing agent).
95  html_file.write('<!-- BEGIN TRACE -->\n')
96  for result in trace_results:
97    html_file.write('  <script class="trace-data" type="application/text">\n')
98    html_file.write(_ConvertToHtmlString(result.raw_data))
99    html_file.write('  </script>\n')
100  html_file.write('<!-- END TRACE -->\n')
101
102  # Write the suffix and finish.
103  html_file.write(html_suffix)
104  html_file.close()
105
106  final_path = os.path.abspath(output_file_name)
107  return final_path
108
109def _ConvertToHtmlString(result):
110  """Convert a trace result to the format to be output into HTML.
111
112  If the trace result is a dictionary or list, JSON-encode it.
113  If the trace result is a string, leave it unchanged.
114  """
115  if isinstance(result, dict) or isinstance(result, list):
116    return json.dumps(result)
117  elif isinstance(result, str):
118    return result
119  else:
120    raise ValueError('Invalid trace result format for HTML output')
121
122def GenerateJSONOutput(trace_results, output_file_name):
123  """Write the results of systrace to a JSON file.
124
125  Args:
126      trace_results: A list of TraceResults.
127      output_file_name: The name of the JSON file that the trace viewer
128          results should be written to.
129  """
130  results = _ConvertTraceListToDictionary(trace_results)
131  results[CONTROLLER_TRACE_DATA_KEY] = (
132      tracing_controller.TRACE_DATA_CONTROLLER_NAME)
133  with open(output_file_name, 'w') as json_file:
134    json.dump(results, json_file)
135  final_path = os.path.abspath(output_file_name)
136  return final_path
137
138def MergeTraceResultsIfNeeded(trace_results):
139  """Merge a list of trace data, if possible. This function can take any list
140     of trace data, but it will only merge the JSON data (since that's all
141     we can merge).
142
143     Args:
144        trace_results: A list of TraceResults containing trace data.
145  """
146  if len(trace_results) <= 1:
147    return trace_results
148  merge_candidates = []
149  for result in trace_results:
150    # Try to detect a JSON file cheaply since that's all we can merge.
151    if result.raw_data[0] != '{':
152      continue
153    try:
154      json_data = json.loads(result.raw_data)
155    except ValueError:
156      continue
157    merge_candidates.append(trace_result.TraceResult(result.source_name,
158                                                     json_data))
159
160  if len(merge_candidates) <= 1:
161    return trace_results
162
163  other_results = [r for r in trace_results
164                   if not r.source_name in
165                   [c.source_name for c in merge_candidates]]
166
167  merged_data = merge_candidates[0].raw_data
168
169  for candidate in merge_candidates[1:]:
170    json_data = candidate.raw_data
171    for key, value in json_data.items():
172      if not str(key) in merged_data or not merged_data[str(key)]:
173        merged_data[str(key)] = value
174
175  return ([trace_result.TraceResult('merged-data', json.dumps(merged_data))]
176              + other_results)
177
178def _EncodeTraceData(trace_string):
179  compressed_trace = StringIO.StringIO()
180  with gzip.GzipFile(fileobj=compressed_trace, mode='w') as f:
181    f.write(trace_string)
182  b64_content = base64.b64encode(compressed_trace.getvalue())
183  return b64_content
184
185def _ConvertTraceListToDictionary(trace_list):
186  trace_dict = {}
187  for trace in trace_list:
188    trace_dict[trace.source_name] = trace.raw_data
189  return trace_dict
190