2 // Copyright 2014 by Henrik RydgÄrd
3 // http://www.github.com/hrydgard/minitrace
4 // Released under the MIT license.
6 // See minitrace.h for basic documentation.
13 #pragma warning (disable:4996)
14 #define WIN32_LEAN_AND_MEAN
16 #define __thread __declspec(thread)
17 #define pthread_mutex_t CRITICAL_SECTION
18 #define pthread_mutex_init(a, b) InitializeCriticalSection(a)
19 #define pthread_mutex_lock(a) EnterCriticalSection(a)
20 #define pthread_mutex_unlock(a) LeaveCriticalSection(a)
21 #define pthread_mutex_destroy(a) DeleteCriticalSection(a)
29 #include "minitrace.h"
32 #define ATTR_NORETURN __attribute__((noreturn))
37 #define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0])
39 // Ugh, this struct is already pretty heavy.
40 // Will probably need to move arguments to a second buffer to support more than one.
41 typedef struct raw_event {
49 mtr_arg_type arg_type;
58 static raw_event_t *buffer;
59 static volatile int count;
60 static int is_tracing = 0;
61 static int64_t time_offset;
62 static int first_line = 1;
64 static __thread int cur_thread_id; // Thread local storage
65 static int cur_process_id;
66 static pthread_mutex_t mutex;
68 #define STRING_POOL_SIZE 100
69 static char *str_pool[100];
71 // Tiny portability layer.
73 // get_cur_thread_id()
74 // get_cur_process_id()
78 static int get_cur_thread_id() {
79 return (int)GetCurrentThreadId();
81 static int get_cur_process_id() {
82 return (int)GetCurrentProcessId();
85 static uint64_t _frequency = 0;
86 static uint64_t _starttime = 0;
88 if (_frequency == 0) {
89 QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency);
90 QueryPerformanceCounter((LARGE_INTEGER*)&_starttime);
93 QueryPerformanceCounter((LARGE_INTEGER*)&time);
94 return ((double) (time - _starttime) / (double) _frequency);
97 // Ctrl+C handling for Windows console apps
98 static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
99 if (is_tracing && fdwCtrlType == CTRL_C_EVENT) {
100 printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
107 void mtr_register_sigint_handler() {
109 SetConsoleCtrlHandler(&CtrlHandler, TRUE);
114 static inline int get_cur_thread_id() {
115 return (int)(intptr_t)pthread_self();
117 static inline int get_cur_process_id() {
118 return (int)getpid();
121 #if defined(BLACKBERRY)
122 double mtr_time_s() {
123 struct timespec time;
124 clock_gettime(CLOCK_MONOTONIC, &time); // Linux must use CLOCK_MONOTONIC_RAW due to time warps
125 return time.tv_sec + time.tv_nsec / 1.0e9;
128 double mtr_time_s() {
131 gettimeofday(&tv, NULL);
136 return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
138 #endif // !BLACKBERRY
140 static void termination_handler(int signum) ATTR_NORETURN;
141 static void termination_handler(int signum) {
144 printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
146 fwrite("\n]}\n", 1, 4, f);
152 void mtr_register_sigint_handler() {
156 // Avoid altering set-to-be-ignored handlers while registering.
157 if (signal(SIGINT, &termination_handler) == SIG_IGN)
158 signal(SIGINT, SIG_IGN);
163 void mtr_init_from_stream(void *stream) {
167 buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t));
171 const char *header = "{\"traceEvents\":[\n";
172 fwrite(header, 1, strlen(header), f);
173 time_offset = (uint64_t)(mtr_time_s() * 1000000);
175 pthread_mutex_init(&mutex, 0);
178 void mtr_init(const char *json_file) {
182 mtr_init_from_stream(fopen(json_file, "wb"));
185 void mtr_shutdown() {
192 fwrite("\n]}\n", 1, 4, f);
194 pthread_mutex_destroy(&mutex);
198 for (i = 0; i < STRING_POOL_SIZE; i++) {
206 const char *mtr_pool_string(const char *str) {
208 for (i = 0; i < STRING_POOL_SIZE; i++) {
210 str_pool[i] = (char*)malloc(strlen(str) + 1);
211 strcpy(str_pool[i], str);
214 if (!strcmp(str, str_pool[i]))
218 return "string pool full";
235 // TODO: fwrite more than one line at a time.
244 // We have to lock while flushing. So we really should avoid flushing as much as possible.
247 pthread_mutex_lock(&mutex);
248 int old_tracing = is_tracing;
249 is_tracing = 0; // Stop logging even if using interlocked increments instead of the mutex. Can cause data loss.
251 for (i = 0; i < count; i++) {
252 raw_event_t *raw = &buffer[i];
254 switch (raw->arg_type) {
255 case MTR_ARG_TYPE_INT:
256 snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":%i", raw->arg_name, raw->a_int);
258 case MTR_ARG_TYPE_STRING_CONST:
259 snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
261 case MTR_ARG_TYPE_STRING_COPY:
262 if (strlen(raw->a_str) > 700) {
263 snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%.*s\"", raw->arg_name, 700, raw->a_str);
265 snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
268 case MTR_ARG_TYPE_NONE:
277 // TODO: Support full 64-bit pointers
278 snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"id\":\"0x%08x\"", (uint32_t)(uintptr_t)raw->id);
281 snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"dur\":%i", (int)raw->a_double);
287 const char *cat = raw->cat;
289 // On Windows, we often end up with backslashes in category.
292 int len = (int)strlen(cat);
294 if (len > 255) len = 255;
295 for (i = 0; i < len; i++) {
296 temp[i] = cat[i] == '\\' ? '/' : cat[i];
303 len = snprintf(linebuf, ARRAY_SIZE(linebuf), "%s{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ",\"ph\":\"%c\",\"name\":\"%s\",\"args\":{%s}%s}",
304 first_line ? "" : ",\n",
305 cat, raw->pid, raw->tid, raw->ts - time_offset, raw->ph, raw->name, arg_buf, id_buf);
306 fwrite(linebuf, 1, len, f);
310 is_tracing = old_tracing;
311 pthread_mutex_unlock(&mutex);
314 void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id) {
318 if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE)
320 double ts = mtr_time_s();
321 if (!cur_thread_id) {
322 cur_thread_id = get_cur_thread_id();
324 if (!cur_process_id) {
325 cur_process_id = get_cur_process_id();
328 #if 0 && _WIN32 // This should work, feel free to enable if you're adventurous and need performance.
329 int bufPos = InterlockedExchangeAdd((LONG volatile *)&count, 1);
330 raw_event_t *ev = &buffer[bufPos];
332 pthread_mutex_lock(&mutex);
333 raw_event_t *ev = &buffer[count];
335 pthread_mutex_unlock(&mutex);
344 memcpy(&x, id, sizeof(double));
345 ev->ts = (int64_t)(x * 1000000);
346 ev->a_double = (ts - x) * 1000000;
348 ev->ts = (int64_t)(ts * 1000000);
350 ev->tid = cur_thread_id;
351 ev->pid = cur_process_id;
352 ev->arg_type = MTR_ARG_TYPE_NONE;
355 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) {
359 if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE)
361 if (!cur_thread_id) {
362 cur_thread_id = get_cur_thread_id();
364 if (!cur_process_id) {
365 cur_process_id = get_cur_process_id();
367 double ts = mtr_time_s();
369 #if 0 && _WIN32 // This should work, feel free to enable if you're adventurous and need performance.
370 int bufPos = InterlockedExchangeAdd((LONG volatile *)&count, 1);
371 raw_event_t *ev = &buffer[bufPos];
373 pthread_mutex_lock(&mutex);
374 raw_event_t *ev = &buffer[count];
376 pthread_mutex_unlock(&mutex);
382 ev->ts = (int64_t)(ts * 1000000);
384 ev->tid = cur_thread_id;
385 ev->pid = cur_process_id;
386 ev->arg_type = arg_type;
387 ev->arg_name = arg_name;
389 case MTR_ARG_TYPE_INT: ev->a_int = (int)(uintptr_t)arg_value; break;
390 case MTR_ARG_TYPE_STRING_CONST: ev->a_str = (const char*)arg_value; break;
391 case MTR_ARG_TYPE_STRING_COPY: ev->a_str = strdup((const char*)arg_value); break;
392 case MTR_ARG_TYPE_NONE: break;