1
0
mirror of https://github.com/shirou/gopsutil.git synced 2025-04-26 13:48:59 +08:00

Compare commits

...

49 Commits

Author SHA1 Message Date
shirou
62a52f910b
Merge pull request #1745 from wangyu-geek/add_anolis_family_in_host_linux
add anolis family with anolis platform in host/host_linux.go
2024-11-16 22:29:20 +09:00
wangyu
8e62971eb0 add anolis family with anolis platform 2024-11-15 19:28:24 +08:00
Lomanic
aa9796d3d7
Merge pull request #1723 from shirou/dependabot/go_modules/golang.org/x/sys-0.26.0
chore(deps): bump golang.org/x/sys from 0.25.0 to 0.26.0
2024-10-28 23:40:06 +01:00
Lomanic
6be0508aeb
Merge pull request #1739 from shirou/dependabot/github_actions/actions/checkout-4.2.2
chore(deps): bump actions/checkout from 4.2.1 to 4.2.2
2024-10-24 11:46:35 +02:00
dependabot[bot]
137fd2acac
chore(deps): bump actions/checkout from 4.2.1 to 4.2.2
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.1 to 4.2.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](eef61447b9...11bd71901b)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-24 01:26:12 +00:00
shirou
aa47e0fde0
Merge pull request #1720 from n4nn31355/feat/win_mem_commit_total
feat(mem): Add windows commit stats
2024-10-23 21:09:47 +09:00
shirou
de435c3739
Merge pull request #1737 from shirou/dependabot/go_modules/github.com/ebitengine/purego-0.8.1
chore(deps): bump github.com/ebitengine/purego from 0.8.0 to 0.8.1
2024-10-22 11:00:38 +09:00
dependabot[bot]
9ae3f38658
chore(deps): bump github.com/ebitengine/purego from 0.8.0 to 0.8.1
Bumps [github.com/ebitengine/purego](https://github.com/ebitengine/purego) from 0.8.0 to 0.8.1.
- [Release notes](https://github.com/ebitengine/purego/releases)
- [Commits](https://github.com/ebitengine/purego/compare/v0.8.0...v0.8.1)

---
updated-dependencies:
- dependency-name: github.com/ebitengine/purego
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-22 01:29:59 +00:00
shirou
a19cedeb30
Merge pull request #1727 from uubulb/fix_sensors_darwin_arm64
sensors: avoid passing nil pointer to CFArrayGetCount
2024-10-13 22:05:28 +09:00
shirou
c9da2cc0aa
Merge pull request #1731 from Lomanic/issue1645
[cpu][disk][netbsd] Generate arm-specific definitions
2024-10-12 21:37:56 +09:00
shirou
44c71b62c0
Merge pull request #1722 from uubulb/fix_windows_mem
fix(mem): possible memory leak on Windows
2024-10-11 15:45:29 +09:00
shirou
30457973b5
Merge pull request #1735 from shirou/dependabot/github_actions/actions/upload-artifact-4.4.3
chore(deps): bump actions/upload-artifact from 4.4.0 to 4.4.3
2024-10-10 10:12:24 +09:00
shirou
7422a58e32
Merge pull request #1733 from shirou/dependabot/github_actions/actions/cache-4.1.1
chore(deps): bump actions/cache from 4.0.2 to 4.1.1
2024-10-10 10:12:11 +09:00
dependabot[bot]
c30c83bf4a
chore(deps): bump actions/upload-artifact from 4.4.0 to 4.4.3
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.0 to 4.4.3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](50769540e7...b4b15b8c7c)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-10 01:05:56 +00:00
dependabot[bot]
9fc4f64913
chore(deps): bump actions/cache from 4.0.2 to 4.1.1
Bumps [actions/cache](https://github.com/actions/cache) from 4.0.2 to 4.1.1.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](0c45773b62...3624ceb22c)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-09 01:04:20 +00:00
Lomanic
68512056e8
Merge pull request #1729 from shirou/dependabot/github_actions/actions/checkout-4.2.1
chore(deps): bump actions/checkout from 4.2.0 to 4.2.1
2024-10-09 00:55:07 +02:00
Lomanic
ec973203e9 [disk][netbsd] Generate cpu_netbsd_arm.go via mktypes.sh 2024-10-09 00:20:53 +02:00
Lomanic
c37a5eba8c [cpu][netbsd] Fix "undefined: cpuTimes" error at compile time on arm
Fixes #1645
2024-10-09 00:20:24 +02:00
dependabot[bot]
9732546382
chore(deps): bump actions/checkout from 4.2.0 to 4.2.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](d632683dd7...eef61447b9)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-08 01:50:59 +00:00
uubulb
10d9c04c2e sensors: avoid passing nil pointer to CFArrayGetCount 2024-10-08 00:53:42 +08:00
dependabot[bot]
4cb0abd1d2
chore(deps): bump golang.org/x/sys from 0.25.0 to 0.26.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.25.0 to 0.26.0.
- [Commits](https://github.com/golang/sys/compare/v0.25.0...v0.26.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-07 01:19:22 +00:00
uubulb
3773f6fe43 fix(mem): possible memory leak on Windows 2024-10-05 15:29:36 +08:00
shirou
45e3591650
Merge pull request #1714 from shirou/dependabot/github_actions/actions/checkout-4.2.0
chore(deps): bump actions/checkout from 4.1.7 to 4.2.0
2024-10-04 10:21:31 +09:00
dependabot[bot]
3c43ac0060
chore(deps): bump actions/checkout from 4.1.7 to 4.2.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 4.2.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](692973e3d9...d632683dd7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-04 01:12:46 +00:00
shirou
023f0cf765
Merge pull request #1721 from shirou/dependabot/github_actions/golangci/golangci-lint-action-6.1.1
chore(deps): bump golangci/golangci-lint-action from 6.1.0 to 6.1.1
2024-10-04 10:12:07 +09:00
dependabot[bot]
b048ca575d
chore(deps): bump golangci/golangci-lint-action from 6.1.0 to 6.1.1
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 6.1.0 to 6.1.1.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](aaa42aa062...971e284b60)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-04 01:04:29 +00:00
Vyacheslav Artemiev
3f241a0b08 feat(mem): Add windows commit stats
Closes Expose MemCommit info for Windows #1719
2024-10-04 01:57:36 +04:00
shirou
2e10d9f7d0
Merge pull request #1715 from henrygd/master
Fix error message typo in sensors_linux
2024-10-01 10:17:32 +09:00
Henry Dollman
112f4c00b7 Fix error message typo in sensors_linux 2024-09-26 14:13:46 -04:00
shirou
7ec134321c
Merge pull request #1713 from joshiste/joshiste-precise-create-time
Fix resolution for process create time limited to seconds
2024-09-26 22:58:15 +09:00
shirou
4140cda4ac
Merge pull request #1702 from uubulb/purego_darwin
feat(cpu, mem, sensors, disk, process)(darwin): cgo-free implementations
2024-09-26 22:37:05 +09:00
Johannes Edmeier
3dc12249d3
Allow subsecont precision for process create time 2024-09-24 18:50:58 +02:00
uubulb
f56b53a155 update purego 2024-09-24 23:11:59 +08:00
shirou
fdc3c05c92
Merge pull request #1712 from al-online/numfds_for_windows
implement process:NumFDs for Windows
2024-09-24 18:50:13 +09:00
huang
462736cb8b add comment for NumFDsWithContext 2024-09-23 22:05:49 +08:00
UUBulb
86b68e8d4f
Merge branch 'master' into purego_darwin 2024-09-20 22:27:05 +08:00
uubulb
9e6efdb991 update disk & cpu & process 2024-09-20 22:24:12 +08:00
huang
cbc32afb65 implement NumFDs for Windows 2024-09-19 23:59:11 +08:00
shirou
6c06ac987e
Merge pull request #1707 from atoulme/add_proc_mountinfo
[common] add HOST_PROC_MOUNTINFO to EnvMap
2024-09-15 22:01:11 +09:00
shirou
7802a18557
Merge pull request #1703 from shirou/dependabot/go_modules/golang.org/x/sys-0.25.0
chore(deps): bump golang.org/x/sys from 0.24.0 to 0.25.0
2024-09-14 16:41:37 +09:00
shirou
e89f21d7dc
Merge pull request #1706 from Lomanic/issue1698
[process][darwin][freebsd][linux][openbsd] Make process.Children not reliant on pgrep
2024-09-11 22:28:07 +09:00
Antoine Toulme
811185d3fe [common] add HOST_PROC_MOUNTINFO to EnvMap 2024-09-10 08:29:09 -07:00
Lomanic
76ccf0d220 [process][darwin][freebsd][linux][openbsd] Make process.Children not reliant on pgrep
pgrep -P $PID exits with status of 1 (and nothing in stdout nor stderr) both if
a process doesn't exist or it doesn't have child processes, so we don't
use it anymore on these OSes. We sort PIDs as pgrep did.

Also deprecate the ErrorNoChildren error when there are no child processes,
this is erroneous (simply check for the length of the returned slice, plus
this is not an error per se), this was only returned on linux anyway.

Fixes #1698
2024-09-10 15:34:36 +02:00
shirou
784157497b
Merge pull request #1705 from shirou/feat/remove_godoc_and_coverall
fix: remove coverall and godocs.io badge from README
2024-09-07 00:09:44 +09:00
shirou
97b1aaee94 fix: remove coverall and godocs.io badge from README 2024-09-07 00:00:45 +09:00
dependabot[bot]
f824d50add
chore(deps): bump golang.org/x/sys from 0.24.0 to 0.25.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.24.0 to 0.25.0.
- [Commits](https://github.com/golang/sys/compare/v0.24.0...v0.25.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-05 01:03:44 +00:00
uubulb
701a74be41 feat(cpu, mem, sensors)(darwin): cgo-free implementations 2024-09-04 23:23:10 +08:00
shirou
211eb8ccb4
Merge pull request #1701 from shirou/dependabot/github_actions/actions/upload-artifact-4.4.0
chore(deps): bump actions/upload-artifact from 4.3.6 to 4.4.0
2024-09-02 10:39:47 +09:00
dependabot[bot]
050902aeaa
chore(deps): bump actions/upload-artifact from 4.3.6 to 4.4.0
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.6 to 4.4.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](834a144ee9...50769540e7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 01:12:28 +00:00
50 changed files with 1543 additions and 1426 deletions

View File

@ -26,13 +26,13 @@ jobs:
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- id: cache-paths
run: |
echo "::set-output name=cache::$(go env GOCACHE)"
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
- name: Cache go modules
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
with:
path: |
${{ steps.cache-paths.outputs.cache }}

View File

@ -21,9 +21,9 @@ jobs:
go-version: 1.17
cache: false
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup golangci-lint
uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0
uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1
with:
args: --verbose
version: latest

View File

@ -10,6 +10,6 @@ jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Release
run: make release

View File

@ -13,13 +13,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: advanced-security/sbom-generator-action@375dee8e6144d9fd0ec1f5667b4f6fb4faacefed # v0.0.1
id: sbom
env:
GITHUB_TOKEN: ${{ github.token }}
- uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
- uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
path: ${{steps.sbom.outputs.fileName }}
name: "SBOM"

View File

@ -8,6 +8,6 @@ jobs:
name: Shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0

View File

@ -27,13 +27,13 @@ jobs:
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- id: go-env
run: |
echo "::set-output name=cache::$(go env GOCACHE)"
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
- name: Cache go modules
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
with:
path: |
${{ steps.go-env.outputs.cache }}

View File

@ -1,6 +1,6 @@
# gopsutil: psutil for golang
[![Test](https://github.com/shirou/gopsutil/actions/workflows/test.yml/badge.svg)](https://github.com/shirou/gopsutil/actions/workflows/test.yml) [![Coverage Status](https://coveralls.io/repos/github/shirou/gopsutil/badge.svg?branch=master)](https://coveralls.io/github/shirou/gopsutil?branch=master) [![Go Reference](https://pkg.go.dev/badge/github.com/shirou/gopsutil/v4.svg)](https://pkg.go.dev/github.com/shirou/gopsutil/v4) [![Go Documentation](https://godocs.io/github.com/shirou/gopsutil/v4?status.svg)](https://godocs.io/github.com/shirou/gopsutil/v4) [![Calendar Versioning](https://img.shields.io/badge/calver-vMAJOR.YY.MM-22bfda.svg)](https://calver.org/)
[![Test](https://github.com/shirou/gopsutil/actions/workflows/test.yml/badge.svg)](https://github.com/shirou/gopsutil/actions/workflows/test.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/shirou/gopsutil/v4.svg)](https://pkg.go.dev/github.com/shirou/gopsutil/v4) [![Calendar Versioning](https://img.shields.io/badge/calver-vMAJOR.YY.MM-22bfda.svg)](https://calver.org/)
This is a port of psutil (https://github.com/giampaolo/psutil). The
challenge is porting all psutil functions on some architectures.

View File

@ -12,13 +12,14 @@ type EnvKeyType string
var EnvKey = EnvKeyType("env")
const (
HostProcEnvKey EnvKeyType = "HOST_PROC"
HostSysEnvKey EnvKeyType = "HOST_SYS"
HostEtcEnvKey EnvKeyType = "HOST_ETC"
HostVarEnvKey EnvKeyType = "HOST_VAR"
HostRunEnvKey EnvKeyType = "HOST_RUN"
HostDevEnvKey EnvKeyType = "HOST_DEV"
HostRootEnvKey EnvKeyType = "HOST_ROOT"
HostProcEnvKey EnvKeyType = "HOST_PROC"
HostSysEnvKey EnvKeyType = "HOST_SYS"
HostEtcEnvKey EnvKeyType = "HOST_ETC"
HostVarEnvKey EnvKeyType = "HOST_VAR"
HostRunEnvKey EnvKeyType = "HOST_RUN"
HostDevEnvKey EnvKeyType = "HOST_DEV"
HostRootEnvKey EnvKeyType = "HOST_ROOT"
HostProcMountinfo EnvKeyType = "HOST_PROC_MOUNTINFO"
)
type EnvMap map[EnvKeyType]string

View File

@ -5,12 +5,15 @@ package cpu
import (
"context"
"fmt"
"strconv"
"strings"
"unsafe"
"github.com/shoenig/go-m1cpu"
"github.com/tklauser/go-sysconf"
"golang.org/x/sys/unix"
"github.com/shirou/gopsutil/v4/internal/common"
)
// sys/resource.h
@ -23,6 +26,24 @@ const (
cpUStates = 5
)
// mach/machine.h
const (
cpuStateUser = 0
cpuStateSystem = 1
cpuStateIdle = 2
cpuStateNice = 3
cpuStateMax = 4
)
// mach/processor_info.h
const (
processorCpuLoadInfo = 2
)
type hostCpuLoadInfoData struct {
cpuTicks [cpuStateMax]uint32
}
// default value. from time.h
var ClocksPerSec = float64(128)
@ -39,11 +60,17 @@ func Times(percpu bool) ([]TimesStat, error) {
}
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
lib, err := common.NewLibrary(common.System)
if err != nil {
return nil, err
}
defer lib.Close()
if percpu {
return perCPUTimes()
return perCPUTimes(lib)
}
return allCPUTimes()
return allCPUTimes(lib)
}
// Returns only one CPUInfoStat on FreeBSD
@ -86,15 +113,9 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
c.CacheSize = int32(cacheSize)
c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor")
if m1cpu.IsAppleSilicon() {
c.Mhz = float64(m1cpu.PCoreHz() / 1_000_000)
} else {
// Use the rated frequency of the CPU. This is a static value and does not
// account for low power or Turbo Boost modes.
cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency")
if err == nil {
c.Mhz = float64(cpuFrequency) / 1000000.0
}
v, err := getFrequency()
if err == nil {
c.Mhz = v
}
return append(ret, c), nil
@ -115,3 +136,63 @@ func CountsWithContext(ctx context.Context, logical bool) (int, error) {
return int(count), nil
}
func perCPUTimes(machLib *common.Library) ([]TimesStat, error) {
machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym)
machTaskSelf := common.GetFunc[common.MachTaskSelfFunc](machLib, common.MachTaskSelfSym)
hostProcessorInfo := common.GetFunc[common.HostProcessorInfoFunc](machLib, common.HostProcessorInfoSym)
vmDeallocate := common.GetFunc[common.VMDeallocateFunc](machLib, common.VMDeallocateSym)
var count, ncpu uint32
var cpuload *hostCpuLoadInfoData
status := hostProcessorInfo(machHostSelf(), processorCpuLoadInfo, &ncpu, uintptr(unsafe.Pointer(&cpuload)), &count)
if status != common.KERN_SUCCESS {
return nil, fmt.Errorf("host_processor_info error=%d", status)
}
defer vmDeallocate(machTaskSelf(), uintptr(unsafe.Pointer(cpuload)), uintptr(ncpu))
ret := []TimesStat{}
loads := unsafe.Slice(cpuload, ncpu)
for i := 0; i < int(ncpu); i++ {
c := TimesStat{
CPU: fmt.Sprintf("cpu%d", i),
User: float64(loads[i].cpuTicks[cpuStateUser]) / ClocksPerSec,
System: float64(loads[i].cpuTicks[cpuStateSystem]) / ClocksPerSec,
Nice: float64(loads[i].cpuTicks[cpuStateNice]) / ClocksPerSec,
Idle: float64(loads[i].cpuTicks[cpuStateIdle]) / ClocksPerSec,
}
ret = append(ret, c)
}
return ret, nil
}
func allCPUTimes(machLib *common.Library) ([]TimesStat, error) {
machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym)
hostStatistics := common.GetFunc[common.HostStatisticsFunc](machLib, common.HostStatisticsSym)
var cpuload hostCpuLoadInfoData
count := uint32(cpuStateMax)
status := hostStatistics(machHostSelf(), common.HOST_CPU_LOAD_INFO,
uintptr(unsafe.Pointer(&cpuload)), &count)
if status != common.KERN_SUCCESS {
return nil, fmt.Errorf("host_statistics error=%d", status)
}
c := TimesStat{
CPU: "cpu-total",
User: float64(cpuload.cpuTicks[cpuStateUser]) / ClocksPerSec,
System: float64(cpuload.cpuTicks[cpuStateSystem]) / ClocksPerSec,
Nice: float64(cpuload.cpuTicks[cpuStateNice]) / ClocksPerSec,
Idle: float64(cpuload.cpuTicks[cpuStateIdle]) / ClocksPerSec,
}
return []TimesStat{c}, nil
}

80
cpu/cpu_darwin_arm64.go Normal file
View File

@ -0,0 +1,80 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && arm64
package cpu
import (
"encoding/binary"
"fmt"
"unsafe"
"github.com/shirou/gopsutil/v4/internal/common"
)
// https://github.com/shoenig/go-m1cpu/blob/v0.1.6/cpu.go
func getFrequency() (float64, error) {
ioKit, err := common.NewLibrary(common.IOKit)
if err != nil {
return 0, err
}
defer ioKit.Close()
coreFoundation, err := common.NewLibrary(common.CoreFoundation)
if err != nil {
return 0, err
}
defer coreFoundation.Close()
ioServiceMatching := common.GetFunc[common.IOServiceMatchingFunc](ioKit, common.IOServiceMatchingSym)
ioServiceGetMatchingServices := common.GetFunc[common.IOServiceGetMatchingServicesFunc](ioKit, common.IOServiceGetMatchingServicesSym)
ioIteratorNext := common.GetFunc[common.IOIteratorNextFunc](ioKit, common.IOIteratorNextSym)
ioRegistryEntryGetName := common.GetFunc[common.IORegistryEntryGetNameFunc](ioKit, common.IORegistryEntryGetNameSym)
ioRegistryEntryCreateCFProperty := common.GetFunc[common.IORegistryEntryCreateCFPropertyFunc](ioKit, common.IORegistryEntryCreateCFPropertySym)
ioObjectRelease := common.GetFunc[common.IOObjectReleaseFunc](ioKit, common.IOObjectReleaseSym)
cfStringCreateWithCString := common.GetFunc[common.CFStringCreateWithCStringFunc](coreFoundation, common.CFStringCreateWithCStringSym)
cfDataGetLength := common.GetFunc[common.CFDataGetLengthFunc](coreFoundation, common.CFDataGetLengthSym)
cfDataGetBytePtr := common.GetFunc[common.CFDataGetBytePtrFunc](coreFoundation, common.CFDataGetBytePtrSym)
cfRelease := common.GetFunc[common.CFReleaseFunc](coreFoundation, common.CFReleaseSym)
matching := ioServiceMatching("AppleARMIODevice")
var iterator uint32
if status := ioServiceGetMatchingServices(common.KIOMainPortDefault, uintptr(matching), &iterator); status != common.KERN_SUCCESS {
return 0.0, fmt.Errorf("IOServiceGetMatchingServices error=%d", status)
}
defer ioObjectRelease(iterator)
pCorekey := cfStringCreateWithCString(common.KCFAllocatorDefault, "voltage-states5-sram", common.KCFStringEncodingUTF8)
defer cfRelease(uintptr(pCorekey))
var pCoreHz uint32
for {
service := ioIteratorNext(iterator)
if !(service > 0) {
break
}
buf := make([]byte, 512)
ioRegistryEntryGetName(service, &buf[0])
if common.GoString(&buf[0]) == "pmgr" {
pCoreRef := ioRegistryEntryCreateCFProperty(service, uintptr(pCorekey), common.KCFAllocatorDefault, common.KNilOptions)
length := cfDataGetLength(uintptr(pCoreRef))
data := cfDataGetBytePtr(uintptr(pCoreRef))
// composite uint32 from the byte array
buf := unsafe.Slice((*byte)(data), length)
// combine the bytes into a uint32 value
b := buf[length-8 : length-4]
pCoreHz = binary.LittleEndian.Uint32(b)
ioObjectRelease(service)
break
}
ioObjectRelease(service)
}
return float64(pCoreHz / 1_000_000), nil
}

View File

@ -1,111 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && cgo
package cpu
/*
#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 <TargetConditionals.h>
#if TARGET_OS_MAC
#include <libproc.h>
#endif
#include <mach/processor_info.h>
#include <mach/vm_map.h>
*/
import "C"
import (
"bytes"
"encoding/binary"
"fmt"
"unsafe"
)
// these CPU times for darwin is borrowed from influxdb/telegraf.
func perCPUTimes() ([]TimesStat, error) {
var (
count C.mach_msg_type_number_t
cpuload *C.processor_cpu_load_info_data_t
ncpu C.natural_t
)
status := C.host_processor_info(C.host_t(C.mach_host_self()),
C.PROCESSOR_CPU_LOAD_INFO,
&ncpu,
(*C.processor_info_array_t)(unsafe.Pointer(&cpuload)),
&count)
if status != C.KERN_SUCCESS {
return nil, fmt.Errorf("host_processor_info error=%d", status)
}
// jump through some cgo casting hoops and ensure we properly free
// the memory that cpuload points to
target := C.vm_map_t(C.mach_task_self_)
address := C.vm_address_t(uintptr(unsafe.Pointer(cpuload)))
defer C.vm_deallocate(target, address, C.vm_size_t(ncpu))
// the body of struct processor_cpu_load_info
// aka processor_cpu_load_info_data_t
var cpu_ticks [C.CPU_STATE_MAX]uint32
// copy the cpuload array to a []byte buffer
// where we can binary.Read the data
size := int(ncpu) * binary.Size(cpu_ticks)
buf := (*[1 << 30]byte)(unsafe.Pointer(cpuload))[:size:size]
bbuf := bytes.NewBuffer(buf)
var ret []TimesStat
for i := 0; i < int(ncpu); i++ {
err := binary.Read(bbuf, binary.LittleEndian, &cpu_ticks)
if err != nil {
return nil, err
}
c := TimesStat{
CPU: fmt.Sprintf("cpu%d", i),
User: float64(cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec,
System: float64(cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec,
Nice: float64(cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec,
Idle: float64(cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec,
}
ret = append(ret, c)
}
return ret, nil
}
func allCPUTimes() ([]TimesStat, error) {
var count C.mach_msg_type_number_t
var cpuload C.host_cpu_load_info_data_t
count = C.HOST_CPU_LOAD_INFO_COUNT
status := C.host_statistics(C.host_t(C.mach_host_self()),
C.HOST_CPU_LOAD_INFO,
C.host_info_t(unsafe.Pointer(&cpuload)),
&count)
if status != C.KERN_SUCCESS {
return nil, fmt.Errorf("host_statistics error=%d", status)
}
c := TimesStat{
CPU: "cpu-total",
User: float64(cpuload.cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec,
System: float64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec,
Nice: float64(cpuload.cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec,
Idle: float64(cpuload.cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec,
}
return []TimesStat{c}, nil
}

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && !arm64
package cpu
import "golang.org/x/sys/unix"
func getFrequency() (float64, error) {
// Use the rated frequency of the CPU. This is a static value and does not
// account for low power or Turbo Boost modes.
cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency")
return float64(cpuFrequency) / 1000000.0, err
}

View File

@ -1,14 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && !cgo
package cpu
import "github.com/shirou/gopsutil/v4/internal/common"
func perCPUTimes() ([]TimesStat, error) {
return []TimesStat{}, common.ErrNotImplementedError
}
func allCPUTimes() ([]TimesStat, error) {
return []TimesStat{}, common.ErrNotImplementedError
}

View File

@ -5,13 +5,12 @@ package cpu
import (
"os"
"runtime"
"testing"
"github.com/shoenig/go-m1cpu"
)
func TestInfo_AppleSilicon(t *testing.T) {
if !m1cpu.IsAppleSilicon() {
if runtime.GOARCH != "arm64" {
t.Skip("wrong cpu type")
}

10
cpu/cpu_netbsd_arm.go Normal file
View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: BSD-3-Clause
package cpu
type cpuTimes struct {
User uint32
Nice uint32
Sys uint32
Intr uint32
Idle uint32
}

View File

@ -5,6 +5,8 @@ package disk
import (
"context"
"fmt"
"unsafe"
"golang.org/x/sys/unix"
@ -92,3 +94,201 @@ func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
func LabelWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
ioKit, err := common.NewLibrary(common.IOKit)
if err != nil {
return nil, err
}
defer ioKit.Close()
coreFoundation, err := common.NewLibrary(common.CoreFoundation)
if err != nil {
return nil, err
}
defer coreFoundation.Close()
ioServiceMatching := common.GetFunc[common.IOServiceMatchingFunc](ioKit, common.IOServiceMatchingSym)
ioServiceGetMatchingServices := common.GetFunc[common.IOServiceGetMatchingServicesFunc](ioKit, common.IOServiceGetMatchingServicesSym)
ioIteratorNext := common.GetFunc[common.IOIteratorNextFunc](ioKit, common.IOIteratorNextSym)
ioObjectRelease := common.GetFunc[common.IOObjectReleaseFunc](ioKit, common.IOObjectReleaseSym)
cfDictionaryAddValue := common.GetFunc[common.CFDictionaryAddValueFunc](coreFoundation, common.CFDictionaryAddValueSym)
cfStringCreateWithCString := common.GetFunc[common.CFStringCreateWithCStringFunc](coreFoundation, common.CFStringCreateWithCStringSym)
cfRelease := common.GetFunc[common.CFReleaseFunc](coreFoundation, common.CFReleaseSym)
kCFBooleanTruePtr, _ := coreFoundation.Dlsym("kCFBooleanTrue")
match := ioServiceMatching("IOMedia")
key := cfStringCreateWithCString(common.KCFAllocatorDefault, common.KIOMediaWholeKey, common.KCFStringEncodingUTF8)
defer cfRelease(uintptr(key))
var drives uint32
kCFBooleanTrue := **(**uintptr)(unsafe.Pointer(&kCFBooleanTruePtr))
cfDictionaryAddValue(uintptr(match), uintptr(key), kCFBooleanTrue)
if status := ioServiceGetMatchingServices(common.KIOMainPortDefault, uintptr(match), &drives); status != common.KERN_SUCCESS {
return nil, fmt.Errorf("IOServiceGetMatchingServices error=%d", status)
}
defer ioObjectRelease(drives)
ic := &ioCounters{
ioKit: ioKit,
coreFoundation: coreFoundation,
ioRegistryEntryCreateCFProperties: common.GetFunc[common.IORegistryEntryCreateCFPropertiesFunc](ioKit, common.IORegistryEntryCreateCFPropertiesSym),
ioObjectRelease: ioObjectRelease,
cfStringCreateWithCString: cfStringCreateWithCString,
cfDictionaryGetValue: common.GetFunc[common.CFDictionaryGetValueFunc](coreFoundation, common.CFDictionaryGetValueSym),
cfNumberGetValue: common.GetFunc[common.CFNumberGetValueFunc](coreFoundation, common.CFNumberGetValueSym),
cfRelease: cfRelease,
}
stats := make([]IOCountersStat, 0, 16)
for {
d := ioIteratorNext(drives)
if !(d > 0) {
break
}
stat, err := ic.getDriveStat(d)
if err != nil {
return nil, err
}
if stat != nil {
stats = append(stats, *stat)
}
ioObjectRelease(d)
}
ret := make(map[string]IOCountersStat, 0)
for i := 0; i < len(stats); i++ {
if len(names) > 0 && !common.StringsHas(names, stats[i].Name) {
continue
}
stats[i].ReadTime = stats[i].ReadTime / 1000 / 1000 // note: read/write time are in ns, but we want ms.
stats[i].WriteTime = stats[i].WriteTime / 1000 / 1000
stats[i].IoTime = stats[i].ReadTime + stats[i].WriteTime
ret[stats[i].Name] = stats[i]
}
return ret, nil
}
const (
kIOBSDNameKey = "BSD Name"
kIOMediaSizeKey = "Size"
kIOMediaPreferredBlockSizeKey = "Preferred Block Size"
kIOBlockStorageDriverStatisticsKey = "Statistics"
kIOBlockStorageDriverStatisticsBytesReadKey = "Bytes (Read)"
kIOBlockStorageDriverStatisticsBytesWrittenKey = "Bytes (Write)"
kIOBlockStorageDriverStatisticsReadsKey = "Operations (Read)"
kIOBlockStorageDriverStatisticsWritesKey = "Operations (Write)"
kIOBlockStorageDriverStatisticsTotalReadTimeKey = "Total Time (Read)"
kIOBlockStorageDriverStatisticsTotalWriteTimeKey = "Total Time (Write)"
)
type ioCounters struct {
ioKit *common.Library
coreFoundation *common.Library
ioRegistryEntryCreateCFProperties common.IORegistryEntryCreateCFPropertiesFunc
ioObjectRelease common.IOObjectReleaseFunc
cfStringCreateWithCString common.CFStringCreateWithCStringFunc
cfDictionaryGetValue common.CFDictionaryGetValueFunc
cfNumberGetValue common.CFNumberGetValueFunc
cfRelease common.CFReleaseFunc
}
func (i *ioCounters) getDriveStat(d uint32) (*IOCountersStat, error) {
ioRegistryEntryGetParentEntry := common.GetFunc[common.IORegistryEntryGetParentEntryFunc](i.ioKit, common.IORegistryEntryGetParentEntrySym)
ioObjectConformsTo := common.GetFunc[common.IOObjectConformsToFunc](i.ioKit, common.IOObjectConformsToSym)
cfStringGetLength := common.GetFunc[common.CFStringGetLengthFunc](i.coreFoundation, common.CFStringGetLengthSym)
cfStringGetCString := common.GetFunc[common.CFStringGetCStringFunc](i.coreFoundation, common.CFStringGetCStringSym)
var parent uint32
if status := ioRegistryEntryGetParentEntry(d, common.KIOServicePlane, &parent); status != common.KERN_SUCCESS {
return nil, fmt.Errorf("IORegistryEntryGetParentEntry error=%d", status)
}
defer i.ioObjectRelease(parent)
if !ioObjectConformsTo(parent, "IOBlockStorageDriver") {
//return nil, fmt.Errorf("ERROR: the object is not of the IOBlockStorageDriver class")
return nil, nil
}
var props unsafe.Pointer
if status := i.ioRegistryEntryCreateCFProperties(d, unsafe.Pointer(&props), common.KCFAllocatorDefault, common.KNilOptions); status != common.KERN_SUCCESS {
return nil, fmt.Errorf("IORegistryEntryCreateCFProperties error=%d", status)
}
defer i.cfRelease(uintptr(props))
key := i.cfStr(kIOBSDNameKey)
defer i.cfRelease(uintptr(key))
name := i.cfDictionaryGetValue(uintptr(props), uintptr(key))
length := cfStringGetLength(uintptr(name)) + 1
buf := make([]byte, length-1)
cfStringGetCString(uintptr(name), &buf[0], length, common.KCFStringEncodingUTF8)
stat, err := i.fillStat(parent)
if err != nil {
return nil, err
}
if stat != nil {
stat.Name = string(buf)
return stat, nil
}
return nil, nil
}
func (i *ioCounters) fillStat(d uint32) (*IOCountersStat, error) {
var props unsafe.Pointer
status := i.ioRegistryEntryCreateCFProperties(d, unsafe.Pointer(&props), common.KCFAllocatorDefault, common.KNilOptions)
if status != common.KERN_SUCCESS {
return nil, fmt.Errorf("IORegistryEntryCreateCFProperties error=%d", status)
}
if props == nil {
return nil, nil
}
defer i.cfRelease(uintptr(props))
key := i.cfStr(kIOBlockStorageDriverStatisticsKey)
defer i.cfRelease(uintptr(key))
v := i.cfDictionaryGetValue(uintptr(props), uintptr(key))
if v == nil {
return nil, fmt.Errorf("CFDictionaryGetValue failed")
}
var stat IOCountersStat
statstab := map[string]uintptr{
kIOBlockStorageDriverStatisticsBytesReadKey: unsafe.Offsetof(stat.ReadBytes),
kIOBlockStorageDriverStatisticsBytesWrittenKey: unsafe.Offsetof(stat.WriteBytes),
kIOBlockStorageDriverStatisticsReadsKey: unsafe.Offsetof(stat.ReadCount),
kIOBlockStorageDriverStatisticsWritesKey: unsafe.Offsetof(stat.WriteCount),
kIOBlockStorageDriverStatisticsTotalReadTimeKey: unsafe.Offsetof(stat.ReadTime),
kIOBlockStorageDriverStatisticsTotalWriteTimeKey: unsafe.Offsetof(stat.WriteTime),
}
for key, off := range statstab {
s := i.cfStr(key)
defer i.cfRelease(uintptr(s))
if num := i.cfDictionaryGetValue(uintptr(v), uintptr(s)); num != nil {
i.cfNumberGetValue(uintptr(num), common.KCFNumberSInt64Type, uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(&stat))+off)))
}
}
return &stat, nil
}
func (i *ioCounters) cfStr(str string) unsafe.Pointer {
return i.cfStringCreateWithCString(common.KCFAllocatorDefault, str, common.KCFStringEncodingUTF8)
}

View File

@ -1,45 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && cgo && !ios
package disk
/*
#cgo LDFLAGS: -framework CoreFoundation -framework IOKit
#include <stdint.h>
#include <CoreFoundation/CoreFoundation.h>
#include "iostat_darwin.h"
*/
import "C"
import (
"context"
"github.com/shirou/gopsutil/v4/internal/common"
)
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
var buf [C.NDRIVE]C.DriveStats
n, err := C.gopsutil_v4_readdrivestat(&buf[0], C.int(len(buf)))
if err != nil {
return nil, err
}
ret := make(map[string]IOCountersStat, 0)
for i := 0; i < int(n); i++ {
d := IOCountersStat{
ReadBytes: uint64(buf[i].read),
WriteBytes: uint64(buf[i].written),
ReadCount: uint64(buf[i].nread),
WriteCount: uint64(buf[i].nwrite),
ReadTime: uint64(buf[i].readtime / 1000 / 1000), // note: read/write time are in ns, but we want ms.
WriteTime: uint64(buf[i].writetime / 1000 / 1000),
IoTime: uint64((buf[i].readtime + buf[i].writetime) / 1000 / 1000),
Name: C.GoString(&buf[i].name[0]),
}
if len(names) > 0 && !common.StringsHas(names, d.Name) {
continue
}
ret[d.Name] = d
}
return ret, nil
}

View File

@ -1,14 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build (darwin && !cgo) || ios
package disk
import (
"context"
"github.com/shirou/gopsutil/v4/internal/common"
)
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
return nil, common.ErrNotImplementedError
}

46
disk/disk_netbsd_arm.go Normal file
View File

@ -0,0 +1,46 @@
//go:build netbsd && arm
// +build netbsd,arm
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs disk/types_netbsd.go
package disk
const (
sizeOfStatvfs = 0xcc8
)
type (
Statvfs struct {
Flag uint32
Bsize uint32
Frsize uint32
Iosize uint32
Blocks uint64
Bfree uint64
Bavail uint64
Bresvd uint64
Files uint64
Ffree uint64
Favail uint64
Fresvd uint64
Syncreads uint64
Syncwrites uint64
Asyncreads uint64
Asyncwrites uint64
Fsidx _Ctype_struct___0
Fsid uint32
Namemax uint32
Owner uint32
Pad_cgo_0 [4]byte
Spare [4]uint64
Fstypename [32]uint8
Mntonname [1024]uint8
Mntfromname [1024]uint8
Mntfromlabel [1024]uint8
}
)
type _Ctype_struct___0 struct {
FsidVal [2]int32
}

View File

@ -1,131 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: Copyright (c) 2017, kadota kyohei
// https://github.com/lufia/iostat/blob/9f7362b77ad333b26c01c99de52a11bdb650ded2/iostat_darwin.c
#include <stdint.h>
#include <CoreFoundation/CoreFoundation.h>
#include "iostat_darwin.h"
#define IOKIT 1 /* to get io_name_t in device_types.h */
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/IOBSD.h>
#include <mach/mach_host.h>
static int getdrivestat(io_registry_entry_t d, DriveStats *stat);
static int fillstat(io_registry_entry_t d, DriveStats *stat);
int
gopsutil_v4_readdrivestat(DriveStats a[], int n)
{
CFMutableDictionaryRef match;
io_iterator_t drives;
io_registry_entry_t d;
kern_return_t status;
int na, rv;
match = IOServiceMatching("IOMedia");
CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
status = IOServiceGetMatchingServices(0, match, &drives);
if(status != KERN_SUCCESS)
return -1;
na = 0;
while(na < n && (d=IOIteratorNext(drives)) > 0){
rv = getdrivestat(d, &a[na]);
if(rv < 0)
return -1;
if(rv > 0)
na++;
IOObjectRelease(d);
}
IOObjectRelease(drives);
return na;
}
static int
getdrivestat(io_registry_entry_t d, DriveStats *stat)
{
io_registry_entry_t parent;
kern_return_t status;
CFDictionaryRef props;
CFStringRef name;
CFNumberRef num;
int rv;
memset(stat, 0, sizeof *stat);
status = IORegistryEntryGetParentEntry(d, kIOServicePlane, &parent);
if(status != KERN_SUCCESS)
return -1;
if(!IOObjectConformsTo(parent, "IOBlockStorageDriver")){
IOObjectRelease(parent);
return 0;
}
status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions);
if(status != KERN_SUCCESS){
IOObjectRelease(parent);
return -1;
}
name = (CFStringRef)CFDictionaryGetValue(props, CFSTR(kIOBSDNameKey));
CFStringGetCString(name, stat->name, NAMELEN, CFStringGetSystemEncoding());
num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaSizeKey));
CFNumberGetValue(num, kCFNumberSInt64Type, &stat->size);
num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaPreferredBlockSizeKey));
CFNumberGetValue(num, kCFNumberSInt64Type, &stat->blocksize);
CFRelease(props);
rv = fillstat(parent, stat);
IOObjectRelease(parent);
if(rv < 0)
return -1;
return 1;
}
static struct {
char *key;
size_t off;
} statstab[] = {
{kIOBlockStorageDriverStatisticsBytesReadKey, offsetof(DriveStats, read)},
{kIOBlockStorageDriverStatisticsBytesWrittenKey, offsetof(DriveStats, written)},
{kIOBlockStorageDriverStatisticsReadsKey, offsetof(DriveStats, nread)},
{kIOBlockStorageDriverStatisticsWritesKey, offsetof(DriveStats, nwrite)},
{kIOBlockStorageDriverStatisticsTotalReadTimeKey, offsetof(DriveStats, readtime)},
{kIOBlockStorageDriverStatisticsTotalWriteTimeKey, offsetof(DriveStats, writetime)},
{kIOBlockStorageDriverStatisticsLatentReadTimeKey, offsetof(DriveStats, readlat)},
{kIOBlockStorageDriverStatisticsLatentWriteTimeKey, offsetof(DriveStats, writelat)},
};
static int
fillstat(io_registry_entry_t d, DriveStats *stat)
{
CFDictionaryRef props, v;
CFNumberRef num;
kern_return_t status;
typeof(statstab[0]) *bp, *ep;
status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions);
if(status != KERN_SUCCESS)
return -1;
v = (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOBlockStorageDriverStatisticsKey));
if(v == NULL){
CFRelease(props);
return -1;
}
ep = &statstab[sizeof(statstab)/sizeof(statstab[0])];
for(bp = &statstab[0]; bp < ep; bp++){
CFStringRef s;
s = CFStringCreateWithCString(kCFAllocatorDefault, bp->key, CFStringGetSystemEncoding());
num = (CFNumberRef)CFDictionaryGetValue(v, s);
if(num)
CFNumberGetValue(num, kCFNumberSInt64Type, ((char*)stat)+bp->off);
CFRelease(s);
}
CFRelease(props);
return 0;
}

View File

@ -1,34 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: Copyright (c) 2017, kadota kyohei
// https://github.com/lufia/iostat/blob/9f7362b77ad333b26c01c99de52a11bdb650ded2/iostat_darwin.h
typedef struct DriveStats DriveStats;
typedef struct CPUStats CPUStats;
enum {
NDRIVE = 16,
NAMELEN = 31
};
struct DriveStats {
char name[NAMELEN+1];
int64_t size;
int64_t blocksize;
int64_t read;
int64_t written;
int64_t nread;
int64_t nwrite;
int64_t readtime;
int64_t writetime;
int64_t readlat;
int64_t writelat;
};
struct CPUStats {
natural_t user;
natural_t nice;
natural_t sys;
natural_t idle;
};
extern int gopsutil_v4_readdrivestat(DriveStats a[], int n);

4
go.mod
View File

@ -3,14 +3,14 @@ module github.com/shirou/gopsutil/v4
go 1.18
require (
github.com/ebitengine/purego v0.8.1
github.com/google/go-cmp v0.6.0
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c
github.com/shoenig/go-m1cpu v0.1.6
github.com/stretchr/testify v1.9.0
github.com/tklauser/go-sysconf v0.3.12
github.com/yusufpapurcu/wmi v1.2.4
golang.org/x/sys v0.24.0
golang.org/x/sys v0.26.0
)
require (

9
go.sum
View File

@ -1,5 +1,7 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -11,9 +13,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
@ -26,8 +25,8 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -315,6 +315,8 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
family = "solus"
case "neokylin":
family = "neokylin"
case "anolis":
family = "anolis"
}
return platform, family, version, nil

View File

@ -5,11 +5,13 @@ package common
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"unsafe"
"github.com/ebitengine/purego"
"golang.org/x/sys/unix"
)
@ -64,3 +66,299 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
return buf, length, nil
}
// Library represents a dynamic library loaded by purego.
type Library struct {
addr uintptr
path string
close func()
}
// library paths
const (
IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit"
CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"
System = "/usr/lib/libSystem.B.dylib"
)
func NewLibrary(path string) (*Library, error) {
lib, err := purego.Dlopen(path, purego.RTLD_LAZY|purego.RTLD_GLOBAL)
if err != nil {
return nil, err
}
closeFunc := func() {
purego.Dlclose(lib)
}
return &Library{
addr: lib,
path: path,
close: closeFunc,
}, nil
}
func (lib *Library) Dlsym(symbol string) (uintptr, error) {
return purego.Dlsym(lib.addr, symbol)
}
func GetFunc[T any](lib *Library, symbol string) T {
var fptr T
purego.RegisterLibFunc(&fptr, lib.addr, symbol)
return fptr
}
func (lib *Library) Close() {
lib.close()
}
// status codes
const (
KERN_SUCCESS = 0
)
// IOKit functions and symbols.
type (
IOServiceGetMatchingServiceFunc func(mainPort uint32, matching uintptr) uint32
IOServiceGetMatchingServicesFunc func(mainPort uint32, matching uintptr, existing *uint32) int
IOServiceMatchingFunc func(name string) unsafe.Pointer
IOServiceOpenFunc func(service, owningTask, connType uint32, connect *uint32) int
IOServiceCloseFunc func(connect uint32) int
IOIteratorNextFunc func(iterator uint32) uint32
IORegistryEntryGetNameFunc func(entry uint32, name *byte) int
IORegistryEntryGetParentEntryFunc func(entry uint32, plane string, parent *uint32) int
IORegistryEntryCreateCFPropertyFunc func(entry uint32, key, allocator uintptr, options uint32) unsafe.Pointer
IORegistryEntryCreateCFPropertiesFunc func(entry uint32, properties unsafe.Pointer, allocator uintptr, options uint32) int
IOObjectConformsToFunc func(object uint32, className string) bool
IOObjectReleaseFunc func(object uint32) int
IOConnectCallStructMethodFunc func(connection, selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int
IOHIDEventSystemClientCreateFunc func(allocator uintptr) unsafe.Pointer
IOHIDEventSystemClientSetMatchingFunc func(client, match uintptr) int
IOHIDServiceClientCopyEventFunc func(service uintptr, eventType int64,
options int32, timeout int64) unsafe.Pointer
IOHIDServiceClientCopyPropertyFunc func(service, property uintptr) unsafe.Pointer
IOHIDEventGetFloatValueFunc func(event uintptr, field int32) float64
IOHIDEventSystemClientCopyServicesFunc func(client uintptr) unsafe.Pointer
)
const (
IOServiceGetMatchingServiceSym = "IOServiceGetMatchingService"
IOServiceGetMatchingServicesSym = "IOServiceGetMatchingServices"
IOServiceMatchingSym = "IOServiceMatching"
IOServiceOpenSym = "IOServiceOpen"
IOServiceCloseSym = "IOServiceClose"
IOIteratorNextSym = "IOIteratorNext"
IORegistryEntryGetNameSym = "IORegistryEntryGetName"
IORegistryEntryGetParentEntrySym = "IORegistryEntryGetParentEntry"
IORegistryEntryCreateCFPropertySym = "IORegistryEntryCreateCFProperty"
IORegistryEntryCreateCFPropertiesSym = "IORegistryEntryCreateCFProperties"
IOObjectConformsToSym = "IOObjectConformsTo"
IOObjectReleaseSym = "IOObjectRelease"
IOConnectCallStructMethodSym = "IOConnectCallStructMethod"
IOHIDEventSystemClientCreateSym = "IOHIDEventSystemClientCreate"
IOHIDEventSystemClientSetMatchingSym = "IOHIDEventSystemClientSetMatching"
IOHIDServiceClientCopyEventSym = "IOHIDServiceClientCopyEvent"
IOHIDServiceClientCopyPropertySym = "IOHIDServiceClientCopyProperty"
IOHIDEventGetFloatValueSym = "IOHIDEventGetFloatValue"
IOHIDEventSystemClientCopyServicesSym = "IOHIDEventSystemClientCopyServices"
)
const (
KIOMainPortDefault = 0
KIOHIDEventTypeTemperature = 15
KNilOptions = 0
)
const (
KIOMediaWholeKey = "Media"
KIOServicePlane = "IOService"
)
// CoreFoundation functions and symbols.
type (
CFGetTypeIDFunc func(cf uintptr) int32
CFNumberCreateFunc func(allocator uintptr, theType int32, valuePtr uintptr) unsafe.Pointer
CFNumberGetValueFunc func(num uintptr, theType int32, valuePtr uintptr) bool
CFDictionaryCreateFunc func(allocator uintptr, keys, values *unsafe.Pointer, numValues int32,
keyCallBacks, valueCallBacks uintptr) unsafe.Pointer
CFDictionaryAddValueFunc func(theDict, key, value uintptr)
CFDictionaryGetValueFunc func(theDict, key uintptr) unsafe.Pointer
CFArrayGetCountFunc func(theArray uintptr) int32
CFArrayGetValueAtIndexFunc func(theArray uintptr, index int32) unsafe.Pointer
CFStringCreateMutableFunc func(alloc uintptr, maxLength int32) unsafe.Pointer
CFStringGetLengthFunc func(theString uintptr) int32
CFStringGetCStringFunc func(theString uintptr, buffer *byte, bufferSize int32, encoding uint32)
CFStringCreateWithCStringFunc func(alloc uintptr, cStr string, encoding uint32) unsafe.Pointer
CFDataGetLengthFunc func(theData uintptr) int32
CFDataGetBytePtrFunc func(theData uintptr) unsafe.Pointer
CFReleaseFunc func(cf uintptr)
)
const (
CFGetTypeIDSym = "CFGetTypeID"
CFNumberCreateSym = "CFNumberCreate"
CFNumberGetValueSym = "CFNumberGetValue"
CFDictionaryCreateSym = "CFDictionaryCreate"
CFDictionaryAddValueSym = "CFDictionaryAddValue"
CFDictionaryGetValueSym = "CFDictionaryGetValue"
CFArrayGetCountSym = "CFArrayGetCount"
CFArrayGetValueAtIndexSym = "CFArrayGetValueAtIndex"
CFStringCreateMutableSym = "CFStringCreateMutable"
CFStringGetLengthSym = "CFStringGetLength"
CFStringGetCStringSym = "CFStringGetCString"
CFStringCreateWithCStringSym = "CFStringCreateWithCString"
CFDataGetLengthSym = "CFDataGetLength"
CFDataGetBytePtrSym = "CFDataGetBytePtr"
CFReleaseSym = "CFRelease"
)
const (
KCFStringEncodingUTF8 = 0x08000100
KCFNumberSInt64Type = 4
KCFNumberIntType = 9
KCFAllocatorDefault = 0
)
// Kernel functions and symbols.
type MachTimeBaseInfo struct {
Numer uint32
Denom uint32
}
type (
HostProcessorInfoFunc func(host uint32, flavor int32, outProcessorCount *uint32, outProcessorInfo uintptr,
outProcessorInfoCnt *uint32) int
HostStatisticsFunc func(host uint32, flavor int32, hostInfoOut uintptr, hostInfoOutCnt *uint32) int
MachHostSelfFunc func() uint32
MachTaskSelfFunc func() uint32
MachTimeBaseInfoFunc func(info uintptr) int
VMDeallocateFunc func(targetTask uint32, vmAddress, vmSize uintptr) int
)
const (
HostProcessorInfoSym = "host_processor_info"
HostStatisticsSym = "host_statistics"
MachHostSelfSym = "mach_host_self"
MachTaskSelfSym = "mach_task_self"
MachTimeBaseInfoSym = "mach_timebase_info"
VMDeallocateSym = "vm_deallocate"
)
const (
CTL_KERN = 1
KERN_ARGMAX = 8
KERN_PROCARGS2 = 49
HOST_VM_INFO = 2
HOST_CPU_LOAD_INFO = 3
HOST_VM_INFO_COUNT = 0xf
)
// System functions and symbols.
type (
ProcPidPathFunc func(pid int32, buffer uintptr, bufferSize uint32) int32
ProcPidInfoFunc func(pid, flavor int32, arg uint64, buffer uintptr, bufferSize int32) int32
)
const (
SysctlSym = "sysctl"
ProcPidPathSym = "proc_pidpath"
ProcPidInfoSym = "proc_pidinfo"
)
const (
MAXPATHLEN = 1024
PROC_PIDPATHINFO_MAXSIZE = 4 * MAXPATHLEN
PROC_PIDTASKINFO = 4
PROC_PIDVNODEPATHINFO = 9
)
// SMC represents a SMC instance.
type SMC struct {
lib *Library
conn uint32
callStruct IOConnectCallStructMethodFunc
}
const ioServiceSMC = "AppleSMC"
const (
KSMCUserClientOpen = 0
KSMCUserClientClose = 1
KSMCHandleYPCEvent = 2
KSMCReadKey = 5
KSMCWriteKey = 6
KSMCGetKeyCount = 7
KSMCGetKeyFromIndex = 8
KSMCGetKeyInfo = 9
)
const (
KSMCSuccess = 0
KSMCError = 1
KSMCKeyNotFound = 132
)
func NewSMC(ioKit *Library) (*SMC, error) {
if ioKit.path != IOKit {
return nil, fmt.Errorf("library is not IOKit")
}
ioServiceGetMatchingService := GetFunc[IOServiceGetMatchingServiceFunc](ioKit, IOServiceGetMatchingServiceSym)
ioServiceMatching := GetFunc[IOServiceMatchingFunc](ioKit, IOServiceMatchingSym)
ioServiceOpen := GetFunc[IOServiceOpenFunc](ioKit, IOServiceOpenSym)
ioObjectRelease := GetFunc[IOObjectReleaseFunc](ioKit, IOObjectReleaseSym)
machTaskSelf := GetFunc[MachTaskSelfFunc](ioKit, MachTaskSelfSym)
ioConnectCallStructMethod := GetFunc[IOConnectCallStructMethodFunc](ioKit, IOConnectCallStructMethodSym)
service := ioServiceGetMatchingService(0, uintptr(ioServiceMatching(ioServiceSMC)))
if service == 0 {
return nil, fmt.Errorf("ERROR: %s NOT FOUND", ioServiceSMC)
}
var conn uint32
if result := ioServiceOpen(service, machTaskSelf(), 0, &conn); result != 0 {
return nil, fmt.Errorf("ERROR: IOServiceOpen failed")
}
ioObjectRelease(service)
return &SMC{
lib: ioKit,
conn: conn,
callStruct: ioConnectCallStructMethod,
}, nil
}
func (s *SMC) CallStruct(selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int {
return s.callStruct(s.conn, selector, inputStruct, inputStructCnt, outputStruct, outputStructCnt)
}
func (s *SMC) Close() error {
ioServiceClose := GetFunc[IOServiceCloseFunc](s.lib, IOServiceCloseSym)
if result := ioServiceClose(s.conn); result != 0 {
return fmt.Errorf("ERROR: IOServiceClose failed")
}
return nil
}
// https://github.com/ebitengine/purego/blob/main/internal/strings/strings.go#L26
func GoString(cStr *byte) string {
if cStr == nil {
return ""
}
var length int
for {
if *(*byte)(unsafe.Add(unsafe.Pointer(cStr), uintptr(length))) == '\x00' {
break
}
length++
}
return string(unsafe.Slice(cStr, length))
}

View File

@ -40,23 +40,3 @@ func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ..
}
return ret, nil
}
func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) {
out, err := invoke.CommandWithContext(ctx, "pgrep", "-P", strconv.Itoa(int(pid)))
if err != nil {
return []int32{}, err
}
lines := strings.Split(string(out), "\n")
ret := make([]int32, 0, len(lines))
for _, l := range lines {
if len(l) == 0 {
continue
}
i, err := strconv.ParseInt(l, 10, 32)
if err != nil {
continue
}
ret = append(ret, int32(i))
}
return ret, nil
}

