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