diff --git a/BUILD.bazel b/BUILD.bazel index c81bd18f..c305041f 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_windows.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..b33d45ab 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.11" +) 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.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.cpp b/src/datadog/platform_util_unix.cpp similarity index 58% rename from src/datadog/platform_util.cpp rename to src/datadog/platform_util_unix.cpp index 854c1178..1797b418 100644 --- a/src/datadog/platform_util.cpp +++ b/src/datadog/platform_util_unix.cpp @@ -1,62 +1,29 @@ -#include "platform_util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include +#include + +#include "platform_util.h" +#include "string_util.h" -// 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 - -#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 +#define DD_SDK_OS "GNU/Linux" +#define DD_SDK_KERNEL "Linux" 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()) { @@ -81,9 +48,7 @@ std::string get_os_version() { return ""; } -#endif -#if defined(__APPLE__) || defined(__linux__) || defined(__unix__) HostInfo _get_host_info() { HostInfo res; @@ -102,86 +67,6 @@ HostInfo _get_host_info() { 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 @@ -192,47 +77,14 @@ HostInfo get_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 -} +int get_process_id() { return ::getpid(); } -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 -} +std::string get_process_name() { return program_invocation_short_name; } 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) {} @@ -246,8 +98,6 @@ InMemoryFile& InMemoryFile::operator=(InMemoryFile&& rhs) { 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`. @@ -280,17 +130,8 @@ Expected InMemoryFile::make(StringView name) { 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; @@ -347,7 +188,6 @@ Optional find_container_id_from_cgroup() { return find_container_id(cgroup_fd); } -#endif } // namespace Optional find_container_id(std::istream& source) { @@ -403,7 +243,6 @@ Optional find_container_id(std::istream& source) { } Optional get_id() { -#if defined(__linux__) || defined(__unix__) auto maybe_cgroup = get_cgroup_version(); if (!maybe_cgroup) return nullopt; @@ -431,9 +270,6 @@ Optional get_id() { } return id; -#else - return nullopt; -#endif } } // namespace container diff --git a/src/datadog/platform_util_windows.cpp b/src/datadog/platform_util_windows.cpp new file mode 100644 index 00000000..e4b20311 --- /dev/null +++ b/src/datadog/platform_util_windows.cpp @@ -0,0 +1,199 @@ +// clang-format off +#include +// clang-format on +#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 + return path.substr(path.find_last_of("/\\") + 1); +} + +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 diff --git a/test/test_tracer.cpp b/test/test_tracer.cpp index 8efe47f4..26a2b233 100644 --- a/test/test_tracer.cpp +++ b/test/test_tracer.cpp @@ -2087,6 +2087,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();