[tor-commits] [torperf/master] Fix extra_stats.py to output more useful data.

karsten at torproject.org karsten at torproject.org
Thu Feb 24 14:31:31 UTC 2011


commit 71069e738bc69647f57987db5c5584473d219baf
Author: Mike Perry <mikeperry-git at 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:





More information about the tor-commits mailing list