this repo has no description

Fix: memory leak

altagos.dev 79132158 b829759e

verified
+132 -54
+59 -7
src/austin.zig
··· 35 35 process: Process, 36 36 frames: []FrameWrapper, 37 37 metric: Metric, 38 + 39 + fn deinit(self: *Sample, allocator: mem.Allocator) void { 40 + for (self.frames) |frame| { 41 + switch (frame) { 42 + .invalid => {}, 43 + .call_stack => |stack| { 44 + allocator.free(stack.module); 45 + allocator.free(stack.function); 46 + }, 47 + } 48 + } 49 + 50 + allocator.free(self.frames); 51 + } 38 52 }; 39 53 40 54 pub const PartialSample = struct { 41 55 process: Process, 42 56 frames: []FrameWrapper, 57 + 58 + fn deinit(self: *PartialSample, allocator: mem.Allocator) void { 59 + for (self.frames) |frame| { 60 + switch (frame) { 61 + .invalid => {}, 62 + .call_stack => |stack| { 63 + allocator.free(stack.module); 64 + allocator.free(stack.function); 65 + }, 66 + } 67 + } 68 + 69 + allocator.free(self.frames); 70 + } 43 71 }; 44 72 45 73 pub const SampleWrapper = union(enum) { 46 74 full: Sample, 47 75 partial: PartialSample, 48 76 metric: Metric, 77 + 78 + fn deinit(self: *SampleWrapper, allocator: mem.Allocator) void { 79 + switch (self.*) { 80 + .metric => {}, 81 + .full => |*sample| { 82 + sample.deinit(allocator); 83 + }, 84 + .partial => |*sample| sample.deinit(allocator), 85 + } 86 + } 49 87 }; 50 88 51 89 pub const Metadata = struct { ··· 63 101 pub const Profile = struct { 64 102 meta: Metadata = .{}, 65 103 samples: []SampleWrapper = undefined, 104 + _arena: mem.Allocator, 66 105 67 - pub fn deinit(self: *Profile, arena: mem.Allocator) void { 68 - arena.free(self.samples); 106 + pub fn deinit(self: *Profile) void { 107 + for (self.samples) |*sample| { 108 + sample.deinit(self._arena); 109 + } 110 + 111 + self._arena.free(self.samples); 69 112 } 70 113 }; 71 114 ··· 98 141 var buffered_reader = std.io.bufferedReader(raw_reader); 99 142 var reader = buffered_reader.reader(); 100 143 101 - var profile = Profile{}; 144 + var profile = Profile{ ._arena = self.arena }; 145 + errdefer profile.deinit(); 102 146 103 147 var samples: std.ArrayList(SampleWrapper) = try .initCapacity(self.arena, 1_000); 104 148 defer samples.deinit(); ··· 175 219 const ParseSampleError = ParseError || mem.Allocator.Error || std.fmt.ParseIntError; 176 220 177 221 fn parseSample(self: *Parser, line: std.ArrayList(u8), line_num: usize) ParseSampleError!SampleWrapper { 178 - var sample = try self.arena.create(Sample); 222 + var sample = Sample{ 223 + .frames = undefined, 224 + .metric = undefined, 225 + .process = undefined, 226 + }; 179 227 180 228 var line_it_back = mem.splitBackwardsScalar(u8, line.items, ' '); 181 229 const metric_raw = line_it_back.next().?; ··· 183 231 const data_raw = line.items[0 .. line.items.len - metric_raw.len - 1]; 184 232 185 233 if (!mem.containsAtLeast(u8, metric_raw, 2, ",")) { 186 - self.parseFullSample(sample, data_raw, line_num) catch |err| { 234 + // defer self.arena.destroy(sample); 235 + 236 + self.parseFullSample(&sample, data_raw, line_num) catch |err| { 187 237 switch (err) { 188 238 ParseError.NoPID, 189 239 ParseError.InvalidPID, ··· 232 282 ), 233 283 }; 234 284 235 - self.parseFullSample(sample, data_raw, line_num) catch |err| { 285 + self.parseFullSample(&sample, data_raw, line_num) catch |err| { 286 + // defer self.arena.destroy(sample); 287 + 236 288 log.debug( 237 289 "Caught {} while parsing line {}, returning metrics " ++ 238 290 "(time delta: {}, idle state: {}, rss memory delta: {}):\n" ++ ··· 251 303 return SampleWrapper{ .metric = sample.metric }; 252 304 }; 253 305 254 - return SampleWrapper{ .full = sample.* }; 306 + return SampleWrapper{ .full = sample }; 255 307 } 256 308 257 309 fn parseFullSample(self: *Parser, sample: *Sample, data_raw: []const u8, line_num: usize) !void {
+27 -47
src/main.zig
··· 1 1 const std = @import("std"); 2 + const builtin = @import("builtin"); 3 + const native_os = builtin.os; 2 4 3 5 const args = @import("args"); 4 6 const austin = @import("austin"); 5 7 8 + const util = @import("util.zig"); 9 + 6 10 pub const std_options = std.Options{ 7 11 .log_level = .debug, 8 - .logFn = logFn, 12 + .logFn = util.logFn, 9 13 }; 10 14 11 15 const Options = struct { ··· 30 34 }; 31 35 }; 32 36 37 + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; 38 + 33 39 pub fn main() !void { 34 - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); 35 - defer arena.deinit(); 36 - const allocator = arena.allocator(); 40 + const gpa, const is_debug = gpa: { 41 + if (native_os.tag == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; 42 + break :gpa switch (builtin.mode) { 43 + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, 44 + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, 45 + }; 46 + }; 47 + defer if (is_debug) { 48 + std.debug.assert(debug_allocator.deinit() == .ok); 49 + }; 37 50 38 51 const options = args.parseForCurrentProcess( 39 52 Options, 40 - allocator, 53 + gpa, 41 54 .print, 42 55 ) catch return; 43 56 defer options.deinit(); ··· 51 64 return; 52 65 } 53 66 67 + try convert(gpa, &options); 68 + } 69 + 70 + fn convert(allocator: std.mem.Allocator, opts: *const args.ParseArgsResult(Options, null)) !void { 54 71 var progress = std.Progress.start(.{ .root_name = "Austin Converter" }); 72 + defer progress.end(); 55 73 56 74 var parser: austin.austin.Parser = .init(allocator); 57 75 var profile: austin.austin.Profile = undefined; 76 + defer profile.deinit(); 58 77 59 78 { 60 79 var node = progress.start("Parsing Austin file", 0); 61 80 defer node.end(); 62 81 63 - if (options.positionals.len == 0) { 82 + if (opts.positionals.len == 0) { 64 83 profile = try parser.parse(std.io.getStdIn().reader(), &node); 65 84 } else { 66 - const path = std.fs.cwd().realpathAlloc(allocator, options.positionals[0]) catch |err| { 67 - std.log.err("Invalid file path ({}): {s}", .{ err, options.positionals[0] }); 85 + const path = std.fs.cwd().realpathAlloc(allocator, opts.positionals[0]) catch |err| { 86 + std.log.err("Invalid file path ({}): {s}", .{ err, opts.positionals[0] }); 68 87 return; 69 88 }; 70 89 defer allocator.free(path); ··· 78 97 79 98 std.log.info("num samples: {} - meta: {}", .{ profile.samples.len, profile.meta }); 80 99 } 81 - 82 - pub fn logFn( 83 - comptime message_level: std.log.Level, 84 - comptime scope: @TypeOf(.enum_literal), 85 - comptime format: []const u8, 86 - args_: anytype, 87 - ) void { 88 - const prefix = comptime blk: { 89 - const level_text = lt: { 90 - switch (message_level) { 91 - .err => { 92 - break :lt "\x1b[31merror \x1b[0m"; 93 - }, 94 - .warn => { 95 - break :lt "\x1b[33mwarning\x1b[0m"; 96 - }, 97 - .info => { 98 - break :lt "\x1b[32minfo \x1b[0m"; 99 - }, 100 - .debug => { 101 - break :lt "\x1b[34mdebug \x1b[0m"; 102 - }, 103 - } 104 - }; 105 - const scope_prefix = scope: { 106 - if (scope == .default) { 107 - break :scope ""; 108 - } 109 - break :scope " \x1b[90m[\x1b[0m" ++ @tagName(scope) ++ "\x1b[90m]\x1b[0m"; 110 - }; 111 - 112 - break :blk level_text ++ scope_prefix ++ "\x1b[90m \x1b[0m"; 113 - }; 114 - 115 - std.debug.lockStdErr(); 116 - defer std.debug.unlockStdErr(); 117 - const stderr = std.io.getStdErr().writer(); 118 - nosuspend stderr.print(prefix ++ format ++ "\x1b[0m\n", args_) catch return; 119 - }
+46
src/util.zig
··· 1 + const std = @import("std"); 2 + 3 + pub fn logFn( 4 + comptime message_level: std.log.Level, 5 + comptime scope: @TypeOf(.enum_literal), 6 + comptime format: []const u8, 7 + args_: anytype, 8 + ) void { 9 + const prefix = comptime blk: { 10 + const level_text = lt: { 11 + switch (message_level) { 12 + .err => { 13 + break :lt "\x1b[31merror \x1b[0m"; 14 + }, 15 + .warn => { 16 + break :lt "\x1b[33mwarning\x1b[0m"; 17 + }, 18 + .info => { 19 + break :lt "\x1b[32minfo \x1b[0m"; 20 + }, 21 + .debug => { 22 + break :lt "\x1b[34mdebug \x1b[0m"; 23 + }, 24 + } 25 + }; 26 + const scope_prefix = scope: { 27 + if (scope == .default) { 28 + break :scope ""; 29 + } 30 + break :scope " \x1b[90m[\x1b[0m" ++ @tagName(scope) ++ "\x1b[90m]\x1b[0m"; 31 + }; 32 + 33 + break :blk level_text ++ scope_prefix ++ "\x1b[90m \x1b[0m"; 34 + }; 35 + 36 + const stderr = std.io.getStdErr().writer(); 37 + var bw = std.io.bufferedWriter(stderr); 38 + const writer = bw.writer(); 39 + 40 + std.debug.lockStdErr(); 41 + defer std.debug.unlockStdErr(); 42 + nosuspend { 43 + writer.print(prefix ++ format ++ "\x1b[0m\n", args_) catch return; 44 + bw.flush() catch return; 45 + } 46 + }