Skip to content

Commit f3ebccc

Browse files
authored
feat: store tracer configuration in an in-memory file (#184)
Resolves [APMAPI-1066]
1 parent 62a9e91 commit f3ebccc

File tree

6 files changed

+154
-0
lines changed

6 files changed

+154
-0
lines changed

include/datadog/tracer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct SpanConfig;
3131
class TraceSampler;
3232
class SpanSampler;
3333
class IDGenerator;
34+
class InMemoryFile;
3435

3536
class Tracer {
3637
std::shared_ptr<Logger> logger_;
@@ -47,6 +48,10 @@ class Tracer {
4748
Optional<std::string> hostname_;
4849
std::size_t tags_header_max_size_;
4950
bool sampling_delegation_enabled_;
51+
// Store the tracer configuration in an in-memory file, allowing it to be
52+
// read to determine if the process is instrumented with a tracer and to
53+
// retrieve relevant tracing information.
54+
std::shared_ptr<InMemoryFile> metadata_file_;
5055

5156
public:
5257
// Create a tracer configured using the specified `config`, and optionally:
@@ -81,6 +86,9 @@ class Tracer {
8186
// Return a JSON object describing this Tracer's configuration. It is the same
8287
// JSON object that was logged when this Tracer was created.
8388
std::string config() const;
89+
90+
private:
91+
void store_config();
8492
};
8593

8694
} // namespace tracing

src/datadog/platform_util.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
# define DD_SDK_KERNEL "Linux"
2626
# include "string_util.h"
2727
# include <fstream>
28+
# include <sys/types.h>
29+
# include <sys/mman.h>
30+
# include <fcntl.h>
31+
# include <errno.h>
2832
# endif
2933
#elif defined(_MSC_VER)
3034
# include <windows.h>
@@ -224,5 +228,58 @@ int at_fork_in_child(void (*on_fork)()) {
224228
#endif
225229
}
226230

231+
InMemoryFile::InMemoryFile(void* handle) : handle_(handle) {}
232+
233+
InMemoryFile::InMemoryFile(InMemoryFile&& rhs) {
234+
std::swap(rhs.handle_, handle_);
235+
}
236+
237+
InMemoryFile& InMemoryFile::operator=(InMemoryFile&& rhs) {
238+
std::swap(handle_, rhs.handle_);
239+
return *this;
240+
}
241+
242+
#if defined(__linux__) || defined(__unix__)
243+
244+
InMemoryFile::~InMemoryFile() {
245+
/// NOTE(@dmehala): No need to close the fd since it is automatically handled
246+
/// by `MFD_CLOEXEC`.
247+
if (handle_ == nullptr) return;
248+
int* data = static_cast<int*>(handle_);
249+
close(*data);
250+
delete (data);
251+
}
252+
253+
bool InMemoryFile::write_then_seal(const std::string& data) {
254+
int fd = *static_cast<int*>(handle_);
255+
256+
size_t written = write(fd, data.data(), data.size());
257+
if (written != data.size()) return false;
258+
259+
return fcntl(fd, F_ADD_SEALS,
260+
F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL) == 0;
261+
}
262+
263+
Expected<InMemoryFile> InMemoryFile::make(StringView name) {
264+
int fd = memfd_create(name.data(), MFD_CLOEXEC | MFD_ALLOW_SEALING);
265+
if (fd == -1) {
266+
std::string err_msg = "failed to create an anonymous file. errno = ";
267+
err_msg += std::to_string(errno);
268+
return Error{Error::Code::OTHER, std::move(err_msg)};
269+
}
270+
271+
int* handle = new int;
272+
*handle = fd;
273+
return InMemoryFile(handle);
274+
}
275+
276+
#else
277+
InMemoryFile::~InMemoryFile() {}
278+
bool InMemoryFile::write_then_seal(const std::string&) { return false; }
279+
Expected<InMemoryFile> InMemoryFile::make(StringView) {
280+
return Error{Error::Code::NOT_IMPLEMENTED, "In-memory file not implemented"};
281+
}
282+
#endif
283+
227284
} // namespace tracing
228285
} // namespace datadog

src/datadog/platform_util.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,53 @@
22

33
// This component provides platform-dependent miscellanea.
44

5+
#include <datadog/expected.h>
6+
#include <datadog/string_view.h>
7+
58
#include <string>
69

