Skip to content

Debugging & Benchmarking

Introduced in v1.18, with a full stepping debugger added in v1.19. A debugger you can drop anywhere, and friendly block timing — both built into the language, zero dependencies.


breakpoint — Stepping Debugger

Drop breakpoint anywhere. When execution reaches it in a terminal, the program pauses into a scoped REPL:

def price(qty)
  total = qty * 4.5
  breakpoint
  total
end

price(10)
🧟 breakpoint — pricing.fk:3
   ──▶ 3 │   breakpoint
   (c)ontinue · s(tep) · n(ext) · stack · vars · where · exit · or type any Frankie expression

(fkdb) vars
  qty = 10
  total = 45.0
(fkdb) total * 2
  => 90.0
(fkdb) c

Commands

Command Effect
c Continue execution
s Step — execute one line, stepping into calls (v1.19)
n Next — one line, stepping over calls (v1.19)
stack Show the Frankie call stack, innermost first (v1.19)
vars List local variables in the paused scope (clean — no stdlib noise)
where Show the current file and line
exit Abort the program
anything else Evaluated as a Frankie expression against the paused scope

Stepping is line-accurate everywhere thanks to the compiler's line maps — including inside required files.

Debug from the very first line (v1.19)

frankiec run --debug app.fk

Breaks before the first statement runs, so you can step through a program you've never read — no need to add a breakpoint line yourself.

Conditional breakpoints

Postfix conditions work:

breakpoint if qty > 100

CI-safe by design

When stdin isn't a terminal (CI, pipes, frankiec test in a pipeline), breakpoints print a notice and are skipped — your test runs never hang.

breakpoint is a contextual keyword — a variable named breakpoint keeps working.


benchmark — Friendly Timing

Times a block, prints the elapsed time, and returns the elapsed milliseconds:

ms = benchmark "crunch" do
  heavy_work()
end
# ⏱  crunch: 132.4ms

benchmark do        # label optional
  quick_thing()
end

Because it returns the elapsed ms, you can assert on it in tests or collect timings in a vector:

timings = [100, 1000, 10000].map do |n|
  benchmark "n=#{n}" do
    (1..n).to_vec.map do |x| x * x end
  end
end
puts timings

benchmark is a contextual keyword — a variable named benchmark keeps working.


Quick Reference

Syntax Description
breakpoint Pause into a scoped debug REPL (skipped when stdin isn't a tty)
breakpoint if cond Conditional breakpoint
frankiec run --debug app.fk Break at the first line automatically (v1.19)
(fkdb) s / n / stack Step in / step over / show call stack (v1.19)
benchmark "label" do ... end Time a block — prints ⏱ label: 12.3ms, returns elapsed ms
benchmark do ... end Same, without a label