From e54d664f7bb1205162c1df3495f8ebc30c23d867 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Wed, 6 Feb 2013 14:04:27 +0100 Subject: [PATCH 01/15] Add connection ID to ORCONN event. Jointly authored with Rob Jansen . --- src/or/control.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/or/control.c b/src/or/control.c index 75f7af99a4..0f202c8648 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -3843,8 +3843,9 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp, orconn_target_get_name(name, sizeof(name), conn); send_control_event(EVENT_OR_CONN_STATUS, ALL_FORMATS, - "650 ORCONN %s %s %s%s%s\r\n", + "650 ORCONN %s %s ID="U64_FORMAT" %s%s%s\r\n", name, status, + U64_PRINTF_ARG(conn->base_.global_identifier), reason ? "REASON=" : "", orconn_end_reason_to_control_string(reason), ncircs_buf); From 8d1f78c556abb570bb80ea84261c954d9746cf33 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Wed, 6 Feb 2013 14:15:32 +0100 Subject: [PATCH 02/15] Add new CONN_BW event. Jointly authored with Rob Jansen . --- src/or/connection.c | 24 +++++++++++++++++++++ src/or/control.c | 52 ++++++++++++++++++++++++++++++++++++++++++++- src/or/control.h | 2 ++ src/or/main.c | 3 +++ src/or/or.h | 6 ++++++ 5 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/or/connection.c b/src/or/connection.c index 87fa79970e..81b499160a 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -3148,6 +3148,18 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read, else edge_conn->n_read = UINT32_MAX; } + + /* In TestingTorNetwork mode, update conn->n_read for OR/DIR/EXIT + * connections, checking for overflow. */ + if (get_options()->TestingTorNetwork && + (conn->type == CONN_TYPE_OR || + conn->type == CONN_TYPE_DIR || + conn->type == CONN_TYPE_EXIT)) { + if (PREDICT_LIKELY(UINT32_MAX - conn->n_read > n_read)) + conn->n_read += (int)n_read; + else + conn->n_read = UINT32_MAX; + } } connection_buckets_decrement(conn, approx_time(), n_read, n_written); @@ -3594,6 +3606,18 @@ connection_handle_write_impl(connection_t *conn, int force) edge_conn->n_written = UINT32_MAX; } + /* In TestingTorNetwork mode, update conn->n_written for OR/DIR/EXIT + * connections, checking for overflow. */ + if (n_written && get_options()->TestingTorNetwork && + (conn->type == CONN_TYPE_OR || + conn->type == CONN_TYPE_DIR || + conn->type == CONN_TYPE_EXIT)) { + if (PREDICT_LIKELY(UINT32_MAX - conn->n_written > n_written)) + conn->n_written += (int)n_written; + else + conn->n_written = UINT32_MAX; + } + connection_buckets_decrement(conn, approx_time(), n_read, n_written); if (result > 0) { diff --git a/src/or/control.c b/src/or/control.c index 0f202c8648..0a49c29d70 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -82,7 +82,8 @@ #define EVENT_BUILDTIMEOUT_SET 0x0017 #define EVENT_SIGNAL 0x0018 #define EVENT_CONF_CHANGED 0x0019 -#define EVENT_MAX_ 0x0019 +#define EVENT_CONN_BW 0x001A +#define EVENT_MAX_ 0x001A /* If EVENT_MAX_ ever hits 0x0020, we need to make the mask wider. */ /** Bitfield: The bit 1<<e is set if any open control @@ -958,6 +959,7 @@ static const struct control_event_t control_event_table[] = { { EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" }, { EVENT_SIGNAL, "SIGNAL" }, { EVENT_CONF_CHANGED, "CONF_CHANGED"}, + { EVENT_CONN_BW, "CONN_BW" }, { 0, NULL }, }; @@ -3906,6 +3908,54 @@ control_event_stream_bandwidth_used(void) return 0; } +/** Print out CONN_BW event for a single OR/DIR/EXIT conn and reset + * bandwidth counters. */ +int +control_event_conn_bandwidth(connection_t *conn) +{ + const char *conn_type_str; + if (!get_options()->TestingTorNetwork || + !EVENT_IS_INTERESTING(EVENT_CONN_BW)) + return 0; + if (!conn->n_read && !conn->n_written) + 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, + (unsigned long)conn->n_written); + conn->n_written = conn->n_read = 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()->TestingTorNetwork && + EVENT_IS_INTERESTING(EVENT_CONN_BW)) { + SMARTLIST_FOREACH(get_connection_array(), connection_t *, conn, + control_event_conn_bandwidth(conn)); + } + return 0; +} + /** A second or more has elapsed: tell any interested control * connections how much bandwidth we used. */ int diff --git a/src/or/control.h b/src/or/control.h index 61062da2c4..fe5c0efa6f 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -50,6 +50,8 @@ int control_event_or_conn_status(or_connection_t *conn, int control_event_bandwidth_used(uint32_t n_read, uint32_t n_written); int control_event_stream_bandwidth(edge_connection_t *edge_conn); int control_event_stream_bandwidth_used(void); +int control_event_conn_bandwidth(connection_t *conn); +int control_event_conn_bandwidth_used(void); void control_event_logmsg(int severity, uint32_t domain, const char *msg); int control_event_descriptors_changed(smartlist_t *routers); int control_event_address_mapped(const char *from, const char *to, diff --git a/src/or/main.c b/src/or/main.c index fd8b6cf674..c145f9dfda 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -351,6 +351,8 @@ connection_remove(connection_t *conn) (int)conn->s, conn_type_to_string(conn->type), smartlist_len(connection_array)); + control_event_conn_bandwidth(conn); + tor_assert(conn->conn_array_index >= 0); current_index = conn->conn_array_index; connection_unregister_events(conn); /* This is redundant, but cheap. */ @@ -1638,6 +1640,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written); control_event_stream_bandwidth_used(); + control_event_conn_bandwidth_used(); if (server_mode(options) && !net_is_disabled() && diff --git a/src/or/or.h b/src/or/or.h index 7b6c65fbb1..08a94aae6c 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1230,6 +1230,12 @@ typedef struct connection_t { /** Unique identifier for this connection on this Tor instance. */ uint64_t global_identifier; + + /** Bytes read since last call to control_event_conn_bandwidth_used() */ + uint32_t n_read; + + /** Bytes written since last call to control_event_conn_bandwidth_used() */ + uint32_t n_written; } connection_t; /** Subtype of connection_t; used for a listener socket. */ From c386d2d6ce4c4f58163acb385c7a5de1da8c5e30 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Wed, 6 Feb 2013 14:37:38 +0100 Subject: [PATCH 03/15] Add new CELL_STATS event. Jointly authored with Rob Jansen . --- src/or/control.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++- src/or/control.h | 1 + src/or/main.c | 1 + src/or/or.h | 36 +++++++++++ src/or/relay.c | 97 ++++++++++++++++++++++++---- src/or/relay.h | 3 +- 6 files changed, 290 insertions(+), 13 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 0a49c29d70..66258775e9 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -83,7 +83,8 @@ #define EVENT_SIGNAL 0x0018 #define EVENT_CONF_CHANGED 0x0019 #define EVENT_CONN_BW 0x001A -#define EVENT_MAX_ 0x001A +#define EVENT_CELL_STATS 0x001B +#define EVENT_MAX_ 0x001B /* If EVENT_MAX_ ever hits 0x0020, we need to make the mask wider. */ /** Bitfield: The bit 1<<e is set if any open control @@ -960,6 +961,7 @@ static const struct control_event_t control_event_table[] = { { EVENT_SIGNAL, "SIGNAL" }, { EVENT_CONF_CHANGED, "CONF_CHANGED"}, { EVENT_CONN_BW, "CONN_BW" }, + { EVENT_CELL_STATS, "CELL_STATS" }, { 0, NULL }, }; @@ -3956,6 +3958,167 @@ control_event_conn_bandwidth_used(void) return 0; } +extern circuit_t *global_circuitlist; + +/** Convert the cell command into a lower-case, human-readable + * string. */ +static const char * +cell_command_to_string(uint8_t command) +{ + switch (command) { + case CELL_PADDING: return "padding"; + case CELL_CREATE: return "create"; + case CELL_CREATED: return "created"; + case CELL_RELAY: return "relay"; + case CELL_DESTROY: return "destroy"; + case CELL_CREATE_FAST: return "create_fast"; + case CELL_CREATED_FAST: return "created_fast"; + case CELL_VERSIONS: return "versions"; + case CELL_NETINFO: return "netinfo"; + case CELL_RELAY_EARLY: return "relay_early"; + case CELL_CREATE2: return "create2"; + case CELL_CREATED2: return "created2"; + case CELL_VPADDING: return "vpadding"; + case CELL_CERTS: return "certs"; + case CELL_AUTH_CHALLENGE: return "auth_challenge"; + case CELL_AUTHENTICATE: return "authenticate"; + case CELL_AUTHORIZE: return "authorize"; + default: return "unrecognized"; + } +} + +/** Helper: append a cell statistics string to event_parts, + * prefixed with key=. 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 include_if_positive is positive, but with + * the (possibly zero) entry from number_to_include. If no + * entry in include_if_positive is positive, no string will + * be added to event_parts. */ +static void +append_cell_stats_by_command(smartlist_t *event_parts, const char *key, + uint64_t *include_if_positive, + uint64_t *number_to_include) +{ + smartlist_t *key_value_strings = smartlist_new(); + int i; + for (i = 0; i <= CELL_MAX_; i++) { + if (include_if_positive[i] > 0) { + smartlist_add_asprintf(key_value_strings, "%s:"U64_FORMAT, + cell_command_to_string(i), + U64_PRINTF_ARG(number_to_include[i])); + } + } + if (key_value_strings->num_used > 0) { + char *joined = smartlist_join_strings(key_value_strings, ",", 0, NULL); + char *result; + tor_asprintf(&result, "%s=%s", key, joined); + smartlist_add(event_parts, result); + SMARTLIST_FOREACH(key_value_strings, char *, cp, tor_free(cp)); + tor_free(joined); + } + smartlist_free(key_value_strings); +} + +/** 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) +{ + /* These arrays temporarily consume slightly over 6 KiB on the stack + * every second, most of which are wasted for the non-existant commands + * between CELL_RELAY_EARLY (9) and CELL_VPADDING (128). But nothing + * beats the stack when it comes to performance. */ + uint64_t added_cells_appward[CELL_MAX_ + 1], + added_cells_exitward[CELL_MAX_ + 1], + removed_cells_appward[CELL_MAX_ + 1], + removed_cells_exitward[CELL_MAX_ + 1], + total_time_appward[CELL_MAX_ + 1], + total_time_exitward[CELL_MAX_ + 1]; + circuit_t *circ; + if (!get_options()->TestingTorNetwork || + !EVENT_IS_INTERESTING(EVENT_CELL_STATS)) + return 0; + for (circ = global_circuitlist; circ; circ = circ->next) { + smartlist_t *event_parts; + char *event_string; + + if (!circ->testing_cell_stats) + continue; + + memset(added_cells_appward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); + memset(added_cells_exitward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); + memset(removed_cells_appward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); + memset(removed_cells_exitward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); + memset(total_time_appward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); + memset(total_time_exitward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); + SMARTLIST_FOREACH_BEGIN(circ->testing_cell_stats, + testing_cell_stats_entry_t *, ent) { + tor_assert(ent->command <= CELL_MAX_); + if (!ent->removed && !ent->exit_ward) { + added_cells_appward[ent->command] += 1; + } else if (!ent->removed && ent->exit_ward) { + added_cells_exitward[ent->command] += 1; + } else if (!ent->exit_ward) { + removed_cells_appward[ent->command] += 1; + total_time_appward[ent->command] += ent->waiting_time * 10; + } else { + removed_cells_exitward[ent->command] += 1; + 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; + + event_parts = smartlist_new(); + if (CIRCUIT_IS_ORIGIN(circ)) { + origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); + char *id_string; + tor_asprintf(&id_string, "ID=%lu", + (unsigned long)ocirc->global_identifier); + smartlist_add(event_parts, id_string); + } else { + or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); + char *queue_string, *conn_string; + tor_asprintf(&queue_string, "InboundQueue=%lu", + (unsigned long)or_circ->p_circ_id); + tor_asprintf(&conn_string, "InboundConn="U64_FORMAT, + U64_PRINTF_ARG(or_circ->p_chan->global_identifier)); + smartlist_add(event_parts, queue_string); + smartlist_add(event_parts, conn_string); + append_cell_stats_by_command(event_parts, "InboundAdded", + added_cells_appward, added_cells_appward); + append_cell_stats_by_command(event_parts, "InboundRemoved", + removed_cells_appward, removed_cells_appward); + append_cell_stats_by_command(event_parts, "InboundTime", + removed_cells_appward, total_time_appward); + } + if (circ->n_chan) { + char *queue_string, *conn_string; + tor_asprintf(&queue_string, "OutboundQueue=%lu", + (unsigned long)circ->n_circ_id); + tor_asprintf(&conn_string, "OutboundConn="U64_FORMAT, + U64_PRINTF_ARG(circ->n_chan->global_identifier)); + smartlist_add(event_parts, queue_string); + smartlist_add(event_parts, conn_string); + append_cell_stats_by_command(event_parts, "OutboundAdded", + added_cells_exitward, added_cells_exitward); + append_cell_stats_by_command(event_parts, "OutboundRemoved", + removed_cells_exitward, removed_cells_exitward); + append_cell_stats_by_command(event_parts, "OutboundTime", + removed_cells_exitward, total_time_exitward); + } + event_string = smartlist_join_strings(event_parts, " ", 0, NULL); + send_control_event(EVENT_CELL_STATS, ALL_FORMATS, + "650 CELL_STATS %s\r\n", event_string); + SMARTLIST_FOREACH(event_parts, char *, cp, tor_free(cp)); + smartlist_free(event_parts); + tor_free(event_string); + } + return 0; +} + /** A second or more has elapsed: tell any interested control * connections how much bandwidth we used. */ int diff --git a/src/or/control.h b/src/or/control.h index fe5c0efa6f..4d950bfc5d 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -52,6 +52,7 @@ int control_event_stream_bandwidth(edge_connection_t *edge_conn); int control_event_stream_bandwidth_used(void); int control_event_conn_bandwidth(connection_t *conn); int control_event_conn_bandwidth_used(void); +int control_event_circuit_cell_stats(void); void control_event_logmsg(int severity, uint32_t domain, const char *msg); int control_event_descriptors_changed(smartlist_t *routers); int control_event_address_mapped(const char *from, const char *to, diff --git a/src/or/main.c b/src/or/main.c index c145f9dfda..ae60548300 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1641,6 +1641,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written); control_event_stream_bandwidth_used(); control_event_conn_bandwidth_used(); + control_event_circuit_cell_stats(); if (server_mode(options) && !net_is_disabled() && diff --git a/src/or/or.h b/src/or/or.h index 08a94aae6c..22e4b96fcc 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -839,6 +839,7 @@ typedef enum { #define CELL_AUTH_CHALLENGE 130 #define CELL_AUTHENTICATE 131 #define CELL_AUTHORIZE 132 +#define CELL_MAX_ 132 /** How long to test reachability before complaining to the user. */ #define TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT (20*60) @@ -1087,6 +1088,21 @@ typedef struct insertion_time_queue_t { struct insertion_time_elem_t *last; /**< Last element in queue. */ } insertion_time_queue_t; +/** Number of cells with the same command consecutively added to a circuit + * queue; used for cell statistics only in TestingTorNetwork mode. */ +typedef struct insertion_command_elem_t { + struct insertion_command_elem_t *next; /**< Next element in queue. */ + /** Which command did these consecutively added cells have? */ + uint8_t command; + unsigned counter; /**< How many cells were inserted? */ +} insertion_command_elem_t; + +/** Queue of insertion commands. */ +typedef struct insertion_command_queue_t { + struct insertion_command_elem_t *first; /**< First element in queue. */ + struct insertion_command_elem_t *last; /**< Last element in queue. */ +} insertion_command_queue_t; + /** A queue of cells on a circuit, waiting to be added to the * or_connection_t's outbuf. */ typedef struct cell_queue_t { @@ -1094,6 +1110,8 @@ typedef struct cell_queue_t { packed_cell_t *tail; /**< The last cell, or NULL if the queue is empty. */ 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. */ @@ -2730,6 +2748,19 @@ typedef struct { struct create_cell_t; +/** Entry in the cell stats list of a circuit; used only when + * TestingTorNetwork is set. */ +typedef struct testing_cell_stats_entry_t { + uint8_t command; /**< cell command number. */ + /** Waiting time in centiseconds if this event is for a removed cell, + * or 0 if this event is for adding a cell to the queue. 22 bits can + * store more than 11 hours, enough to assume that a circuit with this + * delay would long have been closed. */ + unsigned int waiting_time:22; + unsigned int removed:1; /**< 0 for added to, 1 for removed from queue. */ + unsigned int exit_ward:1; /**< 0 for app-ward, 1 for exit-ward. */ +} testing_cell_stats_entry_t; + /** * A circuit is a path over the onion routing * network. Applications can connect to one end of the circuit, and can @@ -2855,6 +2886,11 @@ typedef struct circuit_t { * cells to n_conn. NULL if we have no cells pending, or if we're not * linked to an OR connection. */ struct circuit_t *prev_active_on_n_chan; + + /** Various statistics about cells being added to or removed from this + * circuit's queues; used only when TestingTorNetwork is set and cleared + * after being sent to control port. */ + smartlist_t *testing_cell_stats; } circuit_t; /** Largest number of relay_early cells that we can send on a given diff --git a/src/or/relay.c b/src/or/relay.c index 52ff32f0ca..02b3b1c5ca 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -2045,6 +2045,10 @@ static mp_pool_t *cell_pool = NULL; * statistics. */ static mp_pool_t *it_pool = NULL; +/** Memory pool to allocate insertion_command_elem_t objects used for cell + * statistics in TestingTorNetwork mode. */ +static mp_pool_t *ic_pool = NULL; + /** Allocate structures to hold cells. */ void init_cell_pool(void) @@ -2053,8 +2057,8 @@ init_cell_pool(void) cell_pool = mp_pool_new(sizeof(packed_cell_t), 128*1024); } -/** Free all storage used to hold cells (and insertion times if we measure - * cell statistics). */ +/** Free all storage used to hold cells (and insertion times/commands if we + * measure cell statistics and/or are in TestingTorNetwork mode). */ void free_cell_pool(void) { @@ -2067,6 +2071,10 @@ free_cell_pool(void) mp_pool_destroy(it_pool); it_pool = NULL; } + if (ic_pool) { + mp_pool_destroy(ic_pool); + ic_pool = NULL; + } } /** Free excess storage in cell pool. */ @@ -2145,14 +2153,16 @@ cell_queue_append(cell_queue_t *queue, packed_cell_t *cell) ++queue->n; } -/** Append a newly allocated copy of cell to the end of queue */ +/** Append a newly allocated copy of cell to the end of the + * exit_ward (or app-ward) queue of circ. */ 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 exit_ward, const cell_t *cell, int wide_circ_ids) { 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) { + if (get_options()->CellStatistics || get_options()->TestingTorNetwork) { struct timeval now; uint32_t added; insertion_time_queue_t *it_queue = queue->insertion_times; @@ -2181,6 +2191,38 @@ cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell, } } } + /* Remember that we added a cell to the queue, and remember the cell + * command. */ + if (get_options()->TestingTorNetwork) { + insertion_command_queue_t *ic_queue = queue->insertion_commands; + testing_cell_stats_entry_t *ent = + tor_malloc_zero(sizeof(testing_cell_stats_entry_t)); + ent->command = cell->command; + ent->exit_ward = exit_ward; + if (!circ->testing_cell_stats) + circ->testing_cell_stats = smartlist_new(); + smartlist_add(circ->testing_cell_stats, ent); + 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 == cell->command) { + ic_queue->last->counter++; + } else { + insertion_command_elem_t *elem = mp_pool_get(ic_pool); + elem->next = NULL; + elem->command = cell->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; + } + } + } cell_queue_append(queue, copy); } @@ -2386,7 +2428,8 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) cell = cell_queue_pop(queue); /* Calculate the exact time that this cell has spent in the queue. */ - if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) { + if (get_options()->CellStatistics || + get_options()->TestingTorNetwork) { struct timeval tvnow; uint32_t flushed; uint32_t cell_waiting_time; @@ -2400,7 +2443,6 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) "recently enabled."); } else { insertion_time_elem_t *elem = it_queue->first; - or_circ = TO_OR_CIRCUIT(circ); cell_waiting_time = (uint32_t)((flushed * 10L + SECONDS_IN_A_DAY * 1000L - elem->insertion_time * 10L) % @@ -2413,8 +2455,38 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) it_queue->last = NULL; mp_pool_release(elem); } - or_circ->total_cell_waiting_time += cell_waiting_time; - or_circ->processed_cells++; + if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) { + or_circ = TO_OR_CIRCUIT(circ); + or_circ->total_cell_waiting_time += cell_waiting_time; + or_circ->processed_cells++; + } + if (get_options()->TestingTorNetwork) { + insertion_command_queue_t *ic_queue = queue->insertion_commands; + if (!ic_queue || !ic_queue->first) { + log_info(LD_BUG, "Cannot determine command of cell, which " + "is a bug, because TestingTorNetwork cannot " + "be enabled while running."); + } else { + testing_cell_stats_entry_t *ent = + tor_malloc_zero(sizeof(testing_cell_stats_entry_t)); + insertion_command_elem_t *ic_elem = ic_queue->first; + ent->command = ic_elem->command; + 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); + } + ent->waiting_time = (unsigned int)cell_waiting_time / 10; + ent->removed = 1; + if (circ->n_chan == chan) + ent->exit_ward = 1; + if (!circ->testing_cell_stats) + circ->testing_cell_stats = smartlist_new(); + smartlist_add(circ->testing_cell_stats, ent); + } + } } } @@ -2470,10 +2542,12 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, { cell_queue_t *queue; int streams_blocked; + int exit_ward; if (circ->marked_for_close) return; - if (direction == CELL_DIRECTION_OUT) { + exit_ward = (direction == CELL_DIRECTION_OUT); + if (exit_ward) { queue = &circ->n_chan_cells; streams_blocked = circ->streams_blocked_on_n_chan; } else { @@ -2482,7 +2556,8 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, streams_blocked = circ->streams_blocked_on_p_chan; } - cell_queue_append_packed_copy(queue, cell, chan->wide_circ_ids); + cell_queue_append_packed_copy(circ, queue, exit_ward, cell, + chan->wide_circ_ids); /* If we have too many cells on the circuit, we should stop reading from * the edge streams for a while. */ diff --git a/src/or/relay.h b/src/or/relay.h index 229fb4f737..e3b392ce7f 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -52,7 +52,8 @@ void packed_cell_free(packed_cell_t *cell); 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 exit_ward, const cell_t *cell, int wide_circ_ids); void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, From dd5ce2157d8c47ffa3686b0579813e7b1aae8069 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Wed, 6 Feb 2013 14:26:11 +0100 Subject: [PATCH 04/15] Add new TB_EMPTY event. Jointly authored with Rob Jansen . --- src/or/connection.c | 64 +++++++++++++++++++++++++++ src/or/control.c | 102 +++++++++++++++++++++++++++++++++++++++++++- src/or/control.h | 12 ++++++ src/or/or.h | 6 +++ 4 files changed, 183 insertions(+), 1 deletion(-) diff --git a/src/or/connection.c b/src/or/connection.c index 81b499160a..dfcd1ab210 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -2541,6 +2541,27 @@ record_num_bytes_transferred(connection_t *conn, #endif #ifndef USE_BUFFEREVENTS +/** Last emptied global or relay buckets in msec since midnight; only used + * in TestingTorNetwork mode. */ +static uint32_t global_relayed_read_emptied = 0, + global_relayed_write_emptied = 0, + global_read_emptied = 0, + global_write_emptied = 0; + +/** Check if a bucket has just run out of tokens, and if so, note the + * timestamp for TB_EMPTY events; only used in TestingTorNetwork mode. */ +static void +connection_buckets_empty_ts(uint32_t *timestamp_var, int tokens_before, + size_t tokens_removed) +{ + if (tokens_before > 0 && tokens_before - (int)tokens_removed <= 0) { + struct timeval tvnow; + tor_gettimeofday_cached(&tvnow); + *timestamp_var = (uint32_t)(((tvnow.tv_sec % 86400L) * 1000L) + + ((uint32_t)tvnow.tv_usec / (uint32_t)1000L)); + } +} + /** We just read num_read and wrote num_written bytes * onto conn. Decrement buckets appropriately. */ static void @@ -2563,6 +2584,28 @@ connection_buckets_decrement(connection_t *conn, time_t now, if (!connection_is_rate_limited(conn)) return; /* local IPs are free */ + /* If one or more of our token buckets ran dry just now, note the + * timestamp for TB_EMPTY events. */ + if (get_options()->TestingTorNetwork) { + if (connection_counts_as_relayed_traffic(conn, now)) { + connection_buckets_empty_ts(&global_relayed_read_emptied, + global_relayed_read_bucket, num_read); + connection_buckets_empty_ts(&global_relayed_write_emptied, + global_relayed_write_bucket, num_written); + } + connection_buckets_empty_ts(&global_read_emptied, global_read_bucket, + num_read); + connection_buckets_empty_ts(&global_write_emptied, global_write_bucket, + num_written); + if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) { + or_connection_t *or_conn = TO_OR_CONN(conn); + connection_buckets_empty_ts(&or_conn->read_emptied_time, + or_conn->read_bucket, num_read); + connection_buckets_empty_ts(&or_conn->write_emptied_time, + or_conn->write_bucket, num_written); + } + } + if (connection_counts_as_relayed_traffic(conn, now)) { global_relayed_read_bucket -= (int)num_read; global_relayed_write_bucket -= (int)num_written; @@ -2677,6 +2720,11 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now) smartlist_t *conns = get_connection_array(); int bandwidthrate, bandwidthburst, relayrate, relayburst; + int prev_global_read = global_read_bucket; + int prev_global_write = global_write_bucket; + int prev_relay_read = global_relayed_read_bucket; + int prev_relay_write = global_relayed_write_bucket; + bandwidthrate = (int)options->BandwidthRate; bandwidthburst = (int)options->BandwidthBurst; @@ -2711,12 +2759,25 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now) milliseconds_elapsed, "global_relayed_write_bucket"); + control_event_refill_global(global_read_bucket, prev_global_read, + global_read_emptied, global_write_bucket, + prev_global_write, global_write_emptied, + global_relayed_read_bucket, prev_relay_read, + global_relayed_read_emptied, + global_relayed_write_bucket, prev_relay_write, + global_relayed_write_emptied, + milliseconds_elapsed); + /* refill the per-connection buckets */ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { if (connection_speaks_cells(conn)) { or_connection_t *or_conn = TO_OR_CONN(conn); int orbandwidthrate = or_conn->bandwidthrate; int orbandwidthburst = or_conn->bandwidthburst; + + int prev_conn_read = or_conn->read_bucket; + int prev_conn_write = or_conn->write_bucket; + if (connection_bucket_should_increase(or_conn->read_bucket, or_conn)) { connection_bucket_refill_helper(&or_conn->read_bucket, orbandwidthrate, @@ -2731,6 +2792,9 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now) milliseconds_elapsed, "or_conn->write_bucket"); } + + control_event_refill_conn(or_conn, prev_conn_read, prev_conn_write, + (uint32_t)milliseconds_elapsed); } if (conn->read_blocked_on_bw == 1 /* marked to turn reading back on now */ diff --git a/src/or/control.c b/src/or/control.c index 66258775e9..47d08c295e 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -84,7 +84,8 @@ #define EVENT_CONF_CHANGED 0x0019 #define EVENT_CONN_BW 0x001A #define EVENT_CELL_STATS 0x001B -#define EVENT_MAX_ 0x001B +#define EVENT_TB_EMPTY 0x001C +#define EVENT_MAX_ 0x001C /* If EVENT_MAX_ ever hits 0x0020, we need to make the mask wider. */ /** Bitfield: The bit 1<<e is set if any open control @@ -962,6 +963,7 @@ static const struct control_event_t control_event_table[] = { { EVENT_CONF_CHANGED, "CONF_CHANGED"}, { EVENT_CONN_BW, "CONN_BW" }, { EVENT_CELL_STATS, "CELL_STATS" }, + { EVENT_TB_EMPTY, "TB_EMPTY" }, { 0, NULL }, }; @@ -4119,6 +4121,104 @@ control_event_circuit_cell_stats(void) return 0; } +/** Helper: return the time in millis that a given bucket was empty, + * capped at the time in millis since last refilling that bucket. Return + * 0 if the bucket was not empty during the last refill period. */ +static uint32_t +bucket_millis_empty(int prev_tokens, uint32_t last_empty_time, + uint32_t milliseconds_elapsed) +{ + uint32_t result = 0, refilled; + if (prev_tokens <= 0) { + struct timeval tvnow; + tor_gettimeofday_cached(&tvnow); + refilled = (uint32_t)((tvnow.tv_sec % 86400L) * 1000L + + (uint32_t)tvnow.tv_usec / (uint32_t)1000L); + result = (uint32_t)((refilled + 86400L * 1000L - last_empty_time) % + (86400L * 1000L)); + if (result > milliseconds_elapsed) + result = milliseconds_elapsed; + } + return result; +} + +/** Token buckets have been refilled: tell any interested control + * connections how global and relay token buckets have changed. */ +int +control_event_refill_global(int global_read, int prev_global_read, + uint32_t global_read_emptied_time, + int global_write, int prev_global_write, + uint32_t global_write_emptied_time, + int relay_read, int prev_relay_read, + uint32_t relay_read_emptied_time, + int relay_write, int prev_relay_write, + uint32_t relay_write_emptied_time, + uint32_t milliseconds_elapsed) +{ + uint32_t global_read_empty_time, global_write_empty_time, + relay_read_empty_time, relay_write_empty_time; + if (!get_options()->TestingTorNetwork || + !EVENT_IS_INTERESTING(EVENT_TB_EMPTY)) + return 0; + if (prev_global_read == global_read && + prev_global_write == global_write && + prev_relay_read == relay_read && + prev_relay_write == relay_write) + return 0; + if (prev_global_read <= 0 && prev_global_write <= 0) { + global_read_empty_time = bucket_millis_empty(prev_global_read, + global_read_emptied_time, milliseconds_elapsed); + global_write_empty_time = bucket_millis_empty(prev_global_write, + global_write_emptied_time, milliseconds_elapsed); + send_control_event(EVENT_TB_EMPTY, ALL_FORMATS, + "650 TB_EMPTY GLOBAL READ=%d WRITTEN=%d " + "LAST=%d\r\n", + global_read_empty_time, global_write_empty_time, + milliseconds_elapsed); + } + if (prev_relay_read <= 0 && prev_relay_write <= 0) { + relay_read_empty_time = bucket_millis_empty(prev_relay_read, + relay_read_emptied_time, milliseconds_elapsed); + relay_write_empty_time = bucket_millis_empty(prev_relay_write, + relay_write_emptied_time, milliseconds_elapsed); + send_control_event(EVENT_TB_EMPTY, ALL_FORMATS, + "650 TB_EMPTY RELAY READ=%d WRITTEN=%d " + "LAST=%d\r\n", + relay_read_empty_time, relay_write_empty_time, + milliseconds_elapsed); + } + return 0; +} + +/** Token buckets of a connection have been refilled: tell any interested + * control connections how per-connection token buckets have changed. */ +int +control_event_refill_conn(or_connection_t *or_conn, + int prev_read, int prev_write, + uint32_t milliseconds_elapsed) +{ + uint32_t read_bucket_empty_time, write_bucket_empty_time; + if (!get_options()->TestingTorNetwork || + !EVENT_IS_INTERESTING(EVENT_TB_EMPTY)) + return 0; + if (prev_read == or_conn->read_bucket && + prev_write == or_conn->write_bucket) + return 0; + if (prev_read <= 0 || prev_write <= 0) { + read_bucket_empty_time = bucket_millis_empty(prev_read, + or_conn->read_emptied_time, milliseconds_elapsed); + write_bucket_empty_time = bucket_millis_empty(prev_write, + or_conn->write_emptied_time, milliseconds_elapsed); + send_control_event(EVENT_TB_EMPTY, ALL_FORMATS, + "650 TB_EMPTY ORCONN ID="U64_FORMAT" READ=%d " + "WRITTEN=%d LAST=%d\r\n", + U64_PRINTF_ARG(or_conn->base_.global_identifier), + read_bucket_empty_time, write_bucket_empty_time, + milliseconds_elapsed); + } + return 0; +} + /** A second or more has elapsed: tell any interested control * connections how much bandwidth we used. */ int diff --git a/src/or/control.h b/src/or/control.h index 4d950bfc5d..2977159b91 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -53,6 +53,18 @@ int control_event_stream_bandwidth_used(void); int control_event_conn_bandwidth(connection_t *conn); int control_event_conn_bandwidth_used(void); int control_event_circuit_cell_stats(void); +int control_event_refill_global(int global_read, int prev_global_read, + uint32_t global_read_emptied, + int global_write, int prev_global_write, + uint32_t global_write_emptied, + int relay_read, int prev_relay_read, + uint32_t relay_read_emptied, + int relay_write, int prev_relay_write, + uint32_t relay_write_emptied, + uint32_t milliseconds_elapsed); +int control_event_refill_conn(or_connection_t *or_conn, + int prev_read, int prev_write, + uint32_t milliseconds_elapsed); void control_event_logmsg(int severity, uint32_t domain, const char *msg); int control_event_descriptors_changed(smartlist_t *routers); int control_event_address_mapped(const char *from, const char *to, diff --git a/src/or/or.h b/src/or/or.h index 22e4b96fcc..c2be282911 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1477,6 +1477,12 @@ typedef struct or_connection_t { struct or_connection_t *next_with_same_id; /**< Next connection with same * identity digest as this one. */ + /** Last emptied read token bucket in msec since midnight; only used in + * TestingTorNetwork mode. */ + uint32_t read_emptied_time; + /** Last emptied write token bucket in msec since midnight; only used in + * TestingTorNetwork mode. */ + uint32_t write_emptied_time; } or_connection_t; /** Subtype of connection_t for an "edge connection" -- that is, an entry (ap) From 2925e2fe786dfd9a27c2dff503996712cd180de4 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Sun, 24 Feb 2013 13:32:57 +0100 Subject: [PATCH 05/15] Add new CIRC_BW event. --- src/or/connection.c | 23 +++++++++++++++++- src/or/control.c | 58 +++++++++++++++++++++++++++++++++++++++++---- src/or/control.h | 1 + src/or/main.c | 1 + src/or/or.h | 9 +++++++ 5 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/or/connection.c b/src/or/connection.c index dfcd1ab210..a00351a77a 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -3203,14 +3203,25 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read, /* change *max_to_read */ *max_to_read = at_most - n_read; - /* Update edge_conn->n_read */ + /* Update edge_conn->n_read and ocirc->n_read */ if (conn->type == CONN_TYPE_AP) { edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + circuit_t *circ = circuit_get_by_edge_conn(edge_conn); + origin_circuit_t *ocirc; + /* Check for overflow: */ if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_read > n_read)) edge_conn->n_read += (int)n_read; else edge_conn->n_read = UINT32_MAX; + + if (circ && CIRCUIT_IS_ORIGIN(circ)) { + ocirc = TO_ORIGIN_CIRCUIT(circ); + if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_read > n_read)) + ocirc->n_read += (int)n_read; + else + ocirc->n_read = UINT32_MAX; + } } /* In TestingTorNetwork mode, update conn->n_read for OR/DIR/EXIT @@ -3662,12 +3673,22 @@ connection_handle_write_impl(connection_t *conn, int force) if (n_written && conn->type == CONN_TYPE_AP) { edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + circuit_t *circ = circuit_get_by_edge_conn(edge_conn); + origin_circuit_t *ocirc; /* Check for overflow: */ if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_written > n_written)) edge_conn->n_written += (int)n_written; else edge_conn->n_written = UINT32_MAX; + + if (circ && CIRCUIT_IS_ORIGIN(circ)) { + ocirc = TO_ORIGIN_CIRCUIT(circ); + if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_written > n_written)) + ocirc->n_written += (int)n_written; + else + ocirc->n_written = UINT32_MAX; + } } /* In TestingTorNetwork mode, update conn->n_written for OR/DIR/EXIT diff --git a/src/or/control.c b/src/or/control.c index 47d08c295e..d3b968cde4 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -46,6 +46,8 @@ #include #endif +extern circuit_t *global_circuitlist; /* from circuitlist.c */ + #include "procmon.h" /** Yield true iff s is the state of a control_connection_t that has @@ -85,7 +87,8 @@ #define EVENT_CONN_BW 0x001A #define EVENT_CELL_STATS 0x001B #define EVENT_TB_EMPTY 0x001C -#define EVENT_MAX_ 0x001C +#define EVENT_CIRC_BANDWIDTH_USED 0x001D +#define EVENT_MAX_ 0x001D /* If EVENT_MAX_ ever hits 0x0020, we need to make the mask wider. */ /** Bitfield: The bit 1<<e is set if any open control @@ -260,8 +263,8 @@ control_update_global_event_mask(void) * we want to hear...*/ control_adjust_event_log_severity(); - /* ...then, if we've started logging stream bw, clear the appropriate - * fields. */ + /* ...then, if we've started logging stream or circ bw, clear the + * appropriate fields. */ if (! (old_mask & EVENT_STREAM_BANDWIDTH_USED) && (new_mask & EVENT_STREAM_BANDWIDTH_USED)) { SMARTLIST_FOREACH(conns, connection_t *, conn, @@ -272,6 +275,17 @@ control_update_global_event_mask(void) } }); } + if (! (old_mask & EVENT_CIRC_BANDWIDTH_USED) && + (new_mask & EVENT_CIRC_BANDWIDTH_USED)) { + circuit_t *circ; + origin_circuit_t *ocirc; + for (circ = global_circuitlist; circ; circ = circ->next) { + if (!CIRCUIT_IS_ORIGIN(circ)) + continue; + ocirc = TO_ORIGIN_CIRCUIT(circ); + ocirc->n_written = ocirc->n_read = 0; + } + } } /** Adjust the log severities that result in control_event_logmsg being called @@ -964,6 +978,7 @@ static const struct control_event_t control_event_table[] = { { EVENT_CONN_BW, "CONN_BW" }, { EVENT_CELL_STATS, "CELL_STATS" }, { EVENT_TB_EMPTY, "TB_EMPTY" }, + { EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" }, { 0, NULL }, }; @@ -3865,6 +3880,8 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp, int control_event_stream_bandwidth(edge_connection_t *edge_conn) { + circuit_t *circ; + origin_circuit_t *ocirc; if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) { if (!edge_conn->n_read && !edge_conn->n_written) return 0; @@ -3875,6 +3892,12 @@ control_event_stream_bandwidth(edge_connection_t *edge_conn) (unsigned long)edge_conn->n_read, (unsigned long)edge_conn->n_written); + circ = circuit_get_by_edge_conn(edge_conn); + if (circ && CIRCUIT_IS_ORIGIN(circ)) { + ocirc = TO_ORIGIN_CIRCUIT(circ); + ocirc->n_read += edge_conn->n_read; + ocirc->n_written += edge_conn->n_written; + } edge_conn->n_written = edge_conn->n_read = 0; } @@ -3912,6 +3935,33 @@ control_event_stream_bandwidth_used(void) 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 = global_circuitlist; circ; circ = circ->next) { + if (!CIRCUIT_IS_ORIGIN(circ)) + continue; + ocirc = TO_ORIGIN_CIRCUIT(circ); + if (!ocirc->n_read && !ocirc->n_written) + 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, + (unsigned long)ocirc->n_written); + ocirc->n_written = ocirc->n_read = 0; + } + + return 0; +} + /** Print out CONN_BW event for a single OR/DIR/EXIT conn and reset * bandwidth counters. */ int @@ -3960,8 +4010,6 @@ control_event_conn_bandwidth_used(void) return 0; } -extern circuit_t *global_circuitlist; - /** Convert the cell command into a lower-case, human-readable * string. */ static const char * diff --git a/src/or/control.h b/src/or/control.h index 2977159b91..06a3849493 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -50,6 +50,7 @@ int control_event_or_conn_status(or_connection_t *conn, int control_event_bandwidth_used(uint32_t n_read, uint32_t n_written); int control_event_stream_bandwidth(edge_connection_t *edge_conn); int control_event_stream_bandwidth_used(void); +int control_event_circ_bandwidth_used(void); int control_event_conn_bandwidth(connection_t *conn); int control_event_conn_bandwidth_used(void); int control_event_circuit_cell_stats(void); diff --git a/src/or/main.c b/src/or/main.c index ae60548300..d38167789a 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1641,6 +1641,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written); control_event_stream_bandwidth_used(); control_event_conn_bandwidth_used(); + control_event_circ_bandwidth_used(); control_event_circuit_cell_stats(); if (server_mode(options) && diff --git a/src/or/or.h b/src/or/or.h index c2be282911..d41034cb72 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2967,6 +2967,15 @@ typedef struct origin_circuit_t { /** Linked list of AP streams (or EXIT streams if hidden service) * associated with this circuit. */ edge_connection_t *p_streams; + + /** Bytes read from any attached stream since last call to + * control_event_circ_bandwidth_used() */ + uint32_t n_read; + + /** Bytes written to any attached stream since last call to + * control_event_circ_bandwidth_used() */ + uint32_t n_written; + /** Build state for this circuit. It includes the intended path * length, the chosen exit router, rendezvous information, etc. */ From a84fae789206db4e27486e693488328da7b3474a Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Fri, 24 May 2013 11:25:30 +0200 Subject: [PATCH 06/15] Tweak ORCONN event based on comments by nickm. - Move new ID= parameter in ORCONN event to end. Avoids possible trouble from controllers that parse parameters by position, even though they shouldn't. --- src/or/control.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index d3b968cde4..2accf7f4af 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -3858,18 +3858,17 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp, } ncircs += connection_or_get_num_circuits(conn); if (ncircs && (tp == OR_CONN_EVENT_FAILED || tp == OR_CONN_EVENT_CLOSED)) { - tor_snprintf(ncircs_buf, sizeof(ncircs_buf), "%sNCIRCS=%d", - reason ? " " : "", ncircs); + tor_snprintf(ncircs_buf, sizeof(ncircs_buf), " NCIRCS=%d", ncircs); } orconn_target_get_name(name, sizeof(name), conn); send_control_event(EVENT_OR_CONN_STATUS, ALL_FORMATS, - "650 ORCONN %s %s ID="U64_FORMAT" %s%s%s\r\n", + "650 ORCONN %s %s%s%s%s ID="U64_FORMAT"\r\n", name, status, - U64_PRINTF_ARG(conn->base_.global_identifier), - reason ? "REASON=" : "", + reason ? " REASON=" : "", orconn_end_reason_to_control_string(reason), - ncircs_buf); + ncircs_buf, + U64_PRINTF_ARG(conn->base_.global_identifier)); return 0; } From 2f893624abb65a0df4f0f8ca6fbbe0c00fbf216a Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Fri, 24 May 2013 12:01:32 +0200 Subject: [PATCH 07/15] Tweak CONN_BW event based on comments by nickm. - Rename read/write counters in connection_t to make it clear that these are only used for CONN_BW events. - Add TestingEnableConnBwEvent option. --- doc/tor.1.txt | 6 ++++++ src/or/config.c | 8 ++++++++ src/or/connection.c | 24 ++++++++++++------------ src/or/control.c | 12 ++++++------ src/or/or.h | 13 +++++++++---- 5 files changed, 41 insertions(+), 22 deletions(-) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 0a6f8f5245..209670a158 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2005,6 +2005,7 @@ The following options are used for running a testing Tor network. TestingV3AuthInitialDistDelay 20 seconds TestingAuthDirTimeToLearnReachability 0 minutes TestingEstimatedDescriptorPropagationTime 0 minutes + TestingEnableConnBwEvent 1 **TestingV3AuthInitialVotingInterval** __N__ **minutes**|**hours**:: Like V3AuthVotingInterval, but for initial voting interval before the first @@ -2035,6 +2036,11 @@ The following options are used for running a testing Tor network. Minimum value for the Fast flag. Overrides the ordinary minimum taken from the consensus when TestingTorNetwork is set. (Default: 0.) +**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) + SIGNALS ------- diff --git a/src/or/config.c b/src/or/config.c index 8ca89b6a77..e7060a5dc4 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -218,6 +218,7 @@ static config_var_t option_vars_[] = { VPORT(DNSPort, LINELIST, NULL), V(DNSListenAddress, LINELIST, NULL), V(DownloadExtraInfo, BOOL, "0"), + V(TestingEnableConnBwEvent, BOOL, "0"), V(EnforceDistinctSubnets, BOOL, "1"), V(EntryNodes, ROUTERSET, NULL), V(EntryStatistics, BOOL, "0"), @@ -461,6 +462,7 @@ static const config_var_t testing_tor_network_defaults[] = { V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"), V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"), V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"), + V(TestingEnableConnBwEvent, BOOL, "1"), VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } @@ -3236,6 +3238,12 @@ options_validate(or_options_t *old_options, or_options_t *options, COMPLAIN("TestingEstimatedDescriptorPropagationTime is insanely high."); } + if (options->TestingEnableConnBwEvent && + !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { + REJECT("TestingEnableConnBwEvent 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 --git a/src/or/connection.c b/src/or/connection.c index a00351a77a..f7f028b0b5 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -3224,16 +3224,16 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read, } } - /* In TestingTorNetwork mode, update conn->n_read for OR/DIR/EXIT - * connections, checking for overflow. */ - if (get_options()->TestingTorNetwork && + /* If CONN_BW events are enabled, update conn->n_read_conn_bw for + * OR/DIR/EXIT connections, checking for overflow. */ + if (get_options()->TestingEnableConnBwEvent && (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_DIR || conn->type == CONN_TYPE_EXIT)) { - if (PREDICT_LIKELY(UINT32_MAX - conn->n_read > n_read)) - conn->n_read += (int)n_read; + if (PREDICT_LIKELY(UINT32_MAX - conn->n_read_conn_bw > n_read)) + conn->n_read_conn_bw += (int)n_read; else - conn->n_read = UINT32_MAX; + conn->n_read_conn_bw = UINT32_MAX; } } @@ -3691,16 +3691,16 @@ connection_handle_write_impl(connection_t *conn, int force) } } - /* In TestingTorNetwork mode, update conn->n_written for OR/DIR/EXIT - * connections, checking for overflow. */ - if (n_written && get_options()->TestingTorNetwork && + /* If CONN_BW events are enabled, update conn->n_written_conn_bw for + * OR/DIR/EXIT connections, checking for overflow. */ + if (n_written && get_options()->TestingEnableConnBwEvent && (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_DIR || conn->type == CONN_TYPE_EXIT)) { - if (PREDICT_LIKELY(UINT32_MAX - conn->n_written > n_written)) - conn->n_written += (int)n_written; + if (PREDICT_LIKELY(UINT32_MAX - conn->n_written_conn_bw > n_written)) + conn->n_written_conn_bw += (int)n_written; else - conn->n_written = UINT32_MAX; + conn->n_written_conn_bw = UINT32_MAX; } connection_buckets_decrement(conn, approx_time(), n_read, n_written); diff --git a/src/or/control.c b/src/or/control.c index 2accf7f4af..10f96b345b 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -3967,10 +3967,10 @@ int control_event_conn_bandwidth(connection_t *conn) { const char *conn_type_str; - if (!get_options()->TestingTorNetwork || + if (!get_options()->TestingEnableConnBwEvent || !EVENT_IS_INTERESTING(EVENT_CONN_BW)) return 0; - if (!conn->n_read && !conn->n_written) + if (!conn->n_read_conn_bw && !conn->n_written_conn_bw) return 0; switch (conn->type) { case CONN_TYPE_OR: @@ -3990,9 +3990,9 @@ control_event_conn_bandwidth(connection_t *conn) "READ=%lu WRITTEN=%lu\r\n", U64_PRINTF_ARG(conn->global_identifier), conn_type_str, - (unsigned long)conn->n_read, - (unsigned long)conn->n_written); - conn->n_written = conn->n_read = 0; + (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; } @@ -4001,7 +4001,7 @@ control_event_conn_bandwidth(connection_t *conn) int control_event_conn_bandwidth_used(void) { - if (get_options()->TestingTorNetwork && + if (get_options()->TestingEnableConnBwEvent && EVENT_IS_INTERESTING(EVENT_CONN_BW)) { SMARTLIST_FOREACH(get_connection_array(), connection_t *, conn, control_event_conn_bandwidth(conn)); diff --git a/src/or/or.h b/src/or/or.h index d41034cb72..c807fb00cb 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1249,11 +1249,13 @@ typedef struct connection_t { /** Unique identifier for this connection on this Tor instance. */ uint64_t global_identifier; - /** Bytes read since last call to control_event_conn_bandwidth_used() */ - uint32_t n_read; + /** Bytes read since last call to control_event_conn_bandwidth_used(). + * Only used if we're configured to emit CONN_BW events. */ + uint32_t n_read_conn_bw; - /** Bytes written since last call to control_event_conn_bandwidth_used() */ - uint32_t n_written; + /** Bytes written since last call to control_event_conn_bandwidth_used(). + * Only used if we're configured to emit CONN_BW events. */ + uint32_t n_written_conn_bw; } connection_t; /** Subtype of connection_t; used for a listener socket. */ @@ -3983,6 +3985,9 @@ typedef struct { /** Minimum value for the Fast flag threshold on testing networks. */ uint64_t TestingMinFastFlagThreshold; + /** Enable CONN_BW events. Only altered on testing networks. */ + int TestingEnableConnBwEvent; + /** 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. */ From 26b49f525d5b2a4781755d72738491c016dd15a9 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Fri, 24 May 2013 12:29:42 +0200 Subject: [PATCH 08/15] Tweak CELL_STATS event based on comments by nickm. - Move cell_command_to_string from control.c to command.c. - Use accessor for global_circuitlist instead of extern. - Add a struct for cell statistics by command instead of six arrays. - Split up control_event_circuit_cell_stats by using two helper functions. - Add TestingEnableCellStatsEvent option. - Prepare functions for testing. - Rename a few variables and document a few things better. --- doc/tor.1.txt | 6 ++ src/or/command.c | 27 ++++++ src/or/command.h | 2 + src/or/config.c | 8 ++ src/or/control.c | 225 ++++++++++++++++++++++------------------------- src/or/or.h | 17 ++-- src/or/relay.c | 122 ++++++++++++++----------- src/or/relay.h | 2 +- 8 files changed, 233 insertions(+), 176 deletions(-) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 209670a158..253436e1fa 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2006,6 +2006,7 @@ The following options are used for running a testing Tor network. TestingAuthDirTimeToLearnReachability 0 minutes TestingEstimatedDescriptorPropagationTime 0 minutes TestingEnableConnBwEvent 1 + TestingEnableCellStatsEvent 1 **TestingV3AuthInitialVotingInterval** __N__ **minutes**|**hours**:: Like V3AuthVotingInterval, but for initial voting interval before the first @@ -2041,6 +2042,11 @@ The following options are used for running a testing Tor network. events. Changing this requires that **TestingTorNetwork** is set. (Default: 0) +**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) + SIGNALS ------- diff --git a/src/or/command.c b/src/or/command.c index 876ff526a6..d08633512e 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -52,6 +52,33 @@ static void command_process_created_cell(cell_t *cell, channel_t *chan); static void command_process_relay_cell(cell_t *cell, channel_t *chan); static void command_process_destroy_cell(cell_t *cell, channel_t *chan); +/** Convert the cell command into a lower-case, human-readable + * string. */ +const char * +cell_command_to_string(uint8_t command) +{ + switch (command) { + case CELL_PADDING: return "padding"; + case CELL_CREATE: return "create"; + case CELL_CREATED: return "created"; + case CELL_RELAY: return "relay"; + case CELL_DESTROY: return "destroy"; + case CELL_CREATE_FAST: return "create_fast"; + case CELL_CREATED_FAST: return "created_fast"; + case CELL_VERSIONS: return "versions"; + case CELL_NETINFO: return "netinfo"; + case CELL_RELAY_EARLY: return "relay_early"; + case CELL_CREATE2: return "create2"; + case CELL_CREATED2: return "created2"; + case CELL_VPADDING: return "vpadding"; + case CELL_CERTS: return "certs"; + case CELL_AUTH_CHALLENGE: return "auth_challenge"; + case CELL_AUTHENTICATE: return "authenticate"; + case CELL_AUTHORIZE: return "authorize"; + default: return "unrecognized"; + } +} + #ifdef KEEP_TIMING_STATS /** This is a wrapper function around the actual function that processes the * cell that just arrived on conn. Increment *time diff --git a/src/or/command.h b/src/or/command.h index 913f46a5cd..adea6adeaa 100644 --- a/src/or/command.h +++ b/src/or/command.h @@ -19,6 +19,8 @@ void command_process_var_cell(channel_t *chan, var_cell_t *cell); void command_setup_channel(channel_t *chan); void command_setup_listener(channel_listener_t *chan_l); +const char *cell_command_to_string(uint8_t command); + extern uint64_t stats_n_padding_cells_processed; extern uint64_t stats_n_create_cells_processed; extern uint64_t stats_n_created_cells_processed; diff --git a/src/or/config.c b/src/or/config.c index e7060a5dc4..7a17a9f1f8 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -219,6 +219,7 @@ static config_var_t option_vars_[] = { V(DNSListenAddress, LINELIST, NULL), V(DownloadExtraInfo, BOOL, "0"), V(TestingEnableConnBwEvent, BOOL, "0"), + V(TestingEnableCellStatsEvent, BOOL, "0"), V(EnforceDistinctSubnets, BOOL, "1"), V(EntryNodes, ROUTERSET, NULL), V(EntryStatistics, BOOL, "0"), @@ -463,6 +464,7 @@ static const config_var_t testing_tor_network_defaults[] = { V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"), V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"), V(TestingEnableConnBwEvent, BOOL, "1"), + V(TestingEnableCellStatsEvent, BOOL, "1"), VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } @@ -3244,6 +3246,12 @@ options_validate(or_options_t *old_options, or_options_t *options, "Tor networks!"); } + if (options->TestingEnableCellStatsEvent && + !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { + REJECT("TestingEnableCellStatsEvent 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 --git a/src/or/control.c b/src/or/control.c index 10f96b345b..c06a91182b 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -19,6 +19,7 @@ #include "circuitlist.h" #include "circuitstats.h" #include "circuituse.h" +#include "command.h" #include "config.h" #include "confparse.h" #include "connection.h" @@ -46,8 +47,6 @@ #include #endif -extern circuit_t *global_circuitlist; /* from circuitlist.c */ - #include "procmon.h" /** Yield true iff s is the state of a control_connection_t that has @@ -279,7 +278,7 @@ control_update_global_event_mask(void) (new_mask & EVENT_CIRC_BANDWIDTH_USED)) { circuit_t *circ; origin_circuit_t *ocirc; - for (circ = global_circuitlist; circ; circ = circ->next) { + for (circ = circuit_get_global_list_(); circ; circ = circ->next) { if (!CIRCUIT_IS_ORIGIN(circ)) continue; ocirc = TO_ORIGIN_CIRCUIT(circ); @@ -3944,7 +3943,7 @@ control_event_circ_bandwidth_used(void) if (!EVENT_IS_INTERESTING(EVENT_CIRC_BANDWIDTH_USED)) return 0; - for (circ = global_circuitlist; circ; circ = circ->next) { + for (circ = circuit_get_global_list_(); circ; circ = circ->next) { if (!CIRCUIT_IS_ORIGIN(circ)) continue; ocirc = TO_ORIGIN_CIRCUIT(circ); @@ -4009,162 +4008,150 @@ control_event_conn_bandwidth_used(void) return 0; } -/** Convert the cell command into a lower-case, human-readable - * string. */ -static const char * -cell_command_to_string(uint8_t command) +/** 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 +sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats) { - switch (command) { - case CELL_PADDING: return "padding"; - case CELL_CREATE: return "create"; - case CELL_CREATED: return "created"; - case CELL_RELAY: return "relay"; - case CELL_DESTROY: return "destroy"; - case CELL_CREATE_FAST: return "create_fast"; - case CELL_CREATED_FAST: return "created_fast"; - case CELL_VERSIONS: return "versions"; - case CELL_NETINFO: return "netinfo"; - case CELL_RELAY_EARLY: return "relay_early"; - case CELL_CREATE2: return "create2"; - case CELL_CREATED2: return "created2"; - case CELL_VPADDING: return "vpadding"; - case CELL_CERTS: return "certs"; - case CELL_AUTH_CHALLENGE: return "auth_challenge"; - case CELL_AUTHENTICATE: return "authenticate"; - case CELL_AUTHORIZE: return "authorize"; - default: return "unrecognized"; - } + 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 event_parts, * prefixed with key=. 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 include_if_positive is positive, but with + * if the entry in include_if_non_zero is not zero, but with * the (possibly zero) entry from number_to_include. If no - * entry in include_if_positive is positive, no string will + * entry in include_if_non_zero is positive, no string will * be added to event_parts. */ static void append_cell_stats_by_command(smartlist_t *event_parts, const char *key, - uint64_t *include_if_positive, + uint64_t *include_if_non_zero, uint64_t *number_to_include) { smartlist_t *key_value_strings = smartlist_new(); int i; - for (i = 0; i <= CELL_MAX_; i++) { - if (include_if_positive[i] > 0) { + 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 (key_value_strings->num_used > 0) { + if (smartlist_len(key_value_strings) > 0) { char *joined = smartlist_join_strings(key_value_strings, ",", 0, NULL); - char *result; - tor_asprintf(&result, "%s=%s", key, joined); - smartlist_add(event_parts, result); + 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 cell_stats for circ for inclusion in a + * CELL_STATS event and write result string to event_string. */ +static 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 { + 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) { - /* These arrays temporarily consume slightly over 6 KiB on the stack - * every second, most of which are wasted for the non-existant commands - * between CELL_RELAY_EARLY (9) and CELL_VPADDING (128). But nothing - * beats the stack when it comes to performance. */ - uint64_t added_cells_appward[CELL_MAX_ + 1], - added_cells_exitward[CELL_MAX_ + 1], - removed_cells_appward[CELL_MAX_ + 1], - removed_cells_exitward[CELL_MAX_ + 1], - total_time_appward[CELL_MAX_ + 1], - total_time_exitward[CELL_MAX_ + 1]; circuit_t *circ; - if (!get_options()->TestingTorNetwork || + cell_stats_t *cell_stats; + char *event_string; + if (!get_options()->TestingEnableCellStatsEvent || !EVENT_IS_INTERESTING(EVENT_CELL_STATS)) return 0; - for (circ = global_circuitlist; circ; circ = circ->next) { - smartlist_t *event_parts; - char *event_string; - + cell_stats = tor_malloc(sizeof(cell_stats_t));; + for (circ = circuit_get_global_list_(); circ; circ = circ->next) { if (!circ->testing_cell_stats) continue; - - memset(added_cells_appward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); - memset(added_cells_exitward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); - memset(removed_cells_appward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); - memset(removed_cells_exitward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); - memset(total_time_appward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); - memset(total_time_exitward, 0, (CELL_MAX_ + 1) * sizeof(uint64_t)); - SMARTLIST_FOREACH_BEGIN(circ->testing_cell_stats, - testing_cell_stats_entry_t *, ent) { - tor_assert(ent->command <= CELL_MAX_); - if (!ent->removed && !ent->exit_ward) { - added_cells_appward[ent->command] += 1; - } else if (!ent->removed && ent->exit_ward) { - added_cells_exitward[ent->command] += 1; - } else if (!ent->exit_ward) { - removed_cells_appward[ent->command] += 1; - total_time_appward[ent->command] += ent->waiting_time * 10; - } else { - removed_cells_exitward[ent->command] += 1; - 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; - - event_parts = smartlist_new(); - if (CIRCUIT_IS_ORIGIN(circ)) { - origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); - char *id_string; - tor_asprintf(&id_string, "ID=%lu", - (unsigned long)ocirc->global_identifier); - smartlist_add(event_parts, id_string); - } else { - or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - char *queue_string, *conn_string; - tor_asprintf(&queue_string, "InboundQueue=%lu", - (unsigned long)or_circ->p_circ_id); - tor_asprintf(&conn_string, "InboundConn="U64_FORMAT, - U64_PRINTF_ARG(or_circ->p_chan->global_identifier)); - smartlist_add(event_parts, queue_string); - smartlist_add(event_parts, conn_string); - append_cell_stats_by_command(event_parts, "InboundAdded", - added_cells_appward, added_cells_appward); - append_cell_stats_by_command(event_parts, "InboundRemoved", - removed_cells_appward, removed_cells_appward); - append_cell_stats_by_command(event_parts, "InboundTime", - removed_cells_appward, total_time_appward); - } - if (circ->n_chan) { - char *queue_string, *conn_string; - tor_asprintf(&queue_string, "OutboundQueue=%lu", - (unsigned long)circ->n_circ_id); - tor_asprintf(&conn_string, "OutboundConn="U64_FORMAT, - U64_PRINTF_ARG(circ->n_chan->global_identifier)); - smartlist_add(event_parts, queue_string); - smartlist_add(event_parts, conn_string); - append_cell_stats_by_command(event_parts, "OutboundAdded", - added_cells_exitward, added_cells_exitward); - append_cell_stats_by_command(event_parts, "OutboundRemoved", - removed_cells_exitward, removed_cells_exitward); - append_cell_stats_by_command(event_parts, "OutboundTime", - removed_cells_exitward, total_time_exitward); - } - event_string = smartlist_join_strings(event_parts, " ", 0, NULL); + 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); - SMARTLIST_FOREACH(event_parts, char *, cp, tor_free(cp)); - smartlist_free(event_parts); tor_free(event_string); } + tor_free(cell_stats); return 0; } diff --git a/src/or/or.h b/src/or/or.h index c807fb00cb..3702664a23 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -839,7 +839,7 @@ typedef enum { #define CELL_AUTH_CHALLENGE 130 #define CELL_AUTHENTICATE 131 #define CELL_AUTHORIZE 132 -#define CELL_MAX_ 132 +#define CELL_COMMAND_MAX_ 132 /** How long to test reachability before complaining to the user. */ #define TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT (20*60) @@ -1089,7 +1089,7 @@ typedef struct insertion_time_queue_t { } insertion_time_queue_t; /** Number of cells with the same command consecutively added to a circuit - * queue; used for cell statistics only in TestingTorNetwork mode. */ + * queue; used for cell statistics only if CELL_STATS events are enabled. */ typedef struct insertion_command_elem_t { struct insertion_command_elem_t *next; /**< Next element in queue. */ /** Which command did these consecutively added cells have? */ @@ -2756,8 +2756,8 @@ typedef struct { struct create_cell_t; -/** Entry in the cell stats list of a circuit; used only when - * TestingTorNetwork is set. */ +/** Entry in the cell stats list of a circuit; used only if CELL_STATS + * events are enabled. */ typedef struct testing_cell_stats_entry_t { uint8_t command; /**< cell command number. */ /** Waiting time in centiseconds if this event is for a removed cell, @@ -2766,7 +2766,7 @@ typedef struct testing_cell_stats_entry_t { * delay would long have been closed. */ unsigned int waiting_time:22; unsigned int removed:1; /**< 0 for added to, 1 for removed from queue. */ - unsigned int exit_ward:1; /**< 0 for app-ward, 1 for exit-ward. */ + unsigned int exitward:1; /**< 0 for app-ward, 1 for exit-ward. */ } testing_cell_stats_entry_t; /** @@ -2896,8 +2896,8 @@ typedef struct circuit_t { struct circuit_t *prev_active_on_n_chan; /** Various statistics about cells being added to or removed from this - * circuit's queues; used only when TestingTorNetwork is set and cleared - * after being sent to control port. */ + * circuit's queues; used only if CELL_STATS events are enabled and + * cleared after being sent to control port. */ smartlist_t *testing_cell_stats; } circuit_t; @@ -3988,6 +3988,9 @@ typedef struct { /** Enable CONN_BW events. Only altered on testing networks. */ int TestingEnableConnBwEvent; + /** Enable CELL_STATS events. Only altered on testing networks. */ + int TestingEnableCellStatsEvent; + /** 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 --git a/src/or/relay.c b/src/or/relay.c index 02b3b1c5ca..66c24f3f23 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -2046,7 +2046,7 @@ static mp_pool_t *cell_pool = NULL; static mp_pool_t *it_pool = NULL; /** Memory pool to allocate insertion_command_elem_t objects used for cell - * statistics in TestingTorNetwork mode. */ + * statistics if CELL_STATS events are enabled. */ static mp_pool_t *ic_pool = NULL; /** Allocate structures to hold cells. */ @@ -2058,7 +2058,7 @@ init_cell_pool(void) } /** Free all storage used to hold cells (and insertion times/commands if we - * measure cell statistics and/or are in TestingTorNetwork mode). */ + * measure cell statistics and/or if CELL_STATS events are enabled). */ void free_cell_pool(void) { @@ -2153,16 +2153,68 @@ cell_queue_append(cell_queue_t *queue, packed_cell_t *cell) ++queue->n; } +/** Append command of type command in direction to queue 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 queue and write it to + * command 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 cell to the end of the - * exit_ward (or app-ward) queue of circ. */ + * exitward (or app-ward) queue of circ. */ void cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue, - int exit_ward, const cell_t *cell, + int exitward, const cell_t *cell, int wide_circ_ids) { 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 || get_options()->TestingTorNetwork) { + if (get_options()->CellStatistics || + get_options()->TestingEnableCellStatsEvent) { struct timeval now; uint32_t added; insertion_time_queue_t *it_queue = queue->insertion_times; @@ -2193,35 +2245,15 @@ cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue, } /* Remember that we added a cell to the queue, and remember the cell * command. */ - if (get_options()->TestingTorNetwork) { - insertion_command_queue_t *ic_queue = queue->insertion_commands; + if (get_options()->TestingEnableCellStatsEvent) { testing_cell_stats_entry_t *ent = tor_malloc_zero(sizeof(testing_cell_stats_entry_t)); ent->command = cell->command; - ent->exit_ward = exit_ward; + ent->exitward = exitward; if (!circ->testing_cell_stats) circ->testing_cell_stats = smartlist_new(); smartlist_add(circ->testing_cell_stats, ent); - 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 == cell->command) { - ic_queue->last->counter++; - } else { - insertion_command_elem_t *elem = mp_pool_get(ic_pool); - elem->next = NULL; - elem->command = cell->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; - } - } + cell_command_queue_append(queue, cell->command); } cell_queue_append(queue, copy); } @@ -2429,7 +2461,7 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) /* Calculate the exact time that this cell has spent in the queue. */ if (get_options()->CellStatistics || - get_options()->TestingTorNetwork) { + get_options()->TestingEnableCellStatsEvent) { struct timeval tvnow; uint32_t flushed; uint32_t cell_waiting_time; @@ -2460,28 +2492,20 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) or_circ->total_cell_waiting_time += cell_waiting_time; or_circ->processed_cells++; } - if (get_options()->TestingTorNetwork) { - insertion_command_queue_t *ic_queue = queue->insertion_commands; - if (!ic_queue || !ic_queue->first) { - log_info(LD_BUG, "Cannot determine command of cell, which " - "is a bug, because TestingTorNetwork cannot " - "be enabled while running."); + if (get_options()->TestingEnableCellStatsEvent) { + uint8_t command; + if (cell_command_queue_pop(&command, queue) < 0) { + log_info(LD_GENERAL, "Cannot determine command of cell. " + "Looks like the CELL_STATS event was " + "recently enabled."); } else { testing_cell_stats_entry_t *ent = tor_malloc_zero(sizeof(testing_cell_stats_entry_t)); - insertion_command_elem_t *ic_elem = ic_queue->first; - ent->command = ic_elem->command; - 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); - } + ent->command = command; ent->waiting_time = (unsigned int)cell_waiting_time / 10; ent->removed = 1; if (circ->n_chan == chan) - ent->exit_ward = 1; + ent->exitward = 1; if (!circ->testing_cell_stats) circ->testing_cell_stats = smartlist_new(); smartlist_add(circ->testing_cell_stats, ent); @@ -2542,12 +2566,12 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, { cell_queue_t *queue; int streams_blocked; - int exit_ward; + int exitward; if (circ->marked_for_close) return; - exit_ward = (direction == CELL_DIRECTION_OUT); - if (exit_ward) { + exitward = (direction == CELL_DIRECTION_OUT); + if (exitward) { queue = &circ->n_chan_cells; streams_blocked = circ->streams_blocked_on_n_chan; } else { @@ -2556,7 +2580,7 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, streams_blocked = circ->streams_blocked_on_p_chan; } - cell_queue_append_packed_copy(circ, queue, exit_ward, cell, + cell_queue_append_packed_copy(circ, queue, exitward, cell, chan->wide_circ_ids); /* If we have too many cells on the circuit, we should stop reading from diff --git a/src/or/relay.h b/src/or/relay.h index e3b392ce7f..b5458575ee 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -53,7 +53,7 @@ void packed_cell_free(packed_cell_t *cell); 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(circuit_t *circ, cell_queue_t *queue, - int exit_ward, const cell_t *cell, + int exitward, const cell_t *cell, int wide_circ_ids); void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, From ef67077fba6061a6e5b9a76caf60a33d17a81ce6 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Sat, 25 May 2013 12:21:09 +0200 Subject: [PATCH 09/15] Tweak TB_EMPTY event based on comments by nickm. - Avoid control_event_refill_global function with 13 arguments and increase code reuse factor by moving more code from control.c to connection.c. - Avoid an unsafe uint32_t -> int cast. - Add TestingEnableTbEmptyEvent option. - Prepare functions for testing. - Rename a few functions and improve documentation. --- doc/tor.1.txt | 6 ++ src/or/config.c | 8 +++ src/or/connection.c | 140 ++++++++++++++++++++++++++++++++------------ src/or/control.c | 102 ++++---------------------------- src/or/control.h | 15 +---- src/or/or.h | 11 ++-- 6 files changed, 141 insertions(+), 141 deletions(-) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 253436e1fa..399633406d 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2007,6 +2007,7 @@ The following options are used for running a testing Tor network. TestingEstimatedDescriptorPropagationTime 0 minutes TestingEnableConnBwEvent 1 TestingEnableCellStatsEvent 1 + TestingEnableTbEmptyEvent 1 **TestingV3AuthInitialVotingInterval** __N__ **minutes**|**hours**:: Like V3AuthVotingInterval, but for initial voting interval before the first @@ -2047,6 +2048,11 @@ The following options are used for running a testing Tor network. events. Changing this requires that **TestingTorNetwork** is set. (Default: 0) +**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 --git a/src/or/config.c b/src/or/config.c index 7a17a9f1f8..4ca0338d74 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -220,6 +220,7 @@ static config_var_t option_vars_[] = { V(DownloadExtraInfo, BOOL, "0"), V(TestingEnableConnBwEvent, BOOL, "0"), V(TestingEnableCellStatsEvent, BOOL, "0"), + V(TestingEnableTbEmptyEvent, BOOL, "0"), V(EnforceDistinctSubnets, BOOL, "1"), V(EntryNodes, ROUTERSET, NULL), V(EntryStatistics, BOOL, "0"), @@ -465,6 +466,7 @@ static const config_var_t testing_tor_network_defaults[] = { V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"), V(TestingEnableConnBwEvent, BOOL, "1"), V(TestingEnableCellStatsEvent, BOOL, "1"), + V(TestingEnableTbEmptyEvent, BOOL, "1"), VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } @@ -3252,6 +3254,12 @@ options_validate(or_options_t *old_options, or_options_t *options, "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 --git a/src/or/connection.c b/src/or/connection.c index f7f028b0b5..2f2a421fca 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -2541,25 +2541,33 @@ record_num_bytes_transferred(connection_t *conn, #endif #ifndef USE_BUFFEREVENTS -/** Last emptied global or relay buckets in msec since midnight; only used - * in TestingTorNetwork mode. */ +/** Last time at which the global or relay buckets were emptied in msec + * since midnight. */ static uint32_t global_relayed_read_emptied = 0, global_relayed_write_emptied = 0, global_read_emptied = 0, global_write_emptied = 0; -/** Check if a bucket has just run out of tokens, and if so, note the - * timestamp for TB_EMPTY events; only used in TestingTorNetwork mode. */ -static void -connection_buckets_empty_ts(uint32_t *timestamp_var, int tokens_before, - size_t tokens_removed) +/** Helper: convert given tvnow time value to milliseconds since + * midnight. */ +static uint32_t +msec_since_midnight(struct timeval tvnow) { - if (tokens_before > 0 && tokens_before - (int)tokens_removed <= 0) { - struct timeval tvnow; - tor_gettimeofday_cached(&tvnow); - *timestamp_var = (uint32_t)(((tvnow.tv_sec % 86400L) * 1000L) + - ((uint32_t)tvnow.tv_usec / (uint32_t)1000L)); - } + return (uint32_t)(((tvnow.tv_sec % 86400L) * 1000L) + + ((uint32_t)tvnow.tv_usec / (uint32_t)1000L)); +} + +/** Check if a bucket which had tokens_before tokens and which got + * 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 +connection_buckets_note_empty_ts(uint32_t *timestamp_var, + int tokens_before, size_t tokens_removed, + struct timeval tvnow) +{ + if (tokens_before > 0 && (uint32_t)tokens_before <= tokens_removed) + *timestamp_var = msec_since_midnight(tvnow); } /** We just read num_read and wrote num_written bytes @@ -2586,23 +2594,25 @@ connection_buckets_decrement(connection_t *conn, time_t now, /* If one or more of our token buckets ran dry just now, note the * timestamp for TB_EMPTY events. */ - if (get_options()->TestingTorNetwork) { + if (get_options()->TestingEnableTbEmptyEvent) { + struct timeval tvnow; + tor_gettimeofday_cached(&tvnow); if (connection_counts_as_relayed_traffic(conn, now)) { - connection_buckets_empty_ts(&global_relayed_read_emptied, - global_relayed_read_bucket, num_read); - connection_buckets_empty_ts(&global_relayed_write_emptied, - global_relayed_write_bucket, num_written); + connection_buckets_note_empty_ts(&global_relayed_read_emptied, + global_relayed_read_bucket, num_read, tvnow); + connection_buckets_note_empty_ts(&global_relayed_write_emptied, + global_relayed_write_bucket, num_written, tvnow); } - connection_buckets_empty_ts(&global_read_emptied, global_read_bucket, - num_read); - connection_buckets_empty_ts(&global_write_emptied, global_write_bucket, - num_written); + connection_buckets_note_empty_ts(&global_read_emptied, + global_read_bucket, num_read, tvnow); + connection_buckets_note_empty_ts(&global_write_emptied, + global_write_bucket, num_written, tvnow); if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) { or_connection_t *or_conn = TO_OR_CONN(conn); - connection_buckets_empty_ts(&or_conn->read_emptied_time, - or_conn->read_bucket, num_read); - connection_buckets_empty_ts(&or_conn->write_emptied_time, - or_conn->write_bucket, num_written); + connection_buckets_note_empty_ts(&or_conn->read_emptied_time, + or_conn->read_bucket, num_read, tvnow); + connection_buckets_note_empty_ts(&or_conn->write_emptied_time, + or_conn->write_bucket, num_written, tvnow); } } @@ -2712,6 +2722,28 @@ connection_bucket_refill_helper(int *bucket, int rate, int burst, } } +/** Helper: return the time in milliseconds since last_empty_time + * when a bucket ran empty that previously had tokens_before tokens + * now has tokens_after tokens after refilling at timestamp + * 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 +bucket_millis_empty(int tokens_before, uint32_t last_empty_time, + int tokens_after, int milliseconds_elapsed, + struct timeval tvnow) +{ + uint32_t result = 0, refilled; + if (tokens_before <= 0 && tokens_after > tokens_before) { + refilled = msec_since_midnight(tvnow); + result = (uint32_t)((refilled + 86400L * 1000L - last_empty_time) % + (86400L * 1000L)); + if (result > (uint32_t)milliseconds_elapsed) + result = (uint32_t)milliseconds_elapsed; + } + return result; +} + /** Time has passed; increment buckets appropriately. */ void connection_bucket_refill(int milliseconds_elapsed, time_t now) @@ -2724,6 +2756,7 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now) int prev_global_write = global_write_bucket; int prev_relay_read = global_relayed_read_bucket; int prev_relay_write = global_relayed_write_bucket; + struct timeval tvnow; /*< Only used if TB_EMPTY events are enabled. */ bandwidthrate = (int)options->BandwidthRate; bandwidthburst = (int)options->BandwidthBurst; @@ -2759,14 +2792,31 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now) milliseconds_elapsed, "global_relayed_write_bucket"); - control_event_refill_global(global_read_bucket, prev_global_read, - global_read_emptied, global_write_bucket, - prev_global_write, global_write_emptied, - global_relayed_read_bucket, prev_relay_read, - global_relayed_read_emptied, - global_relayed_write_bucket, prev_relay_write, - global_relayed_write_emptied, - milliseconds_elapsed); + /* If buckets were empty before and have now been refilled, tell any + * interested controllers. */ + if (get_options()->TestingEnableTbEmptyEvent) { + uint32_t global_read_empty_time, global_write_empty_time, + relay_read_empty_time, relay_write_empty_time; + tor_gettimeofday_cached(&tvnow); + global_read_empty_time = bucket_millis_empty(prev_global_read, + global_read_emptied, global_read_bucket, + milliseconds_elapsed, tvnow); + global_write_empty_time = bucket_millis_empty(prev_global_write, + global_write_emptied, global_write_bucket, + milliseconds_elapsed, tvnow); + control_event_tb_empty("GLOBAL", global_read_empty_time, + global_write_empty_time, milliseconds_elapsed); + relay_read_empty_time = bucket_millis_empty(prev_relay_read, + global_relayed_read_emptied, + global_relayed_read_bucket, + milliseconds_elapsed, tvnow); + relay_write_empty_time = bucket_millis_empty(prev_relay_write, + global_relayed_write_emptied, + global_relayed_write_bucket, + milliseconds_elapsed, tvnow); + control_event_tb_empty("RELAY", relay_read_empty_time, + relay_write_empty_time, milliseconds_elapsed); + } /* refill the per-connection buckets */ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { @@ -2793,8 +2843,26 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now) "or_conn->write_bucket"); } - control_event_refill_conn(or_conn, prev_conn_read, prev_conn_write, - (uint32_t)milliseconds_elapsed); + /* If buckets were empty before and have now been refilled, tell any + * interested controllers. */ + if (get_options()->TestingEnableTbEmptyEvent) { + char *bucket; + uint32_t conn_read_empty_time, conn_write_empty_time; + tor_asprintf(&bucket, "ORCONN ID="U64_FORMAT, + U64_PRINTF_ARG(or_conn->base_.global_identifier)); + conn_read_empty_time = bucket_millis_empty(prev_conn_read, + or_conn->read_emptied_time, + or_conn->read_bucket, + milliseconds_elapsed, tvnow); + conn_write_empty_time = bucket_millis_empty(prev_conn_write, + or_conn->write_emptied_time, + or_conn->write_bucket, + milliseconds_elapsed, tvnow); + control_event_tb_empty(bucket, conn_read_empty_time, + conn_write_empty_time, + milliseconds_elapsed); + tor_free(bucket); + } } if (conn->read_blocked_on_bw == 1 /* marked to turn reading back on now */ diff --git a/src/or/control.c b/src/or/control.c index c06a91182b..ac7be8d593 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4155,99 +4155,23 @@ control_event_circuit_cell_stats(void) return 0; } -/** Helper: return the time in millis that a given bucket was empty, - * capped at the time in millis since last refilling that bucket. Return - * 0 if the bucket was not empty during the last refill period. */ -static uint32_t -bucket_millis_empty(int prev_tokens, uint32_t last_empty_time, - uint32_t milliseconds_elapsed) -{ - uint32_t result = 0, refilled; - if (prev_tokens <= 0) { - struct timeval tvnow; - tor_gettimeofday_cached(&tvnow); - refilled = (uint32_t)((tvnow.tv_sec % 86400L) * 1000L + - (uint32_t)tvnow.tv_usec / (uint32_t)1000L); - result = (uint32_t)((refilled + 86400L * 1000L - last_empty_time) % - (86400L * 1000L)); - if (result > milliseconds_elapsed) - result = milliseconds_elapsed; - } - return result; -} - -/** Token buckets have been refilled: tell any interested control - * connections how global and relay token buckets have changed. */ +/** Tokens in bucket have been refilled: the read bucket was empty + * for read_empty_time millis, the write bucket was empty for + * write_empty_time millis, and buckets were last refilled + * milliseconds_elapsed millis ago. Only emit TB_EMPTY event if + * either read or write bucket have been empty before. */ int -control_event_refill_global(int global_read, int prev_global_read, - uint32_t global_read_emptied_time, - int global_write, int prev_global_write, - uint32_t global_write_emptied_time, - int relay_read, int prev_relay_read, - uint32_t relay_read_emptied_time, - int relay_write, int prev_relay_write, - uint32_t relay_write_emptied_time, - uint32_t milliseconds_elapsed) +control_event_tb_empty(const char *bucket, uint32_t read_empty_time, + uint32_t write_empty_time, + int milliseconds_elapsed) { - uint32_t global_read_empty_time, global_write_empty_time, - relay_read_empty_time, relay_write_empty_time; - if (!get_options()->TestingTorNetwork || - !EVENT_IS_INTERESTING(EVENT_TB_EMPTY)) - return 0; - if (prev_global_read == global_read && - prev_global_write == global_write && - prev_relay_read == relay_read && - prev_relay_write == relay_write) - return 0; - if (prev_global_read <= 0 && prev_global_write <= 0) { - global_read_empty_time = bucket_millis_empty(prev_global_read, - global_read_emptied_time, milliseconds_elapsed); - global_write_empty_time = bucket_millis_empty(prev_global_write, - global_write_emptied_time, 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 GLOBAL READ=%d WRITTEN=%d " + "650 TB_EMPTY %s READ=%d WRITTEN=%d " "LAST=%d\r\n", - global_read_empty_time, global_write_empty_time, - milliseconds_elapsed); - } - if (prev_relay_read <= 0 && prev_relay_write <= 0) { - relay_read_empty_time = bucket_millis_empty(prev_relay_read, - relay_read_emptied_time, milliseconds_elapsed); - relay_write_empty_time = bucket_millis_empty(prev_relay_write, - relay_write_emptied_time, milliseconds_elapsed); - send_control_event(EVENT_TB_EMPTY, ALL_FORMATS, - "650 TB_EMPTY RELAY READ=%d WRITTEN=%d " - "LAST=%d\r\n", - relay_read_empty_time, relay_write_empty_time, - milliseconds_elapsed); - } - return 0; -} - -/** Token buckets of a connection have been refilled: tell any interested - * control connections how per-connection token buckets have changed. */ -int -control_event_refill_conn(or_connection_t *or_conn, - int prev_read, int prev_write, - uint32_t milliseconds_elapsed) -{ - uint32_t read_bucket_empty_time, write_bucket_empty_time; - if (!get_options()->TestingTorNetwork || - !EVENT_IS_INTERESTING(EVENT_TB_EMPTY)) - return 0; - if (prev_read == or_conn->read_bucket && - prev_write == or_conn->write_bucket) - return 0; - if (prev_read <= 0 || prev_write <= 0) { - read_bucket_empty_time = bucket_millis_empty(prev_read, - or_conn->read_emptied_time, milliseconds_elapsed); - write_bucket_empty_time = bucket_millis_empty(prev_write, - or_conn->write_emptied_time, milliseconds_elapsed); - send_control_event(EVENT_TB_EMPTY, ALL_FORMATS, - "650 TB_EMPTY ORCONN ID="U64_FORMAT" READ=%d " - "WRITTEN=%d LAST=%d\r\n", - U64_PRINTF_ARG(or_conn->base_.global_identifier), - read_bucket_empty_time, write_bucket_empty_time, + bucket, read_empty_time, write_empty_time, milliseconds_elapsed); } return 0; diff --git a/src/or/control.h b/src/or/control.h index 06a3849493..c79c432ef1 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -54,18 +54,9 @@ int control_event_circ_bandwidth_used(void); int control_event_conn_bandwidth(connection_t *conn); int control_event_conn_bandwidth_used(void); int control_event_circuit_cell_stats(void); -int control_event_refill_global(int global_read, int prev_global_read, - uint32_t global_read_emptied, - int global_write, int prev_global_write, - uint32_t global_write_emptied, - int relay_read, int prev_relay_read, - uint32_t relay_read_emptied, - int relay_write, int prev_relay_write, - uint32_t relay_write_emptied, - uint32_t milliseconds_elapsed); -int control_event_refill_conn(or_connection_t *or_conn, - int prev_read, int prev_write, - uint32_t milliseconds_elapsed); +int control_event_tb_empty(const char *bucket, uint32_t read_empty_time, + uint32_t write_empty_time, + int milliseconds_elapsed); void control_event_logmsg(int severity, uint32_t domain, const char *msg); int control_event_descriptors_changed(smartlist_t *routers); int control_event_address_mapped(const char *from, const char *to, diff --git a/src/or/or.h b/src/or/or.h index 3702664a23..21a36c928d 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1479,11 +1479,11 @@ typedef struct or_connection_t { struct or_connection_t *next_with_same_id; /**< Next connection with same * identity digest as this one. */ - /** Last emptied read token bucket in msec since midnight; only used in - * TestingTorNetwork mode. */ + /** Last emptied read token bucket in msec since midnight; only used if + * TB_EMPTY events are enabled. */ uint32_t read_emptied_time; - /** Last emptied write token bucket in msec since midnight; only used in - * TestingTorNetwork mode. */ + /** Last emptied write token bucket in msec since midnight; only used if + * TB_EMPTY events are enabled. */ uint32_t write_emptied_time; } or_connection_t; @@ -3991,6 +3991,9 @@ typedef struct { /** 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. */ From b33b366a7f8bcab1c9b4859788e3b2c7d3dcf180 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Sat, 25 May 2013 13:04:33 +0200 Subject: [PATCH 10/15] Tweak CIRC_BW event based on comments by nickm. - Rename n_read and n_written in origin_circuit_t to make it clear that these are only used for CIRC_BW events. - Extract new code in control_update_global_event_mask to new clear_circ_bw_fields function. --- src/or/connection.c | 14 +++++++------- src/or/control.c | 35 +++++++++++++++++++++-------------- src/or/or.h | 10 ++++++---- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/or/connection.c b/src/or/connection.c index 2f2a421fca..88def49e50 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -3271,7 +3271,7 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read, /* change *max_to_read */ *max_to_read = at_most - n_read; - /* Update edge_conn->n_read and ocirc->n_read */ + /* Update edge_conn->n_read and ocirc->n_read_circ_bw */ if (conn->type == CONN_TYPE_AP) { edge_connection_t *edge_conn = TO_EDGE_CONN(conn); circuit_t *circ = circuit_get_by_edge_conn(edge_conn); @@ -3285,10 +3285,10 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read, if (circ && CIRCUIT_IS_ORIGIN(circ)) { ocirc = TO_ORIGIN_CIRCUIT(circ); - if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_read > n_read)) - ocirc->n_read += (int)n_read; + if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_read_circ_bw > n_read)) + ocirc->n_read_circ_bw += (int)n_read; else - ocirc->n_read = UINT32_MAX; + ocirc->n_read_circ_bw = UINT32_MAX; } } @@ -3752,10 +3752,10 @@ connection_handle_write_impl(connection_t *conn, int force) if (circ && CIRCUIT_IS_ORIGIN(circ)) { ocirc = TO_ORIGIN_CIRCUIT(circ); - if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_written > n_written)) - ocirc->n_written += (int)n_written; + if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_written_circ_bw > n_written)) + ocirc->n_written_circ_bw += (int)n_written; else - ocirc->n_written = UINT32_MAX; + ocirc->n_written_circ_bw = UINT32_MAX; } } diff --git a/src/or/control.c b/src/or/control.c index ac7be8d593..5e2020f7cb 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -237,6 +237,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) { + if (!CIRCUIT_IS_ORIGIN(circ)) + continue; + ocirc = TO_ORIGIN_CIRCUIT(circ); + ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0; + } +} + /** Set global_event_mask* to the bitwise OR of each live control * connection's event_mask field. */ void @@ -276,14 +290,7 @@ control_update_global_event_mask(void) } if (! (old_mask & EVENT_CIRC_BANDWIDTH_USED) && (new_mask & EVENT_CIRC_BANDWIDTH_USED)) { - circuit_t *circ; - origin_circuit_t *ocirc; - for (circ = circuit_get_global_list_(); circ; circ = circ->next) { - if (!CIRCUIT_IS_ORIGIN(circ)) - continue; - ocirc = TO_ORIGIN_CIRCUIT(circ); - ocirc->n_written = ocirc->n_read = 0; - } + clear_circ_bw_fields(); } } @@ -3893,8 +3900,8 @@ control_event_stream_bandwidth(edge_connection_t *edge_conn) circ = circuit_get_by_edge_conn(edge_conn); if (circ && CIRCUIT_IS_ORIGIN(circ)) { ocirc = TO_ORIGIN_CIRCUIT(circ); - ocirc->n_read += edge_conn->n_read; - ocirc->n_written += edge_conn->n_written; + ocirc->n_read_circ_bw += edge_conn->n_read; + ocirc->n_written_circ_bw += edge_conn->n_written; } edge_conn->n_written = edge_conn->n_read = 0; } @@ -3947,14 +3954,14 @@ control_event_circ_bandwidth_used(void) if (!CIRCUIT_IS_ORIGIN(circ)) continue; ocirc = TO_ORIGIN_CIRCUIT(circ); - if (!ocirc->n_read && !ocirc->n_written) + 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, - (unsigned long)ocirc->n_written); - ocirc->n_written = ocirc->n_read = 0; + (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; diff --git a/src/or/or.h b/src/or/or.h index 21a36c928d..6dd3ce4498 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2971,12 +2971,14 @@ typedef struct origin_circuit_t { edge_connection_t *p_streams; /** Bytes read from any attached stream since last call to - * control_event_circ_bandwidth_used() */ - uint32_t n_read; + * control_event_circ_bandwidth_used(). Only used if we're configured + * to emit CIRC_BW events. */ + uint32_t n_read_circ_bw; /** Bytes written to any attached stream since last call to - * control_event_circ_bandwidth_used() */ - uint32_t n_written; + * control_event_circ_bandwidth_used(). Only used if we're configured + * to emit CIRC_BW events. */ + uint32_t n_written_circ_bw; /** Build state for this circuit. It includes the intended path * length, the chosen exit router, rendezvous information, etc. From e39292f21dd363bd7601b63b4f43a95705c3332b Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Fri, 31 May 2013 15:51:25 +0200 Subject: [PATCH 11/15] Test functions used for TB_EMPTY and CELL_STATS events. --- src/or/connection.c | 5 +- src/or/connection.h | 11 ++ src/or/control.c | 22 +-- src/or/control.h | 26 ++- src/test/Makefile.nmake | 6 +- src/test/include.am | 1 + src/test/test.c | 2 + src/test/test_controller_events.c | 283 ++++++++++++++++++++++++++++++ 8 files changed, 331 insertions(+), 25 deletions(-) create mode 100644 src/test/test_controller_events.c 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 +}; + From 6553bdde8cc2c84434b87b507e628b28e4054b76 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Thu, 12 Sep 2013 09:14:56 +0200 Subject: [PATCH 12/15] Don't format cell stats for unattached circuits. --- src/or/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/or/control.c b/src/or/control.c index 495b7d7b02..616fec4e3f 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4085,7 +4085,7 @@ format_cell_stats(char **event_string, circuit_t *circ, origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); smartlist_add_asprintf(event_parts, "ID=%lu", (unsigned long)ocirc->global_identifier); - } else { + } 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); From d5f0d792dd2e4e5f0314ecd15efe42d1c5e64f25 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Thu, 12 Sep 2013 10:10:38 +0200 Subject: [PATCH 13/15] Pass around const struct timeval * instead of struct timeval. Suggested by nickm. --- src/or/connection.c | 34 +++++++++++++++---------------- src/or/connection.h | 4 ++-- src/test/test_controller_events.c | 18 ++++++++-------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/or/connection.c b/src/or/connection.c index 842523958e..2019b2ff3d 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -2552,10 +2552,10 @@ static uint32_t global_relayed_read_emptied = 0, /** Helper: convert given tvnow time value to milliseconds since * midnight. */ static uint32_t -msec_since_midnight(struct timeval tvnow) +msec_since_midnight(const struct timeval *tvnow) { - return (uint32_t)(((tvnow.tv_sec % 86400L) * 1000L) + - ((uint32_t)tvnow.tv_usec / (uint32_t)1000L)); + return (uint32_t)(((tvnow->tv_sec % 86400L) * 1000L) + + ((uint32_t)tvnow->tv_usec / (uint32_t)1000L)); } /** Check if a bucket which had tokens_before tokens and which got @@ -2565,7 +2565,7 @@ msec_since_midnight(struct timeval tvnow) void connection_buckets_note_empty_ts(uint32_t *timestamp_var, int tokens_before, size_t tokens_removed, - struct timeval tvnow) + const struct timeval *tvnow) { if (tokens_before > 0 && (uint32_t)tokens_before <= tokens_removed) *timestamp_var = msec_since_midnight(tvnow); @@ -2600,20 +2600,20 @@ connection_buckets_decrement(connection_t *conn, time_t now, tor_gettimeofday_cached(&tvnow); if (connection_counts_as_relayed_traffic(conn, now)) { connection_buckets_note_empty_ts(&global_relayed_read_emptied, - global_relayed_read_bucket, num_read, tvnow); + global_relayed_read_bucket, num_read, &tvnow); connection_buckets_note_empty_ts(&global_relayed_write_emptied, - global_relayed_write_bucket, num_written, tvnow); + global_relayed_write_bucket, num_written, &tvnow); } connection_buckets_note_empty_ts(&global_read_emptied, - global_read_bucket, num_read, tvnow); + global_read_bucket, num_read, &tvnow); connection_buckets_note_empty_ts(&global_write_emptied, - global_write_bucket, num_written, tvnow); + global_write_bucket, num_written, &tvnow); if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) { or_connection_t *or_conn = TO_OR_CONN(conn); connection_buckets_note_empty_ts(&or_conn->read_emptied_time, - or_conn->read_bucket, num_read, tvnow); + or_conn->read_bucket, num_read, &tvnow); connection_buckets_note_empty_ts(&or_conn->write_emptied_time, - or_conn->write_bucket, num_written, tvnow); + or_conn->write_bucket, num_written, &tvnow); } } @@ -2732,7 +2732,7 @@ connection_bucket_refill_helper(int *bucket, int rate, int burst, uint32_t bucket_millis_empty(int tokens_before, uint32_t last_empty_time, int tokens_after, int milliseconds_elapsed, - struct timeval tvnow) + const struct timeval *tvnow) { uint32_t result = 0, refilled; if (tokens_before <= 0 && tokens_after > tokens_before) { @@ -2801,20 +2801,20 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now) tor_gettimeofday_cached(&tvnow); global_read_empty_time = bucket_millis_empty(prev_global_read, global_read_emptied, global_read_bucket, - milliseconds_elapsed, tvnow); + milliseconds_elapsed, &tvnow); global_write_empty_time = bucket_millis_empty(prev_global_write, global_write_emptied, global_write_bucket, - milliseconds_elapsed, tvnow); + milliseconds_elapsed, &tvnow); control_event_tb_empty("GLOBAL", global_read_empty_time, global_write_empty_time, milliseconds_elapsed); relay_read_empty_time = bucket_millis_empty(prev_relay_read, global_relayed_read_emptied, global_relayed_read_bucket, - milliseconds_elapsed, tvnow); + milliseconds_elapsed, &tvnow); relay_write_empty_time = bucket_millis_empty(prev_relay_write, global_relayed_write_emptied, global_relayed_write_bucket, - milliseconds_elapsed, tvnow); + milliseconds_elapsed, &tvnow); control_event_tb_empty("RELAY", relay_read_empty_time, relay_write_empty_time, milliseconds_elapsed); } @@ -2854,11 +2854,11 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now) conn_read_empty_time = bucket_millis_empty(prev_conn_read, or_conn->read_emptied_time, or_conn->read_bucket, - milliseconds_elapsed, tvnow); + milliseconds_elapsed, &tvnow); conn_write_empty_time = bucket_millis_empty(prev_conn_write, or_conn->write_emptied_time, or_conn->write_bucket, - milliseconds_elapsed, tvnow); + milliseconds_elapsed, &tvnow); control_event_tb_empty(bucket, conn_read_empty_time, conn_write_empty_time, milliseconds_elapsed); diff --git a/src/or/connection.h b/src/or/connection.h index c0894cd095..53d3da0290 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -218,11 +218,11 @@ void connection_enable_rate_limiting(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, - struct timeval tvnow); + const struct timeval *tvnow); void connection_buckets_note_empty_ts(uint32_t *timestamp_var, int tokens_before, size_t tokens_removed, - struct timeval tvnow); + const struct timeval *tvnow); #endif #endif diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c index 28ba4fa7aa..a0e2c6e4bb 100644 --- a/src/test/test_controller_events.c +++ b/src/test/test_controller_events.c @@ -21,7 +21,7 @@ help_test_bucket_note_empty(uint32_t expected_msec_since_midnight, 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); + tokens_removed, &tvnow); tt_int_op(expected_msec_since_midnight, ==, timestamp_var); done: @@ -57,20 +57,20 @@ test_cntev_bucket_millis_empty(void *arg) 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)); + 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)); + 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)); + 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)); + tt_int_op(100, ==, bucket_millis_empty(0, 42020, 1, 100, &tvnow)); /* 1970-01-02 00:00:00:050000 */ tvnow.tv_sec = 86400; @@ -78,7 +78,7 @@ test_cntev_bucket_millis_empty(void *arg) /* 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)); + tt_int_op(80, ==, bucket_millis_empty(0, 86400000 - 30, 1, 100, &tvnow)); done: ; From b43a37bc5b6813224b9ac727e98a96d6ce2c5f2b Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Thu, 12 Sep 2013 10:51:55 +0200 Subject: [PATCH 14/15] Pass const uint64_t pointers, document array length. Suggested by nickm. --- src/or/control.c | 31 ++++++++++++++++--------------- src/or/control.h | 4 ++-- src/test/test_controller_events.c | 20 ++++++++++++-------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 616fec4e3f..73769733cb 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4048,13 +4048,14 @@ sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats) * 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 include_if_non_zero is not zero, but with - * the (possibly zero) entry from number_to_include. If no + * the (possibly zero) entry from number_to_include. Both + * arrays are expected to have a length of CELL_COMMAND_MAX_ + 1. If no * entry in include_if_non_zero is positive, no string will * be added to event_parts. */ void append_cell_stats_by_command(smartlist_t *event_parts, const char *key, - uint64_t *include_if_non_zero, - uint64_t *number_to_include) + const uint64_t *include_if_non_zero, + const uint64_t *number_to_include) { smartlist_t *key_value_strings = smartlist_new(); int i; @@ -4092,14 +4093,14 @@ format_cell_stats(char **event_string, circuit_t *circ, 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); + (const uint64_t *) cell_stats->added_cells_appward, + (const uint64_t *) cell_stats->added_cells_appward); append_cell_stats_by_command(event_parts, "InboundRemoved", - cell_stats->removed_cells_appward, - cell_stats->removed_cells_appward); + (const uint64_t *) cell_stats->removed_cells_appward, + (const uint64_t *) cell_stats->removed_cells_appward); append_cell_stats_by_command(event_parts, "InboundTime", - cell_stats->removed_cells_appward, - cell_stats->total_time_appward); + (const uint64_t *) cell_stats->removed_cells_appward, + (const uint64_t *) cell_stats->total_time_appward); } if (circ->n_chan) { smartlist_add_asprintf(event_parts, "OutboundQueue=%lu", @@ -4107,14 +4108,14 @@ format_cell_stats(char **event_string, circuit_t *circ, 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); + (const uint64_t *) cell_stats->added_cells_exitward, + (const uint64_t *) cell_stats->added_cells_exitward); append_cell_stats_by_command(event_parts, "OutboundRemoved", - cell_stats->removed_cells_exitward, - cell_stats->removed_cells_exitward); + (const uint64_t *) cell_stats->removed_cells_exitward, + (const uint64_t *) cell_stats->removed_cells_exitward); append_cell_stats_by_command(event_parts, "OutboundTime", - cell_stats->removed_cells_exitward, - cell_stats->total_time_exitward); + (const uint64_t *) cell_stats->removed_cells_exitward, + (const uint64_t *) cell_stats->total_time_exitward); } *event_string = smartlist_join_strings(event_parts, " ", 0, NULL); SMARTLIST_FOREACH(event_parts, char *, cp, tor_free(cp)); diff --git a/src/or/control.h b/src/or/control.h index 1773a87374..1921d9704f 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -120,8 +120,8 @@ 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); + 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 diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c index a0e2c6e4bb..0b5434cd38 100644 --- a/src/test/test_controller_events.c +++ b/src/test/test_controller_events.c @@ -158,28 +158,32 @@ test_cntev_append_cell_stats(void *arg) (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); + append_cell_stats_by_command(event_parts, key, + (const uint64_t *) include_if_non_zero, + (const uint64_t *) 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); + append_cell_stats_by_command(event_parts, key, + (const uint64_t *) include_if_non_zero, + (const uint64_t *) 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); + append_cell_stats_by_command(event_parts, key, + (const uint64_t *) include_if_non_zero, + (const uint64_t *) 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); + append_cell_stats_by_command(event_parts, key, + (const uint64_t *) include_if_non_zero, + (const uint64_t *) number_to_include); tt_str_op("Z=create:4,relay:1", ==, smartlist_pop_last(event_parts)); done: From e46de82c97e694d3bfa399af48b9de9365e264bd Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Mon, 28 Oct 2013 10:48:18 +0100 Subject: [PATCH 15/15] squash! Pass const uint64_t pointers, document array length. Don't cast uint64_t * to const uint64_t * explicitly. The cast is always safe, so C does it for us. Doing the cast explitictly can hide bugs if the input is secretly the wrong type. Suggested by Nick. --- src/or/control.c | 24 ++++++++++++------------ src/test/test_controller_events.c | 16 ++++++++-------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 73769733cb..9e026b391c 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4093,14 +4093,14 @@ format_cell_stats(char **event_string, circuit_t *circ, 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", - (const uint64_t *) cell_stats->added_cells_appward, - (const uint64_t *) cell_stats->added_cells_appward); + cell_stats->added_cells_appward, + cell_stats->added_cells_appward); append_cell_stats_by_command(event_parts, "InboundRemoved", - (const uint64_t *) cell_stats->removed_cells_appward, - (const uint64_t *) cell_stats->removed_cells_appward); + cell_stats->removed_cells_appward, + cell_stats->removed_cells_appward); append_cell_stats_by_command(event_parts, "InboundTime", - (const uint64_t *) cell_stats->removed_cells_appward, - (const uint64_t *) cell_stats->total_time_appward); + cell_stats->removed_cells_appward, + cell_stats->total_time_appward); } if (circ->n_chan) { smartlist_add_asprintf(event_parts, "OutboundQueue=%lu", @@ -4108,14 +4108,14 @@ format_cell_stats(char **event_string, circuit_t *circ, smartlist_add_asprintf(event_parts, "OutboundConn="U64_FORMAT, U64_PRINTF_ARG(circ->n_chan->global_identifier)); append_cell_stats_by_command(event_parts, "OutboundAdded", - (const uint64_t *) cell_stats->added_cells_exitward, - (const uint64_t *) cell_stats->added_cells_exitward); + cell_stats->added_cells_exitward, + cell_stats->added_cells_exitward); append_cell_stats_by_command(event_parts, "OutboundRemoved", - (const uint64_t *) cell_stats->removed_cells_exitward, - (const uint64_t *) cell_stats->removed_cells_exitward); + cell_stats->removed_cells_exitward, + cell_stats->removed_cells_exitward); append_cell_stats_by_command(event_parts, "OutboundTime", - (const uint64_t *) cell_stats->removed_cells_exitward, - (const uint64_t *) cell_stats->total_time_exitward); + 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)); diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c index 0b5434cd38..04a04de5b0 100644 --- a/src/test/test_controller_events.c +++ b/src/test/test_controller_events.c @@ -159,31 +159,31 @@ test_cntev_append_cell_stats(void *arg) /* All array entries empty. */ append_cell_stats_by_command(event_parts, key, - (const uint64_t *) include_if_non_zero, - (const uint64_t *) number_to_include); + 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, - (const uint64_t *) include_if_non_zero, - (const uint64_t *) number_to_include); + 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, - (const uint64_t *) include_if_non_zero, - (const uint64_t *) number_to_include); + 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, - (const uint64_t *) include_if_non_zero, - (const uint64_t *) number_to_include); + include_if_non_zero, + number_to_include); tt_str_op("Z=create:4,relay:1", ==, smartlist_pop_last(event_parts)); done: