tangled
alpha
login
or
join now
altagos.dev
/
austin-converter
0
fork
atom
this repo has no description
0
fork
atom
overview
issues
pulls
pipelines
Fix: memory leak
altagos.dev
8 months ago
79132158
b829759e
verified
This commit was signed with the committer's
known signature
.
altagos.dev
SSH Key Fingerprint:
SHA256:UbTjEcCZlc6GzQWLCuDK3D//HESWD2xFPkzue9XMras=
+132
-54
3 changed files
expand all
collapse all
unified
split
src
austin.zig
main.zig
util.zig
+59
-7
src/austin.zig
···
35
35
process: Process,
36
36
frames: []FrameWrapper,
37
37
metric: Metric,
38
38
+
39
39
+
fn deinit(self: *Sample, allocator: mem.Allocator) void {
40
40
+
for (self.frames) |frame| {
41
41
+
switch (frame) {
42
42
+
.invalid => {},
43
43
+
.call_stack => |stack| {
44
44
+
allocator.free(stack.module);
45
45
+
allocator.free(stack.function);
46
46
+
},
47
47
+
}
48
48
+
}
49
49
+
50
50
+
allocator.free(self.frames);
51
51
+
}
38
52
};
39
53
40
54
pub const PartialSample = struct {
41
55
process: Process,
42
56
frames: []FrameWrapper,
57
57
+
58
58
+
fn deinit(self: *PartialSample, allocator: mem.Allocator) void {
59
59
+
for (self.frames) |frame| {
60
60
+
switch (frame) {
61
61
+
.invalid => {},
62
62
+
.call_stack => |stack| {
63
63
+
allocator.free(stack.module);
64
64
+
allocator.free(stack.function);
65
65
+
},
66
66
+
}
67
67
+
}
68
68
+
69
69
+
allocator.free(self.frames);
70
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
77
+
78
78
+
fn deinit(self: *SampleWrapper, allocator: mem.Allocator) void {
79
79
+
switch (self.*) {
80
80
+
.metric => {},
81
81
+
.full => |*sample| {
82
82
+
sample.deinit(allocator);
83
83
+
},
84
84
+
.partial => |*sample| sample.deinit(allocator),
85
85
+
}
86
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
104
+
_arena: mem.Allocator,
66
105
67
67
-
pub fn deinit(self: *Profile, arena: mem.Allocator) void {
68
68
-
arena.free(self.samples);
106
106
+
pub fn deinit(self: *Profile) void {
107
107
+
for (self.samples) |*sample| {
108
108
+
sample.deinit(self._arena);
109
109
+
}
110
110
+
111
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
101
-
var profile = Profile{};
144
144
+
var profile = Profile{ ._arena = self.arena };
145
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
178
-
var sample = try self.arena.create(Sample);
222
222
+
var sample = Sample{
223
223
+
.frames = undefined,
224
224
+
.metric = undefined,
225
225
+
.process = undefined,
226
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
186
-
self.parseFullSample(sample, data_raw, line_num) catch |err| {
234
234
+
// defer self.arena.destroy(sample);
235
235
+
236
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
235
-
self.parseFullSample(sample, data_raw, line_num) catch |err| {
285
285
+
self.parseFullSample(&sample, data_raw, line_num) catch |err| {
286
286
+
// defer self.arena.destroy(sample);
287
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
254
-
return SampleWrapper{ .full = sample.* };
306
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
2
+
const builtin = @import("builtin");
3
3
+
const native_os = builtin.os;
2
4
3
5
const args = @import("args");
4
6
const austin = @import("austin");
5
7
8
8
+
const util = @import("util.zig");
9
9
+
6
10
pub const std_options = std.Options{
7
11
.log_level = .debug,
8
8
-
.logFn = logFn,
12
12
+
.logFn = util.logFn,
9
13
};
10
14
11
15
const Options = struct {
···
30
34
};
31
35
};
32
36
37
37
+
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
38
38
+
33
39
pub fn main() !void {
34
34
-
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
35
35
-
defer arena.deinit();
36
36
-
const allocator = arena.allocator();
40
40
+
const gpa, const is_debug = gpa: {
41
41
+
if (native_os.tag == .wasi) break :gpa .{ std.heap.wasm_allocator, false };
42
42
+
break :gpa switch (builtin.mode) {
43
43
+
.Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true },
44
44
+
.ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false },
45
45
+
};
46
46
+
};
47
47
+
defer if (is_debug) {
48
48
+
std.debug.assert(debug_allocator.deinit() == .ok);
49
49
+
};
37
50
38
51
const options = args.parseForCurrentProcess(
39
52
Options,
40
40
-
allocator,
53
53
+
gpa,
41
54
.print,
42
55
) catch return;
43
56
defer options.deinit();
···
51
64
return;
52
65
}
53
66
67
67
+
try convert(gpa, &options);
68
68
+
}
69
69
+
70
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
72
+
defer progress.end();
55
73
56
74
var parser: austin.austin.Parser = .init(allocator);
57
75
var profile: austin.austin.Profile = undefined;
76
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
63
-
if (options.positionals.len == 0) {
82
82
+
if (opts.positionals.len == 0) {
64
83
profile = try parser.parse(std.io.getStdIn().reader(), &node);
65
84
} else {
66
66
-
const path = std.fs.cwd().realpathAlloc(allocator, options.positionals[0]) catch |err| {
67
67
-
std.log.err("Invalid file path ({}): {s}", .{ err, options.positionals[0] });
85
85
+
const path = std.fs.cwd().realpathAlloc(allocator, opts.positionals[0]) catch |err| {
86
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
81
-
82
82
-
pub fn logFn(
83
83
-
comptime message_level: std.log.Level,
84
84
-
comptime scope: @TypeOf(.enum_literal),
85
85
-
comptime format: []const u8,
86
86
-
args_: anytype,
87
87
-
) void {
88
88
-
const prefix = comptime blk: {
89
89
-
const level_text = lt: {
90
90
-
switch (message_level) {
91
91
-
.err => {
92
92
-
break :lt "\x1b[31merror \x1b[0m";
93
93
-
},
94
94
-
.warn => {
95
95
-
break :lt "\x1b[33mwarning\x1b[0m";
96
96
-
},
97
97
-
.info => {
98
98
-
break :lt "\x1b[32minfo \x1b[0m";
99
99
-
},
100
100
-
.debug => {
101
101
-
break :lt "\x1b[34mdebug \x1b[0m";
102
102
-
},
103
103
-
}
104
104
-
};
105
105
-
const scope_prefix = scope: {
106
106
-
if (scope == .default) {
107
107
-
break :scope "";
108
108
-
}
109
109
-
break :scope " \x1b[90m[\x1b[0m" ++ @tagName(scope) ++ "\x1b[90m]\x1b[0m";
110
110
-
};
111
111
-
112
112
-
break :blk level_text ++ scope_prefix ++ "\x1b[90m \x1b[0m";
113
113
-
};
114
114
-
115
115
-
std.debug.lockStdErr();
116
116
-
defer std.debug.unlockStdErr();
117
117
-
const stderr = std.io.getStdErr().writer();
118
118
-
nosuspend stderr.print(prefix ++ format ++ "\x1b[0m\n", args_) catch return;
119
119
-
}
+46
src/util.zig
···
1
1
+
const std = @import("std");
2
2
+
3
3
+
pub fn logFn(
4
4
+
comptime message_level: std.log.Level,
5
5
+
comptime scope: @TypeOf(.enum_literal),
6
6
+
comptime format: []const u8,
7
7
+
args_: anytype,
8
8
+
) void {
9
9
+
const prefix = comptime blk: {
10
10
+
const level_text = lt: {
11
11
+
switch (message_level) {
12
12
+
.err => {
13
13
+
break :lt "\x1b[31merror \x1b[0m";
14
14
+
},
15
15
+
.warn => {
16
16
+
break :lt "\x1b[33mwarning\x1b[0m";
17
17
+
},
18
18
+
.info => {
19
19
+
break :lt "\x1b[32minfo \x1b[0m";
20
20
+
},
21
21
+
.debug => {
22
22
+
break :lt "\x1b[34mdebug \x1b[0m";
23
23
+
},
24
24
+
}
25
25
+
};
26
26
+
const scope_prefix = scope: {
27
27
+
if (scope == .default) {
28
28
+
break :scope "";
29
29
+
}
30
30
+
break :scope " \x1b[90m[\x1b[0m" ++ @tagName(scope) ++ "\x1b[90m]\x1b[0m";
31
31
+
};
32
32
+
33
33
+
break :blk level_text ++ scope_prefix ++ "\x1b[90m \x1b[0m";
34
34
+
};
35
35
+
36
36
+
const stderr = std.io.getStdErr().writer();
37
37
+
var bw = std.io.bufferedWriter(stderr);
38
38
+
const writer = bw.writer();
39
39
+
40
40
+
std.debug.lockStdErr();
41
41
+
defer std.debug.unlockStdErr();
42
42
+
nosuspend {
43
43
+
writer.print(prefix ++ format ++ "\x1b[0m\n", args_) catch return;
44
44
+
bw.flush() catch return;
45
45
+
}
46
46
+
}