| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 | |
| 3 # Copyright 2016 The Chromium Authors. All rights reserved. | |
| 4 # Use of this source code is governed by a BSD-style license that can be | |
| 5 # found in the LICENSE file. | |
| 6 | |
| 7 import argparse | |
| 8 import multiprocessing | |
| 9 import os | |
| 10 import pwd | |
| 11 import subprocess | |
| 12 | |
| 13 from twisted.internet import reactor | |
| 14 | |
| 15 from packet_queue import monitoring | |
| 16 from packet_queue import nfqueue | |
| 17 from packet_queue import simulation | |
| 18 | |
| 19 | |
| 20 ENGINE_PORT = 25467 | |
| 21 MTU = 2048 | |
| 22 | |
| 23 | |
| 24 def parse_args(): | |
| 25 parser = argparse.ArgumentParser() | |
| 26 | |
| 27 parser.add_argument( | |
| 28 '--target', type=str, required=True, | |
| 29 help='path of the test binary to run') | |
| 30 parser.add_argument( | |
| 31 '--gtest_filter', type=str, default='EngineBrowserTest.LoadUrl', | |
| 32 help='which specific test to run') | |
| 33 parser.add_argument( | |
| 34 '-w', '--bandwidth', type=int, default=-1, | |
| 35 help='bandwidth cap in bytes/second, unlimited by default') | |
| 36 parser.add_argument( | |
| 37 '-b', '--buffer', type=int, default=-1, | |
| 38 help='buffer size in bytes, unlimited by default') | |
| 39 parser.add_argument( | |
| 40 '-d', '--delay', type=float, default=0.0, | |
| 41 help='one-way packet delay in seconds') | |
| 42 parser.add_argument( | |
| 43 '-l', '--loss', type=float, default=0.0, | |
| 44 help='probability of random packet loss') | |
| 45 | |
| 46 return parser.parse_args() | |
| 47 | |
| 48 | |
| 49 def run_packet_queue(params, ready_event): | |
| 50 """Runs the packet queue with parameters from command-line args. | |
| 51 | |
| 52 This function runs the Twisted reactor, which blocks until the process is | |
| 53 interrupted. | |
| 54 """ | |
| 55 event_log = monitoring.EventLog() | |
| 56 pipes = simulation.PipePair(params, event_log) | |
| 57 | |
| 58 nfqueue.configure('tcp', ENGINE_PORT, pipes, 'lo') | |
| 59 | |
| 60 reactor.callWhenRunning(ready_event.set) | |
| 61 reactor.addSystemEventTrigger( | |
| 62 'before', 'shutdown', report_stats, event_log) | |
| 63 reactor.run() | |
| 64 | |
| 65 | |
| 66 def report_stats(event_log): | |
| 67 """Prints accumulated network stats to stdout.""" | |
| 68 dropped = 0 | |
| 69 delivered = 0 | |
| 70 | |
| 71 for event in event_log.get_pending(): | |
| 72 if event['type'] == 'drop': | |
| 73 dropped += event['value'] | |
| 74 elif event['type'] == 'deliver': | |
| 75 delivered += event['value'] | |
| 76 | |
| 77 print '%i bytes delivered' % delivered | |
| 78 print '%i bytes dropped' % dropped | |
| 79 | |
| 80 | |
| 81 def set_chrome_test_user(): | |
| 82 """If the SUDO_USER environment variable exists, sets the user to that. | |
| 83 | |
| 84 This is required because Chrome browser tests won't run as root. | |
| 85 | |
| 86 If SUDO_USER isn't present, we might be running as a non-root user with | |
| 87 the CAP_NET_ADMIN capability, so the user won't change. | |
| 88 """ | |
| 89 sudo_user = os.getenv('SUDO_USER') | |
| 90 if sudo_user: | |
| 91 uid = pwd.getpwnam(sudo_user).pw_uid | |
| 92 os.setuid(uid) | |
| 93 | |
| 94 | |
| 95 def main(): | |
| 96 """Runs a Blimp test target with network impairment. | |
| 97 | |
| 98 This works by forking to run the packet queue, and then running the test | |
| 99 binary in a subprocess when the packet queue is ready. | |
| 100 """ | |
| 101 args = parse_args() | |
| 102 | |
| 103 simulation_params = { | |
| 104 'bandwidth': args.bandwidth, | |
| 105 'buffer': args.buffer, | |
| 106 'delay': args.delay, | |
| 107 'loss': args.loss, | |
| 108 } | |
| 109 | |
| 110 ready_event = multiprocessing.Event() | |
| 111 packet_queue = multiprocessing.Process( | |
| 112 target=run_packet_queue, args=(simulation_params, ready_event)) | |
| 113 | |
| 114 if subprocess.call(['ip', 'link', 'set', 'dev', 'lo', 'mtu', str(MTU)]): | |
| 115 raise OSError('Failed to set MTU') | |
| 116 | |
| 117 packet_queue.start() | |
| 118 ready_event.wait(timeout=3) | |
| 119 | |
| 120 subprocess.call([ | |
| 121 args.target, | |
| 122 '--disable-setuid-sandbox', | |
| 123 '--single_process', | |
| 124 '--engine-port=%i' % ENGINE_PORT, | |
| 125 '--gtest_filter=%s' % args.gtest_filter, | |
| 126 ], preexec_fn=set_chrome_test_user) | |
| 127 | |
| 128 packet_queue.terminate() | |
| 129 | |
| 130 | |
| 131 if __name__ == '__main__': | |
| 132 main() | |
| OLD | NEW |