diff --git a/src/inspector/widgets/pagelist.zig b/src/inspector/widgets/pagelist.zig index fac6afbed..7e0012896 100644 --- a/src/inspector/widgets/pagelist.zig +++ b/src/inspector/widgets/pagelist.zig @@ -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 {