View File

@ -11,7 +11,10 @@ import (
// ExVirtualMemory represents Windows specific information
// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-performance_information
type ExVirtualMemory struct {
CommitLimit uint64 `json:"commitLimit"`
CommitTotal uint64 `json:"commitTotal"`
VirtualTotal uint64 `json:"virtualTotal"`
VirtualAvail uint64 `json:"virtualAvail"`
}
@ -30,7 +33,16 @@ func (e *ExWindows) VirtualMemory() (*ExVirtualMemory, error) {
return nil, windows.GetLastError()
}
var perfInfo performanceInformation
perfInfo.cb = uint32(unsafe.Sizeof(perfInfo))
perf, _, _ := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&perfInfo)), uintptr(perfInfo.cb))
if perf == 0 {
return nil, windows.GetLastError()
}
ret := &ExVirtualMemory{
CommitLimit: perfInfo.commitLimit * perfInfo.pageSize,
CommitTotal: perfInfo.commitTotal * perfInfo.pageSize,
VirtualTotal: memInfo.ullTotalVirtual,
VirtualAvail: memInfo.ullAvailVirtual,
}

View File

@ -70,3 +70,61 @@ func SwapDevices() ([]*SwapDevice, error) {
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
return nil, common.ErrNotImplementedError
}
type vmStatisticsData struct {
freeCount uint32
activeCount uint32
inactiveCount uint32
wireCount uint32
_ [44]byte // Not used here
}
// VirtualMemory returns VirtualmemoryStat.
func VirtualMemory() (*VirtualMemoryStat, error) {
return VirtualMemoryWithContext(context.Background())
}
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
machLib, err := common.NewLibrary(common.System)
if err != nil {
return nil, err
}
defer machLib.Close()
hostStatistics := common.GetFunc[common.HostStatisticsFunc](machLib, common.HostStatisticsSym)
machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym)
count := uint32(common.HOST_VM_INFO_COUNT)
var vmstat vmStatisticsData
status := hostStatistics(machHostSelf(), common.HOST_VM_INFO,
uintptr(unsafe.Pointer(&vmstat)), &count)
if status != common.KERN_SUCCESS {
return nil, fmt.Errorf("host_statistics error=%d", status)
}
pageSizeAddr, _ := machLib.Dlsym("vm_kernel_page_size")
pageSize := **(**uint64)(unsafe.Pointer(&pageSizeAddr))
total, err := getHwMemsize()
if err != nil {
return nil, err
}
totalCount := uint32(total / pageSize)
availableCount := vmstat.inactiveCount + vmstat.freeCount
usedPercent := 100 * float64(totalCount-availableCount) / float64(totalCount)
usedCount := totalCount - availableCount
return &VirtualMemoryStat{
Total: total,
Available: pageSize * uint64(availableCount),
Used: pageSize * uint64(usedCount),
UsedPercent: usedPercent,
Free: pageSize * uint64(vmstat.freeCount),
Active: pageSize * uint64(vmstat.activeCount),
Inactive: pageSize * uint64(vmstat.inactiveCount),
Wired: pageSize * uint64(vmstat.wireCount),
}, nil
}

