2018-01-08 00:51:19 +00:00
|
|
|
#!/usr/bin/env python2
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from __future__ import print_function
|
|
|
|
|
|
|
|
|
|
|
|
"""stress.py: retr0chat stress tester"""
|
|
|
|
__version__ = "0.9"
|
|
|
|
__author__ = "ed <a@ocv.me>"
|
|
|
|
__credits__ = ["stackoverflow.com"]
|
|
|
|
__license__ = "MIT"
|
|
|
|
__copyright__ = 2018
|
|
|
|
|
|
|
|
|
2018-01-09 01:10:09 +00:00
|
|
|
# config
|
|
|
|
NUM_CLIENTS = 4
|
|
|
|
EVENT_DELAY = 0.01
|
|
|
|
#EVENT_DELAY = 0.001
|
|
|
|
# config end
|
|
|
|
|
|
|
|
|
|
|
|
import multiprocessing
|
2018-01-08 00:51:19 +00:00
|
|
|
import threading
|
|
|
|
import asyncore
|
|
|
|
import socket
|
|
|
|
import signal
|
2018-01-08 22:47:21 +00:00
|
|
|
import random
|
2018-01-08 00:51:19 +00:00
|
|
|
import time
|
|
|
|
import sys
|
|
|
|
|
|
|
|
PY2 = (sys.version_info[0] == 2)
|
|
|
|
|
|
|
|
if PY2:
|
|
|
|
from Queue import Queue
|
|
|
|
else:
|
|
|
|
from queue import Queue
|
|
|
|
|
|
|
|
|
|
|
|
class Client(asyncore.dispatcher):
|
|
|
|
|
2018-01-09 00:07:30 +00:00
|
|
|
def __init__(self, core, port):
|
2018-01-08 00:51:19 +00:00
|
|
|
asyncore.dispatcher.__init__(self)
|
2018-01-08 22:47:21 +00:00
|
|
|
self.core = core
|
|
|
|
self.explain = True
|
2018-01-09 00:07:30 +00:00
|
|
|
self.explain = False
|
2018-01-08 00:51:19 +00:00
|
|
|
self.in_text = u''
|
|
|
|
self.outbox = Queue()
|
|
|
|
self.backlog = None
|
|
|
|
self.dead = False
|
|
|
|
self.stage = 'start'
|
2018-01-09 01:10:09 +00:00
|
|
|
self.stopping = False
|
2018-01-08 22:47:21 +00:00
|
|
|
self.actor_active = False
|
2018-01-08 00:51:19 +00:00
|
|
|
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
2018-01-09 00:07:30 +00:00
|
|
|
self.connect(('127.0.0.1', port))
|
2018-01-08 22:47:21 +00:00
|
|
|
|
|
|
|
thr = threading.Thread(target=self.actor)
|
|
|
|
thr.daemon = True # for emergency purposes
|
|
|
|
thr.start()
|
|
|
|
|
|
|
|
def actor(self):
|
|
|
|
self.actor_active = True
|
|
|
|
print('actor going up')
|
2018-01-09 01:10:09 +00:00
|
|
|
while not self.stopping:
|
2018-01-08 22:47:21 +00:00
|
|
|
time.sleep(0.02)
|
|
|
|
|
|
|
|
if self.stage == 'start':
|
|
|
|
if 'type the text below, then hit [Enter]:' in self.in_text:
|
|
|
|
self.stage = 'qwer'
|
|
|
|
self.in_text = u''
|
|
|
|
for ch in u'qwer asdf\n':
|
|
|
|
self.tx(ch)
|
|
|
|
time.sleep(0.02)
|
|
|
|
continue
|
|
|
|
|
|
|
|
if self.stage == 'qwer':
|
|
|
|
if 'text appeared as you typed' in self.in_text:
|
|
|
|
self.stage = 'color'
|
|
|
|
self.in_text = u''
|
|
|
|
self.tx(u'b')
|
|
|
|
self.outbox.put(b'\xff\xfa\x1f\x00\x80\x00\x24\xff\xf0') # 128x36
|
|
|
|
continue
|
|
|
|
|
|
|
|
if self.stage == 'color':
|
|
|
|
if 'does colours work?' in self.in_text:
|
|
|
|
self.stage = 'codec'
|
|
|
|
self.in_text = u''
|
|
|
|
self.tx(u'y')
|
|
|
|
continue
|
|
|
|
|
|
|
|
if self.stage == 'codec':
|
|
|
|
if 'which line looks like' in self.in_text:
|
|
|
|
self.stage = 'ready'
|
|
|
|
self.tx(u'a')
|
|
|
|
|
2018-01-09 00:07:30 +00:00
|
|
|
time.sleep(0.5)
|
|
|
|
self.in_text = u''
|
|
|
|
|
2018-01-08 22:47:21 +00:00
|
|
|
#return self.flood_single_channel()
|
|
|
|
return self.jump_channels()
|
|
|
|
|
|
|
|
self.actor_active = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def flood_single_channel(self):
|
2018-01-09 01:10:09 +00:00
|
|
|
while not self.stopping:
|
2018-01-08 22:47:21 +00:00
|
|
|
time.sleep(0.02)
|
|
|
|
|
|
|
|
if self.stage == 'ready':
|
|
|
|
if 'fire/' in self.in_text:
|
|
|
|
self.stage = 'main'
|
|
|
|
self.in_Text = u''
|
|
|
|
continue
|
|
|
|
|
|
|
|
if self.stage == 'main':
|
|
|
|
self.stage = 'done'
|
|
|
|
for n in range(4000):
|
|
|
|
time.sleep(0.01)
|
|
|
|
self.tx(u'{0} {1}\n'.format(time.time(), n))
|
|
|
|
continue
|
|
|
|
|
|
|
|
if self.stage == 'done':
|
|
|
|
time.sleep(1)
|
|
|
|
self.text = u'' # dont care
|
|
|
|
if not self.outbox.empty():
|
|
|
|
continue
|
|
|
|
self.tx(u'{0} done\n'.format(time.time()))
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-09 00:07:30 +00:00
|
|
|
def expl(self, msg):
|
2018-01-08 22:47:21 +00:00
|
|
|
if not self.explain:
|
|
|
|
return
|
|
|
|
print(msg)
|
2018-01-09 00:07:30 +00:00
|
|
|
self.await_continue()
|
|
|
|
|
|
|
|
def await_continue(self):
|
|
|
|
self.in_text = u''
|
|
|
|
t0 = time.time()
|
2018-01-09 01:10:09 +00:00
|
|
|
while not self.stopping and not 'zxc mkl' in self.in_text:
|
2018-01-09 00:07:30 +00:00
|
|
|
time.sleep(0.1)
|
|
|
|
if time.time() - t0 > 10:
|
|
|
|
break
|
|
|
|
self.in_text = u''
|
2018-01-08 22:47:21 +00:00
|
|
|
|
|
|
|
def jump_channels(self):
|
|
|
|
script = []
|
|
|
|
active_chan = 0
|
|
|
|
member_of = ['#general']
|
|
|
|
channels_avail = ['#general','#1','#2','#3']
|
|
|
|
|
|
|
|
# maps to channels_avail
|
|
|
|
msg_id = [0,0,0,0]
|
|
|
|
|
|
|
|
# -------- acts ---------
|
|
|
|
# 0 - 9: next channel
|
|
|
|
# 10 - 14: join a channel
|
|
|
|
# 15 - 18: part a channel
|
|
|
|
# 19 - 36: send a message
|
|
|
|
|
|
|
|
for n in range(100000):
|
2018-01-09 01:10:09 +00:00
|
|
|
if self.stopping:
|
2018-01-09 00:07:30 +00:00
|
|
|
break
|
|
|
|
|
2018-01-08 22:47:21 +00:00
|
|
|
if n % 1000 == 0:
|
2018-01-09 00:07:30 +00:00
|
|
|
self.tx(u'at event {0}\n'.format(n))
|
2018-01-08 22:47:21 +00:00
|
|
|
|
2018-01-09 01:10:09 +00:00
|
|
|
while not self.stopping:
|
2018-01-09 00:07:30 +00:00
|
|
|
if not member_of:
|
|
|
|
next_act = 13
|
|
|
|
else:
|
|
|
|
next_act = random.randrange(37)
|
|
|
|
|
|
|
|
if self.explain:
|
|
|
|
print('in [{0}], active [{1}:{2}], msgid [{3}], next [{4}]'.format(
|
|
|
|
','.join(member_of), active_chan, channels_avail[active_chan],
|
|
|
|
','.join(str(x) for x in msg_id), next_act))
|
2018-01-08 22:47:21 +00:00
|
|
|
|
|
|
|
if next_act <= 9:
|
2018-01-09 00:07:30 +00:00
|
|
|
if not member_of:
|
|
|
|
self.expl('tried to jump channel but we are all alone ;_;')
|
|
|
|
continue
|
|
|
|
|
2018-01-08 22:47:21 +00:00
|
|
|
changed_from_i = active_chan
|
|
|
|
changed_from_t = member_of[active_chan]
|
|
|
|
|
|
|
|
active_chan += 1
|
|
|
|
script.append(b'\x18')
|
|
|
|
if active_chan >= len(member_of):
|
|
|
|
# we do not consider the status channel
|
|
|
|
script.append(b'\x18')
|
|
|
|
active_chan = 0
|
|
|
|
|
|
|
|
changed_to_i = active_chan
|
|
|
|
changed_to_t = member_of[active_chan]
|
|
|
|
|
|
|
|
if self.explain:
|
2018-01-09 00:07:30 +00:00
|
|
|
self.expl('switching to next channel from {0} to {1} ({2} to {3})'.format(
|
2018-01-08 22:47:21 +00:00
|
|
|
changed_from_i, changed_to_i,
|
|
|
|
changed_from_t, changed_to_t))
|
|
|
|
for act in script:
|
|
|
|
self.outbox.put(act)
|
2018-01-09 00:07:30 +00:00
|
|
|
self.outbox.put(b'hello\n')
|
|
|
|
self.await_continue()
|
2018-01-08 22:47:21 +00:00
|
|
|
script = []
|
|
|
|
break
|
|
|
|
|
|
|
|
if next_act <= 14:
|
|
|
|
if len(member_of) == len(channels_avail):
|
2018-01-09 00:07:30 +00:00
|
|
|
self.expl('tried to join channel but filled {0} of {1} possible'.format(
|
|
|
|
len(member_of), len(channels_avail)))
|
2018-01-08 22:47:21 +00:00
|
|
|
# out of channels to join, try a different act
|
|
|
|
continue
|
|
|
|
while True:
|
|
|
|
to_join = random.choice(channels_avail)
|
|
|
|
if to_join not in member_of:
|
|
|
|
break
|
|
|
|
member_of.append(to_join)
|
|
|
|
active_chan = len(member_of) - 1
|
2018-01-09 00:07:30 +00:00
|
|
|
self.expl('going to join {0}:{1}, moving from {2}:{3}'.format(
|
|
|
|
len(member_of), to_join, active_chan, member_of[active_chan]))
|
|
|
|
|
|
|
|
script.append(u'/join {0}\n'.format(to_join).encode('utf-8'))
|
|
|
|
|
|
|
|
if self.explain:
|
|
|
|
for act in script:
|
|
|
|
self.outbox.put(act)
|
|
|
|
self.outbox.put(b'hello\n')
|
|
|
|
self.await_continue()
|
|
|
|
script = []
|
2018-01-08 22:47:21 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
if next_act <= 18:
|
|
|
|
if not member_of:
|
2018-01-09 00:07:30 +00:00
|
|
|
self.expl('tried to leave channel but theres nothing to leave')
|
2018-01-08 22:47:21 +00:00
|
|
|
# out of channels to part, try a different act
|
|
|
|
continue
|
|
|
|
to_part = random.choice(member_of)
|
|
|
|
chan_idx = member_of.index(to_part)
|
2018-01-09 00:07:30 +00:00
|
|
|
self.expl('gonna leave {0}:{1}, we are in {2}:{3}'.format(
|
|
|
|
chan_idx, to_part, active_chan, member_of[active_chan]))
|
2018-01-08 22:47:21 +00:00
|
|
|
# jump to the channel to part from
|
|
|
|
while active_chan != chan_idx:
|
2018-01-09 00:07:30 +00:00
|
|
|
self.expl('jumping over from {0} to {1}'.format(
|
|
|
|
active_chan, active_chan + 1 ))
|
2018-01-08 22:47:21 +00:00
|
|
|
active_chan += 1
|
|
|
|
script.append(b'\x18')
|
|
|
|
if active_chan >= len(member_of):
|
2018-01-09 00:07:30 +00:00
|
|
|
self.expl('wraparound; dodging the status chan')
|
2018-01-08 22:47:21 +00:00
|
|
|
# we do not consider the status channel
|
|
|
|
script.append(b'\x18')
|
|
|
|
active_chan = 0
|
|
|
|
if active_chan == len(member_of) - 1:
|
2018-01-09 00:07:30 +00:00
|
|
|
self.expl('we are at the end of the channel list, decreasing int')
|
2018-01-08 22:47:21 +00:00
|
|
|
del member_of[active_chan]
|
|
|
|
active_chan -= 1
|
|
|
|
else:
|
2018-01-09 00:07:30 +00:00
|
|
|
self.expl('we are not at the end of the channel list, keeping it')
|
2018-01-08 22:47:21 +00:00
|
|
|
del member_of[active_chan]
|
2018-01-09 00:07:30 +00:00
|
|
|
if member_of:
|
|
|
|
self.expl('we will end up in {0}:{1}'.format(active_chan, member_of[active_chan]))
|
|
|
|
else:
|
|
|
|
self.expl('we have now left all our channels')
|
|
|
|
|
2018-01-08 22:47:21 +00:00
|
|
|
script.append(b'/part\n')
|
2018-01-09 00:07:30 +00:00
|
|
|
|
|
|
|
if self.explain:
|
|
|
|
for act in script:
|
|
|
|
self.outbox.put(act)
|
|
|
|
self.outbox.put(b'hello\n')
|
|
|
|
self.await_continue()
|
|
|
|
script = []
|
2018-01-08 22:47:21 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
if not member_of:
|
|
|
|
# not in any channels, try a different act
|
|
|
|
continue
|
|
|
|
chan_name = member_of[active_chan]
|
|
|
|
chan_idx = channels_avail.index(chan_name)
|
|
|
|
msg_id[chan_idx] += 1
|
2018-01-09 00:07:30 +00:00
|
|
|
self.expl('gonna talk to {0}:{1}, msg #{2}'.format(
|
|
|
|
chan_idx, chan_name, msg_id[chan_idx]))
|
|
|
|
|
|
|
|
script.append(u'{0} {1} {2}\n'.format(
|
|
|
|
chan_name, msg_id[chan_idx], n).encode('utf-8'))
|
|
|
|
|
|
|
|
if self.explain:
|
|
|
|
for act in script:
|
|
|
|
self.outbox.put(act)
|
|
|
|
self.outbox.put(b'hello\n')
|
|
|
|
self.await_continue()
|
|
|
|
script = []
|
|
|
|
break
|
2018-01-08 22:47:21 +00:00
|
|
|
|
2018-01-09 00:07:30 +00:00
|
|
|
self.tx(u'q\n')
|
2018-01-08 22:47:21 +00:00
|
|
|
|
2018-01-09 01:10:09 +00:00
|
|
|
while not self.stopping:
|
2018-01-08 22:47:21 +00:00
|
|
|
if 'fire/' in self.in_text:
|
|
|
|
break
|
|
|
|
time.sleep(0.01)
|
|
|
|
|
2018-01-09 01:10:09 +00:00
|
|
|
delay = EVENT_DELAY
|
|
|
|
|
2018-01-08 22:47:21 +00:00
|
|
|
for n, ev in enumerate(script):
|
2018-01-09 01:10:09 +00:00
|
|
|
if self.stopping:
|
2018-01-08 22:47:21 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
if n % 100 == 0:
|
2018-01-09 00:07:30 +00:00
|
|
|
print('at event {0}\n'.format(n))
|
2018-01-08 22:47:21 +00:00
|
|
|
|
|
|
|
self.outbox.put(ev)
|
2018-01-09 01:10:09 +00:00
|
|
|
time.sleep(delay)
|
2018-01-08 22:47:21 +00:00
|
|
|
|
2018-01-09 00:07:30 +00:00
|
|
|
self.tx(u'done')
|
2018-01-08 22:47:21 +00:00
|
|
|
print('done')
|
|
|
|
|
|
|
|
|
2018-01-08 00:51:19 +00:00
|
|
|
|
|
|
|
def handle_close(self):
|
|
|
|
self.dead = True
|
|
|
|
|
|
|
|
def tx(self, bv):
|
|
|
|
self.outbox.put(bv.encode('utf-8'))
|
2018-01-08 22:47:21 +00:00
|
|
|
|
2018-01-08 00:51:19 +00:00
|
|
|
def readable(self):
|
|
|
|
return not self.dead
|
|
|
|
|
|
|
|
def writable(self):
|
2018-01-08 22:47:21 +00:00
|
|
|
return (self.backlog or not self.outbox.empty())
|
2018-01-08 00:51:19 +00:00
|
|
|
|
|
|
|
def handle_write(self):
|
|
|
|
msg = self.backlog
|
|
|
|
if not msg:
|
|
|
|
msg = self.outbox.get()
|
|
|
|
sent = self.send(msg)
|
|
|
|
self.backlog = msg[sent:]
|
|
|
|
|
|
|
|
def handle_read(self):
|
|
|
|
if self.dead:
|
|
|
|
print('!!! read when dead')
|
|
|
|
return
|
|
|
|
|
|
|
|
data = self.recv(8192)
|
|
|
|
if not data:
|
|
|
|
self.dead = True
|
|
|
|
return
|
|
|
|
|
|
|
|
self.in_text += data.decode('utf-8', 'ignore')
|
2018-01-09 00:07:30 +00:00
|
|
|
|
|
|
|
#if self.explain:
|
|
|
|
# print(self.in_text)
|
2018-01-08 22:47:21 +00:00
|
|
|
|
|
|
|
|
2018-01-08 00:51:19 +00:00
|
|
|
|
2018-01-09 01:10:09 +00:00
|
|
|
class SubCore(object):
|
|
|
|
|
|
|
|
def __init__(self, port, q):
|
|
|
|
self.q = q
|
|
|
|
self.port = port
|
|
|
|
self.stopped = False
|
|
|
|
self.client = Client(self, self.port)
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
timeout = 0.05
|
|
|
|
while self.q.empty():
|
|
|
|
asyncore.loop(timeout, count=0.5/timeout)
|
|
|
|
|
|
|
|
clean_shutdown = False
|
|
|
|
for n in range(0, 40): # 2sec
|
|
|
|
if not self.client.actor_active:
|
|
|
|
clean_shutdown = True
|
|
|
|
break
|
|
|
|
time.sleep(0.05)
|
|
|
|
|
|
|
|
self.client.close()
|
|
|
|
self.stopped = True
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-08 00:51:19 +00:00
|
|
|
class Core(object):
|
|
|
|
|
|
|
|
def __init__(self):
|
2018-01-09 00:07:30 +00:00
|
|
|
if len(sys.argv) < 2:
|
|
|
|
print('need 1 argument: telnet port')
|
|
|
|
sys.exit(1)
|
|
|
|
|
2018-01-09 01:10:09 +00:00
|
|
|
port = int(sys.argv[1])
|
|
|
|
|
2018-01-08 22:47:21 +00:00
|
|
|
self.stopping = False
|
|
|
|
self.asyncore_alive = False
|
2018-01-08 00:51:19 +00:00
|
|
|
|
|
|
|
signal.signal(signal.SIGINT, self.signal_handler)
|
|
|
|
|
2018-01-09 01:10:09 +00:00
|
|
|
self.clients = []
|
|
|
|
for n in range(NUM_CLIENTS):
|
|
|
|
que = multiprocessing.Queue()
|
|
|
|
cli = multiprocessing.Process(target=self.new_subcore, args=(que,))
|
|
|
|
self.clients.append([cli,que])
|
|
|
|
que.put(port)
|
|
|
|
cli.start()
|
2018-01-08 22:47:21 +00:00
|
|
|
|
2018-01-09 01:10:09 +00:00
|
|
|
def new_subcore(self, q):
|
|
|
|
subcore = SubCore(q.get(), q)
|
|
|
|
subcore.run()
|
|
|
|
q.read()
|
2018-01-08 00:51:19 +00:00
|
|
|
|
|
|
|
def run(self):
|
|
|
|
print(' * test is running')
|
|
|
|
|
|
|
|
while not self.stopping:
|
|
|
|
time.sleep(0.1)
|
|
|
|
|
2018-01-09 01:10:09 +00:00
|
|
|
print('\r\n * subcores stopping')
|
|
|
|
for subcore in self.clients:
|
|
|
|
subcore[1].put('x')
|
|
|
|
|
2018-01-08 22:47:21 +00:00
|
|
|
for n in range(0, 40): # 2sec
|
2018-01-09 01:10:09 +00:00
|
|
|
clean_shutdown = True
|
|
|
|
for subcore in self.clients:
|
|
|
|
if not subcore[1].empty():
|
|
|
|
clean_shutdown = False
|
|
|
|
break
|
|
|
|
if clean_shutdown:
|
2018-01-08 22:47:21 +00:00
|
|
|
print(' * actor stopped')
|
|
|
|
break
|
|
|
|
time.sleep(0.05)
|
|
|
|
|
|
|
|
if not clean_shutdown:
|
2018-01-09 01:10:09 +00:00
|
|
|
print(' -X- some subcores are stuck')
|
|
|
|
for subcore in self.clients:
|
|
|
|
if not subcore[1].empty():
|
|
|
|
subcore[0].terminate()
|
2018-01-08 00:51:19 +00:00
|
|
|
|
2018-01-08 22:47:21 +00:00
|
|
|
print(' * test ended')
|
2018-01-08 00:51:19 +00:00
|
|
|
|
|
|
|
def shutdown(self):
|
|
|
|
self.stopping = True
|
|
|
|
|
|
|
|
def signal_handler(self, signal, frame):
|
|
|
|
self.shutdown()
|
|
|
|
|
|
|
|
|
2018-01-09 01:10:09 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
core = Core()
|
|
|
|
core.run()
|
2018-01-08 22:47:21 +00:00
|
|
|
|