Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
10 changes: 10 additions & 0 deletions src/fwd_analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ class InterleavedFwdFixpointIterator final {
return std::numeric_limits<int>::max();
}

int max_call_depth() const {
int max_call_depth = 0;
for (const Label& label : _prog.labels()) {
// Label depth includes the entry frame; callers need only BPF-to-BPF calls.
max_call_depth = std::max(max_call_depth, label.call_stack_depth() - 1);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return max_call_depth;
}

public:
void operator()(const Label& node);

Expand Down Expand Up @@ -223,6 +232,7 @@ AnalysisResult InterleavedFwdFixpointIterator::run(const AnalysisContext& contex
const Program& prog = context.program;
AnalysisResult result;
InterleavedFwdFixpointIterator analyzer(context, result);
result.max_call_depth = analyzer.max_call_depth();
if (context.runtime().check_for_termination) {
analyzer._wto.for_each_loop_head(
[&](const Label& label) { ebpf_domain_initialize_loop_counter(entry_inv, label, context); });
Expand Down
1 change: 1 addition & 0 deletions src/result.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ struct AnalysisResult {
std::map<Label, InvariantMapPair> invariants;
bool failed = false;
int max_loop_count{};
int max_call_depth{};
Interval exit_value = Interval::top();

[[nodiscard]]
Expand Down
26 changes: 26 additions & 0 deletions src/test/test_cfg_builder_passes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "ir/program.hpp"
#include "ir/syntax.hpp"
#include "platform.hpp"
#include "verifier.hpp"

using namespace prevail;

Expand Down Expand Up @@ -207,6 +208,31 @@ TEST_CASE("pass_insert_termination_counters is off by default", "[passes]") {
}
}

TEST_CASE("AnalysisResult reports maximum nested BPF-to-BPF call depth", "[passes][stats]") {
const ProgramInfo info = default_info();

SECTION("does not count the entry frame") {
InstructionSeq seq;
seq.push_back(at(0, Exit{}));

const Program prog = Program::from_sequence(seq, info, {});
REQUIRE(analyze(prog, {}).max_call_depth == 0);
}

SECTION("counts nested local calls") {
InstructionSeq seq;
// entry -> call subprogram 1 -> call subprogram 2 -> exit.
seq.push_back(at(0, CallLocal{.target = Label{2}}));
seq.push_back(at(1, Exit{}));
seq.push_back(at(2, CallLocal{.target = Label{4}}));
seq.push_back(at(3, Exit{}));
seq.push_back(at(4, Exit{}));

const Program prog = Program::from_sequence(seq, info, {});
REQUIRE(analyze(prog, {}).max_call_depth == 2);
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

TEST_CASE("pass_extract_assertions populates assertions for every label in the CFG", "[passes]") {
const ProgramInfo info = default_info();
InstructionSeq seq;
Expand Down
Loading