View File

@ -1,58 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && cgo
package mem
/*
#include <mach/mach_host.h>
#include <mach/vm_page_size.h>
*/
import "C"
import (
"context"
"fmt"
"unsafe"
)
// VirtualMemory returns VirtualmemoryStat.
func VirtualMemory() (*VirtualMemoryStat, error) {
return VirtualMemoryWithContext(context.Background())
}
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
count := C.mach_msg_type_number_t(C.HOST_VM_INFO_COUNT)
var vmstat C.vm_statistics_data_t
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 nil, fmt.Errorf("host_statistics error=%d", status)
}
pageSize := uint64(C.vm_kernel_page_size)
total, err := getHwMemsize()
if err != nil {
return nil, err
}
totalCount := C.natural_t(total / pageSize)
availableCount := vmstat.inactive_count + vmstat.free_count
usedPercent := 100 * float64(totalCount-availableCount) / float64(totalCount)
usedCount := totalCount - availableCount
return &VirtualMemoryStat{
Total: total,
Available: pageSize * uint64(availableCount),
Used: pageSize * uint64(usedCount),
UsedPercent: usedPercent,
Free: pageSize * uint64(vmstat.free_count),
Active: pageSize * uint64(vmstat.active_count),
Inactive: pageSize * uint64(vmstat.inactive_count),
Wired: pageSize * uint64(vmstat.wire_count),
}, nil
}

