diff --git a/util/util.go b/util/util.go index 385534e2e6..e0fa929d41 100644 --- a/util/util.go +++ b/util/util.go @@ -21,7 +21,6 @@ package util import ( "fmt" - "runtime" "syscall" ) @@ -74,18 +73,7 @@ func GetCurrentProcessTimes() (utime int64, stime int64, err error) { return } -// GetTotalMemory gets total system memory on Linux systems +// GetTotalMemory gets total system memory func GetTotalMemory() uint64 { - switch runtime.GOOS { - case "linux": - // Use sysinfo on Linux - var si syscall.Sysinfo_t - err := syscall.Sysinfo(&si) - if err != nil { - return 0 - } - return si.Totalram - default: - return 0 - } + return getTotalMemory() } diff --git a/util/util_darwin.go b/util/util_darwin.go new file mode 100644 index 0000000000..7c08acdefe --- /dev/null +++ b/util/util_darwin.go @@ -0,0 +1,34 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package util + +import ( + "syscall" + "unsafe" +) + +func getTotalMemory() uint64 { + out, err := syscall.Sysctl("hw.memsize") + if err != nil { + return 0 + } + b := []byte(out) + if len(b) < 8 { + b = append(b, 0) + } + return *(*uint64)(unsafe.Pointer(&b[0])) +} diff --git a/util/util_linux.go b/util/util_linux.go new file mode 100644 index 0000000000..f958ebdf5c --- /dev/null +++ b/util/util_linux.go @@ -0,0 +1,31 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package util + +import ( + "syscall" +) + +func getTotalMemory() uint64 { + var si syscall.Sysinfo_t + err := syscall.Sysinfo(&si) + if err != nil { + return 0 + } + // support 32-bit systems where Totalram is uint32 + return uint64(si.Totalram) * uint64(si.Unit) +} diff --git a/util/util_test.go b/util/util_test.go new file mode 100644 index 0000000000..f33b5c5f95 --- /dev/null +++ b/util/util_test.go @@ -0,0 +1,32 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package util + +import ( + "testing" + + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/require" +) + +func TestGetTotalMemory(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + mem := GetTotalMemory() + require.Greater(t, mem, uint64(0)) +} diff --git a/util/util_windows.go b/util/util_windows.go index c14c527256..e1e1b7924b 100644 --- a/util/util_windows.go +++ b/util/util_windows.go @@ -21,6 +21,7 @@ import ( "math" "syscall" "time" + "unsafe" ) /* misc */ @@ -70,7 +71,38 @@ func filetimeToDuration(ft *syscall.Filetime) time.Duration { return time.Duration(n * 100) } -// GetTotalMemory gets total system memory, returns 0 on Windows +// GetTotalMemory gets total system memory on Windows func GetTotalMemory() uint64 { - return 0 + var memoryStatusEx MemoryStatusEx + memoryStatusEx.dwLength = uint32(unsafe.Sizeof(memoryStatusEx)) + + if err := globalMemoryStatusEx(&memoryStatusEx); err != nil { + return 0 + } + return memoryStatusEx.ullTotalPhys +} + +type MemoryStatusEx struct { + dwLength uint32 + dwMemoryLoad uint32 + ullTotalPhys uint64 + ullAvailPhys uint64 + ullTotalPageFile uint64 + ullAvailPageFile uint64 + ullTotalVirtual uint64 + ullAvailVirtual uint64 + ullAvailExtendedVirtual uint64 +} + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx") +) + +func globalMemoryStatusEx(memoryStatusEx *MemoryStatusEx) error { + ret, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(memoryStatusEx))) + if ret == 0 { + return syscall.GetLastError() + } + return nil }