metric/memory/memory_darwin.go (75 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package memory /* #include <stdlib.h> #include <sys/sysctl.h> #include <sys/mount.h> #include <mach/mach_init.h> #include <mach/mach_host.h> #include <mach/host_info.h> #include <libproc.h> #include <mach/processor_info.h> #include <mach/vm_map.h> */ import "C" import ( "bytes" "encoding/binary" "fmt" "syscall" "unsafe" "github.com/elastic/elastic-agent-libs/opt" "github.com/elastic/elastic-agent-system-metrics/metric/system/resolve" ) type xswUsage struct { Total, Avail, Used uint64 } // get is the darwin implementation for fetching Memory data func get(_ resolve.Resolver) (Memory, error) { var vmstat C.vm_statistics_data_t mem := Memory{} var total uint64 if err := sysctlbyname("hw.memsize", &total); err != nil { return Memory{}, fmt.Errorf("error getting memsize: %w", err) } mem.Total = opt.UintWith(total) if err := vmInfo(&vmstat); err != nil { return Memory{}, fmt.Errorf("error getting VM info") } kern := uint64(vmstat.inactive_count) << 12 free := uint64(vmstat.free_count) << 12 mem.Free = opt.UintWith(free) mem.Used.Bytes = opt.UintWith(total - free) mem.Actual.Free = opt.UintWith(free + kern) mem.Actual.Used.Bytes = opt.UintWith((total - free) - kern) var err error mem.Swap, err = getSwap() if err != nil { return mem, fmt.Errorf("error getting swap memory: %w", err) } return mem, nil } // Get fetches swap data func getSwap() (SwapMetrics, error) { swUsage := xswUsage{} swap := SwapMetrics{} if err := sysctlbyname("vm.swapusage", &swUsage); err != nil { return swap, fmt.Errorf("error getting swap usage: %w", err) } swap.Total = opt.UintWith(swUsage.Total) swap.Used.Bytes = opt.UintWith(swUsage.Used) swap.Free = opt.UintWith(swUsage.Avail) return swap, nil } // generic Sysctl buffer unmarshalling func sysctlbyname(name string, data interface{}) (err error) { val, err := syscall.Sysctl(name) if err != nil { return err } buf := []byte(val) switch v := data.(type) { case *uint64: *v = *(*uint64)(unsafe.Pointer(&buf[0])) return } bbuf := bytes.NewBuffer([]byte(val)) return binary.Read(bbuf, binary.LittleEndian, data) } func vmInfo(vmstat *C.vm_statistics_data_t) error { var count C.mach_msg_type_number_t = C.HOST_VM_INFO_COUNT status := C.host_statistics( C.host_t(C.mach_host_self()), C.HOST_VM_INFO, C.host_info_t(unsafe.Pointer(vmstat)), &count) if status != C.KERN_SUCCESS { return fmt.Errorf("host_statistics=%d", status) } return nil }