View File

@ -1,89 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && !cgo
package mem
import (
"context"
"strconv"
"strings"
"golang.org/x/sys/unix"
)
// Runs vm_stat and returns Free and inactive pages
func getVMStat(vms *VirtualMemoryStat) error {
out, err := invoke.Command("vm_stat")
if err != nil {
return err
}
return parseVMStat(string(out), vms)
}
func parseVMStat(out string, vms *VirtualMemoryStat) error {
var err error
lines := strings.Split(out, "\n")
pagesize := uint64(unix.Getpagesize())
for _, line := range lines {
fields := strings.Split(line, ":")
if len(fields) < 2 {
continue
}
key := strings.TrimSpace(fields[0])
value := strings.Trim(fields[1], " .")
switch key {
case "Pages free":
free, e := strconv.ParseUint(value, 10, 64)
if e != nil {
err = e
}
vms.Free = free * pagesize
case "Pages inactive":
inactive, e := strconv.ParseUint(value, 10, 64)
if e != nil {
err = e
}
vms.Inactive = inactive * pagesize
case "Pages active":
active, e := strconv.ParseUint(value, 10, 64)
if e != nil {
err = e
}
vms.Active = active * pagesize
case "Pages wired down":
wired, e := strconv.ParseUint(value, 10, 64)
if e != nil {
err = e
}
vms.Wired = wired * pagesize
}
}
return err
}
// VirtualMemory returns VirtualmemoryStat.
func VirtualMemory() (*VirtualMemoryStat, error) {
return VirtualMemoryWithContext(context.Background())
}
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
ret := &VirtualMemoryStat{}
total, err := getHwMemsize()
if err != nil {
return nil, err
}
err = getVMStat(ret)
if err != nil {
return nil, err
}
ret.Available = ret.Free + ret.Inactive
ret.Total = total
ret.Used = ret.Total - ret.Available
ret.UsedPercent = 100 * float64(ret.Used) / float64(ret.Total)
return ret, nil
}

