From 06aab68124085e785225816aafd572914235164a Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Thu, 16 Oct 2025 17:46:05 +0200 Subject: [PATCH 01/13] feat: support process discovery v2 schema --- test/test_tracer.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/test_tracer.cpp b/test/test_tracer.cpp index 8efe47f4..b63c2641 100644 --- a/test/test_tracer.cpp +++ b/test/test_tracer.cpp @@ -1851,7 +1851,9 @@ TEST_TRACER("APM tracing disabled") { auto finalized_config = finalize_config(config, clock); REQUIRE(finalized_config); Tracer tracer{*finalized_config}; - { auto root1 = tracer.create_span(); } + { + auto root1 = tracer.create_span(); + } REQUIRE(collector->chunks.size() == 1); REQUIRE(collector->chunks.front().size() == 1); @@ -1930,7 +1932,9 @@ TEST_TRACER("APM tracing disabled") { // When APM Tracing is disabled, we allow one trace per second for service // liveness. To ensure consistency, consume the limiter slot. - { tracer.create_span(); } + { + tracer.create_span(); + } collector->chunks.clear(); // Case 1: extracted context with priority, but no `_dd.p.ts` → depends if @@ -2087,6 +2091,7 @@ TEST_TRACER("process discovery") { CHECK(!fd); } #endif + TEST_TRACER("_dd.p.ksr is NOT set when overriding the sampling decision") { const auto collector = std::make_shared(); From 5e63ca5d7e4a66a1ba75c9b959709280129fe94a Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Thu, 23 Oct 2025 07:22:46 -0400 Subject: [PATCH 02/13] fix: format --- test/test_tracer.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/test_tracer.cpp b/test/test_tracer.cpp index b63c2641..26a2b233 100644 --- a/test/test_tracer.cpp +++ b/test/test_tracer.cpp @@ -1851,9 +1851,7 @@ TEST_TRACER("APM tracing disabled") { auto finalized_config = finalize_config(config, clock); REQUIRE(finalized_config); Tracer tracer{*finalized_config}; - { - auto root1 = tracer.create_span(); - } + { auto root1 = tracer.create_span(); } REQUIRE(collector->chunks.size() == 1); REQUIRE(collector->chunks.front().size() == 1); @@ -1932,9 +1930,7 @@ TEST_TRACER("APM tracing disabled") { // When APM Tracing is disabled, we allow one trace per second for service // liveness. To ensure consistency, consume the limiter slot. - { - tracer.create_span(); - } + { tracer.create_span(); } collector->chunks.clear(); // Case 1: extracted context with priority, but no `_dd.p.ts` → depends if From 87c7c30dc8e245224f2ae466a773a08e5f1530c1 Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Tue, 28 Oct 2025 19:15:49 -0400 Subject: [PATCH 03/13] chore: split platform code per translation unit --- src/datadog/platform_util.cpp | 445 +------------------------- src/datadog/platform_util.h | 13 + src/datadog/platform_util_darwin.cpp | 147 +++++++++ src/datadog/platform_util_unix.cpp | 278 ++++++++++++++++ src/datadog/platform_util_windows.cpp | 196 ++++++++++++ 5 files changed, 639 insertions(+), 440 deletions(-) create mode 100644 src/datadog/platform_util_darwin.cpp create mode 100644 src/datadog/platform_util_unix.cpp create mode 100644 src/datadog/platform_util_windows.cpp diff --git a/src/datadog/platform_util.cpp b/src/datadog/platform_util.cpp index 854c1178..ee9b50e1 100644 --- a/src/datadog/platform_util.cpp +++ b/src/datadog/platform_util.cpp @@ -1,442 +1,7 @@ -#include "platform_util.h" - -#include -#include -#include -#include - -// clang-format off -#if defined(__x86_64__) || defined(_M_X64) -# define DD_SDK_CPU_ARCH "x86_64" -#elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86) -# define DD_SDK_CPU_ARCH "x86" -#elif defined(__aarch64__) || defined(_M_ARM64) -# define DD_SDK_CPU_ARCH "arm64" +#ifdef _MSC_VER +#include "platform_util_windows.cpp" +#elif defined(__APPLE__) || defined(__FreeBSD__) +#include "platform_util_darwin.cpp" #else -# define DD_SDK_CPU_ARCH "unknown" +#include "platform_util_unix.cpp" #endif - -#if defined(__APPLE__) || defined(__linux__) || defined(__unix__) -# include -# include -# include -# include -# if defined(__APPLE__) -# include -# define DD_SDK_OS "Darwin" -# define DD_SDK_KERNEL "Darwin" -# elif defined(__linux__) || defined(__unix__) -# define DD_SDK_OS "GNU/Linux" -# define DD_SDK_KERNEL "Linux" -# include "string_util.h" -# include -# include -# include -# include -# include -# include -# include -# endif -#elif defined(_MSC_VER) -# include -# include -# include -#endif -// clang-format on - -namespace datadog { -namespace tracing { -namespace { - -#if defined(__APPLE__) -std::string get_os_version() { - char os_version[20] = ""; - size_t len = sizeof(os_version); - - sysctlbyname("kern.osproductversion", os_version, &len, NULL, 0); - return os_version; -} -#elif defined(__linux__) -std::string get_os_version() { - std::ifstream os_release_file("/etc/os-release"); - if (!os_release_file.is_open()) { - return ""; - } - - std::string line; - - while (std::getline(os_release_file, line)) { - size_t pos = line.find('='); - if (pos == std::string::npos) { - continue; - } - - std::string key = line.substr(0, pos); - to_lower(key); - if (key == "version") { - std::string value = line.substr(pos + 1); - return value; - } - } - - return ""; -} -#endif - -#if defined(__APPLE__) || defined(__linux__) || defined(__unix__) -HostInfo _get_host_info() { - HostInfo res; - - struct utsname buffer; - if (uname(&buffer) != 0) { - return res; - } - - res.os = DD_SDK_OS; - res.os_version = get_os_version(); - res.hostname = buffer.nodename; - res.cpu_architecture = DD_SDK_CPU_ARCH; - res.kernel_name = DD_SDK_KERNEL; - res.kernel_version = buffer.version; - res.kernel_release = buffer.release; - - return res; -} -#elif defined(_MSC_VER) -std::tuple get_windows_info() { - // NOTE(@dmehala): Retrieving the Windows version has been complicated since - // Windows 8.1. The `GetVersion` function and its variants depend on the - // application manifest, which is the lowest version supported by the - // application. Use `RtlGetVersion` to obtain the accurate OS version - // regardless of the manifest. - using RtlGetVersion = auto(*)(LPOSVERSIONINFOEXW)->NTSTATUS; - - RtlGetVersion func = - (RtlGetVersion)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); - - if (func) { - OSVERSIONINFOEXW os_info; - ZeroMemory(&os_info, sizeof(OSVERSIONINFO)); - os_info.dwOSVersionInfoSize = sizeof(os_info); - - if (func(&os_info) == 0) { - switch (os_info.dwMajorVersion) { - case 5: { - switch (os_info.dwMinorVersion) { - case 0: - return {"Windows 2000", "NT 5.0"}; - case 1: - return {"Windows XP", "NT 5.1"}; - case 2: - return {"Windows XP", "NT 5.2"}; - default: - return {"Windows XP", "NT 5.x"}; - } - }; break; - case 6: { - switch (os_info.dwMinorVersion) { - case 0: - return {"Windows Vista", "NT 6.0"}; - case 1: - return {"Windows 7", "NT 6.1"}; - case 2: - return {"Windows 8", "NT 6.2"}; - case 3: - return {"Windows 8.1", "NT 6.3"}; - default: - return {"Windows 8.1", "NT 6.x"}; - } - }; break; - case 10: { - if (os_info.dwBuildNumber >= 10240 && os_info.dwBuildNumber < 22000) { - return {"Windows 10", "NT 10.0"}; - } else if (os_info.dwBuildNumber >= 22000) { - return {"Windows 11", "21H2"}; - } - }; break; - } - } - } - - return {"", ""}; -} - -HostInfo _get_host_info() { - HostInfo host; - host.cpu_architecture = DD_SDK_CPU_ARCH; - - auto [os, os_version] = get_windows_info(); - host.os = std::move(os); - host.os_version = std::move(os_version); - - char buffer[256]; - if (0 == gethostname(buffer, sizeof(buffer))) { - host.hostname = buffer; - } - - return host; -} -#else -HostInfo _get_host_info() { - HostInfo res; - return res; -} -#endif - -} // namespace - -HostInfo get_host_info() { - static const HostInfo host_info = _get_host_info(); - return host_info; -} - -std::string get_hostname() { return get_host_info().hostname; } - -int get_process_id() { -#if defined(_MSC_VER) - return GetCurrentProcessId(); -#else - return ::getpid(); -#endif -} - -std::string get_process_name() { -#if defined(__APPLE__) || defined(__FreeBSD__) - const char* process_name = getprogname(); - return (process_name != nullptr) ? process_name : "unknown-service"; -#elif defined(__linux__) || defined(__unix__) - return program_invocation_short_name; -#elif defined(_MSC_VER) - TCHAR exe_name[MAX_PATH]; - if (GetModuleFileName(NULL, exe_name, MAX_PATH) <= 0) { - return "unknown-service"; - } -#ifdef UNICODE - std::wstring wStr(exe_name); - std::string path = std::string(wStr.begin(), wStr.end()); -#else - std::string path = std::string(exe_name); -#endif - return path.substr(path.find_last_of("/\\") + 1); -#else - return "unknown-service"; -#endif -} - -int at_fork_in_child(void (*on_fork)()) { -#if defined(_MSC_VER) - // Windows does not have `fork`, and so this is not relevant there. - (void)on_fork; - return 0; -#else - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html - return pthread_atfork(/*before fork*/ nullptr, /*in parent*/ nullptr, - /*in child*/ 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 container { -namespace { -#if defined(__linux__) || defined(__unix__) -/// Magic numbers from linux/magic.h: -/// -constexpr uint64_t TMPFS_MAGIC = 0x01021994; -constexpr uint64_t CGROUP_SUPER_MAGIC = 0x27e0eb; -constexpr uint64_t CGROUP2_SUPER_MAGIC = 0x63677270; - -/// Magic number from linux/proc_ns.h: -/// -constexpr ino_t HOST_CGROUP_NAMESPACE_INODE = 0xeffffffb; - -/// Represents the cgroup version of the current process. -enum class Cgroup : char { v1, v2 }; - -Optional get_inode(std::string_view path) { - struct stat buf; - if (stat(path.data(), &buf) != 0) { - return nullopt; - } - - return buf.st_ino; -} - -// Host namespace inode number are hardcoded, which allows for dectection of -// whether the binary is running in host or not. However, it does not work when -// running in a Docker in Docker environment. -bool is_running_in_host_namespace() { - // linux procfs file that represents the cgroup namespace of the current - // process. - if (auto inode = get_inode("/proc/self/ns/cgroup")) { - return *inode == HOST_CGROUP_NAMESPACE_INODE; - } - - return false; -} - -Optional get_cgroup_version() { - struct statfs buf; - - if (statfs("/sys/fs/cgroup", &buf) != 0) { - return nullopt; - } - - if (buf.f_type == CGROUP_SUPER_MAGIC || buf.f_type == TMPFS_MAGIC) - return Cgroup::v1; - else if (buf.f_type == CGROUP2_SUPER_MAGIC) - return Cgroup::v2; - - return nullopt; -} - -Optional find_container_id_from_cgroup() { - auto cgroup_fd = std::ifstream("/proc/self/cgroup", std::ios::in); - if (!cgroup_fd.is_open()) return nullopt; - - return find_container_id(cgroup_fd); -} -#endif -} // namespace - -Optional find_container_id(std::istream& source) { - std::string line; - - // Look for Docker container IDs in the basic format: `docker-.scope`. - constexpr std::string_view docker_str = "docker-"; - - while (std::getline(source, line)) { - // Example: - // `0::/system.slice/docker-abcdef0123456789abcdef0123456789.scope` - if (auto beg = line.find(docker_str); beg != std::string::npos) { - beg += docker_str.size(); - auto end = line.find(".scope", beg); - if (end == std::string::npos || end - beg <= 0) { - continue; - } - - auto container_id = line.substr(beg, end - beg); - return container_id; - } - } - - // Reset the stream to the beginning. - source.clear(); - source.seekg(0); - - // Perform a second pass using a regular expression for matching container IDs - // in a Fargate environment. This two-step approach is used because STL - // `regex` is relatively slow, so we avoid using it unless necessary. - static const std::string uuid_regex_str = - "[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}" - "|(?:[0-9a-f]{8}(?:-[0-9a-f]{4}){4}$)"; - static const std::string container_regex_str = "[0-9a-f]{64}"; - static const std::string task_regex_str = "[0-9a-f]{32}-\\d+"; - static const std::regex path_reg("(?:.+)?(" + uuid_regex_str + "|" + - container_regex_str + "|" + task_regex_str + - ")(?:\\.scope)?$"); - - while (std::getline(source, line)) { - // Example: - // `0::/system.slice/docker-abcdef0123456789abcdef0123456789.scope` - std::smatch match; - if (std::regex_match(line, match, path_reg) && match.size() == 2) { - assert(match.ready()); - assert(match.size() == 2); - - return match.str(1); - } - } - - return nullopt; -} - -Optional get_id() { -#if defined(__linux__) || defined(__unix__) - auto maybe_cgroup = get_cgroup_version(); - if (!maybe_cgroup) return nullopt; - - ContainerID id; - switch (*maybe_cgroup) { - case Cgroup::v1: { - if (auto maybe_id = find_container_id_from_cgroup()) { - id.value = *maybe_id; - id.type = ContainerID::Type::container_id; - break; - } - } - // NOTE(@dmehala): failed to find the container ID, try getting the cgroup - // inode. - [[fallthrough]]; - case Cgroup::v2: { - if (!is_running_in_host_namespace()) { - auto maybe_inode = get_inode("/sys/fs/cgroup"); - if (maybe_inode) { - id.type = ContainerID::Type::cgroup_inode; - id.value = std::to_string(*maybe_inode); - } - } - }; break; - } - - return id; -#else - return nullopt; -#endif -} - -} // namespace container - -} // namespace tracing -} // namespace datadog diff --git a/src/datadog/platform_util.h b/src/datadog/platform_util.h index a71d2df6..4ac81ead 100644 --- a/src/datadog/platform_util.h +++ b/src/datadog/platform_util.h @@ -6,6 +6,19 @@ #include #include +#include + +// clang-format off +#if defined(__x86_64__) || defined(_M_X64) +# define DD_SDK_CPU_ARCH "x86_64" +#elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86) +# define DD_SDK_CPU_ARCH "x86" +#elif defined(__aarch64__) || defined(_M_ARM64) +# define DD_SDK_CPU_ARCH "arm64" +#else +# define DD_SDK_CPU_ARCH "unknown" +#endif +// clang-format on namespace datadog { namespace tracing { diff --git a/src/datadog/platform_util_darwin.cpp b/src/datadog/platform_util_darwin.cpp new file mode 100644 index 00000000..93e1b179 --- /dev/null +++ b/src/datadog/platform_util_darwin.cpp @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "platform_util.h" + +#define DD_SDK_OS "Darwin" +#define DD_SDK_KERNEL "Darwin" + +namespace datadog { +namespace tracing { +namespace { + +std::string get_os_version() { + char os_version[20] = ""; + size_t len = sizeof(os_version); + + sysctlbyname("kern.osproductversion", os_version, &len, NULL, 0); + return os_version; +} + +HostInfo _get_host_info() { + HostInfo res; + + struct utsname buffer; + if (uname(&buffer) != 0) { + return res; + } + + res.os = DD_SDK_OS; + res.os_version = get_os_version(); + res.hostname = buffer.nodename; + res.cpu_architecture = DD_SDK_CPU_ARCH; + res.kernel_name = DD_SDK_KERNEL; + res.kernel_version = buffer.version; + res.kernel_release = buffer.release; + + return res; +} + +} // namespace + +HostInfo get_host_info() { + static const HostInfo host_info = _get_host_info(); + return host_info; +} + +std::string get_hostname() { return get_host_info().hostname; } + +int get_process_id() { return ::getpid(); } + +std::string get_process_name() { + const char* process_name = getprogname(); + return (process_name != nullptr) ? process_name : "unknown-service"; +} + +int at_fork_in_child(void (*on_fork)()) { + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html + return pthread_atfork(/*before fork*/ nullptr, /*in parent*/ nullptr, + /*in child*/ on_fork); +} + +InMemoryFile::InMemoryFile(void* handle) : handle_(handle) {} + +InMemoryFile::~InMemoryFile() {} + +InMemoryFile::InMemoryFile(InMemoryFile&& rhs) { + std::swap(rhs.handle_, handle_); +} + +InMemoryFile& InMemoryFile::operator=(InMemoryFile&& rhs) { + std::swap(handle_, rhs.handle_); + return *this; +} + +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"}; +} + +namespace container { + +Optional find_container_id(std::istream& source) { + std::string line; + + // Look for Docker container IDs in the basic format: `docker-.scope`. + constexpr std::string_view docker_str = "docker-"; + + while (std::getline(source, line)) { + // Example: + // `0::/system.slice/docker-abcdef0123456789abcdef0123456789.scope` + if (auto beg = line.find(docker_str); beg != std::string::npos) { + beg += docker_str.size(); + auto end = line.find(".scope", beg); + if (end == std::string::npos || end - beg <= 0) { + continue; + } + + auto container_id = line.substr(beg, end - beg); + return container_id; + } + } + + // Reset the stream to the beginning. + source.clear(); + source.seekg(0); + + // Perform a second pass using a regular expression for matching container IDs + // in a Fargate environment. This two-step approach is used because STL + // `regex` is relatively slow, so we avoid using it unless necessary. + static const std::string uuid_regex_str = + "[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}" + "|(?:[0-9a-f]{8}(?:-[0-9a-f]{4}){4}$)"; + static const std::string container_regex_str = "[0-9a-f]{64}"; + static const std::string task_regex_str = "[0-9a-f]{32}-\\d+"; + static const std::regex path_reg("(?:.+)?(" + uuid_regex_str + "|" + + container_regex_str + "|" + task_regex_str + + ")(?:\\.scope)?$"); + + while (std::getline(source, line)) { + // Example: + // `0::/system.slice/docker-abcdef0123456789abcdef0123456789.scope` + std::smatch match; + if (std::regex_match(line, match, path_reg) && match.size() == 2) { + assert(match.ready()); + assert(match.size() == 2); + + return match.str(1); + } + } + + return nullopt; +} + +Optional get_id() { return nullopt; } + +} // namespace container + +} // namespace tracing +} // namespace datadog diff --git a/src/datadog/platform_util_unix.cpp b/src/datadog/platform_util_unix.cpp new file mode 100644 index 00000000..1797b418 --- /dev/null +++ b/src/datadog/platform_util_unix.cpp @@ -0,0 +1,278 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "platform_util.h" +#include "string_util.h" + +#define DD_SDK_OS "GNU/Linux" +#define DD_SDK_KERNEL "Linux" + +namespace datadog { +namespace tracing { +namespace { + +std::string get_os_version() { + std::ifstream os_release_file("/etc/os-release"); + if (!os_release_file.is_open()) { + return ""; + } + + std::string line; + + while (std::getline(os_release_file, line)) { + size_t pos = line.find('='); + if (pos == std::string::npos) { + continue; + } + + std::string key = line.substr(0, pos); + to_lower(key); + if (key == "version") { + std::string value = line.substr(pos + 1); + return value; + } + } + + return ""; +} + +HostInfo _get_host_info() { + HostInfo res; + + struct utsname buffer; + if (uname(&buffer) != 0) { + return res; + } + + res.os = DD_SDK_OS; + res.os_version = get_os_version(); + res.hostname = buffer.nodename; + res.cpu_architecture = DD_SDK_CPU_ARCH; + res.kernel_name = DD_SDK_KERNEL; + res.kernel_version = buffer.version; + res.kernel_release = buffer.release; + + return res; +} + +} // namespace + +HostInfo get_host_info() { + static const HostInfo host_info = _get_host_info(); + return host_info; +} + +std::string get_hostname() { return get_host_info().hostname; } + +int get_process_id() { return ::getpid(); } + +std::string get_process_name() { return program_invocation_short_name; } + +int at_fork_in_child(void (*on_fork)()) { + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html + return pthread_atfork(/*before fork*/ nullptr, /*in parent*/ nullptr, + /*in child*/ on_fork); +} + +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; +} + +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); +} + +namespace container { +namespace { +/// Magic numbers from linux/magic.h: +/// +constexpr uint64_t TMPFS_MAGIC = 0x01021994; +constexpr uint64_t CGROUP_SUPER_MAGIC = 0x27e0eb; +constexpr uint64_t CGROUP2_SUPER_MAGIC = 0x63677270; + +/// Magic number from linux/proc_ns.h: +/// +constexpr ino_t HOST_CGROUP_NAMESPACE_INODE = 0xeffffffb; + +/// Represents the cgroup version of the current process. +enum class Cgroup : char { v1, v2 }; + +Optional get_inode(std::string_view path) { + struct stat buf; + if (stat(path.data(), &buf) != 0) { + return nullopt; + } + + return buf.st_ino; +} + +// Host namespace inode number are hardcoded, which allows for dectection of +// whether the binary is running in host or not. However, it does not work when +// running in a Docker in Docker environment. +bool is_running_in_host_namespace() { + // linux procfs file that represents the cgroup namespace of the current + // process. + if (auto inode = get_inode("/proc/self/ns/cgroup")) { + return *inode == HOST_CGROUP_NAMESPACE_INODE; + } + + return false; +} + +Optional get_cgroup_version() { + struct statfs buf; + + if (statfs("/sys/fs/cgroup", &buf) != 0) { + return nullopt; + } + + if (buf.f_type == CGROUP_SUPER_MAGIC || buf.f_type == TMPFS_MAGIC) + return Cgroup::v1; + else if (buf.f_type == CGROUP2_SUPER_MAGIC) + return Cgroup::v2; + + return nullopt; +} + +Optional find_container_id_from_cgroup() { + auto cgroup_fd = std::ifstream("/proc/self/cgroup", std::ios::in); + if (!cgroup_fd.is_open()) return nullopt; + + return find_container_id(cgroup_fd); +} +} // namespace + +Optional find_container_id(std::istream& source) { + std::string line; + + // Look for Docker container IDs in the basic format: `docker-.scope`. + constexpr std::string_view docker_str = "docker-"; + + while (std::getline(source, line)) { + // Example: + // `0::/system.slice/docker-abcdef0123456789abcdef0123456789.scope` + if (auto beg = line.find(docker_str); beg != std::string::npos) { + beg += docker_str.size(); + auto end = line.find(".scope", beg); + if (end == std::string::npos || end - beg <= 0) { + continue; + } + + auto container_id = line.substr(beg, end - beg); + return container_id; + } + } + + // Reset the stream to the beginning. + source.clear(); + source.seekg(0); + + // Perform a second pass using a regular expression for matching container IDs + // in a Fargate environment. This two-step approach is used because STL + // `regex` is relatively slow, so we avoid using it unless necessary. + static const std::string uuid_regex_str = + "[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}" + "|(?:[0-9a-f]{8}(?:-[0-9a-f]{4}){4}$)"; + static const std::string container_regex_str = "[0-9a-f]{64}"; + static const std::string task_regex_str = "[0-9a-f]{32}-\\d+"; + static const std::regex path_reg("(?:.+)?(" + uuid_regex_str + "|" + + container_regex_str + "|" + task_regex_str + + ")(?:\\.scope)?$"); + + while (std::getline(source, line)) { + // Example: + // `0::/system.slice/docker-abcdef0123456789abcdef0123456789.scope` + std::smatch match; + if (std::regex_match(line, match, path_reg) && match.size() == 2) { + assert(match.ready()); + assert(match.size() == 2); + + return match.str(1); + } + } + + return nullopt; +} + +Optional get_id() { + auto maybe_cgroup = get_cgroup_version(); + if (!maybe_cgroup) return nullopt; + + ContainerID id; + switch (*maybe_cgroup) { + case Cgroup::v1: { + if (auto maybe_id = find_container_id_from_cgroup()) { + id.value = *maybe_id; + id.type = ContainerID::Type::container_id; + break; + } + } + // NOTE(@dmehala): failed to find the container ID, try getting the cgroup + // inode. + [[fallthrough]]; + case Cgroup::v2: { + if (!is_running_in_host_namespace()) { + auto maybe_inode = get_inode("/sys/fs/cgroup"); + if (maybe_inode) { + id.type = ContainerID::Type::cgroup_inode; + id.value = std::to_string(*maybe_inode); + } + } + }; break; + } + + return id; +} + +} // namespace container + +} // namespace tracing +} // namespace datadog diff --git a/src/datadog/platform_util_windows.cpp b/src/datadog/platform_util_windows.cpp new file mode 100644 index 00000000..55fb2aac --- /dev/null +++ b/src/datadog/platform_util_windows.cpp @@ -0,0 +1,196 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "platform_util.h" + +namespace datadog { +namespace tracing { +namespace { + +std::tuple get_windows_info() { + // NOTE(@dmehala): Retrieving the Windows version has been complicated since + // Windows 8.1. The `GetVersion` function and its variants depend on the + // application manifest, which is the lowest version supported by the + // application. Use `RtlGetVersion` to obtain the accurate OS version + // regardless of the manifest. + using RtlGetVersion = auto (*)(LPOSVERSIONINFOEXW)->NTSTATUS; + + RtlGetVersion func = + (RtlGetVersion)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); + + if (func) { + OSVERSIONINFOEXW os_info; + ZeroMemory(&os_info, sizeof(OSVERSIONINFO)); + os_info.dwOSVersionInfoSize = sizeof(os_info); + + if (func(&os_info) == 0) { + switch (os_info.dwMajorVersion) { + case 5: { + switch (os_info.dwMinorVersion) { + case 0: + return {"Windows 2000", "NT 5.0"}; + case 1: + return {"Windows XP", "NT 5.1"}; + case 2: + return {"Windows XP", "NT 5.2"}; + default: + return {"Windows XP", "NT 5.x"}; + } + }; break; + case 6: { + switch (os_info.dwMinorVersion) { + case 0: + return {"Windows Vista", "NT 6.0"}; + case 1: + return {"Windows 7", "NT 6.1"}; + case 2: + return {"Windows 8", "NT 6.2"}; + case 3: + return {"Windows 8.1", "NT 6.3"}; + default: + return {"Windows 8.1", "NT 6.x"}; + } + }; break; + case 10: { + if (os_info.dwBuildNumber >= 10240 && os_info.dwBuildNumber < 22000) { + return {"Windows 10", "NT 10.0"}; + } else if (os_info.dwBuildNumber >= 22000) { + return {"Windows 11", "21H2"}; + } + }; break; + } + } + } + + return {"", ""}; +} + +HostInfo _get_host_info() { + HostInfo host; + host.cpu_architecture = DD_SDK_CPU_ARCH; + + auto [os, os_version] = get_windows_info(); + host.os = std::move(os); + host.os_version = std::move(os_version); + + char buffer[256]; + if (0 == gethostname(buffer, sizeof(buffer))) { + host.hostname = buffer; + } + + return host; +} + +} // namespace + +HostInfo get_host_info() { + static const HostInfo host_info = _get_host_info(); + return host_info; +} + +std::string get_hostname() { return get_host_info().hostname; } + +int get_process_id() { return GetCurrentProcessId(); } + +std::string get_process_name() { + TCHAR exe_name[MAX_PATH]; + if (GetModuleFileName(NULL, exe_name, MAX_PATH) <= 0) { + return "unknown-service"; + } +#ifdef UNICODE + std::wstring wStr(exe_name); + std::string path = std::string(wStr.begin(), wStr.end()); +#else + std::string path = std::string(exe_name); +#endif +} + +int at_fork_in_child(void (*on_fork)()) { + // Windows does not have `fork`, and so this is not relevant there. + (void)on_fork; + return 0; +} + +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; +} + +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"}; +} + +namespace container { + +Optional find_container_id(std::istream& source) { + std::string line; + + // Look for Docker container IDs in the basic format: `docker-.scope`. + constexpr std::string_view docker_str = "docker-"; + + while (std::getline(source, line)) { + // Example: + // `0::/system.slice/docker-abcdef0123456789abcdef0123456789.scope` + if (auto beg = line.find(docker_str); beg != std::string::npos) { + beg += docker_str.size(); + auto end = line.find(".scope", beg); + if (end == std::string::npos || end - beg <= 0) { + continue; + } + + auto container_id = line.substr(beg, end - beg); + return container_id; + } + } + + // Reset the stream to the beginning. + source.clear(); + source.seekg(0); + + // Perform a second pass using a regular expression for matching container IDs + // in a Fargate environment. This two-step approach is used because STL + // `regex` is relatively slow, so we avoid using it unless necessary. + static const std::string uuid_regex_str = + "[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}" + "|(?:[0-9a-f]{8}(?:-[0-9a-f]{4}){4}$)"; + static const std::string container_regex_str = "[0-9a-f]{64}"; + static const std::string task_regex_str = "[0-9a-f]{32}-\\d+"; + static const std::regex path_reg("(?:.+)?(" + uuid_regex_str + "|" + + container_regex_str + "|" + task_regex_str + + ")(?:\\.scope)?$"); + + while (std::getline(source, line)) { + // Example: + // `0::/system.slice/docker-abcdef0123456789abcdef0123456789.scope` + std::smatch match; + if (std::regex_match(line, match, path_reg) && match.size() == 2) { + assert(match.ready()); + assert(match.size() == 2); + + return match.str(1); + } + } + + return nullopt; +} + +Optional get_id() { return nullopt; } + +} // namespace container + +} // namespace tracing +} // namespace datadog From 5f3b79c10dbe587055b1d5fe1cabb9dec6c20ccb Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Wed, 29 Oct 2025 11:58:46 -0400 Subject: [PATCH 04/13] fix compil ation --- BUILD.bazel | 14 +++++++++++++- CMakeLists.txt | 11 ++++++++++- MODULE.bazel | 6 +++++- WORKSPACE | 6 ++++++ src/datadog/platform_util.cpp | 7 ------- 5 files changed, 34 insertions(+), 10 deletions(-) delete mode 100644 src/datadog/platform_util.cpp diff --git a/BUILD.bazel b/BUILD.bazel index c81bd18f..51ea33b0 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -28,7 +28,6 @@ cc_library( "src/datadog/logger.cpp", "src/datadog/msgpack.cpp", "src/datadog/parse_util.cpp", - "src/datadog/platform_util.cpp", "src/datadog/propagation_style.cpp", "src/datadog/random.cpp", "src/datadog/rate.cpp", @@ -84,7 +83,20 @@ cc_library( "src/datadog/threaded_event_scheduler.h", "src/datadog/trace_sampler.h", "src/datadog/w3c_propagation.h", +] + select({ + "@platforms//os:windows": [ + "src/datadog/platform_util_win.cpp", ], + "@platforms//os:linux": [ + "src/datadog/platform_util_unix.cpp", + ], + "@platforms//os:macos": [ + "src/datadog/platform_util_darwin.cpp", + ], + "//conditions:default": [ + "src/datadog/platform_util_unknown.cpp", + ], +}), hdrs = [ "include/datadog/baggage.h", "include/datadog/cerr_logger.h", diff --git a/CMakeLists.txt b/CMakeLists.txt index 2569b429..c432a352 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,7 +184,6 @@ target_sources(dd-trace-cpp-objects src/datadog/logger.cpp src/datadog/msgpack.cpp src/datadog/parse_util.cpp - src/datadog/platform_util.cpp src/datadog/propagation_style.cpp src/datadog/random.cpp src/datadog/rate.cpp @@ -212,6 +211,16 @@ target_sources(dd-trace-cpp-objects src/datadog/w3c_propagation.cpp ) +if (WIN32) + target_sources(dd-trace-cpp-objects PRIVATE src/datadog/platform_util_windows.cpp) +elseif (APPLE) + target_sources(dd-trace-cpp-objects PRIVATE src/datadog/platform_util_darwin.cpp) +elseif (UNIX) + target_sources(dd-trace-cpp-objects PRIVATE src/datadog/platform_util_unix.cpp) +else () + target_sources(dd-trace-cpp-objects PRIVATE src/datadog/platform_util_unknown.cpp) +endif () + target_include_directories(dd-trace-cpp-objects PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/datadog diff --git a/MODULE.bazel b/MODULE.bazel index 809aa0b9..9943380a 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,8 +1,12 @@ module( name = "dd-trace-cpp", - version = "", + version = "2.0.0", ) +bazel_dep( + name = "platforms", + version = "0.0.10" +) bazel_dep( name = "bazel_skylib", version = "1.2.1", diff --git a/WORKSPACE b/WORKSPACE index c14e2091..6ea78dd3 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -26,3 +26,9 @@ http_archive( urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz"], sha256 = "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728", ) + +http_archive( + name = "platforms", + urls = ["https://github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz"], + sha256 = "8150406605389ececb6da07cbcb509d5637a3ab9a24bc69b1101531367d89d74", +) diff --git a/src/datadog/platform_util.cpp b/src/datadog/platform_util.cpp deleted file mode 100644 index ee9b50e1..00000000 --- a/src/datadog/platform_util.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifdef _MSC_VER -#include "platform_util_windows.cpp" -#elif defined(__APPLE__) || defined(__FreeBSD__) -#include "platform_util_darwin.cpp" -#else -#include "platform_util_unix.cpp" -#endif From 0b07150c18179d7913e74537f9995b7869515da9 Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Wed, 29 Oct 2025 12:02:04 -0400 Subject: [PATCH 05/13] fix format --- src/datadog/platform_util_windows.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datadog/platform_util_windows.cpp b/src/datadog/platform_util_windows.cpp index 55fb2aac..f44638e1 100644 --- a/src/datadog/platform_util_windows.cpp +++ b/src/datadog/platform_util_windows.cpp @@ -19,7 +19,7 @@ std::tuple get_windows_info() { // application manifest, which is the lowest version supported by the // application. Use `RtlGetVersion` to obtain the accurate OS version // regardless of the manifest. - using RtlGetVersion = auto (*)(LPOSVERSIONINFOEXW)->NTSTATUS; + using RtlGetVersion = auto(*)(LPOSVERSIONINFOEXW)->NTSTATUS; RtlGetVersion func = (RtlGetVersion)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); From ace002566e484bcfe75e7bc2915f9a764680daa4 Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Wed, 29 Oct 2025 12:05:28 -0400 Subject: [PATCH 06/13] fix bazel --- MODULE.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MODULE.bazel b/MODULE.bazel index 9943380a..b33d45ab 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -5,7 +5,7 @@ module( bazel_dep( name = "platforms", - version = "0.0.10" + version = "0.0.11" ) bazel_dep( name = "bazel_skylib", From 77bd7aaa66e3f9a103b9b77d5f90632c9a6ede4c Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Wed, 29 Oct 2025 12:40:41 -0400 Subject: [PATCH 07/13] fix bazel 2/x --- BUILD.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.bazel b/BUILD.bazel index 51ea33b0..c305041f 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -85,7 +85,7 @@ cc_library( "src/datadog/w3c_propagation.h", ] + select({ "@platforms//os:windows": [ - "src/datadog/platform_util_win.cpp", + "src/datadog/platform_util_windows.cpp", ], "@platforms//os:linux": [ "src/datadog/platform_util_unix.cpp", From ddf0b10e8dbb37dd085fbbb75f357b255f5f68f1 Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Wed, 29 Oct 2025 12:56:29 -0400 Subject: [PATCH 08/13] fix bazel 3/x --- src/datadog/platform_util_windows.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/datadog/platform_util_windows.cpp b/src/datadog/platform_util_windows.cpp index f44638e1..3bd24060 100644 --- a/src/datadog/platform_util_windows.cpp +++ b/src/datadog/platform_util_windows.cpp @@ -1,5 +1,7 @@ -#include +// clang-format off #include +// clang-format on +#include #include #include @@ -19,7 +21,7 @@ std::tuple get_windows_info() { // application manifest, which is the lowest version supported by the // application. Use `RtlGetVersion` to obtain the accurate OS version // regardless of the manifest. - using RtlGetVersion = auto(*)(LPOSVERSIONINFOEXW)->NTSTATUS; + using RtlGetVersion = auto (*)(LPOSVERSIONINFOEXW)->NTSTATUS; RtlGetVersion func = (RtlGetVersion)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); From 3af6bafae6b56c282b39212b5a52505a83413a4f Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Wed, 29 Oct 2025 13:01:35 -0400 Subject: [PATCH 09/13] fix format --- src/datadog/platform_util_windows.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datadog/platform_util_windows.cpp b/src/datadog/platform_util_windows.cpp index 3bd24060..2e169374 100644 --- a/src/datadog/platform_util_windows.cpp +++ b/src/datadog/platform_util_windows.cpp @@ -21,7 +21,7 @@ std::tuple get_windows_info() { // application manifest, which is the lowest version supported by the // application. Use `RtlGetVersion` to obtain the accurate OS version // regardless of the manifest. - using RtlGetVersion = auto (*)(LPOSVERSIONINFOEXW)->NTSTATUS; + using RtlGetVersion = auto(*)(LPOSVERSIONINFOEXW)->NTSTATUS; RtlGetVersion func = (RtlGetVersion)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); From 77f63f8dafd60b8c310853ddfe4f4828da0dcc8f Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Wed, 29 Oct 2025 13:19:13 -0400 Subject: [PATCH 10/13] fix windows comp --- src/datadog/platform_util_windows.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/datadog/platform_util_windows.cpp b/src/datadog/platform_util_windows.cpp index 2e169374..ec051cbf 100644 --- a/src/datadog/platform_util_windows.cpp +++ b/src/datadog/platform_util_windows.cpp @@ -21,7 +21,7 @@ std::tuple get_windows_info() { // application manifest, which is the lowest version supported by the // application. Use `RtlGetVersion` to obtain the accurate OS version // regardless of the manifest. - using RtlGetVersion = auto(*)(LPOSVERSIONINFOEXW)->NTSTATUS; + using RtlGetVersion = auto (*)(LPOSVERSIONINFOEXW)->NTSTATUS; RtlGetVersion func = (RtlGetVersion)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); @@ -111,6 +111,7 @@ std::string get_process_name() { #else std::string path = std::string(exe_name); #endif + return path; } int at_fork_in_child(void (*on_fork)()) { From 5a0436810cdda03cd2967de38f6253765458a8b5 Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Wed, 29 Oct 2025 13:35:09 -0400 Subject: [PATCH 11/13] fix windows comp 2 --- src/datadog/platform_util_windows.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datadog/platform_util_windows.cpp b/src/datadog/platform_util_windows.cpp index ec051cbf..3ffe4be9 100644 --- a/src/datadog/platform_util_windows.cpp +++ b/src/datadog/platform_util_windows.cpp @@ -21,7 +21,7 @@ std::tuple get_windows_info() { // application manifest, which is the lowest version supported by the // application. Use `RtlGetVersion` to obtain the accurate OS version // regardless of the manifest. - using RtlGetVersion = auto (*)(LPOSVERSIONINFOEXW)->NTSTATUS; + using RtlGetVersion = auto(*)(LPOSVERSIONINFOEXW)->NTSTATUS; RtlGetVersion func = (RtlGetVersion)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); From 6553675fb9d5ee5e1dfc36afb8d4d8e61252df99 Mon Sep 17 00:00:00 2001 From: Damien MEHALA Date: Wed, 19 Nov 2025 09:09:31 -0400 Subject: [PATCH 12/13] fix: windows test --- src/datadog/platform_util_windows.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/datadog/platform_util_windows.cpp b/src/datadog/platform_util_windows.cpp index 3ffe4be9..ef54a8e4 100644 --- a/src/datadog/platform_util_windows.cpp +++ b/src/datadog/platform_util_windows.cpp @@ -21,7 +21,7 @@ std::tuple get_windows_info() { // application manifest, which is the lowest version supported by the // application. Use `RtlGetVersion` to obtain the accurate OS version // regardless of the manifest. - using RtlGetVersion = auto(*)(LPOSVERSIONINFOEXW)->NTSTATUS; + using RtlGetVersion = auto (*)(LPOSVERSIONINFOEXW)->NTSTATUS; RtlGetVersion func = (RtlGetVersion)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); @@ -111,7 +111,7 @@ std::string get_process_name() { #else std::string path = std::string(exe_name); #endif - return path; + return path.substr(path.find_last_of("/\\") + 1); } int at_fork_in_child(void (*on_fork)()) { From c9a8b2a7fe048e96a1640faa6c5a67a149def7bb Mon Sep 17 00:00:00 2001 From: Damien MEHALA Date: Wed, 19 Nov 2025 09:17:25 -0400 Subject: [PATCH 13/13] fix format --- src/datadog/platform_util_windows.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datadog/platform_util_windows.cpp b/src/datadog/platform_util_windows.cpp index ef54a8e4..e4b20311 100644 --- a/src/datadog/platform_util_windows.cpp +++ b/src/datadog/platform_util_windows.cpp @@ -21,7 +21,7 @@ std::tuple get_windows_info() { // application manifest, which is the lowest version supported by the // application. Use `RtlGetVersion` to obtain the accurate OS version // regardless of the manifest. - using RtlGetVersion = auto (*)(LPOSVERSIONINFOEXW)->NTSTATUS; + using RtlGetVersion = auto(*)(LPOSVERSIONINFOEXW)->NTSTATUS; RtlGetVersion func = (RtlGetVersion)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion");