Skip to content

Commit 4a35bd9

Browse files
committed
initial commit
0 parents  commit 4a35bd9

25 files changed

+2606
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*.pyc
2+
/client
3+
/server
4+
/sha1_bench
5+
/NPtcp
6+
*~

LICENSE

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
IX Open Source License.
3+
4+
Copyright 2013-16 Board of Trustees of Stanford University
5+
Copyright 2013-16 Ecole Polytechnique Federale Lausanne (EPFL)
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
THE SOFTWARE.

Makefile

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2013-16 Board of Trustees of Stanford University
2+
# Copyright 2013-16 Ecole Polytechnique Federale Lausanne (EPFL)
3+
#
4+
# Permission is hereby granted, free of charge, to any person obtaining a copy
5+
# of this software and associated documentation files (the "Software"), to deal
6+
# in the Software without restriction, including without limitation the rights
7+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
# copies of the Software, and to permit persons to whom the Software is
9+
# furnished to do so, subject to the following conditions:
10+
#
11+
# The above copyright notice and this permission notice shall be included in
12+
# all copies or substantial portions of the Software.
13+
#
14+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
# THE SOFTWARE.
21+
22+
BINS=client server sha1_bench
23+
24+
all: $(BINS)
25+
26+
client: client.c
27+
gcc -O2 -Wall -g client.c -o client -pthread -levent
28+
29+
server: server.c
30+
gcc -O2 -Wall -g server.c -o server -pthread -levent
31+
32+
sha1_bench: sha1_bench.c
33+
gcc -O2 -Wall -g sha1_bench.c -o sha1_bench -pthread -lcrypto
34+
35+
clean:
36+
rm -f $(BINS) *.pyc

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Scripts to run the benchmarks and plot the graphs of the OSDI 2014 [https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-belay.pdf] and SOCC 2015 [http://dl.acm.org/citation.cfm?id=2806848] publications.

bench_common.py

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
import Queue
2+
import contextlib
3+
import os.path
4+
import shlex
5+
import socket
6+
import sys
7+
import threading
8+
import time
9+
import traceback
10+
import getpass
11+
12+
CLIENT_COMM_TIME_LIMIT = 0.005
13+
WARMUP = 0
14+
15+
RESET = '\x1b[0m'
16+
BOLD = '\x1b[1m'
17+
18+
def deploy(shell, files):
19+
with shell._connect_sftp() as sftp:
20+
for file in files:
21+
targetdir='/tmp/'+getpass.getuser()
22+
dest = os.path.join(targetdir, os.path.basename(file))
23+
shell.run(["mkdir", "-p", targetdir])
24+
sftp.put(file, dest)
25+
shell.run(shlex.split('chmod %o %s' % (os.stat(file).st_mode & 0777, dest)))
26+
27+
def get_host_ip(shell):
28+
ip = shell.run(shlex.split('grep host_addr /etc/ix.conf')).output
29+
ip = ip.split('"')[1].split('/')[0]
30+
return ip
31+
32+
def get_cores(shell):
33+
cores = shell.run(shlex.split('grep ^cpu /etc/ix.conf')).output
34+
cores = cores.split('[')[1]
35+
cores = cores.split(']')[0]
36+
cores = cores.split(',')
37+
cores = [x.strip() for x in cores]
38+
return cores
39+
40+
class Clients:
41+
def __init__(self, proc_manager, shells, cmdline, kill, duration = 5):
42+
self.proc_manager = proc_manager
43+
self.shells = shells
44+
self.cmdline = cmdline
45+
self.kill = kill
46+
self.duration = duration
47+
48+
def run(self):
49+
self.channels = []
50+
for shell in self.shells:
51+
channel, pid = self.proc_manager.spawn(shell, self.cmdline, self.kill)
52+
self.channels.append(channel)
53+
channel.settimeout(5)
54+
55+
self.outputs = [channel.makefile('rb') for channel in self.channels]
56+
57+
try:
58+
return self.run_inner()
59+
except Exception, e:
60+
print >>sys.stderr, 'Exception in Clients: %r' % e
61+
traceback.print_exc()
62+
return 0
63+
64+
def run_inner(self):
65+
for i in xrange(len(self.outputs)):
66+
while True:
67+
try:
68+
line = self.outputs[i].readline()
69+
except socket.timeout:
70+
line = ''
71+
if len(line) == 0 or line == 'ready\n':
72+
break
73+
if len(line) == 0:
74+
self.proc_manager.killall()
75+
for i in xrange(len(self.channels)):
76+
print >>sys.stderr, '%s: stdout: %s' % (self.shells[i]._hostname, self.outputs[i].read())
77+
print >>sys.stderr, '%s: stderr: %s' % (self.shells[i]._hostname, self.channels[i].makefile_stderr('rb').read())
78+
raise ValueError('client failed')
79+
80+
before_lines, before_time = self.wait_and_sync(WARMUP)
81+
after_lines, after_time = self.wait_and_sync(self.duration)
82+
83+
msgs = 0
84+
for i in xrange(len(after_lines)):
85+
msgs += int(after_lines[i].split()[1]) - int(before_lines[i].split()[1])
86+
87+
duration = after_time - before_time
88+
89+
return 1.0 * msgs / duration
90+
91+
def wait_and_sync(self, wait):
92+
time.sleep(wait)
93+
94+
for channel in self.channels:
95+
channel.sendall('\n')
96+
97+
t = time.time()
98+
lines = [output.readline() for output in self.outputs]
99+
timestamp = time.time()
100+
if timestamp - t > CLIENT_COMM_TIME_LIMIT:
101+
print >>sys.stderr, 'Warning: synchronization of clients lasted %f ms' % ((timestamp - t) * 1000)
102+
return lines, timestamp
103+
104+
class ProcessManager:
105+
def __init__(self, debug = False):
106+
self.atexit = []
107+
self.threads = []
108+
self.debug = debug
109+
110+
def __enter__(self):
111+
return self
112+
113+
def __exit__(self, type, value, traceback):
114+
self.killall()
115+
for thread in self.threads:
116+
thread.join()
117+
return False
118+
119+
def killall(self):
120+
for shell, kill, cmdline, channel, ignore_stdout in self.atexit:
121+
shell.run(shlex.split(kill), allow_error = True)
122+
channel.status_event.wait(1)
123+
if not channel.exit_status_ready():
124+
print >>sys.stderr, 'Error: cmdline "%s" did not kill this cmdline "%s"' % (kill, cmdline)
125+
else:
126+
status = channel.recv_exit_status()
127+
if status != 0 and status != -1 and status != 143:
128+
print >>sys.stderr, '%s%s-%s-status:%s %d' % (BOLD, shell._hostname, cmdline.split()[0], RESET, status)
129+
if not ignore_stdout:
130+
data = channel.makefile('rb').read()
131+
if len(data) > 0:
132+
if data[-1] == '\n':
133+
data = data[:-1]
134+
print >>sys.stderr, '%s%s-%s-stdout:%s\n%s' % (BOLD, shell._hostname, cmdline.split()[0], RESET, data)
135+
data = channel.makefile_stderr('rb').read()
136+
if len(data) > 0:
137+
if data[-1] == '\n':
138+
data = data[:-1]
139+
print >>sys.stderr, '%s%s-%s-stderr:%s\n%s' % (BOLD, shell._hostname, cmdline.split()[0], RESET, data)
140+
141+
def spawn(self, shell, cmdline, kill = None, cwd = None, ignore_stdout = False):
142+
#print >>sys.stderr, '%s%s:%s %s' % (BOLD, shell._hostname, RESET, cmdline)
143+
channel = shell._get_ssh_transport().open_session()
144+
cmd = []
145+
cmd.append('echo $$')
146+
if cwd is not None:
147+
cmd.append('cd %s' % cwd)
148+
cmd.append('exec %s' % cmdline)
149+
cmd = ' && '.join(cmd)
150+
channel.exec_command(cmd)
151+
152+
pid = simple_readline(channel)
153+
pid = int(pid)
154+
155+
if kill is not None:
156+
self.atexit.append((shell, kill.format(pid = pid), cmdline, channel, ignore_stdout))
157+
158+
if self.debug:
159+
stdout = channel.makefile('rb')
160+
t = threading.Thread(target = _debug_print, args = (('%s-%s-stdout' % (shell._hostname, cmdline.split()[0]), stdout)))
161+
self.threads.append(t)
162+
t.start()
163+
164+
stderr = channel.makefile_stderr('rb')
165+
t = threading.Thread(target = _debug_print, args = (('%s-%s-stderr' % (shell._hostname, cmdline.split()[0]), stderr)))
166+
self.threads.append(t)
167+
t.start()
168+
169+
return channel, pid
170+
171+
def run(self, shell, cmdline, cwd = None):
172+
channel, _ = self.spawn(shell, cmdline, cwd = cwd)
173+
while not channel.exit_status_ready():
174+
time.sleep(.1)
175+
176+
def _debug_print(prefix, file):
177+
while True:
178+
line = readline_retry(file)
179+
if len(line) == 0:
180+
break
181+
print >>sys.stderr, '%s%s:%s %s' % (BOLD, prefix, RESET, line[:-1])
182+
print >>sys.stderr, '%s%s: closed%s' % (BOLD, prefix, RESET)
183+
184+
def simple_readline(channel):
185+
data = []
186+
while True:
187+
byte = channel.recv(1)
188+
if len(byte) == 0:
189+
return None
190+
if byte == '\n':
191+
return ''.join(data)
192+
data.append(byte)
193+
194+
def readline_retry(f):
195+
while True:
196+
try:
197+
return f.readline()
198+
except socket.timeout:
199+
pass
200+
201+
def generator_from_file(file):
202+
while True:
203+
line = readline_retry(file)
204+
if len(line) == 0:
205+
break
206+
yield line
207+
208+
def multiplexer(*generators):
209+
threads = []
210+
queue = Queue.Queue()
211+
212+
def next(id, src):
213+
for line in src:
214+
queue.put((id, line))
215+
queue.put((id, None))
216+
217+
for i in xrange(len(generators)):
218+
t = threading.Thread(target = next, args = (i, generators[i]))
219+
threads.append(t)
220+
t.start()
221+
222+
finished = 0
223+
while finished < len(generators):
224+
try:
225+
src, line = queue.get(timeout = 1)
226+
if line is None:
227+
finished += 1
228+
yield src, line
229+
except Queue.Empty:
230+
pass
231+
232+
def mutilate_benchmark(proc_manager, clients, cmdline, connections, threads, records, depth, latency_connections, latency_qps, time):
233+
connections_per_thread = connections / (threads * (len(clients) - 1))
234+
agents = ','.join([c._hostname for c in clients][1:])
235+
236+
common_cmdline = '/tmp/'+getpass.getuser()+'/mutilate --binary --records=%d %s' % (records, cmdline)
237+
#load_cmdline = common_cmdline + ' --loadonly'
238+
bench_cmdline = 'timeout 600 ' + common_cmdline + ' --noload --threads=1 --depth=%d --measure_depth=1 --connections=%d --measure_connections=%d --measure_qps=%d --agent=%s --time=%d' % (depth, connections_per_thread, latency_connections, latency_qps, agents, time)
239+
240+
#clients[0].run(shlex.split(load_cmdline))
241+
242+
for c in clients[1:]:
243+
proc_manager.spawn(c, '/tmp/'+getpass.getuser()+'/mutilate --agentmode --threads %d' % (threads,), 'kill {pid}')
244+
245+
proc, _ = proc_manager.spawn(clients[0], bench_cmdline, 'kill {pid}')
246+
proc.settimeout(1)
247+
mutilate = proc.makefile('rb')
248+
249+
return generator_from_file(mutilate)
250+
251+
def wait_for_network(client, host, port):
252+
count = 0
253+
while True:
254+
result = client.run(shlex.split('nc -w 1 %s %d' % (host, port)), allow_error = True)
255+
if result.return_code == 0:
256+
break
257+
count += 1
258+
if count > 10:
259+
raise ValueError('ix failed to boot')
260+
261+
def consume(channel):
262+
# TODO: this is just a hack that reads 5 times to flush initial logs of IX
263+
channel.settimeout(1)
264+
try:
265+
for i in xrange(5):
266+
channel.makefile('rb').read()
267+
except socket.timeout:
268+
pass

0 commit comments

Comments
 (0)