os-stats.zig
1 const std = @import("std"); 2 const utils = @import("utils"); 3 4 const fs = std.fs; 5 const os = std.os; 6 const system = os.system; 7 const print = std.debug.print; 8 9 const str = []const u8; 10 11 const CPUSample = struct { 12 /// Time spent in user mode 13 user: u64, 14 /// Time spent processing niced processes 15 nice: u64, 16 /// Time spent in system mode 17 system: u64, 18 /// Time spend idling 19 idle: u64, 20 // iowait: u64, 21 // irq: u64, 22 // softirq: u64, 23 // steal: u64, 24 // guest: u64, 25 // guest_nice: u64, 26 }; 27 28 pub const CPUInfo = struct { 29 usage: f16, // percentage, cannot exceed 100 30 /// CPU Archicture 31 arch: str, 32 /// CPU Model and speed 33 model: str, 34 }; 35 36 pub const OSInfo = struct { 37 type: str, 38 platform: str, 39 version: str, 40 release: str, 41 }; 42 43 pub const RAMStat = struct { 44 percent: u7, // shouldn't exceed 100, the smallest integer that can fit 100 is a u7 45 free: u32, // supports up to 4TB of ram 46 max: u32, 47 }; 48 49 /// get time since boot in second 50 pub fn getUptime() i64 { 51 var ts: os.timespec = undefined; 52 os.clock_gettime(os.linux.CLOCK.BOOTTIME, &ts) catch unreachable; 53 return @as(i64, ts.tv_sec); 54 } 55 56 fn parseCPUStatFields(field_line: []u8) CPUSample { 57 var it = std.mem.splitScalar(u8, field_line, ' '); 58 var result: CPUSample = undefined; 59 // TODO: Optimise using metaprogramming 60 result.user = std.fmt.parseUnsigned(u64, it.first(), 10) catch unreachable; 61 result.nice = std.fmt.parseUnsigned(u64, it.next().?, 10) catch unreachable; 62 result.system = std.fmt.parseUnsigned(u64, it.next().?, 10) catch unreachable; 63 result.idle = std.fmt.parseUnsigned(u64, it.next().?, 10) catch unreachable; 64 // result.iowait = std.fmt.parseUnsigned(u64, it.next().?, 10) catch unreachable; 65 // result.irq = std.fmt.parseUnsigned(u64, it.next().?, 10) catch unreachable; 66 // result.softirq = std.fmt.parseUnsigned(u64, it.next().?, 10) catch unreachable; 67 // result.steal = std.fmt.parseUnsigned(u64, it.next().?, 10) catch unreachable; 68 // result.guest = std.fmt.parseUnsigned(u64, it.next().?, 10) catch unreachable; 69 // result.guest_nice = std.fmt.parseUnsigned(u64, it.next().?, 10) catch unreachable; 70 71 return result; 72 } 73 74 fn getCPUSample() ?CPUSample { 75 // SEE: https://stackoverflow.com/questions/11356330/how-to-get-cpu-usage 76 // SEE: https://www.baeldung.com/linux/get-cpu-usage 77 var buf: [200]u8 = undefined; 78 79 var file_descriptor = fs.openFileAbsolute("/proc/stat", .{ .intended_io_mode = .blocking }) catch return null; 80 81 defer file_descriptor.close(); 82 const file = file_descriptor.reader(); 83 const line = (file.readUntilDelimiter(&buf, '\n') catch return null)[5..]; 84 const stats = parseCPUStatFields(line); 85 86 // print("{s}\n{any}", .{ line, stats }); 87 return stats; 88 } 89 90 /// Query system for CPU Usage 91 /// Returns an integer in between 0 and 100 92 pub fn getCPUPercent(sampleTime: ?u12) ?f16 { 93 const time_to_wait: u64 = std.time.ns_per_ms * @as(u64, @intCast((sampleTime orelse 100))); 94 const sample1 = getCPUSample() orelse return null; 95 std.time.sleep(time_to_wait); 96 const sample2 = getCPUSample() orelse return null; 97 98 const sample1_total_ticks = tick: { 99 var total: u64 = 0; 100 total += sample1.user; 101 total += sample1.nice; 102 total += sample1.system; 103 104 break :tick total; 105 }; 106 107 const sample2_total_ticks = tick: { 108 var total: u64 = 0; 109 total += sample2.user; 110 total += sample2.nice; 111 total += sample2.system; 112 113 break :tick total; 114 }; 115 116 const total_ticks = @abs(sample2_total_ticks - sample1_total_ticks); 117 const idle_ticks = @abs(sample2.idle - sample1.idle); 118 119 const percent = @as(f128, @floatFromInt(total_ticks)) / (@as(f128, @floatFromInt(idle_ticks)) + (@as(f128, @floatFromInt(total_ticks)))) * 100; 120 121 return @floatCast(percent); 122 } 123 124 /// Query RAM Information such as 125 /// - available memory 126 /// - available total 127 /// - RAM usage as percentage 128 pub fn getRAMStats() ?RAMStat { 129 var buff: [50]u8 = undefined; 130 var buf2: [50]u8 = undefined; 131 132 const mem_total_str = utils.parseKVPairOpenFile("/proc/meminfo", "MemTotal", &buff, ':') catch null; 133 const mem_available_str = utils.parseKVPairOpenFile("/proc/meminfo", "MemAvailable", &buf2, ':') catch null; 134 135 if (mem_total_str == null or mem_available_str == null) return null; 136 137 const mem_total = std.fmt.parseUnsigned(u26, mem_total_str.?[0 .. mem_total_str.?.len - 3], 10) catch return null; 138 const mem_available = std.fmt.parseUnsigned(u26, mem_available_str.?[0 .. mem_available_str.?.len - 3], 10) catch return null; 139 const percent: u7 = @intFromFloat(@floor((@as(f32, @floatFromInt(mem_available)) / @as(f32, @floatFromInt(mem_total)) * 100))); 140 141 return .{ 142 .percent = percent, 143 .free = mem_available, 144 .max = mem_total, 145 }; 146 }