mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 16:14:44 +00:00
100 lines
2.2 KiB
Go
100 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/shirou/gopsutil/v3/mem"
|
|
)
|
|
|
|
func collectMemory() (MemoryStatus, error) {
|
|
vm, err := mem.VirtualMemory()
|
|
if err != nil {
|
|
return MemoryStatus{}, err
|
|
}
|
|
|
|
swap, _ := mem.SwapMemory()
|
|
pressure := getMemoryPressure()
|
|
|
|
// On macOS, vm.Cached is 0, so we calculate from file-backed pages.
|
|
cached := vm.Cached
|
|
if runtime.GOOS == "darwin" && cached == 0 {
|
|
cached = getFileBackedMemory()
|
|
}
|
|
|
|
return MemoryStatus{
|
|
Used: vm.Used,
|
|
Total: vm.Total,
|
|
UsedPercent: vm.UsedPercent,
|
|
SwapUsed: swap.Used,
|
|
SwapTotal: swap.Total,
|
|
Cached: cached,
|
|
Pressure: pressure,
|
|
}, nil
|
|
}
|
|
|
|
func getFileBackedMemory() uint64 {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
|
defer cancel()
|
|
out, err := runCmd(ctx, "vm_stat")
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
// Parse page size from first line: "Mach Virtual Memory Statistics: (page size of 16384 bytes)"
|
|
var pageSize uint64 = 4096 // Default
|
|
firstLine := true
|
|
for line := range strings.Lines(out) {
|
|
if firstLine {
|
|
firstLine = false
|
|
if strings.Contains(line, "page size of") {
|
|
if _, after, found := strings.Cut(line, "page size of "); found {
|
|
if before, _, found := strings.Cut(after, " bytes"); found {
|
|
if size, err := strconv.ParseUint(strings.TrimSpace(before), 10, 64); err == nil {
|
|
pageSize = size
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse "File-backed pages: 388975."
|
|
if strings.Contains(line, "File-backed pages:") {
|
|
if _, after, found := strings.Cut(line, ":"); found {
|
|
numStr := strings.TrimSpace(after)
|
|
numStr = strings.TrimSuffix(numStr, ".")
|
|
if pages, err := strconv.ParseUint(numStr, 10, 64); err == nil {
|
|
return pages * pageSize
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func getMemoryPressure() string {
|
|
if runtime.GOOS != "darwin" {
|
|
return ""
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
|
defer cancel()
|
|
out, err := runCmd(ctx, "memory_pressure")
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
lower := strings.ToLower(out)
|
|
if strings.Contains(lower, "critical") {
|
|
return "critical"
|
|
}
|
|
if strings.Contains(lower, "warn") {
|
|
return "warn"
|
|
}
|
|
if strings.Contains(lower, "normal") {
|
|
return "normal"
|
|
}
|
|
return ""
|
|
}
|