Bytecode profiler
On this page
Bytecode profiler
Hot spots tells you which function and line took the time. The Bytecode profiler tells you which expression inside it, how many times it ran, and how long each run took. It uses the code profiler of BoxLang, which counts and times every expression of your templates and classes, and shows the result as a heatmap on your own source.
The Bytecode profiler is a BoxLang+ feature that needs BoxLang 1.19 or newer, for the admin role, and is off until you switch it on. It records the whole server while a session runs, so pages run slower while it does. A session always ends by itself.

Two switches
| Switch | Where | What it does |
|---|---|---|
codeProfilerEnabled | boxlang.json, before the deploy | Templates compiled while it is on carry the counters. It is read when a template is compiled, so set it before the deploy and restart. Left on while nothing records, it costs nothing you can measure. |
| The session | the Bytecode profiler page, or a rule | Recording on or off for the whole server. Instant, no restart. This is the switch the admin uses. |
Switch on the collector too, in Settings, Collection (collectors.profiler.enabled). The page, the rules and the chip in the header exist only while it is on.
{ "codeProfilerEnabled": true }
It needs BoxLang 1.19 or newer. On BoxLang 1.18 or older there is no code profiler, so you do not get this feature: the page shows that this BoxLang does not have the profiler, and Start is hidden. If codeProfilerEnabled is off, the page says so too.
Start a session
Open Bytecode profiler in the Inspect group, pick a length (5 to 60 minutes) and press Start now. A chip with the time left shows in the header of every page, with a Stop button. Use the pages you want to look at. When the time is up, or you press Stop, the session is saved and shows in the list.
Only one session runs at a time. A session also ends when the CPU of the server stays above pauseAboveCpuPct for three seconds (default 85, 0 turns it off), when the collector is switched off, and when the server stops.
Rules
For a problem you cannot reproduce by hand, add a rule: when this request arrives, run the profiler for N minutes. A rule has a path (an exact path, a path that ends with *, or *), a method and the minutes. The request that matches is the first one recorded.
- A rule starts the profiler only when it is off and not resting. While a session runs, rules are ignored, so sessions never overlap.
- After a session, rules rest for
restMinutes(default 30, 0 for no rest). A manual Start ignores the rest. - A rule stays until you remove it. Rules are in the settings file, so servers that share it share the rules.
- To give every server the same rules from a deploy, set
collectors.profiler.rulesinboxlang.json. As with the other lists, it replaces the rules of the console when it is new or changed:
{ "collectors": { "profiler": { "rules": [ { "match": "/checkout", "method": "GET", "minutes": 10 } ] } } }
Read a session
Press a session. It shows how many requests were recorded, the files that ran, and the time in your code, with two views.
- Files: the templates and classes that ran, with the share of the time and a badge.
- Expressions: the widest expressions, with their text, how many times they ran, the time per run and the share.

Press a file or an expression to open its source as a heatmap. A colour bar down the side gives the time of each line, from red (most) to blue (little). The hot expressions are marked inside the line, with the number of times each one ran.

Badges
| Badge | Meaning |
|---|---|
| Source | Text on disk, counted by the profiler. The heatmap works. |
| Compiled | Bytecode on disk, with no source on this server. BoxLang can run templates compiled with boxlang compile, which are bytecode in a file with the same name. In the snapshot of BoxLang I tested they were not counted at all, so there are no expressions and no heatmap for them. |
| Changed | A source file that was edited after the session. The numbers show without the source, because the colours could sit on the wrong text. |
| Not found | The file is not on this server now. |
BxLens does not decompile anything. It reads the source file again and cuts out the lines. Only BoxLang and CFML source files are read, up to 1 MB, secrets in a line are hidden before it leaves the server, and only files that a session holds can be opened.
How to read the numbers
The profiler cuts a line into segments, and the time of a call is charged to the segment before it. So read the line first, and then the segments inside it: in sum += sqr( i ) * 1.0001, the time of sqr shows on the i before it. The times include a small cost of the profiler for every expression, so use them as a ranking and trust the counts. A bracket or a comma is never shown as an expression on its own, but its time is in the line.
From Hot spots
A row of Hot spots that is your own code has an Expressions button. It opens the whole function around the hot line, with the heat, from the newest session that holds the file. The function is found from its name in the source (up to 200 lines). If no session has the file yet, the sheet offers to start one.

Ask Lensy and Share
A session has Ask Lensy, which reads the files and the widest expressions (never a value of the running program) and says what to change first, and Share, which opens the same share sheet as a request or an error: your recipients, your chat channels, or any address. 
Lensy also has a read only tool, bytecodeSessions. See AI tools.

Settings
On the Settings tab of the page, and in boxlang.json under collectors.profiler. All are live.
| Setting | Default | What it does |
|---|---|---|
enabled | false | The collector. Off hides the page and the chip. |
defaultMinutes | 10 | The length a session gets when none is picked. |
maxMinutes | 60 | The longest a session may run, up to 240. |
restMinutes | 30 | The rest after a session before a rule may start another. 0 is none. |
pauseAboveCpuPct | 85 | Stop and save when the CPU of the server stays above this. 0 is never. |
keepDays | 7 | Days a session is kept. |
maxFileKB | 2048 | The largest session. A larger one keeps only the widest expressions of each file and says so. |
rules | none | A list of { match, method, minutes } from boxlang.json. |

Where it is kept
Each session is one document in the disk store (profsess-ID, pruned by age with the other history), and a small index lists the sessions of each server. The last five also stay in memory, so the page works with the disk store off. Servers that share the store see each other's sessions. A session holds at most 60 files and 400 expressions in each.
Safeguards
- Off by default, and never recording without an admin or a rule starting it.
- Every session has an end: its length (at most
maxMinutes), the CPU guard, and the server stopping. - The code of BxLens itself is left out of a session.
- Audit log lines:
profiler.start,profiler.stop,profiler.skip,profiler.rule.add,profiler.rule.removeandprofiler.share. - The profiler is read by name through reflection, so BxLens loads on a BoxLang that does not have it.
Routes
All are for the admin role, need BoxLang+, and the ones that change something follow the CSRF, console.readOnly and console.actions rules.
| Route | What it does |
|---|---|
GET profiler/status | The state, the rules and whether it is ready. |
POST profiler/start, POST profiler/stop | Start (minutes) or stop a session. |
GET/POST profiler/rules, POST profiler/rules/remove | The rules. |
GET profiler/sessions | The sessions of this server and, with BoxLang+ servers, the others. |
GET profiler/sessions/{id}, .../expressions, .../file?path=&function=&line= | One session, its widest expressions, and the heat of a file. |
GET profiler/around?path=&function=&line= | The newest session that holds a file, for Hot spots. |
POST profiler/sessions/{id}/share | Share a session. |