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 #include <atomic>
12 
13 #ifdef _WIN32
14 #pragma warning (disable:4996)
15 #ifndef WIN32_LEAN_AND_MEAN
16 #define WIN32_LEAN_AND_MEAN
17 #endif
18 #include <windows.h>
19 #define __thread __declspec(thread)
20 #define pthread_mutex_t CRITICAL_SECTION
21 #define pthread_mutex_init(a, b) InitializeCriticalSection(a)
22 #define pthread_mutex_lock(a) EnterCriticalSection(a)
23 #define pthread_mutex_unlock(a) LeaveCriticalSection(a)
24 #define pthread_mutex_destroy(a) DeleteCriticalSection(a)
25 #else
26 #include <signal.h>
27 #include <pthread.h>
28 #include <sys/time.h>
29 #include <unistd.h>
30 #endif
31 
32 #define __STDC_FORMAT_MACROS
33 #include <inttypes.h>
34 
35 #include "minitrace.h"
36 
37 #ifdef __GNUC__
38 #define ATTR_NORETURN __attribute__((noreturn))
39 #else
40 #define ATTR_NORETURN
41 #endif
42 
43 #define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0])
44 #define TRUE 1
45 #define FALSE 0
46 
47 // Ugh, this struct is already pretty heavy.
48 // Will probably need to move arguments to a second buffer to support more than one.
49 typedef struct raw_event {
50  const char *name;
51  const char *cat;
52  void *id;
53  int64_t ts;
54  uint32_t pid;
55  uint32_t tid;
56  char ph;
58  const char *arg_name;
59  union {
60  const char *a_str;
61  int a_int;
62  double a_double;
63  };
64 } raw_event_t;
65 
68 static std::atomic_int event_count;
69 static int is_tracing = FALSE;
70 static int is_flushing = FALSE;
71 static int events_in_progress = 0;
72 static int64_t time_offset;
73 static int first_line = 1;
74 static FILE *f;
75 static __thread int cur_thread_id; // Thread local storage
76 static int cur_process_id;
77 static pthread_mutex_t mutex;
78 static pthread_mutex_t event_mutex;
79 
80 #define STRING_POOL_SIZE 100
81 static char *str_pool[100];
82 
83 // forward declaration
84 void mtr_flush_with_state(int);
85 
86 // Tiny portability layer.
87 // Exposes:
88 // get_cur_thread_id()
89 // get_cur_process_id()
90 // mtr_time_s()
91 // pthread basics
92 #ifdef _WIN32
93 static int get_cur_thread_id() {
94  return (int)GetCurrentThreadId();
95 }
96 static int get_cur_process_id() {
97  return (int)GetCurrentProcessId();
98 }
99 
100 static uint64_t _frequency = 0;
101 static uint64_t _starttime = 0;
102 double mtr_time_s() {
103  if (_frequency == 0) {
104  QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency);
105  QueryPerformanceCounter((LARGE_INTEGER*)&_starttime);
106  }
107  __int64 time;
108  QueryPerformanceCounter((LARGE_INTEGER*)&time);
109  return ((double) (time - _starttime) / (double) _frequency);
110 }
111 
112 // Ctrl+C handling for Windows console apps
113 static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
114  if (is_tracing && fdwCtrlType == CTRL_C_EVENT) {
115  printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
116  mtr_flush();
117  mtr_shutdown();
118  }
119  ExitProcess(1);
120 }
121 
123  // For console apps:
124  SetConsoleCtrlHandler(&CtrlHandler, TRUE);
125 }
126 
127 #else
128 
129 static inline int get_cur_thread_id() {
130  return (int)(intptr_t)pthread_self();
131 }
132 static inline int get_cur_process_id() {
133  return (int)getpid();
134 }
135 
136 #if defined(BLACKBERRY)
137 double mtr_time_s() {
138  struct timespec time;
139  clock_gettime(CLOCK_MONOTONIC, &time); // Linux must use CLOCK_MONOTONIC_RAW due to time warps
140  return time.tv_sec + time.tv_nsec / 1.0e9;
141 }
142 #else
143 double mtr_time_s() {
144  static time_t start;
145  struct timeval tv;
146  gettimeofday(&tv, NULL);
147  if (start == 0) {
148  start = tv.tv_sec;
149  }
150  tv.tv_sec -= start;
151  return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
152 }
153 #endif // !BLACKBERRY
154 
155 static void termination_handler(int signum) ATTR_NORETURN;
156 static void termination_handler(int signum) {
157  (void) signum;
158  if (is_tracing) {
159  printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
160  mtr_flush();
161  fwrite("\n]}\n", 1, 4, f);
162  fclose(f);
163  }
164  exit(1);
165 }
166 
168 #ifndef MTR_ENABLED
169  return;
170 #endif
171  // Avoid altering set-to-be-ignored handlers while registering.
172  if (signal(SIGINT, &termination_handler) == SIG_IGN)
173  signal(SIGINT, SIG_IGN);
174 }
175 
176 #endif
177 
178 void mtr_init_from_stream(void *stream) {
179 #ifndef MTR_ENABLED
180  return;
181 #endif
184  is_tracing = 1;
185  event_count = 0;
186  f = (FILE *)stream;
187  const char *header = "{\"traceEvents\":[\n";
188  fwrite(header, 1, strlen(header), f);
189  time_offset = (uint64_t)(mtr_time_s() * 1000000);
190  first_line = 1;
191  pthread_mutex_init(&mutex, 0);
192  pthread_mutex_init(&event_mutex, 0);
193 }
194 
195 void mtr_init(const char *json_file) {
196 #ifndef MTR_ENABLED
197  return;
198 #endif
199  mtr_init_from_stream(fopen(json_file, "wb"));
200 }
201 
202 void mtr_shutdown() {
203  int i;
204 #ifndef MTR_ENABLED
205  return;
206 #endif
207  pthread_mutex_lock(&mutex);
208  is_tracing = FALSE;
209  pthread_mutex_unlock(&mutex);
211 
212  fwrite("\n]}\n", 1, 4, f);
213  fclose(f);
214  pthread_mutex_destroy(&mutex);
215  pthread_mutex_destroy(&event_mutex);
216  f = 0;
217  free(event_buffer);
218  event_buffer = 0;
219  for (i = 0; i < STRING_POOL_SIZE; i++) {
220  if (str_pool[i]) {
221  free(str_pool[i]);
222  str_pool[i] = 0;
223  }
224  }
225 }
226 
227 const char *mtr_pool_string(const char *str) {
228  int i;
229  for (i = 0; i < STRING_POOL_SIZE; i++) {
230  if (!str_pool[i]) {
231  str_pool[i] = (char*)malloc(strlen(str) + 1);
232  strcpy(str_pool[i], str);
233  return str_pool[i];
234  } else {
235  if (!strcmp(str, str_pool[i]))
236  return str_pool[i];
237  }
238  }
239  return "string pool full";
240 }
241 
242 void mtr_start() {
243 #ifndef MTR_ENABLED
244  return;
245 #endif
246  pthread_mutex_lock(&mutex);
247  is_tracing = TRUE;
248  pthread_mutex_unlock(&mutex);
249 }
250 
251 void mtr_stop() {
252 #ifndef MTR_ENABLED
253  return;
254 #endif
255  pthread_mutex_lock(&mutex);
256  is_tracing = FALSE;
257  pthread_mutex_unlock(&mutex);
258 }
259 
260 // TODO: fwrite more than one line at a time.
261 // Flushing is thread safe and process async
262 // using double-buffering mechanism.
263 // Aware: only one flushing process may be
264 // running at any point of time
265 void mtr_flush_with_state(int is_last) {
266 #ifndef MTR_ENABLED
267  return;
268 #endif
269  int i = 0;
270  char linebuf[1024];
271  char arg_buf[1024];
272  char id_buf[256];
273  int event_count_copy = 0;
274  int events_in_progress_copy = 1;
275  raw_event_t *event_buffer_tmp = NULL;
276 
277  // small critical section to swap buffers
278  // - no any new events can be spawn while
279  // swapping since they tied to the same mutex
280  // - checks for any flushing in process
281  pthread_mutex_lock(&mutex);
282  // if not flushing already
283  if (is_flushing) {
284  pthread_mutex_unlock(&mutex);
285  return;
286  }
287  is_flushing = TRUE;
288  event_count_copy = event_count;
289  event_buffer_tmp = flush_buffer;
291  event_buffer = event_buffer_tmp;
292  event_count = 0;
293  // waiting for any unfinished events before swap
294  while (events_in_progress_copy != 0) {
295  pthread_mutex_lock(&event_mutex);
296  events_in_progress_copy = events_in_progress;
297  pthread_mutex_unlock(&event_mutex);
298  }
299  pthread_mutex_unlock(&mutex);
300 
301  for (i = 0; i < event_count_copy; i++) {
302  raw_event_t *raw = &flush_buffer[i];
303  int len;
304  switch (raw->arg_type) {
305  case MTR_ARG_TYPE_INT:
306  snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":%i", raw->arg_name, raw->a_int);
307  break;
309  snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
310  break;
312  if (strlen(raw->a_str) > 700) {
313  snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%.*s\"", raw->arg_name, 700, raw->a_str);
314  } else {
315  snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
316  }
317  break;
318  case MTR_ARG_TYPE_NONE:
319  arg_buf[0] = '\0';
320  break;
321  }
322  if (raw->id) {
323  switch (raw->ph) {
324  case 'S':
325  case 'T':
326  case 'F':
327  // TODO: Support full 64-bit pointers
328  snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"id\":\"0x%08x\"", (uint32_t)(uintptr_t)raw->id);
329  break;
330  case 'X':
331  snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"dur\":%i", (int)raw->a_double);
332  break;
333  }
334  } else {
335  id_buf[0] = 0;
336  }
337  const char *cat = raw->cat;
338 #ifdef _WIN32
339  // On Windows, we often end up with backslashes in category.
340  char temp[256];
341  {
342  int len = (int)strlen(cat);
343  int i;
344  if (len > 255) len = 255;
345  for (i = 0; i < len; i++) {
346  temp[i] = cat[i] == '\\' ? '/' : cat[i];
347  }
348  temp[len] = 0;
349  cat = temp;
350  }
351 #endif
352 
353  len = snprintf(linebuf, ARRAY_SIZE(linebuf), "%s{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ",\"ph\":\"%c\",\"name\":\"%s\",\"args\":{%s}%s}",
354  first_line ? "" : ",\n",
355  cat, raw->pid, raw->tid, raw->ts - time_offset, raw->ph, raw->name, arg_buf, id_buf);
356  fwrite(linebuf, 1, len, f);
357  first_line = 0;
358 
359  if (raw->arg_type == MTR_ARG_TYPE_STRING_COPY) {
360  free((void*)raw->a_str);
361  }
362  #ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
363  free(raw->name);
364  free(raw->cat);
365  #endif
366  }
367 
368  pthread_mutex_lock(&mutex);
369  is_flushing = is_last;
370  pthread_mutex_unlock(&mutex);
371 }
372 
373 void mtr_flush() {
375 }
376 
377 void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id) {
378 #ifndef MTR_ENABLED
379  return;
380 #endif
381  pthread_mutex_lock(&mutex);
383  pthread_mutex_unlock(&mutex);
384  return;
385  }
387  ++event_count;
388  pthread_mutex_lock(&event_mutex);
390  pthread_mutex_unlock(&event_mutex);
391  pthread_mutex_unlock(&mutex);
392 
393  double ts = mtr_time_s();
394  if (!cur_thread_id) {
396  }
397  if (!cur_process_id) {
399  }
400 
401 #ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
402  const size_t category_len = strlen(category);
403  ev->cat = malloc(category_len + 1);
404  strcpy(ev->cat, category);
405 
406  const size_t name_len = strlen(name);
407  ev->name = malloc(name_len + 1);
408  strcpy(ev->name, name);
409 
410 #else
411  ev->cat = category;
412  ev->name = name;
413 #endif
414 
415  ev->id = id;
416  ev->ph = ph;
417  if (ev->ph == 'X') {
418  double x;
419  memcpy(&x, id, sizeof(double));
420  ev->ts = (int64_t)(x * 1000000);
421  ev->a_double = (ts - x) * 1000000;
422  } else {
423  ev->ts = (int64_t)(ts * 1000000);
424  }
425  ev->tid = cur_thread_id;
426  ev->pid = cur_process_id;
428 
429  pthread_mutex_lock(&event_mutex);
431  pthread_mutex_unlock(&event_mutex);
432 }
433 
434 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) {
435 #ifndef MTR_ENABLED
436  return;
437 #endif
438  pthread_mutex_lock(&mutex);
440  pthread_mutex_unlock(&mutex);
441  return;
442  }
444  ++event_count;
445  pthread_mutex_lock(&event_mutex);
447  pthread_mutex_unlock(&event_mutex);
448  pthread_mutex_unlock(&mutex);
449 
450  if (!cur_thread_id) {
452  }
453  if (!cur_process_id) {
455  }
456  double ts = mtr_time_s();
457 
458 #ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
459  const size_t category_len = strlen(category);
460  ev->cat = malloc(category_len + 1);
461  strcpy(ev->cat, category);
462 
463  const size_t name_len = strlen(name);
464  ev->name = malloc(name_len + 1);
465  strcpy(ev->name, name);
466 
467 #else
468  ev->cat = category;
469  ev->name = name;
470 #endif
471 
472  ev->id = id;
473  ev->ts = (int64_t)(ts * 1000000);
474  ev->ph = ph;
475  ev->tid = cur_thread_id;
476  ev->pid = cur_process_id;
477  ev->arg_type = arg_type;
478  ev->arg_name = arg_name;
479  switch (arg_type) {
480  case MTR_ARG_TYPE_INT: ev->a_int = (int)(uintptr_t)arg_value; break;
481  case MTR_ARG_TYPE_STRING_CONST: ev->a_str = (const char*)arg_value; break;
482  case MTR_ARG_TYPE_STRING_COPY: ev->a_str = strdup((const char*)arg_value); break;
483  case MTR_ARG_TYPE_NONE: break;
484  }
485 
486  pthread_mutex_lock(&event_mutex);
488  pthread_mutex_unlock(&event_mutex);
489 }
490 
flush_buffer
static raw_event_t * flush_buffer
Definition: minitrace.cpp:67
FALSE
#define FALSE
Definition: minitrace.cpp:45
events_in_progress
static int events_in_progress
Definition: minitrace.cpp:71
ATTR_NORETURN
#define ATTR_NORETURN
Definition: minitrace.cpp:40
raw_event::arg_type
mtr_arg_type arg_type
Definition: minitrace.cpp:57
raw_event::id
void * id
Definition: minitrace.cpp:52
get_cur_thread_id
static int get_cur_thread_id()
Definition: minitrace.cpp:129
lexy::_unicode_db::category
constexpr lexy::code_point::general_category_t category[]
Definition: unicode_database.hpp:65
internal_mtr_raw_event
void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id)
Definition: minitrace.cpp:377
mtr_register_sigint_handler
void mtr_register_sigint_handler()
Definition: minitrace.cpp:167
mtr_start
void mtr_start()
Definition: minitrace.cpp:242
ARRAY_SIZE
#define ARRAY_SIZE(x)
Definition: minitrace.cpp:43
minitrace.h
internal_mtr_raw_event_arg
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:434
raw_event::ts
int64_t ts
Definition: minitrace.cpp:53
detail::void
j template void())
Definition: json.hpp:4893
mutex
static pthread_mutex_t mutex
Definition: minitrace.cpp:77
cur_thread_id
static __thread int cur_thread_id
Definition: minitrace.cpp:75
first_line
static int first_line
Definition: minitrace.cpp:73
is_flushing
static int is_flushing
Definition: minitrace.cpp:70
STRING_POOL_SIZE
#define STRING_POOL_SIZE
Definition: minitrace.cpp:80
lexy::_detail::cat
typename cat_< A, B >::type cat
Definition: nttp_string.hpp:105
raw_event::pid
uint32_t pid
Definition: minitrace.cpp:54
MTR_ARG_TYPE_INT
@ MTR_ARG_TYPE_INT
Definition: minitrace.h:81
mtr_flush_with_state
void mtr_flush_with_state(int)
Definition: minitrace.cpp:265
event_mutex
static pthread_mutex_t event_mutex
Definition: minitrace.cpp:78
mtr_arg_type
mtr_arg_type
Definition: minitrace.h:79
MTR_ARG_TYPE_STRING_COPY
@ MTR_ARG_TYPE_STRING_COPY
Definition: minitrace.h:85
cur_process_id
static int cur_process_id
Definition: minitrace.cpp:76
str_pool
static char * str_pool[100]
Definition: minitrace.cpp:81
raw_event::a_double
double a_double
Definition: minitrace.cpp:62
mtr_init
void mtr_init(const char *json_file)
Definition: minitrace.cpp:195
raw_event_t
struct raw_event raw_event_t
signal.h
MTR_ARG_TYPE_NONE
@ MTR_ARG_TYPE_NONE
Definition: minitrace.h:80
raw_event::tid
uint32_t tid
Definition: minitrace.cpp:55
mtr_shutdown
void mtr_shutdown()
Definition: minitrace.cpp:202
raw_event::a_int
int a_int
Definition: minitrace.cpp:61
f
static FILE * f
Definition: minitrace.cpp:74
mtr_init_from_stream
void mtr_init_from_stream(void *stream)
Definition: minitrace.cpp:178
raw_event
Definition: minitrace.cpp:49
mtr_stop
void mtr_stop()
Definition: minitrace.cpp:251
get_cur_process_id
static int get_cur_process_id()
Definition: minitrace.cpp:132
raw_event::cat
const char * cat
Definition: minitrace.cpp:51
raw_event::a_str
const char * a_str
Definition: minitrace.cpp:60
raw_event::arg_name
const char * arg_name
Definition: minitrace.cpp:58
time_offset
static int64_t time_offset
Definition: minitrace.cpp:72
MTR_ARG_TYPE_STRING_CONST
@ MTR_ARG_TYPE_STRING_CONST
Definition: minitrace.h:84
raw_event::ph
char ph
Definition: minitrace.cpp:56
mtr_flush
void mtr_flush()
Definition: minitrace.cpp:373
INTERNAL_MINITRACE_BUFFER_SIZE
#define INTERNAL_MINITRACE_BUFFER_SIZE
Definition: minitrace.h:39
is_tracing
static int is_tracing
Definition: minitrace.cpp:69
TRUE
#define TRUE
Definition: minitrace.cpp:44
termination_handler
static void termination_handler(int signum) ATTR_NORETURN
Definition: minitrace.cpp:156
event_buffer
static raw_event_t * event_buffer
Definition: minitrace.cpp:66
mtr_time_s
double mtr_time_s()
Definition: minitrace.cpp:143
mtr_pool_string
const char * mtr_pool_string(const char *str)
Definition: minitrace.cpp:227
event_count
static std::atomic_int event_count
Definition: minitrace.cpp:68
raw_event::name
const char * name
Definition: minitrace.cpp:50


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Jun 28 2024 02:20:07