diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index e5bb0a301..674aaec21 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -30,6 +30,7 @@ set(CUBOS_CORE_SOURCE "src/tel/logging.cpp" "src/tel/metrics.cpp" + "src/tel/tracing.cpp" "src/thread/pool.cpp" "src/thread/task.cpp" diff --git a/core/include/cubos/core/tel/tracing.hpp b/core/include/cubos/core/tel/tracing.hpp new file mode 100644 index 000000000..88654a38c --- /dev/null +++ b/core/include/cubos/core/tel/tracing.hpp @@ -0,0 +1,82 @@ +/// @file +/// @brief Tracing macros. +/// @ingroup core-tel + +#pragma once + +#include +#include +#include + +namespace cubos::core::tel +{ + class SpanManager + { + public: + class Span + { + public: + Span(const std::string& name, std::size_t level, const std::string& file, std::size_t line); + ~Span(); + std::string name(); + + private: + std::thread::id mId; ///< Thread id where span was invoked. + std::string mName; ///< The name of the scope. + std::string mFile; ///< The file from where it was invoked. + std::size_t mLine; ///< The line from where it was invoked. + std::chrono::time_point mStart; ///< Start time when constructed. + std::size_t mLevel; ///< Debug level + }; + + /// @brief Gets the current active span. + /// @return Span* Pointer to the current active span. + static Span* current(); + + /// @brief Decides next span name. + /// @example If current span is `foo` and a `bar` was invoked, this returns "foo:bar`. + /// @return Returns span name. + static std::string nextName(std::string name); + + /// @brief Gets the count of active spans. + /// @return Span count. + static std::size_t count(); + + /// @brief Enters a new span. + /// @param s Pointer to the span to be entered. + static void enter(Span* s); + + /// @brief Exits the current active span. + static void exit(); + + private: + SpanManager() = default; + }; +} // namespace cubos::core::tel + +#define SPAN(a, b) SPAN_INNER(a, b) +#define SPAN_INNER(a, b) a##b + +/// @brief Constructs a new info span. +/// @param name Span name. +#define CUBOS_SPAN_INFO(name) \ + SpanManager::Span SPAN(info_span_, __COUNTER__) \ + { \ + name, 1, __FILE__, __LINE__ \ + } + +/// @brief Constructs a new trace span. +/// @param name Span name. +#define CUBOS_SPAN_TRACE(name) \ + SpanManager::Span SPAN(trace_span_, __COUNTER__) \ + { \ + name, 2, __FILE__, __LINE__ \ + } + +/// @brief Constructs a new debug span. +/// @param name Span name. +#define CUBOS_SPAN_DEBUG(name) \ + SpanManager::Span SPAN(trace_span_, __COUNTER__) \ + { \ + name, 3, __FILE__, __LINE__ \ + } \ No newline at end of file diff --git a/core/src/tel/tracing.cpp b/core/src/tel/tracing.cpp new file mode 100644 index 000000000..16f25ab97 --- /dev/null +++ b/core/src/tel/tracing.cpp @@ -0,0 +1,81 @@ +#include + +#include + +using cubos::core::tel::SpanManager; + +namespace +{ + /// @brief Private type which stores the state of the span tracer. + struct State + { + std::stack spans; + }; +} // namespace + +/// @brief Span tracer state singleton. Guarantees it is initialized exactly once, per thread, when needed. +/// @return Tracer state. +static State& state() +{ + static thread_local State state{}; + return state; +} + +SpanManager::Span::Span(const std::string& name, std::size_t level, const std::string& file, std::size_t line) + : mId(std::this_thread::get_id()) + , mName(SpanManager::nextName(name)) + , mFile(file) + , mLine(line) + , mStart(std::chrono::time_point::clock::now()) + , mLevel(level) +{ + SpanManager::enter(this); + printf("START %s \n", mName.c_str()); +} + +SpanManager::Span::~Span() +{ + auto now = std::chrono::time_point::clock::now(); + std::chrono::duration elapsed = now - mStart; + // TODO store metric + printf("%f END %s - %lu - %lu - %s \n", elapsed.count(), mName.c_str(), mLevel, mLine, mFile.c_str()); + SpanManager::exit(); +} + +std::string SpanManager::Span::name() +{ + return mName; +} + +SpanManager::Span* SpanManager::current() +{ + return state().spans.empty() ? nullptr : state().spans.top(); +} + +std::string SpanManager::nextName(std::string name) +{ + if (current() != nullptr) + { + name = current()->name() + ":" + name; + } + + return name; +} + +std::size_t SpanManager::count() +{ + return state().spans.size(); +} + +void SpanManager::enter(Span* s) +{ + state().spans.push(s); +} + +void SpanManager::exit() +{ + if (!state().spans.empty()) + { + state().spans.pop(); + } +} \ No newline at end of file