ek9_debug_observe
Runs the current debug session until a condition holds or the budget bites, reading the watched paths at every hit of the given lines; answers stoppedBecause, exact hits and the rows.
When to use it
'Run until total goes unset and show me how it got there' is one call: {points:[{file, line}], watch:['total','i'], until:{kind:'setCheck', path:'total', negated:true}, budget:{maxHits, maxWallClockMs}}. 'How did total become wrong?' is one call too, while stopped: {changesOf:['total'], budget} answers every change with before, after and the line that made it.
When not to use it
- Stopping at a line to look around - set a breakpoint with
ek9_debug_sessionorek9_debug_control. - Recording every write in a hot loop - that is the compiled-in probes' job, not a JDWP observation.
Details
Starts the debuggee if it waits at its start, and resumes any stopped thread. until is optional (without it the observation runs to the budget or the program's end) and is {kind:'hits', count} or a v1 condition: {kind:'comparison', path, op (== <> < <= > >=), literal} or {kind:'setCheck', path, negated}. The literal's JSON type matters: a number compares with an Integer or Float, a string with a String or Character, true/false with a Boolean (3 and '3' differ). A comparison with an unset value is not met; a condition that cannot be judged ends the observation with the reason. budget is required: maxHits and maxWallClockMs (at most 95000). It ends on until, the budget, a user breakpoint (which still stops) or the program ending - stoppedBecause 'ended' when it finished with exit code 0, 'died' when it failed; ending at a hit leaves that thread stopped there. A point on a line with no code is refused, with the nearest lines that have code. hits is exact; rows keep the values of the first and last 10 hits unless keep says otherwise (at most 100 each), in the compact form unless format is 'records'.
Arguments
| Argument | Type | Required | What it is |
|---|---|---|---|
budget | object | yes | required: {maxHits, maxWallClockMs} (maxWallClockMs at most 95000). |
changesOf | array of string | paths read where the program is stopped now ('total', 'counter'): every change to each is recorded - before, after, the line that made it, the thread - and never stops. With changesOf, points may be empty; the observation then ends with the budget or the program. | |
format | string: compact | records | compact: each row is {tick, values:[...]} in watch order, with what every row shares said once (a path unreadable at every hit is in 'unreadable' and 'absent' in the rows, one unreadable at some hits is {error}); records: each row is the full runtime-journal record (SPEC 3.2) with isSet and the object identity #N of every value, or {path, error} for a path it could not read. | |
keep | object | how many of the first and last hits keep their values (default {first:10, last:10}, each at most 100); hits stays exact whatever is kept. | |
points | array of object | EK9 source lines, each {file:'main.ek9', line:12}; the file is its path from projectDir ('orders/main.ek9'), or just its name when only one source has it. | |
until | object | optional - without it the observation runs until the budget bites or the program ends. {kind:'hits', count} or a condition: {kind:'comparison', path, op, literal} or {kind:'setCheck', path, negated}. literal is typed JSON: a number (100) for an Integer or Float, a string ('x') for a String or Character, true/false for a Boolean. | |
watch | array of string | paths to read at every hit: 'total', 'rex.legs', 'names[0]', 'scores["bob"]', 'total?'. |
Recorded calls
Real calls, recorded against small projects and checked on every build - a changed answer fails it.
Until a condition
Request
{
"budget" : {
"maxHits" : 100,
"maxWallClockMs" : 30000
},
"points" : [ {
"file" : "countdown.ek9",
"line" : 10
} ],
"until" : {
"kind" : "comparison",
"literal" : 4,
"op" : "==",
"path" : "i"
},
"watch" : [ "i", "total" ]
}
Response
{
"completeness" : "hits are exact; values kept for every hit",
"elapsedMs" : "<timing>",
"hits" : 4,
"meta" : {
"serverMs" : "<timing>"
},
"msPerHit" : "<timing>",
"nextStep" : "ek9_debug_inspect {action:'stop'} to look at the thread stopped at the last hit",
"rows" : [ {
"tick" : 1,
"values" : [ "1", "0" ]
}, {
"tick" : 2,
"values" : [ "2", "1" ]
}, {
"tick" : 3,
"values" : [ "3", "3" ]
}, {
"tick" : 4,
"values" : [ "4", "6" ]
} ],
"runId" : "<timing>",
"runState" : "stopped",
...
Cut short here: 9 more lines. The whole recorded call.