| 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 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 105 elif int(line_dict['rev']) < int(revision): | 105 elif int(line_dict['rev']) < int(revision): |
| 106 break | 106 break |
| 107 if not overwritten: | 107 if not overwritten: |
| 108 existing_lines.insert(0, simplejson.dumps(new_line)) | 108 existing_lines.insert(0, simplejson.dumps(new_line)) |
| 109 | 109 |
| 110 with open(data_file, 'w') as f: | 110 with open(data_file, 'w') as f: |
| 111 f.write('\n'.join(existing_lines)) | 111 f.write('\n'.join(existing_lines)) |
| 112 os.chmod(data_file, 0755) | 112 os.chmod(data_file, 0755) |
| 113 | 113 |
| 114 | 114 |
| 115 def OutputPerfData(revision, graph_name, values, units, units_x, dest_dir): | 115 def OutputPerfData(revision, graph_name, values, units, units_x, dest_dir, |
| 116 is_stacked=False, stack_order=[]): |
| 116 """Outputs perf data to a local text file to be graphed. | 117 """Outputs perf data to a local text file to be graphed. |
| 117 | 118 |
| 118 Args: | 119 Args: |
| 119 revision: The string revision number associated with the perf data. | 120 revision: The string revision number associated with the perf data. |
| 120 graph_name: The string name of the graph on which to plot the data. | 121 graph_name: The string name of the graph on which to plot the data. |
| 121 values: A dict which maps a description to a value. A value is either a | 122 values: A dict which maps a description to a value. A value is either a |
| 122 single data value to be graphed, or a list of 2-tuples | 123 single data value to be graphed, or a list of 2-tuples |
| 123 representing (x, y) points to be graphed for long-running tests. | 124 representing (x, y) points to be graphed for long-running tests. |
| 124 units: The string description for the y-axis units on the graph. | 125 units: The string description for the y-axis units on the graph. |
| 125 units_x: The string description for the x-axis units on the graph. Should | 126 units_x: The string description for the x-axis units on the graph. Should |
| 126 be set to None if the results are not for long-running graphs. | 127 be set to None if the results are not for long-running graphs. |
| 127 dest_dir: The name of the destination directory to which to write. | 128 dest_dir: The name of the destination directory to which to write. |
| 129 is_stacked: True to draw a "stacked" graph. First-come values are |
| 130 stacked at bottom by default. |
| 131 stack_order: A list that contains key strings in the order to stack values |
| 132 in the graph. |
| 128 """ | 133 """ |
| 129 # Update graphs.dat, which contains metadata associated with each graph. | 134 # Update graphs.dat, which contains metadata associated with each graph. |
| 130 existing_graphs = [] | 135 existing_graphs = [] |
| 131 graphs_file = os.path.join(dest_dir, 'graphs.dat') | 136 graphs_file = os.path.join(dest_dir, 'graphs.dat') |
| 132 if os.path.exists(graphs_file): | 137 if os.path.exists(graphs_file): |
| 133 with open(graphs_file, 'r') as f: | 138 with open(graphs_file, 'r') as f: |
| 134 existing_graphs = simplejson.loads(f.read()) | 139 existing_graphs = simplejson.loads(f.read()) |
| 135 is_new_graph = True | 140 is_new_graph = True |
| 136 for graph in existing_graphs: | 141 for graph in existing_graphs: |
| 137 if graph['name'] == graph_name: | 142 if graph['name'] == graph_name: |
| (...skipping 28 matching lines...) Expand all Loading... |
| 166 points = [] | 171 points = [] |
| 167 for point in value: | 172 for point in value: |
| 168 points.append([str(point[0]), str(point[1])]) | 173 points.append([str(point[0]), str(point[1])]) |
| 169 new_traces[description] = points | 174 new_traces[description] = points |
| 170 else: | 175 else: |
| 171 new_traces[description] = [str(value), str(0.0)] | 176 new_traces[description] = [str(value), str(0.0)] |
| 172 new_line = { | 177 new_line = { |
| 173 'traces': new_traces, | 178 'traces': new_traces, |
| 174 'rev': revision | 179 'rev': revision |
| 175 } | 180 } |
| 181 if is_stacked: |
| 182 new_line['stack'] = True |
| 183 new_line['stack_order'] = stack_order |
| 176 | 184 |
| 177 WriteToDataFile(new_line, existing_lines, revision, data_file) | 185 WriteToDataFile(new_line, existing_lines, revision, data_file) |
| 178 | 186 |
| 179 | 187 |
| 180 def OutputEventData(revision, event_dict, dest_dir): | 188 def OutputEventData(revision, event_dict, dest_dir): |
| 181 """Outputs event data to a local text file to be graphed. | 189 """Outputs event data to a local text file to be graphed. |
| 182 | 190 |
| 183 Args: | 191 Args: |
| 184 revision: The string revision number associated with the event data. | 192 revision: The string revision number associated with the event data. |
| 185 event_dict: A dict which maps a description to an array of tuples | 193 event_dict: A dict which maps a description to an array of tuples |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 293 # Issue warning but continue to the next stdio link. | 301 # Issue warning but continue to the next stdio link. |
| 294 logging.warning('Error reading test stdio URL "%s": %s', stdio_url, | 302 logging.warning('Error reading test stdio URL "%s": %s', stdio_url, |
| 295 str(e)) | 303 str(e)) |
| 296 finally: | 304 finally: |
| 297 if fp: | 305 if fp: |
| 298 fp.close() | 306 fp.close() |
| 299 | 307 |
| 300 perf_data_raw = [] | 308 perf_data_raw = [] |
| 301 | 309 |
| 302 def AppendRawPerfData(graph_name, description, value, units, units_x, | 310 def AppendRawPerfData(graph_name, description, value, units, units_x, |
| 303 webapp_name, test_name): | 311 webapp_name, test_name, is_stacked=False): |
| 304 perf_data_raw.append({ | 312 perf_data_raw.append({ |
| 305 'graph_name': graph_name, | 313 'graph_name': graph_name, |
| 306 'description': description, | 314 'description': description, |
| 307 'value': value, | 315 'value': value, |
| 308 'units': units, | 316 'units': units, |
| 309 'units_x': units_x, | 317 'units_x': units_x, |
| 310 'webapp_name': webapp_name, | 318 'webapp_name': webapp_name, |
| 311 'test_name': test_name, | 319 'test_name': test_name, |
| 320 'stack': is_stacked, |
| 312 }) | 321 }) |
| 313 | 322 |
| 314 # First scan for short-running perf test results. | 323 # First scan for short-running perf test results. |
| 315 for match in re.findall( | 324 for match in re.findall( |
| 316 r'RESULT ([^:]+): ([^=]+)= ([-\d\.]+) (\S+)', url_contents): | 325 r'RESULT ([^:]+): ([^=]+)= ([-\d\.]+) (\S+)', url_contents): |
| 317 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], None, | 326 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], None, |
| 318 stdio_url_data['webapp_name'], | 327 stdio_url_data['webapp_name'], |
| 319 stdio_url_data['webapp_name']) | 328 stdio_url_data['webapp_name']) |
| 320 | 329 |
| 321 # Next scan for long-running perf test results. | 330 # Next scan for long-running perf test results. |
| 322 for match in re.findall( | 331 for match in re.findall( |
| 323 r'RESULT ([^:]+): ([^=]+)= (\[[^\]]+\]) (\S+) (\S+)', url_contents): | 332 r'RESULT ([^:]+): ([^=]+)= (\[[^\]]+\]) (\S+) (\S+)', url_contents): |
| 333 # TODO(dmikurube): Change the condition to use stacked graph when we |
| 334 # determine how to specify it. |
| 324 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], match[4], | 335 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], match[4], |
| 325 stdio_url_data['webapp_name'], | 336 stdio_url_data['webapp_name'], |
| 326 stdio_url_data['test_name']) | 337 stdio_url_data['test_name'], |
| 338 match[0].endswith('-DMP')) |
| 327 | 339 |
| 328 # Next scan for events in the test results. | 340 # Next scan for events in the test results. |
| 329 for match in re.findall( | 341 for match in re.findall( |
| 330 r'RESULT _EVENT_: ([^=]+)= (\[[^\]]+\])', url_contents): | 342 r'RESULT _EVENT_: ([^=]+)= (\[[^\]]+\])', url_contents): |
| 331 AppendRawPerfData('_EVENT_', match[0], eval(match[1]), None, None, | 343 AppendRawPerfData('_EVENT_', match[0], eval(match[1]), None, None, |
| 332 stdio_url_data['webapp_name'], | 344 stdio_url_data['webapp_name'], |
| 333 stdio_url_data['test_name']) | 345 stdio_url_data['test_name']) |
| 334 | 346 |
| 335 # For each graph_name/description pair that refers to a long-running test | 347 # For each graph_name/description pair that refers to a long-running test |
| 336 # result or an event, concatenate all the results together (assume results | 348 # result or an event, concatenate all the results together (assume results |
| 337 # in the input file are in the correct order). For short-running test | 349 # in the input file are in the correct order). For short-running test |
| 338 # results, keep just one if more than one is specified. | 350 # results, keep just one if more than one is specified. |
| 339 perf_data = {} # Maps a graph-line key to a perf data dictionary. | 351 perf_data = {} # Maps a graph-line key to a perf data dictionary. |
| 340 for data in perf_data_raw: | 352 for data in perf_data_raw: |
| 341 key_graph = data['graph_name'] | 353 key_graph = data['graph_name'] |
| 342 key_description = data['description'] | 354 key_description = data['description'] |
| 343 if not key_graph in perf_data: | 355 if not key_graph in perf_data: |
| 344 perf_data[key_graph] = { | 356 perf_data[key_graph] = { |
| 345 'graph_name': data['graph_name'], | 357 'graph_name': data['graph_name'], |
| 346 'value': {}, | 358 'value': {}, |
| 347 'units': data['units'], | 359 'units': data['units'], |
| 348 'units_x': data['units_x'], | 360 'units_x': data['units_x'], |
| 349 'webapp_name': data['webapp_name'], | 361 'webapp_name': data['webapp_name'], |
| 350 'test_name': data['test_name'], | 362 'test_name': data['test_name'], |
| 351 } | 363 } |
| 364 perf_data[key_graph]['stack'] = data['stack'] |
| 365 if 'stack_order' not in perf_data[key_graph]: |
| 366 perf_data[key_graph]['stack_order'] = [] |
| 367 if (data['stack'] and |
| 368 data['description'] not in perf_data[key_graph]['stack_order']): |
| 369 perf_data[key_graph]['stack_order'].append(data['description']) |
| 370 |
| 352 if data['graph_name'] != '_EVENT_' and not data['units_x']: | 371 if data['graph_name'] != '_EVENT_' and not data['units_x']: |
| 353 # Short-running test result. | 372 # Short-running test result. |
| 354 perf_data[key_graph]['value'][key_description] = data['value'] | 373 perf_data[key_graph]['value'][key_description] = data['value'] |
| 355 else: | 374 else: |
| 356 # Long-running test result or event. | 375 # Long-running test result or event. |
| 357 if key_description in perf_data[key_graph]['value']: | 376 if key_description in perf_data[key_graph]['value']: |
| 358 perf_data[key_graph]['value'][key_description] += data['value'] | 377 perf_data[key_graph]['value'][key_description] += data['value'] |
| 359 else: | 378 else: |
| 360 perf_data[key_graph]['value'][key_description] = data['value'] | 379 perf_data[key_graph]['value'][key_description] = data['value'] |
| 361 | 380 |
| 362 # Finally, for each graph-line in |perf_data|, update the associated local | 381 # Finally, for each graph-line in |perf_data|, update the associated local |
| 363 # graph data files if necessary. | 382 # graph data files if necessary. |
| 364 for perf_data_key in perf_data: | 383 for perf_data_key in perf_data: |
| 365 perf_data_dict = perf_data[perf_data_key] | 384 perf_data_dict = perf_data[perf_data_key] |
| 366 | 385 |
| 367 dest_dir = os.path.join(LOCAL_GRAPH_DIR, perf_data_dict['webapp_name']) | 386 dest_dir = os.path.join(LOCAL_GRAPH_DIR, perf_data_dict['webapp_name']) |
| 368 if not os.path.exists(dest_dir): | 387 if not os.path.exists(dest_dir): |
| 369 os.mkdir(dest_dir) # Webapp name directory. | 388 os.mkdir(dest_dir) # Webapp name directory. |
| 370 os.chmod(dest_dir, 0755) | 389 os.chmod(dest_dir, 0755) |
| 371 dest_dir = os.path.join(dest_dir, perf_data_dict['test_name']) | 390 dest_dir = os.path.join(dest_dir, perf_data_dict['test_name']) |
| 372 | 391 |
| 373 SetupBaseGraphDirIfNeeded(perf_data_dict['webapp_name'], | 392 SetupBaseGraphDirIfNeeded(perf_data_dict['webapp_name'], |
| 374 perf_data_dict['test_name'], dest_dir) | 393 perf_data_dict['test_name'], dest_dir) |
| 375 if perf_data_dict['graph_name'] == '_EVENT_': | 394 if perf_data_dict['graph_name'] == '_EVENT_': |
| 376 OutputEventData(revision, perf_data_dict['value'], dest_dir) | 395 OutputEventData(revision, perf_data_dict['value'], dest_dir) |
| 377 else: | 396 else: |
| 378 OutputPerfData(revision, perf_data_dict['graph_name'], | 397 OutputPerfData(revision, perf_data_dict['graph_name'], |
| 379 perf_data_dict['value'], | 398 perf_data_dict['value'], |
| 380 perf_data_dict['units'], perf_data_dict['units_x'], | 399 perf_data_dict['units'], perf_data_dict['units_x'], |
| 381 dest_dir) | 400 dest_dir, |
| 401 perf_data_dict['stack'], perf_data_dict['stack_order']) |
| 382 | 402 |
| 383 return True | 403 return True |
| 384 | 404 |
| 385 | 405 |
| 386 def UpdatePerfDataFiles(): | 406 def UpdatePerfDataFiles(): |
| 387 """Updates the Chrome Endure graph data files with the latest test results. | 407 """Updates the Chrome Endure graph data files with the latest test results. |
| 388 | 408 |
| 389 For each known Chrome Endure slave, we scan its latest test results looking | 409 For each known Chrome Endure slave, we scan its latest test results looking |
| 390 for any new test data. Any new data that is found is then appended to the | 410 for any new test data. Any new data that is found is then appended to the |
| 391 data files used to display the Chrome Endure graphs. | 411 data files used to display the Chrome Endure graphs. |
| (...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 595 if not success: | 615 if not success: |
| 596 logging.error('Failed to update perf data files.') | 616 logging.error('Failed to update perf data files.') |
| 597 sys.exit(0) | 617 sys.exit(0) |
| 598 | 618 |
| 599 GenerateIndexPage() | 619 GenerateIndexPage() |
| 600 logging.debug('All done!') | 620 logging.debug('All done!') |
| 601 | 621 |
| 602 | 622 |
| 603 if __name__ == '__main__': | 623 if __name__ == '__main__': |
| 604 main() | 624 main() |
| OLD | NEW |