Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions src/stirling/source_connectors/socket_tracer/uprobe_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ DEFINE_double(stirling_rescan_exp_backoff_factor, 2.0,
"Exponential backoff factor used in decided how often to rescan binaries for "
"dynamically loaded libraries");

DEFINE_string(
stirling_uprobe_opt_out, "",
"Comma separated list of binary filenames that should be excluded from uprobe attachment."
"For a binary at path /path/to/binary, the filename would be binary");

namespace px {
namespace stirling {

Expand All @@ -62,8 +67,13 @@ using ::px::system::KernelVersion;
using ::px::system::KernelVersionOrder;
using ::px::system::ProcPidRootPath;

constexpr std::string_view kUprobeSkippedMessage =
"binary filename '$0' contained in uprobe opt out list, skipping.";

UProbeManager::UProbeManager(bpf_tools::BCCWrapper* bcc) : bcc_(bcc) {
proc_parser_ = std::make_unique<system::ProcParser>();
auto opt_out_list = absl::StrSplit(FLAGS_stirling_uprobe_opt_out, ",", absl::SkipWhitespace());
uprobe_opt_out_ = absl::flat_hash_set<std::string>(opt_out_list.begin(), opt_out_list.end());
}

void UProbeManager::Init(bool disable_go_tls_tracing, bool enable_http2_tracing,
Expand Down Expand Up @@ -447,8 +457,8 @@ StatusOr<std::array<UProbeTmpl, 6>> UProbeManager::GetNodeOpensslUProbeTmpls(con
return iter->second;
}

StatusOr<int> UProbeManager::AttachOpenSSLUProbesOnStaticBinary(const uint32_t pid) {
PX_ASSIGN_OR_RETURN(const std::filesystem::path proc_exe, proc_parser_->GetExePath(pid));
StatusOr<int> UProbeManager::AttachOpenSSLUProbesOnStaticBinary(
const uint32_t pid, const std::filesystem::path& proc_exe) {
const auto host_proc_exe = ProcPidRootPath(pid, proc_exe);

PX_ASSIGN_OR_RETURN(auto elf_reader, ElfReader::Create(host_proc_exe));
Expand All @@ -467,13 +477,19 @@ StatusOr<int> UProbeManager::AttachOpenSSLUProbesOnStaticBinary(const uint32_t p
return kOpenSSLUProbes.size();
}

StatusOr<int> UProbeManager::AttachNodeJsOpenSSLUprobes(const uint32_t pid) {
PX_ASSIGN_OR_RETURN(const std::filesystem::path proc_exe, proc_parser_->GetExePath(pid));

StatusOr<int> UProbeManager::AttachNodeJsOpenSSLUprobes(const uint32_t pid,
const std::filesystem::path& proc_exe) {
if (DetectApplication(proc_exe) != Application::kNode) {
return 0;
}

const std::string exe_cmdline = proc_parser_->GetPIDCmdline(pid);
const auto node_application_filepath = GetNodeApplicationFilename(exe_cmdline);
if (node_application_filepath.has_value() &&
uprobe_opt_out_.contains(node_application_filepath.value())) {
VLOG(1) << absl::Substitute(kUprobeSkippedMessage, node_application_filepath.value());
return 0;
}
const auto host_proc_exe = ProcPidRootPath(pid, proc_exe);

const auto [_, inserted] = nodejs_binaries_.insert(host_proc_exe.string());
Expand Down Expand Up @@ -608,6 +624,13 @@ int UProbeManager::DeployOpenSSLUProbes(const absl::flat_hash_set<md::UPID>& pid
continue;
}

PX_ASSIGN_OR(const auto exe_path, proc_parser_->GetExePath(pid.pid()), continue);

if (uprobe_opt_out_.contains(exe_path.filename().string())) {
VLOG(1) << absl::Substitute(kUprobeSkippedMessage, exe_path.string());
continue;
}

auto count_or = AttachOpenSSLUProbesOnDynamicLib(pid.pid());
if (count_or.ok()) {
uprobe_count += count_or.ValueOrDie();
Expand All @@ -622,7 +645,7 @@ int UProbeManager::DeployOpenSSLUProbes(const absl::flat_hash_set<md::UPID>& pid
count_or.ToString());
}

count_or = AttachNodeJsOpenSSLUprobes(pid.pid());
count_or = AttachNodeJsOpenSSLUprobes(pid.pid(), exe_path);
if (count_or.ok()) {
uprobe_count += count_or.ValueOrDie();
VLOG(1) << absl::Substitute(
Expand All @@ -640,7 +663,7 @@ int UProbeManager::DeployOpenSSLUProbes(const absl::flat_hash_set<md::UPID>& pid

// Attach uprobes to statically linked applications only if no other probes have been attached.
if (FLAGS_stirling_trace_static_tls_binaries && count_or.ok() && count_or.ValueOrDie() == 0) {
count_or = AttachOpenSSLUProbesOnStaticBinary(pid.pid());
count_or = AttachOpenSSLUProbesOnStaticBinary(pid.pid(), exe_path);

if (count_or.ok() && count_or.ValueOrDie() > 0) {
uprobe_count += count_or.ValueOrDie();
Expand Down Expand Up @@ -817,6 +840,12 @@ int UProbeManager::DeployGoUProbes(const absl::flat_hash_set<md::UPID>& pids) {
static int32_t kPID = getpid();

for (const auto& [binary, pid_vec] : ConvertPIDsListToMap(pids)) {
std::filesystem::path binary_path(binary);
auto binary_filepath = binary_path.filename().string();
if (uprobe_opt_out_.contains(binary_filepath)) {
VLOG(1) << absl::Substitute(kUprobeSkippedMessage, binary_filepath);
continue;
}
// Don't bother rescanning binaries that have been scanned before to avoid unnecessary work.
if (!scanned_binaries_.insert(binary).second) {
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ class UProbeManager {
* @return The number of uprobes deployed. It is not an error if the binary
* does not use OpenSSL; instead the return value will be zero.
*/
StatusOr<int> AttachNodeJsOpenSSLUprobes(uint32_t pid);
StatusOr<int> AttachNodeJsOpenSSLUprobes(uint32_t pid, const std::filesystem::path& binary_path);

/**
* Attaches the required probes for TLS tracing to the specified PID if the binary is
Expand All @@ -551,7 +551,8 @@ class UProbeManager {
* @return The number of uprobes deployed. It is not an error if the binary
* does not contain the necessary symbols to probe; instead the return value will be zero.
*/
StatusOr<int> AttachOpenSSLUProbesOnStaticBinary(uint32_t pid);
StatusOr<int> AttachOpenSSLUProbesOnStaticBinary(uint32_t pid,
const std::filesystem::path& binary_path);

/**
* Calls BCCWrapper.AttachUProbe() with a probe template and log any errors to the probe status
Expand Down Expand Up @@ -628,6 +629,7 @@ class UProbeManager {
// Without clean-up, these could consume more-and-more memory.
absl::flat_hash_set<std::string> openssl_probed_binaries_;
absl::flat_hash_set<std::string> scanned_binaries_;
absl::flat_hash_set<std::string> uprobe_opt_out_;
absl::flat_hash_set<std::string> go_probed_binaries_;
absl::flat_hash_set<std::string> go_http2_probed_binaries_;
absl::flat_hash_set<std::string> go_tls_probed_binaries_;
Expand Down
17 changes: 17 additions & 0 deletions src/stirling/utils/detect_application.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,23 @@ Application DetectApplication(const std::filesystem::path& exe) {
return Application::kUnknown;
}

// This method returns the main nodejs application file from a command line. See the following
// examples below:
//
// "node /usr/bin/test.js" -> "test.js"
// "node --node-memory-debug /usr/bin/test.js" -> "test.js"
// "node /usr/bin/test" -> std::nullopt
std::optional<std::string> GetNodeApplicationFilename(std::string_view cmdline) {
std::vector<std::string> cmdline_parts = absl::StrSplit(cmdline, ' ');
for (const auto& part : cmdline_parts) {
if (absl::EndsWith(part, ".js")) {
std::filesystem::path path(part);
return path.filename();
}
}
return {};
}

bool operator<(const SemVer& lhs, const SemVer& rhs) {
std::vector<int> lhs_vec = {lhs.major, lhs.minor, lhs.patch};
std::vector<int> rhs_vec = {rhs.major, rhs.minor, rhs.patch};
Expand Down
3 changes: 3 additions & 0 deletions src/stirling/utils/detect_application.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ enum class Application {
// Returns the application of the input executable.
Application DetectApplication(const std::filesystem::path& exe);

// Returns the filename of a node application from the command line.
std::optional<std::string> GetNodeApplicationFilename(std::string_view cmdline);

// Describes a semantic versioning number.
struct SemVer {
int major = 0;
Expand Down
6 changes: 6 additions & 0 deletions src/stirling/utils/detect_application_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ TEST(DetectApplicationTest, ResultsAreAsExpected) {
EXPECT_EQ(Application::kNode, DetectApplication("/usr/bin/nodejs"));
}

TEST(GetNodeApplicatFilenameTest, ResultsAreAsExpected) {
EXPECT_EQ(GetNodeApplicationFilename("node /usr/bin/test.js"), "test.js");
EXPECT_EQ(GetNodeApplicationFilename("node --node-memory-debug /usr/bin/test.js"), "test.js");
EXPECT_FALSE(GetNodeApplicationFilename("node /usr/bin/test").has_value());
}

TEST(GetSemVerTest, AsExpected) {
ASSERT_OK_AND_ASSIGN(SemVer sem_ver, GetSemVer("v1.12.13-test", true));
EXPECT_EQ(sem_ver.major, 1);
Expand Down