Skip to content

Commit d660b3b

Browse files
[resource_detectors] implementation of remaining process attributes (#3603)
1 parent 6bc8349 commit d660b3b

File tree

4 files changed

+124
-65
lines changed

4 files changed

+124
-65
lines changed

resource_detectors/include/opentelemetry/resource_detectors/detail/process_detector_utils.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <stdint.h>
77
#include <string>
8+
#include <vector>
89

910
#include "opentelemetry/version.h"
1011

@@ -32,20 +33,20 @@ std::string FormFilePath(const int32_t &pid, const char *process_type);
3233
std::string GetExecutablePath(const int32_t &pid);
3334

3435
/**
35-
* Retrieves the command used to launch the process for a given PID.
36+
* Extracts the command-line arguments and the command.
3637
* Platform-specific behavior:
37-
* - Windows: Uses GetCommandLineW() to get the command of the current process.
38-
* - Linux/Unix: Reads the zeroth string of /proc/<pid>/cmdline file.
38+
* - Windows: Uses CommandLineToArgvW() to parse the command line.
39+
* - Linux/Unix: Reads the /proc/<pid>/cmdline file and splits it into command and arguments.
3940
* - TODO: Need to implement for Darwin
4041
*/
41-
std::string ExtractCommand(const std::string &command_line_path);
42+
std::vector<std::string> ExtractCommandWithArgs(const std::string &command_line_path);
4243

4344
/**
44-
* Retrieves the command used to launch the process for a given PID.
45-
* This function is a wrapper around ExtractCommand() and is provided for convenience and
46-
* testability of ExtractCommand().
45+
* Retrieves the command-line arguments and the command used to launch the process for a given PID.
46+
* This function is a wrapper around ExtractCommandWithArgs() and is provided for convenience and
47+
* testability of ExtractCommandWithArgs().
4748
*/
48-
std::string GetCommand(const int32_t &pid);
49+
std::vector<std::string> GetCommandWithArgs(const int32_t &pid);
4950

5051
} // namespace detail
5152
} // namespace resource_detector

resource_detectors/process_detector.cc

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <string>
1717
#include <unordered_map>
1818
#include <utility>
19+
#include <vector>
1920

2021
#ifdef _MSC_VER
2122
# include <process.h>
@@ -50,16 +51,19 @@ opentelemetry::sdk::resource::Resource ProcessResourceDetector::Detect() noexcep
5051

5152
try
5253
{
53-
std::string command = opentelemetry::resource_detector::detail::GetCommand(pid);
54-
if (!command.empty())
54+
std::vector<std::string> command_with_args =
55+
opentelemetry::resource_detector::detail::GetCommandWithArgs(pid);
56+
if (!command_with_args.empty())
5557
{
56-
attributes[semconv::process::kProcessCommand] = std::move(command);
58+
// Commented until they are properly sanitized
59+
// attributes[semconv::process::kProcessCommand] = command_with_args[0];
60+
// attributes[semconv::process::kProcessCommandArgs] = std::move(command_with_args);
5761
}
5862
}
5963
catch (const std::exception &ex)
6064
{
61-
OTEL_INTERNAL_LOG_ERROR("[Process Resource Detector] " << "Error extracting command: "
62-
<< ex.what());
65+
OTEL_INTERNAL_LOG_ERROR("[Process Resource Detector] "
66+
<< "Error extracting command with arguments: " << ex.what());
6367
}
6468

6569
return ResourceDetector::Create(attributes);

resource_detectors/process_detector_utils.cc

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55

66
#include <fstream>
77
#include <string>
8+
#include <vector>
89

910
#ifdef _MSC_VER
1011
// clang-format off
1112
# include <windows.h>
1213
# include <psapi.h>
14+
# include <shellapi.h>
15+
# pragma comment(lib, "shell32.lib")
1316
// clang-format on
1417
#else
1518
# include <sys/types.h>
@@ -68,43 +71,52 @@ std::string GetExecutablePath(const int32_t &pid)
6871
#endif
6972
}
7073

71-
std::string GetCommand(const int32_t &pid)
74+
std::vector<std::string> ExtractCommandWithArgs(const std::string &command_line_path)
7275
{
73-
#ifdef _MSC_VER
74-
// On Windows, GetCommandLineW only works for the CURRENT process,
75-
// so we ignore `pid` and just return the current process's command line.
76-
LPCWSTR wcmd = GetCommandLineW();
77-
if (!wcmd)
76+
std::vector<std::string> commands;
77+
std::ifstream command_line_file(command_line_path, std::ios::in | std::ios::binary);
78+
std::string command;
79+
while (std::getline(command_line_file, command, '\0'))
7880
{
79-
return std::string();
81+
if (!command.empty())
82+
{
83+
commands.push_back(command);
84+
}
8085
}
86+
return commands;
87+
}
8188