710
namespace datadog {
811
namespace tracing {
912

13+
// A wrapper around an in-memory file descriptor.
14+
//
15+
// This class provides a simple interface to create an in-memory file, write
16+
// data to it, and seal it to prevent further modifications.
17+
// Currently, this implementation is only available on Linux as it relies on the
18+
// `memfd_create` system call.
19+
class InMemoryFile final {
20+
/// Internal handle on the in-memory file.
21+
void* handle_ = nullptr;
22+
23+
/// Constructs an `InMemoryFile` from an existing handle.
24+
///
25+
/// @param handle A valid handle to an in-memory file.
26+
InMemoryFile(void* handle);
27+
friend Expected<InMemoryFile> make(StringView);
28+
29+
public:
30+
InMemoryFile(const InMemoryFile&) = delete;
31+
InMemoryFile& operator=(const InMemoryFile&) = delete;
32+
InMemoryFile(InMemoryFile&&);
33+
InMemoryFile& operator=(InMemoryFile&&);
34+
35+
~InMemoryFile();
36+
37+
/// Writes content to the in-memory file and then seals it.
38+
/// Once sealed, further modifications to the file are not possible.
39+
///
40+
/// @param content The data to write into the in-memory file.
41+
/// @return `true` if the write and seal operations succeed, `false`
42+
/// otherwise.
43+
bool write_then_seal(const std::string& content);
44+
45+
/// Creates an in-memory file with the given name.
46+
///
47+
/// @param name The name of the in-memoru file.
48+
/// @return An `InMemoryFile` if successful, or an error on failure.
49+
static Expected<InMemoryFile> make(StringView name);
50+
};
51+
1052
// Hold host information mainly used for telemetry purposes
1153
// and for identifying a tracer.
1254
struct HostInfo final {

src/datadog/random.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,11 @@ std::string uuid() {
8282
return result;
8383
}
8484

85+
std::string short_uuid() {
86+
std::bitset<64> high = random_uint64();
87+
std::string hexed = hex_padded(high.to_ullong());
88+
return hexed.substr(0, 8);
89+
}
90+
8591
} // namespace tracing
8692
} // namespace datadog

src/datadog/random.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ std::uint64_t random_uint64();
1616
// Return a pseudo-random UUID in canonical string form as described in RFC
1717
// 4122. For example, "595af0a4-ff29-4a8c-9f37-f8ff055e0f80".
1818
std::string uuid();
19+
std::string short_uuid();
1920

2021
} // namespace tracing
2122
} // namespace datadog

src/datadog/tracer.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
#include "extraction_util.h"
2020
#include "hex.h"
2121
#include "json.hpp"
22+
#include "msgpack.h"
2223
#include "platform_util.h"
24+
#include "random.h"
2325
#include "span_data.h"
2426
#include "span_sampler.h"
2527
#include "tags.h"
@@ -85,6 +87,8 @@ Tracer::Tracer(const FinalizedTracerConfig& config,
8587
log << "DATADOG TRACER CONFIGURATION - " << configuration;
8688
});
8789
}
90+
91+
store_config();
8892
}
8993

9094
std::string Tracer::config() const {
@@ -110,6 +114,42 @@ std::string Tracer::config() const {
110114
return config.dump();
111115
}
112116

117+
void Tracer::store_config() {
118+
auto maybe_file =
119+
InMemoryFile::make(std::string("datadog-tracer-info-") + short_uuid());
120+
if (auto error = maybe_file.if_error()) {
121+
if (error->code == Error::Code::NOT_IMPLEMENTED) return;
122+
123+
logger_->log_error("Failed to open anonymous file");
124+
return;
125+
}
126+
127+
metadata_file_ = std::make_unique<InMemoryFile>(std::move(*maybe_file));
128+
129+
auto defaults = config_manager_->span_defaults();
130+
131+
std::string buffer;
132+
buffer.reserve(1024);
133+
134+
// clang-format off
135+
msgpack::pack_map(
136+
buffer,
137+
"schema_version", [&](auto& buffer) { msgpack::pack_integer(buffer, std::uint64_t(1)); return Expected<void>{}; },
138+
"runtime_id", [&](auto& buffer) { return msgpack::pack_string(buffer, runtime_id_.string()); },
139+
"tracer_version", [&](auto& buffer) { return msgpack::pack_string(buffer, signature_.library_version); },
140+
"tracer_language", [&](auto& buffer) { return msgpack::pack_string(buffer, signature_.library_language); },
141+
"hostname", [&](auto& buffer) { return msgpack::pack_string(buffer, hostname_.value_or("")); },
142+
"service_name", [&](auto& buffer) { return msgpack::pack_string(buffer, defaults->service); },
143+
"service_env", [&](auto& buffer) { return msgpack::pack_string(buffer, defaults->environment); },
144+
"service_version", [&](auto& buffer) { return msgpack::pack_string(buffer, defaults->version); }
145+
);
146+
// clang-format on
147+
148+
if (!metadata_file_->write_then_seal(buffer)) {
149+
logger_->log_error("Either failed to write or seal the configuration file");
150+
}
151+
}
152+
113153
Span Tracer::create_span() { return create_span(SpanConfig{}); }
114154

115155
Span Tracer::create_span(const SpanConfig& config) {

0 commit comments

Comments
 (0)