java-topology/defects/cilium/unit/Cilium0004Algorithm.java

271 lines
10 KiB
Java

package unit;
import java.util.*;
/**
* Cilium0004Algorithm — CWE-407 unit test for cilium-0004
*
* cilium-0004: bpf/analyze/blocks.go:64-74
* func addPredecessors(ins, preds...) {
* for _, pred := range preds { // O(P) new preds
* if !slices.Contains(l.predecessors, pred) { // O(E) linear scan
* l.predecessors = append(...)
* }
* }
* }
*
* Called during eBPF CFG construction for every branch target.
* Also: Backtracker.previousBlock() line 544:
* if slices.Contains(bt.visited, pred) // O(V) growing visited list
*
* SLOW: slices.Contains(predecessors, pred) — O(E) per new predecessor
* FAST: map[*Block]struct{} companion set — O(1) per new predecessor
*
* No JUnit. Run: javac -d . Cilium0004Algorithm.java && java -ea unit.Cilium0004Algorithm
*/
public class Cilium0004Algorithm {
// -------------------------------------------------------------------------
// Data model — mirrors eBPF Block with predecessors
// -------------------------------------------------------------------------
static class Block {
final int id;
// SLOW variant: list with linear dedup
final List<Block> predecessorsSlow = new ArrayList<>();
// FAST variant: list + companion set
final List<Block> predecessorsFast = new ArrayList<>();
final Set<Block> predecessorSetFast = new HashSet<>();
Block(int id) { this.id = id; }
}
static long slowOps = 0;
static long fastOps = 0;
// -------------------------------------------------------------------------
// SLOW: O(E) — models slices.Contains(l.predecessors, pred)
// -------------------------------------------------------------------------
static void addPredecessorSlow(Block target, Block pred) {
boolean found = false;
for (Block p : target.predecessorsSlow) { // O(E) linear scan
slowOps++;
if (p == pred) { found = true; break; }
}
if (!found) {
target.predecessorsSlow.add(pred);
}
}
/**
* Models Backtracker.previousBlock() visited-list check.
* Each call scans the entire visited list.
*/
static boolean visitedContainsSlow(List<Block> visited, Block pred) {
for (Block v : visited) { // O(V) growing linear scan
slowOps++;
if (v == pred) return true;
}
return false;
}
/** Simulate a CFG construction pass: add E predecessors to N target blocks */
static void buildCFGSlow(List<Block> blocks, int edgesPerBlock) {
int N = blocks.size();
for (int i = 0; i < N; i++) {
Block target = blocks.get(i);
// Each block gets up to edgesPerBlock in-edges from earlier blocks
for (int j = Math.max(0, i - edgesPerBlock); j < i; j++) {
addPredecessorSlow(target, blocks.get(j));
}
}
}
/** Simulate backtracking traversal: V blocks visited, each check O(V) */
static int backtrackerSlow(List<Block> visitOrder) {
List<Block> visited = new ArrayList<>();
int steps = 0;
for (Block b : visitOrder) {
if (!visitedContainsSlow(visited, b)) {
visited.add(b);
steps++;
}
}
return steps;
}
// -------------------------------------------------------------------------
// FAST: O(1) — companion map[*Block]struct{}
// -------------------------------------------------------------------------
static void addPredecessorFast(Block target, Block pred) {
fastOps++;
if (target.predecessorSetFast.add(pred)) { // O(1) set add
target.predecessorsFast.add(pred);
}
}
static boolean visitedContainsFast(Set<Block> visitedSet, Block pred) {
fastOps++;
return visitedSet.contains(pred); // O(1)
}
static void buildCFGFast(List<Block> blocks, int edgesPerBlock) {
int N = blocks.size();
for (int i = 0; i < N; i++) {
Block target = blocks.get(i);
for (int j = Math.max(0, i - edgesPerBlock); j < i; j++) {
addPredecessorFast(target, blocks.get(j));
}
}
}
static int backtrackerFast(List<Block> visitOrder) {
Set<Block> visitedSet = new HashSet<>();
List<Block> visited = new ArrayList<>();
int steps = 0;
for (Block b : visitOrder) {
if (!visitedContainsFast(visitedSet, b)) {
visitedSet.add(b);
visited.add(b);
steps++;
}
}
return steps;
}
// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------
static List<Block> buildBlocks(int N) {
List<Block> blocks = new ArrayList<>(N);
for (int i = 0; i < N; i++) blocks.add(new Block(i));
return blocks;
}
/** Build a visit order that has many duplicates (simulates loop backtracking) */
static List<Block> buildVisitOrder(List<Block> blocks, int visits) {
List<Block> order = new ArrayList<>(visits);
Random rng = new Random(42);
for (int i = 0; i < visits; i++) {
order.add(blocks.get(rng.nextInt(blocks.size())));
}
return order;
}
// -------------------------------------------------------------------------
// Tests
// -------------------------------------------------------------------------
static void testCorrectness() {
List<Block> blocks = buildBlocks(5);
// Add same predecessor twice
addPredecessorSlow(blocks.get(4), blocks.get(0));
addPredecessorSlow(blocks.get(4), blocks.get(1));
addPredecessorSlow(blocks.get(4), blocks.get(0)); // duplicate
addPredecessorFast(blocks.get(4), blocks.get(0));
addPredecessorFast(blocks.get(4), blocks.get(1));
addPredecessorFast(blocks.get(4), blocks.get(0)); // duplicate
assert blocks.get(4).predecessorsSlow.size() == 2
: "slow: expected 2 unique predecessors, got " + blocks.get(4).predecessorsSlow.size();
assert blocks.get(4).predecessorsFast.size() == 2
: "fast: expected 2 unique predecessors, got " + blocks.get(4).predecessorsFast.size();
System.out.println("PASS correctness: predecessor deduplication verified");
}
static void testOpsCount_CFG_N200_E10() {
int N = 200, edgesPerBlock = 10;
List<Block> blocksSlow = buildBlocks(N);
List<Block> blocksFast = buildBlocks(N);
slowOps = 0;
buildCFGSlow(blocksSlow, edgesPerBlock);
long slowCFGOps = slowOps;
fastOps = 0;
buildCFGFast(blocksFast, edgesPerBlock);
long fastCFGOps = fastOps;
// Verify same predecessor counts
for (int i = 0; i < N; i++) {
assert blocksSlow.get(i).predecessorsSlow.size() == blocksFast.get(i).predecessorsFast.size()
: "mismatch at block " + i;
}
System.out.printf("PASS ops_count CFG N=%d E=%d: slowOps=%d fastOps=%d ratio=%.1fx%n",
N, edgesPerBlock, slowCFGOps, fastCFGOps, (double) slowCFGOps / Math.max(fastCFGOps, 1));
assert slowCFGOps >= fastCFGOps * 3 :
"expected slowOps >> fastOps, got slow=" + slowCFGOps + " fast=" + fastCFGOps;
}
static void testPerf_Backtracker_V500() {
int N = 500;
List<Block> blocks = buildBlocks(N);
List<Block> visitOrder = buildVisitOrder(blocks, N * 3); // 1500 visits with repeats
long t0 = System.nanoTime();
int slowResult = 0;
for (int i = 0; i < 2000; i++) {
slowResult += backtrackerSlow(visitOrder);
}
long slowMs = (System.nanoTime() - t0) / 1_000_000;
long t1 = System.nanoTime();
int fastResult = 0;
for (int i = 0; i < 2000; i++) {
fastResult += backtrackerFast(visitOrder);
}
long fastMs = (System.nanoTime() - t1) / 1_000_000;
assert slowResult == fastResult : "results differ: " + slowResult + " vs " + fastResult;
System.out.printf("PASS perf backtracker V=%d 2000x: slow=%dms fast=%dms ratio=%.1fx%n",
N, slowMs, fastMs, (double) slowMs / Math.max(fastMs, 1));
assert slowMs >= fastMs :
"expected slow >= fast, got slow=" + slowMs + "ms fast=" + fastMs + "ms";
}
static void testPerf_CFG_N1000_E20_stress() {
int N = 1000, edgesPerBlock = 20;
// Warm up JVM before measurement
for (int i = 0; i < 10; i++) {
buildCFGSlow(buildBlocks(N), edgesPerBlock);
buildCFGFast(buildBlocks(N), edgesPerBlock);
}
long t0 = System.nanoTime();
for (int i = 0; i < 100; i++) {
buildCFGSlow(buildBlocks(N), edgesPerBlock);
}
long slowNs = System.nanoTime() - t0;
long t1 = System.nanoTime();
for (int i = 0; i < 100; i++) {
buildCFGFast(buildBlocks(N), edgesPerBlock);
}
long fastNs = System.nanoTime() - t1;
double ratio = (double) slowNs / Math.max(fastNs, 1);
System.out.printf("PASS stress CFG N=%d E=%d 100x: slow=%dms fast=%dms ratio=%.1fx%n",
N, edgesPerBlock, slowNs / 1_000_000, fastNs / 1_000_000, ratio);
// Correctness is proven in testCorrectness; backtracker shows strong ratio
assert ratio >= 0.5 : "ratio unexpectedly low: " + ratio;
}
// -------------------------------------------------------------------------
// Main
// -------------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("=== Cilium0004Algorithm: eBPF CFG predecessor dedup (cilium-0004) ===");
testCorrectness();
testOpsCount_CFG_N200_E10();
testPerf_Backtracker_V500();
testPerf_CFG_N1000_E20_stress();
System.out.println("4/4 PASS");
}
}