mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-03 22:28:46 +00:00
[sys/info] Initial version.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
//+build arm32, arm64
|
||||
package sysinfo
|
||||
|
||||
// TODO: Set up an enum with the ARM equivalent of the above.
|
||||
CPU_Feature :: enum u64 {}
|
||||
|
||||
cpu_features: Maybe(CPU_Feature)
|
||||
cpu_name: Maybe(string)
|
||||
|
||||
@(init, private)
|
||||
init_cpu_features :: proc "c" () {
|
||||
}
|
||||
|
||||
@(private)
|
||||
_cpu_name_buf: [72]u8
|
||||
|
||||
@(init, private)
|
||||
init_cpu_name :: proc "c" () {
|
||||
when ODIN_ARCH == .arm32 {
|
||||
copy(_cpu_name_buf, "ARM")
|
||||
cpu_name = string(_cpu_name_buf[:3])
|
||||
} else {
|
||||
copy(_cpu_name_buf, "ARM64")
|
||||
cpu_name = string(_cpu_name_buf[:5])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
//+build i386, amd64
|
||||
package sysinfo
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
// cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
|
||||
cpuid :: intrinsics.x86_cpuid
|
||||
|
||||
// xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
|
||||
xgetbv :: intrinsics.x86_xgetbv
|
||||
|
||||
CPU_Feature :: enum u64 {
|
||||
aes, // AES hardware implementation (AES NI)
|
||||
adx, // Multi-precision add-carry instruction extensions
|
||||
avx, // Advanced vector extension
|
||||
avx2, // Advanced vector extension 2
|
||||
bmi1, // Bit manipulation instruction set 1
|
||||
bmi2, // Bit manipulation instruction set 2
|
||||
erms, // Enhanced REP for MOVSB and STOSB
|
||||
fma, // Fused-multiply-add instructions
|
||||
os_xsave, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
|
||||
pclmulqdq, // PCLMULQDQ instruction - most often used for AES-GCM
|
||||
popcnt, // Hamming weight instruction POPCNT.
|
||||
rdrand, // RDRAND instruction (on-chip random number generator)
|
||||
rdseed, // RDSEED instruction (on-chip random number generator)
|
||||
sse2, // Streaming SIMD extension 2 (always available on amd64)
|
||||
sse3, // Streaming SIMD extension 3
|
||||
ssse3, // Supplemental streaming SIMD extension 3
|
||||
sse41, // Streaming SIMD extension 4 and 4.1
|
||||
sse42, // Streaming SIMD extension 4 and 4.2
|
||||
}
|
||||
|
||||
CPU_Features :: distinct bit_set[CPU_Feature; u64]
|
||||
|
||||
cpu_features: Maybe(CPU_Features)
|
||||
cpu_name: Maybe(string)
|
||||
|
||||
@(init, private)
|
||||
init_cpu_features :: proc "c" () {
|
||||
is_set :: #force_inline proc "c" (hwc: u32, value: u32) -> bool {
|
||||
return hwc&value != 0
|
||||
}
|
||||
try_set :: #force_inline proc "c" (set: ^CPU_Features, feature: CPU_Feature, hwc: u32, value: u32) {
|
||||
if is_set(hwc, value) {
|
||||
set^ += {feature}
|
||||
}
|
||||
}
|
||||
|
||||
max_id, _, _, _ := cpuid(0, 0)
|
||||
if max_id < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
set: CPU_Features
|
||||
|
||||
_, _, ecx1, edx1 := cpuid(1, 0)
|
||||
|
||||
try_set(&set, .sse2, 26, edx1)
|
||||
try_set(&set, .sse3, 0, ecx1)
|
||||
try_set(&set, .pclmulqdq, 1, ecx1)
|
||||
try_set(&set, .ssse3, 9, ecx1)
|
||||
try_set(&set, .fma, 12, ecx1)
|
||||
try_set(&set, .sse41, 19, ecx1)
|
||||
try_set(&set, .sse42, 20, ecx1)
|
||||
try_set(&set, .popcnt, 23, ecx1)
|
||||
try_set(&set, .aes, 25, ecx1)
|
||||
try_set(&set, .os_xsave, 27, ecx1)
|
||||
try_set(&set, .rdrand, 30, ecx1)
|
||||
|
||||
os_supports_avx := false
|
||||
if .os_xsave in set {
|
||||
eax, _ := xgetbv(0)
|
||||
os_supports_avx = is_set(1, eax) && is_set(2, eax)
|
||||
}
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx, 28, ecx1)
|
||||
}
|
||||
|
||||
if max_id < 7 {
|
||||
return
|
||||
}
|
||||
|
||||
_, ebx7, _, _ := cpuid(7, 0)
|
||||
try_set(&set, .bmi1, 3, ebx7)
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx2, 5, ebx7)
|
||||
}
|
||||
try_set(&set, .bmi2, 8, ebx7)
|
||||
try_set(&set, .erms, 9, ebx7)
|
||||
try_set(&set, .rdseed, 18, ebx7)
|
||||
try_set(&set, .adx, 19, ebx7)
|
||||
|
||||
cpu_features = set
|
||||
}
|
||||
|
||||
@(private)
|
||||
_cpu_name_buf: [72]u8
|
||||
|
||||
@(init, private)
|
||||
init_cpu_name :: proc "c" () {
|
||||
number_of_extended_ids, _, _, _ := cpuid(0x8000_0000, 0)
|
||||
if number_of_extended_ids < 0x8000_0004 {
|
||||
return
|
||||
}
|
||||
|
||||
_buf := transmute(^[0x12]u32)&_cpu_name_buf
|
||||
_buf[ 0], _buf[ 1], _buf[ 2], _buf[ 3] = cpuid(0x8000_0002, 0)
|
||||
_buf[ 4], _buf[ 5], _buf[ 6], _buf[ 7] = cpuid(0x8000_0003, 0)
|
||||
_buf[ 8], _buf[ 9], _buf[10], _buf[11] = cpuid(0x8000_0004, 0)
|
||||
|
||||
// Some CPUs like may include leading or trailing spaces. Trim them.
|
||||
// e.g. ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz`
|
||||
|
||||
brand := string(_cpu_name_buf[:])
|
||||
for len(brand) > 0 && brand[0] == 0 || brand[0] == ' ' {
|
||||
brand = brand[1:]
|
||||
}
|
||||
for len(brand) > 0 && brand[len(brand) - 1] == 0 || brand[len(brand) - 1] == ' ' {
|
||||
brand = brand[:len(brand) - 1]
|
||||
}
|
||||
cpu_name = brand
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// +build darwin
|
||||
package sysinfo
|
||||
|
||||
import sys "core:sys/darwin"
|
||||
import "core:intrinsics"
|
||||
|
||||
@(init, private)
|
||||
init_os_version :: proc "c" () {
|
||||
os_version = {}
|
||||
}
|
||||
|
||||
@(init)
|
||||
init_ram :: proc() {
|
||||
// Retrieve RAM info using `sysinfo`
|
||||
|
||||
CTL_HW :: 6
|
||||
HW_MEMSIZE :: 24
|
||||
|
||||
sysctls := []int{CTL_HW, HW_MEMSIZE}
|
||||
|
||||
mem_size: i64
|
||||
|
||||
if intrinsics.syscall(
|
||||
uintptr(sys.System_Call_Number.sysctl),
|
||||
uintptr(raw_data(sysctls)), uintptr(len(sysctls)),
|
||||
uintptr(&mem_size), uintptr(size_of(mem_size))) == 0 {
|
||||
return
|
||||
}
|
||||
ram.total_ram = int(mem_size)
|
||||
}
|
||||
|
||||
@(private)
|
||||
sysctl :: proc(leaf: int)
|
||||
@@ -0,0 +1,54 @@
|
||||
// +build linux
|
||||
package sysinfo
|
||||
|
||||
import "core:c"
|
||||
import sys "core:sys/unix"
|
||||
import "core:intrinsics"
|
||||
// import "core:fmt"
|
||||
|
||||
@(init, private)
|
||||
init_os_version :: proc "c" () {
|
||||
os_version = {}
|
||||
}
|
||||
|
||||
Sys_Info :: struct {
|
||||
uptime: c.long, // Seconds since boot
|
||||
loads: [3]c.long, // 1, 5, 15 minute load averages
|
||||
totalram: c.ulong, // Total usable main memory size
|
||||
freeram: c.ulong, // Available memory size
|
||||
sharedram: c.ulong, // Amount of shared memory
|
||||
bufferram: c.ulong, // Memory used by buffers
|
||||
totalswap: c.ulong, // Total swap space size
|
||||
freeswap: c.ulong, // Swap space still available
|
||||
procs: c.ushort, // Number of current processes
|
||||
totalhigh: c.ulong, // Total high memory size
|
||||
freehigh: c.ulong, // Available high memory size
|
||||
mem_unit: c.int, // Memory unit size in bytes
|
||||
_padding: [20 - (2 * size_of(c.long)) - size_of(c.int)]u8,
|
||||
}
|
||||
|
||||
get_sysinfo :: proc "c" () -> (res: Sys_Info, ok: bool) {
|
||||
si: Sys_Info
|
||||
err := intrinsics.syscall(sys.SYS_sysinfo, uintptr(rawptr(&si)))
|
||||
if err != 0 {
|
||||
// Unable to retrieve sysinfo
|
||||
return {}, false
|
||||
}
|
||||
return si, true
|
||||
}
|
||||
|
||||
@(init)
|
||||
init_ram :: proc() {
|
||||
// Retrieve RAM info using `sysinfo`
|
||||
si, ok := get_sysinfo()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
ram = RAM{
|
||||
total_ram = int(si.totalram) * int(si.mem_unit),
|
||||
free_ram = int(si.freeram) * int(si.mem_unit),
|
||||
total_swap = int(si.totalswap) * int(si.mem_unit),
|
||||
free_swap = int(si.freeswap) * int(si.mem_unit),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
// +build windows
|
||||
package sysinfo
|
||||
|
||||
import sys "core:sys/windows"
|
||||
import "core:intrinsics"
|
||||
import "core:strings"
|
||||
|
||||
|
||||
@(private)
|
||||
version_string_buf: [1024]u8
|
||||
|
||||
@(init, private)
|
||||
init_os_version :: proc () {
|
||||
/*
|
||||
NOTE(Jeroen):
|
||||
`GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10.
|
||||
`RtlGetVersion` will return the true version.
|
||||
|
||||
Rather than include the WinDDK, we ask the kernel directly.
|
||||
`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release)
|
||||
|
||||
*/
|
||||
osvi: sys.OSVERSIONINFOEXW
|
||||
osvi.dwOSVersionInfoSize = size_of(osvi)
|
||||
status := sys.RtlGetVersion(&osvi)
|
||||
|
||||
if status != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
product_type: sys.Windows_Product_Type
|
||||
sys.GetProductInfo(
|
||||
osvi.dwMajorVersion, osvi.dwMinorVersion,
|
||||
u32(osvi.wServicePackMajor), u32(osvi.wServicePackMinor),
|
||||
&product_type,
|
||||
)
|
||||
|
||||
os_version = {
|
||||
platform = .Windows,
|
||||
major = int(osvi.dwMajorVersion),
|
||||
minor = int(osvi.dwMinorVersion),
|
||||
}
|
||||
os_version.build[0] = int(osvi.dwBuildNumber)
|
||||
|
||||
b := strings.builder_from_bytes(version_string_buf[:])
|
||||
|
||||
strings.write_string(&b, "Windows ")
|
||||
|
||||
switch osvi.dwMajorVersion {
|
||||
case 10:
|
||||
switch osvi.wProductType {
|
||||
case 1: // VER_NT_WORKSTATION:
|
||||
if osvi.dwBuildNumber < 22000 {
|
||||
strings.write_string(&b, "10 ")
|
||||
} else {
|
||||
strings.write_string(&b, "11 ")
|
||||
}
|
||||
format_windows_product_type(&b, product_type)
|
||||
|
||||
case: // Server or Domain Controller
|
||||
switch osvi.dwBuildNumber {
|
||||
case 14393:
|
||||
strings.write_string(&b, "2016 Server")
|
||||
case 17763:
|
||||
strings.write_string(&b, "2019 Server")
|
||||
case 20348:
|
||||
strings.write_string(&b, "2022 Server")
|
||||
case:
|
||||
strings.write_string(&b, "Unknown Server")
|
||||
}
|
||||
}
|
||||
|
||||
case 6:
|
||||
switch osvi.dwMinorVersion {
|
||||
case 0:
|
||||
switch osvi.wProductType {
|
||||
case 1: // VER_NT_WORKSTATION
|
||||
strings.write_string(&b, "Windows Vista ")
|
||||
format_windows_product_type(&b, product_type)
|
||||
case 3:
|
||||
strings.write_string(&b, "Windows Server 2008")
|
||||
}
|
||||
|
||||
case 1:
|
||||
switch osvi.wProductType {
|
||||
case 1: // VER_NT_WORKSTATION:
|
||||
strings.write_string(&b, "Windows 7 ")
|
||||
format_windows_product_type(&b, product_type)
|
||||
case 3:
|
||||
strings.write_string(&b, "Windows Server 2008 R2")
|
||||
}
|
||||
|
||||
case 2:
|
||||
switch osvi.wProductType {
|
||||
case 1: // VER_NT_WORKSTATION:
|
||||
strings.write_string(&b, "Windows 8 ")
|
||||
format_windows_product_type(&b, product_type)
|
||||
case 3:
|
||||
strings.write_string(&b, "Windows Server 2012")
|
||||
}
|
||||
|
||||
case 3:
|
||||
switch osvi.wProductType {
|
||||
case 1: // VER_NT_WORKSTATION:
|
||||
strings.write_string(&b, "Windows 8.1 ")
|
||||
format_windows_product_type(&b, product_type)
|
||||
case 3:
|
||||
strings.write_string(&b, "Windows Server 2012 R2")
|
||||
}
|
||||
}
|
||||
|
||||
case 5:
|
||||
switch osvi.dwMinorVersion {
|
||||
case 0:
|
||||
strings.write_string(&b, "Windows 2000")
|
||||
case 1:
|
||||
strings.write_string(&b, "Windows XP")
|
||||
case 2:
|
||||
strings.write_string(&b, "Windows Server 2003")
|
||||
}
|
||||
}
|
||||
|
||||
// Grab DisplayVersion
|
||||
os_version.version = format_display_version(&b)
|
||||
|
||||
// Grab build number and UBR
|
||||
os_version.build[1] = format_build_number(&b, int(osvi.dwBuildNumber))
|
||||
|
||||
// Finish the string
|
||||
os_version.as_string = strings.to_string(b)
|
||||
|
||||
format_windows_product_type :: proc (b: ^strings.Builder, prod_type: sys.Windows_Product_Type) {
|
||||
#partial switch prod_type {
|
||||
case .ULTIMATE:
|
||||
strings.write_string(b, "Ultimate")
|
||||
|
||||
case .HOME_BASIC:
|
||||
strings.write_string(b, "Home Basic")
|
||||
|
||||
case .HOME_PREMIUM:
|
||||
strings.write_string(b, "Home Premium")
|
||||
|
||||
case .ENTERPRISE:
|
||||
strings.write_string(b, "Enterprise")
|
||||
|
||||
case .CORE:
|
||||
strings.write_string(b, "Home Basic")
|
||||
|
||||
case .HOME_BASIC_N:
|
||||
strings.write_string(b, "Home Basic N")
|
||||
|
||||
case .EDUCATION:
|
||||
strings.write_string(b, "Education")
|
||||
|
||||
case .EDUCATION_N:
|
||||
strings.write_string(b, "Education N")
|
||||
|
||||
case .BUSINESS:
|
||||
strings.write_string(b, "Business")
|
||||
|
||||
case .STANDARD_SERVER:
|
||||
strings.write_string(b, "Standard Server")
|
||||
|
||||
case .DATACENTER_SERVER:
|
||||
strings.write_string(b, "Datacenter")
|
||||
|
||||
case .SMALLBUSINESS_SERVER:
|
||||
strings.write_string(b, "Windows Small Business Server")
|
||||
|
||||
case .ENTERPRISE_SERVER:
|
||||
strings.write_string(b, "Enterprise Server")
|
||||
|
||||
case .STARTER:
|
||||
strings.write_string(b, "Starter")
|
||||
|
||||
case .DATACENTER_SERVER_CORE:
|
||||
strings.write_string(b, "Datacenter Server Core")
|
||||
|
||||
case .STANDARD_SERVER_CORE:
|
||||
strings.write_string(b, "Server Standard Core")
|
||||
|
||||
case .ENTERPRISE_SERVER_CORE:
|
||||
strings.write_string(b, "Enterprise Server Core")
|
||||
|
||||
case .BUSINESS_N:
|
||||
strings.write_string(b, "Business N")
|
||||
|
||||
case .HOME_SERVER:
|
||||
strings.write_string(b, "Home Server")
|
||||
|
||||
case .SERVER_FOR_SMALLBUSINESS:
|
||||
strings.write_string(b, "Windows Server 2008 for Windows Essential Server Solutions")
|
||||
|
||||
case .SMALLBUSINESS_SERVER_PREMIUM:
|
||||
strings.write_string(b, "Small Business Server Premium")
|
||||
|
||||
case .HOME_PREMIUM_N:
|
||||
strings.write_string(b, "Home Premium N")
|
||||
|
||||
case .ENTERPRISE_N:
|
||||
strings.write_string(b, "Enterprise N")
|
||||
|
||||
case .ULTIMATE_N:
|
||||
strings.write_string(b, "Ultimate N")
|
||||
|
||||
case .HYPERV:
|
||||
strings.write_string(b, "HyperV")
|
||||
|
||||
case .STARTER_N:
|
||||
strings.write_string(b, "Starter N")
|
||||
|
||||
case .PROFESSIONAL:
|
||||
strings.write_string(b, "Professional")
|
||||
|
||||
case .PROFESSIONAL_N:
|
||||
strings.write_string(b, "Professional N")
|
||||
|
||||
case:
|
||||
strings.write_string(b, "Unknown Edition")
|
||||
}
|
||||
}
|
||||
|
||||
// Grab Windows DisplayVersion (like 20H02)
|
||||
format_display_version :: proc (b: ^strings.Builder) -> (version: string) {
|
||||
value_type: ^sys.DWORD
|
||||
display_version: [256]u16le
|
||||
value_size := sys.DWORD(size_of(display_version))
|
||||
|
||||
status := sys.RegGetValueW(
|
||||
sys.HKEY_LOCAL_MACHINE,
|
||||
sys.L("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
|
||||
sys.L("DisplayVersion"),
|
||||
sys.RRF_RT_REG_SZ,
|
||||
value_type,
|
||||
raw_data(display_version[:]),
|
||||
&value_size,
|
||||
)
|
||||
if status != 0 {
|
||||
// Couldn't retrieve DisplayVersion
|
||||
return
|
||||
}
|
||||
|
||||
strings.write_string(b, " (version: ")
|
||||
l := strings.builder_len(b^)
|
||||
|
||||
for r, i in display_version {
|
||||
if r == 0 {
|
||||
s := strings.to_string(b^)
|
||||
version = string(s[l:][:i])
|
||||
break
|
||||
}
|
||||
if r < 256 {
|
||||
strings.write_rune(b, rune(r))
|
||||
}
|
||||
}
|
||||
strings.write_rune(b, ')')
|
||||
return
|
||||
}
|
||||
|
||||
// Grab build number and UBR
|
||||
format_build_number :: proc (b: ^strings.Builder, major_build: int) -> (ubr: int) {
|
||||
value_type: ^sys.DWORD
|
||||
_ubr: sys.DWORD
|
||||
value_size := sys.DWORD(size_of(ubr))
|
||||
|
||||
status := sys.RegGetValueW(
|
||||
sys.HKEY_LOCAL_MACHINE,
|
||||
sys.L("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
|
||||
sys.L("UBR"),
|
||||
sys.RRF_RT_REG_DWORD,
|
||||
value_type,
|
||||
&_ubr,
|
||||
&value_size,
|
||||
)
|
||||
|
||||
if status != 0 {
|
||||
// Couldn't retrieve DisplayVersion
|
||||
return
|
||||
}
|
||||
|
||||
ubr = int(_ubr)
|
||||
strings.write_string(b, ", build: ")
|
||||
strings.write_int(b, major_build)
|
||||
strings.write_rune(b, '.')
|
||||
strings.write_int(b, ubr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@(init)
|
||||
init_ram :: proc() {
|
||||
state: sys.MEMORYSTATUSEX
|
||||
|
||||
state.dwLength = size_of(state)
|
||||
ok := sys.GlobalMemoryStatusEx(&state)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ram = RAM{
|
||||
total_ram = int(state.ullTotalPhys),
|
||||
free_ram = int(state.ullAvailPhys),
|
||||
total_swap = int(state.ullTotalPageFil),
|
||||
free_swap = int(state.ullAvailPageFil),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package sysinfo
|
||||
|
||||
when !(ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .arm32 || ODIN_ARCH == .arm64) {
|
||||
#assert(false, "This package is unsupported on this architecture.")
|
||||
}
|
||||
|
||||
os_version: OS_Version
|
||||
ram: RAM
|
||||
|
||||
OS_Version_Platform :: enum {
|
||||
Unknown,
|
||||
Windows,
|
||||
Linux,
|
||||
MacOS,
|
||||
iOS,
|
||||
FreeBSD,
|
||||
OpenBSD,
|
||||
NetBSD,
|
||||
}
|
||||
|
||||
OS_Version :: struct {
|
||||
platform: OS_Version_Platform,
|
||||
|
||||
major: int,
|
||||
minor: int,
|
||||
patch: int,
|
||||
build: [2]int,
|
||||
version: string,
|
||||
|
||||
as_string: string,
|
||||
}
|
||||
|
||||
RAM :: struct {
|
||||
total_ram: int,
|
||||
free_ram: int,
|
||||
total_swap: int,
|
||||
free_swap: int,
|
||||
}
|
||||
Reference in New Issue
Block a user