View File

@ -82,6 +82,8 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
if err != nil {
return nil, err
}
defer common.PdhCloseQuery.Call(uintptr(counter.Query))
usedPercent, err := counter.GetValue()
if err != nil {
return nil, err

View File

@ -18,7 +18,7 @@ import (
var (
invoke common.Invoker = common.Invoke{}
ErrorNoChildren = errors.New("process does not have children")
ErrorNoChildren = errors.New("process does not have children") // Deprecated: ErrorNoChildren is never returned by process.Children(), check its returned []*Process slice length instead
ErrorProcessNotRunning = errors.New("process does not exist")
ErrorNotPermitted = errors.New("operation not permitted")
)

View File

@ -4,15 +4,20 @@
package process
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"unsafe"
"github.com/tklauser/go-sysconf"
"golang.org/x/sys/unix"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/internal/common"
"github.com/shirou/gopsutil/v4/net"
)
@ -27,16 +32,6 @@ const (
KernProcPathname = 12 // path to executable
)
var clockTicks = 100 // default value
func init() {
clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
// ignore errors
if err == nil {
clockTicks = int(clkTck)
}
}
type _Ctype_struct___0 struct {
Pad uint64
}
@ -186,65 +181,22 @@ func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, e
return nil, common.ErrNotImplementedError
}
func convertCPUTimes(s string) (ret float64, err error) {
var t int
var _tmp string
if strings.Contains(s, ":") {
_t := strings.Split(s, ":")
switch len(_t) {
case 3:
hour, err := strconv.ParseInt(_t[0], 10, 32)
if err != nil {
return ret, err
}
t += int(hour) * 60 * 60 * clockTicks
mins, err := strconv.ParseInt(_t[1], 10, 32)
if err != nil {
return ret, err
}
t += int(mins) * 60 * clockTicks
_tmp = _t[2]
case 2:
mins, err := strconv.ParseInt(_t[0], 10, 32)
if err != nil {
return ret, err
}
t += int(mins) * 60 * clockTicks
_tmp = _t[1]
case 1, 0:
_tmp = s
default:
return ret, fmt.Errorf("wrong cpu time string")
}
} else {
_tmp = s
}
_t := strings.Split(_tmp, ".")
if err != nil {
return ret, err
}
h, err := strconv.ParseInt(_t[0], 10, 32)
t += int(h) * clockTicks
h, err = strconv.ParseInt(_t[1], 10, 32)
t += int(h)
return float64(t) / float64(clockTicks), nil
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
procs, err := ProcessesWithContext(ctx)
if err != nil {
return nil, err
return nil, nil
}
ret := make([]*Process, 0, len(pids))
for _, pid := range pids {
np, err := NewProcessWithContext(ctx, pid)
ret := make([]*Process, 0, len(procs))
for _, proc := range procs {
ppid, err := proc.PpidWithContext(ctx)
if err != nil {
return nil, err
continue
}
if ppid == p.Pid {
ret = append(ret, proc)
}
ret = append(ret, np)
}
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
return ret, nil
}
@ -323,3 +275,206 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption
return ret, nil
}
var (
procPidPath common.ProcPidPathFunc
procPidInfo common.ProcPidInfoFunc
machTimeBaseInfo common.MachTimeBaseInfoFunc
)
func registerFuncs() (*common.Library, error) {
lib, err := common.NewLibrary(common.System)
if err != nil {
return nil, err
}
procPidPath = common.GetFunc[common.ProcPidPathFunc](lib, common.ProcPidPathSym)
procPidInfo = common.GetFunc[common.ProcPidInfoFunc](lib, common.ProcPidInfoSym)
machTimeBaseInfo = common.GetFunc[common.MachTimeBaseInfoFunc](lib, common.MachTimeBaseInfoSym)
return lib, nil
}
func getTimeScaleToNanoSeconds() float64 {
var timeBaseInfo common.MachTimeBaseInfo
machTimeBaseInfo(uintptr(unsafe.Pointer(&timeBaseInfo)))
return float64(timeBaseInfo.Numer) / float64(timeBaseInfo.Denom)
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
lib, err := registerFuncs()
if err != nil {
return "", err
}
defer lib.Close()
buf := make([]byte, common.PROC_PIDPATHINFO_MAXSIZE)
ret := procPidPath(p.Pid, uintptr(unsafe.Pointer(&buf[0])), common.PROC_PIDPATHINFO_MAXSIZE)
if ret <= 0 {
return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret)
}
return common.GoString(&buf[0]), nil
}
// sys/proc_info.h
type vnodePathInfo struct {
_ [152]byte
vipPath [common.MAXPATHLEN]byte
_ [1176]byte
}
// CwdWithContext retrieves the Current Working Directory for the given process.
// It uses the proc_pidinfo from libproc and will only work for processes the
// EUID can access. Otherwise "operation not permitted" will be returned as the
// error.
// Note: This might also work for other *BSD OSs.
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
lib, err := registerFuncs()
if err != nil {
return "", err
}
defer lib.Close()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var vpi vnodePathInfo
const vpiSize = int32(unsafe.Sizeof(vpi))
ret := procPidInfo(p.Pid, common.PROC_PIDVNODEPATHINFO, 0, uintptr(unsafe.Pointer(&vpi)), vpiSize)
errno, _ := lib.Dlsym("errno")
err = *(**unix.Errno)(unsafe.Pointer(&errno))
if err == unix.EPERM {
return "", ErrorNotPermitted
}
if ret <= 0 {
return "", fmt.Errorf("unknown error: proc_pidinfo returned %d", ret)
}
if ret != vpiSize {
return "", fmt.Errorf("too few bytes; expected %d, got %d", vpiSize, ret)
}
return common.GoString(&vpi.vipPath[0]), nil
}
func procArgs(pid int32) ([]byte, int, error) {
procargs, _, err := common.CallSyscall([]int32{common.CTL_KERN, common.KERN_PROCARGS2, pid})
if err != nil {
return nil, 0, err
}
nargs := procargs[:4]
return procargs, int(binary.LittleEndian.Uint32(nargs)), nil
}
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
return p.cmdlineSliceWithContext(ctx, true)
}
func (p *Process) cmdlineSliceWithContext(ctx context.Context, fallback bool) ([]string, error) {
pargs, nargs, err := procArgs(p.Pid)
if err != nil {
return nil, err
}
// The first bytes hold the nargs int, skip it.
args := bytes.Split((pargs)[unsafe.Sizeof(int(0)):], []byte{0})
var argStr string
// The first element is the actual binary/command path.
// command := args[0]
var argSlice []string
// var envSlice []string
// All other, non-zero elements are arguments. The first "nargs" elements
// are the arguments. Everything else in the slice is then the environment
// of the process.
for _, arg := range args[1:] {
argStr = string(arg[:])
if len(argStr) > 0 {
if nargs > 0 {
argSlice = append(argSlice, argStr)
nargs--
continue
}
break
// envSlice = append(envSlice, argStr)
}
}
return argSlice, err
}
// cmdNameWithContext returns the command name (including spaces) without any arguments
func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) {
r, err := p.cmdlineSliceWithContext(ctx, false)
if err != nil {
return "", err
}
if len(r) == 0 {
return "", nil
}
return r[0], err
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
r, err := p.CmdlineSliceWithContext(ctx)
if err != nil {
return "", err
}
return strings.Join(r, " "), err
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
lib, err := registerFuncs()
if err != nil {
return 0, err
}
defer lib.Close()
var ti ProcTaskInfo
const tiSize = int32(unsafe.Sizeof(ti))
procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize)
return int32(ti.Threadnum), nil
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
lib, err := registerFuncs()
if err != nil {
return nil, err
}
defer lib.Close()
var ti ProcTaskInfo
const tiSize = int32(unsafe.Sizeof(ti))
procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize)
timescaleToNanoSeconds := getTimeScaleToNanoSeconds()
ret := &cpu.TimesStat{
CPU: "cpu",
User: float64(ti.Total_user) * timescaleToNanoSeconds / 1e9,
System: float64(ti.Total_system) * timescaleToNanoSeconds / 1e9,
}
return ret, nil
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
lib, err := registerFuncs()
if err != nil {
return nil, err
}
defer lib.Close()
var ti ProcTaskInfo
const tiSize = int32(unsafe.Sizeof(ti))
procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize)
ret := &MemoryInfoStat{
RSS: uint64(ti.Resident_size),
VMS: uint64(ti.Virtual_size),
Swap: uint64(ti.Pageins),
}
return ret, nil
}

