Skip to content

Commit cca8635

Browse files
Merge branch 'jplis-error'
2 parents f14062a + 033a2e4 commit cca8635

File tree

12 files changed

+256
-163
lines changed

12 files changed

+256
-163
lines changed

luceedebug/build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ dependencies {
3636
// https://mvnrepository.com/artifact/com.google.guava/guava
3737
implementation("com.google.guava:guava:32.1.2-jre")
3838

39-
implementation("org.ow2.asm:asm:9.3")
40-
implementation("org.ow2.asm:asm-util:9.3")
41-
implementation("org.ow2.asm:asm-commons:9.3")
39+
implementation("org.ow2.asm:asm:9.7.1")
40+
implementation("org.ow2.asm:asm-util:9.7.1")
41+
implementation("org.ow2.asm:asm-commons:9.7.1")
4242

4343
// https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api
4444
compileOnly("javax.servlet.jsp:javax.servlet.jsp-api:2.3.3")

luceedebug/src/main/java/luceedebug/Agent.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ private static Map<String, Integer> linearizedCoreInjectClasses() {
155155
result.put("luceedebug.coreinject.DebugManager$1", 0);
156156
result.put("luceedebug.coreinject.ClosureScopeLocalScopeAccessorShim", 0);
157157
result.put("luceedebug.coreinject.ComponentScopeMarkerTraitShim", 0);
158-
result.put("luceedebug.coreinject.DebugFrame$FrameContext", 0);
159158
result.put("luceedebug.coreinject.LuceeVm$SteppingState", 0);
160159
result.put("luceedebug.coreinject.LuceeVm$KlassMap", 0);
161160
result.put("luceedebug.coreinject.LuceeVm$JdwpWorker", 0);
@@ -165,21 +164,25 @@ private static Map<String, Integer> linearizedCoreInjectClasses() {
165164
result.put("luceedebug.coreinject.CfValueDebuggerBridge$MarkerTrait", 0);
166165
result.put("luceedebug.coreinject.ValTracker", 0);
167166
result.put("luceedebug.coreinject.UnsafeUtils", 0);
168-
result.put("luceedebug.coreinject.DebugFrame", 0);
169167
result.put("luceedebug.coreinject.CfValueDebuggerBridge$MarkerTrait$Scope", 0);
170168
result.put("luceedebug.coreinject.DebugManager$PageContextAndOutputStream", 0);
171169
result.put("luceedebug.coreinject.LuceeVm$ThreadMap", 0);
172170
result.put("luceedebug.coreinject.DebugManager", 0);
173171
result.put("luceedebug.coreinject.LuceeVm$JdwpStaticCallable", 0);
174172
result.put("luceedebug.coreinject.CfValueDebuggerBridge", 0);
175-
result.put("luceedebug.coreinject.DebugFrame$FrameContext$SupplierOrNull", 0);
176173
result.put("luceedebug.coreinject.LuceeVm", 0);
177174
result.put("luceedebug.coreinject.ValTracker$CleanerRunner", 0);
178175
result.put("luceedebug.coreinject.ExprEvaluator", 0);
179176

180177
result.put("luceedebug.coreinject.ExprEvaluator$Evaluator", 0);
181178
result.put("luceedebug.coreinject.ExprEvaluator$Lucee6Evaluator", 1);
182179
result.put("luceedebug.coreinject.ExprEvaluator$Lucee5Evaluator", 1);
180+
181+
result.put("luceedebug.coreinject.frame.DebugFrame", 0);
182+
result.put("luceedebug.coreinject.frame.Frame", 1);
183+
result.put("luceedebug.coreinject.frame.Frame$FrameContext", 1);
184+
result.put("luceedebug.coreinject.frame.Frame$FrameContext$SupplierOrNull", 1);
185+
result.put("luceedebug.coreinject.frame.DummyFrame", 1);
183186

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

239+
// touch System.out before agent is loaded, otherwise trying to print from within the agent during jvm initialization phase
240+
// can trigger stackoverflows. And note that System.out.println("") doesn't seem to work, as if printing the empty string
241+
// early returns, and skips whatever classloading we need to do.
242+
// TODO: clarify the exact failure case we are attempting to workaround here.
243+
System.out.println("[luceedebug] version " + Constants.version);
244+
236245
try (var jarFile = new JarFile(parsedArgs.jarPath)) {
237246
inst.appendToSystemClassLoaderSearch(jarFile);
238247
var classInjections = jarFile
@@ -258,14 +267,12 @@ public static void premain(String argString, Instrumentation inst) throws Throwa
258267
final var config = new Config(Config.checkIfFileSystemIsCaseSensitive(parsedArgs.jarPath));
259268
final var transformer = new LuceeTransformer(classInjections, parsedArgs.jdwpHost, parsedArgs.jdwpPort, parsedArgs.debugHost, parsedArgs.debugPort, config);
260269
inst.addTransformer(transformer);
261-
transformer.makeSystemOutPrintlnSafeForUseInTransformer();
262270
}
263271
catch (Throwable e) {
264272
e.printStackTrace();
265273
System.exit(1);
266274
}
267275

268-
System.out.println("[luceedebug] version " + Constants.version);
269276
System.out.println("[luceedebug] agent premain complete");
270277
}
271278
}

luceedebug/src/main/java/luceedebug/LuceeTransformer.java

Lines changed: 50 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,6 @@ static public class ClassInjection {
3131
*/
3232
private ClassInjection[] pendingCoreLoaderClassInjections;
3333

34-
/**
35-
* this print stuff is debug related;
36-
* If you want to println at some arbitrary time very soon after initializing the transformer and registering it with the JVM,
37-
* it's possible that the classes responsible for System.out.println are being loaded when _we_ say System.out.println,
38-
* which results in cryptic ClassCircularityErrors and eventually assertion failures from JVM native code.
39-
*/
40-
private boolean systemOutPrintlnLoaded = false;
41-
private ArrayList<String> pendingPrintln = new ArrayList<>();
42-
private void println(String s) {
43-
if (!systemOutPrintlnLoaded) {
44-
pendingPrintln.add(s);
45-
}
46-
else {
47-
System.out.println(s);
48-
}
49-
}
50-
public void makeSystemOutPrintlnSafeForUseInTransformer() {
51-
System.out.print("");
52-
systemOutPrintlnLoaded = true;
53-
for (var s : pendingPrintln) {
54-
println(s);
55-
}
56-
}
57-
5834
public LuceeTransformer(
5935
ClassInjection[] injections,
6036
String jdwpHost,
@@ -78,63 +54,70 @@ public byte[] transform(ClassLoader loader,
7854
ProtectionDomain protectionDomain,
7955
byte[] classfileBuffer
8056
) throws IllegalClassFormatException {
81-
var classReader = new ClassReader(classfileBuffer);
82-
String superClass = classReader.getSuperName();
57+
try {
58+
var classReader = new ClassReader(classfileBuffer);
59+
String superClass = classReader.getSuperName();
8360

84-
if (className.equals("lucee/runtime/type/scope/ClosureScope")) {
85-
return instrumentClosureScope(classfileBuffer);
86-
}
87-
else if (className.equals("lucee/runtime/ComponentImpl")) {
88-
if (loader == null) {
89-
throw new RuntimeException("instrumention ComponentImpl but core loader not seen yet");
61+
if (className.equals("lucee/runtime/type/scope/ClosureScope")) {
62+
return instrumentClosureScope(classfileBuffer);
9063
}
91-
return instrumentComponentImpl(classfileBuffer, loader);
92-
}
93-
else if (className.equals("lucee/runtime/PageContextImpl")) {
94-
GlobalIDebugManagerHolder.luceeCoreLoader = loader;
95-
96-
try {
97-
Method m = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
98-
m.setAccessible(true);
99-
100-
for (var injection : pendingCoreLoaderClassInjections) {
101-
// warn: reflection ... when does that become unsupported?
102-
m.invoke(GlobalIDebugManagerHolder.luceeCoreLoader, injection.name, injection.bytes, 0, injection.bytes.length);
64+
else if (className.equals("lucee/runtime/ComponentImpl")) {
65+
if (loader == null) {
66+
throw new RuntimeException("instrumention ComponentImpl but core loader not seen yet");
10367
}
104-
105-
pendingCoreLoaderClassInjections = null;
68+
return instrumentComponentImpl(classfileBuffer, loader);
69+
}
70+
else if (className.equals("lucee/runtime/PageContextImpl")) {
71+
GlobalIDebugManagerHolder.luceeCoreLoader = loader;
10672

10773
try {
108-
final var klass = GlobalIDebugManagerHolder.luceeCoreLoader.loadClass("luceedebug.coreinject.DebugManager");
109-
GlobalIDebugManagerHolder.debugManager = (IDebugManager)klass.getConstructor().newInstance();
110-
111-
System.out.println("[luceedebug] Loaded " + GlobalIDebugManagerHolder.debugManager + " with ClassLoader '" + GlobalIDebugManagerHolder.debugManager.getClass().getClassLoader() + "'");
112-
GlobalIDebugManagerHolder.debugManager.spawnWorker(config, jdwpHost, jdwpPort, debugHost, debugPort);
74+
Method m = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
75+
m.setAccessible(true);
76+
77+
for (var injection : pendingCoreLoaderClassInjections) {
78+
// warn: reflection ... when does that become unsupported?
79+
m.invoke(GlobalIDebugManagerHolder.luceeCoreLoader, injection.name, injection.bytes, 0, injection.bytes.length);
80+
}
81+
82+
pendingCoreLoaderClassInjections = null;
83+
84+
try {
85+
final var klass = GlobalIDebugManagerHolder.luceeCoreLoader.loadClass("luceedebug.coreinject.DebugManager");
86+
GlobalIDebugManagerHolder.debugManager = (IDebugManager)klass.getConstructor().newInstance();
87+
88+
System.out.println("[luceedebug] Loaded " + GlobalIDebugManagerHolder.debugManager + " with ClassLoader '" + GlobalIDebugManagerHolder.debugManager.getClass().getClassLoader() + "'");
89+
GlobalIDebugManagerHolder.debugManager.spawnWorker(config, jdwpHost, jdwpPort, debugHost, debugPort);
90+
}
91+
catch (Throwable e) {
92+
e.printStackTrace();
93+
System.exit(1);
94+
}
95+
96+
return classfileBuffer;
11397
}
11498
catch (Throwable e) {
11599
e.printStackTrace();
116100
System.exit(1);
101+
return null;
117102
}
118-
119-
return classfileBuffer;
120103
}
121-
catch (Throwable e) {
122-
e.printStackTrace();
123-
System.exit(1);
124-
return null;
104+
else if (superClass.equals("lucee/runtime/ComponentPageImpl") || superClass.equals("lucee/runtime/PageImpl")) {
105+
// System.out.println("[luceedebug] Instrumenting " + className);
106+
if (GlobalIDebugManagerHolder.luceeCoreLoader == null) {
107+
System.out.println("Got class " + className + " before receiving PageContextImpl, debugging will fail.");
108+
System.exit(1);
109+
}
110+
111+
return instrumentCfmOrCfc(classfileBuffer, classReader, className);
125112
}
126-
}
127-
else if (superClass.equals("lucee/runtime/ComponentPageImpl") || superClass.equals("lucee/runtime/PageImpl")) {
128-
// System.out.println("[luceedebug] Instrumenting " + className);
129-
if (GlobalIDebugManagerHolder.luceeCoreLoader == null) {
130-
System.out.println("Got class " + className + " before receiving PageContextImpl, debugging will fail.");
131-
System.exit(1);
113+
else {
114+
return classfileBuffer;
132115
}
133-
134-
return instrumentCfmOrCfc(classfileBuffer, classReader, className);
135116
}
136-
else {
137-
return classfileBuffer;
117+
catch (Throwable e) {
118+
e.printStackTrace();
119+
System.exit(1);
120+
return null;
138121
}
139122
}
140123

luceedebug/src/main/java/luceedebug/coreinject/CfValueDebuggerBridge.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
import lucee.runtime.type.Array;
1616
import luceedebug.ICfValueDebuggerBridge;
1717
import luceedebug.IDebugEntity;
18+
import luceedebug.coreinject.frame.Frame;
1819

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

34-
private final DebugFrame frame;
35+
private final Frame frame;
3536
public final Object obj;
3637
public final long id;
3738

38-
public CfValueDebuggerBridge(DebugFrame frame, Object obj) {
39+
public CfValueDebuggerBridge(Frame frame, Object obj) {
3940
this.frame = Objects.requireNonNull(frame);
4041
this.obj = Objects.requireNonNull(obj);
4142
this.id = frame.valTracker.idempotentRegisterObject(obj).id;
@@ -45,10 +46,10 @@ public long getID() {
4546
return id;
4647
}
4748

48-
static class MarkerTrait {
49-
static class Scope {
49+
public static class MarkerTrait {
50+
public static class Scope {
5051
public final Map<?,?> scopelike;
51-
Scope(Map<?,?> scopelike) {
52+
public Scope(Map<?,?> scopelike) {
5253
this.scopelike = scopelike;
5354
}
5455
}
@@ -57,7 +58,7 @@ static class Scope {
5758
/**
5859
* @maybeNull_which --> null means "any type"
5960
*/
60-
public static IDebugEntity[] getAsDebugEntity(DebugFrame frame, Object obj, IDebugEntity.DebugEntityType maybeNull_which) {
61+
public static IDebugEntity[] getAsDebugEntity(Frame frame, Object obj, IDebugEntity.DebugEntityType maybeNull_which) {
6162
return getAsDebugEntity(frame.valTracker, obj, maybeNull_which);
6263
}
6364

luceedebug/src/main/java/luceedebug/coreinject/DebugEntity.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
import luceedebug.*;
44

55
public class DebugEntity implements IDebugEntity {
6-
String name;
7-
String value;
8-
int namedVariables;
9-
int indexedVariables;
10-
boolean expensive;
11-
long variablesReference;
6+
public String name;
7+
public String value;
8+
public int namedVariables;
9+
public int indexedVariables;
10+
public boolean expensive;
11+
public long variablesReference;
1212

1313
public String getName() { return name; }
1414
public String getValue() { return value; }

0 commit comments

Comments
 (0)