Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kprobe multi return prog #2984

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ if (HAVE_LIBBPF_UPROBE_MULTI)
set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBBPF_UPROBE_MULTI)
endif(HAVE_LIBBPF_UPROBE_MULTI)

# TODO do real detection
set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_BPF_KPROBE_MULTI_RETURN_PROG)

add_subdirectory(src)
if (BUILD_TESTING)
add_subdirectory(tests)
Expand Down
65 changes: 43 additions & 22 deletions src/attached_probe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,12 @@ AttachedProbe::AttachedProbe(Probe &probe,
BpfProgram &&prog,
bool safe_mode,
BPFfeature &feature,
BTF &btf)
: probe_(probe), prog_(std::move(prog)), btf_(btf)
BTF &btf,
std::optional<BpfProgram> retprog)
: probe_(probe),
prog_(std::move(prog)),
retprog_(std::move(retprog)),
btf_(btf)
{
load_prog(feature);
if (bt_verbose)
Expand Down Expand Up @@ -233,7 +237,7 @@ AttachedProbe::AttachedProbe(Probe &probe,
BPFfeature &feature,
BTF &btf,
bool safe_mode)
: probe_(probe), prog_(std::move(prog)), btf_(btf)
: probe_(probe), prog_(std::move(prog)), retprog_(std::nullopt), btf_(btf)
{
load_prog(feature);
switch (probe_.type) {
Expand Down Expand Up @@ -668,15 +672,12 @@ const std::regex helper_verifier_error_re(
"[0-9]+: \\([0-9]+\\) call ([a-z|A-Z|0-9|_]+)#([0-9]+)");
}

void AttachedProbe::load_prog(BPFfeature &feature)
int AttachedProbe::load_prog_fd(BPFfeature &feature, BpfProgram &prog)
{
if (use_cached_progfd())
return;

auto &insns = prog_.getCode();
auto func_infos = prog_.getFuncInfos();
auto &insns = prog.getCode();
auto func_infos = prog.getFuncInfos();
const char *license = "GPL";
int log_level = 0;
int log_level = 0, progfd = -1;

uint64_t log_buf_size = probe_.log_size;
auto log_buf = std::make_unique<char[]>(log_buf_size);
Expand Down Expand Up @@ -763,7 +764,7 @@ void AttachedProbe::load_prog(BPFfeature &feature)
// one attachpoint in a multi-attachpoint (wildcard or list) probe
// failed, print a warning but continue
LOG(WARNING) << msg << ", skipping.";
return;
return -1;
} else
// explicit match failed, fail hard
throw std::runtime_error(msg);
Expand Down Expand Up @@ -802,25 +803,25 @@ void AttachedProbe::load_prog(BPFfeature &feature)
// This will fall back to the error handling for failed program load,
// which is more robust.
if (btf_fd >= 0) {
progfd_ = bpf_prog_load(static_cast<::bpf_prog_type>(prog_type),
name.c_str(),
license,
reinterpret_cast<const struct bpf_insn *>(
insns.data()),
insns.size() / sizeof(struct bpf_insn),
&opts);
progfd = bpf_prog_load(static_cast<::bpf_prog_type>(prog_type),
name.c_str(),
license,
reinterpret_cast<const struct bpf_insn *>(
insns.data()),
insns.size() / sizeof(struct bpf_insn),
&opts);
close(btf_fd);
}
}

if (opts.attach_btf_obj_fd > 0)
close(opts.attach_btf_obj_fd);
if (progfd_ >= 0)
if (progfd >= 0)
break;
}
}

if (progfd_ < 0) {
if (progfd < 0) {
std::stringstream errmsg;
if (bt_verbose) {
std::cerr << std::endl
Expand Down Expand Up @@ -854,7 +855,7 @@ void AttachedProbe::load_prog(BPFfeature &feature)
// one attachpoint in a multi-attachpoint (wildcard or list) probe failed,
// print a warning but continue
LOG(WARNING) << errmsg.str() << ", skipping.";
return;
return -1;
} else
// explicit match failed, fail hard
throw std::runtime_error(errmsg.str());
Expand All @@ -865,14 +866,27 @@ void AttachedProbe::load_prog(BPFfeature &feature)
uint32_t info_len = sizeof(info);
int ret;

ret = bpf_obj_get_info(progfd_, &info, &info_len);
ret = bpf_obj_get_info(progfd, &info, &info_len);
if (ret == 0) {
std::cout << std::endl << "Program ID: " << info.id << std::endl;
}
std::cout << std::endl
<< "The verifier log: " << std::endl
<< log_buf.get() << std::endl;
}
return progfd;
}

