123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- diff --git a/src/app/config/config.c b/src/app/config/config.c
- index deda2448b..059ab0d76 100644
- --- a/src/app/config/config.c
- +++ b/src/app/config/config.c
- @@ -759,6 +759,7 @@ static const config_var_t option_vars_[] = {
- V(TestingDirAuthVoteHSDirIsStrict, BOOL, "0"),
- VAR_INVIS("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_,
- "0"),
- + V(ThroughputLogFile, FILENAME, NULL),
-
- END_OF_CONFIG_VARS
- };
- @@ -3247,6 +3248,8 @@ warn_about_relative_paths(or_options_t *options)
- n += warn_if_option_path_is_relative("PidFile",options->PidFile);
- n += warn_if_option_path_is_relative("ClientOnionAuthDir",
- options->ClientOnionAuthDir);
- + n += warn_if_option_path_is_relative("ThroughputLogFile",
- + options->ThroughputLogFile);
-
- for (config_line_t *hs_line = options->RendConfigLines; hs_line;
- hs_line = hs_line->next) {
- diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h
- index 32dcd9fb1..5ae69f68a 100644
- --- a/src/app/config/or_options_st.h
- +++ b/src/app/config/or_options_st.h
- @@ -1109,6 +1109,8 @@ struct or_options_t {
- **/
- int DormantCanceledByStartup;
-
- + char *ThroughputLogFile;
- +
- /**
- * Configuration objects for individual modules.
- *
- diff --git a/src/core/include.am b/src/core/include.am
- index 9b4b251c8..9c2796173 100644
- --- a/src/core/include.am
- +++ b/src/core/include.am
- @@ -27,6 +27,7 @@ LIBTOR_APP_A_SOURCES = \
- src/core/mainloop/mainloop_sys.c \
- src/core/mainloop/netstatus.c \
- src/core/mainloop/periodic.c \
- + src/core/mainloop/throughput_logging.c \
- src/core/or/address_set.c \
- src/core/or/channel.c \
- src/core/or/channelpadding.c \
- @@ -233,6 +234,7 @@ noinst_HEADERS += \
- src/core/mainloop/mainloop_sys.h \
- src/core/mainloop/netstatus.h \
- src/core/mainloop/periodic.h \
- + src/core/mainloop/throughput_logging.h \
- src/core/or/addr_policy_st.h \
- src/core/or/address_set.h \
- src/core/or/cell_queue_st.h \
- diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
- index 6094f33e4..39d5ac951 100644
- --- a/src/core/mainloop/connection.c
- +++ b/src/core/mainloop/connection.c
- @@ -71,6 +71,7 @@
- #include "core/mainloop/connection.h"
- #include "core/mainloop/mainloop.h"
- #include "core/mainloop/netstatus.h"
- +#include "core/mainloop/throughput_logging.h"
- #include "core/or/channel.h"
- #include "core/or/channeltls.h"
- #include "core/or/circuitbuild.h"
- @@ -175,7 +176,8 @@ static int connection_finished_connecting(connection_t *conn);
- static int connection_reached_eof(connection_t *conn);
- static int connection_buf_read_from_socket(connection_t *conn,
- ssize_t *max_to_read,
- - int *socket_error);
- + int *socket_error,
- + monotime_coarse_t *now);
- static int connection_process_inbuf(connection_t *conn, int package_partial);
- static void client_check_address_changed(tor_socket_t sock);
- static void set_constrained_socket_buffers(tor_socket_t sock, int size);
- @@ -3580,7 +3582,9 @@ connection_handle_read_impl(connection_t *conn)
-
- conn->timestamp_last_read_allowed = approx_time();
-
- - connection_bucket_refill_single(conn, monotime_coarse_get_stamp());
- + monotime_coarse_t now;
- + monotime_coarse_get(&now);
- + connection_bucket_refill_single(conn, monotime_coarse_to_stamp(&now));
-
- switch (conn->type) {
- case CONN_TYPE_OR_LISTENER:
- @@ -3607,7 +3611,7 @@ connection_handle_read_impl(connection_t *conn)
- tor_assert(!conn->marked_for_close);
-
- before = buf_datalen(conn->inbuf);
- - if (connection_buf_read_from_socket(conn, &max_to_read, &socket_error) < 0) {
- + if (connection_buf_read_from_socket(conn, &max_to_read, &socket_error, &now) < 0) {
- /* There's a read error; kill the connection.*/
- if (conn->type == CONN_TYPE_OR) {
- connection_or_notify_error(TO_OR_CONN(conn),
- @@ -3704,7 +3708,7 @@ connection_handle_read(connection_t *conn)
- */
- static int
- connection_buf_read_from_socket(connection_t *conn, ssize_t *max_to_read,
- - int *socket_error)
- + int *socket_error, monotime_coarse_t *now)
- {
- int result;
- ssize_t at_most = *max_to_read;
- @@ -3802,6 +3806,7 @@ connection_buf_read_from_socket(connection_t *conn, ssize_t *max_to_read,
- tor_tls_get_n_raw_bytes(or_conn->tls, &n_read, &n_written);
- log_debug(LD_GENERAL, "After TLS read of %d: %ld read, %ld written",
- result, (long)n_read, (long)n_written);
- + log_recv_bytes(result, now);
- } else if (conn->linked) {
- if (conn->linked_conn) {
- result = buf_move_to_buf(conn->inbuf, conn->linked_conn->outbuf,
- @@ -4009,7 +4014,9 @@ connection_handle_write_impl(connection_t *conn, int force)
-
- conn->timestamp_last_write_allowed = now;
-
- - connection_bucket_refill_single(conn, monotime_coarse_get_stamp());
- + monotime_coarse_t now_coarse;
- + monotime_coarse_get(&now_coarse);
- + connection_bucket_refill_single(conn, monotime_coarse_to_stamp(&now_coarse));
-
- /* Sometimes, "writable" means "connected". */
- if (connection_state_is_connecting(conn)) {
- @@ -4148,6 +4155,7 @@ connection_handle_write_impl(connection_t *conn, int force)
- * the *_buf_tls functions, we should make them return ssize_t or size_t
- * or something. */
- result = (int)(initial_size-buf_datalen(conn->outbuf));
- + log_sent_bytes(result, &now_coarse);
- } else {
- CONN_LOG_PROTECT(conn,
- result = buf_flush_to_socket(conn->outbuf, conn->s,
- diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c
- index c47e44077..48b415db5 100644
- --- a/src/core/mainloop/mainloop.c
- +++ b/src/core/mainloop/mainloop.c
- @@ -57,6 +57,7 @@
- #include "core/mainloop/mainloop.h"
- #include "core/mainloop/netstatus.h"
- #include "core/mainloop/periodic.h"
- +#include "core/mainloop/throughput_logging.h"
- #include "core/or/channel.h"
- #include "core/or/channelpadding.h"
- #include "core/or/channeltls.h"
- @@ -2339,6 +2340,14 @@ do_main_loop(void)
-
- periodic_events_connect_all();
-
- + bool logging_throughput = (get_options()->ThroughputLogFile != NULL &&
- + strlen(get_options()->ThroughputLogFile) != 0);
- +
- + if (logging_throughput) {
- + init_throughput_logging(1);
- + init_thread_throughput_logging(0);
- + }
- +
- struct timeval one_second = { 1, 0 };
- initialize_periodic_events_event = tor_evtimer_new(
- tor_libevent_get_base(),
- @@ -2391,7 +2400,15 @@ do_main_loop(void)
- }
- #endif /* defined(ENABLE_RESTART_DEBUGGING) */
-
- - return run_main_loop_until_done();
- + int rv = run_main_loop_until_done();
- +
- + if (logging_throughput) {
- + destroy_thread_throughput_logging();
- + write_throughput_log(get_options()->ThroughputLogFile);
- + destroy_throughput_logging();
- + }
- +
- + return rv;
- }
-
- #ifndef _WIN32
- diff --git a/src/core/mainloop/throughput_logging.c b/src/core/mainloop/throughput_logging.c
- new file mode 100644
- index 000000000..ee99de1e5
- --- /dev/null
- +++ b/src/core/mainloop/throughput_logging.c
- @@ -0,0 +1,271 @@
- +#include <stdbool.h>
- +#include <stddef.h>
- +#include <stdint.h>
- +#include <stdio.h>
- +#include <string.h>
- +#include <sys/time.h>
- +
- +#include "core/mainloop/throughput_logging.h"
- +
- +#include "lib/lock/compat_mutex.h"
- +#include "lib/smartlist_core/smartlist_core.h"
- +#include "lib/thread/threads.h"
- +#include "lib/malloc/malloc.h"
- +#include "lib/log/util_bug.h"
- +
- +const unsigned int timestep_ms = 500;
- +
- +bool throughput_logging_enabled = false;
- +monotime_coarse_t throughput_logging_coarse_start_time;
- +// NOTE: we don't lock these variables, so make sure they are set
- +// before any threads have started, and that they don't change
- +// while threads are running
- +
- +double throughput_logging_wall_start_time;
- +tor_mutex_t throughput_logging_lock;
- +
- +smartlist_t **sent_lists = NULL;
- +smartlist_t **recv_lists = NULL;
- +tor_mutex_t *bytes_list_mutexes = NULL;
- +int relay_bytes_lists_len = -1;
- +
- +tor_threadlocal_t thread_sent_list;
- +tor_threadlocal_t thread_recv_list;
- +tor_threadlocal_t thread_logging_mutex;
- +
- +// only call if no threads are running
- +void
- +init_throughput_logging(int num_threads)
- +{
- + tor_assert(!throughput_logging_enabled);
- +
- + tor_mutex_init(&throughput_logging_lock);
- + tor_mutex_acquire(&throughput_logging_lock);
- +
- + relay_bytes_lists_len = num_threads;
- +
- + monotime_coarse_get(&throughput_logging_coarse_start_time);
- + struct timeval ts;
- + gettimeofday(&ts, NULL);
- + throughput_logging_wall_start_time = ts.tv_sec+(ts.tv_usec/1000000.0);
- +
- + sent_lists = tor_malloc_zero(num_threads*sizeof(smartlist_t *));
- + recv_lists = tor_malloc_zero(num_threads*sizeof(smartlist_t *));
- + bytes_list_mutexes = tor_malloc_zero(num_threads*sizeof(tor_mutex_t));
- + for (int i=0; i<num_threads; i++) {
- + tor_mutex_init(&bytes_list_mutexes[i]);
- + tor_mutex_acquire(&bytes_list_mutexes[i]);
- + sent_lists[i] = smartlist_new();
- + recv_lists[i] = smartlist_new();
- + tor_mutex_release(&bytes_list_mutexes[i]);
- + }
- +
- + tor_threadlocal_init(&thread_sent_list);
- + tor_threadlocal_init(&thread_recv_list);
- + tor_threadlocal_init(&thread_logging_mutex);
- +
- + throughput_logging_enabled = true;
- + tor_mutex_release(&throughput_logging_lock);
- +}
- +
- +// only call if no threads are running
- +void
- +destroy_throughput_logging(void)
- +{
- + tor_assert(throughput_logging_enabled);
- +
- + tor_mutex_acquire(&throughput_logging_lock);
- +
- + for (int i=0; i<relay_bytes_lists_len; i++) {
- + tor_mutex_acquire(&bytes_list_mutexes[i]);
- +
- + smartlist_free(sent_lists[i]);
- + smartlist_free(recv_lists[i]);
- + sent_lists[i] = NULL;
- + recv_lists[i] = NULL;
- +
- + tor_mutex_release(&bytes_list_mutexes[i]);
- + tor_mutex_uninit(&bytes_list_mutexes[i]);
- + }
- +
- + tor_free(bytes_list_mutexes);
- + tor_free(sent_lists);
- + tor_free(recv_lists);
- + relay_bytes_lists_len = -1;
- +
- + tor_threadlocal_destroy(&thread_sent_list);
- + tor_threadlocal_destroy(&thread_recv_list);
- + tor_threadlocal_destroy(&thread_logging_mutex);
- +
- + throughput_logging_enabled = false;
- +
- + tor_mutex_release(&throughput_logging_lock);
- + tor_mutex_uninit(&throughput_logging_lock);
- +}
- +
- +void
- +init_thread_throughput_logging(int thread_id)
- +{
- + tor_assert(throughput_logging_enabled);
- +
- + tor_mutex_acquire(&throughput_logging_lock);
- +
- + tor_assert(thread_id < relay_bytes_lists_len && thread_id >= 0);
- + tor_threadlocal_set(&thread_logging_mutex, &bytes_list_mutexes[thread_id]);
- + tor_mutex_acquire(&bytes_list_mutexes[thread_id]);
- + // we acquire this mutex for the lifetime of the thread, hope nobody
- + // tries to acquire it :)
- +
- + tor_threadlocal_set(&thread_sent_list, sent_lists[thread_id]);
- + tor_threadlocal_set(&thread_recv_list, recv_lists[thread_id]);
- +
- + tor_mutex_release(&throughput_logging_lock);
- +}
- +
- +void
- +destroy_thread_throughput_logging(void)
- +{
- + tor_assert(throughput_logging_enabled);
- +
- + tor_threadlocal_set(&thread_sent_list, NULL);
- + tor_threadlocal_set(&thread_recv_list, NULL);
- +
- + tor_mutex_t *mutex = tor_threadlocal_get(&thread_logging_mutex);
- + if (mutex != NULL) {
- + tor_mutex_release(mutex);
- + tor_threadlocal_set(&thread_logging_mutex, NULL);
- + }
- +}
- +
- +static void
- +log_throughput(smartlist_t *list, uint32_t bytes, monotime_coarse_t *time)
- +{
- + tor_assert(throughput_logging_enabled);
- +
- + int64_t ms_since_start = monotime_coarse_diff_msec(&throughput_logging_coarse_start_time, time);
- + int list_index = ms_since_start/timestep_ms;
- +
- + if (list_index >= smartlist_len(list)) {
- + // need to grow the list
- + int additional_elements = (60000-1)/timestep_ms + 1;
- + // want an extra 60 seconds, and we want the ceil
- + int new_size = (list_index+1)+additional_elements;
- + // want enough room to store the current value, plus an extra 60 seconds
- + smartlist_grow(list, new_size);
- + }
- +
- + uint32_t old_bytes = (intptr_t)smartlist_get(list, list_index);
- + uint32_t new_bytes = old_bytes+bytes;
- + if (new_bytes < old_bytes) {
- + new_bytes = UINT32_MAX;
- + }
- + smartlist_set(list, list_index, (void *)(intptr_t)new_bytes);
- +}
- +
- +void
- +log_sent_bytes(uint32_t bytes, monotime_coarse_t *now)
- +{
- + if (bytes > 0 && throughput_logging_enabled) {
- + smartlist_t *sent_list = tor_threadlocal_get(&thread_sent_list);
- + tor_assert(sent_list != NULL);
- + log_throughput(sent_list, bytes, now);
- + }
- +}
- +
- +void
- +log_recv_bytes(uint32_t bytes, monotime_coarse_t *now)
- +{
- + if (bytes > 0 && throughput_logging_enabled) {
- + smartlist_t *recv_list = tor_threadlocal_get(&thread_recv_list);
- + tor_assert(recv_list != NULL);
- + log_throughput(recv_list, bytes, now);
- + }
- +}
- +
- +// only run this function when the threads have finished
- +void
- +write_throughput_log(char *file_name)
- +{
- + if (!throughput_logging_enabled) {
- + log_warn(LD_CONFIG, "Throughput logging was not set up, so didn't write to log file");
- + return;
- + }
- +
- + tor_mutex_acquire(&throughput_logging_lock);
- +
- + if (file_name == NULL || strlen(file_name) == 0) {
- + log_warn(LD_CONFIG, "Was not given a file name for the throughput log");
- + tor_mutex_release(&throughput_logging_lock);
- + return;
- + }
- +
- + FILE *log_file = fopen(file_name, "w");
- +
- + if (log_file == NULL) {
- + log_warn(LD_CONFIG, "Could not open throughput log file %s", file_name);
- + tor_mutex_release(&throughput_logging_lock);
- + return;
- + }
- +
- + for (int i=0; i<relay_bytes_lists_len; i++) {
- + tor_mutex_acquire(&bytes_list_mutexes[i]);
- + // this will block if any threads have not finished
- + }
- +
- + struct timeval ts;
- + gettimeofday(&ts, NULL);
- + double current_time = ts.tv_sec+(ts.tv_usec/1000000.0);
- +
- + // write header
- + fprintf(log_file, "time ");
- + for (int i=0; i<relay_bytes_lists_len; i++) {
- + for (int j=0; j<2; j++) {
- + fprintf(log_file, ", thrd %d %s", 0, (j==0)?"sent":"recv");
- + }
- + }
- + fprintf(log_file, "\n");
- +
- + // write data
- + bool thread_had_data = true;
- + int time_index = 0;
- + while (thread_had_data) {
- + // write line
- + thread_had_data = false;
- + double logging_time = throughput_logging_wall_start_time+(time_index*timestep_ms/1000.0);
- + fprintf(log_file, "%.3f", logging_time);
- +
- + for (int i=0; i<relay_bytes_lists_len; i++) {
- + // write column
- + smartlist_t *sent_list = sent_lists[i];
- + smartlist_t *recv_list = recv_lists[i];
- + uint32_t bytes_sent = 0;
- + uint32_t bytes_recv = 0;
- +
- + if (time_index < smartlist_len(sent_list)) {
- + bytes_sent = (intptr_t)smartlist_get(sent_list, time_index);
- + if (logging_time <= current_time || bytes_sent != 0) {
- + thread_had_data = true;
- + }
- + }
- + if (time_index < smartlist_len(recv_list)) {
- + bytes_recv = (intptr_t)smartlist_get(recv_list, time_index);
- + if (logging_time <= current_time || bytes_recv != 0) {
- + thread_had_data = true;
- + }
- + }
- +
- + fprintf(log_file, ", %11"PRIu32", %11"PRIu32, bytes_sent, bytes_recv);
- + }
- +
- + time_index += 1;
- + fprintf(log_file, "\n");
- + }
- +
- + for (int i=0; i<relay_bytes_lists_len; i++) {
- + tor_mutex_release(&bytes_list_mutexes[i]);
- + }
- +
- + fclose(log_file);
- +
- + tor_mutex_release(&throughput_logging_lock);
- +}
- diff --git a/src/core/mainloop/throughput_logging.h b/src/core/mainloop/throughput_logging.h
- new file mode 100644
- index 000000000..3c39cd435
- --- /dev/null
- +++ b/src/core/mainloop/throughput_logging.h
- @@ -0,0 +1,24 @@
- +#ifndef MAINLOOP_THROUGHPUT_LOG_H
- +#define MAINLOOP_THROUGHPUT_LOG_H
- +
- +#include "lib/time/compat_time.h"
- +
- +// the main thread should run the following before any threads have been
- +// created
- +void init_throughput_logging(int num_threads);
- +// the main thread should run the following after all threads have completed
- +void destroy_throughput_logging(void);
- +
- +// each thread should run the following
- +void init_thread_throughput_logging(int thread_id);
- +void destroy_thread_throughput_logging(void);
- +
- +// each thread should log the sent and received bytes with the following
- +void log_sent_bytes(uint32_t bytes, monotime_coarse_t *now);
- +void log_recv_bytes(uint32_t bytes, monotime_coarse_t *now);
- +
- +// the file should be written to after all threads have finished but before
- +// calling 'destroy_throughput_logging()'
- +void write_throughput_log(char *file_name);
- +
- +#endif
|