Skip to main content

Statistics & reports

The ziskemu cost report, emitted either as a full text report (--stats) or a compact, CI-friendly SDK report (--sdk). Explains the summary's cost-distribution categories, the per-opcode breakdown, and the SDK report's optional sections.

Profiling is ziskemu's main analysis feature. After a run it can emit a cost report, either as a full text report (--stats) or as a compact SDK report (--sdk). Adding symbols (--read-symbols) unlocks the function-level views in Function analysis. Profiling works on any ELF that carries symbols, including optimized release builds — no instrumentation is required.

The cost report (--stats)​

-X / --stats prints overall execution statistics plus a per-opcode breakdown.

ziskemu -e program.elf -i input.bin -X

The report begins with a summary:

REPORT
----------------------------------------
STEPS 92,875,129

COST DISTRIBUTION COST %
------------------------------------------------
BASE 293,601,280 2.57%
MAIN 6,315,508,772 55.22%
OPCODES 1,334,639,984 11.67%
PRECOMPILES 2,565,960,716 22.43%
MEMORY 927,932,629 8.11%

TOTAL 11,437,643,381 100.00%

FROPS 963,440,253 8.42%
RAM USAGE 18,465,008 3.47%
SectionMeaning
STEPSProcessor cycles / instructions executed — a proxy for execution length.
BASEFixed overhead (tables, range checks) independent of program logic.
MAINProcessor cost without per-operation cost. Proportional to STEPS.
OPCODESSimple arithmetic/logic operations on 64-bit values.
PRECOMPILESComplex operations whose operands exceed 64 bits (256-bit math, EC ops, Keccak, DMA).
MEMORYDirect memory reads/writes plus unaligned-access state machines.
TOTALSum of all categories; each row shows its share of the total.
FROPSFrequently-used operations that are pre-calculated; the figure is the cost saved by that optimization.
RAM USAGEMemory used (reported only with the default bump allocator).

Below the summary, COST BY OPCODE and FROPS BY OPCODE tables list per-operation COUNT, COST, and percentage, with the four most expensive operations marked #1–#4. Rows are ordered by cost, highest first.

Optional sections​

The cost report shows more when asked. Each of these adds a section and requires -X:

FlagDefaultAdds
--mem-statsfalseMEM COST BY TYPE — memory cost split by access type.
--mem-full-statsfalseDETAILED MEM COST — the fully itemized memory breakdown.
--opcode-breakdownfalsePer-opcode breakdown of the cheaper-to-prove variants under each opcode, e.g. add split into add_hi0 / add_hif.
--pattern-analysisfalseOPERAND PATTERN ANALYSIS — per base opcode, the operand categories covering a significant share of its operations.
--log-costly-unalignedfalseA log line per costly unaligned memory access (a 4-byte or 8-byte access crossing a word boundary), with pc, function, address, offset, and value.

--pattern-analysis only reports a category when it covers more than 10% of that opcode's operations and at least 4,000 operations in absolute terms, so rare patterns do not crowd the section.

Ordering and FROPS tables​

FlagDefaultDescription
--sort-by-unitsfalseSort the opcode / precompile / FROPS sections by operation count instead of by cost. Requires -X.
--legacy-fropsfalseCompute FROPS coverage against the legacy FROPS tables (the snapshot taken before the FROPS overhaul), so a new FROPS version can be measured against the previous one.

Precompile duplicates​

--duplicates looks for precompile calls that repeat the same input content — same input, same output — and reports how much cost went into those repeats. That figure is the saving a deduplication cache would buy. Every precompile except DMA is covered.

ziskemu -e program.elf -i input.bin -X --duplicates
PRECOMPILE TOTAL UNIQUE % DUP % MAX DUP DUP COST %
-----------------------------------------------------------------------------------------------------------------
keccak 124,500 31,120 25.00% 93,380 75.00% 4,096 1,893,440,000 16.55%

UNIQUE counts distinct inputs, DUP the calls that repeated one, MAX DUP the most repeated single input, and DUP COST the cost attributable to the repeats. Rows are ordered by DUP COST, and a TOTAL row is added when more than one precompile appears.

FlagDefaultDescription
--duplicatesfalseRun the duplicate analysis. Requires -X.
--duplicates-ops <OPS>—Restrict the analysis to these precompiles, comma-separated by opcode name (e.g. keccak,sha256,arith256). All supported precompiles when omitted.
--duplicates-detailfalseAdd the per-precompile call-path detail: where the duplicates come from, most costly first. Requires -S.
--duplicates-depth <N>4How many innermost call-stack frames (leaf plus callers) to record per call for the detail report.
# Which call paths are re-hashing the same data?
ziskemu -e program.elf -i input.bin -X -S \
--duplicates --duplicates-ops keccak --duplicates-detail --duplicates-depth 6

Register step distance​

A register that is not touched for a long stretch still has to hold its value across that whole gap, and the proof has to carry it. These flags measure those gaps.

--reg-step-distance prints a REGISTER STEP DISTANCE table, one row per accessed register:

