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