static void test_circuit_timeout(void *arg) { /* Plan: * 1. Generate 1000 samples * 2. Estimate parameters * 3. If difference, repeat * 4. Save state * 5. load state * 6. Estimate parameters * 7. compare differences */ circuit_build_times_t initial; circuit_build_times_t estimate; circuit_build_times_t final; double timeout1, timeout2; or_state_t *state=NULL; int i, runs; double close_ms; (void)arg; initialize_periodic_events(); circuit_build_times_init(&initial); circuit_build_times_init(&estimate); circuit_build_times_init(&final); state = or_state_new(); circuitbuild_running_unit_tests(); #define timeout0 (build_time_t)(30*1000.0) initial.Xm = 3000; circuit_build_times_initial_alpha(&initial, CBT_DEFAULT_QUANTILE_CUTOFF/100.0, timeout0); close_ms = MAX(circuit_build_times_calculate_timeout(&initial, CBT_DEFAULT_CLOSE_QUANTILE/100.0), CBT_DEFAULT_TIMEOUT_INITIAL_VALUE); do { for (i=0; i < CBT_DEFAULT_MIN_CIRCUITS_TO_OBSERVE; i++) { build_time_t sample = circuit_build_times_generate_sample(&initial,0,1); if (sample > close_ms) { circuit_build_times_add_time(&estimate, CBT_BUILD_ABANDONED); } else { circuit_build_times_add_time(&estimate, sample); } } circuit_build_times_update_alpha(&estimate); timeout1 = circuit_build_times_calculate_timeout(&estimate, CBT_DEFAULT_QUANTILE_CUTOFF/100.0); circuit_build_times_set_timeout(&estimate); log_notice(LD_CIRC, "Timeout1 is %f, Xm is %d", timeout1, estimate.Xm); /* 2% error */ } while (fabs(circuit_build_times_cdf(&initial, timeout0) - circuit_build_times_cdf(&initial, timeout1)) > 0.02); tt_int_op(estimate.total_build_times, OP_LE, CBT_NCIRCUITS_TO_OBSERVE); circuit_build_times_update_state(&estimate, state); circuit_build_times_free_timeouts(&final); tt_int_op(circuit_build_times_parse_state(&final, state), OP_EQ, 0); circuit_build_times_update_alpha(&final); timeout2 = circuit_build_times_calculate_timeout(&final, CBT_DEFAULT_QUANTILE_CUTOFF/100.0); circuit_build_times_set_timeout(&final); log_notice(LD_CIRC, "Timeout2 is %f, Xm is %d", timeout2, final.Xm); /* 5% here because some accuracy is lost due to histogram conversion */ tt_assert(fabs(circuit_build_times_cdf(&initial, timeout0) - circuit_build_times_cdf(&initial, timeout2)) < 0.05); for (runs = 0; runs < 50; runs++) { int build_times_idx = 0; int total_build_times = 0; final.close_ms = final.timeout_ms = CBT_DEFAULT_TIMEOUT_INITIAL_VALUE; estimate.close_ms = estimate.timeout_ms = CBT_DEFAULT_TIMEOUT_INITIAL_VALUE; for (i = 0; i < CBT_DEFAULT_RECENT_CIRCUITS*2; i++) { circuit_build_times_network_circ_success(&estimate); circuit_build_times_add_time(&estimate, circuit_build_times_generate_sample(&estimate, 0, CBT_DEFAULT_QUANTILE_CUTOFF/100.0)); circuit_build_times_network_circ_success(&estimate); circuit_build_times_add_time(&final, circuit_build_times_generate_sample(&final, 0, CBT_DEFAULT_QUANTILE_CUTOFF/100.0)); } tt_assert(!circuit_build_times_network_check_changed(&estimate)); tt_assert(!circuit_build_times_network_check_changed(&final)); /* Reset liveness to be non-live */ final.liveness.network_last_live = 0;
/** * Load histogram from <b>state</b>, shuffling the resulting array * after we do so. Use this result to estimate parameters and * calculate the timeout. * * Return -1 on error. */ int circuit_build_times_parse_state(circuit_build_times_t *cbt, or_state_t *state) { int tot_values = 0; uint32_t loaded_cnt = 0, N = 0; config_line_t *line; unsigned int i; build_time_t *loaded_times; int err = 0; circuit_build_times_init(cbt); if (circuit_build_times_disabled()) { return 0; } /* build_time_t 0 means uninitialized */ loaded_times = tor_malloc_zero(sizeof(build_time_t)*state->TotalBuildTimes); for (line = state->BuildtimeHistogram; line; line = line->next) { smartlist_t *args = smartlist_new(); smartlist_split_string(args, line->value, " ", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); if (smartlist_len(args) < 2) { log_warn(LD_GENERAL, "Unable to parse circuit build times: " "Too few arguments to CircuitBuildTime"); err = 1; SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); smartlist_free(args); break; } else { const char *ms_str = smartlist_get(args,0); const char *count_str = smartlist_get(args,1); uint32_t count, k; build_time_t ms; int ok; ms = (build_time_t)tor_parse_ulong(ms_str, 0, 0, CBT_BUILD_TIME_MAX, &ok, NULL); if (!ok) { log_warn(LD_GENERAL, "Unable to parse circuit build times: " "Unparsable bin number"); err = 1; SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); smartlist_free(args); break; } count = (uint32_t)tor_parse_ulong(count_str, 0, 0, UINT32_MAX, &ok, NULL); if (!ok) { log_warn(LD_GENERAL, "Unable to parse circuit build times: " "Unparsable bin count"); err = 1; SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); smartlist_free(args); break; } if (loaded_cnt+count+state->CircuitBuildAbandonedCount > state->TotalBuildTimes) { log_warn(LD_CIRC, "Too many build times in state file. " "Stopping short before %d", loaded_cnt+count); SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); smartlist_free(args); break; } for (k = 0; k < count; k++) { loaded_times[loaded_cnt++] = ms; } N++; SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); smartlist_free(args); } } log_info(LD_CIRC, "Adding %d timeouts.", state->CircuitBuildAbandonedCount); for (i=0; i < state->CircuitBuildAbandonedCount; i++) { loaded_times[loaded_cnt++] = CBT_BUILD_ABANDONED; } if (loaded_cnt != state->TotalBuildTimes) { log_warn(LD_CIRC, "Corrupt state file? Build times count mismatch. " "Read %d times, but file says %d", loaded_cnt, state->TotalBuildTimes); err = 1; circuit_build_times_reset(cbt); goto done; } circuit_build_times_shuffle_and_store_array(cbt, loaded_times, loaded_cnt); /* Verify that we didn't overwrite any indexes */ for (i=0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { if (!cbt->circuit_build_times[i]) break; tot_values++; } log_info(LD_CIRC, "Loaded %d/%d values from %d lines in circuit time histogram", tot_values, cbt->total_build_times, N); if (cbt->total_build_times != tot_values || cbt->total_build_times > CBT_NCIRCUITS_TO_OBSERVE) { log_warn(LD_CIRC, "Corrupt state file? Shuffled build times mismatch. " "Read %d times, but file says %d", tot_values, state->TotalBuildTimes); err = 1; circuit_build_times_reset(cbt); goto done; } circuit_build_times_set_timeout(cbt); if (!state->CircuitBuildAbandonedCount && cbt->total_build_times) { circuit_build_times_filter_timeouts(cbt); } done: tor_free(loaded_times); return err ? -1 : 0; }