1# Copyright (c) 2014 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 argparse
6import codecs
7import base64
8import gzip
9import json
10import os
11import StringIO
12
13import tracing_project
14
15from py_vulcanize import generate
16
17
18def Main(argv):
19
20  parser = argparse.ArgumentParser(
21      usage='%(prog)s <options> trace_file1 [trace_file2 ...]',
22      epilog='Takes the provided trace file and produces a standalone HTML\n'
23             'file that contains both the trace and the trace viewer.')
24
25  project = tracing_project.TracingProject()
26  project.AddConfigNameOptionToParser(parser)
27
28  parser.add_argument(
29      '--output', dest='output',
30      help='Where to put the generated result. If not '
31           'given, the trace filename is used, with an html suffix.')
32  parser.add_argument(
33      '--quiet', action='store_true',
34      help='Dont print the output file name')
35  parser.add_argument('trace_files', nargs='+')
36  args = parser.parse_args(argv[1:])
37
38  if args.output:
39    output_filename = args.output
40  elif len(args.trace_files) > 1:
41    parser.error('Must specify --output if there are multiple trace files.')
42  else:
43    name_part = os.path.splitext(args.trace_files[0])[0]
44    output_filename = name_part + '.html'
45
46  with codecs.open(output_filename, mode='w', encoding='utf-8') as f:
47    WriteHTMLForTracesToFile(args.trace_files, f, config_name=args.config_name)
48
49  if not args.quiet:
50    print output_filename
51  return 0
52
53
54class ViewerDataScript(generate.ExtraScript):
55
56  def __init__(self, trace_data_string, mime_type):
57    super(ViewerDataScript, self).__init__()
58    self._trace_data_string = trace_data_string
59    self._mime_type = mime_type
60
61  def WriteToFile(self, output_file):
62    output_file.write('<script id="viewer-data" type="%s">\n' % self._mime_type)
63    compressed_trace = StringIO.StringIO()
64    with gzip.GzipFile(fileobj=compressed_trace, mode='w') as f:
65      f.write(self._trace_data_string)
66    b64_content = base64.b64encode(compressed_trace.getvalue())
67    output_file.write(b64_content)
68    output_file.write('\n</script>\n')
69
70
71def WriteHTMLForTraceDataToFile(trace_data_list,
72                                title, output_file,
73                                config_name=None):
74  project = tracing_project.TracingProject()
75
76  if config_name is None:
77    config_name = project.GetDefaultConfigName()
78
79  modules = [
80      'tracing.trace2html',
81      'tracing.extras.importer.gzip_importer',  # Must have for all configs.
82      project.GetModuleNameForConfigName(config_name)
83  ]
84
85  vulcanizer = project.CreateVulcanizer()
86  load_sequence = vulcanizer.CalcLoadSequenceForModuleNames(modules)
87
88  scripts = []
89  for trace_data in trace_data_list:
90    # If the object was previously decoded from valid JSON data (e.g., in
91    # WriteHTMLForTracesToFile), it will be a JSON object at this point and we
92    # should re-serialize it into a string. Other types of data will be already
93    # be strings.
94    if not isinstance(trace_data, basestring):
95      trace_data = json.dumps(trace_data)
96      mime_type = 'application/json'
97    else:
98      mime_type = 'text/plain'
99    scripts.append(ViewerDataScript(trace_data, mime_type))
100  generate.GenerateStandaloneHTMLToFile(
101      output_file, load_sequence, title, extra_scripts=scripts)
102
103
104def WriteHTMLForTracesToFile(trace_filenames, output_file, config_name=None):
105  trace_data_list = []
106  for filename in trace_filenames:
107    with open(filename, 'r') as f:
108      trace_data = f.read()
109      try:
110        trace_data = json.loads(trace_data)
111      except ValueError:
112        pass
113      trace_data_list.append(trace_data)
114
115  title = "Trace from %s" % ','.join(trace_filenames)
116  WriteHTMLForTraceDataToFile(trace_data_list, title, output_file, config_name)
117