|
31 | 31 | import java.awt.event.KeyEvent; |
32 | 32 | import java.io.*; |
33 | 33 | import java.util.ArrayList; |
| 34 | +import java.util.Collections; |
34 | 35 | import java.util.HashMap; |
35 | 36 | import java.util.HashSet; |
36 | 37 | import java.util.Iterator; |
@@ -468,6 +469,123 @@ public synchronized void stepOut() { |
468 | 469 | } |
469 | 470 |
|
470 | 471 |
|
| 472 | + |
| 473 | + /** |
| 474 | + * Get diagnostics from the sketch, whether paused or running. |
| 475 | + * If running, it will temporarily suspend the VM. |
| 476 | + */ |
| 477 | + public String getDiagnostics() { |
| 478 | + return getDiagnostics(runtime); |
| 479 | + } |
| 480 | + |
| 481 | + |
| 482 | + /** |
| 483 | + * Static helper to fetch diagnostics from a Runner, even if not debugging. |
| 484 | + * Uses field reads instead of method invocations to avoid thread state issues. |
| 485 | + */ |
| 486 | + public static String getDiagnostics(Runner targetRuntime) { |
| 487 | + if (targetRuntime == null) return ""; |
| 488 | + VirtualMachine targetVM = targetRuntime.vm(); |
| 489 | + if (targetVM == null) return ""; |
| 490 | + |
| 491 | + targetVM.suspend(); |
| 492 | + try { |
| 493 | + // Find the PApplet subclass |
| 494 | + List<ReferenceType> pAppletClasses = targetVM.classesByName("processing.core.PApplet"); |
| 495 | + if (pAppletClasses.isEmpty()) { |
| 496 | + return "processing.core.PApplet not found in VM"; |
| 497 | + } |
| 498 | + ClassType pAppletBase = (ClassType) pAppletClasses.get(0); |
| 499 | + |
| 500 | + ClassType sketchClass = null; |
| 501 | + for (ReferenceType type : targetVM.allClasses()) { |
| 502 | + if (type instanceof ClassType) { |
| 503 | + ClassType ct = (ClassType) type; |
| 504 | + ClassType superclass = ct.superclass(); |
| 505 | + while (superclass != null) { |
| 506 | + if (superclass.equals(pAppletBase)) { |
| 507 | + sketchClass = ct; |
| 508 | + break; |
| 509 | + } |
| 510 | + superclass = superclass.superclass(); |
| 511 | + } |
| 512 | + if (sketchClass != null) break; |
| 513 | + } |
| 514 | + } |
| 515 | + |
| 516 | + if (sketchClass == null) { |
| 517 | + return "Could not find sketch class extending PApplet"; |
| 518 | + } |
| 519 | + |
| 520 | + // Find instance |
| 521 | + List<ObjectReference> instances = sketchClass.instances(1); |
| 522 | + if (instances.isEmpty()) { |
| 523 | + return "No instance of " + sketchClass.name() + " found"; |
| 524 | + } |
| 525 | + ObjectReference appletInstance = instances.get(0); |
| 526 | + |
| 527 | + // Build diagnostics by reading fields directly (no thread required) |
| 528 | + StringBuilder diag = new StringBuilder(); |
| 529 | + diag.append("Sketch Diagnostics:\n"); |
| 530 | + diag.append(" Class: ").append(sketchClass.name()).append("\n"); |
| 531 | + |
| 532 | + // Read PApplet fields |
| 533 | + appendField(diag, appletInstance, pAppletBase, "width"); |
| 534 | + appendField(diag, appletInstance, pAppletBase, "height"); |
| 535 | + appendField(diag, appletInstance, pAppletBase, "pixelDensity"); |
| 536 | + appendField(diag, appletInstance, pAppletBase, "frameCount"); |
| 537 | + appendField(diag, appletInstance, pAppletBase, "frameRate"); |
| 538 | + appendField(diag, appletInstance, pAppletBase, "focused"); |
| 539 | + |
| 540 | + // Try to get renderer class name from 'g' field (PGraphics) |
| 541 | + try { |
| 542 | + Field gField = pAppletBase.fieldByName("g"); |
| 543 | + if (gField != null) { |
| 544 | + Value gValue = appletInstance.getValue(gField); |
| 545 | + if (gValue instanceof ObjectReference) { |
| 546 | + ObjectReference graphics = (ObjectReference) gValue; |
| 547 | + diag.append(" renderer: ").append(graphics.referenceType().name()).append("\n"); |
| 548 | + } |
| 549 | + } |
| 550 | + } catch (Exception e) { |
| 551 | + diag.append(" renderer: (unavailable)\n"); |
| 552 | + } |
| 553 | + |
| 554 | + return diag.toString(); |
| 555 | + |
| 556 | + } catch (Exception e) { |
| 557 | + return "Error gathering diagnostics: " + e.toString(); |
| 558 | + } finally { |
| 559 | + targetVM.resume(); |
| 560 | + } |
| 561 | + } |
| 562 | + |
| 563 | + /** |
| 564 | + * Helper to append a field value to the diagnostics string. |
| 565 | + */ |
| 566 | + private static void appendField(StringBuilder sb, ObjectReference obj, ClassType type, String fieldName) { |
| 567 | + try { |
| 568 | + Field field = type.fieldByName(fieldName); |
| 569 | + if (field != null) { |
| 570 | + Value value = obj.getValue(field); |
| 571 | + sb.append(" ").append(fieldName).append(": "); |
| 572 | + if (value == null) { |
| 573 | + sb.append("null"); |
| 574 | + } else if (value instanceof com.sun.jdi.PrimitiveValue) { |
| 575 | + sb.append(value.toString()); |
| 576 | + } else if (value instanceof StringReference) { |
| 577 | + sb.append(((StringReference) value).value()); |
| 578 | + } else { |
| 579 | + sb.append(value.toString()); |
| 580 | + } |
| 581 | + sb.append("\n"); |
| 582 | + } |
| 583 | + } catch (Exception e) { |
| 584 | + sb.append(" ").append(fieldName).append(": (error: ").append(e.getMessage()).append(")\n"); |
| 585 | + } |
| 586 | + } |
| 587 | + |
| 588 | + |
471 | 589 | /** |
472 | 590 | * Gather system diagnostics from the running sketch. |
473 | 591 | * <p> |
|
0 commit comments