diff options
author | George Kadianakis <desnacked@riseup.net> | 2021-03-09 15:36:40 +0200 |
---|---|---|
committer | George Kadianakis <desnacked@riseup.net> | 2021-03-17 18:22:38 +0200 |
commit | 0a5ecb334298187a64f58382231245111130aa76 (patch) | |
tree | 90ddab78a61f285b17ab01a7893763e5f39684a8 /src/feature | |
parent | 80b6054bb016aca0b18f8da79f23a859a804ef07 (diff) | |
download | tor-0a5ecb334298187a64f58382231245111130aa76.tar.gz tor-0a5ecb334298187a64f58382231245111130aa76.zip |
Implement backbone of overload statistics.
- Implement overload statistics structure.
- Implement function that keeps track of overload statistics.
- Implement function that writes overload statistics to descriptor.
- Unittest for the whole logic.
Diffstat (limited to 'src/feature')
-rw-r--r-- | src/feature/relay/router.c | 6 | ||||
-rw-r--r-- | src/feature/stats/rephist.c | 116 | ||||
-rw-r--r-- | src/feature/stats/rephist.h | 18 |
3 files changed, 140 insertions, 0 deletions
diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 73e8393761..88160bd1cb 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -3372,6 +3372,12 @@ extrainfo_dump_to_string_stats_helper(smartlist_t *chunks, if (contents) smartlist_add(chunks, contents); } + if (options->OverloadStatistics) { + contents = rep_hist_get_overload_stats_lines(); + if (contents) { + smartlist_add(chunks, contents); + } + } /* bridge statistics */ if (should_record_bridge_info(options)) { const char *bridge_stats = geoip_get_bridge_stats_extrainfo(now); diff --git a/src/feature/stats/rephist.c b/src/feature/stats/rephist.c index f7c9336bff..64bec000df 100644 --- a/src/feature/stats/rephist.c +++ b/src/feature/stats/rephist.c @@ -183,6 +183,122 @@ static time_t started_tracking_stability = 0; /** Map from hex OR identity digest to or_history_t. */ static digestmap_t *history_map = NULL; +/** Represents a state of overload stats. + * + * All the timestamps in this structure have already been rounded down to the + * nearest hour. */ +typedef struct { + /* When did we last experience a general overload? */ + time_t overload_general_time; + + /* When did we last experience a bandwidth-related overload? */ + time_t overload_ratelimits_time; + /* How many times have we gone off the our read limits? */ + uint64_t overload_read_count; + /* How many times have we gone off the our write limits? */ + uint64_t overload_write_count; + + /* When did we last experience a file descriptor exhaustion? */ + time_t overload_fd_exhausted_time; + /* How many times have we experienced a file descriptor exhaustion? */ + uint64_t overload_fd_exhausted; +} overload_stats_t; + +/** Current state of overload stats */ +static overload_stats_t overload_stats; + +/** Return true if this overload happened within the last `n_hours`. */ +static bool +overload_happened_recently(time_t overload_time, unsigned n_hours) +{ + /* An overload is relevant if it happened in the last 72 hours */ + if (overload_time > approx_time() - 3600 * n_hours) { + return true; + } + return false; +} + +/* The current version of the overload stats version */ +#define OVERLOAD_STATS_VERSION 1 + +/** Returns an allocated string for extra-info documents for publishing + * overload statistics. */ +char * +rep_hist_get_overload_stats_lines(void) +{ + char *result = NULL; + smartlist_t *chunks = smartlist_new(); + char tbuf[ISO_TIME_LEN+1]; + + /* First encode the general overload */ + if (overload_happened_recently(overload_stats.overload_general_time, 72)) { + format_iso_time(tbuf, overload_stats.overload_general_time); + smartlist_add_asprintf(chunks, "overload-general %d %s", + OVERLOAD_STATS_VERSION, tbuf); + } + + /* Now do bandwidth-related overloads */ + if (overload_happened_recently(overload_stats.overload_ratelimits_time,24)) { + const or_options_t *options = get_options(); + format_iso_time(tbuf, overload_stats.overload_ratelimits_time); + smartlist_add_asprintf(chunks, + "overload-ratelimits %d %s %" PRIu64 " %" PRIu64 + " %" PRIu64 " %" PRIu64, + OVERLOAD_STATS_VERSION, tbuf, + options->BandwidthRate, options->BandwidthBurst, + overload_stats.overload_read_count, + overload_stats.overload_write_count); + } + + /* Finally file descriptor overloads */ + if (overload_happened_recently( + overload_stats.overload_fd_exhausted_time, 72)) { + format_iso_time(tbuf, overload_stats.overload_fd_exhausted_time); + smartlist_add_asprintf(chunks, "overload-fd-exhausted %d %s", + OVERLOAD_STATS_VERSION, tbuf); + } + + /* Bail early if we had nothing to write */ + if (smartlist_len(chunks) == 0) { + goto done; + } + + result = smartlist_join_strings(chunks, "\n", 0, NULL); + + done: + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_free(chunks); + return result; +} + +/** Round down the time in `a` to the beginning of the current hour */ +#define SET_TO_START_OF_HOUR(a) STMT_BEGIN \ + (a) = approx_time() - (approx_time() % 3600); \ +STMT_END + +/** Note down an overload event of type `overload`. */ +void +rep_hist_note_overload(overload_type_t overload) +{ + switch (overload) { + case OVERLOAD_GENERAL: + SET_TO_START_OF_HOUR(overload_stats.overload_general_time); + break; + case OVERLOAD_READ: + SET_TO_START_OF_HOUR(overload_stats.overload_ratelimits_time); + overload_stats.overload_read_count++; + break; + case OVERLOAD_WRITE: + SET_TO_START_OF_HOUR(overload_stats.overload_ratelimits_time); + overload_stats.overload_write_count++; + break; + case OVERLOAD_FD_EXHAUSTED: + SET_TO_START_OF_HOUR(overload_stats.overload_fd_exhausted_time); + overload_stats.overload_fd_exhausted++; + break; + } +} + /** Return the or_history_t for the OR with identity digest <b>id</b>, * creating it if necessary. */ static or_history_t * diff --git a/src/feature/stats/rephist.h b/src/feature/stats/rephist.h index 7ecbfebfd7..45020d7967 100644 --- a/src/feature/stats/rephist.h +++ b/src/feature/stats/rephist.h @@ -140,6 +140,24 @@ void rep_hist_reset_padding_counts(void); void rep_hist_prep_published_padding_counts(time_t now); void rep_hist_padding_count_timers(uint64_t num_timers); +/** + * Represents the various types of overload we keep track of and expose in our + * extra-info descriptor. +*/ +typedef enum { + /* A general overload -- can have many different causes. */ + OVERLOAD_GENERAL, + /* We went over our configured read rate/burst bandwidth limit */ + OVERLOAD_READ, + /* We went over our configured write rate/burst bandwidth limit */ + OVERLOAD_WRITE, + /* We exhausted the file descriptors in this system */ + OVERLOAD_FD_EXHAUSTED, +} overload_type_t; + +void rep_hist_note_overload(overload_type_t overload); +char *rep_hist_get_overload_stats_lines(void); + #ifdef TOR_UNIT_TESTS struct hs_v2_stats_t; const struct hs_v2_stats_t *rep_hist_get_hs_v2_stats(void); |