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_singpost_action(_o2_log_t* log, void (*callback)(void*));
206void _o2_signpost_interval_begin(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...);
207void _o2_signpost_interval_end(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...);
208void _o2_log_set_stacktrace(_o2_log_t* log, int stacktrace);
209
210// Helper to invoke a callback when the signpost is enabled. The callback
211// gets passed some previously stored context (nullptr for now).
212// TODO: I use a separate function because in the future this might change and I might
213// allow to store some context as part of the activity.
214inline void _o2_signpost_action(_o2_log_t* log, void (*callback)(void*))
215{
216 callback(nullptr);
217}
218
219// This generates a unique id for a signpost. Do not use this directly, use O2_SIGNPOST_ID_GENERATE instead.
220// Notice that this is only valid on a given computer.
221// This is guaranteed to be unique at 5 GHz for at least 63 years, if my math is correct.
222// I doubt we will have a job running for that long or that CPU scaling will shorten that period too much.
223// If you want to use this on the Nostromo, please think twice about it.
224// We use odd numbers so that pointers to things which are not bytes are not confused with
225// the generated ones.
227{
228 return {(log->unique_signpost++ * 2) + 1};
229}
230
231// Generate a unique id for a signpost. Do not use this directly, use O2_SIGNPOST_ID_FROM_POINTER instead.
232// Notice that this will fail for pointers to bytes as it might overlap with the id above.
234{
235 assert(((int64_t)pointer & 1) != 1);
236 _o2_signpost_id_t uniqueId{(int64_t)pointer};
237 return uniqueId;
238}
239
240// Implementation start here. Include this file with O2_SIGNPOST_IMPLEMENTATION defined in one file of your
241// project.
242#ifdef O2_SIGNPOST_IMPLEMENTATION
243#include <cstdarg>
244#include <cstdio>
245#include <cstring>
246#include <execinfo.h>
249void _o2_signpost_event_emit_v(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, va_list args);
250void _o2_signpost_interval_end_v(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, va_list args);
251
252// returns true if the push was successful, false if the stack was full
253// @param spin if true, will spin until the stack is not full
254bool _o2_lock_free_stack_push(_o2_lock_free_stack& stack, const int& value, bool spin)
255{
256 size_t currentTop = stack.top.load(std::memory_order_relaxed);
257 while (true) {
258 if (currentTop == _o2_lock_free_stack::N && spin == false) {
259 return false;
260 } else if (currentTop == _o2_lock_free_stack::N) {
261// Avoid consuming too much CPU time if we are spinning.
262#if defined(__x86_64__) || defined(__i386__)
263 __asm__ __volatile__("pause" ::
264 : "memory");
265#elif defined(__aarch64__)
266 __asm__ __volatile__("yield" ::
267 : "memory");
268#endif
269 continue;
270 }
271
272 if (stack.top.compare_exchange_weak(currentTop, currentTop + 1,
273 std::memory_order_release,
274 std::memory_order_relaxed)) {
275 stack.stack[currentTop] = value;
276 return true;
277 }
278 }
279}
280
282{
283 size_t currentTop = stack.top.load(std::memory_order_relaxed);
284 while (true) {
285 if (currentTop == 0 && spin == false) {
286 return false;
287 } else if (currentTop == 0) {
288// Avoid consuming too much CPU time if we are spinning.
289#if defined(__x86_64__) || defined(__i386__)
290 __asm__ __volatile__("pause" ::
291 : "memory");
292#elif defined(__aarch64__)
293 __asm__ __volatile__("yield" ::
294 : "memory");
295#endif
296 continue;
297 }
298
299 if (stack.top.compare_exchange_weak(currentTop, currentTop - 1,
300 std::memory_order_acquire,
301 std::memory_order_relaxed)) {
302 value = stack.stack[currentTop - 1];
303 return true;
304 }
305 }
306}
307
308void* _o2_log_create(char const* name, int defaultStacktrace)
309{
310 // iterate over the list of logs and check if we already have
311 // one with the same name.
312 o2_log_handle_t* handle = o2_walk_logs([](char const* currentName, void* log, void* context) -> bool {
313 char const* name = (char const*)context;
314 if (strcmp(name, currentName) == 0) {
315 return false;
316 }
317 return true;
318 },
319 (void*)name);
320
321 // If we found one, return it.
322 if (handle) {
323 return handle->log;
324 }
325 // Otherwise, create a new one and add it to the end of the list.
326 auto* log = new _o2_log_t();
327 // Write the initial 256 ids to the inbox, in reverse, so that the
328 // linear search below is just for an handful of elements.
330 for (int i = 0; i < n; i++) {
331 _o2_signpost_index_t signpost_index{n - 1 - i};
332 _o2_lock_free_stack_push(log->slots, signpost_index, true);
333 }
334 log->defaultStacktrace = defaultStacktrace;
335 auto* newHandle = new o2_log_handle_t();
336 newHandle->log = log;
337#ifdef __APPLE__
338 // On macOS, we use the os_signpost API so that when we are
339 // using instruments we can see the messages there.
340 if (defaultStacktrace > 1) {
341 log->os_log = os_log_create(name, OS_LOG_CATEGORY_DYNAMIC_STACK_TRACING);
342 } else {
343 log->os_log = os_log_create(name, OS_LOG_CATEGORY_DYNAMIC_TRACING);
344 }
345#endif
346 newHandle->name = strdup(name);
347 newHandle->next = o2_get_logs_tail().load();
348 // Until I manage to replace the log I have in next, keep trying.
349 // Notice this does not protect against two threads trying to insert
350 // a log with the same name. I should probably do a sorted insert for that.
351 while (!o2_get_logs_tail().compare_exchange_weak(newHandle->next, newHandle,
352 std::memory_order_release,
353 std::memory_order_relaxed)) {
354 newHandle->next = o2_get_logs_tail();
355 }
356
357 return log;
358}
359
360// This will look at the slot in the log associated to the ID.
361// If the slot is empty, it will return the id and increment the indentation level.
362void _o2_signpost_event_emit_v(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, va_list args)
363{
364 // Find the index of the activity
365 int leading = 0;
366
367 // This is the equivalent of exclusive
368 if (id.value != 0) {
369 int i = 0;
370 for (i = 0; i < log->ids.size(); ++i) {
371 if (log->ids[i].value == id.value) {
372 break;
373 }
374 }
375 // If the id is not in the list, then we consider it as a standalone event and we print
376 // it at toplevel.
377 if (i != log->ids.size()) {
378 // we found an interval associated to this id.
379 _o2_activity_t* activity = &log->activities[i];
380 leading = activity->indentation * 2;
381 }
382 }
383
384 char prebuffer[4096];
385 int s = snprintf(prebuffer, 4096, "id%.16" PRIx64 ":%-16s*>%*c", id.value, name, leading, ' ');
386 vsnprintf(prebuffer + s, 4096 - s, format, args);
387 O2_LOG_MACRO("%s", prebuffer);
388 if (log->stacktrace > 1) {
390 // We add one extra frame, because one is for the logging
391 int maxBacktrace = backtrace(traces, (log->stacktrace + 1) < o2::framework::BacktraceHelpers::MAX_BACKTRACE_SIZE ? (log->stacktrace + 1) : o2::framework::BacktraceHelpers::MAX_BACKTRACE_SIZE);
392 o2::framework::BacktraceHelpers::demangled_backtrace_symbols(traces, maxBacktrace, STDERR_FILENO);
393 }
394}
395
396// We separate this so that we can still emit the end signpost when the log is not enabled.
397void _o2_signpost_event_emit(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...)
398{
399 va_list args;
400 va_start(args, format);
401 _o2_signpost_event_emit_v(log, id, name, format, args);
402 va_end(args);
403}
404
405// This will look at the slot in the log associated to the ID.
406// If the slot is empty, it will return the id and increment the indentation level.
407void _o2_signpost_interval_begin(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...)
408{
409 va_list args;
410 va_start(args, format);
411 // This is a unique slot for this interval.
412 _o2_signpost_index_t signpost_index;
413 _o2_lock_free_stack_pop(log->slots, signpost_index, true);
414 // Put the id in the slot, to close things or to attach signposts to a given interval
415 log->ids[signpost_index].value = id.value;
416 auto* activity = &log->activities[signpost_index];
417 activity->indentation = log->current_indentation++;
418 activity->name = name;
419 int leading = activity->indentation * 2;
420 char prebuffer[4096];
421 int s = snprintf(prebuffer, 4096, "id%.16" PRIx64 ":%-16sS>%*c", id.value, name, leading, ' ');
422 vsnprintf(prebuffer + s, 4096 - s, format, args);
423 va_end(args);
424 O2_LOG_MACRO("%s", prebuffer);
425}
426
427void _o2_signpost_interval_end_v(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, va_list args)
428{
429 if (log->stacktrace == 0) {
430 return;
431 }
432 // Find the index of the activity
433 int i = 0;
434 for (i = 0; i < log->ids.size(); ++i) {
435 if (log->ids[i].value == id.value) {
436 break;
437 }
438 }
439 // If we do not find a matching id, then we just emit this as an event in then log.
440 // We should not make this an error because one could have enabled the log after the interval
441 // was started.
442 if (i == log->ids.size()) {
443 _o2_signpost_event_emit_v(log, id, name, format, args);
444 return;
445 }
446 // i is the slot index
447 _o2_activity_t* activity = &log->activities[i];
448 int leading = activity->indentation * 2;
449 char prebuffer[4096];
450 int s = snprintf(prebuffer, 4096, "id%.16" PRIx64 ":%-16sE>%*c", id.value, name, leading, ' ');
451 vsnprintf(prebuffer + s, 4096 - s, format, args);
452 O2_LOG_MACRO("%s", prebuffer);
453 // Clear the slot
454 activity->indentation = -1;
455 activity->name = nullptr;
456 log->ids[i].value = -1;
457 // Put back the slot
458 log->current_indentation--;
459 _o2_signpost_index_t signpost_index{i};
460 _o2_lock_free_stack_push(log->slots, signpost_index, true);
461}
462
463// We separate this so that we can still emit the end signpost when the log is not enabled.
464void _o2_signpost_interval_end(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...)
465{
466 va_list args;
467 va_start(args, format);
468 _o2_signpost_interval_end_v(log, id, name, format, args);
469 va_end(args);
470 return;
471}
472
473void _o2_log_set_stacktrace(_o2_log_t* log, int stacktrace)
474{
475 log->stacktrace = stacktrace;
476}
477// A C function which can be used to enable the signposts
478extern "C" {
479void o2_debug_log_set_stacktrace(_o2_log_t* log, int stacktrace)
480{
481 log->stacktrace = stacktrace;
482}
483}
484#endif // O2_SIGNPOST_IMPLEMENTATION
485
486#if defined(__APPLE__) || defined(O2_FORCE_SIGNPOSTS) || !defined(O2_NSIGNPOSTS)
489#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)
491#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)
492#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)
493// When we enable the log, we set the stacktrace to the default value.
494#define O2_LOG_ENABLE(log) _o2_log_set_stacktrace(private_o2_log_##log, private_o2_log_##log->defaultStacktrace)
495#define O2_LOG_DISABLE(log) _o2_log_set_stacktrace(private_o2_log_##log, 0)
496// For the moment we simply use LOG DEBUG. We should have proper activities so that we can
497// turn on and off the printing.
498#define O2_LOG_DEBUG(log, ...) __extension__({ \
499 if (O2_BUILTIN_UNLIKELY(O2_LOG_ENABLED(log))) { \
500 O2_LOG_MACRO(__VA_ARGS__); \
501 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
502 O2_LOG_MACRO(__VA_ARGS__); \
503 } \
504})
505#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)
506#define O2_SIGNPOST_ID_GENERATE(name, log) _o2_signpost_id_t name = _o2_signpost_id_generate_local(private_o2_log_##log)
507
508// Execute the provided callback if the log is enabled. Useful e.g. to dump IgProf profiles
509// only if the signpost is enabled or to add remote telemetry for certain events.
510#define O2_SIGNPOST_ACTION(log, callback) __extension__({ \
511 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
512 _o2_signpost_action(private_o2_log_##log, callback); \
513 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
514 _o2_signpost_action(private_o2_log_##log, callback); \
515 } \
516})
517
518// In case Instruments is attached, we switch to the Apple signpost API otherwise, both one
519// mac and on linux we use our own implementation, using the logger. We can use the same ids because
520// they are compatible between the two implementations, we also use remove_engineering_type to remove
521// the engineering types from the format string, so that we can use the same format string for both.
522#define O2_SIGNPOST_EVENT_EMIT(log, id, name, format, ...) __extension__({ \
523 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
524 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
525 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
526 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
527 } \
528})
529
530// Similar to the above, however it will print a normal info message if the signpost is not enabled.
531#define O2_SIGNPOST_EVENT_EMIT_INFO(log, id, name, format, ...) __extension__({ \
532 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
533 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
534 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
535 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
536 } else { \
537 O2_LOG_MACRO_RAW(info, remove_engineering_type(format).data(), ##__VA_ARGS__); \
538 } \
539})
540
541// Similar to the above, however it will print a normal info message if the signpost is not enabled.
542#define O2_SIGNPOST_EVENT_EMIT_DETAIL(log, id, name, format, ...) __extension__({ \
543 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
544 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
545 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
546 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
547 } else { \
548 O2_LOG_MACRO_RAW(detail, remove_engineering_type(format).data(), ##__VA_ARGS__); \
549 } \
550})
551
552// Similar to the above, however it will always print a normal error message regardless of the signpost being enabled or not.
553#define O2_SIGNPOST_EVENT_EMIT_ERROR(log, id, name, format, ...) __extension__({ \
554 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
555 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
556 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
557 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
558 } \
559 O2_LOG_MACRO_RAW(error, remove_engineering_type(format).data(), ##__VA_ARGS__); \
560})
561
562// Similar to the above, however it will also print a normal warning message regardless of the signpost being enabled or not.
563#define O2_SIGNPOST_EVENT_EMIT_WARN(log, id, name, format, ...) __extension__({ \
564 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
565 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
566 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
567 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
568 } \
569 O2_LOG_MACRO_RAW(warn, remove_engineering_type(format).data(), ##__VA_ARGS__); \
570})
571
572// Similar to the above, however it will also print a normal critical message regardless of the signpost being enabled or not.
573#define O2_SIGNPOST_EVENT_EMIT_CRITICAL(log, id, name, format, ...) __extension__({ \
574 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
575 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
576 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
577 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
578 } \
579 O2_LOG_MACRO_RAW(critical, remove_engineering_type(format).data(), ##__VA_ARGS__); \
580})
581
582// Similar to the above, however it will also print a normal alarm message regardless of the signpost being enabled or not.
583#define O2_SIGNPOST_EVENT_EMIT_ALARM(log, id, name, format, ...) __extension__({ \
584 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
585 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
586 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
587 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
588 } \
589 O2_LOG_MACRO_RAW(alarm, remove_engineering_type(format).data(), ##__VA_ARGS__); \
590})
591
592// Similar to the above, however it will also print a normal alarm message regardless of the signpost being enabled or not.
593#define O2_SIGNPOST_EVENT_EMIT_IMPORTANT(log, id, name, format, ...) __extension__({ \
594 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
595 O2_SIGNPOST_EVENT_EMIT_MAC(log, id, name, format, ##__VA_ARGS__); \
596 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
597 _o2_signpost_event_emit(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
598 } \
599 O2_LOG_MACRO_RAW(important, remove_engineering_type(format).data(), ##__VA_ARGS__); \
600})
601
602#define O2_SIGNPOST_START(log, id, name, format, ...) \
603 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
604 O2_SIGNPOST_START_MAC(log, id, name, format, ##__VA_ARGS__); \
605 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
606 _o2_signpost_interval_begin(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
607 }
608#define O2_SIGNPOST_END(log, id, name, format, ...) \
609 if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \
610 O2_SIGNPOST_END_MAC(log, id, name, format, ##__VA_ARGS__); \
611 } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \
612 _o2_signpost_interval_end(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \
613 }
614#else // This is the release implementation, it does nothing.
615#define O2_DECLARE_DYNAMIC_LOG(x)
616#define O2_DECLARE_DYNAMIC_STACKTRACE_LOG(x)
617#define O2_DECLARE_LOG(x, category)
618#define O2_LOG_ENABLE(log)
619#define O2_LOG_DISABLE(log)
620#define O2_LOG_DEBUG(log, ...)
621#define O2_SIGNPOST_ID_FROM_POINTER(name, log, pointer)
622#define O2_SIGNPOST_ID_GENERATE(name, log)
623#define O2_SIGNPOST_EVENT_EMIT(log, id, name, format, ...)
624#define O2_SIGNPOST_START(log, id, name, format, ...)
625#define O2_SIGNPOST_END(log, id, name, format, ...)
626#endif
627
628#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:226
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
void _o2_signpost_action(_o2_log_t *log, void(*callback)(void *))
Definition Signpost.h:214
_o2_signpost_id_t _o2_signpost_id_make_with_pointer(_o2_log_t *log, void const *pointer)
Definition Signpost.h:233
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