[or-cvs] [tor/master 1/7] Add metric on uni/bidirectional connection usage.

nickm at torproject.org nickm at torproject.org
Fri Dec 3 17:35:13 UTC 2010


Author: Karsten Loesing <karsten.loesing at gmx.net>
Date: Mon, 2 Aug 2010 15:06:14 +0200
Subject: Add metric on uni/bidirectional connection usage.
Commit: 5dfdf075ac0cb3df66443011d3faa2c95dc53629

---
 doc/spec/dir-spec.txt |   24 ++++++
 src/or/config.c       |   10 ++-
 src/or/connection.c   |    5 +
 src/or/main.c         |    5 +
 src/or/or.h           |    3 +
 src/or/rephist.c      |  198 ++++++++++++++++++++++++++++++++++++++++++++++++-
 src/or/rephist.h      |    6 ++
 src/or/router.c       |   13 +++
 8 files changed, 262 insertions(+), 2 deletions(-)

diff --git a/doc/spec/dir-spec.txt b/doc/spec/dir-spec.txt
index 04e73c4..4c22771 100644
--- a/doc/spec/dir-spec.txt
+++ b/doc/spec/dir-spec.txt
@@ -848,6 +848,30 @@
         Mean number of circuits that are included in any of the deciles,
         rounded up to the next integer.
 
+    "conn-stats-end" YYYY-MM-DD HH:MM:SS (NSEC s) NL
+        [At most once]
+
+        YYYY-MM-DD HH:MM:SS defines the end of the included connection
+        statistics measurement interval of length NSEC seconds (86400
+        seconds by default).
+
+        A "conn-stats-end" line, as well as any other "conn-*" line,
+        is first added after the relay has been running for at least 24
+        hours.
+
+    "conn-bi-direct" BELOW,READ,WRITE,BOTH NL
+        [At most once]
+
+        Number of connections, split into 10-second intervals, that are
+        used uni-directionally or bi-directionally.  Every 10 seconds,
+        we determine for every connection whether we read and wrote less
+        than a threshold of 20 KiB (BELOW), read at least 10 times more
+        than we wrote (READ), wrote at least 10 times more than we read
+        (WRITE), or read and wrote more than the threshold, but not 10
+        times more in either direction (BOTH).  After classifying a
+        connection, read and write counters are reset for the next
+        10-second interval.
+
     "exit-stats-end" YYYY-MM-DD HH:MM:SS (NSEC s) NL
         [At most once.]
 
