Skip to content

Commit 38474a7

Browse files
committed
Add --stirling_uprobe_opt_out cli flag to allow opting specific binaries out of uprobe attachment
Signed-off-by: Dom Del Nano <ddelnano@gmail.com>
1 parent 1d41f9b commit 38474a7

File tree

5 files changed

+58
-11
lines changed

5 files changed

+58
-11
lines changed

src/stirling/source_connectors/socket_tracer/uprobe_manager.cc

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ DEFINE_double(stirling_rescan_exp_backoff_factor, 2.0,
5252
"Exponential backoff factor used in decided how often to rescan binaries for "
5353
"dynamically loaded libraries");
5454

55+
DEFINE_string(
56+
stirling_uprobe_opt_out, "",
57+
"Comma separated list of binary filenames that should be excluded from uprobe attachment."
58+
"For a binary at path /path/to/binary, the filename would be binary");
59+
5560
namespace px {
5661
namespace stirling {
5762

@@ -64,6 +69,7 @@ using ::px::system::ProcPidRootPath;
6469

6570
UProbeManager::UProbeManager(bpf_tools::BCCWrapper* bcc) : bcc_(bcc) {
6671
proc_parser_ = std::make_unique<system::ProcParser>();
72+
uprobe_opt_out_ = absl::StrSplit(FLAGS_stirling_uprobe_opt_out, ",", absl::SkipWhitespace());
6773
}
6874

6975
void UProbeManager::Init(bool disable_go_tls_tracing, bool enable_http2_tracing,
@@ -447,8 +453,8 @@ StatusOr<std::array<UProbeTmpl, 6>> UProbeManager::GetNodeOpensslUProbeTmpls(con
447453
return iter->second;
448454
}
449455

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

454460
PX_ASSIGN_OR_RETURN(auto elf_reader, ElfReader::Create(host_proc_exe));
@@ -467,13 +473,18 @@ StatusOr<int> UProbeManager::AttachOpenSSLUProbesOnStaticBinary(const uint32_t p
467473
return kOpenSSLUProbes.size();
468474
}
469475

470-
StatusOr<int> UProbeManager::AttachNodeJsOpenSSLUprobes(const uint32_t pid) {
471-
PX_ASSIGN_OR_RETURN(const std::filesystem::path proc_exe, proc_parser_->GetExePath(pid));
472-
476+
StatusOr<int> UProbeManager::AttachNodeJsOpenSSLUprobes(const uint32_t pid,
477+
const std::filesystem::path& proc_exe) {
473478
if (DetectApplication(proc_exe) != Application::kNode) {
474479
return 0;
475480
}
476481

482+
const std::string exe_cmdline = proc_parser_->GetPIDCmdline(pid);
483+
const std::string node_application_filepath = GetNodeApplicationFilename(exe_cmdline);
484+
if (std::find(uprobe_opt_out_.begin(), uprobe_opt_out_.end(), node_application_filepath) !=
485+
uprobe_opt_out_.end()) {
486+
return 0;
487+
}
477488
const auto host_proc_exe = ProcPidRootPath(pid, proc_exe);
478489

479490
const auto [_, inserted] = nodejs_binaries_.insert(host_proc_exe.string());
@@ -551,7 +562,7 @@ namespace {
551562

552563
// Convert PID list from list of UPIDs to a map with key=binary name, value=PIDs
553564
std::map<std::string, std::vector<int32_t>> ConvertPIDsListToMap(
554-
const absl::flat_hash_set<md::UPID>& upids) {
565+
const absl::flat_hash_set<md::UPID>& upids, const std::vector<std::string>& binary_filter) {
555566
const system::ProcParser proc_parser;
556567

557568
// Convert to a map of binaries, with the upids that are instances of that binary.
@@ -565,6 +576,11 @@ std::map<std::string, std::vector<int32_t>> ConvertPIDsListToMap(
565576
if (!fs::Exists(host_exe_path)) {
566577
continue;
567578
}
579+
// Add filter here if the executable should be omitted
580+
if (std::find(binary_filter.begin(), binary_filter.end(), host_exe_path.filename()) !=
581+
binary_filter.end()) {
582+
continue;
583+
}
568584
pids[host_exe_path.string()].push_back(upid.pid());
569585
}
570586

@@ -608,6 +624,15 @@ int UProbeManager::DeployOpenSSLUProbes(const absl::flat_hash_set<md::UPID>& pid
608624
continue;
609625
}
610626

627+
PX_ASSIGN_OR(const auto exe_path, proc_parser_->GetExePath(pid.pid()), continue);
628+
629+
if (std::find(uprobe_opt_out_.begin(), uprobe_opt_out_.end(), exe_path.filename()) !=
630+
uprobe_opt_out_.end()) {
631+
VLOG(1) << absl::Substitute(
632+
"binary filename '$0' contained in uprobe opt out list, skipping.", exe_path.string());
633+
continue;
634+
}
635+
611636
auto count_or = AttachOpenSSLUProbesOnDynamicLib(pid.pid());
612637
if (count_or.ok()) {
613638
uprobe_count += count_or.ValueOrDie();
@@ -622,7 +647,7 @@ int UProbeManager::DeployOpenSSLUProbes(const absl::flat_hash_set<md::UPID>& pid
622647
count_or.ToString());
623648
}
624649

625-
count_or = AttachNodeJsOpenSSLUprobes(pid.pid());
650+
count_or = AttachNodeJsOpenSSLUprobes(pid.pid(), exe_path);
626651
if (count_or.ok()) {
627652
uprobe_count += count_or.ValueOrDie();
628653
VLOG(1) << absl::Substitute(
@@ -640,7 +665,7 @@ int UProbeManager::DeployOpenSSLUProbes(const absl::flat_hash_set<md::UPID>& pid
640665

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

645670
if (count_or.ok() && count_or.ValueOrDie() > 0) {
646671
uprobe_count += count_or.ValueOrDie();
@@ -816,7 +841,7 @@ int UProbeManager::DeployGoUProbes(const absl::flat_hash_set<md::UPID>& pids) {
816841

817842
static int32_t kPID = getpid();
818843

819-
for (const auto& [binary, pid_vec] : ConvertPIDsListToMap(pids)) {
844+
for (const auto& [binary, pid_vec] : ConvertPIDsListToMap(pids, uprobe_opt_out_)) {
820845
// Don't bother rescanning binaries that have been scanned before to avoid unnecessary work.
821846
if (!scanned_binaries_.insert(binary).second) {
822847
continue;

src/stirling/source_connectors/socket_tracer/uprobe_manager.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ class UProbeManager {
538538
* @return The number of uprobes deployed. It is not an error if the binary
539539
* does not use OpenSSL; instead the return value will be zero.
540540
*/
541-
StatusOr<int> AttachNodeJsOpenSSLUprobes(uint32_t pid);
541+
StatusOr<int> AttachNodeJsOpenSSLUprobes(uint32_t pid, const std::filesystem::path& binary_path);
542542

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

556557
/**
557558
* Calls BCCWrapper.AttachUProbe() with a probe template and log any errors to the probe status
@@ -628,6 +629,7 @@ class UProbeManager {
628629
// Without clean-up, these could consume more-and-more memory.
629630
absl::flat_hash_set<std::string> openssl_probed_binaries_;
630631
absl::flat_hash_set<std::string> scanned_binaries_;
632+
std::vector<std::string> uprobe_opt_out_;
631633
absl::flat_hash_set<std::string> go_probed_binaries_;
632634
absl::flat_hash_set<std::string> go_http2_probed_binaries_;
633635
absl::flat_hash_set<std::string> go_tls_probed_binaries_;

src/stirling/utils/detect_application.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,17 @@ Application DetectApplication(const std::filesystem::path& exe) {
4949
return Application::kUnknown;
5050
}
5151

52+
std::string GetNodeApplicationFilename(std::string_view cmdline) {
53+
std::vector<std::string> cmdline_parts = absl::StrSplit(cmdline, ' ');
54+
for (const auto& part : cmdline_parts) {
55+
if (absl::EndsWith(part, ".js")) {
56+
std::filesystem::path path(part);
57+
return path.filename();
58+
}
59+
}
60+
return "";
61+
}
62+
5263
bool operator<(const SemVer& lhs, const SemVer& rhs) {
5364
std::vector<int> lhs_vec = {lhs.major, lhs.minor, lhs.patch};
5465
std::vector<int> rhs_vec = {rhs.major, rhs.minor, rhs.patch};

src/stirling/utils/detect_application.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ enum class Application {
3636
// Returns the application of the input executable.
3737
Application DetectApplication(const std::filesystem::path& exe);
3838

39+
// Returns the filename of a node application from the command line.
40+
std::string GetNodeApplicationFilename(std::string_view cmdline);
41+
3942
// Describes a semantic versioning number.
4043
struct SemVer {
4144
int major = 0;

src/stirling/utils/detect_application_test.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ TEST(DetectApplicationTest, ResultsAreAsExpected) {
3434
EXPECT_EQ(Application::kNode, DetectApplication("/usr/bin/nodejs"));
3535
}
3636

37+
TEST(GetNodeApplicatFilenameTest, ResultsAreAsExpected) {
38+
EXPECT_EQ(GetNodeApplicationFilename("node /usr/bin/test.js"), "test.js");
39+
EXPECT_EQ(GetNodeApplicationFilename("node --node-memory-debug /usr/bin/test.js"), "test.js");
40+
EXPECT_EQ(GetNodeApplicationFilename("node /usr/bin/test"), "");
41+
}
42+
3743
TEST(GetSemVerTest, AsExpected) {
3844
ASSERT_OK_AND_ASSIGN(SemVer sem_ver, GetSemVer("v1.12.13-test", true));
3945
EXPECT_EQ(sem_ver.major, 1);

0 commit comments

Comments
 (0)