Lucee 7.1 - Lucee native step debugger / extension (LuceeDebug)

I’ve been working on making LuceeDebug really rock with Lucee 7.1

Having a step debugger really rocks, but I rarely use it myself because as it stands, it has some drawbacks

  • needs agent configuration
  • rewrites bytecode, lucee runs 50% slower when enabled
  • rewritten bytecode has side effects like stack frame overflows

So I decided to dive in and see what could be improved, performance problems with Lucee being my favourite problem…

After some investigation and code review, I had a lightbulb moment, actually Lucee already had half the instrumentation in place via ExecutionLog needed for stepping etc

With that in mind, I deep dived into this, realising that by hijacking this and then adding some additional hooks in Lucee, statically flagged by an ENV var (so zero overhead for PROD when not used), I could achieve the holy grail of Lucee step debugging with almost zero overhead.

Now when enabled without any breakpoints, there is barely any runtime overhead if you don’t have breakpoints set, plus when breakpoints are set, I did some neat performance tricks, like checking the breakpoint line number before the filename (plus other ticks) as the breakpoint polling happens on every block of cfml when enabled.

After I got that all working (which took a while) I decided to look into what else the Debug Adapter Protocol supports

I have also added the following features

  • Streaming console output to the VS Code Debug Console (i.e. SystemOutput()
  • Proper break on Uncaught Exceptions (including exposing the cfcatch scope)
  • Added a new breakpoint(label, condition) BIF to programmatically trigger a breakpoint in the IDE when connected
  • Better request timeout handling, previously with LuceeDebug it just disabled it, now when paused, only the paused thread get temporarily request timeout disabled, stashing the remaining time and restoring after you unpause it

No longer using JWDP which polls, it now uses an event driven approach.

In addition, LuceeDebug now becomes a good old Lucee Extension, no more messing around with java properties, just install the extension and enable via an ENV var

https://luceeserver.atlassian.net/browse/LDEV-1402

Still a WIP and unreleased (still just a proposed PR), Lucee 7.1 is going to fucking rock!

11 Likes

Very compelling improvements, indeed. Thanks for your efforts, and of course thanks to David Rogers (softwareCobbler on GitHub) for his years of work on the original version.

David is truly a pioneer! I’m merely building on his achievements

Anyone interested in using the published version, here is the guide.

For Lucee 7, it currently needs the javax servlet jars to worky work

1 Like

Now that’s it’s all working, I’ve rounded out the DAP client support

Auth enforced, requires Secret token to be set

  • LUCEE_DAP_SECRET env var is required for Lucee and needs to be configured in launch.json

Valid Breakpoint Lines

  • VSCode now shows grey dots in the gutter indicating which lines can have
    breakpoints. No more guessing if a line is executable.

Edit Variables at Runtime

  • Right-click any variable in the Variables panel and change its value while
    paused. Great for testing “what if” scenarios without restarting.

Debug Console

  • Autocomplete: Start typing in the debug console and get suggestions. Type variables.u
    and it suggests userId, userName, etc. Works with nested structs too.
  • CFML evaluation i.e. now(), getApplicationSettings() etc also works

Variable Introspection

  • Select a variable and in addition to dump as json, you’ll see dump getMetaData() - shows all methods,
    properties, and inheritance chain without adding writeDump() to your code.
  • Application scope now shows getApplicationSettings() for current config.

Function Breakpoints

  • Set breakpoints by function name instead of file:line
  • Just type onRequestStart and it breaks wherever that function is defined
  • Wildcards work: on* catches all Application.cfc lifecycle methods
  • Qualified names: User.save only breaks in User.cfc, not Order.save()
  • Conditional: break on processOrder only when arguments.total gt 1000

Full DAP feature set now supported:

  • Line breakpoints with conditions
  • Function breakpoints with conditions + wildcards
  • Exception breakpoints (uncaught)
  • Debug console with autocomplete
  • Variable inspection, modification, and introspection
  • Hover evaluation
  • Valid breakpoint line hints
  • Pause/Continue/Step
6 Likes

5 posts were split to a new topic: How to run multiple LuceeDebug instances on the same server

I don’t know how I missed this but BIG thanks Zack & David for the upgrades and enhancements!

2 Likes

Thanks, will be publishing this soon

3 Likes

This PR has been merged into 7.1.0.45-SNAPSHOT, but use whatever is the latest 7.1 when you read this.

First snapshot builds are published:

For 7.1 / native debugger

Yep, you might have noticed, Lucee 7.0.2 and 7.1 support these lovely Maven GAV co-ordinates for extensions (org.lucee:debugger-extension:3.0.0.0-SNAPSHOT) , the obtuse old GUIDs are still supported

Please refer to the README for setting this up

For Lucee 6.2 and 7.0

Here is the latest agent jar, luceeDebug didn’t support 7 due to jakarta problems. 5.4 might still work, but it’s EOL, YMMV

https://central.sonatype.com/repository/maven-snapshots/org/lucee/debugger-agent/3.0.0.0-SNAPSHOT/debugger-agent-3.0.0.0-20260209.192430-2.jar

2 Likes

Added a quick Docker Demo

First Docker 7.1 images are up too lucee/lucee - Docker Image

The latest build is org.lucee:debugger-extension:3.0.0.1-SNAPSHOT with support for LUCEE_DAP_HOST, which is needed for docker to bind to 0.0.0.0 rather than the default of localhost

3 Likes