From b60ef3d06b3ec689b7d2e65273ac1d82773400ea Mon Sep 17 00:00:00 2001 From: nikhilbhatia08 Date: Wed, 13 Aug 2025 20:42:35 +0530 Subject: [PATCH 1/9] [resource_detectors] implementation of process resource detector as per semconv --- resource_detectors/BUILD | 2 + resource_detectors/CMakeLists.txt | 5 +- .../resource_detectors/process_detector.h | 41 ++++++ .../process_detector_utils.h | 66 +++++++++ resource_detectors/process_detector.cc | 64 +++++++++ resource_detectors/process_detector_utils.cc | 132 ++++++++++++++++++ resource_detectors/test/BUILD | 1 + resource_detectors/test/CMakeLists.txt | 3 +- .../test/process_detector_test.cc | 56 ++++++++ 9 files changed, 367 insertions(+), 3 deletions(-) create mode 100644 resource_detectors/include/opentelemetry/resource_detectors/process_detector.h create mode 100644 resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h create mode 100644 resource_detectors/process_detector.cc create mode 100644 resource_detectors/process_detector_utils.cc create mode 100644 resource_detectors/test/process_detector_test.cc diff --git a/resource_detectors/BUILD b/resource_detectors/BUILD index 503949ca28..d5ddca4b8b 100644 --- a/resource_detectors/BUILD +++ b/resource_detectors/BUILD @@ -14,6 +14,8 @@ cc_library( srcs = [ "container_detector.cc", "container_detector_utils.cc", + "process_detector.cc", + "process_detector_utils.cc", ], deps = [ "//api", diff --git a/resource_detectors/CMakeLists.txt b/resource_detectors/CMakeLists.txt index 108ca00bad..5f6f8239ca 100644 --- a/resource_detectors/CMakeLists.txt +++ b/resource_detectors/CMakeLists.txt @@ -1,8 +1,9 @@ # Copyright The OpenTelemetry Authors # SPDX-License-Identifier: Apache-2.0 -add_library(opentelemetry_resource_detectors container_detector_utils.cc - container_detector.cc) +add_library( + opentelemetry_resource_detectors + container_detector_utils.cc process_detector.cc process_detector_utils.cc) set_target_properties(opentelemetry_resource_detectors PROPERTIES EXPORT_NAME resource_detectors) diff --git a/resource_detectors/include/opentelemetry/resource_detectors/process_detector.h b/resource_detectors/include/opentelemetry/resource_detectors/process_detector.h new file mode 100644 index 0000000000..1a259c6543 --- /dev/null +++ b/resource_detectors/include/opentelemetry/resource_detectors/process_detector.h @@ -0,0 +1,41 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/resource/resource_detector.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace resource_detector +{ + +/** + * ProcessResourceDetector to detect resource attributes when running in a process. + * This detector extracts metadata such as process ID, executable path, and command line arguments + * and sets attributes like process.pid, process.executable.path, and process.command_args following + * the OpenTelemetry semantic conventions. + */ +class ProcessResourceDetector : public opentelemetry::sdk::resource::ResourceDetector +{ +public: + /** + * Detect retrieves the resource attributes for the current process. + * It reads: + * - process.pid from the current process ID + * - process.executable.path from the executable path of the current process + * - process.command from the command used to launch the process + * and returns a Resource with these attributes set. + */ + opentelemetry::sdk::resource::Resource Detect() noexcept override; + /** + * DetectCommandLineArgs retrieves the command line arguments of the current process. + * It reads the command line arguments from the /proc//cmdline file and returns a Resource + * with the process.command_args attribute set. + */ + opentelemetry::sdk::resource::Resource DetectCommandLineArgs() noexcept; +}; + +} // namespace resource_detector +OPENTELEMETRY_END_NAMESPACE diff --git a/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h b/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h new file mode 100644 index 0000000000..7e22922c74 --- /dev/null +++ b/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h @@ -0,0 +1,66 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace resource_detector +{ +namespace detail +{ + +/** + * Forms a file path for a process type based on the given PID. + * for example - /proc//cmdline, /proc//exe + */ +std::string FormFilePath(int32_t &pid, const char *process_type); + +/** + * Retrieves the absolute file system path to the executable for a given PID. + * + * Platform-specific behavior: + * - Windows: Uses OpenProcess() + GetProcessImageFileNameW(). + * - Linux/Unix: Reads the /proc//exe symbolic link. + * + * @param pid Process ID. + */ +std::string GetExecutablePath(int32_t &pid); + +/** + * Retrieves the command used to launch the process for a given PID. + * Platform-specific behavior: + * - Windows: Uses GetCommandLineW() to get the command of the current process. + * - Linux/Unix: Reads the zeroth string of /proc//cmdline file. + */ +std::string ExtractCommand(std::string &command_line_path); + +/** + * Retrieves the command line arguments of the process for a given PID. + * Platform-specific behavior: + * - Windows: Uses GetCommandLineW() to get the command line arguments of the current process. + * - Linux/Unix: Reads the /proc//cmdline file and splits it into null-delimited strings. + */ +std::vector ExtractCommandLineArgs(std::string &command_line_path); + +/** + * Retrieves the command used to launch the process for a given PID. + * This function is a wrapper around ExtractCommand() and is provided for convenience and + * testability of ExtractCommand(). + */ +std::string GetCommand(int32_t &pid); + +/** + * Retrieves the command line arguments of the process for a given PID. + * This function is a wrapper around ExtractCommandLineArgs() and is provided for convenience and + * testability of ExtractCommandLineArgs(). + */ +std::vector GetCommandLineArgs(int32_t &pid); + +} // namespace detail +} // namespace resource_detector +OPENTELEMETRY_END_NAMESPACE diff --git a/resource_detectors/process_detector.cc b/resource_detectors/process_detector.cc new file mode 100644 index 0000000000..3137f6c988 --- /dev/null +++ b/resource_detectors/process_detector.cc @@ -0,0 +1,64 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/resource_detectors/process_detector.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/resource_detectors/process_detector_utils.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/resource/resource_detector.h" +#include "opentelemetry/semconv/incubating/process_attributes.h" +#include "opentelemetry/version.h" + +#include +#include +#include + +#ifdef _MSC_VER +# include +# define getpid _getpid +#else +# include +#endif + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace resource_detector +{ + +opentelemetry::sdk::resource::Resource ProcessResourceDetector::Detect() noexcept +{ + int32_t pid = getpid(); + opentelemetry::sdk::resource::ResourceAttributes attributes; + attributes[semconv::process::kProcessPid] = pid; + + std::string executable_path = opentelemetry::resource_detector::detail::GetExecutablePath(pid); + if (!executable_path.empty()) + { + attributes[semconv::process::kProcessExecutablePath] = std::move(executable_path); + } + + std::string command = opentelemetry::resource_detector::detail::GetCommand(pid); + if (!command.empty()) + { + attributes[semconv::process::kProcessCommand] = std::move(command); + } + + return ResourceDetector::Create(attributes); +} + +opentelemetry::sdk::resource::Resource ProcessResourceDetector::DetectCommandLineArgs() noexcept +{ + int32_t pid = getpid(); + opentelemetry::sdk::resource::ResourceAttributes attributes; + + std::vector command_line_args = + opentelemetry::resource_detector::detail::GetCommandLineArgs(pid); + if (!command_line_args.empty()) + { + attributes[semconv::process::kProcessCommandArgs] = std::move(command_line_args); + } + + return ResourceDetector::Create(attributes); +} + +} // namespace resource_detector +OPENTELEMETRY_END_NAMESPACE diff --git a/resource_detectors/process_detector_utils.cc b/resource_detectors/process_detector_utils.cc new file mode 100644 index 0000000000..2ab5550a46 --- /dev/null +++ b/resource_detectors/process_detector_utils.cc @@ -0,0 +1,132 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/resource_detectors/process_detector_utils.h" +#include "opentelemetry/nostd/string_view.h" + +#include +#include + +#ifdef _MSC_VER +# include +# include +#else +# include +# include +#endif + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace resource_detector +{ +namespace detail +{ + +constexpr const char *kExecutableName = "exe"; +constexpr const char *kCmdlineName = "cmdline"; + +std::string GetExecutablePath(int32_t &pid) +{ +#ifdef _MSC_VER + HANDLE hProcess = + OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, static_cast(pid)); + if (!hProcess) + { + return std::string(); + } + + WCHAR wbuffer[MAX_PATH]; + DWORD len = GetProcessImageFileNameW(hProcess, wbuffer, MAX_PATH); + CloseHandle(hProcess); + + if (len == 0) + { + return std::string(); + } + + // Convert UTF-16 (wide string) to UTF-8 + int size_needed = WideCharToMultiByte(CP_UTF8, 0, wbuffer, len, NULL, 0, NULL, NULL); + std::string utf8_path(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, wbuffer, len, &utf8_path[0], size_needed, NULL, NULL); + + return utf8_path; +#else + std::string path = FormFilePath(pid, kExecutableName); + char buffer[4096]; + + ssize_t len = readlink(path.c_str(), buffer, sizeof(buffer) - 1); + if (len != -1) + { + buffer[len] = '\0'; + return std::string(buffer); + } + + return std::string(); +#endif +} + +std::string GetCommand(int32_t &pid) +{ +#ifdef _MSC_VER + // On Windows, GetCommandLineW only works for the CURRENT process, + // so we ignore `pid` and just return the current process's command line. + LPCWSTR wcmd = GetCommandLineW(); + if (!wcmd) + return std::string(); + + // Convert UTF-16 → UTF-8 + int size_needed = WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, NULL, 0, NULL, NULL); + if (size_needed <= 0) + return std::string(); + + std::string utf8_command(size_needed - 1, 0); // exclude null terminator + WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, &utf8_command[0], size_needed, NULL, NULL); + + return utf8_command; +#else + // This is the path to get the command that was used to start the process + std::string command_line_path = FormFilePath(pid, kCmdlineName); + return ExtractCommand(command_line_path); +#endif +} + +std::vector GetCommandLineArgs(int32_t &pid) +{ + std::string command_line_path = FormFilePath(pid, kCmdlineName); + return ExtractCommandLineArgs(command_line_path); +} + +std::string ExtractCommand(std::string &command_line_path) +{ + std::string command; + std::ifstream command_line_file(command_line_path, std::ios::in | std::ios::binary); + std::getline(command_line_file, command, '\0'); + return command; +} + +std::vector ExtractCommandLineArgs(std::string &command_line_path) +{ + std::vector args; + std::ifstream command_line_file(command_line_path, std::ios::in | std::ios::binary); + std::string line; + while (std::getline(command_line_file, line, '\0')) + { + if (!line.empty()) + { + args.push_back(line); + } + } + return args; +} + +std::string FormFilePath(int32_t &pid, const char *process_type) +{ + static char buff[64]; + int len = std::snprintf(buff, sizeof(buff), "/proc/%d/%s", pid, process_type); + return std::string(buff, len); +} + +} // namespace detail +} // namespace resource_detector +OPENTELEMETRY_END_NAMESPACE diff --git a/resource_detectors/test/BUILD b/resource_detectors/test/BUILD index 2cac8174dc..0c12439654 100644 --- a/resource_detectors/test/BUILD +++ b/resource_detectors/test/BUILD @@ -5,6 +5,7 @@ cc_test( name = "resource_detector_test", srcs = [ "container_detector_test.cc", + "process_detector_test.cc", ], tags = ["test"], deps = [ diff --git a/resource_detectors/test/CMakeLists.txt b/resource_detectors/test/CMakeLists.txt index 9414e64212..4062a278ef 100644 --- a/resource_detectors/test/CMakeLists.txt +++ b/resource_detectors/test/CMakeLists.txt @@ -1,7 +1,8 @@ # Copyright The OpenTelemetry Authors # SPDX-License-Identifier: Apache-2.0 -add_executable(resource_detector_test container_detector_test.cc) +add_executable(resource_detector_test container_detector_test.cc + process_detector_test.cc) # Link the required dependencies target_link_libraries( diff --git a/resource_detectors/test/process_detector_test.cc b/resource_detectors/test/process_detector_test.cc new file mode 100644 index 0000000000..b0666a9e20 --- /dev/null +++ b/resource_detectors/test/process_detector_test.cc @@ -0,0 +1,56 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include + +#include "opentelemetry/resource_detectors/process_detector_utils.h" + +TEST(ProcessDetectorUtilsTest, FormFilePath) +{ + int32_t pid = 1234; + std::string cmdline_path = opentelemetry::resource_detector::detail::FormFilePath(pid, "cmdline"); + std::string exe_path = opentelemetry::resource_detector::detail::FormFilePath(pid, "exe"); + + EXPECT_EQ(cmdline_path, "/proc/1234/cmdline"); + EXPECT_EQ(exe_path, "/proc/1234/exe"); +} + +TEST(ProcessDetectorUtilsTest, ExtractCommand) +{ + std::string filename{"test_command.txt"}; + + { + std::ofstream outfile(filename, std::ios::binary); + const char raw_data[] = "test_command\0arg1\0arg2\0arg3\0"; + outfile.write(raw_data, sizeof(raw_data) - 1); + } + + std::string command = opentelemetry::resource_detector::detail::ExtractCommand(filename); + EXPECT_EQ(command, std::string{"test_command"}); + + std::remove(filename.c_str()); // Cleanup +} + +TEST(ProcessDetectorUtilsTest, ExtractCommandLineArgs) +{ + std::string filename{"test_args.txt"}; + + { + std::ofstream outfile(filename, std::ios::binary); + const char raw_data[] = "test_command\0arg1\0arg2\0arg3\0"; + outfile.write(raw_data, sizeof(raw_data) - 1); + } + + std::vector args = + opentelemetry::resource_detector::detail::ExtractCommandLineArgs(filename); + EXPECT_EQ(args.size(), 4); + EXPECT_EQ(args[0], "test_command"); + EXPECT_EQ(args[1], "arg1"); + EXPECT_EQ(args[2], "arg2"); + EXPECT_EQ(args[3], "arg3"); + + std::remove(filename.c_str()); // Cleanup +} From 0f3b0b31468b030fdcf389bc99e4f84da24bd220 Mon Sep 17 00:00:00 2001 From: nikhilbhatia08 Date: Wed, 13 Aug 2025 21:50:13 +0530 Subject: [PATCH 2/9] removed unclear changes --- .../resource_detectors/process_detector.h | 8 +------ .../process_detector_utils.h | 15 ------------- resource_detectors/process_detector.cc | 16 +------------- resource_detectors/process_detector_utils.cc | 21 ------------------- .../test/process_detector_test.cc | 21 ++++++------------- 5 files changed, 8 insertions(+), 73 deletions(-) diff --git a/resource_detectors/include/opentelemetry/resource_detectors/process_detector.h b/resource_detectors/include/opentelemetry/resource_detectors/process_detector.h index 1a259c6543..229693a051 100644 --- a/resource_detectors/include/opentelemetry/resource_detectors/process_detector.h +++ b/resource_detectors/include/opentelemetry/resource_detectors/process_detector.h @@ -14,7 +14,7 @@ namespace resource_detector /** * ProcessResourceDetector to detect resource attributes when running in a process. * This detector extracts metadata such as process ID, executable path, and command line arguments - * and sets attributes like process.pid, process.executable.path, and process.command_args following + * and sets attributes like process.pid, process.executable.path, and process.command following * the OpenTelemetry semantic conventions. */ class ProcessResourceDetector : public opentelemetry::sdk::resource::ResourceDetector @@ -29,12 +29,6 @@ class ProcessResourceDetector : public opentelemetry::sdk::resource::ResourceDet * and returns a Resource with these attributes set. */ opentelemetry::sdk::resource::Resource Detect() noexcept override; - /** - * DetectCommandLineArgs retrieves the command line arguments of the current process. - * It reads the command line arguments from the /proc//cmdline file and returns a Resource - * with the process.command_args attribute set. - */ - opentelemetry::sdk::resource::Resource DetectCommandLineArgs() noexcept; }; } // namespace resource_detector diff --git a/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h b/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h index 7e22922c74..2e6c410430 100644 --- a/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h +++ b/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h @@ -39,14 +39,6 @@ std::string GetExecutablePath(int32_t &pid); */ std::string ExtractCommand(std::string &command_line_path); -/** - * Retrieves the command line arguments of the process for a given PID. - * Platform-specific behavior: - * - Windows: Uses GetCommandLineW() to get the command line arguments of the current process. - * - Linux/Unix: Reads the /proc//cmdline file and splits it into null-delimited strings. - */ -std::vector ExtractCommandLineArgs(std::string &command_line_path); - /** * Retrieves the command used to launch the process for a given PID. * This function is a wrapper around ExtractCommand() and is provided for convenience and @@ -54,13 +46,6 @@ std::vector ExtractCommandLineArgs(std::string &command_line_path); */ std::string GetCommand(int32_t &pid); -/** - * Retrieves the command line arguments of the process for a given PID. - * This function is a wrapper around ExtractCommandLineArgs() and is provided for convenience and - * testability of ExtractCommandLineArgs(). - */ -std::vector GetCommandLineArgs(int32_t &pid); - } // namespace detail } // namespace resource_detector OPENTELEMETRY_END_NAMESPACE diff --git a/resource_detectors/process_detector.cc b/resource_detectors/process_detector.cc index 3137f6c988..d3394ed067 100644 --- a/resource_detectors/process_detector.cc +++ b/resource_detectors/process_detector.cc @@ -9,6 +9,7 @@ #include "opentelemetry/semconv/incubating/process_attributes.h" #include "opentelemetry/version.h" +#include #include #include #include @@ -45,20 +46,5 @@ opentelemetry::sdk::resource::Resource ProcessResourceDetector::Detect() noexcep return ResourceDetector::Create(attributes); } -opentelemetry::sdk::resource::Resource ProcessResourceDetector::DetectCommandLineArgs() noexcept -{ - int32_t pid = getpid(); - opentelemetry::sdk::resource::ResourceAttributes attributes; - - std::vector command_line_args = - opentelemetry::resource_detector::detail::GetCommandLineArgs(pid); - if (!command_line_args.empty()) - { - attributes[semconv::process::kProcessCommandArgs] = std::move(command_line_args); - } - - return ResourceDetector::Create(attributes); -} - } // namespace resource_detector OPENTELEMETRY_END_NAMESPACE diff --git a/resource_detectors/process_detector_utils.cc b/resource_detectors/process_detector_utils.cc index 2ab5550a46..5cead6c16c 100644 --- a/resource_detectors/process_detector_utils.cc +++ b/resource_detectors/process_detector_utils.cc @@ -91,12 +91,6 @@ std::string GetCommand(int32_t &pid) #endif } -std::vector GetCommandLineArgs(int32_t &pid) -{ - std::string command_line_path = FormFilePath(pid, kCmdlineName); - return ExtractCommandLineArgs(command_line_path); -} - std::string ExtractCommand(std::string &command_line_path) { std::string command; @@ -105,21 +99,6 @@ std::string ExtractCommand(std::string &command_line_path) return command; } -std::vector ExtractCommandLineArgs(std::string &command_line_path) -{ - std::vector args; - std::ifstream command_line_file(command_line_path, std::ios::in | std::ios::binary); - std::string line; - while (std::getline(command_line_file, line, '\0')) - { - if (!line.empty()) - { - args.push_back(line); - } - } - return args; -} - std::string FormFilePath(int32_t &pid, const char *process_type) { static char buff[64]; diff --git a/resource_detectors/test/process_detector_test.cc b/resource_detectors/test/process_detector_test.cc index b0666a9e20..f9f7817e07 100644 --- a/resource_detectors/test/process_detector_test.cc +++ b/resource_detectors/test/process_detector_test.cc @@ -34,23 +34,14 @@ TEST(ProcessDetectorUtilsTest, ExtractCommand) std::remove(filename.c_str()); // Cleanup } -TEST(ProcessDetectorUtilsTest, ExtractCommandLineArgs) +TEST(ProcessDetectorUtilsTest, EmptyCommandFile) { - std::string filename{"test_args.txt"}; + std::string filename{"empty_command.txt"}; + std::ofstream outfile(filename, std::ios::binary); + outfile.close(); - { - std::ofstream outfile(filename, std::ios::binary); - const char raw_data[] = "test_command\0arg1\0arg2\0arg3\0"; - outfile.write(raw_data, sizeof(raw_data) - 1); - } - - std::vector args = - opentelemetry::resource_detector::detail::ExtractCommandLineArgs(filename); - EXPECT_EQ(args.size(), 4); - EXPECT_EQ(args[0], "test_command"); - EXPECT_EQ(args[1], "arg1"); - EXPECT_EQ(args[2], "arg2"); - EXPECT_EQ(args[3], "arg3"); + std::string command = opentelemetry::resource_detector::detail::ExtractCommand(filename); + EXPECT_EQ(command, std::string{""}); std::remove(filename.c_str()); // Cleanup } From 8c9ecddd01b641061cbda3af0bec0dc9cb07ec03 Mon Sep 17 00:00:00 2001 From: nikhilbhatia08 Date: Thu, 14 Aug 2025 20:19:59 +0530 Subject: [PATCH 3/9] fixes suggested by copilot --- resource_detectors/CMakeLists.txt | 3 ++- .../resource_detectors/process_detector_utils.h | 10 +++++----- resource_detectors/process_detector_utils.cc | 12 ++++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/resource_detectors/CMakeLists.txt b/resource_detectors/CMakeLists.txt index 5f6f8239ca..262f00ba4e 100644 --- a/resource_detectors/CMakeLists.txt +++ b/resource_detectors/CMakeLists.txt @@ -3,7 +3,8 @@ add_library( opentelemetry_resource_detectors - container_detector_utils.cc process_detector.cc process_detector_utils.cc) + container_detector_utils.cc container_detector.cc process_detector.cc + process_detector_utils.cc) set_target_properties(opentelemetry_resource_detectors PROPERTIES EXPORT_NAME resource_detectors) diff --git a/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h b/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h index 2e6c410430..f316fcdfc7 100644 --- a/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h +++ b/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h @@ -3,9 +3,9 @@ #pragma once +#include #include -#include "opentelemetry/nostd/string_view.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -18,7 +18,7 @@ namespace detail * Forms a file path for a process type based on the given PID. * for example - /proc//cmdline, /proc//exe */ -std::string FormFilePath(int32_t &pid, const char *process_type); +std::string FormFilePath(const int32_t &pid, const char *process_type); /** * Retrieves the absolute file system path to the executable for a given PID. @@ -29,7 +29,7 @@ std::string FormFilePath(int32_t &pid, const char *process_type); * * @param pid Process ID. */ -std::string GetExecutablePath(int32_t &pid); +std::string GetExecutablePath(const int32_t &pid); /** * Retrieves the command used to launch the process for a given PID. @@ -37,14 +37,14 @@ std::string GetExecutablePath(int32_t &pid); * - Windows: Uses GetCommandLineW() to get the command of the current process. * - Linux/Unix: Reads the zeroth string of /proc//cmdline file. */ -std::string ExtractCommand(std::string &command_line_path); +std::string ExtractCommand(const std::string &command_line_path); /** * Retrieves the command used to launch the process for a given PID. * This function is a wrapper around ExtractCommand() and is provided for convenience and * testability of ExtractCommand(). */ -std::string GetCommand(int32_t &pid); +std::string GetCommand(const int32_t &pid); } // namespace detail } // namespace resource_detector diff --git a/resource_detectors/process_detector_utils.cc b/resource_detectors/process_detector_utils.cc index 5cead6c16c..d20f367dc1 100644 --- a/resource_detectors/process_detector_utils.cc +++ b/resource_detectors/process_detector_utils.cc @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 #include "opentelemetry/resource_detectors/process_detector_utils.h" -#include "opentelemetry/nostd/string_view.h" #include #include @@ -11,6 +10,7 @@ # include # include #else +# include # include # include #endif @@ -26,7 +26,7 @@ namespace detail constexpr const char *kExecutableName = "exe"; constexpr const char *kCmdlineName = "cmdline"; -std::string GetExecutablePath(int32_t &pid) +std::string GetExecutablePath(const int32_t &pid) { #ifdef _MSC_VER HANDLE hProcess = @@ -66,7 +66,7 @@ std::string GetExecutablePath(int32_t &pid) #endif } -std::string GetCommand(int32_t &pid) +std::string GetCommand(const int32_t &pid) { #ifdef _MSC_VER // On Windows, GetCommandLineW only works for the CURRENT process, @@ -91,7 +91,7 @@ std::string GetCommand(int32_t &pid) #endif } -std::string ExtractCommand(std::string &command_line_path) +std::string ExtractCommand(const std::string &command_line_path) { std::string command; std::ifstream command_line_file(command_line_path, std::ios::in | std::ios::binary); @@ -99,9 +99,9 @@ std::string ExtractCommand(std::string &command_line_path) return command; } -std::string FormFilePath(int32_t &pid, const char *process_type) +std::string FormFilePath(const int32_t &pid, const char *process_type) { - static char buff[64]; + char buff[64]; int len = std::snprintf(buff, sizeof(buff), "/proc/%d/%s", pid, process_type); return std::string(buff, len); } From c2f7f4549bd0443e5586ecd95f7aa5dafcd0a3e6 Mon Sep 17 00:00:00 2001 From: nikhilbhatia08 Date: Fri, 15 Aug 2025 11:56:09 +0530 Subject: [PATCH 4/9] disable clang-format for windows --- resource_detectors/process_detector_utils.cc | 8 +++++--- resource_detectors/test/process_detector_test.cc | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/resource_detectors/process_detector_utils.cc b/resource_detectors/process_detector_utils.cc index d20f367dc1..e8166c9641 100644 --- a/resource_detectors/process_detector_utils.cc +++ b/resource_detectors/process_detector_utils.cc @@ -7,8 +7,10 @@ #include #ifdef _MSC_VER -# include +// clang-format off # include +# include +// clang-format on #else # include # include @@ -45,7 +47,7 @@ std::string GetExecutablePath(const int32_t &pid) return std::string(); } - // Convert UTF-16 (wide string) to UTF-8 + // Convert UTF-16 to UTF-8 int size_needed = WideCharToMultiByte(CP_UTF8, 0, wbuffer, len, NULL, 0, NULL, NULL); std::string utf8_path(size_needed, 0); WideCharToMultiByte(CP_UTF8, 0, wbuffer, len, &utf8_path[0], size_needed, NULL, NULL); @@ -75,7 +77,7 @@ std::string GetCommand(const int32_t &pid) if (!wcmd) return std::string(); - // Convert UTF-16 → UTF-8 + // Convert UTF-16 to UTF-8 int size_needed = WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, NULL, 0, NULL, NULL); if (size_needed <= 0) return std::string(); diff --git a/resource_detectors/test/process_detector_test.cc b/resource_detectors/test/process_detector_test.cc index f9f7817e07..7aeacd743a 100644 --- a/resource_detectors/test/process_detector_test.cc +++ b/resource_detectors/test/process_detector_test.cc @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include #include #include From fe47aa6f16c2c39f97484aaf95f5dab9b020200e Mon Sep 17 00:00:00 2001 From: nikhilbhatia08 Date: Mon, 18 Aug 2025 20:32:24 +0000 Subject: [PATCH 5/9] test addition and details folder --- resource_detectors/CMakeLists.txt | 2 +- resource_detectors/container_detector.cc | 2 +- .../container_detector_utils.cc | 2 +- .../{ => detail}/container_detector_utils.h | 0 .../{ => detail}/process_detector_utils.h | 0 resource_detectors/process_detector.cc | 32 +++++++-- resource_detectors/process_detector_utils.cc | 6 +- .../test/container_detector_test.cc | 2 +- .../test/process_detector_test.cc | 66 ++++++++++++++++++- 9 files changed, 98 insertions(+), 14 deletions(-) rename resource_detectors/include/opentelemetry/resource_detectors/{ => detail}/container_detector_utils.h (100%) rename resource_detectors/include/opentelemetry/resource_detectors/{ => detail}/process_detector_utils.h (100%) diff --git a/resource_detectors/CMakeLists.txt b/resource_detectors/CMakeLists.txt index 262f00ba4e..7fdba7d73b 100644 --- a/resource_detectors/CMakeLists.txt +++ b/resource_detectors/CMakeLists.txt @@ -30,7 +30,7 @@ otel_add_component( PATTERN "*.h" PATTERN - "container_detector_utils.h" + "resource_detectors/detail/*" EXCLUDE) if(BUILD_TESTING) diff --git a/resource_detectors/container_detector.cc b/resource_detectors/container_detector.cc index 3cf27d4b9e..a71c7cbc32 100644 --- a/resource_detectors/container_detector.cc +++ b/resource_detectors/container_detector.cc @@ -3,7 +3,7 @@ #include "opentelemetry/resource_detectors/container_detector.h" #include "opentelemetry/nostd/variant.h" -#include "opentelemetry/resource_detectors/container_detector_utils.h" +#include "opentelemetry/resource_detectors/detail/container_detector_utils.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/resource/resource_detector.h" #include "opentelemetry/semconv/incubating/container_attributes.h" diff --git a/resource_detectors/container_detector_utils.cc b/resource_detectors/container_detector_utils.cc index a1c54e2f47..f0a0c383b2 100644 --- a/resource_detectors/container_detector_utils.cc +++ b/resource_detectors/container_detector_utils.cc @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -#include "opentelemetry/resource_detectors/container_detector_utils.h" +#include "opentelemetry/resource_detectors/detail/container_detector_utils.h" #include "opentelemetry/nostd/string_view.h" #include diff --git a/resource_detectors/include/opentelemetry/resource_detectors/container_detector_utils.h b/resource_detectors/include/opentelemetry/resource_detectors/detail/container_detector_utils.h similarity index 100% rename from resource_detectors/include/opentelemetry/resource_detectors/container_detector_utils.h rename to resource_detectors/include/opentelemetry/resource_detectors/detail/container_detector_utils.h diff --git a/resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h b/resource_detectors/include/opentelemetry/resource_detectors/detail/process_detector_utils.h similarity index 100% rename from resource_detectors/include/opentelemetry/resource_detectors/process_detector_utils.h rename to resource_detectors/include/opentelemetry/resource_detectors/detail/process_detector_utils.h diff --git a/resource_detectors/process_detector.cc b/resource_detectors/process_detector.cc index d3394ed067..96de5876bd 100644 --- a/resource_detectors/process_detector.cc +++ b/resource_detectors/process_detector.cc @@ -3,13 +3,15 @@ #include "opentelemetry/resource_detectors/process_detector.h" #include "opentelemetry/nostd/variant.h" -#include "opentelemetry/resource_detectors/process_detector_utils.h" +#include "opentelemetry/resource_detectors/detail/process_detector_utils.h" +#include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/resource/resource_detector.h" #include "opentelemetry/semconv/incubating/process_attributes.h" #include "opentelemetry/version.h" #include +#include #include #include #include @@ -31,16 +33,32 @@ opentelemetry::sdk::resource::Resource ProcessResourceDetector::Detect() noexcep opentelemetry::sdk::resource::ResourceAttributes attributes; attributes[semconv::process::kProcessPid] = pid; - std::string executable_path = opentelemetry::resource_detector::detail::GetExecutablePath(pid); - if (!executable_path.empty()) + try { - attributes[semconv::process::kProcessExecutablePath] = std::move(executable_path); + std::string executable_path = opentelemetry::resource_detector::detail::GetExecutablePath(pid); + if (!executable_path.empty()) + { + attributes[semconv::process::kProcessExecutablePath] = std::move(executable_path); + } + } + catch (const ::std::exception &ex) + { + OTEL_INTERNAL_LOG_ERROR("[Process Resource Detector] " + << "Error extracting the executable path: " << ex.what()); } - std::string command = opentelemetry::resource_detector::detail::GetCommand(pid); - if (!command.empty()) + try + { + std::string command = opentelemetry::resource_detector::detail::GetCommand(pid); + if (!command.empty()) + { + attributes[semconv::process::kProcessCommand] = std::move(command); + } + } + catch (const std::exception &ex) { - attributes[semconv::process::kProcessCommand] = std::move(command); + OTEL_INTERNAL_LOG_ERROR("[Process Resource Detector] " << "Error extracting command: " + << ex.what()); } return ResourceDetector::Create(attributes); diff --git a/resource_detectors/process_detector_utils.cc b/resource_detectors/process_detector_utils.cc index e8166c9641..868e65890e 100644 --- a/resource_detectors/process_detector_utils.cc +++ b/resource_detectors/process_detector_utils.cc @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -#include "opentelemetry/resource_detectors/process_detector_utils.h" +#include "opentelemetry/resource_detectors/detail/process_detector_utils.h" #include #include @@ -75,12 +75,16 @@ std::string GetCommand(const int32_t &pid) // so we ignore `pid` and just return the current process's command line. LPCWSTR wcmd = GetCommandLineW(); if (!wcmd) + { return std::string(); + } // Convert UTF-16 to UTF-8 int size_needed = WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, NULL, 0, NULL, NULL); if (size_needed <= 0) + { return std::string(); + } std::string utf8_command(size_needed - 1, 0); // exclude null terminator WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, &utf8_command[0], size_needed, NULL, NULL); diff --git a/resource_detectors/test/container_detector_test.cc b/resource_detectors/test/container_detector_test.cc index 0243973c46..bc70763370 100644 --- a/resource_detectors/test/container_detector_test.cc +++ b/resource_detectors/test/container_detector_test.cc @@ -7,7 +7,7 @@ #include #include "opentelemetry/nostd/string_view.h" -#include "opentelemetry/resource_detectors/container_detector_utils.h" +#include "opentelemetry/resource_detectors/detail/container_detector_utils.h" TEST(ContainerIdDetectorTest, ExtractValidContainerIdFromLine) { diff --git a/resource_detectors/test/process_detector_test.cc b/resource_detectors/test/process_detector_test.cc index 7aeacd743a..f4abea00fa 100644 --- a/resource_detectors/test/process_detector_test.cc +++ b/resource_detectors/test/process_detector_test.cc @@ -3,11 +3,23 @@ #include #include -#include #include #include -#include "opentelemetry/resource_detectors/process_detector_utils.h" +#ifdef _MSC_VER +// clang-format off +# include +# include +# include +# define getpid _getpid +// clang-format on +#else +# include +# include +# include +#endif + +#include "opentelemetry/resource_detectors/detail/process_detector_utils.h" TEST(ProcessDetectorUtilsTest, FormFilePath) { @@ -46,3 +58,53 @@ TEST(ProcessDetectorUtilsTest, EmptyCommandFile) std::remove(filename.c_str()); // Cleanup } + +TEST(ProcessDetectorUtilsTest, GetExecutablePathTest) +{ + int32_t pid = getpid(); + std::string path; + #ifdef _MSC_VER + HANDLE hProcess = + OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, static_cast(pid)); + if (!hProcess) + { + path = std::string(); + } + else + { + + WCHAR wbuffer[MAX_PATH]; + DWORD len = GetProcessImageFileNameW(hProcess, wbuffer, MAX_PATH); + CloseHandle(hProcess); + + if (len == 0) + { + path = std::string(); + } + else + { + int size_needed = WideCharToMultiByte(CP_UTF8, 0, wbuffer, len, NULL, 0, NULL, NULL); + std::string utf8_path(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, wbuffer, len, &utf8_path[0], size_needed, NULL, NULL); + + path = utf8_path; + } + } +#else + std::string exe_path = opentelemetry::resource_detector::detail::FormFilePath(pid, "exe"); + char buffer[4096]; + + ssize_t len = readlink(exe_path.c_str(), buffer, sizeof(buffer) - 1); + if (len != -1) + { + buffer[len] = '\0'; + path = std::string(buffer); + } + else + { + path = std::string(); + } +#endif + std::string expected_path = opentelemetry::resource_detector::detail::GetExecutablePath(pid); + EXPECT_EQ(path, expected_path); +} From 7dd7658e1a469151d341645cd506c2d982cc588d Mon Sep 17 00:00:00 2001 From: nikhilbhatia08 Date: Tue, 19 Aug 2025 02:54:48 +0530 Subject: [PATCH 6/9] test for GetCommand and format fix --- resource_detectors/BUILD | 1 + resource_detectors/process_detector.cc | 1 + .../test/process_detector_test.cc | 45 +++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/resource_detectors/BUILD b/resource_detectors/BUILD index d5ddca4b8b..d68c1a710d 100644 --- a/resource_detectors/BUILD +++ b/resource_detectors/BUILD @@ -17,6 +17,7 @@ cc_library( "process_detector.cc", "process_detector_utils.cc", ], + copts = ["-fexceptions"], deps = [ "//api", "//resource_detectors:headers", diff --git a/resource_detectors/process_detector.cc b/resource_detectors/process_detector.cc index 96de5876bd..c0bde2555a 100644 --- a/resource_detectors/process_detector.cc +++ b/resource_detectors/process_detector.cc @@ -12,6 +12,7 @@ #include #include +#include #include #include #include diff --git a/resource_detectors/test/process_detector_test.cc b/resource_detectors/test/process_detector_test.cc index f4abea00fa..90e165a37f 100644 --- a/resource_detectors/test/process_detector_test.cc +++ b/resource_detectors/test/process_detector_test.cc @@ -63,14 +63,14 @@ TEST(ProcessDetectorUtilsTest, GetExecutablePathTest) { int32_t pid = getpid(); std::string path; - #ifdef _MSC_VER +#ifdef _MSC_VER HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, static_cast(pid)); if (!hProcess) { path = std::string(); } - else + else { WCHAR wbuffer[MAX_PATH]; @@ -98,7 +98,7 @@ TEST(ProcessDetectorUtilsTest, GetExecutablePathTest) if (len != -1) { buffer[len] = '\0'; - path = std::string(buffer); + path = std::string(buffer); } else { @@ -108,3 +108,42 @@ TEST(ProcessDetectorUtilsTest, GetExecutablePathTest) std::string expected_path = opentelemetry::resource_detector::detail::GetExecutablePath(pid); EXPECT_EQ(path, expected_path); } + +TEST(ProcessDetectorUtilsTest, GetCommandTest) +{ + int32_t pid = getpid(); + std::string command; +#ifdef _MSC_VER + // On Windows, GetCommandLineW only works for the CURRENT process, + // so we ignore `pid` and just return the current process's command line. + LPCWSTR wcmd = GetCommandLineW(); + if (!wcmd) + { + command = std::string(); + } + else + { + + // Convert UTF-16 to UTF-8 + int size_needed = WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, NULL, 0, NULL, NULL); + if (size_needed <= 0) + { + command = std::string(); + } + else + { + std::string utf8_command(size_needed - 1, 0); // exclude null terminator + WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, &utf8_command[0], size_needed, NULL, NULL); + command = utf8_command; + } + } +#else + // This is the path to get the command that was used to start the process + std::string command_line_path = + opentelemetry::resource_detector::detail::FormFilePath(pid, "cmdline"); + command = opentelemetry::resource_detector::detail::GetCommand(pid); +#endif + std::string expected_command = + opentelemetry::resource_detector::detail::ExtractCommand(command_line_path); + EXPECT_EQ(command, expected_command); +} From 20712ac0bbf0ba7f3fd3900f7cfdefa2dd1ee6e8 Mon Sep 17 00:00:00 2001 From: nikhilbhatia08 Date: Tue, 19 Aug 2025 03:25:22 +0530 Subject: [PATCH 7/9] minor error fix --- resource_detectors/test/process_detector_test.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/resource_detectors/test/process_detector_test.cc b/resource_detectors/test/process_detector_test.cc index 90e165a37f..374a3a61cb 100644 --- a/resource_detectors/test/process_detector_test.cc +++ b/resource_detectors/test/process_detector_test.cc @@ -141,9 +141,8 @@ TEST(ProcessDetectorUtilsTest, GetCommandTest) // This is the path to get the command that was used to start the process std::string command_line_path = opentelemetry::resource_detector::detail::FormFilePath(pid, "cmdline"); - command = opentelemetry::resource_detector::detail::GetCommand(pid); + command = opentelemetry::resource_detector::detail::ExtractCommand(command_line_path); #endif - std::string expected_command = - opentelemetry::resource_detector::detail::ExtractCommand(command_line_path); + std::string expected_command = opentelemetry::resource_detector::detail::GetCommand(pid); EXPECT_EQ(command, expected_command); } From e92187d07e4d360ce37e9ddf989c17f555eb7ac3 Mon Sep 17 00:00:00 2001 From: nikhilbhatia08 Date: Wed, 20 Aug 2025 01:43:42 +0530 Subject: [PATCH 8/9] todo for darwin --- .../resource_detectors/detail/process_detector_utils.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resource_detectors/include/opentelemetry/resource_detectors/detail/process_detector_utils.h b/resource_detectors/include/opentelemetry/resource_detectors/detail/process_detector_utils.h index f316fcdfc7..aeb61a8e28 100644 --- a/resource_detectors/include/opentelemetry/resource_detectors/detail/process_detector_utils.h +++ b/resource_detectors/include/opentelemetry/resource_detectors/detail/process_detector_utils.h @@ -22,10 +22,10 @@ std::string FormFilePath(const int32_t &pid, const char *process_type); /** * Retrieves the absolute file system path to the executable for a given PID. - * * Platform-specific behavior: * - Windows: Uses OpenProcess() + GetProcessImageFileNameW(). * - Linux/Unix: Reads the /proc//exe symbolic link. + * - TODO: Need to implement for Darwin * * @param pid Process ID. */ @@ -36,6 +36,7 @@ std::string GetExecutablePath(const int32_t &pid); * Platform-specific behavior: * - Windows: Uses GetCommandLineW() to get the command of the current process. * - Linux/Unix: Reads the zeroth string of /proc//cmdline file. + * - TODO: Need to implement for Darwin */ std::string ExtractCommand(const std::string &command_line_path); From 66c4b49e2125d6aab131e875769b6ba50ba8624e Mon Sep 17 00:00:00 2001 From: nikhilbhatia08 Date: Wed, 20 Aug 2025 10:36:41 +0530 Subject: [PATCH 9/9] buffer len fix --- resource_detectors/process_detector_utils.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/resource_detectors/process_detector_utils.cc b/resource_detectors/process_detector_utils.cc index 868e65890e..27a083ea7e 100644 --- a/resource_detectors/process_detector_utils.cc +++ b/resource_detectors/process_detector_utils.cc @@ -109,6 +109,15 @@ std::string FormFilePath(const int32_t &pid, const char *process_type) { char buff[64]; int len = std::snprintf(buff, sizeof(buff), "/proc/%d/%s", pid, process_type); + if (len < 0) + { + // in case snprintf fails + return std::string(); + } + if (len >= static_cast(sizeof(buff))) + { + return std::string(buff, sizeof(buff) - 1); + } return std::string(buff, len); }