Skip to documentation
SLOP

tiny.profiling.report.site

Reference tiny.profiling report site

Defined in report.

API (7)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callsreportsite
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsreport.serve.Stateregeneratetest; no linksrc.profiling.report.sitetest: site generates a navigable stat...test; no linksrc.profiling.report.sitetest: site generates an empty shell w...report.flamesourcesreport.memoryloadreport.modelloadreport.pageflameFilereport.pageflamePage+6 morereport.sitegenerate
Static calls · unresolved targets: 0 · external targets: 4.

Source: src/profiling/report/root.zig:8

zig
pub const site = @import("site.zig");

Source: src/profiling/report/site.zig

zig
const std = @import("std");const sys = @import("sys");const zen = @import("zen");const profiling = @import("../root.zig");const flame = @import("root.zig").flame;const model = @import("root.zig").model;const page = @import("root.zig").page;pub const style_name = "profile.css";pub const site_title = "tiny profiling";pub const Options = struct {    profiling_dir: []const u8 = profiling.record.default_output_dir,    output_dir: ?[]const u8 = null,};pub const Result = struct {    runs: usize,    pages: usize,    output_dir: []const u8,    index_path: []const u8,};pub const layout =    "<!doctype html>\n" ++    "<html lang=\"en\">\n" ++    "<head>\n" ++    "<meta charset=\"utf-8\">\n" ++    "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n" ++    "<title>{{title}} - {{site.title}}</title>\n" ++    "<meta name=\"description\" content=\"{{description}}\">\n" ++    "<link rel=\"stylesheet\" href=\"{{style.path}}\">\n" ++    "</head>\n" ++    "<body>\n" ++    "<main>\n" ++    "{{content}}\n" ++    "</main>\n" ++    "<script>\n" ++    "document.addEventListener('click', function (event) {\n" ++    "  var th = event.target.closest('th[data-sort]');\n" ++    "  if (!th) return;\n" ++    "  var table = th.closest('table');\n" ++    "  var body = table.tBodies[0];\n" ++    "  if (!body) return;\n" ++    "  var cells = Array.prototype.slice.call(th.parentNode.children);\n" ++    "  var column = cells.indexOf(th);\n" ++    "  var direction = th.getAttribute('data-dir') === 'desc' ? 1 : -1;\n" ++    "  cells.forEach(function (cell) { cell.removeAttribute('data-dir'); });\n" ++    "  th.setAttribute('data-dir', direction === 1 ? 'asc' : 'desc');\n" ++    "  var rows = Array.prototype.slice.call(body.rows);\n" ++    "  rows.sort(function (left, right) {\n" ++    "    var a = left.cells[column], b = right.cells[column];\n" ++    "    var av = a.getAttribute('data-v') || a.textContent.trim();\n" ++    "    var bv = b.getAttribute('data-v') || b.textContent.trim();\n" ++    "    var an = parseFloat(av), bn = parseFloat(bv);\n" ++    "    if (isNaN(an) || isNaN(bn)) return av.localeCompare(bv) * direction;\n" ++    "    return (an - bn) * direction;\n" ++    "  });\n" ++    "  rows.forEach(function (row) { body.appendChild(row); });\n" ++    "});\n" ++    "</script>\n" ++    "</body>\n" ++    "</html>\n";pub const style =    ":root{color-scheme:light;font-family:Inter,ui-sans-serif,system-ui,sans-serif;line-height:1.5;background:#f7f7f4;color:#20231f;" ++    "--accent:#0b6bcb;--ok:#2f6f4e;--bad:#b3261e;--warn:#8a5a00;--muted:#6b7264;--edge:#d6d6ce;--panel:#fffefb}\n" ++    "body{margin:0}\n" ++    "main{max-width:1160px;margin:0 auto;padding:2.2rem 1.25rem 4rem}\n" ++    "h1{font-size:1.7rem;line-height:1.15;margin:.4rem 0 .6rem}\n" ++    "h2{font-size:1.15rem;margin:2rem 0 .4rem}\n" ++    "h3{font-size:.98rem;margin:1.2rem 0 .35rem}\n" ++    "a{color:var(--accent);text-decoration:none}\n" ++    "a:hover{text-decoration:underline}\n" ++    "code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.92em}\n" ++    ".crumbs{font-size:.85rem;color:var(--muted)}\n" ++    ".crumbs a{color:var(--muted)}\n" ++    ".lede{color:#3a4036;margin:.1rem 0 1rem}\n" ++    ".hint{font-size:.85rem;color:var(--muted);margin:.15rem 0 .6rem}\n" ++    ".muted{color:var(--muted)}\n" ++    ".verdict{padding:.6rem .8rem;border:1px solid var(--edge);background:var(--panel)}\n" ++    ".verdict.ok{border-color:#bcd9c6;background:#eef7f0}\n" ++    ".verdict.warn{border-color:#ecd9a8;background:#fdf6e3}\n" ++    ".meta{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,max-content));gap:.4rem 2rem;margin:.6rem 0 1rem;padding:.6rem .8rem;border:1px solid var(--edge);background:var(--panel)}\n" ++    ".meta div{min-width:0}\n" ++    ".meta dt{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}\n" ++    ".meta dd{margin:0;font-variant-numeric:tabular-nums}\n" ++    "table.data{border-collapse:collapse;width:100%;margin:.4rem 0 1rem;font-size:.9rem;background:var(--panel)}\n" ++    "table.data th,table.data td{border:1px solid var(--edge);padding:.35rem .55rem;text-align:left;vertical-align:middle}\n" ++    "table.data thead th{background:#efefe9;font-weight:600;white-space:nowrap}\n" ++    "table.data th[data-sort]{cursor:pointer;user-select:none}\n" ++    "table.data th[data-sort]:after{content:' \\2195';color:#b0b4a8;font-size:.75em}\n" ++    "table.data th[data-dir=desc]:after{content:' \\2193';color:var(--accent)}\n" ++    "table.data th[data-dir=asc]:after{content:' \\2191';color:var(--accent)}\n" ++    "td.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}\n" ++    "td.label{max-width:26rem;overflow-wrap:anywhere}\n" ++    "td.fail{color:var(--bad);font-weight:700}\n" ++    ".bar-cell{position:relative;min-width:7rem}\n" ++    ".cellbar{position:absolute;left:0;top:0;bottom:0;background:rgba(11,107,203,.14)}\n" ++    ".cellval{position:relative}\n" ++    ".badge{display:inline-block;padding:.05rem .45rem;border-radius:99px;font-size:.72rem;font-weight:600;vertical-align:middle}\n" ++    ".badge-passed{background:#e3f2e7;color:var(--ok)}\n" ++    ".badge-failed{background:#fbe4e2;color:var(--bad)}\n" ++    ".badge-dirty{background:#fdf0d4;color:var(--warn)}\n" ++    ".delta-up{color:var(--bad);font-weight:600}\n" ++    ".delta-down{color:var(--ok);font-weight:600}\n" ++    ".delta-flat{color:var(--muted)}\n" ++    ".cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:.8rem}\n" ++    ".card{border:1px solid var(--edge);background:var(--panel);padding:.7rem .8rem}\n" ++    ".card h3{margin:0 0 .1rem;font-size:.95rem}\n" ++    ".card-meta{margin:.05rem 0 .45rem;font-size:.8rem;color:var(--muted)}\n" ++    ".trend{display:flex;align-items:center;gap:.5rem;margin:.15rem 0}\n" ++    ".trend-label{font-size:.7rem;color:var(--muted);width:2.2rem;text-align:right;text-transform:uppercase;letter-spacing:.05em}\n" ++    "svg{display:block;max-width:100%}\n" ++    "td svg{display:inline-block;vertical-align:middle}\n" ++    ".chart-line{fill:none;stroke:var(--accent);stroke-width:1.6}\n" ++    ".chart-band{fill:rgba(11,107,203,.13);stroke:none}\n" ++    ".chart-dot{fill:var(--accent)}\n" ++    ".chart-dot:hover{fill:#063e78}\n" ++    ".chart-sample{fill:rgba(32,35,31,.42)}\n" ++    ".chart-median{stroke:var(--bad);stroke-width:1.4}\n" ++    ".chart-grid{stroke:#e4e4dc;stroke-width:1}\n" ++    ".chart-tick,.chart-empty{fill:var(--muted);font-size:9.5px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n" ++    "details.log{margin:.8rem 0}\n" ++    "details.log summary{cursor:pointer;color:var(--muted)}\n" ++    "details.log pre{overflow:auto;max-height:26rem;padding:.8rem;background:#1f241f;color:#f4f6ef;font-size:.8rem}\n" ++    "svg.flame{width:100%;height:auto;background:var(--panel);border:1px solid var(--edge)}\n" ++    ".ff{cursor:pointer}\n" ++    ".ff rect{stroke:#f7f7f4;stroke-width:.4}\n" ++    ".ff text{font:11px ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;fill:#1c1e1a;pointer-events:none}\n";pub fn generate(allocator: std.mem.Allocator, options: Options) !Result {    const site = try model.load(allocator, options.profiling_dir);    const output_dir = options.output_dir orelse try std.fs.path.join(allocator, &.{ options.profiling_dir, "site" });    sys.fs.deleteTree(output_dir) catch {};    try sys.fs.createDirPath(output_dir);    const style_path = try std.fs.path.join(allocator, &.{ output_dir, style_name });    try sys.fs.writeFile(style_path, style);    var pages: usize = 0;    const index_path = try std.fs.path.join(allocator, &.{ output_dir, "index.html" });    try writePage(allocator, index_path, style_name, .{        .title = "profiling",        .description = "Monorepo profiling runs, trends, and recorded analyses.",        .content = try page.index(allocator, site),    });    pages += 1;    for (site.runs) |*entry| {        const run_dir = try std.fs.path.join(allocator, &.{ output_dir, "runs", entry.dir });        const workloads_dir = try std.fs.path.join(allocator, &.{ run_dir, "workloads" });        try sys.fs.createDirPath(workloads_dir);        const memory_report = try profiling.report.memory.load(allocator, .{ .input = entry.root, .top = 25 });        const has_memory = memory_report.totals.traced_workloads > 0;        const run_path = try std.fs.path.join(allocator, &.{ run_dir, "index.html" });        try writePage(allocator, run_path, "../../" ++ style_name, .{            .title = entry.manifest.run_id,            .description = "Profiling run workloads and recorded analysis.",            .content = try page.run(allocator, entry, has_memory),        });        pages += 1;        if (has_memory) {            const memory_path = try std.fs.path.join(allocator, &.{ run_dir, page.memory_file });            const memory_title = try std.fmt.allocPrint(allocator, "{s} memory", .{entry.manifest.run_id});            try writePage(allocator, memory_path, "../../" ++ style_name, .{                .title = memory_title,                .description = "Memory accountability: traces reconciled against RSS.",                .content = try page.memoryPage(allocator, entry, memory_report),            });            pages += 1;        }        for (entry.run.workloads) |*workload_row| {            const flame_sources = try flame.sources(allocator, workload_row);            const file = try page.workloadFile(allocator, workload_row.name);            const workload_path = try std.fs.path.join(allocator, &.{ workloads_dir, file });            const title = try std.fmt.allocPrint(allocator, "{s} - {s}", .{ workload_row.name, entry.manifest.run_id });            try writePage(allocator, workload_path, "../../../" ++ style_name, .{                .title = title,                .description = "Workload metrics, distributions, and logs.",                .content = try page.workload(allocator, site, entry, workload_row, flame_sources),            });            pages += 1;            if (flame_sources.any()) {                const flame_file = try page.flameFile(allocator, workload_row.name);                const flame_path = try std.fs.path.join(allocator, &.{ workloads_dir, flame_file });                const flame_title = try std.fmt.allocPrint(allocator, "{s} flames - {s}", .{ workload_row.name, entry.manifest.run_id });                try writePage(allocator, flame_path, "../../../" ++ style_name, .{                    .title = flame_title,                    .description = "On-CPU and off-CPU flame graphs.",                    .content = try page.flamePage(allocator, site, entry, workload_row, flame_sources),                });                pages += 1;            }        }    }    return .{        .runs = site.runs.len,        .pages = pages,        .output_dir = output_dir,        .index_path = index_path,    };}const PageContent = struct {    title: []const u8,    description: []const u8,    content: []const u8,};fn writePage(allocator: std.mem.Allocator, path: []const u8, style_rel: []const u8, content: PageContent) !void {    const theme = zen.Theme{        .site_title = site_title,        .layout = layout,        .style_path = style_rel,        .style = style,    };    const theme_page = zen.Page{        .title = content.title,        .description = content.description,        .content = content.content,    };    const plan = try zen.ThemeRenderPlan.inspect(        theme,        theme_page,        zen.default_theme_render_limits,    );    var storage = try zen.ThemeRenderStorage.init(allocator, plan.exactLimits());    defer storage.deinit(allocator);    storage.activate();    const html = try zen.renderPage(&storage, theme, theme_page);    defer storage.reset();    try sys.fs.writeFile(path, html);}test "site generates a navigable static tree" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const profiling_dir = ".zig-cache/profile-web-site-test";    const output_dir = ".zig-cache/profile-web-site-test-out";    defer sys.fs.deleteTree(profiling_dir) catch {};    defer sys.fs.deleteTree(output_dir) catch {};    try model.writeTestFixture(allocator, profiling_dir);    const result = try generate(allocator, .{ .profiling_dir = profiling_dir, .output_dir = output_dir });    try std.testing.expectEqual(@as(usize, 2), result.runs);    try std.testing.expectEqual(@as(usize, 9), result.pages);    const memory_path = try std.fs.path.join(allocator, &.{ output_dir, "runs", "run-200-bbb", "memory.html" });    const memory_html = try sys.fs.readFileAlloc(allocator, memory_path, 8 * 1024 * 1024);    try std.testing.expect(std.mem.indexOf(u8, memory_html, "memory accountability") != null);    try std.testing.expect(std.mem.indexOf(u8, memory_html, "root/refill") != null);    const index_html = try sys.fs.readFileAlloc(allocator, result.index_path, 8 * 1024 * 1024);    try std.testing.expect(std.mem.indexOf(u8, index_html, "<!doctype html>") != null);    try std.testing.expect(std.mem.indexOf(u8, index_html, "profile.css") != null);    try std.testing.expect(std.mem.indexOf(u8, index_html, "tiny profiling") != null);    const workload_path = try std.fs.path.join(allocator, &.{ output_dir, "runs", "run-200-bbb", "workloads", "gpalloc.allocator.html" });    const workload_html = try sys.fs.readFileAlloc(allocator, workload_path, 8 * 1024 * 1024);    try std.testing.expect(std.mem.indexOf(u8, workload_html, "../../../profile.css") != null);    try std.testing.expect(std.mem.indexOf(u8, workload_html, "Timing metrics") != null);    try std.testing.expect(std.mem.indexOf(u8, workload_html, "gpalloc.allocator.flame.html#cpu") != null);    const flame_path = try std.fs.path.join(allocator, &.{ output_dir, "runs", "run-200-bbb", "workloads", "gpalloc.allocator.flame.html" });    const flame_html = try sys.fs.readFileAlloc(allocator, flame_path, 8 * 1024 * 1024);    try std.testing.expect(std.mem.indexOf(u8, flame_html, "flame-cpu") != null);    try std.testing.expect(std.mem.indexOf(u8, flame_html, "flame-offcpu") != null);    try std.testing.expect(std.mem.indexOf(u8, flame_html, "refill") != null);    try std.testing.expect(std.mem.indexOf(u8, flame_html, "futex_wait") != null);    try std.testing.expect(std.mem.indexOf(u8, flame_html, "callchain samples") != null);    try std.testing.expect(std.mem.indexOf(u8, flame_html, "resolved frames") != null);    const style_path = try std.fs.path.join(allocator, &.{ output_dir, style_name });    try std.testing.expect(sys.fs.exists(style_path));}test "site generates an empty shell when no runs exist" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const profiling_dir = ".zig-cache/profile-web-site-empty";    const output_dir = ".zig-cache/profile-web-site-empty-out";    defer sys.fs.deleteTree(profiling_dir) catch {};    defer sys.fs.deleteTree(output_dir) catch {};    try sys.fs.createDirPath(profiling_dir);    const result = try generate(allocator, .{ .profiling_dir = profiling_dir, .output_dir = output_dir });    try std.testing.expectEqual(@as(usize, 0), result.runs);    try std.testing.expectEqual(@as(usize, 1), result.pages);    const index_html = try sys.fs.readFileAlloc(allocator, result.index_path, 1024 * 1024);    try std.testing.expect(std.mem.indexOf(u8, index_html, "No profiling runs found") != null);}

Complete call list for report.site.generate

11 direct calls.

Audit

Definitions8
Public names11
Members6
Version26.7.0
Revisiondaab053ee433