this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

parse austin files

Altagos 4ff9173b dfcf8311

+294 -2
+265
src/austin.zig
··· 1 + const std = @import("std"); 2 + const mem = std.mem; 3 + 4 + pub const Sample = struct { 5 + /// Process ID 6 + pid: usize, 7 + /// Sub-interpreter ID, 8 + iid: usize, 9 + /// Thread ID 10 + tid: usize, 11 + frames: []FrameWrapper, 12 + metric: Metric, 13 + }; 14 + 15 + pub const SampleWrapper = union(enum) { 16 + full: Sample, 17 + metric: Metric, 18 + }; 19 + 20 + pub const Frame = struct { 21 + module: []const u8 = undefined, 22 + function: []const u8 = undefined, 23 + line_number: isize = 0, 24 + }; 25 + 26 + pub const FrameWrapper = union(enum) { 27 + call_stack: Frame, 28 + invalid: void, 29 + }; 30 + 31 + pub const Metric = struct { 32 + time_delta: usize, 33 + /// 1 / true for idle, 0 / false otherwise 34 + idle_state: bool, 35 + /// positive for memory allocations, negative for deallocations 36 + rss_memory_delta: isize, 37 + }; 38 + 39 + pub const Metadata = struct { 40 + austin: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 }, 41 + interval: usize = 0, 42 + mode: enum { full, unknown } = .unknown, // at the moment only full is supported, not memory 43 + memory: usize = 0, 44 + multiprocess: bool = false, 45 + duration: usize = 0, 46 + gc: ?usize = null, 47 + }; 48 + 49 + const MetadataFields = std.meta.FieldEnum(Metadata); 50 + 51 + pub const Profile = struct { 52 + meta: Metadata = .{}, 53 + samples: []SampleWrapper = undefined, 54 + 55 + pub fn deinit(self: *Profile, arena: mem.Allocator) void { 56 + arena.free(self.samples); 57 + } 58 + }; 59 + 60 + pub const Parser = struct { 61 + const ParseError = error{ 62 + InvalidSample, 63 + NoPID, 64 + InvalidPID, 65 + NoThreadInfo, 66 + NoIID, 67 + InvalidIID, 68 + NoTID, 69 + InvalidTID, 70 + NoModule, 71 + NoFunction, 72 + NoLineNumber, 73 + InvalidLineNumber, 74 + NoTimeDelta, 75 + NoIdleState, 76 + NoRSSMemoryDelta, 77 + }; 78 + 79 + arena: mem.Allocator, 80 + 81 + pub fn init(arena: mem.Allocator) Parser { 82 + return .{ .arena = arena }; 83 + } 84 + 85 + pub fn parse(self: *Parser, raw_reader: anytype) !Profile { 86 + var buffered_reader = std.io.bufferedReader(raw_reader); 87 + var reader = buffered_reader.reader(); 88 + 89 + var profile = Profile{}; 90 + 91 + var samples: std.ArrayList(SampleWrapper) = try .initCapacity(self.arena, 1_000); 92 + defer samples.deinit(); 93 + 94 + var reached_end: bool = false; 95 + 96 + var key_raw: std.ArrayList(u8) = .init(self.arena); 97 + var value_raw: std.ArrayList(u8) = .init(self.arena); 98 + 99 + var line: std.ArrayList(u8) = .init(self.arena); 100 + defer line.deinit(); 101 + 102 + var line_num: usize = 1; 103 + while (true) : (line_num += 1) { 104 + defer line.clearAndFree(); 105 + 106 + try reader.streamUntilDelimiter(line.writer(), '\n', null); 107 + 108 + if (line.items.len == 0) { 109 + if (reached_end) break; 110 + continue; 111 + } 112 + 113 + if (line.items[0] == '#') { 114 + // Metadata 115 + var key: MetadataFields = undefined; 116 + var state: enum { key, value } = .key; 117 + 118 + for (line.items) |val| { 119 + if (val == ' ' or val == '#') continue; 120 + 121 + switch (state) { 122 + .key => { 123 + if (val == ':') { 124 + state = .value; 125 + key = std.meta.stringToEnum(MetadataFields, key_raw.items) orelse return error.UnknownMetadataField; 126 + key_raw.clearAndFree(); 127 + } else try key_raw.append(val); 128 + }, 129 + .value => try value_raw.append(val), 130 + } 131 + } 132 + 133 + const value = value_raw.items; 134 + defer value_raw.clearAndFree(); 135 + 136 + switch (key) { 137 + .austin => profile.meta.austin = try .parse(value), 138 + .interval => profile.meta.interval = try std.fmt.parseUnsigned(usize, value, 0), 139 + .memory => profile.meta.memory = try std.fmt.parseUnsigned(usize, value, 0), 140 + .mode => profile.meta.mode = std.meta.stringToEnum(@TypeOf(profile.meta.mode), value) orelse return error.UnknownMetadataFieldValue, 141 + .multiprocess => profile.meta.multiprocess = std.mem.eql(u8, "on", value), 142 + .duration => { 143 + profile.meta.duration = try std.fmt.parseUnsigned(usize, value, 0); 144 + reached_end = true; 145 + }, 146 + .gc => { 147 + profile.meta.gc = try std.fmt.parseUnsigned(usize, value, 0); 148 + reached_end = true; 149 + }, 150 + } 151 + } else { 152 + // Sample 153 + const sample = self.parseSample(line, line_num) catch |err| { 154 + std.log.err("[file:{}] {}", .{ line_num, err }); 155 + if (err == ParseError.InvalidSample) continue; 156 + 157 + std.process.exit(1); 158 + }; 159 + 160 + try samples.append(sample); 161 + } 162 + } 163 + 164 + profile.samples = try samples.toOwnedSlice(); 165 + return profile; 166 + } 167 + 168 + const ParseSampleError = ParseError || mem.Allocator.Error || std.fmt.ParseIntError; 169 + 170 + fn parseSample(self: *Parser, line: std.ArrayList(u8), line_num: usize) ParseSampleError!SampleWrapper { 171 + var sample = try self.arena.create(Sample); 172 + 173 + // var line_it = std.mem.tokenizeScalar(u8, line.items, ' '); 174 + // var metric_raw: []const u8 = undefined; 175 + // var metric_start: usize = 0; 176 + // var i: usize = 0; 177 + // while (line_it.next()) |seg| : (i += 1) { 178 + // if (i > 0) metric_start += metric_raw.len; 179 + // metric_raw = seg; 180 + // } 181 + 182 + var line_it_back = mem.splitBackwardsScalar(u8, line.items, ' '); 183 + const metric_raw = line_it_back.next().?; 184 + 185 + if (!mem.containsAtLeast(u8, metric_raw, 2, ",")) return ParseError.InvalidSample; 186 + 187 + // Metric 188 + var metric = mem.tokenizeScalar(u8, metric_raw, ','); 189 + sample.metric = Metric{ 190 + .time_delta = try std.fmt.parseUnsigned(usize, metric.next() orelse return ParseError.NoTimeDelta, 0), 191 + .idle_state = try std.fmt.parseUnsigned(u8, metric.next() orelse return ParseError.NoIdleState, 0) == 1, 192 + .rss_memory_delta = try std.fmt.parseInt(isize, metric.next() orelse return ParseError.NoRSSMemoryDelta, 0), 193 + }; 194 + 195 + const data_raw = line.items[0 .. line.items.len - metric_raw.len - 1]; 196 + self.parseFullSample(sample, data_raw) catch |err| { 197 + std.log.err("Caught {} while parsing line {} returning metrics ({}):\n{s}", .{ err, line_num, sample.metric, line.items }); 198 + return SampleWrapper{ .metric = sample.metric }; 199 + }; 200 + 201 + // const data_raw = line_it.next() orelse return error.NoCallStack; 202 + // const metric_raw = line_it.next() orelse return error.NoMetrics; 203 + 204 + return SampleWrapper{ .full = sample.* }; 205 + } 206 + 207 + fn parseFullSample(self: *Parser, sample: *Sample, data_raw: []const u8) !void { 208 + var data = std.mem.tokenizeScalar(u8, data_raw, ';'); 209 + 210 + // PID 211 + const pid = data.next() orelse return ParseError.NoPID; 212 + sample.pid = std.fmt.parseUnsigned(usize, pid[1..], 0) catch return ParseError.InvalidPID; 213 + 214 + const thread_raw = data.next() orelse return ParseError.NoThreadInfo; 215 + var thread = std.mem.tokenizeScalar(u8, thread_raw, ':'); 216 + 217 + // IID 218 + const iid = thread.next() orelse return ParseError.NoIID; 219 + sample.iid = std.fmt.parseUnsigned(usize, iid[1..], 0) catch return ParseError.InvalidIID; 220 + 221 + // TID 222 + const tid = thread.next() orelse return ParseError.NoTID; 223 + sample.tid = std.fmt.parseUnsigned(usize, tid, 0) catch return ParseError.InvalidTID; 224 + 225 + // Frames 226 + var frames: std.ArrayList(FrameWrapper) = try .initCapacity(self.arena, 1); 227 + 228 + while (data.next()) |frame_raw| { 229 + // std.debug.print("{s}\n", .{frame_raw}); 230 + var frame = std.mem.tokenizeScalar(u8, frame_raw, ':'); 231 + const module = frame.next() orelse return ParseError.NoModule; 232 + 233 + if (mem.startsWith(u8, frame_raw, "::")) { 234 + try frames.append(FrameWrapper{ .call_stack = Frame{ 235 + .module = "", 236 + .function = "", 237 + .line_number = std.fmt.parseInt(isize, module, 0) catch return ParseError.InvalidLineNumber, 238 + } }); 239 + continue; 240 + } 241 + 242 + if (!mem.eql(u8, module, "INVALID")) { 243 + const function = frame.next() orelse { 244 + // std.debug.print("MODULE: {s} REST: {s}\n", .{ module, data.rest() }); 245 + return ParseError.NoFunction; 246 + }; 247 + const line_number = std.fmt.parseInt(isize, frame.next() orelse return ParseError.NoLineNumber, 0) catch return ParseError.InvalidLineNumber; 248 + 249 + var f: Frame = .{ .line_number = line_number }; 250 + f.module = try self.arena.alloc(u8, module.len); 251 + @memcpy(@constCast(f.module), module); 252 + 253 + f.function = try self.arena.alloc(u8, function.len); 254 + @memcpy(@constCast(f.function), function); 255 + 256 + try frames.append(FrameWrapper{ .call_stack = f }); 257 + } else { 258 + try frames.append(FrameWrapper{ .invalid = {} }); 259 + break; 260 + } 261 + } 262 + 263 + sample.frames = try frames.toOwnedSlice(); 264 + } 265 + };
+22 -2
src/main.zig
··· 1 1 const std = @import("std"); 2 - const args = @import("args"); 3 2 3 + const args = @import("args"); 4 4 const austin = @import("austin"); 5 5 6 6 const Options = struct { ··· 37 37 ) catch return; 38 38 defer options.deinit(); 39 39 40 - if (options.options.help or std.meta.eql(options.options, Options{})) { 40 + if (options.options.help) { 41 41 try args.printHelp( 42 42 Options, 43 43 Options.meta.name, 44 44 std.io.getStdOut().writer(), 45 45 ); 46 + return; 46 47 } 48 + 49 + var parser: austin.austin.Parser = .init(allocator); 50 + var profile: austin.austin.Profile = undefined; 51 + if (options.positionals.len == 0) { 52 + profile = try parser.parse(std.io.getStdIn().reader()); 53 + } else { 54 + const path = std.fs.cwd().realpathAlloc(allocator, options.positionals[0]) catch |err| { 55 + std.log.err("Invalid file path ({}): {s}", .{ err, options.positionals[0] }); 56 + return; 57 + }; 58 + defer allocator.free(path); 59 + 60 + const file = try std.fs.openFileAbsolute(path, .{}); 61 + defer file.close(); 62 + 63 + profile = try parser.parse(file.reader()); 64 + } 65 + 66 + std.log.info("num samples: {} - meta: {}", .{ profile.samples.len, profile.meta }); 47 67 }
+7
src/root.zig
··· 1 1 const std = @import("std"); 2 2 3 + pub const austin = @import("austin.zig"); 4 + 3 5 pub const Formats = enum { 4 6 chrome, 5 7 spall, 8 + austin, 6 9 }; 10 + 11 + test { 12 + _ = austin; 13 + }