378 lines
15 KiB
Java
378 lines
15 KiB
Java
package unit;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* Linux0007Test — CWE-407 benchmark for linux-0007 and linux-0008
|
||
*
|
||
* linux-0007 (PKTGEN_THREAD_DEV_XARRAY):
|
||
* Models net/core/pktgen.c __pktgen_NN_threads() and pktgen_change_name():
|
||
* SLOW: for each thread [O(T)]:
|
||
* pktgen_find_dev() scans if_list [O(D)] per thread
|
||
* Total: O(T × D) per device lookup or rename event
|
||
* FAST: xarray/HashMap keyed by net_device* — O(1) lookup
|
||
* Total: O(1) per lookup
|
||
*
|
||
* linux-0008 (TASKSTATS_LISTENER_HASHSET):
|
||
* Models kernel/taskstats.c add_del_listener() REGISTER path:
|
||
* SLOW: for each CPU in mask [O(C)]:
|
||
* list_for_each_entry over listener_array [O(L)] to check pid exists
|
||
* Total: O(C × L) per TASKSTATS_CMD_ATTR_REGISTER_CPUMASK call
|
||
* FAST: per-CPU hash table keyed by pid — O(1) membership test
|
||
* Total: O(C) per registration
|
||
*/
|
||
public class Linux0007Test {
|
||
|
||
// =========================================================================
|
||
// linux-0007: pktgen __pktgen_NN_threads O(T×D) vs O(1)
|
||
// =========================================================================
|
||
|
||
/** Simulates struct pktgen_dev — one device managed by pktgen. */
|
||
static class PktgenDev {
|
||
final String devName; // odevname
|
||
final Object netdev; // odev pointer (net_device*)
|
||
PktgenDev(String devName, Object netdev) {
|
||
this.devName = devName;
|
||
this.netdev = netdev;
|
||
}
|
||
}
|
||
|
||
/** Simulates struct pktgen_thread — one pktgen thread managing a device list. */
|
||
static class PktgenThread {
|
||
final List<PktgenDev> ifList = new ArrayList<>();
|
||
}
|
||
|
||
/**
|
||
* SLOW: __pktgen_NN_threads — O(T × D) double list scan.
|
||
* Outer: iterate all threads. Inner: scan each thread's if_list by name.
|
||
* @return count of inner loop iterations (models computational work)
|
||
*/
|
||
static long pktgenFindDev_slow(List<PktgenThread> threads, String targetName,
|
||
long[] iterOut) {
|
||
long iters = 0;
|
||
PktgenDev found = null;
|
||
for (PktgenThread t : threads) {
|
||
for (PktgenDev dev : t.ifList) {
|
||
iters++;
|
||
if (dev.devName.equals(targetName)) {
|
||
found = dev;
|
||
break;
|
||
}
|
||
}
|
||
if (found != null) break;
|
||
}
|
||
iterOut[0] += iters;
|
||
return iters;
|
||
}
|
||
|
||
/**
|
||
* FAST: O(1) HashMap lookup by device name.
|
||
* Simulates xarray/hashtable keyed by net_device* or name.
|
||
*/
|
||
static long pktgenFindDev_fast(Map<String, PktgenDev> devMap, String targetName,
|
||
long[] iterOut) {
|
||
iterOut[0] += 1; // single hash lookup = O(1)
|
||
return devMap.containsKey(targetName) ? 1 : 1;
|
||
}
|
||
|
||
/**
|
||
* SLOW: pktgen_change_name — O(T × D) double list scan for net_device* match.
|
||
*/
|
||
static long pktgenChangeName_slow(List<PktgenThread> threads, Object targetNetdev,
|
||
String newName, long[] iterOut) {
|
||
long iters = 0;
|
||
for (PktgenThread t : threads) {
|
||
for (PktgenDev dev : t.ifList) {
|
||
iters++;
|
||
if (dev.netdev == targetNetdev) {
|
||
// proc_remove + proc_create_data (simulated)
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
iterOut[0] += iters;
|
||
return iters;
|
||
}
|
||
|
||
/**
|
||
* FAST: pktgen_change_name with xarray — O(1) lookup by net_device*.
|
||
*/
|
||
static long pktgenChangeName_fast(Map<Object, PktgenDev> devByNetdev,
|
||
Object targetNetdev, String newName,
|
||
long[] iterOut) {
|
||
iterOut[0] += 1;
|
||
return 1;
|
||
}
|
||
|
||
// =========================================================================
|
||
// linux-0008: taskstats add_del_listener O(C×L) vs O(C)
|
||
// =========================================================================
|
||
|
||
/** Simulates struct listener — one registered taskstats listener (pid). */
|
||
static class Listener {
|
||
final int pid;
|
||
boolean valid;
|
||
Listener(int pid) { this.pid = pid; this.valid = true; }
|
||
}
|
||
|
||
/** Simulates struct listener_array — per-CPU listener list. */
|
||
static class ListenerArray {
|
||
final List<Listener> list = new ArrayList<>();
|
||
}
|
||
|
||
/**
|
||
* SLOW: add_del_listener REGISTER path — O(C × L).
|
||
* For each CPU in cpumask: scan listener list to check if pid already registered.
|
||
* @return total inner loop iterations
|
||
*/
|
||
static long registerListener_slow(ListenerArray[] perCpuListeners,
|
||
boolean[] cpuMask, int newPid,
|
||
long[] iterOut) {
|
||
long iters = 0;
|
||
for (int cpu = 0; cpu < cpuMask.length; cpu++) {
|
||
if (!cpuMask[cpu]) continue;
|
||
ListenerArray la = perCpuListeners[cpu];
|
||
boolean exists = false;
|
||
for (Listener l : la.list) { // O(L) linear scan
|
||
iters++;
|
||
if (l.pid == newPid && l.valid) {
|
||
exists = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!exists) {
|
||
la.list.add(new Listener(newPid));
|
||
}
|
||
}
|
||
iterOut[0] += iters;
|
||
return iters;
|
||
}
|
||
|
||
/**
|
||
* FAST: add_del_listener with per-CPU HashSet — O(C).
|
||
* hash_for_each_possible over DECLARE_HASHTABLE → O(1) pid lookup per CPU.
|
||
*/
|
||
static long registerListener_fast(ListenerArray[] perCpuListeners,
|
||
Set<Integer>[] perCpuPidSets,
|
||
boolean[] cpuMask, int newPid,
|
||
long[] iterOut) {
|
||
long iters = 0;
|
||
for (int cpu = 0; cpu < cpuMask.length; cpu++) {
|
||
if (!cpuMask[cpu]) continue;
|
||
iters++; // one O(1) hash lookup per CPU
|
||
if (!perCpuPidSets[cpu].contains(newPid)) {
|
||
perCpuListeners[cpu].list.add(new Listener(newPid));
|
||
perCpuPidSets[cpu].add(newPid);
|
||
}
|
||
}
|
||
iterOut[0] += iters;
|
||
return iters;
|
||
}
|
||
|
||
// =========================================================================
|
||
// main
|
||
// =========================================================================
|
||
|
||
public static void main(String[] args) {
|
||
System.out.println("Linux0007Test — CWE-407 (linux-0007 pktgen, linux-0008 taskstats)");
|
||
System.out.println("=".repeat(72));
|
||
|
||
// ------------------------------------------------------------------
|
||
// linux-0007: pktgen find/rename O(T×D) vs O(1)
|
||
// ------------------------------------------------------------------
|
||
System.out.println("\nlinux-0007: pktgen __pktgen_NN_threads O(T×D) vs O(1)");
|
||
{
|
||
int T = 10; // pktgen threads (one per CPU in practice)
|
||
int D = 100; // devices per thread
|
||
|
||
List<PktgenThread> threads = new ArrayList<>(T);
|
||
Map<String, PktgenDev> devByName = new HashMap<>(T * D * 2);
|
||
Map<Object, PktgenDev> devByNetdev = new IdentityHashMap<>(T * D * 2);
|
||
|
||
// Build: each thread owns D devices, target device is in last thread
|
||
for (int t = 0; t < T; t++) {
|
||
PktgenThread thread = new PktgenThread();
|
||
for (int d = 0; d < D; d++) {
|
||
String name = "eth" + (t * D + d);
|
||
Object netdev = new Object();
|
||
PktgenDev dev = new PktgenDev(name, netdev);
|
||
thread.ifList.add(dev);
|
||
devByName.put(name, dev);
|
||
devByNetdev.put(netdev, dev);
|
||
}
|
||
threads.add(thread);
|
||
}
|
||
|
||
// Target: last device in last thread (worst case for linear scan)
|
||
PktgenThread lastThread = threads.get(T - 1);
|
||
PktgenDev targetDev = lastThread.ifList.get(D - 1);
|
||
String targetName = targetDev.devName;
|
||
Object targetNetdev = targetDev.netdev;
|
||
|
||
int LOOKUPS = 200;
|
||
|
||
// pktgenFindDev: slow vs fast
|
||
long[] sLookupIter = {0}, fLookupIter = {0};
|
||
for (int i = 0; i < LOOKUPS; i++) {
|
||
pktgenFindDev_slow(threads, targetName, sLookupIter);
|
||
pktgenFindDev_fast(devByName, targetName, fLookupIter);
|
||
}
|
||
|
||
System.out.printf(" find T=%d D=%d × %d lookups: slow=%d fast=%d%n",
|
||
T, D, LOOKUPS, sLookupIter[0], fLookupIter[0]);
|
||
assert sLookupIter[0] > fLookupIter[0] * 5
|
||
: "FAIL linux-0007 find: slow=" + sLookupIter[0]
|
||
+ " fast=" + fLookupIter[0];
|
||
System.out.println(" PASS (find slowpath >> fastpath)");
|
||
|
||
// pktgenChangeName: slow vs fast
|
||
long[] sRenameIter = {0}, fRenameIter = {0};
|
||
int RENAMES = 200;
|
||
for (int i = 0; i < RENAMES; i++) {
|
||
pktgenChangeName_slow(threads, targetNetdev, "eth_renamed", sRenameIter);
|
||
pktgenChangeName_fast(devByNetdev, targetNetdev, "eth_renamed", fRenameIter);
|
||
}
|
||
|
||
System.out.printf(" rename T=%d D=%d × %d events: slow=%d fast=%d%n",
|
||
T, D, RENAMES, sRenameIter[0], fRenameIter[0]);
|
||
assert sRenameIter[0] > fRenameIter[0] * 5
|
||
: "FAIL linux-0007 rename: slow=" + sRenameIter[0]
|
||
+ " fast=" + fRenameIter[0];
|
||
System.out.println(" PASS (rename slowpath >> fastpath)");
|
||
}
|
||
|
||
// ------------------------------------------------------------------
|
||
// linux-0007 larger scale
|
||
// ------------------------------------------------------------------
|
||
{
|
||
int T = 32, D = 500;
|
||
|
||
List<PktgenThread> threads = new ArrayList<>(T);
|
||
Map<String, PktgenDev> devByName = new HashMap<>(T * D * 2);
|
||
Map<Object, PktgenDev> devByNetdev = new IdentityHashMap<>(T * D * 2);
|
||
|
||
for (int t = 0; t < T; t++) {
|
||
PktgenThread thread = new PktgenThread();
|
||
for (int d = 0; d < D; d++) {
|
||
String name = "veth" + (t * D + d);
|
||
Object netdev = new Object();
|
||
PktgenDev dev = new PktgenDev(name, netdev);
|
||
thread.ifList.add(dev);
|
||
devByName.put(name, dev);
|
||
devByNetdev.put(netdev, dev);
|
||
}
|
||
threads.add(thread);
|
||
}
|
||
|
||
PktgenDev targetDev = threads.get(T - 1).ifList.get(D - 1);
|
||
String targetName = targetDev.devName;
|
||
Object targetNetdev = targetDev.netdev;
|
||
|
||
int LOOKUPS = 100;
|
||
long[] sIter = {0}, fIter = {0};
|
||
for (int i = 0; i < LOOKUPS; i++) {
|
||
pktgenFindDev_slow(threads, targetName, sIter);
|
||
pktgenFindDev_fast(devByName, targetName, fIter);
|
||
}
|
||
|
||
System.out.printf(" find T=%d D=%d × %d lookups: slow=%d fast=%d%n",
|
||
T, D, LOOKUPS, sIter[0], fIter[0]);
|
||
assert sIter[0] > fIter[0] * 100
|
||
: "FAIL linux-0007 large find: slow=" + sIter[0]
|
||
+ " fast=" + fIter[0];
|
||
System.out.println(" PASS");
|
||
}
|
||
|
||
// ------------------------------------------------------------------
|
||
// linux-0008: taskstats register_listener O(C×L) vs O(C)
|
||
// ------------------------------------------------------------------
|
||
System.out.println("\nlinux-0008: taskstats add_del_listener O(C×L) vs O(C)");
|
||
{
|
||
int C = 64; // CPUs
|
||
int L = 50; // existing listeners per CPU
|
||
|
||
ListenerArray[] perCpu = new ListenerArray[C];
|
||
@SuppressWarnings("unchecked")
|
||
Set<Integer>[] perCpuSets = new Set[C];
|
||
boolean[] allCpus = new boolean[C];
|
||
|
||
for (int cpu = 0; cpu < C; cpu++) {
|
||
perCpu[cpu] = new ListenerArray();
|
||
perCpuSets[cpu] = new HashSet<>();
|
||
allCpus[cpu] = true;
|
||
// Pre-register L existing listeners (pids 1000..1000+L-1)
|
||
for (int l = 0; l < L; l++) {
|
||
int existingPid = 1000 + l;
|
||
perCpu[cpu].list.add(new Listener(existingPid));
|
||
perCpuSets[cpu].add(existingPid);
|
||
}
|
||
}
|
||
|
||
// Register a new pid (not yet present) — worst case: scan all L entries
|
||
int newPid = 9999;
|
||
int REGISTRATIONS = 100;
|
||
|
||
long[] sIter = {0}, fIter = {0};
|
||
for (int i = 0; i < REGISTRATIONS; i++) {
|
||
// Remove to re-trigger not-found path each iteration
|
||
for (int cpu = 0; cpu < C; cpu++) {
|
||
perCpu[cpu].list.removeIf(l -> l.pid == newPid);
|
||
perCpuSets[cpu].remove(newPid);
|
||
}
|
||
registerListener_slow(perCpu, allCpus, newPid, sIter);
|
||
registerListener_fast(perCpu, perCpuSets, allCpus, newPid, fIter);
|
||
}
|
||
|
||
System.out.printf(" C=%d L=%d × %d registrations: slow=%d fast=%d%n",
|
||
C, L, REGISTRATIONS, sIter[0], fIter[0]);
|
||
assert sIter[0] > fIter[0] * 5
|
||
: "FAIL linux-0008 C=" + C + " L=" + L
|
||
+ ": slow=" + sIter[0] + " fast=" + fIter[0];
|
||
System.out.println(" PASS (slow >> fast)");
|
||
}
|
||
|
||
// ------------------------------------------------------------------
|
||
// linux-0008 larger scale
|
||
// ------------------------------------------------------------------
|
||
{
|
||
int C = 128, L = 200;
|
||
|
||
ListenerArray[] perCpu = new ListenerArray[C];
|
||
@SuppressWarnings("unchecked")
|
||
Set<Integer>[] perCpuSets = new Set[C];
|
||
boolean[] allCpus = new boolean[C];
|
||
|
||
for (int cpu = 0; cpu < C; cpu++) {
|
||
perCpu[cpu] = new ListenerArray();
|
||
perCpuSets[cpu] = new HashSet<>();
|
||
allCpus[cpu] = true;
|
||
for (int l = 0; l < L; l++) {
|
||
int existingPid = 1000 + l;
|
||
perCpu[cpu].list.add(new Listener(existingPid));
|
||
perCpuSets[cpu].add(existingPid);
|
||
}
|
||
}
|
||
|
||
int newPid = 88888;
|
||
int REGISTRATIONS = 50;
|
||
long[] sIter = {0}, fIter = {0};
|
||
for (int i = 0; i < REGISTRATIONS; i++) {
|
||
for (int cpu = 0; cpu < C; cpu++) {
|
||
perCpu[cpu].list.removeIf(l -> l.pid == newPid);
|
||
perCpuSets[cpu].remove(newPid);
|
||
}
|
||
registerListener_slow(perCpu, allCpus, newPid, sIter);
|
||
registerListener_fast(perCpu, perCpuSets, allCpus, newPid, fIter);
|
||
}
|
||
|
||
System.out.printf(" C=%d L=%d × %d registrations: slow=%d fast=%d%n",
|
||
C, L, REGISTRATIONS, sIter[0], fIter[0]);
|
||
assert sIter[0] > fIter[0] * 20
|
||
: "FAIL linux-0008 large: slow=" + sIter[0] + " fast=" + fIter[0];
|
||
System.out.println(" PASS");
|
||
}
|
||
|
||
System.out.println("\n=".repeat(72).substring(1));
|
||
System.out.println("Linux0007Test PASSED — linux-0007 and linux-0008 confirmed O(n²)→O(n)");
|
||
}
|
||
}
|