// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once #include "nanotraceglobals.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace NanotraceHR { using Clock = std::chrono::steady_clock; using TimePoint = std::chrono::time_point; using Duration = std::chrono::nanoseconds; static_assert(Clock::is_steady, "clock should be steady"); static_assert(std::is_same_v, "the steady clock should have nano second resolution"); enum class Tracing { IsDisabled, IsEnabled }; constexpr Tracing tracingStatus() { #ifdef NANOTRACEHR_ENABLED return Tracing::IsEnabled; #else return Tracing::IsDisabled; #endif } #if __cplusplus >= 202002L && __has_cpp_attribute(msvc::no_unique_address) # define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] #elif __cplusplus >= 202002L && __has_cpp_attribute(no_unique_address) >= 201803L # define NO_UNIQUE_ADDRESS [[no_unique_address]] #else # define NO_UNIQUE_ADDRESS #endif using ArgumentsString = Utils::BasicSmallString<510>; namespace Literals { struct TracerLiteral { friend constexpr TracerLiteral operator""_t(const char *text, size_t size); constexpr operator std::string_view() const { return text; } operator std::string() const { return std::string{text}; } private: constexpr TracerLiteral(std::string_view text) : text{text} {} std::string_view text; }; constexpr TracerLiteral operator""_t(const char *text, size_t size) { return {std::string_view{text, size}}; } } // namespace Literals using namespace Literals; struct IsArray {}; inline constexpr IsArray isArray; struct IsDictonary {}; inline constexpr IsDictonary isDictonary; namespace Internal { template void convertToString(String &string, std::string_view text) { string.append(R"(")"); string.append(text); string.append(R"(")"); } template void convertToString(String &string, const QImage &image); extern template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image); extern template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image); template void convertToString(String &string, const char (&text)[size]) { string.append(R"(")"); string.append(text); string.append(R"(")"); } template void convertToString(String &string, QStringView text) { string.append(R"(")"); string.append(Utils::PathString{text}); string.append(R"(")"); } template void convertToString(String &string, const QByteArray &text) { string.append(R"(")"); string.append(std::string_view(text.data(), Utils::usize(text))); string.append(R"(")"); } template void convertToString(String &string, bool isTrue) { if (isTrue) string.append("true"); else string.append("false"); } template>> void convertToString(String &string, Callable &&callable) { convertToString(string, callable()); } template void convertToString(String &string, int number) { string.append(Utils::SmallString::number(number)); } template void convertToString(String &string, long long number) { string.append(Utils::SmallString::number(number)); } template void convertToString(String &string, double number) { string.append(Utils::SmallString::number(number)); } template void convertToString(String &string, const std::tuple &dictonary); template void convertToString(String &string, const std::tuple &list); template typename Container, typename... Arguments> void convertToString(String &string, const Container &container); template void convertArrayEntryToString(String &string, const Value &value) { convertToString(string, value); string.append(","); } template void convertArrayToString(String &string, const IsArray &, Entries &...entries) { string.append(R"([)"); (convertArrayEntryToString(string, entries), ...); if (sizeof...(entries)) string.pop_back(); string.append("]"); } template void convertToString(String &string, const std::tuple &list) { std::apply([&](auto &&...entries) { convertArrayToString(string, entries...); }, list); } template void convertDictonaryEntryToString(String &string, const std::tuple &argument) { const auto &[key, value] = argument; convertToString(string, key); string.append(":"); convertToString(string, value); string.append(","); } template void convertDictonaryToString(String &string, const IsDictonary &, Entries &...entries) { string.append(R"({)"); (convertDictonaryEntryToString(string, entries), ...); if (sizeof...(entries)) string.pop_back(); string.append("}"); } template void convertToString(String &string, const std::tuple &dictonary) { std::apply([&](auto &&...entries) { convertDictonaryToString(string, entries...); }, dictonary); } template typename Container, typename... Arguments> void convertToString(String &string, const Container &container) { string.append("["); for (const auto &entry : container) { convertToString(string, entry); string.append(","); } if (container.size()) string.pop_back(); string.append("]"); } template String toArguments(Arguments &&...arguments) { if constexpr (tracingStatus() == Tracing::IsEnabled) { String text; constexpr auto argumentCount = sizeof...(Arguments); text.append("{"); (convertDictonaryEntryToString(text, arguments), ...); if (argumentCount) text.pop_back(); text.append("}"); return text; } else { return {}; } } inline std::string_view toArguments(std::string_view arguments) { return arguments; } template void appendArguments(String &eventArguments) { eventArguments = {}; } template void appendArguments(String &eventArguments, TracerLiteral arguments) { eventArguments = arguments; } template [[maybe_unused]] void appendArguments(String &eventArguments, Arguments &&...arguments) { static_assert( !std::is_same_v, R"(The arguments type of the tracing event queue is a string view. You can only provide trace token arguments as TracerLiteral (""_t).)"); eventArguments = Internal::toArguments(std::forward(arguments)...); } } // namespace Internal template auto keyValue(Key &&key, Value &&value) { return std::forward_as_tuple(std::forward(key), std::forward(value)); } template auto dictonary(Entries &&...entries) { return std::forward_as_tuple(isDictonary, std::forward(entries)...); } template auto array(Entries &&...entries) { return std::forward_as_tuple(isArray, std::forward(entries)...); } enum class IsFlow : std::size_t { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out }; inline bool operator&(IsFlow first, IsFlow second) { return static_cast>(first) & static_cast>(second); } template struct TraceEvent { using StringType = String; using ArgumentType = std::conditional_t, TracerLiteral, String>; using ArgumentsStringType = ArgumentsString; TraceEvent() = default; TraceEvent(const TraceEvent &) = delete; TraceEvent(TraceEvent &&) = delete; TraceEvent &operator=(const TraceEvent &) = delete; TraceEvent &operator=(TraceEvent &&) = delete; ~TraceEvent() = default; String name; String category; ArgumentsString arguments; TimePoint time; Duration duration; std::size_t id = 0; std::size_t bindId : 62; IsFlow flow : 2; char type = ' '; }; using StringViewTraceEvent = TraceEvent; using StringViewWithStringArgumentsTraceEvent = TraceEvent; using StringTraceEvent = TraceEvent; enum class IsEnabled { No, Yes }; template class EventQueue; template using EnabledEventQueue = EventQueue; template void flushEvents(const Utils::span events, std::thread::id threadId, EnabledEventQueue &eventQueue); extern template NANOTRACE_EXPORT void flushEvents(const Utils::span events, std::thread::id threadId, EnabledEventQueue &eventQueue); extern template NANOTRACE_EXPORT void flushEvents(const Utils::span events, std::thread::id threadId, EnabledEventQueue &eventQueue); extern template NANOTRACE_EXPORT void flushEvents( const Utils::span events, std::thread::id threadId, EnabledEventQueue &eventQueue); template void flushInThread(EnabledEventQueue &eventQueue); extern template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue &eventQueue); extern template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue &eventQueue); extern template NANOTRACE_EXPORT void flushInThread( EnabledEventQueue &eventQueue); template class TraceFile; using EnabledTraceFile = TraceFile; NANOTRACE_EXPORT void openFile(EnabledTraceFile &file); NANOTRACE_EXPORT void finalizeFile(EnabledTraceFile &file); template class TraceFile { public: using IsActive = std::false_type; TraceFile(std::string_view) {} }; template<> class TraceFile { public: using IsActive = std::true_type; TraceFile([[maybe_unused]] std::string_view filePath) : filePath{filePath} { openFile(*this); } TraceFile(const TraceFile &) = delete; TraceFile(TraceFile &&) = delete; TraceFile &operator=(const TraceFile &) = delete; TraceFile &operator=(TraceFile &&) = delete; ~TraceFile() { finalizeFile(*this); } std::string filePath; std::mutex fileMutex; std::future processing; std::ofstream out; }; template class EventQueue { public: using IsActive = std::false_type; }; namespace Internal { template class EventQueueTracker { using Queue = EventQueue; public: EventQueueTracker() = default; EventQueueTracker(const EventQueueTracker &) = delete; EventQueueTracker(EventQueueTracker &&) = delete; EventQueueTracker &operator=(const EventQueueTracker &) = delete; EventQueueTracker &operator=(EventQueueTracker &&) = delete; ~EventQueueTracker() { std::lock_guard lock{mutex}; for (auto queue : queues) queue->flush(); } void addQueue(Queue *queue) { std::lock_guard lock{mutex}; queues.push_back(queue); } void removeQueue(Queue *queue) { std::lock_guard lock{mutex}; queues.erase(std::remove(queues.begin(), queues.end(), queue), queues.end()); } static EventQueueTracker &get() { static EventQueueTracker tracker; return tracker; } private: std::mutex mutex; std::vector queues; }; } // namespace Internal template class EventQueue { using TraceEventsSpan = Utils::span; public: using IsActive = std::true_type; EventQueue(EnabledTraceFile *file, TraceEventsSpan eventsOne, TraceEventsSpan eventsTwo); ~EventQueue(); void flush(); EventQueue(const EventQueue &) = delete; EventQueue(EventQueue &&) = delete; EventQueue &operator=(const EventQueue &) = delete; EventQueue &operator=(EventQueue &&) = delete; EnabledTraceFile *file = nullptr; TraceEventsSpan eventsOne; TraceEventsSpan eventsTwo; TraceEventsSpan currentEvents; std::size_t eventsIndex = 0; IsEnabled isEnabled = IsEnabled::Yes; std::mutex mutex; std::thread::id threadId; }; template using StringViewEventQueue = EventQueue; template using StringViewWithStringArgumentsEventQueue = EventQueue; template using StringEventQueue = EventQueue; extern template class NANOTRACE_EXPORT EventQueue; extern template class NANOTRACE_EXPORT EventQueue; extern template class NANOTRACE_EXPORT EventQueue; template class EventQueueData { public: using IsActive = std::true_type; EventQueueData(TraceFile &) {} EventQueue createEventQueue() { return {}; } }; template class EventQueueData { using TraceEvents = std::array; public: using IsActive = std::true_type; EventQueueData(EnabledTraceFile &file) : file{file} {} EventQueue createEventQueue() { return {&file, eventsOne, eventsTwo}; } EnabledTraceFile &file; TraceEvents eventsOne; TraceEvents eventsTwo; }; NANOTRACE_EXPORT EventQueue &globalEventQueue(); template TraceEvent &getTraceEvent(EnabledEventQueue &eventQueue) { if (eventQueue.eventsIndex == eventQueue.currentEvents.size()) flushInThread(eventQueue); return eventQueue.currentEvents[eventQueue.eventsIndex++]; } class BasicDisabledToken { public: using IsActive = std::false_type; BasicDisabledToken() {} BasicDisabledToken(const BasicDisabledToken &) = default; BasicDisabledToken &operator=(const BasicDisabledToken &) = default; BasicDisabledToken(BasicDisabledToken &&other) noexcept = default; BasicDisabledToken &operator=(BasicDisabledToken &&other) noexcept = default; ~BasicDisabledToken() {} constexpr explicit operator bool() const { return false; } static constexpr bool isActive() { return false; } }; class BasicEnabledToken { public: using IsActive = std::true_type; BasicEnabledToken() {} BasicEnabledToken(const BasicEnabledToken &) = default; BasicEnabledToken &operator=(const BasicEnabledToken &) = default; BasicEnabledToken(BasicEnabledToken &&other) noexcept = default; BasicEnabledToken &operator=(BasicEnabledToken &&other) noexcept = default; ~BasicEnabledToken() {} constexpr explicit operator bool() const { return false; } static constexpr bool isActive() { return false; } }; template class Tracer; template class Token : public BasicDisabledToken { public: using ArgumentType = typename Category::ArgumentType; using FlowTokenType = typename Category::FlowTokenType; using AsynchronousTokenType = typename Category::AsynchronousTokenType; using TracerType = typename Category::TracerType; Token() {} ~Token() {} template [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) { return {}; } template [[nodiscard]] std::pair beginAsynchronousWithFlow( ArgumentType, Arguments &&...) { return {}; } template [[nodiscard]] TracerType beginDuration(ArgumentType, Arguments &&...) { return {}; } template [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType, Arguments &&...) { return {}; } template void tick(ArgumentType, Arguments &&...) {} }; template class Token : public BasicEnabledToken { using CategoryFunctionPointer = typename Category::CategoryFunctionPointer; Token(std::size_t id, CategoryFunctionPointer category) : m_id{id} , m_category{category} {} using PrivateTag = typename Category::PrivateTag; public: using ArgumentType = typename Category::ArgumentType; using FlowTokenType = typename Category::FlowTokenType; using AsynchronousTokenType = typename Category::AsynchronousTokenType; using TracerType = typename Category::TracerType; Token(PrivateTag, std::size_t id, CategoryFunctionPointer category) : Token{id, category} {} friend TracerType; friend AsynchronousTokenType; ~Token() {} template [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType name, Arguments &&...arguments) { if (m_id) m_category().begin('b', m_id, name, 0, IsFlow::No, std::forward(arguments)...); return {std::move(name), m_id, m_category}; } template [[nodiscard]] std::pair beginAsynchronousWithFlow( ArgumentType name, Arguments &&...arguments) { std::size_t bindId = 0; if (m_id) { auto category = m_category(); bindId = category.createBindId(); category.begin('b', m_id, name, bindId, IsFlow::Out, std::forward(arguments)...); } return {std::piecewise_construct, std::forward_as_tuple(std::move(name), m_id, m_category), std::forward_as_tuple(std::move(name), bindId, m_category)}; } template [[nodiscard]] TracerType beginDuration(ArgumentType traceName, Arguments &&...arguments) { return {traceName, m_category, std::forward(arguments)...}; } template [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType traceName, Arguments &&...arguments) { std::size_t bindId = m_category().createBindId(); return {std::piecewise_construct, std::forward_as_tuple(PrivateTag{}, bindId, IsFlow::Out, traceName, m_category, std::forward(arguments)...), std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_category)}; } template void tick(ArgumentType name, Arguments &&...arguments) { m_category().begin('i', 0, name, 0, IsFlow::No, std::forward(arguments)...); } private: std::size_t m_id = 0; CategoryFunctionPointer m_category = nullptr; }; template class Category; template using StringViewCategory = Category; template using StringCategory = Category; template using StringViewWithStringArgumentsCategory = Category; using DisabledToken = Token, Tracing::IsDisabled>; template class ObjectToken : public BasicDisabledToken { public: using ArgumentType = typename Category::ArgumentType; ObjectToken() = default; ObjectToken(const ObjectToken &) = delete; ObjectToken &operator=(const ObjectToken &) = delete; ObjectToken(ObjectToken &&other) noexcept = default; ObjectToken &operator=(ObjectToken &&other) noexcept = default; ~ObjectToken() = default; template void change(ArgumentType, Arguments &&...) {} }; template class ObjectToken : public BasicEnabledToken { using CategoryFunctionPointer = typename Category::CategoryFunctionPointer; ObjectToken(std::string_view name, std::size_t id, CategoryFunctionPointer category) : m_name{name} , m_id{id} , m_category{category} {} public: using StringType = typename Category::StringType; using ArgumentType = typename Category::ArgumentType; friend Category; ObjectToken(const ObjectToken &other) : m_name{other.m_name} , m_category{other.m_category} { if (other.m_id) m_id = m_category().beginObject(m_name).m_id; } ObjectToken &operator=(const ObjectToken &other) { if (this != &other) { ~ObjectToken(); if (other.m_id) { m_category = other.m_category; m_name = other.m_name; m_id = other.m_category->beginObject(other.m_name).m_id; } } } ObjectToken(ObjectToken &&other) noexcept : m_name{std::move(other.m_name)} , m_id{std::exchange(other.m_id, 0)} , m_category{std::exchange(other.m_category, nullptr)} {} ObjectToken &operator=(ObjectToken &&other) noexcept { if (&other != this) { m_name = std::move(other.m_name); m_id = std::exchange(other.m_id, 0); m_category = std::exchange(other.m_category, nullptr); } return *this; } ~ObjectToken() { if (m_id) m_category().end('e', m_id, std::move(m_name)); m_id = 0; } template void change(ArgumentType name, Arguments &&...arguments) { if (m_id) { m_category().tick( 'n', m_id, std::move(name), 0, IsFlow::No, std::forward(arguments)...); } } private: StringType m_name; std::size_t m_id = 0; CategoryFunctionPointer m_category = nullptr; }; template class AsynchronousToken : public BasicDisabledToken { public: using ArgumentType = typename Category::ArgumentType; using FlowTokenType = typename Category::FlowTokenType; using TokenType = typename Category::TokenType; AsynchronousToken() {} AsynchronousToken(const AsynchronousToken &) = delete; AsynchronousToken &operator=(const AsynchronousToken &) = delete; AsynchronousToken(AsynchronousToken &&other) noexcept = default; AsynchronousToken &operator=(AsynchronousToken &&other) noexcept = default; ~AsynchronousToken() {} [[nodiscard]] TokenType createToken() { return {}; } template [[nodiscard]] AsynchronousToken begin(ArgumentType, Arguments &&...) { return {}; } template [[nodiscard]] std::pair beginWithFlow(ArgumentType, Arguments &&...) { return {}; } template void tick(ArgumentType, Arguments &&...) {} template FlowTokenType tickWithFlow(ArgumentType, Arguments &&...) { return {}; } template void end(Arguments &&...) {} }; using DisabledAsynchronousToken = AsynchronousToken, Tracing::IsDisabled>; template class FlowToken; template class AsynchronousToken : public BasicEnabledToken { using CategoryFunctionPointer = typename Category::CategoryFunctionPointer; AsynchronousToken(std::string_view name, std::size_t id, CategoryFunctionPointer category) : m_name{name} , m_id{id} , m_category{category} {} using PrivateTag = typename Category::PrivateTag; public: using StringType = typename Category::StringType; using ArgumentType = typename Category::ArgumentType; using FlowTokenType = typename Category::FlowTokenType; using TokenType = typename Category::TokenType; friend Category; friend FlowTokenType; friend TokenType; AsynchronousToken(PrivateTag, std::string_view name, std::size_t id, CategoryFunctionPointer category) : AsynchronousToken{name, id, category} {} AsynchronousToken() {} AsynchronousToken(const AsynchronousToken &) = delete; AsynchronousToken &operator=(const AsynchronousToken &) = delete; AsynchronousToken(AsynchronousToken &&other) noexcept : m_name{std::move(other.m_name)} , m_id{std::exchange(other.m_id, 0)} , m_category{std::exchange(other.m_category, nullptr)} {} AsynchronousToken &operator=(AsynchronousToken &&other) noexcept { if (&other != this) { m_name = std::move(other.m_name); m_id = std::exchange(other.m_id, 0); m_category = std::exchange(other.m_category, nullptr); } return *this; } ~AsynchronousToken() { end(); } [[nodiscard]] TokenType createToken() { return {m_id, m_category}; } template [[nodiscard]] AsynchronousToken begin(ArgumentType name, Arguments &&...arguments) { if (m_id) m_category().begin('b', m_id, name, 0, IsFlow::No, std::forward(arguments)...); return AsynchronousToken{std::move(name), m_id, m_category}; } template [[nodiscard]] std::pair beginWithFlow(ArgumentType name, Arguments &&...arguments) { std::size_t bindId = 0; if (m_id) { auto category = m_category(); bindId = category.createBindId(); category.begin('b', m_id, name, bindId, IsFlow::Out, std::forward(arguments)...); } return {std::piecewise_construct, std::forward_as_tuple(std::move(name), m_id, m_category), std::forward_as_tuple(PrivateTag{}, std::move(name), bindId, m_category)}; } template void tick(ArgumentType name, Arguments &&...arguments) { if (m_id) { m_category().tick( 'n', m_id, std::move(name), 0, IsFlow::No, std::forward(arguments)...); } } template FlowTokenType tickWithFlow(ArgumentType name, Arguments &&...arguments) { std::size_t bindId = 0; if (m_id) { auto category = m_category(); bindId = category.createBindId(); category.tick('n', m_id, name, bindId, IsFlow::Out, std::forward(arguments)...); } return {std::move(name), bindId, m_category}; } template void end(Arguments &&...arguments) { if (m_id && m_name.size()) m_category().end('e', m_id, std::move(m_name), std::forward(arguments)...); m_id = 0; } private: StringType m_name; std::size_t m_id = 0; CategoryFunctionPointer m_category = nullptr; }; template class FlowToken : public BasicDisabledToken { public: FlowToken() = default; using AsynchronousTokenType = typename Category::AsynchronousTokenType; using ArgumentType = typename Category::ArgumentType; using TracerType = typename Category::TracerType; using TokenType = typename Category::TokenType; friend TracerType; friend AsynchronousTokenType; friend TokenType; friend Category; FlowToken(const FlowToken &) = default; FlowToken &operator=(const FlowToken &) = default; FlowToken(FlowToken &&other) noexcept = default; FlowToken &operator=(FlowToken &&other) noexcept = default; ~FlowToken() {} template [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) { return {}; } template [[nodiscard]] std::pair beginAsynchronousWithFlow(ArgumentType, Arguments &&...) { return {}; } template void connectTo(const AsynchronousTokenType &, Arguments &&...) {} template [[nodiscard]] TracerType beginDuration(ArgumentType, Arguments &&...) { return {}; } template [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType, Arguments &&...) { return std::pair(); } template void tick(ArgumentType, Arguments &&...) {} }; template class FlowToken : public BasicDisabledToken { using PrivateTag = typename Category::PrivateTag; using CategoryFunctionPointer = typename Category::CategoryFunctionPointer; public: FlowToken() = default; FlowToken(PrivateTag, std::string_view name, std::size_t bindId, CategoryFunctionPointer category) : m_name{name} , m_bindId{bindId} , m_category{category} {} using StringType = typename Category::StringType; using AsynchronousTokenType = typename Category::AsynchronousTokenType; using ArgumentType = typename Category::ArgumentType; using TracerType = typename Category::TracerType; using TokenType = typename Category::TokenType; friend AsynchronousTokenType; friend TokenType; friend TracerType; friend Category; FlowToken(const FlowToken &) = default; FlowToken &operator=(const FlowToken &) = default; FlowToken(FlowToken &&other) noexcept = default; FlowToken &operator=(FlowToken &&other) noexcept = default; ~FlowToken() {} template [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType name, Arguments &&...arguments) { std::size_t id = 0; if (m_bindId) { auto category = m_category(); id = category.createId(); category->begin('b', id, name, m_bindId, IsFlow::In, std::forward(arguments)...); } return {std::move(name), id, m_category}; } template [[nodiscard]] std::pair beginAsynchronousWithFlow( ArgumentType name, Arguments &&...arguments) { std::size_t id = 0; std::size_t bindId = 0; if (m_bindId) { auto category = m_category(); id = category.createId(); bindId = category.createBindId(); category->begin('b', id, name, bindId, IsFlow::InOut, std::forward(arguments)...); } return {std::piecewise_construct, std::forward_as_tuple(PrivateTag{}, std::move(name), id, m_category), std::forward_as_tuple(PrivateTag{}, std::move(name), bindId, m_category)}; } template void connectTo(const AsynchronousTokenType &token, Arguments &&...arguments) { if (m_bindId && token.m_id) { m_category().begin('b', token.m_id, token.m_name, m_bindId, IsFlow::In, std::forward(arguments)...); } } template [[nodiscard]] TracerType beginDuration(ArgumentType traceName, Arguments &&...arguments) { return {m_bindId, IsFlow::In, traceName, m_category, std::forward(arguments)...}; } template [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType traceName, Arguments &&...arguments) { return {std::piecewise_construct, std::forward_as_tuple(PrivateTag{}, m_bindId, IsFlow::InOut, traceName, m_category, std::forward(arguments)...), std::forward_as_tuple(PrivateTag{}, traceName, m_bindId, m_category)}; } template void tick(ArgumentType name, Arguments &&...arguments) { m_category().tick('i', 0, name, m_bindId, IsFlow::In, std::forward(arguments)...); } private: StringType m_name; std::size_t m_bindId = 0; CategoryFunctionPointer m_category = nullptr; }; template class Category { public: using IsActive = std::false_type; using ArgumentType = typename TraceEvent::ArgumentType; using ArgumentsStringType = typename TraceEvent::ArgumentsStringType; using AsynchronousTokenType = AsynchronousToken; using ObjectTokenType = ObjectToken; using FlowTokenType = FlowToken; using TracerType = Tracer; using TokenType = Token; using CategoryFunctionPointer = Category &(*) (); Category(ArgumentType, EventQueue &, CategoryFunctionPointer) {} Category(ArgumentType, EventQueue &, CategoryFunctionPointer) {} template [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) { return {}; } template [[nodiscard]] std::pair beginAsynchronousWithFlow( ArgumentType, Arguments &&...) {} template [[nodiscard]] ObjectTokenType beginObject(ArgumentType, Arguments &&...) { return {}; } template [[nodiscard]] TracerType beginDuration(ArgumentType, Arguments &&...) { return {}; } template [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType, Arguments &&...) { return std::pair(); } static constexpr bool isActive() { return false; } }; template class Category { class PrivateTag {}; public: using IsActive = std::true_type; using ArgumentType = typename TraceEvent::ArgumentType; using ArgumentsStringType = typename TraceEvent::ArgumentsStringType; using StringType = typename TraceEvent::StringType; using AsynchronousTokenType = AsynchronousToken; using ObjectTokenType = ObjectToken; using FlowTokenType = FlowToken; using TracerType = Tracer; using TokenType = Token; using CategoryFunctionPointer = Category &(*) (); friend AsynchronousTokenType; friend ObjectTokenType; friend TokenType; friend FlowTokenType; friend TracerType; template Category(ArgumentType name, EventQueue &queue, CategoryFunctionPointer self) : m_name{std::move(name)} , m_eventQueue{queue} , m_self{self} { static_assert(std::is_same_v, "A active category is not possible with an inactive event queue!"); m_idCounter = m_globalIdCounter += 1ULL << 32; m_bindIdCounter = m_globalBindIdCounter += 1ULL << 32; } template [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType traceName, Arguments &&...arguments) { std::size_t id = createId(); begin('b', id, std::move(traceName), 0, IsFlow::No, std::forward(arguments)...); return {traceName, id, m_self}; } template [[nodiscard]] std::pair beginAsynchronousWithFlow( ArgumentType traceName, Arguments &&...arguments) { std::size_t id = createId(); std::size_t bindId = createBindId(); begin('b', id, std::move(traceName), bindId, IsFlow::Out, std::forward(arguments)...); return {std::piecewise_construct, std::forward_as_tuple(PrivateTag{}, traceName, id, m_self), std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_self)}; } template [[nodiscard]] ObjectTokenType beginObject(ArgumentType traceName, Arguments &&...arguments) { std::size_t id = createId(); begin('b', id, std::move(traceName), 0, IsFlow::No, std::forward(arguments)...); return {traceName, id, m_self}; } template [[nodiscard]] TracerType beginDuration(ArgumentType traceName, Arguments &&...arguments) { return {traceName, m_self, std::forward(arguments)...}; } template [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType traceName, Arguments &&...arguments) { std::size_t bindId = createBindId(); return {std::piecewise_construct, std::forward_as_tuple(PrivateTag{}, bindId, IsFlow::Out, traceName, m_self, std::forward(arguments)...), std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_self)}; } EnabledEventQueue &eventQueue() const { return m_eventQueue; } std::string_view name() const { return m_name; } static constexpr bool isActive() { return true; } std::size_t createBindId() { return ++m_bindIdCounter; } std::size_t createId() { return ++m_idCounter; } public: IsEnabled isEnabled = IsEnabled::Yes; private: template void begin(char type, std::size_t id, StringType traceName, std::size_t bindId, IsFlow flow, Arguments &&...arguments) { if (isEnabled == IsEnabled::No) return; auto &traceEvent = getTraceEvent(m_eventQueue); traceEvent.name = std::move(traceName); traceEvent.category = m_name; traceEvent.type = type; traceEvent.id = id; traceEvent.bindId = bindId; traceEvent.flow = flow; Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); traceEvent.time = Clock::now(); } template void tick(char type, std::size_t id, StringType traceName, std::size_t bindId, IsFlow flow, Arguments &&...arguments) { if (isEnabled == IsEnabled::No) return; auto time = Clock::now(); auto &traceEvent = getTraceEvent(m_eventQueue); traceEvent.name = std::move(traceName); traceEvent.category = m_name; traceEvent.time = time; traceEvent.type = type; traceEvent.id = id; traceEvent.bindId = bindId; traceEvent.flow = flow; Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); } template void end(char type, std::size_t id, StringType traceName, Arguments &&...arguments) { if (isEnabled == IsEnabled::No) return; auto time = Clock::now(); auto &traceEvent = getTraceEvent(m_eventQueue); traceEvent.name = std::move(traceName); traceEvent.category = m_name; traceEvent.time = time; traceEvent.type = type; traceEvent.id = id; traceEvent.bindId = 0; traceEvent.flow = IsFlow::No; Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); } private: StringType m_name; EnabledEventQueue &m_eventQueue; inline static std::atomic m_globalIdCounter; std::size_t m_idCounter; inline static std::atomic m_globalBindIdCounter; std::size_t m_bindIdCounter; CategoryFunctionPointer m_self; }; template using StringViewCategory = Category; template using StringCategory = Category; template using StringViewWithStringArgumentsCategory = Category; template class Tracer { public: Tracer() = default; using ArgumentType = typename Category::ArgumentType; using TokenType = typename Category::TokenType; using FlowTokenType = typename Category::FlowTokenType; friend TokenType; friend FlowTokenType; friend Category; template [[nodiscard]] Tracer(ArgumentType, Category &, Arguments &&...) {} Tracer(const Tracer &) = delete; Tracer &operator=(const Tracer &) = delete; Tracer(Tracer &&other) noexcept = default; Tracer &operator=(Tracer &&other) noexcept = delete; TokenType createToken() { return {}; } template Tracer beginDuration(ArgumentType, Arguments &&...) { return {}; } template void tick(ArgumentType, Arguments &&...) {} template void end(Arguments &&...) {} ~Tracer() {} }; template class Tracer { using ArgumentType = typename Category::ArgumentType; using StringType = typename Category::StringType; using ArgumentsStringType = typename Category::ArgumentsStringType; using TokenType = typename Category::TokenType; using FlowTokenType = typename Category::FlowTokenType; using PrivateTag = typename Category::PrivateTag; using CategoryFunctionPointer = typename Category::CategoryFunctionPointer; friend FlowTokenType; friend TokenType; friend Category; template [[nodiscard]] Tracer(std::size_t bindId, IsFlow flow, ArgumentType name, CategoryFunctionPointer category, Arguments &&...arguments) : m_name{name} , m_bindId{bindId} , flow{flow} , m_category{category} { if (category().isEnabled == IsEnabled::Yes) { Internal::appendArguments(m_arguments, std::forward(arguments)...); m_start = Clock::now(); } } public: template [[nodiscard]] Tracer(PrivateTag, std::size_t bindId, IsFlow flow, ArgumentType name, CategoryFunctionPointer category, Arguments &&...arguments) : Tracer{bindId, flow, std::move(name), category, std::forward(arguments)...} {} template [[nodiscard]] Tracer(ArgumentType name, CategoryFunctionPointer category, Arguments &&...arguments) : m_name{name} , m_category{category} { if (category().isEnabled == IsEnabled::Yes) { Internal::appendArguments(m_arguments, std::forward(arguments)...); m_start = Clock::now(); } } Tracer(const Tracer &) = delete; Tracer &operator=(const Tracer &) = delete; Tracer(Tracer &&other) noexcept = delete; Tracer &operator=(Tracer &&other) noexcept = delete; TokenType createToken() { return {0, m_category}; } ~Tracer() { sendTrace(); } template Tracer beginDuration(ArgumentType name, Arguments &&...arguments) { return {std::move(name), m_category, std::forward(arguments)...}; } template void tick(ArgumentType name, Arguments &&...arguments) { m_category().begin('i', 0, name, 0, IsFlow::No, std::forward(arguments)...); } template void end(Arguments &&...arguments) { sendTrace(std::forward(arguments)...); m_name = {}; } private: template void sendTrace(Arguments &&...arguments) { if (m_name.size()) { auto category = m_category(); if (category.isEnabled == IsEnabled::Yes) { auto duration = Clock::now() - m_start; auto &traceEvent = getTraceEvent(category.eventQueue()); traceEvent.name = m_name; traceEvent.category = category.name(); traceEvent.time = m_start; traceEvent.duration = duration; traceEvent.bindId = m_bindId; traceEvent.flow = flow; traceEvent.type = 'X'; if (sizeof...(arguments)) { m_arguments.clear(); Internal::appendArguments(traceEvent.arguments, std::forward( arguments)...); } else { traceEvent.arguments = m_arguments; } } } } private: TimePoint m_start; StringType m_name; ArgumentsStringType m_arguments; std::size_t m_bindId; IsFlow flow; CategoryFunctionPointer m_category; }; template Tracer(typename Category::ArgumentType name, Category &category, Arguments &&...) -> Tracer; #ifdef NANOTRACEHR_ENABLED class GlobalTracer { public: template [[nodiscard]] GlobalTracer(std::string name, std::string category, Arguments &&...arguments) : m_name{std::move(name)} , m_category{std::move(category)} { if (globalEventQueue().isEnabled == IsEnabled::Yes) { Internal::appendArguments(m_arguments, std::forward(arguments)...); m_start = Clock::now(); } } ~GlobalTracer() { if (globalEventQueue().isEnabled == IsEnabled::Yes) { auto duration = Clock::now() - m_start; auto &traceEvent = getTraceEvent(globalEventQueue()); traceEvent.name = std::move(m_name); traceEvent.category = std::move(m_category); traceEvent.arguments = std::move(m_arguments); traceEvent.time = std::move(m_start); traceEvent.duration = std::move(duration); traceEvent.type = 'X'; } } private: TimePoint m_start; std::string m_name; std::string m_category; std::string m_arguments; }; #else class GlobalTracer { public: GlobalTracer(std::string_view, std::string_view, std::string_view) {} GlobalTracer(std::string_view, std::string_view) {} ~GlobalTracer() {} }; #endif } // namespace NanotraceHR