Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Script to parse perf data from Chrome Endure test executions, to be graphed. | 6 """Script to parse perf data from Chrome Endure test executions, to be graphed. |
| 7 | 7 |
| 8 This script connects via HTTP to a buildbot master in order to scrape and parse | 8 This script connects via HTTP to a buildbot master in order to scrape and parse |
| 9 perf data from Chrome Endure tests that have been run. The perf data is then | 9 perf data from Chrome Endure tests that have been run. The perf data is then |
| 10 stored in local text files to be graphed by the Chrome Endure graphing code. | 10 stored in local text files to be graphed by the Chrome Endure graphing code. |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 111 elif int(line_dict['rev']) < int(revision): | 111 elif int(line_dict['rev']) < int(revision): |
| 112 break | 112 break |
| 113 if not overwritten: | 113 if not overwritten: |
| 114 existing_lines.insert(0, simplejson.dumps(new_line)) | 114 existing_lines.insert(0, simplejson.dumps(new_line)) |
| 115 | 115 |
| 116 with open(data_file, 'w') as f: | 116 with open(data_file, 'w') as f: |
| 117 f.write('\n'.join(existing_lines)) | 117 f.write('\n'.join(existing_lines)) |
| 118 os.chmod(data_file, 0755) | 118 os.chmod(data_file, 0755) |
| 119 | 119 |
| 120 | 120 |
| 121 def OutputPerfData(revision, graph_name, values, units, units_x, dest_dir): | 121 def OutputPerfData(revision, graph_name, values, units, units_x, dest_dir, |
| 122 is_stacked=False, stack_order=[]): | |
| 122 """Outputs perf data to a local text file to be graphed. | 123 """Outputs perf data to a local text file to be graphed. |
| 123 | 124 |
| 124 Args: | 125 Args: |
| 125 revision: The string revision number associated with the perf data. | 126 revision: The string revision number associated with the perf data. |
| 126 graph_name: The string name of the graph on which to plot the data. | 127 graph_name: The string name of the graph on which to plot the data. |
| 127 values: A dict which maps a description to a value. A value is either a | 128 values: A dict which maps a description to a value. A value is either a |
| 128 single data value to be graphed, or a list of 2-tuples | 129 single data value to be graphed, or a list of 2-tuples |
| 129 representing (x, y) points to be graphed for long-running tests. | 130 representing (x, y) points to be graphed for long-running tests. |
| 130 units: The string description for the y-axis units on the graph. | 131 units: The string description for the y-axis units on the graph. |
| 131 units_x: The string description for the x-axis units on the graph. Should | 132 units_x: The string description for the x-axis units on the graph. Should |
| 132 be set to None if the results are not for long-running graphs. | 133 be set to None if the results are not for long-running graphs. |
| 133 dest_dir: The name of the destination directory to which to write. | 134 dest_dir: The name of the destination directory to which to write. |
| 135 is_stacked: True to draw a "stacked" graph. First-come values are | |
| 136 stacked at bottom by default. | |
| 137 stack_order: A list that contains order to stack values in the graph. | |
|
dennis_jeffrey
2012/09/26 00:58:06
is this an array of perf key strings? If so, mayb
Dai Mikurube (NOT FULLTIME)
2012/09/26 06:11:15
Done.
| |
| 134 """ | 138 """ |
| 135 # Update graphs.dat, which contains metadata associated with each graph. | 139 # Update graphs.dat, which contains metadata associated with each graph. |
| 136 existing_graphs = [] | 140 existing_graphs = [] |
| 137 graphs_file = os.path.join(dest_dir, 'graphs.dat') | 141 graphs_file = os.path.join(dest_dir, 'graphs.dat') |
| 138 if os.path.exists(graphs_file): | 142 if os.path.exists(graphs_file): |
| 139 with open(graphs_file, 'r') as f: | 143 with open(graphs_file, 'r') as f: |
| 140 existing_graphs = simplejson.loads(f.read()) | 144 existing_graphs = simplejson.loads(f.read()) |
| 141 is_new_graph = True | 145 is_new_graph = True |
| 142 for graph in existing_graphs: | 146 for graph in existing_graphs: |
| 143 if graph['name'] == graph_name: | 147 if graph['name'] == graph_name: |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 172 points = [] | 176 points = [] |
| 173 for point in value: | 177 for point in value: |
| 174 points.append([str(point[0]), str(point[1])]) | 178 points.append([str(point[0]), str(point[1])]) |
| 175 new_traces[description] = points | 179 new_traces[description] = points |
| 176 else: | 180 else: |
| 177 new_traces[description] = [str(value), str(0.0)] | 181 new_traces[description] = [str(value), str(0.0)] |
| 178 new_line = { | 182 new_line = { |
| 179 'traces': new_traces, | 183 'traces': new_traces, |
| 180 'rev': revision | 184 'rev': revision |
| 181 } | 185 } |
| 186 if is_stacked: | |
| 187 new_line['stack'] = is_stacked | |
|
dennis_jeffrey
2012/09/26 00:58:06
since we will only include 'stack' in the line if
Dai Mikurube (NOT FULLTIME)
2012/09/26 06:11:15
Done.
| |
| 188 new_line['stack_order'] = stack_order | |
| 182 | 189 |
| 183 WriteToDataFile(new_line, existing_lines, revision, data_file) | 190 WriteToDataFile(new_line, existing_lines, revision, data_file) |
| 184 | 191 |
| 185 | 192 |
| 186 def OutputEventData(revision, event_dict, dest_dir): | 193 def OutputEventData(revision, event_dict, dest_dir): |
| 187 """Outputs event data to a local text file to be graphed. | 194 """Outputs event data to a local text file to be graphed. |
| 188 | 195 |
| 189 Args: | 196 Args: |
| 190 revision: The string revision number associated with the event data. | 197 revision: The string revision number associated with the event data. |
| 191 event_dict: A dict which maps a description to an array of tuples | 198 event_dict: A dict which maps a description to an array of tuples |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 221 | 228 |
| 222 Args: | 229 Args: |
| 223 revision: The string revision number associated with the new perf entry. | 230 revision: The string revision number associated with the new perf entry. |
| 224 content: Fetched stdio data. | 231 content: Fetched stdio data. |
| 225 webapp_name: A name of the webapp. | 232 webapp_name: A name of the webapp. |
| 226 test_name: A name of the test. | 233 test_name: A name of the test. |
| 227 """ | 234 """ |
| 228 perf_data_raw = [] | 235 perf_data_raw = [] |
| 229 | 236 |
| 230 def AppendRawPerfData(graph_name, description, value, units, units_x, | 237 def AppendRawPerfData(graph_name, description, value, units, units_x, |
| 231 webapp_name, test_name): | 238 webapp_name, test_name, is_stacked=False): |
| 232 perf_data_raw.append({ | 239 perf_data_raw.append({ |
| 233 'graph_name': graph_name, | 240 'graph_name': graph_name, |
| 234 'description': description, | 241 'description': description, |
| 235 'value': value, | 242 'value': value, |
| 236 'units': units, | 243 'units': units, |
| 237 'units_x': units_x, | 244 'units_x': units_x, |
| 238 'webapp_name': webapp_name, | 245 'webapp_name': webapp_name, |
| 239 'test_name': test_name, | 246 'test_name': test_name, |
| 247 'stack': is_stacked, | |
| 240 }) | 248 }) |
| 241 | 249 |
| 242 # First scan for short-running perf test results. | 250 # First scan for short-running perf test results. |
| 243 for match in re.findall( | 251 for match in re.findall( |
| 244 r'RESULT ([^:]+): ([^=]+)= ([-\d\.]+) (\S+)', content): | 252 r'RESULT ([^:]+): ([^=]+)= ([-\d\.]+) (\S+)', content): |
| 245 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], None, | 253 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], None, |
| 246 webapp_name, webapp_name) | 254 webapp_name, webapp_name) |
| 247 | 255 |
| 248 # Next scan for long-running perf test results. | 256 # Next scan for long-running perf test results. |
| 249 for match in re.findall( | 257 for match in re.findall( |
| 250 r'RESULT ([^:]+): ([^=]+)= (\[[^\]]+\]) (\S+) (\S+)', content): | 258 r'RESULT ([^:]+): ([^=]+)= (\[[^\]]+\]) (\S+) (\S+)', content): |
| 251 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], match[4], | 259 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], match[4], |
| 252 webapp_name, test_name) | 260 webapp_name, test_name, match[0].endswith('-DMP')) |
|
Dai Mikurube (NOT FULLTIME)
2012/09/20 09:17:30
It's temporary. We need to decide "RESULT" protoc
dennis_jeffrey
2012/09/26 00:58:06
I think this is ok for now, since we append "-DMP"
Dai Mikurube (NOT FULLTIME)
2012/09/26 06:11:15
Done.
| |
| 253 | 261 |
| 254 # Next scan for events in the test results. | 262 # Next scan for events in the test results. |
| 255 for match in re.findall( | 263 for match in re.findall( |
| 256 r'RESULT _EVENT_: ([^=]+)= (\[[^\]]+\])', content): | 264 r'RESULT _EVENT_: ([^=]+)= (\[[^\]]+\])', content): |
| 257 AppendRawPerfData('_EVENT_', match[0], eval(match[1]), None, None, | 265 AppendRawPerfData('_EVENT_', match[0], eval(match[1]), None, None, |
| 258 webapp_name, test_name) | 266 webapp_name, test_name) |
| 259 | 267 |
| 260 # For each graph_name/description pair that refers to a long-running test | 268 # For each graph_name/description pair that refers to a long-running test |
| 261 # result or an event, concatenate all the results together (assume results | 269 # result or an event, concatenate all the results together (assume results |
| 262 # in the input file are in the correct order). For short-running test | 270 # in the input file are in the correct order). For short-running test |
| 263 # results, keep just one if more than one is specified. | 271 # results, keep just one if more than one is specified. |
| 264 perf_data = {} # Maps a graph-line key to a perf data dictionary. | 272 perf_data = {} # Maps a graph-line key to a perf data dictionary. |
| 265 for data in perf_data_raw: | 273 for data in perf_data_raw: |
| 266 key1 = data['graph_name'] | 274 key1 = data['graph_name'] |
| 267 key2 = data['description'] | 275 key2 = data['description'] |
| 268 if not key1 in perf_data: | 276 if not key1 in perf_data: |
| 269 perf_data[key1] = { | 277 perf_data[key1] = { |
| 270 'graph_name': data['graph_name'], | 278 'graph_name': data['graph_name'], |
| 271 'value': {}, | 279 'value': {}, |
| 272 'units': data['units'], | 280 'units': data['units'], |
| 273 'units_x': data['units_x'], | 281 'units_x': data['units_x'], |
| 274 'webapp_name': data['webapp_name'], | 282 'webapp_name': data['webapp_name'], |
| 275 'test_name': data['test_name'], | 283 'test_name': data['test_name'], |
| 276 } | 284 } |
| 285 perf_data[key1]['stack'] = data['stack'] | |
| 286 if 'stack_order' not in perf_data[key1]: | |
| 287 perf_data[key1]['stack_order'] = [] | |
| 288 if data['description'] not in perf_data[key1]['stack_order']: | |
| 289 perf_data[key1]['stack_order'].append(data['description']) | |
|
dennis_jeffrey
2012/09/26 00:58:06
should we only execute lines 286-289 if data['stac
Dai Mikurube (NOT FULLTIME)
2012/09/26 06:11:15
I did it to simplify line 321 (calling OutputPerfD
| |
| 290 | |
| 277 if data['graph_name'] != '_EVENT_' and not data['units_x']: | 291 if data['graph_name'] != '_EVENT_' and not data['units_x']: |
| 278 # Short-running test result. | 292 # Short-running test result. |
| 279 perf_data[key1]['value'][key2] = data['value'] | 293 perf_data[key1]['value'][key2] = data['value'] |
| 280 else: | 294 else: |
| 281 # Long-running test result or event. | 295 # Long-running test result or event. |
| 282 if key2 in perf_data[key1]['value']: | 296 if key2 in perf_data[key1]['value']: |
| 283 perf_data[key1]['value'][key2] += data['value'] | 297 perf_data[key1]['value'][key2] += data['value'] |
| 284 else: | 298 else: |
| 285 perf_data[key1]['value'][key2] = data['value'] | 299 perf_data[key1]['value'][key2] = data['value'] |
| 286 | 300 |
| 287 # Finally, for each graph-line in |perf_data|, update the associated local | 301 # Finally, for each graph-line in |perf_data|, update the associated local |
| 288 # graph data files if necessary. | 302 # graph data files if necessary. |
| 289 for perf_data_key in perf_data: | 303 for perf_data_key in perf_data: |
| 290 perf_data_dict = perf_data[perf_data_key] | 304 perf_data_dict = perf_data[perf_data_key] |
| 291 | 305 |
| 292 dest_dir = os.path.join(LOCAL_GRAPH_DIR, perf_data_dict['webapp_name']) | 306 dest_dir = os.path.join(LOCAL_GRAPH_DIR, perf_data_dict['webapp_name']) |
| 293 if not os.path.exists(dest_dir): | 307 if not os.path.exists(dest_dir): |
| 294 os.mkdir(dest_dir) # Webapp name directory. | 308 os.mkdir(dest_dir) # Webapp name directory. |
| 295 os.chmod(dest_dir, 0755) | 309 os.chmod(dest_dir, 0755) |
| 296 dest_dir = os.path.join(dest_dir, perf_data_dict['test_name']) | 310 dest_dir = os.path.join(dest_dir, perf_data_dict['test_name']) |
| 297 | 311 |
| 298 SetupBaseGraphDirIfNeeded(perf_data_dict['webapp_name'], | 312 SetupBaseGraphDirIfNeeded(perf_data_dict['webapp_name'], |
| 299 perf_data_dict['test_name'], dest_dir) | 313 perf_data_dict['test_name'], dest_dir) |
| 300 if perf_data_dict['graph_name'] == '_EVENT_': | 314 if perf_data_dict['graph_name'] == '_EVENT_': |
| 301 OutputEventData(revision, perf_data_dict['value'], dest_dir) | 315 OutputEventData(revision, perf_data_dict['value'], dest_dir) |
| 302 else: | 316 else: |
| 303 OutputPerfData(revision, perf_data_dict['graph_name'], | 317 OutputPerfData(revision, perf_data_dict['graph_name'], |
| 304 perf_data_dict['value'], | 318 perf_data_dict['value'], |
| 305 perf_data_dict['units'], perf_data_dict['units_x'], | 319 perf_data_dict['units'], perf_data_dict['units_x'], |
| 306 dest_dir) | 320 dest_dir, |
| 321 perf_data_dict['stack'], perf_data_dict['stack_order']) | |
| 307 | 322 |
| 308 | 323 |
| 309 def UpdatePerfDataForSlaveAndBuild(slave_info, build_num): | 324 def UpdatePerfDataForSlaveAndBuild(slave_info, build_num): |
| 310 """Process updated perf data for a particular slave and build number. | 325 """Process updated perf data for a particular slave and build number. |
| 311 | 326 |
| 312 Args: | 327 Args: |
| 313 slave_info: A dictionary containing information about the slave to process. | 328 slave_info: A dictionary containing information about the slave to process. |
| 314 build_num: The particular build number on the slave to process. | 329 build_num: The particular build number on the slave to process. |
| 315 | 330 |
| 316 Returns: | 331 Returns: |
| (...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 619 if not success: | 634 if not success: |
| 620 logging.error('Failed to update perf data files.') | 635 logging.error('Failed to update perf data files.') |
| 621 sys.exit(0) | 636 sys.exit(0) |
| 622 | 637 |
| 623 GenerateIndexPage() | 638 GenerateIndexPage() |
| 624 logging.debug('All done!') | 639 logging.debug('All done!') |
| 625 | 640 |
| 626 | 641 |
| 627 if __name__ == '__main__': | 642 if __name__ == '__main__': |
| 628 main() | 643 main() |
| OLD | NEW |