[tor-commits] [tor/master] Merge branch 'morestats4' into morestats5

nickm at torproject.org nickm at torproject.org
Thu Oct 31 03:06:11 UTC 2013


commit 2e0fad542cccddf9ad8b8dbaeba8b1e825c09ff4
Merge: 49278cd e46de82
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date:   Mon Oct 28 11:30:49 2013 +0100

    Merge branch 'morestats4' into morestats5
    
    Conflicts:
    	doc/tor.1.txt
    	src/or/config.c
    	src/or/connection.h
    	src/or/control.c
    	src/or/control.h
    	src/or/or.h
    	src/or/relay.c
    	src/or/relay.h
    	src/test/test.c

 doc/tor.1.txt                     |   19 +++
 src/or/circuitmux.c               |    2 +-
 src/or/command.c                  |   27 ++++
 src/or/command.h                  |    2 +
 src/or/config.c                   |   24 ++++
 src/or/connection.c               |  180 ++++++++++++++++++++++-
 src/or/connection.h               |    9 ++
 src/or/control.c                  |  274 ++++++++++++++++++++++++++++++++++-
 src/or/control.h                  |   35 +++++
 src/or/main.c                     |    5 +
 src/or/or.h                       |   70 +++++++++
 src/or/relay.c                    |  121 ++++++++++++++--
 src/or/relay.h                    |    3 +-
 src/test/Makefile.nmake           |    6 +-
 src/test/include.am               |    1 +
 src/test/test.c                   |    2 +
 src/test/test_cell_queue.c        |    6 +-
 src/test/test_controller_events.c |  287 +++++++++++++++++++++++++++++++++++++
 18 files changed, 1047 insertions(+), 26 deletions(-)

diff --cc doc/tor.1.txt
index ac33c59,3996334..615da2d
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@@ -2050,19 -2005,11 +2050,22 @@@ The following options are used for runn
         TestingV3AuthInitialDistDelay 20 seconds
         TestingAuthDirTimeToLearnReachability 0 minutes
         TestingEstimatedDescriptorPropagationTime 0 minutes
 +       TestingServerDownloadSchedule 0, 0, 0, 5, 10, 15, 20, 30, 60
 +       TestingClientDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
 +       TestingServerConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
 +       TestingClientConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
 +       TestingBridgeDownloadSchedule 60, 30, 30, 60
 +       TestingClientMaxIntervalWithoutRequest 5 seconds
 +       TestingDirConnectionMaxStall 30 seconds
 +       TestingConsensusMaxDownloadTries 80
 +       TestingDescriptorMaxDownloadTries 80
 +       TestingMicrodescMaxDownloadTries 80
 +       TestingCertMaxDownloadTries 80
+        TestingEnableConnBwEvent 1
+        TestingEnableCellStatsEvent 1
+        TestingEnableTbEmptyEvent 1
  
 -**TestingV3AuthInitialVotingInterval** __N__ **minutes**|**hours**::
 +[[TestingV3AuthInitialVotingInterval]] **TestingV3AuthInitialVotingInterval** __N__ **minutes**|**hours**::
      Like V3AuthVotingInterval, but for initial voting interval before the first
      consensus has been created. Changing this requires that
      **TestingTorNetwork** is set. (Default: 30 minutes)
@@@ -2095,66 -2038,22 +2098,82 @@@
      Minimum value for the Fast flag.  Overrides the ordinary minimum taken
      from the consensus when TestingTorNetwork is set. (Default: 0.)
  
 -**TestingEnableConnBwEvent** **0**|**1**::
 +[[TestingServerDownloadSchedule]] **TestingServerDownloadSchedule** __N__,__N__,__...__::
 +    Schedule for when servers should download things in general. Changing this
 +    requires that **TestingTorNetwork** is set. (Default: 0, 0, 0, 60, 60, 120,
 +    300, 900, 2147483647)
 +
 +[[TestingClientDownloadSchedule]] **TestingClientDownloadSchedule** __N__,__N__,__...__::
 +    Schedule for when clients should download things in general. Changing this
 +    requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600,
 +    2147483647)
 +
 +[[TestingServerConsensusDownloadSchedule]] **TestingServerConsensusDownloadSchedule** __N__,__N__,__...__::
 +    Schedule for when servers should download consensuses. Changing this
 +    requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600,
 +    1800, 1800, 1800, 1800, 1800, 3600, 7200)
 +
 +[[TestingClientConsensusDownloadSchedule]] **TestingClientConsensusDownloadSchedule** __N__,__N__,__...__::
 +    Schedule for when clients should download consensuses. Changing this
 +    requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600,
 +    1800, 3600, 3600, 3600, 10800, 21600, 43200)
 +
 +[[TestingBridgeDownloadSchedule]] **TestingBridgeDownloadSchedule** __N__,__N__,__...__::
 +    Schedule for when clients should download bridge descriptors. Changing this
 +    requires that **TestingTorNetwork** is set. (Default: 3600, 900, 900, 3600)
 +
 +[[TestingClientMaxIntervalWithoutRequest]] **TestingClientMaxIntervalWithoutRequest** __N__ **seconds**|**minutes**::
 +    When directory clients have only a few descriptors to request, they batch
 +    them until they have more, or until this amount of time has passed.
 +    Changing this requires that **TestingTorNetwork** is set. (Default: 10
 +    minutes)
 +
 +[[TestingDirConnectionMaxStall]] **TestingDirConnectionMaxStall** __N__ **seconds**|**minutes**::
 +    Let a directory connection stall this long before expiring it.
 +    Changing this requires that **TestingTorNetwork** is set. (Default:
 +    5 minutes)
 +
 +[[TestingConsensusMaxDownloadTries]] **TestingConsensusMaxDownloadTries** __NUM__::
 +    Try this often to download a consensus before giving up. Changing
 +    this requires that **TestingTorNetwork** is set. (Default: 8)
 +
 +[[TestingDescriptorMaxDownloadTries]] **TestingDescriptorMaxDownloadTries** __NUM__::
 +    Try this often to download a router descriptor before giving up.
 +    Changing this requires that **TestingTorNetwork** is set. (Default: 8)
 +
 +[[TestingMicrodescMaxDownloadTries]] **TestingMicrodescMaxDownloadTries** __NUM__::
 +    Try this often to download a microdesc descriptor before giving up.
 +    Changing this requires that **TestingTorNetwork** is set. (Default: 8)
 +
 +[[TestingCertMaxDownloadTries]] **TestingCertMaxDownloadTries** __NUM__::
 +    Try this often to download a v3 authority certificate before giving up.
 +    Changing this requires that **TestingTorNetwork** is set. (Default: 8)
 +
 +**TestingDirAuthVoteGuard** __node__,__node__,__...__::
 +    A list of identity fingerprints, nicknames, country codes and
 +    address patterns of nodes to vote Guard for regardless of their
 +    uptime and bandwidth. See the **ExcludeNodes** option for more
 +    information on how to specify nodes.
 + +
 +    In order for this option to have any effect, **TestingTorNetwork**
 +    has to be set.
 +
