tracy引入
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <boost/pfr/core.hpp>
|
||||
#include <boost/pfr/core_name.hpp>
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
namespace aethera::observation {
|
||||
namespace detail {
|
||||
void trace_serialize() = delete;
|
||||
template <class Sink, class T>
|
||||
concept Custom_Serializable = requires(Sink& sink, const T& value) { trace_serialize(sink, value); };
|
||||
template <class Sink, class T>
|
||||
void serialize_custom(Sink& sink, const T& value) {
|
||||
trace_serialize(sink, value);
|
||||
}
|
||||
template <class Sink, class T>
|
||||
void serialize_field(Sink& sink, std::string_view name, const T& value) {
|
||||
using Value = std::remove_cvref_t<T>;
|
||||
if constexpr (Custom_Serializable<Sink, Value>) serialize_custom(sink, value);
|
||||
else sink.field(name, value);
|
||||
}
|
||||
template <class Sink, class T, std::size_t... Index>
|
||||
void serialize_aggregate(Sink& sink, const T& value, std::index_sequence<Index...>) {
|
||||
(serialize_field(sink, boost::pfr::get_name<Index, T>(), boost::pfr::get<Index>(value)), ...);
|
||||
}
|
||||
}
|
||||
template <class Sink, class T>
|
||||
void serialize(Sink& sink, const T& value) {
|
||||
using Value = std::remove_cvref_t<T>;
|
||||
if constexpr (detail::Custom_Serializable<Sink, Value>) {
|
||||
detail::serialize_custom(sink, value);
|
||||
} else {
|
||||
static_assert(std::is_aggregate_v<Value>, "observation serialization requires an aggregate or trace_serialize overload");
|
||||
detail::serialize_aggregate(sink, value, std::make_index_sequence<boost::pfr::tuple_size_v<Value>>{});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
#include "Serialize.hpp"
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include <array>
|
||||
#include <charconv>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
#ifdef TRACY_ENABLE
|
||||
#include <tracy/Tracy.hpp>
|
||||
#endif
|
||||
namespace aethera::observation {
|
||||
class Tracy_Text_Sink {
|
||||
public:
|
||||
template <class T>
|
||||
void field(std::string_view name, const T& value) {
|
||||
if (size_ != 0) append(" ");
|
||||
append(name);
|
||||
append("=");
|
||||
append_value(value);
|
||||
}
|
||||
[[nodiscard]] const char* data() const noexcept {
|
||||
return buffer_.data();
|
||||
}
|
||||
[[nodiscard]] std::size_t size() const noexcept {
|
||||
return size_;
|
||||
}
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return size_ == 0;
|
||||
}
|
||||
private:
|
||||
template <class>
|
||||
static constexpr bool always_false = false;
|
||||
void append(std::string_view value) {
|
||||
const auto available = buffer_.size() - size_;
|
||||
const auto count = value.size() < available ? value.size() : available;
|
||||
if (count == 0) return;
|
||||
std::memcpy(buffer_.data() + size_, value.data(), count);
|
||||
size_ += count;
|
||||
}
|
||||
template <class T>
|
||||
void append_number(T value) {
|
||||
std::array<char, 128> text{};
|
||||
const auto [end, error] = std::to_chars(text.data(), text.data() + text.size(), value);
|
||||
if (error == std::errc{}) append(std::string_view(text.data(), static_cast<std::size_t>(end - text.data())));
|
||||
}
|
||||
template <class T>
|
||||
void append_value(const T& value) {
|
||||
using Value = std::remove_cvref_t<T>;
|
||||
if constexpr (std::same_as<Value, bool>) {
|
||||
append(value ? "true" : "false");
|
||||
} else if constexpr (std::is_enum_v<Value>) {
|
||||
append(magic_enum::enum_name(value));
|
||||
} else if constexpr (std::integral<Value> || std::floating_point<Value>) {
|
||||
append_number(value);
|
||||
} else if constexpr (std::same_as<Value, std::string>) {
|
||||
append(value);
|
||||
} else if constexpr (std::same_as<Value, std::string_view>) {
|
||||
append(value);
|
||||
} else if constexpr (std::same_as<Value, char>) {
|
||||
append(std::string_view(&value, 1));
|
||||
} else if constexpr (std::same_as<Value, char*> || std::same_as<Value, const char*>) {
|
||||
append(value == nullptr ? std::string_view("null") : std::string_view(value));
|
||||
} else if constexpr (requires { std::string_view{value}; }) {
|
||||
append(std::string_view{value});
|
||||
} else {
|
||||
static_assert(always_false<Value>, "Tracy tag field type requires a trace_serialize overload that emits supported scalar fields");
|
||||
}
|
||||
}
|
||||
std::array<char, 1024> buffer_{};
|
||||
std::size_t size_{};
|
||||
};
|
||||
#ifdef TRACY_ENABLE
|
||||
template <class Make_Tags>
|
||||
void attach_tracy_tags(tracy::ScopedZone& zone, Make_Tags&& make_tags) {
|
||||
if (!zone.IsActive()) return;
|
||||
Tracy_Text_Sink sink;
|
||||
serialize(sink, make_tags());
|
||||
if (!sink.empty()) zone.Text(sink.data(), sink.size());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef TRACY_ENABLE
|
||||
#define AETHERA_TRACE_ZONE(name, ...) ZoneScopedN(name); ::aethera::observation::attach_tracy_tags(___tracy_scoped_zone, [&]() -> decltype(auto) { return (__VA_ARGS__); })
|
||||
#else
|
||||
#define AETHERA_TRACE_ZONE(name, ...)
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "observation/Tracy.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
namespace {
|
||||
enum class Test_Phase {
|
||||
prepare,
|
||||
publish
|
||||
};
|
||||
namespace custom {
|
||||
struct Resource_Id {
|
||||
std::uint64_t value;
|
||||
};
|
||||
template <class Sink>
|
||||
void trace_serialize(Sink& sink, const Resource_Id& value) {
|
||||
sink.field("resource", value.value);
|
||||
}
|
||||
}
|
||||
struct Test_Tags {
|
||||
std::uint64_t frame;
|
||||
custom::Resource_Id resource;
|
||||
Test_Phase phase;
|
||||
bool dirty;
|
||||
};
|
||||
namespace custom {
|
||||
struct Test_Tags {
|
||||
std::uint64_t resource;
|
||||
};
|
||||
template <class Sink>
|
||||
void trace_serialize(Sink& sink, const Test_Tags& value) {
|
||||
sink.field("id", value.resource);
|
||||
sink.field("kind", "resource");
|
||||
}
|
||||
}
|
||||
TEST(Observation_Serialize, Aggregate_Uses_Pfr_And_Field_Customization) {
|
||||
aethera::observation::Tracy_Text_Sink sink;
|
||||
aethera::observation::serialize(sink, Test_Tags{17, {23}, Test_Phase::prepare, true});
|
||||
EXPECT_EQ(std::string_view(sink.data(), sink.size()), "frame=17 resource=23 phase=prepare dirty=true");
|
||||
}
|
||||
TEST(Observation_Serialize, Adl_Serializer_Overrides_Aggregate_Fallback) {
|
||||
aethera::observation::Tracy_Text_Sink sink;
|
||||
aethera::observation::serialize(sink, custom::Test_Tags{41});
|
||||
EXPECT_EQ(std::string_view(sink.data(), sink.size()), "id=41 kind=resource");
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -1,7 +1,7 @@
|
||||
rcl_init(aethera_kernel)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/taskflow.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/tracy.cmake)
|
||||
set(Aethera_Kernel_dependencies global::proxy aethera_kernel::tracy aethera_kernel::taskflow global::magic_enum)
|
||||
set(Aethera_Kernel_dependencies global::proxy aethera_kernel::taskflow global::magic_enum global::pfr)
|
||||
set(Aethera_BUILD_TESTS TRUE)
|
||||
if (Aethera_BUILD_TESTS)
|
||||
enable_testing()
|
||||
@@ -37,6 +37,7 @@ append_glob_source(Aethera_Kernel_Core_sources "${Aethera_Kernel_source_dir}")
|
||||
add_library(Aethera_Kernel_Core STATIC ${Aethera_Kernel_Core_sources})
|
||||
target_include_directories(Aethera_Kernel_Core PUBLIC
|
||||
"$<BUILD_INTERFACE:${Aethera_Kernel_source_dir}>"
|
||||
"$<BUILD_INTERFACE:${EXTERNAL_DIR}/source/pfr/include>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/third_party>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/third_party/ratas-master/src>"
|
||||
)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../web_server/cmake/rely.cmake")
|
||||
set(Aethera_MCP_dependencies
|
||||
global::drogon
|
||||
global::pfr
|
||||
)
|
||||
rcl_add_dependency_action_targets(Aethera_MCP_env ${Aethera_MCP_dependencies})
|
||||
set_target_properties(Aethera_MCP_env PROPERTIES FOLDER Aethera_MCP)
|
||||
@@ -42,7 +41,6 @@ append_glob_source(Aethera_MCP_core_sources "${Aethera_MCP_core_dir}")
|
||||
add_library(Aethera_MCP_Core STATIC ${Aethera_MCP_core_sources})
|
||||
target_include_directories(Aethera_MCP_Core PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/..>"
|
||||
"$<BUILD_INTERFACE:${EXTERNAL_DIR}/source/pfr/include>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/../web_server/third_party/include>")
|
||||
target_compile_features(Aethera_MCP_Core PUBLIC cxx_std_23)
|
||||
target_compile_definitions(Aethera_MCP_Core PUBLIC NOMINMAX)
|
||||
|
||||
Reference in New Issue
Block a user