Project
Loading...
Searching...
No Matches
Signpost.h
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11#ifndef O2_FRAMEWORK_SIGNPOST_H_
12#define O2_FRAMEWORK_SIGNPOST_H_
13
15#include <atomic>
16#include <array>
17#ifdef __APPLE__
18#include <os/log.h>
19#endif
20
22 char const* name = nullptr;
23 void* log = nullptr;
25};
26
27// Helper function which replaces engineering types with a printf
28// compatible format string.
29template <auto N>
30consteval auto remove_engineering_type(char const (&src)[N])
31{
32 std::array<char, N> res = {};
33 // do whatever string manipulation you want in res.
34 char* t = res.data();
35 for (int i = 0; i < N; ++i) {
36 if (src[i] == '%' && src[i + 1] == '{') {
37 *t++ = src[i];
38 while (src[i] != '}' && src[i] != 0) {
39 ++i;
40 }
41 if (src[i] == 0) {
42 *t = 0;
43 return res;
44 }
45 } else {
46 *t++ = src[i];
47 }
48 }
49 return res;
50}
51
52// Loggers registry is actually a feature available to all platforms
53// We use this to register the loggers and to walk over them.
54// So that also on mac we can have a list of all the registered loggers.
55std::atomic<o2_log_handle_t*>& o2_get_logs_tail();
56o2_log_handle_t* o2_walk_logs(bool (*callback)(char const* name, void* log, void* context), void* context = nullptr);
57
58#ifdef O2_SIGNPOST_IMPLEMENTATION
59// The first log of the list. We make it atomic,
60// so that we can add new logs from different threads.
61std::atomic<o2_log_handle_t*>& o2_get_logs_tail()
62{
63 static std::atomic<o2_log_handle_t*> first = nullptr;
64 return first;
65}
66
73o2_log_handle_t* o2_walk_logs(bool (*callback)(char const* name, void* log, void* context), void* context)
74{
75 // This might skip newly inserted logs, but that is ok.
76 o2_log_handle_t* current = o2_get_logs_tail().load();
77 while (current) {
78 bool cont = callback(current->name, current->log, context);
79 // In case we should not continue, break out of the loop.
80 if (cont == false) {
81 return current;
82 }
83 current = current->next;
84 }
85 return current;
86}
87#endif
88
89#if defined(__APPLE__)
90#include <os/log.h>
91#include <os/signpost.h>
92#include <cstring>
93#define O2_LOG_DEBUG_MAC(log, format, ...) os_log_debug(private_o2_log_##log, format __VA_OPT__(, ) __VA_ARGS__)
94#define O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ...) os_signpost_event_emit(private_o2_log_##log->os_log, (uint64_t)id.value, name, format __VA_OPT__(, ) __VA_ARGS__)
95#define O2_SIGNPOST_START_MAC(log, id, name, format, ...) os_signpost_interval_begin(private_o2_log_##log->os_log, (uint64_t)id.value, name, format __VA_OPT__(, ) __VA_ARGS__)
96#define O2_SIGNPOST_END_MAC(log, id, name, format, ...) os_signpost_interval_end(private_o2_log_##log->os_log, (uint64_t)id.value, name, format __VA_OPT__(, ) __VA_ARGS__)
97#define O2_SIGNPOST_ENABLED_MAC(log) os_signpost_enabled(private_o2_log_##log->os_log)
98#else
99// These are no-ops on linux.
100#define O2_DECLARE_LOG_MAC(x, category)
101#define O2_LOG_DEBUG_MAC(log, ...)
102#define O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ...)
103#define O2_SIGNPOST_START_MAC(log, id, name, format, ...)
104#define O2_SIGNPOST_END_MAC(log, id, name, format, ...)
105#define O2_SIGNPOST_ENABLED_MAC(log) false
106#endif // __APPLE__
107
108// Unless we are on apple we enable checking for signposts only if in debug mode or if we force them.
109#if defined(__APPLE__) || defined(O2_FORCE_SIGNPOSTS) || !defined(O2_NSIGNPOSTS)
110#define O2_LOG_ENABLED(log) private_o2_log_##log->stacktrace
111#else
112#define O2_LOG_ENABLED(log) false
113#endif
114
115#if !defined(O2_LOG_MACRO_RAW) && __has_include("Framework/Logger.h")
116#include "Framework/Logger.h"
117#define O2_LOG_MACRO_RAW(level, ...) LOGF(level, __VA_ARGS__)
118#elif !defined(O2_LOG_MACRO_RAW)
119// If we do not have the fairlogger, we simply print out the signposts to the console.
120// This is useful for things like the tests, which this way do not need to depend on the FairLogger.
121#define O2_LOG_MACRO_RAW(level, format, ...) \
122 do { \
123 printf(#level ":" #format, __VA_ARGS__); \
124 printf("\n"); \
125 } while (0)
126#else
127#define O2_LOG_MACRO_RAW(...)
128#endif // O2_LOG_MACRO_RAW
129
130#if !defined(O2_LOG_MACRO) && __has_include("Framework/Logger.h")
131#include "Framework/Logger.h"
132#define O2_LOG_MACRO(...) LOGF(info, __VA_ARGS__)
133#elif !defined(O2_LOG_MACRO)
134// If we do not have the fairlogger, we simply print out the signposts to the console.
135// This is useful for things like the tests, which this way do not need to depend on the FairLogger.
136#define O2_LOG_MACRO(...) \
137 do { \
138 printf(__VA_ARGS__); \
139 printf("\n"); \
140 } while (0)
141#else
142#define O2_LOG_MACRO(...)
143#endif // O2_LOG_MACRO
144
145// This is the linux implementation, it is not as nice as the apple one and simply prints out
146// the signpost information to the log.
147#include <atomic>
148#include <array>
149#include <cassert>
150#include <cinttypes>
151#include <cstddef>
152
154 static constexpr size_t N = 1024;
155 std::atomic<size_t> top = 0;
156 int stack[N];
157};
158
159// A log is simply an inbox which keeps track of the available id, so that we can print out different signposts
160// with different indentation levels.
161// supports up to 1024 paralle signposts before it spinlocks.
163
165 // How much the activity is indented in the output log.
166 unsigned char indentation = 0;
167 char const* name = nullptr;
168};
169
171 // The id of the activity.
172 int64_t value = -1;
173};
174
175struct _o2_log_t {
176#ifdef __APPLE__
177 os_log_t os_log = nullptr;
178#endif
179 // A circular buffer of available slots. Each unique interval pulls an id from this buffer.
181 // Up to 256 activities can be active at the same time.
182 std::array<_o2_signpost_id_t, 256> ids = {};
183 // The intervals associated with each slot.
184 // We use this to keep track of the indentation level for messages associated to it
185 std::array<_o2_activity_t, 256> activities = {};
186 std::atomic<int64_t> current_indentation = 0;
187 // Each thread needs to maintain a stack for the intervals, so that
188 // you can have nested intervals.
189 std::atomic<int64_t> unique_signpost = 1;
190
191 // how many stacktrace levels print per log.
192 // 0 means the log is disabled.
193 // 1 means only the current signpost is printed.
194 // >1 means the current signpost and n levels of the stacktrace are printed.
195 int stacktrace = 0;
196
197 // Default stacktrace level for the log, when enabled.
199};
200
201bool _o2_lock_free_stack_push(_o2_lock_free_stack& stack, const int& value, bool spin = false);
203void* _o2_log_create(char const* name, int stacktrace);
204void _o2_signpost_event_emit(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...);
205void _o2_signpost_interval_begin(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...);
206void _o2_signpost_interval_end(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...);
207void _o2_log_set_stacktrace(_o2_log_t* log, int stacktrace);
208
209// This generates a unique id for a signpost. Do not use this directly, use O2_SIGNPOST_ID_GENERATE instead.
210// Notice that this is only valid on a given computer.
211// This is guaranteed to be unique at 5 GHz for at least 63 years, if my math is correct.
212// I doubt we will have a job running for that long or that CPU scaling will shorten that period too much.
213// If you want to use this on the Nostromo, please think twice about it.
214// We use odd numbers so that pointers to things which are not bytes are not confused with
215// the generated ones.
217{
218 return {(log->unique_signpost++ * 2) + 1};
219}
220
221// Generate a unique id for a signpost. Do not use this directly, use O2_SIGNPOST_ID_FROM_POINTER instead.
222// Notice that this will fail for pointers to bytes as it might overlap with the id above.
224{
225 assert(((int64_t)pointer & 1) != 1);
226 _o2_signpost_id_t uniqueId{(int64_t)pointer};
227 return uniqueId;
228}
229
230// Implementation start here. Include this file with O2_SIGNPOST_IMPLEMENTATION defined in one file of your
231// project.
232#ifdef O2_SIGNPOST_IMPLEMENTATION
233#include <cstdarg>
234#include <cstdio>
235#include <cstring>
236#include <execinfo.h>
239void _o2_signpost_interval_end_v(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, va_list args);
240
241// returns true if the push was successful, false if the stack was full
242// @param spin if true, will spin until the stack is not full
243bool _o2_lock_free_stack_push(_o2_lock_free_stack& stack, const int& value, bool spin)
244{
245 size_t currentTop = stack.top.load(std::memory_order_relaxed);
246 while (true) {
247 if (currentTop == _o2_lock_free_stack::N && spin == false) {
248 return false;
249 } else if (currentTop == _o2_lock_free_stack::N) {
250// Avoid consuming too much CPU time if we are spinning.
251#if defined(__x86_64__) || defined(__i386__)
252 __asm__ __volatile__("pause" ::
253 : "memory");
254#elif defined(__aarch64__)
255 __asm__ __volatile__("yield" ::
256 : "memory");
257#endif
258 continue;
259 }
260
261 if (stack.top.compare_exchange_weak(currentTop, currentTop + 1,
262 std::memory_order_release,
263 std::memory_order_relaxed)) {
264 stack.stack[currentTop] = value;
265 return true;
266 }
267 }
268}
269
271{
272 size_t currentTop = stack.top.load(std::memory_order_relaxed);
273 while (true) {
274 if (currentTop == 0 && spin == false) {
275 return false;
276 } else if (currentTop == 0) {
277// Avoid consuming too much CPU time if we are spinning.
278#if defined(__x86_64__) || defined(__i386__)
279 __asm__ __volatile__("pause" ::
280 : "memory");
281#elif defined(__aarch64__)
282 __asm__ __volatile__("yield" ::
283 : "memory");
284#endif
285 continue;
286 }
287
288 if (stack.top.compare_exchange_weak(currentTop, currentTop - 1,
289 std::memory_order_acquire,
290 std::memory_order_relaxed)) {
291 value = stack.stack[currentTop - 1];
292 return true;
293 }
294 }
295}
296
297void* _o2_log_create(char const* name, int defaultStacktrace)
298{
299 // iterate over the list of logs and check if we already have
300 // one with the same name.
301 o2_log_handle_t* handle = o2_walk_logs([](char const* currentName, void* log, void* context) -> bool {
302 char const* name = (char const*)context;
303 if (strcmp(name, currentName) == 0) {
304 return false;
305 }
306 return true;
307 },
308 (void*)name);
309
310 // If we found one, return it.
311 if (handle) {
312 return handle->log;
313 }
314 // Otherwise, create a new one and add it to the end of the list.
315 auto* log = new _o2_log_t();
316 // Write the initial 256 ids to the inbox, in reverse, so that the
317 // linear search below is just for an handful of elements.
319 for (int i = 0; i < n; i++) {
320 _o2_signpost_index_t signpost_index{n - 1 - i};
321 _o2_lock_free_stack_push(log->slots, signpost_index, true);
322 }
323 log->defaultStacktrace = defaultStacktrace;
324 auto* newHandle = new o2_log_handle_t();
325 newHandle->log = log;
326#ifdef __APPLE__
327 // On macOS, we use the os_signpost API so that when we are
328 // using instruments we can see the messages there.
329 if (defaultStacktrace > 1) {
330 log->os_log = os_log_create(name, OS_LOG_CATEGORY_DYNAMIC_STACK_TRACING);
331 } else {
332 log->os_log = os_log_create(name, OS_LOG_CATEGORY_DYNAMIC_TRACING);
333 }
334#endif
335 newHandle->name = strdup(name);
336 newHandle->next = o2_get_logs_tail().load();
337 // Until I manage to replace the log I have in next, keep trying.
338 // Notice this does not protect against two threads trying to insert
339 // a log with the same name. I should probably do a sorted insert for that.
340 while (!o2_get_logs_tail().compare_exchange_weak(newHandle->next, newHandle,
341 std::memory_order_release,
342 std::memory_order_relaxed)) {
343 newHandle->next = o2_get_logs_tail();
344 }
345
346 return log;
347}
348
349// This will look at the slot in the log associated to the ID.
350// If the slot is empty, it will return the id and increment the indentation level.
351void _o2_signpost_event_emit(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...)
352{
353 va_list args;
354 va_start(args, format);
355
356 // Find the index of the activity
357 int leading = 0;
358
359 // This is the equivalent of exclusive
360 if (id.value != 0) {
361 int i = 0;
362 for (i = 0; i < log->ids.size(); ++i) {
363 if (log->ids[i].value == id.value) {
364 break;
365 }
366 }
367 // If the id is not in the list, then we consider it as a standalone event and we print
368 // it at toplevel.
369 if (i != log->ids.size()) {
370 // we found an interval associated to this id.
371 _o2_activity_t* activity = &log->activities[i];
372 leading = activity->indentation * 2;
373 }
374 }
375
376 char prebuffer[4096];
377 int s = snprintf(prebuffer, 4096, "id%.16" PRIx64 ":%-16s*>%*c", id.value, name, leading, ' ');
378 vsnprintf(prebuffer + s, 4096 - s, format, args);
379 va_end(args);
380 O2_LOG_MACRO("%s", prebuffer);
381 if (log->stacktrace > 1) {
383 // We add one extra frame, because one is for the logging
384 int maxBacktrace = backtrace(traces, (log->stacktrace + 1) < o2::framework::BacktraceHelpers::MAX_BACKTRACE_SIZE ? (log->stacktrace + 1) : o2::framework::BacktraceHelpers::MAX_BACKTRACE_SIZE);
385 o2::framework::BacktraceHelpers::demangled_backtrace_symbols(traces, maxBacktrace, STDERR_FILENO);
386 }
387}
388
389// This will look at the slot in the log associated to the ID.
390// If the slot is empty, it will return the id and increment the indentation level.
391void _o2_signpost_interval_begin(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...)
392{
393 va_list args;
394 va_start(args, format);
395 // This is a unique slot for this interval.
396 _o2_signpost_index_t signpost_index;
397 _o2_lock_free_stack_pop(log->slots, signpost_index, true);
398 // Put the id in the slot, to close things or to attach signposts to a given interval
399 log->ids[signpost_index].value = id.value;
400 auto* activity = &log->activities[signpost_index];
401 activity->indentation = log->current_indentation++;
402 activity->name = name;
403 int leading = activity->indentation * 2;
404 char prebuffer[4096];
405 int s = snprintf(prebuffer, 4096, "id%.16" PRIx64 ":%-16sS>%*c", id.value, name, leading, ' ');
406 vsnprintf(prebuffer + s, 4096 - s, format, args);
407 va_end(args);
408 O2_LOG_MACRO("%s", prebuffer);
409}
410
411void _o2_signpost_interval_end_v(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, va_list args)
412{
413 if (log->stacktrace == 0) {
414 return;
415 }
416 // Find the index of the activity
417 int i = 0;
418 for (i = 0; i < log->ids.size(); ++i) {
419 if (log->ids[i].value == id.value) {
420 break;
421 }
422 }
423 // If we do not find a matching id, then we just emit this as an event in then log.
424 // We should not make this an error because one could have enabled the log after the interval
425 // was started.
426 if (i == log->ids.size()) {
427 _o2_signpost_event_emit(log, id, name, format, args);
428 return;
429 }
430 // i is the slot index
431 _o2_activity_t* activity = &log->activities[i];
432 int leading = activity->indentation * 2;
433 char prebuffer[4096];
434 int s = snprintf(prebuffer, 4096, "id%.16" PRIx64 ":%-16sE>%*c", id.value, name, leading, ' ');
435 vsnprintf(prebuffer + s, 4096 - s, format, args);
436 O2_LOG_MACRO("%s", prebuffer);
437 // Clear the slot
438 activity->indentation = -1;
439 activity->name = nullptr;
440 log->ids[i].value = -1;
441 // Put back the slot
442 log->current_indentation--;
443 _o2_signpost_index_t signpost_index{i};
444 _o2_lock_free_stack_push(log->slots, signpost_index, true);
445}
446
447// We separate this so that we can still emit the end signpost when the log is not enabled.
448void _o2_signpost_interval_end(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...)
449{
450 va_list args;
451 va_start(args, format);
452 _o2_signpost_interval_end_v(log, id, name, format, args);
453 va_end(args);
454 return;
455}
456
457void _o2_log_set_stacktrace(_o2_log_t* log, int stacktrace)
458{
459 log->stacktrace = stacktrace;
460}
461// A C function which can be used to enable the signposts
462extern "C" {
463void o2_debug_log_set_stacktrace(_o2_log_t* log, int stacktrace)
464{
465 log->stacktrace = stacktrace;
466}
467}
468#endif // O2_SIGNPOST_IMPLEMENTATION
469
470#if defined(__APPLE__) || defined(O2_FORCE_SIGNPOSTS) || !defined(O2_NSIGNPOSTS)
473#define O2_DECLARE_DYNAMIC_LOG(name) static _o2_log_t* private_o2_log_##name = (_o2_log_t*)_o2_log_create("ch.cern.aliceo2." #name, 1)
475#define O2_DECLARE_DYNAMIC_STACKTRACE_LOG(name) static _o2_log_t* private_o2_log_##name = (_o2_log_t*)_o2_log_create("ch.cern.aliceo2." #name, 64)
476#define O2_DECLARE_LOG(name, category) static _o2_log_t* private_o2_log_##name = (_o2_log_t*)_o2_log_create("ch.cern.aliceo2." #name, 1)
477// When we enable the log, we set the stacktrace to the default value.
478#define O2_LOG_ENABLE(log) _o2_log_set_stacktrace(private_o2_log_##log, private_o2_log_##log->defaultStacktrace)
479#define O2_LOG_DISABLE(log) _o2_log_set_stacktrace(private_o2_log_##log, 0)
480// For the moment we simply use LOG DEBUG. We should have proper activities so that we can
481// turn on and off the printing.
482#define O2_LOG_DEBUG(log, ...) __extension__({ \
483 if (O2_BUILTIN_UNLIKELY(O2_LOG_ENABLED(log))) { \
484 O2_LOG_MACRO(__VA_ARGS__); \
485 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
486 O2_LOG_MACRO(__VA_ARGS__); \
487 } \
488})
489#define O2_SIGNPOST_ID_FROM_POINTER(name, log, pointer) _o2_signpost_id_t name = _o2_signpost_id_make_with_pointer(private_o2_log_##log, pointer)
490#define O2_SIGNPOST_ID_GENERATE(name, log) _o2_signpost_id_t name = _o2_signpost_id_generate_local(private_o2_log_##log)
491// In case Instruments is attached, we switch to the Apple signpost API otherwise, both one
492// mac and on linux we use our own implementation, using the logger. We can use the same ids because
493// they are compatible between the two implementations, we also use remove_engineering_type to remove
494// the engineering types from the format string, so that we can use the same format string for both.
495#define O2_SIGNPOST_EVENT_EMIT(log, id, name, format, ...) __extension__({ \
496 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
497 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
498 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
499 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
500 } \
501})
502
503// Similar to the above, however it will print a normal info message if the signpost is not enabled.
504#define O2_SIGNPOST_EVENT_EMIT_INFO(log, id, name, format, ...) __extension__({ \
505 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
506 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
507 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
508 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
509 } else { \
510 O2_LOG_MACRO_RAW(info, remove_engineering_type(format).data(), ##__VA_ARGS__); \
511 } \
512})
513
514// Similar to the above, however it will always print a normal error message regardless of the signpost being enabled or not.
515#define O2_SIGNPOST_EVENT_EMIT_ERROR(log, id, name, format, ...) __extension__({ \
516 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
517 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
518 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
519 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
520 } \
521 O2_LOG_MACRO_RAW(error, remove_engineering_type(format).data(), ##__VA_ARGS__); \
522})
523
524// Similar to the above, however it will also print a normal warning message regardless of the signpost being enabled or not.
525#define O2_SIGNPOST_EVENT_EMIT_WARN(log, id, name, format, ...) __extension__({ \
526 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
527 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
528 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
529 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
530 } \
531 O2_LOG_MACRO_RAW(warn, remove_engineering_type(format).data(), ##__VA_ARGS__); \
532})
533
534#define O2_SIGNPOST_START(log, id, name, format, ...) \
535 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
536 O2_SIGNPOST_START_MAC(log, id, name, format, ##__VA_ARGS__); \
537 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
538 _o2_signpost_interval_begin(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
539 }
540#define O2_SIGNPOST_END(log, id, name, format, ...) \
541 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
542 O2_SIGNPOST_END_MAC(log, id, name, format, ##__VA_ARGS__); \
543 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
544 _o2_signpost_interval_end(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
545 }
546#else // This is the release implementation, it does nothing.
547#define O2_DECLARE_DYNAMIC_LOG(x)
548#define O2_DECLARE_DYNAMIC_STACKTRACE_LOG(x)
549#define O2_DECLARE_LOG(x, category)
550#define O2_LOG_ENABLE(log)
551#define O2_LOG_DISABLE(log)
552#define O2_LOG_DEBUG(log, ...)
553#define O2_SIGNPOST_ID_FROM_POINTER(name, log, pointer)
554#define O2_SIGNPOST_ID_GENERATE(name, log)
555#define O2_SIGNPOST_EVENT_EMIT(log, id, name, format, ...)
556#define O2_SIGNPOST_START(log, id, name, format, ...)
557#define O2_SIGNPOST_END(log, id, name, format, ...)
558#endif
559
560#endif // O2_FRAMEWORK_SIGNPOST_H_
int32_t i
uint32_t res
Definition RawData.h:0
uint32_t stack
Definition RawData.h:1
o2_log_handle_t * o2_walk_logs(bool(*callback)(char const *name, void *log, void *context), void *context=nullptr)
bool _o2_lock_free_stack_push(_o2_lock_free_stack &stack, const int &value, bool spin=false)
void _o2_signpost_interval_end(_o2_log_t *log, _o2_signpost_id_t id, char const *name, char const *const format,...)
void _o2_log_set_stacktrace(_o2_log_t *log, int stacktrace)
void _o2_signpost_event_emit(_o2_log_t *log, _o2_signpost_id_t id, char const *name, char const *const format,...)
bool _o2_lock_free_stack_pop(_o2_lock_free_stack &stack, int &value, bool spin=false)
_o2_signpost_id_t _o2_signpost_id_generate_local(_o2_log_t *log)
Definition Signpost.h:216
std::atomic< o2_log_handle_t * > & o2_get_logs_tail()
consteval auto remove_engineering_type(char const (&src)[N])
Definition Signpost.h:30
#define O2_LOG_MACRO(...)
Definition Signpost.h:136
_o2_signpost_id_t _o2_signpost_id_make_with_pointer(_o2_log_t *log, void const *pointer)
Definition Signpost.h:223
void _o2_signpost_interval_begin(_o2_log_t *log, _o2_signpost_id_t id, char const *name, char const *const format,...)
void * _o2_log_create(char const *name, int stacktrace)
GLdouble n
Definition glcorearb.h:1982
GLenum void ** pointer
Definition glcorearb.h:805
GLenum src
Definition glcorearb.h:1767
GLuint * ids
Definition glcorearb.h:647
GLdouble GLdouble GLdouble GLdouble top
Definition glcorearb.h:4077
GLuint const GLchar * name
Definition glcorearb.h:781
GLint first
Definition glcorearb.h:399
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLint GLint GLsizei GLint GLenum format
Definition glcorearb.h:275
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
char const * name
Definition Signpost.h:167
unsigned char indentation
Definition Signpost.h:166
static constexpr size_t N
Definition Signpost.h:154
std::atomic< int64_t > current_indentation
Definition Signpost.h:186
_o2_lock_free_stack slots
Definition Signpost.h:180
int stacktrace
Definition Signpost.h:195
std::array< _o2_signpost_id_t, 256 > ids
Definition Signpost.h:182
std::array< _o2_activity_t, 256 > activities
Definition Signpost.h:185
std::atomic< int64_t > unique_signpost
Definition Signpost.h:189
int defaultStacktrace
Definition Signpost.h:198
static void demangled_backtrace_symbols(void **backtrace, unsigned int total, int fd)
static constexpr unsigned int MAX_BACKTRACE_SIZE
o2_log_handle_t * next
Definition Signpost.h:24
char const * name
Definition Signpost.h:22