Add metric on uni/bidirectional connection usage.

This commit is contained in:
Karsten Loesing 2010-08-02 15:06:14 +02:00
parent 520182579a
commit 5dfdf075ac
8 changed files with 262 additions and 2 deletions

View File

@ -848,6 +848,30 @@
Mean number of circuits that are included in any of the deciles, Mean number of circuits that are included in any of the deciles,
rounded up to the next integer. rounded up to the next integer.
"conn-stats-end" YYYY-MM-DD HH:MM:SS (NSEC s) NL
[At most once]
YYYY-MM-DD HH:MM:SS defines the end of the included connection
statistics measurement interval of length NSEC seconds (86400
seconds by default).
A "conn-stats-end" line, as well as any other "conn-*" line,
is first added after the relay has been running for at least 24
hours.
"conn-bi-direct" BELOW,READ,WRITE,BOTH NL
[At most once]
Number of connections, split into 10-second intervals, that are
used uni-directionally or bi-directionally. Every 10 seconds,
we determine for every connection whether we read and wrote less
than a threshold of 20 KiB (BELOW), read at least 10 times more
than we wrote (READ), wrote at least 10 times more than we read
(WRITE), or read and wrote more than the threshold, but not 10
times more in either direction (BOTH). After classifying a
connection, read and write counters are reset for the next
10-second interval.
"exit-stats-end" YYYY-MM-DD HH:MM:SS (NSEC s) NL "exit-stats-end" YYYY-MM-DD HH:MM:SS (NSEC s) NL
[At most once.] [At most once.]

View File

