const std = @import("std"); const gpa = std.heap.wasm_allocator; const abi = std.Build.abi.time_report; const fmtEscapeHtml = @import("root").fmtEscapeHtml; const step_list = &@import("root").step_list; const js = struct { extern "time_report" fn updateGeneric( /// The index of the step. step_idx: u32, // The HTML which will be used to populate the template slots. inner_html_ptr: [*]const u8, inner_html_len: usize, ) void; extern "time_report" fn updateCompile( /// The index of the step. step_idx: u32, // The HTML which will be used to populate the template slots. inner_html_ptr: [*]const u8, inner_html_len: usize, // The HTML which will populate the of the file table. file_table_html_ptr: [*]const u8, file_table_html_len: usize, // The HTML which will populate the of the decl table. decl_table_html_ptr: [*]const u8, decl_table_html_len: usize, /// Whether the LLVM backend was used. If not, LLVM-specific statistics are hidden. use_llvm: bool, ) void; extern "time_report" fn updateRunTest( /// The index of the step. step_idx: u32, // The HTML which will populate the of the test table. table_html_ptr: [*]const u8, table_html_len: usize, ) void; }; pub fn genericResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { if (msg_bytes.len != @sizeOf(abi.GenericResult)) @panic("malformed GenericResult message"); const msg: *const abi.GenericResult = @ptrCast(msg_bytes); if (msg.step_idx >= step_list.*.len) @panic("malformed GenericResult message"); const inner_html = try std.fmt.allocPrint(gpa, \\{[step_name]f} \\{[stat_total_time]D} , .{ .step_name = fmtEscapeHtml(step_list.*[msg.step_idx].name), .stat_total_time = msg.ns_total, }); defer gpa.free(inner_html); js.updateGeneric(msg.step_idx, inner_html.ptr, inner_html.len); } pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!void { const max_table_rows = 500; if (msg_bytes.len < @sizeOf(abi.CompileResult)) @panic("malformed CompileResult message"); const hdr: *const abi.CompileResult = @ptrCast(msg_bytes[0..@sizeOf(abi.CompileResult)]); if (hdr.step_idx >= step_list.*.len) @panic("malformed CompileResult message"); var trailing = msg_bytes[@sizeOf(abi.CompileResult)..]; const llvm_pass_timings = trailing[0..hdr.llvm_pass_timings_len]; trailing = trailing[hdr.llvm_pass_timings_len..]; const FileTimeReport = struct { name: []const u8, ns_sema: u64, ns_codegen: u64, ns_link: u64, }; const DeclTimeReport = struct { file_name: []const u8, name: []const u8, sema_count: u32, ns_sema: u64, ns_codegen: u64, ns_link: u64, }; const slowest_files = try gpa.alloc(FileTimeReport, hdr.files_len); defer gpa.free(slowest_files); const slowest_decls = try gpa.alloc(DeclTimeReport, hdr.decls_len); defer gpa.free(slowest_decls); for (slowest_files) |*file_out| { const i = std.mem.indexOfScalar(u8, trailing, 0) orelse @panic("malformed CompileResult message"); file_out.* = .{ .name = trailing[0..i], .ns_sema = 0, .ns_codegen = 0, .ns_link = 0, }; trailing = trailing[i + 1 ..]; } for (slowest_decls) |*decl_out| { const i = std.mem.indexOfScalar(u8, trailing, 0) orelse @panic("malformed CompileResult message"); const file_idx = std.mem.readInt(u32, trailing[i..][1..5], .little); const sema_count = std.mem.readInt(u32, trailing[i..][5..9], .little); const sema_ns = std.mem.readInt(u64, trailing[i..][9..17], .little); const codegen_ns = std.mem.readInt(u64, trailing[i..][17..25], .little); const link_ns = std.mem.readInt(u64, trailing[i..][25..33], .little); const file = &slowest_files[file_idx]; decl_out.* = .{ .file_name = file.name, .name = trailing[0..i], .sema_count = sema_count, .ns_sema = sema_ns, .ns_codegen = codegen_ns, .ns_link = link_ns, }; trailing = trailing[i + 33 ..]; file.ns_sema += sema_ns; file.ns_codegen += codegen_ns; file.ns_link += link_ns; } const S = struct { fn fileLessThan(_: void, lhs: FileTimeReport, rhs: FileTimeReport) bool { const lhs_ns = lhs.ns_sema + lhs.ns_codegen + lhs.ns_link; const rhs_ns = rhs.ns_sema + rhs.ns_codegen + rhs.ns_link; return lhs_ns > rhs_ns; // flipped to sort in reverse order } fn declLessThan(_: void, lhs: DeclTimeReport, rhs: DeclTimeReport) bool { //if (true) return lhs.sema_count > rhs.sema_count; const lhs_ns = lhs.ns_sema + lhs.ns_codegen + lhs.ns_link; const rhs_ns = rhs.ns_sema + rhs.ns_codegen + rhs.ns_link; return lhs_ns > rhs_ns; // flipped to sort in reverse order } }; std.mem.sort(FileTimeReport, slowest_files, {}, S.fileLessThan); std.mem.sort(DeclTimeReport, slowest_decls, {}, S.declLessThan); const stats = hdr.stats; const inner_html = try std.fmt.allocPrint(gpa, \\{[step_name]f} \\{[stat_reachable_files]d} \\{[stat_imported_files]d} \\{[stat_generic_instances]d} \\{[stat_inline_calls]d} \\{[stat_compilation_time]D} \\{[cpu_time_parse]D} \\{[cpu_time_astgen]D} \\{[cpu_time_sema]D} \\{[cpu_time_codegen]D} \\{[cpu_time_link]D} \\{[real_time_files]D} \\{[real_time_decls]D} \\{[real_time_llvm_emit]D} \\{[real_time_link_flush]D} \\
{[llvm_pass_timings]f}
\\ , .{ .step_name = fmtEscapeHtml(step_list.*[hdr.step_idx].name), .stat_reachable_files = stats.n_reachable_files, .stat_imported_files = stats.n_imported_files, .stat_generic_instances = stats.n_generic_instances, .stat_inline_calls = stats.n_inline_calls, .stat_compilation_time = hdr.ns_total, .cpu_time_parse = stats.cpu_ns_parse, .cpu_time_astgen = stats.cpu_ns_astgen, .cpu_time_sema = stats.cpu_ns_sema, .cpu_time_codegen = stats.cpu_ns_codegen, .cpu_time_link = stats.cpu_ns_link, .real_time_files = stats.real_ns_files, .real_time_decls = stats.real_ns_decls, .real_time_llvm_emit = stats.real_ns_llvm_emit, .real_time_link_flush = stats.real_ns_link_flush, .llvm_pass_timings = fmtEscapeHtml(llvm_pass_timings), }); defer gpa.free(inner_html); var file_table_html: std.Io.Writer.Allocating = .init(gpa); defer file_table_html.deinit(); for (slowest_files[0..@min(max_table_rows, slowest_files.len)]) |file| { try file_table_html.writer.print( \\ \\ {f} \\ {D} \\ {D} \\ {D} \\ {D} \\ \\ , .{ fmtEscapeHtml(file.name), file.ns_sema, file.ns_codegen, file.ns_link, file.ns_sema + file.ns_codegen + file.ns_link, }); } if (slowest_files.len > max_table_rows) { try file_table_html.writer.print( \\{d} more rows omitted \\ , .{slowest_files.len - max_table_rows}); } var decl_table_html: std.Io.Writer.Allocating = .init(gpa); defer decl_table_html.deinit(); for (slowest_decls[0..@min(max_table_rows, slowest_decls.len)]) |decl| { try decl_table_html.writer.print( \\ \\ {f} \\ {f} \\ {d} \\ {D} \\ {D} \\ {D} \\ {D} \\ \\ , .{ fmtEscapeHtml(decl.file_name), fmtEscapeHtml(decl.name), decl.sema_count, decl.ns_sema, decl.ns_codegen, decl.ns_link, decl.ns_sema + decl.ns_codegen + decl.ns_link, }); } if (slowest_decls.len > max_table_rows) { try decl_table_html.writer.print( \\{d} more rows omitted \\ , .{slowest_decls.len - max_table_rows}); } js.updateCompile( hdr.step_idx, inner_html.ptr, inner_html.len, file_table_html.written().ptr, file_table_html.written().len, decl_table_html.written().ptr, decl_table_html.written().len, hdr.flags.use_llvm, ); } pub fn runTestResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { if (msg_bytes.len < @sizeOf(abi.RunTestResult)) @panic("malformed RunTestResult message"); const hdr: *const abi.RunTestResult = @ptrCast(msg_bytes[0..@sizeOf(abi.RunTestResult)]); if (hdr.step_idx >= step_list.*.len) @panic("malformed RunTestResult message"); const trailing = msg_bytes[@sizeOf(abi.RunTestResult)..]; const durations: []align(1) const u64 = @ptrCast(trailing[0 .. hdr.tests_len * 8]); var offset: usize = hdr.tests_len * 8; var table_html: std.ArrayList(u8) = .empty; defer table_html.deinit(gpa); for (durations) |test_ns| { const test_name_len = std.mem.indexOfScalar(u8, trailing[offset..], 0) orelse @panic("malformed RunTestResult message"); const test_name = trailing[offset..][0..test_name_len]; offset += test_name_len + 1; try table_html.print(gpa, "{f}", .{fmtEscapeHtml(test_name)}); if (test_ns == std.math.maxInt(u64)) { try table_html.appendSlice(gpa, ""); // didn't run } else { try table_html.print(gpa, "{D}", .{test_ns}); } try table_html.appendSlice(gpa, "\n"); } if (offset != trailing.len) @panic("malformed RunTestResult message"); js.updateRunTest( hdr.step_idx, table_html.items.ptr, table_html.items.len, ); }