View File

@ -212,6 +212,27 @@ type Posix_cred struct {
type Label struct{}
type ProcTaskInfo struct {
Virtual_size uint64
Resident_size uint64
Total_user uint64
Total_system uint64
Threads_user uint64
Threads_system uint64
Policy int32
Faults int32
Pageins int32
Cow_faults int32
Messages_sent int32
Messages_received int32
Syscalls_mach int32
Syscalls_unix int32
Csw int32
Threadnum int32
Numrunning int32
Priority int32
}
type AuditinfoAddr struct {
Auid uint32
Mask AuMask

View File

@ -190,6 +190,27 @@ type Posix_cred struct{}
type Label struct{}
type ProcTaskInfo struct {
Virtual_size uint64
Resident_size uint64
Total_user uint64
Total_system uint64
Threads_user uint64
Threads_system uint64
Policy int32
Faults int32
Pageins int32
Cow_faults int32
Messages_sent int32
Messages_received int32
Syscalls_mach int32
Syscalls_unix int32
Csw int32
Threadnum int32
Numrunning int32
Priority int32
}
type AuditinfoAddr struct {
Auid uint32
Mask AuMask

View File

@ -1,222 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && cgo
package process
// #include <stdlib.h>
// #include <libproc.h>
// #include <string.h>
// #include <sys/errno.h>
// #include <sys/proc_info.h>
// #include <sys/sysctl.h>
// #include <mach/mach_time.h>
import "C"
import (
"bytes"
"context"
"fmt"
"strings"
"syscall"
"unsafe"
"github.com/shirou/gopsutil/v4/cpu"
)
var (
argMax int
timescaleToNanoSeconds float64
)
func init() {
argMax = getArgMax()
timescaleToNanoSeconds = getTimeScaleToNanoSeconds()
}
func getArgMax() int {
var (
mib = [...]C.int{C.CTL_KERN, C.KERN_ARGMAX}
argmax C.int
size C.size_t = C.ulong(unsafe.Sizeof(argmax))
)
retval := C.sysctl(&mib[0], 2, unsafe.Pointer(&argmax), &size, C.NULL, 0)
if retval == 0 {
return int(argmax)
}
return 0
}
func getTimeScaleToNanoSeconds() float64 {
var timeBaseInfo C.struct_mach_timebase_info
C.mach_timebase_info(&timeBaseInfo)
return float64(timeBaseInfo.numer) / float64(timeBaseInfo.denom)
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
var c C.char // need a var for unsafe.Sizeof need a var
const bufsize = C.PROC_PIDPATHINFO_MAXSIZE * unsafe.Sizeof(c)
buffer := (*C.char)(C.malloc(C.size_t(bufsize)))
defer C.free(unsafe.Pointer(buffer))
ret, err := C.proc_pidpath(C.int(p.Pid), unsafe.Pointer(buffer), C.uint32_t(bufsize))
if err != nil {
return "", err
}
if ret <= 0 {
return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret)
}
return C.GoString(buffer), nil
}
// CwdWithContext retrieves the Current Working Directory for the given process.
// It uses the proc_pidinfo from libproc and will only work for processes the
// EUID can access. Otherwise "operation not permitted" will be returned as the
// error.
// Note: This might also work for other *BSD OSs.
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
const vpiSize = C.sizeof_struct_proc_vnodepathinfo
vpi := (*C.struct_proc_vnodepathinfo)(C.malloc(vpiSize))
defer C.free(unsafe.Pointer(vpi))
ret, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDVNODEPATHINFO, 0, unsafe.Pointer(vpi), vpiSize)
if err != nil {
// fmt.Printf("ret: %d %T\n", ret, err)
if err == syscall.EPERM {
return "", ErrorNotPermitted
}
return "", err
}
if ret <= 0 {
return "", fmt.Errorf("unknown error: proc_pidinfo returned %d", ret)
}
if ret != C.sizeof_struct_proc_vnodepathinfo {
return "", fmt.Errorf("too few bytes; expected %d, got %d", vpiSize, ret)
}
return C.GoString(&vpi.pvi_cdir.vip_path[0]), err
}
func procArgs(pid int32) ([]byte, int, error) {
var (
mib = [...]C.int{C.CTL_KERN, C.KERN_PROCARGS2, C.int(pid)}
size C.size_t = C.ulong(argMax)
nargs C.int
result []byte
)
procargs := (*C.char)(C.malloc(C.ulong(argMax)))
defer C.free(unsafe.Pointer(procargs))
retval, err := C.sysctl(&mib[0], 3, unsafe.Pointer(procargs), &size, C.NULL, 0)
if retval == 0 {
C.memcpy(unsafe.Pointer(&nargs), unsafe.Pointer(procargs), C.sizeof_int)
result = C.GoBytes(unsafe.Pointer(procargs), C.int(size))
// fmt.Printf("size: %d %d\n%s\n", size, nargs, hex.Dump(result))
return result, int(nargs), nil
}
return nil, 0, err
}
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
return p.cmdlineSliceWithContext(ctx, true)
}
func (p *Process) cmdlineSliceWithContext(ctx context.Context, fallback bool) ([]string, error) {
pargs, nargs, err := procArgs(p.Pid)
if err != nil {
return nil, err
}
// The first bytes hold the nargs int, skip it.
args := bytes.Split((pargs)[C.sizeof_int:], []byte{0})
var argStr string
// The first element is the actual binary/command path.
// command := args[0]
var argSlice []string
// var envSlice []string
// All other, non-zero elements are arguments. The first "nargs" elements
// are the arguments. Everything else in the slice is then the environment
// of the process.
for _, arg := range args[1:] {
argStr = string(arg[:])
if len(argStr) > 0 {
if nargs > 0 {
argSlice = append(argSlice, argStr)
nargs--
continue
}
break
// envSlice = append(envSlice, argStr)
}
}
return argSlice, err
}
// cmdNameWithContext returns the command name (including spaces) without any arguments
func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) {
r, err := p.cmdlineSliceWithContext(ctx, false)
if err != nil {
return "", err
}
if len(r) == 0 {
return "", nil
}
return r[0], err
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
r, err := p.CmdlineSliceWithContext(ctx)
if err != nil {
return "", err
}
return strings.Join(r, " "), err
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
const tiSize = C.sizeof_struct_proc_taskinfo
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
defer C.free(unsafe.Pointer(ti))
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
if err != nil {
return 0, err
}
return int32(ti.pti_threadnum), nil
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
const tiSize = C.sizeof_struct_proc_taskinfo
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
defer C.free(unsafe.Pointer(ti))
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
if err != nil {
return nil, err
}
ret := &cpu.TimesStat{
CPU: "cpu",
User: float64(ti.pti_total_user) * timescaleToNanoSeconds / 1e9,
System: float64(ti.pti_total_system) * timescaleToNanoSeconds / 1e9,
}
return ret, nil
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
const tiSize = C.sizeof_struct_proc_taskinfo
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
defer C.free(unsafe.Pointer(ti))
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
if err != nil {
return nil, err
}
ret := &MemoryInfoStat{
RSS: uint64(ti.pti_resident_size),
VMS: uint64(ti.pti_virtual_size),
Swap: uint64(ti.pti_pageins),
}
return ret, nil
}

View File

@ -1,134 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && !cgo
package process
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/internal/common"
)
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
out, err := invoke.CommandWithContext(ctx, "lsof", "-p", strconv.Itoa(int(p.Pid)), "-Fpfn")
if err != nil {
return "", fmt.Errorf("bad call to lsof: %w", err)
}
txtFound := 0
lines := strings.Split(string(out), "\n")
fallback := ""
for i := 1; i < len(lines); i++ {
if lines[i] == "ftxt" {
txtFound++
if txtFound == 1 {
fallback = lines[i-1][1:]
}
if txtFound == 2 {
return lines[i-1][1:], nil
}
}
}
if fallback != "" {
return fallback, nil
}
return "", fmt.Errorf("missing txt data returned by lsof")
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
if err != nil {
return "", err
}
return strings.Join(r[0], " "), err
}
func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false, true)
if err != nil {
return "", err
}
if len(r) > 0 && len(r[0]) > 0 {
return r[0][0], err
}
return "", err
}
// CmdlineSliceWithContext returns the command line arguments of the process as a slice with each
// element being an argument. Because of current deficiencies in the way that the command
// line arguments are found, single arguments that have spaces in the will actually be
// reported as two separate items. In order to do something better CGO would be needed
// to use the native darwin functions.
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
if err != nil {
return nil, err
}
return r[0], err
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true, false)
if err != nil {
return 0, err
}
return int32(len(r)), nil
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false, false)
if err != nil {
return nil, err
}
utime, err := convertCPUTimes(r[0][0])
if err != nil {
return nil, err
}
stime, err := convertCPUTimes(r[0][1])
if err != nil {
return nil, err
}
ret := &cpu.TimesStat{
CPU: "cpu",
User: utime,
System: stime,
}
return ret, nil
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false, false)
if err != nil {
return nil, err
}
rss, err := strconv.ParseInt(r[0][0], 10, 64)
if err != nil {
return nil, err
}
vms, err := strconv.ParseInt(r[0][1], 10, 64)
if err != nil {
return nil, err
}
pagein, err := strconv.ParseInt(r[0][2], 10, 64)
if err != nil {
return nil, err
}
ret := &MemoryInfoStat{
RSS: uint64(rss) * 1024,
VMS: uint64(vms) * 1024,
Swap: uint64(pagein),
}
return ret, nil
}

View File

