diff --git a/src/or/connection.c b/src/or/connection.c index 88def49e50..842523958e 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -17,6 +17,7 @@ * part of a subclass (channel_tls_t). */ #define TOR_CHANNEL_INTERNAL_ +#define CONNECTION_PRIVATE #include "channel.h" #include "channeltls.h" #include "circuitbuild.h" @@ -2561,7 +2562,7 @@ msec_since_midnight(struct timeval tvnow) * tokens_removed tokens removed at timestamp tvnow has run * out of tokens, and if so, note the milliseconds since midnight in * timestamp_var for the next TB_EMPTY event. */ -static void +void connection_buckets_note_empty_ts(uint32_t *timestamp_var, int tokens_before, size_t tokens_removed, struct timeval tvnow) @@ -2728,7 +2729,7 @@ connection_bucket_refill_helper(int *bucket, int rate, int burst, * tvnow, capped at milliseconds_elapsed milliseconds since * last refilling that bucket. Return 0 if the bucket has not been empty * since the last refill or has not been refilled. */ -static uint32_t +uint32_t bucket_millis_empty(int tokens_before, uint32_t last_empty_time, int tokens_after, int milliseconds_elapsed, struct timeval tvnow) diff --git a/src/or/connection.h b/src/or/connection.h index 3e656ec06e..c0894cd095 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -214,5 +214,16 @@ void connection_enable_rate_limiting(connection_t *conn); #define connection_type_uses_bufferevent(c) (0) #endif +#ifdef CONNECTION_PRIVATE +/* 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, + struct timeval tvnow); +void connection_buckets_note_empty_ts(uint32_t *timestamp_var, + int tokens_before, + size_t tokens_removed, + struct timeval tvnow); +#endif + #endif diff --git a/src/or/control.c b/src/or/control.c index 5e2020f7cb..495b7d7b02 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4015,27 +4015,11 @@ control_event_conn_bandwidth_used(void) return 0; } -/** 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; - /** Helper: iterate over cell statistics of circ and sum up added * cells, removed cells, and waiting times by cell command and direction. * Store results in cell_stats. Free cell statistics of the * circuit afterwards. */ -static void +void sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats) { memset(cell_stats, 0, sizeof(cell_stats_t)); @@ -4067,7 +4051,7 @@ sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats) * the (possibly zero) entry from number_to_include. If no * entry in include_if_non_zero is positive, no string will * be added to event_parts. */ -static void +void append_cell_stats_by_command(smartlist_t *event_parts, const char *key, uint64_t *include_if_non_zero, uint64_t *number_to_include) @@ -4092,7 +4076,7 @@ append_cell_stats_by_command(smartlist_t *event_parts, const char *key, /** Helper: format cell_stats for circ for inclusion in a * CELL_STATS event and write result string to event_string. */ -static void +void format_cell_stats(char **event_string, circuit_t *circ, cell_stats_t *cell_stats) { diff --git a/src/or/control.h b/src/or/control.h index c79c432ef1..1773a87374 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -97,9 +97,33 @@ void control_event_bootstrap_problem(const char *warn, int reason); void control_event_clients_seen(const char *controller_str); #ifdef CONTROL_PRIVATE -/* Used only by control.c and test.c */ +/* 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); + +/** 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, + uint64_t *include_if_non_zero, + uint64_t *number_to_include); +void format_cell_stats(char **event_string, circuit_t *circ, + cell_stats_t *cell_stats); #endif #endif diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake index 562c8df8b5..6479f9d39a 100644 --- a/src/test/Makefile.nmake +++ b/src/test/Makefile.nmake @@ -12,9 +12,9 @@ LIBS = ..\..\..\build-alpha\lib\libevent.lib \ crypt32.lib gdi32.lib user32.lib TEST_OBJECTS = test.obj test_addr.obj test_containers.obj \ - test_crypto.obj test_data.obj test_dir.obj test_microdesc.obj \ - test_pt.obj test_util.obj test_config.obj test_cell_formats.obj \ - test_replay.obj test_introduce.obj tinytest.obj + test_controller_events.ogj test_crypto.obj test_data.obj test_dir.obj \ + test_microdesc.obj test_pt.obj test_util.obj test_config.obj \ + test_cell_formats.obj test_replay.obj test_introduce.obj tinytest.obj tinytest.obj: ..\ext\tinytest.c $(CC) $(CFLAGS) /D snprintf=_snprintf /c ..\ext\tinytest.c diff --git a/src/test/include.am b/src/test/include.am index 112d1a79d8..84a7643b8d 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -16,6 +16,7 @@ src_test_test_SOURCES = \ src/test/test_addr.c \ src/test/test_cell_formats.c \ src/test/test_containers.c \ + src/test/test_controller_events.c \ src/test/test_crypto.c \ src/test/test_data.c \ src/test/test_dir.c \ diff --git a/src/test/test.c b/src/test/test.c index a9cf899a0e..eec591a166 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -2133,6 +2133,7 @@ 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 controller_event_tests[]; static struct testgroup_t testgroups[] = { { "", test_array }, @@ -2148,6 +2149,7 @@ static struct testgroup_t testgroups[] = { { "config/", config_tests }, { "replaycache/", replaycache_tests }, { "introduce/", introduce_tests }, + { "control/", controller_event_tests }, END_OF_GROUPS }; diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c new file mode 100644 index 0000000000..28ba4fa7aa --- /dev/null +++ b/src/test/test_controller_events.c @@ -0,0 +1,283 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONNECTION_PRIVATE +#define TOR_CHANNEL_INTERNAL_ +#define CONTROL_PRIVATE +#include "or.h" +#include "channel.h" +#include "channeltls.h" +#include "connection.h" +#include "control.h" +#include "test.h" + +static void +help_test_bucket_note_empty(uint32_t expected_msec_since_midnight, + int tokens_before, size_t tokens_removed, + uint32_t msec_since_epoch) +{ + uint32_t timestamp_var = 0; + struct timeval tvnow; + tvnow.tv_sec = msec_since_epoch / 1000; + tvnow.tv_usec = (msec_since_epoch % 1000) * 1000; + connection_buckets_note_empty_ts(×tamp_var, tokens_before, + tokens_removed, tvnow); + tt_int_op(expected_msec_since_midnight, ==, timestamp_var); + + done: + ; +} + +static void +test_cntev_bucket_note_empty(void *arg) +{ + (void)arg; + + /* Two cases with nothing to note, because bucket was empty before; + * 86442200 == 1970-01-02 00:00:42.200000 */ + help_test_bucket_note_empty(0, 0, 0, 86442200); + help_test_bucket_note_empty(0, -100, 100, 86442200); + + /* Nothing to note, because bucket has not been emptied. */ + help_test_bucket_note_empty(0, 101, 100, 86442200); + + /* Bucket was emptied, note 42200 msec since midnight. */ + help_test_bucket_note_empty(42200, 101, 101, 86442200); + help_test_bucket_note_empty(42200, 101, 102, 86442200); +} + +static void +test_cntev_bucket_millis_empty(void *arg) +{ + struct timeval tvnow; + (void)arg; + + /* 1970-01-02 00:00:42.200000 */ + tvnow.tv_sec = 86400 + 42; + tvnow.tv_usec = 200000; + + /* Bucket has not been refilled. */ + tt_int_op(0, ==, bucket_millis_empty(0, 42120, 0, 100, tvnow)); + tt_int_op(0, ==, bucket_millis_empty(-10, 42120, -10, 100, tvnow)); + + /* Bucket was not empty. */ + tt_int_op(0, ==, bucket_millis_empty(10, 42120, 20, 100, tvnow)); + + /* Bucket has been emptied 80 msec ago and has just been refilled. */ + tt_int_op(80, ==, bucket_millis_empty(-20, 42120, -10, 100, tvnow)); + tt_int_op(80, ==, bucket_millis_empty(-10, 42120, 0, 100, tvnow)); + tt_int_op(80, ==, bucket_millis_empty(0, 42120, 10, 100, tvnow)); + + /* Bucket has been emptied 180 msec ago, last refill was 100 msec ago + * which was insufficient to make it positive, so cap msec at 100. */ + tt_int_op(100, ==, bucket_millis_empty(0, 42020, 1, 100, tvnow)); + + /* 1970-01-02 00:00:00:050000 */ + tvnow.tv_sec = 86400; + tvnow.tv_usec = 50000; + + /* Last emptied 30 msec before midnight, tvnow is 50 msec after + * midnight, that's 80 msec in total. */ + tt_int_op(80, ==, bucket_millis_empty(0, 86400000 - 30, 1, 100, tvnow)); + + done: + ; +} + +static void +add_testing_cell_stats_entry(circuit_t *circ, uint8_t command, + unsigned int waiting_time, + unsigned int removed, unsigned int exitward) +{ + testing_cell_stats_entry_t *ent = tor_malloc_zero( + sizeof(testing_cell_stats_entry_t)); + ent->command = command; + ent->waiting_time = waiting_time; + ent->removed = removed; + ent->exitward = exitward; + if (!circ->testing_cell_stats) + circ->testing_cell_stats = smartlist_new(); + smartlist_add(circ->testing_cell_stats, ent); +} + +static void +test_cntev_sum_up_cell_stats(void *arg) +{ + or_circuit_t *or_circ; + circuit_t *circ; + cell_stats_t *cell_stats; + (void)arg; + + or_circ = tor_malloc_zero(sizeof(or_circuit_t)); + or_circ->base_.magic = OR_CIRCUIT_MAGIC; + or_circ->base_.purpose = CIRCUIT_PURPOSE_OR; + circ = TO_CIRCUIT(or_circ); + + /* A single RELAY cell was added to the appward queue. */ + cell_stats = tor_malloc_zero(sizeof(cell_stats_t)); + add_testing_cell_stats_entry(circ, CELL_RELAY, 0, 0, 0); + sum_up_cell_stats_by_command(circ, cell_stats); + tt_int_op(1, ==, cell_stats->added_cells_appward[CELL_RELAY]); + + /* A single RELAY cell was added to the exitward queue. */ + add_testing_cell_stats_entry(circ, CELL_RELAY, 0, 0, 1); + sum_up_cell_stats_by_command(circ, cell_stats); + tt_int_op(1, ==, cell_stats->added_cells_exitward[CELL_RELAY]); + + /* A single RELAY cell was removed from the appward queue where it spent + * 20 msec. */ + add_testing_cell_stats_entry(circ, CELL_RELAY, 2, 1, 0); + sum_up_cell_stats_by_command(circ, cell_stats); + tt_int_op(20, ==, cell_stats->total_time_appward[CELL_RELAY]); + tt_int_op(1, ==, cell_stats->removed_cells_appward[CELL_RELAY]); + + /* A single RELAY cell was removed from the exitward queue where it + * spent 30 msec. */ + add_testing_cell_stats_entry(circ, CELL_RELAY, 3, 1, 1); + sum_up_cell_stats_by_command(circ, cell_stats); + tt_int_op(30, ==, cell_stats->total_time_exitward[CELL_RELAY]); + tt_int_op(1, ==, cell_stats->removed_cells_exitward[CELL_RELAY]); + + done: + ; +} + +static void +test_cntev_append_cell_stats(void *arg) +{ + smartlist_t *event_parts; + const char *key = "Z"; + uint64_t include_if_non_zero[CELL_COMMAND_MAX_ + 1], + number_to_include[CELL_COMMAND_MAX_ + 1]; + (void)arg; + + event_parts = smartlist_new(); + memset(include_if_non_zero, 0, + (CELL_COMMAND_MAX_ + 1) * sizeof(uint64_t)); + memset(number_to_include, 0, + (CELL_COMMAND_MAX_ + 1) * sizeof(uint64_t)); + + /* All array entries empty. */ + append_cell_stats_by_command(event_parts, key, include_if_non_zero, + number_to_include); + tt_int_op(0, ==, smartlist_len(event_parts)); + + /* There's a RELAY cell to include, but the corresponding field in + * include_if_non_zero is still zero. */ + number_to_include[CELL_RELAY] = 1; + append_cell_stats_by_command(event_parts, key, include_if_non_zero, + number_to_include); + tt_int_op(0, ==, smartlist_len(event_parts)); + + /* Now include single RELAY cell. */ + include_if_non_zero[CELL_RELAY] = 2; + append_cell_stats_by_command(event_parts, key, include_if_non_zero, + number_to_include); + tt_str_op("Z=relay:1", ==, smartlist_pop_last(event_parts)); + + /* Add four CREATE cells. */ + include_if_non_zero[CELL_CREATE] = 3; + number_to_include[CELL_CREATE] = 4; + append_cell_stats_by_command(event_parts, key, include_if_non_zero, + number_to_include); + tt_str_op("Z=create:4,relay:1", ==, smartlist_pop_last(event_parts)); + + done: + ; +} + +static void +test_cntev_format_cell_stats(void *arg) +{ + char *event_string; + origin_circuit_t *ocirc; + or_circuit_t *or_circ; + cell_stats_t *cell_stats; + channel_tls_t *n_chan, *p_chan; + (void)arg; + + n_chan = tor_malloc_zero(sizeof(channel_tls_t)); + n_chan->base_.global_identifier = 1; + + ocirc = tor_malloc_zero(sizeof(origin_circuit_t)); + ocirc->base_.magic = ORIGIN_CIRCUIT_MAGIC; + ocirc->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; + ocirc->global_identifier = 2; + ocirc->base_.n_circ_id = 3; + ocirc->base_.n_chan = &(n_chan->base_); + + /* Origin circuit was completely idle. */ + cell_stats = tor_malloc_zero(sizeof(cell_stats_t)); + format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); + tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1", ==, event_string); + + /* Origin circuit had 4 RELAY cells added to its exitward queue. */ + cell_stats->added_cells_exitward[CELL_RELAY] = 4; + format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); + tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4", + ==, event_string); + + /* Origin circuit also had 5 CREATE2 cells added to its exitward + * queue. */ + cell_stats->added_cells_exitward[CELL_CREATE2] = 5; + format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); + tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4," + "create2:5", ==, event_string); + + /* Origin circuit also had 7 RELAY cells removed from its exitward queue + * which together spent 6 msec in the queue. */ + cell_stats->total_time_exitward[CELL_RELAY] = 6; + cell_stats->removed_cells_exitward[CELL_RELAY] = 7; + format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); + tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4," + "create2:5 OutboundRemoved=relay:7 OutboundTime=relay:6", + ==, event_string); + + p_chan = tor_malloc_zero(sizeof(channel_tls_t)); + p_chan->base_.global_identifier = 2; + + or_circ = tor_malloc_zero(sizeof(or_circuit_t)); + or_circ->base_.magic = OR_CIRCUIT_MAGIC; + or_circ->base_.purpose = CIRCUIT_PURPOSE_OR; + or_circ->p_circ_id = 8; + or_circ->p_chan = &(p_chan->base_); + or_circ->base_.n_circ_id = 9; + or_circ->base_.n_chan = &(n_chan->base_); + + /* OR circuit was idle. */ + cell_stats = tor_malloc_zero(sizeof(cell_stats_t)); + format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats); + tt_str_op("InboundQueue=8 InboundConn=2 OutboundQueue=9 OutboundConn=1", + ==, event_string); + + /* OR circuit had 3 RELAY cells added to its appward queue. */ + cell_stats->added_cells_appward[CELL_RELAY] = 3; + format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats); + tt_str_op("InboundQueue=8 InboundConn=2 InboundAdded=relay:3 " + "OutboundQueue=9 OutboundConn=1", ==, event_string); + + /* OR circuit had 7 RELAY cells removed from its appward queue which + * together spent 6 msec in the queue. */ + cell_stats->total_time_appward[CELL_RELAY] = 6; + cell_stats->removed_cells_appward[CELL_RELAY] = 7; + format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats); + tt_str_op("InboundQueue=8 InboundConn=2 InboundAdded=relay:3 " + "InboundRemoved=relay:7 InboundTime=relay:6 " + "OutboundQueue=9 OutboundConn=1", ==, event_string); + + done: + ; +} + +#define TEST(name, flags) \ + { #name, test_cntev_ ## name, flags, 0, NULL } + +struct testcase_t controller_event_tests[] = { + TEST(bucket_note_empty, 0), + TEST(bucket_millis_empty, 0), + TEST(sum_up_cell_stats, 0), + TEST(append_cell_stats, 0), + TEST(format_cell_stats, 0), + END_OF_TESTCASES +}; +