diff --git a/src/or/config.c b/src/or/config.c
index d371276..5ab4b46 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -200,6 +200,7 @@ static config_var_t _option_vars[] = {
   V(ClientOnly,                  BOOL,     "0"),
   V(ConsensusParams,             STRING,   NULL),
   V(ConnLimit,                   UINT,     "1000"),
+  V(ConnStatistics,              BOOL,     "0"),
   V(ConstrainedSockets,          BOOL,     "0"),
   V(ConstrainedSockSize,         MEMUNIT,  "8192"),
   V(ContactInfo,                 STRING,   NULL),
@@ -1391,7 +1392,8 @@ options_act(or_options_t *old_options)
   }
 
   if (options->CellStatistics || options->DirReqStatistics ||
-      options->EntryStatistics || options->ExitPortStatistics) {
+      options->EntryStatistics || options->ExitPortStatistics ||
+      options->ConnStatistics) {
     time_t now = time(NULL);
     if ((!old_options || !old_options->CellStatistics) &&
         options->CellStatistics)
@@ -1405,6 +1407,9 @@ options_act(or_options_t *old_options)
     if ((!old_options || !old_options->ExitPortStatistics) &&
         options->ExitPortStatistics)
       rep_hist_exit_stats_init(now);
+    if ((!old_options || !old_options->ConnStatistics) &&
+        options->ConnStatistics)
+      rep_hist_conn_stats_init(now);
     if (!old_options)
       log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
                  "the *-stats files that will first be written to the "
@@ -1423,6 +1428,9 @@ options_act(or_options_t *old_options)
   if (old_options && old_options->ExitPortStatistics &&
       !options->ExitPortStatistics)
     rep_hist_exit_stats_term();
+  if (old_options && old_options->ConnStatistics &&
+      !options->ConnStatistics)
+    rep_hist_conn_stats_term();
 
   /* Check if we need to parse and add the EntryNodes config option. */
   if (options->EntryNodes &&
diff --git a/src/or/connection.c b/src/or/connection.c
index 1488315..6f9ae20 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -2185,6 +2185,11 @@ connection_buckets_decrement(connection_t *conn, time_t now,
 
   if (!connection_is_rate_limited(conn))
     return; /* local IPs are free */
+
+  if (conn->type == CONN_TYPE_OR)
+    rep_hist_note_or_conn_bytes(conn->global_identifier, num_read,
+                                num_written, now);
+
   if (num_read > 0) {
     rep_hist_note_bytes_read(num_read, now);
   }
diff --git a/src/or/main.c b/src/or/main.c
index 823290a..8d98d3f 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -1204,6 +1204,11 @@ run_scheduled_events(time_t now)
       if (next_write && next_write < next_time_to_write_stats_files)
         next_time_to_write_stats_files = next_write;
     }
+    if (options->ConnStatistics) {
+      time_t next_write = rep_hist_conn_stats_write(time_to_write_stats_files);
+      if (next_write && next_write < next_time_to_write_stats_files)
+        next_time_to_write_stats_files = next_write;
+    }
     time_to_write_stats_files = next_time_to_write_stats_files;
   }
 
diff --git a/src/or/or.h b/src/or/or.h
index 8110500..c9e43fa 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2894,6 +2894,9 @@ typedef struct {
   /** If true, the user wants us to collect statistics on port usage. */
   int ExitPortStatistics;
 
+  /** If true, the user wants us to collect connection statistics. */
+  int ConnStatistics;
+
   /** If true, the user wants us to collect cell statistics. */
   int CellStatistics;
 
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 175ea01..f63e951 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -7,7 +7,7 @@
  * \brief Basic history and "reputation" functionality to remember
  *    which servers have worked in the past, how much bandwidth we've
  *    been using, which ports we tend to want, and so on; further,
- *    exit port statistics and cell statistics.
+ *    exit port statistics, cell statistics, and connection statistics.
  **/
 
 #include "or.h"
@@ -2418,6 +2418,201 @@ rep_hist_buffer_stats_write(time_t now)
   return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL;
 }
 
+/*** Connection statistics ***/
+
+/** Start of the current connection stats interval or 0 if we're not
+ * collecting connection statistics. */
+static time_t start_of_conn_stats_interval;
+
+/** Initialize connection stats. */
+void
+rep_hist_conn_stats_init(time_t now)
+{
+  start_of_conn_stats_interval = now;
+}
+
+#define BIDI_THRESHOLD 20480
+
+#define BIDI_FACTOR 10
+
+#define BIDI_INTERVAL 10
+
+/* Start of next BIDI_INTERVAL second interval. */
+static time_t bidi_next_interval = 0;
+
+/* Number of connections that we read and wrote less than BIDI_THRESHOLD
+ * bytes from/to in BIDI_INTERVAL seconds. */
+static uint32_t below_threshold = 0;
+
+/* Number of connections that we read at least BIDI_FACTOR times more
+ * bytes from than we wrote to in BIDI_INTERVAL seconds. */
+static uint32_t mostly_read = 0;
+
+/* Number of connections that we wrote at least BIDI_FACTOR times more
+ * bytes to than we read from in BIDI_INTERVAL seconds. */
+static uint32_t mostly_written = 0;
+
+/* Number of connections that we read and wrote at least BIDI_THRESHOLD
+ * bytes from/to, but not BIDI_FACTOR times more in either direction in
+ * BIDI_INTERVAL seconds. */
+static uint32_t both_read_and_written = 0;
+
+/* Entry in a map from connection ID to the number of read and written
+ * bytes on this connection in a BIDI_INTERVAL second interval. */
+typedef struct bidi_map_entry_t {
+  HT_ENTRY(bidi_map_entry_t) node;
+  uint64_t conn_id; /**< Connection ID */
+  size_t read; /**< Number of read bytes */
+  size_t written; /**< Number of written bytes */
+} bidi_map_entry_t;
+
+/** Map of OR connections together with the number of read and written
+ * bytes in the current BIDI_INTERVAL second interval. */
+static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map =
+     HT_INITIALIZER();
+
+static int
+bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b)
+{
+  return a->conn_id == b->conn_id;
+}
+
+static unsigned
+bidi_map_ent_hash(const bidi_map_entry_t *entry)
+{
+  return (unsigned) entry->conn_id;
+}
+
+HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
+             bidi_map_ent_eq);
+HT_GENERATE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
+            bidi_map_ent_eq, 0.6, malloc, realloc, free);
+
+static void
+bidi_map_free(void)
+{
+  bidi_map_entry_t **ptr, **next, *ent;
+  for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
+    ent = *ptr;
+    next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
+    tor_free(ent);
+  }
+  HT_CLEAR(bidimap, &bidi_map);
+}
+
+/** Stop collecting connection stats in a way that we can re-start doing
+ * so in rep_hist_conn_stats_init(). */
+void
+rep_hist_conn_stats_term(void)
+{
+  below_threshold = 0;
+  mostly_read = 0;
+  mostly_written = 0;
+  both_read_and_written = 0;
+  start_of_conn_stats_interval = 0;
+  bidi_map_free();
+}
+
+
+/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR
+ * connection <b>conn_id</b> in second <b>when</b>. If this is the first
+ * observation in a new interval, sum up the last observations. Add bytes
+ * for this connection. */
+void
+rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
+                            size_t num_written, time_t when)
+{
+  if (!start_of_conn_stats_interval)
+    return;
+  /* Initialize */
+  if (bidi_next_interval == 0)
+    bidi_next_interval = when + BIDI_INTERVAL;
+  /* Sum up last period's statistics */
+  if (when >= bidi_next_interval) {
+    bidi_map_entry_t **ptr, **next, *ent;
+    for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
+      ent = *ptr;
+      if (ent->read + ent->written < BIDI_THRESHOLD)
+        below_threshold++;
+      else if (ent->read >= ent->written * BIDI_FACTOR)
+        mostly_read++;
+      else if (ent->written >= ent->read * BIDI_FACTOR)
+        mostly_written++;
+      else
+        both_read_and_written++;
+      next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
+      tor_free(ent);
+    }
+    while (when >= bidi_next_interval)
+      bidi_next_interval += BIDI_INTERVAL;
+    log_info(LD_GENERAL, "%d below threshold, %d mostly read, "
+             "%d mostly written, %d both read and written.",
+             below_threshold, mostly_read, mostly_written,
+             both_read_and_written);
+  }
+  /* Add this connection's bytes. */
+  if (num_read > 0 || num_written > 0) {
+    bidi_map_entry_t *entry, lookup;
+    lookup.conn_id = conn_id;
+    entry = HT_FIND(bidimap, &bidi_map, &lookup);
+    if (entry) {
+      entry->written += num_written;
+      entry->read += num_read;
+    } else {
+      entry = tor_malloc_zero(sizeof(bidi_map_entry_t));
+      entry->conn_id = conn_id;
+      entry->written = num_written;
+      entry->read = num_read;
+      HT_INSERT(bidimap, &bidi_map, entry);
+    }
+  }
+}
+
+/** Write conn statistics to $DATADIR/stats/conn-stats and return when
+ * we would next want to write conn stats. */
+time_t
+rep_hist_conn_stats_write(time_t now)
+{
+  char *statsdir = NULL, *filename = NULL;
+  char written[ISO_TIME_LEN+1];
+  open_file_t *open_file = NULL;
+  FILE *out;
+
+  if (!start_of_conn_stats_interval)
+    return 0; /* Not initialized. */
+  if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now)
+    goto done; /* Not ready to write */
+
+  /* write to file */
+  statsdir = get_datadir_fname("stats");
+  if (check_private_dir(statsdir, CPD_CREATE) < 0)
+    goto done;
+  filename = get_datadir_fname2("stats", "conn-stats");
+  out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND,
+                                    0600, &open_file);
+  if (!out)
+    goto done;
+  format_iso_time(written, now);
+  if (fprintf(out, "conn-stats-end %s (%d s)\n", written,
+              (unsigned) (now - start_of_conn_stats_interval)) < 0)
+    goto done;
+
+  if (fprintf(out, "conn-bi-direct %d,%d,%d,%d\n",
+              below_threshold, mostly_read, mostly_written,
+              both_read_and_written) < 0)
+    goto done;
+
+  finish_writing_to_file(open_file);
+  open_file = NULL;
+  start_of_conn_stats_interval = now;
+ done:
+  if (open_file)
+    abort_writing_to_file(open_file);
+  tor_free(filename);
+  tor_free(statsdir);
+  return start_of_conn_stats_interval + WRITE_STATS_INTERVAL;
+}
+
 /** Free all storage held by the OR/link history caches, by the
  * bandwidth history arrays, by the port history, or by statistics . */
 void
@@ -2432,5 +2627,6 @@ rep_hist_free_all(void)
   tor_free(exit_streams);
   built_last_stability_doc_at = 0;
   predicted_ports_free();
+  bidi_map_free();
 }
 
diff --git a/src/or/rephist.h b/src/or/rephist.h
index 8f5a34d..efc8531 100644
--- a/src/or/rephist.h
+++ b/src/or/rephist.h
@@ -76,5 +76,11 @@ void rep_hist_buffer_stats_add_circ(circuit_t *circ,
 time_t rep_hist_buffer_stats_write(time_t now);
 void rep_hist_buffer_stats_term(void);
 
+void rep_hist_conn_stats_init(time_t now);
+void rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
+                                 size_t num_written, time_t when);
+time_t rep_hist_conn_stats_write(time_t now);
+void rep_hist_conn_stats_term(void);
+
 #endif
 
diff --git a/src/or/router.c b/src/or/router.c
index 1f3967d..b612e9a 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -2051,6 +2051,19 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
                         "exit-stats-end", now, &contents) > 0) {
       smartlist_add(chunks, contents);
     }
+    if (options->ConnStatistics &&
+        load_stats_file("stats"PATH_SEPARATOR"conn-stats",
+                        "conn-stats-end", now, &contents) > 0) {
+      size_t pos = strlen(s);
+      if (strlcpy(s + pos, contents, maxlen - strlen(s)) !=
+          strlen(contents)) {
+        log_warn(LD_DIR, "Could not write conn-stats to extra-info "
+                 "descriptor.");
+        s[pos] = '\0';
+        write_stats_to_extrainfo = 0;
+      }
+      tor_free(contents);
+    }
   }
 
   if (should_record_bridge_info(options) && write_stats_to_extrainfo) {
-- 
1.7.1




More information about the tor-commits mailing list