@ -8,6 +8,7 @@ import (
"context"
"errors"
"path/filepath"
"sort"
"strconv"
"strings"
@ -269,18 +270,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
procs, err := ProcessesWithContext(ctx)
if err != nil {
return nil, err
return nil, nil
}
ret := make([]*Process, 0, len(pids))
for _, pid := range pids {
np, err := NewProcessWithContext(ctx, pid)
ret := make([]*Process, 0, len(procs))
for _, proc := range procs {
ppid, err := proc.PpidWithContext(ctx)
if err != nil {
return nil, err
continue
}
if ppid == p.Pid {
ret = append(ret, proc)
}
ret = append(ret, np)
}
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
return ret, nil
}

View File

@ -12,6 +12,7 @@ import (
"math"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
@ -338,21 +339,34 @@ func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, e
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
statFiles, err := filepath.Glob(common.HostProcWithContext(ctx, "[0-9]*/stat"))
if err != nil {
return nil, err
}
if len(pids) == 0 {
return nil, ErrorNoChildren
}
ret := make([]*Process, 0, len(pids))
for _, pid := range pids {
np, err := NewProcessWithContext(ctx, pid)
ret := make([]*Process, 0, len(statFiles))
for _, statFile := range statFiles {
statContents, err := os.ReadFile(statFile)
if err != nil {
return nil, err
continue
}
fields := splitProcStat(statContents)
pid, err := strconv.ParseInt(fields[1], 10, 32)
if err != nil {
continue
}
ppid, err := strconv.ParseInt(fields[4], 10, 32)
if err != nil {
continue
}
if int32(ppid) == p.Pid {
np, err := NewProcessWithContext(ctx, int32(pid))
if err != nil {
continue
}
ret = append(ret, np)
}
ret = append(ret, np)
}
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
return ret, nil
}
@ -1082,8 +1096,7 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (ui
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
ctime := (t / uint64(clockTicks)) + uint64(bootTime)
createTime := int64(ctime * 1000)
createTime := int64((t * 1000 / uint64(clockTicks)) + uint64(bootTime*1000))
rtpriority, err := strconv.ParseInt(fields[18], 10, 32)
if err != nil {

View File

@ -11,6 +11,7 @@ import (
"fmt"
"io"
"path/filepath"
"sort"
"strconv"
"strings"
"unsafe"
@ -286,18 +287,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
procs, err := ProcessesWithContext(ctx)
if err != nil {
return nil, err
return nil, nil
}
ret := make([]*Process, 0, len(pids))
for _, pid := range pids {
np, err := NewProcessWithContext(ctx, pid)
ret := make([]*Process, 0, len(procs))
for _, proc := range procs {
ppid, err := proc.PpidWithContext(ctx)
if err != nil {
return nil, err
continue
}
if ppid == p.Pid {
ret = append(ret, proc)
}
ret = append(ret, np)
}
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
return ret, nil
}

View File

@ -43,6 +43,7 @@ var (
procGetPriorityClass = common.Modkernel32.NewProc("GetPriorityClass")
procGetProcessIoCounters = common.Modkernel32.NewProc("GetProcessIoCounters")
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
procGetProcessHandleCount = common.Modkernel32.NewProc("GetProcessHandleCount")
processorArchitecture uint
)
@ -548,8 +549,21 @@ func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitche
return nil, common.ErrNotImplementedError
}
// NumFDsWithContext returns the number of handles for a process on Windows,
// not the number of file descriptors (FDs).
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError
handle, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid))
if err != nil {
return 0, err
}
defer windows.CloseHandle(handle)
var handleCount uint32
ret, _, err := procGetProcessHandleCount.Call(uintptr(handle), uintptr(unsafe.Pointer(&handleCount)))
if ret == 0 {
return 0, err
}
return int32(handleCount), nil
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {

View File

@ -53,6 +53,7 @@ package process
#include <sys/sysctl.h>
#include <sys/ucred.h>
#include <sys/proc.h>
#include <sys/proc_info.h>
#include <sys/time.h>
#include <sys/_types/_timeval.h>
#include <sys/appleapiopts.h>
@ -154,6 +155,8 @@ type Posix_cred C.struct_posix_cred
type Label C.struct_label
type ProcTaskInfo C.struct_proc_taskinfo
type (
AuditinfoAddr C.struct_auditinfo_addr
AuMask C.struct_au_mask

View File

@ -1,110 +0,0 @@
// SPDX-FileCopyrightText: Copyright (c) 2016-2018, "freedom" Koan-Sin Tan
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/freedomtan/sensors/blob/master/sensors/sensors.m
#import <Foundation/Foundation.h>
#import <IOKit/hidsystem/IOHIDEventSystemClient.h>
#include <unistd.h>
typedef struct __IOHIDEvent *IOHIDEventRef;
typedef struct __IOHIDServiceClient *IOHIDServiceClientRef;
typedef double IOHIDFloat;
IOHIDEventSystemClientRef IOHIDEventSystemClientCreate(CFAllocatorRef allocator);
int IOHIDEventSystemClientSetMatching(IOHIDEventSystemClientRef client, CFDictionaryRef match);
IOHIDEventRef IOHIDServiceClientCopyEvent(IOHIDServiceClientRef, int64_t, int32_t, int64_t);
CFStringRef IOHIDServiceClientCopyProperty(IOHIDServiceClientRef service, CFStringRef property);
IOHIDFloat IOHIDEventGetFloatValue(IOHIDEventRef event, int32_t field);
NSDictionary *matching(int page, int usage) {
NSDictionary *dict = @{
@"PrimaryUsagePage" : [NSNumber numberWithInt:page],
@"PrimaryUsage" : [NSNumber numberWithInt:usage],
};
return dict;
}
NSArray *getProductNames(NSDictionary *sensors) {
IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault);
IOHIDEventSystemClientSetMatching(system, (__bridge CFDictionaryRef)sensors);
NSArray *matchingsrvs = (__bridge NSArray *)IOHIDEventSystemClientCopyServices(system);
long count = [matchingsrvs count];
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < count; i++) {
IOHIDServiceClientRef sc = (IOHIDServiceClientRef)matchingsrvs[i];
NSString *name = (NSString *)IOHIDServiceClientCopyProperty(sc, (__bridge CFStringRef)@"Product");
if (name) {
[array addObject:name];
} else {
[array addObject:@"noname"];
}
}
return array;
}
#define IOHIDEventFieldBase(type) (type << 16)
#define kIOHIDEventTypeTemperature 15
#define kIOHIDEventTypePower 25
NSArray *getThermalValues(NSDictionary *sensors) {
IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault);
IOHIDEventSystemClientSetMatching(system, (__bridge CFDictionaryRef)sensors);
NSArray *matchingsrvs = (__bridge NSArray *)IOHIDEventSystemClientCopyServices(system);
long count = [matchingsrvs count];
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < count; i++) {
IOHIDServiceClientRef sc = (IOHIDServiceClientRef)matchingsrvs[i];
IOHIDEventRef event = IOHIDServiceClientCopyEvent(sc, kIOHIDEventTypeTemperature, 0, 0);
NSNumber *value;
double temp = 0.0;
if (event != 0) {
temp = IOHIDEventGetFloatValue(event, IOHIDEventFieldBase(kIOHIDEventTypeTemperature));
}
value = [NSNumber numberWithDouble:temp];
[array addObject:value];
}
return array;
}
NSString *dumpNamesValues(NSArray *kvsN, NSArray *kvsV) {
NSMutableString *valueString = [[NSMutableString alloc] init];
int count = [kvsN count];
for (int i = 0; i < count; i++) {
NSString *output = [NSString stringWithFormat:@"%s:%lf\n", [kvsN[i] UTF8String], [kvsV[i] doubleValue]];
[valueString appendString:output];
}
return valueString;
}
char *getThermals() {
NSDictionary *thermalSensors = matching(0xff00, 5);
NSArray *thermalNames = getProductNames(thermalSensors);
NSArray *thermalValues = getThermalValues(thermalSensors);
NSString *result = dumpNamesValues(thermalNames, thermalValues);
char *finalStr = strdup([result UTF8String]);
CFRelease(thermalSensors);
CFRelease(thermalNames);
CFRelease(thermalValues);
CFRelease(result);
return finalStr;
}

181
sensors/sensors_darwin.go Normal file
View File

@ -0,0 +1,181 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && !arm64
package sensors
import (
"context"
"fmt"
"unsafe"
"github.com/shirou/gopsutil/v4/internal/common"
)
func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
ioKit, err := common.NewLibrary(common.IOKit)
if err != nil {
return nil, err
}
defer ioKit.Close()
smc, err := common.NewSMC(ioKit)
if err != nil {
return nil, err
}
defer smc.Close()
var temperatures []TemperatureStat
for _, key := range temperatureKeys {
temperatures = append(temperatures, TemperatureStat{
SensorKey: key,
Temperature: getTemperature(smc, key),
})
}
return temperatures, nil
}
var temperatureKeys = []string{
"TA0P", // AMBIENT_AIR_0
"TA1P", // AMBIENT_AIR_1
"TC0D", // CPU_0_DIODE
"TC0H", // CPU_0_HEATSINK
"TC0P", // CPU_0_PROXIMITY
"TB0T", // ENCLOSURE_BASE_0
"TB1T", // ENCLOSURE_BASE_1
"TB2T", // ENCLOSURE_BASE_2
"TB3T", // ENCLOSURE_BASE_3
"TG0D", // GPU_0_DIODE
"TG0H", // GPU_0_HEATSINK
"TG0P", // GPU_0_PROXIMITY
"TH0P", // HARD_DRIVE_BAY
"TM0S", // MEMORY_SLOT_0
"TM0P", // MEMORY_SLOTS_PROXIMITY
"TN0H", // NORTHBRIDGE
"TN0D", // NORTHBRIDGE_DIODE
"TN0P", // NORTHBRIDGE_PROXIMITY
"TI0P", // THUNDERBOLT_0
"TI1P", // THUNDERBOLT_1
"TW0P", // WIRELESS_MODULE
}
type smcReturn struct {
data [32]uint8
dataType uint32
dataSize uint32
kSMC uint8
}
type smcPLimitData struct {
version uint16
length uint16
cpuPLimit uint32
gpuPLimit uint32
memPLimit uint32
}
type smcKeyInfoData struct {
dataSize uint32
dataType uint32
dataAttributes uint8
}
type smcVersion struct {
major byte
minor byte
build byte
reserved byte
release uint16
}
type smcParamStruct struct {
key uint32
vers smcVersion
plimitData smcPLimitData
keyInfo smcKeyInfoData
result uint8
status uint8
data8 uint8
data32 uint32
bytes [32]byte
}
const (
smcKeySize = 4
dataTypeSp78 = "sp78"
)
func getTemperature(smc *common.SMC, key string) float64 {
result, err := readSMC(smc, key)
if err != nil {
return 0.0
}
if result.dataSize == 2 && result.dataType == toUint32(dataTypeSp78) {
return 0.0
}
return float64(result.data[0])
}
func readSMC(smc *common.SMC, key string) (*smcReturn, error) {
input := new(smcParamStruct)
resultSmc := new(smcReturn)
input.key = toUint32(key)
input.data8 = common.KSMCGetKeyInfo
result, err := callSMC(smc, input)
resultSmc.kSMC = result.result
if err != nil || result.result != common.KSMCSuccess {
return resultSmc, fmt.Errorf("ERROR: IOConnectCallStructMethod failed")
}
resultSmc.dataSize = uint32(result.keyInfo.dataSize)
resultSmc.dataType = uint32(result.keyInfo.dataSize)
input.keyInfo.dataSize = result.keyInfo.dataSize
input.data8 = common.KSMCReadKey
result, err = callSMC(smc, input)
resultSmc.kSMC = result.result
if err != nil || result.result != common.KSMCSuccess {
return resultSmc, err
}
resultSmc.data = result.bytes
return resultSmc, nil
}
func callSMC(smc *common.SMC, input *smcParamStruct) (*smcParamStruct, error) {
output := new(smcParamStruct)
inputCnt := unsafe.Sizeof(*input)
outputCnt := unsafe.Sizeof(*output)
result := smc.CallStruct(common.KSMCHandleYPCEvent,
uintptr(unsafe.Pointer(input)), inputCnt, uintptr(unsafe.Pointer(output)), &outputCnt)
if result != 0 {
return output, fmt.Errorf("ERROR: IOConnectCallStructMethod failed")
}
return output, nil
}
func toUint32(key string) uint32 {
if len(key) != smcKeySize {
return 0
}
var ans uint32 = 0
var shift uint32 = 24
for i := 0; i < smcKeySize; i++ {
ans += uint32(key[i]) << shift
shift -= 8
}
return ans
}

View File

