1# Copyright 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 datetime 6import logging 7import os 8import random 9import shutil 10import StringIO 11import sys 12import tempfile 13 14from catapult_base import cloud_storage # pylint: disable=import-error 15 16from telemetry.internal.util import file_handle 17from telemetry.timeline import trace_data as trace_data_module 18from telemetry import value as value_module 19 20from tracing_build import trace2html 21 22 23class TraceValue(value_module.Value): 24 def __init__(self, page, trace_data, important=False, description=None): 25 """A value that contains a TraceData object and knows how to 26 output it. 27 28 Adding TraceValues and outputting as JSON will produce a directory full of 29 HTML files called trace_files. Outputting as chart JSON will also produce 30 an index, files.html, linking to each of these files. 31 """ 32 super(TraceValue, self).__init__( 33 page, name='trace', units='', important=important, 34 description=description, tir_label=None, grouping_keys=None) 35 self._temp_file = self._GetTempFileHandle(trace_data) 36 self._cloud_url = None 37 self._serialized_file_handle = None 38 39 def _GetTempFileHandle(self, trace_data): 40 if self.page: 41 title = self.page.display_name 42 else: 43 title = '' 44 content = StringIO.StringIO() 45 trace2html.WriteHTMLForTraceDataToFile( 46 [trace_data.GetEventsFor(trace_data_module.CHROME_TRACE_PART)], 47 title, 48 content) 49 tf = tempfile.NamedTemporaryFile(delete=False, suffix='.html') 50 tf.write(content.getvalue().encode('utf-8')) 51 tf.close() 52 return file_handle.FromTempFile(tf) 53 54 def __repr__(self): 55 if self.page: 56 page_name = self.page.display_name 57 else: 58 page_name = 'None' 59 return 'TraceValue(%s, %s)' % (page_name, self.name) 60 61 def CleanUp(self): 62 """Cleans up tempfile after it is no longer needed. 63 64 A cleaned up TraceValue cannot be used for further operations. CleanUp() 65 may be called more than once without error. 66 """ 67 if self._temp_file is None: 68 return 69 os.remove(self._temp_file.GetAbsPath()) 70 self._temp_file = None 71 72 def __enter__(self): 73 return self 74 75 def __exit__(self, _, __, ___): 76 self.CleanUp() 77 78 @property 79 def cleaned_up(self): 80 return self._temp_file is None 81 82 @property 83 def filename(self): 84 return self._temp_file.GetAbsPath() 85 86 def GetBuildbotDataType(self, output_context): 87 return None 88 89 def GetBuildbotValue(self): 90 return None 91 92 def GetRepresentativeNumber(self): 93 return None 94 95 def GetRepresentativeString(self): 96 return None 97 98 @staticmethod 99 def GetJSONTypeName(): 100 return 'trace' 101 102 @classmethod 103 def MergeLikeValuesFromSamePage(cls, values): 104 assert len(values) > 0 105 return values[0] 106 107 @classmethod 108 def MergeLikeValuesFromDifferentPages(cls, values): 109 return None 110 111 def AsDict(self): 112 if self._temp_file is None: 113 raise ValueError('Tried to serialize TraceValue without tempfile.') 114 d = super(TraceValue, self).AsDict() 115 if self._serialized_file_handle: 116 d['file_id'] = self._serialized_file_handle.id 117 if self._cloud_url: 118 d['cloud_url'] = self._cloud_url 119 return d 120 121 def Serialize(self, dir_path): 122 if self._temp_file is None: 123 raise ValueError('Tried to serialize nonexistent trace.') 124 file_name = str(self._temp_file.id) + self._temp_file.extension 125 file_path = os.path.abspath(os.path.join(dir_path, file_name)) 126 shutil.copy(self._temp_file.GetAbsPath(), file_path) 127 self._serialized_file_handle = file_handle.FromFilePath(file_path) 128 return self._serialized_file_handle 129 130 def UploadToCloud(self, bucket): 131 if self._temp_file is None: 132 raise ValueError('Tried to upload nonexistent trace to Cloud Storage.') 133 try: 134 if self._serialized_file_handle: 135 fh = self._serialized_file_handle 136 else: 137 fh = self._temp_file 138 remote_path = ('trace-file-id_%s-%s-%d%s' % ( 139 fh.id, 140 datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), 141 random.randint(1, 100000), 142 fh.extension)) 143 self._cloud_url = cloud_storage.Insert( 144 bucket, remote_path, fh.GetAbsPath()) 145 sys.stderr.write( 146 'View generated trace files online at %s for page %s\n' % 147 (self._cloud_url, self.page.url if self.page else 'unknown')) 148 return self._cloud_url 149 except cloud_storage.PermissionError as e: 150 logging.error('Cannot upload trace files to cloud storage due to ' 151 ' permission error: %s' % e.message) 152