@@ -60,15 +60,15 @@ const File = struct {
60
60
ok : u64 ,
61
61
content_mismatch ,
62
62
bad_status : std.http.Status ,
63
- err : anyerror ,
63
+ fetch_error : GetResult.FetchError ,
64
64
65
65
pub fn format (res : Result , comptime fmt : []const u8 , options : std.fmt.FormatOptions , w : anytype ) ! void {
66
66
if (fmt .len != 0 ) std .fmt .invalidFmtError (fmt , res );
67
67
_ = options ;
68
68
switch (res ) {
69
69
.ok = > | query_ns | try w .print ("{}" , .{std .fmt .fmtDuration (query_ns )}),
70
70
.content_mismatch = > try w .writeAll (":warning: incorrect contents" ),
71
- .err = > | err | try w .print (":warning: error: {s}" , .{@errorName (err )}),
71
+ .fetch_error = > | fe | try w .print (":warning: error: {s}" , .{@errorName (fe . err )}),
72
72
.bad_status = > | s | try w .print (":warning: bad status: {d} {s}" , .{ @intFromEnum (s ), s .phrase () orelse "?" }),
73
73
}
74
74
}
@@ -107,7 +107,10 @@ pub fn main() Allocator.Error!u8 {
107
107
108
108
var arena_state : std.heap.ArenaAllocator = .init (gpa );
109
109
defer arena_state .deinit ();
110
- const arena = arena_state .allocator ();
110
+ var thread_safe_arena_state : std.heap.ThreadSafeAllocator = .{
111
+ .child_allocator = arena_state .allocator (),
112
+ };
113
+ const arena = thread_safe_arena_state .allocator ();
111
114
112
115
const args = std .process .argsAlloc (arena ) catch @panic ("argsAlloc failed" );
113
116
if (args .len != 3 ) @panic ("usage: check-mirrors <mirrors.ziggy> <out.md>" );
@@ -132,7 +135,7 @@ pub fn main() Allocator.Error!u8 {
132
135
break :mirrors mirrors ;
133
136
};
134
137
135
- const download_json_raw : []const u8 = try httpGetZsf (arena , & http_client , download_json_url );
138
+ const download_json_raw : []const u8 = try httpGetZsf (arena , arena , & http_client , download_json_url );
136
139
const download_json : std.json.Value = std .json .parseFromSliceLeaky (std .json .Value , arena , download_json_raw , .{}) catch | err | {
137
140
std .debug .panic ("failed to parse download json: {s}" , .{@errorName (err )});
138
141
};
@@ -200,6 +203,9 @@ pub fn main() Allocator.Error!u8 {
200
203
defer out_al .deinit (gpa );
201
204
const out = out_al .writer (gpa );
202
205
206
+ var error_traces_out : std .ArrayListUnmanaged (u8 ) = .empty ;
207
+ defer error_traces_out .deinit (gpa );
208
+
203
209
try out .writeAll ("## Tarballs\n\n " );
204
210
205
211
// Write a table header that looks like this (minus the whitespace on the second line):
@@ -218,6 +224,21 @@ pub fn main() Allocator.Error!u8 {
218
224
for (mirrors ) | m | {
219
225
if (m .file_result != .ok ) any_failures = true ;
220
226
try out .print (" {} |" , .{m .file_result });
227
+ trace : {
228
+ if (builtin .strip_debug_info ) break :trace ;
229
+ const fe = switch (m .file_result ) {
230
+ .fetch_error = > | fe | fe ,
231
+ else = > break :trace ,
232
+ };
233
+ const ert = fe .ert orelse break :trace ;
234
+ const self_info = std .debug .getSelfDebugInfo () catch break :trace ;
235
+ try error_traces_out .append (gpa , '\n ' );
236
+ std .debug .writeStackTrace (ert , error_traces_out .writer (gpa ), self_info , .no_color ) catch | err | switch (err ) {
237
+ error .OutOfMemory = > | e | return e ,
238
+ else = > {},
239
+ };
240
+ try error_traces_out .append (gpa , '\n ' );
241
+ }
221
242
}
222
243
}
223
244
try out .writeAll ("\n | **Avg. time** | |" );
@@ -229,6 +250,11 @@ pub fn main() Allocator.Error!u8 {
229
250
m .ns_div = 0 ;
230
251
}
231
252
253
+ if (error_traces_out .items .len > 0 ) {
254
+ try out .print ("\n\n ### Error Traces\n\n ```{s}```" , .{error_traces_out .items });
255
+ error_traces_out .clearRetainingCapacity ();
256
+ }
257
+
232
258
try out .writeAll ("\n\n ## Signatures\n\n " );
233
259
234
260
// Same table header as before
@@ -245,6 +271,21 @@ pub fn main() Allocator.Error!u8 {
245
271
for (mirrors ) | m | {
246
272
if (m .file_result != .ok ) any_failures = true ;
247
273
try out .print (" {} |" , .{m .file_result });
274
+ trace : {
275
+ if (builtin .strip_debug_info ) break :trace ;
276
+ const fe = switch (m .file_result ) {
277
+ .fetch_error = > | fe | fe ,
278
+ else = > break :trace ,
279
+ };
280
+ const ert = fe .ert orelse break :trace ;
281
+ const self_info = std .debug .getSelfDebugInfo () catch break :trace ;
282
+ try error_traces_out .append (gpa , '\n ' );
283
+ std .debug .writeStackTrace (ert , error_traces_out .writer (gpa ), self_info , .no_color ) catch | err | switch (err ) {
284
+ error .OutOfMemory = > | e | return e ,
285
+ else = > {},
286
+ };
287
+ try error_traces_out .append (gpa , '\n ' );
288
+ }
248
289
}
249
290
}
250
291
try out .writeAll ("\n | **Avg. time** | |" );
@@ -254,6 +295,11 @@ pub fn main() Allocator.Error!u8 {
254
295
// No need to reset, we're not doing any more checks
255
296
}
256
297
298
+ if (error_traces_out .items .len > 0 ) {
299
+ try out .print ("\n\n ### Error Traces\n\n ```{s}```" , .{error_traces_out .items });
300
+ error_traces_out .clearRetainingCapacity ();
301
+ }
302
+
257
303
try out .writeByte ('\n ' );
258
304
259
305
{
@@ -289,18 +335,16 @@ fn checkFile(
289
335
else
290
336
try std .fmt .allocPrint (arena , master_tarball_template , .{file .name });
291
337
292
- const trusted_data = try httpGetZsf (gpa , http_client , trusted_url );
338
+ const trusted_data = try httpGetZsf (gpa , arena , http_client , trusted_url );
293
339
defer gpa .free (trusted_data );
294
340
295
341
var wg : std.Thread.WaitGroup = .{};
296
342
297
343
var oom : std .atomic .Value (bool ) = .init (false );
298
344
for (mirrors ) | * m | {
299
- // `arena` isn't thread-safe, so alloc the URL now.
300
- const url = try std .fmt .allocPrint (arena , "{s}/{s}" , .{ m .url , file .name });
301
345
wg .spawnManager (
302
346
checkOneFile ,
303
- .{ gpa , http_client , m , url , trusted_data , & oom , prog_node },
347
+ .{ gpa , arena , http_client , m , file , trusted_data , & oom , prog_node },
304
348
);
305
349
}
306
350
wg .wait ();
@@ -309,16 +353,22 @@ fn checkFile(
309
353
310
354
fn checkOneFile (
311
355
gpa : Allocator ,
356
+ arena : Allocator ,
312
357
http_client : * std.http.Client ,
313
358
mirror : * Mirror ,
314
- url : [] const u8 ,
359
+ file : * const File ,
315
360
trusted_data : []const u8 ,
316
361
oom : * std .atomic .Value (bool ),
317
362
prog_node : std.Progress.Node ,
318
363
) void {
319
364
const mirror_prog_node = prog_node .start (mirror .username , 0 );
320
365
defer mirror_prog_node .end ();
321
- mirror .file_result = if (httpGet (gpa , http_client , url )) | get_result | switch (get_result ) {
366
+
367
+ const url = std .fmt .allocPrint (arena , "{s}/{s}" , .{ mirror .url , file .name }) catch | err | switch (err ) {
368
+ error .OutOfMemory = > return oom .store (true , .monotonic ),
369
+ };
370
+
371
+ mirror .file_result = if (httpGet (gpa , arena , http_client , url )) | get_result | switch (get_result ) {
322
372
.success = > | success | res : {
323
373
defer gpa .free (success .data );
324
374
if (std .mem .eql (u8 , success .data , trusted_data )) {
@@ -329,24 +379,30 @@ fn checkOneFile(
329
379
break :res .content_mismatch ;
330
380
}
331
381
},
332
- .err = > | err | .{ .err = err },
382
+ .fetch_error = > | fe | .{ .fetch_error = fe },
333
383
.bad_status = > | s | .{ .bad_status = s },
334
384
} else | err | switch (err ) {
335
385
error .OutOfMemory = > return oom .store (true , .monotonic ),
336
386
};
337
387
}
338
388
339
389
const GetResult = union (enum ) {
340
- err : anyerror ,
390
+ fetch_error : FetchError ,
341
391
bad_status : std.http.Status ,
342
392
success : struct {
343
393
/// Allocated into `gpa`, caller must free
344
394
data : []u8 ,
345
395
query_ns : u64 ,
346
396
},
397
+ const FetchError = struct {
398
+ err : anyerror ,
399
+ /// `ert.instruction_addresses` is allocated into `arena`.
400
+ ert : ? std.builtin.StackTrace ,
401
+ };
347
402
};
348
403
fn httpGet (
349
404
gpa : Allocator ,
405
+ arena : Allocator ,
350
406
http_client : * std.http.Client ,
351
407
url : []const u8 ,
352
408
) Allocator.Error ! GetResult {
@@ -358,7 +414,16 @@ fn httpGet(
358
414
.location = .{ .url = url },
359
415
.response_storage = .{ .dynamic = & buf },
360
416
.max_append_size = 512 * 1024 * 1024 ,
361
- }) catch | err | return .{ .err = err };
417
+ }) catch | err | {
418
+ const ert : ? std.builtin.StackTrace = if (@errorReturnTrace ()) | ert | ert : {
419
+ const new_addrs = try arena .dupe (usize , ert .instruction_addresses );
420
+ break :ert .{ .index = ert .index , .instruction_addresses = new_addrs };
421
+ } else null ;
422
+ return .{ .fetch_error = .{
423
+ .err = err ,
424
+ .ert = ert ,
425
+ } };
426
+ };
362
427
if (res .status != .ok ) {
363
428
return .{ .bad_status = res .status };
364
429
}
@@ -369,11 +434,12 @@ fn httpGet(
369
434
}
370
435
fn httpGetZsf (
371
436
gpa : Allocator ,
437
+ arena : Allocator ,
372
438
http_client : * std.http.Client ,
373
439
url : []const u8 ,
374
440
) Allocator.Error ! []u8 {
375
- switch (try httpGet (gpa , http_client , url )) {
376
- .err = > | err | std .debug .panic ("fetch '{s}' failed: {s}" , .{ url , @errorName (err ) }),
441
+ switch (try httpGet (gpa , arena , http_client , url )) {
442
+ .fetch_error = > | fe | std .debug .panic ("fetch '{s}' failed: {s}" , .{ url , @errorName (fe . err ) }),
377
443
.bad_status = > | status | std .debug .panic ("fetch '{s}' failed: bad status: {d} {s}" , .{ url , @intFromEnum (status ), status .phrase () orelse "?" }),
378
444
.success = > | res | return res .data ,
379
445
}
@@ -451,4 +517,5 @@ fn releaseTarballVersion(basename: []const u8) []const u8 {
451
517
const std = @import ("std" );
452
518
const Allocator = std .mem .Allocator ;
453
519
const assert = std .debug .assert ;
520
+ const builtin = @import ("builtin" );
454
521
const ziggy = @import ("ziggy" );
0 commit comments