authorgravatar for leecannon@leecannon.xyzLee Cannon <leecannon@leecannon.xyz> 2021-06-09 19:42:07+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-06-09 21:42:07+03:00
log629e2e784495dd8ac91493fa7bb11e1772698e42
treeec332162f702597f0c539c72042c9b0bb14dd871
parent96c60bcca5fd1a76ded5eba576bfdb1def38139f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Add a logging allocator that uses std.log (#8511)


4 files changed, 232 insertions(+), 51 deletions(-)

lib/std/heap.zig+4
...@@ -16,6 +16,9 @@ const maxInt = std.math.maxInt;...@@ -16,6 +16,9 @@ const maxInt = std.math.maxInt;
1616
17pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;17pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
18pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;18pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
19pub const ScopedLoggingAllocator = @import("heap/logging_allocator.zig").ScopedLoggingAllocator;
20pub const LogToWriterAllocator = @import("heap/log_to_writer_allocator.zig").LogToWriterAllocator;
21pub const logToWriterAllocator = @import("heap/log_to_writer_allocator.zig").logToWriterAllocator;
19pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;22pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
20pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator;23pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator;
2124
...@@ -1162,4 +1165,5 @@ pub fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) !void {...@@ -1162,4 +1165,5 @@ pub fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) !void {
11621165
1163test "heap" {1166test "heap" {
1164 _ = @import("heap/logging_allocator.zig");1167 _ = @import("heap/logging_allocator.zig");
1168 _ = @import("heap/log_to_writer_allocator.zig");
1165}1169}
lib/std/heap/log_to_writer_allocator.zig created+108
...@@ -0,0 +1,108 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("../std.zig");
7const Allocator = std.mem.Allocator;
8
9/// This allocator is used in front of another allocator and logs to the provided writer
10/// on every call to the allocator. Writer errors are ignored.
11pub fn LogToWriterAllocator(comptime Writer: type) type {
12 return struct {
13 allocator: Allocator,
14 parent_allocator: *Allocator,
15 writer: Writer,
16
17 const Self = @This();
18
19 pub fn init(parent_allocator: *Allocator, writer: Writer) Self {
20 return Self{
21 .allocator = Allocator{
22 .allocFn = alloc,
23 .resizeFn = resize,
24 },
25 .parent_allocator = parent_allocator,
26 .writer = writer,
27 };
28 }
29
30 fn alloc(
31 allocator: *Allocator,
32 len: usize,
33 ptr_align: u29,
34 len_align: u29,
35 ra: usize,
36 ) error{OutOfMemory}![]u8 {
37 const self = @fieldParentPtr(Self, "allocator", allocator);
38 self.writer.print("alloc : {}", .{len}) catch {};
39 const result = self.parent_allocator.allocFn(self.parent_allocator, len, ptr_align, len_align, ra);
40 if (result) |buff| {
41 self.writer.print(" success!\n", .{}) catch {};
42 } else |err| {
43 self.writer.print(" failure!\n", .{}) catch {};
44 }
45 return result;
46 }
47
48 fn resize(
49 allocator: *Allocator,
50 buf: []u8,
51 buf_align: u29,
52 new_len: usize,
53 len_align: u29,
54 ra: usize,
55 ) error{OutOfMemory}!usize {
56 const self = @fieldParentPtr(Self, "allocator", allocator);
57 if (new_len == 0) {
58 self.writer.print("free : {}\n", .{buf.len}) catch {};
59 } else if (new_len <= buf.len) {
60 self.writer.print("shrink: {} to {}\n", .{ buf.len, new_len }) catch {};
61 } else {
62 self.writer.print("expand: {} to {}", .{ buf.len, new_len }) catch {};
63 }
64 if (self.parent_allocator.resizeFn(self.parent_allocator, buf, buf_align, new_len, len_align, ra)) |resized_len| {
65 if (new_len > buf.len) {
66 self.writer.print(" success!\n", .{}) catch {};
67 }
68 return resized_len;
69 } else |e| {
70 std.debug.assert(new_len > buf.len);
71 self.writer.print(" failure!\n", .{}) catch {};
72 return e;
73 }
74 }
75 };
76}
77
78/// This allocator is used in front of another allocator and logs to the provided writer
79/// on every call to the allocator. Writer errors are ignored.
80pub fn logToWriterAllocator(
81 parent_allocator: *Allocator,
82 writer: anytype,
83) LogToWriterAllocator(@TypeOf(writer)) {
84 return LogToWriterAllocator(@TypeOf(writer)).init(parent_allocator, writer);
85}
86
87test "LogToWriterAllocator" {
88 var log_buf: [255]u8 = undefined;
89 var fbs = std.io.fixedBufferStream(&log_buf);
90
91 var allocator_buf: [10]u8 = undefined;
92 var fixedBufferAllocator = std.mem.validationWrap(std.heap.FixedBufferAllocator.init(&allocator_buf));
93 const allocator = &logToWriterAllocator(&fixedBufferAllocator.allocator, fbs.writer()).allocator;
94
95 var a = try allocator.alloc(u8, 10);
96 a = allocator.shrink(a, 5);
97 try std.testing.expect(a.len == 5);
98 try std.testing.expectError(error.OutOfMemory, allocator.resize(a, 20));
99 allocator.free(a);
100
101 try std.testing.expectEqualSlices(u8,
102 \\alloc : 10 success!
103 \\shrink: 10 to 5
104 \\expand: 5 to 20 failure!
105 \\free : 5
106 \\
107 , fbs.getWritten());
108}
lib/std/heap/logging_allocator.zig+74-51
...@@ -6,28 +6,56 @@...@@ -6,28 +6,56 @@
6const std = @import("../std.zig");6const std = @import("../std.zig");
7const Allocator = std.mem.Allocator;7const Allocator = std.mem.Allocator;
88
9/// This allocator is used in front of another allocator and logs to the provided stream9/// This allocator is used in front of another allocator and logs to `std.log`
10/// on every call to the allocator. Stream errors are ignored.10/// on every call to the allocator.
11/// If https://github.com/ziglang/zig/issues/2586 is implemented, this API can be improved.11/// For logging to a `std.io.Writer` see `std.heap.LogToWriterAllocator`
12pub fn LoggingAllocator(comptime Writer: type) type {12pub fn LoggingAllocator(
13 comptime success_log_level: std.log.Level,
14 comptime failure_log_level: std.log.Level,
15) type {
16 return ScopedLoggingAllocator(.default, success_log_level, failure_log_level);
17}
18
19/// This allocator is used in front of another allocator and logs to `std.log`
20/// with the given scope on every call to the allocator.
21/// For logging to a `std.io.Writer` see `std.heap.LogToWriterAllocator`
22pub fn ScopedLoggingAllocator(
23 comptime scope: @Type(.EnumLiteral),
24 comptime success_log_level: std.log.Level,
25 comptime failure_log_level: std.log.Level,
26) type {
27 const log = std.log.scoped(scope);
28
13 return struct {29 return struct {
14 allocator: Allocator,30 allocator: Allocator,
15 parent_allocator: *Allocator,31 parent_allocator: *Allocator,
16 writer: Writer,
1732
18 const Self = @This();33 const Self = @This();
1934
20 pub fn init(parent_allocator: *Allocator, writer: Writer) Self {35 pub fn init(parent_allocator: *Allocator) Self {
21 return Self{36 return .{
22 .allocator = Allocator{37 .allocator = Allocator{
23 .allocFn = alloc,38 .allocFn = alloc,
24 .resizeFn = resize,39 .resizeFn = resize,
25 },40 },
26 .parent_allocator = parent_allocator,41 .parent_allocator = parent_allocator,
27 .writer = writer,
28 };42 };
29 }43 }
3044
45 // This function is required as the `std.log.log` function is not public
46 fn logHelper(comptime log_level: std.log.Level, comptime format: []const u8, args: anytype) callconv(.Inline) void {
47 switch (log_level) {
48 .emerg => log.emerg(format, args),
49 .alert => log.alert(format, args),
50 .crit => log.crit(format, args),
51 .err => log.err(format, args),
52 .warn => log.warn(format, args),
53 .notice => log.notice(format, args),
54 .info => log.info(format, args),
55 .debug => log.debug(format, args),
56 }
57 }
58
31 fn alloc(59 fn alloc(
32 allocator: *Allocator,60 allocator: *Allocator,
33 len: usize,61 len: usize,
...@@ -36,12 +64,19 @@ pub fn LoggingAllocator(comptime Writer: type) type {...@@ -36,12 +64,19 @@ pub fn LoggingAllocator(comptime Writer: type) type {
36 ra: usize,64 ra: usize,
37 ) error{OutOfMemory}![]u8 {65 ) error{OutOfMemory}![]u8 {
38 const self = @fieldParentPtr(Self, "allocator", allocator);66 const self = @fieldParentPtr(Self, "allocator", allocator);
39 self.writer.print("alloc : {}", .{len}) catch {};
40 const result = self.parent_allocator.allocFn(self.parent_allocator, len, ptr_align, len_align, ra);67 const result = self.parent_allocator.allocFn(self.parent_allocator, len, ptr_align, len_align, ra);
41 if (result) |buff| {68 if (result) |buff| {
42 self.writer.print(" success!\n", .{}) catch {};69 logHelper(
70 success_log_level,
71 "alloc - success - len: {}, ptr_align: {}, len_align: {}",
72 .{ len, ptr_align, len_align },
73 );
43 } else |err| {74 } else |err| {
44 self.writer.print(" failure!\n", .{}) catch {};75 logHelper(
76 failure_log_level,
77 "alloc - failure: {s} - len: {}, ptr_align: {}, len_align: {}",
78 .{ @errorName(err), len, ptr_align, len_align },
79 );
45 }80 }
46 return result;81 return result;
47 }82 }
...@@ -55,53 +90,41 @@ pub fn LoggingAllocator(comptime Writer: type) type {...@@ -55,53 +90,41 @@ pub fn LoggingAllocator(comptime Writer: type) type {
55 ra: usize,90 ra: usize,
56 ) error{OutOfMemory}!usize {91 ) error{OutOfMemory}!usize {
57 const self = @fieldParentPtr(Self, "allocator", allocator);92 const self = @fieldParentPtr(Self, "allocator", allocator);
58 if (new_len == 0) {93
59 self.writer.print("free : {}\n", .{buf.len}) catch {};
60 } else if (new_len <= buf.len) {
61 self.writer.print("shrink: {} to {}\n", .{ buf.len, new_len }) catch {};
62 } else {
63 self.writer.print("expand: {} to {}", .{ buf.len, new_len }) catch {};
64 }
65 if (self.parent_allocator.resizeFn(self.parent_allocator, buf, buf_align, new_len, len_align, ra)) |resized_len| {94 if (self.parent_allocator.resizeFn(self.parent_allocator, buf, buf_align, new_len, len_align, ra)) |resized_len| {
66 if (new_len > buf.len) {95 if (new_len == 0) {
67 self.writer.print(" success!\n", .{}) catch {};96 logHelper(success_log_level, "free - success - len: {}", .{buf.len});
97 } else if (new_len <= buf.len) {
98 logHelper(
99 success_log_level,
100 "shrink - success - {} to {}, len_align: {}, buf_align: {}",
101 .{ buf.len, new_len, len_align, buf_align },
102 );
103 } else {
104 logHelper(
105 success_log_level,
106 "expand - success - {} to {}, len_align: {}, buf_align: {}",
107 .{ buf.len, new_len, len_align, buf_align },
108 );
68 }109 }
110
69 return resized_len;111 return resized_len;
70 } else |e| {112 } else |err| {
71 std.debug.assert(new_len > buf.len);113 std.debug.assert(new_len > buf.len);
72 self.writer.print(" failure!\n", .{}) catch {};114 logHelper(
73 return e;115 failure_log_level,
116 "expand - failure: {s} - {} to {}, len_align: {}, buf_align: {}",
117 .{ @errorName(err), buf.len, new_len, len_align, buf_align },
118 );
119 return err;
74 }120 }
75 }121 }
76 };122 };
77}123}
78124
79pub fn loggingAllocator(125/// This allocator is used in front of another allocator and logs to `std.log`
80 parent_allocator: *Allocator,126/// on every call to the allocator.
81 writer: anytype,127/// For logging to a `std.io.Writer` see `std.heap.LogToWriterAllocator`
82) LoggingAllocator(@TypeOf(writer)) {128pub fn loggingAllocator(parent_allocator: *Allocator) LoggingAllocator(.debug, .crit) {
83 return LoggingAllocator(@TypeOf(writer)).init(parent_allocator, writer);129 return LoggingAllocator(.debug, .crit).init(parent_allocator);
84}
85
86test "LoggingAllocator" {
87 var log_buf: [255]u8 = undefined;
88 var fbs = std.io.fixedBufferStream(&log_buf);
89
90 var allocator_buf: [10]u8 = undefined;
91 var fixedBufferAllocator = std.mem.validationWrap(std.heap.FixedBufferAllocator.init(&allocator_buf));
92 const allocator = &loggingAllocator(&fixedBufferAllocator.allocator, fbs.writer()).allocator;
93
94 var a = try allocator.alloc(u8, 10);
95 a = allocator.shrink(a, 5);
96 try std.testing.expect(a.len == 5);
97 try std.testing.expectError(error.OutOfMemory, allocator.resize(a, 20));
98 allocator.free(a);
99
100 try std.testing.expectEqualSlices(u8,
101 \\alloc : 10 success!
102 \\shrink: 10 to 5
103 \\expand: 5 to 20 failure!
104 \\free : 5
105 \\
106 , fbs.getWritten());
107}130}
test/compare_output.zig+46
...@@ -599,4 +599,50 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -599,4 +599,50 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
599 \\emergency(c): 599 \\emergency(c):
600 \\600 \\
601 );601 );
602
603 // It is required to override the log function in order to print to stdout instead of stderr
604 cases.add("std.heap.LoggingAllocator logs to std.log",
605 \\const std = @import("std");
606 \\
607 \\pub const log_level: std.log.Level = .debug;
608 \\
609 \\pub fn main() !void {
610 \\ var allocator_buf: [10]u8 = undefined;
611 \\ var fixedBufferAllocator = std.mem.validationWrap(std.heap.FixedBufferAllocator.init(&allocator_buf));
612 \\ const allocator = &std.heap.loggingAllocator(&fixedBufferAllocator.allocator).allocator;
613 \\
614 \\ var a = try allocator.alloc(u8, 10);
615 \\ a = allocator.shrink(a, 5);
616 \\ try std.testing.expect(a.len == 5);
617 \\ try std.testing.expectError(error.OutOfMemory, allocator.resize(a, 20));
618 \\ allocator.free(a);
619 \\}
620 \\
621 \\pub fn log(
622 \\ comptime level: std.log.Level,
623 \\ comptime scope: @TypeOf(.EnumLiteral),
624 \\ comptime format: []const u8,
625 \\ args: anytype,
626 \\) void {
627 \\ const level_txt = switch (level) {
628 \\ .emerg => "emergency",
629 \\ .alert => "alert",
630 \\ .crit => "critical",
631 \\ .err => "error",
632 \\ .warn => "warning",
633 \\ .notice => "notice",
634 \\ .info => "info",
635 \\ .debug => "debug",
636 \\ };
637 \\ const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
638 \\ const stdout = std.io.getStdOut().writer();
639 \\ nosuspend stdout.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
640 \\}
641 ,
642 \\debug: alloc - success - len: 10, ptr_align: 1, len_align: 0
643 \\debug: shrink - success - 10 to 5, len_align: 0, buf_align: 1
644 \\critical: expand - failure: OutOfMemory - 5 to 20, len_align: 0, buf_align: 1
645 \\debug: free - success - len: 5
646 \\
647 );
602}648}