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