From b8534b99190b4f953ee86e7457181ba2595c1de7 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Tue, 21 Apr 2026 16:42:59 -0700 Subject: [PATCH 01/19] Add APX code_hotspots integration test for CpuBurner Java workload --- mcp-local/tests/CpuBurnerOriginal.java | 199 +++++++++++++++++++++++++ mcp-local/tests/constants.py | 22 +++ mcp-local/tests/test_mcp.py | 22 +++ 3 files changed, 243 insertions(+) create mode 100644 mcp-local/tests/CpuBurnerOriginal.java diff --git a/mcp-local/tests/CpuBurnerOriginal.java b/mcp-local/tests/CpuBurnerOriginal.java new file mode 100644 index 0000000..505086e --- /dev/null +++ b/mcp-local/tests/CpuBurnerOriginal.java @@ -0,0 +1,199 @@ +// CpuBurner.java +// Orignal un-optimised version workC function is deliberately slow +// Usage: java CpuBurner +// Example: java CpuBurner 10 +// +// Runs for (approximately) the given duration, calling A/B/C the same number of times. +// A: expensive string manipulation +// B: computationally expensive memcpy (System.arraycopy over large buffers) +// C: floating point / matrix multiplications (INTENTIONALLY SLOWED DOWN) + +import java.nio.charset.StandardCharsets; +import java.util.Locale; + +public final class CpuBurner { + + // Prevent dead-code elimination + private static volatile long SINK_LONG = 0; + private static volatile double SINK_DBL = 0.0; + + // Extra sink used only to keep "wasted" math from being optimized away + private static volatile double WASTE_DBL = 0.0; + + // ---- Workload B buffers ---- + private static final int MEM_SIZE = 8 * 1024 * 1024; // 8 MiB + private static final byte[] SRC = new byte[MEM_SIZE]; + private static final byte[] DST = new byte[MEM_SIZE]; + + // ---- Workload C matrices ---- + private static final int N = 64; + private static final double[][] M1 = new double[N][N]; + private static final double[][] M2 = new double[N][N]; + private static final double[][] OUT = new double[N][N]; + + static { + // Deterministic init for repeatable profiling + long x = 0x9E3779B97F4A7C15L; + for (int i = 0; i < MEM_SIZE; i++) { + x ^= (x << 13); x ^= (x >>> 7); x ^= (x << 17); + SRC[i] = (byte) x; + } + + long y = 0xD1B54A32D192ED03L; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + y ^= (y << 13); y ^= (y >>> 7); y ^= (y << 17); + M1[i][j] = ((y & 0xFFFF) - 32768) / 1024.0; + y ^= (y << 13); y ^= (y >>> 7); y ^= (y << 17); + M2[i][j] = ((y & 0xFFFF) - 32768) / 1024.0; + } + } + } + + public static void main(String[] args) { + if (args.length != 1) { + System.err.println("Usage: java CpuBurner "); + System.err.println("Example: java CpuBurner 10"); + System.exit(2); + } + + final double seconds; + try { + seconds = Double.parseDouble(args[0]); + } catch (NumberFormatException e) { + System.err.println("Invalid number: " + args[0]); + System.exit(2); + return; + } + + if (seconds <= 0.0) { + System.err.println("Duration must be > 0"); + System.exit(2); + } + + final long durationNanos = (long) (seconds * 1_000_000_000L); + final long start = System.nanoTime(); + final long deadline = start + durationNanos; + + long callsA = 0, callsB = 0, callsC = 0; + + // Round-robin A->B->C; only complete full cycles so counts remain equal + while (true) { + if (System.nanoTime() >= deadline) break; + + // A + SINK_LONG ^= workA(callsA); + callsA++; + + // Check between functions to avoid overshooting too much + if (System.nanoTime() >= deadline) { callsA--; break; } + + // B + SINK_LONG ^= workB(callsB); + callsB++; + + if (System.nanoTime() >= deadline) { callsA--; callsB--; break; } + + // C + SINK_DBL += workC(callsC); + callsC++; + + if (System.nanoTime() >= deadline) { callsA--; callsB--; callsC--; break; } + } + + // Ensure exactly equal counts (drop any partial cycle if timing cut it short) + long min = Math.min(callsA, Math.min(callsB, callsC)); + callsA = callsB = callsC = min; + + long elapsedNanos = System.nanoTime() - start; + System.out.println("Elapsed: " + (elapsedNanos / 1_000_000) + " ms"); + System.out.println("Calls: A=" + callsA + " B=" + callsB + " C=" + callsC + " (equal)"); + System.out.println("Sinks: long=" + SINK_LONG + " double=" + String.format(Locale.ROOT, "%.6f", SINK_DBL)); + // WASTE_DBL intentionally not printed; it's only to prevent optimization. + } + + // A: expensive string manipulation + private static long workA(long iter) { + // Mix iteration to avoid identical strings each time + String base = "The_quick_brown_fox_jumps_over_the_lazy_dog_" + iter; + + long h = 1469598103934665603L; // FNV-1a-ish mix + for (int round = 0; round < 500; round++) { + String s1 = new StringBuilder(base).reverse().append('_').append(round).toString(); + String s2 = s1.toUpperCase(Locale.ROOT).replace('_', '-'); + String s3 = s2 + "::" + Integer.toHexString(s2.hashCode()); + + // Byte-level churn + byte[] bytes = s3.getBytes(StandardCharsets.UTF_8); + for (byte b : bytes) { + h ^= (b & 0xFF); + h *= 1099511628211L; + } + + // More string churn + String[] parts = s3.split("::"); + base = parts[0] + "_" + parts[1] + "_" + (h & 0xFFFF); + } + return h; + } + + // B: computationally expensive memcpy (large arraycopy + light checksum) + private static long workB(long iter) { + int chunk = 256 * 1024; // 256 KiB + int copies = 32; // 32 * 256KiB ~= 8MiB copied per call + + // Offset shifts to avoid copying identical regions each call + int off = (int) ((iter * 1315423911L) & (MEM_SIZE - 1)); + off = off & ~(chunk - 1); // align to chunk + + for (int i = 0; i < copies; i++) { + int srcOff = (off + i * chunk) % (MEM_SIZE - chunk); + int dstOff = (srcOff ^ 0x5A5A5A) % (MEM_SIZE - chunk); + System.arraycopy(SRC, srcOff, DST, dstOff, chunk); + } + + // Touch a few bytes so it isn't optimized away + long sum = 0; + for (int i = 0; i < 4096; i += 64) { + sum = (sum * 1315423911L) + (DST[(off + i) % MEM_SIZE] & 0xFF); + } + return sum; + } + + // C: floating point / matrix multiplications (INTENTIONALLY SLOW) + private static double workC(long iter) { + // Naive NxN multiply repeated a few times + double total = 0.0; + int reps = 6; + + for (int r = 0; r < reps; r++) { + // OUT = M1 * M2 + for (int i = 0; i < N; i++) { + // (deliberately avoid caching row references to make access a bit worse) + for (int j = 0; j < N; j++) { + double acc = 0.0; + for (int k = 0; k < N; k++) { + double prod = M1[i][k] * M2[k][j]; + acc += prod; + + // Intentional inefficiency: extra transcendentals in the inner loop. + // WASTE_DBL is volatile so the JIT can't discard this work. + WASTE_DBL += (Math.sin(prod) + Math.cos(prod)) * 1e-12; + } + OUT[i][j] = acc; + } + } + + // Fold some results into total, and slightly perturb inputs so repeats differ + int t = (int) (iter + r); + int ii = (t * 17) & (N - 1); + int jj = (t * 31) & (N - 1); + total += OUT[ii][jj]; + + double tweak = (total * 1e-9) + 1e-12; + M1[ii][jj] += tweak; + M2[jj][ii] -= tweak; + } + return total; + } +} diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index b2682c6..443b009 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -244,6 +244,28 @@ EXPECTED_CHECK_MCA_TOOL_RESPONSE_STATUS = "ok" +CHECK_APX_CPU_HOTSPOTS_JAVA_REQUEST = { + "jsonrpc": "2.0", + "id": 9, + "method": "tools/call", + "params": { + "name": "apx_recipe_run", + "arguments": { + "cmd": ( + "which java || (sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends default-jdk-headless); " + "D=$(mktemp -d); " + "echo cHVibGljIGNsYXNzIENwdUJ1cm5lciB7CiAgcHVibGljIHN0YXRpYyB2b2lkIG1haW4oU3RyaW5nW10gYSkgdGhyb3dzIEV4Y2VwdGlvbiB7CiAgICBsb25nIGU9U3lzdGVtLmN1cnJlbnRUaW1lTWlsbGlzKCkrTG9uZy5wYXJzZUxvbmcoYVswXSkqMTAwMEw7CiAgICBkb3VibGUgeD0wLjUscz0wOwogICAgd2hpbGUoU3lzdGVtLmN1cnJlbnRUaW1lTWlsbGlzKCk8ZSl7Zm9yKGludCBpPTA7aTwxMDAwMDtpKyspe3g9TWF0aC5zaW4oeCtpKSpNYXRoLmNvcyh4LWkpO3MrPXg7fX0KICAgIFN5c3RlbS5vdXQucHJpbnRsbigiZG9uZSBzPSIrcyk7CiAgfQp9Cg== | base64 -d > $D/CpuBurner.java; " + "cd $D && javac CpuBurner.java && java -XX:+PreserveFramePointer -cp . CpuBurner 30; " + "rm -rf $D" + ), + "remote_ip_addr": "localhost", + "remote_usr": "base", + "recipe": "code_hotspots", + "invocation_reason": "Run APX code hotspots recipe against the CpuBurner Java workload to identify CPU hotspots.", + }, + }, + } + CHECK_APX_RECIPE_RUN_REQUEST = { "jsonrpc": "2.0", "id": 8, diff --git a/mcp-local/tests/test_mcp.py b/mcp-local/tests/test_mcp.py index 6532eb1..a74fdfa 100644 --- a/mcp-local/tests/test_mcp.py +++ b/mcp-local/tests/test_mcp.py @@ -228,6 +228,28 @@ def _read_response(expected_id: int, timeout: float = 10.0) -> dict: assert apx_structured.get("recipe") == "code_hotspots", "Test Failed: MCP apx_recipe_run tool failed: recipe mismatch. Expected: code_hotspots, Received: {}".format(apx_structured.get("recipe")) assert apx_structured.get("status") in {"success"}, "Test Failed: MCP apx_recipe_run tool failed: unexpected status. Received: {}".format(apx_structured.get("status")) print("\n***Test Passed: MCP apx_recipe_run tool call completed") + + #Check APX Code Hotspots Tool Test - CpuBurner Java Workload + apx_java_request = json.loads(json.dumps(constants.CHECK_APX_CPU_HOTSPOTS_JAVA_REQUEST)) + apx_java_args = apx_java_request["params"]["arguments"] + apx_java_args["remote_ip_addr"] = os.getenv("APX_TEST_REMOTE_IP", apx_java_args["remote_ip_addr"]) + apx_java_args["remote_usr"] = os.getenv("APX_TEST_REMOTE_USER", apx_java_args["remote_usr"]) + apx_java_args["cmd"] = os.getenv("APX_TEST_JAVA_CMD", apx_java_args["cmd"]) + + raw_socket.sendall(_encode_mcp_message(apx_java_request)) + check_apx_java_response = _read_response(9, timeout=120) + print( + "\n***APX CPU Hotspots (Java) Raw Response: ", + json.dumps(check_apx_java_response, indent=2), + ) + apx_java_structured = check_apx_java_response.get("result", {}).get("structuredContent", {}) + print( + "\n***APX CPU Hotspots (Java) Structured Content: ", + json.dumps(apx_java_structured, indent=2), + ) + assert apx_java_structured.get("recipe") == "code_hotspots", "Test Failed: MCP apx_recipe_run (Java) tool failed: recipe mismatch. Expected: code_hotspots, Received: {}".format(apx_java_structured.get("recipe")) + assert apx_java_structured.get("status") in {"success"}, "Test Failed: MCP apx_recipe_run (Java) tool failed: unexpected status. Received: {}".format(apx_java_structured.get("status")) + print("\n***Test Passed: MCP apx_recipe_run (Java CpuBurner) tool call completed") if __name__ == "__main__": pytest.main([__file__]) From 375c6ec55f91d10afa4cc784a6fc5087af0340a3 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Tue, 21 Apr 2026 17:01:42 -0700 Subject: [PATCH 02/19] Install Java on runner and use CpuBurnerOriginal.java from repo for APX Java hotspots test --- .github/workflows/integration-tests.yml | 5 ++++- mcp-local/tests/constants.py | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 3308c53..6808c83 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -69,7 +69,7 @@ jobs: sudo visudo -cf "/etc/sudoers.d/90-${APX_SSH_USER}-nopasswd" sudo apt-get update - sudo apt-get install -y openssh-server + sudo apt-get install -y openssh-server default-jdk-headless sudo mkdir -p /run/sshd sudo sed -i 's/^#\?PubkeyAuthentication .*/PubkeyAuthentication yes/' /etc/ssh/sshd_config sudo sed -i 's/^#\?PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config @@ -90,6 +90,9 @@ jobs: ssh -o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile="${KNOWN_HOSTS_PATH}" -i "${KEY_PATH}" "${APX_SSH_USER}@127.0.0.1" "sudo -n true && echo ssh_ok" + sudo cp mcp-local/tests/CpuBurnerOriginal.java "/home/${APX_SSH_USER}/CpuBurnerOriginal.java" + sudo chown "${APX_SSH_USER}:${APX_SSH_USER}" "/home/${APX_SSH_USER}/CpuBurnerOriginal.java" + { echo "APX_TEST_SSH_KEY_PATH=${KEY_PATH}" echo "APX_TEST_KNOWN_HOSTS_PATH=${KNOWN_HOSTS_PATH}" diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index 443b009..e24977e 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -252,9 +252,8 @@ "name": "apx_recipe_run", "arguments": { "cmd": ( - "which java || (sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends default-jdk-headless); " "D=$(mktemp -d); " - "echo cHVibGljIGNsYXNzIENwdUJ1cm5lciB7CiAgcHVibGljIHN0YXRpYyB2b2lkIG1haW4oU3RyaW5nW10gYSkgdGhyb3dzIEV4Y2VwdGlvbiB7CiAgICBsb25nIGU9U3lzdGVtLmN1cnJlbnRUaW1lTWlsbGlzKCkrTG9uZy5wYXJzZUxvbmcoYVswXSkqMTAwMEw7CiAgICBkb3VibGUgeD0wLjUscz0wOwogICAgd2hpbGUoU3lzdGVtLmN1cnJlbnRUaW1lTWlsbGlzKCk8ZSl7Zm9yKGludCBpPTA7aTwxMDAwMDtpKyspe3g9TWF0aC5zaW4oeCtpKSpNYXRoLmNvcyh4LWkpO3MrPXg7fX0KICAgIFN5c3RlbS5vdXQucHJpbnRsbigiZG9uZSBzPSIrcyk7CiAgfQp9Cg== | base64 -d > $D/CpuBurner.java; " + "cp ~/CpuBurnerOriginal.java $D/CpuBurner.java; " "cd $D && javac CpuBurner.java && java -XX:+PreserveFramePointer -cp . CpuBurner 30; " "rm -rf $D" ), From 7a89422072bc6e7883b2d73c041b5006bc053b3e Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Tue, 21 Apr 2026 17:09:12 -0700 Subject: [PATCH 03/19] Fix tilde expansion in APX Java cmd - use $HOME instead of ~ --- mcp-local/tests/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index e24977e..78acd17 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -253,7 +253,7 @@ "arguments": { "cmd": ( "D=$(mktemp -d); " - "cp ~/CpuBurnerOriginal.java $D/CpuBurner.java; " + "cp $HOME/CpuBurnerOriginal.java $D/CpuBurner.java; " "cd $D && javac CpuBurner.java && java -XX:+PreserveFramePointer -cp . CpuBurner 30; " "rm -rf $D" ), From 5a4c32f95936499e20919ccacadfb39acb5102d5 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Tue, 21 Apr 2026 17:19:17 -0700 Subject: [PATCH 04/19] Use /tmp for CpuBurnerOriginal.java to avoid env var dependency in APX cmd --- .github/workflows/integration-tests.yml | 2 ++ mcp-local/tests/constants.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 6808c83..bcba24c 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -92,6 +92,8 @@ jobs: sudo cp mcp-local/tests/CpuBurnerOriginal.java "/home/${APX_SSH_USER}/CpuBurnerOriginal.java" sudo chown "${APX_SSH_USER}:${APX_SSH_USER}" "/home/${APX_SSH_USER}/CpuBurnerOriginal.java" + sudo cp mcp-local/tests/CpuBurnerOriginal.java /tmp/CpuBurnerOriginal.java + sudo chmod 644 /tmp/CpuBurnerOriginal.java { echo "APX_TEST_SSH_KEY_PATH=${KEY_PATH}" diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index 78acd17..f4093aa 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -253,7 +253,7 @@ "arguments": { "cmd": ( "D=$(mktemp -d); " - "cp $HOME/CpuBurnerOriginal.java $D/CpuBurner.java; " + "cp /tmp/CpuBurnerOriginal.java $D/CpuBurner.java; " "cd $D && javac CpuBurner.java && java -XX:+PreserveFramePointer -cp . CpuBurner 30; " "rm -rf $D" ), From 24c5a1590407f1b1d307e0c18ee01b3341f75683 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Wed, 22 Apr 2026 16:43:46 -0700 Subject: [PATCH 05/19] fix: scan all JSON lines in extract_run_id to handle Java workload output The previous implementation hardcoded line index 1, which failed for the Java workload where APX may emit the run_id JSON on a different line or where _combine_command_output inserts [stderr] content between lines. Now scans all JSON-looking lines (same pattern as _extract_session_id), returning the first one that contains a valid run_id. --- mcp-local/utils/apx.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/mcp-local/utils/apx.py b/mcp-local/utils/apx.py index 0655a57..caa01ed 100644 --- a/mcp-local/utils/apx.py +++ b/mcp-local/utils/apx.py @@ -533,11 +533,16 @@ def resolve_apx_ssh_mount_env() -> Dict[str, Any]: def extract_run_id(output: str) -> str: if not output: return "" - try: - data = json.loads(output.split("\n")[1]) - return data.get("data", {}).get("run_id", {}) - except Exception: - return "" + candidates = [line.strip() for line in output.splitlines() if line.strip().startswith("{")] + for candidate in candidates: + try: + data = json.loads(candidate) + run_id = data.get("data", {}).get("run_id") + if run_id: + return run_id + except Exception: + continue + return "" def run_command(command: list, cwd: str, parse_output=None) -> tuple: """ From d4a2b6aba073e0ce39b3516467a702ff93700270 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Wed, 22 Apr 2026 16:59:18 -0700 Subject: [PATCH 06/19] fix: revert APX Java cmd to base64 approach, remove file copy steps The cp /tmp/CpuBurnerOriginal.java approach failed because the file is not present on the remote target at workload execution time - it was only copied to the runner host, not the SSH target. The original base64 inline approach is self-contained and does not depend on any pre-staged files. Also removes the now-unnecessary workflow steps that copied the Java file to the runner and /tmp. --- .github/workflows/integration-tests.yml | 5 ----- mcp-local/tests/constants.py | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index bcba24c..bf129cd 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -90,11 +90,6 @@ jobs: ssh -o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile="${KNOWN_HOSTS_PATH}" -i "${KEY_PATH}" "${APX_SSH_USER}@127.0.0.1" "sudo -n true && echo ssh_ok" - sudo cp mcp-local/tests/CpuBurnerOriginal.java "/home/${APX_SSH_USER}/CpuBurnerOriginal.java" - sudo chown "${APX_SSH_USER}:${APX_SSH_USER}" "/home/${APX_SSH_USER}/CpuBurnerOriginal.java" - sudo cp mcp-local/tests/CpuBurnerOriginal.java /tmp/CpuBurnerOriginal.java - sudo chmod 644 /tmp/CpuBurnerOriginal.java - { echo "APX_TEST_SSH_KEY_PATH=${KEY_PATH}" echo "APX_TEST_KNOWN_HOSTS_PATH=${KNOWN_HOSTS_PATH}" diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index f4093aa..443b009 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -252,8 +252,9 @@ "name": "apx_recipe_run", "arguments": { "cmd": ( + "which java || (sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends default-jdk-headless); " "D=$(mktemp -d); " - "cp /tmp/CpuBurnerOriginal.java $D/CpuBurner.java; " + "echo cHVibGljIGNsYXNzIENwdUJ1cm5lciB7CiAgcHVibGljIHN0YXRpYyB2b2lkIG1haW4oU3RyaW5nW10gYSkgdGhyb3dzIEV4Y2VwdGlvbiB7CiAgICBsb25nIGU9U3lzdGVtLmN1cnJlbnRUaW1lTWlsbGlzKCkrTG9uZy5wYXJzZUxvbmcoYVswXSkqMTAwMEw7CiAgICBkb3VibGUgeD0wLjUscz0wOwogICAgd2hpbGUoU3lzdGVtLmN1cnJlbnRUaW1lTWlsbGlzKCk8ZSl7Zm9yKGludCBpPTA7aTwxMDAwMDtpKyspe3g9TWF0aC5zaW4oeCtpKSpNYXRoLmNvcyh4LWkpO3MrPXg7fX0KICAgIFN5c3RlbS5vdXQucHJpbnRsbigiZG9uZSBzPSIrcyk7CiAgfQp9Cg== | base64 -d > $D/CpuBurner.java; " "cd $D && javac CpuBurner.java && java -XX:+PreserveFramePointer -cp . CpuBurner 30; " "rm -rf $D" ), From 1e7973d6a2f18168bb469ca176117ff61022e541 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Mon, 27 Apr 2026 17:04:23 -0700 Subject: [PATCH 07/19] Use CpuBurnerOriginal source for APX Java test --- mcp-local/tests/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index 443b009..aaba0bd 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -254,7 +254,7 @@ "cmd": ( "which java || (sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends default-jdk-headless); " "D=$(mktemp -d); " - "echo cHVibGljIGNsYXNzIENwdUJ1cm5lciB7CiAgcHVibGljIHN0YXRpYyB2b2lkIG1haW4oU3RyaW5nW10gYSkgdGhyb3dzIEV4Y2VwdGlvbiB7CiAgICBsb25nIGU9U3lzdGVtLmN1cnJlbnRUaW1lTWlsbGlzKCkrTG9uZy5wYXJzZUxvbmcoYVswXSkqMTAwMEw7CiAgICBkb3VibGUgeD0wLjUscz0wOwogICAgd2hpbGUoU3lzdGVtLmN1cnJlbnRUaW1lTWlsbGlzKCk8ZSl7Zm9yKGludCBpPTA7aTwxMDAwMDtpKyspe3g9TWF0aC5zaW4oeCtpKSpNYXRoLmNvcyh4LWkpO3MrPXg7fX0KICAgIFN5c3RlbS5vdXQucHJpbnRsbigiZG9uZSBzPSIrcyk7CiAgfQp9Cg== | base64 -d > $D/CpuBurner.java; " + "cp /workspace/mcp-local/tests/CpuBurnerOriginal.java $D/CpuBurner.java; " "cd $D && javac CpuBurner.java && java -XX:+PreserveFramePointer -cp . CpuBurner 30; " "rm -rf $D" ), From 5f523ad671e5bc40bc3bcf0b2d936d8ed0a8f6b2 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Mon, 27 Apr 2026 17:20:35 -0700 Subject: [PATCH 08/19] Improve APX Java hotspot visibility --- mcp-local/tests/constants.py | 2 +- mcp-local/tests/test_mcp.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index aaba0bd..209926e 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -255,7 +255,7 @@ "which java || (sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends default-jdk-headless); " "D=$(mktemp -d); " "cp /workspace/mcp-local/tests/CpuBurnerOriginal.java $D/CpuBurner.java; " - "cd $D && javac CpuBurner.java && java -XX:+PreserveFramePointer -cp . CpuBurner 30; " + "cd $D && javac CpuBurner.java && java -XX:+PreserveFramePointer -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints -cp . CpuBurner 30; " "rm -rf $D" ), "remote_ip_addr": "localhost", diff --git a/mcp-local/tests/test_mcp.py b/mcp-local/tests/test_mcp.py index a74fdfa..a20fda2 100644 --- a/mcp-local/tests/test_mcp.py +++ b/mcp-local/tests/test_mcp.py @@ -249,6 +249,14 @@ def _read_response(expected_id: int, timeout: float = 10.0) -> dict: ) assert apx_java_structured.get("recipe") == "code_hotspots", "Test Failed: MCP apx_recipe_run (Java) tool failed: recipe mismatch. Expected: code_hotspots, Received: {}".format(apx_java_structured.get("recipe")) assert apx_java_structured.get("status") in {"success"}, "Test Failed: MCP apx_recipe_run (Java) tool failed: unexpected status. Received: {}".format(apx_java_structured.get("status")) + apx_java_rows = apx_java_structured.get("rows", []) + if apx_java_rows: + print("\n***APX CPU Hotspots (Java) Top Functions:") + for idx, row in enumerate(apx_java_rows, start=1): + name = row.get("FUNCTION_NAME", "") + samples = row.get("PERIODIC_SAMPLES_SELF", 0) + percent = row.get("PERIODIC_SAMPLES_SELF_PERCENT", 0) + print(f" {idx:>2}. {name} | samples={samples} | percent={percent}") print("\n***Test Passed: MCP apx_recipe_run (Java CpuBurner) tool call completed") if __name__ == "__main__": From 2626fcaf6e59bb54770e9a00ff09a90b23da6d77 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Mon, 4 May 2026 13:51:19 -0700 Subject: [PATCH 09/19] clean up debug messages --- mcp-local/tests/constants.py | 2 +- mcp-local/tests/test_mcp.py | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index 209926e..aaba0bd 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -255,7 +255,7 @@ "which java || (sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends default-jdk-headless); " "D=$(mktemp -d); " "cp /workspace/mcp-local/tests/CpuBurnerOriginal.java $D/CpuBurner.java; " - "cd $D && javac CpuBurner.java && java -XX:+PreserveFramePointer -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints -cp . CpuBurner 30; " + "cd $D && javac CpuBurner.java && java -XX:+PreserveFramePointer -cp . CpuBurner 30; " "rm -rf $D" ), "remote_ip_addr": "localhost", diff --git a/mcp-local/tests/test_mcp.py b/mcp-local/tests/test_mcp.py index a20fda2..a74fdfa 100644 --- a/mcp-local/tests/test_mcp.py +++ b/mcp-local/tests/test_mcp.py @@ -249,14 +249,6 @@ def _read_response(expected_id: int, timeout: float = 10.0) -> dict: ) assert apx_java_structured.get("recipe") == "code_hotspots", "Test Failed: MCP apx_recipe_run (Java) tool failed: recipe mismatch. Expected: code_hotspots, Received: {}".format(apx_java_structured.get("recipe")) assert apx_java_structured.get("status") in {"success"}, "Test Failed: MCP apx_recipe_run (Java) tool failed: unexpected status. Received: {}".format(apx_java_structured.get("status")) - apx_java_rows = apx_java_structured.get("rows", []) - if apx_java_rows: - print("\n***APX CPU Hotspots (Java) Top Functions:") - for idx, row in enumerate(apx_java_rows, start=1): - name = row.get("FUNCTION_NAME", "") - samples = row.get("PERIODIC_SAMPLES_SELF", 0) - percent = row.get("PERIODIC_SAMPLES_SELF_PERCENT", 0) - print(f" {idx:>2}. {name} | samples={samples} | percent={percent}") print("\n***Test Passed: MCP apx_recipe_run (Java CpuBurner) tool call completed") if __name__ == "__main__": From 6f6ac5dfb93b676e4c70882c779779643c171fd1 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Tue, 5 May 2026 16:44:45 -0700 Subject: [PATCH 10/19] Prepare Java CpuBurner on runner and simplify workload cmd --- .github/workflows/integration-tests.yml | 15 +++++++++++++++ mcp-local/tests/constants.py | 8 +------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index bf129cd..1348050 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -97,6 +97,21 @@ jobs: echo "APX_TEST_REMOTE_IP=172.17.0.1" } >> "${GITHUB_ENV}" + - name: Prepare Java CpuBurner workload + run: | + set -euxo pipefail + APX_SSH_USER="apxci" + CPUBURNER_DIR="/home/${APX_SSH_USER}/cpuburner" + + sudo -u "${APX_SSH_USER}" mkdir -p "${CPUBURNER_DIR}" + sudo cp mcp-local/tests/CpuBurnerOriginal.java "${CPUBURNER_DIR}/CpuBurner.java" + sudo chown -R "${APX_SSH_USER}:${APX_SSH_USER}" "${CPUBURNER_DIR}" + sudo -u "${APX_SSH_USER}" javac "${CPUBURNER_DIR}/CpuBurner.java" + + { + echo "APX_TEST_JAVA_CMD=java -XX:+PreserveFramePointer -cp ${CPUBURNER_DIR} CpuBurner 30" + } >> "${GITHUB_ENV}" + - name: Run integration tests env: APX_DEBUG_TRACE: "1" diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index aaba0bd..0ac3469 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -251,13 +251,7 @@ "params": { "name": "apx_recipe_run", "arguments": { - "cmd": ( - "which java || (sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends default-jdk-headless); " - "D=$(mktemp -d); " - "cp /workspace/mcp-local/tests/CpuBurnerOriginal.java $D/CpuBurner.java; " - "cd $D && javac CpuBurner.java && java -XX:+PreserveFramePointer -cp . CpuBurner 30; " - "rm -rf $D" - ), + "cmd": "java -XX:+PreserveFramePointer -cp /home/apxci/cpuburner CpuBurner 30", "remote_ip_addr": "localhost", "remote_usr": "base", "recipe": "code_hotspots", From df2dc4209b89b9bb87629496b10866cbe001b4a3 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Tue, 5 May 2026 16:55:09 -0700 Subject: [PATCH 11/19] Increase Java APX response timeout --- mcp-local/tests/test_mcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp-local/tests/test_mcp.py b/mcp-local/tests/test_mcp.py index a74fdfa..caee4ea 100644 --- a/mcp-local/tests/test_mcp.py +++ b/mcp-local/tests/test_mcp.py @@ -237,7 +237,7 @@ def _read_response(expected_id: int, timeout: float = 10.0) -> dict: apx_java_args["cmd"] = os.getenv("APX_TEST_JAVA_CMD", apx_java_args["cmd"]) raw_socket.sendall(_encode_mcp_message(apx_java_request)) - check_apx_java_response = _read_response(9, timeout=120) + check_apx_java_response = _read_response(9, timeout=240) print( "\n***APX CPU Hotspots (Java) Raw Response: ", json.dumps(check_apx_java_response, indent=2), From 4b859abdcd5e24728a0268c064be0ed0cea1ab7e Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Wed, 6 May 2026 11:39:12 -0700 Subject: [PATCH 12/19] Timeout Java workload and extend APX response wait --- .github/workflows/integration-tests.yml | 2 +- mcp-local/tests/test_mcp.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 1348050..ef4c2d4 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -109,7 +109,7 @@ jobs: sudo -u "${APX_SSH_USER}" javac "${CPUBURNER_DIR}/CpuBurner.java" { - echo "APX_TEST_JAVA_CMD=java -XX:+PreserveFramePointer -cp ${CPUBURNER_DIR} CpuBurner 30" + echo "APX_TEST_JAVA_CMD=timeout 60s java -XX:+PreserveFramePointer -cp ${CPUBURNER_DIR} CpuBurner 30" } >> "${GITHUB_ENV}" - name: Run integration tests diff --git a/mcp-local/tests/test_mcp.py b/mcp-local/tests/test_mcp.py index caee4ea..b4bd9d8 100644 --- a/mcp-local/tests/test_mcp.py +++ b/mcp-local/tests/test_mcp.py @@ -236,8 +236,10 @@ def _read_response(expected_id: int, timeout: float = 10.0) -> dict: apx_java_args["remote_usr"] = os.getenv("APX_TEST_REMOTE_USER", apx_java_args["remote_usr"]) apx_java_args["cmd"] = os.getenv("APX_TEST_JAVA_CMD", apx_java_args["cmd"]) + raw_socket.settimeout(600) raw_socket.sendall(_encode_mcp_message(apx_java_request)) - check_apx_java_response = _read_response(9, timeout=240) + check_apx_java_response = _read_response(9, timeout=600) + raw_socket.settimeout(10) print( "\n***APX CPU Hotspots (Java) Raw Response: ", json.dumps(check_apx_java_response, indent=2), From b0b50f1fd99ab0a88e3896d75ab1eaf77418e165 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Wed, 6 May 2026 11:53:41 -0700 Subject: [PATCH 13/19] Assert CpuBurner workC appears in Java hotspots --- mcp-local/tests/test_mcp.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mcp-local/tests/test_mcp.py b/mcp-local/tests/test_mcp.py index b4bd9d8..7f29301 100644 --- a/mcp-local/tests/test_mcp.py +++ b/mcp-local/tests/test_mcp.py @@ -249,6 +249,17 @@ def _read_response(expected_id: int, timeout: float = 10.0) -> dict: "\n***APX CPU Hotspots (Java) Structured Content: ", json.dumps(apx_java_structured, indent=2), ) + java_rows = apx_java_structured.get("rows", []) + if java_rows: + workc_found = False + for row in java_rows: + if not isinstance(row, dict): + continue + function_name = row.get("FUNCTION_NAME") or row.get("function_name") or "" + if "CpuBurner::workC" in str(function_name): + workc_found = True + break + assert workc_found, "Test Failed: Expected CpuBurner::workC in APX Java hotspots output." assert apx_java_structured.get("recipe") == "code_hotspots", "Test Failed: MCP apx_recipe_run (Java) tool failed: recipe mismatch. Expected: code_hotspots, Received: {}".format(apx_java_structured.get("recipe")) assert apx_java_structured.get("status") in {"success"}, "Test Failed: MCP apx_recipe_run (Java) tool failed: unexpected status. Received: {}".format(apx_java_structured.get("status")) print("\n***Test Passed: MCP apx_recipe_run (Java CpuBurner) tool call completed") From dd5b2b85da6fe4d52f4585e1a0f79fe616de3031 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Wed, 6 May 2026 12:16:59 -0700 Subject: [PATCH 14/19] Remove APX_TEST_JAVA_CMD override from workflow --- .github/workflows/integration-tests.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index ef4c2d4..b4b5d58 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -108,10 +108,6 @@ jobs: sudo chown -R "${APX_SSH_USER}:${APX_SSH_USER}" "${CPUBURNER_DIR}" sudo -u "${APX_SSH_USER}" javac "${CPUBURNER_DIR}/CpuBurner.java" - { - echo "APX_TEST_JAVA_CMD=timeout 60s java -XX:+PreserveFramePointer -cp ${CPUBURNER_DIR} CpuBurner 30" - } >> "${GITHUB_ENV}" - - name: Run integration tests env: APX_DEBUG_TRACE: "1" From b7c7c096f7e53d6a3947920187dd36de877c16ef Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Wed, 6 May 2026 12:36:18 -0700 Subject: [PATCH 15/19] Assert if java outputs rows not found. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- mcp-local/tests/test_mcp.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mcp-local/tests/test_mcp.py b/mcp-local/tests/test_mcp.py index 7f29301..74483af 100644 --- a/mcp-local/tests/test_mcp.py +++ b/mcp-local/tests/test_mcp.py @@ -250,16 +250,16 @@ def _read_response(expected_id: int, timeout: float = 10.0) -> dict: json.dumps(apx_java_structured, indent=2), ) java_rows = apx_java_structured.get("rows", []) - if java_rows: - workc_found = False - for row in java_rows: - if not isinstance(row, dict): - continue - function_name = row.get("FUNCTION_NAME") or row.get("function_name") or "" - if "CpuBurner::workC" in str(function_name): - workc_found = True - break - assert workc_found, "Test Failed: Expected CpuBurner::workC in APX Java hotspots output." + assert java_rows, "Test Failed: Expected non-empty APX Java hotspots rows output." + workc_found = False + for row in java_rows: + if not isinstance(row, dict): + continue + function_name = row.get("FUNCTION_NAME") or row.get("function_name") or "" + if "CpuBurner::workC" in str(function_name): + workc_found = True + break + assert workc_found, "Test Failed: Expected CpuBurner::workC in APX Java hotspots output." assert apx_java_structured.get("recipe") == "code_hotspots", "Test Failed: MCP apx_recipe_run (Java) tool failed: recipe mismatch. Expected: code_hotspots, Received: {}".format(apx_java_structured.get("recipe")) assert apx_java_structured.get("status") in {"success"}, "Test Failed: MCP apx_recipe_run (Java) tool failed: unexpected status. Received: {}".format(apx_java_structured.get("status")) print("\n***Test Passed: MCP apx_recipe_run (Java CpuBurner) tool call completed") From 28b46b740c0e28450c118963a6020661fb7aa14a Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Wed, 6 May 2026 12:52:54 -0700 Subject: [PATCH 16/19] Parse full JSON output before line scanning for run_id --- mcp-local/utils/apx.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mcp-local/utils/apx.py b/mcp-local/utils/apx.py index caa01ed..bc13f86 100644 --- a/mcp-local/utils/apx.py +++ b/mcp-local/utils/apx.py @@ -533,7 +533,9 @@ def resolve_apx_ssh_mount_env() -> Dict[str, Any]: def extract_run_id(output: str) -> str: if not output: return "" - candidates = [line.strip() for line in output.splitlines() if line.strip().startswith("{")] + raw = output.strip() + candidates = [raw] if raw else [] + candidates.extend(line.strip() for line in output.splitlines() if line.strip().startswith("{")) for candidate in candidates: try: data = json.loads(candidate) From f53d9a84152e1e56d8cd3cd80d4938eb668a4cff Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Tue, 12 May 2026 12:42:31 -0700 Subject: [PATCH 17/19] Reduce Java CpuBurner duration to 10s --- mcp-local/tests/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index 0ac3469..4a0edb9 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -251,7 +251,7 @@ "params": { "name": "apx_recipe_run", "arguments": { - "cmd": "java -XX:+PreserveFramePointer -cp /home/apxci/cpuburner CpuBurner 30", + "cmd": "java -XX:+PreserveFramePointer -cp /home/apxci/cpuburner CpuBurner 10", "remote_ip_addr": "localhost", "remote_usr": "base", "recipe": "code_hotspots", From 91754b0bcd82a7303a247a157ed4b9039018dbb4 Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Tue, 12 May 2026 12:53:43 -0700 Subject: [PATCH 18/19] Reduce Java CpuBurner duration to 1s --- mcp-local/tests/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index 4a0edb9..9bbd9d4 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -251,7 +251,7 @@ "params": { "name": "apx_recipe_run", "arguments": { - "cmd": "java -XX:+PreserveFramePointer -cp /home/apxci/cpuburner CpuBurner 10", + "cmd": "java -XX:+PreserveFramePointer -cp /home/apxci/cpuburner CpuBurner 1", "remote_ip_addr": "localhost", "remote_usr": "base", "recipe": "code_hotspots", From da18a9392292d91b53835c326417f38a7e623e7c Mon Sep 17 00:00:00 2001 From: Jaidev Singh Chadha Date: Tue, 12 May 2026 13:06:39 -0700 Subject: [PATCH 19/19] Set Java CpuBurner duration to 3s --- mcp-local/tests/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp-local/tests/constants.py b/mcp-local/tests/constants.py index 9bbd9d4..19a1bb2 100644 --- a/mcp-local/tests/constants.py +++ b/mcp-local/tests/constants.py @@ -251,7 +251,7 @@ "params": { "name": "apx_recipe_run", "arguments": { - "cmd": "java -XX:+PreserveFramePointer -cp /home/apxci/cpuburner CpuBurner 1", + "cmd": "java -XX:+PreserveFramePointer -cp /home/apxci/cpuburner CpuBurner 3", "remote_ip_addr": "localhost", "remote_usr": "base", "recipe": "code_hotspots",