@ -200,6 +200,7 @@ static config_var_t _option_vars[] = {
V(ClientOnly, BOOL, "0"), V(ClientOnly, BOOL, "0"),
V(ConsensusParams, STRING, NULL), V(ConsensusParams, STRING, NULL),
V(ConnLimit, UINT, "1000"), V(ConnLimit, UINT, "1000"),
V(ConnStatistics, BOOL, "0"),
V(ConstrainedSockets, BOOL, "0"), V(ConstrainedSockets, BOOL, "0"),
V(ConstrainedSockSize, MEMUNIT, "8192"), V(ConstrainedSockSize, MEMUNIT, "8192"),
V(ContactInfo, STRING, NULL), V(ContactInfo, STRING, NULL),
@ -1391,7 +1392,8 @@ options_act(or_options_t *old_options)
} }
if (options->CellStatistics || options->DirReqStatistics || if (options->CellStatistics || options->DirReqStatistics ||
options->EntryStatistics || options->ExitPortStatistics) { options->EntryStatistics || options->ExitPortStatistics ||
options->ConnStatistics) {
time_t now = time(NULL); time_t now = time(NULL);
if ((!old_options || !old_options->CellStatistics) && if ((!old_options || !old_options->CellStatistics) &&
options->CellStatistics) options->CellStatistics)
@ -1405,6 +1407,9 @@ options_act(or_options_t *old_options)
if ((!old_options || !old_options->ExitPortStatistics) && if ((!old_options || !old_options->ExitPortStatistics) &&
options->ExitPortStatistics) options->ExitPortStatistics)
rep_hist_exit_stats_init(now); rep_hist_exit_stats_init(now);
if ((!old_options || !old_options->ConnStatistics) &&
options->ConnStatistics)
rep_hist_conn_stats_init(now);
if (!old_options) if (!old_options)
log_notice(LD_CONFIG, "Configured to measure statistics. Look for " log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
"the *-stats files that will first be written to the " "the *-stats files that will first be written to the "
@ -1423,6 +1428,9 @@ options_act(or_options_t *old_options)
if (old_options && old_options->ExitPortStatistics && if (old_options && old_options->ExitPortStatistics &&
!options->ExitPortStatistics) !options->ExitPortStatistics)
rep_hist_exit_stats_term(); rep_hist_exit_stats_term();
if (old_options && old_options->ConnStatistics &&
!options->ConnStatistics)
rep_hist_conn_stats_term();
/* Check if we need to parse and add the EntryNodes config option. */ /* Check if we need to parse and add the EntryNodes config option. */
if (options->EntryNodes && if (options->EntryNodes &&

View File

@ -2185,6 +2185,11 @@ connection_buckets_decrement(connection_t *conn, time_t now,
if (!connection_is_rate_limited(conn)) if (!connection_is_rate_limited(conn))
return; /* local IPs are free */ return; /* local IPs are free */
if (conn->type == CONN_TYPE_OR)
rep_hist_note_or_conn_bytes(conn->global_identifier, num_read,
num_written, now);
if (num_read > 0) { if (num_read > 0) {
rep_hist_note_bytes_read(num_read, now); rep_hist_note_bytes_read(num_read, now);
} }

View File

@ -1204,6 +1204,11 @@ run_scheduled_events(time_t now)
if (next_write && next_write < next_time_to_write_stats_files) if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write; next_time_to_write_stats_files = next_write;
} }
if (options->ConnStatistics) {
time_t next_write = rep_hist_conn_stats_write(time_to_write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
time_to_write_stats_files = next_time_to_write_stats_files; time_to_write_stats_files = next_time_to_write_stats_files;
} }

View File

@ -2894,6 +2894,9 @@ typedef struct {
/** If true, the user wants us to collect statistics on port usage. */ /** If true, the user wants us to collect statistics on port usage. */
int ExitPortStatistics; int ExitPortStatistics;
/** If true, the user wants us to collect connection statistics. */
int ConnStatistics;
/** If true, the user wants us to collect cell statistics. */ /** If true, the user wants us to collect cell statistics. */
int CellStatistics; int CellStatistics;

View File

@ -7,7 +7,7 @@
* \brief Basic history and "reputation" functionality to remember * \brief Basic history and "reputation" functionality to remember
* which servers have worked in the past, how much bandwidth we've * which servers have worked in the past, how much bandwidth we've
* been using, which ports we tend to want, and so on; further, * been using, which ports we tend to want, and so on; further,
* exit port statistics and cell statistics. * exit port statistics, cell statistics, and connection statistics.
**/ **/
#include "or.h" #include "or.h"
@ -2418,6 +2418,201 @@ rep_hist_buffer_stats_write(time_t now)
return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL; return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL;
} }
/*** Connection statistics ***/
/** Start of the current connection stats interval or 0 if we're not
* collecting connection statistics. */
static time_t start_of_conn_stats_interval;
/** Initialize connection stats. */
void
rep_hist_conn_stats_init(time_t now)
{
start_of_conn_stats_interval = now;
}
#define BIDI_THRESHOLD 20480
#define BIDI_FACTOR 10
#define BIDI_INTERVAL 10
/* Start of next BIDI_INTERVAL second interval. */
static time_t bidi_next_interval = 0;
/* Number of connections that we read and wrote less than BIDI_THRESHOLD
* bytes from/to in BIDI_INTERVAL seconds. */
static uint32_t below_threshold = 0;
/* Number of connections that we read at least BIDI_FACTOR times more
* bytes from than we wrote to in BIDI_INTERVAL seconds. */
static uint32_t mostly_read = 0;
/* Number of connections that we wrote at least BIDI_FACTOR times more
* bytes to than we read from in BIDI_INTERVAL seconds. */
static uint32_t mostly_written = 0;
/* Number of connections that we read and wrote at least BIDI_THRESHOLD
* bytes from/to, but not BIDI_FACTOR times more in either direction in
* BIDI_INTERVAL seconds. */
static uint32_t both_read_and_written = 0;
/* Entry in a map from connection ID to the number of read and written
* bytes on this connection in a BIDI_INTERVAL second interval. */
typedef struct bidi_map_entry_t {
HT_ENTRY(bidi_map_entry_t) node;
uint64_t conn_id; /**< Connection ID */
size_t read; /**< Number of read bytes */
size_t written; /**< Number of written bytes */
} bidi_map_entry_t;
/** Map of OR connections together with the number of read and written
* bytes in the current BIDI_INTERVAL second interval. */
static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map =
HT_INITIALIZER();
static int
bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b)
{
return a->conn_id == b->conn_id;
}
static unsigned
bidi_map_ent_hash(const bidi_map_entry_t *entry)
{
return (unsigned) entry->conn_id;
}
HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
bidi_map_ent_eq);
HT_GENERATE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
bidi_map_ent_eq, 0.6, malloc, realloc, free);
static void
bidi_map_free(void)
{
bidi_map_entry_t **ptr, **next, *ent;
for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
ent = *ptr;
next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
tor_free(ent);
}
HT_CLEAR(bidimap, &bidi_map);
}
/** Stop collecting connection stats in a way that we can re-start doing
* so in rep_hist_conn_stats_init(). */
void
rep_hist_conn_stats_term(void)
{
below_threshold = 0;
mostly_read = 0;
mostly_written = 0;
both_read_and_written = 0;
start_of_conn_stats_interval = 0;
bidi_map_free();
}
/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR
* connection <b>conn_id</b> in second <b>when</b>. If this is the first
* observation in a new interval, sum up the last observations. Add bytes
* for this connection. */
void
rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
size_t num_written, time_t when)
{
if (!start_of_conn_stats_interval)
return;
/* Initialize */
if (bidi_next_interval == 0)
bidi_next_interval = when + BIDI_INTERVAL;
/* Sum up last period's statistics */
if (when >= bidi_next_interval) {
bidi_map_entry_t **ptr, **next, *ent;
for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
ent = *ptr;
if (ent->read + ent->written < BIDI_THRESHOLD)
below_threshold++;
else if (ent->read >= ent->written * BIDI_FACTOR)
mostly_read++;
else if (ent->written >= ent->read * BIDI_FACTOR)
mostly_written++;
else
both_read_and_written++;
next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
tor_free(ent);
}
while (when >= bidi_next_interval)
bidi_next_interval += BIDI_INTERVAL;
log_info(LD_GENERAL, "%d below threshold, %d mostly read, "
"%d mostly written, %d both read and written.",
below_threshold, mostly_read, mostly_written,
both_read_and_written);
}
/* Add this connection's bytes. */
if (num_read > 0 || num_written > 0) {
bidi_map_entry_t *entry, lookup;
lookup.conn_id = conn_id;
entry = HT_FIND(bidimap, &bidi_map, &lookup);
if (entry) {
entry->written += num_written;
entry->read += num_read;
} else {
entry = tor_malloc_zero(sizeof(bidi_map_entry_t));
entry->conn_id = conn_id;
entry->written = num_written;
entry->read = num_read;
HT_INSERT(bidimap, &bidi_map, entry);
}
}
}
/** Write conn statistics to $DATADIR/stats/conn-stats and return when
* we would next want to write conn stats. */
time_t
rep_hist_conn_stats_write(time_t now)
{
char *statsdir = NULL, *filename = NULL;
char written[ISO_TIME_LEN+1];
open_file_t *open_file = NULL;
FILE *out;
if (!start_of_conn_stats_interval)
return 0; /* Not initialized. */
if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now)
goto done; /* Not ready to write */
/* write to file */
statsdir = get_datadir_fname("stats");
if (check_private_dir(statsdir, CPD_CREATE) < 0)
goto done;
filename = get_datadir_fname2("stats", "conn-stats");
out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND,
0600, &open_file);
if (!out)
goto done;
format_iso_time(written, now);
if (fprintf(out, "conn-stats-end %s (%d s)\n", written,
(unsigned) (now - start_of_conn_stats_interval)) < 0)
goto done;
if (fprintf(out, "conn-bi-direct %d,%d,%d,%d\n",
below_threshold, mostly_read, mostly_written,
both_read_and_written) < 0)
goto done;
finish_writing_to_file(open_file);
open_file = NULL;
start_of_conn_stats_interval = now;
done:
if (open_file)
abort_writing_to_file(open_file);
tor_free(filename);
tor_free(statsdir);
return start_of_conn_stats_interval + WRITE_STATS_INTERVAL;
}
/** Free all storage held by the OR/link history caches, by the /** Free all storage held by the OR/link history caches, by the
* bandwidth history arrays, by the port history, or by statistics . */ * bandwidth history arrays, by the port history, or by statistics . */
void void
@ -2432,5 +2627,6 @@ rep_hist_free_all(void)
tor_free(exit_streams); tor_free(exit_streams);
built_last_stability_doc_at = 0; built_last_stability_doc_at = 0;
predicted_ports_free(); predicted_ports_free();
bidi_map_free();
} }

