throughput_logging.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. #include <stdbool.h>
  2. #include <stddef.h>
  3. #include <stdint.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <sys/time.h>
  7. #include "core/mainloop/throughput_logging.h"
  8. #include "lib/lock/compat_mutex.h"
  9. #include "lib/smartlist_core/smartlist_core.h"
  10. #include "lib/thread/threads.h"
  11. #include "lib/malloc/malloc.h"
  12. #include "lib/log/util_bug.h"
  13. const unsigned int timestep_ms = 500;
  14. bool throughput_logging_enabled = false;
  15. monotime_coarse_t throughput_logging_coarse_start_time;
  16. // NOTE: we don't lock these variables, so make sure they are set
  17. // before any threads have started, and that they don't change
  18. // while threads are running
  19. double throughput_logging_wall_start_time;
  20. tor_mutex_t throughput_logging_lock;
  21. smartlist_t **sent_lists = NULL;
  22. smartlist_t **recv_lists = NULL;
  23. tor_mutex_t *bytes_list_mutexes = NULL;
  24. int relay_bytes_lists_len = -1;
  25. tor_threadlocal_t thread_sent_list;
  26. tor_threadlocal_t thread_recv_list;
  27. tor_threadlocal_t thread_logging_mutex;
  28. // only call if no threads are running
  29. void
  30. init_throughput_logging(int num_threads)
  31. {
  32. tor_assert(!throughput_logging_enabled);
  33. tor_mutex_init(&throughput_logging_lock);
  34. tor_mutex_acquire(&throughput_logging_lock);
  35. relay_bytes_lists_len = num_threads;
  36. monotime_coarse_get(&throughput_logging_coarse_start_time);
  37. struct timeval ts;
  38. gettimeofday(&ts, NULL);
  39. throughput_logging_wall_start_time = ts.tv_sec+(ts.tv_usec/1000000.0);
  40. sent_lists = tor_malloc_zero(num_threads*sizeof(smartlist_t *));
  41. recv_lists = tor_malloc_zero(num_threads*sizeof(smartlist_t *));
  42. bytes_list_mutexes = tor_malloc_zero(num_threads*sizeof(tor_mutex_t));
  43. for (int i=0; i<num_threads; i++) {
  44. tor_mutex_init(&bytes_list_mutexes[i]);
  45. tor_mutex_acquire(&bytes_list_mutexes[i]);
  46. sent_lists[i] = smartlist_new();
  47. recv_lists[i] = smartlist_new();
  48. tor_mutex_release(&bytes_list_mutexes[i]);
  49. }
  50. tor_threadlocal_init(&thread_sent_list);
  51. tor_threadlocal_init(&thread_recv_list);
  52. tor_threadlocal_init(&thread_logging_mutex);
  53. throughput_logging_enabled = true;
  54. tor_mutex_release(&throughput_logging_lock);
  55. }
  56. // only call if no threads are running
  57. void
  58. destroy_throughput_logging(void)
  59. {
  60. tor_assert(throughput_logging_enabled);
  61. tor_mutex_acquire(&throughput_logging_lock);
  62. for (int i=0; i<relay_bytes_lists_len; i++) {
  63. tor_mutex_acquire(&bytes_list_mutexes[i]);
  64. smartlist_free(sent_lists[i]);
  65. smartlist_free(recv_lists[i]);
  66. sent_lists[i] = NULL;
  67. recv_lists[i] = NULL;
  68. tor_mutex_release(&bytes_list_mutexes[i]);
  69. tor_mutex_uninit(&bytes_list_mutexes[i]);
  70. }
  71. tor_free(bytes_list_mutexes);
  72. tor_free(sent_lists);
  73. tor_free(recv_lists);
  74. relay_bytes_lists_len = -1;
  75. tor_threadlocal_destroy(&thread_sent_list);
  76. tor_threadlocal_destroy(&thread_recv_list);
  77. tor_threadlocal_destroy(&thread_logging_mutex);
  78. throughput_logging_enabled = false;
  79. tor_mutex_release(&throughput_logging_lock);
  80. tor_mutex_uninit(&throughput_logging_lock);
  81. }
  82. void
  83. init_thread_throughput_logging(int thread_id)
  84. {
  85. tor_assert(throughput_logging_enabled);
  86. tor_mutex_acquire(&throughput_logging_lock);
  87. tor_assert(thread_id < relay_bytes_lists_len && thread_id >= 0);
  88. tor_threadlocal_set(&thread_logging_mutex, &bytes_list_mutexes[thread_id]);
  89. tor_mutex_acquire(&bytes_list_mutexes[thread_id]);
  90. // we acquire this mutex for the lifetime of the thread, hope nobody
  91. // tries to acquire it :)
  92. tor_threadlocal_set(&thread_sent_list, sent_lists[thread_id]);
  93. tor_threadlocal_set(&thread_recv_list, recv_lists[thread_id]);
  94. tor_mutex_release(&throughput_logging_lock);
  95. }
  96. void
  97. destroy_thread_throughput_logging(void)
  98. {
  99. tor_assert(throughput_logging_enabled);
  100. tor_threadlocal_set(&thread_sent_list, NULL);
  101. tor_threadlocal_set(&thread_recv_list, NULL);
  102. tor_mutex_t *mutex = tor_threadlocal_get(&thread_logging_mutex);
  103. if (mutex != NULL) {
  104. tor_mutex_release(mutex);
  105. tor_threadlocal_set(&thread_logging_mutex, NULL);
  106. }
  107. }
  108. static void
  109. log_throughput(smartlist_t *list, uint32_t bytes, monotime_coarse_t *time)
  110. {
  111. tor_assert(throughput_logging_enabled);
  112. int64_t ms_since_start = monotime_coarse_diff_msec(&throughput_logging_coarse_start_time, time);
  113. int list_index = ms_since_start/timestep_ms;
  114. if (list_index >= smartlist_len(list)) {
  115. // need to grow the list
  116. int additional_elements = (60000-1)/timestep_ms + 1;
  117. // want an extra 60 seconds, and we want the ceil
  118. int new_size = (list_index+1)+additional_elements;
  119. // want enough room to store the current value, plus an extra 60 seconds
  120. smartlist_grow(list, new_size);
  121. }
  122. uint32_t old_bytes = (intptr_t)smartlist_get(list, list_index);
  123. uint32_t new_bytes = old_bytes+bytes;
  124. if (new_bytes < old_bytes) {
  125. new_bytes = UINT32_MAX;
  126. }
  127. smartlist_set(list, list_index, (void *)(intptr_t)new_bytes);
  128. }
  129. void
  130. log_sent_bytes(uint32_t bytes, monotime_coarse_t *now)
  131. {
  132. if (bytes > 0 && throughput_logging_enabled) {
  133. smartlist_t *sent_list = tor_threadlocal_get(&thread_sent_list);
  134. tor_assert(sent_list != NULL);
  135. log_throughput(sent_list, bytes, now);
  136. }
  137. }
  138. void
  139. log_recv_bytes(uint32_t bytes, monotime_coarse_t *now)
  140. {
  141. if (bytes > 0 && throughput_logging_enabled) {
  142. smartlist_t *recv_list = tor_threadlocal_get(&thread_recv_list);
  143. tor_assert(recv_list != NULL);
  144. log_throughput(recv_list, bytes, now);
  145. }
  146. }
  147. // only run this function when the threads have finished
  148. void
  149. write_throughput_log(char *file_name)
  150. {
  151. if (!throughput_logging_enabled) {
  152. log_warn(LD_CONFIG, "Throughput logging was not set up, so didn't write to log file");
  153. return;
  154. }
  155. tor_mutex_acquire(&throughput_logging_lock);
  156. if (file_name == NULL || strlen(file_name) == 0) {
  157. log_warn(LD_CONFIG, "Was not given a file name for the throughput log");
  158. tor_mutex_release(&throughput_logging_lock);
  159. return;
  160. }
  161. FILE *log_file = fopen(file_name, "w");
  162. if (log_file == NULL) {
  163. log_warn(LD_CONFIG, "Could not open throughput log file %s", file_name);
  164. tor_mutex_release(&throughput_logging_lock);
  165. return;
  166. }
  167. for (int i=0; i<relay_bytes_lists_len; i++) {
  168. tor_mutex_acquire(&bytes_list_mutexes[i]);
  169. // this will block if any threads have not finished
  170. }
  171. struct timeval ts;
  172. gettimeofday(&ts, NULL);
  173. double current_time = ts.tv_sec+(ts.tv_usec/1000000.0);
  174. // write header
  175. fprintf(log_file, "time ");
  176. for (int i=0; i<relay_bytes_lists_len; i++) {
  177. for (int j=0; j<2; j++) {
  178. fprintf(log_file, ", thrd %d %s", 0, (j==0)?"sent":"recv");
  179. }
  180. }
  181. fprintf(log_file, "\n");
  182. // write data
  183. bool thread_had_data = true;
  184. int time_index = 0;
  185. while (thread_had_data) {
  186. // write line
  187. thread_had_data = false;
  188. double logging_time = throughput_logging_wall_start_time+(time_index*timestep_ms/1000.0);
  189. fprintf(log_file, "%.3f", logging_time);
  190. for (int i=0; i<relay_bytes_lists_len; i++) {
  191. // write column
  192. smartlist_t *sent_list = sent_lists[i];
  193. smartlist_t *recv_list = recv_lists[i];
  194. uint32_t bytes_sent = 0;
  195. uint32_t bytes_recv = 0;
  196. if (time_index < smartlist_len(sent_list)) {
  197. bytes_sent = (intptr_t)smartlist_get(sent_list, time_index);
  198. if (logging_time <= current_time || bytes_sent != 0) {
  199. thread_had_data = true;
  200. }
  201. }
  202. if (time_index < smartlist_len(recv_list)) {
  203. bytes_recv = (intptr_t)smartlist_get(recv_list, time_index);
  204. if (logging_time <= current_time || bytes_recv != 0) {
  205. thread_had_data = true;
  206. }
  207. }
  208. fprintf(log_file, ", %11"PRIu32", %11"PRIu32, bytes_sent, bytes_recv);
  209. }
  210. time_index += 1;
  211. fprintf(log_file, "\n");
  212. }
  213. for (int i=0; i<relay_bytes_lists_len; i++) {
  214. tor_mutex_release(&bytes_list_mutexes[i]);
  215. }
  216. fclose(log_file);
  217. tor_mutex_release(&throughput_logging_lock);
  218. }