void AttachedProbe::load_prog(BPFfeature &feature)
{
if (use_cached_progfd())
return;

progfd_ = load_prog_fd(feature, prog_);

if (retprog_ != std::nullopt) {
retprogfd_ = load_prog_fd(feature, *retprog_);
}

cache_progfd();
}
Expand All @@ -893,6 +907,13 @@ void AttachedProbe::attach_multi_kprobe(void)
? BPF_F_KPROBE_MULTI_RETURN
: 0;

#ifdef HAVE_BPF_KPROBE_MULTI_RETURN_PROG
if (retprogfd_ >= 0) {
opts.kprobe_multi.return_prog_fd = retprogfd_;
opts.kprobe_multi.flags |= BPF_F_KPROBE_MULTI_RETURN_PROG;
}
#endif

if (bt_verbose) {
std::cout << "Attaching to " << probe_.funcs.size() << " functions"
<< std::endl;
Expand Down
6 changes: 5 additions & 1 deletion src/attached_probe.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class AttachedProbe {
BpfProgram &&prog,
bool safe_mode,
BPFfeature &feature,
BTF &btf);
BTF &btf,
std::optional<BpfProgram> retprog = std::nullopt);
AttachedProbe(Probe &probe,
BpfProgram &&prog,
int pid,
Expand All @@ -45,6 +46,7 @@ class AttachedProbe {
void resolve_offset_kprobe(bool safe_mode);
bool resolve_offset_uprobe(bool safe_mode);
void load_prog(BPFfeature &feature);
int load_prog_fd(BPFfeature &feature, BpfProgram &prog);
void attach_multi_kprobe(void);
void attach_multi_uprobe(int pid);
void attach_kprobe(bool safe_mode);
Expand Down Expand Up @@ -83,9 +85,11 @@ class AttachedProbe {

Probe &probe_;
BpfProgram prog_;
std::optional<BpfProgram> retprog_;
std::vector<int> perf_event_fds_;
bool close_progfd_ = true;
int progfd_ = -1;
int retprogfd_ = -1;
uint64_t offset_ = 0;
int tracing_fd_ = -1;
std::function<void()> usdt_destructor_;
Expand Down
152 changes: 135 additions & 17 deletions src/bpftrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,31 @@ Probe BPFtrace::generateWatchpointSetupProbe(const std::string &func,
return setup_probe;
}

bool BPFtrace::need_expansion(ast::AttachPoint *attach_point,
bool &underspecified_usdt_probe)
{
// An underspecified usdt probe is a probe that has no wildcards and
// either an empty namespace or a specified PID.
// We try to find a unique match for such a probe.
underspecified_usdt_probe = probetype(attach_point->provider) ==
ProbeType::usdt &&
!has_wildcard(attach_point->target) &&
!has_wildcard(attach_point->ns) &&
!has_wildcard(attach_point->func) &&
(attach_point->ns.empty() || pid() > 0);

if (attach_point->need_expansion &&
(has_wildcard(attach_point->func) || has_wildcard(attach_point->target) ||
has_wildcard(attach_point->ns) || underspecified_usdt_probe))
return true;

// do expansion path for for single kprobe/kretprobe when
// kprobe_multi is supported
return (probetype(attach_point->provider) == ProbeType::kprobe ||
probetype(attach_point->provider) == ProbeType::kretprobe) &&
feature_->has_kprobe_multi();
}

int BPFtrace::add_probe(ast::Probe &p)
{
for (auto attach_point : *p.attach_points) {
Expand All @@ -96,20 +121,10 @@ int BPFtrace::add_probe(ast::Probe &p)
continue;
}

bool underspecified_usdt_probe;

std::vector<std::string> attach_funcs;
// An underspecified usdt probe is a probe that has no wildcards and
// either an empty namespace or a specified PID.
// We try to find a unique match for such a probe.
bool underspecified_usdt_probe = probetype(attach_point->provider) ==
ProbeType::usdt &&
!has_wildcard(attach_point->target) &&
!has_wildcard(attach_point->ns) &&
!has_wildcard(attach_point->func) &&
(attach_point->ns.empty() || pid() > 0);
if (attach_point->need_expansion &&
(has_wildcard(attach_point->func) ||
has_wildcard(attach_point->target) || has_wildcard(attach_point->ns) ||
underspecified_usdt_probe)) {
if (need_expansion(attach_point, underspecified_usdt_probe)) {
std::set<std::string> matches;
try {
matches = probe_matcher_->get_matches_for_ap(*attach_point);
Expand All @@ -128,8 +143,8 @@ int BPFtrace::add_probe(ast::Probe &p)

attach_funcs.insert(attach_funcs.end(), matches.begin(), matches.end());

if (feature_->has_kprobe_multi() && has_wildcard(attach_point->func) &&
!p.need_expansion && attach_funcs.size() &&
if (feature_->has_kprobe_multi() && !p.need_expansion &&
attach_funcs.size() &&
(probetype(attach_point->provider) == ProbeType::kprobe ||
probetype(attach_point->provider) == ProbeType::kretprobe) &&
attach_point->target.empty()) {
Expand Down Expand Up @@ -930,6 +945,46 @@ std::vector<std::unique_ptr<AttachedProbe>> BPFtrace::attach_probe(
return ret;
}

// resolve return program
std::optional<BpfProgram> retprogram = std::nullopt;
if (probe.ret.index >= 0) {
name = get_section_name_for_probe(probe.ret.name,
probe.ret.index,
usdt_location_idx);

orig_name = get_section_name_for_probe(probe.ret.orig_name,
probe.ret.index,
usdt_location_idx);

auto rp = BpfProgram::CreateFromBytecode(bytecode, name, maps);
if (!rp) {
auto orig_program = BpfProgram::CreateFromBytecode(bytecode,
orig_name,
maps);
if (orig_program)
rp.emplace(std::move(*orig_program));
}

retprogram.emplace(std::move(*rp));

if (!retprogram) {
if (probe.ret.name != probe.ret.orig_name)
LOG(ERROR) << "Code not generated for probe: " << probe.name
<< " from: " << probe.orig_name;
else
LOG(ERROR) << "Code not generated for probe: " << probe.name;
return ret;
}

try {
retprogram->assemble();
} catch (const std::runtime_error &ex) {
LOG(ERROR) << "Failed to assemble program for probe: " << probe.ret.name
<< ", " << ex.what();
return ret;
}
}

try {
pid_t pid = child_ ? child_->pid() : this->pid();

Expand All @@ -951,8 +1006,12 @@ std::vector<std::unique_ptr<AttachedProbe>> BPFtrace::attach_probe(
probe, std::move(*program), pid, *feature_, *btf_));
return ret;
} else {
ret.emplace_back(std::make_unique<AttachedProbe>(
probe, std::move(*program), safe_mode_, *feature_, *btf_));
ret.emplace_back(std::make_unique<AttachedProbe>(probe,
std::move(*program),
safe_mode_,
*feature_,
*btf_,
std::move(retprogram)));
return ret;
}
} catch (const EnospcException &e) {
Expand Down Expand Up @@ -1103,6 +1162,63 @@ int BPFtrace::prerun() const
return 0;
}

void BPFtrace::merge_kprobes(void)
{
std::vector<Probe> retkprobes;

for (auto &kprobe : resources.probes) {
// kprobes only
if (kprobe.type != ProbeType::kprobe)
continue;

// with kprobe_multi attach
if (!kprobe.funcs.size())
continue;

auto rit = std::find_if(
resources.probes.begin(), resources.probes.end(), [&](const auto &e) {
if (e.type != ProbeType::kretprobe)
return false;

if (e.attach_point == kprobe.attach_point)
return true;

return e.funcs.size() ==
kprobe.funcs.size() /* && TODO match funcs */;
});

if (rit == resources.probes.end())
continue;

auto kretprobe = *rit;

kprobe.ret.index = kretprobe.index;
kprobe.ret.name = kretprobe.name;
kprobe.ret.orig_name = kretprobe.orig_name;

retkprobes.push_back(kretprobe);
}

for (auto it = resources.probes.begin(); it != resources.probes.end();) {
auto kretprobe = *it;

if (kretprobe.type != ProbeType::kretprobe) {
it++;
continue;
}

auto found = std::any_of(
retkprobes.begin(), retkprobes.end(), [&](const auto &cand) {
return kretprobe.attach_point == cand.attach_point;
});

if (found)
it = resources.probes.erase(it);
else
it++;
}
}

int BPFtrace::run(BpfBytecode bytecode)
{
int err = prerun();
Expand Down Expand Up @@ -1153,6 +1269,8 @@ int BPFtrace::run(BpfBytecode bytecode)
}
}

merge_kprobes();

// The kernel appears to fire some probes in the order that they were
// attached and others in reverse order. In order to make sure that blocks
// are executed in the same order they were declared, iterate over the probes
Expand Down
3 changes: 3 additions & 0 deletions src/bpftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ class BPFtrace {
Config config_;

private:
bool need_expansion(ast::AttachPoint *attach_point,
bool &underspecified_usdt_probe);
void merge_kprobes(void);
int run_special_probe(std::string name,
const BpfBytecode &bytecode,
void (*trigger)(void));
Expand Down
Loading
Loading