Add diagnostics gathering feature to debugger and UI (#1297)

* Add diagnostics gathering feature to debugger and UI

* Improve error messaging in Debugger and optimize token skipping in DiagnosticsDialog

* add suggested chaanges

* clean up
This commit is contained in:
harsh kumar
2026-02-06 20:14:36 +05:30
committed by GitHub
parent f10710944b
commit e42055f867
4 changed files with 151 additions and 3 deletions

View File

@@ -2946,4 +2946,13 @@ public abstract class Editor extends JFrame implements RunnerListener {
super.show(component, x, y);
}
}
/**
* Called when clicking on the version number in the footer.
* Return a string with diagnostic info from the sketch,
* or empty string (or null) if not implemented/available.
*/
public String getSketchDiagnostics() {
return "";
}
}

View File

@@ -109,7 +109,7 @@ public class EditorFooter extends Box {
Base.DEBUG = !Base.DEBUG;
editor.updateDevelopMenu();
}
copyDebugInformationToClipboard();
copyFullDiagnosticsToClipboard();
}
});
@@ -120,13 +120,23 @@ public class EditorFooter extends Box {
updateTheme();
}
public static void copyDebugInformationToClipboard() {
var debugInformation = String.join("\n",
public static String getSystemDebugInformation() {
return String.join("\n",
"Version: " + Base.getVersionName(),
"Revision: " + Base.getRevision(),
"OS: " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " + System.getProperty("os.arch"),
"Java: " + System.getProperty("java.version") + " " + System.getProperty("java.vendor")
);
}
public static void copyDebugInformationToClipboard() {
var stringSelection = new StringSelection(getSystemDebugInformation());
var clipboard = java.awt.Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, null);
}
public void copyFullDiagnosticsToClipboard() {
var debugInformation = getSystemDebugInformation() + "\n\n" + editor.getSketchDiagnostics();
var stringSelection = new StringSelection(debugInformation);
var clipboard = java.awt.Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, null);

View File

@@ -2234,4 +2234,14 @@ public class JavaEditor extends Editor {
}
return count;
}
@Override
public String getSketchDiagnostics() {
if (debugger.isStarted()) {
return debugger.getDiagnostics();
} else if (runtime != null) {
return Debugger.getDiagnostics(runtime);
}
return super.getSketchDiagnostics();
}
}

View File

@@ -31,6 +31,7 @@ import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -458,6 +459,124 @@ public class Debugger {
}
/**
* Get diagnostics from the sketch, whether paused or running.
* If running, it will temporarily suspend the VM.
*/
public String getDiagnostics() {
return getDiagnostics(runtime);
}
/**
* Static helper to fetch diagnostics from a Runner, even if not debugging.
* Uses field reads instead of method invocations to avoid thread state issues.
*/
public static String getDiagnostics(Runner targetRuntime) {
if (targetRuntime == null) return "";
VirtualMachine targetVM = targetRuntime.vm();
if (targetVM == null) return "";
targetVM.suspend();
try {
// Find the PApplet subclass
List<ReferenceType> pAppletClasses = targetVM.classesByName("processing.core.PApplet");
if (pAppletClasses.isEmpty()) {
return "processing.core.PApplet not found in VM";
}
ClassType pAppletBase = (ClassType) pAppletClasses.get(0);
ClassType sketchClass = null;
for (ReferenceType type : targetVM.allClasses()) {
if (type instanceof ClassType) {
ClassType ct = (ClassType) type;
ClassType superclass = ct.superclass();
while (superclass != null) {
if (superclass.equals(pAppletBase)) {
sketchClass = ct;
break;
}
superclass = superclass.superclass();
}
if (sketchClass != null) break;
}
}
if (sketchClass == null) {
return "Could not find sketch class extending PApplet";
}
// Find instance
List<ObjectReference> instances = sketchClass.instances(1);
if (instances.isEmpty()) {
return "No instance of " + sketchClass.name() + " found";
}
ObjectReference appletInstance = instances.get(0);
// Build diagnostics by reading fields directly (no thread required)
StringBuilder diag = new StringBuilder();
diag.append("Sketch Diagnostics:\n");
diag.append(" Class: ").append(sketchClass.name()).append("\n");
// Read PApplet fields
appendField(diag, appletInstance, pAppletBase, "width");
appendField(diag, appletInstance, pAppletBase, "height");
appendField(diag, appletInstance, pAppletBase, "pixelDensity");
appendField(diag, appletInstance, pAppletBase, "frameCount");
appendField(diag, appletInstance, pAppletBase, "frameRate");
appendField(diag, appletInstance, pAppletBase, "focused");
// Try to get renderer class name from 'g' field (PGraphics)
try {
Field gField = pAppletBase.fieldByName("g");
if (gField != null) {
Value gValue = appletInstance.getValue(gField);
if (gValue instanceof ObjectReference) {
ObjectReference graphics = (ObjectReference) gValue;
diag.append(" renderer: ").append(graphics.referenceType().name()).append("\n");
}
}
} catch (Exception e) {
diag.append(" renderer: (unavailable)\n");
}
return diag.toString();
} catch (Exception e) {
return "Error gathering diagnostics: " + e.toString();
} finally {
targetVM.resume();
}
}
/**
* Helper to append a field value to the diagnostics string.
*/
private static void appendField(StringBuilder sb, ObjectReference obj, ClassType type, String fieldName) {
try {
Field field = type.fieldByName(fieldName);
if (field != null) {
Value value = obj.getValue(field);
sb.append(" ").append(fieldName).append(": ");
if (value == null) {
sb.append("null");
} else if (value instanceof com.sun.jdi.PrimitiveValue) {
sb.append(value.toString());
} else if (value instanceof StringReference) {
sb.append(((StringReference) value).value());
} else {
sb.append(value.toString());
}
sb.append("\n");
}
} catch (Exception e) {
sb.append(" ").append(fieldName).append(": (error: ").append(e.getMessage()).append(")\n");
}
}
// /** Print the current stack trace. */
// public synchronized void printStackTrace() {
// if (isStarted()) {