Skip to content

Commit f974c70

Browse files
author
Pete Stevenson
authored
Add rr sub-tree, impl. perf buffer record & replay. (#1708)
Summary: We add the `rr` sub-tree to implement BPF record & replay. In this PR, we include _only_ perf buffer record and replay. BPF maps & arrays will be included in a subsequent PR. Type of change: /kind feature Test Plan: We add a new test case: `rr_bpf_test`. --------- Signed-off-by: Pete Stevenson <jps@pixielabs.ai>
1 parent 5db3fe6 commit f974c70

11 files changed

Lines changed: 762 additions & 7 deletions

File tree

src/stirling/bpf_tools/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pl_cc_library(
3939
"//src/stirling/bpf_tools/bcc_bpf:task_struct_mem_read",
4040
"//src/stirling/bpf_tools/bcc_bpf_intf:cc_library",
4141
"//src/stirling/bpf_tools/probe_specs:cc_library",
42+
"//src/stirling/bpf_tools/rr:cc_library",
4243
"//src/stirling/obj_tools:cc_library",
4344
"//src/stirling/utils:cc_library",
4445
"@com_github_iovisor_bcc//:bcc",

src/stirling/bpf_tools/bcc_wrapper.cc

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "src/common/perf/scoped_timer.h"
3232
#include "src/common/system/config.h"
3333
#include "src/common/system/kernel_version.h"
34+
#include "src/stirling/bpf_tools/rr/rr.h"
3435
#include "src/stirling/bpf_tools/task_struct_resolver.h"
3536
#include "src/stirling/utils/linux_headers.h"
3637

@@ -367,7 +368,7 @@ Status BCCWrapperImpl::OpenPerfBuffer(const PerfBufferSpec& perf_buffer_spec) {
367368
auto& data_fn = perf_buffer_spec.probe_output_fn;
368369
auto& loss_fn = perf_buffer_spec.probe_loss_fn;
369370

370-
PX_RETURN_IF_ERROR(BPF()->open_perf_buffer(name, data_fn, loss_fn, cb_cookie, num_pages));
371+
PX_RETURN_IF_ERROR(bpf_.open_perf_buffer(name, data_fn, loss_fn, cb_cookie, num_pages));
371372

372373
++num_open_perf_buffers_;
373374
return Status::OK();
@@ -461,6 +462,23 @@ void BCCWrapperImpl::Close() {
461462
DetachTracepoints();
462463
}
463464

465+
Status RecordingBCCWrapperImpl::OpenPerfBuffer(const PerfBufferSpec& perf_buffer_spec) {
466+
PerfBufferSpec pbs(perf_buffer_spec);
467+
pbs.recorder = recorder_.get();
468+
469+
const int num_pages = CommonPerfBufferSetup(pbs);
470+
471+
const std::string name = std::string(pbs.name);
472+
void* cb_cookie = &perf_buffer_specs_[num_open_perf_buffers_];
473+
auto data_fn = &RecordPerfBufferEvent;
474+
auto loss_fn = &RecordPerfBufferLoss;
475+
476+
PX_RETURN_IF_ERROR(bpf_.open_perf_buffer(name, data_fn, loss_fn, cb_cookie, num_pages));
477+
++num_open_perf_buffers_;
478+
479+
return Status::OK();
480+
}
481+
464482
std::unique_ptr<BCCWrapper> CreateBCC() { return std::make_unique<BCCWrapperImpl>(); }
465483

466484
std::unique_ptr<WrappedBCCStackTable> WrappedBCCStackTable::Create(bpf_tools::BCCWrapper* bcc,

src/stirling/bpf_tools/bcc_wrapper.h

Lines changed: 137 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "src/common/base/base.h"
4747
#include "src/common/json/json.h"
4848
#include "src/stirling/bpf_tools/probe_specs/probe_specs.h"
49+
#include "src/stirling/bpf_tools/rr/rr.h"
4950
#include "src/stirling/bpf_tools/task_struct_resolver.h"
5051
#include "src/stirling/obj_tools/elf_reader.h"
5152

@@ -89,7 +90,11 @@ class BCCWrapper {
8990
return task_struct_offsets_opt_;
9091
}
9192

92-
virtual ebpf::BPF* BPF() = 0;
93+
virtual StatusOr<ebpf::BPF*> BPF() = 0;
94+
virtual StatusOr<BPFRecorder*> GetBPFRecorder() const = 0;
95+
virtual StatusOr<BPFReplayer*> GetBPFReplayer() const = 0;
96+
virtual bool IsRecording() const = 0;
97+
virtual bool IsReplaying() const = 0;
9398

9499
/**
95100
* Compiles the BPF code.
@@ -271,7 +276,11 @@ class BCCWrapperImpl : public BCCWrapper {
271276
Close();
272277
}
273278

274-
ebpf::BPF* BPF() override { return &bpf_; }
279+
StatusOr<ebpf::BPF*> BPF() override { return &bpf_; }
280+
StatusOr<BPFRecorder*> GetBPFRecorder() const override { return error::Internal("Wrong impl."); }
281+
StatusOr<BPFReplayer*> GetBPFReplayer() const override { return error::Internal("Wrong impl."); }
282+
bool IsRecording() const override { return false; }
283+
bool IsReplaying() const override { return false; }
275284

276285
Status InitBPFProgram(std::string_view bpf_program, std::vector<std::string> cflags = {},
277286
bool requires_linux_headers = true,
@@ -340,11 +349,111 @@ class BCCWrapperImpl : public BCCWrapper {
340349
// DEBUG_SOURCE = 0x8,
341350
// DEBUG_BPF_REGISTER_STATE = 0x10,
342351
// DEBUG_BTF = 0x20,
352+
protected:
343353
ebpf::BPF bpf_;
344354
};
345355

356+
class RecordingBCCWrapperImpl : public BCCWrapperImpl {
357+
public:
358+
bool IsRecording() const override { return true; }
359+
bool IsReplaying() const override { return false; }
360+
StatusOr<BPFRecorder*> GetBPFRecorder() const override { return recorder_.get(); }
361+
StatusOr<BPFReplayer*> GetBPFReplayer() const override { return error::Internal("Wrong impl."); }
362+
363+
Status OpenPerfBuffer(const PerfBufferSpec& perf_buffer) override;
364+
365+
RecordingBCCWrapperImpl() { recorder_ = std::make_unique<BPFRecorder>(); }
366+
367+
void WriteProto(const std::string& pb_file_path) { recorder_->WriteProto(pb_file_path); }
368+
369+
private:
370+
std::unique_ptr<BPFRecorder> recorder_;
371+
};
372+
373+
class ReplayingBCCWrapperImpl : public BCCWrapper {
374+
public:
375+
bool IsRecording() const override { return false; }
376+
bool IsReplaying() const override { return true; }
377+
StatusOr<BPFRecorder*> GetBPFRecorder() const override { return error::Internal("Wrong impl."); }
378+
StatusOr<BPFReplayer*> GetBPFReplayer() const override { return replayer_.get(); }
379+
380+
virtual ~ReplayingBCCWrapperImpl() {}
381+
382+
ReplayingBCCWrapperImpl() { replayer_ = std::make_unique<BPFReplayer>(); }
383+
384+
StatusOr<ebpf::BPF*> BPF() override { return error::Internal("Wrong impl."); }
385+
386+
Status InitBPFProgram(std::string_view, std::vector<std::string> cflags = {},
387+
bool requires_linux_headers = true,
388+
bool always_infer_task_struct_offsets = false) override {
389+
PX_UNUSED(cflags);
390+
PX_UNUSED(requires_linux_headers);
391+
PX_UNUSED(always_infer_task_struct_offsets);
392+
return Status::OK();
393+
}
394+
Status AttachKProbe(const KProbeSpec&) override { return Status::OK(); }
395+
Status AttachUProbe(const UProbeSpec&) override { return Status::OK(); }
396+
Status AttachTracepoint(const TracepointSpec&) override { return Status::OK(); }
397+
Status AttachSamplingProbe(const SamplingProbeSpec&) override { return Status::OK(); }
398+
Status AttachPerfEvent(const PerfEventSpec&) override { return Status::OK(); }
399+
Status AttachKProbes(const ArrayView<KProbeSpec>&) override { return Status::OK(); }
400+
Status AttachTracepoints(const ArrayView<TracepointSpec>&) override { return Status::OK(); }
401+
Status AttachUProbes(const ArrayView<UProbeSpec>&) override { return Status::OK(); }
402+
Status AttachSamplingProbes(const ArrayView<SamplingProbeSpec>&) override { return Status::OK(); }
403+
Status AttachXDP(const std::string&, const std::string&) override { return Status::OK(); }
404+
Status AttachPerfEvents(const ArrayView<PerfEventSpec>&) override { return Status::OK(); }
405+
Status PopulateBPFPerfArray(const std::string&, const uint32_t, const uint64_t) override {
406+
return Status::OK();
407+
}
408+
409+
Status OpenPerfBuffer(const PerfBufferSpec& pbs) override {
410+
perf_buffer_specs_.push_back(std::make_unique<PerfBufferSpec>(pbs));
411+
return Status::OK();
412+
}
413+
414+
Status OpenPerfBuffers(const ArrayView<PerfBufferSpec>& perf_buffer_specs) override {
415+
for (const auto& pbs : perf_buffer_specs) {
416+
PX_RETURN_IF_ERROR(OpenPerfBuffer(pbs));
417+
}
418+
return Status::OK();
419+
}
420+
421+
Status PollPerfBuffer(const std::string& name, const int timeout_ms = 0) override {
422+
PX_UNUSED(timeout_ms);
423+
for (const auto& pbs : perf_buffer_specs_) {
424+
if (pbs->name == name) {
425+
replayer_->ReplayPerfBufferEvents(*pbs);
426+
return Status::OK();
427+
}
428+
}
429+
return error::NotFound(absl::Substitute("Perf buffer \"$0\" not found.", name));
430+
}
431+
432+
void PollPerfBuffers(const int timeout_ms = 0) override {
433+
PX_UNUSED(timeout_ms);
434+
for (const auto& pbs : perf_buffer_specs_) {
435+
replayer_->ReplayPerfBufferEvents(*pbs);
436+
}
437+
};
438+
439+
void Close() override{};
440+
441+
Status ClosePerfBuffer(const PerfBufferSpec&) override { return Status::OK(); }
442+
443+
Status OpenReplayProtobuf(const std::string& file_path) {
444+
return replayer_->OpenReplayProtobuf(file_path);
445+
}
446+
447+
private:
448+
std::unique_ptr<BPFReplayer> replayer_;
449+
std::vector<std::unique_ptr<PerfBufferSpec>> perf_buffer_specs_;
450+
};
451+
346452
std::unique_ptr<BCCWrapper> CreateBCC();
347453

454+
////////////////////////////////////////////////////////////////////////////////////////////////////
455+
////////////////////////////////////////////////////////////////////////////////////////////////////
456+
// Array Table.
348457
template <typename T>
349458
class WrappedBCCArrayTable {
350459
public:
@@ -378,7 +487,8 @@ class WrappedBCCArrayTableImpl : public WrappedBCCArrayTable<T> {
378487
}
379488

380489
WrappedBCCArrayTableImpl(bpf_tools::BCCWrapper* bcc, const std::string& name) : name_(name) {
381-
underlying_ = std::make_unique<U>(bcc->BPF()->get_array_table<T>(name_));
490+
ebpf::BPF* bpf = bcc->BPF().ConsumeValueOrDie();
491+
underlying_ = std::make_unique<U>(bpf->get_array_table<T>(name_));
382492
}
383493

384494
protected:
@@ -389,6 +499,9 @@ class WrappedBCCArrayTableImpl : public WrappedBCCArrayTable<T> {
389499
std::unique_ptr<U> underlying_;
390500
};
391501

502+
////////////////////////////////////////////////////////////////////////////////////////////////////
503+
////////////////////////////////////////////////////////////////////////////////////////////////////
504+
// Map / BPF Hash Table
392505
template <typename K, typename V, bool kUserSpaceManaged = false>
393506
class WrappedBCCMap {
394507
public:
@@ -472,7 +585,8 @@ class WrappedBCCMapImpl : public WrappedBCCMap<K, V, kUserSpaceManaged> {
472585
}
473586

474587
WrappedBCCMapImpl(bpf_tools::BCCWrapper* bcc, const std::string& name) : name_(name) {
475-
underlying_ = std::make_unique<U>(bcc->BPF()->get_hash_table<K, V>(name_));
588+
ebpf::BPF* bpf = bcc->BPF().ConsumeValueOrDie();
589+
underlying_ = std::make_unique<U>(bpf->get_hash_table<K, V>(name_));
476590
}
477591

478592
protected:
@@ -484,6 +598,9 @@ class WrappedBCCMapImpl : public WrappedBCCMap<K, V, kUserSpaceManaged> {
484598
absl::flat_hash_set<K> shadow_keys_;
485599
};
486600

601+
////////////////////////////////////////////////////////////////////////////////////////////////////
602+
////////////////////////////////////////////////////////////////////////////////////////////////////
603+
// Per CPU Array Table
487604
template <typename T>
488605
class WrappedBCCPerCPUArrayTable {
489606
public:
@@ -511,14 +628,18 @@ class WrappedBCCPerCPUArrayTableImpl : public WrappedBCCPerCPUArrayTable<T> {
511628

512629
WrappedBCCPerCPUArrayTableImpl(bpf_tools::BCCWrapper* bcc, const std::string& name)
513630
: name_(name) {
514-
underlying_ = std::make_unique<U>(bcc->BPF()->get_percpu_array_table<T>(name_));
631+
ebpf::BPF* bpf = bcc->BPF().ConsumeValueOrDie();
632+
underlying_ = std::make_unique<U>(bpf->get_percpu_array_table<T>(name_));
515633
}
516634

517635
private:
518636
const std::string name_;
519637
std::unique_ptr<U> underlying_;
520638
};
521639

640+
////////////////////////////////////////////////////////////////////////////////////////////////////
641+
////////////////////////////////////////////////////////////////////////////////////////////////////
642+
// Stack Table
522643
class WrappedBCCStackTable {
523644
public:
524645
static std::unique_ptr<WrappedBCCStackTable> Create(bpf_tools::BCCWrapper* bcc,
@@ -545,7 +666,8 @@ class WrappedBCCStackTableImpl : public WrappedBCCStackTable {
545666
void ClearStackID(const int stack_id) override { underlying_->clear_stack_id(stack_id); }
546667

547668
WrappedBCCStackTableImpl(bpf_tools::BCCWrapper* bcc, const std::string& name) : name_(name) {
548-
underlying_ = std::make_unique<U>(bcc->BPF()->get_stack_table(name_));
669+
ebpf::BPF* bpf = bcc->BPF().ConsumeValueOrDie();
670+
underlying_ = std::make_unique<U>(bpf->get_stack_table(name_));
549671
}
550672

551673
protected:
@@ -555,7 +677,16 @@ class WrappedBCCStackTableImpl : public WrappedBCCStackTable {
555677
std::unique_ptr<U> underlying_;
556678
};
557679

680+
////////////////////////////////////////////////////////////////////////////////////////////////////
681+
////////////////////////////////////////////////////////////////////////////////////////////////////
558682
// Creators fns for wrapped maps & arrays:
683+
// template <typename BaseT, typename ImplT>
684+
// std::unique_ptr<BaseT> CreateBCCWrappedMapOrArray(BCCWrapper* bcc, const std::string& name) {
685+
// // The decision logic for "normal" vs. "recording" vs. "replaying" impl. will be inserted
686+
// // here in a future PR.
687+
// return std::make_unique<ImplT>(bcc, name);
688+
// }
689+
559690
template <typename BaseT, typename ImplT>
560691
std::unique_ptr<BaseT> CreateBCCWrappedMapOrArray(BCCWrapper* bcc, const std::string& name) {
561692
return std::make_unique<ImplT>(bcc, name);

src/stirling/bpf_tools/probe_specs/probe_specs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ namespace px {
4444
namespace stirling {
4545
namespace bpf_tools {
4646

47+
class BPFRecorder;
48+
4749
enum class BPFProbeAttachType {
4850
// Attach to function entry.
4951
kEntry = BPF_PROBE_ENTRY,
@@ -180,6 +182,9 @@ struct PerfBufferSpec {
180182
// to count this buffer's size against.
181183
PerfBufferSizeCategory size_category = PerfBufferSizeCategory::kUncategorized;
182184

185+
// This will be populated and used only if the BPF recording BCC wrapper is used.
186+
BPFRecorder* recorder = nullptr;
187+
183188
std::string ToString() const {
184189
return absl::Substitute("name=$0 size_bytes=$1 size_category=$2", name, size_bytes,
185190
magic_enum::enum_name(size_category));
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright 2018- The Pixie Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# SPDX-License-Identifier: Apache-2.0
16+
17+
load("//bazel:pl_build_system.bzl", "pl_cc_bpf_test", "pl_cc_library")
18+
load("//bazel:proto_compile.bzl", "pl_cc_proto_library", "pl_proto_library")
19+
20+
package(default_visibility = ["//src/stirling:__subpackages__"])
21+
22+
pl_proto_library(
23+
name = "rr_pl_proto",
24+
srcs = ["rr.proto"],
25+
deps = [],
26+
)
27+
28+
pl_cc_proto_library(
29+
name = "rr_pl_cc_proto",
30+
proto = ":rr_pl_proto",
31+
deps = [],
32+
)
33+
34+
pl_cc_library(
35+
name = "cc_library",
36+
srcs = glob(
37+
["*.cc"],
38+
exclude = [
39+
"**/*_test.cc",
40+
],
41+
),
42+
hdrs = glob(
43+
[
44+
"*.h",
45+
],
46+
),
47+
deps = [
48+
"//src/stirling/bpf_tools/probe_specs:cc_library",
49+
"//src/stirling/bpf_tools/rr:rr_pl_cc_proto",
50+
],
51+
)
52+
53+
pl_cc_bpf_test(
54+
name = "rr_bpf_test",
55+
srcs = ["rr_bpf_test.cc"],
56+
tags = [
57+
"cpu:16",
58+
"requires_bpf",
59+
],
60+
deps = [
61+
":cc_library",
62+
"//src/stirling/bpf_tools:cc_library",
63+
"//src/stirling/bpf_tools/rr/testing/bpf:rr_test_bpf_text",
64+
],
65+
)

0 commit comments

Comments
 (0)