providers/darwin/host_darwin.go (202 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.
//go:build amd64 || arm64
package darwin
import (
"context"
"errors"
"fmt"
"os"
"time"
"github.com/elastic/go-sysinfo/internal/registry"
"github.com/elastic/go-sysinfo/providers/shared"
"github.com/elastic/go-sysinfo/types"
)
func init() {
registry.Register(darwinSystem{})
}
type darwinSystem struct{}
func (s darwinSystem) Host() (types.Host, error) {
return newHost()
}
type host struct {
info types.HostInfo
}
func (h *host) Info() types.HostInfo {
return h.info
}
func (h *host) CPUTime() (types.CPUTimes, error) {
cpu, err := getHostCPULoadInfo()
if err != nil {
return types.CPUTimes{}, fmt.Errorf("failed to get host CPU usage: %w", err)
}
ticksPerSecond := time.Duration(getClockTicks())
return types.CPUTimes{
User: time.Duration(cpu.User) * time.Second / ticksPerSecond,
System: time.Duration(cpu.System) * time.Second / ticksPerSecond,
Idle: time.Duration(cpu.Idle) * time.Second / ticksPerSecond,
Nice: time.Duration(cpu.Nice) * time.Second / ticksPerSecond,
}, nil
}
func (h *host) Memory() (*types.HostMemoryInfo, error) {
var mem types.HostMemoryInfo
// Total physical memory.
total, err := MemTotal()
if err != nil {
return nil, fmt.Errorf("failed to get total physical memory: %w", err)
}
mem.Total = total
// Page size for computing byte totals.
pageSizeBytes, err := getPageSize()
if err != nil {
return nil, fmt.Errorf("failed to get page size: %w", err)
}
// Swap
swap, err := getSwapUsage()
if err != nil {
return nil, fmt.Errorf("failed to get swap usage: %w", err)
}
mem.VirtualTotal = swap.Total
mem.VirtualUsed = swap.Used
mem.VirtualFree = swap.Available
// Virtual Memory Statistics
vmStat, err := getHostVMInfo64()
if errors.Is(err, types.ErrNotImplemented) {
return &mem, nil
}
if err != nil {
return nil, fmt.Errorf("failed to get virtual memory statistics: %w", err)
}
inactiveBytes := uint64(vmStat.Inactive_count) * pageSizeBytes
purgeableBytes := uint64(vmStat.Purgeable_count) * pageSizeBytes
mem.Metrics = map[string]uint64{
"active_bytes": uint64(vmStat.Active_count) * pageSizeBytes,
"compressed_bytes": uint64(vmStat.Compressor_page_count) * pageSizeBytes,
"compressions_bytes": uint64(vmStat.Compressions) * pageSizeBytes, // Cumulative compressions.
"copy_on_write_faults": vmStat.Cow_faults,
"decompressions_bytes": uint64(vmStat.Decompressions) * pageSizeBytes, // Cumulative decompressions.
"external_bytes": uint64(vmStat.External_page_count) * pageSizeBytes, // File Cache / File-backed pages
"inactive_bytes": inactiveBytes,
"internal_bytes": uint64(vmStat.Internal_page_count) * pageSizeBytes, // App Memory / Anonymous
"page_ins_bytes": uint64(vmStat.Pageins) * pageSizeBytes,
"page_outs_bytes": uint64(vmStat.Pageouts) * pageSizeBytes,
"purgeable_bytes": purgeableBytes,
"purged_bytes": uint64(vmStat.Purges) * pageSizeBytes,
"reactivated_bytes": uint64(vmStat.Reactivations) * pageSizeBytes,
"speculative_bytes": uint64(vmStat.Speculative_count) * pageSizeBytes,
"swap_ins_bytes": uint64(vmStat.Swapins) * pageSizeBytes,
"swap_outs_bytes": uint64(vmStat.Swapouts) * pageSizeBytes,
"throttled_bytes": uint64(vmStat.Throttled_count) * pageSizeBytes,
"translation_faults": vmStat.Faults,
"uncompressed_bytes": uint64(vmStat.Total_uncompressed_pages_in_compressor) * pageSizeBytes,
"wired_bytes": uint64(vmStat.Wire_count) * pageSizeBytes,
"zero_filled_bytes": uint64(vmStat.Zero_fill_count) * pageSizeBytes,
}
// From Activity Monitor: Memory Used = App Memory (internal) + Wired + Compressed
// https://support.apple.com/en-us/HT201538
mem.Used = uint64(vmStat.Internal_page_count+vmStat.Wire_count+vmStat.Compressor_page_count) * pageSizeBytes
mem.Free = uint64(vmStat.Free_count) * pageSizeBytes
mem.Available = mem.Free + inactiveBytes + purgeableBytes
return &mem, nil
}
func (h *host) FQDNWithContext(ctx context.Context) (string, error) {
return shared.FQDNWithContext(ctx)
}
func (h *host) FQDN() (string, error) {
return h.FQDNWithContext(context.Background())
}
func (h *host) LoadAverage() (*types.LoadAverageInfo, error) {
load, err := getLoadAverage()
if err != nil {
return nil, fmt.Errorf("failed to get loadavg: %w", err)
}
scale := float64(load.scale)
return &types.LoadAverageInfo{
One: float64(load.load[0]) / scale,
Five: float64(load.load[1]) / scale,
Fifteen: float64(load.load[2]) / scale,
}, nil
}
func newHost() (*host, error) {
h := &host{}
r := &reader{}
r.architecture(h)
r.nativeArchitecture(h)
r.bootTime(h)
r.hostname(h)
r.network(h)
r.kernelVersion(h)
r.os(h)
r.time(h)
r.uniqueID(h)
return h, r.Err()
}
type reader struct {
errs []error
}
func (r *reader) addErr(err error) bool {
if err != nil {
if !errors.Is(err, types.ErrNotImplemented) {
r.errs = append(r.errs, err)
}
return true
}
return false
}
func (r *reader) Err() error {
if len(r.errs) > 0 {
return errors.Join(r.errs...)
}
return nil
}
func (r *reader) architecture(h *host) {
v, err := Architecture()
if r.addErr(err) {
return
}
h.info.Architecture = v
}
func (r *reader) nativeArchitecture(h *host) {
v, err := NativeArchitecture()
if r.addErr(err) {
return
}
h.info.NativeArchitecture = v
}
func (r *reader) bootTime(h *host) {
v, err := BootTime()
if r.addErr(err) {
return
}
h.info.BootTime = v
}
func (r *reader) hostname(h *host) {
v, err := os.Hostname()
if r.addErr(err) {
return
}
h.info.Hostname = v
}
func (r *reader) network(h *host) {
ips, macs, err := shared.Network()
if r.addErr(err) {
return
}
h.info.IPs = ips
h.info.MACs = macs
}
func (r *reader) kernelVersion(h *host) {
v, err := KernelVersion()
if r.addErr(err) {
return
}
h.info.KernelVersion = v
}
func (r *reader) os(h *host) {
v, err := OperatingSystem()
if r.addErr(err) {
return
}
h.info.OS = v
}
func (r *reader) time(h *host) {
h.info.Timezone, h.info.TimezoneOffsetSec = time.Now().Zone()
}
func (r *reader) uniqueID(h *host) {
v, err := MachineID()
if r.addErr(err) {
return
}
h.info.UniqueID = v
}