inspector: clarify page compression memory

The PageList overview mixed structural state with a long list of exact
byte counters, making the compression result difficult to interpret.

Keep the overview focused on grid and scrollback structure, and add a
dedicated compression section before scrollbar details. Present page
states, uncompressed size, encoded ratio, resident estimate, and savings
using readable units and scoped help text.
This commit is contained in:
Mitchell Hashimoto
2026-07-09 09:44:57 -07:00
parent 0fb89f4ffe
commit 6d5dda40db

View File

@@ -4,7 +4,6 @@ const terminal = @import("../../terminal/main.zig");
const pagepkg = @import("../../terminal/page.zig");
const stylepkg = @import("../../terminal/style.zig");
const widgets = @import("../widgets.zig");
const units = @import("../units.zig");
const PageList = terminal.PageList;
@@ -37,6 +36,13 @@ pub const Inspector = struct {
summaryTable(pages);
}
if (cimgui.c.ImGui_CollapsingHeader(
"Page Compression",
cimgui.c.ImGuiTreeNodeFlags_DefaultOpen,
)) {
compressionTable(pages);
}
if (cimgui.c.ImGui_CollapsingHeader(
"Scrollbar & Regions",
cimgui.c.ImGuiTreeNodeFlags_DefaultOpen,
@@ -112,8 +118,6 @@ pub const Inspector = struct {
};
fn summaryTable(pages: *const PageList) void {
const memory = pages.memoryStats();
if (!cimgui.c.ImGui_BeginTable(
"pagelist_summary",
3,
@@ -139,17 +143,6 @@ fn summaryTable(pages: *const PageList) void {
_ = cimgui.c.ImGui_TableSetColumnIndex(2);
cimgui.c.ImGui_Text("%zu", pages.totalPages());
summaryCountRow(
"Resident Pages",
"Pages whose raw backing memory is currently resident.",
memory.resident_pages,
);
summaryCountRow(
"Compressed Pages",
"Pages retained as encoded data with their raw backing memory discarded.",
memory.compressed_pages,
);
cimgui.c.ImGui_TableNextRow();
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
cimgui.c.ImGui_Text("Total Rows");
@@ -158,57 +151,19 @@ fn summaryTable(pages: *const PageList) void {
_ = cimgui.c.ImGui_TableSetColumnIndex(2);
cimgui.c.ImGui_Text("%zu", pages.total_rows);
summaryBytesRow(
"Logical Page Bytes",
"Bytes counted against the scrollback limit. Compression does not change this value.",
pages.page_size,
);
summaryBytesRow(
"Raw Mapping Bytes",
"Total initialized raw page mappings, including discarded mappings retained for restoration.",
memory.raw_bytes,
);
summaryBytesRow(
"Resident Raw Bytes",
"Raw page mapping bytes which have not been discarded.",
memory.resident_raw_bytes,
);
summaryBytesRow(
"Decommitted Raw Bytes",
"Raw page mapping bytes discarded while their virtual addresses remain reserved.",
memory.decommitted_raw_bytes,
);
summaryBytesRow(
"Encoded Bytes",
"Exact encoded storage retained for compressed pages.",
memory.encoded_bytes,
);
summaryBytesRow(
"Estimated Resident Bytes",
"Resident raw allocation backing, including unused pool-item tails, plus encoded storage. Allocator and representation overhead are excluded.",
memory.estimatedResidentBytes(),
);
summaryBytesRow(
"Estimated Savings",
"Decommitted raw mapping bytes minus their replacement encoded storage.",
memory.estimatedSavings(),
);
cimgui.c.ImGui_TableNextRow();
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
cimgui.c.ImGui_Text("Max Size");
cimgui.c.ImGui_Text("Scrollback Limit");
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
widgets.helpMarker(
\\Maximum bytes before pages must be evicated. The total
\\used bytes may be higher due to minimum individual page
\\sizes but the next allocation that would exceed this limit
\\will evict pages from the front of the list to free up space.
\\Maximum uncompressed logical page memory before the oldest
\\history is evicted. Minimum page allocation sizes can make
\\current usage temporarily exceed this value.
);
_ = cimgui.c.ImGui_TableSetColumnIndex(2);
cimgui.c.ImGui_Text(
"%zu KiB",
units.toKibiBytes(pages.maxSize()),
);
var limit_buf: [64]u8 = undefined;
const limit = formatBytes(&limit_buf, pages.maxSize());
cimgui.c.ImGui_TextUnformatted(limit.ptr);
cimgui.c.ImGui_TableNextRow();
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
@@ -217,46 +172,142 @@ fn summaryTable(pages: *const PageList) void {
widgets.helpMarker("Current viewport anchoring mode.");
_ = cimgui.c.ImGui_TableSetColumnIndex(2);
cimgui.c.ImGui_Text("%s", @tagName(pages.viewport).ptr);
cimgui.c.ImGui_TableNextRow();
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
cimgui.c.ImGui_Text("Tracked Pins");
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
widgets.helpMarker("Number of pins tracked for automatic updates.");
_ = cimgui.c.ImGui_TableSetColumnIndex(2);
cimgui.c.ImGui_Text("%zu", pages.countTrackedPins());
}
fn summaryCountRow(
label: [:0]const u8,
help: [:0]const u8,
value: usize,
) void {
cimgui.c.ImGui_TableNextRow();
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
cimgui.c.ImGui_Text("%s", label.ptr);
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
widgets.helpMarker(help);
_ = cimgui.c.ImGui_TableSetColumnIndex(2);
cimgui.c.ImGui_Text("%zu", value);
}
fn compressionTable(pages: *const PageList) void {
const memory = pages.memoryStats();
fn summaryBytesRow(
label: [:0]const u8,
help: [:0]const u8,
bytes: usize,
) void {
cimgui.c.ImGui_TableNextRow();
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
cimgui.c.ImGui_Text("%s", label.ptr);
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
widgets.helpMarker(help);
_ = cimgui.c.ImGui_TableSetColumnIndex(2);
cimgui.c.ImGui_Text(
"%zu bytes (%zu KiB)",
bytes,
units.toKibiBytes(bytes),
if (!cimgui.c.ImGui_BeginTable(
"pagelist_compression",
3,
cimgui.c.ImGuiTableFlags_BordersInnerV |
cimgui.c.ImGuiTableFlags_RowBg |
cimgui.c.ImGuiTableFlags_SizingFixedFit,
)) return;
defer cimgui.c.ImGui_EndTable();
compressionTextRow(
"Platform Support",
"Whether this target can discard physical page memory while retaining " ++
"its virtual address range. The scrollback-compression setting may " ++
"still disable automatic compression on a supported platform.",
if (terminal.compression_enabled) "supported" else "unsupported",
);
var state_buf: [96]u8 = undefined;
const state = std.fmt.bufPrintZ(
&state_buf,
"{d} compressed, {d} resident",
.{ memory.compressed_pages, memory.resident_pages },
) catch unreachable;
compressionTextRow(
"Page States",
"Compressed pages retain an encoded allocation while their raw mapping " ++
"is decommitted. Resident pages still have physical raw backing. " ++
"Active, visible, and recently changed pages are expected to be resident.",
state,
);
var raw_buf: [64]u8 = undefined;
compressionTextRow(
"Uncompressed Size",
"Total size of all raw page mappings. This is the approximate page " ++
"backing memory required if no pages were compressed and is also " ++
"the virtual address space retained across compression.",
formatBytes(&raw_buf, memory.raw_bytes),
);
var encoded_buf: [64]u8 = undefined;
var compressed_raw_buf: [64]u8 = undefined;
var storage_buf: [160]u8 = undefined;
const storage = if (memory.decommitted_raw_bytes > 0) storage: {
const ratio = percentage(
memory.encoded_bytes,
memory.decommitted_raw_bytes,
);
break :storage std.fmt.bufPrintZ(
&storage_buf,
"{s} encoded / {s} raw ({d:.1}%)",
.{
formatBytes(&encoded_buf, memory.encoded_bytes),
formatBytes(
&compressed_raw_buf,
memory.decommitted_raw_bytes,
),
ratio,
},
) catch unreachable;
} else "none";
compressionTextRow(
"Compressed Storage",
"Encoded allocation size compared with the original raw size of only " ++
"the compressed pages. The percentage is the compression ratio, " ++
"so smaller is better. Raw physical pages have been discarded.",
storage,
);
var resident_buf: [64]u8 = undefined;
compressionTextRow(
"Estimated Resident",
"Physical page backing estimated to remain after compression: resident " ++
"raw allocations, including unused pool tails, plus encoded storage. " ++
"Node and allocator metadata and unrelated terminal memory are excluded.",
formatBytes(&resident_buf, memory.estimatedResidentBytes()),
);
var savings_bytes_buf: [64]u8 = undefined;
var savings_buf: [128]u8 = undefined;
const savings = memory.estimatedSavings();
const savings_text = std.fmt.bufPrintZ(
&savings_buf,
"{s} ({d:.1}% of raw page memory)",
.{
formatBytes(&savings_bytes_buf, savings),
percentage(savings, memory.raw_bytes),
},
) catch unreachable;
compressionTextRow(
"Estimated Savings",
"Physical page backing avoided across the complete PageList. This is " ++
"decommitted raw memory minus replacement encoded storage and is " ++
"not a measurement of total process RSS.",
savings_text,
);
}
fn compressionTextRow(
label: [:0]const u8,
help: [:0]const u8,
value: [:0]const u8,
) void {
cimgui.c.ImGui_TableNextRow();
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
cimgui.c.ImGui_Text("%s", label.ptr);
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
widgets.helpMarker(help);
_ = cimgui.c.ImGui_TableSetColumnIndex(2);
cimgui.c.ImGui_TextUnformatted(value.ptr);
}
fn formatBytes(buf: []u8, bytes: usize) [:0]const u8 {
if (bytes >= 1024 * 1024) {
const value: f64 = @as(f64, @floatFromInt(bytes)) / (1024 * 1024);
return std.fmt.bufPrintZ(buf, "{d:.2} MiB", .{value}) catch unreachable;
}
if (bytes >= 1024) {
const value: f64 = @as(f64, @floatFromInt(bytes)) / 1024;
return std.fmt.bufPrintZ(buf, "{d:.1} KiB", .{value}) catch unreachable;
}
return std.fmt.bufPrintZ(buf, "{d} B", .{bytes}) catch unreachable;
}
fn percentage(numerator: usize, denominator: usize) f64 {
if (denominator == 0) return 0;
return 100 *
@as(f64, @floatFromInt(numerator)) /
@as(f64, @floatFromInt(denominator));
}
fn scrollbarInfo(pages: *PageList) void {