diff --git a/src/stirling/benchmarks/bpf_map_ops_benchmark.cc b/src/stirling/benchmarks/bpf_map_ops_benchmark.cc index 3213254c1d4..e8dea247b3b 100644 --- a/src/stirling/benchmarks/bpf_map_ops_benchmark.cc +++ b/src/stirling/benchmarks/bpf_map_ops_benchmark.cc @@ -27,7 +27,7 @@ #include "src/stirling/utils/proc_path_tools.h" using ::px::stirling::GetSelfPath; -using ::px::stirling::bpf_tools::BCCWrapper; +using ::px::stirling::bpf_tools::BCCWrapperImpl; using ::px::stirling::bpf_tools::BPFProbeAttachType; using ::px::stirling::bpf_tools::UProbeSpec; using ::px::stirling::bpf_tools::WrappedBCCMap; @@ -50,7 +50,7 @@ NO_OPT_ATTR void BPFMapPopulateTrigger(int n) { } } -void SetupCleanupProbe(BCCWrapper* bcc) { +void SetupCleanupProbe(BCCWrapperImpl* bcc) { std::filesystem::path self_path = GetSelfPath().ValueOrDie(); auto elf_reader_or_s = px::stirling::obj_tools::ElfReader::Create(self_path.string()); PX_CHECK_OK(elf_reader_or_s.status()); @@ -75,7 +75,7 @@ void SetupCleanupProbe(BCCWrapper* bcc) { PX_CHECK_OK(bcc->AttachUProbe(uprobe)); } -void SetupPopulateProbe(BCCWrapper* bcc) { +void SetupPopulateProbe(BCCWrapperImpl* bcc) { std::filesystem::path self_path = GetSelfPath().ValueOrDie(); auto elf_reader_or_s = px::stirling::obj_tools::ElfReader::Create(self_path.string()); PX_CHECK_OK(elf_reader_or_s.status()); @@ -102,7 +102,7 @@ void SetupPopulateProbe(BCCWrapper* bcc) { // NOLINTNEXTLINE : runtime/references. static void BM_userspace_update_remove(benchmark::State& state) { - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; std::string_view kProgram = "BPF_HASH(map, int, int);"; PX_CHECK_OK(bcc_wrapper.InitBPFProgram(kProgram)); auto bpf_map = WrappedBCCMap::Create(&bcc_wrapper, "map"); @@ -124,7 +124,7 @@ static void BM_userspace_update_remove(benchmark::State& state) { // NOLINTNEXTLINE : runtime/references. static void BM_bpf_triggered_update_remove(benchmark::State& state) { - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; std::string_view kProgram = R"( #include @@ -192,7 +192,7 @@ int map_populate_uprobe(struct pt_regs* ctx) { // NOLINTNEXTLINE : runtime/references. static void BM_userspace_update_get_remove(benchmark::State& state) { - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; std::string_view kProgram = "BPF_HASH(map, int, int);"; PX_CHECK_OK(bcc_wrapper.InitBPFProgram(kProgram)); auto bpf_map = WrappedBCCMap::Create(&bcc_wrapper, "map"); @@ -217,7 +217,7 @@ static void BM_userspace_update_get_remove(benchmark::State& state) { // NOLINTNEXTLINE : runtime/references. static void BM_bpf_triggered_update_get_remove(benchmark::State& state) { - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; std::string_view kProgram = R"( #include diff --git a/src/stirling/benchmarks/symbolization_benchmark.cc b/src/stirling/benchmarks/symbolization_benchmark.cc index 52386da5707..118a9afeef2 100644 --- a/src/stirling/benchmarks/symbolization_benchmark.cc +++ b/src/stirling/benchmarks/symbolization_benchmark.cc @@ -24,7 +24,7 @@ #include "src/stirling/obj_tools/address_converter.h" #include "src/stirling/obj_tools/elf_reader.h" -using ::px::stirling::bpf_tools::BCCWrapper; +using ::px::stirling::bpf_tools::BCCWrapperImpl; using ::px::stirling::bpf_tools::WrappedBCCArrayTable; using ::px::stirling::bpf_tools::WrappedBCCStackTable; using ::px::stirling::obj_tools::ElfReader; @@ -64,7 +64,7 @@ const std::string_view kProgram = R"( } )"; -std::vector CollectStackTrace(BCCWrapper* bcc_wrapper, +std::vector CollectStackTrace(BCCWrapperImpl* bcc_wrapper, const std::filesystem::path& self_path) { ::px::stirling::bpf_tools::UProbeSpec spec = { .binary_path = self_path.string(), @@ -90,7 +90,7 @@ std::vector CollectStackTrace(BCCWrapper* bcc_wrapper, // NOLINTNEXTLINE : runtime/references. static void BM_elf_reader_symbolization(benchmark::State& state) { - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; PX_ASSIGN_OR_EXIT(std::filesystem::path self_path, ::px::fs::ReadSymlink("/proc/self/exe")); std::vector addrs = CollectStackTrace(&bcc_wrapper, self_path); PX_ASSIGN_OR_EXIT(auto elf_reader, @@ -112,7 +112,7 @@ static void BM_elf_reader_symbolization(benchmark::State& state) { // NOLINTNEXTLINE : runtime/references. static void BM_elf_reader_symbolization_indexed(benchmark::State& state) { - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; PX_ASSIGN_OR_EXIT(std::filesystem::path self_path, ::px::fs::ReadSymlink("/proc/self/exe")); std::vector addrs = CollectStackTrace(&bcc_wrapper, self_path); PX_ASSIGN_OR_EXIT(auto elf_reader, ElfReader::Create(self_path.string())); @@ -134,7 +134,7 @@ static void BM_elf_reader_symbolization_indexed(benchmark::State& state) { // NOLINTNEXTLINE : runtime/references. static void BM_bcc_symbolization(benchmark::State& state) { - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; PX_ASSIGN_OR_EXIT(std::filesystem::path self_path, ::px::fs::ReadSymlink("/proc/self/exe")); std::vector addrs = CollectStackTrace(&bcc_wrapper, self_path); auto bcc_symbolizer = WrappedBCCStackTable::Create(&bcc_wrapper, "stack_traces"); diff --git a/src/stirling/bpf_tools/bcc_wrapper.cc b/src/stirling/bpf_tools/bcc_wrapper.cc index 6a83611014a..a8c84b102de 100644 --- a/src/stirling/bpf_tools/bcc_wrapper.cc +++ b/src/stirling/bpf_tools/bcc_wrapper.cc @@ -97,9 +97,9 @@ StatusOr BCCWrapper::ComputeTaskStructOffsets() { return task_struct_offsets_opt_.value(); } -Status BCCWrapper::InitBPFProgram(std::string_view bpf_program, std::vector cflags, - bool requires_linux_headers, - bool always_infer_task_struct_offsets) { +Status BCCWrapperImpl::InitBPFProgram(std::string_view bpf_program, std::vector cflags, + bool requires_linux_headers, + bool always_infer_task_struct_offsets) { using utils::TaskStructOffsets; if (!IsRoot()) { @@ -177,7 +177,7 @@ Status BCCWrapper::InitBPFProgram(std::string_view bpf_program, std::vector& probes) { +Status BCCWrapperImpl::AttachKProbes(const ArrayView& probes) { for (const KProbeSpec& p : probes) { PX_RETURN_IF_ERROR(AttachKProbe(p)); } return Status::OK(); } -Status BCCWrapper::AttachTracepoints(const ArrayView& probes) { +Status BCCWrapperImpl::AttachTracepoints(const ArrayView& probes) { for (const TracepointSpec& spec : probes) { PX_RETURN_IF_ERROR(AttachTracepoint(spec)); } return Status::OK(); } -Status BCCWrapper::AttachUProbes(const ArrayView& probes) { +Status BCCWrapperImpl::AttachUProbes(const ArrayView& probes) { for (const UProbeSpec& p : probes) { PX_RETURN_IF_ERROR(AttachUProbe(p)); } return Status::OK(); } -Status BCCWrapper::AttachSamplingProbes(const ArrayView& probes) { +Status BCCWrapperImpl::AttachSamplingProbes(const ArrayView& probes) { for (const SamplingProbeSpec& p : probes) { PX_RETURN_IF_ERROR(AttachSamplingProbe(p)); } @@ -273,7 +273,7 @@ Status BCCWrapper::AttachSamplingProbes(const ArrayView& prob // This will replace the XDP program previously-attached on the the same device. // Newer kernel allows attaching multiple XDP programs on the same device: // https://lwn.net/Articles/801478/ -Status BCCWrapper::AttachXDP(const std::string& dev_name, const std::string& fn_name) { +Status BCCWrapperImpl::AttachXDP(const std::string& dev_name, const std::string& fn_name) { int fn_fd = -1; ebpf::StatusTuple load_status = bpf_.load_func(fn_name, BPF_PROG_TYPE_XDP, fn_fd); @@ -293,7 +293,7 @@ Status BCCWrapper::AttachXDP(const std::string& dev_name, const std::string& fn_ } // TODO(PL-1294): This can fail in rare cases. See the cited issue. Find the root cause. -Status BCCWrapper::DetachKProbe(const KProbeSpec& probe) { +Status BCCWrapperImpl::DetachKProbe(const KProbeSpec& probe) { VLOG(1) << "Detaching kprobe: " << probe.ToString(); PX_RETURN_IF_ERROR(bpf_.detach_kprobe(GetKProbeTargetName(probe), static_cast(probe.attach_type))); @@ -301,7 +301,7 @@ Status BCCWrapper::DetachKProbe(const KProbeSpec& probe) { return Status::OK(); } -Status BCCWrapper::DetachUProbe(const UProbeSpec& probe) { +Status BCCWrapperImpl::DetachUProbe(const UProbeSpec& probe) { VLOG(1) << "Detaching uprobe " << probe.ToString(); if (fs::Exists(probe.binary_path)) { @@ -313,7 +313,7 @@ Status BCCWrapper::DetachUProbe(const UProbeSpec& probe) { return Status::OK(); } -Status BCCWrapper::DetachTracepoint(const TracepointSpec& probe) { +Status BCCWrapperImpl::DetachTracepoint(const TracepointSpec& probe) { VLOG(1) << "Detaching tracepoint " << probe.ToString(); PX_RETURN_IF_ERROR(bpf_.detach_tracepoint(probe.tracepoint)); @@ -322,7 +322,7 @@ Status BCCWrapper::DetachTracepoint(const TracepointSpec& probe) { return Status::OK(); } -void BCCWrapper::DetachKProbes() { +void BCCWrapperImpl::DetachKProbes() { for (const auto& p : kprobes_) { auto res = DetachKProbe(p); LOG_IF(ERROR, !res.ok()) << res.msg(); @@ -330,7 +330,7 @@ void BCCWrapper::DetachKProbes() { kprobes_.clear(); } -void BCCWrapper::DetachUProbes() { +void BCCWrapperImpl::DetachUProbes() { for (const auto& p : uprobes_) { auto res = DetachUProbe(p); LOG_IF(ERROR, !res.ok()) << res.msg(); @@ -338,7 +338,7 @@ void BCCWrapper::DetachUProbes() { uprobes_.clear(); } -void BCCWrapper::DetachTracepoints() { +void BCCWrapperImpl::DetachTracepoints() { for (const auto& t : tracepoints_) { auto res = DetachTracepoint(t); LOG_IF(ERROR, !res.ok()) << res.msg(); @@ -346,7 +346,7 @@ void BCCWrapper::DetachTracepoints() { tracepoints_.clear(); } -Status BCCWrapper::OpenPerfBuffer(const PerfBufferSpec& perf_buffer) { +Status BCCWrapperImpl::OpenPerfBuffer(const PerfBufferSpec& perf_buffer) { const int kPageSizeBytes = system::Config::GetInstance().PageSizeBytes(); int num_pages = IntRoundUpDivide(perf_buffer.size_bytes, kPageSizeBytes); @@ -364,21 +364,21 @@ Status BCCWrapper::OpenPerfBuffer(const PerfBufferSpec& perf_buffer) { return Status::OK(); } -Status BCCWrapper::OpenPerfBuffers(const ArrayView& perf_buffers) { +Status BCCWrapperImpl::OpenPerfBuffers(const ArrayView& perf_buffers) { for (const PerfBufferSpec& p : perf_buffers) { PX_RETURN_IF_ERROR(OpenPerfBuffer(p)); } return Status::OK(); } -Status BCCWrapper::ClosePerfBuffer(const PerfBufferSpec& perf_buffer) { +Status BCCWrapperImpl::ClosePerfBuffer(const PerfBufferSpec& perf_buffer) { VLOG(1) << "Closing perf buffer: " << perf_buffer.name; PX_RETURN_IF_ERROR(bpf_.close_perf_buffer(std::string(perf_buffer.name))); --num_open_perf_buffers_; return Status::OK(); } -void BCCWrapper::ClosePerfBuffers() { +void BCCWrapperImpl::ClosePerfBuffers() { for (const PerfBufferSpec& p : perf_buffers_) { auto res = ClosePerfBuffer(p); LOG_IF(ERROR, !res.ok()) << res.msg(); @@ -386,7 +386,7 @@ void BCCWrapper::ClosePerfBuffers() { perf_buffers_.clear(); } -Status BCCWrapper::AttachPerfEvent(const PerfEventSpec& perf_event) { +Status BCCWrapperImpl::AttachPerfEvent(const PerfEventSpec& perf_event) { VLOG(1) << absl::Substitute("Attaching perf event:\n type=$0\n probe_fn=$1", magic_enum::enum_name(perf_event.type), perf_event.probe_fn); PX_RETURN_IF_ERROR(bpf_.attach_perf_event(perf_event.type, perf_event.config, @@ -397,14 +397,14 @@ Status BCCWrapper::AttachPerfEvent(const PerfEventSpec& perf_event) { return Status::OK(); } -Status BCCWrapper::AttachPerfEvents(const ArrayView& perf_events) { +Status BCCWrapperImpl::AttachPerfEvents(const ArrayView& perf_events) { for (const PerfEventSpec& p : perf_events) { PX_RETURN_IF_ERROR(AttachPerfEvent(p)); } return Status::OK(); } -Status BCCWrapper::DetachPerfEvent(const PerfEventSpec& perf_event) { +Status BCCWrapperImpl::DetachPerfEvent(const PerfEventSpec& perf_event) { VLOG(1) << absl::Substitute("Detaching perf event:\n type=$0\n probe_fn=$1", magic_enum::enum_name(perf_event.type), perf_event.probe_fn); PX_RETURN_IF_ERROR(bpf_.detach_perf_event(perf_event.type, perf_event.config)); @@ -412,7 +412,7 @@ Status BCCWrapper::DetachPerfEvent(const PerfEventSpec& perf_event) { return Status::OK(); } -void BCCWrapper::DetachPerfEvents() { +void BCCWrapperImpl::DetachPerfEvents() { for (const PerfEventSpec& p : perf_events_) { auto res = DetachPerfEvent(p); LOG_IF(ERROR, !res.ok()) << res.msg(); @@ -420,7 +420,7 @@ void BCCWrapper::DetachPerfEvents() { perf_events_.clear(); } -std::string BCCWrapper::GetKProbeTargetName(const KProbeSpec& probe) { +std::string BCCWrapperImpl::GetKProbeTargetName(const KProbeSpec& probe) { auto target = std::string(probe.kernel_fn); if (probe.is_syscall) { target = bpf_.get_syscall_fnname(target); @@ -428,20 +428,20 @@ std::string BCCWrapper::GetKProbeTargetName(const KProbeSpec& probe) { return target; } -void BCCWrapper::PollPerfBuffer(std::string_view perf_buffer_name, int timeout_ms) { +void BCCWrapperImpl::PollPerfBuffer(std::string_view perf_buffer_name, int timeout_ms) { auto perf_buffer = bpf_.get_perf_buffer(std::string(perf_buffer_name)); if (perf_buffer != nullptr) { perf_buffer->poll(timeout_ms); } } -void BCCWrapper::PollPerfBuffers(int timeout_ms) { +void BCCWrapperImpl::PollPerfBuffers(int timeout_ms) { for (const auto& spec : perf_buffers_) { PollPerfBuffer(spec.name, timeout_ms); } } -void BCCWrapper::Close() { +void BCCWrapperImpl::Close() { DetachPerfEvents(); ClosePerfBuffers(); DetachKProbes(); @@ -449,6 +449,8 @@ void BCCWrapper::Close() { DetachTracepoints(); } +std::unique_ptr CreateBCC() { return std::make_unique(); } + } // namespace bpf_tools } // namespace stirling } // namespace px diff --git a/src/stirling/bpf_tools/bcc_wrapper.h b/src/stirling/bpf_tools/bcc_wrapper.h index 3137d285c1e..42d845fe32e 100644 --- a/src/stirling/bpf_tools/bcc_wrapper.h +++ b/src/stirling/bpf_tools/bcc_wrapper.h @@ -71,6 +71,7 @@ namespace bpf_tools { */ class BCCWrapper { public: + virtual ~BCCWrapper() {} inline static const size_t kCPUCount = ebpf::BPFTable::get_possible_cpu_count(); /** @@ -88,13 +89,7 @@ class BCCWrapper { return task_struct_offsets_opt_; } - ~BCCWrapper() { - // Not really required, because BPF destructor handles these. - // But we do it anyways out of paranoia. - Close(); - } - - ebpf::BPF& BPF() { return bpf_; } + virtual ebpf::BPF& BPF() = 0; /** * Compiles the BPF code. @@ -107,37 +102,37 @@ class BCCWrapper { * @return error if no root access, code could not be compiled, or required linux headers are not * available. */ - Status InitBPFProgram(std::string_view bpf_program, std::vector cflags = {}, - bool requires_linux_headers = true, - bool always_infer_task_struct_offsets = false); + virtual Status InitBPFProgram(std::string_view bpf_program, std::vector cflags = {}, + bool requires_linux_headers = true, + bool always_infer_task_struct_offsets = false) = 0; /** * Attach a single kprobe. * @param probe Specifications of the kprobe (attach point, trace function, etc.). * @return Error if probe fails to attach. */ - Status AttachKProbe(const KProbeSpec& probe); + virtual Status AttachKProbe(const KProbeSpec& probe) = 0; /** * Attach a single uprobe. * @param probe Specifications of the uprobe (attach point, trace function, etc.). * @return Error if probe fails to attach. */ - Status AttachUProbe(const UProbeSpec& probe); + virtual Status AttachUProbe(const UProbeSpec& probe) = 0; /** * Attach a single tracepoint * @param probe Specifications of the tracepoint (attach point, trace function, etc.). * @return Error if probe fails to attach. */ - Status AttachTracepoint(const TracepointSpec& probe); + virtual Status AttachTracepoint(const TracepointSpec& probe) = 0; /** * Attach a single sampling probe. * @param probe Specifications of the probe (bpf function and sampling frequency). * @return Error if probe fails to attach. */ - Status AttachSamplingProbe(const SamplingProbeSpec& probe); + virtual Status AttachSamplingProbe(const SamplingProbeSpec& probe) = 0; /** * Open a perf buffer for reading events. @@ -145,7 +140,7 @@ class BCCWrapper { * PollPerfBuffer(). * @return Error if perf buffer cannot be opened (e.g. perf buffer does not exist). */ - Status OpenPerfBuffer(const PerfBufferSpec& perf_buffer); + virtual Status OpenPerfBuffer(const PerfBufferSpec& perf_buffer) = 0; /** * Attach a perf event, which runs a probe every time a perf counter reaches a threshold @@ -153,54 +148,54 @@ class BCCWrapper { * @param perf_event Specification of the perf event and its sampling frequency. * @return Error if the perf event could not be attached. */ - Status AttachPerfEvent(const PerfEventSpec& perf_event); + virtual Status AttachPerfEvent(const PerfEventSpec& perf_event) = 0; /** * Convenience function that attaches multiple kprobes. * @param probes Vector of probes. * @return Error of first probe to fail to attach (remaining probe attachments are not attempted). */ - Status AttachKProbes(const ArrayView& probes); + virtual Status AttachKProbes(const ArrayView& probes) = 0; /** * Convenience function that attaches multiple tracepoints. * @param probes Vector of TracepointSpec. * @return Error of first probe to fail to attach (remaining probe attachments are not attempted). */ - Status AttachTracepoints(const ArrayView& probes); + virtual Status AttachTracepoints(const ArrayView& probes) = 0; /** * Convenience function that attaches multiple uprobes. * @param probes Vector of probes. * @return Error of first probe to fail to attach (remaining probe attachments are not attempted). */ - Status AttachUProbes(const ArrayView& uprobes); + virtual Status AttachUProbes(const ArrayView& uprobes) = 0; /** * Convenience function that attaches multiple uprobes. * @param probes Vector of probes. * @return Error of first probe to fail to attach (remaining probe attachments are not attempted). */ - Status AttachSamplingProbes(const ArrayView& probes); + virtual Status AttachSamplingProbes(const ArrayView& probes) = 0; /** * Convenience function that attaches a XDP program. */ - Status AttachXDP(const std::string& dev_name, const std::string& fn_name); + virtual Status AttachXDP(const std::string& dev_name, const std::string& fn_name) = 0; /** * Convenience function that opens multiple perf buffers. * @param probes Vector of perf buffer descriptors. * @return Error of first failure (remaining perf buffer opens are not attempted). */ - Status OpenPerfBuffers(const ArrayView& perf_buffers); + virtual Status OpenPerfBuffers(const ArrayView& perf_buffers) = 0; /** * Convenience function that opens multiple perf events. * @param probes Vector of perf event descriptors. * @return Error of first failure (remaining perf event attaches are not attempted). */ - Status AttachPerfEvents(const ArrayView& perf_events); + virtual Status AttachPerfEvents(const ArrayView& perf_events) = 0; /** * Convenience function that populates a BPFPerfEventArray (aka BPF_PERF_ARRAY), used to directly @@ -212,11 +207,8 @@ class BCCWrapper { * @param config PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_INSTRUCTIONS, etc... * @return Error status. */ - Status PopulateBPFPerfArray(const std::string& table_name, const uint32_t type, - const uint64_t config) { - PX_RETURN_IF_ERROR(bpf_.open_perf_event(table_name, type, config)); - return Status::OK(); - } + virtual Status PopulateBPFPerfArray(const std::string& table_name, const uint32_t type, + const uint64_t config) = 0; /** * Drains all of the opened perf buffers, calling the handle function that was @@ -227,13 +219,13 @@ class BCCWrapper { * Default is 0, because if nothing is ready, then we want to go back to sleep * and catch new events in the next iteration. */ - void PollPerfBuffers(int timeout_ms = 0); - void PollPerfBuffer(std::string_view perf_buffer_name, int timeout_ms); + virtual void PollPerfBuffers(int timeout_ms = 0) = 0; + virtual void PollPerfBuffer(std::string_view perf_buffer_name, int timeout_ms) = 0; /** * Detaches all probes, and closes all perf buffers that are open. */ - void Close(); + virtual void Close() = 0; // These are static counters of attached/open probes across all instances. // It is meant for verification that we have cleaned-up all resources in tests. @@ -241,13 +233,70 @@ class BCCWrapper { static size_t num_open_perf_buffers() { return num_open_perf_buffers_; } static size_t num_attached_perf_events() { return num_attached_perf_events_; } + protected: + FRIEND_TEST(BCCWrapperTest, DetachUProbe); + virtual Status ClosePerfBuffer(const PerfBufferSpec& perf_buffer) = 0; + + // These are static counters across all instances, because: + // 1) We want to ensure we have cleaned all BPF resources up across *all* instances (no leaks). + // 2) It is for verification only, and it doesn't make sense to create accessors from stirling to + // here. + inline static size_t num_attached_kprobes_; + inline static size_t num_attached_uprobes_; + inline static size_t num_attached_tracepoints_; + inline static size_t num_open_perf_buffers_; + inline static size_t num_attached_perf_events_; + + private: + // This is shared by all source connectors that uses BCCWrapper. + inline static std::optional task_struct_offsets_opt_; +}; + +class BCCWrapperImpl : public BCCWrapper { + public: + virtual ~BCCWrapperImpl() { + // Not really required, because BPF destructor handles these. + // But we do it anyways out of paranoia. + Close(); + } + + ebpf::BPF& BPF() override { return bpf_; } + + Status InitBPFProgram(std::string_view bpf_program, std::vector cflags = {}, + bool requires_linux_headers = true, + bool always_infer_task_struct_offsets = false) override; + Status AttachKProbe(const KProbeSpec& probe) override; + Status AttachUProbe(const UProbeSpec& probe) override; + Status AttachTracepoint(const TracepointSpec& probe) override; + Status AttachSamplingProbe(const SamplingProbeSpec& probe) override; + Status OpenPerfBuffer(const PerfBufferSpec& perf_buffer) override; + Status AttachPerfEvent(const PerfEventSpec& perf_event) override; + Status AttachKProbes(const ArrayView& probes) override; + Status AttachTracepoints(const ArrayView& probes) override; + Status AttachUProbes(const ArrayView& uprobes) override; + Status AttachSamplingProbes(const ArrayView& probes) override; + Status AttachXDP(const std::string& dev_name, const std::string& fn_name) override; + Status OpenPerfBuffers(const ArrayView& perf_buffers) override; + Status AttachPerfEvents(const ArrayView& perf_events) override; + Status PopulateBPFPerfArray(const std::string& table_name, const uint32_t type, + const uint64_t config) override { + PX_RETURN_IF_ERROR(bpf_.open_perf_event(table_name, type, config)); + return Status::OK(); + } + void PollPerfBuffers(int timeout_ms = 0) override; + void PollPerfBuffer(std::string_view perf_buffer_name, int timeout_ms) override; + void Close() override; + + protected: + Status ClosePerfBuffer(const PerfBufferSpec& perf_buffer) override; + std::vector perf_buffers_; + private: FRIEND_TEST(BCCWrapperTest, DetachUProbe); Status DetachKProbe(const KProbeSpec& probe); Status DetachUProbe(const UProbeSpec& probe); Status DetachTracepoint(const TracepointSpec& probe); - Status ClosePerfBuffer(const PerfBufferSpec& perf_buffer); Status DetachPerfEvent(const PerfEventSpec& perf_event); // Detaches all kprobes/uprobes/perf buffers/perf events that were attached by the wrapper. @@ -264,7 +313,6 @@ class BCCWrapper { std::vector kprobes_; std::vector uprobes_; std::vector tracepoints_; - std::vector perf_buffers_; std::vector perf_events_; std::string system_headers_include_dir_; @@ -280,22 +328,10 @@ class BCCWrapper { // DEBUG_BPF_REGISTER_STATE = 0x10, // DEBUG_BTF = 0x20, ebpf::BPF bpf_; - - // These are static counters across all instances, because: - // 1) We want to ensure we have cleaned all BPF resources up across *all* instances (no leaks). - // 2) It is for verification only, and it doesn't make sense to create accessors from stirling to - // here. - inline static size_t num_attached_kprobes_; - inline static size_t num_attached_uprobes_; - inline static size_t num_attached_tracepoints_; - inline static size_t num_open_perf_buffers_; - inline static size_t num_attached_perf_events_; - - private: - // This is shared by all source connectors that uses BCCWrapper. - inline static std::optional task_struct_offsets_opt_; }; +std::unique_ptr CreateBCC(); + template class WrappedBCCArrayTable { public: diff --git a/src/stirling/bpf_tools/bcc_wrapper_bpf_test.cc b/src/stirling/bpf_tools/bcc_wrapper_bpf_test.cc index d9b61117fe3..c3a3fb1c4b1 100644 --- a/src/stirling/bpf_tools/bcc_wrapper_bpf_test.cc +++ b/src/stirling/bpf_tools/bcc_wrapper_bpf_test.cc @@ -71,7 +71,7 @@ TEST(BCCWrapperTest, InitDefault) { // but since packaged headers are not included and PL_HOST_ENV is not defined, // it essentially boils down to a local headers search. // If the test host doesn't have Linux headers, we expect this test to fail. - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; ASSERT_OK(bcc_wrapper.InitBPFProgram(kBCCProgram)); } @@ -82,13 +82,13 @@ TEST(BCCWrapperTest, InitWithTaskStructResolver) { // BCCWrapper::InitBPFProgram // TaskStructResolver // BCCWrapper::InitBPFProgram - // (The second instance of BCCWrapper shouldn't call TaskStructResolver again.) + // (The second instance of BCCWrapperImpl shouldn't call TaskStructResolver again.) std::vector cflags = {}; bool requires_linux_headers = true; bool always_infer_task_struct_offsets = true; - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; ASSERT_OK(bcc_wrapper.InitBPFProgram(kBCCProgram, cflags, requires_linux_headers, always_infer_task_struct_offsets)); } @@ -96,7 +96,7 @@ TEST(BCCWrapperTest, InitWithTaskStructResolver) { TEST(BCCWrapperTest, DetachUProbe) { TestExeWrapper test_exe; - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; ASSERT_OK(bcc_wrapper.InitBPFProgram(kBCCProgram)); UProbeSpec spec = { @@ -131,7 +131,7 @@ TEST(BCCWrapperTest, GetTGIDStartTime) { bool requires_linux_headers = true; bool always_infer_task_struct_offsets = true; - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; ASSERT_OK(bcc_wrapper.InitBPFProgram(get_tgid_start_time_bcc_script, cflags, requires_linux_headers, always_infer_task_struct_offsets)); @@ -171,7 +171,7 @@ TEST(BCCWrapperTest, GetTGIDStartTime) { TEST(BCCWrapperTest, TestMapClearingAPIs) { // Test to show that get_table_offline() with clear_table=true actually clears the table. - bpf_tools::BCCWrapper bcc_wrapper; + bpf_tools::BCCWrapperImpl bcc_wrapper; std::string_view kProgram = "BPF_HASH(alphabet, char const * const, uint64_t, 26);"; ASSERT_OK(bcc_wrapper.InitBPFProgram(kProgram)); auto alphabet = WrappedBCCMap::Create(&bcc_wrapper, "alphabet"); @@ -201,9 +201,9 @@ TEST(BCCWrapperTest, TestMapClearingAPIs) { ASSERT_THAT(alphabet->GetTableOffline(), IsEmpty()); } -// Tests that BCCWrapper can load and attach UPD filter defined in the XDP program. +// Tests that BCCWrapperImpl can load and attach UPD filter defined in the XDP program. TEST(BCCWrapperTest, LoadUPDFilterWithXDP) { - bpf_tools::BCCWrapper bcc_wrapper; + bpf_tools::BCCWrapperImpl bcc_wrapper; std::string_view xdp_program = R"bcc( #include @@ -239,7 +239,7 @@ TEST(BCCWrapperTest, LoadUPDFilterWithXDP) { } TEST(BCCWrapper, Tracepoint) { - bpf_tools::BCCWrapper bcc_wrapper; + bpf_tools::BCCWrapperImpl bcc_wrapper; // Including sched.h is a workaround of a test failure on 5.18.4 kernel. // See https://github.com/iovisor/bcc/issues/4092. diff --git a/src/stirling/bpf_tools/task_struct_resolver.cc b/src/stirling/bpf_tools/task_struct_resolver.cc index c94dbfbd760..8fad2061f76 100644 --- a/src/stirling/bpf_tools/task_struct_resolver.cc +++ b/src/stirling/bpf_tools/task_struct_resolver.cc @@ -233,7 +233,7 @@ StatusOr ResolveTaskStructOffsetsCore() { .probe_fn = "task_struct_probe"}; // Deploy the BPF program. - auto bcc = std::make_unique(); + auto bcc = std::make_unique(); std::vector cflags; // Important! Must tell BCCWrapper that we don't need linux headers, otherwise we may // enter an infinite loop if BCCWrapper tries to run the TaskStructResolver again. @@ -353,7 +353,7 @@ StatusOr ResolveTaskStructExitCodeOffset() { SubProcess proc; PX_RETURN_IF_ERROR(proc.Start([]() -> int { return exit_code; }, {.stop_before_exec = true})); - auto bcc = std::make_unique(); + auto bcc = std::make_unique(); PX_RETURN_IF_ERROR( bcc->InitBPFProgram(bcc_script, /*cflags*/ {}, /*requires_linux_headers*/ false)); PX_RETURN_IF_ERROR(bcc->AttachTracepoint({std::string("sched:sched_process_exit"), diff --git a/src/stirling/bpf_tools/uprobe_extra_trigger_bpf_test.cc b/src/stirling/bpf_tools/uprobe_extra_trigger_bpf_test.cc index 14106d39838..ae76c1254c5 100644 --- a/src/stirling/bpf_tools/uprobe_extra_trigger_bpf_test.cc +++ b/src/stirling/bpf_tools/uprobe_extra_trigger_bpf_test.cc @@ -52,7 +52,7 @@ constexpr char kBCCProgram[] = R"( // but which have share the same file inode behind the scenes. // Disabling this test since this bug doesn't seem to reproduce with podman. TEST(BCCWrapper, DISABLED_UnexpectedExtraTrigger) { - BCCWrapper bcc_wrapper; + BCCWrapperImpl bcc_wrapper; ASSERT_OK(bcc_wrapper.InitBPFProgram(kBCCProgram)); ::px::stirling::testing::Go1_19_GRPCServerContainer server1; diff --git a/src/stirling/core/BUILD.bazel b/src/stirling/core/BUILD.bazel index 68566fe1810..ab795229aad 100644 --- a/src/stirling/core/BUILD.bazel +++ b/src/stirling/core/BUILD.bazel @@ -51,6 +51,7 @@ pl_cc_library( "//src/shared/types:cc_library", "//src/shared/types/typespb/wrapper:cc_library", "//src/shared/upid:cc_library", + "//src/stirling/bpf_tools:cc_library", "//src/stirling/proto:stirling_pl_cc_proto", "//src/stirling/utils:cc_library", ], diff --git a/src/stirling/core/source_connector.h b/src/stirling/core/source_connector.h index c7f13ac0666..f3359195931 100644 --- a/src/stirling/core/source_connector.h +++ b/src/stirling/core/source_connector.h @@ -18,6 +18,7 @@ #pragma once +#include #include #include #include @@ -25,6 +26,7 @@ #include "src/common/base/base.h" #include "src/common/system/system.h" #include "src/shared/types/types.h" +#include "src/stirling/bpf_tools/bcc_wrapper.h" #include "src/stirling/core/connector_context.h" #include "src/stirling/core/data_table.h" #include "src/stirling/core/frequency_manager.h" @@ -173,5 +175,16 @@ class SourceConnector : public NotCopyable { const ArrayView table_schemas_; }; +class BCCSourceConnector : public SourceConnector { + public: + bpf_tools::BCCWrapper& BCC() { return *bcc_; } + + protected: + explicit BCCSourceConnector(std::string_view source_name, + const ArrayView& table_schemas) + : SourceConnector(source_name, table_schemas), bcc_(bpf_tools::CreateBCC()) {} + std::unique_ptr bcc_; +}; + } // namespace stirling } // namespace px diff --git a/src/stirling/core/unit_connector.h b/src/stirling/core/unit_connector.h index 8cf23e775a9..4ee38e4da27 100644 --- a/src/stirling/core/unit_connector.h +++ b/src/stirling/core/unit_connector.h @@ -248,7 +248,7 @@ class UnitConnector { Status TransferDataThread() { // Drain the perf buffers before starting the thread. // Otherwise, perf buffers may already be full, causing lost events and flaky test results. - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); // Check to ensure that the transfer data thread will run within a human time frame. // If this is triggered, please find a new upper bound or implement a special case. diff --git a/src/stirling/e2e_tests/bpf_map_leak_bpf_test.cc b/src/stirling/e2e_tests/bpf_map_leak_bpf_test.cc index 0fd10441483..5386b9a7c97 100644 --- a/src/stirling/e2e_tests/bpf_map_leak_bpf_test.cc +++ b/src/stirling/e2e_tests/bpf_map_leak_bpf_test.cc @@ -123,7 +123,7 @@ TEST_P(BPFMapLeakTest, UnclosedConnection) { DataTables data_tables(SocketTraceConnector::kTables); - auto conn_info_map = ConnInfoMapT::Create(source_.get(), "conn_info_map"); + auto conn_info_map = ConnInfoMapT::Create(&source_->BCC(), "conn_info_map"); // Confirm that the leaked BPF map entry exists. source_->TransferData(ctx_.get()); diff --git a/src/stirling/e2e_tests/stirling_bpf_test.cc b/src/stirling/e2e_tests/stirling_bpf_test.cc index 03a2e009bbc..9cfdfdc4309 100644 --- a/src/stirling/e2e_tests/stirling_bpf_test.cc +++ b/src/stirling/e2e_tests/stirling_bpf_test.cc @@ -68,16 +68,16 @@ TEST_F(StirlingBPFTest, CleanupTest) { // Wait for thread to initialize. ASSERT_OK(stirling_->WaitUntilRunning(/* timeout */ std::chrono::seconds(5))); - EXPECT_GT(SocketTraceConnector::num_attached_probes(), 0); - EXPECT_GT(SocketTraceConnector::num_open_perf_buffers(), 0); + EXPECT_GT(bpf_tools::BCCWrapper::num_attached_probes(), 0); + EXPECT_GT(bpf_tools::BCCWrapper::num_open_perf_buffers(), 0); std::thread killer_thread = std::thread(&AsyncKill, stirling_.get()); ASSERT_TRUE(killer_thread.joinable()); killer_thread.join(); - EXPECT_EQ(SocketTraceConnector::num_attached_probes(), 0); - EXPECT_EQ(SocketTraceConnector::num_open_perf_buffers(), 0); + EXPECT_EQ(bpf_tools::BCCWrapper::num_attached_probes(), 0); + EXPECT_EQ(bpf_tools::BCCWrapper::num_open_perf_buffers(), 0); } } // namespace stirling diff --git a/src/stirling/obj_tools/elf_reader_symbolizer_bpf_test.cc b/src/stirling/obj_tools/elf_reader_symbolizer_bpf_test.cc index 8aa534cc67e..6cad7430bb3 100644 --- a/src/stirling/obj_tools/elf_reader_symbolizer_bpf_test.cc +++ b/src/stirling/obj_tools/elf_reader_symbolizer_bpf_test.cc @@ -70,7 +70,7 @@ int sample_stack_trace(struct pt_regs* ctx) { StatusOr> CollectStackTrace() { PX_ASSIGN_OR_RETURN(std::filesystem::path self_path, fs::ReadSymlink("/proc/self/exe")); - bpf_tools::BCCWrapper bcc_wrapper; + bpf_tools::BCCWrapperImpl bcc_wrapper; bpf_tools::UProbeSpec spec = { .binary_path = self_path.string(), diff --git a/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_connector.cc b/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_connector.cc index 3e99b304da2..33d3d880d14 100644 --- a/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_connector.cc +++ b/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_connector.cc @@ -173,10 +173,10 @@ Status DynamicTraceConnector::InitImpl() { sampling_freq_mgr_.set_period(kSamplingPeriod); push_freq_mgr_.set_period(kPushPeriod); - PX_RETURN_IF_ERROR(InitBPFProgram(bcc_program_.code)); + PX_RETURN_IF_ERROR(bcc_->InitBPFProgram(bcc_program_.code)); for (const auto& uprobe_spec : bcc_program_.uprobe_specs) { - PX_RETURN_IF_ERROR(AttachUProbe(uprobe_spec)); + PX_RETURN_IF_ERROR(bcc_->AttachUProbe(uprobe_spec)); } // TODO(yzhao/oazizi): Might need to change this if we need to support multiple perf buffers. @@ -187,7 +187,7 @@ Status DynamicTraceConnector::InitImpl() { .cb_cookie = this, }; - PX_RETURN_IF_ERROR(OpenPerfBuffer(spec)); + PX_RETURN_IF_ERROR(bcc_->OpenPerfBuffer(spec)); return Status::OK(); } @@ -495,7 +495,7 @@ void DynamicTraceConnector::TransferDataImpl(ConnectorContext* ctx) { return; } - PollPerfBuffers(); + bcc_->PollPerfBuffers(); for (const auto& item : data_items_) { // TODO(yzhao): Right now only support scalar types. We should replace type with ScalarType diff --git a/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_connector.h b/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_connector.h index 652b6dff2b8..fd5f438e782 100644 --- a/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_connector.h +++ b/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_connector.h @@ -32,7 +32,7 @@ namespace px { namespace stirling { -class DynamicTraceConnector : public SourceConnector, public bpf_tools::BCCWrapper { +class DynamicTraceConnector : public BCCSourceConnector { public: static constexpr auto kSamplingPeriod = std::chrono::milliseconds{100}; static constexpr auto kPushPeriod = std::chrono::milliseconds{1000}; @@ -51,7 +51,7 @@ class DynamicTraceConnector : public SourceConnector, public bpf_tools::BCCWrapp // Consider how to expand to multiple tables if/when needed. DynamicTraceConnector(std::string_view name, std::unique_ptr table_schema, dynamic_tracing::BCCProgram bcc_program) - : SourceConnector(name, ArrayView(&table_schema->Get(), 1)), + : BCCSourceConnector(name, ArrayView(&table_schema->Get(), 1)), table_schema_(std::move(table_schema)), bcc_program_(std::move(bcc_program)) {} diff --git a/src/stirling/source_connectors/perf_profiler/perf_profile_connector.cc b/src/stirling/source_connectors/perf_profiler/perf_profile_connector.cc index 2b4c891b51d..574f2640afb 100644 --- a/src/stirling/source_connectors/perf_profiler/perf_profile_connector.cc +++ b/src/stirling/source_connectors/perf_profiler/perf_profile_connector.cc @@ -55,7 +55,7 @@ namespace px { namespace stirling { PerfProfileConnector::PerfProfileConnector(std::string_view source_name) - : SourceConnector(source_name, kTables), + : BCCSourceConnector(source_name, kTables), stack_trace_sampling_period_( std::chrono::milliseconds{FLAGS_stirling_profiler_stack_trace_sample_period_ms}), sampling_period_( @@ -141,14 +141,14 @@ Status PerfProfileConnector::InitImpl() { {{std::string(kHistogramAName), HandleHistoEvent, HandleHistoLoss, this, perf_buffer_size}, {std::string(kHistogramBName), HandleHistoEvent, HandleHistoLoss, this, perf_buffer_size}}); - PX_RETURN_IF_ERROR(InitBPFProgram(profiler_bcc_script, defines)); - PX_RETURN_IF_ERROR(AttachSamplingProbes(probe_specs)); - PX_RETURN_IF_ERROR(OpenPerfBuffers(perf_buffer_specs)); + PX_RETURN_IF_ERROR(bcc_->InitBPFProgram(profiler_bcc_script, defines)); + PX_RETURN_IF_ERROR(bcc_->AttachSamplingProbes(probe_specs)); + PX_RETURN_IF_ERROR(bcc_->OpenPerfBuffers(perf_buffer_specs)); - stack_traces_a_ = WrappedBCCStackTable::Create(this, "stack_traces_a"); - stack_traces_b_ = WrappedBCCStackTable::Create(this, "stack_traces_b"); + stack_traces_a_ = WrappedBCCStackTable::Create(bcc_.get(), "stack_traces_a"); + stack_traces_b_ = WrappedBCCStackTable::Create(bcc_.get(), "stack_traces_b"); - profiler_state_ = WrappedBCCArrayTable::Create(this, "profiler_state"); + profiler_state_ = WrappedBCCArrayTable::Create(bcc_.get(), "profiler_state"); LOG(INFO) << "PerfProfiler: Stack trace profiling sampling probe successfully deployed."; @@ -185,7 +185,7 @@ Status PerfProfileConnector::StopImpl() { // Must call Close() after attach_uprobes_thread_ has joined, // otherwise the two threads will cause concurrent accesses to BCC, // that will cause races and undefined behavior. - Close(); + bcc_->Close(); return Status::OK(); } @@ -345,7 +345,7 @@ void PerfProfileConnector::ProcessBPFStackTraces(ConnectorContext* ctx, DataTabl // Read out the perf buffer that contains the histogram for this iteration. // TODO(jps): change PollPerfBuffer() to use std::chrono. constexpr int kPollTimeoutMS = 0; - PollPerfBuffer(perfbuf_name, kPollTimeoutMS); + bcc_->PollPerfBuffer(perfbuf_name, kPollTimeoutMS); ++transfer_count_; diff --git a/src/stirling/source_connectors/perf_profiler/perf_profile_connector.h b/src/stirling/source_connectors/perf_profiler/perf_profile_connector.h index 2a1992f897c..5409f57fe04 100644 --- a/src/stirling/source_connectors/perf_profiler/perf_profile_connector.h +++ b/src/stirling/source_connectors/perf_profiler/perf_profile_connector.h @@ -49,7 +49,7 @@ namespace profiler { static constexpr std::string_view kNotSymbolizedMessage = ""; } // namespace profiler -class PerfProfileConnector : public SourceConnector, public bpf_tools::BCCWrapper { +class PerfProfileConnector : public BCCSourceConnector { public: static constexpr std::string_view kName = "perf_profiler"; static constexpr auto kTables = MakeArray(kStackTraceTable); diff --git a/src/stirling/source_connectors/perf_profiler/stringifier_bpf_test.cc b/src/stirling/source_connectors/perf_profiler/stringifier_bpf_test.cc index 7cdf26a6fc8..d1800fd5f1f 100644 --- a/src/stirling/source_connectors/perf_profiler/stringifier_bpf_test.cc +++ b/src/stirling/source_connectors/perf_profiler/stringifier_bpf_test.cc @@ -108,12 +108,14 @@ class StringifierTest : public ::testing::Test { protected: void SetUp() override { + bcc_wrapper_ = bpf_tools::CreateBCC(); + // Register our BPF program in the kernel. - ASSERT_OK(bcc_wrapper_.InitBPFProgram(stringifer_test_bcc_script)); + ASSERT_OK(bcc_wrapper_->InitBPFProgram(stringifer_test_bcc_script)); // Bind the BCC API to the shared BPF maps created by our BPF program. - stack_traces_ = WrappedBCCStackTable::Create(&bcc_wrapper_, "stack_traces"); - histogram_ = Histogram::Create(&bcc_wrapper_, "histogram"); + stack_traces_ = WrappedBCCStackTable::Create(bcc_wrapper_.get(), "stack_traces"); + histogram_ = Histogram::Create(bcc_wrapper_.get(), "histogram"); // Create a symbolizer (needed for the stringifer). ASSERT_OK_AND_ASSIGN(symbolizer_, BCCSymbolizer::Create()); @@ -163,7 +165,7 @@ class StringifierTest : public ::testing::Test { uint64_t num_stack_ids_reused_ = 0; - bpf_tools::BCCWrapper bcc_wrapper_; + std::unique_ptr bcc_wrapper_; std::unique_ptr stack_traces_; std::unique_ptr histogram_; @@ -210,9 +212,9 @@ TEST_F(StringifierTest, MemoizationTest) { "stack_trace_sampler"}; // Attach uprobes & kprobes for this test case: - ASSERT_OK(bcc_wrapper_.AttachKProbe(kPidKprobe)); - ASSERT_OK(bcc_wrapper_.AttachUProbe(kFooUprobe)); - ASSERT_OK(bcc_wrapper_.AttachUProbe(kBarUprobe)); + ASSERT_OK(bcc_wrapper_->AttachKProbe(kPidKprobe)); + ASSERT_OK(bcc_wrapper_->AttachUProbe(kFooUprobe)); + ASSERT_OK(bcc_wrapper_->AttachUProbe(kBarUprobe)); // Foo() & Bar() tickle our uprobes and kprobe. Both simply return the pid. uint32_t pid; @@ -223,7 +225,7 @@ TEST_F(StringifierTest, MemoizationTest) { // ... later, we will verify that the stringifier cleared the stack traces map, // as it is required to do. And for this verification to work, we need to // stop collecting data now. - bcc_wrapper_.Close(); + bcc_wrapper_->Close(); // Used below, in calls into BCC APIs. constexpr bool kClearTable = true; diff --git a/src/stirling/source_connectors/pid_runtime/pid_runtime_connector.cc b/src/stirling/source_connectors/pid_runtime/pid_runtime_connector.cc index f41205e6604..2eeb0db5b07 100644 --- a/src/stirling/source_connectors/pid_runtime/pid_runtime_connector.cc +++ b/src/stirling/source_connectors/pid_runtime/pid_runtime_connector.cc @@ -31,15 +31,15 @@ namespace stirling { Status PIDRuntimeConnector::InitImpl() { sampling_freq_mgr_.set_period(kSamplingPeriod); push_freq_mgr_.set_period(kPushPeriod); - PX_RETURN_IF_ERROR(InitBPFProgram(pidruntime_bcc_script)); - PX_RETURN_IF_ERROR(AttachSamplingProbes(kSamplingProbes)); + PX_RETURN_IF_ERROR(bcc_->InitBPFProgram(pidruntime_bcc_script)); + PX_RETURN_IF_ERROR(bcc_->AttachSamplingProbes(kSamplingProbes)); - bpf_data_ = BPFMapDataT::Create(this, "pid_cpu_time"); + bpf_data_ = BPFMapDataT::Create(bcc_.get(), "pid_cpu_time"); return Status::OK(); } Status PIDRuntimeConnector::StopImpl() { - Close(); + bcc_->Close(); return Status::OK(); } diff --git a/src/stirling/source_connectors/pid_runtime/pid_runtime_connector.h b/src/stirling/source_connectors/pid_runtime/pid_runtime_connector.h index f1e43b674a7..bce9c35084d 100644 --- a/src/stirling/source_connectors/pid_runtime/pid_runtime_connector.h +++ b/src/stirling/source_connectors/pid_runtime/pid_runtime_connector.h @@ -33,7 +33,7 @@ namespace px { namespace stirling { -class PIDRuntimeConnector : public SourceConnector, public bpf_tools::BCCWrapper { +class PIDRuntimeConnector : public BCCSourceConnector { public: using BPFMapDataT = bpf_tools::WrappedBCCMap; @@ -68,8 +68,7 @@ class PIDRuntimeConnector : public SourceConnector, public bpf_tools::BCCWrapper void TransferDataImpl(ConnectorContext* ctx) override; protected: - explicit PIDRuntimeConnector(std::string_view name) - : SourceConnector(name, kTables), bpf_tools::BCCWrapper() {} + explicit PIDRuntimeConnector(std::string_view name) : BCCSourceConnector(name, kTables) {} private: // Freq. (in Hz) at which to trigger bpf func. diff --git a/src/stirling/source_connectors/proc_exit/proc_exit_connector.cc b/src/stirling/source_connectors/proc_exit/proc_exit_connector.cc index fbbd26ec3f8..2976dc85207 100644 --- a/src/stirling/source_connectors/proc_exit/proc_exit_connector.cc +++ b/src/stirling/source_connectors/proc_exit/proc_exit_connector.cc @@ -59,7 +59,7 @@ constexpr char kJavaProcCrashedWithProfilerCounter[] = "java_proc_crashed_with_p constexpr char kJavaProcCrashedWithoutProfilerCounter[] = "java_proc_crashed_without_profiler"; ProcExitConnector::ProcExitConnector(std::string_view name) - : SourceConnector(name, kTables), + : BCCSourceConnector(name, kTables), java_proc_crashed_counter_( BuildCounter(kJavaProcCrashedCounter, "Count of the crashed Java processes")), java_proc_crashed_with_profiler_counter_(BuildCounter( @@ -77,18 +77,18 @@ Status ProcExitConnector::InitImpl() { sampling_freq_mgr_.set_period(kSamplingPeriod); push_freq_mgr_.set_period(kPushPeriod); - PX_RETURN_IF_ERROR(InitBPFProgram(proc_exit_trace_bcc_script)); + PX_RETURN_IF_ERROR(bcc_->InitBPFProgram(proc_exit_trace_bcc_script)); // Writes exit_code_offset to BPF array. Note that the other offsets are injected into BCC code // through macros. - const auto& offset_opt = BCCWrapper::task_struct_offsets_opt(); + const auto& offset_opt = bpf_tools::BCCWrapper::task_struct_offsets_opt(); if (offset_opt.has_value()) { LOG(INFO) << absl::Substitute( "Writing exit_code's offset to BPF array: offset=$0 bpf_array=$1 index=$2", offset_opt.value().exit_code_offset, kProcExitControlValuesArrayName, TASK_STRUCT_EXIT_CODE_OFFSET_INDEX); auto control_table = bpf_tools::WrappedBCCPerCPUArrayTable::Create( - this, kProcExitControlValuesArrayName); + bcc_.get(), kProcExitControlValuesArrayName); PX_RETURN_IF_ERROR(control_table->SetValues(TASK_STRUCT_EXIT_CODE_OFFSET_INDEX, offset_opt.value().exit_code_offset)); } @@ -98,8 +98,8 @@ Status ProcExitConnector::InitImpl() { kPerfBufferPerCPUSizeBytes, bpf_tools::PerfBufferSizeCategory::kControl}, }); - PX_RETURN_IF_ERROR(AttachTracepoints(kTracepointSpecs)); - PX_RETURN_IF_ERROR(OpenPerfBuffers(perf_buffer_specs)); + PX_RETURN_IF_ERROR(bcc_->AttachTracepoints(kTracepointSpecs)); + PX_RETURN_IF_ERROR(bcc_->OpenPerfBuffers(perf_buffer_specs)); return Status::OK(); } @@ -113,7 +113,7 @@ uint8_t GetExitSignal(uint32_t exit_code) { return exit_code & 0x7F; } void ProcExitConnector::TransferDataImpl(ConnectorContext* ctx) { DCHECK(data_tables_.size() == 1) << "Expect only one data table for proc_exit tracer"; - PollPerfBuffers(); + bcc_->PollPerfBuffers(); DataTable* data_table = data_tables_[0]; for (auto& event : events_) { diff --git a/src/stirling/source_connectors/proc_exit/proc_exit_connector.h b/src/stirling/source_connectors/proc_exit/proc_exit_connector.h index 62b3052fb05..90347b6ed07 100644 --- a/src/stirling/source_connectors/proc_exit/proc_exit_connector.h +++ b/src/stirling/source_connectors/proc_exit/proc_exit_connector.h @@ -35,7 +35,7 @@ namespace stirling { namespace proc_exit_tracer { // This connector is not registered yet, so it has no effect. -class ProcExitConnector : public SourceConnector, public bpf_tools::BCCWrapper { +class ProcExitConnector : public BCCSourceConnector { public: static constexpr std::string_view kName = "proc_exit_tracer"; diff --git a/src/stirling/source_connectors/socket_tracer/socket_trace_bpf_test.cc b/src/stirling/source_connectors/socket_tracer/socket_trace_bpf_test.cc index 2b9256e853f..1397d2e0804 100644 --- a/src/stirling/source_connectors/socket_tracer/socket_trace_bpf_test.cc +++ b/src/stirling/source_connectors/socket_tracer/socket_trace_bpf_test.cc @@ -306,7 +306,7 @@ TEST_F(SocketTraceBPFTest, FileIONotTraced) { close(fd1); // Finally drain all BPF events. - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); // Those to file I/O FDs should not have been reported. ASSERT_NOT_OK(GetConnTracker(getpid(), fd1)); @@ -359,7 +359,7 @@ TEST_F(SocketTraceBPFTest, NonInetTrafficNotTraced) { server_thread.join(); // Finally drain all BPF events. - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); // Those to file I/O FDs should not have been reported. ASSERT_NOT_OK(GetConnTracker(getpid(), client_fd)); @@ -379,7 +379,7 @@ TEST_F(SocketTraceBPFTest, NoProtocolWritesNotCaptured) { testing::ClientServerSystem system; system.RunClientServer<&TCPSocket::Read, &TCPSocket::Write>(script); - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); // We expect to see a ConnTracker allocated for ConnStats, but the data buffers should be empty // for unknown or unsupported protocols. @@ -501,7 +501,7 @@ TEST_F(SocketTraceBPFTest, LargeMessages) { testing::ClientServerSystem system; system.RunClientServer<&TCPSocket::Recv, &TCPSocket::Send>(script); - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); ASSERT_OK_AND_ASSIGN(auto* client_tracker, GetMutableConnTracker(system.ClientPID(), system.ClientFD())); @@ -749,7 +749,7 @@ class UDPSocketTraceBPFTest : public SocketTraceBPFTest { // Drain the perf buffers before beginning the test to make sure perf buffers are empty. // Otherwise, the test may flake due to events not being received in user-space. - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); // Uncomment to enable tracing: // FLAGS_stirling_conn_trace_pid = pid_; @@ -776,7 +776,7 @@ TEST_F(UDPSocketTraceBPFTest, UDPSendToRecvFrom) { ASSERT_EQ(client_remote.sin_port, server_.port()); EXPECT_EQ(client_recv_data, kHTTPRespMsg1); - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); ASSERT_OK_AND_ASSIGN(auto* tracker, GetMutableConnTracker(pid_, client_.sockfd())); EXPECT_EQ(tracker->send_data().data_buffer().Head(), kHTTPReqMsg1); @@ -803,7 +803,7 @@ TEST_F(UDPSocketTraceBPFTest, UDPSendMsgRecvMsg) { ASSERT_EQ(client_remote.sin_port, server_.port()); EXPECT_EQ(client_recv_data, kHTTPRespMsg1); - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); ASSERT_OK_AND_ASSIGN(auto* tracker, GetMutableConnTracker(pid_, client_.sockfd())); EXPECT_EQ(tracker->send_data().data_buffer().Head(), kHTTPReqMsg1); @@ -830,7 +830,7 @@ TEST_F(UDPSocketTraceBPFTest, UDPSendMMsgRecvMMsg) { ASSERT_EQ(client_remote.sin_port, server_.port()); EXPECT_EQ(client_recv_data, kHTTPRespMsg1); - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); ASSERT_OK_AND_ASSIGN(auto* tracker, GetMutableConnTracker(pid_, client_.sockfd())); EXPECT_EQ(tracker->send_data().data_buffer().Head(), kHTTPReqMsg1); @@ -865,7 +865,7 @@ TEST_F(UDPSocketTraceBPFTest, NonBlockingRecv) { ASSERT_EQ(client_remote.sin_port, server_.port()); EXPECT_EQ(recv_data, kHTTPRespMsg1); - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); ASSERT_OK_AND_ASSIGN(auto* tracker, GetMutableConnTracker(pid_, client_.sockfd())); EXPECT_EQ(tracker->send_data().data_buffer().Head(), kHTTPReqMsg1); diff --git a/src/stirling/source_connectors/socket_tracer/socket_trace_connector.cc b/src/stirling/source_connectors/socket_tracer/socket_trace_connector.cc index c7d4c7e52d0..defa622f73a 100644 --- a/src/stirling/source_connectors/socket_tracer/socket_trace_connector.cc +++ b/src/stirling/source_connectors/socket_tracer/socket_trace_connector.cc @@ -186,13 +186,13 @@ constexpr char openssl_tls_source_help[] = "Records the number of times a protocol was traced along with additional debugging information"; SocketTraceConnector::SocketTraceConnector(std::string_view source_name) - : SourceConnector(source_name, kTables), + : BCCSourceConnector(source_name, kTables), conn_stats_(&conn_trackers_mgr_), openssl_trace_mismatched_fds_counter_family_( BuildCounterFamily(openssl_mismatched_fds_metric, openssl_mismatched_fds_help)), openssl_trace_tls_source_counter_family_( BuildCounterFamily(openssl_tls_source_metric, openssl_tls_source_help)), - uprobe_mgr_(this) { + uprobe_mgr_(&this->BCC()) { proc_parser_ = std::make_unique(); InitProtocolTransferSpecs(); } @@ -432,14 +432,14 @@ Status SocketTraceConnector::InitBPF() { absl::StrCat("-DENABLE_AMQP_TRACING=", protocol_transfer_specs_[kProtocolAMQP].enabled), absl::StrCat("-DENABLE_MONGO_TRACING=", "true"), }; - PX_RETURN_IF_ERROR(InitBPFProgram(socket_trace_bcc_script, defines)); + PX_RETURN_IF_ERROR(bcc_->InitBPFProgram(socket_trace_bcc_script, defines)); - PX_RETURN_IF_ERROR(AttachKProbes(kProbeSpecs)); + PX_RETURN_IF_ERROR(bcc_->AttachKProbes(kProbeSpecs)); LOG(INFO) << absl::Substitute("Number of kprobes deployed = $0", kProbeSpecs.size()); LOG(INFO) << "Probes successfully deployed."; const auto perf_buffer_specs = InitPerfBufferSpecs(); - PX_RETURN_IF_ERROR(OpenPerfBuffers(perf_buffer_specs)); + PX_RETURN_IF_ERROR(bcc_->OpenPerfBuffers(perf_buffer_specs)); LOG(INFO) << absl::Substitute("Number of perf buffers opened = $0", perf_buffer_specs.size()); // Set trace role to BPF probes. @@ -501,16 +501,16 @@ Status SocketTraceConnector::InitImpl() { socket_info_mgr_ = s.ConsumeValueOrDie(); } - conn_info_map_mgr_ = std::make_shared(this); + conn_info_map_mgr_ = std::make_shared(bcc_.get()); ConnTracker::SetConnInfoMapManager(conn_info_map_mgr_); uprobe_mgr_.Init(FLAGS_stirling_disable_golang_tls_tracing, protocol_transfer_specs_[kProtocolHTTP2].enabled, FLAGS_stirling_disable_self_tracing); - openssl_trace_state_ = WrappedBCCArrayTable::Create(this, "openssl_trace_state"); + openssl_trace_state_ = WrappedBCCArrayTable::Create(bcc_.get(), "openssl_trace_state"); openssl_trace_state_debug_ = WrappedBCCMap::Create( - this, "openssl_trace_state_debug"); + bcc_.get(), "openssl_trace_state_debug"); return Status::OK(); } @@ -536,7 +536,7 @@ Status SocketTraceConnector::StopImpl() { // Must call Close() after attach_uprobes_thread_ has joined, // otherwise the two threads will cause concurrent accesses to BCC, // that will cause races and undefined behavior. - Close(); + bcc_->Close(); return Status::OK(); } @@ -616,7 +616,7 @@ void SocketTraceConnector::UpdateCommonState(ConnectorContext* ctx) { // so raw data will be pushed to connection trackers more aggressively. // No data is lost, but this is a side-effect of sorts that affects timing of transfers. // It may be worth noting during debug. - PollPerfBuffers(); + bcc_->PollPerfBuffers(); // Set-up current state for connection inference purposes. if (socket_info_mgr_ != nullptr) { @@ -730,7 +730,7 @@ void SocketTraceConnector::TransferDataImpl(ConnectorContext* ctx) { if (sampling_freq_mgr_.count() % (kDebugDumpPeriod / kSamplingPeriod) == 0) { if (debug_level_ >= 1) { LOG(INFO) << "Context: " << DumpContext(ctx); - LOG(INFO) << "BPF map info: " << BPFMapsInfo(static_cast(this)); + LOG(INFO) << "BPF map info: " << BPFMapsInfo(bcc_.get()); } } @@ -790,7 +790,7 @@ void SocketTraceConnector::TransferDataImpl(ConnectorContext* ctx) { Status SocketTraceConnector::UpdateBPFProtocolTraceRole(traffic_protocol_t protocol, uint64_t role_mask) { - auto control_map = WrappedBCCPerCPUArrayTable::Create(this, kControlMapName); + auto control_map = WrappedBCCPerCPUArrayTable::Create(bcc_.get(), kControlMapName); return control_map->SetValues(static_cast(protocol), role_mask); } @@ -805,12 +805,14 @@ Status SocketTraceConnector::TestOnlySetTargetPID() { "Enable CONN_TRACE for pid=$0 following --test_only_socket_trace_target_pid", pid); FLAGS_stirling_conn_trace_pid = pid; } - auto control_map = WrappedBCCPerCPUArrayTable::Create(this, kControlValuesArrayName); + auto control_map = + WrappedBCCPerCPUArrayTable::Create(bcc_.get(), kControlValuesArrayName); return control_map->SetValues(kTargetTGIDIndex, pid); } Status SocketTraceConnector::DisableSelfTracing() { - auto control_map = WrappedBCCPerCPUArrayTable::Create(this, kControlValuesArrayName); + auto control_map = + WrappedBCCPerCPUArrayTable::Create(bcc_.get(), kControlValuesArrayName); const int64_t self_pid = getpid(); return control_map->SetValues(kStirlingTGIDIndex, self_pid); } diff --git a/src/stirling/source_connectors/socket_tracer/socket_trace_connector.h b/src/stirling/source_connectors/socket_tracer/socket_trace_connector.h index 03fe368a6b6..b92ef05dae5 100644 --- a/src/stirling/source_connectors/socket_tracer/socket_trace_connector.h +++ b/src/stirling/source_connectors/socket_tracer/socket_trace_connector.h @@ -89,7 +89,7 @@ enum TraceMode : int32_t { OnForNewerKernel = 2, }; -class SocketTraceConnector : public SourceConnector, public bpf_tools::BCCWrapper { +class SocketTraceConnector : public BCCSourceConnector { public: static constexpr std::string_view kName = "socket_tracer"; static constexpr auto kTables = diff --git a/src/stirling/source_connectors/socket_tracer/testing/socket_trace_bpf_test_fixture.h b/src/stirling/source_connectors/socket_tracer/testing/socket_trace_bpf_test_fixture.h index 718329d2828..deab24ea596 100644 --- a/src/stirling/source_connectors/socket_tracer/testing/socket_trace_bpf_test_fixture.h +++ b/src/stirling/source_connectors/socket_tracer/testing/socket_trace_bpf_test_fixture.h @@ -86,7 +86,7 @@ class SocketTraceBPFTestFixture : public ::testing::Test { void StartTransferDataThread() { // Drain the perf buffers before starting the thread. // Otherwise, perf buffers may already be full, causing lost events and flaky test results. - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); transfer_data_thread_ = std::thread([this]() { transfer_enable_ = true; diff --git a/src/stirling/source_connectors/stirling_error/stirling_error_bpf_test.cc b/src/stirling/source_connectors/stirling_error/stirling_error_bpf_test.cc index bfe5563d92b..7eb9f8a910c 100644 --- a/src/stirling/source_connectors/stirling_error/stirling_error_bpf_test.cc +++ b/src/stirling/source_connectors/stirling_error/stirling_error_bpf_test.cc @@ -423,7 +423,7 @@ TEST_F(StirlingErrorTest, UProbeDeploymentError) { ASSERT_OK(stirling_->RunAsThread()); ASSERT_OK(stirling_->WaitUntilRunning(std::chrono::seconds(5))); - bpf_tools::BCCWrapper bcc_wrapper; + bpf_tools::BCCWrapperImpl bcc_wrapper; bpf_tools::UProbeSpec spec{ .binary_path = "/usr/lib/x86_64-linux-gnu/libssl.so.1.1", .symbol = "foo", diff --git a/src/stirling/source_connectors/tcp_stats/tcp_stats_connector.cc b/src/stirling/source_connectors/tcp_stats/tcp_stats_connector.cc index 38167b4617e..e5d0423e7fb 100644 --- a/src/stirling/source_connectors/tcp_stats/tcp_stats_connector.cc +++ b/src/stirling/source_connectors/tcp_stats/tcp_stats_connector.cc @@ -68,22 +68,22 @@ Status TCPStatsConnector::InitImpl() { sampling_freq_mgr_.set_period(kSamplingPeriod); push_freq_mgr_.set_period(kPushPeriod); - PX_RETURN_IF_ERROR(InitBPFProgram(tcpstats_bcc_script)); - PX_RETURN_IF_ERROR(AttachKProbes(kProbeSpecs)); - PX_RETURN_IF_ERROR(OpenPerfBuffers(perf_buffer_specs)); + PX_RETURN_IF_ERROR(bcc_->InitBPFProgram(tcpstats_bcc_script)); + PX_RETURN_IF_ERROR(bcc_->AttachKProbes(kProbeSpecs)); + PX_RETURN_IF_ERROR(bcc_->OpenPerfBuffers(perf_buffer_specs)); LOG(INFO) << absl::Substitute("Successfully deployed $0 kprobes.", kProbeSpecs.size()); return Status::OK(); } Status TCPStatsConnector::StopImpl() { - Close(); + bcc_->Close(); return Status::OK(); } void TCPStatsConnector::TransferDataImpl(ConnectorContext* ctx) { DCHECK_EQ(data_tables_.size(), 1U) << "Only one table is allowed per TCPStatsConnector."; - PollPerfBuffers(); + bcc_->PollPerfBuffers(); DataTable* data_table = data_tables_[0]; auto* agg_stats = tcp_stats_.UpdateStats(events_); diff --git a/src/stirling/source_connectors/tcp_stats/tcp_stats_connector.h b/src/stirling/source_connectors/tcp_stats/tcp_stats_connector.h index a18d83f0f66..71752ecb425 100644 --- a/src/stirling/source_connectors/tcp_stats/tcp_stats_connector.h +++ b/src/stirling/source_connectors/tcp_stats/tcp_stats_connector.h @@ -32,7 +32,7 @@ namespace px { namespace stirling { -class TCPStatsConnector : public SourceConnector, public bpf_tools::BCCWrapper { +class TCPStatsConnector : public BCCSourceConnector { public: static constexpr std::string_view kName = "tcp_stats"; static constexpr auto kSamplingPeriod = std::chrono::milliseconds{1000}; @@ -54,8 +54,7 @@ class TCPStatsConnector : public SourceConnector, public bpf_tools::BCCWrapper { void TransferDataImpl(ConnectorContext* ctx) override; protected: - explicit TCPStatsConnector(std::string_view name) - : SourceConnector(name, kTables), bpf_tools::BCCWrapper() {} + explicit TCPStatsConnector(std::string_view name) : BCCSourceConnector(name, kTables) {} private: std::vector events_; diff --git a/src/stirling/source_connectors/tcp_stats/testing/tcp_stats_bpf_test_fixture.h b/src/stirling/source_connectors/tcp_stats/testing/tcp_stats_bpf_test_fixture.h index 4e45304b8bf..22bb308ad6c 100644 --- a/src/stirling/source_connectors/tcp_stats/testing/tcp_stats_bpf_test_fixture.h +++ b/src/stirling/source_connectors/tcp_stats/testing/tcp_stats_bpf_test_fixture.h @@ -52,7 +52,7 @@ class TcpTraceBPFTestFixture : public ::testing::Test { // Otherwise, perf buffers may already be full, causing lost events and flaky test results. ASSERT_TRUE(source_ != nullptr); - source_->PollPerfBuffers(); + source_->BCC().PollPerfBuffers(); transfer_data_thread_ = std::thread([this]() { transfer_enable_ = true;