master
  1const std = @import("std");
  2const gpa = std.heap.wasm_allocator;
  3const abi = std.Build.abi.time_report;
  4const fmtEscapeHtml = @import("root").fmtEscapeHtml;
  5const step_list = &@import("root").step_list;
  6
  7const js = struct {
  8    extern "time_report" fn updateGeneric(
  9        /// The index of the step.
 10        step_idx: u32,
 11        // The HTML which will be used to populate the template slots.
 12        inner_html_ptr: [*]const u8,
 13        inner_html_len: usize,
 14    ) void;
 15    extern "time_report" fn updateCompile(
 16        /// The index of the step.
 17        step_idx: u32,
 18        // The HTML which will be used to populate the template slots.
 19        inner_html_ptr: [*]const u8,
 20        inner_html_len: usize,
 21        // The HTML which will populate the <tbody> of the file table.
 22        file_table_html_ptr: [*]const u8,
 23        file_table_html_len: usize,
 24        // The HTML which will populate the <tbody> of the decl table.
 25        decl_table_html_ptr: [*]const u8,
 26        decl_table_html_len: usize,
 27        /// Whether the LLVM backend was used. If not, LLVM-specific statistics are hidden.
 28        use_llvm: bool,
 29    ) void;
 30    extern "time_report" fn updateRunTest(
 31        /// The index of the step.
 32        step_idx: u32,
 33        // The HTML which will populate the <tbody> of the test table.
 34        table_html_ptr: [*]const u8,
 35        table_html_len: usize,
 36    ) void;
 37};
 38
 39pub fn genericResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
 40    if (msg_bytes.len != @sizeOf(abi.GenericResult)) @panic("malformed GenericResult message");
 41    const msg: *const abi.GenericResult = @ptrCast(msg_bytes);
 42    if (msg.step_idx >= step_list.*.len) @panic("malformed GenericResult message");
 43    const inner_html = try std.fmt.allocPrint(gpa,
 44        \\<code slot="step-name">{[step_name]f}</code>
 45        \\<span slot="stat-total-time">{[stat_total_time]D}</span>
 46    , .{
 47        .step_name = fmtEscapeHtml(step_list.*[msg.step_idx].name),
 48        .stat_total_time = msg.ns_total,
 49    });
 50    defer gpa.free(inner_html);
 51    js.updateGeneric(msg.step_idx, inner_html.ptr, inner_html.len);
 52}
 53
 54pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!void {
 55    const max_table_rows = 500;
 56
 57    if (msg_bytes.len < @sizeOf(abi.CompileResult)) @panic("malformed CompileResult message");
 58    const hdr: *const abi.CompileResult = @ptrCast(msg_bytes[0..@sizeOf(abi.CompileResult)]);
 59    if (hdr.step_idx >= step_list.*.len) @panic("malformed CompileResult message");
 60    var trailing = msg_bytes[@sizeOf(abi.CompileResult)..];
 61
 62    const llvm_pass_timings = trailing[0..hdr.llvm_pass_timings_len];
 63    trailing = trailing[hdr.llvm_pass_timings_len..];
 64
 65    const FileTimeReport = struct {
 66        name: []const u8,
 67        ns_sema: u64,
 68        ns_codegen: u64,
 69        ns_link: u64,
 70    };
 71    const DeclTimeReport = struct {
 72        file_name: []const u8,
 73        name: []const u8,
 74        sema_count: u32,
 75        ns_sema: u64,
 76        ns_codegen: u64,
 77        ns_link: u64,
 78    };
 79
 80    const slowest_files = try gpa.alloc(FileTimeReport, hdr.files_len);
 81    defer gpa.free(slowest_files);
 82
 83    const slowest_decls = try gpa.alloc(DeclTimeReport, hdr.decls_len);
 84    defer gpa.free(slowest_decls);
 85
 86    for (slowest_files) |*file_out| {
 87        const i = std.mem.indexOfScalar(u8, trailing, 0) orelse @panic("malformed CompileResult message");
 88        file_out.* = .{
 89            .name = trailing[0..i],
 90            .ns_sema = 0,
 91            .ns_codegen = 0,
 92            .ns_link = 0,
 93        };
 94        trailing = trailing[i + 1 ..];
 95    }
 96
 97    for (slowest_decls) |*decl_out| {
 98        const i = std.mem.indexOfScalar(u8, trailing, 0) orelse @panic("malformed CompileResult message");
 99        const file_idx = std.mem.readInt(u32, trailing[i..][1..5], .little);
100        const sema_count = std.mem.readInt(u32, trailing[i..][5..9], .little);
101        const sema_ns = std.mem.readInt(u64, trailing[i..][9..17], .little);
102        const codegen_ns = std.mem.readInt(u64, trailing[i..][17..25], .little);
103        const link_ns = std.mem.readInt(u64, trailing[i..][25..33], .little);
104        const file = &slowest_files[file_idx];
105        decl_out.* = .{
106            .file_name = file.name,
107            .name = trailing[0..i],
108            .sema_count = sema_count,
109            .ns_sema = sema_ns,
110            .ns_codegen = codegen_ns,
111            .ns_link = link_ns,
112        };
113        trailing = trailing[i + 33 ..];
114        file.ns_sema += sema_ns;
115        file.ns_codegen += codegen_ns;
116        file.ns_link += link_ns;
117    }
118
119    const S = struct {
120        fn fileLessThan(_: void, lhs: FileTimeReport, rhs: FileTimeReport) bool {
121            const lhs_ns = lhs.ns_sema + lhs.ns_codegen + lhs.ns_link;
122            const rhs_ns = rhs.ns_sema + rhs.ns_codegen + rhs.ns_link;
123            return lhs_ns > rhs_ns; // flipped to sort in reverse order
124        }
125        fn declLessThan(_: void, lhs: DeclTimeReport, rhs: DeclTimeReport) bool {
126            //if (true) return lhs.sema_count > rhs.sema_count;
127            const lhs_ns = lhs.ns_sema + lhs.ns_codegen + lhs.ns_link;
128            const rhs_ns = rhs.ns_sema + rhs.ns_codegen + rhs.ns_link;
129            return lhs_ns > rhs_ns; // flipped to sort in reverse order
130        }
131    };
132    std.mem.sort(FileTimeReport, slowest_files, {}, S.fileLessThan);
133    std.mem.sort(DeclTimeReport, slowest_decls, {}, S.declLessThan);
134
135    const stats = hdr.stats;
136    const inner_html = try std.fmt.allocPrint(gpa,
137        \\<code slot="step-name">{[step_name]f}</code>
138        \\<span slot="stat-reachable-files">{[stat_reachable_files]d}</span>
139        \\<span slot="stat-imported-files">{[stat_imported_files]d}</span>
140        \\<span slot="stat-generic-instances">{[stat_generic_instances]d}</span>
141        \\<span slot="stat-inline-calls">{[stat_inline_calls]d}</span>
142        \\<span slot="stat-compilation-time">{[stat_compilation_time]D}</span>
143        \\<span slot="cpu-time-parse">{[cpu_time_parse]D}</span>
144        \\<span slot="cpu-time-astgen">{[cpu_time_astgen]D}</span>
145        \\<span slot="cpu-time-sema">{[cpu_time_sema]D}</span>
146        \\<span slot="cpu-time-codegen">{[cpu_time_codegen]D}</span>
147        \\<span slot="cpu-time-link">{[cpu_time_link]D}</span>
148        \\<span slot="real-time-files">{[real_time_files]D}</span>
149        \\<span slot="real-time-decls">{[real_time_decls]D}</span>
150        \\<span slot="real-time-llvm-emit">{[real_time_llvm_emit]D}</span>
151        \\<span slot="real-time-link-flush">{[real_time_link_flush]D}</span>
152        \\<pre slot="llvm-pass-timings"><code>{[llvm_pass_timings]f}</code></pre>
153        \\
154    , .{
155        .step_name = fmtEscapeHtml(step_list.*[hdr.step_idx].name),
156        .stat_reachable_files = stats.n_reachable_files,
157        .stat_imported_files = stats.n_imported_files,
158        .stat_generic_instances = stats.n_generic_instances,
159        .stat_inline_calls = stats.n_inline_calls,
160        .stat_compilation_time = hdr.ns_total,
161
162        .cpu_time_parse = stats.cpu_ns_parse,
163        .cpu_time_astgen = stats.cpu_ns_astgen,
164        .cpu_time_sema = stats.cpu_ns_sema,
165        .cpu_time_codegen = stats.cpu_ns_codegen,
166        .cpu_time_link = stats.cpu_ns_link,
167        .real_time_files = stats.real_ns_files,
168        .real_time_decls = stats.real_ns_decls,
169        .real_time_llvm_emit = stats.real_ns_llvm_emit,
170        .real_time_link_flush = stats.real_ns_link_flush,
171
172        .llvm_pass_timings = fmtEscapeHtml(llvm_pass_timings),
173    });
174    defer gpa.free(inner_html);
175
176    var file_table_html: std.Io.Writer.Allocating = .init(gpa);
177    defer file_table_html.deinit();
178
179    for (slowest_files[0..@min(max_table_rows, slowest_files.len)]) |file| {
180        try file_table_html.writer.print(
181            \\<tr>
182            \\  <th scope="row"><code>{f}</code></th>
183            \\  <td>{D}</td>
184            \\  <td>{D}</td>
185            \\  <td>{D}</td>
186            \\  <td>{D}</td>
187            \\</tr>
188            \\
189        , .{
190            fmtEscapeHtml(file.name),
191            file.ns_sema,
192            file.ns_codegen,
193            file.ns_link,
194            file.ns_sema + file.ns_codegen + file.ns_link,
195        });
196    }
197    if (slowest_files.len > max_table_rows) {
198        try file_table_html.writer.print(
199            \\<tr><td colspan="4">{d} more rows omitted</td></tr>
200            \\
201        , .{slowest_files.len - max_table_rows});
202    }
203
204    var decl_table_html: std.Io.Writer.Allocating = .init(gpa);
205    defer decl_table_html.deinit();
206
207    for (slowest_decls[0..@min(max_table_rows, slowest_decls.len)]) |decl| {
208        try decl_table_html.writer.print(
209            \\<tr>
210            \\  <th scope="row"><code>{f}</code></th>
211            \\  <th scope="row"><code>{f}</code></th>
212            \\  <td>{d}</td>
213            \\  <td>{D}</td>
214            \\  <td>{D}</td>
215            \\  <td>{D}</td>
216            \\  <td>{D}</td>
217            \\</tr>
218            \\
219        , .{
220            fmtEscapeHtml(decl.file_name),
221            fmtEscapeHtml(decl.name),
222            decl.sema_count,
223            decl.ns_sema,
224            decl.ns_codegen,
225            decl.ns_link,
226            decl.ns_sema + decl.ns_codegen + decl.ns_link,
227        });
228    }
229    if (slowest_decls.len > max_table_rows) {
230        try decl_table_html.writer.print(
231            \\<tr><td colspan="6">{d} more rows omitted</td></tr>
232            \\
233        , .{slowest_decls.len - max_table_rows});
234    }
235
236    js.updateCompile(
237        hdr.step_idx,
238        inner_html.ptr,
239        inner_html.len,
240        file_table_html.written().ptr,
241        file_table_html.written().len,
242        decl_table_html.written().ptr,
243        decl_table_html.written().len,
244        hdr.flags.use_llvm,
245    );
246}
247
248pub fn runTestResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
249    if (msg_bytes.len < @sizeOf(abi.RunTestResult)) @panic("malformed RunTestResult message");
250    const hdr: *const abi.RunTestResult = @ptrCast(msg_bytes[0..@sizeOf(abi.RunTestResult)]);
251    if (hdr.step_idx >= step_list.*.len) @panic("malformed RunTestResult message");
252    const trailing = msg_bytes[@sizeOf(abi.RunTestResult)..];
253
254    const durations: []align(1) const u64 = @ptrCast(trailing[0 .. hdr.tests_len * 8]);
255    var offset: usize = hdr.tests_len * 8;
256
257    var table_html: std.ArrayList(u8) = .empty;
258    defer table_html.deinit(gpa);
259
260    for (durations) |test_ns| {
261        const test_name_len = std.mem.indexOfScalar(u8, trailing[offset..], 0) orelse @panic("malformed RunTestResult message");
262        const test_name = trailing[offset..][0..test_name_len];
263        offset += test_name_len + 1;
264        try table_html.print(gpa, "<tr><th scope=\"row\"><code>{f}</code></th>", .{fmtEscapeHtml(test_name)});
265        if (test_ns == std.math.maxInt(u64)) {
266            try table_html.appendSlice(gpa, "<td class=\"empty-cell\"></td>"); // didn't run
267        } else {
268            try table_html.print(gpa, "<td>{D}</td>", .{test_ns});
269        }
270        try table_html.appendSlice(gpa, "</tr>\n");
271    }
272
273    if (offset != trailing.len) @panic("malformed RunTestResult message");
274
275    js.updateRunTest(
276        hdr.step_idx,
277        table_html.items.ptr,
278        table_html.items.len,
279    );
280}