mirror of
https://github.com/ziglang/zig.git
synced 2024-12-14 07:10:16 +00:00
Add a logging allocator that uses std.log (#8511)
This commit is contained in:
parent
96c60bcca5
commit
629e2e7844
@ -16,6 +16,9 @@ const maxInt = std.math.maxInt;
|
||||
|
||||
pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
|
||||
pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
|
||||
pub const ScopedLoggingAllocator = @import("heap/logging_allocator.zig").ScopedLoggingAllocator;
|
||||
pub const LogToWriterAllocator = @import("heap/log_to_writer_allocator.zig").LogToWriterAllocator;
|
||||
pub const logToWriterAllocator = @import("heap/log_to_writer_allocator.zig").logToWriterAllocator;
|
||||
pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
|
||||
pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator;
|
||||
|
||||
@ -1162,4 +1165,5 @@ pub fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) !void {
|
||||
|
||||
test "heap" {
|
||||
_ = @import("heap/logging_allocator.zig");
|
||||
_ = @import("heap/log_to_writer_allocator.zig");
|
||||
}
|
||||
|
108
lib/std/heap/log_to_writer_allocator.zig
Normal file
108
lib/std/heap/log_to_writer_allocator.zig
Normal file
@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
/// This allocator is used in front of another allocator and logs to the provided writer
|
||||
/// on every call to the allocator. Writer errors are ignored.
|
||||
pub fn LogToWriterAllocator(comptime Writer: type) type {
|
||||
return struct {
|
||||
allocator: Allocator,
|
||||
parent_allocator: *Allocator,
|
||||
writer: Writer,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(parent_allocator: *Allocator, writer: Writer) Self {
|
||||
return Self{
|
||||
.allocator = Allocator{
|
||||
.allocFn = alloc,
|
||||
.resizeFn = resize,
|
||||
},
|
||||
.parent_allocator = parent_allocator,
|
||||
.writer = writer,
|
||||
};
|
||||
}
|
||||
|
||||
fn alloc(
|
||||
allocator: *Allocator,
|
||||
len: usize,
|
||||
ptr_align: u29,
|
||||
len_align: u29,
|
||||
ra: usize,
|
||||
) error{OutOfMemory}![]u8 {
|
||||
const self = @fieldParentPtr(Self, "allocator", allocator);
|
||||
self.writer.print("alloc : {}", .{len}) catch {};
|
||||
const result = self.parent_allocator.allocFn(self.parent_allocator, len, ptr_align, len_align, ra);
|
||||
if (result) |buff| {
|
||||
self.writer.print(" success!\n", .{}) catch {};
|
||||
} else |err| {
|
||||
self.writer.print(" failure!\n", .{}) catch {};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn resize(
|
||||
allocator: *Allocator,
|
||||
buf: []u8,
|
||||
buf_align: u29,
|
||||
new_len: usize,
|
||||
len_align: u29,
|
||||
ra: usize,
|
||||
) error{OutOfMemory}!usize {
|
||||
const self = @fieldParentPtr(Self, "allocator", allocator);
|
||||
if (new_len == 0) {
|
||||
self.writer.print("free : {}\n", .{buf.len}) catch {};
|
||||
} else if (new_len <= buf.len) {
|
||||
self.writer.print("shrink: {} to {}\n", .{ buf.len, new_len }) catch {};
|
||||
} else {
|
||||
self.writer.print("expand: {} to {}", .{ buf.len, new_len }) catch {};
|
||||
}
|
||||
if (self.parent_allocator.resizeFn(self.parent_allocator, buf, buf_align, new_len, len_align, ra)) |resized_len| {
|
||||
if (new_len > buf.len) {
|
||||
self.writer.print(" success!\n", .{}) catch {};
|
||||
}
|
||||
return resized_len;
|
||||
} else |e| {
|
||||
std.debug.assert(new_len > buf.len);
|
||||
self.writer.print(" failure!\n", .{}) catch {};
|
||||
return e;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This allocator is used in front of another allocator and logs to the provided writer
|
||||
/// on every call to the allocator. Writer errors are ignored.
|
||||
pub fn logToWriterAllocator(
|
||||
parent_allocator: *Allocator,
|
||||
writer: anytype,
|
||||
) LogToWriterAllocator(@TypeOf(writer)) {
|
||||
return LogToWriterAllocator(@TypeOf(writer)).init(parent_allocator, writer);
|
||||
}
|
||||
|
||||
test "LogToWriterAllocator" {
|
||||
var log_buf: [255]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&log_buf);
|
||||
|
||||
var allocator_buf: [10]u8 = undefined;
|
||||
var fixedBufferAllocator = std.mem.validationWrap(std.heap.FixedBufferAllocator.init(&allocator_buf));
|
||||
const allocator = &logToWriterAllocator(&fixedBufferAllocator.allocator, fbs.writer()).allocator;
|
||||
|
||||
var a = try allocator.alloc(u8, 10);
|
||||
a = allocator.shrink(a, 5);
|
||||
try std.testing.expect(a.len == 5);
|
||||
try std.testing.expectError(error.OutOfMemory, allocator.resize(a, 20));
|
||||
allocator.free(a);
|
||||
|
||||
try std.testing.expectEqualSlices(u8,
|
||||
\\alloc : 10 success!
|
||||
\\shrink: 10 to 5
|
||||
\\expand: 5 to 20 failure!
|
||||
\\free : 5
|
||||
\\
|
||||
, fbs.getWritten());
|
||||
}
|
@ -6,28 +6,56 @@
|
||||
const std = @import("../std.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
/// This allocator is used in front of another allocator and logs to the provided stream
|
||||
/// on every call to the allocator. Stream errors are ignored.
|
||||
/// If https://github.com/ziglang/zig/issues/2586 is implemented, this API can be improved.
|
||||
pub fn LoggingAllocator(comptime Writer: type) type {
|
||||
/// This allocator is used in front of another allocator and logs to `std.log`
|
||||
/// on every call to the allocator.
|
||||
/// For logging to a `std.io.Writer` see `std.heap.LogToWriterAllocator`
|
||||
pub fn LoggingAllocator(
|
||||
comptime success_log_level: std.log.Level,
|
||||
comptime failure_log_level: std.log.Level,
|
||||
) type {
|
||||
return ScopedLoggingAllocator(.default, success_log_level, failure_log_level);
|
||||
}
|
||||
|
||||
/// This allocator is used in front of another allocator and logs to `std.log`
|
||||
/// with the given scope on every call to the allocator.
|
||||
/// For logging to a `std.io.Writer` see `std.heap.LogToWriterAllocator`
|
||||
pub fn ScopedLoggingAllocator(
|
||||
comptime scope: @Type(.EnumLiteral),
|
||||
comptime success_log_level: std.log.Level,
|
||||
comptime failure_log_level: std.log.Level,
|
||||
) type {
|
||||
const log = std.log.scoped(scope);
|
||||
|
||||
return struct {
|
||||
allocator: Allocator,
|
||||
parent_allocator: *Allocator,
|
||||
writer: Writer,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(parent_allocator: *Allocator, writer: Writer) Self {
|
||||
return Self{
|
||||
pub fn init(parent_allocator: *Allocator) Self {
|
||||
return .{
|
||||
.allocator = Allocator{
|
||||
.allocFn = alloc,
|
||||
.resizeFn = resize,
|
||||
},
|
||||
.parent_allocator = parent_allocator,
|
||||
.writer = writer,
|
||||
};
|
||||
}
|
||||
|
||||
// This function is required as the `std.log.log` function is not public
|
||||
fn logHelper(comptime log_level: std.log.Level, comptime format: []const u8, args: anytype) callconv(.Inline) void {
|
||||
switch (log_level) {
|
||||
.emerg => log.emerg(format, args),
|
||||
.alert => log.alert(format, args),
|
||||
.crit => log.crit(format, args),
|
||||
.err => log.err(format, args),
|
||||
.warn => log.warn(format, args),
|
||||
.notice => log.notice(format, args),
|
||||
.info => log.info(format, args),
|
||||
.debug => log.debug(format, args),
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc(
|
||||
allocator: *Allocator,
|
||||
len: usize,
|
||||
@ -36,12 +64,19 @@ pub fn LoggingAllocator(comptime Writer: type) type {
|
||||
ra: usize,
|
||||
) error{OutOfMemory}![]u8 {
|
||||
const self = @fieldParentPtr(Self, "allocator", allocator);
|
||||
self.writer.print("alloc : {}", .{len}) catch {};
|
||||
const result = self.parent_allocator.allocFn(self.parent_allocator, len, ptr_align, len_align, ra);
|
||||
if (result) |buff| {
|
||||
self.writer.print(" success!\n", .{}) catch {};
|
||||
logHelper(
|
||||
success_log_level,
|
||||
"alloc - success - len: {}, ptr_align: {}, len_align: {}",
|
||||
.{ len, ptr_align, len_align },
|
||||
);
|
||||
} else |err| {
|
||||
self.writer.print(" failure!\n", .{}) catch {};
|
||||
logHelper(
|
||||
failure_log_level,
|
||||
"alloc - failure: {s} - len: {}, ptr_align: {}, len_align: {}",
|
||||
.{ @errorName(err), len, ptr_align, len_align },
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -55,53 +90,41 @@ pub fn LoggingAllocator(comptime Writer: type) type {
|
||||
ra: usize,
|
||||
) error{OutOfMemory}!usize {
|
||||
const self = @fieldParentPtr(Self, "allocator", allocator);
|
||||
if (new_len == 0) {
|
||||
self.writer.print("free : {}\n", .{buf.len}) catch {};
|
||||
} else if (new_len <= buf.len) {
|
||||
self.writer.print("shrink: {} to {}\n", .{ buf.len, new_len }) catch {};
|
||||
} else {
|
||||
self.writer.print("expand: {} to {}", .{ buf.len, new_len }) catch {};
|
||||
}
|
||||
|
||||
if (self.parent_allocator.resizeFn(self.parent_allocator, buf, buf_align, new_len, len_align, ra)) |resized_len| {
|
||||
if (new_len > buf.len) {
|
||||
self.writer.print(" success!\n", .{}) catch {};
|
||||
if (new_len == 0) {
|
||||
logHelper(success_log_level, "free - success - len: {}", .{buf.len});
|
||||
} else if (new_len <= buf.len) {
|
||||
logHelper(
|
||||
success_log_level,
|
||||
"shrink - success - {} to {}, len_align: {}, buf_align: {}",
|
||||
.{ buf.len, new_len, len_align, buf_align },
|
||||
);
|
||||
} else {
|
||||
logHelper(
|
||||
success_log_level,
|
||||
"expand - success - {} to {}, len_align: {}, buf_align: {}",
|
||||
.{ buf.len, new_len, len_align, buf_align },
|
||||
);
|
||||
}
|
||||
|
||||
return resized_len;
|
||||
} else |e| {
|
||||
} else |err| {
|
||||
std.debug.assert(new_len > buf.len);
|
||||
self.writer.print(" failure!\n", .{}) catch {};
|
||||
return e;
|
||||
logHelper(
|
||||
failure_log_level,
|
||||
"expand - failure: {s} - {} to {}, len_align: {}, buf_align: {}",
|
||||
.{ @errorName(err), buf.len, new_len, len_align, buf_align },
|
||||
);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn loggingAllocator(
|
||||
parent_allocator: *Allocator,
|
||||
writer: anytype,
|
||||
) LoggingAllocator(@TypeOf(writer)) {
|
||||
return LoggingAllocator(@TypeOf(writer)).init(parent_allocator, writer);
|
||||
}
|
||||
|
||||
test "LoggingAllocator" {
|
||||
var log_buf: [255]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&log_buf);
|
||||
|
||||
var allocator_buf: [10]u8 = undefined;
|
||||
var fixedBufferAllocator = std.mem.validationWrap(std.heap.FixedBufferAllocator.init(&allocator_buf));
|
||||
const allocator = &loggingAllocator(&fixedBufferAllocator.allocator, fbs.writer()).allocator;
|
||||
|
||||
var a = try allocator.alloc(u8, 10);
|
||||
a = allocator.shrink(a, 5);
|
||||
try std.testing.expect(a.len == 5);
|
||||
try std.testing.expectError(error.OutOfMemory, allocator.resize(a, 20));
|
||||
allocator.free(a);
|
||||
|
||||
try std.testing.expectEqualSlices(u8,
|
||||
\\alloc : 10 success!
|
||||
\\shrink: 10 to 5
|
||||
\\expand: 5 to 20 failure!
|
||||
\\free : 5
|
||||
\\
|
||||
, fbs.getWritten());
|
||||
/// This allocator is used in front of another allocator and logs to `std.log`
|
||||
/// on every call to the allocator.
|
||||
/// For logging to a `std.io.Writer` see `std.heap.LogToWriterAllocator`
|
||||
pub fn loggingAllocator(parent_allocator: *Allocator) LoggingAllocator(.debug, .crit) {
|
||||
return LoggingAllocator(.debug, .crit).init(parent_allocator);
|
||||
}
|
||||
|
@ -599,4 +599,50 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\emergency(c):
|
||||
\\
|
||||
);
|
||||
|
||||
// It is required to override the log function in order to print to stdout instead of stderr
|
||||
cases.add("std.heap.LoggingAllocator logs to std.log",
|
||||
\\const std = @import("std");
|
||||
\\
|
||||
\\pub const log_level: std.log.Level = .debug;
|
||||
\\
|
||||
\\pub fn main() !void {
|
||||
\\ var allocator_buf: [10]u8 = undefined;
|
||||
\\ var fixedBufferAllocator = std.mem.validationWrap(std.heap.FixedBufferAllocator.init(&allocator_buf));
|
||||
\\ const allocator = &std.heap.loggingAllocator(&fixedBufferAllocator.allocator).allocator;
|
||||
\\
|
||||
\\ var a = try allocator.alloc(u8, 10);
|
||||
\\ a = allocator.shrink(a, 5);
|
||||
\\ try std.testing.expect(a.len == 5);
|
||||
\\ try std.testing.expectError(error.OutOfMemory, allocator.resize(a, 20));
|
||||
\\ allocator.free(a);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn log(
|
||||
\\ comptime level: std.log.Level,
|
||||
\\ comptime scope: @TypeOf(.EnumLiteral),
|
||||
\\ comptime format: []const u8,
|
||||
\\ args: anytype,
|
||||
\\) void {
|
||||
\\ const level_txt = switch (level) {
|
||||
\\ .emerg => "emergency",
|
||||
\\ .alert => "alert",
|
||||
\\ .crit => "critical",
|
||||
\\ .err => "error",
|
||||
\\ .warn => "warning",
|
||||
\\ .notice => "notice",
|
||||
\\ .info => "info",
|
||||
\\ .debug => "debug",
|
||||
\\ };
|
||||
\\ const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
|
||||
\\ const stdout = std.io.getStdOut().writer();
|
||||
\\ nosuspend stdout.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
|
||||
\\}
|
||||
,
|
||||
\\debug: alloc - success - len: 10, ptr_align: 1, len_align: 0
|
||||
\\debug: shrink - success - 10 to 5, len_align: 0, buf_align: 1
|
||||
\\critical: expand - failure: OutOfMemory - 5 to 20, len_align: 0, buf_align: 1
|
||||
\\debug: free - success - len: 5
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user