mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-06 06:38:20 +00:00
[core:sys/info] Remove @(init) where practical
This commit is contained in:
14
base/runtime/os_specific_essence.odin
Normal file
14
base/runtime/os_specific_essence.odin
Normal file
@@ -0,0 +1,14 @@
|
||||
#+build essence
|
||||
#+private
|
||||
package runtime
|
||||
|
||||
_HAS_RAND_BYTES :: false
|
||||
|
||||
// TODO(bill): reimplement `os.write`
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
return 0, -1
|
||||
}
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
trap()
|
||||
}
|
||||
@@ -6,10 +6,7 @@ import "core:sys/info"
|
||||
// is_supported returns true iff hardware accelerated AES
|
||||
// is supported.
|
||||
is_supported :: proc "contextless" () -> bool {
|
||||
features, ok := info.cpu.features.?
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
features := info.cpu_features() or_return
|
||||
|
||||
// Note: Everything with AES-NI and PCLMULQDQ has support for
|
||||
// the required SSE extxtensions.
|
||||
|
||||
@@ -227,10 +227,7 @@ is_performant :: proc "contextless" () -> bool {
|
||||
req_features :: info.CPU_Features{.V}
|
||||
}
|
||||
|
||||
features, ok := info.cpu.features.?
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
features := info.cpu_features() or_return
|
||||
|
||||
return features >= req_features
|
||||
} else when ODIN_ARCH == .wasm64p32 || ODIN_ARCH == .wasm32 {
|
||||
|
||||
@@ -39,12 +39,9 @@ _VEC_TWO: simd.u64x4 : {2, 0, 2, 0}
|
||||
// is_performant returns true iff the target and current host both support
|
||||
// "enough" SIMD to make this implementation performant.
|
||||
is_performant :: proc "contextless" () -> bool {
|
||||
req_features :: info.CPU_Features{.avx, .avx2}
|
||||
features := info.cpu_features() or_return
|
||||
|
||||
features, ok := info.cpu.features.?
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
req_features :: info.CPU_Features{.avx, .avx2}
|
||||
|
||||
return features >= req_features
|
||||
}
|
||||
|
||||
@@ -52,10 +52,7 @@ K_15 :: simd.u64x2{0xa4506ceb90befffa, 0xc67178f2bef9a3f7}
|
||||
// is_hardware_accelerated_256 returns true iff hardware accelerated
|
||||
// SHA-224/SHA-256 is supported.
|
||||
is_hardware_accelerated_256 :: proc "contextless" () -> bool {
|
||||
features, ok := info.cpu.features.?
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
features := info.cpu_features() or_return
|
||||
|
||||
req_features :: info.CPU_Features{
|
||||
.sse2,
|
||||
|
||||
@@ -40,35 +40,34 @@ CPU_Feature :: enum u64 {
|
||||
}
|
||||
|
||||
CPU_Features :: distinct bit_set[CPU_Feature; u64]
|
||||
CPU :: struct {
|
||||
name: Maybe(string),
|
||||
features: Maybe(CPU_Features),
|
||||
physical_cores: int,
|
||||
logical_cores: int,
|
||||
}
|
||||
cpu: CPU
|
||||
|
||||
// Retrieving CPU features on ARM is even more expensive than on Intel, so we're doing this lookup only once, before `main()`
|
||||
@(private) _features: CPU_Features
|
||||
@(private) _features_ok: bool
|
||||
|
||||
|
||||
@(private) _name_buf: [256]u8
|
||||
@(private) _name: string
|
||||
|
||||
@(private)
|
||||
cpu_name_buf: [128]byte
|
||||
_cpu_name :: proc() -> (name: string) {
|
||||
return _name
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
init_cpu_name :: proc "contextless" () {
|
||||
generic := true
|
||||
|
||||
_init_cpu_name :: proc "contextless" () {
|
||||
when ODIN_OS == .Darwin {
|
||||
if unix.sysctlbyname("machdep.cpu.brand_string", &cpu_name_buf) {
|
||||
cpu.name = string(cstring(rawptr(&cpu_name_buf)))
|
||||
generic = false
|
||||
if unix.sysctlbyname("machdep.cpu.brand_string", &_name_buf) {
|
||||
_name = string(cstring(rawptr(&_name_buf)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if generic {
|
||||
when ODIN_ARCH == .arm64 {
|
||||
copy(cpu_name_buf[:], "ARM64")
|
||||
cpu.name = string(cpu_name_buf[:len("ARM64")])
|
||||
} else {
|
||||
copy(cpu_name_buf[:], "ARM")
|
||||
cpu.name = string(cpu_name_buf[:len("ARM")])
|
||||
}
|
||||
when ODIN_ARCH == .arm64 {
|
||||
copy(_name_buf[:], "ARM64")
|
||||
_name = string(_name_buf[:len("ARM64")])
|
||||
} else {
|
||||
copy(_name_buf[:], "ARM")
|
||||
_name = string(_name_buf[:len("ARM")])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,11 @@ package sysinfo
|
||||
|
||||
import "core:sys/unix"
|
||||
|
||||
@(init, private)
|
||||
init_cpu_core_count :: proc "contextless" () {
|
||||
physical, logical: i64
|
||||
unix.sysctlbyname("hw.physicalcpu", &physical)
|
||||
unix.sysctlbyname("hw.logicalcpu", &logical)
|
||||
cpu.physical_cores = int(physical)
|
||||
cpu.logical_cores = int(logical)
|
||||
}
|
||||
@(private)
|
||||
_cpu_core_count :: proc "contextless" () -> (physical: int, logical: int, ok: bool) {
|
||||
_physical, _logical: i64
|
||||
unix.sysctlbyname("hw.physicalcpu", &_physical)
|
||||
unix.sysctlbyname("hw.logicalcpu", &_logical)
|
||||
|
||||
return int(_physical), int(_logical), true
|
||||
}
|
||||
@@ -2,15 +2,17 @@ package sysinfo
|
||||
|
||||
import "core:sys/unix"
|
||||
|
||||
@(init, private)
|
||||
init_cpu_features :: proc "contextless" () {
|
||||
@(static) features: CPU_Features
|
||||
defer cpu.features = features
|
||||
@(private)
|
||||
_cpu_features :: proc "contextless" () -> (features: CPU_Features, ok: bool) {
|
||||
return _features, true
|
||||
}
|
||||
|
||||
try_set :: proc "contextless" (name: cstring, feature: CPU_Feature) -> (ok: bool) {
|
||||
@(init, private)
|
||||
_init_cpu_features :: proc "contextless" () -> () {
|
||||
try_set :: proc "contextless" (features: ^CPU_Features, name: cstring, feature: CPU_Feature) -> (ok: bool) {
|
||||
support: b32
|
||||
if ok = unix.sysctlbyname(name, &support); ok && support {
|
||||
features += { feature }
|
||||
features^ += { feature }
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -20,79 +22,79 @@ init_cpu_features :: proc "contextless" () {
|
||||
|
||||
// Advanced SIMD & floating-point capabilities:
|
||||
{
|
||||
if !try_set("hw.optional.AdvSIMD", .asimd) {
|
||||
try_set("hw.optional.neon", .asimd)
|
||||
if !try_set(&_features, "hw.optional.AdvSIMD", .asimd) {
|
||||
try_set(&_features, "hw.optional.neon", .asimd)
|
||||
}
|
||||
|
||||
try_set("hw.optional.floatingpoint", .floatingpoint)
|
||||
try_set(&_features, "hw.optional.floatingpoint", .floatingpoint)
|
||||
|
||||
if !try_set("hw.optional.AdvSIMD_HPFPCvt", .asimdhp) {
|
||||
try_set("hw.optional.neon_hpfp", .asimdhp)
|
||||
if !try_set(&_features, "hw.optional.AdvSIMD_HPFPCvt", .asimdhp) {
|
||||
try_set(&_features, "hw.optional.neon_hpfp", .asimdhp)
|
||||
}
|
||||
|
||||
try_set("hw.optional.arm.FEAT_BF16", .bf16)
|
||||
// try_set("hw.optional.arm.FEAT_DotProd", .dotprod)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_BF16", .bf16)
|
||||
// try_set(&_features, "hw.optional.arm.FEAT_DotProd", .dotprod)
|
||||
|
||||
if !try_set("hw.optional.arm.FEAT_FCMA", .fcma) {
|
||||
try_set("hw.optional.armv8_3_compnum", .fcma)
|
||||
if !try_set(&_features, "hw.optional.arm.FEAT_FCMA", .fcma) {
|
||||
try_set(&_features, "hw.optional.armv8_3_compnum", .fcma)
|
||||
}
|
||||
|
||||
if !try_set("hw.optional.arm.FEAT_FHM", .fhm) {
|
||||
try_set("hw.optional.armv8_2_fhm", .fhm)
|
||||
if !try_set(&_features, "hw.optional.arm.FEAT_FHM", .fhm) {
|
||||
try_set(&_features, "hw.optional.armv8_2_fhm", .fhm)
|
||||
}
|
||||
|
||||
if !try_set("hw.optional.arm.FEAT_FP16", .fp16) {
|
||||
try_set("hw.optional.neon_fp16", .fp16)
|
||||
if !try_set(&_features, "hw.optional.arm.FEAT_FP16", .fp16) {
|
||||
try_set(&_features, "hw.optional.neon_fp16", .fp16)
|
||||
}
|
||||
|
||||
try_set("hw.optional.arm.FEAT_FRINTTS", .frint)
|
||||
try_set("hw.optional.arm.FEAT_I8MM", .i8mm)
|
||||
try_set("hw.optional.arm.FEAT_JSCVT", .jscvt)
|
||||
try_set("hw.optional.arm.FEAT_RDM", .rdm)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_FRINTTS", .frint)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_I8MM", .i8mm)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_JSCVT", .jscvt)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_RDM", .rdm)
|
||||
}
|
||||
|
||||
// Integer capabilities:
|
||||
{
|
||||
try_set("hw.optional.arm.FEAT_FlagM", .flagm)
|
||||
try_set("hw.optional.arm.FEAT_FlagM2", .flagm2)
|
||||
try_set("hw.optional.armv8_crc32", .crc32)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_FlagM", .flagm)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_FlagM2", .flagm2)
|
||||
try_set(&_features, "hw.optional.armv8_crc32", .crc32)
|
||||
}
|
||||
|
||||
// Atomic and memory ordering instruction capabilities:
|
||||
{
|
||||
try_set("hw.optional.arm.FEAT_LRCPC", .lrcpc)
|
||||
try_set("hw.optional.arm.FEAT_LRCPC2", .lrcpc2)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_LRCPC", .lrcpc)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_LRCPC2", .lrcpc2)
|
||||
|
||||
if !try_set("hw.optional.arm.FEAT_LSE", .lse) {
|
||||
try_set("hw.optional.armv8_1_atomics", .lse)
|
||||
if !try_set(&_features, "hw.optional.arm.FEAT_LSE", .lse) {
|
||||
try_set(&_features, "hw.optional.armv8_1_atomics", .lse)
|
||||
}
|
||||
|
||||
// try_set("hw.optional.arm.FEAT_LSE2", .lse2)
|
||||
// try_set(&_features, "hw.optional.arm.FEAT_LSE2", .lse2)
|
||||
}
|
||||
|
||||
// Encryption capabilities:
|
||||
{
|
||||
try_set("hw.optional.arm.FEAT_AES", .aes)
|
||||
try_set("hw.optional.arm.FEAT_PMULL", .pmull)
|
||||
try_set("hw.optional.arm.FEAT_SHA1", .sha1)
|
||||
try_set("hw.optional.arm.FEAT_SHA256", .sha256)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_AES", .aes)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_PMULL", .pmull)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_SHA1", .sha1)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_SHA256", .sha256)
|
||||
|
||||
if !try_set("hw.optional.arm.FEAT_SHA512", .sha512) {
|
||||
try_set("hw.optional.armv8_2_sha512", .sha512)
|
||||
if !try_set(&_features, "hw.optional.arm.FEAT_SHA512", .sha512) {
|
||||
try_set(&_features, "hw.optional.armv8_2_sha512", .sha512)
|
||||
}
|
||||
|
||||
if !try_set("hw.optional.arm.FEAT_SHA3", .sha3) {
|
||||
try_set("hw.optional.armv8_2_sha3", .sha3)
|
||||
if !try_set(&_features, "hw.optional.arm.FEAT_SHA3", .sha3) {
|
||||
try_set(&_features, "hw.optional.armv8_2_sha3", .sha3)
|
||||
}
|
||||
}
|
||||
|
||||
// General capabilities:
|
||||
{
|
||||
// try_set("hw.optional.arm.FEAT_BTI", .bti)
|
||||
// try_set("hw.optional.arm.FEAT_DPB", .dpb)
|
||||
// try_set("hw.optional.arm.FEAT_DPB2", .dpb2)
|
||||
// try_set("hw.optional.arm.FEAT_ECV", .ecv)
|
||||
try_set("hw.optional.arm.FEAT_SB", .sb)
|
||||
try_set("hw.optional.arm.FEAT_SSBS", .ssbs)
|
||||
// try_set(&_features, "hw.optional.arm.FEAT_BTI", .bti)
|
||||
// try_set(&_features, "hw.optional.arm.FEAT_DPB", .dpb)
|
||||
// try_set(&_features, "hw.optional.arm.FEAT_DPB2", .dpb2)
|
||||
// try_set(&_features, "hw.optional.arm.FEAT_ECV", .ecv)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_SB", .sb)
|
||||
try_set(&_features, "hw.optional.arm.FEAT_SSBS", .ssbs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,12 @@ package sysinfo
|
||||
|
||||
import "base: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
|
||||
@@ -43,16 +49,14 @@ CPU_Feature :: enum u64 {
|
||||
}
|
||||
|
||||
CPU_Features :: distinct bit_set[CPU_Feature; u64]
|
||||
CPU :: struct {
|
||||
name: Maybe(string),
|
||||
features: Maybe(CPU_Features),
|
||||
physical_cores: int, // Initialized by cpu_<os>.odin
|
||||
logical_cores: int, // Initialized by cpu_<os>.odin
|
||||
}
|
||||
cpu: CPU
|
||||
|
||||
// `cpuid` is a barrier so we try not performing this query this more than necessary.
|
||||
// `atomic_load_explicit` is also a barrier, so we're doing this lookup only once, before `main()`
|
||||
@(private) _features: CPU_Features
|
||||
@(private) _features_ok: bool
|
||||
|
||||
@(init, private)
|
||||
init_cpu_features :: proc "contextless" () {
|
||||
_init_cpu_features :: proc "contextless" () {
|
||||
is_set :: #force_inline proc "c" (bit: u32, value: u32) -> bool {
|
||||
return (value>>bit) & 0x1 != 0
|
||||
}
|
||||
@@ -64,29 +68,30 @@ init_cpu_features :: proc "contextless" () {
|
||||
|
||||
max_id, _, _, _ := cpuid(0, 0)
|
||||
if max_id < 1 {
|
||||
_features = {}
|
||||
_features_ok = false
|
||||
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)
|
||||
try_set(&_features, .sse2, 26, edx1)
|
||||
try_set(&_features, .sse3, 0, ecx1)
|
||||
try_set(&_features, .pclmulqdq, 1, ecx1)
|
||||
try_set(&_features, .ssse3, 9, ecx1)
|
||||
try_set(&_features, .fma, 12, ecx1)
|
||||
try_set(&_features, .sse41, 19, ecx1)
|
||||
try_set(&_features, .sse42, 20, ecx1)
|
||||
try_set(&_features, .popcnt, 23, ecx1)
|
||||
try_set(&_features, .aes, 25, ecx1)
|
||||
try_set(&_features, .os_xsave, 27, ecx1)
|
||||
try_set(&_features, .rdrand, 30, ecx1)
|
||||
|
||||
_features_ok = true
|
||||
|
||||
when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||
// xgetbv is an illegal instruction under FreeBSD 13, OpenBSD 7.1 and NetBSD 10
|
||||
// return before probing further
|
||||
cpu.features = set
|
||||
return
|
||||
}
|
||||
|
||||
@@ -97,15 +102,15 @@ init_cpu_features :: proc "contextless" () {
|
||||
// after they started checking both OSXSAVE and XSAVE.
|
||||
//
|
||||
// See: crbug.com/375968
|
||||
os_supports_avx := false
|
||||
os_supports_avx := false
|
||||
os_supports_avx512 := false
|
||||
if .os_xsave in set && is_set(26, ecx1) {
|
||||
if .os_xsave in _features && is_set(26, ecx1) {
|
||||
eax, _ := xgetbv(0)
|
||||
os_supports_avx = is_set(1, eax) && is_set(2, eax)
|
||||
os_supports_avx = is_set(1, eax) && is_set(2, eax)
|
||||
os_supports_avx512 = is_set(5, eax) && is_set(6, eax) && is_set(7, eax)
|
||||
}
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx, 28, ecx1)
|
||||
try_set(&_features, .avx, 28, ecx1)
|
||||
}
|
||||
|
||||
if max_id < 7 {
|
||||
@@ -113,56 +118,61 @@ init_cpu_features :: proc "contextless" () {
|
||||
}
|
||||
|
||||
_, ebx7, ecx7, edx7 := cpuid(7, 0)
|
||||
try_set(&set, .bmi1, 3, ebx7)
|
||||
try_set(&set, .sha, 29, ebx7)
|
||||
try_set(&_features, .bmi1, 3, ebx7)
|
||||
try_set(&_features, .sha, 29, ebx7)
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx2, 5, ebx7)
|
||||
try_set(&_features, .avx2, 5, ebx7)
|
||||
}
|
||||
if os_supports_avx512 {
|
||||
try_set(&set, .avx512f, 16, ebx7)
|
||||
try_set(&set, .avx512dq, 17, ebx7)
|
||||
try_set(&set, .avx512ifma, 21, ebx7)
|
||||
try_set(&set, .avx512pf, 26, ebx7)
|
||||
try_set(&set, .avx512er, 27, ebx7)
|
||||
try_set(&set, .avx512cd, 28, ebx7)
|
||||
try_set(&set, .avx512bw, 30, ebx7)
|
||||
try_set(&_features, .avx512f, 16, ebx7)
|
||||
try_set(&_features, .avx512dq, 17, ebx7)
|
||||
try_set(&_features, .avx512ifma, 21, ebx7)
|
||||
try_set(&_features, .avx512pf, 26, ebx7)
|
||||
try_set(&_features, .avx512er, 27, ebx7)
|
||||
try_set(&_features, .avx512cd, 28, ebx7)
|
||||
try_set(&_features, .avx512bw, 30, ebx7)
|
||||
|
||||
// XMM/YMM are also required for 128/256-bit instructions
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx512vl, 31, ebx7)
|
||||
try_set(&_features, .avx512vl, 31, ebx7)
|
||||
}
|
||||
|
||||
try_set(&set, .avx512vbmi, 1, ecx7)
|
||||
try_set(&set, .avx512vbmi2, 6, ecx7)
|
||||
try_set(&set, .avx512vnni, 11, ecx7)
|
||||
try_set(&set, .avx512bitalg, 12, ecx7)
|
||||
try_set(&set, .avx512vpopcntdq, 14, ecx7)
|
||||
try_set(&_features, .avx512vbmi, 1, ecx7)
|
||||
try_set(&_features, .avx512vbmi2, 6, ecx7)
|
||||
try_set(&_features, .avx512vnni, 11, ecx7)
|
||||
try_set(&_features, .avx512bitalg, 12, ecx7)
|
||||
try_set(&_features, .avx512vpopcntdq, 14, ecx7)
|
||||
|
||||
try_set(&set, .avx512vp2intersect, 8, edx7)
|
||||
try_set(&set, .avx512fp16, 23, edx7)
|
||||
try_set(&_features, .avx512vp2intersect, 8, edx7)
|
||||
try_set(&_features, .avx512fp16, 23, edx7)
|
||||
|
||||
eax7_1, _, _, _ := cpuid(7, 1)
|
||||
try_set(&set, .avx512bf16, 5, eax7_1)
|
||||
try_set(&_features, .avx512bf16, 5, eax7_1)
|
||||
}
|
||||
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
|
||||
try_set(&_features, .bmi2, 8, ebx7)
|
||||
try_set(&_features, .erms, 9, ebx7)
|
||||
try_set(&_features, .rdseed, 18, ebx7)
|
||||
try_set(&_features, .adx, 19, ebx7)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_cpu_name_buf: [72]u8
|
||||
_cpu_features :: proc "contextless" () -> (features: CPU_Features, ok: bool) {
|
||||
return _features, _features_ok
|
||||
}
|
||||
|
||||
// `cpuid` is a barrier so we try not performing this query this more than necessary.
|
||||
// `atomic_load_explicit` is also a barrier, so we're doing this lookup only once, before `main()`
|
||||
@(private) _name_buf: [72]u8
|
||||
@(private) _name: string
|
||||
|
||||
@(init, private)
|
||||
init_cpu_name :: proc "contextless" () {
|
||||
_init_cpu_name :: proc "contextless" () {
|
||||
number_of_extended_ids, _, _, _ := cpuid(0x8000_0000, 0)
|
||||
if number_of_extended_ids < 0x8000_0004 {
|
||||
return
|
||||
}
|
||||
|
||||
_buf := (^[0x12]u32)(&_cpu_name_buf)
|
||||
_buf := (^[0x12]u32)(&_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)
|
||||
@@ -170,18 +180,16 @@ init_cpu_name :: proc "contextless" () {
|
||||
// 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:]
|
||||
_name = string(_name_buf[:])
|
||||
for len(_name) > 0 && _name[0] == 0 || _name[0] == ' ' {
|
||||
_name = _name[1:]
|
||||
}
|
||||
for len(brand) > 0 && brand[len(brand) - 1] == 0 || brand[len(brand) - 1] == ' ' {
|
||||
brand = brand[:len(brand) - 1]
|
||||
for len(_name) > 0 && _name[len(_name) - 1] == 0 || _name[len(_name) - 1] == ' ' {
|
||||
_name = _name[:len(_name) - 1]
|
||||
}
|
||||
cpu.name = brand
|
||||
}
|
||||
|
||||
// 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
|
||||
@(private)
|
||||
_cpu_name :: proc "contextless" () -> (name: string) {
|
||||
return _name
|
||||
}
|
||||
@@ -4,10 +4,16 @@ package sysinfo
|
||||
|
||||
import "base:runtime"
|
||||
import "core:sys/linux"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
|
||||
@(private)
|
||||
_cpu_features :: proc "contextless" () -> (features: CPU_Features, ok: bool) {
|
||||
return _features, _features_ok
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
init_cpu_features :: proc "contextless" () {
|
||||
_init_cpu_features :: proc "contextless" () {
|
||||
context = runtime.default_context()
|
||||
fd, err := linux.open("/proc/cpuinfo", {})
|
||||
if err != .NONE { return }
|
||||
@@ -18,9 +24,6 @@ init_cpu_features :: proc "contextless" () {
|
||||
n, rerr := linux.read(fd, buf[:])
|
||||
if rerr != .NONE || n == 0 { return }
|
||||
|
||||
features: CPU_Features
|
||||
defer cpu.features = features
|
||||
|
||||
str := string(buf[:n])
|
||||
for line in strings.split_lines_iterator(&str) {
|
||||
key, _, value := strings.partition(line, ":")
|
||||
@@ -29,39 +32,72 @@ init_cpu_features :: proc "contextless" () {
|
||||
|
||||
if key != "Features" { continue }
|
||||
|
||||
_features_ok = true
|
||||
|
||||
for feature in strings.split_by_byte_iterator(&value, ' ') {
|
||||
switch feature {
|
||||
case "asimd", "neon": features += { .asimd }
|
||||
case "fp": features += { .floatingpoint }
|
||||
case "asimdhp": features += { .asimdhp }
|
||||
case "asimdbf16": features += { .bf16 }
|
||||
case "fcma": features += { .fcma }
|
||||
case "asimdfhm": features += { .fhm }
|
||||
case "fphp", "half": features += { .fp16 }
|
||||
case "frint": features += { .frint }
|
||||
case "i8mm": features += { .i8mm }
|
||||
case "jscvt": features += { .jscvt }
|
||||
case "asimdrdm": features += { .rdm }
|
||||
case "asimd", "neon": _features += { .asimd }
|
||||
case "fp": _features += { .floatingpoint }
|
||||
case "asimdhp": _features += { .asimdhp }
|
||||
case "asimdbf16": _features += { .bf16 }
|
||||
case "fcma": _features += { .fcma }
|
||||
case "asimdfhm": _features += { .fhm }
|
||||
case "fphp", "half": _features += { .fp16 }
|
||||
case "frint": _features += { .frint }
|
||||
case "i8mm": _features += { .i8mm }
|
||||
case "jscvt": _features += { .jscvt }
|
||||
case "asimdrdm": _features += { .rdm }
|
||||
|
||||
case "flagm": features += { .flagm }
|
||||
case "flagm2": features += { .flagm2 }
|
||||
case "crc32": features += { .crc32 }
|
||||
case "flagm": _features += { .flagm }
|
||||
case "flagm2": _features += { .flagm2 }
|
||||
case "crc32": _features += { .crc32 }
|
||||
|
||||
case "atomics": features += { .lse }
|
||||
case "lrcpc": features += { .lrcpc }
|
||||
case "ilrcpc": features += { .lrcpc2 }
|
||||
case "atomics": _features += { .lse }
|
||||
case "lrcpc": _features += { .lrcpc }
|
||||
case "ilrcpc": _features += { .lrcpc2 }
|
||||
|
||||
case "aes": features += { .aes }
|
||||
case "pmull": features += { .pmull }
|
||||
case "sha1": features += { .sha1 }
|
||||
case "sha2": features += { .sha256 }
|
||||
case "sha3": features += { .sha3 }
|
||||
case "sha512": features += { .sha512 }
|
||||
case "aes": _features += { .aes }
|
||||
case "pmull": _features += { .pmull }
|
||||
case "sha1": _features += { .sha1 }
|
||||
case "sha2": _features += { .sha256 }
|
||||
case "sha3": _features += { .sha3 }
|
||||
case "sha512": _features += { .sha512 }
|
||||
|
||||
case "sb": features += { .sb }
|
||||
case "ssbs": features += { .ssbs }
|
||||
case "sb": _features += { .sb }
|
||||
case "ssbs": _features += { .ssbs }
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_cpu_core_count :: proc "contextless" () -> (physical: int, logical: int, ok: bool) {
|
||||
context = runtime.default_context()
|
||||
fd, err := linux.open("/proc/cpuinfo", {})
|
||||
if err != .NONE { return }
|
||||
defer linux.close(fd)
|
||||
|
||||
// This is probably enough right?
|
||||
buf: [4096]byte
|
||||
n, rerr := linux.read(fd, buf[:])
|
||||
if rerr != .NONE || n == 0 { return }
|
||||
|
||||
physical_ok, logical_ok: bool
|
||||
|
||||
str := string(buf[:n])
|
||||
for line in strings.split_lines_iterator(&str) {
|
||||
key, _, value := strings.partition(line, ":")
|
||||
key = strings.trim_space(key)
|
||||
value = strings.trim_space(value)
|
||||
|
||||
if key == "cpu cores" && !physical_ok{
|
||||
physical, physical_ok = strconv.parse_int(value)
|
||||
}
|
||||
|
||||
if key == "siblings" && !logical_ok{
|
||||
logical, logical_ok = strconv.parse_int(value)
|
||||
}
|
||||
}
|
||||
return physical, logical, physical_ok || logical_ok
|
||||
}
|
||||
@@ -7,10 +7,9 @@ import "core:sys/linux"
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
|
||||
@(init, private)
|
||||
init_cpu_core_count :: proc "contextless" () {
|
||||
@(private)
|
||||
_cpu_core_count :: proc "contextless" () -> (physical: int, logical: int, ok: bool) {
|
||||
context = runtime.default_context()
|
||||
|
||||
fd, err := linux.open("/proc/cpuinfo", {})
|
||||
if err != .NONE { return }
|
||||
defer linux.close(fd)
|
||||
@@ -20,22 +19,21 @@ init_cpu_core_count :: proc "contextless" () {
|
||||
n, rerr := linux.read(fd, buf[:])
|
||||
if rerr != .NONE || n == 0 { return }
|
||||
|
||||
physical_ok, logical_ok: bool
|
||||
|
||||
str := string(buf[:n])
|
||||
for line in strings.split_lines_iterator(&str) {
|
||||
key, _, value := strings.partition(line, ":")
|
||||
key = strings.trim_space(key)
|
||||
value = strings.trim_space(value)
|
||||
|
||||
if key == "cpu cores" {
|
||||
if num_physical_cores, ok := strconv.parse_int(value); ok {
|
||||
cpu.physical_cores = num_physical_cores
|
||||
}
|
||||
if key == "cpu cores" && !physical_ok{
|
||||
physical, physical_ok = strconv.parse_int(value)
|
||||
}
|
||||
|
||||
if key == "siblings" {
|
||||
if num_logical_cores, ok := strconv.parse_int(value); ok {
|
||||
cpu.logical_cores = num_logical_cores
|
||||
}
|
||||
if key == "siblings" && !logical_ok{
|
||||
logical, logical_ok = strconv.parse_int(value)
|
||||
}
|
||||
}
|
||||
return physical, logical, physical_ok || logical_ok
|
||||
}
|
||||
@@ -3,14 +3,13 @@
|
||||
package sysinfo
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
import "base:runtime"
|
||||
import "core:sys/linux"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
|
||||
@(init, private)
|
||||
init_cpu_features :: proc "contextless" () {
|
||||
_features: CPU_Features
|
||||
defer cpu.features = _features
|
||||
|
||||
_init_cpu_features :: proc "contextless" () {
|
||||
HWCAP_Bits :: enum u64 {
|
||||
I = 'I' - 'A',
|
||||
M = 'M' - 'A',
|
||||
@@ -72,6 +71,8 @@ init_cpu_features :: proc "contextless" () {
|
||||
}
|
||||
}
|
||||
|
||||
_features_ok = true
|
||||
|
||||
// hwprobe for other features.
|
||||
{
|
||||
pairs := []linux.RISCV_HWProbe{
|
||||
@@ -107,7 +108,43 @@ init_cpu_features :: proc "contextless" () {
|
||||
}
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
init_cpu_name :: proc "contextless" () {
|
||||
cpu.name = "RISCV64"
|
||||
@(private)
|
||||
_cpu_features :: proc "contextless" () -> (features: CPU_Features, ok: bool) {
|
||||
return _features, _features_ok
|
||||
}
|
||||
|
||||
@(private)
|
||||
_cpu_name :: proc() -> (name: string) {
|
||||
return "RISCV64"
|
||||
}
|
||||
|
||||
@(private)
|
||||
_cpu_core_count :: proc "contextless" () -> (physical: int, logical: int, ok: bool) {
|
||||
context = runtime.default_context() // No allocations, only needed because `core:strings` wants it.
|
||||
fd, err := linux.open("/proc/cpuinfo", {})
|
||||
if err != .NONE { return }
|
||||
defer linux.close(fd)
|
||||
|
||||
// This is probably enough right?
|
||||
buf: [4096]byte
|
||||
n, rerr := linux.read(fd, buf[:])
|
||||
if rerr != .NONE || n == 0 { return }
|
||||
|
||||
physical_ok, logical_ok: bool
|
||||
|
||||
str := string(buf[:n])
|
||||
for line in strings.split_lines_iterator(&str) {
|
||||
key, _, value := strings.partition(line, ":")
|
||||
key = strings.trim_space(key)
|
||||
value = strings.trim_space(value)
|
||||
|
||||
if key == "cpu cores" && !physical_ok{
|
||||
physical, physical_ok = strconv.parse_int(value)
|
||||
}
|
||||
|
||||
if key == "siblings" && !logical_ok{
|
||||
logical, logical_ok = strconv.parse_int(value)
|
||||
}
|
||||
}
|
||||
return physical, logical, physical_ok || logical_ok
|
||||
}
|
||||
14
core/sys/info/cpu_other.odin
Normal file
14
core/sys/info/cpu_other.odin
Normal file
@@ -0,0 +1,14 @@
|
||||
#+build openbsd, freebsd, netbsd, essence, haiku
|
||||
package sysinfo
|
||||
|
||||
@(private)
|
||||
_cpu_core_count :: proc "contextless" () -> (physical: int, logical: int, ok: bool) {
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
when ODIN_ARCH == .arm32 || ODIN_ARCH == .arm64 {
|
||||
@(private)
|
||||
_cpu_features :: proc "contextless" () -> (features: CPU_Features, ok: bool) {
|
||||
return {}, false
|
||||
}
|
||||
}
|
||||
@@ -95,10 +95,8 @@ CPU_Feature :: enum u64 {
|
||||
}
|
||||
|
||||
CPU_Features :: distinct bit_set[CPU_Feature; u64]
|
||||
CPU :: struct {
|
||||
name: Maybe(string),
|
||||
features: Maybe(CPU_Features),
|
||||
physical_cores: int,
|
||||
logical_cores: int,
|
||||
}
|
||||
cpu: CPU
|
||||
|
||||
// Looking up CPU features is expensive on RISCV, and `atomic_load_explicit`
|
||||
// is also a barrier, so we're doing this lookup only once, before `main()`
|
||||
@(private) _features: CPU_Features
|
||||
@(private) _features_ok: bool
|
||||
20
core/sys/info/cpu_wasm.odin
Normal file
20
core/sys/info/cpu_wasm.odin
Normal file
@@ -0,0 +1,20 @@
|
||||
#+build wasm32, wasm64p32
|
||||
package sysinfo
|
||||
|
||||
@(private)
|
||||
_cpu_core_count :: proc "contextless" () -> (physical: int, logical: int, ok: bool) {
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
CPU_Feature :: enum u64 {}
|
||||
CPU_Features :: distinct bit_set[CPU_Feature; u64]
|
||||
|
||||
@(private)
|
||||
_cpu_features :: proc "contextless" () -> (features: CPU_Features, ok: bool) {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_cpu_name :: proc() -> (name: string) {
|
||||
return "wasm32" when ODIN_ARCH == .wasm32 else "wasm64p32"
|
||||
}
|
||||
@@ -2,30 +2,34 @@ package sysinfo
|
||||
|
||||
import sys "core:sys/windows"
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
|
||||
@(init, private)
|
||||
init_cpu_core_count :: proc "contextless" () {
|
||||
context = runtime.default_context()
|
||||
@(private)
|
||||
_cpu_core_count :: proc "contextless" () -> (physical: int, logical: int, ok: bool) {
|
||||
// Reportedly, Windows Server supports a maximum of 256 logical cores.
|
||||
// The most scratch memory we need therefore is 8192 bytes = 256 * size_of(sys.SYSTEM_LOGICAL_PROCESSOR_INFORMATION)
|
||||
infos: [256]sys.SYSTEM_LOGICAL_PROCESSOR_INFORMATION
|
||||
|
||||
infos: []sys.SYSTEM_LOGICAL_PROCESSOR_INFORMATION
|
||||
defer delete(infos)
|
||||
|
||||
returned_length: sys.DWORD
|
||||
// Query for the required buffer size.
|
||||
if ok := sys.GetLogicalProcessorInformation(raw_data(infos), &returned_length); !ok {
|
||||
infos = make([]sys.SYSTEM_LOGICAL_PROCESSOR_INFORMATION, returned_length / size_of(sys.SYSTEM_LOGICAL_PROCESSOR_INFORMATION))
|
||||
returned_length: sys.DWORD
|
||||
sys.GetLogicalProcessorInformation(nil, &returned_length)
|
||||
|
||||
if int(returned_length) > size_of(infos) {
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
count := int(returned_length) / size_of(sys.SYSTEM_LOGICAL_PROCESSOR_INFORMATION)
|
||||
|
||||
// If it still doesn't work, return
|
||||
if ok := sys.GetLogicalProcessorInformation(raw_data(infos), &returned_length); !ok {
|
||||
return
|
||||
if ok := sys.GetLogicalProcessorInformation(raw_data(infos[:]), &returned_length); !ok {
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
for info in infos {
|
||||
for info in infos[:count] {
|
||||
#partial switch info.Relationship {
|
||||
case .RelationProcessorCore: cpu.physical_cores += 1
|
||||
case .RelationNumaNode: cpu.logical_cores += int(intrinsics.count_ones(info.ProcessorMask))
|
||||
case .RelationProcessorCore: physical += 1
|
||||
case .RelationNumaNode: logical += int(intrinsics.count_ones(info.ProcessorMask))
|
||||
}
|
||||
}
|
||||
|
||||
return physical, logical, true
|
||||
}
|
||||
@@ -16,66 +16,93 @@ Example:
|
||||
import si "core:sys/info"
|
||||
|
||||
main :: proc() {
|
||||
fmt.printfln("Odin: %v", ODIN_VERSION)
|
||||
fmt.printfln("OS: %v", si.os_version.as_string)
|
||||
fmt.printfln("OS: %#v", si.os_version)
|
||||
fmt.printfln("CPU: %v", si.cpu.name)
|
||||
fmt.printfln("CPU cores: %vc/%vt", si.cpu.physical_cores, si.cpu.logical_cores)
|
||||
fmt.printfln("RAM: %#.1M", si.ram.total_ram)
|
||||
fmt.printfln("Odin: %v", ODIN_VERSION)
|
||||
if version, version_ok := si.os_version(context.allocator); version_ok {
|
||||
defer si.destroy_os_version(version, context.allocator)
|
||||
fmt.printfln("OS (full): %v", version.full)
|
||||
fmt.printfln("OS (rel): %v", version.release)
|
||||
fmt.printfln("OS: %v", version.os)
|
||||
fmt.printfln("Kernel: %v", version.kernel)
|
||||
}
|
||||
fmt.printfln("CPU: %v", si.cpu_name())
|
||||
|
||||
fmt.println()
|
||||
for gpu, i in si.gpus {
|
||||
fmt.printfln("GPU #%v:", i)
|
||||
fmt.printfln("\tVendor: %v", gpu.vendor_name)
|
||||
fmt.printfln("\tModel: %v", gpu.model_name)
|
||||
fmt.printfln("\tVRAM: %#.1M", gpu.total_ram)
|
||||
if features, features_ok := si.cpu_features(); features_ok {
|
||||
fmt.printfln(" %v", features)
|
||||
}
|
||||
if physical, logical, cores_ok := si.cpu_core_count(); cores_ok {
|
||||
fmt.printfln("CPU cores: %vc/%vt", physical, logical)
|
||||
}
|
||||
|
||||
if total_ram, free_ram, total_swap, free_swap, ram_ok := si.ram_stats(); ram_ok {
|
||||
fmt.printfln("RAM: %#.1M/%#.1M", free_ram, total_ram)
|
||||
fmt.printfln("SWAP: %#.1M/%#.1M", free_swap, total_swap)
|
||||
}
|
||||
|
||||
it: si.GPU_Iterator
|
||||
for gpu, i in si.iterate_gpus(&it) {
|
||||
fmt.printfln("%d:", i)
|
||||
fmt.printfln("\tVendor: %v", gpu.vendor)
|
||||
fmt.printfln("\tModel: %v", gpu.model)
|
||||
fmt.printfln("\tVRAM: %#.1M", gpu.vram)
|
||||
fmt.printfln("\tDriver: %v", gpu.driver)
|
||||
}
|
||||
}
|
||||
|
||||
- Example Windows output:
|
||||
|
||||
Odin: dev-2025-10
|
||||
OS: Windows 10 Professional (version: 22H2), build: 19045.6396
|
||||
OS: OS_Version{
|
||||
platform = "Windows",
|
||||
_ = Version{
|
||||
major = 10,
|
||||
minor = 0,
|
||||
patch = 0,
|
||||
},
|
||||
build = [
|
||||
19045,
|
||||
6396,
|
||||
],
|
||||
version = "22H2",
|
||||
as_string = "Windows 10 Professional (version: 22H2), build: 19045.6396",
|
||||
}
|
||||
Odin: dev-2026-02
|
||||
OS (full): Windows 10 Professional (version: 22H2), build: 19045.6575
|
||||
OS (rel): 22H2
|
||||
OS: Version{major = 10, minor = 0, patch = 0}
|
||||
Kernel: Version{major = 10, minor = 19045, patch = 6575}
|
||||
CPU: AMD Ryzen 9 5950X 16-Core Processor
|
||||
CPU_Features{aes, adx, avx, avx2, bmi1, bmi2, erms, fma, os_xsave, pclmulqdq, popcnt, rdrand, rdseed, sha, sse2, sse3, ssse3, sse41, sse42}
|
||||
CPU cores: 16c/32t
|
||||
RAM: 63.9 GiB
|
||||
RAM: 32.1 GiB/63.9 GiB
|
||||
SWAP: 21.6 GiB/73.4 GiB
|
||||
|
||||
GPU #0:
|
||||
Vendor: Advanced Micro Devices, Inc.
|
||||
Model: AMD Radeon RX 9070
|
||||
VRAM: 15.9 GiB
|
||||
|
||||
- Example Linux output:
|
||||
|
||||
Odin: dev-2026-02
|
||||
OS (full): Ubuntu 24.04.3 LTS, Linux 6.6.87.2-microsoft-standard-WSL2
|
||||
OS (rel): microsoft-standard-WSL2
|
||||
OS: Version{major = 24, minor = 4, patch = 3}
|
||||
Kernel: Version{major = 6, minor = 6, patch = 87}
|
||||
CPU: AMD Ryzen 9 5950X 16-Core Processor
|
||||
CPU_Features{aes, adx, avx, avx2, bmi1, bmi2, erms, fma, os_xsave, pclmulqdq, popcnt, rdrand, rdseed, sha, sse2, sse3, ssse3, sse41, sse42}
|
||||
CPU cores: 16c/32t
|
||||
RAM: 29.2 GiB/31.3 GiB
|
||||
SWAP: 8.0 GiB/8.0 GiB
|
||||
|
||||
- Example macOS output:
|
||||
|
||||
ODIN: dev-2022-09
|
||||
OS: OS_Version{
|
||||
platform = "MacOS",
|
||||
major = 21,
|
||||
minor = 5,
|
||||
patch = 0,
|
||||
build = [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
version = "21F79",
|
||||
as_string = "macOS Monterey 12.4 (build 21F79, kernel 21.5.0)",
|
||||
}
|
||||
CPU: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
|
||||
RAM: 8.0 GiB
|
||||
Odin: dev-2026-02
|
||||
OS (full): macOS Tahoe 26.3.0 (build 25D125, kernel 25.3.0)
|
||||
OS (rel): 25D125
|
||||
OS: Version{major = 26, minor = 3, patch = 0}
|
||||
Kernel: Version{major = 25, minor = 3, patch = 0}
|
||||
CPU: Apple M4 Pro
|
||||
CPU_Features{asimd, floatingpoint, asimdhp, bf16, fcma, fhm, fp16, frint, i8mm, jscvt, rdm, flagm, flagm2, crc32, lse, lrcpc, lrcpc2, aes, pmull, sha1, sha256, sha512, sha3, sb}
|
||||
CPU cores: 12c/12t
|
||||
RAM: 0.0 B/24.0 GiB
|
||||
SWAP: 0.0 B/0.0 B
|
||||
|
||||
- Example FreeBSD output:
|
||||
|
||||
Odin: dev-2026-02
|
||||
OS (full): FreeBSD 15.0-RELEASE-p2 releng/15.0-n281005-5fb0f8e9e61d GENERIC, revision 199506
|
||||
OS (rel):
|
||||
OS: Version{major = 15, minor = 0, patch = 199506}
|
||||
Kernel: Version{major = 15, minor = 0, patch = 199506}
|
||||
CPU: AMD Ryzen 9 5950X 16-Core Processor
|
||||
CPU_Features{aes, fma, os_xsave, pclmulqdq, popcnt, rdrand, sse2, sse3, ssse3, sse41, sse42}
|
||||
RAM: 7.6 GiB/7.9 GiB
|
||||
SWAP: 0.0 B/0.0 B
|
||||
*/
|
||||
package sysinfo
|
||||
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
#+build openbsd, netbsd
|
||||
package sysinfo
|
||||
|
||||
import sys "core:sys/unix"
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
import "base:runtime"
|
||||
import "core:strings"
|
||||
import sys "core:sys/unix"
|
||||
|
||||
@(init, private)
|
||||
init_os_version :: proc "contextless" () {
|
||||
context = runtime.default_context()
|
||||
|
||||
_os_version :: proc (allocator: runtime.Allocator, loc := #caller_location) -> (res: OS_Version, ok: bool) {
|
||||
when ODIN_OS == .NetBSD {
|
||||
os_version.platform = .NetBSD
|
||||
res.platform = .NetBSD
|
||||
} else {
|
||||
os_version.platform = .OpenBSD
|
||||
res.platform = .OpenBSD
|
||||
}
|
||||
|
||||
kernel_version_buf: [1024]u8
|
||||
|
||||
b := strings.builder_from_bytes(version_string_buf[:])
|
||||
b := strings.builder_make_none(allocator = allocator, loc = loc)
|
||||
// Retrieve kernel info using `sysctl`, e.g. OpenBSD and NetBSD
|
||||
mib := []i32{sys.CTL_KERN, sys.KERN_OSTYPE}
|
||||
if !sys.sysctl(mib, &kernel_version_buf) {
|
||||
@@ -36,19 +32,8 @@ init_os_version :: proc "contextless" () {
|
||||
version := string(cstring(raw_data(kernel_version_buf[:])))
|
||||
strings.write_string(&b, version)
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
|
||||
// Parse kernel version
|
||||
triplet := strings.split(version, ".", context.temp_allocator)
|
||||
if len(triplet) == 2 {
|
||||
major, major_ok := strconv.parse_int(triplet[0])
|
||||
minor, minor_ok := strconv.parse_int(triplet[1])
|
||||
|
||||
if major_ok && minor_ok {
|
||||
os_version.major = major
|
||||
os_version.minor = minor
|
||||
}
|
||||
}
|
||||
res.kernel = _parse_version(version)
|
||||
|
||||
// Retrieve kernel revision using `sysctl`, e.g. 199506
|
||||
mib = []i32{sys.CTL_KERN, sys.KERN_OSREV}
|
||||
@@ -56,20 +41,27 @@ init_os_version :: proc "contextless" () {
|
||||
if !sys.sysctl(mib, &revision) {
|
||||
return
|
||||
}
|
||||
os_version.patch = revision
|
||||
res.kernel.patch = revision
|
||||
strings.write_string(&b, ", build ")
|
||||
strings.write_int(&b, revision)
|
||||
|
||||
// Finalize pretty name.
|
||||
os_version.as_string = strings.to_string(b)
|
||||
res.full = strings.to_string(b)
|
||||
|
||||
return res, true
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
init_ram :: proc "contextless" () {
|
||||
@(private)
|
||||
_ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) {
|
||||
// Retrieve RAM info using `sysctl`
|
||||
mib := []i32{sys.CTL_HW, sys.HW_PHYSMEM64}
|
||||
mem_size: u64
|
||||
if sys.sysctl(mib, &mem_size) {
|
||||
ram.total_ram = int(mem_size)
|
||||
if sys.sysctl(mib, &total_ram) {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
|
||||
mib = []i32{sys.CTL_HW, sys.HW_USERMEM64}
|
||||
if sys.sysctl(mib, &free_ram) {
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,19 +1,16 @@
|
||||
package sysinfo
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:strconv"
|
||||
import "base:runtime"
|
||||
import "core:strings"
|
||||
import "core:sys/unix"
|
||||
import NS "core:sys/darwin/Foundation"
|
||||
|
||||
@(init, private)
|
||||
init_platform :: proc "contextless" () {
|
||||
context = runtime.default_context()
|
||||
@(private)
|
||||
_os_version :: proc (allocator: runtime.Allocator, loc := #caller_location) -> (res: OS_Version, ok: bool) {
|
||||
ws :: strings.write_string
|
||||
wi :: strings.write_int
|
||||
|
||||
b := strings.builder_from_bytes(version_string_buf[:])
|
||||
b := strings.builder_make_none(allocator = allocator, loc = loc)
|
||||
|
||||
version: NS.OperatingSystemVersion
|
||||
{
|
||||
@@ -21,18 +18,15 @@ init_platform :: proc "contextless" () {
|
||||
|
||||
info := NS.ProcessInfo.processInfo()
|
||||
version = info->operatingSystemVersion()
|
||||
mem := info->physicalMemory()
|
||||
|
||||
ram.total_ram = int(mem)
|
||||
}
|
||||
|
||||
macos_version = {int(version.majorVersion), int(version.minorVersion), int(version.patchVersion)}
|
||||
res.os = {int(version.majorVersion), int(version.minorVersion), int(version.patchVersion)}
|
||||
|
||||
when ODIN_PLATFORM_SUBTARGET_IOS {
|
||||
os_version.platform = .iOS
|
||||
res.platform = .iOS
|
||||
ws(&b, "iOS")
|
||||
} else {
|
||||
os_version.platform = .MacOS
|
||||
res.platform = .MacOS
|
||||
switch version.majorVersion {
|
||||
case 26: ws(&b, "macOS Tahoe")
|
||||
case 15: ws(&b, "macOS Sequoia")
|
||||
@@ -69,34 +63,37 @@ init_platform :: proc "contextless" () {
|
||||
{
|
||||
build_buf: [12]u8
|
||||
mib := []i32{unix.CTL_KERN, unix.KERN_OSVERSION}
|
||||
ok := unix.sysctl(mib, &build_buf)
|
||||
build := string(cstring(raw_data(build_buf[:]))) if ok else "Unknown"
|
||||
|
||||
build := "Unknown"
|
||||
if unix.sysctl(mib, &build_buf) {
|
||||
build = string(cstring(raw_data(build_buf[:])))
|
||||
}
|
||||
ws(&b, " (build ")
|
||||
|
||||
build_start := len(b.buf)
|
||||
ws(&b, build)
|
||||
os_version.version = string(b.buf[build_start:][:len(build)])
|
||||
res.release = string(b.buf[build_start:][:len(build)])
|
||||
}
|
||||
|
||||
{
|
||||
// Match on XNU kernel version
|
||||
version_bits: [12]u8 // enough for 999.999.999\x00
|
||||
mib := []i32{unix.CTL_KERN, unix.KERN_OSRELEASE}
|
||||
ok := unix.sysctl(mib, &version_bits)
|
||||
kernel := string(cstring(raw_data(version_bits[:]))) if ok else "Unknown"
|
||||
|
||||
major, _, tail := strings.partition(kernel, ".")
|
||||
minor, _, patch := strings.partition(tail, ".")
|
||||
|
||||
os_version.major, _ = strconv.parse_int(major, 10)
|
||||
os_version.minor, _ = strconv.parse_int(minor, 10)
|
||||
os_version.patch, _ = strconv.parse_int(patch, 10)
|
||||
|
||||
kernel := "Unknown"
|
||||
if unix.sysctl(mib, &version_bits) {
|
||||
kernel = string(cstring(raw_data(version_bits[:])))
|
||||
res.kernel = _parse_version(kernel)
|
||||
}
|
||||
ws(&b, ", kernel ")
|
||||
ws(&b, kernel)
|
||||
ws(&b, ")")
|
||||
}
|
||||
|
||||
os_version.as_string = string(b.buf[:])
|
||||
res.full = strings.to_string(b)
|
||||
return res, true
|
||||
}
|
||||
|
||||
@(private)
|
||||
_ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) {
|
||||
NS.scoped_autoreleasepool()
|
||||
info := NS.ProcessInfo.processInfo()
|
||||
return i64(info->physicalMemory()), 0, 0, 0, true
|
||||
}
|
||||
@@ -2,18 +2,15 @@ package sysinfo
|
||||
|
||||
import sys "core:sys/unix"
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
import "base:runtime"
|
||||
|
||||
@(init, private)
|
||||
init_os_version :: proc "contextless" () {
|
||||
context = runtime.default_context()
|
||||
|
||||
os_version.platform = .FreeBSD
|
||||
@(private)
|
||||
_os_version :: proc (allocator: runtime.Allocator, loc := #caller_location) -> (res: OS_Version, ok: bool) {
|
||||
res.platform = .FreeBSD
|
||||
|
||||
kernel_version_buf: [1024]u8
|
||||
|
||||
b := strings.builder_from_bytes(version_string_buf[:])
|
||||
b := strings.builder_make_none(allocator = allocator, loc = loc)
|
||||
// Retrieve kernel info using `sysctl`, e.g. FreeBSD 13.1-RELEASE-p2 GENERIC
|
||||
mib := []i32{sys.CTL_KERN, sys.KERN_VERSION}
|
||||
if !sys.sysctl(mib, &kernel_version_buf) {
|
||||
@@ -24,21 +21,18 @@ init_os_version :: proc "contextless" () {
|
||||
pretty_name = strings.trim(pretty_name, "\n")
|
||||
strings.write_string(&b, pretty_name)
|
||||
|
||||
// l := strings.builder_len(b)
|
||||
|
||||
// Retrieve kernel revision using `sysctl`, e.g. 199506
|
||||
mib = []i32{sys.CTL_KERN, sys.KERN_OSREV}
|
||||
revision: int
|
||||
if !sys.sysctl(mib, &revision) {
|
||||
return
|
||||
}
|
||||
os_version.patch = revision
|
||||
|
||||
strings.write_string(&b, ", revision ")
|
||||
strings.write_int(&b, revision)
|
||||
|
||||
// Finalize pretty name.
|
||||
os_version.as_string = strings.to_string(b)
|
||||
res.full = strings.to_string(b)
|
||||
|
||||
// Retrieve kernel release using `sysctl`, e.g. 13.1-RELEASE-p2
|
||||
mib = []i32{sys.CTL_KERN, sys.KERN_OSRELEASE}
|
||||
@@ -46,32 +40,28 @@ init_os_version :: proc "contextless" () {
|
||||
return
|
||||
}
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
|
||||
// Parse kernel version
|
||||
release := string(cstring(raw_data(kernel_version_buf[:])))
|
||||
version_bits := strings.split_n(release, "-", 2, context.temp_allocator)
|
||||
if len(version_bits) > 1 {
|
||||
// Parse major, minor from KERN_OSRELEASE
|
||||
triplet := strings.split(version_bits[0], ".", context.temp_allocator)
|
||||
if len(triplet) == 2 {
|
||||
major, major_ok := strconv.parse_int(triplet[0])
|
||||
minor, minor_ok := strconv.parse_int(triplet[1])
|
||||
version_bits, _, _ := strings.partition(release, "-")
|
||||
res.kernel = _parse_version(version_bits)
|
||||
res.kernel.patch = revision
|
||||
|
||||
if major_ok && minor_ok {
|
||||
os_version.major = major
|
||||
os_version.minor = minor
|
||||
}
|
||||
}
|
||||
}
|
||||
res.os = res.kernel
|
||||
|
||||
return res, true
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
init_ram :: proc "contextless" () {
|
||||
@(private)
|
||||
_ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) {
|
||||
// Retrieve RAM info using `sysctl`
|
||||
mib := []i32{sys.CTL_HW, sys.HW_PHYSMEM}
|
||||
mem_size: u64
|
||||
if sys.sysctl(mib, &mem_size) {
|
||||
ram.total_ram = int(mem_size)
|
||||
if sys.sysctl(mib, &total_ram) {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
|
||||
mib = []i32{sys.CTL_HW, sys.HW_USERMEM}
|
||||
if sys.sysctl(mib, &free_ram) {
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,18 +2,14 @@ package sysinfo
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:sys/linux"
|
||||
|
||||
@(init, private)
|
||||
init_os_version :: proc "contextless" () {
|
||||
context = runtime.default_context()
|
||||
@(private)
|
||||
_os_version :: proc (allocator: runtime.Allocator, loc := #caller_location) -> (res: OS_Version, ok: bool) {
|
||||
res.platform = .Linux
|
||||
|
||||
os_version.platform = .Linux
|
||||
|
||||
b := strings.builder_from_bytes(version_string_buf[:])
|
||||
b := strings.builder_make_none(allocator = allocator, loc = loc)
|
||||
|
||||
// Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
|
||||
pretty_parse: {
|
||||
@@ -23,10 +19,7 @@ init_os_version :: proc "contextless" () {
|
||||
break pretty_parse
|
||||
}
|
||||
|
||||
defer {
|
||||
cerrno := linux.close(fd)
|
||||
assert(cerrno == .NONE, "Failed to close the file descriptor")
|
||||
}
|
||||
defer linux.close(fd)
|
||||
|
||||
os_release_buf: [2048]u8
|
||||
n, read_errno := linux.read(fd, os_release_buf[:])
|
||||
@@ -36,17 +29,27 @@ init_os_version :: proc "contextless" () {
|
||||
}
|
||||
release := string(os_release_buf[:n])
|
||||
|
||||
// Search the line in the file until we find "PRETTY_NAME="
|
||||
NEEDLE :: "PRETTY_NAME=\""
|
||||
_, _, post := strings.partition(release, NEEDLE)
|
||||
if len(post) > 0 {
|
||||
end := strings.index_any(post, "\"\n")
|
||||
if end > -1 && post[end] == '"' {
|
||||
strings.write_string(&b, post[:end])
|
||||
{
|
||||
// Search the line in the file until we find "PRETTY_NAME="
|
||||
_, _, post := strings.partition(release, `PRETTY_NAME="`)
|
||||
if len(post) > 0 {
|
||||
end := strings.index_any(post, "\"\n")
|
||||
if end > -1 && post[end] == '"' {
|
||||
strings.write_string(&b, post[:end])
|
||||
}
|
||||
}
|
||||
if strings.builder_len(b) == 0 {
|
||||
strings.write_string(&b, "Unknown Linux Distro")
|
||||
}
|
||||
}
|
||||
if strings.builder_len(b) == 0 {
|
||||
strings.write_string(&b, "Unknown Linux Distro")
|
||||
|
||||
{
|
||||
// Search the line in the file until we find "VERSION="
|
||||
_, _, post := strings.partition(release, `VERSION="`)
|
||||
if len(post) > 0 {
|
||||
pre, _, _ := strings.partition(post, ` `)
|
||||
res.os = _parse_version(pre)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,43 +66,30 @@ init_os_version :: proc "contextless" () {
|
||||
strings.write_string(&b, string(cstring(&uts.release[0])))
|
||||
release_str := string(b.buf[release_i:])
|
||||
|
||||
os_version.as_string = strings.to_string(b)
|
||||
res.full = strings.to_string(b)
|
||||
|
||||
// Parse the Linux version out of the release string
|
||||
version_loop: {
|
||||
version_num, _, version_suffix := strings.partition(release_str, "-")
|
||||
os_version.version = version_suffix
|
||||
res.release = version_suffix
|
||||
res.kernel = _parse_version(version_num)
|
||||
|
||||
i: int
|
||||
for part in strings.split_iterator(&version_num, ".") {
|
||||
defer i += 1
|
||||
|
||||
dst: ^int
|
||||
switch i {
|
||||
case 0: dst = &os_version.major
|
||||
case 1: dst = &os_version.minor
|
||||
case 2: dst = &os_version.patch
|
||||
case: break version_loop
|
||||
}
|
||||
|
||||
num, ok := strconv.parse_int(part)
|
||||
if !ok { break version_loop }
|
||||
|
||||
dst^ = num
|
||||
}
|
||||
}
|
||||
return res, true
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
init_ram :: proc "contextless" () {
|
||||
@(private)
|
||||
_ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) {
|
||||
// Retrieve RAM info using `sysinfo`
|
||||
sys_info: linux.Sys_Info
|
||||
errno := linux.sysinfo(&sys_info)
|
||||
assert_contextless(errno == .NONE, "Good luck to whoever's debugging this, something's seriously cucked up!")
|
||||
ram = RAM{
|
||||
total_ram = int(sys_info.totalram) * int(sys_info.mem_unit),
|
||||
free_ram = int(sys_info.freeram) * int(sys_info.mem_unit),
|
||||
total_swap = int(sys_info.totalswap) * int(sys_info.mem_unit),
|
||||
free_swap = int(sys_info.freeswap) * int(sys_info.mem_unit),
|
||||
}
|
||||
}
|
||||
|
||||
total_ram = i64(sys_info.totalram) * i64(sys_info.mem_unit)
|
||||
free_ram = i64(sys_info.freeram) * i64(sys_info.mem_unit)
|
||||
total_swap = i64(sys_info.totalswap) * i64(sys_info.mem_unit)
|
||||
free_swap = i64(sys_info.freeswap) * i64(sys_info.mem_unit)
|
||||
ok = true
|
||||
|
||||
return
|
||||
}
|
||||
14
core/sys/info/platform_other.odin
Normal file
14
core/sys/info/platform_other.odin
Normal file
@@ -0,0 +1,14 @@
|
||||
#+build essence, haiku
|
||||
package sysinfo
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@(private)
|
||||
_ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_os_version :: proc(allocator: runtime.Allocator, loc := #caller_location) -> (res: OS_Version, ok: bool) {
|
||||
return {}, false
|
||||
}
|
||||
14
core/sys/info/platform_wasm.odin
Normal file
14
core/sys/info/platform_wasm.odin
Normal file
@@ -0,0 +1,14 @@
|
||||
#+build wasm32, wasm64p32
|
||||
package sysinfo
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@(private)
|
||||
_ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_os_version :: proc(allocator: runtime.Allocator, loc := #caller_location) -> (res: OS_Version, ok: bool) {
|
||||
return {}, false
|
||||
}
|
||||
@@ -1,33 +1,27 @@
|
||||
package sysinfo
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf16"
|
||||
import sys "core:sys/windows"
|
||||
import "base:intrinsics"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf16"
|
||||
import "base:runtime"
|
||||
|
||||
@(init, private)
|
||||
init_os_version :: proc "contextless" () {
|
||||
// NOTE(Jeroen): Only needed for the string builder.
|
||||
context = runtime.default_context()
|
||||
|
||||
@(private)
|
||||
_os_version :: proc (allocator: runtime.Allocator, loc := #caller_location) -> (res: OS_Version, ok: bool) {
|
||||
/*
|
||||
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)
|
||||
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)
|
||||
*/
|
||||
os_version.platform = .Windows
|
||||
res.platform = .Windows
|
||||
|
||||
osvi: sys.OSVERSIONINFOEXW
|
||||
osvi.dwOSVersionInfoSize = size_of(osvi)
|
||||
status := sys.RtlGetVersion(&osvi)
|
||||
|
||||
if status != 0 {
|
||||
return
|
||||
if status := sys.RtlGetVersion(&osvi); status != 0 {
|
||||
return res, false
|
||||
}
|
||||
|
||||
product_type: sys.Windows_Product_Type
|
||||
@@ -37,12 +31,13 @@ init_os_version :: proc "contextless" () {
|
||||
&product_type,
|
||||
)
|
||||
|
||||
os_version.major = int(osvi.dwMajorVersion)
|
||||
os_version.minor = int(osvi.dwMinorVersion)
|
||||
os_version.build[0] = int(osvi.dwBuildNumber)
|
||||
res.os.major = int(osvi.dwMajorVersion)
|
||||
res.os.minor = int(osvi.dwMinorVersion)
|
||||
res.kernel.major = int(osvi.dwMajorVersion)
|
||||
res.kernel.minor = int(osvi.dwBuildNumber)
|
||||
|
||||
b := strings.builder_make_none(allocator = allocator, loc = loc)
|
||||
|
||||
b := strings.builder_from_bytes(version_string_buf[:])
|
||||
strings.write_string(&b, "Windows ")
|
||||
|
||||
switch osvi.dwMajorVersion {
|
||||
@@ -120,13 +115,13 @@ init_os_version :: proc "contextless" () {
|
||||
}
|
||||
|
||||
// Grab DisplayVersion
|
||||
os_version.version = format_display_version(&b)
|
||||
res.release = format_display_version(&b)
|
||||
|
||||
// Grab build number and UBR
|
||||
os_version.build[1] = format_build_number(&b, int(osvi.dwBuildNumber))
|
||||
res.kernel.patch = format_build_number(&b, int(osvi.dwBuildNumber))
|
||||
|
||||
// Finish the string
|
||||
os_version.as_string = strings.to_string(b)
|
||||
res.full = strings.to_string(b)
|
||||
|
||||
format_windows_product_type :: proc (b: ^strings.Builder, prod_type: sys.Windows_Product_Type) {
|
||||
#partial switch prod_type {
|
||||
@@ -253,29 +248,33 @@ init_os_version :: proc "contextless" () {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return res, true
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
init_ram :: proc "contextless" () {
|
||||
@(private)
|
||||
_ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) {
|
||||
state: sys.MEMORYSTATUSEX
|
||||
|
||||
state.dwLength = size_of(state)
|
||||
ok := sys.GlobalMemoryStatusEx(&state)
|
||||
if !ok {
|
||||
if ok := sys.GlobalMemoryStatusEx(&state); !ok {
|
||||
return
|
||||
}
|
||||
ram = RAM{
|
||||
total_ram = int(state.ullTotalPhys),
|
||||
free_ram = int(state.ullAvailPhys),
|
||||
total_swap = int(state.ullTotalPageFil),
|
||||
free_swap = int(state.ullAvailPageFil),
|
||||
}
|
||||
|
||||
total_ram = i64(state.ullTotalPhys)
|
||||
free_ram = i64(state.ullAvailPhys)
|
||||
total_swap = i64(state.ullTotalPageFil)
|
||||
free_swap = i64(state.ullAvailPageFil)
|
||||
ok = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
init_gpu_info :: proc "contextless" () {
|
||||
_iterate_gpus :: proc(it: ^GPU_Iterator, minimum_vram := i64(256 * 1024 * 1024)) -> (gpu: GPU, index: int, ok: bool) {
|
||||
GPU_ROOT_KEY :: `SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}`
|
||||
|
||||
defer it.index += 1
|
||||
|
||||
gpu_key: sys.HKEY
|
||||
if status := sys.RegOpenKeyExW(
|
||||
sys.HKEY_LOCAL_MACHINE,
|
||||
@@ -284,75 +283,64 @@ init_gpu_info :: proc "contextless" () {
|
||||
sys.KEY_ENUMERATE_SUB_KEYS,
|
||||
&gpu_key,
|
||||
); status != i32(sys.ERROR_SUCCESS) {
|
||||
return
|
||||
return {}, it.index, false
|
||||
}
|
||||
defer sys.RegCloseKey(gpu_key)
|
||||
|
||||
gpu: ^GPU
|
||||
buf_wstring: [100]u16
|
||||
buf_len := u32(len(buf_wstring))
|
||||
buf_key: [4 * len(buf_wstring)]u8
|
||||
buf_leaf: [100]u8
|
||||
leaf: string
|
||||
|
||||
index := sys.DWORD(0)
|
||||
gpu_count := 0
|
||||
gpu_loop: for gpu_count < MAX_GPUS {
|
||||
defer index += 1
|
||||
|
||||
buf_wstring: [100]u16
|
||||
buf_len := u32(len(buf_wstring))
|
||||
buf_key: [4 * len(buf_wstring)]u8
|
||||
buf_leaf: [100]u8
|
||||
buf_scratch: [100]u8
|
||||
gpu_loop: {
|
||||
defer it._index += 1
|
||||
|
||||
if status := sys.RegEnumKeyW(
|
||||
gpu_key,
|
||||
index,
|
||||
auto_cast it._index,
|
||||
&buf_wstring[0],
|
||||
&buf_len,
|
||||
); status != i32(sys.ERROR_SUCCESS) {
|
||||
break gpu_loop
|
||||
return {}, it.index, false
|
||||
}
|
||||
|
||||
utf16.decode_to_utf8(buf_leaf[:], buf_wstring[:])
|
||||
leaf := string(cstring(&buf_leaf[0]))
|
||||
leaf = string(cstring(&buf_leaf[0]))
|
||||
|
||||
// Skip leaves that are not of the form 000x
|
||||
if !is_integer(leaf) {
|
||||
continue
|
||||
if is_integer(leaf) {
|
||||
break gpu_loop
|
||||
}
|
||||
|
||||
n := copy(buf_key[:], GPU_ROOT_KEY)
|
||||
buf_key[n] = '\\'
|
||||
copy(buf_key[n+1:], leaf)
|
||||
|
||||
key_len := len(GPU_ROOT_KEY) + len(leaf) + 1
|
||||
|
||||
utf16.encode_string(buf_wstring[:], string(buf_key[:key_len]))
|
||||
key := cstring16(&buf_wstring[0])
|
||||
|
||||
if res, ok := read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "ProviderName", buf_scratch[:]); ok {
|
||||
if vendor, s_ok := intern_gpu_string(res); s_ok {
|
||||
gpu = &_gpus[gpu_count]
|
||||
gpu.vendor_name = vendor
|
||||
} else {
|
||||
break gpu_loop
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if res, ok := read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "DriverDesc", buf_scratch[:]); ok {
|
||||
if model_name, s_ok := intern_gpu_string(res); s_ok {
|
||||
gpu = &_gpus[gpu_count]
|
||||
gpu.model_name = model_name
|
||||
} else {
|
||||
break gpu_loop
|
||||
}
|
||||
}
|
||||
|
||||
if vram, ok := read_reg_i64(sys.HKEY_LOCAL_MACHINE, key, "HardwareInformation.qwMemorySize"); ok {
|
||||
gpu.total_ram = int(vram)
|
||||
}
|
||||
gpu_count += 1 // not deferred at the top because it only increments if we get this far.
|
||||
}
|
||||
gpus = _gpus[:gpu_count]
|
||||
|
||||
n := copy(buf_key[:], GPU_ROOT_KEY)
|
||||
buf_key[n] = '\\'
|
||||
copy(buf_key[n+1:], leaf)
|
||||
|
||||
key_len := len(GPU_ROOT_KEY) + len(leaf) + 1
|
||||
|
||||
utf16.encode_string(buf_wstring[:], string(buf_key[:key_len]))
|
||||
key := cstring16(&buf_wstring[0])
|
||||
|
||||
// Determine if this is a real GPU, or perhaps a screen mirroring or RDP driver
|
||||
// Real devices tend to have more than 256 MiB of VRAM
|
||||
gpu.vram, _ = read_reg_i64 (sys.HKEY_LOCAL_MACHINE, key, "HardwareInformation.qwMemorySize")
|
||||
if gpu.vram < minimum_vram {
|
||||
return
|
||||
}
|
||||
|
||||
// Real devices tend to have a matching PCI device
|
||||
matching, _ := read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "MatchingDeviceId", it._buffer[:100])
|
||||
if !strings.has_prefix(matching, "PCI\\VEN") {
|
||||
return
|
||||
}
|
||||
|
||||
gpu.vendor, _ = read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "ProviderName", it._buffer[ 0:][:100])
|
||||
gpu.model, _ = read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "DriverDesc", it._buffer[100:][:100])
|
||||
gpu.driver, _ = read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "DriverVersion", it._buffer[200:][:100])
|
||||
|
||||
return gpu, it.index, true
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
@@ -1,15 +1,110 @@
|
||||
package sysinfo
|
||||
|
||||
when !(ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .arm32 || ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 || ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32) {
|
||||
#assert(false, "This package is unsupported on this architecture.")
|
||||
import "base:runtime"
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
|
||||
#assert(
|
||||
ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || \
|
||||
ODIN_ARCH == .arm32 || ODIN_ARCH == .arm64 || \
|
||||
ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 || \
|
||||
ODIN_ARCH == .riscv64,
|
||||
"This package is unsupported on this architecture.")
|
||||
|
||||
/*
|
||||
Retrieves the number of physical and logical CPU cores
|
||||
|
||||
Returns:
|
||||
- physical: The number of physical cores
|
||||
- logical: The number of logical cores
|
||||
- ok: `true` when we could retrieve the CPU information, `false` otherwise
|
||||
*/
|
||||
cpu_core_count :: proc "contextless" () -> (physical: int, logical: int, ok: bool) {
|
||||
return _cpu_core_count()
|
||||
}
|
||||
|
||||
os_version: OS_Version
|
||||
ram: RAM
|
||||
gpus: []GPU
|
||||
/*
|
||||
Retrieves CPU features where available
|
||||
|
||||
// Only on MacOS, contains the actual MacOS version, while the `os_version` contains the kernel version.
|
||||
macos_version: Version
|
||||
The results are cached
|
||||
|
||||
Returns:
|
||||
- features: An architecture-specific `bit_set`
|
||||
- ok: `true` when we could retrieve the CPU features, `false` otherwise
|
||||
*/
|
||||
cpu_features :: proc "contextless" () -> (features: CPU_Features, ok: bool) {
|
||||
return _cpu_features()
|
||||
}
|
||||
|
||||
/*
|
||||
Retrieves the CPU's name
|
||||
|
||||
The results are cached
|
||||
|
||||
Returns:
|
||||
- name: A `string` containing the CPU model name, empty if the lookup failed
|
||||
*/
|
||||
cpu_name :: proc() -> (name: string) {
|
||||
return _cpu_name()
|
||||
}
|
||||
|
||||
/*
|
||||
Retrieves RAM statistics
|
||||
|
||||
Unavailable stats will be returned as `0` bytes
|
||||
|
||||
Returns:
|
||||
- total_ram: Total RAM reported by the operating system, in bytes
|
||||
- free_ram: Free RAM reported by the operating system, in bytes
|
||||
- total_swap: Total SWAP reported by the operating system, in bytes
|
||||
- free_swap: Free SWAP reported by the operating system, in bytes
|
||||
- ok: `true` when we could retrieve RAM statistics, `false` otherwise
|
||||
*/
|
||||
ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) {
|
||||
return _ram_stats()
|
||||
}
|
||||
|
||||
/*
|
||||
Retrieves OS version information
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
|
||||
You can use `destroy_os_version` to free the results
|
||||
|
||||
Inputs:
|
||||
- allocator: A `runtime.Allocator` on which the version strings will be allocated
|
||||
- loc: The caller location
|
||||
|
||||
Returns:
|
||||
- res: An `OS_Version` struct
|
||||
- ok: `true` when we could retrieve OS version information, `false` otherwise
|
||||
*/
|
||||
os_version :: proc(allocator: runtime.Allocator, loc := #caller_location) -> (res: OS_Version, ok: bool) {
|
||||
return _os_version(allocator = allocator, loc = loc)
|
||||
}
|
||||
|
||||
/*
|
||||
Releases an `OS_Version`'s strings
|
||||
|
||||
*Frees Using Provided Allocator*
|
||||
|
||||
Inputs:
|
||||
- version: An `OS_Version` struct
|
||||
- allocator: A `runtime.Allocator` on which the version strings will be freed
|
||||
*/
|
||||
destroy_os_version :: proc(version: OS_Version, allocator: runtime.Allocator) {
|
||||
delete(version.full, allocator)
|
||||
// `version.release` is part of `version.full` and does not need to be freed separately.
|
||||
}
|
||||
|
||||
OS_Version :: struct {
|
||||
platform: OS_Version_Platform, // Windows, Linux, MacOS, iOS, etc.
|
||||
full: string, // e.g. Windows 10 Professional (version: 22H2), build: 19045.6575
|
||||
release: string, // e.g. 22H2
|
||||
|
||||
os: Version, // e.g. {major = 10, minor = 10, patch = 0}
|
||||
kernel: Version, // e.g. {major = 10, minor = 19045, patch = 6575}
|
||||
}
|
||||
|
||||
OS_Version_Platform :: enum {
|
||||
Unknown,
|
||||
@@ -26,54 +121,69 @@ Version :: struct {
|
||||
major, minor, patch: int,
|
||||
}
|
||||
|
||||
OS_Version :: struct {
|
||||
platform: OS_Version_Platform,
|
||||
/*
|
||||
Iterates over GPU adapters
|
||||
|
||||
using _: Version,
|
||||
build: [2]int,
|
||||
version: string,
|
||||
On Windows: Enumerates `Computer\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}`
|
||||
Elsewhere: Unsupported at the moment, returns `{}, 0, false`
|
||||
|
||||
as_string: string,
|
||||
}
|
||||
Important: The `vendor` name, `model` name and `driver` version strings are backed by the `GPU_Iterator`.
|
||||
Clone them if you want to these to persist.
|
||||
|
||||
RAM :: struct {
|
||||
total_ram: int,
|
||||
free_ram: int,
|
||||
total_swap: int,
|
||||
free_swap: int,
|
||||
Inputs:
|
||||
- it: A pointer to a `GPU_Iterator`
|
||||
- minimum_vram: The number of bytes of VRAM an adapter has to have to be considered, default 256 MiB
|
||||
(This excludes most screen mirroring / remote desktop drivers)
|
||||
|
||||
Returns:
|
||||
gpu: A `GPU` struct which contains `vendor` name, `model` name, `driver` version and `vram` in bytes
|
||||
index: Loop index, optional
|
||||
ok: `true` if this was a success and we should continue, `false` otherwise
|
||||
*/
|
||||
iterate_gpus :: proc(it: ^GPU_Iterator, minimum_vram := i64(256 * 1024 * 1024)) -> (gpu: GPU, index: int, ok: bool) {
|
||||
when ODIN_OS == .Windows {
|
||||
return _iterate_gpus(it, minimum_vram)
|
||||
} else {
|
||||
// Not implemented on another OS, yet
|
||||
return {}, 0, false
|
||||
}
|
||||
}
|
||||
|
||||
GPU :: struct {
|
||||
vendor_name: string,
|
||||
model_name: string,
|
||||
total_ram: int,
|
||||
vendor: string,
|
||||
model: string,
|
||||
driver: string,
|
||||
vram: i64,
|
||||
}
|
||||
|
||||
GPU_Iterator :: struct {
|
||||
// Public iterator index
|
||||
index: int,
|
||||
|
||||
// Internal buffer + index
|
||||
_buffer: [512]u8,
|
||||
_index: int,
|
||||
}
|
||||
|
||||
@(private)
|
||||
version_string_buf: [1024]u8
|
||||
_parse_version :: proc (str: string) -> (res: Version) {
|
||||
str := str
|
||||
i: int
|
||||
for part in strings.split_iterator(&str, ".") {
|
||||
defer i += 1
|
||||
dst: ^int
|
||||
switch i {
|
||||
case 0: dst = &res.major
|
||||
case 1: dst = &res.minor
|
||||
case 2: dst = &res.patch
|
||||
case: return
|
||||
}
|
||||
|
||||
@(private)
|
||||
MAX_GPUS :: 16
|
||||
|
||||
@(private)
|
||||
_gpus: [MAX_GPUS]GPU
|
||||
|
||||
@(private)
|
||||
_gpu_string_buf: [MAX_GPUS * 256 * 2]u8 // Reserve up to 256 bytes for each GPU's vendor and model name
|
||||
|
||||
@(private)
|
||||
_gpu_string_offset: int
|
||||
|
||||
@(private)
|
||||
intern_gpu_string :: proc "contextless" (str: string) -> (res: string, ok: bool) {
|
||||
if _gpu_string_offset + len(str) + 1 > size_of(_gpu_string_buf) {
|
||||
return "", false
|
||||
if num, num_ok := strconv.parse_int(part); !num_ok {
|
||||
return
|
||||
} else {
|
||||
dst^ = num
|
||||
}
|
||||
}
|
||||
|
||||
n := copy(_gpu_string_buf[_gpu_string_offset:], str)
|
||||
_gpu_string_buf[_gpu_string_offset + len(str)] = 0
|
||||
res = string(_gpu_string_buf[_gpu_string_offset:][:len(str)])
|
||||
_gpu_string_offset += n + 1
|
||||
|
||||
return res, true
|
||||
return
|
||||
}
|
||||
@@ -46,4 +46,5 @@ CTL_HW :: 6
|
||||
HW_VERSION :: 16
|
||||
HW_SERIALNO :: 17
|
||||
HW_UUID :: 18
|
||||
HW_PHYSMEM64 :: 19
|
||||
HW_PHYSMEM64 :: 19
|
||||
HW_USERMEM64 :: 20
|
||||
Reference in New Issue
Block a user