| author | |
| committer | |
| log | 629e2e784495dd8ac91493fa7bb11e1772698e42 |
| tree | ec332162f702597f0c539c72042c9b0bb14dd871 |
| parent | 96c60bcca5fd1a76ded5eba576bfdb1def38139f |
| signature |
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; |
| 16 | 16 | ||
| 17 | pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator; | 17 | pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator; |
| 18 | pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator; | 18 | pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator; |
| 19 | pub const ScopedLoggingAllocator = @import("heap/logging_allocator.zig").ScopedLoggingAllocator; | ||
| 20 | pub const LogToWriterAllocator = @import("heap/log_to_writer_allocator.zig").LogToWriterAllocator; | ||
| 21 | pub const logToWriterAllocator = @import("heap/log_to_writer_allocator.zig").logToWriterAllocator; | ||
| 19 | pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator; | 22 | pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator; |
| 20 | pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator; | 23 | pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator; |
| 21 | 24 | ||
| ... | @@ -1162,4 +1165,5 @@ pub fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) !void { | ... | @@ -1162,4 +1165,5 @@ pub fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) !void { |
| 1162 | 1165 | ||
| 1163 | test "heap" { | 1166 | test "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. | ||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const 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. | ||
| 11 | pub 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. | ||
| 80 | pub fn logToWriterAllocator( | ||
| 81 | parent_allocator: *Allocator, | ||
| 82 | writer: anytype, | ||
| 83 | ) LogToWriterAllocator(@TypeOf(writer)) { | ||
| 84 | return LogToWriterAllocator(@TypeOf(writer)).init(parent_allocator, writer); | ||
| 85 | } | ||
| 86 | |||
| 87 | test "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 @@ |
| 6 | const std = @import("../std.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const Allocator = std.mem.Allocator; | 7 | const Allocator = std.mem.Allocator; |
| 8 | 8 | ||
| 9 | /// This allocator is used in front of another allocator and logs to the provided stream | 9 | /// 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` |
| 12 | pub fn LoggingAllocator(comptime Writer: type) type { | 12 | pub 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` | ||
| 22 | pub 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, | ||
| 17 | 32 | ||
| 18 | const Self = @This(); | 33 | const Self = @This(); |
| 19 | 34 | ||
| 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 | } |
| 30 | 44 | ||
| 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 | } |
| 78 | 124 | ||
| 79 | pub 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)) { | 128 | pub 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 | |||
| 86 | test "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 | } |