00001
00002
00003
00004
00005
00006
00007
00008 #include <stdlib.h>
00009 #include <stdio.h>
00010 #include <string.h>
00011
00012 #ifdef _WIN32
00013 #pragma warning (disable:4996)
00014 #define WIN32_LEAN_AND_MEAN
00015 #include <windows.h>
00016 #define __thread __declspec(thread)
00017 #define pthread_mutex_t CRITICAL_SECTION
00018 #define pthread_mutex_init(a, b) InitializeCriticalSection(a)
00019 #define pthread_mutex_lock(a) EnterCriticalSection(a)
00020 #define pthread_mutex_unlock(a) LeaveCriticalSection(a)
00021 #define pthread_mutex_destroy(a) DeleteCriticalSection(a)
00022 #else
00023 #include <signal.h>
00024 #include <pthread.h>
00025 #include <sys/time.h>
00026 #include <unistd.h>
00027 #endif
00028
00029 #include "minitrace.h"
00030
00031 #define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0])
00032
00033 namespace minitrace {
00034
00035
00036
00037 typedef struct raw_event {
00038 const char *name;
00039 const char *cat;
00040 void *id;
00041 int64_t ts;
00042 uint32_t pid;
00043 uint32_t tid;
00044 char ph;
00045 mtr_arg_type arg_type;
00046 const char *arg_name;
00047 union {
00048 const char *a_str;
00049 int a_int;
00050 double a_double;
00051 };
00052 } raw_event_t;
00053
00054 static raw_event_t *buffer;
00055 static volatile int count;
00056 static int is_tracing = 0;
00057 static int64_t time_offset;
00058 static int first_line = 1;
00059 static FILE *file;
00060 static __thread int cur_thread_id;
00061 static pthread_mutex_t mutex;
00062
00063 #define STRING_POOL_SIZE 100
00064 static char *str_pool[100];
00065
00066
00067
00068
00069
00070
00071 #ifdef _WIN32
00072 static int get_cur_thread_id() {
00073 return (int)GetCurrentThreadId();
00074 }
00075
00076 static uint64_t _frequency = 0;
00077 static uint64_t _starttime = 0;
00078
00079 inline int64_t mtr_time_usec(){
00080 static int64_t prev = 0;
00081 if (_frequency == 0) {
00082 QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency);
00083 QueryPerformanceCounter((LARGE_INTEGER*)&_starttime);
00084 }
00085 __int64 time;
00086 QueryPerformanceCounter((LARGE_INTEGER*)&time);
00087 int64_t now = 1.0e6 * ((double) (time - _starttime) / (double) _frequency);
00088 if( now <= prev) now = prev + 1;
00089 prev = now;
00090 return now;
00091 }
00092
00093
00094 static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
00095 if (is_tracing && fdwCtrlType == CTRL_C_EVENT) {
00096 printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
00097 mtr_flush();
00098 mtr_shutdown();
00099 }
00100 ExitProcess(1);
00101 }
00102
00103 void mtr_register_sigint_handler() {
00104
00105 SetConsoleCtrlHandler(&CtrlHandler, TRUE);
00106 }
00107
00108 #else
00109
00110 static inline int get_cur_thread_id() {
00111 return (int)(intptr_t)pthread_self();
00112 }
00113
00114 #if defined(BLACKBERRY)
00115 inline int64_t mtr_time_usec(){
00116 static int64_t prev = 0;
00117 struct timespec time;
00118 clock_gettime(CLOCK_MONOTONIC, &time);
00119 int64_t now = time.tv_sec*1000000 + time.tv_nsec / 1000;
00120 if( now <= prev) now = prev + 1;
00121 prev = now;
00122 return now;
00123 }
00124 #else
00125 int64_t mtr_time_usec()
00126 {
00127 static int64_t prev = 0;
00128 struct timeval tv;
00129 gettimeofday(&tv, nullptr);
00130 int64_t now = 1000000*tv.tv_sec + tv.tv_usec;
00131 if( now <= prev) now = prev + 1;
00132 prev = now;
00133 return now;
00134 }
00135 #endif // !BLACKBERRY
00136
00137 static void termination_handler(int ) {
00138 if (is_tracing) {
00139 printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
00140 mtr_flush();
00141 fwrite("\n]}\n", 1, 4, file);
00142 fclose(file);
00143 }
00144 exit(1);
00145 }
00146
00147 void mtr_register_sigint_handler() {
00148 #ifndef MTR_ENABLED
00149 return;
00150 #endif
00151
00152 if (signal(SIGINT, &termination_handler) == SIG_IGN)
00153 signal(SIGINT, SIG_IGN);
00154 }
00155
00156 #endif
00157
00158 void mtr_init(const char *json_file) {
00159 #ifndef MTR_ENABLED
00160 return;
00161 #endif
00162 buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t));
00163 is_tracing = 1;
00164 count = 0;
00165 file = fopen(json_file, "wb");
00166 const char *header = "{\"traceEvents\":[\n";
00167 fwrite(header, 1, strlen(header), file);
00168 time_offset = mtr_time_usec();
00169 first_line = 1;
00170 pthread_mutex_init(&mutex, 0);
00171 }
00172
00173 void mtr_shutdown() {
00174 int i;
00175 #ifndef MTR_ENABLED
00176 return;
00177 #endif
00178 is_tracing = 0;
00179 mtr_flush();
00180 fwrite("\n]}\n", 1, 4, file);
00181 fclose(file);
00182 pthread_mutex_destroy(&mutex);
00183 file = 0;
00184 free(buffer);
00185 buffer = 0;
00186 for (i = 0; i < STRING_POOL_SIZE; i++) {
00187 if (str_pool[i]) {
00188 free(str_pool[i]);
00189 str_pool[i] = 0;
00190 }
00191 }
00192 }
00193
00194 const char *mtr_pool_string(const char *str) {
00195 int i;
00196 for (i = 0; i < STRING_POOL_SIZE; i++) {
00197 if (!str_pool[i]) {
00198 str_pool[i] = (char *)malloc(strlen(str) + 1);
00199 strcpy(str_pool[i], str);
00200 return str_pool[i];
00201 } else {
00202 if (!strcmp(str, str_pool[i]))
00203 return str_pool[i];
00204 }
00205 }
00206 return "string pool full";
00207 }
00208
00209 void mtr_start() {
00210 #ifndef MTR_ENABLED
00211 return;
00212 #endif
00213 is_tracing = 1;
00214 }
00215
00216 void mtr_stop() {
00217 #ifndef MTR_ENABLED
00218 return;
00219 #endif
00220 is_tracing = 0;
00221 }
00222
00223
00224 void mtr_flush() {
00225 #ifndef MTR_ENABLED
00226 return;
00227 #endif
00228 int i = 0;
00229 char linebuf[1024];
00230 char arg_buf[256];
00231 char id_buf[256];
00232
00233
00234
00235 pthread_mutex_lock(&mutex);
00236 int old_tracing = is_tracing;
00237 is_tracing = 0;
00238
00239 for (i = 0; i < count; i++) {
00240 raw_event_t *raw = &buffer[i];
00241 int len;
00242 switch (raw->arg_type) {
00243 case MTR_ARG_TYPE_INT:
00244 snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":%i", raw->arg_name, raw->a_int);
00245 break;
00246 case MTR_ARG_TYPE_STRING_CONST:
00247 snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
00248 break;
00249 case MTR_ARG_TYPE_STRING_COPY:
00250 if (strlen(raw->a_str) > 700) {
00251 ((char*)raw->a_str)[700] = 0;
00252 }
00253 snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
00254 break;
00255 case MTR_ARG_TYPE_NONE:
00256 default:
00257 arg_buf[0] = '\0';
00258 break;
00259 }
00260 if (raw->id) {
00261 switch (raw->ph) {
00262 case 'S':
00263 case 'T':
00264 case 'F':
00265
00266 snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"id\":\"0x%08x\"", (uint32_t)(uintptr_t)raw->id);
00267 break;
00268 case 'X':
00269 snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"dur\":%i", (int)raw->a_double);
00270 break;
00271 }
00272 } else {
00273 id_buf[0] = 0;
00274 }
00275 const char *cat = raw->cat;
00276 #ifdef _WIN32
00277
00278 {
00279 char temp[256];
00280 int len = (int)strlen(cat);
00281 int i;
00282 if (len > 255) len = 255;
00283 for (i = 0; i < len; i++) {
00284 temp[i] = cat[i] == '\\' ? '/' : cat[i];
00285 }
00286 temp[len] = 0;
00287 cat = temp;
00288 }
00289 #endif
00290
00291 len = snprintf(linebuf, ARRAY_SIZE(linebuf), "%s{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ",\"ph\":\"%c\",\"name\":\"%s\",\"args\":{%s}%s}",
00292 first_line ? "" : ",\n",
00293 cat, raw->pid, raw->tid, raw->ts - time_offset, raw->ph, raw->name, arg_buf, id_buf);
00294 fwrite(linebuf, 1, len, file);
00295 fflush(file);
00296 first_line = 0;
00297 }
00298 count = 0;
00299 is_tracing = old_tracing;
00300 pthread_mutex_unlock(&mutex);
00301 }
00302
00303 void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id) {
00304 #ifndef MTR_ENABLED
00305 return;
00306 #endif
00307 if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE)
00308 return;
00309 int64_t ts = mtr_time_usec();
00310 if (!cur_thread_id) {
00311 cur_thread_id = get_cur_thread_id();
00312 }
00313
00314 #if 0 && _WIN32 // TODO: This needs testing
00315 int bufPos = InterlockedIncrement(&count);
00316 raw_event_t *ev = &buffer[count - 1];
00317 #else
00318 pthread_mutex_lock(&mutex);
00319 raw_event_t *ev = &buffer[count];
00320 count++;
00321 pthread_mutex_unlock(&mutex);
00322 #endif
00323
00324 ev->cat = category;
00325 ev->name = name;
00326 ev->id = id;
00327 ev->ph = ph;
00328 if (ev->ph == 'X') {
00329 int64_t x;
00330 memcpy(&x, id, sizeof(int64_t));
00331 ev->ts = x;
00332 ev->a_double = (ts - x);
00333 } else {
00334 ev->ts = ts;
00335 }
00336 ev->tid = cur_thread_id;
00337 ev->pid = 0;
00338 }
00339
00340 void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value) {
00341 #ifndef MTR_ENABLED
00342 return;
00343 #endif
00344 if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE)
00345 return;
00346 if (!cur_thread_id) {
00347 cur_thread_id = get_cur_thread_id();
00348 }
00349 int64_t ts = mtr_time_usec();
00350
00351 #if 0 && _WIN32 // TODO: This needs testing
00352 int bufPos = InterlockedIncrement(&count);
00353 raw_event_t *ev = &buffer[count - 1];
00354 #else
00355 pthread_mutex_lock(&mutex);
00356 raw_event_t *ev = &buffer[count];
00357 count++;
00358 pthread_mutex_unlock(&mutex);
00359 #endif
00360
00361 ev->cat = category;
00362 ev->name = name;
00363 ev->id = id;
00364 ev->ts = ts;
00365 ev->ph = ph;
00366 ev->tid = cur_thread_id;
00367 ev->pid = 0;
00368 ev->arg_type = arg_type;
00369 ev->arg_name = arg_name;
00370 switch (arg_type) {
00371 case MTR_ARG_TYPE_INT: ev->a_int = (int)(uintptr_t)arg_value; break;
00372 case MTR_ARG_TYPE_STRING_CONST: ev->a_str = (const char*)arg_value; break;
00373 case MTR_ARG_TYPE_STRING_COPY: ev->a_str = strdup((const char*)arg_value); break;
00374 default:
00375 break;
00376 }
00377 }
00378
00379 }