View File

@ -76,5 +76,11 @@ void rep_hist_buffer_stats_add_circ(circuit_t *circ,
time_t rep_hist_buffer_stats_write(time_t now); time_t rep_hist_buffer_stats_write(time_t now);
void rep_hist_buffer_stats_term(void); void rep_hist_buffer_stats_term(void);
void rep_hist_conn_stats_init(time_t now);
void rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
size_t num_written, time_t when);
time_t rep_hist_conn_stats_write(time_t now);
void rep_hist_conn_stats_term(void);
#endif #endif

View File

@ -2051,6 +2051,19 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
"exit-stats-end", now, &contents) > 0) { "exit-stats-end", now, &contents) > 0) {
smartlist_add(chunks, contents); smartlist_add(chunks, contents);
} }
if (options->ConnStatistics &&
load_stats_file("stats"PATH_SEPARATOR"conn-stats",
"conn-stats-end", now, &contents) > 0) {
size_t pos = strlen(s);
if (strlcpy(s + pos, contents, maxlen - strlen(s)) !=
strlen(contents)) {
log_warn(LD_DIR, "Could not write conn-stats to extra-info "
"descriptor.");
s[pos] = '\0';
write_stats_to_extrainfo = 0;
}
tor_free(contents);
}
} }
if (should_record_bridge_info(options) && write_stats_to_extrainfo) { if (should_record_bridge_info(options) && write_stats_to_extrainfo) {