diff --git a/include/datadog/tracer.h b/include/datadog/tracer.h index 49b0211c..949b19b4 100644 --- a/include/datadog/tracer.h +++ b/include/datadog/tracer.h @@ -31,6 +31,7 @@ struct SpanConfig; class TraceSampler; class SpanSampler; class IDGenerator; +class InMemoryFile; class Tracer { std::shared_ptr logger_; @@ -47,6 +48,10 @@ class Tracer { Optional hostname_; std::size_t tags_header_max_size_; bool sampling_delegation_enabled_; + // Store the tracer configuration in an in-memory file, allowing it to be + // read to determine if the process is instrumented with a tracer and to + // retrieve relevant tracing information. + std::shared_ptr metadata_file_; public: // Create a tracer configured using the specified `config`, and optionally: @@ -81,6 +86,9 @@ class Tracer { // Return a JSON object describing this Tracer's configuration. It is the same // JSON object that was logged when this Tracer was created. std::string config() const; + + private: + void store_config(); }; } // namespace tracing diff --git a/src/datadog/platform_util.cpp b/src/datadog/platform_util.cpp index f395d7cf..169094f5 100644 --- a/src/datadog/platform_util.cpp +++ b/src/datadog/platform_util.cpp @@ -25,6 +25,10 @@ # define DD_SDK_KERNEL "Linux" # include "string_util.h" # include +# include +# include +# include +# include # endif #elif defined(_MSC_VER) # include @@ -224,5 +228,58 @@ int at_fork_in_child(void (*on_fork)()) { #endif } +InMemoryFile::InMemoryFile(void* handle) : handle_(handle) {} + +InMemoryFile::InMemoryFile(InMemoryFile&& rhs) { + std::swap(rhs.handle_, handle_); +} + +InMemoryFile& InMemoryFile::operator=(InMemoryFile&& rhs) { + std::swap(handle_, rhs.handle_); + return *this; +} + +#if defined(__linux__) || defined(__unix__) + +InMemoryFile::~InMemoryFile() { + /// NOTE(@dmehala): No need to close the fd since it is automatically handled + /// by `MFD_CLOEXEC`. + if (handle_ == nullptr) return; + int* data = static_cast(handle_); + close(*data); + delete (data); +} + +bool InMemoryFile::write_then_seal(const std::string& data) { + int fd = *static_cast(handle_); + + size_t written = write(fd, data.data(), data.size()); + if (written != data.size()) return false; + + return fcntl(fd, F_ADD_SEALS, + F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL) == 0; +} + +Expected InMemoryFile::make(StringView name) { + int fd = memfd_create(name.data(), MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd == -1) { + std::string err_msg = "failed to create an anonymous file. errno = "; + err_msg += std::to_string(errno); + return Error{Error::Code::OTHER, std::move(err_msg)}; + } + + int* handle = new int; + *handle = fd; + return InMemoryFile(handle); +} + +#else +InMemoryFile::~InMemoryFile() {} +bool InMemoryFile::write_then_seal(const std::string&) { return false; } +Expected InMemoryFile::make(StringView) { + return Error{Error::Code::NOT_IMPLEMENTED, "In-memory file not implemented"}; +} +#endif + } // namespace tracing } // namespace datadog diff --git a/src/datadog/platform_util.h b/src/datadog/platform_util.h index cfadfecd..5b0acb7a 100644 --- a/src/datadog/platform_util.h +++ b/src/datadog/platform_util.h @@ -2,11 +2,53 @@ // This component provides platform-dependent miscellanea. +#include +#include + #include namespace datadog { namespace tracing { +// A wrapper around an in-memory file descriptor. +// +// This class provides a simple interface to create an in-memory file, write +// data to it, and seal it to prevent further modifications. +// Currently, this implementation is only available on Linux as it relies on the +// `memfd_create` system call. +class InMemoryFile final { + /// Internal handle on the in-memory file. + void* handle_ = nullptr; + + /// Constructs an `InMemoryFile` from an existing handle. + /// + /// @param handle A valid handle to an in-memory file. + InMemoryFile(void* handle); + friend Expected make(StringView); + + public: + InMemoryFile(const InMemoryFile&) = delete; + InMemoryFile& operator=(const InMemoryFile&) = delete; + InMemoryFile(InMemoryFile&&); + InMemoryFile& operator=(InMemoryFile&&); + + ~InMemoryFile(); + + /// Writes content to the in-memory file and then seals it. + /// Once sealed, further modifications to the file are not possible. + /// + /// @param content The data to write into the in-memory file. + /// @return `true` if the write and seal operations succeed, `false` + /// otherwise. + bool write_then_seal(const std::string& content); + + /// Creates an in-memory file with the given name. + /// + /// @param name The name of the in-memoru file. + /// @return An `InMemoryFile` if successful, or an error on failure. + static Expected make(StringView name); +}; + // Hold host information mainly used for telemetry purposes // and for identifying a tracer. struct HostInfo final { diff --git a/src/datadog/random.cpp b/src/datadog/random.cpp index 3125df04..48138af2 100644 --- a/src/datadog/random.cpp +++ b/src/datadog/random.cpp @@ -82,5 +82,11 @@ std::string uuid() { return result; } +std::string short_uuid() { + std::bitset<64> high = random_uint64(); + std::string hexed = hex_padded(high.to_ullong()); + return hexed.substr(0, 8); +} + } // namespace tracing } // namespace datadog diff --git a/src/datadog/random.h b/src/datadog/random.h index c8f2ea17..2c3312eb 100644 --- a/src/datadog/random.h +++ b/src/datadog/random.h @@ -16,6 +16,7 @@ std::uint64_t random_uint64(); // Return a pseudo-random UUID in canonical string form as described in RFC // 4122. For example, "595af0a4-ff29-4a8c-9f37-f8ff055e0f80". std::string uuid(); +std::string short_uuid(); } // namespace tracing } // namespace datadog diff --git a/src/datadog/tracer.cpp b/src/datadog/tracer.cpp index 248f0b34..7b530258 100644 --- a/src/datadog/tracer.cpp +++ b/src/datadog/tracer.cpp @@ -19,7 +19,9 @@ #include "extraction_util.h" #include "hex.h" #include "json.hpp" +#include "msgpack.h" #include "platform_util.h" +#include "random.h" #include "span_data.h" #include "span_sampler.h" #include "tags.h" @@ -85,6 +87,8 @@ Tracer::Tracer(const FinalizedTracerConfig& config, log << "DATADOG TRACER CONFIGURATION - " << configuration; }); } + + store_config(); } std::string Tracer::config() const { @@ -110,6 +114,42 @@ std::string Tracer::config() const { return config.dump(); } +void Tracer::store_config() { + auto maybe_file = + InMemoryFile::make(std::string("datadog-tracer-info-") + short_uuid()); + if (auto error = maybe_file.if_error()) { + if (error->code == Error::Code::NOT_IMPLEMENTED) return; + + logger_->log_error("Failed to open anonymous file"); + return; + } + + metadata_file_ = std::make_unique(std::move(*maybe_file)); + + auto defaults = config_manager_->span_defaults(); + + std::string buffer; + buffer.reserve(1024); + + // clang-format off + msgpack::pack_map( + buffer, + "schema_version", [&](auto& buffer) { msgpack::pack_integer(buffer, std::uint64_t(1)); return Expected{}; }, + "runtime_id", [&](auto& buffer) { return msgpack::pack_string(buffer, runtime_id_.string()); }, + "tracer_version", [&](auto& buffer) { return msgpack::pack_string(buffer, signature_.library_version); }, + "tracer_language", [&](auto& buffer) { return msgpack::pack_string(buffer, signature_.library_language); }, + "hostname", [&](auto& buffer) { return msgpack::pack_string(buffer, hostname_.value_or("")); }, + "service_name", [&](auto& buffer) { return msgpack::pack_string(buffer, defaults->service); }, + "service_env", [&](auto& buffer) { return msgpack::pack_string(buffer, defaults->environment); }, + "service_version", [&](auto& buffer) { return msgpack::pack_string(buffer, defaults->version); } + ); + // clang-format on + + if (!metadata_file_->write_then_seal(buffer)) { + logger_->log_error("Either failed to write or seal the configuration file"); + } +} + Span Tracer::create_span() { return create_span(SpanConfig{}); } Span Tracer::create_span(const SpanConfig& config) {