Skip to content

Commit 14c8dee

Browse files
committed
feat: store tracer configuration in-memory file
Resolves [APMAPI-1066]
1 parent f590dce commit 14c8dee

File tree

6 files changed

+157
-1
lines changed

6 files changed

+157
-1
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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "platform_util.h"
22

3+
#include <cassert>
34
// clang-format off
45
#if defined(__x86_64__) || defined(_M_X64)
56
# define DD_SDK_CPU_ARCH "x86_64"
@@ -25,6 +26,10 @@
2526
# define DD_SDK_KERNEL "Linux"
2627
# include "string_util.h"
2728
# include <fstream>
29+
# include <sys/types.h>
30+
# include <sys/mman.h>
31+
# include <fcntl.h>
32+
# include <errno.h>
2833
# endif
2934
#elif defined(_MSC_VER)
3035
# include <windows.h>
@@ -98,7 +103,7 @@ std::tuple<std::string, std::string> get_windows_info() {
98103
// application manifest, which is the lowest version supported by the
99104
// application. Use `RtlGetVersion` to obtain the accurate OS version
100105
// regardless of the manifest.
101-
using RtlGetVersion = auto(*)(LPOSVERSIONINFOEXW)->NTSTATUS;
106+
using RtlGetVersion = auto (*)(LPOSVERSIONINFOEXW)->NTSTATUS;
102107

103108
RtlGetVersion func =
104109
(RtlGetVersion)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion");
@@ -224,5 +229,56 @@ int at_fork_in_child(void (*on_fork)()) {
224229
#endif
225230
}
226231

232+
InMemoryFile::InMemoryFile(void* handle) : handle_(handle) {}
233+
234+
InMemoryFile::InMemoryFile(InMemoryFile&& rhs) {
235+
std::swap(rhs.handle_, handle_);
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+
delete (data);
250+
}
251+
252+
bool InMemoryFile::write_then_seal(const std::string& data) {
253+
int fd = *static_cast<int*>(handle_);
254+
255+
size_t written = write(fd, data.data(), data.size());
256+
if (written != data.size()) return false;
257+
258+
return fcntl(fd, F_ADD_SEALS,
259+
F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL) == 0;
260+
}
261+
262+
Expected<InMemoryFile> InMemoryFile::make(std::string_view name) {
263+
int fd = memfd_create(name.data(), MFD_CLOEXEC | MFD_ALLOW_SEALING);
264+
if (fd == -1) {
265+
std::string err_msg = "failed to create an anonymous file. errno = ";
266+
err_msg += std::to_string(errno);
267+
return Error{Error::Code::OTHER, std::move(err_msg)};
268+
}
269+
270+
int* handle = new int;
271+
*handle = fd;
272+
return InMemoryFile(handle);
273+
}
274+
275+
#else
276+
InMemoryFile::~InMemoryFile() {}
277+
bool InMemoryFile::write_then_seal(const std::string&) { return false; }
278+
Expected<InMemoryFile> InMemoryFile::make(StringView) {
279+
return Error{Error::Code::NOT_IMPLEMENTED, "In-memory file not implemented"};
280+
}
281+
#endif
282+
227283
} // namespace tracing
228284
} // namespace datadog

src/datadog/platform_util.h

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

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

5+
#include <datadog/expected.h>
6+
#include <datadog/optional.h>
7+
#include <datadog/string_view.h>
8+
59
#include <string>
610

711
namespace datadog {
812
namespace tracing {
913

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

1315
#include <algorithm>
1416
#include <cassert>
@@ -19,7 +21,9 @@
1921
#include "extraction_util.h"
2022
#include "hex.h"
2123
#include "json.hpp"
24+
#include "msgpack.h"
2225
#include "platform_util.h"
26+
#include "random.h"
2327
#include "span_data.h"
2428
#include "span_sampler.h"
2529
#include "tags.h"
@@ -85,6 +89,8 @@ Tracer::Tracer(const FinalizedTracerConfig& config,
8589
log << "DATADOG TRACER CONFIGURATION - " << configuration;
8690
});
8791
}
92+
93+
store_config();
8894
}
8995

9096
std::string Tracer::config() const {
@@ -110,6 +116,42 @@ std::string Tracer::config() const {
110116
return config.dump();
111117
}
112118

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

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

0 commit comments

Comments
 (0)