82-
// Convert UTF-16 to UTF-8
83-
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, NULL, 0, NULL, NULL);
84-
if (size_needed <= 0)
89+
std::vector<std::string> GetCommandWithArgs(const int32_t &pid)
90+
{
91+
#ifdef _MSC_VER
92+
int argc = 0;
93+
LPWSTR *argvW = CommandLineToArgvW(GetCommandLineW(), &argc);
94+
if (!argvW)
8595
{
86-
return std::string();
96+
return {}; // returns an empty vector if CommandLineToArgvW fails
8797
}
8898

89-
std::string utf8_command(size_needed - 1, 0); // exclude null terminator
90-
WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, &utf8_command[0], size_needed, NULL, NULL);
99+
std::vector<std::string> args;
100+
for (int i = 0; i < argc; i++)
101+
{
102+
// Convert UTF-16 to UTF-8
103+
int size_needed = WideCharToMultiByte(CP_UTF8, 0, argvW[i], -1, NULL, 0, NULL, NULL);
104+
if (size_needed > 0)
105+
{
106+
std::string arg(size_needed - 1, 0);
107+
WideCharToMultiByte(CP_UTF8, 0, argvW[i], -1, &arg[0], size_needed, NULL, NULL);
108+
args.push_back(arg);
109+
}
110+
}
91111

92-
return utf8_command;
112+
LocalFree(argvW);
113+
return args;
93114
#else
94-
// This is the path to get the command that was used to start the process
95115
std::string command_line_path = FormFilePath(pid, kCmdlineName);
96-
return ExtractCommand(command_line_path);
116+
return ExtractCommandWithArgs(command_line_path);
97117
#endif
98118
}
99119

100-
std::string ExtractCommand(const std::string &command_line_path)
101-
{
102-
std::string command;
103-
std::ifstream command_line_file(command_line_path, std::ios::in | std::ios::binary);
104-
std::getline(command_line_file, command, '\0');
105-
return command;
106-
}
107-
108120
std::string FormFilePath(const int32_t &pid, const char *process_type)
109121
{
110122
char buff[64];

resource_detectors/test/process_detector_test.cc

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <stdint.h>
66
#include <fstream>
77
#include <string>
8+
#include <vector>
89

910
#ifdef _MSC_VER
1011
// clang-format off
@@ -31,30 +32,32 @@ TEST(ProcessDetectorUtilsTest, FormFilePath)
3132
EXPECT_EQ(exe_path, "/proc/1234/exe");
3233
}
3334

34-
TEST(ProcessDetectorUtilsTest, ExtractCommand)
35+
TEST(ProcessDetectorUtilsTest, ExtractCommandWithArgs)
3536
{
36-
std::string filename{"test_command.txt"};
37+
std::string filename{"test_command_args.txt"};
3738

3839
{
3940
std::ofstream outfile(filename, std::ios::binary);
4041
const char raw_data[] = "test_command\0arg1\0arg2\0arg3\0";
4142
outfile.write(raw_data, sizeof(raw_data) - 1);
4243
}
4344

44-
std::string command = opentelemetry::resource_detector::detail::ExtractCommand(filename);
45-
EXPECT_EQ(command, std::string{"test_command"});
45+
std::vector<std::string> args =
46+
opentelemetry::resource_detector::detail::ExtractCommandWithArgs(filename);
47+
EXPECT_EQ(args, (std::vector<std::string>{"test_command", "arg1", "arg2", "arg3"}));
4648

4749
std::remove(filename.c_str()); // Cleanup
4850
}
4951

50-
TEST(ProcessDetectorUtilsTest, EmptyCommandFile)
52+
TEST(ProcessDetectorUtilsTest, EmptyCommandWithArgsFile)
5153
{
52-
std::string filename{"empty_command.txt"};
54+
std::string filename{"empty_command_args.txt"};
5355
std::ofstream outfile(filename, std::ios::binary);
5456
outfile.close();
5557

56-
std::string command = opentelemetry::resource_detector::detail::ExtractCommand(filename);
57-
EXPECT_EQ(command, std::string{""});
58+
std::vector<std::string> args =
59+
opentelemetry::resource_detector::detail::ExtractCommandWithArgs(filename);
60+
EXPECT_TRUE(args.empty());
5861

5962
std::remove(filename.c_str()); // Cleanup
6063
}
@@ -109,40 +112,79 @@ TEST(ProcessDetectorUtilsTest, GetExecutablePathTest)
109112
EXPECT_EQ(path, expected_path);
110113
}
111114

