1# Copyright 2016 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 re 6 7from telemetry.timeline import chrome_trace_category_filter 8 9TRACE_BUFFER_SIZE_IN_KB = 'trace_buffer_size_in_kb' 10 11RECORD_MODE = 'record_mode' 12 13RECORD_CONTINUOUSLY = 'record-continuously' 14RECORD_UNTIL_FULL = 'record-until-full' 15RECORD_AS_MUCH_AS_POSSIBLE = 'record-as-much-as-possible' 16ECHO_TO_CONSOLE = 'trace-to-console' 17 18RECORD_MODES = { 19 RECORD_UNTIL_FULL, 20 RECORD_CONTINUOUSLY, 21 RECORD_AS_MUCH_AS_POSSIBLE, 22 ECHO_TO_CONSOLE, 23} 24 25ENABLE_SYSTRACE_PARAM = 'enable_systrace' 26UMA_HISTOGRAM_NAMES_PARAM = 'histogram_names' 27 28def _ConvertStringToCamelCase(string): 29 """Convert an underscore/hyphen-case string to its camel-case counterpart. 30 31 This function is the inverse of Chromium's ConvertFromCamelCase function 32 in src/content/browser/devtools/protocol/tracing_handler.cc. 33 """ 34 parts = re.split(r'[-_]', string) 35 return parts[0] + ''.join([p.title() for p in parts[1:]]) 36 37# TODO(crbug.com/971471): Don't do this anymore. 38def _ConvertDictKeysToCamelCaseRecursively(data): 39 """Recursively convert dictionary keys from underscore/hyphen- to camel-case. 40 41 This function is the inverse of Chromium's ConvertDictKeyStyle function 42 in src/content/browser/devtools/protocol/tracing_handler.cc. 43 """ 44 if isinstance(data, dict): 45 return {_ConvertStringToCamelCase(k): 46 _ConvertDictKeysToCamelCaseRecursively(v) 47 for k, v in data.iteritems()} 48 elif isinstance(data, list): 49 return map(_ConvertDictKeysToCamelCaseRecursively, data) 50 else: 51 return data 52 53 54class ChromeTraceConfig(object): 55 """Stores configuration options specific to the Chrome tracing agent. 56 57 This produces the trace config JSON string for tracing in Chrome. 58 59 Attributes: 60 record_mode: can be any mode in RECORD_MODES. This corresponds to 61 record modes in chrome. 62 category_filter: Object that specifies which tracing categories to trace. 63 """ 64 65 def __init__(self): 66 self._record_mode = RECORD_CONTINUOUSLY 67 self._category_filter = ( 68 chrome_trace_category_filter.ChromeTraceCategoryFilter()) 69 self._memory_dump_config = None 70 self._enable_systrace = False 71 self._uma_histogram_names = [] 72 self._trace_buffer_size_in_kb = None 73 self._trace_format = None 74 75 @property 76 def trace_format(self): 77 return self._trace_format 78 79 def SetProtoTraceFormat(self): 80 self._trace_format = 'proto' 81 82 def SetJsonTraceFormat(self): 83 self._trace_format = 'json' 84 85 def SetLowOverheadFilter(self): 86 self._category_filter = ( 87 chrome_trace_category_filter.CreateLowOverheadFilter()) 88 89 def SetDefaultOverheadFilter(self): 90 self._category_filter = ( 91 chrome_trace_category_filter.CreateDefaultOverheadFilter()) 92 93 def SetDebugOverheadFilter(self): 94 self._category_filter = ( 95 chrome_trace_category_filter.CreateDebugOverheadFilter()) 96 97 @property 98 def category_filter(self): 99 return self._category_filter 100 101 @property 102 def enable_systrace(self): 103 return self._enable_systrace 104 105 def SetCategoryFilter(self, cf): 106 if isinstance(cf, chrome_trace_category_filter.ChromeTraceCategoryFilter): 107 self._category_filter = cf 108 else: 109 raise TypeError( 110 'Must pass SetCategoryFilter a ChromeTraceCategoryFilter instance') 111 112 def SetMemoryDumpConfig(self, dump_config): 113 """Memory dump config stores the triggers for memory dumps.""" 114 if isinstance(dump_config, MemoryDumpConfig): 115 self._memory_dump_config = dump_config 116 else: 117 raise TypeError( 118 'Must pass SetMemoryDumpConfig a MemoryDumpConfig instance') 119 120 def SetEnableSystrace(self): 121 self._enable_systrace = True 122 123 def SetTraceBufferSizeInKb(self, size): 124 self._trace_buffer_size_in_kb = size 125 126 def EnableUMAHistograms(self, *args): 127 for uma_histogram_name in args: 128 self._uma_histogram_names.append(uma_histogram_name) 129 130 def HasUMAHistograms(self): 131 return len(self._uma_histogram_names) != 0 132 133 @property 134 def record_mode(self): 135 return self._record_mode 136 137 @record_mode.setter 138 def record_mode(self, value): 139 assert value in RECORD_MODES 140 self._record_mode = value 141 142 def GetChromeTraceConfigForStartupTracing(self): 143 """Map the config to a JSON string for startup tracing. 144 145 All keys in the returned dictionary use underscore-case (e.g. 146 'record_mode'). In addition, the 'record_mode' value uses hyphen-case 147 (e.g. 'record-until-full'). 148 """ 149 result = { 150 RECORD_MODE: self._record_mode 151 } 152 result.update(self._category_filter.GetDictForChromeTracing()) 153 if self._memory_dump_config: 154 result.update(self._memory_dump_config.GetDictForChromeTracing()) 155 if self._enable_systrace: 156 result[ENABLE_SYSTRACE_PARAM] = True 157 if self._uma_histogram_names: 158 result[UMA_HISTOGRAM_NAMES_PARAM] = self._uma_histogram_names 159 if self._trace_buffer_size_in_kb: 160 result[TRACE_BUFFER_SIZE_IN_KB] = self._trace_buffer_size_in_kb 161 return result 162 163 def GetChromeTraceConfigForDevTools(self): 164 """Map the config to a DevTools API config dictionary. 165 166 All keys in the returned dictionary use camel-case (e.g. 'recordMode'). 167 In addition, the 'recordMode' value also uses camel-case (e.g. 168 'recordUntilFull'). This is to invert the camel-case -> 169 underscore/hyphen-delimited mapping performed in Chromium devtools. 170 """ 171 result = self.GetChromeTraceConfigForStartupTracing() 172 if result[RECORD_MODE]: 173 result[RECORD_MODE] = _ConvertStringToCamelCase( 174 result[RECORD_MODE]) 175 if self._enable_systrace: 176 result.update({ENABLE_SYSTRACE_PARAM: True}) 177 return _ConvertDictKeysToCamelCaseRecursively(result) 178 179 180class MemoryDumpConfig(object): 181 """Stores the triggers for memory dumps in ChromeTraceConfig.""" 182 def __init__(self): 183 self._triggers = [] 184 185 def AddTrigger(self, mode, periodic_interval_ms): 186 """Adds a new trigger to config. 187 188 Args: 189 periodic_interval_ms: Dump time period in milliseconds. 190 level_of_detail: Memory dump level of detail string. 191 Valid arguments are "background", "light" and "detailed". 192 """ 193 assert mode in ['background', 'light', 'detailed'] 194 assert periodic_interval_ms > 0 195 self._triggers.append({'mode': mode, 196 'periodic_interval_ms': periodic_interval_ms}) 197 198 def GetDictForChromeTracing(self): 199 """Returns the dump config as dictionary for chrome tracing.""" 200 # An empty trigger list would mean no periodic memory dumps. 201 return {'memory_dump_config': {'triggers': self._triggers}} 202