ziskemu -e program.elf -i input.bin -X --reg-step-distance
ColumnMeaning
ACCESSESNumber of times the register was accessed.
MAX DISTLargest gap, in steps, between two consecutive accesses.
RATIOMAX DIST as a fraction of the limit.
MAX FDISTThe same maximum, assuming a periodic flush touches every register.
FRATIOMAX FDIST as a fraction of the limit.
>=80% / >=LIMIT / >=2xHow many gaps reached 80%, 100%, and 200% of the limit.
FlagDefaultDescription
--reg-step-distancefalseEmit the table above. Requires -X.
--reg-step-limit-bits <BITS>22The distance limit is 2^BITS steps — 4194304 by default. Sets the reference for RATIO, FRATIO, and the three gap counters.
--reg-step-flush-bits <BITS>22Flush period: every 2^BITS steps every register is assumed to be forcibly accessed. Those forced accesses are not counted in ACCESSES; they only feed MAX FDIST / FRATIO.

--reg-step-check is the fast version. It splits the execution into instances of 2^--reg-step-flush-bits steps, each starting with a flush that accesses every register, and reports how many instances contain at least one gap over the limit. It runs on the fast emulation path and does not need -X, so a whole program can be checked quickly:

ziskemu -e program.elf -i input.bin --reg-step-check
REGISTER STEP CHECK: OK, no instance over the 4194304 steps limit (limit=4194304 instance=2^22=4194304 max_dist=1839221)

When the check fails, the verdict becomes EXCEEDED in <n> of <total> instances and a second line names the registers involved with their worst distance.

Snapshots and comparison​

A snapshot is an aggregate view of a run — cost distribution, RAM/ROM usage, memory tables, base opcodes, precompiles, and FROPS — written as a CSV. There is no per-function detail in it, so snapshots stay small and are meant to be committed, kept in CI artifacts, and diffed.

FlagDefaultDescription
--save-stats <FILE>—Write this run's snapshot to FILE. Requires -X.
--ref-stats <FILE>—Load a snapshot saved earlier and print a reference-vs-current comparison. Requires -X.
--diff-stats <OLD> <NEW>—Compare two saved snapshots and print the comparison without running the emulator — no ELF or input needed. OLD is the reference, so deltas are NEW - OLD.
# Record a baseline
ziskemu -e program.elf -i input.bin -X --save-stats baseline.csv

# Measure a change against it
ziskemu -e program.elf -i input.bin -X --ref-stats baseline.csv

# Or compare two recorded runs later, with no run at all
ziskemu --diff-stats baseline.csv candidate.csv

The comparison covers the cost distribution, base opcodes, precompiles, and FROPS.

Comparison output​

FlagDefaultDescription
--diff-format <FORMAT>colorcolor for the human-readable, colour-coded view; csv for the plain separator-delimited view, for scripting.
--color <WHEN>autoauto colours only when stdout is a terminal; always and never force the choice.
--legacy-displayfalseEquivalent to --diff-format csv. Also implied by --sdk.
--csv-separator <SEP>,Field separator for --save-stats and the csv comparison view. A single character.
note

Snapshots are read back with the separator auto-detected from the file, so a snapshot saved with a non-default --csv-separator still compares correctly without repeating the flag.

HTML reports​

--html-report renders the statistics as a standalone HTML page instead of a CSV. It collects the statistics itself, so -X is not required — though the run still prints the text report to stdout.

# Single run -> report.html
ziskemu -e program.elf -i input.bin --html-report

# Explicit path
ziskemu -e program.elf -i input.bin --html-report cost.html

# Comparison against a baseline
ziskemu -e program.elf -i input.bin --ref-stats baseline.csv --html-report diff.html

# Comparison of two snapshots, without running
ziskemu --diff-stats baseline.csv candidate.csv --html-report diff.html
FlagDefaultDescription
--html-report [FILE]report.htmlRender the page to FILE. The value is optional; passing the flag alone writes report.html in the current directory.

The snapshot content is passed straight to the renderer, so no intermediate CSV file is written unless --save-stats also asked for one. When a reference is supplied — with --ref-stats, or with --diff-stats, which needs no run — the comparison report is rendered instead of the single-run one.

The page is self-contained (inline CSS, inline logo, no external requests) and organized into collapsible sections: cost distribution, cost by base opcode, cost by precompiled opcode, FROPS by opcode, the memory tables, and memory offsets. Opcode tables can be re-sorted by count or by cost in the browser.

The report binary​

The ZisK source tree also builds a small report binary that renders snapshot CSVs that already exist:

# From a ZisK checkout
cargo run --bin report -- stats.csv # single-run report
cargo run --bin report -- baseline.csv new.csv # comparison (first file is the reference)
warning

report always writes to emulator/src/report/report.html inside the ZisK source tree — the destination is baked in at compile time and ignores the directory you run it from. It is also not part of a ZisK release: ziskup installs cargo-zisk, cargo-zisk-dev, and ziskemu only. Use ziskemu --diff-stats <OLD> <NEW> --html-report <FILE> instead unless you are working inside the repository.

SDK report mode (--sdk)​

--sdk produces a compact, boxed summary ideal for CI/CD and quick checks. By default it shows only the summary; request extra sections explicitly.

ziskemu -e program.elf -i input.bin --sdk
FlagDefaultDescription
--sdkfalseEmit the compact SDK report (summary only unless a section flag is added).
--opcodesfalseAdd the opcode-distribution section.
--top-functionsfalseAdd the top-cost-functions section (requires --read-symbols).
--profile-tagsfalseAdd the profile-tags section (developer-inserted measurement markers).
--sdk-width <WIDTH>120Width, in characters, of the SDK report.
# Summary + opcodes + top functions
ziskemu -e program.elf -i input.bin --sdk --opcodes --top-functions -S

For per-function rankings, the PC histogram, call tracking, and the Firefox Profiler export, continue to Function analysis.