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}