Diagnostics¶
Revenant ships focused tools for answering "what is it actually doing": the welcome setup audit, the exhaustive configuration report, the structured log, and the CPU profiling helper.
-welcome¶
revenant -welcome
The welcome report opens the display and resolves the real widget configuration
but exits before creating a terminal backend or starting a shell. It provides a
short, offline assessment of readability, app-defaults, live X resources,
xrdb, and primary, emoji, and CJK font matches. A bare tiny bitmap setup gets
an explicit scalable-font resource fragment; known distribution families also
get package suggestions for missing tools or fonts.
The report also points to terminal.love as an optional scheme catalog with a live demo and an Xresources export by default. This is a recommendation only: Revenant neither contacts the site nor imports or loads the result.
The advanced Unicode sample is emitted only when stdout is a terminal and
TERM_PROGRAM identifies Revenant, so redirected reports contain no styling
escapes and the probe does not attribute another terminal's rendering to
Revenant. Child shells receive TERM_PROGRAM and TERM_PROGRAM_VERSION from
Revenant's generated build identity. A multiplexer may replace that identity;
run the report directly in Revenant for the visual check. The final plain-text
support block includes only bounded system, backend, renderer, identity, and
font details. Use -report-config when the full resource inventory and
provenance are needed.
-report-config¶
revenant -report-config
revenant -fa Hack -fs 12 -report-config
revenant -report-config > revenant.resources.txt
The report resolves the same Xt resource database startup would use, prints
the effective value of every setting in .Xresources syntax, and exits
without opening a window or starting a shell. On a terminal, values are
coloured by origin โ command line, X resources, compiled default, unset โ
and each is annotated with a one-line explanation and its support status.
Redirected output is plain text; NO_COLOR also disables colour.
It is deliberately exhaustive. After the focused summary it prints:
- every Xt input channel it consulted: the resolved
XTermapp-defaults file, the serverRESOURCE_MANAGERproperty,XENVIRONMENT,XFILESEARCHPATH,XUSERFILESEARCHPATH,XAPPLRESDIR, command-line resources, and the compiled fallbacks; - all 331 resources in xterm patch 411's application, VT100, Tek4014, and VT-font tables, plus 17 behind disabled compile options, each marked supported, accepted but ignored, or unsupported;
- all 131 patterns from patch 411's
XTerm.ad, so widget paths such asmainMenu*redraw*Labelare checked too; - inherited resources of the Xt shell, the VT100 widget, and the Athena menu, scrollbar, and toolbar components;
- your
VT100.translations, re-printed in reusable syntax, with each action checked against the 114 actions xterm registers; - the font section: the ten font-menu slots, actual size order, derived
faceSizevalues, and the fontconfig match forfaceName.
Xt does not retain which file a merged value came from, so values from
app-defaults and xrdb are labelled X resources together; command-line
values are identified separately.
The log¶
Diagnostics go to standard error as
hh:mm:ss subsystem: message
The default threshold is warning, so a healthy ordinary launch writes
nothing. Choose debug, info, warning, or error with -log LEVEL or the
logLevel X resource; the chosen level and everything more severe are
enabled. The xterm-compatible -debug and +debug options remain aliases for
-log debug and -log warning. The legacy Boolean debug resource is used
only when logLevel is unset. On a terminal the timestamp is cyan and the
message coloured by severity; redirected output is plain, and NO_COLOR is
honoured.
With debug on, startup logs the full command line, compiled defaults, the
relevant RESOURCE_MANAGER entries, merged values after precedence, and the
selected backend. Runtime logs cover the PTY lifecycle and byte previews,
terminal effects, key encoding, menus, font choices, scrollbar changes,
resizes, and rendered-frame summaries. Each PTY read previews at most 256
input bytes. Control and non-ASCII bytes use backslash escapes (\\e for
Escape and \\xNN otherwise), and a truncated preview reports the number of
omitted bytes explicitly.
FR-STYLEFAMILY means an explicit Xft boldFont or wideBoldFont entry
resolved outside the already-selected role family. Revenant keeps the family
decision stable and renders that atom with the role's normal instance; the
warning names the slot and both families.
PTY previews can contain application data. Review or redact a debug log before sharing it if the terminal displayed sensitive output.
The command-line level is applied before the display is opened; an X resource takes effect once Xt has resolved the display's resource database. Keeping the default warning threshold also keeps synchronous per-key and per-frame logging out of the rendering path, so enable debug only while reproducing something:
revenant -log debug 2> revenant.log
Font-routing snapshots¶
The font-routing report answers which configured or automatic font actually served each distinct terminal atom. Collection is disabled by default. Enable it, bind the snapshot action, and keep standard error separate from the PTY:
-report-font-routing alone opens a terminal and collects routes; it does not
print a report and exit like -report-config. The snapshot action must be
invoked in that newly opened terminal. It cannot inspect another already
running terminal's font choices.
revenant -report-font-routing \
-xrm 'XTerm*vt100.translations: #override <Key>F12: report-font-routing()' \
2>font-routing.ndjson
The snapshot is written to the terminal's own standard error, which is why the
example redirects it to a file: sharing that stream with the PTY would interleave
JSON with whatever the shell is printing. Collection itself prints nothing and
changes nothing on screen, so a terminal started with -report-font-routing and
never asked for a snapshot looks exactly like any other.
After the relevant text has appeared, press F12. Each output line is an
independent JSON object with "schema": 1. The snapshot contains:
loadrecords for the configured slot chain entries and their effective file, collection index, and variation coordinates;fontslotdistinguishes the eight Xft font-menu sizes. These describe the latest universe-build attempt, including retained old roles after a failed reload;warnrecords with stable codes such asFR-BADPATTERN,FR-DUPROLE,FR-STYLEFAMILY, andFR-UVSMISS;- bounded, first-use
routerecords showing the atom, committed width class, capturing semantic slot, active font-menu slot, winning rung, role identity, routing misses, and any style-to-normal degradation; FR-REPORTBOUNDif more than 4096 distinct keys were observed;FR-LOADBOUNDonly if allocation prevented retaining every load record from the latest build;- one final
snapshotrecord with the generation, effective DPI, collection state, and route-record count.
rung is entry1, entry2, a literal numbered name such as
fallbackFace7, system, or tofu. Tofu records keep file, index, and
coords present with null values. Routing miss codes are cmap, uvs,
shape, ink, budget, truncated, advance, and reserve; style fallback
is a separate
informational object because it never changes the routed family.
Four of those say something different about why a usable-looking font did not
serve, which otherwise look identical on screen:
cmap,uvsandink: the face does not cover the atom, lacks the exact variation sequence, or produces no ink for it under the current paint policy;advance: the face covers the atom and shapes it, but its glyphs are wider than the committed cells and the fallback width rule refused them. This is the one that used to be reported asshape;budget:limitFontsetswas already spent, so the candidate was never opened;reserve: the presentation-aware discovery scan had already appended its maximum number of candidates, so it stopped looking rather than finding nothing.
If a route exceeds its 64 recorded misses, it carries
"missesTruncated": true; routing itself continues normally.
The report is emitted only by the explicit action. There is no guaranteed
exit-time dump. Invoking the action while collection is off writes exactly one
disabled snapshot record, making a missing -report-font-routing visible.
Collection is intended for diagnosis rather than permanent use: it retains up
to 4096 routing records and makes the normally batched one-byte path observable
one atom at a time.
Inspecting one font with HarfBuzz¶
When a sequence looks surprising, inspect the font independently of terminal
routing, cell sizing, and clipping. hb-shape reports the glyph IDs, clusters,
and positions selected by HarfBuzz; hb-view shapes the same text and writes
the font's rasterized result to a PNG:
hb-shape font-fixtures-stage/fonts/NotoColorEmoji.ttf '๐จโ๐ฉโ๐งโ๐ฆ'
hb-view --font-file=font-fixtures-stage/fonts/NotoColorEmoji.ttf \
--output-file=family-current.png '๐จโ๐ฉโ๐งโ๐ฆ'
hb-view --font-file=font-fixtures-stage/fonts/NotoColorEmoji-2.034.ttf \
--output-file=family-2.034.png '๐จโ๐ฉโ๐งโ๐ฆ'
This is the quickest way to distinguish font artwork from a Revenant shaping
or rendering bug. If the standalone PNG has the same design, the terminal is
faithfully painting that font. If its glyph selection or image differs, inspect
Revenant's font: route debug record and clipping next. The historical 2.034
fixture is useful here because it has the earlier colorful family artwork,
while the current pinned Noto release has the achromatic family design
introduced by Google's Emoji 15.1 update.
At debug level, font: route-cache miss identifies the first family decision
for a key and font: route-cache hit identifies reuse. These records include
the base codepoint, active font-menu slot, committed width, and resolved role.
They are performance diagnostics, not the specified NDJSON routing report, and
their prose is not a stable machine-readable interface.
Use tools/font-fixture-info.py FONT... alongside these commands to inspect
format tables, strikes, coverage, and per-probe ink paths. hb-view is a
diagnostic, not an acceptance oracle: terminal tests must still assert committed
cell width, clipping, fallback, and cursor behavior under Xvfb.
Regression helpers¶
Patch-411 font-name deposition¶
tools/t0-facename-oracle.py is a
deposition harness, not a Revenant test. It starts stock patch-411 xterm in a
sterile resource session and isolated fontconfig universe, asks the maintained
face-list, style, slot-order, and governor questions, and records xterm's
answers. A separate Revenant conformance test consumes the reviewed fixture.
The script has a PEP 723 header, so use uv run rather than manually managing
fonttools and wcwidth. The complete record/check procedure and command are
in compat/README.md.
--record writes only under
/tmp; it never overwrites the blessed fixture.
Live xterm font compatibility¶
Use the optional live comparison when font loading, point-size handling, or font-menu sizing changes:
just xterm-font-compat
just xterm-font-compat build-agent-gcc
It opens disposable xterm and Revenant windows on the current $DISPLAY, so
it works directly inside an existing VNC desktop. The helper supplies
tools/xterm-font-compat.Xresources to both programs with command-line -xrm
entries; it neither reads a dotfile nor changes the server's resource database.
The profile deliberately combines faceName: DejaVu Sans Mono:size=11 with a
conflicting faceSize: 16, then checks exact window size, requested size,
resize increments, and base size at the default, largest, and smallest
font-menu positions.
This is an external compatibility oracle, not part of just test, Meson, or
normal CI. It depends on the installed xterm, the live X server and window
manager, wmctrl, and DejaVu Sans Mono. Missing dependencies produce a focused
error; a mismatch leaves both program logs in a named temporary directory.
Five small X11 utilities are built alongside Revenant but not installed. Give
them the top-level window ID reported by the shell: realized log line:
./build-ghostty/xtp-send-font-keys 0x4e00027 # two Shift+KP_Add presses
./build-ghostty/xtp-send-font-keys 0x4e00027 - 4 # four Shift+KP_Subtract
./build-ghostty/xtp-send-font-keys 0x4e00027 insert 1
./build-ghostty/xtp-send-font-keys 0x4e00027 page-up 1
./build-ghostty/xtp-send-wheel 0x4e00027 up 4 # four wheel ticks
./build-ghostty/xtp-send-selection 0x4e00027 10 10 200 60
./build-ghostty/xtp-send-shift-click 0x4e00027 15 15
./build-ghostty/xtp-resize-loop 0x4e00027 # four narrow/wide cycles
Each press should produce one action larger-vt-font and one font: select
record; each wheel tick reports the requested delta and the resulting
viewport {offset, length, total}.
The Shift-click helper sends a Shift-modified motion, Button-1 press, and Button-1 release at one pixel coordinate. It is useful for exercising OSC 8 hover and activation policy without a physical pointer.
The resize helper defaults to four 400x300 to 1000x700 cycles separated by
100 ms. Override the cycle count and delay, or provide all four dimensions:
./build-ghostty/xtp-resize-loop WINDOW-ID CYCLES DELAY-MS
./build-ghostty/xtp-resize-loop WINDOW-ID 10 20 320 240 1200 800
It can also resize in terminal cells. It reads the window's base size and cell
increments from WM_NORMAL_HINTS, so this form is independent of the selected
font and scrollbar width:
./build-ghostty/xtp-resize-loop WINDOW-ID --grid 38 80 24 250
Readline 8.3 wrapped-prompt reproducer¶
The known Readline 8.3 regression needs only one resize across a prompt's wrap boundary; repeated dragging is not required. In one terminal, start the fixed 45-column OSC 133-marked prompt:
just reflow-prompt
Copy the top-level window ID from the shell: realized window=... debug line.
In another terminal, perform one 80-to-38-to-80-column cycle:
just reflow-resize 0x4e00027
Before the resize the cursor is at column 45. Released Readline 8.3 leaves it
at column 37 (over the u in plus) after the window returns to 80 columns:
it has dropped the final eight-byte invisible OSC run from its cursor
calculation. Bash development commit 1e9f5e10b2 should restore column 45.
The fixture supplies its own Bash startup file and therefore does not depend
on the user's Bash files.
An early Ghostty 1.3.1 X11 reproducer disabled Ghostty's automatic shell
integration but used --noprofile without --norc:
PS1="someitnh rellayl logn so you can see" \
GDK_BACKEND=x11 ghostty --shell-integration=none -e bash --noprofile
That observation is tracked as Ghostty
discussion #14026.
The user's .bashrc still loaded in that command and changed the exported
PS1 from a plain prompt to one containing OSC 133 marks. Controlled Revenant
tests now show that a plain long Bash prompt recovers correctly, while both
OSC-marked and ordinary SGR-styled prompts can finish with the cursor inside
the visible prompt. An equivalent native-Wayland manual run did not reproduce,
but the upstream terminal-independent PTY fixture needs only one
TIOCSWINSZ; recheck whether that manual prompt actually wrapped before it
was widened.
See the complete Readline 8.3 wrapped-prompt diagnosis for the terminal/PTY background, exact controls, cursor-offset arithmetic, upstream fix, and local-build validation. Do not add a libghostty or Revenant workaround.
CPU flamegraphs¶
sudo dnf install perf flamegraph # Fedora
tools/flamegraph # default long-output workload
tools/flamegraph -- cat /tmp/big.txt # your own workload
tools/flamegraph --no-build # reuse the compiled profile build
tools/flamegraph builds a separate build-profile/ (debugoptimized, no
LTO, frame pointers kept, matching symbols in libghostty), records with
perf's DWARF unwinder, and writes the raw capture, collapsed stacks, a text
report, and flamegraph.svg under the ignored profiles/ directory. Wider
frames consumed more CPU; these are on-CPU profiles, so time blocked on the
PTY or X server does not appear.
Sampling frequency, unwinder, build directory, and output location are
overridable with XTP_PROFILE_FREQUENCY, XTP_PROFILE_CALL_GRAPH,
XTP_PROFILE_BUILD_DIR, and XTP_PROFILE_OUTPUT_DIR. A permissions error
from perf usually means /proc/sys/kernel/perf_event_paranoid is too
strict.