package unit; import java.util.*; /** * AsteriskTest — CWE-407 benchmark for asterisk-0001 and asterisk-0002 * * asterisk-0001 (ASTERISK_MEETME_CONF_FIND): * Models find_conf(): conference lookup in global confs linked list. * SLOW: AST_LIST_TRAVERSE — O(n_conferences) per lookup * FAST: ao2_container hash lookup — O(1) * * asterisk-0002 (ASTERISK_CONFBRIDGE_USER_FIND): * Models user-by-channel-name lookup in active_list linked list. * SLOW: AST_LIST_TRAVERSE + strcasecmp — O(n_participants) per operation * FAST: HashMap keyed by channel name — O(1) */ public class AsteriskTest { // ------------------------------------------------------------------------- // Data model // ------------------------------------------------------------------------- static class Conference { final String confno; Conference(String confno) { this.confno = confno; } } static class ConfUser { final String channelName; ConfUser(String channelName) { this.channelName = channelName; } } // ------------------------------------------------------------------------- // asterisk-0001: find_conf — linked list O(n) vs HashMap O(1) // ------------------------------------------------------------------------- /** SLOW: linear scan of all conferences. Returns ops = comparisons made. */ static long findConf_slow(List confs, String confno) { long ops = 0; for (Conference cnf : confs) { ops++; if (cnf.confno.equals(confno)) return ops; } return ops; // not found } /** FAST: HashMap lookup. Returns ops = 1 (hash probe). */ static long findConf_fast(Map confsMap, String confno) { confsMap.get(confno); return 1; } // ------------------------------------------------------------------------- // asterisk-0002: confbridge user find — linked list O(n) vs HashMap O(1) // ------------------------------------------------------------------------- /** SLOW: linear traverse of active_list + waiting_list by channel name. */ static long findUserByChannel_slow(List activeList, List waitingList, String channelName) { long ops = 0; for (ConfUser u : activeList) { ops++; if (u.channelName.equalsIgnoreCase(channelName)) return ops; } for (ConfUser u : waitingList) { ops++; if (u.channelName.equalsIgnoreCase(channelName)) return ops; } return ops; // not found } /** FAST: HashMap keyed by channel name — O(1). */ static long findUserByChannel_fast(Map usersByName, String channelName) { usersByName.get(channelName.toLowerCase()); return 1; } // ------------------------------------------------------------------------- // Benchmark harness // ------------------------------------------------------------------------- static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) { slow.run(); fast.run(); long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000; long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000; double r = fOps > 0 ? (double) sOps / fOps : 0; System.out.printf(" %-52s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n", label, sMs, sOps, fMs, fOps, r); } // ------------------------------------------------------------------------- // Setup helpers // ------------------------------------------------------------------------- static List makeConfs(int n) { List list = new ArrayList<>(n); for (int i = 0; i < n; i++) list.add(new Conference("conf" + i)); return list; } static Map makeConfsMap(List confs) { Map map = new HashMap<>(confs.size() * 2); for (Conference c : confs) map.put(c.confno, c); return map; } static List makeUsers(String prefix, int n) { List list = new ArrayList<>(n); for (int i = 0; i < n; i++) list.add(new ConfUser("SIP/" + prefix + i + "-0000" + i)); return list; } static Map makeUserMap(List active, List waiting) { Map map = new HashMap<>((active.size() + waiting.size()) * 2); for (ConfUser u : active) map.put(u.channelName.toLowerCase(), u); for (ConfUser u : waiting) map.put(u.channelName.toLowerCase(), u); return map; } // ------------------------------------------------------------------------- // Main // ------------------------------------------------------------------------- public static void main(String[] args) { System.out.println("AsteriskTest — CWE-407 benchmarks: asterisk-0001 + asterisk-0002"); System.out.println("================================================================="); int passed = 0, total = 0; // ----- asterisk-0001: find_conf ----- System.out.println("\n[asterisk-0001] app_meetme: find_conf linked-list traversal"); { // C=500 concurrent conferences; target is last in list (worst case) int C = 500; List confs = makeConfs(C); Map confsMap = makeConfsMap(confs); String target = confs.get(C - 1).confno; // last element = worst case int LOOKUPS = 100_000; long[] sOps = {0}, fOps = {0}; Runnable slow = () -> { long ops = 0; for (int i = 0; i < LOOKUPS; i++) ops += findConf_slow(confs, target); sOps[0] = ops; }; Runnable fast = () -> { long ops = 0; for (int i = 0; i < LOOKUPS; i++) ops += findConf_fast(confsMap, target); fOps[0] = ops; }; slow.run(); fast.run(); bench("find_conf C=500 last-match (100k lookups)", slow, fast, sOps[0], fOps[0]); total++; assert sOps[0] > fOps[0] * 100 : "FAIL: slow=" + sOps[0] + " fast=" + fOps[0]; passed++; } { // C=1000, target not found (full scan every time) int C = 1000; List confs = makeConfs(C); Map confsMap = makeConfsMap(confs); String target = "confNOTEXIST"; int LOOKUPS = 50_000; long[] sOps = {0}, fOps = {0}; Runnable slow = () -> { long ops = 0; for (int i = 0; i < LOOKUPS; i++) ops += findConf_slow(confs, target); sOps[0] = ops; }; Runnable fast = () -> { long ops = 0; for (int i = 0; i < LOOKUPS; i++) ops += findConf_fast(confsMap, target); fOps[0] = ops; }; slow.run(); fast.run(); bench("find_conf C=1000 not-found (50k lookups)", slow, fast, sOps[0], fOps[0]); total++; assert sOps[0] > fOps[0] * 500 : "FAIL: slow=" + sOps[0] + " fast=" + fOps[0]; passed++; } // ----- asterisk-0002: confbridge user find ----- System.out.println("\n[asterisk-0002] app_confbridge: user find in active_list"); { // P=500 active participants; target is last (worst case for kick/mute) int P = 500; List active = makeUsers("active", P); List waiting = makeUsers("waiting", 50); Map userMap = makeUserMap(active, waiting); String target = active.get(P - 1).channelName; // last = worst case int OPS = 50_000; long[] sOps = {0}, fOps = {0}; Runnable slow = () -> { long ops = 0; for (int i = 0; i < OPS; i++) ops += findUserByChannel_slow(active, waiting, target); sOps[0] = ops; }; Runnable fast = () -> { long ops = 0; for (int i = 0; i < OPS; i++) ops += findUserByChannel_fast(userMap, target); fOps[0] = ops; }; slow.run(); fast.run(); bench("confbridge user-find P=500 last-match (50k)", slow, fast, sOps[0], fOps[0]); total++; assert sOps[0] > fOps[0] * 100 : "FAIL: slow=" + sOps[0] + " fast=" + fOps[0]; passed++; } { // P=2000 active, target not found at all (e.g. stale AMI kick) int P = 2000; List active = makeUsers("big", P); List waiting = Collections.emptyList(); Map userMap = makeUserMap(active, waiting); String target = "SIP/ghost-00000000"; // not present int OPS = 20_000; long[] sOps = {0}, fOps = {0}; Runnable slow = () -> { long ops = 0; for (int i = 0; i < OPS; i++) ops += findUserByChannel_slow(active, waiting, target); sOps[0] = ops; }; Runnable fast = () -> { long ops = 0; for (int i = 0; i < OPS; i++) ops += findUserByChannel_fast(userMap, target); fOps[0] = ops; }; slow.run(); fast.run(); bench("confbridge user-find P=2000 not-found (20k)", slow, fast, sOps[0], fOps[0]); total++; assert sOps[0] > fOps[0] * 1000 : "FAIL: slow=" + sOps[0] + " fast=" + fOps[0]; passed++; } System.out.println("\n" + passed + "/" + total + " PASS"); if (passed < total) System.exit(1); } }