From e100d14a8dc7251a3d73b2289797c45ac629387f Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy Date: Tue, 11 Mar 2025 13:10:55 -0400 Subject: [PATCH 1/3] util: implement GetTotalMemory for mac and windows --- util/util_darwin.go | 35 ++++++++++++++++++++++++++++++++++ util/util_linux.go | 30 +++++++++++++++++++++++++++++ util/{util.go => util_nix.go} | 16 ++-------------- util/util_windows.go | 36 +++++++++++++++++++++++++++++++++-- 4 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 util/util_darwin.go create mode 100644 util/util_linux.go rename util/{util.go => util_nix.go} (88%) diff --git a/util/util_darwin.go b/util/util_darwin.go new file mode 100644 index 0000000000..1670160fbe --- /dev/null +++ b/util/util_darwin.go @@ -0,0 +1,35 @@ +// 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 ( + "fmt" + "syscall" +) + +func getTotalMemory() uint64 { + out, err := syscall.Sysctl("hw.memsize") + if err != nil { + return 0 + } + var mem uint64 + _, err = fmt.Sscanf(out, "%d", &mem) + if err != nil { + return 0 + } + return mem +} diff --git a/util/util_linux.go b/util/util_linux.go new file mode 100644 index 0000000000..8ec5e2867f --- /dev/null +++ b/util/util_linux.go @@ -0,0 +1,30 @@ +// 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 + } + return si.Totalram +} diff --git a/util/util.go b/util/util_nix.go similarity index 88% rename from util/util.go rename to util/util_nix.go index 385534e2e6..e0fa929d41 100644 --- a/util/util.go +++ b/util/util_nix.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_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 } From 324825946a0441b56375da272685e286ab0ff563 Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy Date: Tue, 11 Mar 2025 13:48:01 -0400 Subject: [PATCH 2/3] test + fixes --- util/{util_nix.go => util.go} | 0 util/util_darwin.go | 11 +++++------ util/util_linux.go | 3 ++- util/util_test.go | 28 ++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 7 deletions(-) rename util/{util_nix.go => util.go} (100%) create mode 100644 util/util_test.go diff --git a/util/util_nix.go b/util/util.go similarity index 100% rename from util/util_nix.go rename to util/util.go diff --git a/util/util_darwin.go b/util/util_darwin.go index 1670160fbe..7c08acdefe 100644 --- a/util/util_darwin.go +++ b/util/util_darwin.go @@ -17,8 +17,8 @@ package util import ( - "fmt" "syscall" + "unsafe" ) func getTotalMemory() uint64 { @@ -26,10 +26,9 @@ func getTotalMemory() uint64 { if err != nil { return 0 } - var mem uint64 - _, err = fmt.Sscanf(out, "%d", &mem) - if err != nil { - return 0 + b := []byte(out) + if len(b) < 8 { + b = append(b, 0) } - return mem + return *(*uint64)(unsafe.Pointer(&b[0])) } diff --git a/util/util_linux.go b/util/util_linux.go index 8ec5e2867f..f958ebdf5c 100644 --- a/util/util_linux.go +++ b/util/util_linux.go @@ -26,5 +26,6 @@ func getTotalMemory() uint64 { if err != nil { return 0 } - return si.Totalram + // 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..cab2158b3e --- /dev/null +++ b/util/util_test.go @@ -0,0 +1,28 @@ +// 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/stretchr/testify/require" +) + +func TestGetTotalMemory(t *testing.T) { + mem := GetTotalMemory() + require.Greater(t, mem, uint64(0)) +} From 164fca170525207325b394f4880d44458928e479 Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy Date: Tue, 11 Mar 2025 13:53:40 -0400 Subject: [PATCH 3/3] fix linter --- util/util_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/util_test.go b/util/util_test.go index cab2158b3e..f33b5c5f95 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -19,10 +19,14 @@ 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)) }