Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/datadog/tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct SpanConfig;
class TraceSampler;
class SpanSampler;
class IDGenerator;
class InMemoryFile;

class Tracer {
std::shared_ptr<Logger> logger_;
Expand All @@ -47,6 +48,10 @@ class Tracer {
Optional<std::string> 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<InMemoryFile> metadata_file_;

public:
// Create a tracer configured using the specified `config`, and optionally:
Expand Down Expand Up @@ -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
Expand Down
57 changes: 57 additions & 0 deletions src/datadog/platform_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
# define DD_SDK_KERNEL "Linux"
# include "string_util.h"
# include <fstream>
# include <sys/types.h>
# include <sys/mman.h>
# include <fcntl.h>
# include <errno.h>
# endif
#elif defined(_MSC_VER)
# include <windows.h>
Expand Down Expand Up @@ -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<int*>(handle_);
close(*data);
delete (data);
}

bool InMemoryFile::write_then_seal(const std::string& data) {
int fd = *static_cast<int*>(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> InMemoryFile::make(StringView name) {
int fd = memfd_create(name.data(), MFD_CLOEXEC | MFD_ALLOW_SEALING);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: The RFC suggest to use libdatadog for this. Is this temporary or will the cpp tracer now follow that advice?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It suggest, doesn't enforce. Since dd-trace-cpp has no dependency on libdatadog, it will not be that efficient to use it.

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> InMemoryFile::make(StringView) {
return Error{Error::Code::NOT_IMPLEMENTED, "In-memory file not implemented"};
}
#endif

} // namespace tracing
} // namespace datadog
42 changes: 42 additions & 0 deletions src/datadog/platform_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,53 @@

// This component provides platform-dependent miscellanea.

#include <datadog/expected.h>
#include <datadog/string_view.h>

#include <string>

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<InMemoryFile> 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<InMemoryFile> make(StringView name);
};

// Hold host information mainly used for telemetry purposes
// and for identifying a tracer.
struct HostInfo final {
Expand Down
6 changes: 6 additions & 0 deletions src/datadog/random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions src/datadog/random.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
40 changes: 40 additions & 0 deletions src/datadog/tracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -85,6 +87,8 @@ Tracer::Tracer(const FinalizedTracerConfig& config,
log << "DATADOG TRACER CONFIGURATION - " << configuration;
});
}

store_config();
}

std::string Tracer::config() const {
Expand All @@ -110,6 +114,42 @@ std::string Tracer::config() const {
return config.dump();
}

void Tracer::store_config() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this lead to incorrect usage? If store_config is called twice, will that result in multiple descriptors for this process? If so, what are the implications of that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every call to this method will result in N file descriptors. Since Tracer::store_config() is a private method and we control how it’s called, I believe we can ensure it’s used correctly and avoid any unintended issues.

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<InMemoryFile>(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<void>{}; },
"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) {
Expand Down
Loading