| 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 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 211 new_events[description] = value_list | 211 new_events[description] = value_list |
| 212 | 212 |
| 213 new_line = { | 213 new_line = { |
| 214 'rev': revision, | 214 'rev': revision, |
| 215 'events': new_events | 215 'events': new_events |
| 216 } | 216 } |
| 217 | 217 |
| 218 WriteToDataFile(new_line, existing_lines, revision, data_file) | 218 WriteToDataFile(new_line, existing_lines, revision, data_file) |
| 219 | 219 |
| 220 | 220 |
| 221 def UpdatePerfDataFromFetchedContent(revision, content, webapp_name, test_name): |
| 222 """Update perf data from fetched stdio data. |
| 223 |
| 224 Args: |
| 225 revision: The string revision number associated with the new perf entry. |
| 226 content: Fetched stdio data. |
| 227 webapp_name: A name of the webapp. |
| 228 test_name: A name of the test. |
| 229 """ |
| 230 perf_data_raw = [] |
| 231 |
| 232 def AppendRawPerfData(graph_name, description, value, units, units_x, |
| 233 webapp_name, test_name, is_stacked=False): |
| 234 perf_data_raw.append({ |
| 235 'graph_name': graph_name, |
| 236 'description': description, |
| 237 'value': value, |
| 238 'units': units, |
| 239 'units_x': units_x, |
| 240 'webapp_name': webapp_name, |
| 241 'test_name': test_name, |
| 242 'stack': is_stacked, |
| 243 }) |
| 244 |
| 245 # First scan for short-running perf test results. |
| 246 for match in re.findall( |
| 247 r'RESULT ([^:]+): ([^=]+)= ([-\d\.]+) (\S+)', content): |
| 248 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], None, |
| 249 webapp_name, webapp_name) |
| 250 |
| 251 # Next scan for long-running perf test results. |
| 252 for match in re.findall( |
| 253 r'RESULT ([^:]+): ([^=]+)= (\[[^\]]+\]) (\S+) (\S+)', content): |
| 254 # TODO(dmikurube): Change the condition to use stacked graph when we |
| 255 # determine how to specify it. |
| 256 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], match[4], |
| 257 webapp_name, test_name, match[0].endswith('-DMP')) |
| 258 |
| 259 # Next scan for events in the test results. |
| 260 for match in re.findall( |
| 261 r'RESULT _EVENT_: ([^=]+)= (\[[^\]]+\])', content): |
| 262 AppendRawPerfData('_EVENT_', match[0], eval(match[1]), None, None, |
| 263 webapp_name, test_name) |
| 264 |
| 265 # For each graph_name/description pair that refers to a long-running test |
| 266 # result or an event, concatenate all the results together (assume results |
| 267 # in the input file are in the correct order). For short-running test |
| 268 # results, keep just one if more than one is specified. |
| 269 perf_data = {} # Maps a graph-line key to a perf data dictionary. |
| 270 for data in perf_data_raw: |
| 271 key_graph = data['graph_name'] |
| 272 key_description = data['description'] |
| 273 if not key_graph in perf_data: |
| 274 perf_data[key_graph] = { |
| 275 'graph_name': data['graph_name'], |
| 276 'value': {}, |
| 277 'units': data['units'], |
| 278 'units_x': data['units_x'], |
| 279 'webapp_name': data['webapp_name'], |
| 280 'test_name': data['test_name'], |
| 281 } |
| 282 perf_data[key_graph]['stack'] = data['stack'] |
| 283 if 'stack_order' not in perf_data[key_graph]: |
| 284 perf_data[key_graph]['stack_order'] = [] |
| 285 if (data['stack'] and |
| 286 data['description'] not in perf_data[key_graph]['stack_order']): |
| 287 perf_data[key_graph]['stack_order'].append(data['description']) |
| 288 |
| 289 if data['graph_name'] != '_EVENT_' and not data['units_x']: |
| 290 # Short-running test result. |
| 291 perf_data[key_graph]['value'][key_description] = data['value'] |
| 292 else: |
| 293 # Long-running test result or event. |
| 294 if key_description in perf_data[key_graph]['value']: |
| 295 perf_data[key_graph]['value'][key_description] += data['value'] |
| 296 else: |
| 297 perf_data[key_graph]['value'][key_description] = data['value'] |
| 298 |
| 299 # Finally, for each graph-line in |perf_data|, update the associated local |
| 300 # graph data files if necessary. |
| 301 for perf_data_key in perf_data: |
| 302 perf_data_dict = perf_data[perf_data_key] |
| 303 |
| 304 dest_dir = os.path.join(LOCAL_GRAPH_DIR, perf_data_dict['webapp_name']) |
| 305 if not os.path.exists(dest_dir): |
| 306 os.mkdir(dest_dir) # Webapp name directory. |
| 307 os.chmod(dest_dir, 0755) |
| 308 dest_dir = os.path.join(dest_dir, perf_data_dict['test_name']) |
| 309 |
| 310 SetupBaseGraphDirIfNeeded(perf_data_dict['webapp_name'], |
| 311 perf_data_dict['test_name'], dest_dir) |
| 312 if perf_data_dict['graph_name'] == '_EVENT_': |
| 313 OutputEventData(revision, perf_data_dict['value'], dest_dir) |
| 314 else: |
| 315 OutputPerfData(revision, perf_data_dict['graph_name'], |
| 316 perf_data_dict['value'], |
| 317 perf_data_dict['units'], perf_data_dict['units_x'], |
| 318 dest_dir, |
| 319 perf_data_dict['stack'], perf_data_dict['stack_order']) |
| 320 |
| 321 |
| 221 def UpdatePerfDataForSlaveAndBuild(slave_info, build_num): | 322 def UpdatePerfDataForSlaveAndBuild(slave_info, build_num): |
| 222 """Process updated perf data for a particular slave and build number. | 323 """Process updated perf data for a particular slave and build number. |
| 223 | 324 |
| 224 Args: | 325 Args: |
| 225 slave_info: A dictionary containing information about the slave to process. | 326 slave_info: A dictionary containing information about the slave to process. |
| 226 build_num: The particular build number on the slave to process. | 327 build_num: The particular build number on the slave to process. |
| 227 | 328 |
| 228 Returns: | 329 Returns: |
| 229 True if the perf data for the given slave/build is updated properly, or | 330 True if the perf data for the given slave/build is updated properly, or |
| 230 False if any critical error occurred. | 331 False if any critical error occurred. |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 298 if time.time() - start_time >= 30: # Read for at most 30 seconds. | 399 if time.time() - start_time >= 30: # Read for at most 30 seconds. |
| 299 break | 400 break |
| 300 except (urllib2.URLError, socket.error), e: | 401 except (urllib2.URLError, socket.error), e: |
| 301 # Issue warning but continue to the next stdio link. | 402 # Issue warning but continue to the next stdio link. |
| 302 logging.warning('Error reading test stdio URL "%s": %s', stdio_url, | 403 logging.warning('Error reading test stdio URL "%s": %s', stdio_url, |
| 303 str(e)) | 404 str(e)) |
| 304 finally: | 405 finally: |
| 305 if fp: | 406 if fp: |
| 306 fp.close() | 407 fp.close() |
| 307 | 408 |
| 308 perf_data_raw = [] | 409 UpdatePerfDataFromFetchedContent(revision, url_contents, |
| 309 | 410 stdio_url_data['webapp_name'], |
| 310 def AppendRawPerfData(graph_name, description, value, units, units_x, | 411 stdio_url_data['test_name']) |
| 311 webapp_name, test_name, is_stacked=False): | |
| 312 perf_data_raw.append({ | |
| 313 'graph_name': graph_name, | |
| 314 'description': description, | |
| 315 'value': value, | |
| 316 'units': units, | |
| 317 'units_x': units_x, | |
| 318 'webapp_name': webapp_name, | |
| 319 'test_name': test_name, | |
| 320 'stack': is_stacked, | |
| 321 }) | |
| 322 | |
| 323 # First scan for short-running perf test results. | |
| 324 for match in re.findall( | |
| 325 r'RESULT ([^:]+): ([^=]+)= ([-\d\.]+) (\S+)', url_contents): | |
| 326 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], None, | |
| 327 stdio_url_data['webapp_name'], | |
| 328 stdio_url_data['webapp_name']) | |
| 329 | |
| 330 # Next scan for long-running perf test results. | |
| 331 for match in re.findall( | |
| 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. | |
| 335 AppendRawPerfData(match[0], match[1], eval(match[2]), match[3], match[4], | |
| 336 stdio_url_data['webapp_name'], | |
| 337 stdio_url_data['test_name'], | |
| 338 match[0].endswith('-DMP')) | |
| 339 | |
| 340 # Next scan for events in the test results. | |
| 341 for match in re.findall( | |
| 342 r'RESULT _EVENT_: ([^=]+)= (\[[^\]]+\])', url_contents): | |
| 343 AppendRawPerfData('_EVENT_', match[0], eval(match[1]), None, None, | |
| 344 stdio_url_data['webapp_name'], | |
| 345 stdio_url_data['test_name']) | |
| 346 | |
| 347 # For each graph_name/description pair that refers to a long-running test | |
| 348 # result or an event, concatenate all the results together (assume results | |
| 349 # in the input file are in the correct order). For short-running test | |
| 350 # results, keep just one if more than one is specified. | |
| 351 perf_data = {} # Maps a graph-line key to a perf data dictionary. | |
| 352 for data in perf_data_raw: | |
| 353 key_graph = data['graph_name'] | |
| 354 key_description = data['description'] | |
| 355 if not key_graph in perf_data: | |
| 356 perf_data[key_graph] = { | |
| 357 'graph_name': data['graph_name'], | |
| 358 'value': {}, | |
| 359 'units': data['units'], | |
| 360 'units_x': data['units_x'], | |
| 361 'webapp_name': data['webapp_name'], | |
| 362 'test_name': data['test_name'], | |
| 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 | |
| 371 if data['graph_name'] != '_EVENT_' and not data['units_x']: | |
| 372 # Short-running test result. | |
| 373 perf_data[key_graph]['value'][key_description] = data['value'] | |
| 374 else: | |
| 375 # Long-running test result or event. | |
| 376 if key_description in perf_data[key_graph]['value']: | |
| 377 perf_data[key_graph]['value'][key_description] += data['value'] | |
| 378 else: | |
| 379 perf_data[key_graph]['value'][key_description] = data['value'] | |
| 380 | |
| 381 # Finally, for each graph-line in |perf_data|, update the associated local | |
| 382 # graph data files if necessary. | |
| 383 for perf_data_key in perf_data: | |
| 384 perf_data_dict = perf_data[perf_data_key] | |
| 385 | |
| 386 dest_dir = os.path.join(LOCAL_GRAPH_DIR, perf_data_dict['webapp_name']) | |
| 387 if not os.path.exists(dest_dir): | |
| 388 os.mkdir(dest_dir) # Webapp name directory. | |
| 389 os.chmod(dest_dir, 0755) | |
| 390 dest_dir = os.path.join(dest_dir, perf_data_dict['test_name']) | |
| 391 | |
| 392 SetupBaseGraphDirIfNeeded(perf_data_dict['webapp_name'], | |
| 393 perf_data_dict['test_name'], dest_dir) | |
| 394 if perf_data_dict['graph_name'] == '_EVENT_': | |
| 395 OutputEventData(revision, perf_data_dict['value'], dest_dir) | |
| 396 else: | |
| 397 OutputPerfData(revision, perf_data_dict['graph_name'], | |
| 398 perf_data_dict['value'], | |
| 399 perf_data_dict['units'], perf_data_dict['units_x'], | |
| 400 dest_dir, | |
| 401 perf_data_dict['stack'], perf_data_dict['stack_order']) | |
| 402 | 412 |
| 403 return True | 413 return True |
| 404 | 414 |
| 405 | 415 |
| 406 def UpdatePerfDataFiles(): | 416 def UpdatePerfDataFiles(): |
| 407 """Updates the Chrome Endure graph data files with the latest test results. | 417 """Updates the Chrome Endure graph data files with the latest test results. |
| 408 | 418 |
| 409 For each known Chrome Endure slave, we scan its latest test results looking | 419 For each known Chrome Endure slave, we scan its latest test results looking |
| 410 for any new test data. Any new data that is found is then appended to the | 420 for any new test data. Any new data that is found is then appended to the |
| 411 data files used to display the Chrome Endure graphs. | 421 data files used to display the Chrome Endure graphs. |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 598 with open(index_file, 'w') as f: | 608 with open(index_file, 'w') as f: |
| 599 f.write(page) | 609 f.write(page) |
| 600 os.chmod(index_file, 0755) | 610 os.chmod(index_file, 0755) |
| 601 | 611 |
| 602 | 612 |
| 603 def main(): | 613 def main(): |
| 604 parser = optparse.OptionParser() | 614 parser = optparse.OptionParser() |
| 605 parser.add_option( | 615 parser.add_option( |
| 606 '-v', '--verbose', action='store_true', default=False, | 616 '-v', '--verbose', action='store_true', default=False, |
| 607 help='Use verbose logging.') | 617 help='Use verbose logging.') |
| 618 parser.add_option( |
| 619 '-s', '--stdin', action='store_true', default=False, |
| 620 help='Input from stdin instead of slaves for testing this script.') |
| 608 options, _ = parser.parse_args(sys.argv) | 621 options, _ = parser.parse_args(sys.argv) |
| 609 | 622 |
| 610 logging_level = logging.DEBUG if options.verbose else logging.INFO | 623 logging_level = logging.DEBUG if options.verbose else logging.INFO |
| 611 logging.basicConfig(level=logging_level, | 624 logging.basicConfig(level=logging_level, |
| 612 format='[%(asctime)s] %(levelname)s: %(message)s') | 625 format='[%(asctime)s] %(levelname)s: %(message)s') |
| 613 | 626 |
| 614 success = UpdatePerfDataFiles() | 627 if options.stdin: |
| 615 if not success: | 628 content = sys.stdin.read() |
| 616 logging.error('Failed to update perf data files.') | 629 UpdatePerfDataFromFetchedContent('12345', content, 'webapp', 'test') |
| 617 sys.exit(0) | 630 else: |
| 631 success = UpdatePerfDataFiles() |
| 632 if not success: |
| 633 logging.error('Failed to update perf data files.') |
| 634 sys.exit(0) |
| 618 | 635 |
| 619 GenerateIndexPage() | 636 GenerateIndexPage() |
| 620 logging.debug('All done!') | 637 logging.debug('All done!') |
| 621 | 638 |
| 622 | 639 |
| 623 if __name__ == '__main__': | 640 if __name__ == '__main__': |
| 624 main() | 641 main() |
| OLD | NEW |