Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(40)

Side by Side Diff: Tools/Scripts/webkitpy/thirdparty/webpagereplay/perftracker/runner.py

Issue 18418010: Check in the thirdparty libs needed for webkitpy. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2010 Google Inc. All Rights Reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 description = """
17 This is a script for running automated network tests of chrome.
18
19 There is an optional -e <filename> flag that instead runs an automated
20 web-page-replay test. It runs WPR record mode on the set of URLs specified
21 in the config file, then runs replay mode on the same set of URLs and
22 records any cache misses to <filename>.
23 """
24
25 import sys
26 if sys.version < '2.6':
27 print 'Need Python 2.6 or greater.'
28 sys.exit(1)
29
30 import cookielib
31 import getpass
32 import json
33 import logging
34 import optparse
35 import os
36 import platform
37 import shutil
38 import signal
39 import subprocess
40 import tempfile
41 import time
42 import urllib
43 import urllib2
44 import runner_cfg
45
46
47 # Some constants for our program
48
49 # The location of the Replay script
50 REPLAY_PATH = '../replay.py'
51
52 # The name of the application we're using
53 BENCHMARK_APPLICATION_NAME = 'perftracker'
54
55 # The location of the PerfTracker extension
56 PERFTRACKER_EXTENSION_PATH = './extension'
57
58 # The server_port is the port which runs the webserver to test against.
59 SERVER_PORT = 8000
60
61 # For SPDY testing, where we have both a frontend and backend server,
62 # this is the port to run the backend server.
63 BACKEND_SERVER_PORT = 8001
64
65
66 def DoAppEngineLogin(username, password):
67 """Log into the Google AppEngine app.
68
69 This code credit to: http://dalelane.co.uk/blog/?p=303
70 """
71 target_authenticated_url = runner_cfg.appengine_url
72
73 # We use a cookie to authenticate with Google App Engine by registering a
74 # cookie handler here, this will automatically store the cookie returned
75 # when we use urllib2 to open http://<server>.appspot.com/_ah/login
76 cookiejar = cookielib.LWPCookieJar()
77 opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
78 urllib2.install_opener(opener)
79
80 #
81 # get an AuthToken from Google accounts
82 #
83 try:
84 auth_uri = 'https://www.google.com/accounts/ClientLogin'
85 authreq_data = urllib.urlencode({ 'Email': username,
86 'Passwd': password,
87 'service': 'ah',
88 'source': BENCHMARK_APPLICATION_NAME,
89 'accountType': 'HOSTED_OR_GOOGLE' })
90 auth_req = urllib2.Request(auth_uri, data=authreq_data)
91 auth_resp = urllib2.urlopen(auth_req)
92 auth_resp_body = auth_resp.read()
93 # The auth response includes several fields. The part we're
94 # interested in is the bit after 'Auth='.
95 auth_resp_dict = dict(x.split('=') for x in auth_resp_body.split('\n') if x)
96 authtoken = auth_resp_dict['Auth']
97
98 # Get a cookie:
99 # The call to request a cookie will also automatically redirect us to
100 # the page that we want to go to. The cookie jar will automatically
101 # provide the cookie when we reach the redirected location.
102 serv_args = {}
103 serv_args['continue'] = target_authenticated_url
104 serv_args['auth'] = authtoken
105 full_serv_uri = '%s_ah/login?%s' % (target_authenticated_url,
106 urllib.urlencode(serv_args))
107
108 serv_req = urllib2.Request(full_serv_uri)
109 serv_resp = urllib2.urlopen(serv_req)
110 print 'AppEngineLogin succeeded.'
111 except Exception, e:
112 logging.critical('DoAppEngineLogin failed: %s', e)
113 return None
114 return full_serv_uri
115
116
117 def ClobberTmpDirectory(tmpdir):
118 """Remove a temporary directory."""
119 # Do sanity checking so we don't clobber the wrong thing
120 if tmpdir == '/tmp/' or not tmpdir.startswith('/tmp/'):
121 logging.warn('Directory must start with /tmp/ to clobber: %s', tmpdir)
122 else:
123 try:
124 shutil.rmtree(tmpdir)
125 except os.error:
126 logging.error("Could not delete: %s", tmpdir)
127
128
129 def _XvfbPidFilename(slave_build_name):
130 """Returns the filename to the Xvfb pid file.
131
132 This name is unique for each builder.
133 This is used by the linux builders.
134 """
135 return os.path.join(tempfile.gettempdir(), 'xvfb-%s.pid' % slave_build_name)
136
137
138 def StartVirtualX(slave_build_name, build_dir):
139 """Start a virtual X server and set the DISPLAY environment variable so sub
140 processes will use the virtual X server. Also start icewm. This only works
141 on Linux and assumes that xvfb and icewm are installed.
142
143 Args:
144 slave_build_name: The name of the build that we use for the pid file.
145 E.g., webkit-rel-linux.
146 build_dir: The directory where binaries are produced. If this is non-empty,
147 we try running xdisplaycheck from |build_dir| to verify our X
148 connection.
149 """
150 # We use a pid file to make sure we don't have any xvfb processes running
151 # from a previous test run.
152 StopVirtualX(slave_build_name)
153
154 # Start a virtual X server that we run the tests in. This makes it so we can
155 # run the tests even if we didn't start the tests from an X session.
156 proc = subprocess.Popen(['Xvfb', ':9', '-screen', '0', '1024x768x24', '-ac'],
157 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
158 xvfb_pid_filename = _XvfbPidFilename(slave_build_name)
159 open(xvfb_pid_filename, 'w').write(str(proc.pid))
160 os.environ['DISPLAY'] = ':9'
161
162 # Verify that Xvfb has started by using xdisplaycheck.
163 if len(build_dir) > 0:
164 xdisplaycheck_path = os.path.join(build_dir, 'xdisplaycheck')
165 if os.path.exists(xdisplaycheck_path):
166 logging.debug('Verifying Xvfb has started...')
167 status, output = commands.getstatusoutput(xdisplaycheck_path)
168 if status != 0:
169 logging.debug('Xvfb return code (None if still running): %s',
170 proc.poll())
171 logging.debug('Xvfb stdout and stderr:', proc.communicate())
172 raise Exception(output)
173 logging.debug('...OK')
174 # Some ChromeOS tests need a window manager.
175 subprocess.Popen('icewm', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
176
177
178 def StopVirtualX(slave_build_name):
179 """Try and stop the virtual X server if one was started with StartVirtualX.
180
181 When the X server dies, it takes down the window manager with it.
182 If a virtual x server is not running, this method does nothing.
183 """
184 xvfb_pid_filename = _XvfbPidFilename(slave_build_name)
185 if os.path.exists(xvfb_pid_filename):
186 # If the process doesn't exist, we raise an exception that we can ignore.
187 try:
188 os.kill(int(open(xvfb_pid_filename).read()), signal.SIGKILL)
189 except OSError:
190 pass
191 os.remove(xvfb_pid_filename)
192
193
194 def _svn(cmd):
195 """Returns output of given svn command."""
196 svn = subprocess.Popen(
197 ['svn', '--non-interactive', cmd],
198 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
199 return svn.communicate()[0]
200
201
202 def _get_value_for_key(lines, key):
203 """Given list of |lines| with colon separated key value pairs,
204 return the value of |key|."""
205 for line in lines:
206 parts = line.split(':')
207 if parts[0].strip() == key:
208 return parts[1].strip()
209 return None
210
211
212 def GetCPU():
213 # When /proc/cpuinfo exists it is more reliable than platform.
214 if os.path.exists('/proc/cpuinfo'):
215 with open('/proc/cpuinfo') as f:
216 model_name = _get_value_for_key(f.readlines(), 'model name')
217 if model_name:
218 return model_name
219 return platform.processor()
220
221
222 def GetVersion():
223 svn_info = _svn('info')
224 if svn_info:
225 revision = _get_value_for_key(svn_info.split('\n'), 'Revision')
226 if revision:
227 return revision
228 return 'unknown'
229
230
231 class TestInstance:
232 def __init__(self, network, log_level, log_file, record,
233 diff_unknown_requests, screenshot_dir, cache_miss_file=None,
234 use_deterministic_script=False,
235 use_chrome_deterministic_js=True,
236 use_closest_match=False,
237 use_server_delay=False):
238 self.network = network
239 self.log_level = log_level
240 self.log_file = log_file
241 self.record = record
242 self.proxy_process = None
243 self.spdy_proxy_process = None
244 self.diff_unknown_requests = diff_unknown_requests
245 self.screenshot_dir = screenshot_dir
246 self.cache_miss_file = cache_miss_file
247 self.use_deterministic_script = use_deterministic_script
248 self.use_chrome_deterministic_js = use_chrome_deterministic_js
249 self.use_closest_match = use_closest_match
250 self.use_server_delay = use_server_delay
251
252 def GenerateConfigFile(self, notes=''):
253 # The PerfTracker extension requires this name in order to kick off.
254 ext_suffix = 'startbenchmark.html'
255 self.filename = tempfile.mktemp(suffix=ext_suffix, prefix='')
256 benchmark = {
257 'user': getpass.getuser(),
258 'notes': str(notes),
259 'cmdline': ' '.join(sys.argv),
260 'server_url': runner_cfg.appengine_url,
261 'server_login': options.login_url,
262 'client_hostname': platform.node(),
263 'harness_version': GetVersion(),
264 'cpu': GetCPU(),
265 'iterations': str(runner_cfg.iterations),
266 'download_bandwidth_kbps': str(self.network['bandwidth_kbps']['down']),
267 'upload_bandwidth_kbps': str(self.network['bandwidth_kbps']['up']),
268 'round_trip_time_ms': str(self.network['round_trip_time_ms']),
269 'packet_loss_rate': str(self.network['packet_loss_percent']),
270 'protocol': self.network['protocol'],
271 'urls': runner_cfg.urls,
272 'record': self.record,
273 'screenshot_dir': self.screenshot_dir,
274 }
275 with open(self.filename, 'w+') as f:
276 f.write("""
277 <body>
278 <h3>Running benchmark...</h3>
279 <script>
280 // Sometimes the extension doesn't load.
281 // Set a timer, if the extension didn't write an ACK into the status
282 // box, then reload this page.
283 setTimeout(function() {
284 var status = document.getElementById("status");
285 if (status.textContent != "ACK") {
286 console.log("Benchmark stuck? Reloading.");
287 window.location.reload(true);
288 }
289 }, 30000);
290 </script>
291 <textarea id=json style="width:100%%;height:80%%;">
292 %s
293 </textarea>
294 <textarea id=status></textarea>
295 </body>
296 """ % json.dumps(benchmark, indent=2))
297
298
299 def StartProxy(self):
300 # To run SPDY, we use the SPDY-to-HTTP gateway. The gateway will answer
301 # on port |SERVER_PORT|, contacting the backend server (the replay server)
302 # which will run on |BACKEND_SERVER_PORT|.
303 port = SERVER_PORT
304 init_cwnd = 10
305 protocol = self.network['protocol']
306 if 'spdy' in protocol:
307 port = BACKEND_SERVER_PORT
308 init_cwnd = 32
309
310 if protocol == 'http-base':
311 init_cwnd = 3 # See RFC3390
312
313 cmdline = [
314 REPLAY_PATH,
315 '--no-dns_forwarding',
316 '--port', str(port),
317 '--shaping_port', str(SERVER_PORT),
318 '--log_level', self.log_level,
319 '--init_cwnd', str(init_cwnd),
320 ]
321 if self.cache_miss_file:
322 cmdline += ['-e', self.cache_miss_file]
323 if self.use_closest_match:
324 cmdline += ['--use_closest_match']
325 if self.use_server_delay:
326 cmdline += ['--use_server_delay']
327 if not self.use_deterministic_script:
328 cmdline += ['--inject_scripts=""']
329 if self.log_file:
330 cmdline += ['--log_file', self.log_file]
331 if self.network['bandwidth_kbps']['down']:
332 cmdline += ['-d', str(self.network['bandwidth_kbps']['down']) + 'Kbit/s']
333 if self.network['bandwidth_kbps']['up']:
334 cmdline += ['-u', str(self.network['bandwidth_kbps']['up']) + 'Kbit/s']
335 if self.network['round_trip_time_ms']:
336 cmdline += ['-m', str(self.network['round_trip_time_ms'])]
337 if self.network['packet_loss_percent']:
338 cmdline += ['-p', str(self.network['packet_loss_percent'] / 100.0)]
339 if not self.diff_unknown_requests:
340 cmdline.append('--no-diff_unknown_requests')
341 if self.screenshot_dir:
342 cmdline += ['-I', self.screenshot_dir]
343 if self.record:
344 cmdline.append('-r')
345 cmdline.append(runner_cfg.replay_data_archive)
346
347 logging.info('Starting Web-Page-Replay: %s', ' '.join(cmdline))
348 self.proxy_process = subprocess.Popen(cmdline)
349
350 def StopProxy(self):
351 if self.proxy_process:
352 logging.debug('Stopping Web-Page-Replay')
353 # Use a SIGINT here so that it can do graceful cleanup.
354 # Otherwise we'll leave subprocesses hanging.
355 self.proxy_process.send_signal(signal.SIGINT)
356 self.proxy_process.wait()
357
358 def StartSpdyProxy(self):
359 cert_file = ""
360 key_file = ""
361 protocol = self.network['protocol']
362 if protocol == "spdy":
363 cert_file = runner_cfg.ssl['certfile']
364 key_file = runner_cfg.ssl['keyfile']
365
366 proxy_cfg = "--proxy1=%s" % ",".join((
367 "", # listen_host
368 str(SERVER_PORT), # listen_port
369 cert_file, # cert_file
370 key_file, # key_file
371 "127.0.0.1", # http_host
372 str(BACKEND_SERVER_PORT), # http_port
373 "", # https_host
374 "", # https_port
375 "0", # spdy_only
376 ))
377
378 # TODO(mbelshe): Remove the logfile when done with debugging the flipserver.
379 logfile = "/tmp/flipserver.log"
380 try:
381 os.remove(logfile)
382 except OSError:
383 pass
384 cmdline = [
385 runner_cfg.spdy_proxy_server_path,
386 proxy_cfg,
387 "--force_spdy",
388 "--v=2",
389 "--logfile=" + logfile
390 ]
391 logging.debug('Starting SPDY proxy: %s', ' '.join(cmdline))
392 self.spdy_proxy_process = subprocess.Popen(cmdline,
393 stdout=subprocess.PIPE,
394 stderr=subprocess.STDOUT)
395
396 def StopSpdyProxy(self):
397 if self.spdy_proxy_process:
398 logging.debug('Stopping SPDY Proxy')
399 try:
400 # For the SPDY server we kill it, because it has no dependencies.
401 self.spdy_proxy_process.kill()
402 except OSError:
403 pass
404 self.spdy_proxy_process.wait()
405
406 def RunChrome(self, chrome_cmdline):
407 start_file_url = 'file://' + self.filename
408
409 profile_dir = tempfile.mkdtemp(prefix='chrome.profile.')
410
411 use_virtualx = False
412 switch_away_from_root = None
413 if platform.system() == 'Linux':
414 use_virtualx = True
415 if os.getuid() == 0:
416 user_uid = int(os.getenv('SUDO_UID'))
417 switch_away_from_root = lambda: os.setuid(user_uid)
418 os.chown(profile_dir, user_uid, -1)
419
420 try:
421 if use_virtualx:
422 StartVirtualX(platform.node(), '/tmp')
423
424 server_host_port_pair = '127.0.0.1:%s' % SERVER_PORT
425 cmdline = [
426 runner_cfg.chrome_path,
427 '--activate-on-launch',
428 '--disable-background-networking',
429 # Stop the translate bar from appearing at the top of the page. When
430 # it's there, the screenshots are shorter than they should be.
431 '--disable-translate',
432
433 '--enable-benchmarking',
434 '--enable-logging',
435 '--enable-experimental-extension-apis',
436 '--host-resolver-rules=MAP * %s,EXCLUDE %s' % (
437 server_host_port_pair, runner_cfg.appengine_host),
438 '--ignore-certificate-errors',
439 '--load-extension=' + PERFTRACKER_EXTENSION_PATH,
440 '--log-level=0',
441 '--no-first-run',
442 '--no-proxy-server',
443 '--start-maximized',
444 '--user-data-dir=' + profile_dir,
445 ]
446 if self.use_chrome_deterministic_js:
447 cmdline += ['--no-js-randomness']
448 if self.cache_miss_file:
449 cmdline += ['--no-sandbox']
450
451 spdy_mode = None
452 if self.network['protocol'] == 'spdy':
453 spdy_mode = 'ssl'
454 if self.network['protocol'] == 'spdy-nossl':
455 spdy_mode = 'no-ssl'
456 if spdy_mode:
457 cmdline.append(
458 '--use-spdy=%s,exclude=%s' % (spdy_mode, runner_cfg.appengine_url))
459 if chrome_cmdline:
460 cmdline.extend(chrome_cmdline.split(' '))
461 cmdline.append(start_file_url)
462
463 logging.info('Starting Chrome: %s', ' '.join(cmdline))
464 chrome = subprocess.Popen(cmdline, preexec_fn=switch_away_from_root)
465 returncode = chrome.wait()
466 if returncode:
467 logging.error('Chrome returned status code %d. It may have crashed.',
468 returncode)
469 finally:
470 ClobberTmpDirectory(profile_dir)
471 if use_virtualx:
472 StopVirtualX(platform.node())
473
474 def RunTest(self, notes, chrome_cmdline):
475 try:
476 self.GenerateConfigFile(notes)
477 self.StartProxy()
478 protocol = self.network['protocol']
479 if 'spdy' in protocol:
480 self.StartSpdyProxy()
481 self.RunChrome(chrome_cmdline)
482 finally:
483 logging.debug('Cleaning up test')
484 self.StopProxy()
485 self.StopSpdyProxy()
486 self.Cleanup()
487
488 def Cleanup(self):
489 if os.path.exists(self.filename):
490 os.remove(self.filename)
491
492
493 def ConfigureLogging(log_level_name, log_file_name):
494 """Configure logging level and format.
495
496 Args:
497 log_level_name: 'debug', 'info', 'warning', 'error', or 'critical'.
498 log_file_name: a file name
499 """
500 if logging.root.handlers:
501 logging.critical('A logging method (e.g. "logging.warn(...)")'
502 ' was called before logging was configured.')
503 log_level = getattr(logging, log_level_name.upper())
504 log_format = '%(asctime)s %(levelname)s %(message)s'
505 logging.basicConfig(level=log_level, format=log_format)
506 if log_file_name:
507 fh = logging.FileHandler(log_file_name)
508 fh.setLevel(log_level)
509 fh.setFormatter(logging.Formatter(log_format))
510 logging.getLogger().addHandler(fh)
511
512
513 def main(options, cache_miss_file):
514 # When in record mode, override most of the configuration.
515 if options.record:
516 runner_cfg.replay_data_archive = options.record
517 runner_cfg.iterations = 1
518 runner_cfg.networks = [
519 {
520 'bandwidth_kbps': {
521 'up': 0,
522 'down': 0
523 },
524 'round_trip_time_ms': 0,
525 'packet_loss_percent': 0,
526 'protocol': 'http',
527 }
528 ]
529
530 while True:
531 for network in runner_cfg.networks:
532 logging.debug("Running network configuration: %s", network)
533 test = TestInstance(
534 network, options.log_level, options.log_file, options.record,
535 options.diff_unknown_requests, options.screenshot_dir,
536 cache_miss_file, options.use_deterministic_script,
537 options.use_chrome_deterministic_js, options.use_closest_match,
538 options.use_server_delay)
539 test.RunTest(options.notes, options.chrome_cmdline)
540 if not options.infinite or options.record:
541 break
542 if runner_cfg.inter_run_cleanup_script:
543 logging.debug("Running inter-run-cleanup-script")
544 subprocess.call([runner_cfg.inter_run_cleanup_script], shell=True)
545
546
547 if __name__ == '__main__':
548 log_levels = ('debug', 'info', 'warning', 'error', 'critical')
549
550 class PlainHelpFormatter(optparse.IndentedHelpFormatter):
551 def format_description(self, description):
552 if description:
553 return description + '\n'
554 else:
555 return ''
556
557 option_parser = optparse.OptionParser(
558 usage='%prog -u user',
559 formatter=PlainHelpFormatter(),
560 description=description,
561 epilog='')
562
563 option_parser.add_option('-l', '--log_level', default='error',
564 action='store',
565 type='choice',
566 choices=log_levels,
567 help='Minimum verbosity level to log')
568 option_parser.add_option('-f', '--log_file', default=None,
569 action='store',
570 type='string',
571 help='Log file to use in addition to writting logs to stderr.')
572 option_parser.add_option('-r', '--record', default=False,
573 action='store_true',
574 dest='do_record',
575 help=('Record URLs to file specified by runner_cfg.'))
576 option_parser.add_option('-i', '--infinite', default=False,
577 action='store_true',
578 help='Loop infinitely, repeating the test.')
579 option_parser.add_option('-o', '--chrome_cmdline', default=None,
580 action='store',
581 type='string',
582 help='Command line options to pass to chrome.')
583 option_parser.add_option('-n', '--notes', default='',
584 action='store',
585 type='string',
586 help='Notes to record with this test run.')
587 option_parser.add_option('-u', '--user', default=None,
588 action='store',
589 type='string',
590 help='Username for logging into appengine.')
591 option_parser.add_option('-D', '--no-diff_unknown_requests', default=True,
592 action='store_false',
593 dest='diff_unknown_requests',
594 help='During replay, do not show a diff of any unknown requests against '
595 'their nearest match in the archive.')
596 option_parser.add_option('-I', '--screenshot_dir', default=None,
597 action='store',
598 type='string',
599 help='Save PNG images of the loaded page in the given directory.')
600 option_parser.add_option('-d', '--deterministic_script', default=False,
601 action='store_true',
602 dest='use_deterministic_script',
603 help='During a record, inject JavaScript to make sources of '
604 'entropy such as Date() and Math.random() deterministic. CAUTION: '
605 'Without this option many web pages will not replay properly.')
606 option_parser.add_option('-j', '--no_chrome_deterministic_js', default=True,
607 action='store_false',
608 dest='use_chrome_deterministic_js',
609 help='Enable Chrome\'s deterministic implementations of javascript.'
610 'This makes sources of entropy such as Date() and Math.random()'
611 'deterministic.')
612 option_parser.add_option('-e', '--cache_miss_file', default=None,
613 action='store',
614 dest='cache_miss_file',
615 type='string',
616 help='Archive file to record cache misses in replay mode.')
617 option_parser.add_option('-C', '--use_closest_match', default=False,
618 action='store_true',
619 dest='use_closest_match',
620 help='During replay, if a request is not found, serve the closest match'
621 'in the archive instead of giving a 404.')
622 option_parser.add_option('-U', '--use_server_delay', default=False,
623 action='store_true',
624 dest='use_server_delay',
625 help='During replay, simulate server delay by delaying response time to'
626 'requests.')
627
628
629 options, args = option_parser.parse_args()
630
631 ConfigureLogging(options.log_level, options.log_file)
632
633 # Collect login credentials and verify
634 user = options.user or runner_cfg.appengine_user
635 if user:
636 password = (runner_cfg.appengine_password
637 or getpass.getpass(user + ' password: '))
638 options.login_url = DoAppEngineLogin(user, password)
639 if not options.login_url:
640 exit(-1)
641 elif runner_cfg.appengine_host != 'localhost':
642 option_parser.error('Must specify an appengine user for login')
643 exit(-1)
644 else:
645 options.login_url = ''
646
647 # run the recording round, if specified
648 if options.do_record and options.cache_miss_file:
649 logging.debug("Running on record mode")
650 options.record = runner_cfg.replay_data_archive
651 main(options, options.cache_miss_file)
652 options.do_record = False
653
654 options.record = None
655 # run the replay round
656 logging.debug("Running on replay mode")
657 sys.exit(main(options, options.cache_miss_file))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698