Add iOS on-device debugging support#4999
Open
shai-almog wants to merge 5 commits into
Open
Conversation
Adds a JDWP-compatible debugger for ParparVM-built iOS apps so jdb /
IntelliJ / VS Code can attach to a real device or the iOS Simulator
and set breakpoints, walk the stack, and inspect locals + Strings.
Three pieces:
- ParparVM translator emits per-method side-tables (locals addresses,
variable names, line tables) and a cn1-symbols.txt sidecar when
-Dcn1.onDeviceDebug=true is set. Release builds are unaffected.
- A listener thread (Ports/iOSPort/nativeSources/cn1_debugger.{h,m})
is compiled into debug builds, dials out to a desktop proxy over
TCP, and services set/clear-bp, resume, step, get-stack/locals,
get-object-class, and get-string commands. The hot path in
__CN1_DEBUG_INFO is one predictable load+branch when nothing is
attached.
- A new Maven module (cn1-debug-proxy) bridges that custom protocol
to JDWP so any standard Java debugger speaks to it. Includes a
minimum-viable JDWP implementation covering everything jdb needs
for breakpoint, where, locals, and String inspection.
Maven goals: cn1:ios-on-device-debugging (launches the proxy) and
cn1:buildIosOnDeviceDebug (cloud build target).
Build-hint UX: codename1.arg.ios.onDeviceDebug=true plus
proxyHost/proxyPort. End-user docs live in
docs/developer-guide/On-Device-Debugging.asciidoc.
Contributor
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
Contributor
Cloudflare Preview
|
Collaborator
Author
|
Compared 20 screenshots: 20 matched. |
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
Contributor
✅ ByteCodeTranslator Quality ReportTest & Coverage
Benchmark Results
Static Analysis
Generated automatically by the PR CI workflow. |
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
- Force-off ios.onDeviceDebug on release builds (ios.buildType=release)
in both the translator JVM flag and the Info.plist injection, so a
stray hint in codenameone_settings.properties can't leak the debug
listener thread into an App Store binary.
- Document the new hints (ios.onDeviceDebug, .proxyHost, .proxyPort,
.waitForAttach) in the iOS build hints table in
Advanced-Topics-Under-The-Hood.asciidoc.
- Drop unused Parser.getClasses() that triggered MS_EXPOSE_REP.
- Rework the dev-guide chapter: remove the {cn1-release-version}
sentence from Prerequisites, drop the "macOS with Xcode required"
claim (the cloud build path works equally), drop the redundant
JDWP-debugger line, collapse the duplicated build instructions
into one step that points at the normal build flow, switch to
build-hint vocabulary, and strip the codename1.arg. prefixes from
the user-facing hint names.
- Fix Vale prose-linter regressions (contractions, first-person,
Latinisms).
# Conflicts: # maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
Contributor
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
Quality-of-life improvements that emerged while running the proxy end-to-end
locally against the iOS simulator.
Device-side runtime (cn1_debugger.m + .h):
- cn1_debugger_start() no longer blocks the AppDelegate on
didFinishLaunchingWithOptions. The proxy connection runs on its own
thread regardless of CN1ProxyWaitForAttach, so UIKit can finish boot,
draw the launch transition, and -- when waitForAttach is on -- present
a translucent "Waiting for debugger..." overlay UIWindow. The previous
behaviour left the user staring at the splash with no signal that the
app was waiting on anything.
- New cn1_debugger_run_when_ready(block) API lets the AppDelegate defer
the VM start callback until the proxy reports the IDE has attached.
When waitForAttach is off (or on-device-debug is disabled at build
time) the block runs synchronously and behaves identically to the
pre-change boot flow.
GLAppDelegate:
- Calls cn1_debugger_run_when_ready around the VM callback so wait-mode
no longer races against splash dismissal, and captures the location
launch option into the block so it survives the deferral.
JDWP proxy (JdwpServer.java):
- acceptAndServe() now loops on accept() so the developer can detach and
reattach the IDE without restarting the proxy. Per-attach state is
reset via closeJdwpSession(); breakpoint registrations persist across
attaches.
- After handshake completes the proxy schedules an auto-resume that
releases the device-side waitForAttach gate 500 ms later. The delay
gives IntelliJ / VS Code time to register breakpoints before the app
races past them; without this the app sat on the waiting overlay
forever because most JDWP debuggers don't auto-send VM.Resume.
Misc:
- Add /artifacts/ to .gitignore (build wrapper drop-zone used by the
new ios-on-device-debugging mojo).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a JDWP-compatible debugger for ParparVM-built iOS apps so
jdb,IntelliJ IDEA, VS Code, Eclipse, NetBeans — anything that speaks
JDWP — can attach to a real device or the iOS Simulator and set
breakpoints, walk the stack, and inspect locals + Strings on the
running app.
End-user documentation is in
docs/developer-guide/On-Device-Debugging.asciidoc.Architecture
Three pieces, each independent:
Translator instrumentation. When
-Dcn1.onDeviceDebug=trueisset, the ParparVM translator emits per-method side-tables (locals
address arrays, variable names, line tables) and a
cn1-symbols.txtsidecar that the desktop proxy uses for nameresolution. Release builds are completely unaffected — gated by a
CN1_ON_DEVICE_DEBUGpreprocessor define.Device runtime.
Ports/iOSPort/nativeSources/cn1_debugger.{h,m}is compiled into debug builds only. It dials out to a desktop
proxy over TCP, then services the wire protocol from a listener
thread: set/clear-bp, resume, step (into/over/out), get-stack,
get-locals, get-object-class, get-string. Suspend/resume yields
the GC bit so a paused thread doesn't block collection. The hot
path in
__CN1_DEBUG_INFOis one predictable load+branch whennothing is attached (
__builtin_expect(cn1DebuggerActive, 0)).Desktop proxy. New Maven module
maven/cn1-debug-proxy/contains a minimum-viable JDWP server (
JdwpServer) thattranslates our custom protocol to/from JDWP. Covers the commands
jdbactually issues — handshake, VM.Version/IDSizes/Capabilities,AllClasses[WithGeneric], Method.LineTable/VariableTable[WithGeneric],
EventRequest.Set/Clear, Event.Composite (VM_START, BREAKPOINT,
SINGLE_STEP, VM_DEATH), ThreadReference.Name/Frames/FrameCount/Status,
StackFrame.GetValues, ObjectReference.ReferenceType, StringReference.Value.
Build hints (UX)
In
common/codenameone_settings.properties:For a physical device, set
proxyHostto the laptop's LAN IP.New Maven goals
mvn cn1:ios-on-device-debugging— autodetects the symbol sidecar,launches the proxy, and prints attach instructions (
jdb -attach localhost:8000).mvn cn1:buildIosOnDeviceDebug— cloud-build target that forces theon-device-debug flag on. Routes through the debug iOS pipeline (a
new
ios-on-device-debugant target maps to debug cert / ad-hocprovisioning).
IPhoneBuilderreadsios.onDeviceDebugand (a) threads-Dcn1.onDeviceDebug=trueinto the translator JVM and (b) injectsCN1ProxyHost/CN1ProxyPort/CN1ProxyWaitForAttachand an ATSexemption into Info.plist when the flag is set.
What works today
java.lang.Stringvalue inspection — strings show as"hello".variables view.
Known limitations (documented in the dev guide)
"evaluate expression" can read existing values but not call methods).
-ghaving been used at javac time— Codename One archetypes set this by default; classes built
without it show up as
v1,v2, ...Performance
Release builds: zero overhead (no listener, no metadata, no per-line
callback — guarded by
CN1_ON_DEVICE_DEBUGpreprocessor define).Debug builds, no debugger attached: one predictable load+branch per
source line (the existing
__CN1_DEBUG_INFOfor stack-trace linerecording is already there; we add a flag check).
Debug builds, debugger attached: ~2-3× slowdown in tight numeric
loops, consistent with
-goverhead on other native VMs.Verification
End-to-end smoke test against a real iOS Simulator app:
mvn cn1:buildIosXcodeProject -Dcodename1.arg.ios.onDeviceDebug=truegenerates an Xcode project;
xcodebuildsucceeds foriphonesimulator/arm64.jdb -attachsucceeds.
stop at com.example.DebugApp:22, the breakpoint fires andjdbshows the full 9-frame stack walked up throughDisplay.executeSerialCall/mainEDTLoop/CodenameOneThread.run.localsshowsname = "world",greeting = "hello, world", plusthe
FormandButtoninstances with class + identity.print namereturnsname = "world"directly viaStringReference.Value.Companion BuildDaemon PR mirrors the cloud-build binding:
https://github.com/codenameone/BuildDaemon (separate PR to follow).
Test plan
mvn -Plocal-dev-javase installstill succeeds with thenew
cn1-debug-proxymodule registered.current behaviour when
ios.onDeviceDebugis unset.jdbsmoke test on a fresh archetypeproject.
buildIosOnDeviceDebugbuild round-trips andthe resulting
.ipaconnects to the proxy from a tethereddevice on the same Wi-Fi.
build.