Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
3f95ff6
feat: eliminate codeflash.toml — auto-detect Java config from build f…
misrasaurabh1 Mar 20, 2026
9d01710
fix: skip behavior instrumentation for replay test files
misrasaurabh1 Mar 20, 2026
df55e74
fix: support JUnit 4 in replay test generation
misrasaurabh1 Mar 20, 2026
9b1fc14
fix: avoid duplicate method names for overloaded Java methods in repl…
misrasaurabh1 Mar 20, 2026
721655f
test: add tests for JUnit 4 support, overload handling, instrumentati…
misrasaurabh1 Mar 20, 2026
b0d4a5e
test: add tests for JFR parser, graceful timeout, and project root re…
misrasaurabh1 Mar 20, 2026
d441bb9
fix: properly instrument replay tests instead of skipping them
misrasaurabh1 Mar 20, 2026
c087d0d
feat: smart ReplayHelper with behavior capture and performance timing
misrasaurabh1 Mar 20, 2026
0fc5bba
fix: e2e java tracer runs on all codeflash changes and validates repl…
misrasaurabh1 Mar 20, 2026
dae9b48
fix: restore correct argument order in process_pyproject_config
misrasaurabh1 Mar 20, 2026
74cbe2a
fix: Windows compatibility for Java config detection tests
misrasaurabh1 Mar 20, 2026
be616d1
fix: flush e2e test output to CI logs in real-time
misrasaurabh1 Mar 20, 2026
803fb64
fix: add missing type params for dict in _write_maven_properties and …
github-actions[bot] Mar 20, 2026
13dae81
fix: increase JFR sampling frequency and make Workload exercise funct…
misrasaurabh1 Mar 20, 2026
3c63b60
fix: preserve pom.xml formatting in config writer and align write/rem…
mashraf-222 Mar 24, 2026
5942ae9
fix: prefer closer config file over parent Java build file in monorep…
mashraf-222 Mar 24, 2026
1292144
fix: capture real line numbers in tracer and track dropped captures (…
mashraf-222 Mar 24, 2026
970c9f8
fix: use 3 package components for tracer instrumentation scope (TODO-39)
mashraf-222 Mar 24, 2026
af924cb
fix: set language singleton early so git diff auto-detection works fo…
mashraf-222 Mar 18, 2026
ef49ad7
fix: set language singleton early so git diff auto-detection works fo…
mashraf-222 Mar 18, 2026
ecb8d48
feat(07-01): make get_git_diff() language-agnostic using registry ext…
mashraf-222 Mar 18, 2026
2394fdd
feat(07-02): add LanguageConfig dataclass and find_all_config_files()
mashraf-222 Mar 18, 2026
0bf63e5
test(07-02): verify find_all_functions_in_file uses registry lookup (…
mashraf-222 Mar 18, 2026
fbefa87
feat(08-01): extract apply_language_config and add tests
mashraf-222 Mar 18, 2026
e4131c1
feat(08-01): wire multi-language orchestration loop into main.py
mashraf-222 Mar 18, 2026
e363927
feat(08-02): extract normalize_toml_config and apply in find_all_conf…
mashraf-222 Mar 18, 2026
82c278c
feat(08-02): add per-language error isolation in orchestration loop
mashraf-222 Mar 18, 2026
22839eb
test(08-02): add summary logging tests for orchestration results
mashraf-222 Mar 18, 2026
a92c320
docs(08-02): complete error isolation and config normalization plan
mashraf-222 Mar 18, 2026
0219de9
docs(08): create gap closure plan for CLI path routing
mashraf-222 Mar 18, 2026
64f092f
docs(08): revise 08-03 plan based on checker feedback
mashraf-222 Mar 18, 2026
369aee3
fix(08): revise plan 03 based on checker feedback
mashraf-222 Mar 18, 2026
51649ff
feat(08-03): add --file language filtering to multi-language orchestr…
mashraf-222 Mar 18, 2026
ecd08ab
test(08-03): add CLI path routing tests for --file, --all, and no-flags
mashraf-222 Mar 18, 2026
f350013
test(09-02): add failing tests for unconfigured language detection
mashraf-222 Mar 18, 2026
7a80478
feat(09-02): add unconfigured language auto-detection and config crea…
mashraf-222 Mar 18, 2026
dff13a6
test(10-01): fill Python unit test gaps for multi-language modules
mashraf-222 Mar 19, 2026
9c50bc1
fix(10-03): resolve test pollution in multi-language orchestration tests
mashraf-222 Mar 19, 2026
68e40aa
style: auto-fix ruff formatting in config_parser.py
github-actions[bot] Mar 19, 2026
e41517d
chore: remove planning files from PR
mashraf-222 Mar 24, 2026
cdaa526
fix: align multi-language discovery with zero-config Java and add mon…
mashraf-222 Mar 25, 2026
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
12 changes: 2 additions & 10 deletions .github/workflows/e2e-java-tracer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,9 @@ name: E2E - Java Tracer
on:
pull_request:
paths:
- 'codeflash/languages/java/**'
- 'codeflash/languages/base.py'
- 'codeflash/languages/registry.py'
- 'codeflash/tracer.py'
- 'codeflash/benchmarking/function_ranker.py'
- 'codeflash/discovery/functions_to_optimize.py'
- 'codeflash/optimization/**'
- 'codeflash/verification/**'
- 'codeflash/**'
- 'codeflash-java-runtime/**'
- 'tests/test_languages/fixtures/java_tracer_e2e/**'
- 'tests/scripts/end_to_end_test_java_tracer.py'
- 'tests/**'
- '.github/workflows/e2e-java-tracer.yaml'

workflow_dispatch:
Expand Down
4 changes: 0 additions & 4 deletions code_to_optimize/java-gradle/codeflash.toml

This file was deleted.

6 changes: 0 additions & 6 deletions code_to_optimize/java/codeflash.toml

This file was deleted.

236 changes: 198 additions & 38 deletions codeflash-java-runtime/src/main/java/com/codeflash/ReplayHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,179 @@

public class ReplayHelper {

private final Connection db;
private final Connection traceDb;

// Codeflash instrumentation state — read from environment variables once
private final String mode; // "behavior", "performance", or null
private final int loopIndex;
private final String testIteration;
private final String outputFile; // SQLite path for behavior capture
private final int innerIterations; // for performance looping

// Behavior mode: lazily opened SQLite connection for writing results
private Connection behaviorDb;
private boolean behaviorDbInitialized;

public ReplayHelper(String traceDbPath) {
try {
this.db = DriverManager.getConnection("jdbc:sqlite:" + traceDbPath);
this.traceDb = DriverManager.getConnection("jdbc:sqlite:" + traceDbPath);
} catch (SQLException e) {
throw new RuntimeException("Failed to open trace database: " + traceDbPath, e);
}

// Read codeflash instrumentation env vars (set by the test runner)
this.mode = System.getenv("CODEFLASH_MODE");
this.loopIndex = parseIntEnv("CODEFLASH_LOOP_INDEX", 1);
this.testIteration = getEnvOrDefault("CODEFLASH_TEST_ITERATION", "0");
this.outputFile = System.getenv("CODEFLASH_OUTPUT_FILE");
this.innerIterations = parseIntEnv("CODEFLASH_INNER_ITERATIONS", 10);
}

public void replay(String className, String methodName, String descriptor, int invocationIndex) throws Exception {
// Query the function_calls table for this method at the given index
// Deserialize args and resolve method (done once, outside timing)
Object[] allArgs = loadArgs(className, methodName, descriptor, invocationIndex);
Class<?> targetClass = Class.forName(className);

Type[] paramTypes = Type.getArgumentTypes(descriptor);
Class<?>[] paramClasses = new Class<?>[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
paramClasses[i] = typeToClass(paramTypes[i]);
}

Method method = targetClass.getDeclaredMethod(methodName, paramClasses);
method.setAccessible(true);
boolean isStatic = Modifier.isStatic(method.getModifiers());

Object instance = null;
if (!isStatic) {
try {
java.lang.reflect.Constructor<?> ctor = targetClass.getDeclaredConstructor();
ctor.setAccessible(true);
instance = ctor.newInstance();
} catch (NoSuchMethodException e) {
instance = new org.objenesis.ObjenesisStd().newInstance(targetClass);
}
}

// Get the calling test method name from the stack trace
String testMethodName = getCallingTestMethodName();
// Module name = the test class that called us
String testClassName = getCallingTestClassName();

if ("behavior".equals(mode)) {
replayBehavior(method, instance, allArgs, className, methodName, testClassName, testMethodName);
} else if ("performance".equals(mode)) {
replayPerformance(method, instance, allArgs, className, methodName, testClassName, testMethodName);
} else {
// No codeflash mode — just invoke (trace-only or manual testing)
method.invoke(instance, allArgs);
}
}

private void replayBehavior(Method method, Object instance, Object[] args,
String className, String methodName,
String testClassName, String testMethodName) throws Exception {
String invId = testIteration + "_" + testMethodName;

// Print start marker (same format as behavior instrumentation)
System.out.println("!$######" + testClassName + ":" + testClassName + "." + testMethodName
+ ":" + methodName + ":" + loopIndex + ":" + invId + "######$!");

long startNs = System.nanoTime();
Object result;
try {
result = method.invoke(instance, args);
} catch (java.lang.reflect.InvocationTargetException e) {
throw (Exception) e.getCause();
}
long durationNs = System.nanoTime() - startNs;

// Print end marker
System.out.println("!######" + testClassName + ":" + testClassName + "." + testMethodName
+ ":" + methodName + ":" + loopIndex + ":" + invId + ":" + durationNs + "######!");

// Write return value to SQLite for correctness comparison
if (outputFile != null && !outputFile.isEmpty()) {
writeBehaviorResult(testClassName, testMethodName, methodName, invId, durationNs, result);
}
}

private void replayPerformance(Method method, Object instance, Object[] args,
String className, String methodName,
String testClassName, String testMethodName) throws Exception {
// Performance mode: run inner loop for JIT warmup, print timing for each iteration
int maxInner = innerIterations;
for (int inner = 0; inner < maxInner; inner++) {
int loopId = (loopIndex - 1) * maxInner + inner;
String invId = testMethodName;

// Print start marker
System.out.println("!$######" + testClassName + ":" + testClassName + "." + testMethodName
+ ":" + methodName + ":" + loopId + ":" + invId + "######$!");

long startNs = System.nanoTime();
try {
method.invoke(instance, args);
} catch (java.lang.reflect.InvocationTargetException e) {
// Swallow — performance mode doesn't check correctness
}
long durationNs = System.nanoTime() - startNs;

// Print end marker
System.out.println("!######" + testClassName + ":" + testClassName + "." + testMethodName
+ ":" + methodName + ":" + loopId + ":" + invId + ":" + durationNs + "######!");
}
}

private void writeBehaviorResult(String testClassName, String testMethodName,
String functionName, String invId,
long durationNs, Object result) {
try {
ensureBehaviorDb();
String sql = "INSERT INTO test_results VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
try (PreparedStatement ps = behaviorDb.prepareStatement(sql)) {
ps.setString(1, testClassName); // test_module_path
ps.setString(2, testClassName); // test_class_name
ps.setString(3, testMethodName); // test_function_name
ps.setString(4, functionName); // function_getting_tested
ps.setInt(5, loopIndex); // loop_index
ps.setString(6, invId); // iteration_id
ps.setLong(7, durationNs); // runtime
ps.setBytes(8, serializeResult(result)); // return_value
ps.setString(9, "function_call"); // verification_type
ps.executeUpdate();
}
} catch (Exception e) {
System.err.println("ReplayHelper: SQLite behavior write error: " + e.getMessage());
}
}

private void ensureBehaviorDb() throws SQLException {
if (behaviorDbInitialized) return;
behaviorDbInitialized = true;
behaviorDb = DriverManager.getConnection("jdbc:sqlite:" + outputFile);
try (java.sql.Statement stmt = behaviorDb.createStatement()) {
stmt.execute("CREATE TABLE IF NOT EXISTS test_results (" +
"test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, " +
"function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, " +
"runtime INTEGER, return_value BLOB, verification_type TEXT)");
}
}

private byte[] serializeResult(Object result) {
if (result == null) return null;
try {
return Serializer.serialize(result);
} catch (Exception e) {
// Fall back to String.valueOf if Kryo fails
return String.valueOf(result).getBytes(java.nio.charset.StandardCharsets.UTF_8);
}
}

private Object[] loadArgs(String className, String methodName, String descriptor, int invocationIndex)
throws SQLException {
byte[] argsBlob;
try (PreparedStatement stmt = db.prepareStatement(
try (PreparedStatement stmt = traceDb.prepareStatement(
"SELECT args FROM function_calls " +
"WHERE classname = ? AND function = ? AND descriptor = ? " +
"ORDER BY time_ns LIMIT 1 OFFSET ?")) {
Expand All @@ -43,46 +202,35 @@ public void replay(String className, String methodName, String descriptor, int i
}
}

// Deserialize args
Object deserialized = Serializer.deserialize(argsBlob);
if (!(deserialized instanceof Object[])) {
throw new RuntimeException("Deserialized args is not Object[], got: "
+ (deserialized == null ? "null" : deserialized.getClass().getName()));
}
Object[] allArgs = (Object[]) deserialized;

// Load the target class
Class<?> targetClass = Class.forName(className);
return (Object[]) deserialized;
}

// Parse descriptor to find parameter types
Type[] paramTypes = Type.getArgumentTypes(descriptor);
Class<?>[] paramClasses = new Class<?>[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
paramClasses[i] = typeToClass(paramTypes[i]);
private static String getCallingTestMethodName() {
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
// Walk up: [0]=getStackTrace, [1]=this method, [2]=replay(), [3]=calling test method
for (int i = 3; i < stack.length; i++) {
String method = stack[i].getMethodName();
if (method.startsWith("replay_")) {
return method;
}
}
return stack.length > 3 ? stack[3].getMethodName() : "unknown";
}

// Find the method
Method method = targetClass.getDeclaredMethod(methodName, paramClasses);
method.setAccessible(true);

boolean isStatic = Modifier.isStatic(method.getModifiers());

if (isStatic) {
method.invoke(null, allArgs);
} else {
// Args contain only explicit parameters (no 'this').
// Create a default instance via no-arg constructor or Kryo.
Object instance;
try {
java.lang.reflect.Constructor<?> ctor = targetClass.getDeclaredConstructor();
ctor.setAccessible(true);
instance = ctor.newInstance();
} catch (NoSuchMethodException e) {
// Fall back to Objenesis instantiation (no constructor needed)
instance = new org.objenesis.ObjenesisStd().newInstance(targetClass);
private static String getCallingTestClassName() {
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
for (int i = 3; i < stack.length; i++) {
String cls = stack[i].getClassName();
if (cls.contains("ReplayTest") || cls.contains("replay")) {
return cls;
}
method.invoke(instance, allArgs);
}
return stack.length > 3 ? stack[3].getClassName() : "unknown";
}

private static Class<?> typeToClass(Type type) throws ClassNotFoundException {
Expand All @@ -106,11 +254,23 @@ private static Class<?> typeToClass(Type type) throws ClassNotFoundException {
}
}

private static int parseIntEnv(String name, int defaultValue) {
String val = System.getenv(name);
if (val == null || val.isEmpty()) return defaultValue;
try { return Integer.parseInt(val); } catch (NumberFormatException e) { return defaultValue; }
}

private static String getEnvOrDefault(String name, String defaultValue) {
String val = System.getenv(name);
return (val != null && !val.isEmpty()) ? val : defaultValue;
}

public void close() {
try {
if (db != null) db.close();
} catch (SQLException e) {
System.err.println("Error closing ReplayHelper: " + e.getMessage());
try { if (traceDb != null) traceDb.close(); } catch (SQLException e) {
System.err.println("Error closing ReplayHelper trace db: " + e.getMessage());
}
try { if (behaviorDb != null) behaviorDb.close(); } catch (SQLException e) {
System.err.println("Error closing ReplayHelper behavior db: " + e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public final class TraceRecorder {
private final TracerConfig config;
private final TraceWriter writer;
private final ConcurrentHashMap<String, AtomicInteger> functionCounts = new ConcurrentHashMap<>();
private final AtomicInteger droppedCaptures = new AtomicInteger(0);
private final int maxFunctionCount;
private final ExecutorService serializerExecutor;

Expand Down Expand Up @@ -82,11 +83,13 @@ private void onEntryImpl(String className, String methodName, String descriptor,
argsBlob = future.get(SERIALIZATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
future.cancel(true);
droppedCaptures.incrementAndGet();
System.err.println("[codeflash-tracer] Serialization timed out for " + className + "."
+ methodName);
return;
} catch (Exception e) {
Throwable cause = e.getCause() != null ? e.getCause() : e;
droppedCaptures.incrementAndGet();
System.err.println("[codeflash-tracer] Serialization failed for " + className + "."
+ methodName + ": " + cause.getClass().getSimpleName() + ": " + cause.getMessage());
return;
Expand All @@ -113,11 +116,15 @@ public void flush() {
}
metadata.put("totalCaptures", String.valueOf(totalCaptures));

int dropped = droppedCaptures.get();
metadata.put("droppedCaptures", String.valueOf(dropped));

writer.writeMetadata(metadata);
writer.flush();
writer.close();

System.err.println("[codeflash-tracer] Captured " + totalCaptures
+ " invocations across " + functionCounts.size() + " methods");
+ " invocations across " + functionCounts.size() + " methods"
+ (dropped > 0 ? " (" + dropped + " dropped due to serialization timeout/failure)" : ""));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.util.Collections;
import java.util.Map;

public class TracingClassVisitor extends ClassVisitor {

private final String internalClassName;
private final Map<String, Integer> methodLineNumbers;
private String sourceFile;

public TracingClassVisitor(ClassVisitor classVisitor, String internalClassName) {
public TracingClassVisitor(ClassVisitor classVisitor, String internalClassName,
Map<String, Integer> methodLineNumbers) {
super(Opcodes.ASM9, classVisitor);
this.internalClassName = internalClassName;
this.methodLineNumbers = methodLineNumbers != null ? methodLineNumbers : Collections.emptyMap();
}

@Override
Expand All @@ -37,7 +43,8 @@ public MethodVisitor visitMethod(int access, String name, String descriptor,
return mv;
}

int lineNumber = methodLineNumbers.getOrDefault(name + descriptor, 0);
return new TracingMethodAdapter(mv, access, name, descriptor,
internalClassName, 0, sourceFile != null ? sourceFile : "");
internalClassName, lineNumber, sourceFile != null ? sourceFile : "");
}
}
Loading