minitrace.cpp
Go to the documentation of this file.
00001 // minitrace
00002 // Copyright 2014 by Henrik RydgÄrd
00003 // http://www.github.com/hrydgard/minitrace
00004 // Released under the MIT license.
00005 
00006 // See minitrace.h for basic documentation.
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 // Ugh, this struct is already pretty heavy.
00036 // Will probably need to move arguments to a second buffer to support more than one.
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;      // Thread local storage
00061 static pthread_mutex_t mutex;
00062 
00063 #define STRING_POOL_SIZE 100
00064 static char *str_pool[100];
00065 
00066 // Tiny portability layer.
00067 // Exposes:
00068 //       get_cur_thread_id()
00069 //       mtr_time_s()
00070 //       pthread basics
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 = static_cast<int64_t>( 1.0e6 * ((double)(time - _starttime) / (double)_frequency));
00088   if( now <= prev) now = prev + 1;
00089   prev = now;
00090   return now;
00091 }
00092 
00093 // Ctrl+C handling for Windows console apps
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         // For console apps:
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); // Linux must use CLOCK_MONOTONIC_RAW due to time warps
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         // Avoid altering set-to-be-ignored handlers while registering.
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 // TODO: fwrite more than one line at a time.
00224 void mtr_flush() {
00225 #ifndef MTR_ENABLED
00226         return;
00227 #endif
00228         char linebuf[1024];
00229         char arg_buf[256];
00230         char id_buf[256];
00231         // We have to lock while flushing. So we really should avoid flushing as much as possible.
00232 
00233 
00234         pthread_mutex_lock(&mutex);
00235         int old_tracing = is_tracing;
00236         is_tracing = 0; // Stop logging even if using interlocked increments instead of the mutex. Can cause data loss.
00237 
00238         for (int i = 0; i < count; i++) {
00239                 raw_event_t *raw = &buffer[i];
00240                 int len;
00241                 switch (raw->arg_type) {
00242                 case MTR_ARG_TYPE_INT:
00243                         snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":%i", raw->arg_name, raw->a_int);
00244                         break;
00245                 case MTR_ARG_TYPE_STRING_CONST:
00246                         snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
00247                         break;
00248                 case MTR_ARG_TYPE_STRING_COPY:
00249                         if (strlen(raw->a_str) > 700) {
00250                                 ((char*)raw->a_str)[700] = 0;
00251                         }
00252                         snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
00253                         break;
00254                 case MTR_ARG_TYPE_NONE:
00255                 default:
00256                         arg_buf[0] = '\0';
00257                         break;
00258                 }
00259                 if (raw->id) {
00260                         switch (raw->ph) {
00261                         case 'S':
00262                         case 'T':
00263                         case 'F':
00264                                 // TODO: Support full 64-bit pointers
00265                                 snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"id\":\"0x%08x\"", (uint32_t)(uintptr_t)raw->id);
00266                                 break;
00267                         case 'X':
00268                                 snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"dur\":%i", (int)raw->a_double);
00269                                 break;
00270                         }
00271                 } else {
00272                         id_buf[0] = 0;
00273                 }
00274                 const char *cat = raw->cat;
00275 #ifdef _WIN32
00276                 // On Windows, we often end up with backslashes in category.
00277                 {
00278                         char temp[256];
00279                         int cat_len = (int)strlen(cat);
00280             if (cat_len > 255)
00281                 cat_len = 255;
00282             for (int a = 0; a < cat_len; a++)
00283             {
00284                                 temp[a] = cat[a] == '\\' ? '/' : cat[a];
00285                         }
00286             temp[cat_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 = static_cast<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 }


behaviortree_cpp
Author(s): Michele Colledanchise, Davide Faconti
autogenerated on Sat Jun 8 2019 20:17:15