112-
TEST(ProcessDetectorUtilsTest, GetCommandTest)
115+
TEST(ProcessDetectorUtilsTest, CommandTest)
113116
{
114117
int32_t pid = getpid();
115118
std::string command;
116119
#ifdef _MSC_VER
117-
// On Windows, GetCommandLineW only works for the CURRENT process,
118-
// so we ignore `pid` and just return the current process's command line.
119-
LPCWSTR wcmd = GetCommandLineW();
120-
if (!wcmd)
120+
int argc = 0;
121+
LPWSTR *argvW = CommandLineToArgvW(GetCommandLineW(), &argc);
122+
123+
if (argvW && argc > 0)
121124
{
122-
command = std::string();
125+
int size_needed = WideCharToMultiByte(CP_UTF8, 0, argvW[0], -1, NULL, 0, NULL, NULL);
126+
if (size_needed > 0)
127+
{
128+
std::string arg(size_needed - 1, 0);
129+
WideCharToMultiByte(CP_UTF8, 0, argvW[0], -1, &arg[0], size_needed, NULL, NULL);
130+
command = arg;
131+
}
132+
133+
LocalFree(argvW);
123134
}
124135
else
125136
{
137+
command = std::string();
138+
}
139+
#else
140+
std::string command_line_path =
141+
opentelemetry::resource_detector::detail::FormFilePath(pid, "cmdline");
142+
std::ifstream command_line_file(command_line_path, std::ios::in | std::ios::binary);
143+
std::getline(command_line_file, command, '\0');
144+
#endif
145+
std::vector<std::string> expected_command_with_args =
146+
opentelemetry::resource_detector::detail::GetCommandWithArgs(pid);
147+
std::string expected_command;
148+
if (!expected_command_with_args.empty())
149+
{
150+
expected_command = expected_command_with_args[0];
151+
}
152+
EXPECT_EQ(command, expected_command);
153+
}
126154

127-
// Convert UTF-16 to UTF-8
128-
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, NULL, 0, NULL, NULL);
129-
if (size_needed <= 0)
130-
{
131-
command = std::string();
132-
}
133-
else
155+
TEST(ProcessDetectorUtilsTest, GetCommandWithArgsTest)
156+
{
157+
int32_t pid = getpid();
158+
std::vector<std::string> args;
159+
#ifdef _MSC_VER
160+
int argc = 0;
161+
LPWSTR *argvW = CommandLineToArgvW(GetCommandLineW(), &argc);
162+
if (!argvW)
163+
{
164+
args = {};
165+
}
166+
else
167+
{
168+
for (int i = 0; i < argc; i++)
134169
{
135-
std::string utf8_command(size_needed - 1, 0); // exclude null terminator
136-
WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, &utf8_command[0], size_needed, NULL, NULL);
137-
command = utf8_command;
170+
// Convert UTF-16 to UTF-8
171+
int size_needed = WideCharToMultiByte(CP_UTF8, 0, argvW[i], -1, NULL, 0, NULL, NULL);
172+
if (size_needed > 0)
173+
{
174+
std::string arg(size_needed - 1, 0);
175+
WideCharToMultiByte(CP_UTF8, 0, argvW[i], -1, &arg[0], size_needed, NULL, NULL);
176+
args.push_back(arg);
177+
}
138178
}
139179
}
180+
181+
LocalFree(argvW);
140182
#else
141-
// This is the path to get the command that was used to start the process
142183
std::string command_line_path =
143184
opentelemetry::resource_detector::detail::FormFilePath(pid, "cmdline");
144-
command = opentelemetry::resource_detector::detail::ExtractCommand(command_line_path);
185+
args = opentelemetry::resource_detector::detail::ExtractCommandWithArgs(command_line_path);
145186
#endif
146-
std::string expected_command = opentelemetry::resource_detector::detail::GetCommand(pid);
147-
EXPECT_EQ(command, expected_command);
187+
std::vector<std::string> expected_args =
188+
opentelemetry::resource_detector::detail::GetCommandWithArgs(pid);
189+
EXPECT_EQ(args, expected_args);
148190
}

0 commit comments

Comments
 (0)