[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r21402: {torflow} Track guard changes, fix bugs, refactor code. Also update th (in torflow/trunk: . CircuitAnalysis/BuildTimes/CBT-Test CircuitAnalysis/BuildTimes/CBT-Test/tor-data)
Author: mikeperry
Date: 2010-01-11 19:50:06 +0000 (Mon, 11 Jan 2010)
New Revision: 21402
Modified:
torflow/trunk/
torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/cbttest.py
torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/run_test.sh
torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/tor-data/torrc
Log:
Track guard changes, fix bugs, refactor code.
Also update the svn:external to the new URL.
Property changes on: torflow/trunk
___________________________________________________________________
Modified: svn:externals
- TorCtl https://tor-svn.freehaven.net/svn/torctl/trunk/python/TorCtl
+ TorCtl https://svn.torproject.org/svn/torctl/trunk/python/TorCtl
Modified: torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/cbttest.py
===================================================================
--- torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/cbttest.py 2010-01-11 19:49:09 UTC (rev 21401)
+++ torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/cbttest.py 2010-01-11 19:50:06 UTC (rev 21402)
@@ -12,18 +12,31 @@
import socket,sys,time,getopt,os,threading,atexit
sys.path.append("../../../")
sys.path.append("../")
+import shutil
from TorCtl import TorUtil
import TorCtl
from TorCtl.TorCtl import EventHandler, PreEventListener
from TorCtl import PathSupport, TorCtl
from TorCtl.PathSupport import ExitPolicyRestriction,OrNodeRestriction,RestrictionError
from TorCtl.TorUtil import plog
+import traceback
# XXX: Add to config
-MAX_CIRCUITS = 5
+MAX_CIRCUITS = 10
+PCT_SKIP = 10
+# XXX: Are these two the right way to go?
+# Should we maybe have MIN_STREAK and MIN_FUZZY too?
STREAK_RATIO = 0.5
-FUZZY_RATIO = 0.5
+FUZZY_RATIO = 0.5
+MIN_STREAK = 50
+MIN_FUZZY = 50
+# CLI Options variables.
+# Yes, a hack.
+full_run = False
+output_dir = None
+pct_start = None
+
# Original value of FetchUselessDescriptors
FUDValue = None
@@ -33,20 +46,64 @@
self.end_time = 0
class CircHandler(EventHandler):
- def __init__(self, c):
+ def __init__(self, c, guards):
EventHandler.__init__(self)
self.c = c
self.circs = {}
self.live_circs = {}
self.timeout_circs = {}
self.closed_circs = {}
+ self.built_circs = {}
self.circ_times = {}
+ self.up_guards = {}
+ for g in guards:
+ self.up_guards[g.idhex] = g
+ self.down_guards = {}
+ self.buildtimes_file = file(output_dir+"/buildtimes", "w")
def heartbeat_event(self, event):
if len(self.live_circs) < MAX_CIRCUITS:
circ_id = self.c.extend_circuit()
plog("INFO", "Launched circuit: "+str(circ_id))
+ def guard_event(self, event):
+ changed = False
+ plog("NOTICE", "Guard $"+event.idhex+" is "+event.status)
+ # XXX: remove from our list of guards and get a new one
+ if event.status == "DOWN":
+ if event.idhex in self.up_guards:
+ self.down_guards[event.idhex] = self.up_guards[event.idhex]
+ del self.up_guards[event.idhex]
+ # If more than 2 are down, add one
+ if len(self.up_guards) < 2:
+ changed = True
+ guards = get_guards(2-len(self.up_guards))
+ for g in guards:
+ plog("NOTICE", "Adding guard $"+g.idhex)
+ self.up_guards[g.idhex] = g
+ elif event.status == "DROPPED":
+ if event.idhex in self.up_guards:
+ del self.up_guards[event.idhex]
+ if event.idhex in self.down_guards:
+ del self.down_guards[event.idhex]
+ guards = get_guards(1)
+ changed = True
+ for g in guards:
+ plog("NOTICE", "Adding guard $"+g.idhex)
+ self.up_guards[g.idhex] = g
+ elif event.status == "UP":
+ plog("NOTICE", "Adding guard $"+g.idhex)
+ if event.idhex in self.down_guards:
+ self.up_guards[event.idhex] = self.down_guards[event.idhex]
+ del self.down_guards[event.idhex]
+ if changed:
+ guard_str = ",".join(map(lambda r: "$"+r.idhex, self.up_guards.values()))
+ if self.down_guards:
+ guard_str += ","+",".join(map(lambda r:
+ "$"+r.idhex, self.down_guards.values()))
+ plog("NOTICE", "Setting guards: "+guard_str)
+ self.c.setcont("EntryNodes", guard_str)
+
def close_all_circs(self):
lines = self.c.sendAndRecv("GETINFO circuit-status\r\n")[0][2]
if lines: lines = lines.split("\n")
@@ -59,7 +116,7 @@
self.circs[int(line_parts[0])] = True
self.c.close_circuit(int(line_parts[0]))
- # XXX: Also time circs...
+ # XXX: Log built and timeout circs to buildtimes_file for post-analysis
def circ_status_event(self, circ_event):
if circ_event.status == 'LAUNCHED':
self.circs[circ_event.circ_id] = circ_event.status
@@ -67,25 +124,31 @@
self.live_circs[circ_event.circ_id] = True
elif circ_event.status == 'BUILT':
self.circs[circ_event.circ_id] = circ_event.status
+ self.built_circs[circ_event.circ_id] = True
self.c.close_circuit(circ_event.circ_id)
if circ_event.circ_id in self.circ_times:
self.circ_times[circ_event.circ_id].end_time = circ_event.arrived_at
plog("INFO", "Closing circuit "+str(circ_event.circ_id)+" with build time of "+str(self.circ_times[circ_event.circ_id].end_time-self.circ_times[circ_event.circ_id].start_time))
elif circ_event.status == 'FAILED' or circ_event.status == 'CLOSED':
- plog("INFO", circ_event.status+" circuit "+str(circ_event.circ_id))
self.circs[circ_event.circ_id] = circ_event.status
- # XXX: Record this differently..
- #if circ_event.circ_id in self.circ_times:
- # self.circ_times[circ_event.circ_id].end_time = circ_event.arrived_at
- del self.live_circs[circ_event.circ_id]
+ if circ_event.circ_id in self.live_circs:
+ del self.live_circs[circ_event.circ_id]
if circ_event.reason == 'TIMEOUT':
self.timeout_circs[circ_event.circ_id] = True
+ if circ_event.circ_id in self.circ_times:
+ self.circ_times[circ_event.circ_id].end_time = circ_event.arrived_at
+ plog("INFO", circ_event.status+" timeout circuit "+str(circ_event.circ_id)+" with build time of "+str(self.circ_times[circ_event.circ_id].end_time-self.circ_times[circ_event.circ_id].start_time))
else:
self.closed_circs[circ_event.circ_id] = True
class BuildTimeoutTracker(PreEventListener):
- def __init__(self):
+ def __init__(self, cond):
PreEventListener.__init__(self)
+ self.cond = cond
+ self.reset()
+ self.reset_total = 0
+
+ def reset(self):
self.last_timeout = 0
self.timeout_streak = 0
self.timeout_fuzzy_streak = 0
@@ -99,6 +162,17 @@
plog("INFO", "Got buildtimeout event: "+bt_event.set_type+" TOTAL_TIMES="
+str(bt_event.total_times)+" TIMEOUT_MS="
+str(bt_event.timeout_ms))
+
+ # Need to handle RESET events..
+ # XXX: Should these count towards our totals, or should we just start
+ # over? Probably, but then that breaks a lot of our asserts
+ # below...
+ if bt_event.set_type == "RESET":
+ plog("NOTICE", "Got RESET event. Resetting counts")
+ self.reset_total += self.total_times
+ self.reset()
+ return
+
if not self.total_times:
self.total_times = bt_event.total_times-1
self.total_times +=1
@@ -110,11 +184,34 @@
if not self.buildtimeout_fuzzy:
self.buildtimeout_fuzzy = bt_event
+ fuzzy_last = int(round(self.buildtimeout_fuzzy.timeout_ms, -3))
+ fuzzy_curr = int(round(bt_event.timeout_ms, -3))
+ fuzzy_diff = abs(fuzzy_last-fuzzy_curr)
+ if fuzzy_diff > 1000:
+ self.buildtimeout_fuzzy = None
+ self.fuzzy_streak_count = 0
+ self.cond.min_circs = 0
+ try: os.unlink(output_dir+"/state.min")
+ except: pass
+ elif not self.cond.min_circs:
+ assert(self.fuzzy_streak_count ==
+ (bt_event.total_times - self.buildtimeout_fuzzy.total_times))
+ self.fuzzy_streak_count += 1
+ if (self.fuzzy_streak_count >= self.total_times*FUZZY_RATIO):
+ plog("NOTICE",
+ "Fuzzy termination condition reached at "
+ +str(self.total_times-self.fuzzy_streak_count)
+ +" with streak of "+str(self.fuzzy_streak_count)
+ +" and reset count of "+str(self.reset_total))
+ self.cond.min_circs = self.reset_total+self.total_times \
+ - self.fuzzy_streak_count
+ shutil.copyfile('./tor-data/state', output_dir+"/state.min")
+
strict_last = int(round(self.buildtimeout_strict.timeout_ms, -3))
strict_curr = int(round(bt_event.timeout_ms, -3))
strict_diff = abs(strict_last-strict_curr)
if strict_diff > 0:
- self.buildtimeout_strict = bt_event
+ self.buildtimeout_strict = None
self.strict_streak_count = 0
else:
if (self.strict_streak_count != (bt_event.total_times -
@@ -126,32 +223,42 @@
assert(self.strict_streak_count ==
(bt_event.total_times - self.buildtimeout_strict.total_times))
self.strict_streak_count += 1
- if (self.strict_streak_count >= self.total_times*STREAK_RATIO):
+ if (self.cond.min_circs and self.strict_streak_count >= self.total_times*STREAK_RATIO):
plog("NOTICE",
"Strict termination condition reached at "
+str(self.total_times-self.strict_streak_count)
- +" with streak of "+str(self.strict_streak_count))
- # XXX: Signal termination condition
+ +" with streak of "+str(self.strict_streak_count)
+ +" and reset count of "+str(self.reset_total))
+ shutil.copyfile('./tor-data/state', output_dir+"/state.full")
+ self.cond.acquire()
+ self.cond.num_circs = self.reset_total+self.total_times-\
+ self.strict_streak_count
+ self.cond.notify()
+ self.cond.release()
- fuzzy_last = int(round(self.buildtimeout_fuzzy.timeout_ms, -3))
- fuzzy_curr = int(round(bt_event.timeout_ms, -3))
- fuzzy_diff = abs(fuzzy_last-fuzzy_curr)
- if fuzzy_diff > 1000:
- self.buildtimeout_fuzzy = bt_event
- self.fuzzy_streak_count = 0
- else:
- assert(self.fuzzy_streak_count ==
- (bt_event.total_times - self.buildtimeout_fuzzy.total_times))
- self.fuzzy_streak_count += 1
- if (self.strict_streak_count >= self.total_times*STREAK_RATIO):
- plog("NOTICE",
- "Strict termination condition reached at "
- +str(self.total_times-self.strict_streak_count)
- +" with streak of "+str(self.strict_streak_count))
- # XXX: Signal termination condition
+def get_guards(c, n):
+ # Get list of live routers
+ sorted_rlist = filter(lambda r: not r.down,
+ c.read_routers(c.get_network_status()))
+ sorted_rlist.sort(lambda x, y: cmp(y.bw, x.bw))
+ for i in xrange(len(sorted_rlist)): sorted_rlist[i].list_rank = i
+ guard_rst = PathSupport.FlagsRestriction(["Guard"], [])
+ pct_rst = PathSupport.PercentileRestriction(pct_start, pct_start+PCT_SKIP, sorted_rlist)
+ guard_gen = PathSupport.UniformGenerator(sorted_rlist,
+ PathSupport.NodeRestrictionList([guard_rst, pct_rst]))
+ guard_gen.rewind()
+ ggen = guard_gen.generate()
+
+ # Generate 3 guards
+ guards = []
+ for i in xrange(n):
+ guards.append(ggen.next())
+
+ return guards
+
def cleanup():
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((TorUtil.control_host,TorUtil.control_port))
@@ -171,9 +278,23 @@
t = c.launch_thread()
c.authenticate_cookie(file("./tor-data/control_auth_cookie", "r"))
c.debug(file(filename+".log", "w", buffering=0))
- h = CircHandler(c)
+
+ guards = get_guards(c, 3)
+ guard_str = ",".join(map(lambda r: "$"+r.idhex, guards))
+
+ plog("NOTICE", "Choosing guards: "+guard_str)
+ # Setconf guards for percentile range
+ c.set_option("EntryNodes", guard_str)
+ c.set_option("StrictNodes", "1")
+
+ cond = threading.Condition()
+ cond.min_circs = 0 # Python haxx
+ cond.num_circs = 0 # Python haxx
+ cond.acquire()
+
+ h = CircHandler(c, guards)
c.set_event_handler(h)
- c.add_event_listener(BuildTimeoutTracker())
+ c.add_event_listener(BuildTimeoutTracker(cond))
global FUDValue
if not FUDValue:
@@ -182,28 +303,48 @@
c.set_events([TorCtl.EVENT_TYPE.BUILDTIMEOUT_SET,
TorCtl.EVENT_TYPE.BW,
+ TorCtl.EVENT_TYPE.GUARD,
TorCtl.EVENT_TYPE.CIRC], True)
# Close all the already open circuits to start fresh
h.close_all_circs()
- return (c,t)
+ cond.wait()
+ cond.release()
+ # Write to output_file:
+ # 1. Num circs
+ # 2. Guards used
+ # 3. Failure quantile (in rerun only)
+ out = file(output_dir+"/result", "a")
+ out.write("NUM_CIRCS: "+str(cond.min_circs))
+ out.write("MIN_CIRCS: "+str(cond.num_circs))
+
+ return 0
+
def getargs():
if len(sys.argv[1:]) < 3:
usage()
sys.exit(2)
try:
- opts,args = getopt.getopt(sys.argv[1:],"p:o:fmr")
+ opts,args = getopt.getopt(sys.argv[1:],"p:o:b:fmr")
except getopt.GetoptError,err:
print str(err)
usage()
+
+ global pct_start
+ global output_dir
+
for o,a in opts:
- if o == '-n': pass
- elif o == '-d': pass
+ if o == '-p':
+ pct_start = int(a)
+ elif o == '-o':
+ output_dir = a
+ # XXX: -r retest
else:
assert False, "Bad option"
- return 0
+ return (output_dir, pct_start)
+
def usage():
print 'usage: FOAD'
sys.exit(1)
@@ -213,17 +354,19 @@
TorUtil.read_config('cbt.cfg')
try:
- # XXX: Setconf guards for percentile range
+ getargs()
atexit.register(cleanup)
- (c,t) = open_controller("cbtest")
- t.join()
+ return open_controller("cbtest")
except PathSupport.NoNodesRemain:
print 'No nodes remain at this percentile range.'
- return
+ return 1
+ except Exception, e:
+ plog("ERROR", "Misc exception: "+str(e))
+ traceback.print_exc()
+ return 23
- return (c,t)
#print "Using max_circuits: "+str(TorUtil.max_circuits)
if __name__ == '__main__':
- main()
+ sys.exit(main())
#profile.run("main()", "prof.out")
Modified: torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/run_test.sh
===================================================================
--- torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/run_test.sh 2010-01-11 19:49:09 UTC (rev 21401)
+++ torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/run_test.sh 2010-01-11 19:50:06 UTC (rev 21402)
@@ -3,7 +3,7 @@
# 1. Fire up a Tor
# 2. For every 5% percentile, loop N times:
# 3. Remove state file + hup tor.
-# 4. Run test with 3 guards from a percentile for c>10 circuits:
+# 4. Run test with 3 guards from a percentile for c>50 circuits:
# 5. Record MIN_CIRCS=c/2 when past c/2 circuits did not change
# timeout more than +/-1.
# 6. Copy state file to saved location w/ guards and c/2
@@ -34,48 +34,55 @@
# though..
# C. SETCONF EntryNodes/StrictEntryNodes
-TOR_DIR=../../tor.git/src/or
+TOR_DIR=../../../../tor.git/src/or
TOR_DATA=./tor-data/
-kill `cat $TOR_DATA/tor.pid`
-if [ $? -eq 0 ]
-then
- sleep 10
+if [ -f $TOR_DATA/tor.pid ]; then
+ kill `cat $TOR_DATA/tor.pid`
+ if [ $? -eq 0 ]
+ then
+ sleep 10
+ fi
fi
-$TOR_DIR/tor -f $TOR_DATA/torrc
-
-# XXX: Handle no guards left case
-for p in 65 0 5 10 15 20 25 30 35 40 45 50 55 60
+for p in 0 10 20 30 40 50 60 70 80 90
do
- rm $TOR_DATA/state
- kill -HUP `cat $TOR_DATA/tor.pid`
N=0
while [ $N -lt 10 ]
do
- mkdir -p results/$p/$N/min
- mkdir -p results/$p/$N/full
- echo ./cbt-test.py -m -P 5 -p $p -o results/$p/$N/min/result -b results/$p/$N/min/buildtimes
- # XXX: check retval
- cp $TOR_DATA/state results/$p/$N/min/state
- echo ./cbt-test.py -f -P 5 -p $p -o results/$p/$N/full/result -b results/$p/$N/full/buildtimes
- cp $TOR_DATA/state results/$p/$N/full/state
+ if [ -f $TOR_DATA/tor.pid ]; then
+ kill `cat $TOR_DATA/tor.pid`
+ wait `cat $TOR_DATA/tor.pid`
+ fi
+ rm $TOR_DATA/state
+ $TOR_DIR/tor -f $TOR_DATA/torrc &
+ sleep 10
+ mkdir -p results/$p/$N
+ ./cbttest.py -p $p -o results/$p/$N || exit
N=`expr $N + 1`
done
done
+exit
+
for p in `ls -1 results`
do
for n in `ls -1 results/$p`
do
- for t in `ls -1 results/$p/$n`
+ for state in `ls -1 results/$p/$n/state.*`
do
M=0
while [ $M -lt 3 ]
do
- cp results/$p/$n/$t $TOR_DATA/state
- kill -HUP `cat $TOR_DATA/tor.pid`
- echo ./cbt-test.py -r -o results/$p/$n/$t/result -b results/$p/$N/buildtimes.redo.$M
+ if [ -f $TOR_DATA/tor.pid ]; then
+ kill `cat $TOR_DATA/tor.pid`
+ wait `cat $TOR_DATA/tor.pid`
+ fi
+ cp $state $TOR_DATA/state
+ $TOR_DIR/tor -f $TOR_DATA/torrc &
+ sleep 10
+ # XXX: M times?? need diff result files..
+ ./cbt-test.py -r -o results/$p/$n || exit
M=`expr $N + 1`
done
done
Modified: torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/tor-data/torrc
===================================================================
--- torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/tor-data/torrc 2010-01-11 19:49:09 UTC (rev 21401)
+++ torflow/trunk/CircuitAnalysis/BuildTimes/CBT-Test/tor-data/torrc 2010-01-11 19:50:06 UTC (rev 21402)
@@ -6,3 +6,4 @@
CookieAuthentication 1
NewCircuitPeriod 1
Log info file ./tor-data/tor.log
+PidFile ./tor-data/tor.pid