diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index ef9d24c853eeb5dd407635e020b5f1dcfe0625e6..733430840339fbdb7f41f7edc569c46d8054d7f6 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -12,6 +12,11 @@ #include "or.h" /********* START VARIABLES **********/ +/** Global list of circuit build times */ +// XXX: Make this a smartlist.. +uint16_t circuit_build_times[NCIRCUITS_TO_OBSERVE]; +int build_times_idx = 0; +int total_build_times = 0; /** A global list of all circuits at this hop. */ extern circuit_t *global_circuitlist; @@ -60,6 +65,156 @@ static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); static void entry_guards_changed(void); static time_t start_of_month(time_t when); +static int circuit_build_times_add_time(time_t time); +/** circuit_build_times is a circular array, so loop around when + * array is full + * + * time units are milliseconds + */ +static +int +circuit_build_times_add_time(long time) +{ + if(time > UINT16_MAX) { + log_notice(LD_CIRC, + "Circuit build time of %dms exceeds max. Capping at 65536ms", time); + time = UINT16_MAX; + } + circuit_build_times[build_times_idx] = time; + build_times_idx = (build_times_idx + 1) % NCIRCUITS_TO_OBSERVE; + if(total_build_times + 1 < NCIRCUITS_TO_OBSERVE) + total_build_times++; + + return 0; +} + +/** + * Calculate histogram + */ +void +circuit_build_times_create_histogram(uint16_t * histogram) +{ + int i, c; + // calculate histogram + for(i = 0; i < NCIRCUITS_TO_OBSERVE; i++) { + if(circuit_build_times[i] == 0) continue; /* 0 <-> uninitialized */ + + c = (circuit_build_times[i] / BUILDTIME_BIN_WIDTH); + histogram[c]++; + } +} + +/** + * Find maximum circuit build time + */ +uint16_t +circuit_build_times_max() +{ + int i = 0, max_build_time = 0; + for( i = 0; i < NCIRCUITS_TO_OBSERVE; i++) { + if(circuit_build_times[i] > max_build_time) + max_build_time = circuit_build_times[i]; + } + return max_build_time; +} + +uint16_t +circuit_build_times_min() +{ + int i = 0; + uint16_t min_build_time = UINT16_MAX; + for( i = 0; i < NCIRCUITS_TO_OBSERVE; i++) { + if(circuit_build_times[i] && /* 0 <-> uninitialized */ + circuit_build_times[i] < min_build_time) + min_build_time = circuit_build_times[i]; + } + return min_build_time; +} + +/** + * output a histogram of current circuit build times + */ +void +circuit_build_times_update_state(or_state_t * state) +{ + uint16_t max_build_time = 0, *histogram; + int i = 0, nbins = 0; + config_line_t **next, *line; + + max_build_time = circuit_build_times_max(); + nbins = 1 + (max_build_time / BUILDTIME_BIN_WIDTH); + histogram = tor_malloc_zero(nbins * sizeof(uint16_t)); + + circuit_build_times_create_histogram(histogram); + // write to state + config_free_lines(state->BuildtimeHistogram); + next = &state->BuildtimeHistogram; + *next = NULL; + + state->TotalBuildTimes = total_build_times; + + // total build times? + for(i = 0; i < nbins; i++) { + if(histogram[i] == 0) continue; // compress the histogram by skipping the blanks + *next = line = tor_malloc_zero(sizeof(config_line_t)); + line->key = tor_strdup("CircuitBuildTimeBin"); + line->value = tor_malloc(20); + tor_snprintf(line->value, 20, "%d %d", i*BUILDTIME_BIN_WIDTH, + histogram[i]); + next = &(line->next); + } + if(!get_options()->AvoidDiskWrites) + or_state_mark_dirty(get_or_state(), 0); + + if(histogram) tor_free(histogram); +} + +int +find_next_available(int chosen) +{// find index of next open slot in circuit_build_times + int idx = 0; + for(idx = (chosen + 1) % NCIRCUITS_TO_OBSERVE; idx < chosen; + idx = ((idx + 1 ) % NCIRCUITS_TO_OBSERVE)) { + if(circuit_build_times[idx] == 0) { + return idx; + } + } + return 0; +} + +/** Load histogram from state */ +int +circuit_build_times_parse_state(or_state_t *state, char **msg) +{ + config_line_t *line; + msg = NULL; + memset(circuit_build_times, 0, NCIRCUITS_TO_OBSERVE); + total_build_times = state->TotalBuildTimes; + + for(line = state->BuildtimeHistogram; line; line = line->next) { + smartlist_t * args = smartlist_create(); + smartlist_split_string(args, line->value, " ", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + if(smartlist_len(args) < 2) { + *msg = tor_strdup("Unable to parse circuit build times: " + "Too few arguments to CircuitBuildTIme"); + break; + } else { + uint16_t ms, count, i; + /* XXX: use tor_strtol */ + ms = atol(smartlist_get(args,0)); + count = atol(smartlist_get(args,1)); + for(i = 0; i < count; i++) { + circuit_build_times_add_time(ms); + } + } + } + return (msg ? -1 : 0); +} + + + + /** Iterate over values of circ_id, starting from conn-\>next_circ_id, * and with the high bit specified by conn-\>circ_id_type, until we get * a circ_id that is not in use by any other circuit on that conn. @@ -641,8 +796,13 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) log_debug(LD_CIRC,"starting to send subsequent skin."); hop = onion_next_hop_in_cpath(circ->cpath); if (!hop) { + struct timeval end; + tor_gettimeofday(&end); /* done building the circuit. whew. */ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN); + circuit_build_times_add_time(tor_mdiff(&circ->_base.timestamp_created, + &end)); + circuit_build_times_recompute(); log_info(LD_CIRC,"circuit built!"); circuit_reset_failure_count(0); if (circ->build_state->onehop_tunnel) diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index e1da11716842840082c50eab6f9a528bca9c33c3..54bda94001915719d626204aa3d9fd17e4747aa7 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -379,6 +379,7 @@ static void init_circuit_base(circuit_t *circ) { circ->timestamp_created = time(NULL); + tor_gettimeofday(&circ->highres_created); circ->package_window = circuit_initial_package_window(); circ->deliver_window = CIRCWINDOW_START; diff --git a/src/or/config.c b/src/or/config.c index d830229d3b13f6011efd8687f77140c7bd2efebc..0345ca72816aeacae65841efefb2514f7c0d3aeb 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -409,6 +409,11 @@ static config_var_t _state_vars[] = { V(LastRotatedOnionKey, ISOTIME, NULL), V(LastWritten, ISOTIME, NULL), + VAR("TotalBuildTimes", UINT, TotalBuildTimes, NULL), + VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL), + VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL), + + { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } }; @@ -597,6 +602,10 @@ static config_var_description_t options_description[] = { /* Hidden service options: HiddenService: dir,excludenodes, nodes, * options, port. PublishHidServDescriptor */ + /* Circuit build time histogram options */ + { "CircuitBuildTimeBin", "Histogram of recent circuit build times"}, + { "TotalBuildTimes", "Total number of buildtimes in histogram"}, + /* Nonpersistent options: __LeaveStreamsUnattached, __AllDirActionsPrivate */ { NULL, NULL }, }; @@ -5060,6 +5069,13 @@ or_state_set(or_state_t *new_state) log_warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err); tor_free(err); } + + if(circuit_build_times_parse_state(global_state, &err) < 0) { + log_warn(LD_GENERAL,"%s",err); + tor_free(err); + + } + } /** Reload the persistent state from disk, generating a new state as needed. @@ -5192,6 +5208,7 @@ or_state_save(time_t now) * to avoid redundant writes. */ entry_guards_update_state(global_state); rep_hist_update_state(global_state); + circuit_build_times_update_state(global_state); if (accounting_is_enabled(get_options())) accounting_run_housekeeping(now); diff --git a/src/or/or.h b/src/or/or.h index 8587ea61fca2bdd54308cfc7f20d80d902f7263b..be54ab4bf7503d79262b16fad2aa88ee82d881a7 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1884,6 +1884,13 @@ typedef struct crypt_path_t { DH_KEY_LEN) #define ONIONSKIN_REPLY_LEN (DH_KEY_LEN+DIGEST_LEN) +// XXX: Do we want to artifically tweak CircuitIdleTimeout and +// the number of circuits we build at a time if < MIN here? +#define MIN_CIRCUITS_TO_OBSERVE 1000 +#define NCIRCUITS_TO_OBSERVE 10000 /* approx 3 weeks worth of circuits */ +#define BUILDTIME_BIN_WIDTH 50 + + /** Information used to build a circuit. */ typedef struct { /** Intended length of the final circuit. */ @@ -1977,6 +1984,7 @@ typedef struct circuit_t { time_t timestamp_created; /**< When was this circuit created? */ time_t timestamp_dirty; /**< When the circuit was first used, or 0 if the * circuit is clean. */ + struct timeval highres_created; /**< When exactly was this circuit created? */ uint16_t marked_for_close; /**< Should we close this circuit at the end of * the main loop? (If true, holds the line number @@ -2683,6 +2691,11 @@ typedef struct { int BWHistoryWriteInterval; smartlist_t *BWHistoryWriteValues; + /** Build time histogram */ + config_line_t * BuildtimeHistogram; + uint16_t TotalBuildTimes; + + /** What version of Tor wrote this state file? */ char *TorVersion; @@ -2852,6 +2865,11 @@ void bridges_retry_all(void); void entry_guards_free_all(void); +void circuit_build_times_update_state(or_state_t *state); +int circuit_build_times_parse_state(or_state_t *state, char **msg); + + + /********************************* circuitlist.c ***********************/ circuit_t * _circuit_get_global_list(void);