++[[TestingEnableConnBwEvent]] **TestingEnableConnBwEvent** **0**|**1**::
+     If this option is set, then Tor controllers may register for CONN_BW
+     events.  Changing this requires that **TestingTorNetwork** is set.
+     (Default: 0)
+ 
 -**TestingEnableCellStatsEvent** **0**|**1**::
++[[TestingEnableCellStatsEvent]] **TestingEnableCellStatsEvent** **0**|**1**::
+     If this option is set, then Tor controllers may register for CELL_STATS
+     events.  Changing this requires that **TestingTorNetwork** is set.
+     (Default: 0)
+ 
 -**TestingEnableTbEmptyEvent** **0**|**1**::
++[[TestingEnableTbEmptyEvent]] **TestingEnableTbEmptyEvent** **0**|**1**::
+     If this option is set, then Tor controllers may register for TB_EMPTY
+     events.  Changing this requires that **TestingTorNetwork** is set.
+     (Default: 0)
+ 
+ 
  SIGNALS
  -------
  
diff --cc src/or/circuitmux.c
index 47b4230,545cfd0..f2af943
--- a/src/or/circuitmux.c
+++ b/src/or/circuitmux.c
@@@ -1859,40 -1743,3 +1859,40 @@@ circuitmux_assert_okay_pass_three(circu
    }
  }
  
 +/*DOCDOC */
 +void
 +circuitmux_append_destroy_cell(channel_t *chan,
 +                               circuitmux_t *cmux,
 +                               circid_t circ_id,
 +                               uint8_t reason)
 +{
 +  cell_t cell;
 +  memset(&cell, 0, sizeof(cell_t));
 +  cell.circ_id = circ_id;
 +  cell.command = CELL_DESTROY;
 +  cell.payload[0] = (uint8_t) reason;
 +
-   cell_queue_append_packed_copy(&cmux->destroy_cell_queue, &cell,
++  cell_queue_append_packed_copy(NULL, &cmux->destroy_cell_queue, 0, &cell,
 +                                chan->wide_circ_ids, 0);
 +
 +  /* Destroy entering the queue, update counters */
 +  ++(cmux->destroy_ctr);
 +  ++global_destroy_ctr;
 +  log_debug(LD_CIRC,
 +            "Cmux at %p queued a destroy for circ %u, cmux counter is now "
 +            I64_FORMAT", global counter is now "I64_FORMAT,
 +            cmux, circ_id,
 +            I64_PRINTF_ARG(cmux->destroy_ctr),
 +            I64_PRINTF_ARG(global_destroy_ctr));
 +
 +  /* XXXX Duplicate code from append_cell_to_circuit_queue */
 +  if (!channel_has_queued_writes(chan)) {
 +    /* There is no data at all waiting to be sent on the outbuf.  Add a
 +     * cell, so that we can notice when it gets flushed, flushed_some can
 +     * get called, and we can start putting more data onto the buffer then.
 +     */
 +    log_debug(LD_GENERAL, "Primed a buffer.");
 +    channel_flush_from_first_active_circuit(chan, 1);
 +  }
 +}
 +
diff --cc src/or/config.c
index 95cede0,4ca0338..e0e5bb6
--- a/src/or/config.c
+++ b/src/or/config.c
@@@ -488,21 -464,9 +491,24 @@@ static const config_var_t testing_tor_n
    V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"),
    V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"),
    V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"),
 +  V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 5, 10, 15, "
 +                                 "20, 30, 60"),
 +  V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, 15, 20, "
 +                                 "30, 60"),
 +  V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
 +                                 "15, 20, 30, 60"),
 +  V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
 +                                 "15, 20, 30, 60"),
 +  V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"),
 +  V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"),
 +  V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"),
 +  V(TestingConsensusMaxDownloadTries, UINT, "80"),
 +  V(TestingDescriptorMaxDownloadTries, UINT, "80"),
 +  V(TestingMicrodescMaxDownloadTries, UINT, "80"),
 +  V(TestingCertMaxDownloadTries, UINT, "80"),
