commit 71069e738bc69647f57987db5c5584473d219baf
Author: Mike Perry <mikeperry-git(a)fscked.org>
Date: Wed Feb 23 00:23:43 2011 -0800
Fix extra_stats.py to output more useful data.
Documentation follows in next commit. Also requires a TorCtl update to latest
origin/master.
---
extra_stats.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 120 insertions(+), 20 deletions(-)
diff --git a/extra_stats.py b/extra_stats.py
old mode 100644
new mode 100755
index eac037b..fb8d38a
--- a/extra_stats.py
+++ b/extra_stats.py
@@ -1,45 +1,144 @@
-import sys, time, math
+#!/usr/bin/python
+
+import sys, time
+import TorCtl.TorUtil as TorUtil
import TorCtl.TorCtl as TorCtl
HOST = "127.0.0.1"
+class Circuit:
+ def __init__(self, launch_time, circ_id):
+ self.circ_id = circ_id
+ self.launch_time = launch_time
+ self.close_time = launch_time
+ self.stream_end_time = 0
+ self.path = []
+ self.build_times = []
+ self.failed = False
+ self.fail_reason = None
+ self.used = False
+ self.strm_id = 0
+ self.stream_failed = False
+ self.stream_fail_reason = None
+
class WriteStats(TorCtl.PostEventListener):
def __init__(self, port, filename):
TorCtl.PostEventListener.__init__(self)
self._port = int(port)
self._filename = filename
self._conn = None
+ self.all_circs = {}
+ self.ignore_streams = {}
+ self.current_timeout = None
+ self.current_quantile = None
def connect(self):
self._conn = TorCtl.connect(HOST, self._port)
+ self._conn.debug(file("control.log", "w"))
if not self._conn:
sys.exit(2)
def setup_listener(self):
- self._conn.set_events(["Stream"])
+ try:
+ # buildtimeout_set will not be available in 0.2.1
+ self._conn.set_events(["STREAM", "CIRC", "BUILDTIMEOUT_SET"],
+ extended=True)
+ except:
+ self._conn.set_events(["STREAM", "CIRC"], extended=True)
self._conn.add_event_listener(self)
+ def buildtimeout_set_event(self, b):
+ self.current_timeout = b.timeout_ms
+ self.current_quantile = b.cutoff_quantile
+ result = b.event_name + " " +b.body
+ self.write_result(result)
+
+ def circ_status_event(self, c):
+ if c.status == "LAUNCHED":
+ self.all_circs[c.circ_id] = Circuit(c.arrived_at, c.circ_id)
+ elif c.circ_id not in self.all_circs:
+ return
+
+ circ = self.all_circs[c.circ_id]
+ if c.status == "EXTENDED":
+ circ.build_times.append(c.arrived_at-circ.launch_time)
+ elif c.status == "BUILT":
+ circ.path = c.path
+ elif c.status == "FAILED":
+ circ.fail_reason = c.reason
+ if c.remote_reason: circ.fail_reason += ":"+c.remote_reason
+ circ.path = c.path
+ circ.close_time = c.arrived_at
+ circ.failed = True
+ self.write_circ(circ)
+ del self.all_circs[c.circ_id]
+ elif c.status == "CLOSED":
+ circ.close_time = c.arrived_at
+ self.write_circ(circ)
+ del self.all_circs[c.circ_id]
+
+ def write_circ(self, circ):
+ result = "CIRC_ID=%d LAUNCH=%s PATH=%s BUILDTIMES=%s " \
+ % (circ.circ_id, str(circ.launch_time), ",".join(circ.path),
+ ",".join(map(str, circ.build_times)))
+ if circ.failed:
+ result += "FAIL_REASONS=%s " % circ.fail_reason
+ if circ.stream_failed:
+ result += "STREAM_FAIL_REASONS=%s " % circ.stream_fail_reason
+ elif circ.used:
+ result += "USED_AT=%s USED_BY=%d " % \
+ (str(circ.stream_end_time), circ.strm_id)
+
+ if self.current_timeout:
+ result += "TIMEOUT=%d QUANTILE=%f " % \
+ (self.current_timeout, self.current_quantile)
+
+ self.write_result(result)
+
def stream_status_event(self, event):
- if event.source == 'EXIT' or \
- event.status == 'DETACHED':
- for circs in self._conn.get_info("circuit-status").itervalues():
- for circ in circs.split("\n"):
- entry = circ.split(" ")
- if entry[0] == str(event.circ_id):
- if event.source == 'EXIT':
- self.write_result(True, event.arrived_at, entry[2].split(","))
- else:
- self.write_result(False, event.arrived_at, entry[2].split(","))
-
- def write_result(self, successful, now, relays):
- success = ""
- if successful:
- success = "ok"
- else:
- success = "error"
+ if event.status == "NEW":
+ if event.purpose != "USER":
+ self.ignore_streams[event.strm_id] = True
+ return
+ if event.strm_id in self.ignore_streams:
+ if event.status == "CLOSED":
+ del self.ignore_streams[event.strm_id]
+ return
+ if event.circ_id not in self.all_circs:
+ if event.circ_id:
+ TorUtil.plog("WARN",
+ "Unknown circuit id %d has a stream event %d %s" % \
+ (event.circ_id, event.strm_id, event.status))
+ return
+ circ = self.all_circs[event.circ_id]
+ if event.status == 'DETACHED' or event.status == 'FAILED':
+ # Detached usually means there was some failure
+ assert not circ.strm_id
+ circ.used = True
+ circ.strm_id = event.strm_id
+ circ.stream_failed = True
+ circ.stream_end_time = event.arrived_at
+ if event.reason:
+ circ.stream_fail_reason = event.reason
+ if event.remote_reason:
+ circ.stream_fail_reason += " "+event.remote_reason
+ self.write_circ(circ)
+ # We have no explicit assurance here that tor will not
+ # try to reuse this circuit later... But we should
+ # print out a warn above if that happens.
+ del self.all_circs[event.circ_id]
+ if event.status == 'CLOSED':
+ assert not circ.strm_id or circ.stream_failed
+ circ.used = True
+ circ.strm_id = event.strm_id
+ circ.stream_end_time = event.arrived_at
+ self.write_circ(circ)
+ del self.all_circs[event.circ_id]
+ def write_result(self, result):
+ # XXX: hrmm. seems wasteful to keep opening+closing..
statsfile = open(self._filename, 'a')
- statsfile.write("%s %d %s\n" % (success, math.floor(now), " ".join(relays)))
+ statsfile.write(result+"\n")
statsfile.close()
def main():
@@ -54,6 +153,7 @@ def main():
stats.connect()
stats.setup_listener()
try:
+ # XXX: join instead
while True:
time.sleep(500)
except: