Skip to content

Commit

Permalink
Merge branch 'jplis-error'
Browse files Browse the repository at this point in the history
  • Loading branch information
softwareCobbler committed Nov 23, 2024
2 parents f14062a + 033a2e4 commit cca8635
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 163 deletions.
6 changes: 3 additions & 3 deletions luceedebug/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ dependencies {
// https://mvnrepository.com/artifact/com.google.guava/guava
implementation("com.google.guava:guava:32.1.2-jre")

implementation("org.ow2.asm:asm:9.3")
implementation("org.ow2.asm:asm-util:9.3")
implementation("org.ow2.asm:asm-commons:9.3")
implementation("org.ow2.asm:asm:9.7.1")
implementation("org.ow2.asm:asm-util:9.7.1")
implementation("org.ow2.asm:asm-commons:9.7.1")

// https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api
compileOnly("javax.servlet.jsp:javax.servlet.jsp-api:2.3.3")
Expand Down
17 changes: 12 additions & 5 deletions luceedebug/src/main/java/luceedebug/Agent.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,6 @@ private static Map<String, Integer> linearizedCoreInjectClasses() {
result.put("luceedebug.coreinject.DebugManager$1", 0);
result.put("luceedebug.coreinject.ClosureScopeLocalScopeAccessorShim", 0);
result.put("luceedebug.coreinject.ComponentScopeMarkerTraitShim", 0);
result.put("luceedebug.coreinject.DebugFrame$FrameContext", 0);
result.put("luceedebug.coreinject.LuceeVm$SteppingState", 0);
result.put("luceedebug.coreinject.LuceeVm$KlassMap", 0);
result.put("luceedebug.coreinject.LuceeVm$JdwpWorker", 0);
Expand All @@ -165,21 +164,25 @@ private static Map<String, Integer> linearizedCoreInjectClasses() {
result.put("luceedebug.coreinject.CfValueDebuggerBridge$MarkerTrait", 0);
result.put("luceedebug.coreinject.ValTracker", 0);
result.put("luceedebug.coreinject.UnsafeUtils", 0);
result.put("luceedebug.coreinject.DebugFrame", 0);
result.put("luceedebug.coreinject.CfValueDebuggerBridge$MarkerTrait$Scope", 0);
result.put("luceedebug.coreinject.DebugManager$PageContextAndOutputStream", 0);
result.put("luceedebug.coreinject.LuceeVm$ThreadMap", 0);
result.put("luceedebug.coreinject.DebugManager", 0);
result.put("luceedebug.coreinject.LuceeVm$JdwpStaticCallable", 0);
result.put("luceedebug.coreinject.CfValueDebuggerBridge", 0);
result.put("luceedebug.coreinject.DebugFrame$FrameContext$SupplierOrNull", 0);
result.put("luceedebug.coreinject.LuceeVm", 0);
result.put("luceedebug.coreinject.ValTracker$CleanerRunner", 0);
result.put("luceedebug.coreinject.ExprEvaluator", 0);

result.put("luceedebug.coreinject.ExprEvaluator$Evaluator", 0);
result.put("luceedebug.coreinject.ExprEvaluator$Lucee6Evaluator", 1);
result.put("luceedebug.coreinject.ExprEvaluator$Lucee5Evaluator", 1);

result.put("luceedebug.coreinject.frame.DebugFrame", 0);
result.put("luceedebug.coreinject.frame.Frame", 1);
result.put("luceedebug.coreinject.frame.Frame$FrameContext", 1);
result.put("luceedebug.coreinject.frame.Frame$FrameContext$SupplierOrNull", 1);
result.put("luceedebug.coreinject.frame.DummyFrame", 1);

return result;
}
Expand Down Expand Up @@ -233,6 +236,12 @@ public static void premain(String argString, Instrumentation inst) throws Throwa
//
System.setProperty("org.osgi.framework.bootdelegation", "com.sun.jdi,com.sun.jdi.connect,com.sun.jdi.event,com.sun.jdi.request,luceedebug,luceedebug_shadow.*");

// touch System.out before agent is loaded, otherwise trying to print from within the agent during jvm initialization phase
// can trigger stackoverflows. And note that System.out.println("") doesn't seem to work, as if printing the empty string
// early returns, and skips whatever classloading we need to do.
// TODO: clarify the exact failure case we are attempting to workaround here.
System.out.println("[luceedebug] version " + Constants.version);

try (var jarFile = new JarFile(parsedArgs.jarPath)) {
inst.appendToSystemClassLoaderSearch(jarFile);
var classInjections = jarFile
Expand All @@ -258,14 +267,12 @@ public static void premain(String argString, Instrumentation inst) throws Throwa
final var config = new Config(Config.checkIfFileSystemIsCaseSensitive(parsedArgs.jarPath));
final var transformer = new LuceeTransformer(classInjections, parsedArgs.jdwpHost, parsedArgs.jdwpPort, parsedArgs.debugHost, parsedArgs.debugPort, config);
inst.addTransformer(transformer);
transformer.makeSystemOutPrintlnSafeForUseInTransformer();
}
catch (Throwable e) {
e.printStackTrace();
System.exit(1);
}

System.out.println("[luceedebug] version " + Constants.version);
System.out.println("[luceedebug] agent premain complete");
}
}
117 changes: 50 additions & 67 deletions luceedebug/src/main/java/luceedebug/LuceeTransformer.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,6 @@ static public class ClassInjection {
*/
private ClassInjection[] pendingCoreLoaderClassInjections;

/**
* this print stuff is debug related;
* If you want to println at some arbitrary time very soon after initializing the transformer and registering it with the JVM,
* it's possible that the classes responsible for System.out.println are being loaded when _we_ say System.out.println,
* which results in cryptic ClassCircularityErrors and eventually assertion failures from JVM native code.
*/
private boolean systemOutPrintlnLoaded = false;
private ArrayList<String> pendingPrintln = new ArrayList<>();
private void println(String s) {
if (!systemOutPrintlnLoaded) {
pendingPrintln.add(s);
}
else {
System.out.println(s);
}
}
public void makeSystemOutPrintlnSafeForUseInTransformer() {
System.out.print("");
systemOutPrintlnLoaded = true;
for (var s : pendingPrintln) {
println(s);
}
}

public LuceeTransformer(
ClassInjection[] injections,
String jdwpHost,
Expand All @@ -78,63 +54,70 @@ public byte[] transform(ClassLoader loader,
ProtectionDomain protectionDomain,
byte[] classfileBuffer
) throws IllegalClassFormatException {
var classReader = new ClassReader(classfileBuffer);
String superClass = classReader.getSuperName();
try {
var classReader = new ClassReader(classfileBuffer);
String superClass = classReader.getSuperName();

if (className.equals("lucee/runtime/type/scope/ClosureScope")) {
return instrumentClosureScope(classfileBuffer);
}
else if (className.equals("lucee/runtime/ComponentImpl")) {
if (loader == null) {
throw new RuntimeException("instrumention ComponentImpl but core loader not seen yet");
if (className.equals("lucee/runtime/type/scope/ClosureScope")) {
return instrumentClosureScope(classfileBuffer);
}
return instrumentComponentImpl(classfileBuffer, loader);
}
else if (className.equals("lucee/runtime/PageContextImpl")) {
GlobalIDebugManagerHolder.luceeCoreLoader = loader;

try {
Method m = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
m.setAccessible(true);

for (var injection : pendingCoreLoaderClassInjections) {
// warn: reflection ... when does that become unsupported?
m.invoke(GlobalIDebugManagerHolder.luceeCoreLoader, injection.name, injection.bytes, 0, injection.bytes.length);
else if (className.equals("lucee/runtime/ComponentImpl")) {
if (loader == null) {
throw new RuntimeException("instrumention ComponentImpl but core loader not seen yet");
}

pendingCoreLoaderClassInjections = null;
return instrumentComponentImpl(classfileBuffer, loader);
}
else if (className.equals("lucee/runtime/PageContextImpl")) {
GlobalIDebugManagerHolder.luceeCoreLoader = loader;

try {
final var klass = GlobalIDebugManagerHolder.luceeCoreLoader.loadClass("luceedebug.coreinject.DebugManager");
GlobalIDebugManagerHolder.debugManager = (IDebugManager)klass.getConstructor().newInstance();

System.out.println("[luceedebug] Loaded " + GlobalIDebugManagerHolder.debugManager + " with ClassLoader '" + GlobalIDebugManagerHolder.debugManager.getClass().getClassLoader() + "'");
GlobalIDebugManagerHolder.debugManager.spawnWorker(config, jdwpHost, jdwpPort, debugHost, debugPort);
Method m = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
m.setAccessible(true);

for (var injection : pendingCoreLoaderClassInjections) {
// warn: reflection ... when does that become unsupported?
m.invoke(GlobalIDebugManagerHolder.luceeCoreLoader, injection.name, injection.bytes, 0, injection.bytes.length);
}

pendingCoreLoaderClassInjections = null;

try {
final var klass = GlobalIDebugManagerHolder.luceeCoreLoader.loadClass("luceedebug.coreinject.DebugManager");
GlobalIDebugManagerHolder.debugManager = (IDebugManager)klass.getConstructor().newInstance();

System.out.println("[luceedebug] Loaded " + GlobalIDebugManagerHolder.debugManager + " with ClassLoader '" + GlobalIDebugManagerHolder.debugManager.getClass().getClassLoader() + "'");
GlobalIDebugManagerHolder.debugManager.spawnWorker(config, jdwpHost, jdwpPort, debugHost, debugPort);
}
catch (Throwable e) {
e.printStackTrace();
System.exit(1);
}

return classfileBuffer;
}
catch (Throwable e) {
e.printStackTrace();
System.exit(1);
return null;
}

return classfileBuffer;
}
catch (Throwable e) {
e.printStackTrace();
System.exit(1);
return null;
else if (superClass.equals("lucee/runtime/ComponentPageImpl") || superClass.equals("lucee/runtime/PageImpl")) {
// System.out.println("[luceedebug] Instrumenting " + className);
if (GlobalIDebugManagerHolder.luceeCoreLoader == null) {
System.out.println("Got class " + className + " before receiving PageContextImpl, debugging will fail.");
System.exit(1);
}

return instrumentCfmOrCfc(classfileBuffer, classReader, className);
}
}
else if (superClass.equals("lucee/runtime/ComponentPageImpl") || superClass.equals("lucee/runtime/PageImpl")) {
// System.out.println("[luceedebug] Instrumenting " + className);
if (GlobalIDebugManagerHolder.luceeCoreLoader == null) {
System.out.println("Got class " + className + " before receiving PageContextImpl, debugging will fail.");
System.exit(1);
else {
return classfileBuffer;
}

return instrumentCfmOrCfc(classfileBuffer, classReader, className);
}
else {
return classfileBuffer;
catch (Throwable e) {
e.printStackTrace();
System.exit(1);
return null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
import lucee.runtime.type.Array;
import luceedebug.ICfValueDebuggerBridge;
import luceedebug.IDebugEntity;
import luceedebug.coreinject.frame.Frame;

class CfValueDebuggerBridge implements ICfValueDebuggerBridge {
public class CfValueDebuggerBridge implements ICfValueDebuggerBridge {
// Pin some ephemeral evaluated things so they don't get GC'd immediately.
// It would be better to pin them to a "session" or something with a meaningful lifetime,
// rather than hope they live long enough in this cache to be useful.
Expand All @@ -27,15 +28,15 @@ class CfValueDebuggerBridge implements ICfValueDebuggerBridge {
.maximumSize(50)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
static void pin(Object obj) {
public static void pin(Object obj) {
pinnedObjects.put(System.identityHashCode(obj), obj);
}

private final DebugFrame frame;
private final Frame frame;
public final Object obj;
public final long id;

public CfValueDebuggerBridge(DebugFrame frame, Object obj) {
public CfValueDebuggerBridge(Frame frame, Object obj) {
this.frame = Objects.requireNonNull(frame);
this.obj = Objects.requireNonNull(obj);
this.id = frame.valTracker.idempotentRegisterObject(obj).id;
Expand All @@ -45,10 +46,10 @@ public long getID() {
return id;
}

static class MarkerTrait {
static class Scope {
public static class MarkerTrait {
public static class Scope {
public final Map<?,?> scopelike;
Scope(Map<?,?> scopelike) {
public Scope(Map<?,?> scopelike) {
this.scopelike = scopelike;
}
}
Expand All @@ -57,7 +58,7 @@ static class Scope {
/**
* @maybeNull_which --> null means "any type"
*/
public static IDebugEntity[] getAsDebugEntity(DebugFrame frame, Object obj, IDebugEntity.DebugEntityType maybeNull_which) {
public static IDebugEntity[] getAsDebugEntity(Frame frame, Object obj, IDebugEntity.DebugEntityType maybeNull_which) {
return getAsDebugEntity(frame.valTracker, obj, maybeNull_which);
}

Expand Down
12 changes: 6 additions & 6 deletions luceedebug/src/main/java/luceedebug/coreinject/DebugEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import luceedebug.*;

public class DebugEntity implements IDebugEntity {
String name;
String value;
int namedVariables;
int indexedVariables;
boolean expensive;
long variablesReference;
public String name;
public String value;
public int namedVariables;
public int indexedVariables;
public boolean expensive;
public long variablesReference;

public String getName() { return name; }
public String getValue() { return value; }
Expand Down
Loading

0 comments on commit cca8635

Please sign in to comment.