minitrace.cpp
Go to the documentation of this file.
1 // minitrace
2 // Copyright 2014 by Henrik RydgĂ„rd
3 // http://www.github.com/hrydgard/minitrace
4 // Released under the MIT license.
5 
6 // See minitrace.h for basic documentation.
7 
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <string.h>
11 
12 #ifdef _WIN32
13 #pragma warning (disable:4996)
14 #define WIN32_LEAN_AND_MEAN
15 #include <windows.h>
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)
22 #else
23 #include <signal.h>
24 #include <pthread.h>
25 #include <sys/time.h>
26 #include <unistd.h>
27 #endif
28 
29 #include "minitrace.h"
30 
31 #define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0])
32 
33 namespace minitrace {
34 
35 // Ugh, this struct is already pretty heavy.
36 // Will probably need to move arguments to a second buffer to support more than one.
37 typedef struct raw_event {
38  const char *name;
39  const char *cat;
40  void *id;
41  int64_t ts;
42  uint32_t pid;
43  uint32_t tid;
44  char ph;
46  const char *arg_name;
47  union {
48  const char *a_str;
49  int a_int;
50  double a_double;
51  };
52 } raw_event_t;
53 
55 static volatile int count;
56 static int is_tracing = 0;
57 static int64_t time_offset;
58 static int first_line = 1;
59 static FILE *file;
60 static __thread int cur_thread_id; // Thread local storage
61 static pthread_mutex_t mutex;
62 
63 #define STRING_POOL_SIZE 100
64 static char *str_pool[100];
65 
66 // Tiny portability layer.
67 // Exposes:
68 // get_cur_thread_id()
69 // mtr_time_s()
70 // pthread basics
71 #ifdef _WIN32
72 static int get_cur_thread_id() {
73  return (int)GetCurrentThreadId();
74 }
75 
76 static uint64_t _frequency = 0;
77 static uint64_t _starttime = 0;
78 
79 inline int64_t mtr_time_usec(){
80  static int64_t prev = 0;
81  if (_frequency == 0) {
82  QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency);
83  QueryPerformanceCounter((LARGE_INTEGER*)&_starttime);
84  }
85  __int64 time;
86  QueryPerformanceCounter((LARGE_INTEGER*)&time);
87  int64_t now = 1.0e6 * ((double) (time - _starttime) / (double) _frequency);
88  if( now <= prev) now = prev + 1;
89  prev = now;
90  return now;
91 }
92 
93 // Ctrl+C handling for Windows console apps
94 static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
95  if (is_tracing && fdwCtrlType == CTRL_C_EVENT) {
96  printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
97  mtr_flush();
98  mtr_shutdown();
99  }
100  ExitProcess(1);
101 }
102 
104  // For console apps:
105  SetConsoleCtrlHandler(&CtrlHandler, TRUE);
106 }
107 
108 #else
109 
110 static inline int get_cur_thread_id() {
111  return (int)(intptr_t)pthread_self();
112 }
113 
114 #if defined(BLACKBERRY)
115 inline int64_t mtr_time_usec(){
116  static int64_t prev = 0;
117  struct timespec time;
118  clock_gettime(CLOCK_MONOTONIC, &time); // Linux must use CLOCK_MONOTONIC_RAW due to time warps
119  int64_t now = time.tv_sec*1000000 + time.tv_nsec / 1000;
120  if( now <= prev) now = prev + 1;
121  prev = now;
122  return now;
123 }
124 #else
125 int64_t mtr_time_usec()
126 {
127  static int64_t prev = 0;
128  struct timeval tv;
129  gettimeofday(&tv, nullptr);
130  int64_t now = 1000000*tv.tv_sec + tv.tv_usec;
131  if( now <= prev) now = prev + 1;
132  prev = now;
133  return now;
134 }
135 #endif // !BLACKBERRY
136 
137 static void termination_handler(int ) {
138  if (is_tracing) {
139  printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
140  mtr_flush();
141  fwrite("\n]}\n", 1, 4, file);
142  fclose(file);
143  }
144  exit(1);
145 }
146 
148 #ifndef MTR_ENABLED
149  return;
150 #endif
151  // Avoid altering set-to-be-ignored handlers while registering.
152  if (signal(SIGINT, &termination_handler) == SIG_IGN)
153  signal(SIGINT, SIG_IGN);
154 }
155 
156 #endif
157 
158 void mtr_init(const char *json_file) {
159 #ifndef MTR_ENABLED
160  return;
161 #endif
162  buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t));
163  is_tracing = 1;
164  count = 0;
165  file = fopen(json_file, "wb");
166  const char *header = "{\"traceEvents\":[\n";
167  fwrite(header, 1, strlen(header), file);
168  time_offset = mtr_time_usec();
169  first_line = 1;
170  pthread_mutex_init(&mutex, 0);
171 }
172 
173 void mtr_shutdown() {
174  int i;
175 #ifndef MTR_ENABLED
176  return;
177 #endif
178  is_tracing = 0;
179  mtr_flush();
180  fwrite("\n]}\n", 1, 4, file);
181  fclose(file);
182  pthread_mutex_destroy(&mutex);
183  file = 0;
184  free(buffer);
185  buffer = 0;
186  for (i = 0; i < STRING_POOL_SIZE; i++) {
187  if (str_pool[i]) {
188  free(str_pool[i]);
189  str_pool[i] = 0;
190  }
191  }
192 }
193 
194 const char *mtr_pool_string(const char *str) {
195  int i;
196  for (i = 0; i < STRING_POOL_SIZE; i++) {
197  if (!str_pool[i]) {
198  str_pool[i] = (char *)malloc(strlen(str) + 1);
199  strcpy(str_pool[i], str);
200  return str_pool[i];
201  } else {
202  if (!strcmp(str, str_pool[i]))
203  return str_pool[i];
204  }
205  }
206  return "string pool full";
207 }
208 
209 void mtr_start() {
210 #ifndef MTR_ENABLED
211  return;
212 #endif
213  is_tracing = 1;
214 }
215 
216 void mtr_stop() {
217 #ifndef MTR_ENABLED
218  return;
219 #endif
220  is_tracing = 0;
221 }
222 
223 // TODO: fwrite more than one line at a time.
224 void mtr_flush() {
225 #ifndef MTR_ENABLED
226  return;
227 #endif
228  int i = 0;
229  char linebuf[1024];
230  char arg_buf[256];
231  char id_buf[256];
232  // We have to lock while flushing. So we really should avoid flushing as much as possible.
233 
234 
235  pthread_mutex_lock(&mutex);
236  int old_tracing = is_tracing;
237  is_tracing = 0; // Stop logging even if using interlocked increments instead of the mutex. Can cause data loss.
238 
239  for (i = 0; i < count; i++) {
240  raw_event_t *raw = &buffer[i];
241  int len;
242  switch (raw->arg_type) {
243  case MTR_ARG_TYPE_INT:
244  snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":%i", raw->arg_name, raw->a_int);
245  break;
247  snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
248  break;
250  if (strlen(raw->a_str) > 700) {
251  ((char*)raw->a_str)[700] = 0;
252  }
253  snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
254  break;
255  case MTR_ARG_TYPE_NONE:
256  default:
257  arg_buf[0] = '\0';
258  break;
259  }
260  if (raw->id) {
261  switch (raw->ph) {
262  case 'S':
263  case 'T':
264  case 'F':
265  // TODO: Support full 64-bit pointers
266  snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"id\":\"0x%08x\"", (uint32_t)(uintptr_t)raw->id);
267  break;
268  case 'X':
269  snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"dur\":%i", (int)raw->a_double);
270  break;
271  }
272  } else {
273  id_buf[0] = 0;
274  }
275  const char *cat = raw->cat;
276 #ifdef _WIN32
277  // On Windows, we often end up with backslashes in category.
278  {
279  char temp[256];
280  int len = (int)strlen(cat);
281  int i;
282  if (len > 255) len = 255;
283  for (i = 0; i < len; i++) {
284  temp[i] = cat[i] == '\\' ? '/' : cat[i];
285  }
286  temp[len] = 0;
287  cat = temp;
288  }
289 #endif
290 
291  len = snprintf(linebuf, ARRAY_SIZE(linebuf), "%s{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ",\"ph\":\"%c\",\"name\":\"%s\",\"args\":{%s}%s}",
292  first_line ? "" : ",\n",
293  cat, raw->pid, raw->tid, raw->ts - time_offset, raw->ph, raw->name, arg_buf, id_buf);
294  fwrite(linebuf, 1, len, file);
295  fflush(file);
296  first_line = 0;
297  }
298  count = 0;
299  is_tracing = old_tracing;
300  pthread_mutex_unlock(&mutex);
301 }
302 
303 void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id) {
304 #ifndef MTR_ENABLED
305  return;
306 #endif
307  if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE)
308  return;
309  int64_t ts = mtr_time_usec();
310  if (!cur_thread_id) {
311  cur_thread_id = get_cur_thread_id();
312  }
313 
314 #if 0 && _WIN32 // TODO: This needs testing
315  int bufPos = InterlockedIncrement(&count);
316  raw_event_t *ev = &buffer[count - 1];
317 #else
318  pthread_mutex_lock(&mutex);
319  raw_event_t *ev = &buffer[count];
320  count++;
321  pthread_mutex_unlock(&mutex);
322 #endif
323 
324  ev->cat = category;
325  ev->name = name;
326  ev->id = id;
327  ev->ph = ph;
328  if (ev->ph == 'X') {
329  int64_t x;
330  memcpy(&x, id, sizeof(int64_t));
331  ev->ts = x;
332  ev->a_double = (ts - x);
333  } else {
334  ev->ts = ts;
335  }
336  ev->tid = cur_thread_id;
337  ev->pid = 0;
338 }
339 
340 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) {
341 #ifndef MTR_ENABLED
342  return;
343 #endif
344  if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE)
345  return;
346  if (!cur_thread_id) {
347  cur_thread_id = get_cur_thread_id();
348  }
349  int64_t ts = mtr_time_usec();
350 
351 #if 0 && _WIN32 // TODO: This needs testing
352  int bufPos = InterlockedIncrement(&count);
353  raw_event_t *ev = &buffer[count - 1];
354 #else
355  pthread_mutex_lock(&mutex);
356  raw_event_t *ev = &buffer[count];
357  count++;
358  pthread_mutex_unlock(&mutex);
359 #endif
360 
361  ev->cat = category;
362  ev->name = name;
363  ev->id = id;
364  ev->ts = ts;
365  ev->ph = ph;
366  ev->tid = cur_thread_id;
367  ev->pid = 0;
368  ev->arg_type = arg_type;
369  ev->arg_name = arg_name;
370  switch (arg_type) {
371  case MTR_ARG_TYPE_INT: ev->a_int = (int)(uintptr_t)arg_value; break;
372  case MTR_ARG_TYPE_STRING_CONST: ev->a_str = (const char*)arg_value; break;
373  case MTR_ARG_TYPE_STRING_COPY: ev->a_str = strdup((const char*)arg_value); break;
374  default:
375  break;
376  }
377 }
378 
379 }
const char * a_str
Definition: minitrace.cpp:48
void mtr_shutdown()
Definition: minitrace.cpp:173
const char * name
Definition: minitrace.cpp:38
void mtr_register_sigint_handler()
Definition: minitrace.cpp:147
static int get_cur_thread_id()
Definition: minitrace.cpp:110
void mtr_init(const char *json_file)
Definition: minitrace.cpp:158
static raw_event_t * buffer
Definition: minitrace.cpp:54
static FILE * file
Definition: minitrace.cpp:59
static int first_line
Definition: minitrace.cpp:58
static pthread_mutex_t mutex
Definition: minitrace.cpp:61
void mtr_stop()
Definition: minitrace.cpp:216
#define ARRAY_SIZE(x)
Definition: minitrace.cpp:31
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)
Definition: minitrace.cpp:340
struct minitrace::raw_event raw_event_t
void mtr_start()
Definition: minitrace.cpp:209
static __thread int cur_thread_id
Definition: minitrace.cpp:60
static int is_tracing
Definition: minitrace.cpp:56
int64_t mtr_time_usec()
Definition: minitrace.cpp:125
#define STRING_POOL_SIZE
Definition: minitrace.cpp:63
void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id)
Definition: minitrace.cpp:303
static volatile int count
Definition: minitrace.cpp:55
#define INTERNAL_MINITRACE_BUFFER_SIZE
Definition: minitrace.h:35
static void termination_handler(int)
Definition: minitrace.cpp:137
const char * mtr_pool_string(const char *str)
Definition: minitrace.cpp:194
const char * arg_name
Definition: minitrace.cpp:46
const char * cat
Definition: minitrace.cpp:39
static char * str_pool[100]
Definition: minitrace.cpp:64
void mtr_flush()
Definition: minitrace.cpp:224
static int64_t time_offset
Definition: minitrace.cpp:57
mtr_arg_type arg_type
Definition: minitrace.cpp:45


behaviortree_cpp
Author(s): Michele Colledanchise, Davide Faconti
autogenerated on Sat Feb 2 2019 04:01:53