@ -0,0 +1,194 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && arm64
package sensors
import (
"context"
"unsafe"
"github.com/shirou/gopsutil/v4/internal/common"
)
func ReadTemperaturesArm() []TemperatureStat {
temperatures, _ := TemperaturesWithContext(context.Background())
return temperatures
}
func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
ioKit, err := common.NewLibrary(common.IOKit)
if err != nil {
return nil, err
}
defer ioKit.Close()
coreFoundation, err := common.NewLibrary(common.CoreFoundation)
if err != nil {
return nil, err
}
defer coreFoundation.Close()
ta := &temperatureArm{
ioKit: ioKit,
cf: coreFoundation,
cfRelease: common.GetFunc[common.CFReleaseFunc](coreFoundation, common.CFReleaseSym),
cfStringCreateWithCString: common.GetFunc[common.CFStringCreateWithCStringFunc](coreFoundation, common.CFStringCreateWithCStringSym),
cfArrayGetCount: common.GetFunc[common.CFArrayGetCountFunc](coreFoundation, common.CFArrayGetCountSym),
cfArrayGetValueAtIndex: common.GetFunc[common.CFArrayGetValueAtIndexFunc](coreFoundation, common.CFArrayGetValueAtIndexSym),
ioHIDEventSystemClientCreate: common.GetFunc[common.IOHIDEventSystemClientCreateFunc](ioKit, common.IOHIDEventSystemClientCreateSym),
ioHIDEventSystemClientSetMatching: common.GetFunc[common.IOHIDEventSystemClientSetMatchingFunc](ioKit, common.IOHIDEventSystemClientSetMatchingSym),
ioHIDEventSystemClientCopyServices: common.GetFunc[common.IOHIDEventSystemClientCopyServicesFunc](ioKit, common.IOHIDEventSystemClientCopyServicesSym),
}
ta.matching(0xff00, 5)
thermalNames := ta.getProductNames()
thermalValues := ta.getThermalValues()
result := dumpNameValues(thermalNames, thermalValues)
ta.cfRelease(uintptr(ta.sensors))
return result, nil
}
func dumpNameValues(kvsN []string, kvsV []float64) []TemperatureStat {
count := len(kvsN)
temperatureMap := make(map[string]TemperatureStat)
for i := 0; i < count; i++ {
temperatureMap[kvsN[i]] = TemperatureStat{
SensorKey: kvsN[i],
Temperature: kvsV[i],
}
}
temperatures := make([]TemperatureStat, 0, len(temperatureMap))
for _, stat := range temperatureMap {
temperatures = append(temperatures, stat)
}
return temperatures
}
type temperatureArm struct {
ioKit *common.Library
cf *common.Library
cfRelease common.CFReleaseFunc
cfStringCreateWithCString common.CFStringCreateWithCStringFunc
cfArrayGetCount common.CFArrayGetCountFunc
cfArrayGetValueAtIndex common.CFArrayGetValueAtIndexFunc
ioHIDEventSystemClientCreate common.IOHIDEventSystemClientCreateFunc
ioHIDEventSystemClientSetMatching common.IOHIDEventSystemClientSetMatchingFunc
ioHIDEventSystemClientCopyServices common.IOHIDEventSystemClientCopyServicesFunc
sensors unsafe.Pointer
}
func (ta *temperatureArm) getProductNames() []string {
ioHIDServiceClientCopyProperty := common.GetFunc[common.IOHIDServiceClientCopyPropertyFunc](ta.ioKit, common.IOHIDServiceClientCopyPropertySym)
cfStringGetLength := common.GetFunc[common.CFStringGetLengthFunc](ta.cf, common.CFStringGetLengthSym)
cfStringGetCString := common.GetFunc[common.CFStringGetCStringFunc](ta.cf, common.CFStringGetCStringSym)
var names []string
system := ta.ioHIDEventSystemClientCreate(common.KCFAllocatorDefault)
ta.ioHIDEventSystemClientSetMatching(uintptr(system), uintptr(ta.sensors))
matchingsrvs := ta.ioHIDEventSystemClientCopyServices(uintptr(system))
if matchingsrvs == nil {
return nil
}
count := ta.cfArrayGetCount(uintptr(matchingsrvs))
var i int32
str := ta.cfStr("Product")
for i = 0; i < count; i++ {
sc := ta.cfArrayGetValueAtIndex(uintptr(matchingsrvs), i)
name := ioHIDServiceClientCopyProperty(uintptr(sc), uintptr(str))
if name != nil {
length := cfStringGetLength(uintptr(name)) + 1 // null terminator
buf := make([]byte, length-1)
cfStringGetCString(uintptr(name), &buf[0], length, common.KCFStringEncodingUTF8)
names = append(names, string(buf))
ta.cfRelease(uintptr(name))
} else {
names = append(names, "noname")
}
}
ta.cfRelease(uintptr(matchingsrvs))
ta.cfRelease(uintptr(str))
return names
}
func (ta *temperatureArm) getThermalValues() []float64 {
ioHIDServiceClientCopyEvent := common.GetFunc[common.IOHIDServiceClientCopyEventFunc](ta.ioKit, common.IOHIDServiceClientCopyEventSym)
ioHIDEventGetFloatValue := common.GetFunc[common.IOHIDEventGetFloatValueFunc](ta.ioKit, common.IOHIDEventGetFloatValueSym)
system := ta.ioHIDEventSystemClientCreate(common.KCFAllocatorDefault)
ta.ioHIDEventSystemClientSetMatching(uintptr(system), uintptr(ta.sensors))
matchingsrvs := ta.ioHIDEventSystemClientCopyServices(uintptr(system))
if matchingsrvs == nil {
return nil
}
count := ta.cfArrayGetCount(uintptr(matchingsrvs))
var values []float64
var i int32
for i = 0; i < count; i++ {
sc := ta.cfArrayGetValueAtIndex(uintptr(matchingsrvs), i)
event := ioHIDServiceClientCopyEvent(uintptr(sc), common.KIOHIDEventTypeTemperature, 0, 0)
temp := 0.0
if event != nil {
temp = ioHIDEventGetFloatValue(uintptr(event), ioHIDEventFieldBase(common.KIOHIDEventTypeTemperature))
ta.cfRelease(uintptr(event))
}
values = append(values, temp)
}
ta.cfRelease(uintptr(matchingsrvs))
return values
}
func (ta *temperatureArm) matching(page, usage int) {
cfNumberCreate := common.GetFunc[common.CFNumberCreateFunc](ta.cf, common.CFNumberCreateSym)
cfDictionaryCreate := common.GetFunc[common.CFDictionaryCreateFunc](ta.cf, common.CFDictionaryCreateSym)
pageNum := cfNumberCreate(common.KCFAllocatorDefault, common.KCFNumberIntType, uintptr(unsafe.Pointer(&page)))
usageNum := cfNumberCreate(common.KCFAllocatorDefault, common.KCFNumberIntType, uintptr(unsafe.Pointer(&usage)))
k1 := ta.cfStr("PrimaryUsagePage")
k2 := ta.cfStr("PrimaryUsage")
keys := []unsafe.Pointer{k1, k2}
values := []unsafe.Pointer{pageNum, usageNum}
kCFTypeDictionaryKeyCallBacks, _ := ta.cf.Dlsym("kCFTypeDictionaryKeyCallBacks")
kCFTypeDictionaryValueCallBacks, _ := ta.cf.Dlsym("kCFTypeDictionaryValueCallBacks")
ta.sensors = cfDictionaryCreate(common.KCFAllocatorDefault, &keys[0], &values[0], 2,
kCFTypeDictionaryKeyCallBacks,
kCFTypeDictionaryValueCallBacks)
ta.cfRelease(uintptr(pageNum))
ta.cfRelease(uintptr(usageNum))
ta.cfRelease(uintptr(k1))
ta.cfRelease(uintptr(k2))
}
func (ta *temperatureArm) cfStr(str string) unsafe.Pointer {
return ta.cfStringCreateWithCString(common.KCFAllocatorDefault, str, common.KCFStringEncodingUTF8)
}
func ioHIDEventFieldBase(i int32) int32 {
return i << 16
}

View File

@ -1,95 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && cgo
package sensors
// #cgo CFLAGS: -x objective-c
// #cgo LDFLAGS: -framework Foundation -framework IOKit
// #include "smc_darwin.h"
// #include "darwin_arm_sensors.h"
import "C"
import (
"bufio"
"context"
"math"
"runtime"
"strconv"
"strings"
"unsafe"
)
func ReadTemperaturesArm() []TemperatureStat {
cStr := C.getThermals()
defer C.free(unsafe.Pointer(cStr))
var stats []TemperatureStat
goStr := C.GoString(cStr)
scanner := bufio.NewScanner(strings.NewReader(goStr))
for scanner.Scan() {
split := strings.Split(scanner.Text(), ":")
if len(split) != 2 {
continue
}
val, err := strconv.ParseFloat(split[1], 32)
if err != nil {
continue
}
sensorKey := strings.Split(split[0], " ")[0]
val = math.Abs(val)
stats = append(stats, TemperatureStat{
SensorKey: sensorKey,
Temperature: float64(val),
})
}
return stats
}
func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
if runtime.GOARCH == "arm64" {
return ReadTemperaturesArm(), nil
}
temperatureKeys := []string{
C.AMBIENT_AIR_0,
C.AMBIENT_AIR_1,
C.CPU_0_DIODE,
C.CPU_0_HEATSINK,
C.CPU_0_PROXIMITY,
C.ENCLOSURE_BASE_0,
C.ENCLOSURE_BASE_1,
C.ENCLOSURE_BASE_2,
C.ENCLOSURE_BASE_3,
C.GPU_0_DIODE,
C.GPU_0_HEATSINK,
C.GPU_0_PROXIMITY,
C.HARD_DRIVE_BAY,
C.MEMORY_SLOT_0,
C.MEMORY_SLOTS_PROXIMITY,
C.NORTHBRIDGE,
C.NORTHBRIDGE_DIODE,
C.NORTHBRIDGE_PROXIMITY,
C.THUNDERBOLT_0,
C.THUNDERBOLT_1,
C.WIRELESS_MODULE,
}
var temperatures []TemperatureStat
C.gopsutil_v4_open_smc()
defer C.gopsutil_v4_close_smc()
for _, key := range temperatureKeys {
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
temperatures = append(temperatures, TemperatureStat{
SensorKey: key,
Temperature: float64(C.gopsutil_v4_get_temperature(ckey)),
})
}
return temperatures, nil
}

View File

@ -1,14 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && !cgo
package sensors
import (
"context"
"github.com/shirou/gopsutil/v4/internal/common"
)
func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
return []TemperatureStat{}, common.ErrNotImplementedError
}

View File

@ -24,7 +24,7 @@ func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
files, err := getTemperatureFiles(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get tempreteure files, %w", err)
return nil, fmt.Errorf("failed to get temperature files, %w", err)
}
if len(files) == 0 { // handle distributions without hwmon, like raspbian #391, parse legacy thermal_zone files

View File

@ -1,170 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <stdio.h>
#include <string.h>
#include "smc_darwin.h"
#define IOSERVICE_SMC "AppleSMC"
#define IOSERVICE_MODEL "IOPlatformExpertDevice"
#define DATA_TYPE_SP78 "sp78"
typedef enum {
kSMCUserClientOpen = 0,
kSMCUserClientClose = 1,
kSMCHandleYPCEvent = 2,
kSMCReadKey = 5,
kSMCWriteKey = 6,
kSMCGetKeyCount = 7,
kSMCGetKeyFromIndex = 8,
kSMCGetKeyInfo = 9,
} selector_t;
typedef struct {
unsigned char major;
unsigned char minor;
unsigned char build;
unsigned char reserved;
unsigned short release;
} SMCVersion;
typedef struct {
uint16_t version;
uint16_t length;
uint32_t cpuPLimit;
uint32_t gpuPLimit;
uint32_t memPLimit;
} SMCPLimitData;
typedef struct {
IOByteCount data_size;
uint32_t data_type;
uint8_t data_attributes;
} SMCKeyInfoData;
typedef struct {
uint32_t key;
SMCVersion vers;
SMCPLimitData p_limit_data;
SMCKeyInfoData key_info;
uint8_t result;
uint8_t status;
uint8_t data8;
uint32_t data32;
uint8_t bytes[32];
} SMCParamStruct;
typedef enum {
kSMCSuccess = 0,
kSMCError = 1,
kSMCKeyNotFound = 0x84,
} kSMC_t;
typedef struct {
uint8_t data[32];
uint32_t data_type;
uint32_t data_size;
kSMC_t kSMC;
} smc_return_t;
static const int SMC_KEY_SIZE = 4; // number of characters in an SMC key.
static io_connect_t conn; // our connection to the SMC.
kern_return_t gopsutil_v4_open_smc(void) {
kern_return_t result;
io_service_t service;
service = IOServiceGetMatchingService(0, IOServiceMatching(IOSERVICE_SMC));
if (service == 0) {
// Note: IOServiceMatching documents 0 on failure
printf("ERROR: %s NOT FOUND\n", IOSERVICE_SMC);
return kIOReturnError;
}
result = IOServiceOpen(service, mach_task_self(), 0, &conn);
IOObjectRelease(service);
return result;
}
kern_return_t gopsutil_v4_close_smc(void) { return IOServiceClose(conn); }
static uint32_t to_uint32(char *key) {
uint32_t ans = 0;
uint32_t shift = 24;
if (strlen(key) != SMC_KEY_SIZE) {
return 0;
}
for (int i = 0; i < SMC_KEY_SIZE; i++) {
ans += key[i] << shift;
shift -= 8;
}
return ans;
}
static kern_return_t call_smc(SMCParamStruct *input, SMCParamStruct *output) {
kern_return_t result;
size_t input_cnt = sizeof(SMCParamStruct);
size_t output_cnt = sizeof(SMCParamStruct);
result = IOConnectCallStructMethod(conn, kSMCHandleYPCEvent, input, input_cnt,
output, &output_cnt);
if (result != kIOReturnSuccess) {
result = err_get_code(result);
}
return result;
}
static kern_return_t read_smc(char *key, smc_return_t *result_smc) {
kern_return_t result;
SMCParamStruct input;
SMCParamStruct output;
memset(&input, 0, sizeof(SMCParamStruct));
memset(&output, 0, sizeof(SMCParamStruct));
memset(result_smc, 0, sizeof(smc_return_t));
input.key = to_uint32(key);
input.data8 = kSMCGetKeyInfo;
result = call_smc(&input, &output);
result_smc->kSMC = output.result;
if (result != kIOReturnSuccess || output.result != kSMCSuccess) {
return result;
}
result_smc->data_size = output.key_info.data_size;
result_smc->data_type = output.key_info.data_type;
input.key_info.data_size = output.key_info.data_size;
input.data8 = kSMCReadKey;
result = call_smc(&input, &output);
result_smc->kSMC = output.result;
if (result != kIOReturnSuccess || output.result != kSMCSuccess) {
return result;
}
memcpy(result_smc->data, output.bytes, sizeof(output.bytes));
return result;
}
double gopsutil_v4_get_temperature(char *key) {
kern_return_t result;
smc_return_t result_smc;
result = read_smc(key, &result_smc);
if (!(result == kIOReturnSuccess) && result_smc.data_size == 2 &&
result_smc.data_type == to_uint32(DATA_TYPE_SP78)) {
return 0.0;
}
return (double)result_smc.data[0];
}

View File

@ -1,33 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
#ifndef __SMC_H__
#define __SMC_H__ 1
#include <IOKit/IOKitLib.h>
#define AMBIENT_AIR_0 "TA0P"
#define AMBIENT_AIR_1 "TA1P"
#define CPU_0_DIODE "TC0D"
#define CPU_0_HEATSINK "TC0H"
#define CPU_0_PROXIMITY "TC0P"
#define ENCLOSURE_BASE_0 "TB0T"
#define ENCLOSURE_BASE_1 "TB1T"
#define ENCLOSURE_BASE_2 "TB2T"
#define ENCLOSURE_BASE_3 "TB3T"
#define GPU_0_DIODE "TG0D"
#define GPU_0_HEATSINK "TG0H"
#define GPU_0_PROXIMITY "TG0P"
#define HARD_DRIVE_BAY "TH0P"
#define MEMORY_SLOT_0 "TM0S"
#define MEMORY_SLOTS_PROXIMITY "TM0P"
#define NORTHBRIDGE "TN0H"
#define NORTHBRIDGE_DIODE "TN0D"
#define NORTHBRIDGE_PROXIMITY "TN0P"
#define THUNDERBOLT_0 "TI0P"
#define THUNDERBOLT_1 "TI1P"
#define WIRELESS_MODULE "TW0P"
kern_return_t gopsutil_v4_open_smc(void);
kern_return_t gopsutil_v4_close_smc(void);
double gopsutil_v4_get_temperature(char *);
#endif // __SMC_H__