+   V(TestingEnableConnBwEvent,    BOOL,     "1"),
+   V(TestingEnableCellStatsEvent, BOOL,     "1"),
+   V(TestingEnableTbEmptyEvent,   BOOL,     "1"),
    VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"),
  
    { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
@@@ -3431,42 -3242,24 +3437,60 @@@ options_validate(or_options_t *old_opti
      COMPLAIN("TestingEstimatedDescriptorPropagationTime is insanely high.");
    }
  
 +  if (options->TestingClientMaxIntervalWithoutRequest < 1) {
 +    REJECT("TestingClientMaxIntervalWithoutRequest is way too low.");
 +  } else if (options->TestingClientMaxIntervalWithoutRequest > 3600) {
 +    COMPLAIN("TestingClientMaxIntervalWithoutRequest is insanely high.");
 +  }
 +
 +  if (options->TestingDirConnectionMaxStall < 5) {
 +    REJECT("TestingDirConnectionMaxStall is way too low.");
 +  } else if (options->TestingDirConnectionMaxStall > 3600) {
 +    COMPLAIN("TestingDirConnectionMaxStall is insanely high.");
 +  }
 +
 +  if (options->TestingConsensusMaxDownloadTries < 2) {
 +    REJECT("TestingConsensusMaxDownloadTries must be greater than 1.");
 +  } else if (options->TestingConsensusMaxDownloadTries > 800) {
 +    COMPLAIN("TestingConsensusMaxDownloadTries is insanely high.");
 +  }
 +
 +  if (options->TestingDescriptorMaxDownloadTries < 2) {
 +    REJECT("TestingDescriptorMaxDownloadTries must be greater than 1.");
 +  } else if (options->TestingDescriptorMaxDownloadTries > 800) {
 +    COMPLAIN("TestingDescriptorMaxDownloadTries is insanely high.");
 +  }
 +
 +  if (options->TestingMicrodescMaxDownloadTries < 2) {
 +    REJECT("TestingMicrodescMaxDownloadTries must be greater than 1.");
 +  } else if (options->TestingMicrodescMaxDownloadTries > 800) {
 +    COMPLAIN("TestingMicrodescMaxDownloadTries is insanely high.");
 +  }
 +
 +  if (options->TestingCertMaxDownloadTries < 2) {
 +    REJECT("TestingCertMaxDownloadTries must be greater than 1.");
 +  } else if (options->TestingCertMaxDownloadTries > 800) {
 +    COMPLAIN("TestingCertMaxDownloadTries is insanely high.");
 +  }
 +
+   if (options->TestingEnableConnBwEvent &&
+       !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
+     REJECT("TestingEnableConnBwEvent may only be changed in testing "
+            "Tor networks!");
+   }
+ 
+   if (options->TestingEnableCellStatsEvent &&
+       !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
+     REJECT("TestingEnableCellStatsEvent may only be changed in testing "
+            "Tor networks!");
+   }
+ 
+   if (options->TestingEnableTbEmptyEvent &&
+       !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
+     REJECT("TestingEnableTbEmptyEvent may only be changed in testing "
+            "Tor networks!");
+   }
+ 
    if (options->TestingTorNetwork) {
      log_warn(LD_CONFIG, "TestingTorNetwork is set. This will make your node "
                          "almost unusable in the public Tor network, and is "
diff --cc src/or/connection.h
index 0454ac2,53d3da0..4073d9f
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@@ -215,7 -215,14 +215,16 @@@ void connection_enable_rate_limiting(co
  #endif
  
  #ifdef CONNECTION_PRIVATE
 +STATIC void connection_free_(connection_t *conn);
++
+ /* Used only by connection.c and test*.c */
+ uint32_t bucket_millis_empty(int tokens_before, uint32_t last_empty_time,
+                              int tokens_after, int milliseconds_elapsed,
+                              const struct timeval *tvnow);
+ void connection_buckets_note_empty_ts(uint32_t *timestamp_var,
+                                       int tokens_before,
+                                       size_t tokens_removed,
+                                       const struct timeval *tvnow);
  #endif
  
  #endif
diff --cc src/or/control.c
index 65c543a,9e026b3..49212de
--- a/src/or/control.c
+++ b/src/or/control.c
@@@ -190,6 -237,20 +191,20 @@@ log_severity_to_event(int severity
    }
  }
  
+ /** Helper: clear bandwidth counters of all origin circuits. */
+ static void
+ clear_circ_bw_fields(void)
+ {
+   circuit_t *circ;
+   origin_circuit_t *ocirc;
 -  for (circ = circuit_get_global_list_(); circ; circ = circ->next) {
++  TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
+     if (!CIRCUIT_IS_ORIGIN(circ))
+       continue;
+     ocirc = TO_ORIGIN_CIRCUIT(circ);
+     ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
+   }
+ }
+ 
  /** Set <b>global_event_mask*</b> to the bitwise OR of each live control
   * connection's event_mask field. */
  void
@@@ -916,7 -981,10 +935,11 @@@ static const struct control_event_t con
    { EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" },
    { EVENT_SIGNAL, "SIGNAL" },
    { EVENT_CONF_CHANGED, "CONF_CHANGED"},
+   { EVENT_CONN_BW, "CONN_BW" },
+   { EVENT_CELL_STATS, "CELL_STATS" },
+   { EVENT_TB_EMPTY, "TB_EMPTY" },
+   { EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" },
 +  { EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" },
    { 0, NULL },
  };
  
@@@ -3902,6 -3940,235 +3933,235 @@@ control_event_stream_bandwidth_used(voi
    return 0;
  }
  
+ /** A second or more has elapsed: tell any interested control connections
+  * how much bandwidth origin circuits have used. */
+ int
+ control_event_circ_bandwidth_used(void)
+ {
+   circuit_t *circ;
+   origin_circuit_t *ocirc;
+   if (!EVENT_IS_INTERESTING(EVENT_CIRC_BANDWIDTH_USED))
+     return 0;
+ 
 -  for (circ = circuit_get_global_list_(); circ; circ = circ->next) {
++  TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
+     if (!CIRCUIT_IS_ORIGIN(circ))
+       continue;
+     ocirc = TO_ORIGIN_CIRCUIT(circ);
+     if (!ocirc->n_read_circ_bw && !ocirc->n_written_circ_bw)
+       continue;
+     send_control_event(EVENT_CIRC_BANDWIDTH_USED, ALL_FORMATS,
+                        "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu\r\n",
+                        ocirc->global_identifier,
+                        (unsigned long)ocirc->n_read_circ_bw,
+                        (unsigned long)ocirc->n_written_circ_bw);
+     ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
+   }
+ 
+   return 0;
+ }
+ 
+ /** Print out CONN_BW event for a single OR/DIR/EXIT <b>conn</b> and reset
+   * bandwidth counters. */
+ int
+ control_event_conn_bandwidth(connection_t *conn)
+ {
+   const char *conn_type_str;
+   if (!get_options()->TestingEnableConnBwEvent ||
+       !EVENT_IS_INTERESTING(EVENT_CONN_BW))
+     return 0;
+   if (!conn->n_read_conn_bw && !conn->n_written_conn_bw)
+     return 0;
+   switch (conn->type) {
+     case CONN_TYPE_OR:
+       conn_type_str = "OR";
+       break;
+     case CONN_TYPE_DIR:
+       conn_type_str = "DIR";
+       break;
+     case CONN_TYPE_EXIT:
+       conn_type_str = "EXIT";
+       break;
+     default:
+       return 0;
+   }
+   send_control_event(EVENT_CONN_BW, ALL_FORMATS,
+                      "650 CONN_BW ID="U64_FORMAT" TYPE=%s "
+                      "READ=%lu WRITTEN=%lu\r\n",
+                      U64_PRINTF_ARG(conn->global_identifier),
+                      conn_type_str,
+                      (unsigned long)conn->n_read_conn_bw,
+                      (unsigned long)conn->n_written_conn_bw);
+   conn->n_written_conn_bw = conn->n_read_conn_bw = 0;
+   return 0;
+ }
+ 
+ /** A second or more has elapsed: tell any interested control
+  * connections how much bandwidth connections have used. */
+ int
+ control_event_conn_bandwidth_used(void)
+ {
+   if (get_options()->TestingEnableConnBwEvent &&
+       EVENT_IS_INTERESTING(EVENT_CONN_BW)) {
+     SMARTLIST_FOREACH(get_connection_array(), connection_t *, conn,
+                       control_event_conn_bandwidth(conn));
+   }
+   return 0;
+ }
+ 
+ /** Helper: iterate over cell statistics of <b>circ</b> and sum up added
+  * cells, removed cells, and waiting times by cell command and direction.
+  * Store results in <b>cell_stats</b>.  Free cell statistics of the
+  * circuit afterwards. */
+ void
+ sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats)
+ {
+   memset(cell_stats, 0, sizeof(cell_stats_t));
+   SMARTLIST_FOREACH_BEGIN(circ->testing_cell_stats,
+                           testing_cell_stats_entry_t *, ent) {
+     tor_assert(ent->command <= CELL_COMMAND_MAX_);
+     if (!ent->removed && !ent->exitward) {
+       cell_stats->added_cells_appward[ent->command] += 1;
+     } else if (!ent->removed && ent->exitward) {
+       cell_stats->added_cells_exitward[ent->command] += 1;
+     } else if (!ent->exitward) {
+       cell_stats->removed_cells_appward[ent->command] += 1;
+       cell_stats->total_time_appward[ent->command] += ent->waiting_time * 10;
+     } else {
+       cell_stats->removed_cells_exitward[ent->command] += 1;
+       cell_stats->total_time_exitward[ent->command] += ent->waiting_time * 10;
+     }
+     tor_free(ent);
+   } SMARTLIST_FOREACH_END(ent);
+   smartlist_free(circ->testing_cell_stats);
+   circ->testing_cell_stats = NULL;
+ }
+ 
+ /** Helper: append a cell statistics string to <code>event_parts</code>,
+  * prefixed with <code>key</code>=.  Statistics consist of comma-separated
+  * key:value pairs with lower-case command strings as keys and cell
+  * numbers or total waiting times as values.  A key:value pair is included
+  * if the entry in <code>include_if_non_zero</code> is not zero, but with
+  * the (possibly zero) entry from <code>number_to_include</code>.  Both
+  * arrays are expected to have a length of CELL_COMMAND_MAX_ + 1.  If no
+  * entry in <code>include_if_non_zero</code> is positive, no string will
+  * be added to <code>event_parts</code>. */
+ void
+ append_cell_stats_by_command(smartlist_t *event_parts, const char *key,
+                              const uint64_t *include_if_non_zero,
+                              const uint64_t *number_to_include)
+ {
+   smartlist_t *key_value_strings = smartlist_new();
+   int i;
+   for (i = 0; i <= CELL_COMMAND_MAX_; i++) {
+     if (include_if_non_zero[i] > 0) {
+       smartlist_add_asprintf(key_value_strings, "%s:"U64_FORMAT,
+                              cell_command_to_string(i),
+                              U64_PRINTF_ARG(number_to_include[i]));
+     }
+   }
+   if (smartlist_len(key_value_strings) > 0) {
+     char *joined = smartlist_join_strings(key_value_strings, ",", 0, NULL);
+     smartlist_add_asprintf(event_parts, "%s=%s", key, joined);
+     SMARTLIST_FOREACH(key_value_strings, char *, cp, tor_free(cp));
+     tor_free(joined);
+   }
+   smartlist_free(key_value_strings);
+ }
+ 
+ /** Helper: format <b>cell_stats</b> for <b>circ</b> for inclusion in a
+  * CELL_STATS event and write result string to <b>event_string</b>. */
+ void
+ format_cell_stats(char **event_string, circuit_t *circ,
+                   cell_stats_t *cell_stats)
+ {
+   smartlist_t *event_parts = smartlist_new();
+   if (CIRCUIT_IS_ORIGIN(circ)) {
+     origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+     smartlist_add_asprintf(event_parts, "ID=%lu",
+                  (unsigned long)ocirc->global_identifier);
+   } else if (TO_OR_CIRCUIT(circ)->p_chan) {
+     or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+     smartlist_add_asprintf(event_parts, "InboundQueue=%lu",
+                  (unsigned long)or_circ->p_circ_id);
+     smartlist_add_asprintf(event_parts, "InboundConn="U64_FORMAT,
+                  U64_PRINTF_ARG(or_circ->p_chan->global_identifier));
+     append_cell_stats_by_command(event_parts, "InboundAdded",
+                                  cell_stats->added_cells_appward,
+                                  cell_stats->added_cells_appward);
+     append_cell_stats_by_command(event_parts, "InboundRemoved",
+                                  cell_stats->removed_cells_appward,
+                                  cell_stats->removed_cells_appward);
+     append_cell_stats_by_command(event_parts, "InboundTime",
+                                  cell_stats->removed_cells_appward,
+                                  cell_stats->total_time_appward);
+   }
+   if (circ->n_chan) {
+     smartlist_add_asprintf(event_parts, "OutboundQueue=%lu",
+                      (unsigned long)circ->n_circ_id);
+     smartlist_add_asprintf(event_parts, "OutboundConn="U64_FORMAT,
+                  U64_PRINTF_ARG(circ->n_chan->global_identifier));
+     append_cell_stats_by_command(event_parts, "OutboundAdded",
+                                  cell_stats->added_cells_exitward,
+                                  cell_stats->added_cells_exitward);
+     append_cell_stats_by_command(event_parts, "OutboundRemoved",
+                                  cell_stats->removed_cells_exitward,
+                                  cell_stats->removed_cells_exitward);
+     append_cell_stats_by_command(event_parts, "OutboundTime",
+                                  cell_stats->removed_cells_exitward,
+                                  cell_stats->total_time_exitward);
+   }
+   *event_string = smartlist_join_strings(event_parts, " ", 0, NULL);
+   SMARTLIST_FOREACH(event_parts, char *, cp, tor_free(cp));
+   smartlist_free(event_parts);
+ }
+ 
+ /** A second or more has elapsed: tell any interested control connection
+  * how many cells have been processed for a given circuit. */
+ int
+ control_event_circuit_cell_stats(void)
+ {
+   circuit_t *circ;
+   cell_stats_t *cell_stats;
+   char *event_string;
+   if (!get_options()->TestingEnableCellStatsEvent ||
+       !EVENT_IS_INTERESTING(EVENT_CELL_STATS))
+     return 0;
+   cell_stats = tor_malloc(sizeof(cell_stats_t));;
 -  for (circ = circuit_get_global_list_(); circ; circ = circ->next) {
++  TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
+     if (!circ->testing_cell_stats)
+       continue;
+     sum_up_cell_stats_by_command(circ, cell_stats);
+     format_cell_stats(&event_string, circ, cell_stats);
+     send_control_event(EVENT_CELL_STATS, ALL_FORMATS,
+                        "650 CELL_STATS %s\r\n", event_string);
+     tor_free(event_string);
+   }
+   tor_free(cell_stats);
+   return 0;
+ }
+ 
+ /** Tokens in <b>bucket</b> have been refilled: the read bucket was empty
+  * for <b>read_empty_time</b> millis, the write bucket was empty for
+  * <b>write_empty_time</b> millis, and buckets were last refilled
+  * <b>milliseconds_elapsed</b> millis ago.  Only emit TB_EMPTY event if
+  * either read or write bucket have been empty before. */
+ int
+ control_event_tb_empty(const char *bucket, uint32_t read_empty_time,
+                        uint32_t write_empty_time,
+                        int milliseconds_elapsed)
+ {
+   if (get_options()->TestingEnableTbEmptyEvent &&
+       EVENT_IS_INTERESTING(EVENT_TB_EMPTY) &&
+       (read_empty_time > 0 || write_empty_time > 0)) {
+     send_control_event(EVENT_TB_EMPTY, ALL_FORMATS,
+                        "650 TB_EMPTY %s READ=%d WRITTEN=%d "
+                        "LAST=%d\r\n",
+                        bucket, read_empty_time, write_empty_time,
+                        milliseconds_elapsed);
+   }
+   return 0;
+ }
+ 
  /** A second or more has elapsed: tell any interested control
   * connections how much bandwidth we used. */
  int
diff --cc src/or/control.h
index 099782a,1921d97..c8db643
--- a/src/or/control.h
+++ b/src/or/control.h
@@@ -85,72 -92,38 +92,100 @@@ void enable_control_logging(void)
  void monitor_owning_controller_process(const char *process_spec);
  
  void control_event_bootstrap(bootstrap_status_t status, int progress);
 -void control_event_bootstrap_problem(const char *warn, int reason);
 +MOCK_DECL(void, control_event_bootstrap_problem,(const char *warn,
 +                                                 int reason));
  
  void control_event_clients_seen(const char *controller_str);
 +void control_event_transport_launched(const char *mode,
 +                                      const char *transport_name,
 +                                      tor_addr_t *addr, uint16_t port);
 +
 +void control_free_all(void);
  
  #ifdef CONTROL_PRIVATE
 -/* Used only by control.c and test*.c */
 -size_t write_escaped_data(const char *data, size_t len, char **out);
 -size_t read_escaped_data(const char *data, size_t len, char **out);
 +/* Recognized asynchronous event types.  It's okay to expand this list
 + * because it is used both as a list of v0 event types, and as indices
 + * into the bitfield to determine which controllers want which events.
 + */
 +#define EVENT_MIN_                    0x0001
 +#define EVENT_CIRCUIT_STATUS          0x0001
 +#define EVENT_STREAM_STATUS           0x0002
 +#define EVENT_OR_CONN_STATUS          0x0003
 +#define EVENT_BANDWIDTH_USED          0x0004
 +#define EVENT_CIRCUIT_STATUS_MINOR    0x0005
 +#define EVENT_NEW_DESC                0x0006
 +#define EVENT_DEBUG_MSG               0x0007
 +#define EVENT_INFO_MSG                0x0008
 +#define EVENT_NOTICE_MSG              0x0009
 +#define EVENT_WARN_MSG                0x000A
 +#define EVENT_ERR_MSG                 0x000B
 +#define EVENT_ADDRMAP                 0x000C
 +/* Exposed above */
 +// #define EVENT_AUTHDIR_NEWDESCS     0x000D
 +#define EVENT_DESCCHANGED             0x000E
 +/* Exposed above */
 +// #define EVENT_NS                   0x000F
 +#define EVENT_STATUS_CLIENT           0x0010
 +#define EVENT_STATUS_SERVER           0x0011
 +#define EVENT_STATUS_GENERAL          0x0012
 +#define EVENT_GUARD                   0x0013
 +#define EVENT_STREAM_BANDWIDTH_USED   0x0014
 +#define EVENT_CLIENTS_SEEN            0x0015
 +#define EVENT_NEWCONSENSUS            0x0016
 +#define EVENT_BUILDTIMEOUT_SET        0x0017
 +#define EVENT_SIGNAL                  0x0018
 +#define EVENT_CONF_CHANGED            0x0019
++#define EVENT_CONN_BW                 0x001A
++#define EVENT_CELL_STATS              0x001B
++#define EVENT_TB_EMPTY                0x001C
++#define EVENT_CIRC_BANDWIDTH_USED     0x001D
 +#define EVENT_TRANSPORT_LAUNCHED      0x0020
 +#define EVENT_MAX_                    0x0020
 +/* If EVENT_MAX_ ever hits 0x0040, we need to make the mask into a
 + * different structure. */
 +
 +/* Used only by control.c and test.c */
 +STATIC size_t write_escaped_data(const char *data, size_t len, char **out);
 +STATIC size_t read_escaped_data(const char *data, size_t len, char **out);
 +/** Flag for event_format_t.  Indicates that we should use the one standard
 +    format.  (Other formats previous existed, and are now deprecated)
 + */
 +#define ALL_FORMATS 1
 +/** Bit field of flags to select how to format a controller event.  Recognized
 + * flag is ALL_FORMATS. */
 +typedef int event_format_t;
 +
 +#ifdef TOR_UNIT_TESTS
 +MOCK_DECL(STATIC void,
 +send_control_event_string,(uint16_t event, event_format_t which,
 +                           const char *msg));
 +
 +void control_testing_set_global_event_mask(uint64_t mask);
 +#endif
+ 
+ /** Helper structure: temporarily stores cell statistics for a circuit. */
+ typedef struct cell_stats_t {
+   /** Number of cells added in app-ward direction by command. */
+   uint64_t added_cells_appward[CELL_COMMAND_MAX_ + 1];
+   /** Number of cells added in exit-ward direction by command. */
+   uint64_t added_cells_exitward[CELL_COMMAND_MAX_ + 1];
+   /** Number of cells removed in app-ward direction by command. */
+   uint64_t removed_cells_appward[CELL_COMMAND_MAX_ + 1];
+   /** Number of cells removed in exit-ward direction by command. */
+   uint64_t removed_cells_exitward[CELL_COMMAND_MAX_ + 1];
+   /** Total waiting time of cells in app-ward direction by command. */
+   uint64_t total_time_appward[CELL_COMMAND_MAX_ + 1];
+   /** Total waiting time of cells in exit-ward direction by command. */
+   uint64_t total_time_exitward[CELL_COMMAND_MAX_ + 1];
+ } cell_stats_t;
+ void sum_up_cell_stats_by_command(circuit_t *circ,
+                                   cell_stats_t *cell_stats);
+ void append_cell_stats_by_command(smartlist_t *event_parts,
+                                   const char *key,
+                                   const uint64_t *include_if_non_zero,
+                                   const uint64_t *number_to_include);
+ void format_cell_stats(char **event_string, circuit_t *circ,
+                        cell_stats_t *cell_stats);
  #endif
  
  #endif
diff --cc src/or/or.h
index 2f0f9eb,6dd3ce4..92c0692
--- a/src/or/or.h
+++ b/src/or/or.h
@@@ -1139,10 -1106,12 +1155,12 @@@ typedef struct insertion_command_queue_
  /** A queue of cells on a circuit, waiting to be added to the
   * or_connection_t's outbuf. */
  typedef struct cell_queue_t {
 -  packed_cell_t *head; /**< The first cell, or NULL if the queue is empty. */
 -  packed_cell_t *tail; /**< The last cell, or NULL if the queue is empty. */
 +  /** Linked list of packed_cell_t*/
 +  TOR_SIMPLEQ_HEAD(cell_simpleq, packed_cell_t) head;
    int n; /**< The number of cells in the queue. */
    insertion_time_queue_t *insertion_times; /**< Insertion times of cells. */
+  /** Commands of inserted cells. */
+   insertion_command_queue_t *insertion_commands;
  } cell_queue_t;
  
  /** Beginning of a RELAY cell payload. */
@@@ -4065,10 -3987,15 +4126,19 @@@ typedef struct 
    /** Minimum value for the Fast flag threshold on testing networks. */
    uint64_t TestingMinFastFlagThreshold;
  
 +  /** Relays in a testing network which should be voted Guard
 +   * regardless of uptime and bandwidth. */
 +  routerset_t *TestingDirAuthVoteGuard;
 +
+   /** Enable CONN_BW events.  Only altered on testing networks. */
+   int TestingEnableConnBwEvent;
+ 
+   /** Enable CELL_STATS events.  Only altered on testing networks. */
+   int TestingEnableCellStatsEvent;
+ 
+   /** Enable TB_EMPTY events.  Only altered on testing networks. */
+   int TestingEnableTbEmptyEvent;
+ 
    /** If true, and we have GeoIP data, and we're a bridge, keep a per-country
     * count of how many client addresses have contacted us so that we can help
     * the bridge authority guess which countries have blocked access to us. */
diff --cc src/or/relay.c
index d12850d,66c24f3..7947ca3
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@@ -2144,14 -2153,68 +2152,68 @@@ cell_queue_append(cell_queue_t *queue, 
    ++queue->n;
  }
  
- /** Append a newly allocated copy of <b>cell</b> to the end of <b>queue</b> */
+ /** Append command of type <b>command</b> in direction to <b>queue</b> for
+  * CELL_STATS event. */
+ static void
+ cell_command_queue_append(cell_queue_t *queue, uint8_t command)
+ {
+   insertion_command_queue_t *ic_queue = queue->insertion_commands;
+   if (!ic_pool)
+     ic_pool = mp_pool_new(sizeof(insertion_command_elem_t), 1024);
+   if (!ic_queue) {
+     ic_queue = tor_malloc_zero(sizeof(insertion_command_queue_t));
+     queue->insertion_commands = ic_queue;
+   }
+   if (ic_queue->last && ic_queue->last->command == command) {
+     ic_queue->last->counter++;
+   } else {
+     insertion_command_elem_t *elem = mp_pool_get(ic_pool);
+     elem->next = NULL;
+     elem->command = command;
+     elem->counter = 1;
+     if (ic_queue->last) {
+       ic_queue->last->next = elem;
+       ic_queue->last = elem;
+     } else {
+       ic_queue->first = ic_queue->last = elem;
+     }
+   }
+ }
+ 
+ /** Retrieve oldest command from <b>queue</b> and write it to
+  * <b>command</b> for CELL_STATS event.  Return 0 for success, -1
+  * otherwise. */
+ static int
+ cell_command_queue_pop(uint8_t *command, cell_queue_t *queue)
+ {
+   int res = -1;
+   insertion_command_queue_t *ic_queue = queue->insertion_commands;
+   if (ic_queue && ic_queue->first) {
+     insertion_command_elem_t *ic_elem = ic_queue->first;
+     ic_elem->counter--;
+     if (ic_elem->counter < 1) {
+       ic_queue->first = ic_elem->next;
+       if (ic_elem == ic_queue->last)
+         ic_queue->last = NULL;
+       mp_pool_release(ic_elem);
+     }
+     *command = ic_elem->command;
+     res = 0;
+   }
+   return res;
+ }
+ 
+ /** Append a newly allocated copy of <b>cell</b> to the end of the
+  * <b>exitward</b> (or app-ward) <b>queue</b> of <b>circ</b>. */
  void
- cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell,
+ cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue,
+                               int exitward, const cell_t *cell,
 -                              int wide_circ_ids)
 +                              int wide_circ_ids, int use_stats)
  {
    packed_cell_t *copy = packed_cell_copy(cell, wide_circ_ids);
    /* Remember the time when this cell was put in the queue. */
-   if (get_options()->CellStatistics && use_stats) {
 -  if (get_options()->CellStatistics ||
 -      get_options()->TestingEnableCellStatsEvent) {
++  if ((get_options()->CellStatistics ||
++      get_options()->TestingEnableCellStatsEvent) && use_stats) {
      struct timeval now;
      uint32_t added;
      insertion_time_queue_t *it_queue = queue->insertion_times;
@@@ -2517,13 -2564,9 +2613,14 @@@ append_cell_to_circuit_queue(circuit_t 
                               cell_t *cell, cell_direction_t direction,
                               streamid_t fromstream)
  {
 +  or_circuit_t *orcirc = NULL;
    cell_queue_t *queue;
    int streams_blocked;
 +#if 0
 +  uint32_t tgt_max_middle_cells, p_len, n_len, tmp, hard_max_middle_cells;
 +#endif
 +
+   int exitward;
    if (circ->marked_for_close)
      return;
  
@@@ -2536,95 -2580,8 +2634,96 @@@
      streams_blocked = circ->streams_blocked_on_p_chan;
    }
  
 +  /*
 +   * Disabling this for now because of a possible guard discovery attack
 +   */
 +#if 0
 +  /* Are we a middle circuit about to exceed ORCIRC_MAX_MIDDLE_CELLS? */
 +  if ((circ->n_chan != NULL) && CIRCUIT_IS_ORCIRC(circ)) {
 +    orcirc = TO_OR_CIRCUIT(circ);
 +    if (orcirc->p_chan) {
 +      /* We are a middle circuit if we have both n_chan and p_chan */
 +      /* We'll need to know the current preferred maximum */
 +      tgt_max_middle_cells = get_max_middle_cells();
 +      if (tgt_max_middle_cells > 0) {
 +        /* Do we need to initialize middle_max_cells? */
 +        if (orcirc->max_middle_cells == 0) {
 +          orcirc->max_middle_cells = tgt_max_middle_cells;
 +        } else {
 +          if (tgt_max_middle_cells > orcirc->max_middle_cells) {
 +            /* If we want to increase the cap, we can do so right away */
 +            orcirc->max_middle_cells = tgt_max_middle_cells;
 +          } else if (tgt_max_middle_cells < orcirc->max_middle_cells) {
 +            /*
 +             * If we're shrinking the cap, we can't shrink past either queue;
 +             * compare tgt_max_middle_cells rather than tgt_max_middle_cells *
 +             * ORCIRC_MAX_MIDDLE_KILL_THRESH so the queues don't shrink enough
 +             * to generate spurious warnings, either.
 +             */
 +            n_len = circ->n_chan_cells.n;
 +            p_len = orcirc->p_chan_cells.n;
 +            tmp = tgt_max_middle_cells;
 +            if (tmp < n_len) tmp = n_len;
 +            if (tmp < p_len) tmp = p_len;
 +            orcirc->max_middle_cells = tmp;
 +          }
 +          /* else no change */
 +        }
 +      } else {
 +        /* tgt_max_middle_cells == 0 indicates we should disable the cap */
 +        orcirc->max_middle_cells = 0;
 +      }
 +
 +      /* Now we know orcirc->max_middle_cells is set correctly */
 +      if (orcirc->max_middle_cells > 0) {
 +        hard_max_middle_cells =
 +          (uint32_t)(((double)orcirc->max_middle_cells) *
 +                     ORCIRC_MAX_MIDDLE_KILL_THRESH);
 +
 +        if ((unsigned)queue->n + 1 >= hard_max_middle_cells) {
 +          /* Queueing this cell would put queue over the kill theshold */
 +          log_warn(LD_CIRC,
 +                   "Got a cell exceeding the hard cap of %u in the "
 +                   "%s direction on middle circ ID %u on chan ID "
 +                   U64_FORMAT "; killing the circuit.",
 +                   hard_max_middle_cells,
 +                   (direction == CELL_DIRECTION_OUT) ? "n" : "p",
 +                   (direction == CELL_DIRECTION_OUT) ?
 +                     circ->n_circ_id : orcirc->p_circ_id,
 +                   U64_PRINTF_ARG(
 +                     (direction == CELL_DIRECTION_OUT) ?
 +                        circ->n_chan->global_identifier :
 +                        orcirc->p_chan->global_identifier));
 +          circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
 +          return;
 +        } else if ((unsigned)queue->n + 1 == orcirc->max_middle_cells) {
 +          /* Only use ==, not >= for this test so we don't spam the log */
 +          log_warn(LD_CIRC,
 +                   "While trying to queue a cell, reached the soft cap of %u "
 +                   "in the %s direction on middle circ ID %u "
 +                   "on chan ID " U64_FORMAT ".",
 +                   orcirc->max_middle_cells,
 +                   (direction == CELL_DIRECTION_OUT) ? "n" : "p",
 +                   (direction == CELL_DIRECTION_OUT) ?
 +                     circ->n_circ_id : orcirc->p_circ_id,
 +                   U64_PRINTF_ARG(
 +                     (direction == CELL_DIRECTION_OUT) ?
 +                        circ->n_chan->global_identifier :
 +                        orcirc->p_chan->global_identifier));
 +        }
 +      }
 +    }
 +  }
 +#endif
 +
-   cell_queue_append_packed_copy(queue, cell, chan->wide_circ_ids, 1);
+   cell_queue_append_packed_copy(circ, queue, exitward, cell,
 -                                chan->wide_circ_ids);
++                                chan->wide_circ_ids, 1);
 +
 +  if (PREDICT_UNLIKELY(cell_queues_check_size())) {
 +    /* We ran the OOM handler */
 +    if (circ->marked_for_close)
 +      return;
 +  }
  
    /* If we have too many cells on the circuit, we should stop reading from
     * the edge streams for a while. */
diff --cc src/or/relay.h
index e1b5e38,b545857..20eecfb
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@@ -51,11 -50,11 +51,12 @@@ size_t packed_cell_mem_cost(void)
  /* For channeltls.c */
  void packed_cell_free(packed_cell_t *cell);
  
 +void cell_queue_init(cell_queue_t *queue);
  void cell_queue_clear(cell_queue_t *queue);
  void cell_queue_append(cell_queue_t *queue, packed_cell_t *cell);
- void cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell,
+ void cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue,
+                                    int exitward, const cell_t *cell,
 -                                   int wide_circ_ids);
 +                                   int wide_circ_ids, int use_stats);
  
  void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
                                    cell_t *cell, cell_direction_t direction,
diff --cc src/test/include.am
index 41e1153,84a7643..5510293
--- a/src/test/include.am
+++ b/src/test/include.am
@@@ -18,22 -14,16 +18,23 @@@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR=
  src_test_test_SOURCES = \
  	src/test/test.c \
  	src/test/test_addr.c \
 +	src/test/test_buffers.c \
  	src/test/test_cell_formats.c \
 +	src/test/test_circuitlist.c \
 +	src/test/test_circuitmux.c \
  	src/test/test_containers.c \
+ 	src/test/test_controller_events.c \
  	src/test/test_crypto.c \
 +	src/test/test_cell_queue.c \
  	src/test/test_data.c \
  	src/test/test_dir.c \
 +	src/test/test_extorport.c \
  	src/test/test_introduce.c \
  	src/test/test_microdesc.c \
 +	src/test/test_options.c \
  	src/test/test_pt.c \
  	src/test/test_replay.c \
 +	src/test/test_socks.c \
  	src/test/test_util.c \
  	src/test/test_config.c \
  	src/ext/tinytest.c
diff --cc src/test/test.c
index 6b45acf,eec591a..562de48
--- a/src/test/test.c
+++ b/src/test/test.c
@@@ -1614,12 -2133,7 +1614,13 @@@ extern struct testcase_t config_tests[]
  extern struct testcase_t introduce_tests[];
  extern struct testcase_t replaycache_tests[];
  extern struct testcase_t cell_format_tests[];
 +extern struct testcase_t circuitlist_tests[];
 +extern struct testcase_t circuitmux_tests[];
 +extern struct testcase_t cell_queue_tests[];
 +extern struct testcase_t options_tests[];
 +extern struct testcase_t socks_tests[];
 +extern struct testcase_t extorport_tests[];
+ extern struct testcase_t controller_event_tests[];
  
  static struct testgroup_t testgroups[] = {
    { "", test_array },
@@@ -1637,10 -2149,7 +1638,11 @@@
    { "config/", config_tests },
    { "replaycache/", replaycache_tests },
    { "introduce/", introduce_tests },
 +  { "circuitlist/", circuitlist_tests },
 +  { "circuitmux/", circuitmux_tests },
 +  { "options/", options_tests },
 +  { "extorport/", extorport_tests },
+   { "control/", controller_event_tests },
    END_OF_GROUPS
  };
  
diff --cc src/test/test_cell_queue.c
index cf2d11a,0000000..1eac073
mode 100644,000000..100644
--- a/src/test/test_cell_queue.c
+++ b/src/test/test_cell_queue.c
@@@ -1,146 -1,0 +1,148 @@@
 +/* Copyright (c) 2013, The Tor Project, Inc. */
 +/* See LICENSE for licensing information */
 +
 +#define CIRCUITLIST_PRIVATE
 +#define RELAY_PRIVATE
 +#include "or.h"
 +#include "circuitlist.h"
 +#include "relay.h"
 +#include "test.h"
 +
 +static void
 +test_cq_manip(void *arg)
 +{
 +  packed_cell_t *pc1=NULL, *pc2=NULL, *pc3=NULL, *pc4=NULL, *pc_tmp=NULL;
 +  cell_queue_t cq;
 +  cell_t cell;
 +  (void) arg;
 +
 +  init_cell_pool();
 +  cell_queue_init(&cq);
 +  tt_int_op(cq.n, ==, 0);
 +
 +  pc1 = packed_cell_new();
 +  pc2 = packed_cell_new();
 +  pc3 = packed_cell_new();
 +  pc4 = packed_cell_new();
 +  tt_assert(pc1 && pc2 && pc3 && pc4);
 +
 +  tt_ptr_op(NULL, ==, cell_queue_pop(&cq));
 +
 +  /* Add and remove a singleton. */
 +  cell_queue_append(&cq, pc1);
 +  tt_int_op(cq.n, ==, 1);
 +  tt_ptr_op(pc1, ==, cell_queue_pop(&cq));
 +  tt_int_op(cq.n, ==, 0);
 +
 +  /* Add and remove four items */
 +  cell_queue_append(&cq, pc4);
 +  cell_queue_append(&cq, pc3);
 +  cell_queue_append(&cq, pc2);
 +  cell_queue_append(&cq, pc1);
 +  tt_int_op(cq.n, ==, 4);
 +  tt_ptr_op(pc4, ==, cell_queue_pop(&cq));
 +  tt_ptr_op(pc3, ==, cell_queue_pop(&cq));
 +  tt_ptr_op(pc2, ==, cell_queue_pop(&cq));
 +  tt_ptr_op(pc1, ==, cell_queue_pop(&cq));
 +  tt_int_op(cq.n, ==, 0);
 +  tt_ptr_op(NULL, ==, cell_queue_pop(&cq));
 +
 +  /* Try a packed copy (wide, then narrow, which is a bit of a cheat, since a
 +   * real cell queue has only one type.) */
 +  memset(&cell, 0, sizeof(cell));
 +  cell.circ_id = 0x12345678;
 +  cell.command = 10;
 +  strlcpy((char*)cell.payload, "Lorax ipsum gruvvulus thneed amet, snergelly "
 +          "once-ler lerkim, sed do barbaloot tempor gluppitus ut labore et "
 +          "truffula magna aliqua.",
 +          sizeof(cell.payload));
-   cell_queue_append_packed_copy(&cq, &cell, 1 /*wide*/, 0 /*stats*/);
++  cell_queue_append_packed_copy(NULL /*circ*/, &cq, 0 /*exitward*/, &cell,
++                                1 /*wide*/, 0 /*stats*/);
 +  cell.circ_id = 0x2013;
-   cell_queue_append_packed_copy(&cq, &cell, 0 /*wide*/, 0 /*stats*/);
++  cell_queue_append_packed_copy(NULL /*circ*/, &cq, 0 /*exitward*/, &cell,
++                                0 /*wide*/, 0 /*stats*/);
 +  tt_int_op(cq.n, ==, 2);
 +
 +  pc_tmp = cell_queue_pop(&cq);
 +  tt_int_op(cq.n, ==, 1);
 +  tt_ptr_op(pc_tmp, !=, NULL);
 +  test_mem_op(pc_tmp->body, ==, "\x12\x34\x56\x78\x0a", 5);
 +  test_mem_op(pc_tmp->body+5, ==, cell.payload, sizeof(cell.payload));
 +  packed_cell_free(pc_tmp);
 +
 +  pc_tmp = cell_queue_pop(&cq);
 +  tt_int_op(cq.n, ==, 0);
 +  tt_ptr_op(pc_tmp, !=, NULL);
 +  test_mem_op(pc_tmp->body, ==, "\x20\x13\x0a", 3);
 +  test_mem_op(pc_tmp->body+3, ==, cell.payload, sizeof(cell.payload));
 +  packed_cell_free(pc_tmp);
 +  pc_tmp = NULL;
 +
 +  tt_ptr_op(NULL, ==, cell_queue_pop(&cq));
 +
 +  /* Now make sure cell_queue_clear works. */
 +  cell_queue_append(&cq, pc2);
 +  cell_queue_append(&cq, pc1);
 +  tt_int_op(cq.n, ==, 2);
 +  cell_queue_clear(&cq);
 +  pc2 = pc1 = NULL; /* prevent double-free */
 +  tt_int_op(cq.n, ==, 0);
 +
 + done:
 +  packed_cell_free(pc1);
 +  packed_cell_free(pc2);
 +  packed_cell_free(pc3);
 +  packed_cell_free(pc4);
 +  packed_cell_free(pc_tmp);
 +
 +  cell_queue_clear(&cq);
 +  free_cell_pool();
 +}
 +
 +static void
 +test_circuit_n_cells(void *arg)
 +{
 +  packed_cell_t *pc1=NULL, *pc2=NULL, *pc3=NULL, *pc4=NULL, *pc5=NULL;
 +  origin_circuit_t *origin_c=NULL;
 +  or_circuit_t *or_c=NULL;
 +
 +  (void)arg;
 +
 +  init_cell_pool();
 +
 +  pc1 = packed_cell_new();
 +  pc2 = packed_cell_new();
 +  pc3 = packed_cell_new();
 +  pc4 = packed_cell_new();
 +  pc5 = packed_cell_new();
 +  tt_assert(pc1 && pc2 && pc3 && pc4 && pc5);
 +
 +  or_c = or_circuit_new(0, NULL);
 +  origin_c = origin_circuit_new();
 +  origin_c->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL;
 +
 +  tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 0);
 +  cell_queue_append(&or_c->p_chan_cells, pc1);
 +  tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 1);
 +  cell_queue_append(&or_c->base_.n_chan_cells, pc2);
 +  cell_queue_append(&or_c->base_.n_chan_cells, pc3);
 +  tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 3);
 +
 +  tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), ==, 0);
 +  cell_queue_append(&origin_c->base_.n_chan_cells, pc4);
 +  cell_queue_append(&origin_c->base_.n_chan_cells, pc5);
 +  tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), ==, 2);
 +
 + done:
 +  circuit_free(TO_CIRCUIT(or_c));
 +  circuit_free(TO_CIRCUIT(origin_c));
 +
 +  free_cell_pool();
 +}
 +
 +struct testcase_t cell_queue_tests[] = {
 +  { "basic", test_cq_manip, TT_FORK, NULL, NULL, },
 +  { "circ_n_cells", test_circuit_n_cells, TT_FORK, NULL, NULL },
 +  END_OF_TESTCASES
 +};
 +






More information about the tor-commits mailing list