basic_timers.cc 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /*
  2. *
  3. * Copyright 2015 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. #include <grpc/support/port_platform.h>
  19. #include "src/core/lib/profiling/timers.h"
  20. #ifdef GRPC_BASIC_PROFILER
  21. #include <grpc/support/alloc.h>
  22. #include <grpc/support/log.h>
  23. #include <grpc/support/sync.h>
  24. #include <grpc/support/time.h>
  25. #include <inttypes.h>
  26. #include <pthread.h>
  27. #include <stdio.h>
  28. #include <string.h>
  29. #include "src/core/lib/gpr/env.h"
  30. typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type;
  31. typedef struct gpr_timer_entry {
  32. gpr_timespec tm;
  33. const char* tagstr;
  34. const char* file;
  35. short line;
  36. char type;
  37. uint8_t important;
  38. int thd;
  39. } gpr_timer_entry;
  40. #define MAX_COUNT 1000000
  41. typedef struct gpr_timer_log {
  42. size_t num_entries;
  43. struct gpr_timer_log* next;
  44. struct gpr_timer_log* prev;
  45. gpr_timer_entry log[MAX_COUNT];
  46. } gpr_timer_log;
  47. typedef struct gpr_timer_log_list {
  48. gpr_timer_log* head;
  49. /* valid iff head!=NULL */
  50. gpr_timer_log* tail;
  51. } gpr_timer_log_list;
  52. static __thread gpr_timer_log* g_thread_log;
  53. static gpr_once g_once_init = GPR_ONCE_INIT;
  54. static FILE* output_file;
  55. static const char* output_filename_or_null = NULL;
  56. static pthread_mutex_t g_mu;
  57. static pthread_cond_t g_cv;
  58. static gpr_timer_log_list g_in_progress_logs;
  59. static gpr_timer_log_list g_done_logs;
  60. static int g_shutdown;
  61. static pthread_t g_writing_thread;
  62. static __thread int g_thread_id;
  63. static int g_next_thread_id;
  64. static int g_writing_enabled = 1;
  65. static const char* output_filename() {
  66. if (output_filename_or_null == NULL) {
  67. output_filename_or_null = gpr_getenv("LATENCY_TRACE");
  68. if (output_filename_or_null == NULL ||
  69. strlen(output_filename_or_null) == 0) {
  70. output_filename_or_null = "latency_trace.txt";
  71. }
  72. }
  73. return output_filename_or_null;
  74. }
  75. static int timer_log_push_back(gpr_timer_log_list* list, gpr_timer_log* log) {
  76. if (list->head == NULL) {
  77. list->head = list->tail = log;
  78. log->next = log->prev = NULL;
  79. return 1;
  80. } else {
  81. log->prev = list->tail;
  82. log->next = NULL;
  83. list->tail->next = log;
  84. list->tail = log;
  85. return 0;
  86. }
  87. }
  88. static gpr_timer_log* timer_log_pop_front(gpr_timer_log_list* list) {
  89. gpr_timer_log* out = list->head;
  90. if (out != NULL) {
  91. list->head = out->next;
  92. if (list->head != NULL) {
  93. list->head->prev = NULL;
  94. } else {
  95. list->tail = NULL;
  96. }
  97. }
  98. return out;
  99. }
  100. static void timer_log_remove(gpr_timer_log_list* list, gpr_timer_log* log) {
  101. if (log->prev == NULL) {
  102. list->head = log->next;
  103. if (list->head != NULL) {
  104. list->head->prev = NULL;
  105. }
  106. } else {
  107. log->prev->next = log->next;
  108. }
  109. if (log->next == NULL) {
  110. list->tail = log->prev;
  111. if (list->tail != NULL) {
  112. list->tail->next = NULL;
  113. }
  114. } else {
  115. log->next->prev = log->prev;
  116. }
  117. }
  118. static void write_log(gpr_timer_log* log) {
  119. size_t i;
  120. if (output_file == NULL) {
  121. output_file = fopen(output_filename(), "w");
  122. }
  123. for (i = 0; i < log->num_entries; i++) {
  124. gpr_timer_entry* entry = &(log->log[i]);
  125. if (gpr_time_cmp(entry->tm, gpr_time_0(entry->tm.clock_type)) < 0) {
  126. entry->tm = gpr_time_0(entry->tm.clock_type);
  127. }
  128. fprintf(output_file,
  129. "{\"t\": %" PRId64
  130. ".%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": "
  131. "\"%s\", \"file\": \"%s\", \"line\": %d, \"imp\": %d}\n",
  132. entry->tm.tv_sec, entry->tm.tv_nsec, entry->thd, entry->type,
  133. entry->tagstr, entry->file, entry->line, entry->important);
  134. }
  135. }
  136. static void* writing_thread(void* unused) {
  137. gpr_timer_log* log;
  138. pthread_mutex_lock(&g_mu);
  139. for (;;) {
  140. while ((log = timer_log_pop_front(&g_done_logs)) == NULL && !g_shutdown) {
  141. pthread_cond_wait(&g_cv, &g_mu);
  142. }
  143. if (log != NULL) {
  144. pthread_mutex_unlock(&g_mu);
  145. write_log(log);
  146. free(log);
  147. pthread_mutex_lock(&g_mu);
  148. }
  149. if (g_shutdown) {
  150. pthread_mutex_unlock(&g_mu);
  151. return NULL;
  152. }
  153. }
  154. }
  155. static void flush_logs(gpr_timer_log_list* list) {
  156. gpr_timer_log* log;
  157. while ((log = timer_log_pop_front(list)) != NULL) {
  158. write_log(log);
  159. free(log);
  160. }
  161. }
  162. static void finish_writing(void) {
  163. pthread_mutex_lock(&g_mu);
  164. g_shutdown = 1;
  165. pthread_cond_signal(&g_cv);
  166. pthread_mutex_unlock(&g_mu);
  167. pthread_join(g_writing_thread, NULL);
  168. gpr_log(GPR_INFO, "flushing logs");
  169. pthread_mutex_lock(&g_mu);
  170. flush_logs(&g_done_logs);
  171. flush_logs(&g_in_progress_logs);
  172. pthread_mutex_unlock(&g_mu);
  173. if (output_file) {
  174. fclose(output_file);
  175. }
  176. }
  177. void gpr_timers_set_log_filename(const char* filename) {
  178. output_filename_or_null = filename;
  179. }
  180. static void init_output() {
  181. pthread_attr_t attr;
  182. pthread_attr_init(&attr);
  183. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
  184. pthread_create(&g_writing_thread, &attr, &writing_thread, NULL);
  185. pthread_attr_destroy(&attr);
  186. atexit(finish_writing);
  187. }
  188. static void rotate_log() {
  189. /* Using malloc here, as this code could end up being called by gpr_malloc */
  190. gpr_timer_log* log = static_cast<gpr_timer_log*>(malloc(sizeof(*log)));
  191. gpr_once_init(&g_once_init, init_output);
  192. log->num_entries = 0;
  193. pthread_mutex_lock(&g_mu);
  194. if (g_thread_log != NULL) {
  195. timer_log_remove(&g_in_progress_logs, g_thread_log);
  196. if (timer_log_push_back(&g_done_logs, g_thread_log)) {
  197. pthread_cond_signal(&g_cv);
  198. }
  199. } else {
  200. g_thread_id = g_next_thread_id++;
  201. }
  202. timer_log_push_back(&g_in_progress_logs, log);
  203. pthread_mutex_unlock(&g_mu);
  204. g_thread_log = log;
  205. }
  206. static void gpr_timers_log_add(const char* tagstr, marker_type type,
  207. int important, const char* file, int line) {
  208. gpr_timer_entry* entry;
  209. if (!g_writing_enabled) {
  210. return;
  211. }
  212. if (g_thread_log == NULL || g_thread_log->num_entries == MAX_COUNT) {
  213. rotate_log();
  214. }
  215. entry = &g_thread_log->log[g_thread_log->num_entries++];
  216. entry->tm = gpr_now(GPR_CLOCK_PRECISE);
  217. entry->tagstr = tagstr;
  218. entry->type = type;
  219. entry->file = file;
  220. entry->line = (short)line;
  221. entry->important = important != 0;
  222. entry->thd = g_thread_id;
  223. }
  224. /* Latency profiler API implementation. */
  225. void gpr_timer_add_mark(const char* tagstr, int important, const char* file,
  226. int line) {
  227. gpr_timers_log_add(tagstr, MARK, important, file, line);
  228. }
  229. void gpr_timer_begin(const char* tagstr, int important, const char* file,
  230. int line) {
  231. gpr_timers_log_add(tagstr, BEGIN, important, file, line);
  232. }
  233. void gpr_timer_end(const char* tagstr, int important, const char* file,
  234. int line) {
  235. gpr_timers_log_add(tagstr, END, important, file, line);
  236. }
  237. void gpr_timer_set_enabled(int enabled) { g_writing_enabled = enabled; }
  238. /* Basic profiler specific API functions. */
  239. void gpr_timers_global_init(void) {}
  240. void gpr_timers_global_destroy(void) {}
  241. #else /* !GRPC_BASIC_PROFILER */
  242. void gpr_timers_global_init(void) {}
  243. void gpr_timers_global_destroy(void) {}
  244. void gpr_timers_set_log_filename(const char* filename) {}
  245. void gpr_timer_set_enabled(int enabled) {}
  246. #endif /* GRPC_BASIC_PROFILER */