• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 #include <grpc/support/port_platform.h>
20 
21 #include "src/core/lib/profiling/timers.h"
22 
23 #ifdef GRPC_BASIC_PROFILER
24 
25 #include <grpc/support/alloc.h>
26 #include <grpc/support/log.h>
27 #include <grpc/support/sync.h>
28 #include <grpc/support/time.h>
29 #include <inttypes.h>
30 #include <pthread.h>
31 #include <stdio.h>
32 #include <string.h>
33 
34 #include "src/core/lib/gpr/env.h"
35 
36 typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type;
37 
38 typedef struct gpr_timer_entry {
39   gpr_timespec tm;
40   const char* tagstr;
41   const char* file;
42   short line;
43   char type;
44   uint8_t important;
45   int thd;
46 } gpr_timer_entry;
47 
48 #define MAX_COUNT 1000000
49 
50 typedef struct gpr_timer_log {
51   size_t num_entries;
52   struct gpr_timer_log* next;
53   struct gpr_timer_log* prev;
54   gpr_timer_entry log[MAX_COUNT];
55 } gpr_timer_log;
56 
57 typedef struct gpr_timer_log_list {
58   gpr_timer_log* head;
59   /* valid iff head!=NULL */
60   gpr_timer_log* tail;
61 } gpr_timer_log_list;
62 
63 static __thread gpr_timer_log* g_thread_log;
64 static gpr_once g_once_init = GPR_ONCE_INIT;
65 static FILE* output_file;
66 static const char* output_filename_or_null = NULL;
67 static pthread_mutex_t g_mu;
68 static pthread_cond_t g_cv;
69 static gpr_timer_log_list g_in_progress_logs;
70 static gpr_timer_log_list g_done_logs;
71 static int g_shutdown;
72 static pthread_t g_writing_thread;
73 static __thread int g_thread_id;
74 static int g_next_thread_id;
75 static int g_writing_enabled = 1;
76 
output_filename()77 static const char* output_filename() {
78   if (output_filename_or_null == NULL) {
79     output_filename_or_null = gpr_getenv("LATENCY_TRACE");
80     if (output_filename_or_null == NULL ||
81         strlen(output_filename_or_null) == 0) {
82       output_filename_or_null = "latency_trace.txt";
83     }
84   }
85   return output_filename_or_null;
86 }
87 
timer_log_push_back(gpr_timer_log_list * list,gpr_timer_log * log)88 static int timer_log_push_back(gpr_timer_log_list* list, gpr_timer_log* log) {
89   if (list->head == NULL) {
90     list->head = list->tail = log;
91     log->next = log->prev = NULL;
92     return 1;
93   } else {
94     log->prev = list->tail;
95     log->next = NULL;
96     list->tail->next = log;
97     list->tail = log;
98     return 0;
99   }
100 }
101 
timer_log_pop_front(gpr_timer_log_list * list)102 static gpr_timer_log* timer_log_pop_front(gpr_timer_log_list* list) {
103   gpr_timer_log* out = list->head;
104   if (out != NULL) {
105     list->head = out->next;
106     if (list->head != NULL) {
107       list->head->prev = NULL;
108     } else {
109       list->tail = NULL;
110     }
111   }
112   return out;
113 }
114 
timer_log_remove(gpr_timer_log_list * list,gpr_timer_log * log)115 static void timer_log_remove(gpr_timer_log_list* list, gpr_timer_log* log) {
116   if (log->prev == NULL) {
117     list->head = log->next;
118     if (list->head != NULL) {
119       list->head->prev = NULL;
120     }
121   } else {
122     log->prev->next = log->next;
123   }
124   if (log->next == NULL) {
125     list->tail = log->prev;
126     if (list->tail != NULL) {
127       list->tail->next = NULL;
128     }
129   } else {
130     log->next->prev = log->prev;
131   }
132 }
133 
write_log(gpr_timer_log * log)134 static void write_log(gpr_timer_log* log) {
135   size_t i;
136   if (output_file == NULL) {
137     output_file = fopen(output_filename(), "w");
138   }
139   for (i = 0; i < log->num_entries; i++) {
140     gpr_timer_entry* entry = &(log->log[i]);
141     if (gpr_time_cmp(entry->tm, gpr_time_0(entry->tm.clock_type)) < 0) {
142       entry->tm = gpr_time_0(entry->tm.clock_type);
143     }
144     fprintf(output_file,
145             "{\"t\": %" PRId64
146             ".%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": "
147             "\"%s\", \"file\": \"%s\", \"line\": %d, \"imp\": %d}\n",
148             entry->tm.tv_sec, entry->tm.tv_nsec, entry->thd, entry->type,
149             entry->tagstr, entry->file, entry->line, entry->important);
150   }
151 }
152 
writing_thread(void * unused)153 static void* writing_thread(void* unused) {
154   gpr_timer_log* log;
155   pthread_mutex_lock(&g_mu);
156   for (;;) {
157     while ((log = timer_log_pop_front(&g_done_logs)) == NULL && !g_shutdown) {
158       pthread_cond_wait(&g_cv, &g_mu);
159     }
160     if (log != NULL) {
161       pthread_mutex_unlock(&g_mu);
162       write_log(log);
163       free(log);
164       pthread_mutex_lock(&g_mu);
165     }
166     if (g_shutdown) {
167       pthread_mutex_unlock(&g_mu);
168       return NULL;
169     }
170   }
171 }
172 
flush_logs(gpr_timer_log_list * list)173 static void flush_logs(gpr_timer_log_list* list) {
174   gpr_timer_log* log;
175   while ((log = timer_log_pop_front(list)) != NULL) {
176     write_log(log);
177     free(log);
178   }
179 }
180 
finish_writing(void)181 static void finish_writing(void) {
182   pthread_mutex_lock(&g_mu);
183   g_shutdown = 1;
184   pthread_cond_signal(&g_cv);
185   pthread_mutex_unlock(&g_mu);
186   pthread_join(g_writing_thread, NULL);
187 
188   gpr_log(GPR_INFO, "flushing logs");
189 
190   pthread_mutex_lock(&g_mu);
191   flush_logs(&g_done_logs);
192   flush_logs(&g_in_progress_logs);
193   pthread_mutex_unlock(&g_mu);
194 
195   if (output_file) {
196     fclose(output_file);
197   }
198 }
199 
gpr_timers_set_log_filename(const char * filename)200 void gpr_timers_set_log_filename(const char* filename) {
201   output_filename_or_null = filename;
202 }
203 
init_output()204 static void init_output() {
205   pthread_attr_t attr;
206   pthread_attr_init(&attr);
207   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
208   pthread_create(&g_writing_thread, &attr, &writing_thread, NULL);
209   pthread_attr_destroy(&attr);
210 
211   atexit(finish_writing);
212 }
213 
rotate_log()214 static void rotate_log() {
215   /* Using malloc here, as this code could end up being called by gpr_malloc */
216   gpr_timer_log* log = static_cast<gpr_timer_log*>(malloc(sizeof(*log)));
217   gpr_once_init(&g_once_init, init_output);
218   log->num_entries = 0;
219   pthread_mutex_lock(&g_mu);
220   if (g_thread_log != NULL) {
221     timer_log_remove(&g_in_progress_logs, g_thread_log);
222     if (timer_log_push_back(&g_done_logs, g_thread_log)) {
223       pthread_cond_signal(&g_cv);
224     }
225   } else {
226     g_thread_id = g_next_thread_id++;
227   }
228   timer_log_push_back(&g_in_progress_logs, log);
229   pthread_mutex_unlock(&g_mu);
230   g_thread_log = log;
231 }
232 
gpr_timers_log_add(const char * tagstr,marker_type type,int important,const char * file,int line)233 static void gpr_timers_log_add(const char* tagstr, marker_type type,
234                                int important, const char* file, int line) {
235   gpr_timer_entry* entry;
236 
237   if (!g_writing_enabled) {
238     return;
239   }
240 
241   if (g_thread_log == NULL || g_thread_log->num_entries == MAX_COUNT) {
242     rotate_log();
243   }
244 
245   entry = &g_thread_log->log[g_thread_log->num_entries++];
246 
247   entry->tm = gpr_now(GPR_CLOCK_PRECISE);
248   entry->tagstr = tagstr;
249   entry->type = type;
250   entry->file = file;
251   entry->line = (short)line;
252   entry->important = important != 0;
253   entry->thd = g_thread_id;
254 }
255 
256 /* Latency profiler API implementation. */
gpr_timer_add_mark(const char * tagstr,int important,const char * file,int line)257 void gpr_timer_add_mark(const char* tagstr, int important, const char* file,
258                         int line) {
259   gpr_timers_log_add(tagstr, MARK, important, file, line);
260 }
261 
gpr_timer_begin(const char * tagstr,int important,const char * file,int line)262 void gpr_timer_begin(const char* tagstr, int important, const char* file,
263                      int line) {
264   gpr_timers_log_add(tagstr, BEGIN, important, file, line);
265 }
266 
gpr_timer_end(const char * tagstr,int important,const char * file,int line)267 void gpr_timer_end(const char* tagstr, int important, const char* file,
268                    int line) {
269   gpr_timers_log_add(tagstr, END, important, file, line);
270 }
271 
gpr_timer_set_enabled(int enabled)272 void gpr_timer_set_enabled(int enabled) { g_writing_enabled = enabled; }
273 
274 /* Basic profiler specific API functions. */
gpr_timers_global_init(void)275 void gpr_timers_global_init(void) {}
276 
gpr_timers_global_destroy(void)277 void gpr_timers_global_destroy(void) {}
278 
279 #else  /* !GRPC_BASIC_PROFILER */
gpr_timers_global_init(void)280 void gpr_timers_global_init(void) {}
281 
gpr_timers_global_destroy(void)282 void gpr_timers_global_destroy(void) {}
283 
gpr_timers_set_log_filename(const char * filename)284 void gpr_timers_set_log_filename(const char* filename) {}
285 
gpr_timer_set_enabled(int enabled)286 void gpr_timer_set_enabled(int enabled) {}
287 #endif /* GRPC_BASIC_PROFILER */
288