From aac76623b3c4a943f63d84ff3f5b98d3ef9fe414 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Fri, 24 May 2024 17:38:58 +0200 Subject: [PATCH 01/27] start porting process sampler --- NOTICE | 4 + devdocs/pipeline-map.md | 6 + pkg/internal/infraolly/process/cache.go | 22 + .../infraolly/process/harvester_linux.go | 214 + .../infraolly/process/harvester_linux_test.go | 273 + .../infraolly/process/harvester_unix.go | 32 + .../process/harvester_unix_mock_test.go | 41 + pkg/internal/infraolly/process/process.go | 42 + .../infraolly/process/process_mock_test.go | 144 + .../process_retriever_cached_darwin.go | 429 ++ .../process_retriever_cached_darwin_test.go | 4919 +++++++++++++++++ pkg/internal/infraolly/process/sample.go | 36 + .../infraolly/process/sampler_darwin.go | 175 + .../infraolly/process/sampler_darwin_test.go | 155 + .../infraolly/process/sampler_linux.go | 180 + .../infraolly/process/sampler_linux_test.go | 199 + .../infraolly/process/sampler_unix_test.go | 32 + pkg/internal/infraolly/process/snapshot.go | 47 + .../infraolly/process/snapshot_darwin.go | 248 + .../infraolly/process/snapshot_darwin_test.go | 431 ++ .../infraolly/process/snapshot_linux.go | 380 ++ .../infraolly/process/snapshot_linux_test.go | 248 + .../infraolly/process/snapshot_mock_test.go | 169 + 23 files changed, 8426 insertions(+) create mode 100644 pkg/internal/infraolly/process/cache.go create mode 100644 pkg/internal/infraolly/process/harvester_linux.go create mode 100644 pkg/internal/infraolly/process/harvester_linux_test.go create mode 100644 pkg/internal/infraolly/process/harvester_unix.go create mode 100644 pkg/internal/infraolly/process/harvester_unix_mock_test.go create mode 100644 pkg/internal/infraolly/process/process.go create mode 100644 pkg/internal/infraolly/process/process_mock_test.go create mode 100644 pkg/internal/infraolly/process/process_retriever_cached_darwin.go create mode 100644 pkg/internal/infraolly/process/process_retriever_cached_darwin_test.go create mode 100644 pkg/internal/infraolly/process/sample.go create mode 100644 pkg/internal/infraolly/process/sampler_darwin.go create mode 100644 pkg/internal/infraolly/process/sampler_darwin_test.go create mode 100644 pkg/internal/infraolly/process/sampler_linux.go create mode 100644 pkg/internal/infraolly/process/sampler_linux_test.go create mode 100644 pkg/internal/infraolly/process/sampler_unix_test.go create mode 100644 pkg/internal/infraolly/process/snapshot.go create mode 100644 pkg/internal/infraolly/process/snapshot_darwin.go create mode 100644 pkg/internal/infraolly/process/snapshot_darwin_test.go create mode 100644 pkg/internal/infraolly/process/snapshot_linux.go create mode 100644 pkg/internal/infraolly/process/snapshot_linux_test.go create mode 100644 pkg/internal/infraolly/process/snapshot_mock_test.go diff --git a/NOTICE b/NOTICE index a25dd0413..269115732 100644 --- a/NOTICE +++ b/NOTICE @@ -31,6 +31,10 @@ The Initial Developer of some parts of the product, which are copied from, deriv inspired by the DataDog Agent (https://github.com/DataDog/datadog-agent). Copyright DataDog. +The Initial Developer of some parts of the product, which are copied from, derived from, or +inspired by the New Relic Infrastructure Agent (https://github.com/newrelic/infrastructure-agent). +Copyright New Relic. + Grafana Beyla uses third-party libraries or other resources that may be distributed under licenses different than the Grafana Beyla software. The licenses for these third-party libraries are listed in the attached third_party_licenses.csv file diff --git a/devdocs/pipeline-map.md b/devdocs/pipeline-map.md index d39043666..9793bacfc 100644 --- a/devdocs/pipeline-map.md +++ b/devdocs/pipeline-map.md @@ -22,6 +22,8 @@ flowchart TD TA(TraceAttacher) -.-> EBPF1(ebpf.Tracer) TA -.-> |creates one per executable| EBPF2(ebpf.Tracer) TA -.-> EBPF3(ebpf.Tracer) + ET --> PN + PN(ProcessNotifier) end subgraph Decoration and forwarding pipeline EBPF1 -.-> TR @@ -41,6 +43,10 @@ flowchart TD KDB(KubeDatabase):::optional <-.- | Aggregated & indexed Pod info | KD IF("Informer
(Kube API)"):::optional -.-> |Pods & ReplicaSets status| KDB IF -.-> |new Kube objects| KWE + subgraph Process pipeline + PN -.-> |PIDs map| PS + PS(ProcessSampler) + end ``` ## Network metrics pipeline diff --git a/pkg/internal/infraolly/process/cache.go b/pkg/internal/infraolly/process/cache.go new file mode 100644 index 000000000..cc65ad3e3 --- /dev/null +++ b/pkg/internal/infraolly/process/cache.go @@ -0,0 +1,22 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +//go:build linux +// +build linux + +package process + +import ( + "github.com/hashicorp/golang-lru/v2/simplelru" +) + +type cacheEntry struct { + process *linuxProcess + lastSample *Sample // The last event we generated for this process, so we can re-use metadata which doesn't change +} + +// removeUntilLen removes the oldest entries until the cache reaches the given length. +func removeUntilLen(c *simplelru.LRU[int32, *cacheEntry], newLength int) { + for c.Len() > newLength { + c.RemoveOldest() + } +} diff --git a/pkg/internal/infraolly/process/harvester_linux.go b/pkg/internal/infraolly/process/harvester_linux.go new file mode 100644 index 000000000..f87f50551 --- /dev/null +++ b/pkg/internal/infraolly/process/harvester_linux.go @@ -0,0 +1,214 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +// Package process provides all the tools and functionality for sampling processes. It is divided in three main +// components: +// - Snapshot: provides OS-level information of a process at a given spot +// - Harvester: manages process Snapshots to create actual Process Samples with the actual metrics. +// - Sampler: uses the harvester to coordinate the creation of the Process Samples dataset, as being reported to NR +package process + +import ( + "github.com/hashicorp/golang-lru/v2/simplelru" + "github.com/newrelic/infrastructure-agent/internal/agent" + "github.com/newrelic/infrastructure-agent/pkg/config" + "github.com/newrelic/infrastructure-agent/pkg/metrics" + "github.com/newrelic/infrastructure-agent/pkg/metrics/acquire" + "github.com/newrelic/infrastructure-agent/pkg/metrics/types" + "github.com/pkg/errors" + "github.com/shirou/gopsutil/v3/process" + "github.com/sirupsen/logrus" +) + +func newHarvester(ctx agent.AgentContext, cache *simplelru.LRU[int32, *cacheEntry]) *linuxHarvester { + cfg := ctx.Config() + // If not config, assuming root mode as default + privileged := cfg == nil || cfg.RunMode == config.ModeRoot || cfg.RunMode == config.ModePrivileged + disableZeroRSSFilter := cfg != nil && cfg.DisableZeroRSSFilter + stripCommandLine := (cfg != nil && cfg.StripCommandLine) || (cfg == nil && config.DefaultStripCommandLine) + + return &linuxHarvester{ + privileged: privileged, + disableZeroRSSFilter: disableZeroRSSFilter, + stripCommandLine: stripCommandLine, + serviceForPid: ctx.GetServiceForPid, + cache: cache, + } +} + +// linuxHarvester is a Harvester implementation that uses various linux sources and manages process caches +type linuxHarvester struct { + privileged bool + disableZeroRSSFilter bool + stripCommandLine bool + cache *cache + serviceForPid func(int) (string, bool) +} + +var _ Harvester = (*linuxHarvester)(nil) // static interface assertion + +// Pids returns a slice of process IDs that are running now +func (*linuxHarvester) Pids() ([]int32, error) { + return process.Pids() +} + +// Returns a sample of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the +// time since this process was sampled for the last time. If the process has been sampled for the first time, this value +// will be ignored +func (ps *linuxHarvester) Do(pid int32, elapsedSeconds float64) (*Sample, error) { + // Reuses process information that does not vary + cached, hasCachedSample := ps.cache.Get(pid) + + // If cached is nil, the linux process will be created from fresh data + if !hasCachedSample { + cached = &cacheEntry{} + } + var err error + cached.process, err = getLinuxProcess(pid, cached.process, ps.privileged) + if err != nil { + return nil, errors.Wrap(err, "can't create process") + } + + // We don't need to report processes which are not using memory. This filters out certain kernel processes. + if !ps.disableZeroRSSFilter && cached.process.VmRSS() == 0 { + return nil, errProcessWithoutRSS + } + + // Creates a fresh process sample and populates it with the metrics data + sample := metrics.NewProcessSample(pid) + + if err := ps.populateStaticData(sample, cached.process); err != nil { + return nil, errors.Wrap(err, "can't populate static attributes") + } + + // As soon as we have successfully stored the static (reusable) values, we can cache the entry + if !hasCachedSample { + ps.cache.Add(pid, cached) + } + + if err := ps.populateGauges(sample, cached.process); err != nil { + return nil, errors.Wrap(err, "can't fetch gauge data") + } + + if err := ps.populateIOCounters(sample, cached.lastSample, cached.process, elapsedSeconds); err != nil { + return nil, errors.Wrap(err, "can't fetch deltas") + } + + // This must happen every time, even if we already had a cached sample for the process, because + // the available process name metadata may have changed underneath us (if we pick up a new + // service/PID association, etc) + sample.ProcessDisplayName = ps.determineProcessDisplayName(sample) + + sample.Type("ProcessSample") + cached.lastSample = sample + + return sample, nil +} + +// populateStaticData populates the sample with the process data won't vary during the process life cycle +func (ps *linuxHarvester) populateStaticData(sample *Sample, process Snapshot) error { + var err error + sample.CmdLine, err = process.CmdLine(!ps.stripCommandLine) + if err != nil { + return errors.Wrap(err, "acquiring command line") + } + + sample.ProcessID = process.Pid() + + sample.User, err = process.Username() + if err != nil { + mplog.WithError(err).WithField("processID", sample.ProcessID).Debug("Can't get Username for process.") + } + + sample.CommandName = process.Command() + sample.ParentProcessID = process.Ppid() + + return nil +} + +// populateGauges populates the sample with gauge data that represents the process state at a given point +func (ps *linuxHarvester) populateGauges(sample *Sample, process Snapshot) error { + var err error + + cpuTimes, err := process.CPUTimes() + if err != nil { + return err + } + sample.CPUPercent = cpuTimes.Percent + + totalCPU := cpuTimes.User + cpuTimes.System + + if totalCPU > 0 { + sample.CPUUserPercent = (cpuTimes.User / totalCPU) * sample.CPUPercent + sample.CPUSystemPercent = (cpuTimes.System / totalCPU) * sample.CPUPercent + } else { + sample.CPUUserPercent = 0 + sample.CPUSystemPercent = 0 + } + + if ps.privileged { + fds, err := process.NumFDs() + if err != nil { + return err + } + if fds >= 0 { + sample.FdCount = &fds + } + } + + // Extra status data + sample.Status = process.Status() + sample.ThreadCount = process.NumThreads() + sample.MemoryVMSBytes = process.VmSize() + sample.MemoryRSSBytes = process.VmRSS() + + return nil +} + +// populateIOCounters fills the sample with the IO counters data. For the "X per second" metrics, it requires the +// last process sample for comparative purposes +func (ps *linuxHarvester) populateIOCounters(sample, lastSample *Sample, source Snapshot, elapsedSeconds float64) error { + ioCounters, err := source.IOCounters() + if err != nil { + return err + } + if ioCounters != nil { + // Delta + if lastSample != nil && lastSample.LastIOCounters != nil { + lastCounters := lastSample.LastIOCounters + + mplog.WithField(config.TracesFieldName, config.FeatureTrace).Tracef("ReadCount: %d, WriteCount: %d, ReadBytes: %d, WriteBytes: %d", ioCounters.ReadCount, ioCounters.WriteCount, ioCounters.ReadBytes, ioCounters.WriteBytes) + ioReadCountPerSecond := acquire.CalculateSafeDelta(ioCounters.ReadCount, lastCounters.ReadCount, elapsedSeconds) + ioWriteCountPerSecond := acquire.CalculateSafeDelta(ioCounters.WriteCount, lastCounters.WriteCount, elapsedSeconds) + ioReadBytesPerSecond := acquire.CalculateSafeDelta(ioCounters.ReadBytes, lastCounters.ReadBytes, elapsedSeconds) + ioWriteBytesPerSecond := acquire.CalculateSafeDelta(ioCounters.WriteBytes, lastCounters.WriteBytes, elapsedSeconds) + + sample.IOReadCountPerSecond = &ioReadCountPerSecond + sample.IOWriteCountPerSecond = &ioWriteCountPerSecond + sample.IOReadBytesPerSecond = &ioReadBytesPerSecond + sample.IOWriteBytesPerSecond = &ioWriteBytesPerSecond + } + + // Cumulative + sample.IOTotalReadCount = &ioCounters.ReadCount + sample.IOTotalWriteCount = &ioCounters.WriteCount + sample.IOTotalReadBytes = &ioCounters.ReadBytes + sample.IOTotalWriteBytes = &ioCounters.WriteBytes + + sample.LastIOCounters = ioCounters + } + return nil +} + +// determineProcessDisplayName generates a human-friendly name for this process. By default, we use the command name. +// If we know of a service for this pid, that'll be the name. +func (ps *linuxHarvester) determineProcessDisplayName(sample *Sample) string { + displayName := sample.CommandName + if serviceName, ok := ps.serviceForPid(int(sample.ProcessID)); ok && len(serviceName) > 0 { + mplog.WithFieldsF(func() logrus.Fields { + return logrus.Fields{"serviceName": serviceName, "displayName": displayName, "ProcessID": sample.ProcessID} + }).Debug("Using service name as display name.") + displayName = serviceName + } + + return displayName +} diff --git a/pkg/internal/infraolly/process/harvester_linux_test.go b/pkg/internal/infraolly/process/harvester_linux_test.go new file mode 100644 index 000000000..98b094e85 --- /dev/null +++ b/pkg/internal/infraolly/process/harvester_linux_test.go @@ -0,0 +1,273 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package process + +import ( + "fmt" + "math" + "os" + "os/exec" + "os/user" + "strings" + "testing" + "time" + + "github.com/hashicorp/golang-lru/v2/simplelru" + "github.com/newrelic/infrastructure-agent/internal/agent/mocks" + "github.com/newrelic/infrastructure-agent/internal/testhelpers" + "github.com/newrelic/infrastructure-agent/pkg/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestLinuxHarvester_IsPrivileged(t *testing.T) { + cases := []struct { + mode string + privileged bool + }{ + {mode: config.ModeRoot, privileged: true}, + {mode: config.ModePrivileged, privileged: true}, + {mode: config.ModeUnprivileged, privileged: false}, + } + for _, c := range cases { + t.Run(fmt.Sprint("mode ", c.mode), func(t *testing.T) { + ctx := new(mocks.AgentContext) + ctx.On("Config").Return(&config.Config{RunMode: c.mode}) + ctx.On("GetServiceForPid", mock.Anything).Return("", false) + + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + h := newHarvester(ctx, &cache) + + // If not privileged, it is expected to not report neither FDs nor IO counters + sample, err := h.Do(int32(os.Getpid()), 100) + require.NoError(t, err) + if c.privileged { + assert.NotNil(t, sample.FdCount) + assert.NotNil(t, sample.IOTotalReadCount) + } else { + assert.Nil(t, sample.FdCount) + assert.Nil(t, sample.IOTotalReadCount) + } + }) + } +} + +func TestLinuxHarvester_Pids(t *testing.T) { + // Given a process harvester + ctx := new(mocks.AgentContext) + ctx.On("Config").Return(&config.Config{}) + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + h := newHarvester(ctx, &cache) + + // When th Pids are retrieved + pids, err := h.Pids() + + // A pids list is returned + require.NoError(t, err) + require.NotEmpty(t, pids) + + // And it contains the pids of the running processes (e.g. current testing executable) + require.Contains(t, pids, int32(os.Getpid())) +} + +func TestLinuxHarvester_Do(t *testing.T) { + // Given a process harvester + ctx := new(mocks.AgentContext) + ctx.On("Config").Return(&config.Config{}) + ctx.On("GetServiceForPid", mock.Anything).Return("", false) + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + h := newHarvester(ctx, &cache) + + // When retrieving for a given process sample (e.g. the current testing executable) + sample, err := h.Do(int32(os.Getpid()), 0) + + // It returns the corresponding process sample with valid data + require.NoError(t, err) + require.NotNil(t, sample) + + assert.Equal(t, int32(os.Getpid()), sample.ProcessID) + assert.Equal(t, "process.test", sample.CommandName) + assert.Contains(t, sample.CmdLine, os.Args[0]) + assert.NotEmpty(t, sample.User) + assert.Contains(t, "RSD", sample.Status, + "process status must be R (running), S (interruptible sleep) or D (uninterruptible sleep)") + assert.True(t, sample.MemoryVMSBytes > 0) + assert.True(t, sample.ThreadCount > 0) + assert.Equal(t, "process.test", sample.ProcessDisplayName) + assert.Equal(t, "ProcessSample", sample.EventType) +} + +func TestLinuxHarvester_Do_Privileged(t *testing.T) { + current, err := user.Current() + require.NoError(t, err) + if current.Username != "root" { + t.Skip("this test requires privileges. Current user: ", current.Username) + } + + // Given a process harvester running in privileged mode + ctx := new(mocks.AgentContext) + ctx.On("Config").Return(&config.Config{RunMode: config.ModeRoot}) + ctx.On("GetServiceForPid", mock.Anything).Return("", false) + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + h := newHarvester(ctx, &cache) + + // When retrieving for a given process sample (e.g. the current testing executable) + sample, err := h.Do(int32(os.Getpid()), 0) + + // It returns the corresponding process sample with valid data + require.NoError(t, err) + require.NotNil(t, sample) + + assert.NotNil(t, sample.FdCount) + + // And when the process sample is retrieved again + sample, err = h.Do(int32(os.Getpid()), 0) + require.NoError(t, err) + require.NotNil(t, sample) + + // Per second deltas are returned + assert.NotNil(t, sample.IOReadBytesPerSecond) + assert.NotNil(t, sample.IOReadCountPerSecond) + assert.NotNil(t, sample.IOWriteBytesPerSecond) + assert.NotNil(t, sample.IOWriteCountPerSecond) +} + +func TestLinuxHarvester_Do_DisableStripCommandLine(t *testing.T) { + cmd := exec.Command("/bin/sleep", "1m") + require.NoError(t, cmd.Start()) + defer func() { + _ = cmd.Process.Kill() + }() + + // Given a process harvester + ctx := new(mocks.AgentContext) + // configure to not strip the command line + ctx.On("Config").Return(&config.Config{StripCommandLine: false}) + ctx.On("GetServiceForPid", mock.Anything).Return("", false) + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + h := newHarvester(ctx, &cache) + + testhelpers.Eventually(t, 5*time.Second, func(t require.TestingT) { + // When retrieving for a given process sample (e.g. the current testing executable) + sample, err := h.Do(int32(cmd.Process.Pid), 0) + + // It returns the corresponding Command line without stripping arguments + require.NoError(t, err) + require.NotNil(t, sample) + + assert.False(t, strings.HasSuffix(sample.CmdLine, "sleep"), "%q should have arguments", sample.CmdLine) + assert.Contains(t, sample.CmdLine, "sleep") + }) +} + +func TestLinuxHarvester_Do_EnableStripCommandLine(t *testing.T) { + cmd := exec.Command("/bin/sleep", "1m") + require.NoError(t, cmd.Start()) + defer func() { + _ = cmd.Process.Kill() + }() + + // Given a process harvester + ctx := new(mocks.AgentContext) + // configure to not strip the command line + ctx.On("Config").Return(&config.Config{StripCommandLine: true}) + ctx.On("GetServiceForPid", mock.Anything).Return("", false) + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + h := newHarvester(ctx, &cache) + + testhelpers.Eventually(t, 5*time.Second, func(t require.TestingT) { + // When retrieving for a given process sample (e.g. the current testing executable) + sample, err := h.Do(int32(cmd.Process.Pid), 0) + + // It returns the corresponding Command line without stripping arguments + require.NoError(t, err) + require.NotNil(t, sample) + + assert.True(t, strings.HasSuffix(sample.CmdLine, "sleep"), "%q should not have arguments", sample.CmdLine) + }) +} + +func TestLinuxHarvester_Do_InvalidateCache_DifferentCmd(t *testing.T) { + currentPid := int32(os.Getpid()) + + // Given a process harvester + ctx := new(mocks.AgentContext) + ctx.On("Config").Return(&config.Config{}) + ctx.On("GetServiceForPid", mock.Anything).Return("", false) + + // That has cached an old process sharing the PID with a new process + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + cache.Add(currentPid, &cacheEntry{process: &linuxProcess{cmdLine: "something old"}}) + h := newHarvester(ctx, &cache) + + // When the process is harvested + sample, err := h.Do(currentPid, 0) + require.NoError(t, err) + + // The sample is updated + assert.NotEmpty(t, sample.CommandName) + assert.NotEqual(t, "something old", sample.CommandName) +} + +func TestLinuxHarvester_Do_InvalidateCache_DifferentPid(t *testing.T) { + currentPid := int32(os.Getpid()) + + // Given a process harvester + ctx := new(mocks.AgentContext) + ctx.On("Config").Return(&config.Config{}) + ctx.On("GetServiceForPid", mock.Anything).Return("", false) + + // That has cached an old process sharing the PID with a new process + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + cache.Add(currentPid, &cacheEntry{process: &linuxProcess{stats: procStats{ppid: -1}}}) + h := newHarvester(ctx, &cache) + + // When the process is harvested + sample, err := h.Do(currentPid, 0) + require.NoError(t, err) + + // The sample is updated + assert.NotEqual(t, -1, sample.ParentProcessID) +} + +func TestLinuxHarvester_GetServiceForPid(t *testing.T) { + // Given a process harvester + ctx := new(mocks.AgentContext) + ctx.On("Config").Return(&config.Config{}) + // That matches a given PID with an existing service name + ctx.On("GetServiceForPid", os.Getpid()).Return("MyServiceIdentifier", true) + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + h := newHarvester(ctx, &cache) + + // When retrieving the process sampler + sample, err := h.Do(int32(os.Getpid()), 0) + + // It returns the corresponding process names + require.NoError(t, err) + require.NotNil(t, sample) + assert.Equal(t, "MyServiceIdentifier", sample.ProcessDisplayName) + assert.Equal(t, "process.test", sample.CommandName) + assert.Contains(t, sample.CmdLine, os.Args[0]) +} + +func TestLinuxHarvester_GetServiceForPid_OnEmptyUseCommandName(t *testing.T) { + + // Given a process harvester + ctx := new(mocks.AgentContext) + ctx.On("Config").Return(&config.Config{}) + // That matches a given PID with an existing service name that is EMPTY + ctx.On("GetServiceForPid", os.Getpid()).Return("", true) + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + h := newHarvester(ctx, &cache) + + // When retrieving the process sampler + sample, err := h.Do(int32(os.Getpid()), 0) + + // It returns the corresponding process names + require.NoError(t, err) + require.NotNil(t, sample) + assert.Equal(t, sample.CommandName, sample.ProcessDisplayName) + assert.Equal(t, "process.test", sample.CommandName) + assert.Contains(t, sample.CmdLine, os.Args[0]) +} diff --git a/pkg/internal/infraolly/process/harvester_unix.go b/pkg/internal/infraolly/process/harvester_unix.go new file mode 100644 index 000000000..495487cc6 --- /dev/null +++ b/pkg/internal/infraolly/process/harvester_unix.go @@ -0,0 +1,32 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +//go:build linux || darwin +// +build linux darwin + +// Package process provides all the tools and functionality for sampling processes. It is divided in three main +// components: +// - Snapshot: provides OS-level information of a process at a given spot +// - Harvester: manages process Snapshots to create actual Process Samples with the actual metrics. +// - Sampler: uses the harvester to coordinate the creation of the Process Samples dataset, as being reported to NR +package process + +import ( + "fmt" + + "github.com/newrelic/infrastructure-agent/pkg/log" + "github.com/newrelic/infrastructure-agent/pkg/metrics/types" +) + +var mplog = log.WithComponent("ProcessSampler") + +var errProcessWithoutRSS = fmt.Errorf("process with zero rss") + +// Harvester manages sampling for individual processes. It is used by the Process Sampler to get information about the +// existing processes. +type Harvester interface { + // Pids return the IDs of all the processes that are currently running + Pids() ([]int32, error) + // Do performs the actual harvesting operation, returning a process sample containing all the metrics data + // for the last elapsedSeconds + Do(pid int32, elapsedSeconds float64) (*Sample, error) +} diff --git a/pkg/internal/infraolly/process/harvester_unix_mock_test.go b/pkg/internal/infraolly/process/harvester_unix_mock_test.go new file mode 100644 index 000000000..04910f47a --- /dev/null +++ b/pkg/internal/infraolly/process/harvester_unix_mock_test.go @@ -0,0 +1,41 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +//go:build linux || darwin +// +build linux darwin + +package process + +import ( + "github.com/newrelic/infrastructure-agent/pkg/metrics/types" + "github.com/stretchr/testify/mock" +) + +type HarvesterMock struct { + mock.Mock +} + +func (h *HarvesterMock) Pids() ([]int32, error) { + args := h.Called() + + return args.Get(0).([]int32), args.Error(1) +} + +func (h *HarvesterMock) ShouldReturnPids(pids []int32, err error) { + h. + On("Pids"). + Once(). + Return(pids, err) +} + +func (h *HarvesterMock) Do(pid int32, elapsedSeconds float64) (*Sample, error) { + args := h.Called(pid, elapsedSeconds) + + return args.Get(0).(*Sample), args.Error(1) +} + +func (h *HarvesterMock) ShouldDo(pid int32, elapsedSeconds float64, sample *Sample, err error) { + h. + On("Do", pid, elapsedSeconds). + Once(). + Return(sample, err) +} diff --git a/pkg/internal/infraolly/process/process.go b/pkg/internal/infraolly/process/process.go new file mode 100644 index 000000000..d23b1c963 --- /dev/null +++ b/pkg/internal/infraolly/process/process.go @@ -0,0 +1,42 @@ +// Copyright New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package process + +import ( + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/process" +) + +// Process it's an interface to abstract gopsutil process so we can mock it and test not coupling to infra +type Process interface { + Username() (string, error) + Name() (string, error) + Cmdline() (string, error) + ProcessId() int32 + Parent() (Process, error) + NumThreads() (int32, error) + Status() ([]string, error) + MemoryInfo() (*process.MemoryInfoStat, error) + CPUPercent() (float64, error) + Times() (*cpu.TimesStat, error) +} + +// ProcessWrapper is necessary to implement the interface as gopsutil process is not exporting Pid() +type ProcessWrapper struct { + *process.Process +} + +// ProcessId returns the Pid of the process +func (p *ProcessWrapper) ProcessId() int32 { + return p.Process.Pid +} + +// Parent return the process' parent +func (p *ProcessWrapper) Parent() (Process, error) { + par, err := p.Process.Parent() + if err != nil { + return &ProcessWrapper{}, err + } + return &ProcessWrapper{par}, nil +} diff --git a/pkg/internal/infraolly/process/process_mock_test.go b/pkg/internal/infraolly/process/process_mock_test.go new file mode 100644 index 000000000..3a155081d --- /dev/null +++ b/pkg/internal/infraolly/process/process_mock_test.go @@ -0,0 +1,144 @@ +// Copyright New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package process + +import ( + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/process" + "github.com/stretchr/testify/mock" +) + +type ProcessMock struct { + mock.Mock +} + +func (s *ProcessMock) Username() (string, error) { + args := s.Called() + + return args.String(0), args.Error(1) +} + +func (s *ProcessMock) ShouldReturnUsername(username string, err error) { + s. + On("Username"). + Once(). + Return(username, err) +} + +func (s *ProcessMock) Name() (string, error) { + args := s.Called() + + return args.String(0), args.Error(1) +} + +func (s *ProcessMock) ShouldReturnName(name string, err error) { + s. + On("Name"). + Once(). + Return(name, err) +} + +func (s *ProcessMock) Cmdline() (string, error) { + args := s.Called() + + return args.String(0), args.Error(1) +} + +func (s *ProcessMock) ShouldReturnCmdLine(cmdLine string, err error) { + s. + On("Cmdline"). + Once(). + Return(cmdLine, err) +} + +func (s *ProcessMock) ProcessId() int32 { + args := s.Called() + + return args.Get(0).(int32) +} + +func (s *ProcessMock) ShouldReturnProcessId(processId int32) { + s.ShouldReturnProcessIdMultipleTimes(processId, 1) +} + +func (s *ProcessMock) ShouldReturnProcessIdMultipleTimes(processId int32, times int) { + s. + On("ProcessId"). + Times(times). + Return(processId) +} + +func (s *ProcessMock) Parent() (Process, error) { + args := s.Called() + + return args.Get(0).(Process), args.Error(1) +} + +func (s *ProcessMock) ShouldReturnParent(process Process, err error) { + s. + On("Parent"). + Once(). + Return(process, err) +} + +func (s *ProcessMock) NumThreads() (int32, error) { + args := s.Called() + + return args.Get(0).(int32), args.Error(1) +} + +func (s *ProcessMock) ShouldReturnNumThreads(num int32, err error) { + s. + On("NumThreads"). + Once(). + Return(num, err) +} + +func (s *ProcessMock) Status() ([]string, error) { + args := s.Called() + + return args.Get(0).([]string), args.Error(1) +} +func (s *ProcessMock) ShouldReturnStatus(status []string, err error) { + s. + On("Status"). + Once(). + Return(status, err) +} + +func (s *ProcessMock) MemoryInfo() (*process.MemoryInfoStat, error) { + args := s.Called() + + return args.Get(0).(*process.MemoryInfoStat), args.Error(1) +} +func (s *ProcessMock) ShouldReturnMemoryInfo(memInfo *process.MemoryInfoStat, err error) { + s. + On("MemoryInfo"). + Once(). + Return(memInfo, err) +} + +func (s *ProcessMock) CPUPercent() (float64, error) { + args := s.Called() + + return args.Get(0).(float64), args.Error(1) +} +func (s *ProcessMock) ShouldReturnCPUPercent(percent float64, err error) { + s. + On("CPUPercent"). + Once(). + Return(percent, err) +} + +func (s *ProcessMock) Times() (*cpu.TimesStat, error) { + args := s.Called() + + return args.Get(0).(*cpu.TimesStat), args.Error(1) +} +func (s *ProcessMock) ShouldReturnTimes(times *cpu.TimesStat, err error) { + s. + On("Times"). + Once(). + Return(times, err) +} diff --git a/pkg/internal/infraolly/process/process_retriever_cached_darwin.go b/pkg/internal/infraolly/process/process_retriever_cached_darwin.go new file mode 100644 index 000000000..e21582b56 --- /dev/null +++ b/pkg/internal/infraolly/process/process_retriever_cached_darwin.go @@ -0,0 +1,429 @@ +// Copyright New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package process + +import ( + "fmt" + "github.com/newrelic/infrastructure-agent/pkg/helpers" + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/process" + "os/exec" + "strconv" + "strings" + "sync" + "time" +) + +const ( + ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) +) + +type CommandRunner func(command string, stdin string, arguments ...string) (string, error) + +var commandRunner CommandRunner = helpers.RunCommand + +// ProcessRetrieverCached acts as a process.ProcessRetriever and retrieves a process.Process from its pid +// it uses an in-memory cache to store the information of all running processes with a short ttl enough to +// read information of all processes with just 2 calls to ps +// it uses c&p parts of code of gopsutil which was the 1st approach but makes too may system calls +type ProcessRetrieverCached struct { + cache cache +} + +func NewProcessRetrieverCached(ttl time.Duration) *ProcessRetrieverCached { + return &ProcessRetrieverCached{cache: cache{ttl: ttl}} +} + +// ProcessById returns a process.Process by pid or error if not found +func (s *ProcessRetrieverCached) ProcessById(pid int32) (Process, error) { + procs, err := s.processesFromCache() + if err != nil { + return nil, err + } + if proc, ok := procs[pid]; ok { + return &proc, nil + } + + return nil, fmt.Errorf("cannot find process with pid %v", pid) +} + +// processesFromCache returns all processes running. These will be retrieved and cached for cache.ttl time +func (s *ProcessRetrieverCached) processesFromCache() (map[int32]psItem, error) { + s.cache.Lock() + defer s.cache.Unlock() + + if s.cache.expired() { + psBin, err := exec.LookPath("ps") + if err != nil { + return nil, err + } + // it's easier to get the thread num per process from different call + processesThreads, err := s.getProcessThreads(psBin) + if err != nil { + return nil, err + } + // it's easier to get the thread num per process from different call + fullCmd, err := s.getProcessFullCmd(psBin) + if err != nil { + return nil, err + } + //get all processes and inject numThreads + items, err := s.retrieveProcesses(psBin) + if err != nil { + return nil, err + } + items = addThreadsAndCmdToPsItems(items, processesThreads, fullCmd) + s.cache.update(items) + } + + return s.cache.items, nil +} + +func addThreadsAndCmdToPsItems(items map[int32]psItem, processesThreads map[int32]int32, processesCmd map[int32]string) map[int32]psItem { + itemsWithAllInfo := make(map[int32]psItem) + for pid, item := range items { + if numThreads, ok := processesThreads[pid]; ok { + item.numThreads = numThreads + } + if cmd, ok := processesCmd[pid]; ok { + item.cmdLine = cmd + } + itemsWithAllInfo[pid] = item + } + return itemsWithAllInfo +} + +func (s *ProcessRetrieverCached) retrieveProcesses(psBin string) (map[int32]psItem, error) { + + // get all processes info + args := []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"} + out, err := commandRunner(psBin, "", args...) + if err != nil { + return nil, err + } + + lines := strings.Split(out, "\n") + items := make(map[int32]psItem) + for _, line := range lines[1:] { + var lineItems []string + for _, lineItem := range strings.Split(line, " ") { + if lineItem == "" { + continue + } + lineItems = append(lineItems, strings.TrimSpace(lineItem)) + } + if len(lineItems) > 10 { + pid, _ := strconv.Atoi(lineItems[0]) + ppid, _ := strconv.Atoi(lineItems[1]) + user := lineItems[2] + state := lineItems[3] + utime := lineItems[4] + stime := lineItems[5] + etime := lineItems[6] + rss, _ := strconv.ParseInt(lineItems[7], 10, 64) + vsize, _ := strconv.ParseInt(lineItems[8], 10, 64) + pagein, _ := strconv.ParseInt(lineItems[9], 10, 64) + command := strings.Join(lineItems[10:], " ") + + item := psItem{ + pid: int32(pid), + ppid: int32(ppid), + username: user, + state: []string{convertStateToGopsutilState(state[0:1])}, + utime: utime, + stime: stime, + etime: etime, + rss: rss, + vsize: vsize, + pagein: pagein, + command: command, + } + items[int32(pid)] = item + } else { + mplog.WithField("ps_output", out).Error("ps output is expected to have >10 columns") + } + } + return items, nil +} + +// convertStateToGopsutilState converts ps state to gopsutil v3 state +// C&P from https://github.com/shirou/gopsutil/blob/v3.21.11/v3/process/process.go#L575 +func convertStateToGopsutilState(letter string) string { + // Sources + // Darwin: http://www.mywebuniversity.com/Man_Pages/Darwin/man_ps.html + // FreeBSD: https://www.freebsd.org/cgi/man.cgi?ps + // Linux https://man7.org/linux/man-pages/man1/ps.1.html + // OpenBSD: https://man.openbsd.org/ps.1#state + // Solaris: https://github.com/collectd/collectd/blob/1da3305c10c8ff9a63081284cf3d4bb0f6daffd8/src/processes.c#L2115 + switch letter { + case "A": + return process.Daemon + case "D", "U": + return process.Blocked + case "E": + return process.Detached + case "I": + return process.Idle + case "L": + return process.Lock + case "O": + return process.Orphan + case "R": + return process.Running + case "S": + return process.Sleep + case "T", "t": + // "t" is used by Linux to signal stopped by the debugger during tracing + return process.Stop + case "W": + return process.Wait + case "Y": + return process.System + case "Z": + return process.Zombie + default: + return process.UnknownState + } +} + +func (s *ProcessRetrieverCached) getProcessThreads(psBin string) (map[int32]int32, error) { + // get all processes info with threads + args := []string{"ax", "-M", "-c"} + out, err := commandRunner(psBin, "", args...) + if err != nil { + return nil, err + } + + lines := strings.Split(out, "\n") + processThreads := make(map[int32]int32) + for _, line := range lines[1:] { + if len(line) > 0 && line[0] != ' ' { + //we exclude main process for simplicity + continue + } + for _, lineItem := range strings.Split(line, " ") { + if lineItem == "" { + continue + } + pidAsInt, err := strconv.Atoi(strings.TrimSpace(lineItem)) + if err != nil { + mplog.Warnf("pid %v doesn't look like an int", pidAsInt) + continue + } + pid := int32(pidAsInt) + if _, ok := processThreads[pid]; !ok { + processThreads[pid] = 1 //main process already included + } + processThreads[pid]++ + //we are only interested in pid so break and process next line + break + } + } + + return processThreads, nil +} + +// getProcessFullCmd retrieves the full process command line w/o arguments (as commands can have spaces in mac :( ) +func (s *ProcessRetrieverCached) getProcessFullCmd(psBin string) (map[int32]string, error) { + // get all processes info with threads + args := []string{"ax", "-o", "pid,command"} + out, err := commandRunner(psBin, "", args...) + if err != nil { + return nil, err + } + + lines := strings.Split(out, "\n") + processThreads := make(map[int32]string) + for _, line := range lines[1:] { + var lineItems []string + for _, lineItem := range strings.Split(line, " ") { + if lineItem == "" { + continue + } + lineItems = append(lineItems, strings.TrimSpace(lineItem)) + } + if len(lineItems) > 1 { + pidAsInt, _ := strconv.Atoi(lineItems[0]) + cmd := strings.Join(lineItems[1:], " ") + pid := int32(pidAsInt) + if _, ok := processThreads[pid]; !ok { + processThreads[pid] = cmd + } + } + } + + return processThreads, nil +} + +// createTime retrieves ceate time from ps output etime field +// it is a c&p of gopsutil process.CreateTimeWithContext +func createTime(etime string) (int64, error) { + elapsedSegments := strings.Split(strings.Replace(etime, "-", ":", 1), ":") + var elapsedDurations []time.Duration + for i := len(elapsedSegments) - 1; i >= 0; i-- { + p, err := strconv.ParseInt(elapsedSegments[i], 10, 0) + if err != nil { + return 0, err + } + elapsedDurations = append(elapsedDurations, time.Duration(p)) + } + + var elapsed = elapsedDurations[0] * time.Second + if len(elapsedDurations) > 1 { + elapsed += elapsedDurations[1] * time.Minute + } + if len(elapsedDurations) > 2 { + elapsed += elapsedDurations[2] * time.Hour + } + if len(elapsedDurations) > 3 { + elapsed += elapsedDurations[3] * time.Hour * 24 + } + + start := time.Now().Add(-elapsed) + return start.Unix() * 1000, nil +} + +// times retrieves ceate time from ps output utime and stime fields +// it is a c&p of gopsutil process.TimesWithContext +func times(utime string, stime string) (*cpu.TimesStat, error) { + uCpuTimes, err := convertCPUTimes(utime) + if err != nil { + return nil, err + } + sCpuTimes, err := convertCPUTimes(stime) + if err != nil { + return nil, err + } + + ret := &cpu.TimesStat{ + CPU: "cpu", + User: uCpuTimes, + System: sCpuTimes, + } + return ret, nil +} + +// convertCPUTimes converts ps format cputime to time units that are in USER_HZ or Jiffies +// it is a c&p of gopsutil process.convertCPUTimes +func convertCPUTimes(s string) (ret float64, err error) { + var t int + var _tmp string + if strings.Contains(s, ":") { + _t := strings.Split(s, ":") + hour, err := strconv.Atoi(_t[0]) + if err != nil { + return ret, err + } + t += hour * 60 * 100 + _tmp = _t[1] + } else { + _tmp = s + } + + _t := strings.Split(_tmp, ".") + if err != nil { + return ret, err + } + h, _ := strconv.Atoi(_t[0]) + t += h * 100 + h, _ = strconv.Atoi(_t[1]) + t += h + return float64(t) / ClockTicks, nil +} + +// psItem stores the information of a process and implements process.Process +type psItem struct { + pid int32 + ppid int32 + numThreads int32 + username string + state []string + command string + cmdLine string + utime string + stime string + etime string + rss int64 + vsize int64 + pagein int64 +} + +func (p *psItem) Username() (string, error) { + return p.username, nil +} + +func (p *psItem) Name() (string, error) { + return p.command, nil +} + +func (p *psItem) Cmdline() (string, error) { + return p.cmdLine, nil +} + +func (p *psItem) ProcessId() int32 { + return p.pid +} + +func (p *psItem) Parent() (Process, error) { + return &psItem{pid: p.ppid}, nil +} + +func (p *psItem) NumThreads() (int32, error) { + return p.numThreads, nil +} + +func (p *psItem) Status() ([]string, error) { + return p.state, nil +} + +func (p *psItem) MemoryInfo() (*process.MemoryInfoStat, error) { + return &process.MemoryInfoStat{ + RSS: uint64(p.rss) * 1024, + VMS: uint64(p.vsize) * 1024, + Swap: uint64(p.pagein), + }, nil +} + +// CPUPercent returns how many percent of the CPU time this process uses +// it is a c&p of gopsutil process.CPUPercent +func (p *psItem) CPUPercent() (float64, error) { + crt_time, err := createTime(p.etime) + if err != nil { + return 0, err + } + + cput, err := p.Times() + if err != nil { + return 0, err + } + + created := time.Unix(0, crt_time*int64(time.Millisecond)) + totalTime := time.Since(created).Seconds() + if totalTime <= 0 { + return 0, nil + } + + return 100 * cput.Total() / totalTime, nil +} + +func (p *psItem) Times() (*cpu.TimesStat, error) { + return times(p.utime, p.stime) +} + +// cache in-memory cache not to call ps for every process +type cache struct { + ttl time.Duration + sync.Mutex + items map[int32]psItem + createdAt time.Time +} + +func (c *cache) expired() bool { + return c == nil || c.createdAt.IsZero() || time.Since(c.createdAt) > c.ttl +} + +func (c *cache) update(items map[int32]psItem) { + c.items = items + c.createdAt = time.Now() +} diff --git a/pkg/internal/infraolly/process/process_retriever_cached_darwin_test.go b/pkg/internal/infraolly/process/process_retriever_cached_darwin_test.go new file mode 100644 index 000000000..503b3998f --- /dev/null +++ b/pkg/internal/infraolly/process/process_retriever_cached_darwin_test.go @@ -0,0 +1,4919 @@ +// Copyright New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package process + +import ( + "errors" + "github.com/shirou/gopsutil/v3/cpu" + process2 "github.com/shirou/gopsutil/v3/process" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "math" + "testing" + "time" +) + +var psOut = []string{ + `PID PPID USER STAT UTIME STIME ELAPSED RSS VSZ PAGEIN COMMAND + 1 0 root Ss 3:56.38 18:41.21 07-21:03:49 12000 4481064 0 launchd + 68 1 joe S 0:20.99 0:38.18 07-21:03:41 920 4471000 0 Google Chrome + 73 1 root Ss 2:06.17 4:13.62 07-21:03:41 3108 4477816 0 fseventsd + 74 48 pam Ss 0:00.02 0:00.09 07-21:03:41 64 4322064 0 systemstats`, + + `PID PPID USER STAT UTIME STIME ELAPSED RSS VSZ PAGEIN COMMAND + 1 0 root Ss 3:58.38 18:51.21 07-21:04:49 12200 4482064 0 launchd + 68 1 joe Ss 0:23.99 0:48.18 07-21:04:41 910 4473000 0 Google Chrome + 74 48 pam Ss 0:00.10 0:20.09 07-21:04:41 84 4324064 0 systemstats`, +} + +var psCmdOut = []string{ + `PID COMMAND + 1 /sbin/launchd + 68 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/94.0.4606.61/Helpers/chrome_crashpad_handler --monitor-self-annotation=ptype=crashpad-handler --url=https://clients2.google.com/cr/report --annotation=channel= --annotation=plat=OS X --annotation=prod=Chrome_Mac --annotation=ver=94.0.4606.61 --handshake-fd=6 + 73 /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/Support/fseventsd + 74 /usr/sbin/systemstats --daemon`, + + `PID COMMAND + 1 /sbin/launchd + 68 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/94.0.4606.61/Helpers/chrome_crashpad_handler --monitor-self-annotation=ptype=crashpad-handler --url=https://clients2.google.com/cr/report --annotation=channel= --annotation=plat=OS X --annotation=prod=Chrome_Mac --annotation=ver=94.0.4606.61 --handshake-fd=6 + 74 /usr/sbin/systemstats --daemon`, +} + +var psThreadsOut = []string{ + `USER PID TT %CPU STAT PRI STIME UTIME COMMAND +root 1 ?? 0.0 S 31T 0:00.36 0:00.08 launchd + 1 0.0 S 20T 0:00.12 0:00.01 + 1 0.0 S 37T 0:00.00 0:00.00 + 1 0.0 S 37T 0:00.00 0:00.00 +joe 68 ?? 0.0 S 4T 0:01.13 0:00.30 syslogd + 68 0.0 S 4T 0:00.00 0:00.00 +root 73 ?? 0.0 S 4T 0:01.13 0:00.30 fseventsd +pam 74 0.0 S 4T 0:00.00 0:00.00 systemstats`, + + `USER PID TT %CPU STAT PRI STIME UTIME COMMAND +root 1 ?? 0.0 S 31T 0:00.36 0:00.08 launchd + 1 0.0 S 20T 0:00.12 0:00.01 + 1 0.0 S 37T 0:00.00 0:00.00 + 1 0.0 S 37T 0:00.00 0:00.00 +joe 68 ?? 0.0 S 4T 0:01.13 0:00.30 syslogd + 68 0.0 S 4T 0:00.00 0:00.00 +pam 74 0.0 S 4T 0:00.00 0:00.00 systemstats`, +} + +// commandRunnerMock mocks CommandRunner and so we can mock ps results +type commandRunnerMock struct { + mock.Mock +} + +func (c *commandRunnerMock) run(command string, stdin string, arguments ...string) (string, error) { + args := c.Called(command, stdin, arguments) + + return args.String(0), args.Error(1) +} + +func (c *commandRunnerMock) ShouldRunCommand(command string, stdin string, arguments []string, output string, err error) { + c. + On("run", command, stdin, arguments). + Once(). + Return(output, err) +} + +func (c *commandRunnerMock) ShouldRunCommandMultipleTimes(command string, stdin string, arguments []string, output string, err error) { + c. + On("run", command, stdin, arguments). + Return(output, err) +} + +func Test_ProcessRetrieverCached_InvalidPsOutputShouldNotBreakTheInternet(t *testing.T) { + + tests := []struct { + name string + psOut string + psThreadsOut string + psCmdOut string + }{ + { + name: "empty content in ps", + psOut: "", + psThreadsOut: psThreadsOut[0], + psCmdOut: psCmdOut[0], + }, + { + name: "empty content in ps threads", + psOut: psOut[0], + psThreadsOut: "", + psCmdOut: psCmdOut[0], + }, + { + name: "empty content in ps cmd", + psOut: psOut[0], + psThreadsOut: psThreadsOut[0], + psCmdOut: "", + }, + { + name: "some invalid data", + psOut: "some invalid data\nin\nmultiple lines", + }, + { + name: "some missing columns", + psOut: `PID PPID USER STAT RSS VSZ PAGEIN COMMAND + 1 0 root Ss 12000 4481064 0 /sbin/launchd + 68 1 joe S 920 4471000 0 /usr/sbin/syslogd + 73 1 root Ss 3108 4477816 0 /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/Support/fseventsd + 74 48 pam Ss 64 4322064 0 /usr/sbin/systemstats --daemon`, + }, + } + + ttl := time.Second * 0 + ret := NewProcessRetrieverCached(ttl) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmdRunMock := &commandRunnerMock{} + commandRunner = cmdRunMock.run + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, tt.psThreadsOut, nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, tt.psCmdOut, nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, tt.psOut, nil) + _, err := ret.ProcessById(999) + assert.EqualError(t, err, "cannot find process with pid 999") + //mocked objects assertions + mock.AssertExpectationsForObjects(t, cmdRunMock) + }) + } +} + +func Test_ProcessRetrieverCached_ProcessById_PsErrorOnThreads(t *testing.T) { + expectedError := errors.New("this is an error") + cmdRunMock := &commandRunnerMock{} + commandRunner = cmdRunMock.run + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], expectedError) + + ttl := time.Second * 0 + ret := NewProcessRetrieverCached(ttl) + _, err := ret.ProcessById(68) + assert.Equal(t, expectedError, err) + + //mocked objects assertions + mock.AssertExpectationsForObjects(t, cmdRunMock) +} + +func Test_ProcessRetrieverCached_ProcessById_PsErrorOnPsInfo(t *testing.T) { + expectedError := errors.New("this is an error") + cmdRunMock := &commandRunnerMock{} + commandRunner = cmdRunMock.run + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[0], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], expectedError) + + ttl := time.Second * 0 + ret := NewProcessRetrieverCached(ttl) + _, err := ret.ProcessById(68) + assert.Equal(t, expectedError, err) + + //mocked objects assertions + mock.AssertExpectationsForObjects(t, cmdRunMock) +} + +func Test_ProcessRetrieverCached_ProcessById_NonExistingProcess(t *testing.T) { + cmdRunMock := &commandRunnerMock{} + commandRunner = cmdRunMock.run + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[0], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], nil) + + ttl := time.Second * 0 + ret := NewProcessRetrieverCached(ttl) + _, err := ret.ProcessById(99999999) + assert.EqualError(t, err, "cannot find process with pid 99999999") + + //mocked objects assertions + mock.AssertExpectationsForObjects(t, cmdRunMock) +} + +func Test_ProcessRetrieverCached_ProcessById_ExistingProcess(t *testing.T) { + cmdRunMock := &commandRunnerMock{} + commandRunner = cmdRunMock.run + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[0], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], nil) + + ttl := time.Second * 10 + ret := NewProcessRetrieverCached(ttl) + process, err := ret.ProcessById(68) + assert.Nil(t, err) + assert.Equal(t, int32(68), process.ProcessId()) + assert.Equal(t, "Google Chrome", noError(process.Name())) + assert.Equal(t, "/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/94.0.4606.61/Helpers/chrome_crashpad_handler --monitor-self-annotation=ptype=crashpad-handler --url=https://clients2.google.com/cr/report --annotation=channel= --annotation=plat=OS X --annotation=prod=Chrome_Mac --annotation=ver=94.0.4606.61 --handshake-fd=6", + noError(process.Cmdline())) + assert.Equal(t, "joe", noError(process.Username())) + assert.Equal(t, int32(1), noError(process.Parent()).(Process).ProcessId()) + assert.Equal(t, []string{process2.Sleep}, noError(process.Status())) + assert.Equal(t, &cpu.TimesStat{CPU: "cpu", User: 20.99, System: 38.18}, noError(process.Times())) + assert.Equal(t, &process2.MemoryInfoStat{RSS: uint64(920) * 1024, VMS: uint64(4471000) * 1024, Swap: uint64(0)}, noError(process.MemoryInfo())) + assert.Equal(t, int32(2), noError(process.NumThreads())) + assert.Equal(t, 0.00869, math.Round(noError(process.CPUPercent()).(float64)*100000)/100000) + + //mocked objects assertions + mock.AssertExpectationsForObjects(t, cmdRunMock) +} + +func Test_ProcessRetrieverCached_processesFromCache_reuseCacheIfTtlNotExpired(t *testing.T) { + + cmdRunMock := &commandRunnerMock{} + commandRunner = cmdRunMock.run + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[0], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], nil) + + ttl := time.Second * 10 + ret := NewProcessRetrieverCached(ttl) + itemsFirstCall, err := ret.processesFromCache() + assert.Nil(t, err) + itemsSecondCall, err := ret.processesFromCache() + assert.Nil(t, err) + assert.Equal(t, itemsFirstCall, itemsSecondCall) + + //mocked objects assertions + mock.AssertExpectationsForObjects(t, cmdRunMock) +} + +func Test_ProcessRetrieverCached_processesFromCache_cleanCacheIfTtlExpired(t *testing.T) { + + cmdRunMock := &commandRunnerMock{} + commandRunner = cmdRunMock.run + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[0], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[1], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[1], nil) + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[1], nil) + + ttl := time.Second * 0 + ret := NewProcessRetrieverCached(ttl) + itemsFirstCall, err := ret.processesFromCache() + assert.Nil(t, err) + itemsSecondCall, err := ret.processesFromCache() + assert.Nil(t, err) + assert.Len(t, itemsFirstCall, 4) + assert.Len(t, itemsSecondCall, 3) + assert.Equal(t, itemsSecondCall[74].stime, "0:20.09") + + //mocked objects assertions + mock.AssertExpectationsForObjects(t, cmdRunMock) +} + +func Test_addThreadsAndCmdToPsItems(t *testing.T) { + + tests := []struct { + name string + items map[int32]psItem + processesThreads map[int32]int32 + processesCmd map[int32]string + expectedItems map[int32]psItem + }{ + { + name: "empty items", + items: map[int32]psItem{}, + processesThreads: map[int32]int32{}, + processesCmd: map[int32]string{}, + expectedItems: map[int32]psItem{}, + }, + { + name: "empty items but info in threads and cmd", + items: map[int32]psItem{}, + processesThreads: map[int32]int32{1: 12, 343: 23}, + processesCmd: map[int32]string{1: "/some/command"}, + expectedItems: map[int32]psItem{}, + }, + { + name: "non items should not change original", + items: map[int32]psItem{1: {pid: 1, command: "some_command"}, 2: {pid: 2, command: "another_command"}}, + processesThreads: map[int32]int32{1: 12, 2: 4, 5: 343}, + processesCmd: map[int32]string{1: "/bin/some_command", 5: "already_dead_command", 2: "/bin/another_command"}, + expectedItems: map[int32]psItem{ + 1: { + pid: 1, + command: "some_command", + numThreads: 12, + cmdLine: "/bin/some_command", + }, 2: { + pid: 2, + command: "another_command", + numThreads: 4, + cmdLine: "/bin/another_command", + }}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + origItems := copyItems(tt.items) + fullInfoItems := addThreadsAndCmdToPsItems(tt.items, tt.processesThreads, tt.processesCmd) + assert.Equal(t, tt.expectedItems, fullInfoItems) + assert.Equal(t, tt.items, origItems) + }) + } +} + +func copyItems(origItems map[int32]psItem) map[int32]psItem { + dest := make(map[int32]psItem) + for pid, item := range origItems { + dest[pid] = item + } + return dest +} + +func Test_ProcessRetrieverCached_retrieveProcesses(t *testing.T) { + expected := map[int32]psItem{ + + 1: { + pid: 1, + ppid: 0, + username: "root", + state: []string{process2.Sleep}, + utime: "3:56.38", + stime: "18:41.21", + etime: "07-21:03:49", + rss: 12000, + vsize: 4481064, + pagein: 0, + numThreads: 0, + command: "launchd", + cmdLine: "", + }, + 68: { + pid: 68, + ppid: 1, + username: "joe", + state: []string{process2.Sleep}, + utime: "0:20.99", + stime: "0:38.18", + etime: "07-21:03:41", + rss: 920, + vsize: 4471000, + pagein: 0, + numThreads: 0, + command: "Google Chrome", + cmdLine: "", + }, + 73: { + pid: 73, + ppid: 1, + username: "root", + state: []string{process2.Sleep}, + utime: "2:06.17", + stime: "4:13.62", + etime: "07-21:03:41", + rss: 3108, + vsize: 4477816, + pagein: 0, + numThreads: 0, + command: "fseventsd", + cmdLine: "", + }, + 74: { + pid: 74, + ppid: 48, + username: "pam", + state: []string{process2.Sleep}, + utime: "0:00.02", + stime: "0:00.09", + etime: "07-21:03:41", + rss: 64, + vsize: 4322064, + pagein: 0, + numThreads: 0, + command: "systemstats", + cmdLine: "", + }, + } + + cmdRunMock := &commandRunnerMock{} + commandRunner = cmdRunMock.run + cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], nil) + + ttl := time.Second * 10 + ret := NewProcessRetrieverCached(ttl) + psItems, err := ret.retrieveProcesses("/bin/ps") + + assert.Nil(t, err) + assert.Len(t, psItems, 4) + for pid, expectedPsItem := range expected { + assert.Equal(t, psItems[pid], expectedPsItem) + } + //mocked objects assertions + mock.AssertExpectationsForObjects(t, cmdRunMock) +} + +func benchmark_ProcessRetrieverCached_getProcessThreads(psThreadsOut string, b *testing.B) { + cmdRunMock := &commandRunnerMock{} + commandRunner = cmdRunMock.run + cmdRunMock.ShouldRunCommandMultipleTimes("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut, nil) + + ttl := time.Second * 10 + ret := NewProcessRetrieverCached(ttl) + + // run the Fib function b.N times + for n := 0; n < b.N; n++ { + ret.getProcessThreads("/bin/ps") + } +} + +func Benchmark_ProcessRetrieverCached_getProcessThreads10(b *testing.B) { + benchmark_ProcessRetrieverCached_getProcessThreads(psOutThreads10, b) +} +func Benchmark_ProcessRetrieverCached_getProcessThreads100(b *testing.B) { + benchmark_ProcessRetrieverCached_getProcessThreads(psOutThreads100, b) +} +func Benchmark_ProcessRetrieverCached_getProcessThreads1000(b *testing.B) { + benchmark_ProcessRetrieverCached_getProcessThreads(psOutThreads500, b) +} + +func benchmark_ProcessRetrieverCached_retrieveProcesses(psOut string, b *testing.B) { + cmdRunMock := &commandRunnerMock{} + commandRunner = cmdRunMock.run + cmdRunMock.ShouldRunCommandMultipleTimes("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut, nil) + + ttl := time.Second * 0 + ret := NewProcessRetrieverCached(ttl) + + // run the Fib function b.N times + for n := 0; n < b.N; n++ { + ret.retrieveProcesses("/bin/ps") + } +} + +func Benchmark_ProcessRetrieverCached_retrieveProcesses10(b *testing.B) { + benchmark_ProcessRetrieverCached_retrieveProcesses(psOut10, b) +} +func Benchmark_ProcessRetrieverCached_retrieveProcesses100(b *testing.B) { + benchmark_ProcessRetrieverCached_retrieveProcesses(psOut100, b) +} +func Benchmark_ProcessRetrieverCached_retrieveProcesses1000(b *testing.B) { + benchmark_ProcessRetrieverCached_retrieveProcesses(psOut500, b) +} + +func noError(i interface{}, _ error) interface{} { + return i +} + +var psOut10 = `PID PPID USER STAT UTIME STIME ELAPSED RSS VSZ PAGEIN COMMAND + 1 0 root Ss 4:11.67 20:07.54 08-14:58:33 14376 4480016 0 /sbin/launchd + 68 1 root Ss 0:22.59 0:41.16 08-14:58:25 852 4471000 0 /usr/bin/some_command with some parameters + 69 1 root Ss 0:21.18 0:38.61 08-14:58:25 4416 4503784 0 /usr/bin/some_command with some parameters + 72 1 root Ss 0:01.86 0:04.56 08-14:58:25 464 4403040 0 /usr/bin/some_command with some parameters + 73 1 root Ss 2:15.80 4:35.94 08-14:58:25 2976 4477292 0 /usr/bin/some_command with some parameters + 74 1 root Ss 0:00.02 0:00.10 08-14:58:25 64 4322064 0 /usr/bin/some_command with some parameters + 75 1 root Ss 0:13.98 0:11.16 08-14:58:25 3572 4504692 0 /usr/bin/some_command with some parameters + 81 1 root Ss 0:00.02 0:00.10 08-14:58:25 72 4312320 0 /usr/bin/some_command with some parameters + 82 1 root Ss 2:38.05 1:57.49 08-14:58:25 4396 4515104 0 /usr/bin/some_command with some parameters + 84 1 root Ss 0:22.27 0:41.25 08-14:58:25 3224 4506768 0 /usr/bin/some_command with some parameters` + +var psOut100 = `PID PPID USER STAT UTIME STIME ELAPSED RSS VSZ PAGEIN COMMAND + 1 0 root Ss 4:11.67 20:07.54 08-14:58:33 14376 4480016 0 /sbin/launchd + 68 1 root Ss 0:22.59 0:41.16 08-14:58:25 852 4471000 0 /usr/bin/some_command with some parameters + 69 1 root Ss 0:21.18 0:38.61 08-14:58:25 4416 4503784 0 /usr/bin/some_command with some parameters + 72 1 root Ss 0:01.86 0:04.56 08-14:58:25 464 4403040 0 /usr/bin/some_command with some parameters + 73 1 root Ss 2:15.80 4:35.94 08-14:58:25 2976 4477292 0 /usr/bin/some_command with some parameters + 74 1 root Ss 0:00.02 0:00.10 08-14:58:25 64 4322064 0 /usr/bin/some_command with some parameters + 75 1 root Ss 0:13.98 0:11.16 08-14:58:25 3572 4504692 0 /usr/bin/some_command with some parameters + 81 1 root Ss 0:00.02 0:00.10 08-14:58:25 72 4312320 0 /usr/bin/some_command with some parameters + 82 1 root Ss 2:38.05 1:57.49 08-14:58:25 4396 4515104 0 /usr/bin/some_command with some parameters + 84 1 root Ss 0:22.27 0:41.25 08-14:58:25 3224 4506768 0 /usr/bin/some_command with some parameters + 86 1 root Ss 0:39.19 1:53.93 08-14:58:25 4808 4503136 0 /usr/bin/some_command with some parameters + 88 1 root Ss 21:00.91 24:52.86 08-14:58:25 17640 4579384 0 /usr/bin/some_command with some parameters + 91 1 root Ss 0:10.63 0:15.49 08-14:58:25 1632 4515168 0 /usr/bin/some_command with some parameters + 93 1 root Ss 2:11.10 2:51.66 08-14:58:25 10816 4586644 0 /usr/bin/some_command with some parameters + 99 1 root Ss 0:03.12 0:04.29 08-14:58:25 1584 4469432 0 /usr/bin/some_command with some parameters + 103 1 root Ss 10:21.66 15:23.52 08-14:58:25 12592 4814408 0 /usr/bin/some_command with some parameters + 104 1 root RNs 224:31.63 95:24.02 08-14:58:25 148944 10665428 0 /usr/bin/some_command with some parameters + 106 1 root Ss 0:08.78 0:09.93 08-14:58:25 2148 4522824 0 /usr/bin/some_command with some parameters + 107 1 root Ss 0:41.81 0:24.94 08-14:58:25 2188 4500416 0 /usr/bin/some_command with some parameters + 114 1 root Ss 0:00.00 0:00.03 08-14:58:25 8 4409456 0 /usr/bin/some_command with some parameters + 115 1 root Ss 2:07.05 0:56.41 08-14:58:25 7844 4509284 0 /usr/bin/some_command with some parameters + 116 1 root Ss 11:16.42 11:07.60 08-14:58:25 8596 4532768 0 /usr/bin/some_command with some parameters + 117 1 root Ss 0:11.28 0:11.58 08-14:58:25 5508 4517024 0 /usr/bin/some_command with some parameters + 118 1 root Ss 0:00.11 0:00.24 08-14:58:25 232 5017516 0 /usr/bin/some_command with some parameters + 119 1 root Ss 4:09.83 4:01.65 08-14:58:25 6028 4502240 0 /usr/bin/some_command with some parameters + 120 1 _timed Ss 0:01.47 0:07.47 08-14:58:25 1244 4500700 0 /usr/bin/some_command with some parameters + 123 1 root Ss 0:15.88 0:05.03 08-14:58:25 4320 4504936 0 /usr/bin/some_command with some parameters + 124 1 root Ss 0:00.00 0:00.05 08-14:58:25 36 4403280 0 /usr/bin/some_command with some parameters + 126 1 _locationd Ss 1:05.54 1:07.10 08-14:58:25 5500 4522344 0 /usr/bin/some_command with some parameters + 128 1 root Ss 0:00.00 0:00.05 08-14:58:25 56 4436712 0 /usr/bin/some_command with some parameters + 129 1 _displaypolicyd Ss 0:00.20 0:02.01 08-14:58:25 380 4472456 0 /usr/bin/some_command with some parameters + 132 1 root Ss 1:03.47 0:34.12 08-14:58:25 7100 4506616 0 /usr/bin/some_command with some parameters + 135 1 _distnote Ss 0:05.55 0:03.92 08-14:58:25 892 4367444 0 /usr/bin/some_command with some parameters + 139 1 root SNs 0:56.36 1:31.65 08-14:58:25 3392 4403952 0 /usr/bin/some_command with some parameters + 140 1 root Ss 0:00.09 0:00.46 08-14:58:25 1128 4468904 0 /usr/bin/some_command with some parameters + 141 1 root Ss 0:00.18 0:01.91 08-14:58:25 1268 4502020 0 /usr/bin/some_command with some parameters + 142 1 root Ss 0:00.00 0:00.03 08-14:58:25 80 4411744 0 /usr/bin/some_command with some parameters + 144 1 root Ss 0:23.44 0:34.43 08-14:58:25 4004 4504992 0 /usr/bin/some_command with some parameters + 145 1 root Ss 0:33.93 1:17.70 08-14:58:25 1984 4469284 0 /usr/bin/some_command with some parameters + 147 1 root Ss 0:41.57 0:49.94 08-14:58:25 3380 4501864 0 /usr/bin/some_command with some parameters + 148 1 root Ss 0:01.39 0:03.99 08-14:58:25 1356 4502052 0 /usr/bin/some_command with some parameters + 151 1 root Ss 4:33.07 1:27.82 08-14:58:25 8772 4544432 0 /usr/bin/some_command with some parameters + 152 1 root Ss 0:49.94 1:25.03 08-14:58:25 2808 4469628 0 /usr/bin/some_command with some parameters + 153 1 root Ss 0:02.71 0:05.10 08-14:58:25 1540 4517692 0 /usr/bin/some_command with some parameters + 156 1 _analyticsd Ss 0:08.73 0:11.37 08-14:58:25 4536 4511792 0 /usr/bin/some_command with some parameters + 191 1 root Ss 1:12.18 0:45.09 08-14:58:25 7972 4508044 0 /usr/bin/some_command with some parameters + 195 1 root Ss 0:16.43 0:14.18 08-14:58:24 4872 4503448 0 /usr/bin/some_command with some parameters + 199 1 root S 0:00.32 0:00.52 08-14:58:24 92 4367444 0 /usr/bin/some_command with some parameters + 206 1 root Ss 0:19.05 0:15.72 08-14:58:24 6592 4511260 0 /usr/bin/some_command with some parameters + 208 1 _trustd Ss 3:19.93 0:39.78 08-14:58:24 6948 4518924 0 /usr/bin/some_command with some parameters + 215 1 _networkd Ss 0:56.22 2:25.35 08-14:58:24 5692 4512388 0 /usr/bin/some_command with some parameters + 232 1 _mdnsresponder Ss 0:50.06 1:34.61 08-14:58:22 5168 4515992 0 /usr/bin/some_command with some parameters + 248 1 root Ss 0:03.37 0:05.61 08-14:58:22 288 4501552 0 /usr/bin/some_command with some parameters + 250 1 root Ss 0:00.18 0:01.24 08-14:58:22 896 4506016 0 /usr/bin/some_command with some parameters + 252 1 root Ss 0:00.00 0:00.01 08-14:58:22 12 4400400 0 /usr/bin/some_command with some parameters + 254 1 root Ss 0:00.14 0:00.62 08-14:58:22 548 4468908 0 /usr/bin/some_command with some parameters + 255 1 root Ss 3:23.68 2:37.34 08-14:58:22 9636 4514220 0 /usr/bin/some_command with some parameters + 256 1 _coreaudiod Ss 19:18.84 12:29.64 08-14:58:22 6700 4531492 0 /usr/bin/some_command with some parameters + 257 1 _nsurlsessiond Ss 0:06.68 0:19.85 08-14:58:22 4332 4521772 0 /usr/bin/some_command with some parameters + 263 1 root Ss 0:02.09 0:07.21 08-14:58:22 1436 4503404 0 /usr/bin/some_command with some parameters + 264 1 _cmiodalassistants Ss 5:01.51 2:35.11 08-14:58:22 8700 4556188 0 /usr/bin/some_command with some parameters + 269 1 root Ss 1:39.02 1:56.92 08-14:58:22 2132 4502064 0 /usr/bin/some_command with some parameters + 271 1 _coreaudiod S 0:00.24 0:00.52 08-14:58:22 92 4367408 0 /usr/bin/some_command with some parameters + 272 1 root Ss 0:00.14 0:01.46 08-14:58:22 456 4501676 0 /usr/bin/some_command with some parameters + 279 1 _locationd S 0:00.29 0:00.57 08-14:58:22 96 4367444 0 /usr/bin/some_command with some parameters + 300 1 root Ss 0:01.46 0:05.79 08-14:58:21 2236 4514480 0 /usr/bin/some_command with some parameters + 307 1 _softwareupdate S 0:00.24 0:00.55 08-14:58:21 92 4367408 0 /usr/bin/some_command with some parameters + 313 1 root Ss 0:00.64 0:01.36 08-14:58:21 128 4524496 0 /usr/bin/some_command with some parameters + 322 1 root Ss 0:01.54 0:04.68 08-14:58:21 5552 4516592 0 /usr/bin/some_command with some parameters + 337 1 root Ss 0:00.24 0:01.86 08-14:58:19 428 4501512 0 /usr/bin/some_command with some parameters + 397 1 root Ss 102:37.16 33:46.59 08-14:58:17 135092 4975516 0 /usr/bin/some_command with some parameters + 398 1 _nsurlsessiond S 0:00.23 0:00.54 08-14:58:16 92 4367408 0 /usr/bin/some_command with some parameters + 419 1 root Ss 37:27.33 9:27.48 08-14:58:10 61484 8032180 0 /usr/bin/some_command with some parameters + 422 1 _driverkit Ss 0:01.03 0:02.65 08-14:58:10 708 4810472 0 /usr/bin/some_command with some parameters + 423 1 _driverkit Ss 0:00.33 0:01.21 08-14:58:10 676 4810472 0 /usr/bin/some_command with some parameters + 425 1 _driverkit Ss 0:00.00 0:00.03 08-14:58:10 8 4801256 0 /usr/bin/some_command with some parameters + 427 1 _driverkit Ss 0:21.25 0:48.49 08-14:58:10 996 4810488 0 /usr/bin/some_command with some parameters + 428 1 _driverkit Ss 0:00.00 0:00.02 08-14:58:10 8 4801256 0 /usr/bin/some_command with some parameters + 430 1 _driverkit Ss 0:00.02 0:00.08 08-14:58:10 8 4808424 0 /usr/bin/some_command with some parameters + 432 1 _driverkit Ss 0:00.31 0:00.95 08-14:58:10 676 4810472 0 /usr/bin/some_command with some parameters + 434 1 _driverkit Ss 0:00.02 0:00.04 08-14:58:10 20 4816640 0 /usr/bin/some_command with some parameters + 435 1 _driverkit Ss 0:00.00 0:00.00 08-14:58:10 8 4800252 0 /usr/bin/some_command with some parameters + 437 1 _spotlight S 0:00.43 0:00.65 08-14:58:09 256 4367444 0 /usr/bin/some_command with some parameters + 460 1 root Ss 0:00.13 0:00.95 08-14:58:06 196 4384904 0 /usr/bin/some_command with some parameters + 474 1 _windowserver S 0:00.22 0:00.52 08-14:57:56 92 4367408 0 /usr/bin/some_command with some parameters + 481 1 _appinstalld S 0:00.22 0:00.50 08-14:57:54 92 4367408 0 /usr/bin/some_command with some parameters + 492 1 root Ss 2:58.80 4:36.41 08-14:57:50 9684 4585224 0 /usr/bin/some_command with some parameters + 501 1 _appleevents Ss 0:02.48 0:03.54 08-14:57:47 2896 4501716 0 /usr/bin/some_command with some parameters + 503 1 root Ss 0:00.01 0:00.05 08-14:57:47 148 4436644 0 /usr/bin/some_command with some parameters + 508 1 root Ss 69:22.80 284:56.76 08-14:57:47 29604 4491692 0 /usr/bin/some_command with some parameters + 515 82 root S 0:00.43 0:02.99 08-14:57:41 764 4502752 0 /usr/bin/some_command with some parameters + 528 1 root Ss 0:00.15 0:01.08 08-14:57:39 1104 4502164 0 /usr/bin/some_command with some parameters + 541 1 _appleevents S 0:00.21 0:00.50 08-14:57:36 92 4367408 0 /usr/bin/some_command with some parameters + 555 1 root Ss 0:00.89 0:02.12 08-14:57:33 2816 4501864 0 /usr/bin/some_command with some parameters + 558 1 someuser S 0:22.12 0:10.91 08-14:57:32 2344 4368112 0 /usr/bin/some_command with some parameters + 583 1 root Ss 0:00.29 0:00.83 08-14:57:31 864 4500984 0 /usr/bin/some_command with some parameters + 631 1 root Ss 0:00.04 0:00.02 08-14:57:28 32 4418196 0 /usr/bin/some_command with some parameters + 638 1 someuser S 0:49.92 1:27.51 08-14:57:28 2968 4469948 0 /usr/bin/some_command with some parameters + 673 1 someuser Ss 0:24.13 0:25.01 08-14:57:27 19868 4782072 0 /usr/bin/some_command with some parameters` + +var psOut500 = `PID PPID USER STAT UTIME STIME ELAPSED RSS VSZ PAGEIN COMMAND + 1 0 root Ss 4:11.67 20:07.54 08-14:58:33 14376 4480016 0 /sbin/launchd + 68 1 root Ss 0:22.59 0:41.16 08-14:58:25 852 4471000 0 /usr/bin/some_command with some parameters + 69 1 root Ss 0:21.18 0:38.61 08-14:58:25 4416 4503784 0 /usr/bin/some_command with some parameters + 72 1 root Ss 0:01.86 0:04.56 08-14:58:25 464 4403040 0 /usr/bin/some_command with some parameters + 73 1 root Ss 2:15.80 4:35.94 08-14:58:25 2976 4477292 0 /usr/bin/some_command with some parameters + 74 1 root Ss 0:00.02 0:00.10 08-14:58:25 64 4322064 0 /usr/bin/some_command with some parameters + 75 1 root Ss 0:13.98 0:11.16 08-14:58:25 3572 4504692 0 /usr/bin/some_command with some parameters + 81 1 root Ss 0:00.02 0:00.10 08-14:58:25 72 4312320 0 /usr/bin/some_command with some parameters + 82 1 root Ss 2:38.05 1:57.49 08-14:58:25 4396 4515104 0 /usr/bin/some_command with some parameters + 84 1 root Ss 0:22.27 0:41.25 08-14:58:25 3224 4506768 0 /usr/bin/some_command with some parameters + 86 1 root Ss 0:39.19 1:53.93 08-14:58:25 4808 4503136 0 /usr/bin/some_command with some parameters + 88 1 root Ss 21:00.91 24:52.86 08-14:58:25 17640 4579384 0 /usr/bin/some_command with some parameters + 91 1 root Ss 0:10.63 0:15.49 08-14:58:25 1632 4515168 0 /usr/bin/some_command with some parameters + 93 1 root Ss 2:11.10 2:51.66 08-14:58:25 10816 4586644 0 /usr/bin/some_command with some parameters + 99 1 root Ss 0:03.12 0:04.29 08-14:58:25 1584 4469432 0 /usr/bin/some_command with some parameters + 103 1 root Ss 10:21.66 15:23.52 08-14:58:25 12592 4814408 0 /usr/bin/some_command with some parameters + 104 1 root RNs 224:31.63 95:24.02 08-14:58:25 148944 10665428 0 /usr/bin/some_command with some parameters + 106 1 root Ss 0:08.78 0:09.93 08-14:58:25 2148 4522824 0 /usr/bin/some_command with some parameters + 107 1 root Ss 0:41.81 0:24.94 08-14:58:25 2188 4500416 0 /usr/bin/some_command with some parameters + 114 1 root Ss 0:00.00 0:00.03 08-14:58:25 8 4409456 0 /usr/bin/some_command with some parameters + 115 1 root Ss 2:07.05 0:56.41 08-14:58:25 7844 4509284 0 /usr/bin/some_command with some parameters + 116 1 root Ss 11:16.42 11:07.60 08-14:58:25 8596 4532768 0 /usr/bin/some_command with some parameters + 117 1 root Ss 0:11.28 0:11.58 08-14:58:25 5508 4517024 0 /usr/bin/some_command with some parameters + 118 1 root Ss 0:00.11 0:00.24 08-14:58:25 232 5017516 0 /usr/bin/some_command with some parameters + 119 1 root Ss 4:09.83 4:01.65 08-14:58:25 6028 4502240 0 /usr/bin/some_command with some parameters + 120 1 _timed Ss 0:01.47 0:07.47 08-14:58:25 1244 4500700 0 /usr/bin/some_command with some parameters + 123 1 root Ss 0:15.88 0:05.03 08-14:58:25 4320 4504936 0 /usr/bin/some_command with some parameters + 124 1 root Ss 0:00.00 0:00.05 08-14:58:25 36 4403280 0 /usr/bin/some_command with some parameters + 126 1 _locationd Ss 1:05.54 1:07.10 08-14:58:25 5500 4522344 0 /usr/bin/some_command with some parameters + 128 1 root Ss 0:00.00 0:00.05 08-14:58:25 56 4436712 0 /usr/bin/some_command with some parameters + 129 1 _displaypolicyd Ss 0:00.20 0:02.01 08-14:58:25 380 4472456 0 /usr/bin/some_command with some parameters + 132 1 root Ss 1:03.47 0:34.12 08-14:58:25 7100 4506616 0 /usr/bin/some_command with some parameters + 135 1 _distnote Ss 0:05.55 0:03.92 08-14:58:25 892 4367444 0 /usr/bin/some_command with some parameters + 139 1 root SNs 0:56.36 1:31.65 08-14:58:25 3392 4403952 0 /usr/bin/some_command with some parameters + 140 1 root Ss 0:00.09 0:00.46 08-14:58:25 1128 4468904 0 /usr/bin/some_command with some parameters + 141 1 root Ss 0:00.18 0:01.91 08-14:58:25 1268 4502020 0 /usr/bin/some_command with some parameters + 142 1 root Ss 0:00.00 0:00.03 08-14:58:25 80 4411744 0 /usr/bin/some_command with some parameters + 144 1 root Ss 0:23.44 0:34.43 08-14:58:25 4004 4504992 0 /usr/bin/some_command with some parameters + 145 1 root Ss 0:33.93 1:17.70 08-14:58:25 1984 4469284 0 /usr/bin/some_command with some parameters + 147 1 root Ss 0:41.57 0:49.94 08-14:58:25 3380 4501864 0 /usr/bin/some_command with some parameters + 148 1 root Ss 0:01.39 0:03.99 08-14:58:25 1356 4502052 0 /usr/bin/some_command with some parameters + 151 1 root Ss 4:33.07 1:27.82 08-14:58:25 8772 4544432 0 /usr/bin/some_command with some parameters + 152 1 root Ss 0:49.94 1:25.03 08-14:58:25 2808 4469628 0 /usr/bin/some_command with some parameters + 153 1 root Ss 0:02.71 0:05.10 08-14:58:25 1540 4517692 0 /usr/bin/some_command with some parameters + 156 1 _analyticsd Ss 0:08.73 0:11.37 08-14:58:25 4536 4511792 0 /usr/bin/some_command with some parameters + 191 1 root Ss 1:12.18 0:45.09 08-14:58:25 7972 4508044 0 /usr/bin/some_command with some parameters + 195 1 root Ss 0:16.43 0:14.18 08-14:58:24 4872 4503448 0 /usr/bin/some_command with some parameters + 199 1 root S 0:00.32 0:00.52 08-14:58:24 92 4367444 0 /usr/bin/some_command with some parameters + 206 1 root Ss 0:19.05 0:15.72 08-14:58:24 6592 4511260 0 /usr/bin/some_command with some parameters + 208 1 _trustd Ss 3:19.93 0:39.78 08-14:58:24 6948 4518924 0 /usr/bin/some_command with some parameters + 215 1 _networkd Ss 0:56.22 2:25.35 08-14:58:24 5692 4512388 0 /usr/bin/some_command with some parameters + 232 1 _mdnsresponder Ss 0:50.06 1:34.61 08-14:58:22 5168 4515992 0 /usr/bin/some_command with some parameters + 248 1 root Ss 0:03.37 0:05.61 08-14:58:22 288 4501552 0 /usr/bin/some_command with some parameters + 250 1 root Ss 0:00.18 0:01.24 08-14:58:22 896 4506016 0 /usr/bin/some_command with some parameters + 252 1 root Ss 0:00.00 0:00.01 08-14:58:22 12 4400400 0 /usr/bin/some_command with some parameters + 254 1 root Ss 0:00.14 0:00.62 08-14:58:22 548 4468908 0 /usr/bin/some_command with some parameters + 255 1 root Ss 3:23.68 2:37.34 08-14:58:22 9636 4514220 0 /usr/bin/some_command with some parameters + 256 1 _coreaudiod Ss 19:18.84 12:29.64 08-14:58:22 6700 4531492 0 /usr/bin/some_command with some parameters + 257 1 _nsurlsessiond Ss 0:06.68 0:19.85 08-14:58:22 4332 4521772 0 /usr/bin/some_command with some parameters + 263 1 root Ss 0:02.09 0:07.21 08-14:58:22 1436 4503404 0 /usr/bin/some_command with some parameters + 264 1 _cmiodalassistants Ss 5:01.51 2:35.11 08-14:58:22 8700 4556188 0 /usr/bin/some_command with some parameters + 269 1 root Ss 1:39.02 1:56.92 08-14:58:22 2132 4502064 0 /usr/bin/some_command with some parameters + 271 1 _coreaudiod S 0:00.24 0:00.52 08-14:58:22 92 4367408 0 /usr/bin/some_command with some parameters + 272 1 root Ss 0:00.14 0:01.46 08-14:58:22 456 4501676 0 /usr/bin/some_command with some parameters + 279 1 _locationd S 0:00.29 0:00.57 08-14:58:22 96 4367444 0 /usr/bin/some_command with some parameters + 300 1 root Ss 0:01.46 0:05.79 08-14:58:21 2236 4514480 0 /usr/bin/some_command with some parameters + 307 1 _softwareupdate S 0:00.24 0:00.55 08-14:58:21 92 4367408 0 /usr/bin/some_command with some parameters + 313 1 root Ss 0:00.64 0:01.36 08-14:58:21 128 4524496 0 /usr/bin/some_command with some parameters + 322 1 root Ss 0:01.54 0:04.68 08-14:58:21 5552 4516592 0 /usr/bin/some_command with some parameters + 337 1 root Ss 0:00.24 0:01.86 08-14:58:19 428 4501512 0 /usr/bin/some_command with some parameters + 397 1 root Ss 102:37.16 33:46.59 08-14:58:17 135092 4975516 0 /usr/bin/some_command with some parameters + 398 1 _nsurlsessiond S 0:00.23 0:00.54 08-14:58:16 92 4367408 0 /usr/bin/some_command with some parameters + 419 1 root Ss 37:27.33 9:27.48 08-14:58:10 61484 8032180 0 /usr/bin/some_command with some parameters + 422 1 _driverkit Ss 0:01.03 0:02.65 08-14:58:10 708 4810472 0 /usr/bin/some_command with some parameters + 423 1 _driverkit Ss 0:00.33 0:01.21 08-14:58:10 676 4810472 0 /usr/bin/some_command with some parameters + 425 1 _driverkit Ss 0:00.00 0:00.03 08-14:58:10 8 4801256 0 /usr/bin/some_command with some parameters + 427 1 _driverkit Ss 0:21.25 0:48.49 08-14:58:10 996 4810488 0 /usr/bin/some_command with some parameters + 428 1 _driverkit Ss 0:00.00 0:00.02 08-14:58:10 8 4801256 0 /usr/bin/some_command with some parameters + 430 1 _driverkit Ss 0:00.02 0:00.08 08-14:58:10 8 4808424 0 /usr/bin/some_command with some parameters + 432 1 _driverkit Ss 0:00.31 0:00.95 08-14:58:10 676 4810472 0 /usr/bin/some_command with some parameters + 434 1 _driverkit Ss 0:00.02 0:00.04 08-14:58:10 20 4816640 0 /usr/bin/some_command with some parameters + 435 1 _driverkit Ss 0:00.00 0:00.00 08-14:58:10 8 4800252 0 /usr/bin/some_command with some parameters + 437 1 _spotlight S 0:00.43 0:00.65 08-14:58:09 256 4367444 0 /usr/bin/some_command with some parameters + 460 1 root Ss 0:00.13 0:00.95 08-14:58:06 196 4384904 0 /usr/bin/some_command with some parameters + 474 1 _windowserver S 0:00.22 0:00.52 08-14:57:56 92 4367408 0 /usr/bin/some_command with some parameters + 481 1 _appinstalld S 0:00.22 0:00.50 08-14:57:54 92 4367408 0 /usr/bin/some_command with some parameters + 492 1 root Ss 2:58.80 4:36.41 08-14:57:50 9684 4585224 0 /usr/bin/some_command with some parameters + 501 1 _appleevents Ss 0:02.48 0:03.54 08-14:57:47 2896 4501716 0 /usr/bin/some_command with some parameters + 503 1 root Ss 0:00.01 0:00.05 08-14:57:47 148 4436644 0 /usr/bin/some_command with some parameters + 508 1 root Ss 69:22.80 284:56.76 08-14:57:47 29604 4491692 0 /usr/bin/some_command with some parameters + 515 82 root S 0:00.43 0:02.99 08-14:57:41 764 4502752 0 /usr/bin/some_command with some parameters + 528 1 root Ss 0:00.15 0:01.08 08-14:57:39 1104 4502164 0 /usr/bin/some_command with some parameters + 541 1 _appleevents S 0:00.21 0:00.50 08-14:57:36 92 4367408 0 /usr/bin/some_command with some parameters + 555 1 root Ss 0:00.89 0:02.12 08-14:57:33 2816 4501864 0 /usr/bin/some_command with some parameters + 558 1 someuser S 0:22.12 0:10.91 08-14:57:32 2344 4368112 0 /usr/bin/some_command with some parameters + 583 1 root Ss 0:00.29 0:00.83 08-14:57:31 864 4500984 0 /usr/bin/some_command with some parameters + 631 1 root Ss 0:00.04 0:00.02 08-14:57:28 32 4418196 0 /usr/bin/some_command with some parameters + 638 1 someuser S 0:49.92 1:27.51 08-14:57:28 2968 4469948 0 /usr/bin/some_command with some parameters + 673 1 someuser Ss 0:24.13 0:25.01 08-14:57:27 19868 4782072 0 /usr/bin/some_command with some parameters + 677 1 _windowserver Ss 273:54.03 183:35.46 08-14:57:27 84708 11097548 0 /usr/bin/some_command with some parameters + 735 1 _securityagent S 0:00.20 0:00.48 08-14:57:23 92 4367408 0 /usr/bin/some_command with some parameters + 762 1 root Ss 0:00.07 0:00.24 08-14:57:22 344 4468988 0 /usr/bin/some_command with some parameters + 860 1 root Ss 0:37.02 0:09.34 08-14:57:16 1792 4501828 0 /usr/bin/some_command with some parameters + 978 2828 someuser S 0:03.61 0:04.06 01-00:22:51 19400 30027412 0 /usr/bin/some_command with some parameters + 2054 1 someuser S 0:21.49 0:26.70 08-14:56:26 5044 4508552 0 /usr/bin/some_command with some parameters + 2059 1 someuser S 1:38.52 0:24.47 08-14:56:25 6868 4522040 0 /usr/bin/some_command with some parameters + 2142 1 _appstore S 0:00.20 0:00.47 08-14:55:24 92 4367408 0 /usr/bin/some_command with some parameters + 2155 1 _assetcache S 0:00.20 0:00.46 08-14:55:20 92 4367408 0 /usr/bin/some_command with some parameters + 2156 1 someuser SN 0:13.00 0:20.89 08-14:55:07 3424 4508336 0 /usr/bin/some_command with some parameters + 2157 1 _spotlight SN 0:12.93 0:20.55 08-14:55:07 2916 4509148 0 /usr/bin/some_command with some parameters + 2165 1 _spotlight S 0:00.06 0:00.14 08-14:55:03 200 4453292 0 /usr/bin/some_command with some parameters + 2316 1 someuser S 0:14.93 0:17.03 08-14:54:54 5132 4504040 0 /usr/bin/some_command with some parameters + 2324 1 someuser S 0:02.52 0:03.70 08-14:54:53 3316 4476480 0 /usr/bin/some_command with some parameters + 2325 1 someuser S 0:02.67 0:10.56 08-14:54:53 3468 4513940 0 /usr/bin/some_command with some parameters + 2328 1 someuser S 0:09.30 0:22.60 08-14:54:53 5180 4551980 0 /usr/bin/some_command with some parameters + 2329 1 someuser S 0:01.98 0:04.14 08-14:54:53 2624 4501644 0 /usr/bin/some_command with some parameters + 2330 1 someuser S 1:32.26 0:32.06 08-14:54:53 14524 5183340 0 /usr/bin/some_command with some parameters + 2331 1 someuser S 0:04.71 0:07.80 08-14:54:53 7152 4509632 0 /usr/bin/some_command with some parameters + 2332 1 someuser S 0:13.12 0:12.46 08-14:54:53 9296 4526744 0 /usr/bin/some_command with some parameters + 2334 1 someuser S 0:22.69 0:16.18 08-14:54:53 5976 4699464 0 /usr/bin/some_command with some parameters + 2348 1 someuser S 0:02.28 0:07.72 08-14:54:51 3436 4517492 0 /usr/bin/some_command with some parameters + 2349 1 someuser S 0:00.90 0:01.91 08-14:54:50 2592 4502988 0 /usr/bin/some_command with some parameters + 2350 1 someuser S 0:27.60 0:32.25 08-14:54:50 18180 5390300 0 /usr/bin/some_command with some parameters + 2361 1 someuser S 0:00.02 0:00.13 08-14:54:47 188 4429328 0 /usr/bin/some_command with some parameters + 2363 1 someuser S 0:04.66 0:09.83 08-14:54:44 6200 4520040 0 /usr/bin/some_command with some parameters + 2364 1 someuser S 0:42.31 0:18.23 08-14:54:44 1880 4502788 0 /usr/bin/some_command with some parameters + 2367 1 someuser S 0:00.27 0:01.99 08-14:54:44 748 4502224 0 /usr/bin/some_command with some parameters + 2369 1 someuser Ss 0:09.77 0:13.68 08-14:54:43 5776 4982052 0 /usr/bin/some_command with some parameters + 2371 1 someuser S 0:22.72 0:23.78 08-14:54:43 4388 4502616 0 /usr/bin/some_command with some parameters + 2383 1 someuser S 0:00.30 0:00.68 08-14:54:42 128 4524688 0 /usr/bin/some_command with some parameters + 2389 1 someuser S 0:02.71 0:06.13 08-14:54:41 5080 4555908 0 /usr/bin/some_command with some parameters + 2391 1 someuser S 0:00.52 0:03.02 08-14:54:40 3400 4502792 0 /usr/bin/some_command with some parameters + 2397 1 someuser S 0:01.87 0:04.29 08-14:54:37 2140 4502668 0 /usr/bin/some_command with some parameters + 2399 1 someuser Ss 0:40.92 0:24.48 08-14:54:37 9528 5349444 0 /usr/bin/some_command with some parameters + 2402 1 root Ss 0:00.64 0:01.66 08-14:54:37 3036 4475828 0 /usr/bin/some_command with some parameters + 2411 1 someuser S 0:13.29 0:30.20 08-14:54:34 5852 4558504 0 /usr/bin/some_command with some parameters + 2412 1 someuser S 0:20.66 0:27.38 08-14:54:33 4020 4502560 0 /usr/bin/some_command with some parameters + 2414 1 someuser S 0:01.99 0:05.92 08-14:54:33 2620 4516816 0 /usr/bin/some_command with some parameters + 2417 1 someuser S 0:01.15 0:04.26 08-14:54:32 2556 4802024 0 /usr/bin/some_command with some parameters + 2420 1 someuser S 0:15.13 0:16.16 08-14:54:30 3516 4503164 0 /usr/bin/some_command with some parameters + 2421 1 someuser S 0:00.68 0:01.09 08-14:54:30 524 4468844 0 /usr/bin/some_command with some parameters + 2425 1 someuser S 0:21.08 0:36.98 08-14:54:30 5288 4513816 0 /usr/bin/some_command with some parameters + 2430 1 someuser S 0:01.20 0:05.17 08-14:54:28 3080 4514368 0 /usr/bin/some_command with some parameters + 2441 1 someuser S 0:02.28 0:09.12 08-14:54:25 2728 4501860 0 /usr/bin/some_command with some parameters + 2448 1 someuser S 0:00.05 0:00.11 08-14:54:25 632 4463028 0 /usr/bin/some_command with some parameters + 2456 1 _reportmemoryexception S 0:00.20 0:00.44 08-14:54:20 92 4367408 0 /usr/bin/some_command with some parameters + 2458 1 root Ss 0:00.02 0:00.08 08-14:54:19 648 4468844 0 /usr/bin/some_command with some parameters + 2478 1 _applepay S 0:00.20 0:00.44 08-14:54:10 92 4367408 0 /usr/bin/some_command with some parameters + 2532 1 _fpsd Ss 0:00.49 0:01.17 08-14:52:15 88 4389624 0 /usr/bin/some_command with some parameters + 2555 1 666 S 0:00.43 0:00.53 08-14:51:26 236 4367444 0 /usr/bin/some_command with some parameters + 2556 1 newrelic S 0:00.42 0:00.52 08-14:51:25 236 4367444 0 /usr/bin/some_command with some parameters + 2730 1 newrelic SN 0:11.91 0:18.84 08-14:48:59 1856 4507356 0 /usr/bin/some_command with some parameters + 2731 1 666 SN 0:11.94 0:18.87 08-14:48:59 1856 4507352 0 /usr/bin/some_command with some parameters + 2736 1 666 S 0:21.02 0:21.51 08-14:48:58 1416 4507568 0 /usr/bin/some_command with some parameters + 2737 1 newrelic S 0:21.17 0:21.43 08-14:48:58 1416 4507572 0 /usr/bin/some_command with some parameters + 2827 1 someuser S 30:58.89 10:27.94 08-14:42:42 81748 5816616 0 /usr/bin/some_command with some parameters + 2828 1 someuser S 90:43.25 36:25.00 08-14:42:41 243936 6135992 0 /usr/bin/some_command with some parameters + 2832 1 someuser S 12:42.13 7:03.75 08-14:42:40 61980 43791692 0 /usr/bin/some_command with some parameters + 2834 1 someuser S 11:17.00 10:16.84 08-14:42:40 33144 10090544 0 /usr/bin/some_command with some parameters + 2836 1 someuser S 31:11.18 15:32.71 08-14:42:40 61296 5733652 0 /usr/bin/some_command with some parameters + 2838 1 someuser S 244:03.96 87:55.31 08-14:42:39 347524 6441364 0 /usr/bin/some_command with some parameters + 2839 1 someuser T 0:00.00 0:00.00 08-14:42:38 4 4260072 0 /usr/bin/some_command with some parameters + 2840 1 someuser S 0:06.80 0:11.98 08-14:42:37 6368 5391612 0 /usr/bin/some_command with some parameters + 2842 1 someuser S 1:45.21 1:09.08 08-14:42:37 16584 5192932 0 /usr/bin/some_command with some parameters + 2843 1 someuser S 0:35.97 0:29.42 08-14:42:36 5276 5154488 0 /usr/bin/some_command with some parameters + 2844 1 someuser S 2:16.88 1:55.23 08-14:42:36 21520 5473872 0 /usr/bin/some_command with some parameters + 2848 1 someuser S 0:18.41 0:19.18 08-14:42:36 6056 5188224 0 /usr/bin/some_command with some parameters + 2861 1 someuser S 0:00.05 0:00.37 08-14:42:35 260 4464252 0 /usr/bin/some_command with some parameters + 2872 1 someuser S 0:01.13 0:05.69 08-14:42:34 1212 4510572 0 /usr/bin/some_command with some parameters + 2882 1 root Ss 0:00.05 0:00.17 08-14:42:33 132 4428416 0 /usr/bin/some_command with some parameters + 2885 1 someuser S 0:00.05 0:00.35 08-14:42:33 248 4464252 0 /usr/bin/some_command with some parameters + 2889 1 someuser S 0:04.63 0:08.78 08-14:42:30 4540 4534984 0 /usr/bin/some_command with some parameters + 2892 2832 someuser S 42:37.65 16:27.26 08-14:42:29 29456 5027544 0 /usr/bin/some_command with some parameters + 2899 2832 someuser S 1:22.11 1:04.56 08-14:42:29 10328 4714188 0 /usr/bin/some_command with some parameters + 2913 1 someuser S 0:00.05 0:00.33 08-14:42:28 256 4472508 0 /usr/bin/some_command with some parameters + 2915 2832 someuser S 116:23.29 14:11.11 08-14:42:27 224592 47242100 0 /usr/bin/some_command with some parameters + 2924 2834 someuser S 3:57.25 1:44.85 08-14:42:27 7540 5165308 0 /usr/bin/some_command with some parameters + 2925 2834 someuser S 0:34.10 0:28.53 08-14:42:27 8392 4703840 0 /usr/bin/some_command with some parameters + 2928 1 someuser S 0:00.14 0:01.05 08-14:42:27 708 4669756 0 /usr/bin/some_command with some parameters + 2930 1 someuser S 0:00.93 0:01.42 08-14:42:25 1736 4501280 0 /usr/bin/some_command with some parameters + 2948 1 root Ss 0:04.21 0:10.50 08-14:42:24 1664 4501036 0 /usr/bin/some_command with some parameters + 2949 2827 someuser S 0:00.01 0:00.02 08-14:42:24 8 4311692 0 /usr/bin/some_command with some parameters + 2984 2828 someuser S 58:50.54 27:06.48 08-14:42:20 76684 7939928 0 /usr/bin/some_command with some parameters + 2986 2828 someuser S 29:43.51 13:05.10 08-14:42:20 42148 4812112 0 /usr/bin/some_command with some parameters + 2991 2828 someuser S 1:00.32 0:37.67 08-14:42:19 5548 4747448 0 /usr/bin/some_command with some parameters + 2997 2828 someuser S 2:23.76 0:43.92 08-14:42:18 32048 29994144 0 /usr/bin/some_command with some parameters + 2998 2828 someuser S 0:05.01 0:05.43 08-14:42:18 4672 29984524 0 /usr/bin/some_command with some parameters + 2999 2828 someuser S 3:23.90 1:52.55 08-14:42:18 36272 30024356 0 /usr/bin/some_command with some parameters + 3016 1 someuser S 0:00.01 0:00.12 08-14:42:17 52 4469476 0 /usr/bin/some_command with some parameters + 3033 2836 someuser S 8:11.46 2:45.88 08-14:42:12 7912 4855280 0 /usr/bin/some_command with some parameters + 3059 1 someuser Ss 0:04.76 0:06.36 08-14:42:08 552 4470796 0 /usr/bin/some_command with some parameters + 3062 1 someuser S 0:01.74 0:05.01 08-14:42:08 1552 4515872 0 /usr/bin/some_command with some parameters + 3063 1 someuser S 1:30.60 0:37.89 08-14:42:08 7528 4506740 0 /usr/bin/some_command with some parameters + 3071 1 someuser S< 0:00.03 0:00.20 08-14:42:08 128 4501628 0 /usr/bin/some_command with some parameters + 3073 1 someuser S 17:32.46 11:44.81 08-14:42:08 25632 5352928 0 /usr/bin/some_command with some parameters + 3080 1 someuser S 0:01.60 0:05.41 08-14:42:08 3480 4968212 0 /usr/bin/some_command with some parameters + 3083 1 someuser S 0:06.43 0:09.02 08-14:42:08 2016 4500992 0 /usr/bin/some_command with some parameters + 3088 1 someuser S 3:32.35 3:59.93 08-14:42:08 16712 6625084 0 /usr/bin/some_command with some parameters + 3091 1 someuser S 0:00.08 0:00.23 08-14:42:08 580 4469020 0 /usr/bin/some_command with some parameters + 3093 2836 someuser S 0:00.51 0:01.06 08-14:42:07 1160 4719516 0 /usr/bin/some_command with some parameters + 3094 2836 someuser S 1:02.52 0:53.95 08-14:42:07 10076 4738620 0 /usr/bin/some_command with some parameters + 3095 2836 someuser S 21:28.85 5:06.94 08-14:42:07 36928 47125408 0 /usr/bin/some_command with some parameters + 3146 1 someuser S 0:31.08 0:29.05 08-14:42:01 9736 5409664 0 /usr/bin/some_command with some parameters + 3181 1 someuser S 12:19.70 10:55.35 08-14:41:57 21268 5190672 0 /usr/bin/some_command with some parameters + 3211 3073 someuser S 17:41.68 6:32.44 08-14:41:54 1456 4732408 0 /usr/bin/some_command with some parameters + 3288 3073 someuser S 2:51.70 1:11.08 08-14:41:52 6856 4686332 0 /usr/bin/some_command with some parameters + 3312 2828 someuser S 4:06.39 0:56.98 08-14:41:49 18672 34530964 0 /usr/bin/some_command with some parameters + 3337 2828 someuser S 3:42.05 1:53.09 08-14:41:48 42672 30220176 0 /usr/bin/some_command with some parameters + 3543 2834 someuser S 1:34.97 0:32.71 08-14:41:36 3084 8908360 0 /usr/bin/some_command with some parameters + 3544 2834 someuser S 1:04.96 0:30.34 08-14:41:36 9656 8995708 0 /usr/bin/some_command with some parameters + 3545 2834 someuser S 0:33.10 0:16.46 08-14:41:36 10096 8986332 0 /usr/bin/some_command with some parameters + 3564 2834 someuser S 13:31.51 3:18.03 08-14:41:35 52736 9309328 0 /usr/bin/some_command with some parameters + 3566 2834 someuser S 0:00.88 0:02.21 08-14:41:35 2744 8995380 0 /usr/bin/some_command with some parameters + 3569 2834 someuser S 17:07.58 3:54.01 08-14:41:34 89788 9460448 0 /usr/bin/some_command with some parameters + 3571 2834 someuser S 0:00.91 0:02.28 08-14:41:34 2904 8994356 0 /usr/bin/some_command with some parameters + 3623 3073 someuser S 52:17.67 11:34.11 08-14:41:18 51392 5076344 0 /usr/bin/some_command with some parameters + 3656 1 someuser S 0:00.02 0:00.14 08-14:41:01 2204 4461572 0 /usr/bin/some_command with some parameters + 3732 1 root S 7:12.99 1:10.20 08-14:40:36 1584 4476304 0 /usr/bin/some_command with some parameters + 3736 1 someuser S 4:22.99 1:24.41 08-14:40:36 2716 4512484 0 /usr/bin/some_command with some parameters + 3742 1 root Ss 7:29.21 6:04.37 08-14:40:36 3888 4494528 0 /usr/bin/some_command with some parameters + 3743 1 someuser S 0:13.27 0:13.21 08-14:40:36 4040 4733128 0 /usr/bin/some_command with some parameters + 3747 1 root Ss 8:05.21 2:59.27 08-14:40:36 3408 4483984 0 /usr/bin/some_command with some parameters + 3769 1 root Ss 1:26.36 3:15.75 08-14:40:33 788 4477924 0 /usr/bin/some_command with some parameters + 3811 1 _driverkit Ss 0:12.28 0:11.01 08-14:40:30 1120 4826876 0 /usr/bin/some_command with some parameters + 3813 1 _driverkit Ss 0:00.00 0:00.01 08-14:40:30 8 4808448 0 /usr/bin/some_command with some parameters + 3834 1 someuser S 0:05.59 0:17.21 08-14:40:26 5236 4660040 0 /usr/bin/some_command with some parameters + 3857 1 someuser S 0:00.07 0:00.37 08-14:34:54 140 4469332 0 /usr/bin/some_command with some parameters + 4074 1 root Ss 0:34.56 0:54.92 08-13:54:53 2040 4505404 0 /usr/bin/some_command with some parameters + 4168 1 someuser S 0:00.23 0:01.53 08-09:51:17 1164 4501232 0 /usr/bin/some_command with some parameters + 5222 2828 someuser S 16:02.55 4:18.96 08-04:00:15 117528 34562148 0 /usr/bin/some_command with some parameters + 5252 2828 someuser S 0:14.30 0:19.63 08-04:00:13 3400 4736224 0 /usr/bin/some_command with some parameters + 5347 1 _fpsd Ss 0:00.04 0:00.17 08-03:59:11 1036 4444676 0 /usr/bin/some_command with some parameters + 5407 1 someuser S 0:00.30 0:00.89 08-03:58:41 284 4504764 0 /usr/bin/some_command with some parameters + 6280 1 nobody S 0:00.18 0:00.40 08-03:18:55 92 4367408 0 /usr/bin/some_command with some parameters + 6305 1 someuser S 282:50.93 82:22.21 08-03:17:56 2292124 18217416 0 /usr/bin/some_command with some parameters + 6351 6305 someuser S 0:11.88 0:24.93 08-03:17:53 404 4403508 0 /usr/bin/some_command with some parameters + 6365 6305 someuser S 0:04.48 0:04.98 08-03:17:47 2524 4892876 0 /usr/bin/some_command with some parameters + 6368 6305 someuser S 0:00.73 0:02.15 08-03:17:46 1916 4730504 0 /usr/bin/some_command with some parameters + 6774 1 someuser S 1:14.28 0:54.60 08-03:15:45 11608 6030076 0 /usr/bin/some_command with some parameters + 6796 1 someuser Ss 0:01.93 0:06.99 08-03:15:42 5352 5006176 0 /usr/bin/some_command with some parameters + 6947 1 someuser Ss 0:23.99 0:26.77 08-03:12:59 14192 4400464 0 /usr/bin/some_command with some parameters + 7649 2838 someuser S 0:02.69 0:08.67 07-22:38:33 4660 4716376 0 /usr/bin/some_command with some parameters + 7651 1 someuser S 0:01.60 0:05.57 07-22:38:31 4752 5132776 0 /usr/bin/some_command with some parameters + 7961 1 someuser S 0:02.56 0:00.59 08-02:58:43 432 4371696 0 /usr/bin/some_command with some parameters + 9260 25257 someuser S 3:51.10 15:40.43 02-01:11:16 558980 5146896 0 /usr/bin/some_command with some parameters +12403 1 someuser S 0:03.22 0:07.88 08-02:12:17 4604 4767956 0 /usr/bin/some_command with some parameters +13175 1 someuser S 0:22.98 0:34.21 07-22:17:23 6344 5200676 0 /usr/bin/some_command with some parameters +13178 13175 someuser S 5:12.88 4:16.86 07-22:17:23 21612 6243308 0 /usr/bin/some_command with some parameters +13179 13175 someuser S 0:03.66 0:04.15 07-22:17:23 4196 5014868 0 /usr/bin/some_command with some parameters +13201 13178 someuser S 0:04.69 0:05.74 07-22:17:18 4668 5177360 0 /usr/bin/some_command with some parameters +13207 13201 someuser S 0:16.94 0:25.36 07-22:17:17 2108 4446856 0 /usr/bin/some_command with some parameters +13208 13201 someuser S 0:13.28 0:12.44 07-22:17:17 7412 5051112 0 /usr/bin/some_command with some parameters +13209 13201 someuser S 3:11.69 5:39.34 07-22:17:17 7780 5011776 0 /usr/bin/some_command with some parameters +13210 13201 someuser S 0:35.56 0:49.66 07-22:17:17 9224 5048820 0 /usr/bin/some_command with some parameters +13213 13207 someuser Z 0:00.00 0:00.00 07-22:17:16 0 /usr/bin/some_command with some parameters +13219 13210 someuser S 62:01.45 273:27.39 07-22:17:13 8684 6685852 0 /usr/bin/some_command with some parameters +13565 2832 someuser S 0:07.72 0:10.21 08-02:05:51 3224 4704560 0 /usr/bin/some_command with some parameters +15552 2828 someuser S 0:21.59 0:09.03 22:23:01 52380 34493004 0 /usr/bin/some_command with some parameters +20135 2828 someuser S 0:07.21 0:10.85 08-01:34:55 21200 30212992 0 /usr/bin/some_command with some parameters +22878 1 someuser S 0:03.53 0:06.54 07-16:03:13 4464 4715080 0 /usr/bin/some_command with some parameters +23677 1 root Ss 0:08.84 0:10.65 05-21:56:12 6440 4508224 0 /usr/bin/some_command with some parameters +25255 1 someuser S 0:41.36 6:19.76 07-03:55:41 352 4338244 0 /usr/bin/some_command with some parameters +25257 1 someuser S 6:47.93 10:51.33 07-03:55:41 4000 4557808 0 /usr/bin/some_command with some parameters +25320 25257 someuser S 0:03.25 0:05.20 07-03:55:33 600 4329640 0 /usr/bin/some_command with some parameters +27923 1 root SNs 0:00.08 0:00.25 01-22:35:02 1204 4469016 0 /usr/bin/some_command with some parameters +29226 1 someuser S 0:23.33 0:58.42 05-21:48:02 6600 4613240 0 /usr/bin/some_command with some parameters +29631 2828 someuser S 0:46.31 0:35.11 04-19:33:24 41232 30292244 0 /usr/bin/some_command with some parameters +29686 2828 someuser S 5:01.84 1:31.59 04-19:33:06 65136 30334312 0 /usr/bin/some_command with some parameters +29894 2828 someuser S 0:13.88 0:05.57 01-22:27:32 6896 30011808 0 /usr/bin/some_command with some parameters +31499 2828 someuser S 0:42.96 0:37.98 04-19:17:27 51460 30313196 0 /usr/bin/some_command with some parameters +31632 2828 someuser S 5:00.31 1:03.01 04-19:16:15 42316 30307640 0 /usr/bin/some_command with some parameters +32179 1 someuser Ss 0:46.91 0:33.35 07-00:25:38 9824 5290628 0 /usr/bin/some_command with some parameters +32424 2828 someuser S 0:04.34 0:02.46 01-22:11:47 9352 30013604 0 /usr/bin/some_command with some parameters +33878 1 someuser S 1:11.98 2:05.21 04-19:05:27 12044 5461036 0 /usr/bin/some_command with some parameters +33945 25257 someuser S 4:06.13 49:50.93 04-19:05:09 1084480 5695004 0 /usr/bin/some_command with some parameters +37665 1 someuser S 0:09.40 0:08.78 19:56:02 18148 4738308 0 /usr/bin/some_command with some parameters +37728 2828 someuser S 0:01.70 0:01.72 01-21:26:00 6720 29984528 0 /usr/bin/some_command with some parameters +38532 1 someuser S 0:00.18 0:00.34 19:12:29 4148 4507568 0 /usr/bin/some_command with some parameters +38747 1 root Ss 0:01.51 0:01.02 19:02:32 6952 4507708 0 /usr/bin/some_command with some parameters +40037 1 someuser S 0:11.66 0:17.17 06-19:11:03 6660 5376404 0 /usr/bin/some_command with some parameters +40686 2828 someuser S 0:05.48 0:06.76 01-21:05:59 20752 30001376 0 /usr/bin/some_command with some parameters +40698 2828 someuser S 0:10.13 0:05.07 01-21:05:52 12364 30248656 0 /usr/bin/some_command with some parameters +40707 2828 someuser S 0:01.26 0:02.23 01-21:05:49 16620 29990580 0 /usr/bin/some_command with some parameters +41159 2828 someuser S 0:01.59 0:01.43 01-21:01:44 4488 30007584 0 /usr/bin/some_command with some parameters +41458 1 root Ss 0:00.23 0:00.58 17:31:36 8844 4502512 0 /usr/bin/some_command with some parameters +41491 1 root Ss 0:01.84 0:03.01 17:31:03 2428 4462264 0 /usr/bin/some_command with some parameters +41501 1 someuser S 0:00.25 0:00.38 17:01:05 7196 4507212 0 /usr/bin/some_command with some parameters +41507 1 someuser Ss 0:02.07 0:01.29 17:01:04 39712 5001496 0 /usr/bin/some_command with some parameters +41513 1 root Ss 0:00.07 0:00.32 17:01:04 4624 4506916 0 /usr/bin/some_command with some parameters +41520 1 root Ss 0:00.10 0:00.09 17:01:03 3408 4501336 0 /usr/bin/some_command with some parameters +41747 1 someuser S 0:00.56 0:01.09 15:16:53 25600 4553952 0 /usr/bin/some_command with some parameters +41837 1 root Ss 0:03.48 0:01.11 13:14:29 9504 4508288 0 /usr/bin/some_command with some parameters +41852 1 root Ss 0:00.06 0:00.15 11:14:25 2240 4469368 0 /usr/bin/some_command with some parameters +41855 1 root Ss 0:00.12 0:00.10 11:14:23 4268 4469548 0 /usr/bin/some_command with some parameters +41869 1 someuser S 0:00.34 0:00.27 11:14:21 3032 4501416 0 /usr/bin/some_command with some parameters +41875 1 someuser S 0:00.17 0:00.15 11:14:20 7516 4501204 0 /usr/bin/some_command with some parameters +41878 1 someuser S 0:00.51 0:00.58 11:14:20 34728 4939584 0 /usr/bin/some_command with some parameters +41886 1 root Ss 0:06.23 0:00.47 11:14:12 2740 4558804 0 /usr/bin/some_command with some parameters +41890 1 root Ss 0:00.65 0:02.81 11:14:11 4040 4470284 0 /usr/bin/some_command with some parameters +41897 1 root Ss 0:00.07 0:00.17 11:14:08 10828 4442748 0 /usr/bin/some_command with some parameters +41908 1 someuser Ss 0:00.17 0:00.39 11:13:56 2316 4472152 0 /usr/bin/some_command with some parameters +41912 1 root Ss 0:00.04 0:00.20 11:13:39 3524 4469036 0 /usr/bin/some_command with some parameters +41926 1 root Ss 0:01.47 0:03.00 09:13:37 3596 4504084 0 /usr/bin/some_command with some parameters +42029 1 _netbios SNs 0:00.06 0:00.18 07:11:50 3056 4469768 0 /usr/bin/some_command with some parameters +42082 1 someuser S 0:00.38 0:00.55 05:10:33 10624 4709112 0 /usr/bin/some_command with some parameters +42094 1 _driverkit Ss 0:00.00 0:00.01 04:06:08 1240 4802280 0 /usr/bin/some_command with some parameters +42095 1 _driverkit Ss 0:00.00 0:00.01 04:06:08 1248 4803304 0 /usr/bin/some_command with some parameters +42096 1 _driverkit Ss 0:00.29 0:00.72 04:06:08 1676 4810472 0 /usr/bin/some_command with some parameters +42097 1 _driverkit Ss 0:00.01 0:00.03 04:06:08 1276 4807400 0 /usr/bin/some_command with some parameters +42098 1 _driverkit Ss 0:00.00 0:00.01 04:06:08 1236 4801256 0 /usr/bin/some_command with some parameters +42100 1 _driverkit Ss 0:00.00 0:00.01 04:06:08 1248 4826876 0 /usr/bin/some_command with some parameters +42115 1 root Ss 0:00.01 0:00.04 04:06:07 1168 4419408 0 /usr/bin/some_command with some parameters +42121 1 someuser S 0:00.71 0:00.98 04:06:07 18876 4672452 0 /usr/bin/some_command with some parameters +42139 1 someuser S 0:00.18 0:00.28 04:06:03 12144 4512888 0 /usr/bin/some_command with some parameters +42155 1 someuser S 0:00.07 0:00.17 04:06:02 5080 4635844 0 /usr/bin/some_command with some parameters +42306 1 _spotlight S 0:00.93 0:00.47 04:05:57 3272 4503812 0 /usr/bin/some_command with some parameters +42930 1 newrelic S 0:00.66 0:00.20 04:05:33 3060 4503644 0 /usr/bin/some_command with some parameters +42931 1 666 S 0:00.75 0:00.26 04:05:33 3308 4503780 0 /usr/bin/some_command with some parameters +42958 1 someuser S 0:02.07 0:07.75 06-18:12:41 4012 4560160 0 /usr/bin/some_command with some parameters +43266 1 someuser S 0:00.28 0:00.53 04:05:22 10800 5133048 0 /usr/bin/some_command with some parameters +43267 1 someuser S 0:00.31 0:00.30 04:05:22 5900 4521780 0 /usr/bin/some_command with some parameters +43686 1 someuser S 0:00.31 0:00.19 04:05:06 6176 4500888 0 /usr/bin/some_command with some parameters +43718 2828 someuser S 0:21.05 0:06.89 04:04:51 61656 29988608 0 /usr/bin/some_command with some parameters +43719 1 _gamecontrollerd Ss 0:35.23 0:34.34 04:04:50 6484 4501660 0 /usr/bin/some_command with some parameters +43720 1 _coreaudiod Ss 0:00.27 0:00.28 04:04:49 1784 4470408 0 /usr/bin/some_command with some parameters +43724 1 someuser S 0:00.35 0:00.50 04:04:40 13356 4555412 0 /usr/bin/some_command with some parameters +43725 1 someuser S 0:00.35 0:00.27 04:04:39 7656 4504552 0 /usr/bin/some_command with some parameters +43726 1 someuser S 0:00.05 0:00.10 04:04:38 4756 4469172 0 /usr/bin/some_command with some parameters +43728 2828 someuser S 0:01.16 0:00.50 04:04:36 33280 29981708 0 /usr/bin/some_command with some parameters +43729 2828 someuser S 0:00.96 0:00.56 04:04:32 39820 30201408 0 /usr/bin/some_command with some parameters +43731 1 root Ss 0:00.07 0:00.12 04:04:31 5112 4501764 0 /usr/bin/some_command with some parameters +43865 1 someuser Ss 0:00.20 0:00.26 04:01:01 11512 4513268 0 /usr/bin/some_command with some parameters +43867 1 someuser S 0:00.11 0:00.14 04:01:01 2668 4501528 0 /usr/bin/some_command with some parameters +43868 1 root Ss 4:07.09 1:17.30 04:01:01 9064 4510512 0 /usr/bin/some_command with some parameters +43869 1 someuser S 0:00.09 0:00.10 04:01:01 6516 4503240 0 /usr/bin/some_command with some parameters +43871 1 someuser S 0:00.27 0:00.39 04:01:01 9324 4510864 0 /usr/bin/some_command with some parameters +43873 1 root Ss 0:00.05 0:00.03 04:01:00 2604 4469424 0 /usr/bin/some_command with some parameters +43874 1 _fpsd Ss 0:00.02 0:00.03 04:01:00 2580 4462948 0 /usr/bin/some_command with some parameters +43880 1 root Ss 0:00.01 0:00.02 04:01:00 1172 4428420 0 /usr/bin/some_command with some parameters +43881 1 someuser S 0:00.03 0:00.04 04:01:00 5300 4501132 0 /usr/bin/some_command with some parameters +43882 1 someuser S 0:00.95 0:00.91 04:01:00 14520 5154420 0 /usr/bin/some_command with some parameters +43883 1 root Ss 0:00.10 0:00.15 04:01:00 5856 4501964 0 /usr/bin/some_command with some parameters +43889 1 someuser S 0:00.19 0:00.44 04:00:59 12500 4559048 0 /usr/bin/some_command with some parameters +43890 1 someuser S 0:00.01 0:00.04 04:00:59 3476 4469156 0 /usr/bin/some_command with some parameters +43892 1 root Ss 0:00.13 0:00.08 04:00:59 6208 4505372 0 /usr/bin/some_command with some parameters +43893 139 root SN 6:02.86 1:53.28 04:00:59 56592 4579620 0 /usr/bin/some_command with some parameters +43895 1 someuser S 0:00.42 0:01.13 04:00:59 10948 4525368 0 /usr/bin/some_command with some parameters +43896 1 someuser S 0:00.03 0:00.03 04:00:58 4056 4460904 0 /usr/bin/some_command with some parameters +43898 1 someuser S 0:00.21 0:00.57 04:00:58 10428 4517276 0 /usr/bin/some_command with some parameters +43901 1 someuser S 0:00.15 0:00.36 04:00:57 10512 4983912 0 /usr/bin/some_command with some parameters +43904 1 someuser S 0:00.28 0:00.88 04:00:57 15404 4508360 0 /usr/bin/some_command with some parameters +43907 1 someuser S 0:00.07 0:00.07 04:00:56 8780 4501212 0 /usr/bin/some_command with some parameters +43908 1 _installcoordinationd Ss 0:00.03 0:00.04 04:00:56 3548 4461340 0 /usr/bin/some_command with some parameters +43910 1 root Ss 0:00.08 0:00.05 04:00:56 2204 4469212 0 /usr/bin/some_command with some parameters +43916 1 root Ss 0:00.01 0:00.02 04:00:55 2456 4428548 0 /usr/bin/some_command with some parameters +43918 1 root Ss 0:00.17 0:00.22 04:00:51 4868 4502444 0 /usr/bin/some_command with some parameters +43936 1 someuser S 0:13.05 0:02.34 04:00:45 102644 4904648 0 /usr/bin/some_command with some parameters +43941 1 someuser S 0:00.03 0:00.06 04:00:45 6680 4505292 0 /usr/bin/some_command with some parameters +43942 1 root Ss 0:00.01 0:00.05 04:00:44 1216 4387164 0 /usr/bin/some_command with some parameters +43956 1 root Ss 0:00.11 0:00.16 04:00:12 7552 4501572 0 /usr/bin/some_command with some parameters +43957 1 root Ss 0:00.01 0:00.03 04:00:12 2224 4469360 0 /usr/bin/some_command with some parameters +43966 1 someuser S 0:00.22 0:00.41 03:59:56 9244 4502860 0 /usr/bin/some_command with some parameters +43971 1 someuser S 0:00.46 0:00.84 03:59:39 14664 4514540 0 /usr/bin/some_command with some parameters +43973 1 someuser S 0:00.07 0:00.08 03:59:38 6744 4501644 0 /usr/bin/some_command with some parameters +43974 1 someuser S 0:00.12 0:00.19 03:59:38 9636 4534772 0 /usr/bin/some_command with some parameters +43975 1 someuser S 0:00.03 0:00.11 03:59:38 2436 4501720 0 /usr/bin/some_command with some parameters +43976 1 someuser S 0:00.07 0:00.21 03:59:38 6548 4504276 0 /usr/bin/some_command with some parameters +43977 1 _assetcache Ss 0:00.04 0:00.06 03:59:38 4788 4462272 0 /usr/bin/some_command with some parameters +43978 1 root Ss 0:00.04 0:00.03 03:59:38 3144 4472376 0 /usr/bin/some_command with some parameters +43983 1 root SNs 0:00.00 0:00.01 03:59:21 396 4383348 0 /usr/bin/some_command with some parameters +43984 1 root Ss 0:00.00 0:00.01 03:59:21 1200 4418664 0 /usr/bin/some_command with some parameters +44067 1 someuser S 0:00.05 0:00.08 03:59:06 6920 5019924 0 /usr/bin/some_command with some parameters +44068 1 someuser S 0:00.38 0:00.92 03:59:06 12356 4511004 0 /usr/bin/some_command with some parameters +44070 1 someuser Ss 0:00.02 0:00.04 03:59:05 7044 4526648 0 /usr/bin/some_command with some parameters +44072 1 someuser S 0:00.38 0:00.88 03:59:05 25828 4555100 0 /usr/bin/some_command with some parameters +44073 1 someuser S 0:00.02 0:00.04 03:59:05 4544 4451212 0 /usr/bin/some_command with some parameters +44074 1 someuser S 0:00.05 0:00.05 03:59:05 6052 4486044 0 /usr/bin/some_command with some parameters +44075 1 someuser Ss 0:00.01 0:00.01 03:59:04 2360 4404832 0 /usr/bin/some_command with some parameters +44076 1 someuser S 0:00.02 0:00.04 03:59:04 6496 4489536 0 /usr/bin/some_command with some parameters +44083 1 someuser S 0:00.07 0:00.08 03:58:50 7508 4505784 0 /usr/bin/some_command with some parameters +44084 1 someuser S 0:00.01 0:00.01 03:58:50 3188 4484560 0 /usr/bin/some_command with some parameters +44085 1 someuser S 0:00.13 0:00.07 03:58:50 5084 4502116 0 /usr/bin/some_command with some parameters +44086 1 root Ss 0:00.08 0:00.18 03:58:50 4188 4475624 0 /usr/bin/some_command with some parameters +44090 1 someuser S 0:00.16 0:00.33 03:58:49 12008 4712592 0 /usr/bin/some_command with some parameters +44098 1 someuser Ss 0:34.74 0:36.48 03:58:36 4884 4544372 0 /usr/bin/some_command with some parameters +44099 1 root Ss 0:00.06 0:00.06 03:58:35 5176 4501788 0 /usr/bin/some_command with some parameters +44100 1 someuser S 0:00.24 0:00.58 03:58:35 13316 4717696 0 /usr/bin/some_command with some parameters +44101 1 root Ss 0:00.04 0:00.01 03:58:35 1204 4414572 0 /usr/bin/some_command with some parameters +44103 1 someuser S 0:00.17 0:00.38 03:58:19 12108 4984140 0 /usr/bin/some_command with some parameters +44153 1 root Ss 0:00.20 0:00.23 03:52:58 2776 4471612 0 /usr/bin/some_command with some parameters +44167 1 root Ss 0:00.33 0:00.21 03:52:37 3912 4514384 0 /usr/bin/some_command with some parameters +44185 1 someuser Ss 0:00.74 0:00.41 03:51:04 38244 4967236 0 /usr/bin/some_command with some parameters +44520 1 root Ss 0:21.43 0:01.69 03:37:59 3624 4478804 0 /usr/bin/some_command with some parameters +44805 1 someuser Ss 0:00.78 0:00.37 03:25:11 40244 5132076 0 /usr/bin/some_command with some parameters +44913 1 root Ss 0:00.02 0:00.02 03:23:40 992 4409696 0 /usr/bin/some_command with some parameters +45056 1 root Ss 0:00.01 0:00.03 03:16:33 3544 4457836 0 /usr/bin/some_command with some parameters +45060 1 root Ss 0:00.03 0:00.02 03:16:16 2480 4484908 0 /usr/bin/some_command with some parameters +45062 1 root Ss 0:00.00 0:00.01 03:16:16 1616 4428404 0 /usr/bin/some_command with some parameters +45063 1 root Ss 0:00.01 0:00.02 03:16:16 3164 4493296 0 /usr/bin/some_command with some parameters +45064 1 someuser S 0:00.03 0:00.02 03:16:16 5876 4469764 0 /usr/bin/some_command with some parameters +45065 1 someuser Ss 0:00.01 0:00.04 03:16:15 8272 4482376 0 /usr/bin/some_command with some parameters +45066 1 root Ss 0:00.00 0:00.01 03:16:15 2820 4452456 0 /usr/bin/some_command with some parameters +45067 1 root Ss 0:00.00 0:00.01 03:16:15 2868 4452456 0 /usr/bin/some_command with some parameters +45068 1 root Ss 0:00.03 0:00.02 03:16:15 2740 4493316 0 /usr/bin/some_command with some parameters +45069 1 someuser Ss 0:00.02 0:00.02 03:16:15 9216 4457000 0 /usr/bin/some_command with some parameters +45070 1 someuser Ss 0:00.08 0:00.17 03:16:14 31604 4614864 0 /usr/bin/some_command with some parameters +45071 1 someuser Ss 0:00.02 0:00.02 03:16:14 9204 4490792 0 /usr/bin/some_command with some parameters +45073 1 root Ss 0:00.01 0:00.02 03:16:05 2704 4434688 0 /usr/bin/some_command with some parameters +45096 1 _appstore Ss 0:00.20 0:00.47 03:15:29 11272 4512396 0 /usr/bin/some_command with some parameters +45097 1 someuser S 0:00.07 0:00.09 03:15:29 5152 4461828 0 /usr/bin/some_command with some parameters +45098 1 root Ss 0:00.06 0:00.03 03:15:28 4412 4502452 0 /usr/bin/some_command with some parameters +45101 1 someuser S 0:00.24 0:00.10 03:15:12 8556 4505324 0 /usr/bin/some_command with some parameters +45104 1 root Ss 0:00.01 0:00.02 03:14:56 2960 4436732 0 /usr/bin/some_command with some parameters +45105 1 root Ss 0:00.01 0:00.02 03:14:56 1996 4436764 0 /usr/bin/some_command with some parameters +45106 1 root Ss 0:00.02 0:00.03 03:14:55 4512 4448868 0 /usr/bin/some_command with some parameters +45111 1 _applepay Ss 0:00.06 0:00.11 03:14:39 4036 4465524 0 /usr/bin/some_command with some parameters +45174 1 someuser S 0:00.36 0:00.38 03:10:31 2328 4469464 0 /usr/bin/some_command with some parameters +45206 2828 someuser S 0:13.66 0:09.04 08-00:49:53 12592 29970564 0 /usr/bin/some_command with some parameters +45624 1 someuser S 0:00.01 0:00.03 02:54:41 6752 4436088 0 /usr/bin/some_command with some parameters +45782 1 someuser S 0:00.02 0:00.04 02:49:03 2348 4500936 0 /usr/bin/some_command with some parameters +45792 2828 someuser S 0:02.15 0:01.37 02:48:40 50716 29998868 0 /usr/bin/some_command with some parameters +45933 1 someuser S 0:00.11 0:00.11 02:43:47 5596 4500920 0 /usr/bin/some_command with some parameters +45982 1 _iconservices Ss 0:00.03 0:00.01 02:40:31 1884 4426912 0 /usr/bin/some_command with some parameters +46122 25257 someuser S 3:58.32 55:53.41 05-19:37:35 1090964 5687776 0 /usr/bin/some_command with some parameters +46396 1 someuser S 0:03.56 0:04.82 05-19:35:48 3460 4508436 0 /usr/bin/some_command with some parameters +46645 2828 someuser S 0:00.85 0:00.34 02:17:06 44544 30006608 0 /usr/bin/some_command with some parameters +46738 2828 someuser S 0:02.09 0:01.33 02:16:11 58224 30072984 0 /usr/bin/some_command with some parameters +47353 2828 someuser S 0:09.82 0:01.73 01:56:34 54452 30222164 0 /usr/bin/some_command with some parameters +47355 2828 someuser S 0:04.39 0:00.96 01:56:29 47800 30005420 0 /usr/bin/some_command with some parameters +49788 1 root Ss 0:00.03 0:00.07 01:06:50 7128 4470308 0 /usr/bin/some_command with some parameters +51166 1 _softwareupdate Ss 1:26.27 1:40.78 06-16:20:59 7132 4600944 0 /usr/bin/some_command with some parameters +51168 1 root Ss 0:00.25 0:01.68 06-16:20:58 644 4504988 0 /usr/bin/some_command with some parameters +51169 1 _atsserver Ss 0:00.25 0:00.74 06-16:20:58 788 4470832 0 /usr/bin/some_command with some parameters +51368 1 someuser S 0:04.07 0:09.22 06-15:27:09 6624 4537084 0 /usr/bin/some_command with some parameters +52356 2828 someuser S 0:01.51 0:00.48 31:56 58868 34199560 0 /usr/bin/some_command with some parameters +52359 2828 someuser S 0:06.29 0:01.36 31:53 55940 30230764 0 /usr/bin/some_command with some parameters +53270 1 root Ss 0:00.03 0:00.04 30:48 3076 4460200 0 /usr/bin/some_command with some parameters +53628 1 root Ss 0:00.01 0:00.02 29:30 3176 4425516 0 /usr/bin/some_command with some parameters +53631 1 root Ss 0:00.03 0:00.02 29:29 1476 4424424 0 /usr/bin/some_command with some parameters +53753 1 someuser S 0:00.06 0:00.06 23:38 4072 4476468 0 /usr/bin/some_command with some parameters +53792 1 root Ss 0:00.03 0:00.08 21:02 3104 4479136 0 /usr/bin/some_command with some parameters +53793 1 root Ss 0:00.00 0:00.01 21:02 2372 4405596 0 /usr/bin/some_command with some parameters +53835 2838 someuser S 2:02.77 0:49.41 20:45 40960 4694608 0 /usr/bin/some_command with some parameters +53836 2838 someuser S 1:14.02 0:24.45 20:43 29924 4802016 0 /usr/bin/some_command with some parameters +53837 1 someuser Ss 0:00.12 0:00.13 20:43 5212 4550420 0 /usr/bin/some_command with some parameters +53838 1 someuser Ss 0:00.03 0:00.13 20:43 2288 4892308 0 /usr/bin/some_command with some parameters +53839 1 someuser Ss 0:00.04 0:00.24 20:42 1752 4849368 0 /usr/bin/some_command with some parameters +53885 2828 someuser S 0:01.36 0:00.27 17:24 53856 30073136 0 /usr/bin/some_command with some parameters +53929 2828 someuser S 0:01.62 0:00.37 14:25 49896 30006408 0 /usr/bin/some_command with some parameters +53931 2828 someuser S 0:00.09 0:00.04 14:20 20312 29973136 0 /usr/bin/some_command with some parameters +54166 1 someuser S 0:00.17 0:00.23 12:08 14300 4862340 0 /usr/bin/some_command with some parameters +54402 1 someuser S 0:00.08 0:00.05 02:40 9952 4477448 0 /usr/bin/some_command with some parameters +54840 1 someuser S 0:00.05 0:00.02 00:14 5132 4444152 0 /usr/bin/some_command with some parameters +55706 1 root Ss 0:00.01 0:00.06 01-19:23:23 264 4452756 0 /usr/bin/some_command with some parameters +56786 2828 someuser S 2:44.32 0:46.63 01-19:17:22 101724 30335308 0 /usr/bin/some_command with some parameters +67087 1 someuser S< 0:00.02 0:00.13 05-12:33:55 128 4502220 0 /usr/bin/some_command with some parameters +70071 1 root Ss 0:00.09 0:00.22 01-03:34:12 2100 4485696 0 /usr/bin/some_command with some parameters +70682 1 _usbmuxd Ss 0:00.05 0:00.15 01-03:32:05 1596 4464252 0 /usr/bin/some_command with some parameters +70696 1 someuser S 0:00.47 0:01.53 01-03:32:04 6708 4703432 0 /usr/bin/some_command with some parameters +70752 1 someuser S 0:00.07 0:00.32 01-03:31:59 2964 4507376 0 /usr/bin/some_command with some parameters +70896 1 _driverkit Ss 0:00.00 0:00.02 01-03:30:59 220 4800232 0 /usr/bin/some_command with some parameters +70898 1 _driverkit Ss 0:31.53 1:25.95 01-03:30:59 756 4810996 0 /usr/bin/some_command with some parameters +70899 1 _driverkit Ss 0:24.49 0:36.97 01-03:30:59 684 4810496 0 /usr/bin/some_command with some parameters +71311 1 root Ss 0:14.93 0:25.21 01-03:27:30 11168 4506632 0 /usr/bin/some_command with some parameters +75951 2828 someuser S 0:07.09 0:05.92 05-23:52:24 8116 29977444 0 /usr/bin/some_command with some parameters +76232 2828 someuser S 0:32.61 0:25.42 05-23:50:45 22396 30003880 0 /usr/bin/some_command with some parameters +79317 2828 someuser S 0:12.80 0:09.77 05-01:26:20 7100 30014992 0 /usr/bin/some_command with some parameters +79623 2828 someuser S 0:27.32 0:15.83 01-02:17:54 39500 34189824 0 /usr/bin/some_command with some parameters +79636 2828 someuser S 0:23.39 0:15.08 01-02:17:50 38148 34197540 0 /usr/bin/some_command with some parameters +79637 2828 someuser S 0:00.31 0:00.60 01-02:17:50 6348 29973680 0 /usr/bin/some_command with some parameters +79692 2828 someuser S 0:41.06 0:19.53 01-02:17:38 105872 30086076 0 /usr/bin/some_command with some parameters +79727 1 someuser S 13:34.28 13:44.94 06-20:37:35 26636 5376912 0 /usr/bin/some_command with some parameters +79738 2828 someuser S 0:51.34 0:25.93 01-02:17:16 62400 30105596 0 /usr/bin/some_command with some parameters +80172 2828 someuser S 0:10.71 0:04.62 01-02:13:44 30536 30002272 0 /usr/bin/some_command with some parameters +87090 6305 someuser ?Es 0:00.00 0:00.00 07-23:41:16 0 /usr/bin/some_command with some parameters +87324 87090 someuser Z 0:00.00 0:00.00 07-23:41:12 0 /usr/bin/some_command with some parameters +87339 2828 someuser S 2:04.19 0:29.69 01-01:22:11 47436 34221640 0 /usr/bin/some_command with some parameters +89436 1 someuser S 0:05.20 0:06.94 05-23:11:51 5904 4538312 0 /usr/bin/some_command with some parameters +89517 2828 someuser S 20:37.55 3:52.48 04-00:35:47 209016 63653976 0 /usr/bin/some_command with some parameters +92412 2828 someuser S 1:40.63 0:34.70 01-00:57:02 79664 42633048 0 /usr/bin/some_command with some parameters +96559 1 someuser S 0:27.56 0:15.84 04-23:43:59 18544 5430400 0 /usr/bin/some_command with some parameters +97411 2828 someuser S 0:02.14 0:02.34 02-02:46:46 4928 29984652 0 /usr/bin/some_command with some parameters +98939 2828 someuser S 0:34.14 0:09.05 01-00:34:12 51468 30021300 0 /usr/bin/some_command with some parameters +99779 2828 someuser S 0:12.27 0:04.10 01-00:29:06 24548 30019412 0 /usr/bin/some_command with some parameters +99817 6305 someuser S 0:00.38 0:01.25 04-23:18:44 2604 42554704 0 /usr/bin/some_command with some parameters +99889 2828 someuser S 0:05.75 0:05.06 01-00:28:17 27912 30287484 0 /usr/bin/some_command with some parameters + 2956 2949 root Ss 0:00.02 0:00.03 08-14:42:23 12 4469120 0 /usr/bin/some_command with some parameters + 2959 2956 someuser S 0:00.26 0:00.30 08-14:42:22 8 4370964 0 /usr/bin/some_command with some parameters + 6945 2959 someuser S+ 0:00.01 0:00.05 08-03:13:00 112 4297872 0 /usr/bin/some_command with some parameters + 6948 6947 someuser Ss+ 0:00.38 0:00.58 08-03:12:59 200 4362720 0 /usr/bin/some_command with some parameters + 6999 6947 someuser Ss+ 0:00.29 0:00.52 08-03:12:57 8 4348436 0 /usr/bin/some_command with some parameters + 7049 6947 someuser Ss+ 0:00.20 0:00.20 08-03:12:56 8 4338196 0 /usr/bin/some_command with some parameters +11147 6947 someuser Ss+ 0:00.46 0:00.42 08-02:35:38 8 4338196 0 /usr/bin/some_command with some parameters +65815 6947 someuser Ss+ 0:00.67 0:01.10 08-00:18:51 3016 4362844 0 /usr/bin/some_command with some parameters + 1393 6947 someuser Ss+ 0:00.31 0:00.36 07-23:15:08 8 4338064 0 /usr/bin/some_command with some parameters +26136 6305 someuser Ss+ 0:00.33 0:00.54 07-03:31:53 228 4370964 0 /usr/bin/some_command with some parameters +42855 6947 someuser Ss 0:00.33 0:00.61 01-20:49:21 3192 4355472 0 /usr/bin/some_command with some parameters +54887 42855 root R+ 0:00.00 0:00.01 00:01 1076 4269016 0 /usr/bin/some_command with some parameters` + +var psOutThreads10 = `USER PID TT %CPU STAT PRI STIME UTIME COMMAND +root 1 ?? 0.0 S 31T 0:00.39 0:00.09 /usr/bin/some_command with some parameters + 1 0.0 S 20T 0:00.15 0:00.04 + 1 0.6 S 37T 0:00.01 0:00.00 +root 68 ?? 0.0 S 4T 0:01.24 0:00.33 /usr/bin/some_command with some parameters + 68 0.0 S 4T 0:00.00 0:00.00 + 68 0.0 S 4T 0:00.00 0:00.00 + 68 0.0 S 4T 0:00.00 0:00.00 +root 69 ?? 0.0 S 31T 0:00.20 0:00.08 /usr/bin/some_command with some parameters + 69 0.0 S 31T 0:00.00 0:00.00 + 69 0.0 S 31T 0:00.00 0:00.00 + 69 0.0 S 4T 0:00.01 0:00.00 +root 72 ?? 0.0 S 20T 0:00.98 0:00.94 /usr/bin/some_command with some parameters + 72 0.0 S 20T 0:00.00 0:00.00 +root 73 ?? 0.0 S 49T 0:27.84 1:22.66 /usr/bin/some_command with some parameters + 73 0.0 S 31T 0:00.00 0:00.00 + 73 0.0 S 31T 0:00.02 0:00.01 + 73 0.0 S 50R 0:16.31 0:03.31 + 73 0.0 S 31T 0:01.93 0:07.66 + 73 0.0 S 31T 0:13.32 0:04.10 + 73 0.0 S 31T 0:57.43 0:05.99 + 73 0.0 S 31T 0:13.38 0:01.60 + 73 0.0 S 31T 0:09.00 0:00.96 + 73 0.0 S 31T 0:02.29 0:00.64 + 73 0.0 S 31T 0:08.64 0:01.02 + 73 0.0 S 31T 0:00.01 0:00.00 + 73 0.0 S 31T 0:00.00 0:00.00 + 73 0.0 S 31T 0:00.00 0:00.00 +root 74 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters + 74 0.0 S 20T 0:00.04 0:00.01 +root 75 ?? 0.0 S 4T 0:04.35 0:07.60 /usr/bin/some_command with some parameters + 75 0.0 S 4T 0:00.00 0:00.00 + 75 0.0 S 4T 0:00.00 0:00.00 +root 81 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters + 81 0.0 S 20T 0:00.05 0:00.01 +root 82 ?? 0.0 S 4T 0:06.92 0:05.91 /usr/bin/some_command with some parameters + 82 0.0 S 4T 0:00.00 0:00.00 + 82 0.0 S 4T 0:00.00 0:00.00 +root 84 ?? 0.0 S 31T 0:04.14 0:02.10 /usr/bin/some_command with some parameters + 84 0.0 S 31T 0:00.12 0:00.03 + 84 0.0 S 31T 0:00.25 0:00.13 + 84 0.0 S 31T 0:00.65 0:00.28 + 84 0.0 S 31T 0:00.21 0:00.10 + 84 0.0 S 31T 0:00.33 0:00.18 + 84 0.0 S 31T 0:00.37 0:00.16 + 84 0.0 S 31T 0:00.01 0:00.00 +root 86 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 86 0.0 S 37T 0:00.01 0:00.01 + 86 0.0 S 37T 0:00.00 0:00.00 +root 88 ?? 0.0 S 20T 0:10.90 0:10.20 /usr/bin/some_command with some parameters + 88 0.0 S 20T 24:38.78 20:49.45 + 88 0.0 S 20T 0:00.00 0:00.00 + 88 0.0 S 20T 0:00.00 0:00.00 +root 91 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 91 0.0 S 31T 0:00.00 0:00.00 + 91 0.0 S 31T 0:00.00 0:00.00 + 91 0.0 S 31T 0:00.00 0:00.00 +root 93 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 93 0.0 S 20T 0:00.01 0:00.01 + 93 0.0 S 20T 0:00.01 0:00.00 + 93 0.0 S 20T 0:00.00 0:00.00 +root 99 ?? 0.0 S 31T 0:00.04 0:00.01 /usr/bin/some_command with some parameters + 99 0.0 S 97R 0:00.16 0:00.06 + 99 0.0 S 31T 0:00.04 0:00.06 +` + +var psOutThreads100 = `USER PID TT %CPU STAT PRI STIME UTIME COMMAND +root 1 ?? 0.0 S 31T 0:00.39 0:00.09 /usr/bin/some_command with some parameters + 1 0.0 S 20T 0:00.15 0:00.04 + 1 0.6 S 37T 0:00.01 0:00.00 +root 68 ?? 0.0 S 4T 0:01.24 0:00.33 /usr/bin/some_command with some parameters + 68 0.0 S 4T 0:00.00 0:00.00 + 68 0.0 S 4T 0:00.00 0:00.00 + 68 0.0 S 4T 0:00.00 0:00.00 +root 69 ?? 0.0 S 31T 0:00.20 0:00.08 /usr/bin/some_command with some parameters + 69 0.0 S 31T 0:00.00 0:00.00 + 69 0.0 S 31T 0:00.00 0:00.00 + 69 0.0 S 4T 0:00.01 0:00.00 +root 72 ?? 0.0 S 20T 0:00.98 0:00.94 /usr/bin/some_command with some parameters + 72 0.0 S 20T 0:00.00 0:00.00 +root 73 ?? 0.0 S 49T 0:27.84 1:22.66 /usr/bin/some_command with some parameters + 73 0.0 S 31T 0:00.00 0:00.00 + 73 0.0 S 31T 0:00.02 0:00.01 + 73 0.0 S 50R 0:16.31 0:03.31 + 73 0.0 S 31T 0:01.93 0:07.66 + 73 0.0 S 31T 0:13.32 0:04.10 + 73 0.0 S 31T 0:57.43 0:05.99 + 73 0.0 S 31T 0:13.38 0:01.60 + 73 0.0 S 31T 0:09.00 0:00.96 + 73 0.0 S 31T 0:02.29 0:00.64 + 73 0.0 S 31T 0:08.64 0:01.02 + 73 0.0 S 31T 0:00.01 0:00.00 + 73 0.0 S 31T 0:00.00 0:00.00 + 73 0.0 S 31T 0:00.00 0:00.00 +root 74 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters + 74 0.0 S 20T 0:00.04 0:00.01 +root 75 ?? 0.0 S 4T 0:04.35 0:07.60 /usr/bin/some_command with some parameters + 75 0.0 S 4T 0:00.00 0:00.00 + 75 0.0 S 4T 0:00.00 0:00.00 +root 81 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters + 81 0.0 S 20T 0:00.05 0:00.01 +root 82 ?? 0.0 S 4T 0:06.92 0:05.91 /usr/bin/some_command with some parameters + 82 0.0 S 4T 0:00.00 0:00.00 + 82 0.0 S 4T 0:00.00 0:00.00 +root 84 ?? 0.0 S 31T 0:04.14 0:02.10 /usr/bin/some_command with some parameters + 84 0.0 S 31T 0:00.12 0:00.03 + 84 0.0 S 31T 0:00.25 0:00.13 + 84 0.0 S 31T 0:00.65 0:00.28 + 84 0.0 S 31T 0:00.21 0:00.10 + 84 0.0 S 31T 0:00.33 0:00.18 + 84 0.0 S 31T 0:00.37 0:00.16 + 84 0.0 S 31T 0:00.01 0:00.00 +root 86 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 86 0.0 S 37T 0:00.01 0:00.01 + 86 0.0 S 37T 0:00.00 0:00.00 +root 88 ?? 0.0 S 20T 0:10.90 0:10.20 /usr/bin/some_command with some parameters + 88 0.0 S 20T 24:38.78 20:49.45 + 88 0.0 S 20T 0:00.00 0:00.00 + 88 0.0 S 20T 0:00.00 0:00.00 +root 91 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 91 0.0 S 31T 0:00.00 0:00.00 + 91 0.0 S 31T 0:00.00 0:00.00 + 91 0.0 S 31T 0:00.00 0:00.00 +root 93 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 93 0.0 S 20T 0:00.01 0:00.01 + 93 0.0 S 20T 0:00.01 0:00.00 + 93 0.0 S 20T 0:00.00 0:00.00 +root 99 ?? 0.0 S 31T 0:00.04 0:00.01 /usr/bin/some_command with some parameters + 99 0.0 S 97R 0:00.16 0:00.06 + 99 0.0 S 31T 0:00.04 0:00.06 +root 103 ?? 0.0 S 31T 0:00.15 0:00.09 /usr/bin/some_command with some parameters + 103 0.0 S 50R 0:24.25 0:09.17 + 103 0.0 S 31T 0:00.00 0:00.00 + 103 0.0 S 4T 0:00.03 0:00.02 + 103 0.0 S 37T 0:00.00 0:00.00 + 103 0.0 S 20T 0:00.01 0:00.00 + 103 0.0 S 31T 0:00.00 0:00.00 + 103 0.0 S 37T 0:00.00 0:00.00 + 103 0.0 S 4T 0:00.00 0:00.00 +root 104 ?? 0.0 S 4T 0:01.01 0:09.28 /usr/bin/some_command with some parameters + 104 0.0 S 4T 2:14.81 5:02.47 + 104 0.0 S 4T 0:00.44 0:01.08 + 104 0.0 S 4T 0:12.12 2:53.73 + 104 0.0 S 4T 0:00.02 0:00.01 + 104 0.0 S 4T 0:15.99 0:06.45 + 104 0.0 S 4T 1:28.59 2:46.98 + 104 0.0 S 4T 0:04.30 0:12.81 + 104 0.0 S 4T 0:08.71 0:17.49 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:59.96 6:08.35 + 104 0.0 S 4T 0:23.11 0:36.48 + 104 0.0 S 4T 1:34.48 2:09.53 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:44.10 0:19.85 + 104 0.0 S 4T 0:00.52 0:00.17 + 104 0.0 S 4T 2:14.01 5:01.32 + 104 0.0 S 4T 2:13.19 5:01.40 + 104 0.0 S 4T 2:13.78 5:03.91 + 104 0.0 S 4T 2:15.10 5:04.41 + 104 0.0 S 4T 2:12.32 5:03.10 + 104 0.0 S 4T 0:12.37 2:53.81 + 104 0.0 S 4T 0:12.10 2:53.81 + 104 0.0 S 4T 2:14.30 5:02.53 + 104 0.0 S 4T 2:10.42 5:00.82 + 104 0.0 S 4T 2:14.03 5:02.43 + 104 0.0 S 4T 2:11.48 5:03.86 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:08.54 0:06.59 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:05.34 0:01.55 + 104 0.0 S 4T 0:03.66 0:01.06 + 104 0.0 S 4T 0:00.18 0:00.03 + 104 0.0 S 4T 0:37.36 0:06.80 + 104 0.0 S 4T 0:01.95 0:01.44 + 104 0.0 S 4T 0:37.35 0:40.82 + 104 0.0 S 4T 0:12.91 0:09.45 + 104 0.0 S 4T 0:02.40 0:00.68 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:08.22 0:07.64 + 104 0.0 S 4T 0:00.17 0:00.02 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.10 0:00.03 + 104 0.0 S 4T 0:01.59 0:01.74 + 104 0.0 S 4T 0:01.66 0:00.43 + 104 0.0 S 4T 0:00.98 0:00.21 + 104 0.0 S 4T 0:01.56 0:00.92 + 104 0.0 S 4T 0:00.02 0:00.00 + 104 0.0 S 4T 0:00.38 0:00.14 + 104 0.0 S 4T 0:00.92 0:00.25 + 104 0.0 S 4T 0:17.54 0:09.81 + 104 0.0 S 4T 0:05.13 0:01.18 + 104 0.0 S 4T 0:11.10 1:14.80 + 104 0.0 S 4T 2:27.08 11:01.59 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.76 0:00.28 + 104 0.3 S 4T 21:15.35 38:57.00 + 104 0.0 S 4T 0:01.84 0:00.31 + 104 0.0 S 4T 0:17.74 0:28.64 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:15.35 0:45.24 + 104 0.0 S 4T 0:00.70 0:00.30 + 104 0.0 S 4T 0:00.62 0:00.27 + 104 0.0 S 4T 0:00.64 0:00.31 + 104 0.0 S 4T 0:00.55 0:00.28 + 104 0.0 S 4T 0:02.64 0:04.64 + 104 0.0 S 4T 0:03.42 0:01.28 + 104 0.0 S 4T 0:05.43 0:02.17 + 104 0.0 S 4T 0:00.18 0:00.05 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.18 0:00.05 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:33.60 0:37.38 + 104 0.0 S 4T 0:24.14 0:08.43 + 104 0.0 S 4T 2:35.92 4:31.62 + 104 0.0 S 4T 0:04.54 0:03.01 + 104 0.0 S 4T 1:29.91 1:00.41 + 104 0.0 S 4T 0:04.88 0:01.94 + 104 0.0 S 4T 0:07.19 0:05.44 + 104 0.0 S 4T 0:02.68 0:00.83 + 104 0.0 S 4T 0:56.96 1:49.87 + 104 0.0 S 4T 0:12.03 0:07.99 + 104 0.0 S 4T 0:15.67 0:12.93 + 104 0.0 S 4T 0:25.82 1:14.97 + 104 0.0 S 4T 0:01.66 0:02.08 + 104 0.0 S 4T 0:08.01 0:40.99 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:02.42 0:02.15 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:01.54 0:01.59 + 104 0.0 S 4T 0:01.13 0:00.29 + 104 0.0 S 4T 0:02.33 0:00.85 + 104 0.0 S 4T 0:20.38 0:20.06 + 104 0.0 S 4T 1:52.14 25:52.43 + 104 0.0 S 4T 2:07.78 2:26.81 + 104 0.0 S 4T 4:04.54 4:24.85 + 104 0.0 S 4T 0:00.01 0:00.00 + 104 0.0 S 4T 0:00.88 0:02.39 + 104 0.0 S 4T 0:00.98 0:02.32 + 104 0.0 S 4T 0:00.83 0:02.10 + 104 0.3 S 4T 0:00.87 0:02.00 + 104 0.0 S 4T 0:00.62 0:01.89 + 104 0.0 S 4T 0:00.24 0:00.51 + 104 0.0 S 4T 0:00.85 0:04.37 + 104 0.0 S 4T 0:00.01 0:00.01 + 104 0.1 S 4T 0:00.11 0:00.17 + 104 0.0 S 4T 0:00.07 0:00.11 + 104 0.0 S 4T 0:00.02 0:00.03 + 104 0.0 S 4T 0:00.01 0:00.01 + 104 0.0 S 4T 0:00.00 0:00.00 +root 106 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 106 0.0 S 31T 0:00.00 0:00.00 +root 107 ?? 0.0 S 31T 0:24.80 0:41.84 /usr/bin/some_command with some parameters + 107 0.0 S 37T 0:00.00 0:00.00 +root 114 ?? 0.0 S 37T 0:00.03 0:00.00 /usr/bin/some_command with some parameters +root 115 ?? 0.0 S 4T 0:00.78 0:00.70 /usr/bin/some_command with some parameters + 115 0.0 S 4T 0:00.00 0:00.01 + 115 0.0 S 4T 0:00.00 0:00.00 + 115 0.0 S 4T 0:00.00 0:00.00 + 115 0.0 S 4T 0:00.01 0:00.02 +root 116 ?? 0.0 S 20T 0:00.42 0:00.96 /usr/bin/some_command with some parameters + 116 0.0 S 31T 0:00.00 0:00.00 + 116 0.0 S 20T 0:00.00 0:00.00 + 116 0.0 S 31T 0:00.20 0:00.07 + 116 0.0 S 31T 0:00.00 0:00.00 + 116 0.0 S 31T 0:00.00 0:00.00 + 116 0.0 S 31T 0:00.00 0:00.00 +root 117 ?? 0.0 S 31T 0:06.27 0:08.00 /usr/bin/some_command with some parameters + 117 0.0 S 31T 0:00.00 0:00.00 + 117 0.0 S 31T 0:00.01 0:00.00 +root 118 ?? 0.0 S 20T 0:00.02 0:00.01 /usr/bin/some_command with some parameters + 118 0.0 S 20T 0:00.22 0:00.11 + 118 0.0 S 20T 0:00.00 0:00.00 + 118 0.0 S 20T 0:00.00 0:00.00 + 118 0.0 S 20T 0:00.00 0:00.00 + 118 0.0 S 20T 0:00.00 0:00.00 +root 119 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 119 0.0 S 4T 0:00.00 0:00.00 + 119 0.0 S 4T 0:00.01 0:00.01 +_timed 120 ?? 0.0 S 31T 0:03.13 0:00.61 /usr/bin/some_command with some parameters + 120 0.0 S 4T 0:00.02 0:00.00 +root 123 ?? 0.0 S 31T 0:01.98 0:06.46 /usr/bin/some_command with some parameters + 123 0.0 S 31T 0:00.17 0:00.03 + 123 0.0 S 31T 0:00.00 0:00.00 +root 124 ?? 0.0 S 20T 0:00.00 0:00.00 auditd -l + 124 0.0 S 20T 0:00.04 0:00.00 +_locationd 126 ?? 0.0 S 4T 0:00.62 0:00.12 /usr/bin/some_command with some parameters + 126 0.0 S 4T 0:00.04 0:00.05 + 126 0.0 S 4T 0:00.01 0:00.00 + 126 0.0 S 4T 0:00.00 0:00.00 +root 128 ?? 0.0 S 20T 0:00.00 0:00.00 autofsd + 128 0.0 S 20T 0:00.00 0:00.00 +_displaypolicyd 129 ?? 0.0 S 20T 0:00.43 0:00.05 /usr/bin/some_command with some parameters + 129 0.0 S 20T 0:00.00 0:00.00 + 129 0.0 S 20T 0:00.16 0:00.08 + 129 0.0 S 20T 0:00.93 0:00.02 + 129 0.0 S 20T 0:00.05 0:00.00 + 129 0.0 S 20T 0:00.01 0:00.00 +root 132 ?? 0.0 S 4T 0:00.05 0:00.06 /usr/bin/some_command with some parameters + 132 0.0 S 4T 0:00.00 0:00.00 + 132 0.0 S 4T 0:00.00 0:00.00 +_distnote 135 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 135 0.0 S 31T 0:00.76 0:01.09 +root 139 ?? 0.0 S 20T 0:00.06 0:00.02 /usr/bin/some_command with some parameters + 139 0.0 S 20T 1:27.75 0:55.96 + 139 0.0 S 20T 0:04.08 0:00.52 +root 140 ?? 0.0 S 31T 0:00.05 0:00.00 /usr/bin/some_command with some parameters + 140 0.0 S 31T 0:00.00 0:00.00 +root 141 ?? 0.0 S 4T 0:00.07 0:00.03 /usr/bin/some_command with some parameters + 141 0.0 S 4T 0:00.00 0:00.00 + 141 0.0 S 4T 0:00.00 0:00.00 +root 142 ?? 0.0 S 31T 0:00.02 0:00.00 /usr/bin/some_command with some parameters + 142 0.0 S 31T 0:00.01 0:00.00 + 142 0.0 S 31T 0:00.00 0:00.00 +root 144 ?? 0.0 S 31T 0:19.66 0:16.97 /usr/bin/some_command with some parameters + 144 0.0 S 31T 0:11.46 0:04.77 + 144 0.0 S 37T 0:00.03 0:00.01 +root 145 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 145 0.0 S 37T 0:00.08 0:00.04 +root 147 ?? 0.0 S 31T 0:00.30 0:00.25 /usr/bin/some_command with some parameters + 147 0.0 S 31T 0:00.00 0:00.00 +root 148 ?? 0.0 S 55R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 148 0.0 S 19T 0:00.00 0:00.00 + 148 0.0 S 31T 0:00.00 0:00.00 + 148 0.0 S 31T 0:00.01 0:00.00 +root 151 ?? 0.0 S 31T 0:00.19 0:00.27 /usr/bin/some_command with some parameters + 151 0.0 S 31T 0:00.43 0:00.77 + 151 0.0 S 31T 0:00.01 0:00.00 +root 152 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 152 0.0 S 4T 0:00.00 0:00.00 + 152 0.0 S 4T 0:00.00 0:00.00 +root 153 ?? 0.0 S 31T 0:04.59 0:02.48 /usr/bin/some_command with some parameters + 153 0.0 S 31T 0:00.16 0:00.16 + 153 0.0 S 31T 0:00.07 0:00.01 + 153 0.0 S 31T 0:00.01 0:00.00 +_analyticsd 156 ?? 0.0 S 31T 0:00.23 0:00.16 /usr/bin/some_command with some parameters + 156 0.0 S 31T 0:00.01 0:00.00 +root 191 ?? 0.0 S 4T 0:00.05 0:00.04 /usr/bin/some_command with some parameters + 191 0.0 S 4T 0:00.00 0:00.01 + 191 0.0 S 4T 0:00.00 0:00.00 + 191 0.0 S 4T 0:00.00 0:00.00 + 191 0.0 S 4T 0:00.00 0:00.00 +root 195 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 195 0.0 S 4T 0:00.27 0:00.03 + 195 0.0 S 4T 0:00.03 0:00.06 +root 199 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters + 199 0.0 S 31T 0:00.51 0:00.29 +root 206 ?? 0.0 S 4T 0:01.29 0:01.82 /usr/bin/some_command with some parameters + 206 0.0 S 4T 0:00.00 0:00.00 +_trustd 208 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 208 0.0 S 4T 0:00.00 0:00.00 +_networkd 215 ?? 0.0 S 4T 0:01.52 0:00.19 /usr/bin/some_command with some parameters + 215 0.0 S 4T 0:00.01 0:00.00 + 215 0.0 S 4T 0:00.00 0:00.00 +_mdnsresponder 232 ?? 0.0 S 31T 0:02.58 0:03.05 /usr/bin/some_command with some parameters + 232 0.0 S 31T 1:14.24 0:37.44 + 232 0.0 S 37T 0:00.00 0:00.00 +root 248 ?? 0.0 S 31T 0:05.23 0:03.32 /usr/bin/some_command with some parameters + 248 0.0 S 31T 0:00.00 0:00.00 + 248 0.0 S 37T 0:00.00 0:00.00 +root 250 ?? 0.0 S 4T 0:00.14 0:00.05 /usr/bin/some_command with some parameters + 250 0.0 S 4T 0:00.00 0:00.00 +root 252 ?? 0.0 S 31T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 252 0.0 S 31T 0:00.00 0:00.00 +root 254 ?? 0.0 S 20T 0:00.49 0:00.12 /usr/bin/some_command with some parameters + 254 0.0 S 20T 0:00.00 0:00.00 +root 255 ?? 0.0 S 31T 0:25.65 0:13.33 /usr/bin/some_command with some parameters + 255 0.0 S 31T 0:00.25 0:00.03 + 255 0.0 S 31T 0:20.97 0:06.65 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:02.38 0:01.51 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:00.00 0:00.00 +_coreaudiod 256 ?? 0.0 S 63T 0:00.26 0:00.11 /usr/bin/some_command with some parameters + 256 0.0 S 19T 0:00.00 0:00.00 + 256 0.0 S 31T 0:00.00 0:00.00 + 256 0.0 S 31T 0:00.95 0:00.05 + 256 0.3 S 97R 0:02.08 0:05.95 + 256 0.3 S 97R 0:02.04 0:05.63 + 256 0.3 S 61T 0:00.70 0:00.25 + 256 0.0 S 31T 0:00.00 0:00.00 + 256 0.8 S 61R 0:00.40 0:00.27 +_nsurlsessiond 257 ?? 0.0 S 4T 0:00.38 0:00.15 /usr/bin/some_command with some parameters + 257 0.0 S 4T 0:00.00 0:00.00 +root 263 ?? 0.0 S 4T 0:00.69 0:00.11 /usr/bin/some_command with some parameters + 263 0.0 S 4T 0:00.01 0:00.00 + 263 0.0 S 4T 0:00.00 0:00.00 +_cmiodalassistants 264 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 264 1.1 S 97R 2:00.26 4:45.36 + 264 0.1 S 31T 0:01.94 0:01.28 + 264 0.0 S 31T 0:00.00 0:00.00 +root 269 ?? 0.0 S 20T 1:55.12 1:38.27 /usr/bin/some_command with some parameters + 269 0.0 S 20T 0:00.94 0:00.31 + 269 0.0 S 20T 0:00.00 0:00.00 +_coreaudiod 271 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters + 271 0.0 S 31T 0:00.52 0:00.21 +root 272 ?? 0.0 S 4T 0:00.06 0:00.01 /usr/bin/some_command with some parameters + 272 0.0 S 4T 0:00.00 0:00.00 +_locationd 279 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters + 279 0.0 S 31T 0:00.56 0:00.26 +root 300 ?? 0.0 S 4T 0:00.05 0:00.01 /usr/bin/some_command with some parameters + 300 0.0 S 4T 0:00.07 0:00.01 +_softwareupdate 307 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters + 307 0.0 S 31T 0:00.54 0:00.21 +root 313 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 313 0.0 S 31T 0:00.00 0:00.00 +root 322 ?? 0.0 S 20T 0:00.03 0:00.03 /usr/bin/some_command with some parameters + 322 0.0 S 20T 0:00.00 0:00.00 + 322 0.0 S 20T 0:00.00 0:00.00 +root 337 ?? 0.0 S 4T 0:00.08 0:00.03 /usr/bin/some_command with some parameters + 337 0.0 S 4T 0:00.00 0:00.00 +root 397 ?? 0.0 S 31T 0:14.02 0:12.57 /usr/bin/some_command with some parameters + 397 0.0 S 31T 0:00.00 0:00.00 + 397 0.0 S 31T 0:00.00 0:00.00 + 397 0.0 S 31T 1:32.16 7:28.27 + 397 0.0 R 31T 1:32.29 7:28.11 + 397 0.0 S 31T 1:33.32 7:28.98 + 397 0.0 S 31T 1:32.30 7:28.34 + 397 0.0 S 31T 1:32.30 7:28.44 + 397 0.0 S 31T 1:32.33 7:27.11 + 397 0.0 S 31T 0:10.06 0:22.50 + 397 0.0 S 31T 0:00.25 0:01.70 + 397 0.0 S 31T 2:20.84 5:59.52 + 397 0.0 S 31T 0:00.29 0:00.12 + 397 0.0 S 31T 5:05.54 5:08.26 + 397 0.0 S 4T 0:00.16 0:00.34 + 397 0.0 S 31T 0:00.04 0:00.02 + 397 0.0 S 31T 0:43.11 1:10.88 + 397 0.0 S 31T 0:00.72 0:00.56 + 397 0.0 S 31T 0:13.98 0:26.76 + 397 0.0 S 31T 0:01.40 0:01.34 + 397 0.0 S 31T 0:02.07 0:01.84 + 397 0.0 S 31T 0:00.59 0:00.76 + 397 0.0 S 31T 0:00.37 0:00.67 + 397 0.0 S 31T 0:00.15 0:00.13 + 397 0.0 S 31T 0:00.02 0:00.02 + 397 0.0 R 54R 0:00.01 0:00.02 + 397 0.0 S 31T 0:00.03 0:00.01 +_nsurlsessiond 398 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 398 0.0 S 31T 0:00.53 0:00.21 +root 419 ?? 0.0 S 31T 0:00.71 0:00.84 /usr/bin/some_command with some parameters + 419 0.0 S 31T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.01 0:00.00 + 419 0.0 S 31T 0:00.01 0:00.01 + 419 0.0 S 37T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.01 + 419 0.0 S 31T 0:00.00 0:00.00 +_driverkit 422 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 422 0.0 S 63R 0:00.09 0:00.03 +_driverkit 423 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 423 0.0 S 63R 0:00.00 0:00.00 +_driverkit 425 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 425 0.0 S 31T 0:00.00 0:00.00 +_driverkit 427 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 427 0.0 S 63R 0:03.41 0:01.46 +_driverkit 428 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 428 0.0 S 31T 0:00.00 0:00.00 +_driverkit 430 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 430 0.0 S 63R 0:00.00 0:00.00 +_driverkit 432 ?? 0.0 S 63R 0:00.94 0:00.30 /usr/bin/some_command with some parameters + 432 0.0 S 31T 0:00.00 0:00.00 +_driverkit 434 ?? 0.0 S 63R 0:00.04 0:00.02 /usr/bin/some_command with some parameters + 434 0.0 S 31T 0:00.00 0:00.00 +_driverkit 435 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 435 0.0 S 31T 0:00.00 0:00.00 +_spotlight 437 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 437 0.0 S 31T 0:00.13 0:00.08 +root 460 ?? 0.0 S 63R 0:00.88 0:00.12 /usr/bin/some_command with some parameters + 460 0.0 S 31T 0:00.00 0:00.00 +_windowserver 474 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 474 0.0 S 31T 0:00.52 0:00.20 +_appinstalld 481 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 481 0.0 S 31T 0:00.50 0:00.19 +root 492 ?? 0.0 S 51R 0:17.61 0:22.47 /usr/bin/some_command with some parameters + 492 0.1 S 55R 4:03.41 2:28.94 + 492 0.0 S 37T 0:03.98 0:03.88 + 492 0.0 S 51T 0:00.00 0:00.00 + 492 0.0 S 37T 0:00.00 0:00.00 + 492 0.0 S 31T 0:00.00 0:00.00 + 492 0.0 S 51R 0:00.00 0:00.00 +_appleevents 501 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 501 0.0 S 31T 0:00.00 0:00.00 +root 503 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 503 0.0 S 4T 0:00.00 0:00.00 +root 508 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 508 23.4 S 4T 0:06.35 0:01.50 + 508 0.0 S 4T 0:00.60 0:00.14 +root 515 ?? 0.0 S 4T 0:00.12 0:00.03 /usr/bin/some_command with some parameters + 515 0.0 S 4T 0:00.00 0:00.00 + 515 0.0 S 4T 0:00.00 0:00.00 +root 528 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 528 0.0 S 4T 0:00.00 0:00.00 +_appleevents 541 ?? 0.0 S 31T 0:00.01 0:00.02 /usr/bin/some_command with some parameters + 541 0.0 S 31T 0:00.50 0:00.19 +root 555 ?? 0.0 S 4T 0:00.04 0:00.01 /usr/bin/some_command with some parameters + 555 0.0 S 4T 0:00.00 0:00.00 +someuser 558 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 558 0.0 S 20T 0:00.86 0:01.75 +root 583 ?? 0.0 S 4T 0:00.06 0:00.01 /usr/bin/some_command with some parameters + 583 0.0 S 4T 0:00.00 0:00.00 +root 631 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 631 0.0 S 20T 0:00.01 0:00.00 +someuser 638 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 638 0.0 S 4T 0:00.01 0:00.00 + 638 0.0 S 4T 0:00.00 0:00.00 +someuser 673 ?? 0.0 S 47T 0:10.83 0:08.96 /usr/bin/some_command with some parameters + 673 0.0 S 19T 0:00.00 0:00.00 + 673 0.0 S 31T 0:00.00 0:00.00 + 673 0.0 S 37T 0:00.00 0:00.00 +_windowserver 677 ?? 26.8 S 79R 100:32.78 206:51.42 /usr/bin/some_command with some parameters + 677 2.8 S 79R 42:18.54 27:27.26 + 677 0.0 S 37T 0:00.00 0:00.00 + 677 0.0 S 79R 0:00.00 0:00.00 + 677 0.0 S 31T 0:00.01 0:00.00 + 677 0.0 S 31T 0:00.16 0:00.20 + 677 0.0 S 31T 0:28.55 0:11.03 + 677 1.7 U 31T 0:23.73 0:19.24 + 677 1.9 S 31T 0:14.59 0:11.88 + 677 0.0 S 79R 0:13.48 0:11.02 + 677 0.6 S 79R 0:01.43 0:00.84 + 677 0.2 S 79T 0:04.30 0:03.51 + 677 21.0 S 79T 0:04.35 0:03.53 + 677 1.2 S 79R 0:00.53 0:00.29 + 677 0.0 S 31T 0:00.00 0:00.00 + 677 1.2 S 79R 0:00.09 0:00.05 +_securityagent 735 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 735 0.0 S 31T 0:00.47 0:00.18 +root 762 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 762 0.0 S 4T 0:00.01 0:00.00 +root 860 ?? 0.0 S 31T 0:01.12 0:01.52 /usr/bin/some_command with some parameters + 860 0.0 S 31T 0:00.58 0:00.39 + 860 0.0 S 31T 0:00.00 0:00.01 +someuser 978 ?? 0.0 S 31T 0:03.65 0:02.88 /usr/bin/some_command with some parameters + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 0T 0:00.02 0:00.02 + 978 0.0 S 31T 0:00.13 0:00.06 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.10 0:00.02 + 978 0.0 S 31T 0:00.02 0:00.00 + 978 0.0 S 31T 0:00.01 0:00.00 +someuser 2054 ?? 0.0 S 4T 0:01.04 0:01.49 /usr/bin/some_command with some parameters + 2054 0.0 S 4T 0:00.01 0:00.00 + 2054 0.0 S 4T 0:00.00 0:00.00 +` + +var psOutThreads500 = `USER PID TT %CPU STAT PRI STIME UTIME COMMAND +root 1 ?? 0.0 S 31T 0:00.39 0:00.09 /usr/bin/some_command with some parameters + 1 0.0 S 20T 0:00.15 0:00.04 + 1 0.6 S 37T 0:00.01 0:00.00 +root 68 ?? 0.0 S 4T 0:01.24 0:00.33 /usr/bin/some_command with some parameters + 68 0.0 S 4T 0:00.00 0:00.00 + 68 0.0 S 4T 0:00.00 0:00.00 + 68 0.0 S 4T 0:00.00 0:00.00 +root 69 ?? 0.0 S 31T 0:00.20 0:00.08 /usr/bin/some_command with some parameters + 69 0.0 S 31T 0:00.00 0:00.00 + 69 0.0 S 31T 0:00.00 0:00.00 + 69 0.0 S 4T 0:00.01 0:00.00 +root 72 ?? 0.0 S 20T 0:00.98 0:00.94 /usr/bin/some_command with some parameters + 72 0.0 S 20T 0:00.00 0:00.00 +root 73 ?? 0.0 S 49T 0:27.84 1:22.66 /usr/bin/some_command with some parameters + 73 0.0 S 31T 0:00.00 0:00.00 + 73 0.0 S 31T 0:00.02 0:00.01 + 73 0.0 S 50R 0:16.31 0:03.31 + 73 0.0 S 31T 0:01.93 0:07.66 + 73 0.0 S 31T 0:13.32 0:04.10 + 73 0.0 S 31T 0:57.43 0:05.99 + 73 0.0 S 31T 0:13.38 0:01.60 + 73 0.0 S 31T 0:09.00 0:00.96 + 73 0.0 S 31T 0:02.29 0:00.64 + 73 0.0 S 31T 0:08.64 0:01.02 + 73 0.0 S 31T 0:00.01 0:00.00 + 73 0.0 S 31T 0:00.00 0:00.00 + 73 0.0 S 31T 0:00.00 0:00.00 +root 74 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters + 74 0.0 S 20T 0:00.04 0:00.01 +root 75 ?? 0.0 S 4T 0:04.35 0:07.60 /usr/bin/some_command with some parameters + 75 0.0 S 4T 0:00.00 0:00.00 + 75 0.0 S 4T 0:00.00 0:00.00 +root 81 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters + 81 0.0 S 20T 0:00.05 0:00.01 +root 82 ?? 0.0 S 4T 0:06.92 0:05.91 /usr/bin/some_command with some parameters + 82 0.0 S 4T 0:00.00 0:00.00 + 82 0.0 S 4T 0:00.00 0:00.00 +root 84 ?? 0.0 S 31T 0:04.14 0:02.10 /usr/bin/some_command with some parameters + 84 0.0 S 31T 0:00.12 0:00.03 + 84 0.0 S 31T 0:00.25 0:00.13 + 84 0.0 S 31T 0:00.65 0:00.28 + 84 0.0 S 31T 0:00.21 0:00.10 + 84 0.0 S 31T 0:00.33 0:00.18 + 84 0.0 S 31T 0:00.37 0:00.16 + 84 0.0 S 31T 0:00.01 0:00.00 +root 86 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 86 0.0 S 37T 0:00.01 0:00.01 + 86 0.0 S 37T 0:00.00 0:00.00 +root 88 ?? 0.0 S 20T 0:10.90 0:10.20 /usr/bin/some_command with some parameters + 88 0.0 S 20T 24:38.78 20:49.45 + 88 0.0 S 20T 0:00.00 0:00.00 + 88 0.0 S 20T 0:00.00 0:00.00 +root 91 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 91 0.0 S 31T 0:00.00 0:00.00 + 91 0.0 S 31T 0:00.00 0:00.00 + 91 0.0 S 31T 0:00.00 0:00.00 +root 93 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 93 0.0 S 20T 0:00.01 0:00.01 + 93 0.0 S 20T 0:00.01 0:00.00 + 93 0.0 S 20T 0:00.00 0:00.00 +root 99 ?? 0.0 S 31T 0:00.04 0:00.01 /usr/bin/some_command with some parameters + 99 0.0 S 97R 0:00.16 0:00.06 + 99 0.0 S 31T 0:00.04 0:00.06 +root 103 ?? 0.0 S 31T 0:00.15 0:00.09 /usr/bin/some_command with some parameters + 103 0.0 S 50R 0:24.25 0:09.17 + 103 0.0 S 31T 0:00.00 0:00.00 + 103 0.0 S 4T 0:00.03 0:00.02 + 103 0.0 S 37T 0:00.00 0:00.00 + 103 0.0 S 20T 0:00.01 0:00.00 + 103 0.0 S 31T 0:00.00 0:00.00 + 103 0.0 S 37T 0:00.00 0:00.00 + 103 0.0 S 4T 0:00.00 0:00.00 +root 104 ?? 0.0 S 4T 0:01.01 0:09.28 /usr/bin/some_command with some parameters + 104 0.0 S 4T 2:14.81 5:02.47 + 104 0.0 S 4T 0:00.44 0:01.08 + 104 0.0 S 4T 0:12.12 2:53.73 + 104 0.0 S 4T 0:00.02 0:00.01 + 104 0.0 S 4T 0:15.99 0:06.45 + 104 0.0 S 4T 1:28.59 2:46.98 + 104 0.0 S 4T 0:04.30 0:12.81 + 104 0.0 S 4T 0:08.71 0:17.49 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:59.96 6:08.35 + 104 0.0 S 4T 0:23.11 0:36.48 + 104 0.0 S 4T 1:34.48 2:09.53 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:44.10 0:19.85 + 104 0.0 S 4T 0:00.52 0:00.17 + 104 0.0 S 4T 2:14.01 5:01.32 + 104 0.0 S 4T 2:13.19 5:01.40 + 104 0.0 S 4T 2:13.78 5:03.91 + 104 0.0 S 4T 2:15.10 5:04.41 + 104 0.0 S 4T 2:12.32 5:03.10 + 104 0.0 S 4T 0:12.37 2:53.81 + 104 0.0 S 4T 0:12.10 2:53.81 + 104 0.0 S 4T 2:14.30 5:02.53 + 104 0.0 S 4T 2:10.42 5:00.82 + 104 0.0 S 4T 2:14.03 5:02.43 + 104 0.0 S 4T 2:11.48 5:03.86 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:08.54 0:06.59 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:05.34 0:01.55 + 104 0.0 S 4T 0:03.66 0:01.06 + 104 0.0 S 4T 0:00.18 0:00.03 + 104 0.0 S 4T 0:37.36 0:06.80 + 104 0.0 S 4T 0:01.95 0:01.44 + 104 0.0 S 4T 0:37.35 0:40.82 + 104 0.0 S 4T 0:12.91 0:09.45 + 104 0.0 S 4T 0:02.40 0:00.68 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:08.22 0:07.64 + 104 0.0 S 4T 0:00.17 0:00.02 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.10 0:00.03 + 104 0.0 S 4T 0:01.59 0:01.74 + 104 0.0 S 4T 0:01.66 0:00.43 + 104 0.0 S 4T 0:00.98 0:00.21 + 104 0.0 S 4T 0:01.56 0:00.92 + 104 0.0 S 4T 0:00.02 0:00.00 + 104 0.0 S 4T 0:00.38 0:00.14 + 104 0.0 S 4T 0:00.92 0:00.25 + 104 0.0 S 4T 0:17.54 0:09.81 + 104 0.0 S 4T 0:05.13 0:01.18 + 104 0.0 S 4T 0:11.10 1:14.80 + 104 0.0 S 4T 2:27.08 11:01.59 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.76 0:00.28 + 104 0.3 S 4T 21:15.35 38:57.00 + 104 0.0 S 4T 0:01.84 0:00.31 + 104 0.0 S 4T 0:17.74 0:28.64 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:15.35 0:45.24 + 104 0.0 S 4T 0:00.70 0:00.30 + 104 0.0 S 4T 0:00.62 0:00.27 + 104 0.0 S 4T 0:00.64 0:00.31 + 104 0.0 S 4T 0:00.55 0:00.28 + 104 0.0 S 4T 0:02.64 0:04.64 + 104 0.0 S 4T 0:03.42 0:01.28 + 104 0.0 S 4T 0:05.43 0:02.17 + 104 0.0 S 4T 0:00.18 0:00.05 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:00.18 0:00.05 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:33.60 0:37.38 + 104 0.0 S 4T 0:24.14 0:08.43 + 104 0.0 S 4T 2:35.92 4:31.62 + 104 0.0 S 4T 0:04.54 0:03.01 + 104 0.0 S 4T 1:29.91 1:00.41 + 104 0.0 S 4T 0:04.88 0:01.94 + 104 0.0 S 4T 0:07.19 0:05.44 + 104 0.0 S 4T 0:02.68 0:00.83 + 104 0.0 S 4T 0:56.96 1:49.87 + 104 0.0 S 4T 0:12.03 0:07.99 + 104 0.0 S 4T 0:15.67 0:12.93 + 104 0.0 S 4T 0:25.82 1:14.97 + 104 0.0 S 4T 0:01.66 0:02.08 + 104 0.0 S 4T 0:08.01 0:40.99 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:02.42 0:02.15 + 104 0.0 S 4T 0:00.00 0:00.00 + 104 0.0 S 4T 0:01.54 0:01.59 + 104 0.0 S 4T 0:01.13 0:00.29 + 104 0.0 S 4T 0:02.33 0:00.85 + 104 0.0 S 4T 0:20.38 0:20.06 + 104 0.0 S 4T 1:52.14 25:52.43 + 104 0.0 S 4T 2:07.78 2:26.81 + 104 0.0 S 4T 4:04.54 4:24.85 + 104 0.0 S 4T 0:00.01 0:00.00 + 104 0.0 S 4T 0:00.88 0:02.39 + 104 0.0 S 4T 0:00.98 0:02.32 + 104 0.0 S 4T 0:00.83 0:02.10 + 104 0.3 S 4T 0:00.87 0:02.00 + 104 0.0 S 4T 0:00.62 0:01.89 + 104 0.0 S 4T 0:00.24 0:00.51 + 104 0.0 S 4T 0:00.85 0:04.37 + 104 0.0 S 4T 0:00.01 0:00.01 + 104 0.1 S 4T 0:00.11 0:00.17 + 104 0.0 S 4T 0:00.07 0:00.11 + 104 0.0 S 4T 0:00.02 0:00.03 + 104 0.0 S 4T 0:00.01 0:00.01 + 104 0.0 S 4T 0:00.00 0:00.00 +root 106 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 106 0.0 S 31T 0:00.00 0:00.00 +root 107 ?? 0.0 S 31T 0:24.80 0:41.84 /usr/bin/some_command with some parameters + 107 0.0 S 37T 0:00.00 0:00.00 +root 114 ?? 0.0 S 37T 0:00.03 0:00.00 /usr/bin/some_command with some parameters +root 115 ?? 0.0 S 4T 0:00.78 0:00.70 /usr/bin/some_command with some parameters + 115 0.0 S 4T 0:00.00 0:00.01 + 115 0.0 S 4T 0:00.00 0:00.00 + 115 0.0 S 4T 0:00.00 0:00.00 + 115 0.0 S 4T 0:00.01 0:00.02 +root 116 ?? 0.0 S 20T 0:00.42 0:00.96 /usr/bin/some_command with some parameters + 116 0.0 S 31T 0:00.00 0:00.00 + 116 0.0 S 20T 0:00.00 0:00.00 + 116 0.0 S 31T 0:00.20 0:00.07 + 116 0.0 S 31T 0:00.00 0:00.00 + 116 0.0 S 31T 0:00.00 0:00.00 + 116 0.0 S 31T 0:00.00 0:00.00 +root 117 ?? 0.0 S 31T 0:06.27 0:08.00 /usr/bin/some_command with some parameters + 117 0.0 S 31T 0:00.00 0:00.00 + 117 0.0 S 31T 0:00.01 0:00.00 +root 118 ?? 0.0 S 20T 0:00.02 0:00.01 /usr/bin/some_command with some parameters + 118 0.0 S 20T 0:00.22 0:00.11 + 118 0.0 S 20T 0:00.00 0:00.00 + 118 0.0 S 20T 0:00.00 0:00.00 + 118 0.0 S 20T 0:00.00 0:00.00 + 118 0.0 S 20T 0:00.00 0:00.00 +root 119 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 119 0.0 S 4T 0:00.00 0:00.00 + 119 0.0 S 4T 0:00.01 0:00.01 +_timed 120 ?? 0.0 S 31T 0:03.13 0:00.61 /usr/bin/some_command with some parameters + 120 0.0 S 4T 0:00.02 0:00.00 +root 123 ?? 0.0 S 31T 0:01.98 0:06.46 /usr/bin/some_command with some parameters + 123 0.0 S 31T 0:00.17 0:00.03 + 123 0.0 S 31T 0:00.00 0:00.00 +root 124 ?? 0.0 S 20T 0:00.00 0:00.00 auditd -l + 124 0.0 S 20T 0:00.04 0:00.00 +_locationd 126 ?? 0.0 S 4T 0:00.62 0:00.12 /usr/bin/some_command with some parameters + 126 0.0 S 4T 0:00.04 0:00.05 + 126 0.0 S 4T 0:00.01 0:00.00 + 126 0.0 S 4T 0:00.00 0:00.00 +root 128 ?? 0.0 S 20T 0:00.00 0:00.00 autofsd + 128 0.0 S 20T 0:00.00 0:00.00 +_displaypolicyd 129 ?? 0.0 S 20T 0:00.43 0:00.05 /usr/bin/some_command with some parameters + 129 0.0 S 20T 0:00.00 0:00.00 + 129 0.0 S 20T 0:00.16 0:00.08 + 129 0.0 S 20T 0:00.93 0:00.02 + 129 0.0 S 20T 0:00.05 0:00.00 + 129 0.0 S 20T 0:00.01 0:00.00 +root 132 ?? 0.0 S 4T 0:00.05 0:00.06 /usr/bin/some_command with some parameters + 132 0.0 S 4T 0:00.00 0:00.00 + 132 0.0 S 4T 0:00.00 0:00.00 +_distnote 135 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 135 0.0 S 31T 0:00.76 0:01.09 +root 139 ?? 0.0 S 20T 0:00.06 0:00.02 /usr/bin/some_command with some parameters + 139 0.0 S 20T 1:27.75 0:55.96 + 139 0.0 S 20T 0:04.08 0:00.52 +root 140 ?? 0.0 S 31T 0:00.05 0:00.00 /usr/bin/some_command with some parameters + 140 0.0 S 31T 0:00.00 0:00.00 +root 141 ?? 0.0 S 4T 0:00.07 0:00.03 /usr/bin/some_command with some parameters + 141 0.0 S 4T 0:00.00 0:00.00 + 141 0.0 S 4T 0:00.00 0:00.00 +root 142 ?? 0.0 S 31T 0:00.02 0:00.00 /usr/bin/some_command with some parameters + 142 0.0 S 31T 0:00.01 0:00.00 + 142 0.0 S 31T 0:00.00 0:00.00 +root 144 ?? 0.0 S 31T 0:19.66 0:16.97 /usr/bin/some_command with some parameters + 144 0.0 S 31T 0:11.46 0:04.77 + 144 0.0 S 37T 0:00.03 0:00.01 +root 145 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 145 0.0 S 37T 0:00.08 0:00.04 +root 147 ?? 0.0 S 31T 0:00.30 0:00.25 /usr/bin/some_command with some parameters + 147 0.0 S 31T 0:00.00 0:00.00 +root 148 ?? 0.0 S 55R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 148 0.0 S 19T 0:00.00 0:00.00 + 148 0.0 S 31T 0:00.00 0:00.00 + 148 0.0 S 31T 0:00.01 0:00.00 +root 151 ?? 0.0 S 31T 0:00.19 0:00.27 /usr/bin/some_command with some parameters + 151 0.0 S 31T 0:00.43 0:00.77 + 151 0.0 S 31T 0:00.01 0:00.00 +root 152 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 152 0.0 S 4T 0:00.00 0:00.00 + 152 0.0 S 4T 0:00.00 0:00.00 +root 153 ?? 0.0 S 31T 0:04.59 0:02.48 /usr/bin/some_command with some parameters + 153 0.0 S 31T 0:00.16 0:00.16 + 153 0.0 S 31T 0:00.07 0:00.01 + 153 0.0 S 31T 0:00.01 0:00.00 +_analyticsd 156 ?? 0.0 S 31T 0:00.23 0:00.16 /usr/bin/some_command with some parameters + 156 0.0 S 31T 0:00.01 0:00.00 +root 191 ?? 0.0 S 4T 0:00.05 0:00.04 /usr/bin/some_command with some parameters + 191 0.0 S 4T 0:00.00 0:00.01 + 191 0.0 S 4T 0:00.00 0:00.00 + 191 0.0 S 4T 0:00.00 0:00.00 + 191 0.0 S 4T 0:00.00 0:00.00 +root 195 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 195 0.0 S 4T 0:00.27 0:00.03 + 195 0.0 S 4T 0:00.03 0:00.06 +root 199 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters + 199 0.0 S 31T 0:00.51 0:00.29 +root 206 ?? 0.0 S 4T 0:01.29 0:01.82 /usr/bin/some_command with some parameters + 206 0.0 S 4T 0:00.00 0:00.00 +_trustd 208 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 208 0.0 S 4T 0:00.00 0:00.00 +_networkd 215 ?? 0.0 S 4T 0:01.52 0:00.19 /usr/bin/some_command with some parameters + 215 0.0 S 4T 0:00.01 0:00.00 + 215 0.0 S 4T 0:00.00 0:00.00 +_mdnsresponder 232 ?? 0.0 S 31T 0:02.58 0:03.05 /usr/bin/some_command with some parameters + 232 0.0 S 31T 1:14.24 0:37.44 + 232 0.0 S 37T 0:00.00 0:00.00 +root 248 ?? 0.0 S 31T 0:05.23 0:03.32 /usr/bin/some_command with some parameters + 248 0.0 S 31T 0:00.00 0:00.00 + 248 0.0 S 37T 0:00.00 0:00.00 +root 250 ?? 0.0 S 4T 0:00.14 0:00.05 /usr/bin/some_command with some parameters + 250 0.0 S 4T 0:00.00 0:00.00 +root 252 ?? 0.0 S 31T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 252 0.0 S 31T 0:00.00 0:00.00 +root 254 ?? 0.0 S 20T 0:00.49 0:00.12 /usr/bin/some_command with some parameters + 254 0.0 S 20T 0:00.00 0:00.00 +root 255 ?? 0.0 S 31T 0:25.65 0:13.33 /usr/bin/some_command with some parameters + 255 0.0 S 31T 0:00.25 0:00.03 + 255 0.0 S 31T 0:20.97 0:06.65 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:02.38 0:01.51 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:00.00 0:00.00 + 255 0.0 S 31T 0:00.00 0:00.00 +_coreaudiod 256 ?? 0.0 S 63T 0:00.26 0:00.11 /usr/bin/some_command with some parameters + 256 0.0 S 19T 0:00.00 0:00.00 + 256 0.0 S 31T 0:00.00 0:00.00 + 256 0.0 S 31T 0:00.95 0:00.05 + 256 0.3 S 97R 0:02.08 0:05.95 + 256 0.3 S 97R 0:02.04 0:05.63 + 256 0.3 S 61T 0:00.70 0:00.25 + 256 0.0 S 31T 0:00.00 0:00.00 + 256 0.8 S 61R 0:00.40 0:00.27 +_nsurlsessiond 257 ?? 0.0 S 4T 0:00.38 0:00.15 /usr/bin/some_command with some parameters + 257 0.0 S 4T 0:00.00 0:00.00 +root 263 ?? 0.0 S 4T 0:00.69 0:00.11 /usr/bin/some_command with some parameters + 263 0.0 S 4T 0:00.01 0:00.00 + 263 0.0 S 4T 0:00.00 0:00.00 +_cmiodalassistants 264 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 264 1.1 S 97R 2:00.26 4:45.36 + 264 0.1 S 31T 0:01.94 0:01.28 + 264 0.0 S 31T 0:00.00 0:00.00 +root 269 ?? 0.0 S 20T 1:55.12 1:38.27 /usr/bin/some_command with some parameters + 269 0.0 S 20T 0:00.94 0:00.31 + 269 0.0 S 20T 0:00.00 0:00.00 +_coreaudiod 271 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters + 271 0.0 S 31T 0:00.52 0:00.21 +root 272 ?? 0.0 S 4T 0:00.06 0:00.01 /usr/bin/some_command with some parameters + 272 0.0 S 4T 0:00.00 0:00.00 +_locationd 279 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters + 279 0.0 S 31T 0:00.56 0:00.26 +root 300 ?? 0.0 S 4T 0:00.05 0:00.01 /usr/bin/some_command with some parameters + 300 0.0 S 4T 0:00.07 0:00.01 +_softwareupdate 307 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters + 307 0.0 S 31T 0:00.54 0:00.21 +root 313 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 313 0.0 S 31T 0:00.00 0:00.00 +root 322 ?? 0.0 S 20T 0:00.03 0:00.03 /usr/bin/some_command with some parameters + 322 0.0 S 20T 0:00.00 0:00.00 + 322 0.0 S 20T 0:00.00 0:00.00 +root 337 ?? 0.0 S 4T 0:00.08 0:00.03 /usr/bin/some_command with some parameters + 337 0.0 S 4T 0:00.00 0:00.00 +root 397 ?? 0.0 S 31T 0:14.02 0:12.57 /usr/bin/some_command with some parameters + 397 0.0 S 31T 0:00.00 0:00.00 + 397 0.0 S 31T 0:00.00 0:00.00 + 397 0.0 S 31T 1:32.16 7:28.27 + 397 0.0 R 31T 1:32.29 7:28.11 + 397 0.0 S 31T 1:33.32 7:28.98 + 397 0.0 S 31T 1:32.30 7:28.34 + 397 0.0 S 31T 1:32.30 7:28.44 + 397 0.0 S 31T 1:32.33 7:27.11 + 397 0.0 S 31T 0:10.06 0:22.50 + 397 0.0 S 31T 0:00.25 0:01.70 + 397 0.0 S 31T 2:20.84 5:59.52 + 397 0.0 S 31T 0:00.29 0:00.12 + 397 0.0 S 31T 5:05.54 5:08.26 + 397 0.0 S 4T 0:00.16 0:00.34 + 397 0.0 S 31T 0:00.04 0:00.02 + 397 0.0 S 31T 0:43.11 1:10.88 + 397 0.0 S 31T 0:00.72 0:00.56 + 397 0.0 S 31T 0:13.98 0:26.76 + 397 0.0 S 31T 0:01.40 0:01.34 + 397 0.0 S 31T 0:02.07 0:01.84 + 397 0.0 S 31T 0:00.59 0:00.76 + 397 0.0 S 31T 0:00.37 0:00.67 + 397 0.0 S 31T 0:00.15 0:00.13 + 397 0.0 S 31T 0:00.02 0:00.02 + 397 0.0 R 54R 0:00.01 0:00.02 + 397 0.0 S 31T 0:00.03 0:00.01 +_nsurlsessiond 398 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 398 0.0 S 31T 0:00.53 0:00.21 +root 419 ?? 0.0 S 31T 0:00.71 0:00.84 /usr/bin/some_command with some parameters + 419 0.0 S 31T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.01 0:00.00 + 419 0.0 S 31T 0:00.01 0:00.01 + 419 0.0 S 37T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.00 + 419 0.0 S 4T 0:00.00 0:00.01 + 419 0.0 S 31T 0:00.00 0:00.00 +_driverkit 422 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 422 0.0 S 63R 0:00.09 0:00.03 +_driverkit 423 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 423 0.0 S 63R 0:00.00 0:00.00 +_driverkit 425 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 425 0.0 S 31T 0:00.00 0:00.00 +_driverkit 427 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 427 0.0 S 63R 0:03.41 0:01.46 +_driverkit 428 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 428 0.0 S 31T 0:00.00 0:00.00 +_driverkit 430 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 430 0.0 S 63R 0:00.00 0:00.00 +_driverkit 432 ?? 0.0 S 63R 0:00.94 0:00.30 /usr/bin/some_command with some parameters + 432 0.0 S 31T 0:00.00 0:00.00 +_driverkit 434 ?? 0.0 S 63R 0:00.04 0:00.02 /usr/bin/some_command with some parameters + 434 0.0 S 31T 0:00.00 0:00.00 +_driverkit 435 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 435 0.0 S 31T 0:00.00 0:00.00 +_spotlight 437 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 437 0.0 S 31T 0:00.13 0:00.08 +root 460 ?? 0.0 S 63R 0:00.88 0:00.12 /usr/bin/some_command with some parameters + 460 0.0 S 31T 0:00.00 0:00.00 +_windowserver 474 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 474 0.0 S 31T 0:00.52 0:00.20 +_appinstalld 481 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 481 0.0 S 31T 0:00.50 0:00.19 +root 492 ?? 0.0 S 51R 0:17.61 0:22.47 /usr/bin/some_command with some parameters + 492 0.1 S 55R 4:03.41 2:28.94 + 492 0.0 S 37T 0:03.98 0:03.88 + 492 0.0 S 51T 0:00.00 0:00.00 + 492 0.0 S 37T 0:00.00 0:00.00 + 492 0.0 S 31T 0:00.00 0:00.00 + 492 0.0 S 51R 0:00.00 0:00.00 +_appleevents 501 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 501 0.0 S 31T 0:00.00 0:00.00 +root 503 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 503 0.0 S 4T 0:00.00 0:00.00 +root 508 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 508 23.4 S 4T 0:06.35 0:01.50 + 508 0.0 S 4T 0:00.60 0:00.14 +root 515 ?? 0.0 S 4T 0:00.12 0:00.03 /usr/bin/some_command with some parameters + 515 0.0 S 4T 0:00.00 0:00.00 + 515 0.0 S 4T 0:00.00 0:00.00 +root 528 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 528 0.0 S 4T 0:00.00 0:00.00 +_appleevents 541 ?? 0.0 S 31T 0:00.01 0:00.02 /usr/bin/some_command with some parameters + 541 0.0 S 31T 0:00.50 0:00.19 +root 555 ?? 0.0 S 4T 0:00.04 0:00.01 /usr/bin/some_command with some parameters + 555 0.0 S 4T 0:00.00 0:00.00 +someuser 558 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 558 0.0 S 20T 0:00.86 0:01.75 +root 583 ?? 0.0 S 4T 0:00.06 0:00.01 /usr/bin/some_command with some parameters + 583 0.0 S 4T 0:00.00 0:00.00 +root 631 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 631 0.0 S 20T 0:00.01 0:00.00 +someuser 638 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 638 0.0 S 4T 0:00.01 0:00.00 + 638 0.0 S 4T 0:00.00 0:00.00 +someuser 673 ?? 0.0 S 47T 0:10.83 0:08.96 /usr/bin/some_command with some parameters + 673 0.0 S 19T 0:00.00 0:00.00 + 673 0.0 S 31T 0:00.00 0:00.00 + 673 0.0 S 37T 0:00.00 0:00.00 +_windowserver 677 ?? 26.8 S 79R 100:32.78 206:51.42 /usr/bin/some_command with some parameters + 677 2.8 S 79R 42:18.54 27:27.26 + 677 0.0 S 37T 0:00.00 0:00.00 + 677 0.0 S 79R 0:00.00 0:00.00 + 677 0.0 S 31T 0:00.01 0:00.00 + 677 0.0 S 31T 0:00.16 0:00.20 + 677 0.0 S 31T 0:28.55 0:11.03 + 677 1.7 U 31T 0:23.73 0:19.24 + 677 1.9 S 31T 0:14.59 0:11.88 + 677 0.0 S 79R 0:13.48 0:11.02 + 677 0.6 S 79R 0:01.43 0:00.84 + 677 0.2 S 79T 0:04.30 0:03.51 + 677 21.0 S 79T 0:04.35 0:03.53 + 677 1.2 S 79R 0:00.53 0:00.29 + 677 0.0 S 31T 0:00.00 0:00.00 + 677 1.2 S 79R 0:00.09 0:00.05 +_securityagent 735 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 735 0.0 S 31T 0:00.47 0:00.18 +root 762 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 762 0.0 S 4T 0:00.01 0:00.00 +root 860 ?? 0.0 S 31T 0:01.12 0:01.52 /usr/bin/some_command with some parameters + 860 0.0 S 31T 0:00.58 0:00.39 + 860 0.0 S 31T 0:00.00 0:00.01 +someuser 978 ?? 0.0 S 31T 0:03.65 0:02.88 /usr/bin/some_command with some parameters + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 0T 0:00.02 0:00.02 + 978 0.0 S 31T 0:00.13 0:00.06 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.00 0:00.00 + 978 0.0 S 31T 0:00.10 0:00.02 + 978 0.0 S 31T 0:00.02 0:00.00 + 978 0.0 S 31T 0:00.01 0:00.00 +someuser 2054 ?? 0.0 S 4T 0:01.04 0:01.49 /usr/bin/some_command with some parameters + 2054 0.0 S 4T 0:00.01 0:00.00 + 2054 0.0 S 4T 0:00.00 0:00.00 +someuser 2059 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2059 0.0 S 4T 0:00.01 0:00.01 + 2059 0.0 S 4T 0:00.00 0:00.00 +_appstore 2142 ?? 0.0 S 31T 0:00.01 0:00.02 /usr/bin/some_command with some parameters + 2142 0.0 S 31T 0:00.46 0:00.18 +_assetcache 2155 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters + 2155 0.0 S 31T 0:00.45 0:00.18 +someuser 2156 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2156 0.0 S 4T 0:00.00 0:00.00 +_spotlight 2157 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2157 0.0 S 4T 0:00.00 0:00.00 +_spotlight 2165 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 2165 0.0 S 4T 0:00.00 0:00.00 +someuser 2316 ?? 0.0 S 31T 0:04.73 0:05.82 /usr/bin/some_command with some parameters + 2316 0.0 S 20T 0:00.00 0:00.00 +someuser 2324 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2324 0.0 S 37T 0:00.00 0:00.00 + 2324 0.0 S 37T 0:00.00 0:00.00 +someuser 2325 ?? 0.0 S 31T 0:00.52 0:00.11 /usr/bin/some_command with some parameters + 2325 0.0 S 31T 0:00.00 0:00.00 + 2325 0.0 S 31T 0:00.00 0:00.00 + 2325 0.0 S 31T 0:00.05 0:00.00 + 2325 0.0 S 20T 0:00.00 0:00.00 + 2325 0.0 S 20T 0:00.00 0:00.00 +someuser 2328 ?? 0.0 S 4T 0:01.96 0:01.06 /usr/bin/some_command with some parameters + 2328 0.0 S 4T 0:00.04 0:00.01 + 2328 0.0 S 4T 0:00.00 0:00.00 + 2328 0.0 S 4T 0:00.00 0:00.00 + 2328 0.0 S 4T 0:00.00 0:00.00 +someuser 2329 ?? 0.0 S 31T 0:01.99 0:00.36 /usr/bin/some_command with some parameters + 2329 0.0 S 31T 0:00.00 0:00.00 +someuser 2330 ?? 0.0 S 31T 0:22.30 1:25.11 /usr/bin/some_command with some parameters + 2330 0.0 S 19T 0:00.00 0:00.00 + 2330 0.0 S 31T 0:00.31 0:00.10 + 2330 0.0 S 31T 0:00.00 0:00.00 + 2330 0.0 S 31T 0:00.00 0:00.00 + 2330 0.0 S 31T 0:00.00 0:00.00 + 2330 0.0 S 31T 0:00.00 0:00.00 +someuser 2331 ?? 0.0 S 31T 0:00.06 0:00.04 /usr/bin/some_command with some parameters + 2331 0.0 S 37T 0:00.00 0:00.00 +someuser 2332 ?? 0.0 S 31T 0:00.71 0:00.34 /usr/bin/some_command with some parameters + 2332 0.0 S 31T 0:00.02 0:00.00 + 2332 0.0 S 31T 0:00.00 0:00.00 +someuser 2334 ?? 0.0 S 31T 0:02.77 0:01.40 /usr/bin/some_command with some parameters + 2334 0.0 S 31T 0:00.01 0:00.00 + 2334 0.0 S 37T 0:00.31 0:00.09 + 2334 0.0 S 31T 0:00.00 0:00.00 + 2334 0.0 S 37T 0:00.00 0:00.00 +someuser 2348 ?? 0.0 S 31T 0:02.24 0:00.66 /usr/bin/some_command with some parameters + 2348 0.0 S 31T 0:00.07 0:00.01 + 2348 0.0 S 31T 0:00.00 0:00.00 + 2348 0.0 S 31T 0:00.00 0:00.00 + 2348 0.0 S 31T 0:00.00 0:00.00 + 2348 0.0 S 31T 0:00.00 0:00.00 +someuser 2349 ?? 0.0 S 31T 0:01.18 0:00.45 /usr/bin/some_command with some parameters + 2349 0.0 S 37T 0:00.00 0:00.00 +someuser 2350 ?? 0.0 S 46T 0:14.08 0:12.57 /usr/bin/some_command with some parameters + 2350 0.0 S 46T 0:00.00 0:00.00 + 2350 0.0 S 46T 0:00.59 0:00.20 + 2350 0.0 S 46T 0:00.00 0:00.00 +someuser 2361 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2361 0.0 S 37T 0:00.02 0:00.00 +someuser 2363 ?? 0.0 S 31T 0:00.11 0:00.11 /usr/bin/some_command with some parameters + 2363 0.0 S 31T 0:00.00 0:00.00 + 2363 0.0 S 31T 0:00.00 0:00.00 +someuser 2364 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2364 0.0 S 4T 0:00.05 0:00.07 +someuser 2367 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2367 0.0 S 4T 0:00.00 0:00.00 +someuser 2369 ?? 0.0 S 4T 0:05.78 0:05.65 /usr/bin/some_command with some parameters + 2369 0.0 S 4T 0:00.98 0:00.44 + 2369 0.0 S 4T 0:00.00 0:00.00 + 2369 0.0 S 4T 0:00.00 0:00.00 +someuser 2371 ?? 0.0 S 4T 0:12.60 0:13.23 /usr/bin/some_command with some parameters + 2371 0.0 S 4T 0:00.00 0:00.00 + 2371 0.0 S 4T 0:00.00 0:00.00 + 2371 0.0 S 4T 0:00.00 0:00.00 + 2371 0.0 S 4T 0:00.00 0:00.00 + 2371 0.0 S 4T 0:00.00 0:00.00 +someuser 2383 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2383 0.0 S 31T 0:00.00 0:00.00 +someuser 2389 ?? 0.0 S 31T 0:00.23 0:00.08 /usr/bin/some_command with some parameters + 2389 0.0 S 31T 0:00.00 0:00.00 + 2389 0.0 S 31T 0:00.13 0:00.05 +someuser 2391 ?? 0.0 S 31T 0:00.99 0:00.22 /usr/bin/some_command with some parameters + 2391 0.0 S 4T 0:00.00 0:00.00 +someuser 2397 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2397 0.0 S 4T 0:00.00 0:00.00 +someuser 2399 ?? 0.0 S 46T 0:14.24 0:30.95 /usr/bin/some_command with some parameters + 2399 0.0 S 46T 0:01.32 0:00.87 + 2399 0.0 S 37T 0:00.00 0:00.00 + 2399 0.0 S 46T 0:00.00 0:00.00 +root 2402 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2402 0.0 S 31T 0:00.00 0:00.00 +someuser 2411 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2411 0.0 S 4T 0:00.02 0:00.01 + 2411 0.0 S 4T 0:00.00 0:00.00 +someuser 2412 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2412 0.0 S 4T 0:00.00 0:00.00 + 2412 0.0 S 4T 0:00.00 0:00.00 +someuser 2414 ?? 0.0 S 4T 0:04.02 0:01.69 /usr/bin/some_command with some parameters + 2414 0.0 S 4T 0:00.56 0:00.10 + 2414 0.0 S 4T 0:00.00 0:00.00 + 2414 0.0 S 4T 0:00.00 0:00.00 +someuser 2417 ?? 0.0 S 31T 0:00.80 0:00.29 /usr/bin/some_command with some parameters + 2417 0.0 S 31T 0:00.00 0:00.00 + 2417 0.0 S 31T 0:00.00 0:00.00 +someuser 2420 ?? 0.0 S 4T 0:05.78 0:05.41 /usr/bin/some_command with some parameters + 2420 0.0 S 4T 0:00.00 0:00.00 + 2420 0.0 S 4T 0:00.00 0:00.00 +someuser 2421 ?? 0.0 S 31T 0:00.03 0:00.01 /usr/bin/some_command with some parameters + 2421 0.0 S 31T 0:00.00 0:00.00 +someuser 2425 ?? 0.0 S 31T 0:18.03 0:12.80 /usr/bin/some_command with some parameters + 2425 0.0 S 31T 0:13.33 0:05.22 + 2425 0.0 S 31T 0:00.02 0:00.00 +someuser 2430 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2430 0.0 S 4T 0:00.03 0:00.01 + 2430 0.0 S 4T 0:00.00 0:00.00 +someuser 2441 ?? 0.0 S 4T 0:00.11 0:00.04 /usr/bin/some_command with some parameters + 2441 0.0 S 4T 0:00.00 0:00.00 + 2441 0.0 S 4T 0:00.00 0:00.00 +someuser 2448 ?? 0.0 S 4T 0:00.02 0:00.04 /usr/bin/some_command with some parameters + 2448 0.0 S 4T 0:00.01 0:00.00 +_reportmemoryexception 2456 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 2456 0.0 S 31T 0:00.44 0:00.17 +root 2458 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 2458 0.0 S 4T 0:00.00 0:00.00 +_applepay 2478 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 2478 0.0 S 31T 0:00.44 0:00.17 +_fpsd 2532 ?? 0.0 S 31T 0:00.01 0:00.02 /usr/bin/some_command with some parameters + 2532 0.0 S 31T 0:01.16 0:00.47 +666 2555 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 2555 0.0 S 31T 0:00.11 0:00.08 +newrelic 2556 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 2556 0.0 S 31T 0:00.10 0:00.08 +newrelic 2730 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2730 0.0 S 4T 0:00.00 0:00.00 +666 2731 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2731 0.0 S 4T 0:00.00 0:00.00 +666 2736 ?? 0.0 S 4T 0:01.24 0:01.51 /usr/bin/some_command with some parameters + 2736 0.0 S 4T 0:00.03 0:00.00 +newrelic 2737 ?? 0.0 S 4T 0:01.24 0:01.51 /usr/bin/some_command with some parameters + 2737 0.0 S 4T 0:00.03 0:00.00 +someuser 2827 ?? 3.6 S 47T 3:24.32 12:09.34 /usr/bin/some_command with some parameters + 2827 0.0 S 47T 0:36.63 0:21.76 + 2827 0.0 S 31T 0:04.50 0:05.66 + 2827 0.6 S 47T 0:00.03 0:00.08 + 2827 0.1 S 47T 0:00.15 0:00.10 + 2827 0.0 S 37T 0:00.02 0:00.07 + 2827 0.0 S 47T 0:00.03 0:00.08 + 2827 0.2 S 47T 0:00.02 0:00.06 + 2827 0.2 S 37T 0:00.03 0:00.08 + 2827 0.3 S 37T 0:00.08 0:00.26 + 2827 0.0 S 31T 0:00.00 0:00.00 + 2827 0.0 S 31T 0:00.00 0:00.00 +someuser 2828 ?? 0.0 S 46T 16:03.60 66:28.44 /usr/bin/some_command with some parameters + 2828 0.0 S 31T 0:00.00 0:00.00 + 2828 0.0 S 31T 0:00.46 0:00.34 + 2828 0.0 S 31T 0:00.92 0:01.03 + 2828 0.0 S 31T 10:41.96 10:41.22 + 2828 0.0 S 31T 0:07.12 0:19.95 + 2828 0.0 S 31T 0:00.08 0:00.02 + 2828 0.0 S 31T 0:00.00 0:00.00 + 2828 0.0 S 31T 0:00.01 0:00.00 + 2828 0.0 S 31T 0:10.27 0:00.81 + 2828 0.0 S 31T 0:25.33 2:56.06 + 2828 0.0 S 31T 0:00.00 0:00.00 + 2828 0.0 S 0T 0:00.00 0:00.00 + 2828 0.0 S 31T 0:00.01 0:00.00 + 2828 0.0 S 31T 0:00.28 0:00.22 + 2828 0.0 S 31T 0:00.24 0:00.14 + 2828 0.0 S 46T 0:00.06 0:00.02 + 2828 0.0 S 46T 3:19.50 2:30.69 + 2828 0.0 S 31T 0:01.52 0:01.64 + 2828 0.0 S 31T 0:00.01 0:00.00 + 2828 0.0 S 31T 0:00.31 0:00.05 + 2828 0.0 S 31T 0:00.22 0:00.05 + 2828 0.0 S 19T 0:00.00 0:00.00 + 2828 0.0 S 31T 0:00.03 0:00.01 + 2828 0.0 S 31T 0:01.38 0:00.91 + 2828 0.0 S 31T 0:00.01 0:00.00 + 2828 0.0 S 31T 0:00.01 0:00.00 + 2828 0.0 S 31T 0:00.01 0:00.00 + 2828 0.0 S 31T 0:00.01 0:00.00 + 2828 0.0 S 31T 0:00.01 0:00.00 + 2828 0.0 S 31T 0:00.02 0:00.00 + 2828 0.0 S 31T 0:00.01 0:00.00 + 2828 0.0 S 31T 0:00.13 0:00.15 + 2828 0.0 S 31T 0:00.01 0:00.00 + 2828 0.0 S 0T 0:00.00 0:00.00 + 2828 0.0 S 31T 0:00.00 0:00.00 + 2828 0.0 S 31T 0:00.00 0:00.00 +someuser 2832 ?? 0.2 S 46T 2:47.01 8:47.93 /usr/bin/some_command with some parameters + 2832 0.0 S 31T 0:02.83 0:02.86 + 2832 0.0 S 0T 0:00.16 0:00.11 + 2832 0.2 S 31T 1:48.95 1:18.65 + 2832 0.0 S 31T 0:00.00 0:00.00 + 2832 0.0 S 31T 0:00.00 0:00.00 + 2832 0.0 S 31T 0:04.27 0:09.82 + 2832 0.0 S 31T 0:03.52 0:09.53 + 2832 0.0 S 31T 0:06.21 0:11.86 + 2832 0.0 S 31T 0:00.00 0:00.00 + 2832 0.0 S 31T 0:01.21 0:00.11 + 2832 0.0 S 31T 0:01.20 0:00.11 + 2832 0.0 S 31T 0:01.21 0:00.11 + 2832 0.0 S 31T 0:01.21 0:00.11 + 2832 0.0 S 31T 0:00.00 0:00.00 + 2832 0.0 S 31T 0:00.09 0:00.02 + 2832 0.0 S 31T 0:00.00 0:00.00 + 2832 0.0 S 31T 0:00.01 0:00.00 + 2832 0.0 S 31T 0:00.01 0:00.00 + 2832 0.0 S 31T 0:00.03 0:00.02 + 2832 0.0 S 31T 0:09.04 0:02.71 + 2832 0.0 S 31T 0:00.02 0:00.00 + 2832 0.0 S 46T 0:53.22 0:30.59 + 2832 0.0 S 31T 0:00.00 0:00.00 + 2832 0.0 S 31T 0:00.37 0:00.41 + 2832 0.0 S 0T 0:00.00 0:00.00 + 2832 0.0 S 31T 0:00.00 0:00.00 + 2832 0.0 S 31T 0:00.05 0:00.02 + 2832 0.0 S 31T 0:00.03 0:00.01 + 2832 0.0 S 31T 0:00.00 0:00.00 + 2832 0.0 S 31T 0:00.00 0:00.00 +someuser 2834 ?? 0.0 S 46T 1:31.25 3:58.95 /usr/bin/some_command with some parameters + 2834 0.0 S 31T 0:07.01 0:12.77 + 2834 0.0 S 0T 0:00.12 0:00.11 + 2834 0.0 S 31T 1:24.23 1:37.63 + 2834 0.0 S 31T 0:00.02 0:00.01 + 2834 0.0 S 31T 0:00.00 0:00.00 + 2834 0.0 S 31T 0:00.98 0:00.66 + 2834 0.0 S 31T 0:00.87 0:00.73 + 2834 0.0 S 31T 0:00.98 0:00.75 + 2834 0.0 S 31T 0:00.00 0:00.00 + 2834 0.0 S 31T 0:00.00 0:00.00 + 2834 0.0 S 31T 0:00.11 0:00.04 + 2834 0.0 S 31T 0:00.00 0:00.00 + 2834 0.0 S 31T 0:00.03 0:00.02 + 2834 0.0 S 31T 0:00.01 0:00.00 + 2834 0.0 S 31T 0:00.02 0:00.00 + 2834 0.0 S 31T 0:05.78 0:01.83 + 2834 0.0 S 31T 0:00.04 0:00.02 + 2834 0.0 S 31T 0:01.42 0:00.09 + 2834 0.0 S 31T 0:01.47 0:00.09 + 2834 0.0 S 31T 0:01.44 0:00.09 + 2834 0.0 S 31T 0:01.45 0:00.09 + 2834 0.0 S 46T 0:34.34 0:22.84 + 2834 0.0 S 31T 0:00.03 0:00.02 + 2834 0.0 S 0T 0:00.00 0:00.00 + 2834 0.0 S 31T 0:00.26 0:00.42 + 2834 0.0 S 31T 0:17.54 0:11.36 + 2834 0.0 S 31T 0:10.92 0:07.12 + 2834 0.0 S 37T 0:00.00 0:00.00 +someuser 2836 ?? 0.0 S 28T 1:13.22 2:38.49 /usr/bin/some_command with some parameters + 2836 0.0 S 28T 0:00.00 0:00.00 + 2836 0.0 S 28T 0:00.23 0:00.26 + 2836 0.0 S 0T 0:00.97 0:00.65 + 2836 0.0 S 28T 0:41.17 0:53.42 + 2836 0.0 S 28T 0:00.04 0:00.03 + 2836 0.0 S 28T 0:00.10 0:00.04 + 2836 0.0 S 28T 0:00.03 0:00.02 + 2836 0.0 S 28T 0:00.00 0:00.00 + 2836 0.0 S 28T 0:00.27 0:00.03 + 2836 0.0 S 0T 0:00.73 0:02.01 + 2836 0.0 S 28T 0:00.03 0:00.10 + 2836 0.0 S 28T 2:59.40 5:19.92 + 2836 0.0 S 28T 0:05.51 0:16.63 + 2836 0.0 S 28T 0:00.00 0:00.00 + 2836 0.0 S 28T 0:01.00 0:01.60 + 2836 0.0 S 28T 0:00.01 0:00.01 + 2836 0.0 S 28T 1:31.26 4:01.24 + 2836 0.0 S 28T 0:00.03 0:00.01 + 2836 0.0 S 28T 0:00.00 0:00.00 + 2836 0.0 S 28T 0:01.24 0:00.39 + 2836 0.0 S 28T 2:58.51 0:48.56 + 2836 0.0 S 28T 0:00.63 0:00.03 + 2836 0.0 S 28T 0:50.04 1:15.25 + 2836 0.0 S 28T 0:00.35 0:00.08 + 2836 0.0 S 28T 0:03.12 0:00.91 + 2836 0.0 S 28T 0:01.97 0:00.30 + 2836 0.0 S 28T 0:00.06 0:00.01 + 2836 0.0 S 28T 0:00.04 0:00.01 + 2836 0.0 S 28T 0:02.95 0:00.93 + 2836 0.0 S 28T 0:04.52 0:01.20 + 2836 0.0 S 28T 0:01.26 0:00.90 + 2836 0.0 S 28T 0:35.10 0:24.68 + 2836 0.0 S 28T 0:00.04 0:00.02 + 2836 0.0 S 28T 0:28.24 0:16.75 + 2836 0.0 S 28T 0:05.82 0:01.66 + 2836 0.0 S 28T 0:00.31 0:00.46 + 2836 0.0 S 28T 0:00.05 0:00.01 + 2836 0.0 S 28T 0:00.04 0:00.02 + 2836 0.0 S 19T 0:00.00 0:00.00 + 2836 0.0 S 28T 0:18.64 2:15.96 + 2836 0.0 S 28T 0:00.02 0:00.01 + 2836 0.0 S 28T 0:00.00 0:00.00 + 2836 0.0 S 28T 0:00.00 0:00.00 +someuser 2838 ?? 1.6 S 46T 17:08.54 22:01.14 /usr/bin/some_command with some parameters + 2838 0.0 S 31T 0:01.57 0:00.80 + 2838 0.0 S 31T 0:00.00 0:00.00 + 2838 0.0 S 31T 0:03.78 0:01.50 + 2838 0.0 S 31T 0:10.84 0:06.58 + 2838 0.1 S 31T 1:18.21 0:15.73 + 2838 0.1 S 31T 1:17.68 0:15.27 + 2838 0.1 S 31T 1:17.53 0:14.74 + 2838 0.0 S 31T 0:01.98 0:00.98 + 2838 0.0 S 31T 0:09.71 0:06.75 + 2838 0.1 S 31T 1:19.00 0:17.83 + 2838 0.0 S 46T 2:51.36 1:51.45 + 2838 0.0 S 31T 0:48.84 1:23.12 + 2838 0.0 S 31T 0:08.61 0:03.85 + 2838 1.2 S 31T 6:48.39 2:04.46 + 2838 0.0 S 19T 0:00.00 0:00.00 + 2838 0.0 S 31T 0:00.00 0:00.00 + 2838 0.1 S 31T 0:58.76 0:08.79 + 2838 0.1 S 31T 0:58.62 0:08.80 + 2838 0.1 S 31T 0:56.48 0:09.06 + 2838 0.1 S 31T 0:09.79 0:01.50 + 2838 0.1 S 31T 0:03.29 0:00.54 + 2838 0.1 S 31T 0:03.26 0:00.56 + 2838 0.1 S 31T 0:03.28 0:00.56 + 2838 0.0 S 31T 0:00.03 0:00.02 + 2838 0.7 S 31T 0:07.26 0:09.31 + 2838 0.1 S 31T 0:03.25 0:00.47 + 2838 0.0 S 31R 0:00.16 0:00.13 + 2838 7.0 S 46R 0:03.09 2:03.34 + 2838 8.4 S 31T 0:03.83 1:56.14 + 2838 2.2 S 31T 0:07.17 0:37.92 + 2838 0.4 S 57T 0:01.45 0:06.86 + 2838 1.8 S 31T 0:34.69 0:11.36 + 2838 0.0 S 31T 0:00.58 0:00.89 + 2838 0.5 S 31T 0:01.81 0:19.41 + 2838 6.7 S 46R 0:09.32 1:57.44 + 2838 0.0 S 97R 0:00.96 0:00.84 + 2838 0.7 S 46R 0:00.63 0:13.44 + 2838 0.1 S 97R 0:00.94 0:02.07 + 2838 0.1 S 31T 0:00.33 0:05.60 + 2838 4.1 S 31T 0:02.02 0:51.42 + 2838 4.0 S 31T 0:02.02 0:51.34 + 2838 4.0 S 31T 0:02.02 0:51.43 + 2838 4.0 S 31T 0:02.01 0:51.39 + 2838 4.0 S 31T 0:02.02 0:51.36 + 2838 0.0 S 31T 0:00.04 0:00.03 + 2838 0.0 S 31T 0:00.63 0:16.54 + 2838 0.0 S 31T 0:00.63 0:16.53 + 2838 0.4 S 31T 0:02.25 0:22.82 + 2838 0.0 S 31T 0:00.63 0:16.52 + 2838 4.0 S 31T 0:02.03 0:51.44 + 2838 0.0 S 31T 0:00.11 0:00.05 + 2838 0.0 S 31T 0:00.11 0:00.05 + 2838 0.0 S 31T 0:00.11 0:00.05 + 2838 0.0 S 31T 0:00.11 0:00.05 + 2838 0.0 S 31T 0:00.11 0:00.05 + 2838 0.0 S 31T 0:00.11 0:00.05 + 2838 2.8 S 31T 0:01.00 0:42.64 + 2838 2.9 S 31T 0:01.00 0:42.74 + 2838 2.9 S 31T 0:01.00 0:42.65 + 2838 2.9 S 31T 0:00.98 0:42.73 + 2838 2.9 S 31T 0:01.00 0:42.64 + 2838 2.8 S 31T 0:01.01 0:42.55 + 2838 2.8 S 46T 0:02.01 0:03.06 + 2838 2.4 S 20T 0:00.91 0:01.40 + 2838 0.0 S 37T 0:00.76 0:01.30 + 2838 3.0 S 46T 0:00.52 0:00.80 + 2838 1.0 S 46T 0:00.38 0:00.67 + 2838 0.0 S 31T 0:00.46 0:00.80 + 2838 0.0 S 31T 0:00.00 0:00.00 +someuser 2839 ?? 0.0 S 46T 0:00.00 0:00.00 /usr/bin/some_command with some parameters +someuser 2840 ?? 0.0 S 4T 0:07.76 0:04.96 /usr/bin/some_command with some parameters + 2840 0.0 S 4T 0:01.14 0:00.37 + 2840 0.0 S 4T 0:00.00 0:00.00 + 2840 0.0 S 4T 0:00.00 0:00.00 +someuser 2842 ?? 0.0 S 46T 0:41.47 1:23.77 /usr/bin/some_command with some parameters + 2842 0.0 S 19T 0:00.00 0:00.00 + 2842 0.0 S 46T 0:01.57 0:00.92 + 2842 0.0 S 37T 0:00.00 0:00.00 +someuser 2843 ?? 0.0 S 47T 0:22.58 0:33.09 /usr/bin/some_command with some parameters + 2843 0.0 S 47T 0:01.30 0:00.59 + 2843 0.0 S 37T 0:00.00 0:00.00 +someuser 2844 ?? 0.0 S 46T 0:29.59 0:51.01 /usr/bin/some_command with some parameters + 2844 0.0 S 46T 0:03.21 0:01.96 + 2844 0.0 S 31T 0:00.04 0:00.00 + 2844 0.0 S 20T 0:00.00 0:00.00 + 2844 0.0 S 20T 0:00.00 0:00.00 +someuser 2848 ?? 0.0 S 4T 0:10.12 0:13.79 /usr/bin/some_command with some parameters + 2848 0.0 S 4T 0:00.88 0:00.35 + 2848 0.0 S 4T 0:01.33 0:00.58 + 2848 0.0 S 4T 0:00.00 0:00.00 + 2848 0.0 S 4T 0:00.00 0:00.00 +someuser 2861 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 2861 0.0 S 31T 0:00.33 0:00.04 + 2861 0.0 S 31T 0:00.03 0:00.00 + 2861 0.0 S 31T 0:00.00 0:00.00 +someuser 2872 ?? 0.0 S 4T 0:00.27 0:00.11 /usr/bin/some_command with some parameters + 2872 0.0 S 4T 0:00.04 0:00.01 + 2872 0.0 S 4T 0:00.00 0:00.00 +root 2882 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 2882 0.0 S 4T 0:00.02 0:00.00 +someuser 2885 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 2885 0.0 S 31T 0:00.32 0:00.05 + 2885 0.0 S 31T 0:00.02 0:00.00 + 2885 0.0 S 31T 0:00.00 0:00.00 +someuser 2889 ?? 0.0 S 31T 0:02.32 0:01.03 /usr/bin/some_command with some parameters + 2889 0.0 S 31T 0:00.00 0:00.00 +someuser 2892 ?? 5.5 S 31T 11:25.69 27:31.32 /usr/bin/some_command with some parameters + 2892 0.0 S 31T 0:00.34 0:00.41 + 2892 0.0 S 31T 0:15.36 0:16.26 + 2892 0.0 S 31T 0:04.59 0:06.11 + 2892 0.0 S 0T 0:00.14 0:00.10 + 2892 0.6 S 31T 2:39.79 3:07.42 + 2892 2.0 S 31T 2:16.48 12:19.61 + 2892 0.0 S 31T 0:00.00 0:00.00 + 2892 0.0 S 31T 0:00.00 0:00.00 + 2892 0.0 S 31T 0:00.00 0:00.00 +someuser 2899 ?? 0.0 S 31T 0:00.02 0:00.05 /usr/bin/some_command with some parameters + 2899 0.0 S 31T 0:00.17 0:00.16 + 2899 0.0 S 31T 0:32.75 1:16.03 + 2899 0.0 S 31T 0:00.09 0:00.02 + 2899 0.0 S 0T 0:02.16 0:00.21 + 2899 0.0 S 31T 0:00.01 0:00.00 + 2899 0.0 S 31T 0:00.01 0:00.00 + 2899 0.0 S 31T 0:00.00 0:00.00 +someuser 2913 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 2913 0.0 S 31T 0:00.31 0:00.04 + 2913 0.0 S 31T 0:00.01 0:00.00 + 2913 0.0 S 31T 0:00.00 0:00.00 +someuser 2915 ?? 0.3 S 31T 3:51.63 59:54.10 /usr/bin/some_command with some parameters + 2915 0.0 S 31T 0:19.84 0:22.22 + 2915 0.0 S 0T 0:00.11 0:00.10 + 2915 0.6 S 31T 2:43.61 3:46.09 + 2915 0.0 S 31T 0:00.01 0:00.00 + 2915 2.8 S 31T 3:57.14 19:14.88 + 2915 0.0 S 31T 0:00.94 0:01.53 + 2915 0.0 S 31T 0:07.65 0:39.99 + 2915 0.0 S 31T 0:07.57 0:39.59 + 2915 0.0 S 31T 0:07.61 0:39.10 + 2915 0.0 S 31T 0:07.67 0:40.79 + 2915 0.0 S 31T 0:00.11 0:00.48 + 2915 0.0 S 31T 0:00.24 0:00.46 + 2915 0.0 S 31T 0:00.06 0:00.02 + 2915 0.0 S 31T 0:00.03 0:00.55 + 2915 0.0 S 31T 0:00.03 0:00.48 + 2915 0.0 S 31T 0:00.01 0:00.22 + 2915 0.0 S 31T 0:00.01 0:00.10 + 2915 0.0 S 31T 0:00.00 0:00.06 +someuser 2924 ?? 0.0 S 31T 0:23.34 0:27.91 /usr/bin/some_command with some parameters + 2924 0.0 S 31T 0:00.19 0:00.41 + 2924 0.0 S 31T 0:00.06 0:00.01 + 2924 0.0 S 31T 0:00.14 0:00.11 + 2924 0.0 S 0T 0:00.14 0:00.10 + 2924 0.0 S 31T 0:20.16 0:24.92 + 2924 0.3 S 31T 1:00.63 3:05.41 + 2924 0.0 S 31T 0:00.00 0:00.00 +someuser 2925 ?? 0.0 S 31T 0:00.02 0:00.06 /usr/bin/some_command with some parameters + 2925 0.0 S 31T 0:00.10 0:00.08 + 2925 0.0 S 31T 0:20.11 0:31.57 + 2925 0.0 S 31T 0:00.09 0:00.02 + 2925 0.0 S 0T 0:00.83 0:00.11 + 2925 0.0 S 31T 0:00.15 0:00.05 + 2925 0.0 S 31T 0:00.00 0:00.00 +someuser 2928 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 2928 0.0 S 31T 0:00.54 0:00.06 + 2928 0.0 S 31T 0:00.01 0:00.00 + 2928 0.0 S 31T 0:00.00 0:00.00 +someuser 2930 ?? 0.0 S 31T 0:00.54 0:00.82 /usr/bin/some_command with some parameters + 2930 0.0 S 31T 0:00.00 0:00.00 +root 2948 ?? 0.0 S 31T 0:03.08 0:01.19 /usr/bin/some_command with some parameters + 2948 0.0 S 31T 0:00.00 0:00.00 +someuser 2949 ?? 0.0 S 31T 0:00.02 0:00.01 /usr/bin/some_command with some parameters +someuser 2984 ?? 0.0 S 31T 18:41.03 38:08.48 /usr/bin/some_command with some parameters + 2984 0.0 S 31T 0:00.32 0:00.40 + 2984 0.0 S 31T 0:00.01 0:00.00 + 2984 0.0 S 31T 2:54.77 3:38.20 + 2984 0.0 S 31T 3:30.37 14:49.00 + 2984 0.0 S 31T 0:07.78 0:16.90 + 2984 0.0 S 31T 0:00.82 0:00.48 + 2984 0.0 S 31T 0:05.01 0:10.98 + 2984 0.0 S 54T 0:06.15 0:14.80 + 2984 0.0 S 0T 0:00.36 0:00.38 + 2984 0.0 S 31T 0:00.00 0:00.00 +someuser 2986 ?? 0.0 S 31T 0:00.07 0:00.13 /usr/bin/some_command with some parameters + 2986 0.0 S 31T 0:00.68 0:00.88 + 2986 0.1 S 31T 8:41.43 28:57.47 + 2986 0.0 S 31T 0:00.08 0:00.03 + 2986 0.0 S 31T 0:00.12 0:00.08 + 2986 0.0 S 0T 0:04.88 0:00.32 + 2986 0.0 S 0T 0:03.61 0:00.24 + 2986 0.0 S 31T 0:00.01 0:00.01 + 2986 0.0 S 31T 0:00.06 0:00.01 + 2986 0.0 S 31T 0:00.07 0:00.01 + 2986 0.0 S 31T 0:00.01 0:00.00 + 2986 0.0 S 31T 0:00.00 0:00.00 + 2986 0.0 S 31T 0:00.00 0:00.00 +someuser 2991 ?? 0.0 S 31T 0:18.98 0:40.44 /usr/bin/some_command with some parameters + 2991 0.0 S 31T 0:00.00 0:00.00 + 2991 0.0 S 0T 0:00.13 0:00.10 + 2991 0.0 S 31T 0:09.96 0:11.54 + 2991 0.0 S 31T 0:00.09 0:00.02 + 2991 0.0 S 31T 0:00.00 0:00.00 + 2991 0.0 S 31T 0:00.01 0:00.00 + 2991 0.0 S 31T 0:00.00 0:00.00 +someuser 2997 ?? 0.0 S 31T 0:20.21 1:53.46 /usr/bin/some_command with some parameters + 2997 0.0 S 31T 0:00.00 0:00.00 + 2997 0.0 S 0T 0:00.13 0:00.10 + 2997 0.0 S 31T 0:15.84 0:25.18 + 2997 0.0 S 31T 0:00.01 0:00.00 + 2997 0.0 S 31T 0:00.10 0:00.26 + 2997 0.0 S 31T 0:00.00 0:00.00 + 2997 0.0 S 31T 0:00.02 0:00.01 + 2997 0.0 S 31T 0:00.01 0:00.01 + 2997 0.0 S 31T 0:00.00 0:00.01 + 2997 0.0 S 31T 0:00.01 0:00.02 + 2997 0.0 S 31T 0:00.00 0:00.00 + 2997 0.0 S 31T 0:00.23 0:00.13 + 2997 0.0 S 31T 0:00.00 0:00.00 + 2997 0.0 S 31T 0:00.01 0:00.00 +someuser 2998 ?? 0.0 S 31T 0:02.59 0:03.34 /usr/bin/some_command with some parameters + 2998 0.0 S 31T 0:00.01 0:00.00 + 2998 0.0 S 0T 0:00.15 0:00.10 + 2998 0.0 S 31T 0:01.21 0:01.23 + 2998 0.0 S 31T 0:00.01 0:00.00 + 2998 0.0 S 31T 0:00.01 0:00.00 + 2998 0.0 S 31T 0:00.00 0:00.00 + 2998 0.0 S 31T 0:00.00 0:00.00 + 2998 0.0 S 31T 0:00.00 0:00.00 + 2998 0.0 S 31T 0:00.00 0:00.00 + 2998 0.0 S 31T 0:00.00 0:00.00 + 2998 0.0 S 31T 0:00.00 0:00.00 + 2998 0.0 S 31T 0:00.27 0:00.12 + 2998 0.0 S 31T 0:00.11 0:00.03 + 2998 0.0 S 31T 0:00.00 0:00.00 +someuser 2999 ?? 0.0 S 31T 0:46.82 1:58.19 /usr/bin/some_command with some parameters + 2999 0.0 S 31T 0:00.00 0:00.00 + 2999 0.0 S 0T 0:00.14 0:00.09 + 2999 0.0 S 31T 0:15.19 0:20.96 + 2999 0.0 S 31T 0:00.01 0:00.02 + 2999 0.0 S 31T 0:00.37 0:01.38 + 2999 0.0 S 31T 0:00.00 0:00.00 + 2999 0.0 S 31T 0:00.03 0:00.04 + 2999 0.0 S 31T 0:00.03 0:00.04 + 2999 0.0 S 31T 0:00.02 0:00.04 + 2999 0.0 S 31T 0:00.03 0:00.05 + 2999 0.0 S 31T 0:00.00 0:00.00 + 2999 0.0 S 31T 0:00.01 0:00.04 + 2999 0.0 S 31T 0:00.48 0:00.15 + 2999 0.0 S 31T 0:00.00 0:00.00 + 2999 0.0 S 31T 0:00.00 0:00.00 +someuser 3016 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 3016 0.0 S 20T 0:00.00 0:00.00 +someuser 3033 ?? 0.0 S 31T 1:29.64 4:29.00 /usr/bin/some_command with some parameters + 3033 0.0 S 31T 0:00.72 0:00.47 + 3033 0.0 S 31T 0:00.01 0:00.00 + 3033 0.0 S 31T 0:00.15 0:00.11 + 3033 0.0 S 0T 0:00.12 0:00.09 + 3033 0.0 S 31T 0:40.64 0:51.82 + 3033 0.0 S 31T 0:33.94 2:49.91 + 3033 0.0 S 31T 0:00.00 0:00.00 +someuser 3059 ?? 0.0 S 31T 0:04.49 0:00.80 gpg-agent --homedir /usr/bin/some_command with some parameters +someuser 3062 ?? 0.0 S 31T 0:00.62 0:00.24 /usr/bin/some_command with some parameters + 3062 0.0 S 19T 0:00.00 0:00.00 + 3062 0.0 S 31T 0:00.01 0:00.00 +someuser 3063 ?? 0.0 S 4T 0:00.14 0:00.12 /usr/bin/some_command with some parameters + 3063 0.0 S 4T 0:00.01 0:00.01 + 3063 0.0 S 4T 0:00.01 0:00.02 + 3063 0.0 S 4T 0:00.00 0:00.00 + 3063 0.0 S 4T 0:00.00 0:00.00 +someuser 3071 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 3071 0.0 S 20T 0:00.00 0:00.00 +someuser 3073 ?? 0.2 S 20T 5:16.24 10:35.13 /usr/bin/some_command with some parameters + 3073 0.0 S 20T 0:00.08 0:00.02 + 3073 0.0 S 20T 0:00.16 0:00.12 + 3073 0.2 S 20T 4:03.29 3:27.79 + 3073 0.0 S 20T 0:00.00 0:00.00 + 3073 0.0 S 20T 0:01.45 0:21.67 + 3073 0.0 S 20T 0:01.43 0:21.43 + 3073 0.0 S 20T 0:01.41 0:21.23 + 3073 0.0 S 20T 0:00.00 0:00.00 + 3073 0.0 S 20T 0:00.00 0:00.00 + 3073 0.0 S 20T 0:00.00 0:00.00 + 3073 0.0 S 20T 0:00.00 0:00.00 + 3073 0.0 S 20T 0:00.00 0:00.00 + 3073 0.0 S 20T 0:00.09 0:00.02 + 3073 0.0 S 20T 0:00.00 0:00.00 + 3073 0.0 S 20T 0:00.01 0:00.00 + 3073 0.0 S 20T 0:00.00 0:00.00 + 3073 0.0 S 20T 0:00.01 0:00.00 + 3073 0.0 S 20T 0:00.27 0:00.11 + 3073 0.0 S 20T 0:01.36 0:00.77 + 3073 0.0 S 20T 0:00.01 0:00.00 + 3073 0.0 S 20T 0:00.31 0:00.44 + 3073 0.0 S 20T 0:00.00 0:00.00 + 3073 0.1 S 20T 0:00.25 0:00.32 + 3073 0.0 S 20T 0:00.22 0:00.28 + 3073 0.1 S 20T 0:00.26 0:00.34 + 3073 0.0 S 20T 0:00.00 0:00.00 + 3073 0.0 S 20T 0:00.00 0:00.00 + 3073 0.0 S 20T 0:00.00 0:00.00 +someuser 3080 ?? 0.0 S 46T 0:03.17 0:01.25 /usr/bin/some_command with some parameters + 3080 0.0 S 46T 0:00.45 0:00.09 + 3080 0.0 S 37T 0:00.00 0:00.00 +someuser 3083 ?? 0.0 S 4T 0:06.89 0:05.98 /usr/bin/some_command with some parameters + 3083 0.0 S 4T 0:00.00 0:00.00 +someuser 3088 ?? 0.0 S 20T 0:16.14 0:15.99 /usr/bin/some_command with some parameters + 3088 0.0 S 20T 0:00.00 0:00.00 + 3088 0.0 S 20T 0:29.70 0:28.62 + 3088 0.0 S 20T 0:00.01 0:00.01 + 3088 0.0 S 20T 0:00.01 0:00.01 + 3088 0.0 S 20T 0:00.00 0:00.00 + 3088 0.0 S 20T 0:00.00 0:00.00 + 3088 0.0 S 20T 0:08.88 0:44.56 + 3088 0.0 S 20T 0:05.83 0:08.89 + 3088 0.0 S 20T 0:00.38 0:00.11 + 3088 0.0 S 20T 0:41.72 0:28.30 + 3088 0.0 S 20T 0:00.13 0:00.10 + 3088 0.0 S 20T 0:03.64 0:01.80 + 3088 0.0 S 20T 0:01.36 0:00.56 + 3088 0.0 S 20T 0:00.00 0:00.00 + 3088 0.0 S 20T 0:01.67 0:00.69 + 3088 0.0 S 20T 0:00.00 0:00.00 + 3088 0.0 S 20T 0:02.85 0:05.42 + 3088 0.0 S 20T 0:30.76 0:26.13 + 3088 0.0 S 20T 0:00.03 0:00.01 + 3088 0.0 S 20T 0:00.00 0:00.01 + 3088 0.0 S 20T 0:00.16 0:00.11 + 3088 0.0 S 20T 0:00.04 0:00.05 + 3088 0.0 S 20T 0:02.61 0:01.24 + 3088 0.0 S 20T 0:01.54 0:00.83 + 3088 0.0 S 20T 0:00.74 0:00.29 + 3088 0.0 S 20T 0:01.15 0:00.46 + 3088 0.0 S 20T 0:02.32 0:01.10 + 3088 0.0 S 20T 0:03.38 0:01.60 + 3088 0.0 S 20T 0:01.86 0:00.84 + 3088 0.0 S 20T 0:01.69 0:00.88 + 3088 0.0 S 20T 0:02.14 0:01.09 + 3088 0.0 S 20T 0:00.45 0:00.19 + 3088 0.0 S 20T 0:01.06 0:00.48 + 3088 0.0 S 20T 0:00.01 0:00.00 + 3088 0.0 S 20T 0:00.00 0:00.01 + 3088 0.0 S 20T 0:00.02 0:00.01 + 3088 0.0 S 20T 0:00.01 0:00.00 + 3088 0.0 S 20T 0:00.00 0:00.00 +someuser 3091 ?? 0.0 S 4T 0:00.01 0:00.04 /usr/bin/some_command with some parameters + 3091 0.0 S 4T 0:00.02 0:00.00 +someuser 3093 ?? 0.0 S 31T 0:00.28 0:00.17 /usr/bin/some_command with some parameters + 3093 0.0 S 31T 0:00.01 0:00.00 + 3093 0.0 S 0T 0:00.13 0:00.10 + 3093 0.0 S 31T 0:00.27 0:00.10 + 3093 0.0 S 31T 0:00.03 0:00.02 + 3093 0.0 S 31T 0:00.00 0:00.00 +someuser 3094 ?? 0.0 S 31T 0:00.03 0:00.07 /usr/bin/some_command with some parameters + 3094 0.0 S 31T 0:00.09 0:00.07 + 3094 0.0 S 31T 0:31.44 0:58.88 + 3094 0.0 S 31T 0:00.08 0:00.03 + 3094 0.0 S 31T 0:00.00 0:00.00 + 3094 0.0 S 0T 0:00.12 0:00.01 + 3094 0.0 S 31T 0:00.27 0:00.04 +someuser 3095 ?? 0.0 S 31T 1:29.01 11:00.31 /usr/bin/some_command with some parameters + 3095 0.0 S 31T 0:00.06 0:00.05 + 3095 0.0 S 0T 0:00.12 0:00.10 + 3095 0.0 S 31T 0:43.62 1:04.66 + 3095 0.0 S 31T 0:00.01 0:00.00 + 3095 0.0 S 31T 1:00.96 4:59.78 + 3095 0.0 S 31T 0:00.81 0:00.80 + 3095 0.0 S 31T 0:02.30 0:07.20 + 3095 0.0 S 31T 0:02.30 0:07.27 + 3095 0.0 S 31T 0:02.39 0:07.26 + 3095 0.0 S 31T 0:02.31 0:07.21 + 3095 0.0 S 31T 0:00.01 0:00.02 + 3095 0.0 S 31T 0:00.11 0:00.04 + 3095 0.0 S 31T 0:00.01 0:00.00 + 3095 0.0 S 31T 0:00.36 0:00.14 + 3095 0.0 S 31T 0:00.05 0:00.01 +someuser 3146 ?? 0.0 S 46T 0:20.20 0:26.52 /usr/bin/some_command with some parameters + 3146 0.0 S 46T 0:01.17 0:00.58 + 3146 0.0 S 31T 0:00.00 0:00.00 + 3146 0.0 S 31T 0:00.00 0:00.00 +someuser 3181 ?? 0.3 S 46T 9:32.32 10:30.99 /usr/bin/some_command with some parameters + 3181 0.0 S 46T 0:01.30 0:00.81 + 3181 0.0 S 46T 0:00.00 0:00.00 + 3181 0.0 S 46T 0:00.00 0:00.00 + 3181 0.0 S 46T 0:00.00 0:00.00 + 3181 0.0 S 46T 0:00.00 0:00.00 + 3181 0.0 S 46T 0:00.00 0:00.00 + 3181 0.0 S 46T 0:00.00 0:00.00 + 3181 0.0 S 46T 0:00.00 0:00.00 + 3181 0.0 S 46T 0:00.00 0:00.00 + 3181 0.0 S 37T 0:00.00 0:00.00 + 3181 0.0 S 31T 0:00.00 0:00.00 +someuser 3211 ?? 0.0 S 20T 0:00.13 0:00.17 /usr/bin/some_command with some parameters + 3211 0.0 S 20T 0:00.10 0:00.01 + 3211 0.0 S 20T 0:00.18 0:00.12 + 3211 0.0 S 20T 0:00.15 0:00.12 + 3211 0.0 S 20T 2:46.82 1:29.37 + 3211 0.0 S 20T 3:44.57 16:11.83 + 3211 0.0 S 20T 0:00.00 0:00.00 +someuser 3288 ?? 0.0 S 20T 0:00.05 0:00.04 /usr/bin/some_command with some parameters + 3288 0.0 S 20T 0:00.07 0:00.02 + 3288 0.0 S 20T 0:00.15 0:00.12 + 3288 0.1 S 20T 1:07.39 2:51.00 + 3288 0.0 S 20T 0:00.09 0:00.02 + 3288 0.0 S 20T 0:00.02 0:00.01 + 3288 0.0 S 20T 0:00.00 0:00.00 +someuser 3312 ?? 0.0 S 31T 0:33.92 2:38.45 /usr/bin/some_command with some parameters + 3312 0.0 S 31T 0:00.01 0:00.00 + 3312 0.0 S 0T 0:00.12 0:00.09 + 3312 0.0 S 31T 0:05.15 0:04.22 + 3312 0.0 S 31T 0:00.01 0:00.00 + 3312 0.0 S 31T 0:04.08 0:15.29 + 3312 0.0 S 31T 0:00.00 0:00.00 + 3312 0.0 S 31T 0:00.16 0:00.52 + 3312 0.0 S 31T 0:00.28 0:00.54 + 3312 0.0 S 31T 0:00.21 0:00.71 + 3312 0.0 S 31T 0:00.44 0:00.74 + 3312 0.0 S 31T 0:00.01 0:00.03 + 3312 0.0 S 31T 0:00.85 0:00.21 + 3312 0.0 S 31T 0:00.00 0:00.00 + 3312 0.0 S 31T 0:01.40 0:00.32 + 3312 0.0 S 31T 0:00.03 0:00.02 + 3312 0.0 S 31T 0:00.02 0:00.01 +someuser 3337 ?? 0.0 S 31T 0:51.88 2:16.56 /usr/bin/some_command with some parameters + 3337 0.0 S 31T 0:00.01 0:00.00 + 3337 0.0 S 0T 0:00.13 0:00.09 + 3337 0.0 S 31T 0:16.44 0:19.64 + 3337 0.0 S 31T 0:00.01 0:00.00 + 3337 0.0 S 31T 0:00.65 0:01.07 + 3337 0.0 S 31T 0:00.00 0:00.00 + 3337 0.0 S 31T 0:00.07 0:00.05 + 3337 0.0 S 31T 0:00.06 0:00.04 + 3337 0.0 S 31T 0:00.07 0:00.05 + 3337 0.0 S 31T 0:00.05 0:00.04 + 3337 0.0 S 31T 0:00.00 0:00.00 + 3337 0.0 S 31T 0:00.50 0:00.20 + 3337 0.0 S 31T 0:00.42 0:00.16 + 3337 0.0 S 31T 0:00.13 0:00.15 + 3337 0.0 S 31T 0:00.04 0:00.03 +someuser 3543 ?? 0.0 S 31T 0:31.51 1:34.86 /usr/bin/some_command with some parameters + 3543 0.0 S 31T 0:00.13 0:00.01 + 3543 0.0 S 31T 0:00.15 0:00.11 + 3543 0.0 S 0T 0:00.15 0:00.11 + 3543 0.0 S 31T 0:00.00 0:00.00 + 3543 0.0 S 31T 0:00.05 0:00.03 + 3543 0.0 S 31T 0:00.05 0:00.02 + 3543 0.0 S 31T 0:00.02 0:00.01 + 3543 0.0 S 31T 0:00.00 0:00.00 + 3543 0.0 S 31T 0:00.00 0:00.00 +someuser 3544 ?? 0.0 S 31T 0:16.69 0:54.27 /usr/bin/some_command with some parameters + 3544 0.0 S 31T 0:00.03 0:00.00 + 3544 0.0 S 0T 0:00.12 0:00.11 + 3544 0.0 S 31T 0:10.40 0:09.03 + 3544 0.0 S 31T 0:00.01 0:00.00 + 3544 0.0 S 31T 0:00.15 0:00.09 + 3544 0.0 S 31T 0:00.00 0:00.00 + 3544 0.0 S 31T 0:00.00 0:00.00 + 3544 0.0 S 31T 0:00.01 0:00.00 + 3544 0.0 S 31T 0:00.01 0:00.00 + 3544 0.0 S 31T 0:00.00 0:00.00 + 3544 0.0 S 31T 0:00.00 0:00.00 + 3544 0.0 S 31T 0:00.00 0:00.00 + 3544 0.0 S 31T 0:00.01 0:00.01 + 3544 0.0 S 31T 0:00.00 0:00.00 + 3544 0.0 S 31T 0:00.00 0:00.00 +someuser 3545 ?? 0.0 S 31T 0:08.99 0:27.17 /usr/bin/some_command with some parameters + 3545 0.0 S 31T 0:00.03 0:00.00 + 3545 0.0 S 0T 0:00.13 0:00.10 + 3545 0.0 S 31T 0:06.04 0:05.41 + 3545 0.0 S 31T 0:00.01 0:00.00 + 3545 0.0 S 31T 0:00.09 0:00.05 + 3545 0.0 S 31T 0:00.00 0:00.00 + 3545 0.0 S 31T 0:00.00 0:00.00 + 3545 0.0 S 31T 0:00.00 0:00.00 + 3545 0.0 S 31T 0:00.00 0:00.00 + 3545 0.0 S 31T 0:00.00 0:00.00 + 3545 0.0 S 31T 0:00.00 0:00.00 + 3545 0.0 S 31T 0:00.00 0:00.00 + 3545 0.0 S 31T 0:00.00 0:00.00 + 3545 0.0 S 31T 0:00.00 0:00.00 +someuser 3564 ?? 0.2 S 31T 1:56.38 11:04.14 /usr/bin/some_command with some parameters + 3564 0.0 S 31T 0:00.02 0:00.00 + 3564 0.0 S 0T 0:00.12 0:00.10 + 3564 0.0 S 31T 0:30.11 0:36.49 + 3564 0.0 S 31T 0:00.00 0:00.00 + 3564 0.0 S 31T 0:18.17 0:51.64 + 3564 0.0 S 31T 0:00.00 0:00.00 + 3564 0.0 S 31T 0:00.01 0:00.01 + 3564 0.0 S 31T 0:00.02 0:00.01 + 3564 0.0 S 31T 0:00.02 0:00.02 + 3564 0.0 S 31T 0:00.02 0:00.02 + 3564 0.0 S 31T 0:00.00 0:00.00 + 3564 0.0 S 31T 0:00.00 0:00.00 + 3564 0.0 S 31T 0:00.00 0:00.00 + 3564 0.0 S 31T 0:00.00 0:00.00 + 3564 0.0 S 31T 0:00.00 0:00.00 + 3564 0.0 S 31T 0:00.02 0:00.02 + 3564 0.0 S 31T 0:00.00 0:00.00 +someuser 3566 ?? 0.0 S 31T 0:00.89 0:00.49 /usr/bin/some_command with some parameters + 3566 0.0 S 31T 0:00.07 0:00.01 + 3566 0.0 S 0T 0:00.14 0:00.11 + 3566 0.0 S 31T 0:00.22 0:00.04 + 3566 0.0 S 31T 0:00.01 0:00.00 + 3566 0.0 S 31T 0:00.01 0:00.00 + 3566 0.0 S 31T 0:00.00 0:00.00 + 3566 0.0 S 31T 0:00.00 0:00.00 + 3566 0.0 S 31T 0:00.00 0:00.00 + 3566 0.0 S 31T 0:00.00 0:00.00 + 3566 0.0 S 31T 0:00.00 0:00.00 + 3566 0.0 S 31T 0:00.00 0:00.00 + 3566 0.0 S 31T 0:00.00 0:00.00 + 3566 0.0 S 31T 0:00.16 0:00.12 + 3566 0.0 S 31T 0:00.00 0:00.00 +someuser 3569 ?? 0.1 S 31T 2:18.42 13:40.07 /usr/bin/some_command with some parameters + 3569 0.0 S 31T 0:00.02 0:00.00 + 3569 0.0 S 0T 0:00.12 0:00.10 + 3569 0.0 S 31T 0:30.67 0:38.85 + 3569 0.0 S 31T 0:00.01 0:00.00 + 3569 0.0 S 31T 0:21.88 1:15.72 + 3569 0.0 S 31T 0:00.00 0:00.00 + 3569 0.0 S 31T 0:00.15 0:00.27 + 3569 0.0 S 31T 0:00.14 0:00.26 + 3569 0.0 S 31T 0:00.13 0:00.29 + 3569 0.0 S 31T 0:00.13 0:00.32 + 3569 0.0 S 31T 0:00.00 0:00.00 + 3569 0.0 S 31T 0:00.00 0:00.00 + 3569 0.0 S 31T 0:00.00 0:00.00 + 3569 0.0 S 31T 0:00.00 0:00.00 + 3569 0.0 S 31T 0:00.00 0:00.00 + 3569 0.0 S 31T 0:00.00 0:00.00 + 3569 0.0 S 31T 0:00.00 0:00.00 + 3569 0.0 S 31T 0:00.00 0:00.00 + 3569 0.0 S 31T 0:00.00 0:00.00 + 3569 0.0 S 31T 0:00.03 0:00.02 + 3569 0.0 S 31T 0:00.00 0:00.00 +someuser 3571 ?? 0.0 S 31T 0:00.97 0:00.52 /usr/bin/some_command with some parameters + 3571 0.0 S 31T 0:00.07 0:00.01 + 3571 0.0 S 0T 0:00.14 0:00.11 + 3571 0.0 S 31T 0:00.23 0:00.05 + 3571 0.0 S 31T 0:00.01 0:00.00 + 3571 0.0 S 31T 0:00.01 0:00.00 + 3571 0.0 S 31T 0:00.00 0:00.00 + 3571 0.0 S 31T 0:00.00 0:00.00 + 3571 0.0 S 31T 0:00.00 0:00.00 + 3571 0.0 S 31T 0:00.00 0:00.00 + 3571 0.0 S 31T 0:00.00 0:00.00 + 3571 0.0 S 31T 0:00.00 0:00.00 + 3571 0.0 S 31T 0:00.00 0:00.00 + 3571 0.0 S 31T 0:00.15 0:00.12 + 3571 0.0 S 31T 0:00.00 0:00.00 +someuser 3623 ?? 1.6 S 20T 3:07.53 26:05.10 /usr/bin/some_command with some parameters + 3623 0.0 S 20T 0:00.02 0:00.00 + 3623 0.0 S 20T 0:00.14 0:00.12 + 3623 0.1 S 20T 2:59.93 3:58.42 + 3623 0.0 S 20T 0:00.00 0:00.00 + 3623 0.0 S 20T 3:50.67 16:36.39 + 3623 0.0 S 20T 0:00.00 0:00.00 + 3623 0.0 S 20T 0:07.04 0:08.13 + 3623 0.0 S 20T 0:07.15 0:08.16 + 3623 0.0 S 20T 0:07.15 0:08.21 + 3623 0.0 S 20T 0:07.13 0:08.20 + 3623 0.0 S 20T 0:00.00 0:00.00 + 3623 0.0 S 20T 0:00.02 0:00.01 + 3623 0.0 S 20T 0:00.01 0:00.00 + 3623 0.0 S 20T 0:00.01 0:00.00 + 3623 0.0 S 20T 0:00.01 0:00.00 + 3623 0.0 S 20T 0:00.01 0:00.00 + 3623 0.0 S 20T 0:00.77 0:00.26 + 3623 0.0 S 20T 0:00.00 0:00.00 + 3623 0.0 S 20T 0:00.10 0:00.56 + 3623 0.0 S 20T 0:00.11 0:00.62 + 3623 0.0 S 20T 0:00.09 0:00.60 + 3623 0.0 S 20T 0:00.11 0:00.66 + 3623 0.0 S 20T 0:00.11 0:00.62 + 3623 0.0 S 20T 0:00.14 0:00.70 + 3623 0.0 S 20T 0:00.06 0:00.35 + 3623 0.0 S 20T 0:00.05 0:00.30 + 3623 0.0 S 20T 0:00.00 0:00.00 +someuser 3656 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 3656 0.0 S 4T 0:00.00 0:00.00 +root 3732 ?? 0.0 S 20T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 3732 0.4 S 20T 0:57.98 7:08.01 + 3732 0.0 S 20T 0:00.00 0:00.00 + 3732 0.0 S 20T 0:00.46 0:00.25 + 3732 0.0 S 20T 0:00.00 0:00.00 + 3732 0.0 S 20T 0:00.00 0:00.00 + 3732 0.0 S 20T 0:11.95 0:05.79 +someuser 3736 ?? 0.0 S 20T 0:00.33 0:00.26 /usr/bin/some_command with some parameters + 3736 0.0 S 20T 1:11.05 4:16.13 + 3736 0.0 S 20T 0:00.00 0:00.00 + 3736 0.0 S 20T 0:00.46 0:00.25 + 3736 0.0 S 20T 0:00.00 0:00.00 + 3736 0.0 S 20T 0:11.65 0:06.09 + 3736 0.0 S 20T 0:00.00 0:00.00 + 3736 0.0 S 20T 0:00.00 0:00.00 + 3736 0.0 S 20T 0:00.00 0:00.00 +root 3742 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 3742 0.0 S 31T 1:32.58 3:35.36 + 3742 0.0 S 31T 0:05.20 0:00.21 + 3742 0.0 S 31T 0:00.50 0:00.28 + 3742 0.0 S 31T 0:00.00 0:00.00 + 3742 0.0 S 31T 0:00.03 0:00.02 + 3742 0.0 S 31T 0:17.45 0:06.37 + 3742 0.0 S 31T 0:04.79 0:02.33 + 3742 0.0 S 31T 0:28.30 0:10.24 + 3742 0.0 S 31T 0:05.11 0:02.47 + 3742 0.0 S 31T 1:33.15 2:44.53 + 3742 0.0 S 31T 0:00.00 0:00.00 + 3742 0.0 S 31T 1:13.45 0:32.12 + 3742 0.0 S 31T 0:00.06 0:00.03 + 3742 0.0 S 31T 0:00.05 0:00.01 + 3742 0.0 S 31T 0:00.05 0:00.01 + 3742 0.0 S 31T 0:00.65 0:01.64 + 3742 0.0 S 31T 0:00.11 0:00.11 + 3742 0.0 S 31T 0:00.00 0:00.00 + 3742 0.0 S 31T 0:00.00 0:00.00 + 3742 0.0 S 31T 0:00.00 0:00.00 + 3742 0.0 S 31T 0:00.00 0:00.00 +someuser 3743 ?? 0.0 S 46T 0:09.36 0:11.95 /usr/bin/some_command with some parameters + 3743 0.0 S 31T 0:00.00 0:00.00 + 3743 0.0 S 31T 0:00.00 0:00.00 + 3743 0.0 S 31T 0:00.00 0:00.00 + 3743 0.0 S 31T 0:00.00 0:00.00 + 3743 0.0 S 46T 0:01.34 0:00.57 + 3743 0.0 S 31T 0:00.00 0:00.00 + 3743 0.0 S 37T 0:00.00 0:00.00 +root 3747 ?? 0.0 S 20T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 3747 0.0 R 20T 1:30.26 5:34.22 + 3747 0.0 S 20T 0:00.00 0:00.00 + 3747 0.0 S 20T 0:00.66 0:00.29 + 3747 0.0 S 20T 0:00.00 0:00.00 + 3747 0.0 S 20T 0:20.98 0:10.77 + 3747 0.0 S 20T 0:00.28 0:00.10 + 3747 0.0 S 20T 0:00.25 0:00.07 + 3747 0.0 S 20T 0:00.23 0:00.07 + 3747 0.0 S 20T 0:03.57 0:06.72 + 3747 0.0 S 20T 0:00.00 0:00.00 + 3747 0.0 S 20T 0:01.41 0:01.08 + 3747 0.0 S 20T 0:00.75 0:01.43 + 3747 0.0 S 20T 0:11.93 0:27.25 + 3747 0.0 S 20T 0:00.00 0:00.00 + 3747 0.0 S 20T 0:00.00 0:00.00 + 3747 0.0 S 20T 0:00.01 0:00.00 + 3747 0.0 S 20T 0:00.01 0:00.00 +root 3769 ?? 0.0 S 31T 0:53.06 0:37.06 /usr/bin/some_command with some parameters + 3769 0.0 S 31T 0:00.42 0:00.27 + 3769 0.0 S 31T 0:00.00 0:00.00 + 3769 0.0 S 31T 1:28.59 0:29.68 + 3769 0.0 S 31T 0:00.00 0:00.00 + 3769 0.0 S 31T 0:00.00 0:00.00 + 3769 0.0 S 31T 0:15.11 0:07.92 + 3769 0.0 S 31T 0:00.00 0:00.00 + 3769 0.0 S 31T 0:13.86 0:07.25 +_driverkit 3811 ?? 0.0 S 63R 0:11.05 0:12.34 /usr/bin/some_command with some parameters + 3811 0.0 S 31T 0:00.00 0:00.00 +_driverkit 3813 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 3813 0.0 S 31T 0:00.00 0:00.00 +someuser 3834 ?? 0.0 S 20T 0:10.13 0:04.30 /usr/bin/some_command with some parameters + 3834 0.0 S 20T 0:00.00 0:00.00 + 3834 0.0 S 20T 0:00.00 0:00.00 + 3834 0.0 S 20T 0:00.00 0:00.00 + 3834 0.0 S 20T 0:04.18 0:00.63 + 3834 0.0 S 20T 0:00.50 0:00.12 + 3834 0.0 S 20T 0:00.00 0:00.00 + 3834 0.0 S 20T 0:00.00 0:00.00 +someuser 3857 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 3857 0.0 S 4T 0:00.00 0:00.00 +root 4074 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 4074 0.0 S 4T 0:08.20 0:08.17 + 4074 0.0 S 4T 0:11.99 0:12.76 + 4074 0.0 S 4T 0:00.47 0:00.32 + 4074 0.0 S 4T 0:00.15 0:00.06 + 4074 0.0 S 4T 0:00.09 0:00.04 + 4074 0.0 S 4T 0:00.00 0:00.00 +someuser 4168 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 4168 0.0 S 4T 0:00.00 0:00.00 +someuser 5222 ?? 0.0 S 31T 2:04.89 9:15.88 /usr/bin/some_command with some parameters + 5222 0.0 S 31T 0:00.14 0:00.16 + 5222 0.0 S 0T 0:00.12 0:00.09 + 5222 0.0 S 31T 0:16.89 0:21.20 + 5222 0.0 S 31T 0:00.03 0:00.02 + 5222 0.0 S 31T 0:07.50 0:29.90 + 5222 0.0 S 31T 0:01.60 0:02.46 + 5222 0.0 S 31T 0:00.60 0:01.87 + 5222 0.0 S 31T 0:00.58 0:01.99 + 5222 0.0 S 31T 0:00.59 0:01.92 + 5222 0.0 S 31T 0:00.58 0:01.89 + 5222 0.0 S 31T 0:00.03 0:00.07 + 5222 0.0 S 31T 0:00.03 0:00.01 + 5222 0.0 S 31T 0:00.43 0:00.16 + 5222 0.0 S 31T 0:00.40 0:00.15 + 5222 0.0 S 31T 0:00.01 0:00.01 + 5222 0.0 S 31T 0:00.01 0:00.01 + 5222 0.0 S 31T 0:00.00 0:00.00 + 5222 0.0 S 31T 0:00.01 0:00.01 +someuser 5252 ?? 0.0 S 47T 0:01.94 0:04.23 /usr/bin/some_command with some parameters + 5252 0.0 S 31T 0:00.30 0:00.22 + 5252 0.0 S 31T 0:00.44 0:00.21 + 5252 0.0 S 0T 0:00.13 0:00.09 + 5252 0.0 S 31T 0:00.37 0:00.17 + 5252 0.0 S 19T 0:00.00 0:00.00 + 5252 0.0 S 31T 0:00.05 0:00.02 + 5252 0.0 S 31T 0:00.01 0:00.00 + 5252 0.0 S 31T 0:00.00 0:00.00 +_fpsd 5347 ?? 0.0 S 31T 0:00.17 0:00.04 /usr/bin/some_command with some parameters +someuser 5407 ?? 0.0 S 31T 0:00.45 0:00.24 /usr/bin/some_command with some parameters + 5407 0.0 S 37T 0:00.00 0:00.00 +nobody 6280 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters + 6280 0.0 S 31T 0:00.40 0:00.16 +someuser 6305 ?? 1.3 S 46T 14:38.38 21:54.25 /usr/bin/some_command with some parameters + 6305 0.0 S 31T 0:00.58 0:03.33 + 6305 0.0 S 31T 1:56.75 2:30.84 + 6305 0.0 S 31T 0:00.19 0:05.45 + 6305 0.0 S 31T 0:55.35 9:47.97 + 6305 0.0 S 31T 0:02.03 0:12.22 + 6305 0.2 S 31T 0:08.09 1:37.46 + 6305 0.0 S 31T 0:19.19 0:24.56 + 6305 0.0 S 31T 0:00.54 0:03.37 + 6305 0.0 S 31T 0:00.43 0:00.52 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:00.00 0:00.06 + 6305 0.0 S 31T 1:14.17 18:13.85 + 6305 0.0 S 31T 0:19.65 1:18.76 + 6305 0.0 S 31T 0:03.51 0:09.72 + 6305 0.0 S 31T 0:00.22 0:00.13 + 6305 0.0 S 31T 0:32.14 0:20.07 + 6305 0.0 S 31T 0:00.03 0:00.00 + 6305 0.0 S 31T 0:00.02 0:00.01 + 6305 0.0 S 31T 0:00.10 0:00.08 + 6305 0.0 S 31T 1:56.78 2:30.94 + 6305 0.0 S 31T 1:56.75 2:31.11 + 6305 0.0 S 31T 1:56.49 2:31.40 + 6305 0.0 S 31T 1:56.60 2:30.67 + 6305 0.0 S 31T 1:56.62 2:30.67 + 6305 0.0 S 31T 1:56.76 2:31.07 + 6305 0.0 S 31T 1:56.71 2:30.79 + 6305 0.0 S 31T 1:56.79 2:30.63 + 6305 0.0 S 31T 1:56.53 2:30.88 + 6305 0.0 S 31T 0:00.11 0:04.80 + 6305 0.0 S 31T 0:00.07 0:03.24 + 6305 0.0 S 31T 0:00.05 0:02.38 + 6305 0.0 S 46T 2:56.91 2:01.51 + 6305 3.3 S 31T 12:58.58 72:36.81 + 6305 0.0 S 31T 0:55.46 9:47.97 + 6305 0.0 S 31T 0:55.40 9:47.91 + 6305 0.0 S 31T 0:01.74 0:02.82 + 6305 0.3 S 31T 4:43.67 12:23.04 + 6305 0.0 S 31T 0:01.57 0:03.01 + 6305 0.2 S 31T 1:10.77 2:20.80 + 6305 0.0 S 31T 0:02.30 0:02.43 + 6305 0.0 S 31T 0:03.38 0:02.56 + 6305 0.0 S 31T 0:03.83 0:02.92 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:14.64 0:17.93 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.4 S 31T 2:45.00 6:39.29 + 6305 0.0 S 31T 0:00.03 0:01.13 + 6305 0.0 S 31T 0:00.04 0:01.79 + 6305 0.0 S 31T 0:00.03 0:01.41 + 6305 0.0 S 31T 0:00.02 0:01.08 + 6305 0.0 S 31T 0:00.26 0:02.26 + 6305 0.0 S 31T 0:00.21 0:00.05 + 6305 0.0 S 0T 0:02.77 0:01.00 + 6305 0.0 S 31T 0:00.75 0:00.83 + 6305 0.0 S 31T 0:00.02 0:00.01 + 6305 0.0 S 31T 0:00.24 0:00.09 + 6305 0.0 S 31T 0:00.05 0:00.02 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:00.03 0:00.00 + 6305 0.0 S 0T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:00.07 0:00.03 + 6305 0.0 S 31T 0:01.45 0:01.30 + 6305 0.0 S 31T 0:00.08 0:00.96 + 6305 0.0 S 31T 0:00.02 0:00.83 + 6305 0.0 S 31T 0:00.01 0:00.63 + 6305 0.0 S 31T 0:00.01 0:00.47 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:01.03 0:00.80 + 6305 0.0 S 31T 0:00.88 0:00.72 + 6305 0.0 S 31T 0:01.09 0:00.83 + 6305 0.0 S 31T 0:00.99 0:00.78 + 6305 0.0 S 31T 0:00.01 0:00.32 + 6305 0.0 S 31T 0:00.15 0:00.00 + 6305 0.0 S 31T 0:00.09 0:00.53 + 6305 0.0 S 31T 0:00.22 0:00.13 + 6305 0.0 S 31T 0:00.13 0:00.48 + 6305 0.0 S 31T 0:01.48 0:31.13 + 6305 0.0 S 31T 0:00.19 0:00.00 + 6305 0.0 S 31T 0:00.09 0:00.08 + 6305 0.0 S 31T 0:07.05 0:09.79 + 6305 0.0 S 31T 0:00.48 0:00.30 + 6305 0.0 S 31T 0:00.05 0:00.09 + 6305 0.0 S 31T 0:00.26 0:00.56 + 6305 0.0 S 31T 0:00.05 0:00.23 + 6305 0.0 S 31T 0:00.02 0:00.09 + 6305 0.0 S 31T 0:00.38 0:01.15 + 6305 0.0 S 31T 0:00.01 0:00.04 + 6305 0.0 S 31T 0:00.07 0:00.90 + 6305 0.0 S 31T 0:00.02 0:00.08 + 6305 0.0 S 31T 0:00.00 0:00.10 + 6305 0.0 S 31T 0:00.06 0:00.13 + 6305 0.0 S 31T 0:00.01 0:00.08 + 6305 0.0 S 31T 0:00.00 0:00.01 + 6305 0.0 S 31T 0:00.04 0:00.59 + 6305 0.0 S 31T 0:00.00 0:00.01 + 6305 0.0 S 31T 0:00.19 0:00.27 + 6305 0.0 S 31T 0:00.13 0:05.79 + 6305 0.0 S 31T 0:02.17 0:17.73 + 6305 0.0 S 31T 0:02.58 0:19.51 + 6305 0.0 S 31T 0:01.45 0:15.04 + 6305 0.0 S 31T 0:02.03 0:13.55 + 6305 0.0 S 31T 0:01.58 0:12.37 + 6305 0.0 S 31T 0:01.38 0:15.01 + 6305 0.0 S 31T 0:01.20 0:10.89 + 6305 0.0 S 31T 0:00.01 0:00.06 + 6305 0.0 S 31T 0:01.75 0:15.56 + 6305 0.0 S 31T 0:01.38 0:14.93 + 6305 0.0 S 31T 0:01.38 0:13.37 + 6305 0.0 S 31T 0:01.05 0:12.57 + 6305 0.0 S 31T 0:01.17 0:13.21 + 6305 0.0 S 31T 0:01.30 0:14.13 + 6305 0.0 S 31T 0:00.77 0:10.35 + 6305 0.0 S 31T 0:01.16 0:11.87 + 6305 0.0 S 31T 0:01.37 0:14.07 + 6305 0.0 S 31T 0:00.99 0:11.23 + 6305 0.0 S 31T 0:00.88 0:09.94 + 6305 0.0 S 31T 0:00.96 0:11.20 + 6305 0.0 S 31T 0:01.04 0:13.12 + 6305 0.0 S 31T 0:01.21 0:11.56 + 6305 0.0 S 31T 0:01.20 0:11.02 + 6305 0.0 S 31T 0:01.29 0:15.79 + 6305 0.0 S 31T 0:00.63 0:08.23 + 6305 0.0 S 31T 0:01.32 0:11.65 + 6305 0.0 S 31T 0:01.34 0:11.90 + 6305 0.0 S 31T 0:00.94 0:11.78 + 6305 0.0 S 31T 0:00.01 0:00.01 + 6305 0.0 S 31T 0:00.86 0:09.48 + 6305 0.0 S 31T 0:00.67 0:10.34 + 6305 0.0 S 31T 0:00.85 0:09.31 + 6305 0.0 S 31T 0:00.97 0:11.96 + 6305 0.0 S 31T 0:00.69 0:07.93 + 6305 0.0 S 31T 0:00.66 0:09.77 + 6305 0.0 S 31T 0:00.46 0:07.58 + 6305 0.0 S 31T 0:00.75 0:08.26 + 6305 0.0 S 31T 0:00.72 0:09.74 + 6305 0.0 S 31T 0:00.81 0:11.11 + 6305 0.0 S 31T 0:00.79 0:07.88 + 6305 0.0 S 31T 0:00.44 0:06.56 + 6305 0.0 S 31T 0:00.36 0:05.39 + 6305 0.0 S 31T 0:00.61 0:07.06 + 6305 0.0 S 31T 0:00.87 0:08.89 + 6305 0.0 S 31T 0:00.62 0:08.71 + 6305 0.0 S 31T 0:00.72 0:07.11 + 6305 0.0 S 31T 0:00.55 0:09.69 + 6305 0.0 S 31T 0:00.53 0:08.28 + 6305 0.0 S 31T 0:00.58 0:06.60 + 6305 0.0 S 31T 0:00.59 0:08.23 + 6305 0.0 S 31T 0:00.68 0:07.10 + 6305 0.0 S 31T 0:00.42 0:08.30 + 6305 0.0 S 31T 0:00.69 0:08.53 + 6305 0.0 S 31T 0:00.56 0:06.80 + 6305 0.0 S 31T 0:00.43 0:08.04 + 6305 0.0 S 31T 0:00.43 0:06.37 + 6305 0.0 S 31T 0:00.65 0:08.07 + 6305 0.0 S 31T 0:00.64 0:06.97 + 6305 0.0 S 31T 0:00.59 0:06.51 + 6305 0.0 S 31T 0:00.31 0:05.59 + 6305 0.0 S 31T 0:00.40 0:04.95 + 6305 0.0 S 31T 0:00.52 0:06.14 + 6305 0.0 S 31T 0:00.98 0:06.32 + 6305 0.0 S 31T 0:00.54 0:07.47 + 6305 0.0 S 31T 0:00.37 0:05.68 + 6305 0.0 S 31T 0:00.45 0:08.21 + 6305 0.0 S 31T 0:00.34 0:04.88 + 6305 0.0 S 31T 0:00.52 0:04.65 + 6305 0.0 S 31T 0:00.50 0:05.71 + 6305 0.0 S 31T 0:00.43 0:05.11 + 6305 0.0 S 31T 0:00.20 0:03.59 + 6305 0.0 S 31T 0:00.18 0:01.24 + 6305 0.0 S 31T 0:00.20 0:01.65 + 6305 0.0 S 31T 0:00.16 0:01.14 + 6305 0.0 S 31T 0:00.24 0:02.09 + 6305 0.0 S 31T 0:00.12 0:01.40 + 6305 0.0 S 31T 0:00.11 0:01.56 + 6305 0.0 S 31T 0:00.09 0:01.35 + 6305 0.0 S 31T 0:00.09 0:01.50 + 6305 0.0 S 31T 0:00.07 0:01.12 + 6305 0.0 S 31T 0:00.17 0:02.92 + 6305 0.0 S 31T 0:00.19 0:01.78 + 6305 0.0 S 31T 0:00.04 0:01.05 + 6305 0.0 S 31T 0:00.07 0:01.62 + 6305 0.0 S 31T 0:00.08 0:01.47 + 6305 0.0 S 31T 0:00.56 0:02.55 + 6305 0.0 S 31T 0:00.05 0:00.76 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:00.00 0:00.01 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:00.01 0:00.01 + 6305 0.0 S 31T 0:00.03 0:00.44 + 6305 0.0 S 31T 0:00.12 0:01.07 + 6305 0.0 S 31T 0:00.03 0:00.31 + 6305 0.0 S 31T 0:01.55 0:07.47 + 6305 0.0 S 31T 0:00.95 0:05.26 + 6305 1.2 S 31T 0:00.24 0:01.77 + 6305 0.0 S 31T 0:00.23 0:01.26 + 6305 0.0 S 31T 0:00.17 0:01.56 + 6305 0.0 S 31T 0:00.48 0:03.07 + 6305 0.0 S 31T 0:00.01 0:00.08 + 6305 0.0 S 31T 0:00.26 0:01.80 + 6305 1.5 S 31T 0:00.17 0:01.06 + 6305 0.0 S 31T 0:00.02 0:00.02 + 6305 0.0 S 31T 0:00.02 0:00.14 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 31T 0:00.00 0:00.00 + 6305 0.0 S 37T 0:00.00 0:00.00 + 6305 0.0 S 4T 0:00.00 0:00.00 + 6305 0.0 S 37T 0:00.01 0:00.01 + 6305 0.0 S 46T 0:00.00 0:00.00 +someuser 6351 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 6351 0.0 S 31T 0:24.98 0:11.90 + 6351 0.0 S 31T 0:00.00 0:00.00 +someuser 6365 ?? 0.0 S 31T 0:02.68 0:02.63 /usr/bin/some_command with some parameters + 6365 0.0 S 31T 0:00.61 0:00.45 + 6365 0.0 S 31T 0:00.01 0:00.00 + 6365 0.0 S 31T 0:00.14 0:00.11 + 6365 0.0 S 0T 0:00.12 0:00.10 + 6365 0.0 S 31T 0:00.32 0:00.16 + 6365 0.0 S 31T 0:00.53 0:00.98 + 6365 0.0 S 31T 0:00.00 0:00.00 +someuser 6368 ?? 0.0 S 31T 0:00.03 0:00.04 /usr/bin/some_command with some parameters + 6368 0.0 S 31T 0:00.04 0:00.02 + 6368 0.0 S 0T 0:00.14 0:00.10 + 6368 0.0 S 31T 0:00.73 0:00.28 + 6368 0.0 S 31T 0:00.16 0:00.03 + 6368 0.0 S 31T 0:00.01 0:00.01 + 6368 0.0 S 31T 0:00.00 0:00.00 +someuser 6774 ?? 0.0 S 46T 0:24.25 0:42.34 /usr/bin/some_command with some parameters + 6774 0.0 S 31T 0:04.32 0:04.53 + 6774 0.0 S 46T 0:00.00 0:00.00 + 6774 0.0 S 46T 0:00.00 0:00.00 + 6774 0.0 S 31T 0:00.00 0:00.00 +someuser 6796 ?? 0.0 S 46T 0:04.38 0:01.51 /usr/bin/some_command with some parameters + 6796 0.0 S 46T 0:00.47 0:00.09 + 6796 0.0 S 31T 0:00.00 0:00.00 + 6796 0.0 S 37T 0:00.00 0:00.00 +someuser 6947 ?? 0.0 S 31T 0:27.15 0:24.09 tmux +someuser 7649 ?? 0.0 S 46T 0:04.70 0:02.03 /usr/bin/some_command with some parameters + 7649 0.0 S 46T 0:00.43 0:00.09 + 7649 0.0 S 19T 0:00.00 0:00.00 + 7649 0.0 S 31T 0:00.00 0:00.00 + 7649 0.0 S 37T 0:00.00 0:00.00 +someuser 7651 ?? 0.0 S 46T 0:03.32 0:01.25 /usr/bin/some_command with some parameters + 7651 0.0 S 46T 0:00.49 0:00.09 + 7651 0.0 S 37T 0:00.00 0:00.00 + 7651 0.0 S 37T 0:00.00 0:00.00 +someuser 7961 ?? 0.0 S 20T 0:00.59 0:02.56 /usr/bin/some_command with some parameters +someuser 9260 ?? 0.0 S 42T 0:03.22 0:04.05 /usr/bin/some_command with some parameters + 9260 0.0 S 31T 0:42.22 0:05.66 + 9260 0.0 S 31T 0:01.14 0:23.88 + 9260 0.0 S 31T 0:00.12 0:00.14 + 9260 0.0 S 31T 0:00.01 0:00.00 + 9260 0.0 S 31T 12:00.97 1:03.81 + 9260 0.0 S 31T 0:00.00 0:00.00 + 9260 0.0 S 31T 0:05.92 0:03.89 + 9260 0.0 S 31T 0:00.00 0:00.00 + 9260 0.0 S 31T 0:02.90 0:01.01 + 9260 0.0 S 31T 0:09.95 0:03.94 + 9260 0.0 S 31T 0:00.00 0:00.00 + 9260 0.0 S 31T 0:00.00 0:00.00 + 9260 0.0 S 31T 0:00.62 0:00.40 + 9260 0.0 S 31T 0:01.02 0:01.37 + 9260 0.0 S 31T 0:08.48 0:02.01 + 9260 0.0 S 31T 0:00.00 0:00.00 + 9260 0.0 S 31T 0:01.28 0:00.30 + 9260 0.0 S 31T 0:01.11 0:01.11 + 9260 0.0 S 31T 0:00.01 0:00.01 + 9260 0.0 S 31T 0:00.03 0:00.01 + 9260 0.0 S 31T 0:09.80 0:02.43 + 9260 0.0 S 31T 0:00.27 0:00.06 + 9260 0.0 S 31T 0:00.00 0:00.00 + 9260 0.0 S 19T 0:00.00 0:00.00 + 9260 0.0 S 31T 0:01.46 0:34.31 + 9260 0.0 S 31T 0:12.38 0:14.26 + 9260 0.0 S 31T 0:12.50 0:14.27 + 9260 0.0 S 31T 0:12.40 0:14.23 + 9260 0.0 S 20T 0:01.37 0:00.59 + 9260 0.0 S 20T 0:01.22 0:00.53 + 9260 0.0 S 20T 0:00.07 0:00.03 + 9260 0.0 S 61T 0:00.00 0:00.00 + 9260 0.0 S 31T 0:00.00 0:00.00 +someuser 12403 ?? 0.0 S 31T 0:04.96 0:02.66 /usr/bin/some_command with some parameters + 12403 0.0 S 37T 0:00.65 0:00.15 + 12403 0.0 S 37T 0:00.00 0:00.00 +someuser 13175 ?? 0.0 S 46T 0:10.60 0:14.64 /usr/bin/some_command with some parameters + 13175 0.0 S 4T 0:00.01 0:00.01 + 13175 0.0 S 31T 0:00.06 0:00.07 + 13175 0.0 S 31T 0:00.00 0:00.00 + 13175 0.0 S 31T 0:00.00 0:00.00 + 13175 0.0 S 46T 0:01.09 0:00.50 + 13175 0.0 S 31T 0:00.05 0:00.03 + 13175 0.0 S 4T 0:00.06 0:00.05 + 13175 0.0 S 37T 0:00.00 0:00.00 + 13175 0.0 S 37T 0:00.00 0:00.00 +someuser 13178 ?? 0.0 S 31T 0:15.15 0:20.39 /usr/bin/some_command with some parameters + 13178 0.0 S 31T 0:44.89 0:26.59 + 13178 0.0 S 31T 0:00.00 0:00.00 + 13178 0.0 S 31T 0:15.04 0:20.25 + 13178 0.0 S 31T 0:15.16 0:20.54 + 13178 0.0 S 31T 0:00.00 0:00.00 + 13178 0.0 S 31T 0:14.73 0:19.94 + 13178 0.0 S 31T 0:00.00 0:00.00 + 13178 0.0 S 31T 0:00.00 0:00.00 + 13178 0.0 S 31T 0:15.60 0:21.09 + 13178 0.0 S 31T 0:00.00 0:00.00 + 13178 0.0 S 31T 0:14.89 0:20.23 + 13178 0.0 S 31T 0:12.79 0:17.16 + 13178 0.0 S 31T 0:15.57 0:20.91 + 13178 0.0 S 31T 0:15.23 0:20.53 + 13178 0.0 S 31T 0:12.72 0:17.05 + 13178 0.0 S 31T 0:14.85 0:20.27 + 13178 0.0 S 31T 0:15.46 0:20.75 + 13178 0.0 S 31T 0:08.81 0:11.94 + 13178 0.0 S 20T 0:00.04 0:00.00 + 13178 0.3 S 31T 0:14.74 0:19.94 + 13178 0.2 S 31T 0:11.98 0:16.27 +someuser 13179 ?? 0.0 S 31T 0:00.02 0:00.01 /usr/bin/some_command with some parameters + 13179 0.0 S 31T 0:00.25 0:00.12 + 13179 0.0 S 31T 0:00.00 0:00.00 + 13179 0.0 S 31T 0:00.29 0:00.27 + 13179 0.0 S 31T 0:00.00 0:00.00 + 13179 0.0 S 31T 0:00.33 0:00.29 + 13179 0.0 S 31T 0:00.00 0:00.00 + 13179 0.0 S 31T 0:00.30 0:00.29 + 13179 0.0 S 31T 0:00.24 0:00.21 + 13179 0.0 S 31T 0:00.32 0:00.26 + 13179 0.0 S 31T 0:00.29 0:00.27 + 13179 0.0 S 31T 0:00.31 0:00.27 + 13179 0.0 S 31T 0:00.29 0:00.28 + 13179 0.0 S 31T 0:00.22 0:00.24 + 13179 0.0 S 31T 0:00.29 0:00.27 + 13179 0.0 S 31T 0:00.29 0:00.26 + 13179 0.0 S 31T 0:00.25 0:00.24 + 13179 0.0 S 31T 0:00.27 0:00.24 + 13179 0.0 S 31T 0:00.21 0:00.16 +someuser 13201 ?? 0.0 S 31T 0:00.42 0:00.33 /usr/bin/some_command with some parameters + 13201 0.0 S 31T 0:00.35 0:00.18 + 13201 0.0 S 31T 0:00.00 0:00.00 + 13201 0.0 S 31T 0:00.07 0:00.01 + 13201 0.0 S 31T 0:00.01 0:00.00 + 13201 0.0 S 31T 0:00.00 0:00.00 + 13201 0.0 S 31T 0:00.00 0:00.00 + 13201 0.0 S 31T 0:00.00 0:00.00 + 13201 0.0 S 31T 0:00.00 0:00.00 + 13201 0.0 S 31T 0:00.00 0:00.00 + 13201 0.0 S 31T 0:00.00 0:00.00 + 13201 0.0 S 31T 0:00.34 0:00.30 + 13201 0.0 S 31T 0:00.00 0:00.00 + 13201 0.0 S 31T 0:00.38 0:00.34 + 13201 0.0 S 31T 0:00.40 0:00.32 + 13201 0.0 S 31T 0:00.36 0:00.30 + 13201 0.0 S 31T 0:00.39 0:00.34 + 13201 0.0 S 31T 0:00.36 0:00.31 + 13201 0.0 S 31T 0:00.37 0:00.32 + 13201 0.0 S 31T 0:00.39 0:00.32 + 13201 0.0 S 31T 0:00.44 0:00.35 + 13201 0.0 S 31T 0:00.36 0:00.32 + 13201 0.0 S 31T 0:00.32 0:00.31 + 13201 0.0 S 31T 0:00.41 0:00.34 + 13201 0.0 S 31T 0:00.38 0:00.31 +someuser 13207 ?? 0.0 S 31T 0:12.54 0:15.84 com.docker.vpnkit --ethernet fd:3 --diagnostics fd:4 --pcap fd:5 --vsock-path vms/0/connect --host-names host.docker.internal,docker.for.mac.host.internal,docker.for.mac.localhost --listen-backlog 32 --mtu 1500 --allowed-bind-addresses 0.0.0.0 --http /usr/bin/some_command with some parameters + 13207 0.0 S 31T 0:03.22 0:00.28 + 13207 0.0 S 31T 0:03.23 0:00.28 + 13207 0.0 S 31T 0:03.22 0:00.28 + 13207 0.0 S 31T 0:03.22 0:00.28 +someuser 13208 ?? 0.0 S 31T 0:00.05 0:00.02 docker serve --address unix:///Users/someuser/.docker/run/docker-cli-api.sock + 13208 0.0 S 31T 0:01.57 0:00.98 + 13208 0.0 S 31T 0:00.81 0:00.91 + 13208 0.0 S 31T 0:00.86 0:00.97 + 13208 0.0 S 31T 0:00.76 0:00.85 + 13208 0.0 S 31T 0:00.79 0:00.92 + 13208 0.0 S 31T 0:00.76 0:00.86 + 13208 0.0 S 31T 0:00.81 0:00.94 + 13208 0.0 S 31T 0:00.75 0:00.87 + 13208 0.0 S 31T 0:00.80 0:00.92 + 13208 0.0 S 31T 0:00.75 0:00.89 + 13208 0.0 S 31T 0:00.00 0:00.00 + 13208 0.0 S 31T 0:00.72 0:00.90 + 13208 0.0 S 31T 0:00.00 0:00.00 + 13208 0.0 S 31T 0:00.84 0:00.91 + 13208 0.0 S 31T 0:00.73 0:00.79 + 13208 0.0 S 31T 0:00.87 0:00.96 + 13208 0.0 S 31T 0:00.59 0:00.63 +someuser 13209 ?? 0.0 S 31T 0:21.43 0:12.21 vpnkit-bridge --disable wsl2-cross-distro-service,wsl2-bootstrap-expose-ports,transfused --addr listen://1999 host + 13209 0.0 S 31T 1:09.44 0:39.38 + 13209 0.0 S 31T 0:19.72 0:11.04 + 13209 0.1 S 31T 0:20.30 0:11.48 + 13209 0.0 S 31T 0:20.49 0:11.54 + 13209 0.0 S 31T 0:19.68 0:11.18 + 13209 0.0 S 31T 0:00.00 0:00.00 + 13209 0.0 S 31T 0:18.70 0:10.39 + 13209 0.0 S 31T 0:20.89 0:11.77 + 13209 0.0 S 31T 0:19.80 0:11.19 + 13209 0.0 S 31T 0:19.19 0:10.81 + 13209 0.0 S 31T 0:15.30 0:08.80 + 13209 0.0 S 31T 0:21.64 0:12.16 + 13209 0.0 S 31T 0:18.95 0:10.76 + 13209 0.0 S 31T 0:20.61 0:11.57 + 13209 0.0 S 31T 0:14.18 0:07.93 +someuser 13210 ?? 0.0 S 31T 0:02.99 0:02.18 com.docker.driver.amd64-linux -addr fd:3 -debug -native-api + 13210 0.0 S 31T 0:08.70 0:04.96 + 13210 0.0 S 31T 0:03.44 0:02.47 + 13210 0.0 S 31T 0:02.62 0:01.93 + 13210 0.0 S 31T 0:02.40 0:01.88 + 13210 0.0 S 31T 0:00.00 0:00.00 + 13210 0.0 S 31T 0:02.96 0:02.22 + 13210 0.0 S 31T 0:02.72 0:02.04 + 13210 0.0 S 31T 0:02.96 0:02.15 + 13210 0.0 S 31T 0:03.01 0:02.23 + 13210 0.0 S 31T 0:00.00 0:00.00 + 13210 0.0 S 31T 0:00.00 0:00.00 + 13210 0.0 S 31T 0:03.18 0:02.38 + 13210 0.0 S 31T 0:02.95 0:02.21 + 13210 0.0 S 31T 0:03.37 0:02.49 + 13210 0.0 S 31T 0:00.00 0:00.00 + 13210 0.0 S 31T 0:02.96 0:02.23 + 13210 0.0 S 31T 0:02.66 0:02.06 + 13210 0.0 S 31T 0:02.78 0:02.15 +someuser 13219 ?? 0.0 S 31T 0:00.03 0:00.01 com.docker.hyperkit -A -u -F vms/0/hyperkit.pid -c 6 -m 2048M -s 0:0,hostbridge -s 31,lpc -s 1:0,virtio-vpnkit,path=vpnkit.eth.sock,uuid=254b47b9-08d1-4825-812d-21a5c072a954 -U e5194447-4cb5-4962-9f8a-6926b08ac2b9 -s 2:0,virtio-blk,/Users/someuser/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw -s 3,virtio-sock,guest_cid=3,path=vms/0,guest_forwards=2376;1525 -s 4,virtio-rnd -l com1,null,asl,log=vms/0/console-ring -f kexec,/Applications/Docker.app/Contents/Resources/linuxkit/kernel,/Applications/Docker.app/Contents/Resources/linuxkit/initrd.img,earlyprintk=serial page_poison=1 vsyscall=emulate panic=1 nospec_store_bypass_disable noibrs noibpb no_stf_barrier mitigations=off console=ttyS0 console=ttyS1 vpnkit.connect=connect://2/1999 + 13219 0.0 S 31T 0:00.24 0:00.08 + 13219 0.0 S 31T 0:47.37 0:14.79 + 13219 0.6 S 31T 18:03.18 5:13.88 + 13219 0.0 S 31T 0:01.02 0:00.21 + 13219 0.0 S 31T 0:03.91 0:01.07 + 13219 0.0 S 31T 0:04.41 0:00.07 + 13219 0.0 S 31T 1:26.07 0:15.54 + 13219 0.0 S 31T 1:22.96 0:18.63 + 13219 1.3 S 31T 40:46.89 8:59.16 + 13219 0.6 S 31T 42:32.76 9:23.27 + 13219 0.9 S 31T 41:26.00 9:08.48 + 13219 3.9 S 31T 40:25.64 9:00.49 + 13219 1.4 S 31T 37:32.72 8:13.76 + 13219 1.5 S 31T 49:41.46 11:22.61 + 13219 0.0 S 31T 0:00.00 0:00.00 +someuser 13565 ?? 0.0 S 47T 0:01.95 0:03.59 /usr/bin/some_command with some parameters + 13565 0.0 S 31T 0:00.29 0:00.20 + 13565 0.0 S 31T 0:00.44 0:00.19 + 13565 0.0 S 0T 0:00.12 0:00.10 + 13565 0.0 S 31T 0:00.14 0:00.06 + 13565 0.0 S 19T 0:00.00 0:00.00 + 13565 0.0 S 61T 0:00.00 0:00.00 +someuser 15552 ?? 0.0 S 31T 0:06.78 0:11.04 /usr/bin/some_command with some parameters + 15552 0.0 S 31T 0:00.00 0:00.00 + 15552 0.0 S 0T 0:00.01 0:00.01 + 15552 0.0 S 31T 0:00.65 0:00.39 + 15552 0.0 S 31T 0:00.01 0:00.01 + 15552 0.0 S 31T 0:00.06 0:00.26 + 15552 0.0 S 31T 0:00.00 0:00.00 + 15552 0.0 S 31T 0:00.01 0:00.28 + 15552 0.0 S 31T 0:00.01 0:00.32 + 15552 0.0 S 31T 0:00.01 0:00.30 + 15552 0.0 S 31T 0:00.01 0:00.27 + 15552 0.0 S 31T 0:00.00 0:00.00 + 15552 0.0 S 31T 0:00.05 0:00.31 + 15552 0.0 S 31T 0:00.11 0:00.02 + 15552 0.0 S 31T 0:00.05 0:00.02 + 15552 0.0 S 31T 0:00.10 0:00.03 +someuser 20135 ?? 0.0 S 31T 0:06.37 0:04.95 /usr/bin/some_command with some parameters + 20135 0.0 S 31T 0:00.01 0:00.00 + 20135 0.0 S 0T 0:00.13 0:00.09 + 20135 0.0 S 31T 0:01.40 0:00.74 + 20135 0.0 S 31T 0:00.00 0:00.00 + 20135 0.0 S 31T 0:00.22 0:00.17 + 20135 0.0 S 31T 0:00.00 0:00.00 + 20135 0.0 S 31T 0:00.00 0:00.00 + 20135 0.0 S 31T 0:00.01 0:00.00 + 20135 0.0 S 31T 0:00.01 0:00.00 + 20135 0.0 S 31T 0:00.01 0:00.00 + 20135 0.0 S 31T 0:00.00 0:00.00 + 20135 0.0 S 31T 0:00.44 0:00.13 + 20135 0.0 S 31T 0:00.03 0:00.01 + 20135 0.0 S 31T 0:00.01 0:00.00 +someuser 22878 ?? 0.0 S 31T 0:03.45 0:01.37 /usr/bin/some_command with some parameters + 22878 0.0 S 37T 0:00.47 0:00.09 + 22878 0.0 S 37T 0:00.00 0:00.00 +root 23677 ?? 0.0 S 4T 0:00.03 0:00.03 /usr/bin/some_command with some parameters + 23677 0.0 S 4T 0:00.00 0:00.00 +someuser 25255 ?? 0.1 S 42T 6:20.26 0:41.43 /usr/bin/some_command with some parameters +someuser 25257 ?? 0.0 S 42T 0:33.20 0:39.82 /usr/bin/some_command with some parameters + 25257 0.0 S 31T 3:27.77 0:49.18 + 25257 0.0 S 31T 0:05.84 0:06.98 + 25257 0.0 S 31T 0:00.00 0:00.00 + 25257 0.0 S 31T 0:00.00 0:00.00 + 25257 0.0 S 31T 0:04.04 0:04.28 + 25257 0.2 S 31T 5:43.56 4:03.97 + 25257 0.0 S 31T 0:00.07 0:00.03 + 25257 0.0 S 31T 0:00.12 0:00.09 + 25257 0.0 S 31T 0:14.99 0:08.07 + 25257 0.0 S 31T 0:00.51 0:01.78 + 25257 0.0 S 31T 0:05.98 0:07.04 + 25257 0.0 S 31T 0:06.15 0:06.98 + 25257 0.0 S 31T 0:05.36 0:06.37 + 25257 0.0 S 31T 0:05.34 0:06.34 + 25257 0.0 S 19T 0:00.00 0:00.00 + 25257 0.0 S 31T 0:05.13 0:06.33 + 25257 0.0 S 31T 0:03.31 0:04.24 + 25257 0.0 S 31T 0:03.52 0:04.25 + 25257 0.0 S 61T 0:00.00 0:00.00 + 25257 0.0 S 20T 0:00.00 0:00.00 +someuser 25320 ?? 0.0 S 31T 0:01.42 0:00.23 /usr/bin/some_command with some parameters + 25320 0.0 S 31T 0:03.79 0:03.03 +root 27923 ?? 0.0 S 31T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 27923 0.0 S 4T 0:00.00 0:00.00 +someuser 29226 ?? 0.0 S 4T 0:00.37 0:00.03 /usr/bin/some_command with some parameters + 29226 0.0 S 4T 0:00.01 0:00.00 + 29226 0.0 S 4T 0:00.00 0:00.00 +someuser 29631 ?? 0.0 S 31T 0:21.95 0:26.43 /usr/bin/some_command with some parameters + 29631 0.0 S 31T 0:00.00 0:00.00 + 29631 0.0 S 0T 0:00.08 0:00.04 + 29631 0.0 S 31T 0:01.79 0:01.89 + 29631 0.0 S 31T 0:00.01 0:00.01 + 29631 0.0 S 31T 0:00.30 0:01.31 + 29631 0.0 S 31T 0:00.00 0:00.00 + 29631 0.0 S 31T 0:00.06 0:00.18 + 29631 0.0 S 31T 0:00.03 0:00.22 + 29631 0.0 S 31T 0:00.02 0:00.16 + 29631 0.0 S 31T 0:00.05 0:00.18 + 29631 0.0 S 31T 0:00.01 0:00.04 + 29631 0.0 S 31T 0:00.41 0:00.08 + 29631 0.0 S 31T 0:00.05 0:00.01 + 29631 0.0 S 31T 0:00.06 0:00.03 +someuser 29686 ?? 0.0 S 31T 0:37.11 1:54.63 /usr/bin/some_command with some parameters + 29686 0.0 S 31T 0:00.00 0:00.00 + 29686 0.0 S 0T 0:00.06 0:00.04 + 29686 0.0 S 31T 0:03.42 0:03.91 + 29686 0.0 S 31T 0:00.01 0:00.02 + 29686 0.0 S 31T 0:00.59 0:04.69 + 29686 0.0 S 31T 0:00.00 0:00.00 + 29686 0.0 S 31T 0:00.02 0:00.08 + 29686 0.0 S 31T 0:00.02 0:00.08 + 29686 0.0 S 31T 0:00.02 0:00.08 + 29686 0.0 S 31T 0:00.03 0:00.08 + 29686 0.0 S 31T 0:00.00 0:00.00 + 29686 0.0 S 31T 0:00.22 0:00.07 + 29686 0.0 S 31T 0:00.18 0:00.07 + 29686 0.0 S 31T 0:00.00 0:00.00 +someuser 29894 ?? 0.0 S 31T 0:03.26 0:07.72 /usr/bin/some_command with some parameters + 29894 0.0 S 31T 0:00.00 0:00.00 + 29894 0.0 S 0T 0:00.04 0:00.03 + 29894 0.0 S 31T 0:00.43 0:00.21 + 29894 0.0 S 31T 0:00.00 0:00.00 + 29894 0.0 S 31T 0:00.10 0:00.46 + 29894 0.0 S 31T 0:00.00 0:00.00 + 29894 0.0 S 31T 0:00.01 0:00.06 + 29894 0.0 S 31T 0:00.01 0:00.07 + 29894 0.0 S 31T 0:00.02 0:00.12 + 29894 0.0 S 31T 0:00.01 0:00.07 + 29894 0.0 S 31T 0:00.00 0:00.00 + 29894 0.0 S 31T 0:00.21 0:00.04 + 29894 0.0 S 31T 0:00.09 0:00.05 + 29894 0.0 S 31T 0:00.02 0:00.01 +someuser 31499 ?? 0.0 S 31T 0:24.21 0:26.45 /usr/bin/some_command with some parameters + 31499 0.0 S 31T 0:00.00 0:00.00 + 31499 0.0 S 0T 0:00.05 0:00.04 + 31499 0.0 S 31T 0:01.51 0:01.19 + 31499 0.0 S 31T 0:00.01 0:00.01 + 31499 0.0 S 31T 0:00.22 0:00.86 + 31499 0.0 S 31T 0:00.00 0:00.00 + 31499 0.0 S 31T 0:00.02 0:00.24 + 31499 0.0 S 31T 0:00.06 0:00.30 + 31499 0.0 S 31T 0:00.02 0:00.24 + 31499 0.0 S 31T 0:00.04 0:00.39 + 31499 0.0 S 31T 0:00.00 0:00.00 + 31499 0.0 S 31T 0:00.44 0:00.08 + 31499 0.0 S 31T 0:00.07 0:00.02 + 31499 0.0 S 31T 0:00.07 0:00.04 +someuser 31632 ?? 0.0 S 31T 0:33.04 3:00.11 /usr/bin/some_command with some parameters + 31632 0.0 S 31T 0:00.00 0:00.00 + 31632 0.0 S 0T 0:00.07 0:00.04 + 31632 0.0 S 31T 0:07.05 0:08.22 + 31632 0.0 S 31T 0:00.01 0:00.01 + 31632 0.0 S 31T 0:07.97 0:34.81 + 31632 0.0 S 31T 0:00.00 0:00.00 + 31632 0.0 S 31T 0:00.38 0:05.44 + 31632 0.0 S 31T 0:00.40 0:05.95 + 31632 0.0 S 31T 0:00.39 0:05.04 + 31632 0.0 S 31T 0:00.42 0:05.37 + 31632 0.0 S 31T 0:00.00 0:00.00 + 31632 0.0 S 31T 0:00.44 0:00.07 + 31632 0.0 S 31T 0:00.12 0:00.04 + 31632 0.0 S 31T 0:00.07 0:00.06 + 31632 0.0 S 31T 0:00.00 0:00.00 +someuser 32179 ?? 0.0 S 4T 0:11.92 0:31.08 /usr/bin/some_command with some parameters + 32179 0.0 S 4T 0:01.70 0:00.46 + 32179 0.0 S 4T 0:00.00 0:00.00 +someuser 32424 ?? 0.0 S 31T 0:01.39 0:02.07 /usr/bin/some_command with some parameters + 32424 0.0 S 31T 0:00.00 0:00.00 + 32424 0.0 S 0T 0:00.04 0:00.03 + 32424 0.0 S 31T 0:00.30 0:00.14 + 32424 0.0 S 31T 0:00.00 0:00.00 + 32424 0.0 S 31T 0:00.02 0:00.08 + 32424 0.0 S 31T 0:00.00 0:00.00 + 32424 0.0 S 31T 0:00.00 0:00.00 + 32424 0.0 S 31T 0:00.00 0:00.01 + 32424 0.0 S 31T 0:00.00 0:00.01 + 32424 0.0 S 31T 0:00.00 0:00.01 + 32424 0.0 S 31T 0:00.00 0:00.00 + 32424 0.0 S 31T 0:00.16 0:00.04 + 32424 0.0 S 31T 0:00.05 0:00.04 + 32424 0.0 S 31T 0:00.01 0:00.00 +someuser 33878 ?? 0.0 S 4T 0:25.60 0:38.00 /usr/bin/some_command with some parameters + 33878 0.0 S 4T 1:25.59 0:20.82 + 33878 0.0 S 4T 0:00.97 0:00.64 + 33878 0.0 S 3T 0:00.22 0:00.19 + 33878 0.0 S 4T 0:09.85 0:10.18 + 33878 0.0 S 4T 0:00.69 0:01.10 + 33878 0.0 S 4T 0:00.65 0:00.32 + 33878 0.0 S 4T 0:00.58 0:00.42 + 33878 0.0 S 4T 0:00.00 0:00.00 + 33878 0.0 S 4T 0:00.00 0:00.00 +someuser 33945 ?? 0.0 S 42T 0:02.18 0:02.60 /usr/bin/some_command with some parameters + 33945 0.0 S 31T 0:21.66 0:03.84 + 33945 0.0 S 31T 0:00.88 0:12.15 + 33945 0.0 S 31T 0:00.23 0:00.19 + 33945 0.0 S 31T 0:00.01 0:00.00 + 33945 0.0 S 31T 17:48.57 0:24.29 + 33945 0.0 S 31T 30:13.52 2:32.90 + 33945 0.0 S 31T 0:00.00 0:00.00 + 33945 0.0 S 31T 0:08.96 0:05.76 + 33945 0.0 S 31T 0:00.00 0:00.00 + 33945 0.0 S 31T 0:04.72 0:01.54 + 33945 0.0 S 31T 0:16.20 0:06.16 + 33945 0.0 S 31T 0:01.72 0:00.32 + 33945 0.0 S 31T 0:00.00 0:00.00 + 33945 0.0 S 31T 0:01.13 0:00.61 + 33945 0.0 S 31T 0:01.56 0:02.05 + 33945 0.0 S 31T 0:19.88 0:15.23 + 33945 0.0 S 31T 0:09.60 0:09.37 + 33945 0.0 S 31T 0:00.00 0:00.00 + 33945 0.0 S 31T 0:00.00 0:00.00 + 33945 0.0 S 31T 0:03.13 0:01.01 + 33945 0.0 S 31T 0:02.45 0:01.37 + 33945 0.0 S 31T 0:00.00 0:00.00 + 33945 0.0 S 31T 0:00.00 0:00.00 + 33945 0.0 S 31T 0:13.29 0:03.91 + 33945 0.0 S 31T 0:00.47 0:00.10 + 33945 0.0 S 31T 0:00.00 0:00.00 + 33945 0.0 S 19T 0:00.00 0:00.00 + 33945 0.0 S 31T 0:00.14 0:00.07 + 33945 0.0 S 31T 0:00.23 0:02.59 + 33945 0.0 S 31T 0:00.00 0:00.00 +someuser 37665 ?? 0.0 S 46T 0:09.04 0:10.03 /usr/bin/some_command with some parameters + 37665 0.0 S 46T 0:00.13 0:00.07 + 37665 0.0 S 37T 0:00.00 0:00.00 + 37665 0.0 S 31T 0:00.00 0:00.00 + 37665 0.0 S 37T 0:00.00 0:00.00 +someuser 37728 ?? 0.0 S 31T 0:00.97 0:01.21 /usr/bin/some_command with some parameters + 37728 0.0 S 31T 0:00.00 0:00.00 + 37728 0.0 S 0T 0:00.03 0:00.02 + 37728 0.0 S 31T 0:00.42 0:00.39 + 37728 0.0 S 31T 0:00.00 0:00.00 + 37728 0.0 S 31T 0:00.00 0:00.00 + 37728 0.0 S 31T 0:00.00 0:00.00 + 37728 0.0 S 31T 0:00.00 0:00.00 + 37728 0.0 S 31T 0:00.00 0:00.00 + 37728 0.0 S 31T 0:00.00 0:00.00 + 37728 0.0 S 31T 0:00.00 0:00.00 + 37728 0.0 S 31T 0:00.00 0:00.00 + 37728 0.0 S 31T 0:00.09 0:00.03 + 37728 0.0 S 31T 0:00.04 0:00.02 + 37728 0.0 S 31T 0:00.00 0:00.00 +someuser 38532 ?? 0.0 S 4T 0:00.07 0:00.05 /usr/bin/some_command with some parameters + 38532 0.0 S 4T 0:00.03 0:00.01 +root 38747 ?? 0.0 S 4T 0:00.06 0:00.06 /usr/bin/some_command with some parameters + 38747 0.0 S 4T 0:00.00 0:00.00 +someuser 40037 ?? 0.0 S 4T 0:09.30 0:08.43 /usr/bin/some_command with some parameters + 40037 0.0 S 4T 0:00.00 0:00.00 + 40037 0.0 S 4T 0:01.34 0:00.54 + 40037 0.0 S 4T 0:00.01 0:00.00 + 40037 0.0 S 4T 0:00.07 0:00.02 + 40037 0.0 S 4T 0:00.00 0:00.00 + 40037 0.0 S 4T 0:00.00 0:00.00 +someuser 40686 ?? 0.0 S 31T 0:03.71 0:02.99 /usr/bin/some_command with some parameters + 40686 0.0 S 31T 0:00.00 0:00.00 + 40686 0.0 S 0T 0:00.03 0:00.02 + 40686 0.0 S 31T 0:00.70 0:00.36 + 40686 0.0 S 31T 0:00.00 0:00.00 + 40686 0.0 S 31T 0:00.03 0:00.09 + 40686 0.0 S 31T 0:00.10 0:00.02 + 40686 0.0 S 31T 0:00.00 0:00.01 + 40686 0.0 S 31T 0:00.00 0:00.00 + 40686 0.0 S 31T 0:00.00 0:00.00 + 40686 0.0 S 31T 0:00.00 0:00.00 + 40686 0.0 S 31T 0:00.00 0:00.01 + 40686 0.0 S 31T 0:00.12 0:00.03 + 40686 0.0 S 31T 0:00.02 0:00.01 + 40686 0.0 S 31T 0:00.01 0:00.01 + 40686 0.0 S 31T 0:00.03 0:00.01 +someuser 40698 ?? 0.0 S 31T 0:03.41 0:05.44 /usr/bin/some_command with some parameters + 40698 0.0 S 31T 0:00.00 0:00.00 + 40698 0.0 S 0T 0:00.03 0:00.02 + 40698 0.0 S 31T 0:00.37 0:00.17 + 40698 0.0 S 31T 0:00.00 0:00.00 + 40698 0.0 S 31T 0:00.05 0:00.17 + 40698 0.0 S 31T 0:00.00 0:00.00 + 40698 0.0 S 31T 0:00.00 0:00.01 + 40698 0.0 S 31T 0:00.00 0:00.01 + 40698 0.0 S 31T 0:00.00 0:00.01 + 40698 0.0 S 31T 0:00.00 0:00.02 + 40698 0.0 S 31T 0:00.00 0:00.00 + 40698 0.0 S 31T 0:00.00 0:00.00 + 40698 0.0 S 31T 0:00.24 0:00.04 + 40698 0.0 S 31T 0:00.02 0:00.00 + 40698 0.0 S 31T 0:00.01 0:00.00 +someuser 40707 ?? 0.0 S 31T 0:01.40 0:00.84 /usr/bin/some_command with some parameters + 40707 0.0 S 31T 0:00.00 0:00.00 + 40707 0.0 S 0T 0:00.04 0:00.02 + 40707 0.0 S 31T 0:00.28 0:00.12 + 40707 0.0 S 31T 0:00.00 0:00.00 + 40707 0.0 S 31T 0:00.03 0:00.05 + 40707 0.0 S 31T 0:00.00 0:00.00 + 40707 0.0 S 31T 0:00.00 0:00.00 + 40707 0.0 S 31T 0:00.00 0:00.00 + 40707 0.0 S 31T 0:00.00 0:00.01 + 40707 0.0 S 31T 0:00.00 0:00.00 + 40707 0.0 S 31T 0:00.00 0:00.00 + 40707 0.0 S 31T 0:00.10 0:00.03 + 40707 0.0 S 31T 0:00.02 0:00.00 + 40707 0.0 S 31T 0:00.01 0:00.01 +someuser 41159 ?? 0.0 S 31T 0:00.78 0:00.57 /usr/bin/some_command with some parameters + 41159 0.0 S 31T 0:00.00 0:00.00 + 41159 0.0 S 0T 0:00.03 0:00.02 + 41159 0.0 S 31T 0:00.21 0:00.09 + 41159 0.0 S 31T 0:00.00 0:00.00 + 41159 0.0 S 31T 0:00.02 0:00.07 + 41159 0.0 S 31T 0:00.00 0:00.00 + 41159 0.0 S 31T 0:00.00 0:00.01 + 41159 0.0 S 31T 0:00.00 0:00.00 + 41159 0.0 S 31T 0:00.00 0:00.00 + 41159 0.0 S 31T 0:00.00 0:00.00 + 41159 0.0 S 31T 0:00.00 0:00.00 + 41159 0.0 S 31T 0:00.11 0:00.03 + 41159 0.0 S 31T 0:00.04 0:00.03 + 41159 0.0 S 31T 0:00.01 0:00.00 +root 41458 ?? 0.0 S 31T 0:00.24 0:00.05 /usr/bin/some_command with some parameters + 41458 0.0 S 4T 0:00.00 0:00.00 +root 41491 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 41491 0.0 S 4T 0:00.00 0:00.00 +someuser 41501 ?? 0.0 S 4T 0:00.04 0:00.04 /usr/bin/some_command with some parameters + 41501 0.0 S 4T 0:00.02 0:00.01 +someuser 41507 ?? 0.0 S 4T 0:00.61 0:01.35 /usr/bin/some_command with some parameters + 41507 0.0 S 4T 0:00.05 0:00.04 + 41507 0.0 S 4T 0:00.04 0:00.04 + 41507 0.0 S 4T 0:00.02 0:00.02 + 41507 0.0 S 4T 0:00.01 0:00.01 + 41507 0.0 S 4T 0:00.00 0:00.00 +root 41513 ?? 0.0 S 4T 0:00.02 0:00.02 /usr/bin/some_command with some parameters + 41513 0.0 S 4T 0:00.00 0:00.00 +root 41520 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 41520 0.0 S 4T 0:00.00 0:00.00 +someuser 41747 ?? 0.0 S 4T 0:00.34 0:00.10 /usr/bin/some_command with some parameters + 41747 0.0 S 4T 0:00.00 0:00.00 + 41747 0.0 S 4T 0:00.00 0:00.00 +root 41837 ?? 0.0 S 4T 0:00.24 0:00.19 /usr/bin/some_command with some parameters + 41837 0.0 S 4T 0:00.00 0:00.00 + 41837 0.0 S 4T 0:00.01 0:00.00 +root 41852 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 41852 0.0 S 4T 0:00.00 0:00.00 +root 41855 ?? 0.0 S 4T 0:00.02 0:00.05 /usr/bin/some_command with some parameters + 41855 0.0 S 4T 0:00.00 0:00.00 +someuser 41869 ?? 0.0 S 4T 0:00.01 0:00.04 /usr/bin/some_command with some parameters + 41869 0.0 S 4T 0:00.01 0:00.00 +someuser 41875 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 41875 0.0 S 4T 0:00.00 0:00.00 +someuser 41878 ?? 0.0 S 46R 0:00.18 0:00.08 /usr/bin/some_command with some parameters + 41878 0.0 S 20T 0:00.00 0:00.00 +root 41886 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 41886 0.0 S 4T 0:00.00 0:00.01 +root 41890 ?? 0.0 S 4T 0:00.08 0:00.02 /usr/bin/some_command with some parameters + 41890 0.0 S 4T 0:00.01 0:00.00 + 41890 0.0 S 4T 0:00.00 0:00.00 + 41890 0.0 S 4T 0:00.00 0:00.00 +root 41897 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 41897 0.0 S 4T 0:00.15 0:00.04 +someuser 41908 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 41908 0.0 S 37T 0:00.00 0:00.00 + 41908 0.0 S 37T 0:00.00 0:00.00 + 41908 0.0 S 31T 0:00.00 0:00.00 +root 41912 ?? 0.0 S 4T 0:00.06 0:00.03 /usr/bin/some_command with some parameters + 41912 0.0 S 4T 0:00.00 0:00.00 +root 41926 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 41926 0.0 S 20T 0:00.00 0:00.00 + 41926 0.0 S 31T 0:01.15 0:00.50 + 41926 0.0 S 31T 0:00.01 0:00.01 +_netbios 42029 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 42029 0.0 S 4T 0:00.00 0:00.00 +someuser 42082 ?? 0.0 S 20T 0:00.34 0:00.31 /usr/bin/some_command with some parameters + 42082 0.0 S 20T 0:00.03 0:00.01 + 42082 0.0 S 20T 0:00.00 0:00.00 + 42082 0.0 S 20T 0:00.00 0:00.00 +_driverkit 42094 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 42094 0.0 S 31T 0:00.00 0:00.00 +_driverkit 42095 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 42095 0.0 S 31T 0:00.00 0:00.00 +_driverkit 42096 ?? 0.0 S 63R 0:00.75 0:00.30 /usr/bin/some_command with some parameters + 42096 0.0 S 31T 0:00.00 0:00.00 +_driverkit 42097 ?? 0.0 S 63R 0:00.02 0:00.00 /usr/bin/some_command with some parameters + 42097 0.0 S 31T 0:00.00 0:00.00 +_driverkit 42098 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 42098 0.0 S 31T 0:00.00 0:00.00 +_driverkit 42100 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 42100 0.0 S 31T 0:00.00 0:00.00 +root 42115 ?? 0.0 S 4T 0:00.03 0:00.01 /usr/bin/some_command with some parameters + 42115 0.0 S 4T 0:00.01 0:00.00 +someuser 42121 ?? 0.0 S 4T 0:00.17 0:00.09 /usr/bin/some_command with some parameters + 42121 0.0 S 4T 0:00.00 0:00.00 +someuser 42139 ?? 0.0 S 4T 0:00.10 0:00.09 /usr/bin/some_command with some parameters + 42139 0.0 S 4T 0:00.00 0:00.00 +someuser 42155 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 42155 0.0 S 4T 0:00.00 0:00.00 +_spotlight 42306 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 42306 0.0 S 4T 0:00.00 0:00.00 +newrelic 42930 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 42930 0.0 S 4T 0:00.00 0:00.00 +666 42931 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 42931 0.0 S 4T 0:00.00 0:00.00 +someuser 42958 ?? 0.0 S 31T 0:04.06 0:01.29 /usr/bin/some_command with some parameters + 42958 0.0 S 19T 0:00.00 0:00.00 + 42958 0.0 S 37T 0:00.32 0:00.07 + 42958 0.0 S 60R 0:00.00 0:00.00 + 42958 0.0 S 55R 0:00.00 0:00.00 + 42958 0.0 S 31T 0:00.00 0:00.00 + 42958 0.0 S 31T 0:00.00 0:00.00 +someuser 43266 ?? 0.0 S 4T 0:00.35 0:00.23 /usr/bin/some_command with some parameters + 43266 0.0 S 4T 0:00.04 0:00.01 + 43266 0.0 S 4T 0:00.00 0:00.00 + 43266 0.0 S 4T 0:00.00 0:00.00 +someuser 43267 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 43267 0.0 S 4T 0:00.02 0:00.02 +someuser 43686 ?? 0.0 S 4T 0:00.07 0:00.18 /usr/bin/some_command with some parameters + 43686 0.0 S 4T 0:00.00 0:00.00 +someuser 43718 ?? 0.0 S 31T 0:03.94 0:13.45 /usr/bin/some_command with some parameters + 43718 0.0 S 31T 0:00.00 0:00.00 + 43718 0.0 S 0T 0:00.01 0:00.01 + 43718 0.0 S 31T 0:00.91 0:01.19 + 43718 0.0 S 31T 0:00.00 0:00.00 + 43718 0.0 S 31T 0:00.00 0:00.00 + 43718 0.0 S 31T 0:00.00 0:00.00 + 43718 0.0 S 31T 0:00.00 0:00.00 + 43718 0.0 S 31T 0:00.00 0:00.00 + 43718 0.0 S 31T 0:00.00 0:00.00 + 43718 0.0 S 31T 0:00.00 0:00.00 + 43718 0.0 S 31T 0:00.00 0:00.00 + 43718 0.0 S 31T 0:00.01 0:00.00 + 43718 0.0 S 31T 0:00.50 0:00.19 + 43718 0.0 S 31T 0:00.05 0:00.04 + 43718 0.0 S 31T 0:00.02 0:00.01 + 43718 0.0 S 31T 0:00.02 0:00.01 + 43718 0.0 S 31T 0:00.02 0:00.02 + 43718 0.0 S 31T 0:00.02 0:00.02 + 43718 0.0 S 31T 0:00.02 0:00.01 + 43718 0.0 S 31T 0:00.03 0:00.01 + 43718 0.0 S 31T 0:00.01 0:00.00 + 43718 0.0 S 31T 0:00.01 0:00.00 + 43718 0.0 S 31T 0:00.00 0:00.00 +_gamecontrollerd 43719 ?? 0.0 S 4T 0:19.83 0:24.56 /usr/bin/some_command with some parameters + 43719 0.0 S 4T 0:00.24 0:00.19 + 43719 0.0 S 4T 0:00.04 0:00.03 + 43719 0.0 S 4T 0:00.02 0:00.01 + 43719 0.0 S 4T 0:00.00 0:00.00 +_coreaudiod 43720 ?? 0.0 S 4T 0:00.03 0:00.01 Core Audio Driver (ZoomAudioDevice.driver) + 43720 0.0 S 4T 0:00.23 0:00.25 + 43720 0.0 S 4T 0:00.00 0:00.00 +someuser 43724 ?? 0.0 S 20T 0:00.28 0:00.28 /usr/bin/some_command with some parameters + 43724 0.0 S 20T 0:00.02 0:00.01 + 43724 0.0 S 20T 0:00.00 0:00.00 + 43724 0.0 S 20T 0:00.00 0:00.00 +someuser 43725 ?? 0.0 S 31T 0:00.05 0:00.13 /usr/bin/some_command with some parameters + 43725 0.0 S 31T 0:00.00 0:00.00 +someuser 43726 ?? 0.0 S 4T 0:00.04 0:00.04 /usr/bin/some_command with some parameters + 43726 0.0 S 4T 0:00.00 0:00.00 +someuser 43728 ?? 0.0 S 31T 0:00.31 0:00.77 /usr/bin/some_command with some parameters + 43728 0.0 S 31T 0:00.00 0:00.00 + 43728 0.0 S 0T 0:00.01 0:00.01 + 43728 0.0 S 31T 0:00.08 0:00.06 + 43728 0.0 S 31T 0:00.00 0:00.00 + 43728 0.0 S 31T 0:00.00 0:00.00 + 43728 0.0 S 31T 0:00.00 0:00.00 + 43728 0.0 S 31T 0:00.00 0:00.00 + 43728 0.0 S 31T 0:00.00 0:00.00 + 43728 0.0 S 31T 0:00.00 0:00.00 + 43728 0.0 S 31T 0:00.00 0:00.00 + 43728 0.0 S 31T 0:00.00 0:00.00 + 43728 0.0 S 31T 0:00.01 0:00.00 + 43728 0.0 S 31T 0:00.01 0:00.01 + 43728 0.0 S 31T 0:00.01 0:00.00 +someuser 43729 ?? 0.0 S 31T 0:00.39 0:00.76 /usr/bin/some_command with some parameters + 43729 0.0 S 31T 0:00.00 0:00.00 + 43729 0.0 S 0T 0:00.01 0:00.01 + 43729 0.0 S 31T 0:00.07 0:00.05 + 43729 0.0 S 31T 0:00.00 0:00.00 + 43729 0.0 S 31T 0:00.00 0:00.00 + 43729 0.0 S 31T 0:00.00 0:00.00 + 43729 0.0 S 31T 0:00.00 0:00.00 + 43729 0.0 S 31T 0:00.00 0:00.00 + 43729 0.0 S 31T 0:00.00 0:00.00 + 43729 0.0 S 31T 0:00.00 0:00.00 + 43729 0.0 S 31T 0:00.00 0:00.00 + 43729 0.0 S 31T 0:00.02 0:00.00 + 43729 0.0 S 31T 0:00.01 0:00.01 + 43729 0.0 S 31T 0:00.01 0:00.00 +root 43731 ?? 0.0 S 31T 0:00.07 0:00.04 /usr/bin/some_command with some parameters + 43731 0.0 S 31T 0:00.01 0:00.00 +someuser 43865 ?? 0.0 S 4T 0:00.20 0:00.09 /usr/bin/some_command with some parameters + 43865 0.0 S 4T 0:00.00 0:00.00 +someuser 43867 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 43867 0.0 S 4T 0:00.00 0:00.00 +root 43868 ?? 0.0 S 31T 0:07.98 0:13.93 /usr/bin/some_command with some parameters + 43868 0.0 S 19T 0:00.00 0:00.00 + 43868 0.0 S 31T 0:00.00 0:00.00 +someuser 43869 ?? 0.0 S 4T 0:00.05 0:00.06 /usr/bin/some_command with some parameters + 43869 0.0 S 4T 0:00.00 0:00.00 +someuser 43871 ?? 0.0 S 4T 0:00.04 0:00.04 /usr/bin/some_command with some parameters + 43871 0.0 S 4T 0:00.00 0:00.00 + 43871 0.0 S 4T 0:00.00 0:00.00 +root 43873 ?? 0.0 S 4T 0:00.02 0:00.04 /usr/bin/some_command with some parameters + 43873 0.0 S 4T 0:00.01 0:00.00 +_fpsd 43874 ?? 0.0 S 4T 0:00.03 0:00.02 /usr/bin/some_command with some parameters + 43874 0.0 S 4T 0:00.00 0:00.00 +root 43880 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 43880 0.0 S 4T 0:00.00 0:00.00 +someuser 43881 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 43881 0.0 S 4T 0:00.00 0:00.00 +someuser 43882 ?? 0.0 S 46T 0:00.73 0:00.88 /usr/bin/some_command with some parameters + 43882 0.0 S 46T 0:00.08 0:00.03 + 43882 0.0 S 37T 0:00.00 0:00.00 +root 43883 ?? 0.0 S 31T 0:00.05 0:00.04 /usr/bin/some_command with some parameters + 43883 0.0 S 31T 0:00.01 0:00.00 +someuser 43889 ?? 0.0 S 31T 0:00.29 0:00.16 /usr/bin/some_command with some parameters + 43889 0.0 S 37T 0:00.03 0:00.01 + 43889 0.0 S 37T 0:00.00 0:00.00 +someuser 43890 ?? 0.0 S 4T 0:00.02 0:00.01 /usr/bin/some_command with some parameters + 43890 0.0 S 4T 0:00.01 0:00.00 +root 43892 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 43892 0.0 S 4T 0:00.00 0:00.00 +root 43893 ?? 0.0 S 20T 0:00.19 0:00.38 /usr/bin/some_command with some parameters + 43893 0.0 S 20T 0:00.09 0:00.07 + 43893 0.0 S 20T 0:00.17 0:00.20 + 43893 0.0 S 20T 0:00.05 0:00.06 + 43893 0.0 S 20T 0:00.12 0:00.19 + 43893 0.0 S 20T 0:00.05 0:00.08 + 43893 0.0 S 20T 0:00.04 0:00.05 + 43893 0.0 S 20T 0:00.42 0:00.30 + 43893 0.0 S 20T 0:00.17 0:00.44 + 43893 0.0 S 20T 0:03.44 0:14.51 + 43893 0.0 S 20T 0:00.00 0:00.00 + 43893 0.0 S 20T 0:00.28 0:00.93 + 43893 0.0 S 20T 0:00.01 0:00.00 + 43893 0.0 S 20T 0:01.11 0:02.00 + 43893 0.0 S 20T 0:00.02 0:00.01 + 43893 1.8 S 20T 1:03.41 4:28.47 + 43893 0.0 S 20T 0:03.14 0:02.00 + 43893 0.0 S 20T 0:38.76 1:20.10 + 43893 0.0 S 20T 0:00.00 0:00.00 + 43893 0.0 S 20T 0:00.00 0:00.00 + 43893 0.0 S 20T 0:00.00 0:00.00 + 43893 0.0 S 20T 0:00.01 0:00.01 +someuser 43895 ?? 0.0 S 4T 0:00.21 0:00.08 /usr/bin/some_command with some parameters + 43895 0.0 S 4T 0:00.01 0:00.00 + 43895 0.0 S 4T 0:00.00 0:00.00 + 43895 0.0 S 4T 0:00.00 0:00.00 +someuser 43896 ?? 0.0 S 31T 0:00.01 0:00.02 /usr/bin/some_command with some parameters + 43896 0.0 S 31T 0:00.02 0:00.01 +someuser 43898 ?? 0.0 S 4T 0:00.27 0:00.10 /usr/bin/some_command with some parameters + 43898 0.0 S 4T 0:00.00 0:00.00 +someuser 43901 ?? 0.0 S 46T 0:00.21 0:00.12 /usr/bin/some_command with some parameters + 43901 0.0 S 46T 0:00.03 0:00.01 + 43901 0.0 S 37T 0:00.00 0:00.00 +someuser 43904 ?? 0.0 S 4T 0:00.20 0:00.10 /usr/bin/some_command with some parameters + 43904 0.0 S 4T 0:00.01 0:00.00 +someuser 43907 ?? 0.0 S 4T 0:00.04 0:00.05 /usr/bin/some_command with some parameters + 43907 0.0 S 4T 0:00.01 0:00.00 +_installcoordinationd 43908 ?? 0.0 S 4T 0:00.02 0:00.03 /usr/bin/some_command with some parameters + 43908 0.0 S 4T 0:00.00 0:00.00 +root 43910 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 43910 0.0 S 4T 0:00.04 0:00.07 +root 43916 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 43916 0.0 S 20T 0:00.01 0:00.00 +root 43918 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 43918 0.0 S 4T 0:00.01 0:00.00 +someuser 43936 ?? 0.0 S 31T 0:01.79 0:12.34 /usr/bin/some_command with some parameters + 43936 0.0 S 37T 0:00.00 0:00.00 +someuser 43941 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 43941 0.0 S 4T 0:00.00 0:00.00 +root 43942 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 43942 0.0 S 4T 0:00.01 0:00.00 +root 43956 ?? 0.0 S 4T 0:00.06 0:00.08 /usr/bin/some_command with some parameters + 43956 0.0 S 4T 0:00.01 0:00.00 +root 43957 ?? 0.0 S 31T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 43957 0.0 S 37T 0:00.00 0:00.00 +someuser 43966 ?? 0.0 S 4T 0:00.06 0:00.05 /usr/bin/some_command with some parameters + 43966 0.0 S 4T 0:00.00 0:00.00 +someuser 43971 ?? 0.0 S 4T 0:00.03 0:00.01 /usr/bin/some_command with some parameters + 43971 0.0 S 4T 0:00.00 0:00.00 +someuser 43973 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 43973 0.0 S 4T 0:00.00 0:00.00 +someuser 43974 ?? 0.0 S 4T 0:00.02 0:00.01 /usr/bin/some_command with some parameters + 43974 0.0 S 4T 0:00.00 0:00.00 +someuser 43975 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 43975 0.0 S 4T 0:00.00 0:00.00 +someuser 43976 ?? 0.0 S 4T 0:00.02 0:00.03 /usr/bin/some_command with some parameters + 43976 0.0 S 4T 0:00.00 0:00.00 + 43976 0.0 S 4T 0:00.00 0:00.00 +_assetcache 43977 ?? 0.0 S 4T 0:00.03 0:00.03 /usr/bin/some_command with some parameters + 43977 0.0 S 4T 0:00.00 0:00.00 +root 43978 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 43978 0.0 S 4T 0:00.00 0:00.00 +root 43983 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 43983 0.0 S 20T 0:00.00 0:00.00 +root 43984 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 43984 0.0 S 4T 0:00.00 0:00.00 +someuser 44067 ?? 0.0 S 4T 0:00.02 0:00.03 /usr/bin/some_command with some parameters + 44067 0.0 S 4T 0:00.00 0:00.00 + 44067 0.0 S 4T 0:00.00 0:00.00 +someuser 44068 ?? 0.0 S 4T 0:00.17 0:00.07 /usr/bin/some_command with some parameters + 44068 0.0 S 4T 0:00.01 0:00.01 + 44068 0.0 S 4T 0:00.08 0:00.03 + 44068 0.0 S 4T 0:00.03 0:00.01 +someuser 44070 ?? 0.0 S 4T 0:00.02 0:00.01 /usr/bin/some_command with some parameters + 44070 0.0 S 4T 0:00.00 0:00.00 +someuser 44072 ?? 0.0 S 4T 0:00.43 0:00.17 /usr/bin/some_command with some parameters + 44072 0.0 S 4T 0:00.01 0:00.01 + 44072 0.0 S 4T 0:00.00 0:00.00 + 44072 0.0 S 4T 0:00.00 0:00.00 + 44072 0.0 S 4T 0:00.00 0:00.00 + 44072 0.0 S 4T 0:00.00 0:00.00 +someuser 44073 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 44073 0.0 S 4T 0:00.01 0:00.01 +someuser 44074 ?? 0.0 S 4T 0:00.02 0:00.01 /usr/bin/some_command with some parameters + 44074 0.0 S 4T 0:00.00 0:00.00 +someuser 44075 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 44075 0.0 S 4T 0:00.00 0:00.00 +someuser 44076 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 44076 0.0 S 4T 0:00.00 0:00.00 +someuser 44083 ?? 0.0 S 4T 0:00.03 0:00.04 /usr/bin/some_command with some parameters + 44083 0.0 S 4T 0:00.01 0:00.00 +someuser 44084 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 44084 0.0 S 4T 0:00.00 0:00.00 +someuser 44085 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 44085 0.0 S 4T 0:00.00 0:00.00 +root 44086 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 44086 0.0 S 4T 0:00.02 0:00.01 +someuser 44090 ?? 0.0 S 31T 0:00.16 0:00.09 /usr/bin/some_command with some parameters + 44090 0.0 S 37T 0:00.03 0:00.01 + 44090 0.0 S 37T 0:00.00 0:00.00 +someuser 44098 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 44098 0.0 S 4T 0:00.00 0:00.00 +root 44099 ?? 0.0 S 4T 0:00.02 0:00.04 /usr/bin/some_command with some parameters + 44099 0.0 S 4T 0:00.00 0:00.00 + 44099 0.0 S 4T 0:00.00 0:00.00 +someuser 44100 ?? 0.0 S 4T 0:00.23 0:00.13 /usr/bin/some_command with some parameters + 44100 0.0 S 4T 0:00.04 0:00.01 + 44100 0.0 S 4T 0:00.00 0:00.00 + 44100 0.0 S 4T 0:00.00 0:00.00 +root 44101 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 44101 0.0 S 4T 0:00.00 0:00.00 +someuser 44103 ?? 0.0 S 46T 0:00.20 0:00.13 /usr/bin/some_command with some parameters + 44103 0.0 S 46T 0:00.03 0:00.00 + 44103 0.0 S 37T 0:00.00 0:00.00 +root 44153 ?? 0.0 S 31T 0:00.02 0:00.01 /usr/bin/some_command with some parameters + 44153 0.0 S 31T 0:00.00 0:00.00 + 44153 0.0 S 4T 0:00.00 0:00.00 +root 44167 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 44167 0.0 S 31T 0:00.00 0:00.00 +someuser 44185 ?? 0.0 S 4T 0:00.30 0:00.67 /usr/bin/some_command with some parameters + 44185 0.0 S 4T 0:00.01 0:00.00 + 44185 0.0 S 4T 0:00.01 0:00.00 + 44185 0.0 S 4T 0:00.00 0:00.00 +root 44520 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 44520 0.0 S 4T 0:00.01 0:00.04 +someuser 44805 ?? 0.0 S 4T 0:00.24 0:00.39 /usr/bin/some_command with some parameters + 44805 0.0 S 4T 0:00.05 0:00.05 + 44805 0.0 S 4T 0:00.00 0:00.00 + 44805 0.0 S 4T 0:00.02 0:00.13 + 44805 0.0 S 4T 0:00.01 0:00.00 + 44805 0.0 S 4T 0:00.00 0:00.01 + 44805 0.0 S 4T 0:00.00 0:00.00 + 44805 0.0 S 4T 0:00.00 0:00.00 +root 44913 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 44913 0.0 S 20T 0:00.01 0:00.00 +root 45056 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 45056 0.0 S 4T 0:00.00 0:00.00 +root 45060 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 45060 0.0 S 4T 0:00.00 0:00.00 +root 45062 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 45062 0.0 S 4T 0:00.00 0:00.00 +root 45063 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 45063 0.0 S 31T 0:00.00 0:00.00 +someuser 45064 ?? 0.0 S 4T 0:00.02 0:00.03 /usr/bin/some_command with some parameters + 45064 0.0 S 4T 0:00.00 0:00.00 +someuser 45065 ?? 0.0 S 31T 0:00.03 0:00.01 /usr/bin/some_command with some parameters + 45065 0.0 S 31T 0:00.00 0:00.00 +root 45066 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 45066 0.0 S 4T 0:00.00 0:00.00 +root 45067 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 45067 0.0 S 4T 0:00.00 0:00.00 +root 45068 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 45068 0.0 S 4T 0:00.00 0:00.00 +someuser 45069 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 45069 0.0 S 31T 0:00.00 0:00.00 +someuser 45070 ?? 0.0 S 31T 0:00.16 0:00.07 /usr/bin/some_command with some parameters + 45070 0.0 S 37T 0:00.00 0:00.00 + 45070 0.0 S 31T 0:00.00 0:00.00 +someuser 45071 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 45071 0.0 S 31T 0:00.00 0:00.00 +root 45073 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 45073 0.0 S 4T 0:00.00 0:00.00 +_appstore 45096 ?? 0.0 S 4T 0:00.18 0:00.07 /usr/bin/some_command with some parameters + 45096 0.0 S 4T 0:00.00 0:00.00 +someuser 45097 ?? 0.0 S 4T 0:00.03 0:00.05 /usr/bin/some_command with some parameters + 45097 0.0 S 4T 0:00.01 0:00.00 +root 45098 ?? 0.0 S 4T 0:00.01 0:00.05 /usr/bin/some_command with some parameters + 45098 0.0 S 4T 0:00.00 0:00.00 +someuser 45101 ?? 0.0 S 4T 0:00.02 0:00.04 /usr/bin/some_command with some parameters + 45101 0.0 S 4T 0:00.00 0:00.00 +root 45104 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 45104 0.0 S 4T 0:00.00 0:00.00 +root 45105 ?? 0.0 S 20T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 45105 0.0 S 4T 0:00.01 0:00.00 +root 45106 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 45106 0.0 S 4T 0:00.00 0:00.00 +_applepay 45111 ?? 0.0 S 50T 0:00.03 0:00.03 /usr/bin/some_command with some parameters + 45111 0.0 S 31T 0:00.00 0:00.00 +someuser 45174 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 45174 0.0 S 4T 0:00.02 0:00.02 +someuser 45206 ?? 0.0 S 31T 0:04.32 0:10.39 /usr/bin/some_command with some parameters + 45206 0.0 S 31T 0:00.00 0:00.00 + 45206 0.0 S 0T 0:00.12 0:00.09 + 45206 0.0 S 31T 0:02.48 0:02.72 + 45206 0.0 S 31T 0:00.00 0:00.00 + 45206 0.0 S 31T 0:00.01 0:00.00 + 45206 0.0 S 31T 0:00.00 0:00.00 + 45206 0.0 S 31T 0:00.00 0:00.00 + 45206 0.0 S 31T 0:00.00 0:00.00 + 45206 0.0 S 31T 0:00.00 0:00.00 + 45206 0.0 S 31T 0:00.00 0:00.00 + 45206 0.0 S 31T 0:00.00 0:00.00 + 45206 0.0 S 31T 0:00.14 0:00.03 + 45206 0.0 S 31T 0:00.00 0:00.00 + 45206 0.0 S 31T 0:00.02 0:00.00 +someuser 45624 ?? 0.0 S 4T 0:00.03 0:00.01 /usr/bin/some_command with some parameters + 45624 0.0 S 4T 0:00.00 0:00.00 +someuser 45782 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 45782 0.0 S 4T 0:00.00 0:00.00 +someuser 45792 ?? 0.0 S 31T 0:00.69 0:01.23 /usr/bin/some_command with some parameters + 45792 0.0 S 31T 0:00.05 0:00.02 + 45792 0.0 S 31T 0:00.00 0:00.00 + 45792 0.0 S 0T 0:00.00 0:00.00 + 45792 0.0 S 31T 0:00.16 0:00.09 + 45792 0.0 S 31T 0:00.00 0:00.00 + 45792 0.0 S 31T 0:00.01 0:00.03 + 45792 0.0 S 31T 0:00.02 0:00.01 + 45792 0.0 S 31T 0:00.00 0:00.00 + 45792 0.0 S 31T 0:00.00 0:00.00 + 45792 0.0 S 31T 0:00.00 0:00.01 + 45792 0.0 S 31T 0:00.00 0:00.00 + 45792 0.0 S 31T 0:00.00 0:00.00 + 45792 0.0 S 31T 0:00.01 0:00.00 + 45792 0.0 S 31T 0:00.01 0:00.01 +someuser 45933 ?? 0.0 S 20T 0:00.04 0:00.02 /usr/bin/some_command with some parameters + 45933 0.0 S 20T 0:00.00 0:00.00 +_iconservices 45982 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 45982 0.0 S 4T 0:00.00 0:00.00 +someuser 46122 ?? 0.0 S 42T 0:03.35 0:04.46 /usr/bin/some_command with some parameters + 46122 0.0 S 31T 0:21.72 0:06.13 + 46122 0.0 S 31T 0:00.09 0:00.09 + 46122 0.0 S 31T 0:00.24 0:00.26 + 46122 0.0 S 31T 0:00.01 0:00.00 + 46122 1.0 S 31T 24:06.44 0:28.11 + 46122 1.5 S 31T 29:27.74 2:20.05 + 46122 0.0 S 31T 0:00.00 0:00.00 + 46122 0.0 S 31T 0:12.63 0:07.90 + 46122 0.0 S 31T 0:00.00 0:00.00 + 46122 0.0 S 31T 0:07.66 0:02.06 + 46122 0.0 S 31T 0:27.64 0:10.88 + 46122 0.0 S 31T 0:02.82 0:00.63 + 46122 0.0 S 31T 0:00.00 0:00.00 + 46122 0.0 S 31T 0:00.14 0:00.07 + 46122 0.0 S 31T 0:00.21 0:00.26 + 46122 0.0 S 31T 0:21.79 0:11.83 + 46122 0.0 S 31T 0:06.17 0:05.60 + 46122 0.0 S 31T 0:00.00 0:00.00 + 46122 0.0 S 31T 0:00.00 0:00.00 + 46122 0.0 S 31T 0:03.87 0:00.84 + 46122 0.0 S 31T 0:04.06 0:03.00 + 46122 0.0 S 31T 0:00.01 0:00.00 + 46122 0.0 S 31T 0:00.00 0:00.00 + 46122 0.0 S 31T 0:26.09 0:06.42 + 46122 0.0 S 31T 0:00.98 0:00.22 + 46122 0.0 S 31T 0:00.00 0:00.00 + 46122 0.0 S 19T 0:00.00 0:00.00 + 46122 0.0 S 31T 0:00.14 0:00.07 + 46122 0.0 S 31T 0:12.82 0:10.30 + 46122 0.0 S 31T 0:00.00 0:00.00 +someuser 46396 ?? 0.0 S 4T 0:00.05 0:00.04 /usr/bin/some_command with some parameters + 46396 0.0 S 4T 0:00.00 0:00.00 +someuser 46645 ?? 0.0 S 31T 0:00.23 0:00.60 /usr/bin/some_command with some parameters + 46645 0.0 S 31T 0:00.02 0:00.01 + 46645 0.0 S 31T 0:00.00 0:00.00 + 46645 0.0 S 0T 0:00.00 0:00.00 + 46645 0.0 S 31T 0:00.06 0:00.03 + 46645 0.0 S 31T 0:00.00 0:00.00 + 46645 0.0 S 31T 0:00.02 0:00.04 + 46645 0.0 S 31T 0:00.00 0:00.00 + 46645 0.0 S 31T 0:00.00 0:00.01 + 46645 0.0 S 31T 0:00.00 0:00.01 + 46645 0.0 S 31T 0:00.00 0:00.02 + 46645 0.0 S 31T 0:00.00 0:00.02 + 46645 0.0 S 31T 0:00.00 0:00.00 + 46645 0.0 S 31T 0:00.00 0:00.00 + 46645 0.0 S 31T 0:00.00 0:00.01 +someuser 46738 ?? 0.0 S 31T 0:00.59 0:01.17 /usr/bin/some_command with some parameters + 46738 0.0 S 31T 0:00.05 0:00.02 + 46738 0.0 S 31T 0:00.00 0:00.00 + 46738 0.0 S 0T 0:00.00 0:00.00 + 46738 0.0 S 31T 0:00.14 0:00.08 + 46738 0.0 S 31T 0:00.00 0:00.00 + 46738 0.0 S 31T 0:00.02 0:00.06 + 46738 0.0 S 31T 0:00.03 0:00.01 + 46738 0.0 S 31T 0:00.00 0:00.00 + 46738 0.0 S 31T 0:00.00 0:00.01 + 46738 0.0 S 31T 0:00.00 0:00.00 + 46738 0.0 S 31T 0:00.00 0:00.00 + 46738 0.0 S 31T 0:00.00 0:00.00 + 46738 0.0 S 31T 0:00.00 0:00.00 + 46738 0.0 S 31T 0:00.03 0:00.02 +someuser 47353 ?? 0.0 S 31T 0:00.88 0:04.50 /usr/bin/some_command with some parameters + 47353 0.0 S 31T 0:00.07 0:00.03 + 47353 0.0 S 31T 0:00.00 0:00.00 + 47353 0.0 S 0T 0:00.00 0:00.00 + 47353 0.0 S 31T 0:00.25 0:00.23 + 47353 0.0 S 31T 0:00.00 0:00.00 + 47353 0.0 S 31T 0:00.23 0:01.09 + 47353 0.0 S 31T 0:00.00 0:00.00 + 47353 0.0 S 31T 0:00.01 0:00.03 + 47353 0.0 S 31T 0:00.02 0:00.03 + 47353 0.0 S 31T 0:00.01 0:00.03 + 47353 0.0 S 31T 0:00.01 0:00.03 + 47353 0.0 S 31T 0:00.00 0:00.00 + 47353 0.0 S 31T 0:00.00 0:00.00 + 47353 0.0 S 31T 0:00.02 0:00.02 +someuser 47355 ?? 0.0 S 31T 0:00.46 0:02.51 /usr/bin/some_command with some parameters + 47355 0.0 S 31T 0:00.04 0:00.02 + 47355 0.0 S 31T 0:00.00 0:00.00 + 47355 0.0 S 0T 0:00.00 0:00.00 + 47355 0.0 S 31T 0:00.09 0:00.05 + 47355 0.0 S 31T 0:00.00 0:00.00 + 47355 0.0 S 31T 0:00.04 0:00.18 + 47355 0.0 S 31T 0:00.00 0:00.00 + 47355 0.0 S 31T 0:00.01 0:00.04 + 47355 0.0 S 31T 0:00.00 0:00.01 + 47355 0.0 S 31T 0:00.00 0:00.03 + 47355 0.0 S 31T 0:00.00 0:00.02 + 47355 0.0 S 31T 0:00.00 0:00.00 + 47355 0.0 S 31T 0:00.01 0:00.00 + 47355 0.0 S 31T 0:00.02 0:00.02 +root 49788 ?? 0.0 S 4T 0:00.03 0:00.01 /usr/bin/some_command with some parameters + 49788 0.0 S 4T 0:00.03 0:00.01 +_softwareupdate 51166 ?? 0.0 S 4T 0:08.83 0:00.85 /usr/bin/some_command with some parameters + 51166 0.0 S 4T 0:00.58 0:00.03 + 51166 0.0 S 4T 0:00.00 0:00.00 + 51166 0.0 S 4T 0:00.04 0:00.02 + 51166 0.0 S 4T 0:00.01 0:00.00 +root 51168 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 51168 0.0 S 4T 0:00.00 0:00.00 +_atsserver 51169 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 51169 0.0 S 4T 0:00.00 0:00.00 +someuser 51368 ?? 0.0 S 4T 0:00.82 0:00.33 /usr/bin/some_command with some parameters + 51368 0.0 S 4T 0:00.01 0:00.00 +someuser 52356 ?? 0.0 S 31T 0:00.36 0:00.97 /usr/bin/some_command with some parameters + 52356 0.0 S 31T 0:00.03 0:00.01 + 52356 0.0 S 31T 0:00.00 0:00.00 + 52356 0.0 S 0T 0:00.00 0:00.00 + 52356 0.0 S 31T 0:00.05 0:00.04 + 52356 0.0 S 31T 0:00.00 0:00.00 + 52356 0.0 S 31T 0:00.01 0:00.03 + 52356 0.0 S 31T 0:00.01 0:00.00 + 52356 0.0 S 31T 0:00.00 0:00.00 + 52356 0.0 S 31T 0:00.00 0:00.00 + 52356 0.0 S 31T 0:00.00 0:00.01 + 52356 0.0 S 31T 0:00.00 0:00.00 + 52356 0.0 S 31T 0:00.00 0:00.00 + 52356 0.0 S 31T 0:00.01 0:00.00 + 52356 0.0 S 31T 0:00.01 0:00.01 +someuser 52359 ?? 0.0 S 31T 0:00.57 0:03.45 /usr/bin/some_command with some parameters + 52359 0.0 S 31T 0:00.04 0:00.02 + 52359 0.0 S 31T 0:00.00 0:00.00 + 52359 0.0 S 0T 0:00.00 0:00.00 + 52359 0.0 S 31T 0:00.26 0:00.39 + 52359 0.0 S 31T 0:00.00 0:00.00 + 52359 0.0 S 31T 0:00.44 0:01.68 + 52359 0.0 S 31T 0:00.00 0:00.00 + 52359 0.0 S 31T 0:00.01 0:00.02 + 52359 0.0 S 31T 0:00.00 0:00.02 + 52359 0.0 S 31T 0:00.01 0:00.03 + 52359 0.0 S 31T 0:00.01 0:00.01 + 52359 0.0 S 31T 0:00.00 0:00.00 + 52359 0.0 S 31T 0:00.01 0:00.00 + 52359 0.0 S 31T 0:00.00 0:00.00 +root 53270 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 53270 0.0 S 4T 0:00.00 0:00.00 +root 53628 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 53628 0.0 S 4T 0:00.00 0:00.00 +root 53631 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 53631 0.0 S 4T 0:00.00 0:00.00 +someuser 53753 ?? 0.0 S 4T 0:00.05 0:00.06 /usr/bin/some_command with some parameters + 53753 0.0 S 4T 0:00.00 0:00.00 +root 53792 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 53792 0.0 S 19T 0:00.00 0:00.00 + 53792 0.0 S 31T 0:00.00 0:00.00 +root 53793 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 53793 0.0 S 4T 0:00.00 0:00.00 +someuser 53835 ?? 0.0 S 46T 0:00.04 0:00.06 /usr/bin/some_command with some parameters + 53835 0.0 S 46T 0:00.00 0:00.00 + 53835 24.1 S 31T 1:21.34 3:38.44 + 53835 0.1 S 20T 0:00.12 0:00.08 + 53835 0.1 S 20T 0:00.06 0:00.04 + 53835 0.2 S 46T 0:00.03 0:00.02 +someuser 53836 ?? 0.4 S 46T 0:04.23 0:05.61 /usr/bin/some_command with some parameters + 53836 0.0 S 46T 0:00.00 0:00.00 + 53836 6.1 S 31T 0:16.08 1:47.22 + 53836 0.5 S 37T 0:00.90 0:00.16 + 53836 0.4 S 46T 0:00.48 0:00.09 + 53836 0.0 S 31T 0:00.00 0:00.00 +someuser 53837 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters + 53837 0.0 S 4T 0:00.00 0:00.00 + 53837 0.0 S 4T 0:00.00 0:00.00 +someuser 53838 ?? 0.0 S 4T 0:00.12 0:00.01 /usr/bin/some_command with some parameters + 53838 0.0 S 4T 0:00.00 0:00.00 +someuser 53839 ?? 0.0 S 4T 0:00.24 0:00.02 /usr/bin/some_command with some parameters + 53839 0.0 S 4T 0:00.00 0:00.00 +someuser 53885 ?? 0.0 S 31T 0:00.20 0:00.89 /usr/bin/some_command with some parameters + 53885 0.0 S 31T 0:00.02 0:00.01 + 53885 0.0 S 31T 0:00.00 0:00.00 + 53885 0.0 S 0T 0:00.00 0:00.00 + 53885 0.0 S 31T 0:00.04 0:00.04 + 53885 0.0 S 31T 0:00.00 0:00.00 + 53885 0.0 S 31T 0:00.02 0:00.06 + 53885 0.0 S 31T 0:00.01 0:00.00 + 53885 0.0 S 31T 0:00.00 0:00.00 + 53885 0.0 S 31T 0:00.00 0:00.00 + 53885 0.0 S 31T 0:00.00 0:00.02 + 53885 0.0 S 31T 0:00.00 0:00.00 + 53885 0.0 S 31T 0:00.00 0:00.00 + 53885 0.0 S 31T 0:00.00 0:00.00 + 53885 0.0 S 31T 0:00.00 0:00.01 +someuser 53929 ?? 0.0 S 31T 0:00.20 0:01.08 /usr/bin/some_command with some parameters + 53929 0.0 S 31T 0:00.01 0:00.00 + 53929 0.0 S 31T 0:00.00 0:00.00 + 53929 0.0 S 0T 0:00.00 0:00.00 + 53929 0.0 S 31T 0:00.05 0:00.07 + 53929 0.0 S 31T 0:00.00 0:00.00 + 53929 0.0 S 31T 0:00.06 0:00.22 + 53929 0.0 S 31T 0:00.00 0:00.00 + 53929 0.0 S 31T 0:00.00 0:00.02 + 53929 0.0 S 31T 0:00.00 0:00.03 + 53929 0.0 S 31T 0:00.00 0:00.01 + 53929 0.0 S 31T 0:00.00 0:00.02 + 53929 0.0 S 31T 0:00.00 0:00.00 + 53929 0.0 S 31T 0:00.00 0:00.00 + 53929 0.0 S 31T 0:00.01 0:00.00 +someuser 53931 ?? 0.0 S 31T 0:00.03 0:00.08 /usr/bin/some_command with some parameters + 53931 0.0 S 31T 0:00.00 0:00.00 + 53931 0.0 S 31T 0:00.00 0:00.00 + 53931 0.0 S 31T 0:00.00 0:00.00 + 53931 0.0 S 0T 0:00.00 0:00.00 + 53931 0.0 S 31T 0:00.00 0:00.00 + 53931 0.0 S 31T 0:00.00 0:00.00 + 53931 0.0 S 31T 0:00.00 0:00.00 + 53931 0.0 S 31T 0:00.00 0:00.00 + 53931 0.0 S 31T 0:00.00 0:00.00 + 53931 0.0 S 31T 0:00.00 0:00.00 + 53931 0.0 S 31T 0:00.00 0:00.00 + 53931 0.0 S 31T 0:00.00 0:00.00 + 53931 0.0 S 31T 0:00.00 0:00.00 +someuser 54166 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 54166 0.0 S 4T 0:00.00 0:00.00 + 54166 0.0 S 4T 0:00.00 0:00.00 + 54166 0.0 S 4T 0:00.00 0:00.00 +someuser 55004 ?? 0.0 S 4T 0:00.78 0:02.70 /usr/bin/some_command with some parameters + 55004 0.0 S 4T 0:00.05 0:00.03 + 55004 0.0 S 4T 0:00.00 0:00.00 + 55004 0.0 S 4T 0:00.00 0:00.00 +someuser 55005 ?? 0.0 S 4T 0:00.03 0:00.03 /usr/bin/some_command with some parameters + 55005 0.0 S 4T 0:00.00 0:00.00 + 55005 0.0 S 4T 0:00.00 0:00.00 +someuser 55006 ?? 0.0 S 4T 0:00.03 0:00.04 /usr/bin/some_command with some parameters + 55006 0.0 S 4T 0:00.01 0:00.00 +someuser 55007 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 55007 0.0 S 4T 0:00.00 0:00.00 +someuser 55008 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 55008 0.0 S 4T 0:00.00 0:00.00 +someuser 55010 ?? 0.0 S 4T 0:00.03 0:00.04 /usr/bin/some_command with some parameters + 55010 0.0 S 4T 0:00.00 0:00.00 +someuser 55011 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 55011 0.0 S 4T 0:00.00 0:00.00 +_spotlight 55287 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 55287 0.0 S 4T 0:00.00 0:00.04 + 55287 0.0 S 4T 0:00.00 0:00.00 +someuser 55291 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 55291 0.0 S 4T 0:00.00 0:00.04 + 55291 0.0 S 4T 0:00.10 0:00.09 + 55291 0.0 S 4T 0:00.00 0:00.00 +someuser 55294 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 55294 0.0 S 4T 0:00.00 0:00.04 + 55294 0.0 S 4T 0:00.05 0:00.07 + 55294 0.0 S 4T 0:00.00 0:00.00 +someuser 55348 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters + 55348 0.0 S 4T 0:00.00 0:00.04 + 55348 0.0 S 4T 0:00.00 0:00.00 + 55348 0.0 S 4T 0:00.00 0:00.00 +someuser 55349 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 55349 0.0 S 4T 0:00.00 0:00.04 + 55349 0.0 S 4T 0:00.00 0:00.00 + 55349 0.0 S 4T 0:00.00 0:00.00 +root 55706 ?? 0.0 S 31T 0:00.05 0:00.01 /usr/bin/some_command with some parameters + 55706 0.0 S 20T 0:00.00 0:00.00 +someuser 56786 ?? 0.0 S 31T 0:21.59 1:26.39 /usr/bin/some_command with some parameters + 56786 0.0 S 31T 0:00.00 0:00.00 + 56786 0.0 S 0T 0:00.04 0:00.02 + 56786 0.0 S 31T 0:01.50 0:01.81 + 56786 0.0 S 31T 0:00.01 0:00.01 + 56786 0.0 S 31T 0:00.10 0:00.47 + 56786 0.0 S 31T 0:00.02 0:00.01 + 56786 0.0 S 31T 0:00.00 0:00.02 + 56786 0.0 S 31T 0:00.01 0:00.03 + 56786 0.0 S 31T 0:00.00 0:00.01 + 56786 0.0 S 31T 0:00.00 0:00.01 + 56786 0.0 S 31T 0:00.00 0:00.00 + 56786 0.0 S 31T 0:00.09 0:00.03 + 56786 0.0 S 31T 0:00.02 0:00.04 + 56786 0.0 S 31T 0:00.19 0:00.07 + 56786 0.0 S 31T 0:00.01 0:00.08 + 56786 0.0 S 31T 0:00.00 0:00.00 + 56786 0.0 S 31T 0:00.00 0:00.00 + 56786 0.0 S 31T 0:00.00 0:00.00 + 56786 0.0 S 31T 0:00.00 0:00.00 +someuser 67087 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 67087 0.0 S 20T 0:00.00 0:00.00 +root 70071 ?? 0.0 S 37T 0:00.20 0:00.09 /usr/bin/some_command with some parameters + 70071 0.0 S 31T 0:00.02 0:00.01 + 70071 0.0 S 20T 0:00.00 0:00.00 +_usbmuxd 70682 ?? 0.0 S 31T 0:00.09 0:00.04 /usr/bin/some_command with some parameters + 70682 0.0 S 31T 0:00.06 0:00.01 + 70682 0.0 S 31T 0:00.00 0:00.00 +someuser 70696 ?? 0.0 S 4T 0:00.79 0:00.35 /usr/bin/some_command with some parameters + 70696 0.0 S 4T 0:00.00 0:00.00 + 70696 0.0 S 4T 0:00.00 0:00.00 + 70696 0.0 S 4T 0:00.12 0:00.02 + 70696 0.0 S 4T 0:00.00 0:00.00 + 70696 0.0 S 4T 0:00.00 0:00.00 +someuser 70752 ?? 0.0 S 4T 0:00.07 0:00.03 /usr/bin/some_command with some parameters + 70752 0.0 S 4T 0:00.00 0:00.00 + 70752 0.0 S 4T 0:00.00 0:00.00 + 70752 0.0 S 4T 0:00.01 0:00.00 +_driverkit 70896 ?? 0.0 S 63R 0:00.01 0:00.00 /usr/bin/some_command with some parameters + 70896 0.0 S 31T 0:00.00 0:00.00 +_driverkit 70898 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters + 70898 0.0 S 37T 0:00.14 0:00.05 + 70898 0.0 S 63R 0:00.03 0:00.01 +_driverkit 70899 ?? 0.0 S 63R 0:38.05 0:25.27 /usr/bin/some_command with some parameters + 70899 0.0 S 31T 0:00.00 0:00.00 +root 71311 ?? 0.0 S 31T 0:00.37 0:00.25 /usr/bin/some_command with some parameters + 71311 0.0 S 19T 0:00.00 0:00.00 + 71311 0.0 S 4T 0:00.00 0:00.00 + 71311 0.0 S 4T 0:00.00 0:00.00 +someuser 75951 ?? 0.0 S 31T 0:03.06 0:05.41 /usr/bin/some_command with some parameters + 75951 0.0 S 31T 0:00.00 0:00.00 + 75951 0.0 S 0T 0:00.09 0:00.06 + 75951 0.0 S 31T 0:01.48 0:01.12 + 75951 0.0 S 31T 0:00.00 0:00.00 + 75951 0.0 S 31T 0:00.03 0:00.10 + 75951 0.0 S 31T 0:00.00 0:00.00 + 75951 0.0 S 31T 0:00.00 0:00.00 + 75951 0.0 S 31T 0:00.00 0:00.01 + 75951 0.0 S 31T 0:00.00 0:00.00 + 75951 0.0 S 31T 0:00.00 0:00.00 + 75951 0.0 S 31T 0:00.00 0:00.00 + 75951 0.0 S 31T 0:00.12 0:00.03 + 75951 0.0 S 31T 0:00.08 0:00.02 + 75951 0.0 S 31T 0:00.00 0:00.00 +someuser 76232 ?? 0.0 S 31T 0:15.22 0:22.98 /usr/bin/some_command with some parameters + 76232 0.0 S 31T 0:00.00 0:00.00 + 76232 0.0 S 0T 0:00.08 0:00.06 + 76232 0.0 S 31T 0:02.42 0:02.74 + 76232 0.0 S 31T 0:00.01 0:00.02 + 76232 0.0 S 31T 0:00.10 0:00.34 + 76232 0.0 S 31T 0:00.00 0:00.00 + 76232 0.0 S 31T 0:00.01 0:00.02 + 76232 0.0 S 31T 0:00.01 0:00.01 + 76232 0.0 S 31T 0:00.00 0:00.02 + 76232 0.0 S 31T 0:00.01 0:00.01 + 76232 0.0 S 31T 0:00.00 0:00.00 + 76232 0.0 S 31T 0:00.01 0:00.04 + 76232 0.0 S 31T 0:00.17 0:00.04 + 76232 0.0 S 31T 0:00.00 0:00.00 + 76232 0.0 S 31T 0:00.09 0:00.03 +someuser 79317 ?? 0.0 S 31T 0:05.42 0:07.07 /usr/bin/some_command with some parameters + 79317 0.0 S 31T 0:00.00 0:00.00 + 79317 0.0 S 0T 0:00.08 0:00.05 + 79317 0.0 S 31T 0:01.01 0:00.60 + 79317 0.0 S 31T 0:00.00 0:00.00 + 79317 0.0 S 31T 0:00.06 0:00.25 + 79317 0.0 S 31T 0:00.00 0:00.00 + 79317 0.0 S 31T 0:00.00 0:00.03 + 79317 0.0 S 31T 0:00.00 0:00.01 + 79317 0.0 S 31T 0:00.00 0:00.01 + 79317 0.0 S 31T 0:00.00 0:00.02 + 79317 0.0 S 31T 0:00.00 0:00.00 + 79317 0.0 S 31T 0:00.34 0:00.04 + 79317 0.0 S 31T 0:00.01 0:00.00 + 79317 0.0 S 31T 0:00.20 0:00.04 + 79317 0.0 S 31T 0:00.02 0:00.01 +someuser 79623 ?? 0.0 S 31T 0:10.60 0:19.96 /usr/bin/some_command with some parameters + 79623 0.0 S 31T 0:00.00 0:00.00 + 79623 0.0 S 0T 0:00.02 0:00.02 + 79623 0.0 S 31T 0:01.10 0:01.15 + 79623 0.0 S 31T 0:00.00 0:00.00 + 79623 0.0 S 31T 0:00.00 0:00.00 + 79623 0.0 S 31T 0:00.00 0:00.00 + 79623 0.0 S 31T 0:00.00 0:00.00 + 79623 0.0 S 31T 0:00.00 0:00.00 + 79623 0.0 S 31T 0:00.00 0:00.00 + 79623 0.0 S 31T 0:00.00 0:00.00 + 79623 0.0 S 31T 0:00.00 0:00.00 + 79623 0.0 S 31T 0:00.02 0:00.01 + 79623 0.0 S 31T 0:00.04 0:00.01 + 79623 0.0 S 31T 0:00.17 0:00.06 + 79623 0.0 S 31T 0:00.02 0:00.01 + 79623 0.0 S 31T 0:00.00 0:00.00 +someuser 79636 ?? 0.0 S 31T 0:10.19 0:18.21 /usr/bin/some_command with some parameters + 79636 0.0 S 31T 0:00.00 0:00.00 + 79636 0.0 S 0T 0:00.02 0:00.02 + 79636 0.0 S 31T 0:01.04 0:01.12 + 79636 0.0 S 31T 0:00.00 0:00.00 + 79636 0.0 S 31T 0:00.00 0:00.00 + 79636 0.0 S 31T 0:00.00 0:00.00 + 79636 0.0 S 31T 0:00.00 0:00.00 + 79636 0.0 S 31T 0:00.00 0:00.00 + 79636 0.0 S 31T 0:00.00 0:00.00 + 79636 0.0 S 31T 0:00.00 0:00.00 + 79636 0.0 S 31T 0:00.00 0:00.00 + 79636 0.0 S 31T 0:00.01 0:00.01 + 79636 0.0 S 31T 0:00.03 0:00.01 + 79636 0.0 S 31T 0:00.15 0:00.06 + 79636 0.0 S 31T 0:00.01 0:00.00 + 79636 0.0 S 31T 0:00.00 0:00.00 +someuser 79637 ?? 0.0 S 31T 0:00.26 0:00.20 /usr/bin/some_command with some parameters + 79637 0.0 S 31T 0:00.00 0:00.00 + 79637 0.0 S 31T 0:00.03 0:00.02 + 79637 0.0 S 0T 0:00.02 0:00.02 + 79637 0.0 S 31T 0:00.17 0:00.05 + 79637 0.0 S 31T 0:00.00 0:00.00 + 79637 0.0 S 31T 0:00.00 0:00.00 + 79637 0.0 S 31T 0:00.00 0:00.00 + 79637 0.0 S 31T 0:00.00 0:00.00 + 79637 0.0 S 31T 0:00.00 0:00.00 + 79637 0.0 S 31T 0:00.00 0:00.00 + 79637 0.0 S 31T 0:00.00 0:00.00 + 79637 0.0 S 31T 0:00.00 0:00.00 + 79637 0.0 S 31T 0:00.04 0:00.01 + 79637 0.0 S 31T 0:00.00 0:00.00 +someuser 79692 ?? 0.0 S 31T 0:11.25 0:18.01 /usr/bin/some_command with some parameters + 79692 0.0 S 31T 0:00.00 0:00.00 + 79692 0.0 S 0T 0:00.02 0:00.02 + 79692 0.0 S 31T 0:01.06 0:01.05 + 79692 0.0 S 31T 0:00.01 0:00.02 + 79692 0.0 S 31T 0:00.04 0:00.17 + 79692 0.0 S 31T 0:00.00 0:00.00 + 79692 0.0 S 31T 0:00.00 0:00.00 + 79692 0.0 S 31T 0:00.00 0:00.00 + 79692 0.0 S 31T 0:00.00 0:00.01 + 79692 0.0 S 31T 0:00.00 0:00.01 + 79692 0.0 S 31T 0:00.00 0:00.00 + 79692 0.0 S 31T 0:00.10 0:00.01 + 79692 0.0 S 31T 0:00.14 0:00.05 + 79692 0.0 S 31T 0:00.10 0:00.05 +someuser 79727 ?? 0.0 S 4T 0:47.45 1:38.46 /usr/bin/some_command with some parameters + 79727 0.0 S 4T 0:00.85 0:00.52 + 79727 0.0 S 4T 0:00.28 0:00.22 + 79727 0.0 S 4T 0:00.08 0:00.07 + 79727 0.0 S 4T 0:00.00 0:00.00 +someuser 79738 ?? 0.0 S 31T 0:12.99 0:22.71 /usr/bin/some_command with some parameters + 79738 0.0 S 31T 0:00.00 0:00.00 + 79738 0.0 S 0T 0:00.02 0:00.02 + 79738 0.0 S 31T 0:01.21 0:01.39 + 79738 0.0 S 31T 0:00.01 0:00.01 + 79738 0.0 S 31T 0:00.37 0:00.69 + 79738 0.0 S 31T 0:00.00 0:00.00 + 79738 0.0 S 31T 0:00.07 0:00.06 + 79738 0.0 S 31T 0:00.07 0:00.06 + 79738 0.0 S 31T 0:00.07 0:00.07 + 79738 0.0 S 31T 0:00.07 0:00.07 + 79738 0.0 S 31T 0:00.00 0:00.00 + 79738 0.0 S 31T 0:00.05 0:00.01 + 79738 0.0 S 31T 0:00.17 0:00.06 + 79738 0.0 S 31T 0:00.04 0:00.06 +someuser 80172 ?? 0.0 S 31T 0:03.00 0:06.88 /usr/bin/some_command with some parameters + 80172 0.0 S 31T 0:00.00 0:00.00 + 80172 0.0 S 0T 0:00.02 0:00.02 + 80172 0.0 S 31T 0:00.30 0:00.20 + 80172 0.0 S 31T 0:00.00 0:00.00 + 80172 0.0 S 31T 0:00.06 0:00.15 + 80172 0.0 S 31T 0:00.00 0:00.00 + 80172 0.0 S 31T 0:00.00 0:00.00 + 80172 0.0 S 31T 0:00.01 0:00.01 + 80172 0.0 S 31T 0:00.01 0:00.01 + 80172 0.0 S 31T 0:00.00 0:00.01 + 80172 0.0 S 31T 0:00.00 0:00.00 + 80172 0.0 S 31T 0:00.11 0:00.02 + 80172 0.0 S 31T 0:00.09 0:00.03 + 80172 0.0 S 31T 0:00.00 0:00.00 +someuser 87339 ?? 0.0 S 31T 0:16.87 1:17.95 /usr/bin/some_command with some parameters + 87339 0.0 S 31T 0:00.00 0:00.00 + 87339 0.0 S 0T 0:00.02 0:00.02 + 87339 0.0 S 31T 0:03.27 0:03.72 + 87339 0.0 S 31T 0:00.00 0:00.00 + 87339 0.0 S 31T 0:01.53 0:09.72 + 87339 0.0 S 31T 0:00.00 0:00.00 + 87339 0.0 S 31T 0:00.07 0:00.24 + 87339 0.0 S 31T 0:00.09 0:00.27 + 87339 0.0 S 31T 0:00.08 0:00.26 + 87339 0.0 S 31T 0:00.08 0:00.26 + 87339 0.0 S 31T 0:00.00 0:00.00 + 87339 0.0 S 31T 0:00.09 0:00.03 + 87339 0.0 S 31T 0:00.02 0:00.01 + 87339 0.0 S 31T 0:00.17 0:00.06 + 87339 0.0 S 31T 0:00.02 0:00.01 + 87339 0.0 S 31T 0:00.00 0:00.00 +someuser 89436 ?? 0.0 S 4T 0:00.04 0:00.02 /usr/bin/some_command with some parameters + 89436 0.0 S 4T 0:00.00 0:00.00 +someuser 89517 ?? 0.0 S 31T 1:21.10 12:08.73 /usr/bin/some_command with some parameters + 89517 0.0 S 31T 0:00.00 0:00.00 + 89517 0.0 S 0T 0:00.05 0:00.03 + 89517 0.0 S 31T 0:24.50 0:33.51 + 89517 0.0 S 31T 0:00.00 0:00.00 + 89517 0.0 S 31T 1:10.56 3:32.25 + 89517 0.0 S 31T 0:00.00 0:00.00 + 89517 0.0 S 31T 0:00.14 0:00.17 + 89517 0.0 S 31T 0:00.10 0:00.15 + 89517 0.0 S 31T 0:00.21 0:00.23 + 89517 0.0 S 31T 0:00.10 0:00.18 + 89517 0.0 S 31T 0:00.00 0:00.00 + 89517 0.0 S 31T 0:00.04 0:00.03 + 89517 0.0 S 31T 0:00.18 0:00.15 + 89517 0.0 S 31T 0:00.20 0:00.16 + 89517 0.0 S 31T 0:00.20 0:00.15 + 89517 0.0 S 31T 0:00.18 0:00.15 + 89517 0.0 S 31T 0:00.17 0:00.12 + 89517 0.0 S 31T 0:00.18 0:00.12 + 89517 0.0 S 31T 0:00.18 0:00.12 + 89517 0.0 S 31T 0:00.22 0:00.12 + 89517 0.0 S 31T 0:00.14 0:00.02 + 89517 0.0 S 31T 0:00.12 0:00.05 + 89517 0.0 S 31T 0:00.10 0:00.17 +someuser 92412 ?? 0.0 S 31T 0:19.36 1:03.93 /usr/bin/some_command with some parameters + 92412 0.0 S 31T 0:00.00 0:00.00 + 92412 0.0 S 0T 0:00.02 0:00.02 + 92412 0.0 S 31T 0:02.58 0:02.93 + 92412 0.0 S 31T 0:00.00 0:00.00 + 92412 0.0 S 31T 0:01.08 0:06.32 + 92412 0.0 S 31T 0:00.00 0:00.00 + 92412 0.0 S 31T 0:00.06 0:00.17 + 92412 0.0 S 31T 0:00.04 0:00.18 + 92412 0.0 S 31T 0:00.04 0:00.17 + 92412 0.0 S 31T 0:00.05 0:00.17 + 92412 0.0 S 31T 0:00.00 0:00.00 + 92412 0.0 S 31T 0:00.11 0:00.02 + 92412 0.0 S 31T 0:00.03 0:00.02 + 92412 0.0 S 31T 0:00.03 0:00.02 + 92412 0.0 S 31T 0:00.01 0:00.02 + 92412 0.0 S 31T 0:00.20 0:00.07 + 92412 0.0 S 31T 0:00.01 0:00.00 + 92412 0.0 S 31T 0:00.00 0:00.00 +someuser 96559 ?? 0.0 S 46T 0:13.24 0:26.60 /usr/bin/some_command with some parameters + 96559 0.0 S 46T 0:00.60 0:00.29 + 96559 0.0 S 31T 0:00.00 0:00.00 +someuser 97411 ?? 0.0 S 31T 0:01.26 0:01.54 /usr/bin/some_command with some parameters + 97411 0.0 S 31T 0:00.00 0:00.00 + 97411 0.0 S 0T 0:00.04 0:00.03 + 97411 0.0 S 31T 0:00.64 0:00.45 + 97411 0.0 S 31T 0:00.00 0:00.00 + 97411 0.0 S 31T 0:00.00 0:00.00 + 97411 0.0 S 31T 0:00.00 0:00.00 + 97411 0.0 S 31T 0:00.00 0:00.00 + 97411 0.0 S 31T 0:00.00 0:00.00 + 97411 0.0 S 31T 0:00.00 0:00.00 + 97411 0.0 S 31T 0:00.00 0:00.00 + 97411 0.0 S 31T 0:00.00 0:00.00 + 97411 0.0 S 31T 0:00.19 0:00.04 + 97411 0.0 S 31T 0:00.01 0:00.01 + 97411 0.0 S 31T 0:00.00 0:00.00 +someuser 98939 ?? 0.0 S 31T 0:05.17 0:23.65 /usr/bin/some_command with some parameters + 98939 0.0 S 31T 0:00.00 0:00.00 + 98939 0.0 S 0T 0:00.02 0:00.02 + 98939 0.0 S 31T 0:00.81 0:00.64 + 98939 0.0 S 31T 0:00.00 0:00.00 + 98939 0.0 S 31T 0:00.88 0:02.66 + 98939 0.0 S 31T 0:00.00 0:00.00 + 98939 0.0 S 31T 0:00.02 0:00.04 + 98939 0.0 S 31T 0:00.01 0:00.05 + 98939 0.0 S 31T 0:00.04 0:00.07 + 98939 0.0 S 31T 0:00.02 0:00.03 + 98939 0.0 S 31T 0:00.00 0:00.01 + 98939 0.0 S 31T 0:00.12 0:00.02 + 98939 0.0 S 31T 0:00.14 0:00.05 + 98939 0.0 S 31T 0:00.08 0:00.15 + 98939 0.0 S 31T 0:00.01 0:00.07 +someuser 99779 ?? 0.0 S 31T 0:02.61 0:08.14 /usr/bin/some_command with some parameters + 99779 0.0 S 31T 0:00.00 0:00.00 + 99779 0.0 S 0T 0:00.02 0:00.02 + 99779 0.0 S 31T 0:00.47 0:00.22 + 99779 0.0 S 31T 0:00.00 0:00.00 + 99779 0.0 S 31T 0:00.09 0:00.32 + 99779 0.0 S 31T 0:00.00 0:00.00 + 99779 0.0 S 31T 0:00.00 0:00.00 + 99779 0.0 S 31T 0:00.00 0:00.00 + 99779 0.0 S 31T 0:00.00 0:00.00 + 99779 0.0 S 31T 0:00.00 0:00.01 + 99779 0.0 S 31T 0:00.00 0:00.00 + 99779 0.0 S 31T 0:00.08 0:00.02 + 99779 0.0 S 31T 0:00.07 0:00.02 + 99779 0.0 S 31T 0:00.00 0:00.00 +someuser 99817 ?? 0.0 S 31T 0:00.60 0:00.17 /usr/bin/some_command with some parameters + 99817 0.0 S 31T 0:00.00 0:00.00 + 99817 0.0 S 31T 0:00.07 0:00.05 + 99817 0.0 S 0T 0:00.06 0:00.05 + 99817 0.0 S 31T 0:00.14 0:00.04 + 99817 0.0 S 31T 0:00.00 0:00.00 + 99817 0.0 S 31T 0:00.12 0:00.05 + 99817 0.0 S 31T 0:00.00 0:00.00 + 99817 0.0 S 31T 0:00.00 0:00.00 + 99817 0.0 S 31T 0:00.00 0:00.00 + 99817 0.0 S 31T 0:00.00 0:00.00 + 99817 0.0 S 31T 0:00.00 0:00.00 + 99817 0.0 S 31T 0:00.00 0:00.00 + 99817 0.0 S 31T 0:00.00 0:00.00 +someuser 99889 ?? 0.0 S 31T 0:02.90 0:03.20 /usr/bin/some_command with some parameters + 99889 0.0 S 31T 0:00.00 0:00.00 + 99889 0.0 S 0T 0:00.02 0:00.01 + 99889 0.0 S 31T 0:00.47 0:00.24 + 99889 0.0 S 31T 0:00.00 0:00.00 + 99889 0.0 S 31T 0:00.05 0:00.15 + 99889 0.0 S 31T 0:00.06 0:00.01 + 99889 0.0 S 31T 0:00.00 0:00.00 + 99889 0.0 S 31T 0:00.01 0:00.01 + 99889 0.0 S 31T 0:00.00 0:00.01 + 99889 0.0 S 31T 0:00.00 0:00.02 + 99889 0.0 S 31T 0:00.00 0:00.00 + 99889 0.0 S 31T 0:00.09 0:00.03 + 99889 0.0 S 31T 0:00.02 0:00.01 + 99889 0.0 S 31T 0:00.01 0:00.01 +root 2956 s000 0.0 S 31T 0:00.03 0:00.02 /usr/bin/some_command with some parameters + 2956 0.0 S 31T 0:00.00 0:00.00 +someuser 2959 s000 0.0 S 31T 0:00.30 0:00.26 -bash +someuser 6945 s000 0.0 S 31T 0:00.05 0:00.01 tmux +someuser 6948 s004 0.0 S 31T 0:00.58 0:00.38 -bash +someuser 6999 s005 0.0 S 31T 0:00.52 0:00.29 -bash +someuser 7049 s006 0.0 S 31T 0:00.20 0:00.20 -bash +someuser 11147 s007 0.0 S 31T 0:00.42 0:00.46 -bash +someuser 65815 s008 0.0 S 31T 0:01.10 0:00.67 -bash +someuser 1393 s010 0.0 S 31T 0:00.36 0:00.31 -bash +someuser 26136 s014 0.0 S 31T 0:00.54 0:00.33 /usr/bin/some_command with some parameters +someuser 42855 s026 0.0 S 31T 0:00.64 0:00.36 -bash +root 55350 s026 0.0 R 31T 0:00.00 0:00.00 ps -ax -M +` diff --git a/pkg/internal/infraolly/process/sample.go b/pkg/internal/infraolly/process/sample.go new file mode 100644 index 000000000..0b2030d79 --- /dev/null +++ b/pkg/internal/infraolly/process/sample.go @@ -0,0 +1,36 @@ +package process + +import "github.com/shirou/gopsutil/process" + +type Sample struct { + ProcessDisplayName string `json:"processDisplayName"` + ProcessID int32 `json:"processId"` + CommandName string `json:"commandName"` + User string `json:"userName,omitempty"` + MemoryRSSBytes int64 `json:"memoryResidentSizeBytes"` + MemoryVMSBytes int64 `json:"memoryVirtualSizeBytes"` + CPUPercent float64 `json:"cpuPercent"` + CPUUserPercent float64 `json:"cpuUserPercent"` + CPUSystemPercent float64 `json:"cpuSystemPercent"` + ContainerImage string `json:"containerImage,omitempty"` + ContainerImageName string `json:"containerImageName,omitempty"` + ContainerName string `json:"containerName,omitempty"` + ContainerID string `json:"containerId,omitempty"` + Contained string `json:"contained,omitempty"` + CmdLine string `json:"commandLine,omitempty"` + Status string `json:"state,omitempty"` + ParentProcessID int32 `json:"parentProcessId,omitempty"` + ThreadCount int32 `json:"threadCount,omitempty"` + FdCount *int32 `json:"fileDescriptorCount,omitempty"` + IOReadCountPerSecond *float64 `json:"ioReadCountPerSecond,omitempty"` + IOWriteCountPerSecond *float64 `json:"ioWriteCountPerSecond,omitempty"` + IOReadBytesPerSecond *float64 `json:"ioReadBytesPerSecond,omitempty"` + IOWriteBytesPerSecond *float64 `json:"ioWriteBytesPerSecond,omitempty"` + IOTotalReadCount *uint64 `json:"ioTotalReadCount,omitempty"` + IOTotalWriteCount *uint64 `json:"ioTotalWriteCount,omitempty"` + IOTotalReadBytes *uint64 `json:"ioTotalReadBytes,omitempty"` + IOTotalWriteBytes *uint64 `json:"ioTotalWriteBytes,omitempty"` + // Auxiliary values, not to be reported + LastIOCounters *process.IOCountersStat `json:"-"` + ContainerLabels map[string]string `json:"-"` +} diff --git a/pkg/internal/infraolly/process/sampler_darwin.go b/pkg/internal/infraolly/process/sampler_darwin.go new file mode 100644 index 000000000..56ae8f3f9 --- /dev/null +++ b/pkg/internal/infraolly/process/sampler_darwin.go @@ -0,0 +1,175 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package process + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "github.com/newrelic/infrastructure-agent/internal/agent" + "github.com/newrelic/infrastructure-agent/pkg/config" + "github.com/newrelic/infrastructure-agent/pkg/metrics" + "github.com/newrelic/infrastructure-agent/pkg/metrics/sampler" + "github.com/newrelic/infrastructure-agent/pkg/metrics/types" + "github.com/newrelic/infrastructure-agent/pkg/sample" +) + +// processSampler is an implementation of the metrics_sender.Sampler interface, which returns runtime information about +// the currently running processes +type processSampler struct { + harvest Harvester + containerSamplers []metrics.ContainerSampler + lastRun time.Time + hasAlreadyRun bool + interval time.Duration +} + +var ( + _ sampler.Sampler = (*processSampler)(nil) // static interface assertion + containerNotRunningErrs = map[string]struct{}{} +) + +// NewProcessSampler creates and returns a new process Sampler, given an agent context. +func NewProcessSampler(ctx agent.AgentContext) sampler.Sampler { + hasConfig := ctx != nil && ctx.Config() != nil + + ttlSecs := config.DefaultContainerCacheMetadataLimit + apiVersion := "" + interval := config.FREQ_INTERVAL_FLOOR_PROCESS_METRICS + dockerContainerdNamespace := "" + if hasConfig { + cfg := ctx.Config() + ttlSecs = cfg.ContainerMetadataCacheLimit + apiVersion = cfg.DockerApiVersion + dockerContainerdNamespace = cfg.DockerContainerdNamespace + interval = cfg.MetricsProcessSampleRate + } + harvester := newHarvester(ctx) + containerSamplers := metrics.GetContainerSamplers(time.Duration(ttlSecs)*time.Second, apiVersion, dockerContainerdNamespace) + + return &processSampler{ + harvest: harvester, + containerSamplers: containerSamplers, + interval: time.Second * time.Duration(interval), + } + +} + +func (ps *processSampler) OnStartup() {} + +func (ps *processSampler) Name() string { + return "ProcessSampler" +} + +func (ps *processSampler) Interval() time.Duration { + return ps.interval +} + +func (ps *processSampler) Disabled() bool { + return ps.Interval() <= config.FREQ_DISABLE_SAMPLING +} + +// Sample returns samples for all the running processes, decorated with container runtime information, if applies. +func (ps *processSampler) Sample() (results sample.EventBatch, err error) { + var elapsedMs int64 + var elapsedSeconds float64 + now := time.Now() + if ps.hasAlreadyRun { + elapsedMs = (now.UnixNano() - ps.lastRun.UnixNano()) / 1000000 + } + elapsedSeconds = float64(elapsedMs) / 1000 + ps.lastRun = now + + pids, err := ps.harvest.Pids() + if err != nil { + return nil, err + } + + var containerDecorators []metrics.ProcessDecorator + + for _, containerSampler := range ps.containerSamplers { + if !containerSampler.Enabled() { + continue + } + + decorator, err := containerSampler.NewDecorator() + if err != nil { + // ensure containerDecorator is set to nil if error + decorator = nil + if id := containerIDFromNotRunningErr(err); id != "" { + if _, ok := containerNotRunningErrs[id]; !ok { + containerNotRunningErrs[id] = struct{}{} + mplog.WithError(err).Warn("instantiating container sampler process decorator") + } + } else { + mplog.WithError(err).Warn("instantiating container sampler process decorator") + if strings.Contains(err.Error(), "client is newer than server") { + mplog.WithError(err).Error("Only docker api version from 1.24 upwards are officially supported. You can still use the docker_api_version configuration to work with older versions. You can check https://docs.docker.com/develop/sdk/ what api version maps with each docker version.") + } + } + } else { + containerDecorators = append(containerDecorators, decorator) + } + } + + for _, pid := range pids { + var processSample *Sample + var err error + + processSample, err = ps.harvest.Do(pid, elapsedSeconds) + if err != nil { + procLog := mplog.WithError(err) + if errors.Is(err, errProcessWithoutRSS) { + procLog = procLog.WithField(config.TracesFieldName, config.ProcessTrace) + } + + procLog.WithField("pid", pid).Debug("Skipping process.") + continue + } + + for _, containerDecorator := range containerDecorators { + if containerDecorator != nil { + containerDecorator.Decorate(processSample) + } + } + + results = append(results, ps.normalizeSample(processSample)) + } + + ps.hasAlreadyRun = true + return results, nil +} + +func (ps *processSampler) normalizeSample(s *Sample) sample.Event { + if len(s.ContainerLabels) > 0 { + sb, err := json.Marshal(s) + if err == nil { + bm := &types.FlatProcessSample{} + if err = json.Unmarshal(sb, bm); err == nil { + for name, value := range s.ContainerLabels { + key := fmt.Sprintf("containerLabel_%s", name) + (*bm)[key] = value + } + return bm + } + } else { + mplog.WithError(err).WithField("sample", fmt.Sprintf("%+v", s)).Debug("normalizeSample can't operate on the sample.") + } + } + return s +} + +func containerIDFromNotRunningErr(err error) string { + prefix := "Error response from daemon: Container " + suffix := " is not running" + msg := err.Error() + i := strings.Index(msg, prefix) + j := strings.Index(msg, suffix) + if i == -1 || j == -1 { + return "" + } + return msg[len(prefix):j] +} diff --git a/pkg/internal/infraolly/process/sampler_darwin_test.go b/pkg/internal/infraolly/process/sampler_darwin_test.go new file mode 100644 index 000000000..d7a5fd170 --- /dev/null +++ b/pkg/internal/infraolly/process/sampler_darwin_test.go @@ -0,0 +1,155 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package process + +import ( + "errors" + "testing" + + "github.com/newrelic/infrastructure-agent/pkg/metrics" + + "github.com/newrelic/infrastructure-agent/internal/agent/mocks" + "github.com/newrelic/infrastructure-agent/pkg/config" + "github.com/newrelic/infrastructure-agent/pkg/metrics/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestProcessSampler_Sample(t *testing.T) { + ctx := new(mocks.AgentContext) + cfg := &config.Config{RunMode: config.ModeRoot} + ctx.On("Config").Times(3).Return(cfg) + + harvester := &HarvesterMock{} + sampler := NewProcessSampler(ctx).(*processSampler) + sampler.harvest = harvester + + samples := []*Sample{ + { + ProcessDisplayName: "proc 1", + ProcessID: 1, + }, + { + ProcessDisplayName: "proc 2", + ProcessID: 2, + }, + { + ProcessDisplayName: "proc 3", + ProcessID: 3, + }, + } + var pids []int32 + for _, s := range samples { + pids = append(pids, s.ProcessID) + } + + harvester.ShouldReturnPids(pids, nil) + for _, s := range samples { + harvester.ShouldDo(s.ProcessID, 0, s, nil) + } + + eventBatch, err := sampler.Sample() + assert.Nil(t, err) + assert.Len(t, eventBatch, len(samples)) + for i, e := range eventBatch { + assert.Equal(t, samples[i], e) + } + + mock.AssertExpectationsForObjects(t, ctx, harvester) +} + +func TestProcessSampler_Sample_ErrorOnProcessShouldNotStop(t *testing.T) { + ctx := new(mocks.AgentContext) + cfg := &config.Config{RunMode: config.ModeRoot} + ctx.On("Config").Times(3).Return(cfg) + + harvester := &HarvesterMock{} + sampler := NewProcessSampler(ctx).(*processSampler) + sampler.harvest = harvester + + samples := []struct { + pid int32 + name string + err error + }{ + { + name: "proc 1", + pid: 1, + }, + { + name: "proc 2", + pid: 2, + err: errors.New("some error"), + }, + { + name: "proc 3", + pid: 3, + }, + } + var pids []int32 + for _, s := range samples { + pids = append(pids, s.pid) + } + + harvester.ShouldReturnPids(pids, nil) + for _, s := range samples { + harvester.ShouldDo(s.pid, 0, &Sample{ProcessID: s.pid, ProcessDisplayName: s.name}, s.err) + } + + eventBatch, err := sampler.Sample() + assert.Nil(t, err) + assert.Len(t, eventBatch, 2) + assert.Equal(t, int32(1), eventBatch[0].(*Sample).ProcessID) + assert.Equal(t, int32(3), eventBatch[1].(*Sample).ProcessID) + + mock.AssertExpectationsForObjects(t, ctx, harvester) +} + +func TestProcessSampler_Sample_DockerDecorator(t *testing.T) { + ctx := new(mocks.AgentContext) + cfg := &config.Config{RunMode: config.ModeRoot} + ctx.On("Config").Times(3).Return(cfg) + + harvester := &HarvesterMock{} + sampler := NewProcessSampler(ctx).(*processSampler) + sampler.harvest = harvester + sampler.containerSamplers = []metrics.ContainerSampler{&fakeContainerSampler{}} + + samples := []*Sample{ + { + ProcessDisplayName: "proc 1", + ProcessID: 1, + }, + { + ProcessDisplayName: "proc 2", + ProcessID: 2, + }, + { + ProcessDisplayName: "proc 3", + ProcessID: 3, + }, + } + var pids []int32 + for _, s := range samples { + pids = append(pids, s.ProcessID) + } + + harvester.ShouldReturnPids(pids, nil) + for _, s := range samples { + harvester.ShouldDo(s.ProcessID, 0, s, nil) + } + + eventBatch, err := sampler.Sample() + assert.Nil(t, err) + assert.Len(t, eventBatch, len(samples)) + for i, e := range eventBatch { + flatProcessSample := e.(*types.FlatProcessSample) + assert.Equal(t, float64(samples[i].ProcessID), (*flatProcessSample)["processId"]) + assert.Equal(t, samples[i].ProcessDisplayName, (*flatProcessSample)["processDisplayName"]) + assert.Equal(t, "decorated", (*flatProcessSample)["containerImage"]) + assert.Equal(t, "value1", (*flatProcessSample)["containerLabel_label1"]) + assert.Equal(t, "value2", (*flatProcessSample)["containerLabel_label2"]) + } + + mock.AssertExpectationsForObjects(t, ctx, harvester) +} diff --git a/pkg/internal/infraolly/process/sampler_linux.go b/pkg/internal/infraolly/process/sampler_linux.go new file mode 100644 index 000000000..a99dc3820 --- /dev/null +++ b/pkg/internal/infraolly/process/sampler_linux.go @@ -0,0 +1,180 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package process + +import ( + "encoding/json" + "errors" + "fmt" + "math" + "strings" + "time" + + "github.com/hashicorp/golang-lru/v2/simplelru" + "github.com/newrelic/infrastructure-agent/internal/agent" + "github.com/newrelic/infrastructure-agent/pkg/config" + "github.com/newrelic/infrastructure-agent/pkg/metrics" + "github.com/newrelic/infrastructure-agent/pkg/metrics/sampler" + "github.com/newrelic/infrastructure-agent/pkg/metrics/types" + "github.com/newrelic/infrastructure-agent/pkg/sample" +) + +// processSampler is an implementation of the metrics_sender.Sampler interface, which returns runtime information about +// the currently running processes +type processSampler struct { + harvest Harvester + containerSamplers []metrics.ContainerSampler + lastRun time.Time + hasAlreadyRun bool + interval time.Duration + cache *cache +} + +var ( + _ sampler.Sampler = (*processSampler)(nil) // static interface assertion + containerNotRunningErrs = map[string]struct{}{} +) + +// NewProcessSampler creates and returns a new process Sampler, given an agent context. +func NewProcessSampler(ctx agent.AgentContext) sampler.Sampler { + hasConfig := ctx != nil && ctx.Config() != nil + + ttlSecs := config.DefaultContainerCacheMetadataLimit + apiVersion := "" + dockerContainerdNamespace := "" + interval := config.FREQ_INTERVAL_FLOOR_PROCESS_METRICS + if hasConfig { + cfg := ctx.Config() + ttlSecs = cfg.ContainerMetadataCacheLimit + apiVersion = cfg.DockerApiVersion + dockerContainerdNamespace = cfg.DockerContainerdNamespace + interval = cfg.MetricsProcessSampleRate + } + // we purge entries explicitly so size is unbounded + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + harvest := newHarvester(ctx, &cache) + containerSamplers := metrics.GetContainerSamplers(time.Duration(ttlSecs)*time.Second, apiVersion, dockerContainerdNamespace) + + return &processSampler{ + harvest: harvest, + containerSamplers: containerSamplers, + cache: &cache, + interval: time.Second * time.Duration(interval), + } +} + +func (ps *processSampler) OnStartup() {} + +func (ps *processSampler) Name() string { + return "ProcessSampler" +} + +func (ps *processSampler) Interval() time.Duration { + return ps.interval +} + +func (ps *processSampler) Disabled() bool { + return ps.Interval() <= config.FREQ_DISABLE_SAMPLING +} + +// Sample returns samples for all the running processes, decorated with Docker runtime information, if applies. +func (ps *processSampler) Sample() (results sample.EventBatch, err error) { + var elapsedMs int64 + var elapsedSeconds float64 + now := time.Now() + if ps.hasAlreadyRun { + elapsedMs = (now.UnixNano() - ps.lastRun.UnixNano()) / 1000000 + } + elapsedSeconds = float64(elapsedMs) / 1000 + ps.lastRun = now + + pids, err := ps.harvest.Pids() + if err != nil { + return nil, err + } + + var containerDecorators []metrics.ProcessDecorator + + for _, containerSampler := range ps.containerSamplers { + if !containerSampler.Enabled() { + continue + } + + decorator, err := containerSampler.NewDecorator() + if err != nil { + if id := containerIDFromNotRunningErr(err); id != "" { + if _, ok := containerNotRunningErrs[id]; !ok { + containerNotRunningErrs[id] = struct{}{} + + mplog.WithError(err).Warn("instantiating container sampler process decorator") + } + } else { + mplog.WithError(err).Warn("instantiating container sampler process decorator") + if strings.Contains(err.Error(), "client is newer than server") { + mplog.WithError(err).Error("Only docker api version from 1.24 upwards are officially supported. You can still use the docker_api_version configuration to work with older versions. You can check https://docs.docker.com/develop/sdk/ what api version maps with each docker version.") + } + } + } else { + containerDecorators = append(containerDecorators, decorator) + } + } + + for _, pid := range pids { + var processSample *Sample + var err error + + processSample, err = ps.harvest.Do(pid, elapsedSeconds) + if err != nil { + procLog := mplog.WithError(err) + if errors.Is(err, errProcessWithoutRSS) { + procLog = procLog.WithField(config.TracesFieldName, config.ProcessTrace) + } + + procLog.WithField("pid", pid).Debug("Skipping process.") + continue + } + + for _, containerDecorator := range containerDecorators { + if containerDecorator != nil { + containerDecorator.Decorate(processSample) + } + } + + results = append(results, ps.normalizeSample(processSample)) + } + + ps.cache.items.RemoveUntilLen(len(pids)) + ps.hasAlreadyRun = true + return results, nil +} + +func (ps *processSampler) normalizeSample(s *Sample) sample.Event { + if len(s.ContainerLabels) > 0 { + sb, err := json.Marshal(s) + if err == nil { + bm := &types.FlatProcessSample{} + if err = json.Unmarshal(sb, bm); err == nil { + for name, value := range s.ContainerLabels { + key := fmt.Sprintf("containerLabel_%s", name) + (*bm)[key] = value + } + return bm + } + } else { + mplog.WithError(err).WithField("sample", fmt.Sprintf("%+v", s)).Debug("normalizeSample can't operate on the sample.") + } + } + return s +} + +func containerIDFromNotRunningErr(err error) string { + prefix := "Error response from daemon: Container " + suffix := " is not running" + msg := err.Error() + i := strings.Index(msg, prefix) + j := strings.Index(msg, suffix) + if i == -1 || j == -1 { + return "" + } + return msg[len(prefix):j] +} diff --git a/pkg/internal/infraolly/process/sampler_linux_test.go b/pkg/internal/infraolly/process/sampler_linux_test.go new file mode 100644 index 000000000..cacab82b9 --- /dev/null +++ b/pkg/internal/infraolly/process/sampler_linux_test.go @@ -0,0 +1,199 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package process + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/newrelic/infrastructure-agent/internal/agent" + "github.com/newrelic/infrastructure-agent/internal/agent/mocks" + agentTypes "github.com/newrelic/infrastructure-agent/internal/agent/types" + "github.com/newrelic/infrastructure-agent/pkg/config" + "github.com/newrelic/infrastructure-agent/pkg/entity" + "github.com/newrelic/infrastructure-agent/pkg/entity/host" + "github.com/newrelic/infrastructure-agent/pkg/metrics" + "github.com/newrelic/infrastructure-agent/pkg/metrics/types" + "github.com/newrelic/infrastructure-agent/pkg/plugins/ids" + "github.com/newrelic/infrastructure-agent/pkg/sample" + "github.com/newrelic/infrastructure-agent/pkg/sysinfo" + "github.com/newrelic/infrastructure-agent/pkg/sysinfo/hostname" +) + +func TestProcessSampler_DockerDecorator(t *testing.T) { + // Given a Process Sampler + ctx := new(mocks.AgentContext) + ctx.On("Config").Return(&config.Config{}) + ctx.On("GetServiceForPid", mock.Anything).Return("", false) + ps := NewProcessSampler(ctx).(*processSampler) + ps.harvest = &harvesterMock{samples: map[int32]*Sample{ + 1: { + ProcessID: 1, + ProcessDisplayName: "Hello", + }, + 2: { + ProcessID: 2, + ProcessDisplayName: "Bye", + }, + }} + ps.containerSamplers = []metrics.ContainerSampler{&fakeContainerSampler{}} + + // When asking for the process samples + samples, err := ps.Sample() + require.NoError(t, err) + + // They are returned, decorated and normalized + require.Len(t, samples, 2) + + for i := range samples { + flatProcessSample := samples[i].(*types.FlatProcessSample) + switch int32((*flatProcessSample)["processId"].(float64)) { + case 1: + assert.Equal(t, "Hello", (*flatProcessSample)["processDisplayName"]) + case 2: + assert.Equal(t, "Bye", (*flatProcessSample)["processDisplayName"]) + default: + assert.Failf(t, fmt.Sprintf("Unknown process: %#v", *flatProcessSample), "") + } + assert.Equal(t, "decorated", (*flatProcessSample)["containerImage"]) + assert.Equal(t, "value1", (*flatProcessSample)["containerLabel_label1"]) + assert.Equal(t, "value2", (*flatProcessSample)["containerLabel_label2"]) + } +} + +type harvesterMock struct { + samples map[int32]*Sample +} + +func (hm *harvesterMock) Pids() ([]int32, error) { + keys := make([]int32, 0) + for k := range hm.samples { + keys = append(keys, k) + } + return keys, nil +} + +func (hm *harvesterMock) Do(pid int32, _ float64) (*Sample, error) { + return hm.samples[pid], nil +} + +func BenchmarkProcessSampler(b *testing.B) { + pm := NewProcessSampler(&dummyAgentContext{}) + + for i := 0; i < b.N; i++ { + _, _ = pm.Sample() + } +} + +// Tests procs monitor without the Docker container metadata cache +func BenchmarkProcessSampler_NoCache(b *testing.B) { + pm := NewProcessSampler(&dummyAgentContext{ + cfg: &config.Config{ + ContainerMetadataCacheLimit: -5, + }}) + + for i := 0; i < b.N; i++ { + _, _ = pm.Sample() + } +} + +// DummyAgentContext replaces mock agent context because mocks management can have impact in benchmarks +type dummyAgentContext struct { + agent.AgentContext + cfg *config.Config +} + +func (*dummyAgentContext) Context() context.Context { + return context.TODO() +} + +func (*dummyAgentContext) ActiveEntitiesChannel() chan string { + return nil +} + +func (*dummyAgentContext) AddReconnecting(agent.Plugin) {} + +func (*dummyAgentContext) EntityKey() string { + return "" +} + +func (*dummyAgentContext) CacheServicePids(source string, pidMap map[int]string) {} + +func (d *dummyAgentContext) Config() *config.Config { + return d.cfg +} + +func (*dummyAgentContext) GetServiceForPid(pid int) (service string, ok bool) { + return "", false +} + +func (*dummyAgentContext) HostnameResolver() hostname.Resolver { + return nil +} + +func (*dummyAgentContext) Reconnect() {} + +func (*dummyAgentContext) SendData(agentTypes.PluginOutput) {} + +func (*dummyAgentContext) SendEvent(event sample.Event, entityKey entity.Key) {} + +func (*dummyAgentContext) Unregister(ids.PluginID) {} + +func (*dummyAgentContext) Version() string { + return "" +} + +func (dummyAgentContext) IDLookup() host.IDLookup { + idLookupTable := make(host.IDLookup) + idLookupTable[sysinfo.HOST_SOURCE_HOSTNAME_SHORT] = "short_hostname" + return idLookupTable +} + +func Test_checkContainerNotRunning(t *testing.T) { + type args struct { + err error + } + tests := []struct { + name string + args args + want string + }{ + { + name: "match", + args: args{err: errors.New("Error response from daemon: Container e9c57d578de9e487f6f703d04b1b237b1ff3d926d9cc2a4adfcbe8e1946e841f is not running")}, + want: "e9c57d578de9e487f6f703d04b1b237b1ff3d926d9cc2a4adfcbe8e1946e841f", + }, + { + name: "match2", + args: args{err: errors.New("Error response from daemon: Container cb33a2dfaa4b25dddcd509b434bc6cd6c088a4e39a2611776d45fdb02b763039 is not running")}, + want: "cb33a2dfaa4b25dddcd509b434bc6cd6c088a4e39a2611776d45fdb02b763039", + }, + { + name: "nomatch", + args: args{err: errors.New("not legit")}, + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := containerIDFromNotRunningErr(tt.args.err); got != tt.want { + t.Errorf("check() = %v, want %v", got, tt.want) + } + }) + } +} + +func Benchmark_checkContainerNotRunning(b *testing.B) { + err := errors.New("Error response from daemon: Container e9c57d578de9e487f6f703d04b1b237b1ff3d926d9cc2a4adfcbe8e1946e841f is not running") + for i := 0; i < b.N; i++ { + if id := containerIDFromNotRunningErr(err); id != "e9c57d578de9e487f6f703d04b1b237b1ff3d926d9cc2a4adfcbe8e1946e841f" { + b.Fatalf("check() = %s, want %s", id, "e9c57d578de9e487f6f703d04b1b237b1ff3d926d9cc2a4adfcbe8e1946e841f") + } + } +} diff --git a/pkg/internal/infraolly/process/sampler_unix_test.go b/pkg/internal/infraolly/process/sampler_unix_test.go new file mode 100644 index 000000000..995c3d81f --- /dev/null +++ b/pkg/internal/infraolly/process/sampler_unix_test.go @@ -0,0 +1,32 @@ +//go:build !windows +// +build !windows + +// Copyright New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package process + +import ( + "github.com/newrelic/infrastructure-agent/pkg/metrics" + "github.com/newrelic/infrastructure-agent/pkg/metrics/types" +) + +type fakeContainerSampler struct{} + +func (cs *fakeContainerSampler) Enabled() bool { + return true +} + +func (*fakeContainerSampler) NewDecorator() (metrics.ProcessDecorator, error) { //nolint:ireturn + return &fakeDecorator{}, nil +} + +type fakeDecorator struct{} + +func (pd *fakeDecorator) Decorate(process *Sample) { + process.ContainerImage = "decorated" + process.ContainerLabels = map[string]string{ + "label1": "value1", + "label2": "value2", + } +} diff --git a/pkg/internal/infraolly/process/snapshot.go b/pkg/internal/infraolly/process/snapshot.go new file mode 100644 index 000000000..8d07dc212 --- /dev/null +++ b/pkg/internal/infraolly/process/snapshot.go @@ -0,0 +1,47 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package process + +import ( + "github.com/shirou/gopsutil/v3/process" +) + +// CPUInfo represents CPU usage statistics at a given point +type CPUInfo struct { + // Percent is the total CPU usage percent + Percent float64 + // User is the CPU user time + User float64 + // System is the CPU system time + System float64 +} + +// Snapshot represents the status of a process at a given time. Instances of Snapshot must not be +// reused for different samples +type Snapshot interface { + // Pid returns the Process ID + Pid() int32 + // Ppid returns the Parent Process ID + Ppid() int32 + // Status returns the state of the process: R (running or runnable), D (uninterruptible sleep), S (interruptible + // sleep), Z (defunct/zombie) or T (stopped) + Status() string + // Command returns the process Command name + Command() string + // CmdLine returns the process invoking command line, with or without arguments + CmdLine(withArgs bool) (string, error) + // Username returns the name of the process owner user + Username() (string, error) + // CPUTimes returns the CPU consumption percentages for the process + CPUTimes() (CPUInfo, error) + // IOCounters returns the I/O statistics for the process + IOCounters() (*process.IOCountersStat, error) + // NumThreads returns the number of threads that are being used by the process + NumThreads() int32 + // NumFDs returns the number of File Descriptors that are open by the process + NumFDs() (int32, error) + // VmRSS returns the Resident Set Size (memory in RAM) of the process + VmRSS() int64 + // VmSize returns the total memory of the process (RSS + virtual memory) + VmSize() int64 +} diff --git a/pkg/internal/infraolly/process/snapshot_darwin.go b/pkg/internal/infraolly/process/snapshot_darwin.go new file mode 100644 index 000000000..a5706e399 --- /dev/null +++ b/pkg/internal/infraolly/process/snapshot_darwin.go @@ -0,0 +1,248 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package process + +import ( + "os" + "runtime" + "strings" + "time" + + "github.com/shirou/gopsutil/v3/process" + + "github.com/newrelic/infrastructure-agent/pkg/helpers" +) + +// darwinProcess is an implementation of the process.Snapshot interface for darwin hosts. +type darwinProcess struct { + // if privileged == false, some operations will be avoided: FD and IO count + privileged bool + + stats procStats + process Process + lastCPU CPUInfo + lastTime time.Time + + // data that will be reused between samples of the same process + pid int32 + user string + cmdLine string +} + +// needed to calculate RSS +var pageSize int64 + +func init() { + pageSize = int64(os.Getpagesize()) + if pageSize <= 0 { + pageSize = 4096 // default value + } +} + +var _ Snapshot = (*darwinProcess)(nil) // static interface assertion + +// getDarwinProcess returns a darwin process snapshot, trying to reuse the data from a previous snapshot of the same +// process. +func getDarwinProcess(proc Process, privileged bool) (*darwinProcess, error) { + + stats, err := collectProcStats(proc) + if err != nil { + return nil, err + } + + return &darwinProcess{ + privileged: privileged, + pid: proc.ProcessId(), + process: proc, + stats: stats, + }, nil +} + +func (pw *darwinProcess) Pid() int32 { + return pw.pid +} + +func (pw *darwinProcess) Username() (string, error) { + var err error + if pw.user == "" { // caching user + pw.user, err = pw.process.Username() + if err != nil { + return "", err + } + } + return pw.user, nil +} + +func (pw *darwinProcess) IOCounters() (*process.IOCountersStat, error) { + //Not implemented in darwin yet + return nil, nil +} + +// NumFDs returns the number of file descriptors. It returns -1 (and nil error) if the Agent does not have privileges to +// access this information. +func (pw *darwinProcess) NumFDs() (int32, error) { + //Not implemented in darwin yet + return -1, nil +} + +// /////////////////////////// +// Data to be derived from /proc//stat in linux systems. In darwin this structure will be populated +// if no error happens retrieving the information from process and will allow to cache some process vallues +// to avoid calling multiple times to same method +// /////////////////////////// +type procStats struct { + command string + ppid int32 + numThreads int32 + state string + vmRSS int64 + vmSize int64 + cpu CPUInfo +} + +// collectProcStats will gather information about the process and will return procStats struct with the necessary information +// not to call process methods more than once per iteration. It will return error if any of the expected +// items returns an error. +func collectProcStats(p Process) (procStats, error) { + var s procStats + name, err := p.Name() + if err != nil { + return s, err + } + + var ppid int32 + var parent Process + if p.ProcessId() != 1 { + parent, err = p.Parent() + if err == nil { + ppid = parent.ProcessId() + } + } + numThreads, err := p.NumThreads() + if err != nil { + return s, err + } + status, err := p.Status() + if err != nil { + return s, err + } + memInfo, err := p.MemoryInfo() + if err != nil { + return s, err + } + cpuPercent, err := p.CPUPercent() + if err != nil { + return s, err + } + times, err := p.Times() + if err != nil { + return s, err + } + + // unit64 to int64 conversion so there are options to lose data if rss > 9,223 PetaBytes + rss := int64(memInfo.RSS) + if rss > 0 { + s.vmRSS = rss + } + vms := int64(memInfo.VMS) + if vms > 0 { + s.vmSize = vms + } + + s.command = name + s.ppid = ppid + s.numThreads = numThreads + if len(status) > 0 { + s.state = status[0] + } + s.cpu = CPUInfo{ + Percent: cpuPercent, + User: times.User, + System: times.System, + } + + return s, nil +} + +func (pw *darwinProcess) CPUTimes() (CPUInfo, error) { + now := time.Now() + + if pw.lastTime.IsZero() { + // invoked first time + pw.lastCPU = pw.stats.cpu + pw.lastTime = now + return pw.stats.cpu, nil + } + + // Calculate CPU percent from user time, system time, and last harvested cpu counters + numcpu := runtime.NumCPU() + delta := (now.Sub(pw.lastTime).Seconds()) * float64(numcpu) + pw.stats.cpu.Percent = calculatePercent(pw.lastCPU, pw.stats.cpu, delta, numcpu) + pw.lastCPU = pw.stats.cpu + pw.lastTime = now + + return pw.stats.cpu, nil +} + +func calculatePercent(t1, t2 CPUInfo, delta float64, numcpu int) float64 { + if delta <= 0 { + return 0 + } + deltaProc := t2.User + t2.System - t1.User - t1.System + overallPercent := ((deltaProc / delta) * 100) * float64(numcpu) + return overallPercent +} + +func (pw *darwinProcess) Ppid() int32 { + return pw.stats.ppid +} + +func (pw *darwinProcess) NumThreads() int32 { + return pw.stats.numThreads +} + +func (pw *darwinProcess) Status() string { + return pw.stats.state +} + +func (pw *darwinProcess) VmRSS() int64 { + return pw.stats.vmRSS +} + +func (pw *darwinProcess) VmSize() int64 { + return pw.stats.vmSize +} + +func (pw *darwinProcess) Command() string { + return pw.stats.command +} + +// CmdLine is taken from ps. As commands can have spaces, it's difficult parse parameters +// so no params for now +func (pw *darwinProcess) CmdLine(withArgs bool) (string, error) { + if pw.cmdLine != "" { + return pw.cmdLine, nil + } + + procCmdline, err := pw.process.Cmdline() + if err != nil { + return "", nil + } + + if len(procCmdline) == 0 { + return "", nil // zombie process + } + + // Ignoring dash on session commands + if procCmdline[0] == '-' { + procCmdline = procCmdline[1:] + } + + if !withArgs { + parts := strings.Split(procCmdline, " ") + procCmdline = parts[0] + } + + pw.cmdLine = helpers.SanitizeCommandLine(procCmdline) + return pw.cmdLine, nil +} diff --git a/pkg/internal/infraolly/process/snapshot_darwin_test.go b/pkg/internal/infraolly/process/snapshot_darwin_test.go new file mode 100644 index 000000000..77122dee0 --- /dev/null +++ b/pkg/internal/infraolly/process/snapshot_darwin_test.go @@ -0,0 +1,431 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package process + +import ( + "errors" + "testing" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/process" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func Test_collectProcStats_NameError(t *testing.T) { + proc := &ProcessMock{} + expectedError := errors.New("some error") + + proc.ShouldReturnName("", expectedError) + + stats, err := collectProcStats(proc) + + assert.Equal(t, expectedError, err) + assert.Equal(t, procStats{}, stats) + //mocked objects assertions + mock.AssertExpectationsForObjects(t, proc) +} + +func Test_collectProcStats_NumThreadsError(t *testing.T) { + proc := &ProcessMock{} + expectedError := errors.New("some error") + + proc.ShouldReturnName("some name", nil) + proc.ShouldReturnProcessId(1) + proc.ShouldReturnNumThreads(0, expectedError) + + stats, err := collectProcStats(proc) + + assert.Equal(t, expectedError, err) + assert.Equal(t, procStats{}, stats) + //mocked objects assertions + mock.AssertExpectationsForObjects(t, proc) +} + +func Test_collectProcStats_StatusError(t *testing.T) { + proc := &ProcessMock{} + expectedError := errors.New("some error") + + proc.ShouldReturnName("some name", nil) + proc.ShouldReturnProcessId(1) + proc.ShouldReturnNumThreads(4, nil) + proc.ShouldReturnStatus([]string{}, expectedError) + + stats, err := collectProcStats(proc) + + assert.Equal(t, expectedError, err) + assert.Equal(t, procStats{}, stats) + //mocked objects assertions + mock.AssertExpectationsForObjects(t, proc) +} + +func Test_collectProcStats_MemoryInfoError(t *testing.T) { + proc := &ProcessMock{} + expectedError := errors.New("some error") + + proc.ShouldReturnName("some name", nil) + proc.ShouldReturnProcessId(1) + proc.ShouldReturnNumThreads(4, nil) + proc.ShouldReturnStatus([]string{"some status"}, nil) + proc.ShouldReturnMemoryInfo(&process.MemoryInfoStat{}, expectedError) + + stats, err := collectProcStats(proc) + + assert.Equal(t, expectedError, err) + assert.Equal(t, procStats{}, stats) + //mocked objects assertions + mock.AssertExpectationsForObjects(t, proc) +} + +func Test_collectProcStats_CpuPercentError(t *testing.T) { + proc := &ProcessMock{} + expectedError := errors.New("some error") + + proc.ShouldReturnName("some name", nil) + proc.ShouldReturnProcessId(1) + proc.ShouldReturnNumThreads(4, nil) + proc.ShouldReturnStatus([]string{"some status"}, nil) + proc.ShouldReturnMemoryInfo(&process.MemoryInfoStat{}, nil) + proc.ShouldReturnCPUPercent(0, expectedError) + + stats, err := collectProcStats(proc) + + assert.Equal(t, expectedError, err) + assert.Equal(t, procStats{}, stats) + //mocked objects assertions + mock.AssertExpectationsForObjects(t, proc) +} + +func Test_collectProcStats_CpuTimesError(t *testing.T) { + proc := &ProcessMock{} + expectedError := errors.New("some error") + + proc.ShouldReturnName("some name", nil) + proc.ShouldReturnProcessId(1) + proc.ShouldReturnNumThreads(4, nil) + proc.ShouldReturnStatus([]string{"some status"}, nil) + proc.ShouldReturnMemoryInfo(&process.MemoryInfoStat{}, nil) + proc.ShouldReturnCPUPercent(0, nil) + proc.ShouldReturnTimes(&cpu.TimesStat{}, expectedError) + + stats, err := collectProcStats(proc) + + assert.Equal(t, expectedError, err) + assert.Equal(t, procStats{}, stats) + //mocked objects assertions + mock.AssertExpectationsForObjects(t, proc) +} + +func Test_collectProcStats_NoErrorsInitProcess(t *testing.T) { + tests := []struct { + name string + command string + processId int32 + numThreads int32 + status string + memStat *process.MemoryInfoStat + cpuPercent float64 + timesStat *cpu.TimesStat + expected procStats + }{ + { + name: "invalid rss", + command: "some command", + processId: 1, + numThreads: 3, + status: "some status", + memStat: &process.MemoryInfoStat{ + RSS: 0, + VMS: 232, + }, + cpuPercent: 10, + timesStat: &cpu.TimesStat{User: 2, System: 8}, + expected: procStats{ + command: "some command", + ppid: 0, + numThreads: 3, + state: "some status", + vmRSS: 0, + vmSize: 232, + cpu: CPUInfo{ + Percent: 10, + User: 2, + System: 8, + }, + }, + }, + { + name: "invalid vmsize", + command: "some command", + processId: 1, + numThreads: 3, + status: "some status", + memStat: &process.MemoryInfoStat{ + RSS: 45, + VMS: 0, + }, + cpuPercent: 10, + timesStat: &cpu.TimesStat{User: 2, System: 8}, + expected: procStats{ + command: "some command", + ppid: 0, + numThreads: 3, + state: "some status", + vmRSS: 45, + vmSize: 0, + cpu: CPUInfo{ + Percent: 10, + User: 2, + System: 8, + }, + }, + }, + { + name: "happy path", + command: "some command", + processId: 1, + numThreads: 3, + status: "some status", + memStat: &process.MemoryInfoStat{ + RSS: 45, + VMS: 22, + }, + cpuPercent: 10, + timesStat: &cpu.TimesStat{User: 2, System: 8}, + expected: procStats{ + command: "some command", + ppid: 0, + numThreads: 3, + state: "some status", + vmRSS: 45, + vmSize: 22, + cpu: CPUInfo{ + Percent: 10, + User: 2, + System: 8, + }, + }, + }, + } + + proc := &ProcessMock{} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + proc.ShouldReturnName(tt.command, nil) + proc.ShouldReturnProcessId(tt.processId) + proc.ShouldReturnNumThreads(tt.numThreads, nil) + proc.ShouldReturnStatus([]string{tt.status}, nil) + proc.ShouldReturnMemoryInfo(tt.memStat, nil) + proc.ShouldReturnCPUPercent(tt.cpuPercent, nil) + proc.ShouldReturnTimes(tt.timesStat, nil) + + stats, err := collectProcStats(proc) + + assert.Nil(t, err) + assert.Equal(t, tt.expected, stats) + //mocked objects assertions + mock.AssertExpectationsForObjects(t, proc) + }) + } +} + +func Test_collectProcStats_NoErrorsProcessWithParent(t *testing.T) { + tests := []struct { + name string + command string + processId int32 + parentProcessId int32 + parentProcessErr error + numThreads int32 + status string + memStat *process.MemoryInfoStat + cpuPercent float64 + timesStat *cpu.TimesStat + expected procStats + }{ + { + name: "parent process ok", + command: "some command", + processId: 16, + parentProcessId: 11, + parentProcessErr: nil, + numThreads: 3, + status: "some status", + memStat: &process.MemoryInfoStat{ + RSS: 0, + VMS: 232, + }, + cpuPercent: 10, + timesStat: &cpu.TimesStat{User: 2, System: 8}, + expected: procStats{ + command: "some command", + ppid: 11, + numThreads: 3, + state: "some status", + vmRSS: 0, + vmSize: 232, + cpu: CPUInfo{ + Percent: 10, + User: 2, + System: 8, + }, + }, + }, + { + name: "error getting parent process", + command: "some command", + processId: 16, + parentProcessId: 11, + parentProcessErr: errors.New("some error"), + numThreads: 3, + status: "some status", + memStat: &process.MemoryInfoStat{ + RSS: 0, + VMS: 232, + }, + cpuPercent: 10, + timesStat: &cpu.TimesStat{User: 2, System: 8}, + expected: procStats{ + command: "some command", + ppid: 0, + numThreads: 3, + state: "some status", + vmRSS: 0, + vmSize: 232, + cpu: CPUInfo{ + Percent: 10, + User: 2, + System: 8, + }, + }, + }, + } + + parentProc := &ProcessMock{} + proc := &ProcessMock{} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + proc.ShouldReturnName(tt.command, nil) + proc.ShouldReturnParent(parentProc, tt.parentProcessErr) + if tt.parentProcessErr == nil { + parentProc.ShouldReturnProcessId(tt.parentProcessId) + } + proc.ShouldReturnProcessId(tt.processId) + proc.ShouldReturnNumThreads(tt.numThreads, nil) + proc.ShouldReturnStatus([]string{tt.status}, nil) + proc.ShouldReturnMemoryInfo(tt.memStat, nil) + proc.ShouldReturnCPUPercent(tt.cpuPercent, nil) + proc.ShouldReturnTimes(tt.timesStat, nil) + + stats, err := collectProcStats(proc) + + assert.Nil(t, err) + assert.Equal(t, tt.expected, stats) + //mocked objects assertions + mock.AssertExpectationsForObjects(t, proc) + }) + } +} + +func Test_calculatePercent(t *testing.T) { + tests := []struct { + name string + t1 CPUInfo + t2 CPUInfo + delta float64 + numcpu int + expectedPercent float64 + }{ + { + name: "delta 0", + expectedPercent: 0, + }, + { + name: "bad delta", + delta: -1, + expectedPercent: 0, + }, + { + name: "good delta", + delta: 10, + numcpu: 2, + t1: CPUInfo{ + User: 24, + System: 33, + }, + t2: CPUInfo{ + User: 42, + System: 55, + }, + expectedPercent: ((44 / 10) * 100) * 2, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + percent := calculatePercent(tt.t1, tt.t2, tt.delta, tt.numcpu) + assert.Equal(t, tt.expectedPercent, percent) + }) + } +} + +//nolint:exhaustruct +func Test_Calculate_Process_CmdLine(t *testing.T) { + t.Parallel() + tests := []struct { + name string + cmdLine string + args bool + expected string + }{ + { + name: "empty", + cmdLine: "", + expected: "", + }, + { + name: "ignoring dash on session commands", + cmdLine: "-zsh", + expected: "zsh", + }, + { + name: "no arguments & args enabled", + cmdLine: "/sbin/launchd", + args: true, + expected: "/sbin/launchd", + }, + { + name: "no arguments & args disabled", + cmdLine: "/sbin/launchd", + args: false, + expected: "/sbin/launchd", + }, + { + name: "arguments & args enabled", + cmdLine: "/sbin/launchd -arg_a=1 -arg_b 2", + args: true, + expected: "/sbin/launchd -arg_a=1 -arg_b 2", + }, + { + name: "arguments & args disabled", + cmdLine: "/sbin/launchd -arg_a=1 -arg_b 2", + args: false, + expected: "/sbin/launchd", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + process := &ProcessMock{} + process.ShouldReturnCmdLine(tt.cmdLine, nil) + darwinProcess := darwinProcess{ + process: process, + } + + result, err := darwinProcess.CmdLine(tt.args) + assert.NoError(t, err) + assert.Equal(t, tt.expected, result) + }) + } +} diff --git a/pkg/internal/infraolly/process/snapshot_linux.go b/pkg/internal/infraolly/process/snapshot_linux.go new file mode 100644 index 000000000..a75a8ba59 --- /dev/null +++ b/pkg/internal/infraolly/process/snapshot_linux.go @@ -0,0 +1,380 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package process + +import ( + "fmt" + "io/ioutil" + "os" + "runtime" + "strconv" + "strings" + "time" + + "github.com/newrelic/infrastructure-agent/pkg/helpers" + "github.com/pkg/errors" + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/process" +) + +// linuxProcess is an implementation of the process.Snapshot interface for linux hosts. It is designed to be highly +// optimized and avoid unnecessary/duplicated system calls. +type linuxProcess struct { + // if privileged == false, some operations will be avoided: FD and IO count. + privileged bool + + stats procStats + process *process.Process + lastCPU CPUInfo + lastTime time.Time + + // data that will be reused between samples of the same process. + pid int32 + user string + cmdLine string +} + +// needed to calculate RSS. +var pageSize int64 + +// needed to calculate CPU times. +var clockTicks int64 + +// for testing getting username from getent. +var getEntCommand = helpers.RunCommand //nolint:gochecknoglobals + +var ( + errMalformedGetentEntry = errors.New("malformed getent entry") + errInvalidUidsForProcess = errors.New("invalid uids for process") +) + +func init() { + pageSize = int64(os.Getpagesize()) + if pageSize <= 0 { + pageSize = 4096 // default value + } + + clockTicks = int64(cpu.ClocksPerSec) + if clockTicks <= 0 { + clockTicks = 100 // default value + } +} + +var _ Snapshot = (*linuxProcess)(nil) // static interface assertion + +// getLinuxProcess returns a linux process snapshot, trying to reuse the data from a previous snapshot of the same +// process. +func getLinuxProcess(pid int32, previous *linuxProcess, privileged bool) (*linuxProcess, error) { + var gops *process.Process + var err error + + procStats, err := readProcStat(pid) + if err != nil { + return nil, err + } + + // Reusing information from the last snapshot for the same process + // If the name or the PPID changed from the previous, we'll consider this sample is just + // a new process that shares the PID with an old one. + // if a process with the same CommandName but different CmdLine or User name + // occupies the same PID, the cache won't refresh the CmdLine and Username. + if previous == nil || procStats.command != previous.Command() || procStats.ppid != previous.Ppid() { + gops, err = process.NewProcess(pid) + if err != nil { + return nil, err + } + return &linuxProcess{ + privileged: privileged, + pid: pid, + process: gops, + stats: procStats, + }, nil + } + + // Otherwise, instead of creating a new process snapshot, we just reuse the previous one, with updated data + previous.stats = procStats + + return previous, nil +} + +func (pw *linuxProcess) Pid() int32 { + return pw.pid +} + +func (pw *linuxProcess) Username() (string, error) { + var err error + if pw.user == "" { // caching user + // try to get it from gopsutil and return it if ok + pw.user, err = pw.process.Username() + if err == nil { + return pw.user, nil + } + + // get the uid to be retrieved from getent + uid, err := pw.uid() + if err != nil { + return "", err + } + + // try to get it using getent + pw.user, err = usernameFromGetent(uid) + if err != nil { + return "", err + } + } + return pw.user, nil +} + +func (pw *linuxProcess) uid() (int32, error) { + uuids, err := pw.process.Uids() + if err != nil { + return 0, fmt.Errorf("error getting process uids: %w", err) //nolint:wrapcheck + } + + if len(uuids) == 0 { + return 0, errInvalidUidsForProcess //nolint:wrapcheck + } + + return uuids[0], nil +} + +// usernameFromGetent returns the username using getent https://man7.org/linux/man-pages/man1/getent.1.html +// getent passwd format example: +// deleteme:x:63367:63367:Dynamic User:/:/usr/sbin/nologin +func usernameFromGetent(uid int32) (string, error) { + out, err := getEntCommand("/usr/bin/getent", "", []string{"passwd", fmt.Sprintf("%d", uid)}...) + if err != nil { + return "", err + } + + if sepIdx := strings.Index(out, ":"); sepIdx > 0 { + return out[0:sepIdx], nil + } + + return "", errMalformedGetentEntry //nolint:wrapcheck +} + +func (pw *linuxProcess) IOCounters() (*process.IOCountersStat, error) { + if !pw.privileged { + return nil, nil + } + return pw.process.IOCounters() +} + +// NumFDs returns the number of file descriptors. It returns -1 (and nil error) if the Agent does not have privileges to +// access this information. +func (pw *linuxProcess) NumFDs() (int32, error) { + if !pw.privileged { + return -1, nil + } + pid := pw.process.Pid + statPath := helpers.HostProc(strconv.Itoa(int(pid)), "fd") + d, err := os.Open(statPath) + if err != nil { + return 0, err + } + defer d.Close() + fnames, err := d.Readdirnames(-1) + return int32(len(fnames)), nil +} + +///////////////////////////// +// Data to be derived from /proc//stat +///////////////////////////// + +type procStats struct { + command string + ppid int32 + numThreads int32 + state string + vmRSS int64 + vmSize int64 + cpu CPUInfo +} + +// /proc//stat standard field indices according to: http://man7.org/linux/man-pages/man5/proc.5.html +// because the first two fields are treated separately those indices are smaller with 2 elements than in the doc. +const ( + statState = 0 + statPPID = 1 + statUtime = 11 + statStime = 12 + statNumThreads = 17 + statVsize = 20 + statRss = 21 +) + +// readProcStat will gather information about the pid from /proc//stat file. +func readProcStat(pid int32) (procStats, error) { + statPath := helpers.HostProc(strconv.Itoa(int(pid)), "stat") + + content, err := ioutil.ReadFile(statPath) + if err != nil { + return procStats{}, err + } + + return parseProcStat(string(content)) +} + +// parseProcStat is used to parse the content of the /proc//stat file. +func parseProcStat(content string) (procStats, error) { + stats := procStats{} + + i := strings.Index(content, "(") + if i == -1 { + return stats, fmt.Errorf("could not find command name start symbol '(' for stats: %s", content) + } + // Drop the first first field which is the pid. + content = content[i+1:] + + i = strings.Index(content, ")") + if i == -1 { + return stats, fmt.Errorf("could not find command name end symbol ')' for stats: %s", content) + } + + // Command Name found as the second field inside the brackets. + stats.command = content[:i] + + fields := strings.Fields(content[i+1:]) + + // Process State + stats.state = fields[statState] + + // Parent PID + ppid, err := strconv.ParseInt(fields[statPPID], 10, 32) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + stats.ppid = int32(ppid) + + // User time + utime, err := strconv.ParseInt(fields[statUtime], 10, 64) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + stats.cpu.User = float64(utime) / float64(clockTicks) + + // System time + stime, err := strconv.ParseInt(fields[statStime], 10, 64) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + stats.cpu.System = float64(stime) / float64(clockTicks) + + // Number of threads + nthreads, err := strconv.ParseInt(fields[statNumThreads], 10, 32) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + stats.numThreads = int32(nthreads) + + // VM Memory size + stats.vmSize, err = strconv.ParseInt(fields[statVsize], 10, 64) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + + // VM RSS size + stats.vmRSS, err = strconv.ParseInt(fields[statRss], 10, 64) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + stats.vmRSS *= pageSize + + return stats, nil +} + +func (pw *linuxProcess) CPUTimes() (CPUInfo, error) { + now := time.Now() + + if pw.lastTime.IsZero() { + // invoked first time + pw.lastCPU = pw.stats.cpu + pw.lastTime = now + return pw.stats.cpu, nil + } + + // Calculate CPU percent from user time, system time, and last harvested cpu counters + numcpu := runtime.NumCPU() + delta := (now.Sub(pw.lastTime).Seconds()) * float64(numcpu) + pw.stats.cpu.Percent = calculatePercent(pw.lastCPU, pw.stats.cpu, delta, numcpu) + pw.lastCPU = pw.stats.cpu + pw.lastTime = now + + return pw.stats.cpu, nil +} + +func calculatePercent(t1, t2 CPUInfo, delta float64, numcpu int) float64 { + if delta == 0 { + return 0 + } + deltaProc := t2.User + t2.System - t1.User - t1.System + overallPercent := ((deltaProc / delta) * 100) * float64(numcpu) + return overallPercent +} + +func (pw *linuxProcess) Ppid() int32 { + return pw.stats.ppid +} + +func (pw *linuxProcess) NumThreads() int32 { + return pw.stats.numThreads +} + +func (pw *linuxProcess) Status() string { + return pw.stats.state +} + +func (pw *linuxProcess) VmRSS() int64 { + return pw.stats.vmRSS +} + +func (pw *linuxProcess) VmSize() int64 { + return pw.stats.vmSize +} + +func (pw *linuxProcess) Command() string { + return pw.stats.command +} + +////////////////////////// +// Data to be derived from /proc//cmdline: command line, and command line without arguments +////////////////////////// + +func (pw *linuxProcess) CmdLine(withArgs bool) (string, error) { + if pw.cmdLine != "" { + return pw.cmdLine, nil + } + + cmdPath := helpers.HostProc(strconv.Itoa(int(pw.pid)), "cmdline") + procCmdline, err := ioutil.ReadFile(cmdPath) + if err != nil { + procCmdline = nil // we can't be sure internal libraries return nil on error + } + + if len(procCmdline) == 0 { + return "", nil // zombie process + } + + // Ignoring dash on session commands + if procCmdline[0] == '-' { + procCmdline = procCmdline[1:] + } + + cmdLineBytes := make([]byte, 0, len(procCmdline)) + for i := 0; i < len(procCmdline); i++ { + if procCmdline[i] == 0 { + // ignoring the trailing zero that ends /proc//cmdline, but adding the last character if the file + // does not end in zero + if withArgs && i < len(procCmdline)-1 { + cmdLineBytes = append(cmdLineBytes, ' ') + } else { + break + } + } else { + cmdLineBytes = append(cmdLineBytes, procCmdline[i]) + } + } + pw.cmdLine = helpers.SanitizeCommandLine(string(cmdLineBytes)) + return pw.cmdLine, nil +} diff --git a/pkg/internal/infraolly/process/snapshot_linux_test.go b/pkg/internal/infraolly/process/snapshot_linux_test.go new file mode 100644 index 000000000..fd57d3e33 --- /dev/null +++ b/pkg/internal/infraolly/process/snapshot_linux_test.go @@ -0,0 +1,248 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +// +//nolint:goerr113 +package process + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path" + "testing" + + "github.com/newrelic/infrastructure-agent/pkg/helpers" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLinuxProcess_CmdLine(t *testing.T) { + hostProc := os.Getenv("HOST_PROC") + defer os.Setenv("HOST_PROC", hostProc) + tmpDir, err := ioutil.TempDir("", "proc") + require.NoError(t, err) + processDir := path.Join(tmpDir, "12345") + require.NoError(t, os.MkdirAll(processDir, 0o755)) + _ = os.Setenv("HOST_PROC", tmpDir) + + testCases := []struct { + rawProcCmdline []byte + expected string + }{ + {[]byte{0}, ""}, + {[]byte{'b', 'a', 's', 'h', 0}, "bash"}, + {[]byte{'/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0}, "/bin/bash"}, + {[]byte{'/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0, 'a', 'r', 'g', 0}, "/bin/bash arg"}, + {[]byte{'-', '/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0, 'a', 'r', 'g', 0}, "/bin/bash arg"}, + { + []byte{'/', 'a', ' ', 'f', 'o', 'l', 'd', 'e', 'r', '/', 'c', 'm', 'd', 0, '-', 'a', 'g', 0, 'x', 'x', 0}, + "/a folder/cmd -ag xx", + }, + } + for _, tc := range testCases { + require.NoError(t, ioutil.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) + lp := linuxProcess{pid: 12345} + actual, err := lp.CmdLine(true) + assert.NoError(t, err) + assert.Equal(t, tc.expected, actual) + } +} + +func TestLinuxProcess_CmdLine_NoArgs(t *testing.T) { + hostProc := os.Getenv("HOST_PROC") + defer os.Setenv("HOST_PROC", hostProc) + tmpDir, err := ioutil.TempDir("", "proc") + require.NoError(t, err) + processDir := path.Join(tmpDir, "12345") + require.NoError(t, os.MkdirAll(processDir, 0o755)) + _ = os.Setenv("HOST_PROC", tmpDir) + + testCases := []struct { + rawProcCmdline []byte + expected string + }{ + {[]byte{0}, ""}, + {[]byte{'b', 'a', 's', 'h', 0}, "bash"}, + {[]byte{'-', 'b', 'a', 's', 'h', 0}, "bash"}, + {[]byte{'/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0}, "/bin/bash"}, + {[]byte{'/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0, 'a', 'r', 'g', 0}, "/bin/bash"}, + { + []byte{'/', 'a', ' ', 'f', 'o', 'l', 'd', 'e', 'r', '/', 'c', 'm', 'd', 0, '-', 'a', 'g', 0, 'x', 'x', 0}, + "/a folder/cmd", + }, + } + for _, tc := range testCases { + require.NoError(t, ioutil.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) + lp := linuxProcess{pid: 12345} + actual, err := lp.CmdLine(false) + assert.NoError(t, err) + assert.Equal(t, tc.expected, actual) + } +} + +// Test nonstandard implementations of the /proc//cmdline format, which don't use zeroes to separate nor +// end the command lines. (e.g. Nginx create processes whose cmdline is "nginx: master process /usr/sbin/nginx" +func TestLinuxProcess_CmdLine_NotStandard(t *testing.T) { + hostProc := os.Getenv("HOST_PROC") + defer os.Setenv("HOST_PROC", hostProc) + tmpDir, err := ioutil.TempDir("", "proc") + require.NoError(t, err) + processDir := path.Join(tmpDir, "12345") + require.NoError(t, os.MkdirAll(processDir, 0o755)) + _ = os.Setenv("HOST_PROC", tmpDir) + + testCases := []struct { + rawProcCmdline []byte + expected string + }{ + {[]byte("nginx: worker process"), "nginx: worker process"}, + {[]byte("nginx: master process /usr/sbin/nginx"), "nginx: master process /usr/sbin/nginx"}, + { + []byte("nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"), + "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf", + }, + } + for _, tc := range testCases { + require.NoError(t, ioutil.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) + lp := linuxProcess{pid: 12345} + + // Testing both the cases with and without command line stripping + actual, err := lp.CmdLine(true) + assert.NoError(t, err) + assert.Equal(t, tc.expected, actual) + + actual, err = lp.CmdLine(false) + assert.NoError(t, err) + assert.Equal(t, tc.expected, actual) + } +} + +func TestLinuxProcess_CmdLine_ProcessNotExist(t *testing.T) { + lp := linuxProcess{pid: 999999999} + actual, err := lp.CmdLine(true) + assert.NoError(t, err) + assert.Equal(t, "", actual) +} + +func TestLinuxProcess_CmdLine_ProcessNotExist_NoStrip(t *testing.T) { + lp := linuxProcess{pid: 999999999} + actual, err := lp.CmdLine(false) + assert.NoError(t, err) + assert.Equal(t, "", actual) +} + +func TestParseProcStatMultipleWordsProcess(t *testing.T) { + content := `465 (node /home/ams-) S 7648 465 465 0 -1 4202496 85321 6128 0 0 378 60 9 2 20 0 11 0 6384148 1005015040 21241 18446744073709551615 4194304 36236634 140729243085280 140729243069424 140119099392231 0 0 4096 16898 18446744073709551615 0 0 17 1 0 0 0 0 0 38337168 38426896 57044992 140729243093258 140729243093333 140729243093333 140729243095018 0` + + expected := procStats{ + command: "node /home/ams-", + ppid: 7648, + numThreads: 11, + state: "S", + vmRSS: 87003136, + vmSize: 1005015040, + cpu: CPUInfo{ + Percent: 0, + User: 3.78, + System: 0.6, + }, + } + actual, err := parseProcStat(content) + assert.NoError(t, err) + + assert.Equal(t, expected, actual) +} + +func TestParseProcStatSingleWordProcess(t *testing.T) { + content := `1232 (newrelic-infra) S 1 1232 1232 0 -1 1077960960 4799 282681 88 142 24 15 193 94 20 0 12 0 1071 464912384 4490 18446744073709551615 1 1 0 0 0 0 0 0 2143420159 0 0 0 17 0 0 0 14 0 0 0 0 0 0 0 0 0 0` + + expected := procStats{ + command: "newrelic-infra", + ppid: 1, + numThreads: 12, + state: "S", + vmRSS: 18391040, + vmSize: 464912384, + cpu: CPUInfo{ + Percent: 0, + + User: 0.24, + System: 0.15, + }, + } + actual, err := parseProcStat(content) + assert.NoError(t, err) + + assert.Equal(t, expected, actual) +} + +func TestParseProcStatUntrimmedCommand(t *testing.T) { + cases := []struct { + input string + expected procStats + }{{ + input: "11155 (/usr/bin/spamd ) S 1 11155 11155 0 -1 1077944640 19696 1028 0 0 250 32 0 0 20 0 1 0 6285571 300249088 18439 18446744073709551615 4194304 4198572 140721992060048 140721992059288 139789215727443 0 0 4224 92163 18446744072271262725 0 0 17 1 0 0 0 0 0 6298944 6299796 18743296 140721992060730 140721992060807 140721992060807 140721992060905 0\n", + expected: procStats{command: "/usr/bin/spamd ", state: "S", ppid: 1, cpu: CPUInfo{User: 2.50, System: 0.32}, numThreads: 1, vmSize: 300249088, vmRSS: 18439 * pageSize}, + }, { + input: "11159 (spamd child) S 11155 11155 11155 0 -1 1077944384 459 0 0 0 1 0 0 0 20 0 1 0 6285738 300249088 17599 18446744073709551615 4194304 4198572 140721992060048 140721992059288 139789215727443 0 0 4224 2048 18446744072271262725 0 0 17 0 0 0 0 0 0 6298944 6299796 18743296 140721992060730 140721992060807 140721992060807 140721992060905 0\n", + expected: procStats{command: "spamd child", state: "S", ppid: 11155, cpu: CPUInfo{User: 0.01, System: 0}, numThreads: 1, vmSize: 300249088, vmRSS: 17599 * pageSize}, + }, { + input: "11160 ( spamd child) S 11155 11155 11155 0 -1 1077944384 459 0 0 0 0 0 0 0 20 0 1 0 6285738 300249088 17599 18446744073709551615 4194304 4198572 140721992060048 140721992059288 139789215727443 0 0 4224 2048 18446744072271262725 0 0 17 0 0 0 0 0 0 6298944 6299796 18743296 140721992060730 140721992060807 140721992060807 140721992060905 0\n", + expected: procStats{command: " spamd child", state: "S", ppid: 11155, cpu: CPUInfo{User: 0, System: 0}, numThreads: 1, vmSize: 300249088, vmRSS: 17599 * pageSize}, + }} + + for n, c := range cases { + t.Run(fmt.Sprint("test", n), func(t *testing.T) { + actual, err := parseProcStat(c.input) + assert.NoError(t, err) + assert.Equal(t, c.expected, actual) + }) + } +} + +func Test_usernameFromGetent(t *testing.T) { //nolint:paralleltest + testCases := []struct { + name string + getEntResult string + getEntError error + expectedUsername string + expectedError error + }{ + { + name: "happy path, user exists", + getEntResult: "deleteme:x:63367:63367:Dynamic User:/:/usr/sbin/nologin", + expectedUsername: "deleteme", + }, + { + name: "getent returns error (i.e. does not exist)", + getEntError: errors.New("some error"), + expectedUsername: "", + expectedError: errors.New("some error"), + }, + { + name: "getent returns unexpected formatted entry", + getEntResult: "this is an unexpected format", + expectedUsername: "", + expectedError: errMalformedGetentEntry, + }, + } + + //nolint:paralleltest + for i := range testCases { + testCase := testCases[i] + t.Run(testCase.name, func(t *testing.T) { + getEntCommand = func(command string, stdin string, args ...string) (string, error) { + return testCase.getEntResult, testCase.getEntError + } + defer func() { + getEntCommand = helpers.RunCommand + }() + + username, err := usernameFromGetent(123) + assert.Equal(t, testCase.expectedUsername, username) + assert.Equal(t, testCase.expectedError, err) + }) + } +} diff --git a/pkg/internal/infraolly/process/snapshot_mock_test.go b/pkg/internal/infraolly/process/snapshot_mock_test.go new file mode 100644 index 000000000..2728cd9e5 --- /dev/null +++ b/pkg/internal/infraolly/process/snapshot_mock_test.go @@ -0,0 +1,169 @@ +// Copyright New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package process + +import ( + "github.com/shirou/gopsutil/v3/process" + "github.com/stretchr/testify/mock" +) + +type SnapshotMock struct { + mock.Mock +} + +func (s *SnapshotMock) Pid() int32 { + args := s.Called() + + return args.Get(0).(int32) +} + +func (s *SnapshotMock) ShouldReturnPid(pid int32) { + s. + On("Pid"). + Once(). + Return(pid) +} + +func (s *SnapshotMock) Ppid() int32 { + args := s.Called() + + return args.Get(0).(int32) +} + +func (s *SnapshotMock) ShouldReturnPpid(ppid int32) { + s. + On("Ppid"). + Once(). + Return(ppid) +} + +func (s *SnapshotMock) Status() string { + args := s.Called() + + return args.String(0) +} + +func (s *SnapshotMock) ShouldReturnStatus(status string) { + s. + On("Status"). + Once(). + Return(status) +} + +func (s *SnapshotMock) Command() string { + args := s.Called() + + return args.String(0) +} + +func (s *SnapshotMock) ShouldReturnCommand(command string) { + s. + On("Command"). + Once(). + Return(command) +} + +func (s *SnapshotMock) CmdLine(withArgs bool) (string, error) { + args := s.Called(withArgs) + + return args.String(0), args.Error(1) +} + +func (s *SnapshotMock) ShouldReturnCmdLine(withArgs bool, cmd string, err error) { + s. + On("CmdLine", withArgs). + Once(). + Return(cmd, err) +} + +func (s *SnapshotMock) Username() (string, error) { + args := s.Called() + + return args.String(0), args.Error(1) +} + +func (s *SnapshotMock) ShouldReturnUsername(cmd string, err error) { + s. + On("Username"). + Once(). + Return(cmd, err) +} + +func (s *SnapshotMock) CPUTimes() (CPUInfo, error) { + args := s.Called() + + return args.Get(0).(CPUInfo), args.Error(1) +} + +func (s *SnapshotMock) ShouldReturnCPUTimes(cpu CPUInfo, err error) { + s. + On("CPUTimes"). + Once(). + Return(cpu, err) +} + +func (s *SnapshotMock) IOCounters() (*process.IOCountersStat, error) { + args := s.Called() + + return args.Get(0).(*process.IOCountersStat), args.Error(1) +} + +func (s *SnapshotMock) ShouldReturnIOCounters(io *process.IOCountersStat, err error) { + s. + On("IOCounters"). + Once(). + Return(io, err) +} + +func (s *SnapshotMock) NumThreads() int32 { + args := s.Called() + + return args.Get(0).(int32) +} + +func (s *SnapshotMock) ShouldReturnNumThreads(num int32) { + s. + On("NumThreads"). + Once(). + Return(num) +} + +func (s *SnapshotMock) NumFDs() (int32, error) { + args := s.Called() + + return args.Get(0).(int32), args.Error(1) +} + +func (s *SnapshotMock) ShouldReturnNumFDs(num int32, err error) { + s. + On("NumFDs"). + Once(). + Return(num, err) +} + +func (s *SnapshotMock) VmRSS() int64 { + args := s.Called() + + return args.Get(0).(int64) +} + +func (s *SnapshotMock) ShouldReturnVmRSS(rss int64) { + s. + On("VmRSS"). + Once(). + Return(rss) +} + +func (s *SnapshotMock) VmSize() int64 { + args := s.Called() + + return args.Get(0).(int64) +} + +func (s *SnapshotMock) ShouldReturnVmSize(size int64) { + s. + On("VmSize"). + Once(). + Return(size) +} From 7c55aa6d7a5cdd12c554a902aab3d43c5d761ee7 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Mon, 27 May 2024 12:04:39 +0200 Subject: [PATCH 02/27] refactored harvester_linux.go --- go.mod | 7 +- go.sum | 20 + pkg/internal/infraolly/process/cache.go | 2 +- .../{harvester_unix.go => harvester.go} | 26 +- .../infraolly/process/harvester_linux.go | 89 +- .../infraolly/process/harvester_linux_test.go | 15 +- ...ix_mock_test.go => harvester_mock_test.go} | 1 - .../process_retriever_cached_darwin.go | 1 - pkg/internal/infraolly/process/sample.go | 55 +- .../infraolly/process/sampler_darwin.go | 7 +- .../infraolly/process/sampler_darwin_test.go | 5 - .../infraolly/process/sampler_linux.go | 7 +- .../infraolly/process/sampler_linux_test.go | 12 - .../infraolly/process/sampler_unix_test.go | 4 - .../infraolly/process/snapshot_darwin.go | 1 - .../infraolly/process/snapshot_linux.go | 3 +- .../infraolly/process/snapshot_linux_test.go | 2 - vendor/github.com/lufia/plan9stats/.gitignore | 12 + vendor/github.com/lufia/plan9stats/LICENSE | 29 + vendor/github.com/lufia/plan9stats/README.md | 2 + vendor/github.com/lufia/plan9stats/cpu.go | 288 +++ vendor/github.com/lufia/plan9stats/doc.go | 2 + vendor/github.com/lufia/plan9stats/host.go | 303 +++ vendor/github.com/lufia/plan9stats/int.go | 31 + vendor/github.com/lufia/plan9stats/opts.go | 21 + vendor/github.com/lufia/plan9stats/stats.go | 88 + .../github.com/power-devops/perfstat/LICENSE | 23 + .../power-devops/perfstat/c_helpers.c | 159 ++ .../power-devops/perfstat/c_helpers.h | 58 + .../power-devops/perfstat/config.go | 18 + .../power-devops/perfstat/cpustat.go | 98 + .../power-devops/perfstat/diskstat.go | 137 + .../github.com/power-devops/perfstat/doc.go | 315 +++ .../power-devops/perfstat/fsstat.go | 31 + .../power-devops/perfstat/helpers.go | 764 ++++++ .../power-devops/perfstat/lparstat.go | 26 + .../power-devops/perfstat/lvmstat.go | 72 + .../power-devops/perfstat/memstat.go | 84 + .../power-devops/perfstat/netstat.go | 117 + .../power-devops/perfstat/procstat.go | 75 + .../power-devops/perfstat/sysconf.go | 195 ++ .../power-devops/perfstat/systemcfg.go | 635 +++++ .../power-devops/perfstat/types_cpu.go | 186 ++ .../power-devops/perfstat/types_disk.go | 176 ++ .../power-devops/perfstat/types_fs.go | 195 ++ .../power-devops/perfstat/types_lpar.go | 68 + .../power-devops/perfstat/types_lvm.go | 31 + .../power-devops/perfstat/types_memory.go | 101 + .../power-devops/perfstat/types_network.go | 163 ++ .../power-devops/perfstat/types_process.go | 43 + .../power-devops/perfstat/uptime.go | 35 + vendor/github.com/shirou/gopsutil/v3/LICENSE | 61 + .../shirou/gopsutil/v3/common/env.go | 23 + .../github.com/shirou/gopsutil/v3/cpu/cpu.go | 200 ++ .../shirou/gopsutil/v3/cpu/cpu_aix.go | 16 + .../shirou/gopsutil/v3/cpu/cpu_aix_cgo.go | 66 + .../shirou/gopsutil/v3/cpu/cpu_aix_nocgo.go | 92 + .../shirou/gopsutil/v3/cpu/cpu_darwin.go | 117 + .../shirou/gopsutil/v3/cpu/cpu_darwin_cgo.go | 111 + .../gopsutil/v3/cpu/cpu_darwin_nocgo.go | 14 + .../shirou/gopsutil/v3/cpu/cpu_dragonfly.go | 156 ++ .../gopsutil/v3/cpu/cpu_dragonfly_amd64.go | 9 + .../shirou/gopsutil/v3/cpu/cpu_fallback.go | 31 + .../shirou/gopsutil/v3/cpu/cpu_freebsd.go | 168 ++ .../shirou/gopsutil/v3/cpu/cpu_freebsd_386.go | 9 + .../gopsutil/v3/cpu/cpu_freebsd_amd64.go | 9 + .../shirou/gopsutil/v3/cpu/cpu_freebsd_arm.go | 9 + .../gopsutil/v3/cpu/cpu_freebsd_arm64.go | 9 + .../shirou/gopsutil/v3/cpu/cpu_linux.go | 479 ++++ .../shirou/gopsutil/v3/cpu/cpu_netbsd.go | 119 + .../gopsutil/v3/cpu/cpu_netbsd_amd64.go | 9 + .../gopsutil/v3/cpu/cpu_netbsd_arm64.go | 9 + .../shirou/gopsutil/v3/cpu/cpu_openbsd.go | 137 + .../shirou/gopsutil/v3/cpu/cpu_openbsd_386.go | 10 + .../gopsutil/v3/cpu/cpu_openbsd_amd64.go | 10 + .../shirou/gopsutil/v3/cpu/cpu_openbsd_arm.go | 10 + .../gopsutil/v3/cpu/cpu_openbsd_arm64.go | 10 + .../gopsutil/v3/cpu/cpu_openbsd_riscv64.go | 10 + .../shirou/gopsutil/v3/cpu/cpu_plan9.go | 50 + .../shirou/gopsutil/v3/cpu/cpu_solaris.go | 269 ++ .../shirou/gopsutil/v3/cpu/cpu_windows.go | 229 ++ .../gopsutil/v3/internal/common/binary.go | 637 +++++ .../gopsutil/v3/internal/common/common.go | 464 ++++ .../v3/internal/common/common_darwin.go | 66 + .../v3/internal/common/common_freebsd.go | 82 + .../v3/internal/common/common_linux.go | 353 +++ .../v3/internal/common/common_netbsd.go | 66 + .../v3/internal/common/common_openbsd.go | 66 + .../v3/internal/common/common_unix.go | 62 + .../v3/internal/common/common_windows.go | 304 +++ .../gopsutil/v3/internal/common/endian.go | 10 + .../gopsutil/v3/internal/common/sleep.go | 21 + .../gopsutil/v3/internal/common/warnings.go | 30 + .../github.com/shirou/gopsutil/v3/mem/mem.go | 120 + .../shirou/gopsutil/v3/mem/mem_aix.go | 16 + .../shirou/gopsutil/v3/mem/mem_aix_cgo.go | 51 + .../shirou/gopsutil/v3/mem/mem_aix_nocgo.go | 78 + .../shirou/gopsutil/v3/mem/mem_bsd.go | 87 + .../shirou/gopsutil/v3/mem/mem_darwin.go | 72 + .../shirou/gopsutil/v3/mem/mem_darwin_cgo.go | 58 + .../gopsutil/v3/mem/mem_darwin_nocgo.go | 89 + .../shirou/gopsutil/v3/mem/mem_fallback.go | 34 + .../shirou/gopsutil/v3/mem/mem_freebsd.go | 167 ++ .../shirou/gopsutil/v3/mem/mem_linux.go | 532 ++++ .../shirou/gopsutil/v3/mem/mem_netbsd.go | 87 + .../shirou/gopsutil/v3/mem/mem_openbsd.go | 100 + .../shirou/gopsutil/v3/mem/mem_openbsd_386.go | 38 + .../gopsutil/v3/mem/mem_openbsd_amd64.go | 32 + .../shirou/gopsutil/v3/mem/mem_openbsd_arm.go | 38 + .../gopsutil/v3/mem/mem_openbsd_arm64.go | 38 + .../gopsutil/v3/mem/mem_openbsd_riscv64.go | 38 + .../shirou/gopsutil/v3/mem/mem_plan9.go | 68 + .../shirou/gopsutil/v3/mem/mem_solaris.go | 213 ++ .../shirou/gopsutil/v3/mem/mem_windows.go | 166 ++ .../github.com/shirou/gopsutil/v3/net/net.go | 273 ++ .../shirou/gopsutil/v3/net/net_aix.go | 330 +++ .../shirou/gopsutil/v3/net/net_aix_cgo.go | 36 + .../shirou/gopsutil/v3/net/net_aix_nocgo.go | 95 + .../shirou/gopsutil/v3/net/net_darwin.go | 291 +++ .../shirou/gopsutil/v3/net/net_fallback.go | 93 + .../shirou/gopsutil/v3/net/net_freebsd.go | 128 + .../shirou/gopsutil/v3/net/net_linux.go | 910 +++++++ .../shirou/gopsutil/v3/net/net_linux_111.go | 12 + .../shirou/gopsutil/v3/net/net_linux_116.go | 12 + .../shirou/gopsutil/v3/net/net_openbsd.go | 319 +++ .../shirou/gopsutil/v3/net/net_solaris.go | 144 ++ .../shirou/gopsutil/v3/net/net_unix.go | 224 ++ .../shirou/gopsutil/v3/net/net_windows.go | 779 ++++++ .../shirou/gopsutil/v3/process/process.go | 627 +++++ .../shirou/gopsutil/v3/process/process_bsd.go | 76 + .../gopsutil/v3/process/process_darwin.go | 325 +++ .../v3/process/process_darwin_amd64.go | 236 ++ .../v3/process/process_darwin_arm64.go | 213 ++ .../gopsutil/v3/process/process_darwin_cgo.go | 222 ++ .../v3/process/process_darwin_nocgo.go | 127 + .../gopsutil/v3/process/process_fallback.go | 203 ++ .../gopsutil/v3/process/process_freebsd.go | 342 +++ .../v3/process/process_freebsd_386.go | 192 ++ .../v3/process/process_freebsd_amd64.go | 192 ++ .../v3/process/process_freebsd_arm.go | 192 ++ .../v3/process/process_freebsd_arm64.go | 202 ++ .../gopsutil/v3/process/process_linux.go | 1187 +++++++++ .../gopsutil/v3/process/process_openbsd.go | 387 +++ .../v3/process/process_openbsd_386.go | 202 ++ .../v3/process/process_openbsd_amd64.go | 200 ++ .../v3/process/process_openbsd_arm.go | 202 ++ .../v3/process/process_openbsd_arm64.go | 203 ++ .../v3/process/process_openbsd_riscv64.go | 204 ++ .../gopsutil/v3/process/process_plan9.go | 203 ++ .../gopsutil/v3/process/process_posix.go | 185 ++ .../gopsutil/v3/process/process_solaris.go | 303 +++ .../gopsutil/v3/process/process_windows.go | 1165 +++++++++ .../v3/process/process_windows_32bit.go | 108 + .../v3/process/process_windows_64bit.go | 79 + .../shoenig/go-m1cpu/.golangci.yaml | 12 + vendor/github.com/shoenig/go-m1cpu/LICENSE | 363 +++ vendor/github.com/shoenig/go-m1cpu/Makefile | 12 + vendor/github.com/shoenig/go-m1cpu/README.md | 66 + vendor/github.com/shoenig/go-m1cpu/cpu.go | 213 ++ .../shoenig/go-m1cpu/incompatible.go | 53 + .../github.com/stretchr/objx/.codeclimate.yml | 21 + vendor/github.com/stretchr/objx/.gitignore | 11 + vendor/github.com/stretchr/objx/LICENSE | 22 + vendor/github.com/stretchr/objx/README.md | 80 + vendor/github.com/stretchr/objx/Taskfile.yml | 27 + vendor/github.com/stretchr/objx/accessors.go | 197 ++ .../github.com/stretchr/objx/conversions.go | 280 ++ vendor/github.com/stretchr/objx/doc.go | 66 + vendor/github.com/stretchr/objx/map.go | 214 ++ vendor/github.com/stretchr/objx/mutations.go | 77 + vendor/github.com/stretchr/objx/security.go | 12 + vendor/github.com/stretchr/objx/tests.go | 17 + .../github.com/stretchr/objx/type_specific.go | 346 +++ .../stretchr/objx/type_specific_codegen.go | 2261 +++++++++++++++++ vendor/github.com/stretchr/objx/value.go | 159 ++ .../github.com/stretchr/testify/mock/doc.go | 44 + .../github.com/stretchr/testify/mock/mock.go | 1241 +++++++++ vendor/modules.txt | 21 + 178 files changed, 28650 insertions(+), 160 deletions(-) rename pkg/internal/infraolly/process/{harvester_unix.go => harvester.go} (76%) rename pkg/internal/infraolly/process/{harvester_unix_mock_test.go => harvester_mock_test.go} (93%) create mode 100644 vendor/github.com/lufia/plan9stats/.gitignore create mode 100644 vendor/github.com/lufia/plan9stats/LICENSE create mode 100644 vendor/github.com/lufia/plan9stats/README.md create mode 100644 vendor/github.com/lufia/plan9stats/cpu.go create mode 100644 vendor/github.com/lufia/plan9stats/doc.go create mode 100644 vendor/github.com/lufia/plan9stats/host.go create mode 100644 vendor/github.com/lufia/plan9stats/int.go create mode 100644 vendor/github.com/lufia/plan9stats/opts.go create mode 100644 vendor/github.com/lufia/plan9stats/stats.go create mode 100644 vendor/github.com/power-devops/perfstat/LICENSE create mode 100644 vendor/github.com/power-devops/perfstat/c_helpers.c create mode 100644 vendor/github.com/power-devops/perfstat/c_helpers.h create mode 100644 vendor/github.com/power-devops/perfstat/config.go create mode 100644 vendor/github.com/power-devops/perfstat/cpustat.go create mode 100644 vendor/github.com/power-devops/perfstat/diskstat.go create mode 100644 vendor/github.com/power-devops/perfstat/doc.go create mode 100644 vendor/github.com/power-devops/perfstat/fsstat.go create mode 100644 vendor/github.com/power-devops/perfstat/helpers.go create mode 100644 vendor/github.com/power-devops/perfstat/lparstat.go create mode 100644 vendor/github.com/power-devops/perfstat/lvmstat.go create mode 100644 vendor/github.com/power-devops/perfstat/memstat.go create mode 100644 vendor/github.com/power-devops/perfstat/netstat.go create mode 100644 vendor/github.com/power-devops/perfstat/procstat.go create mode 100644 vendor/github.com/power-devops/perfstat/sysconf.go create mode 100644 vendor/github.com/power-devops/perfstat/systemcfg.go create mode 100644 vendor/github.com/power-devops/perfstat/types_cpu.go create mode 100644 vendor/github.com/power-devops/perfstat/types_disk.go create mode 100644 vendor/github.com/power-devops/perfstat/types_fs.go create mode 100644 vendor/github.com/power-devops/perfstat/types_lpar.go create mode 100644 vendor/github.com/power-devops/perfstat/types_lvm.go create mode 100644 vendor/github.com/power-devops/perfstat/types_memory.go create mode 100644 vendor/github.com/power-devops/perfstat/types_network.go create mode 100644 vendor/github.com/power-devops/perfstat/types_process.go create mode 100644 vendor/github.com/power-devops/perfstat/uptime.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/LICENSE create mode 100644 vendor/github.com/shirou/gopsutil/v3/common/env.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix_cgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix_nocgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin_cgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin_nocgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_dragonfly.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_dragonfly_amd64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_fallback.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_386.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_amd64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_arm.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_arm64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_linux.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd_amd64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd_arm64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_386.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_amd64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_arm.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_arm64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_riscv64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_plan9.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_solaris.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/cpu/cpu_windows.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/binary.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_darwin.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_freebsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_linux.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_netbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_openbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_unix.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_windows.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/endian.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/sleep.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/warnings.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_aix.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_aix_cgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_aix_nocgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_bsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin_cgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin_nocgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_fallback.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_freebsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_linux.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_netbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_386.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_amd64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_arm.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_arm64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_riscv64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_plan9.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_solaris.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/mem/mem_windows.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_aix.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_aix_cgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_aix_nocgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_darwin.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_fallback.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_freebsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_linux.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_linux_111.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_linux_116.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_openbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_solaris.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_unix.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/net/net_windows.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_bsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_darwin.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_darwin_amd64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_darwin_arm64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_darwin_cgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_darwin_nocgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_fallback.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_freebsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_386.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_amd64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_linux.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_openbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_386.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_amd64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_riscv64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_plan9.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_posix.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_solaris.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_windows.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_windows_32bit.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/process/process_windows_64bit.go create mode 100644 vendor/github.com/shoenig/go-m1cpu/.golangci.yaml create mode 100644 vendor/github.com/shoenig/go-m1cpu/LICENSE create mode 100644 vendor/github.com/shoenig/go-m1cpu/Makefile create mode 100644 vendor/github.com/shoenig/go-m1cpu/README.md create mode 100644 vendor/github.com/shoenig/go-m1cpu/cpu.go create mode 100644 vendor/github.com/shoenig/go-m1cpu/incompatible.go create mode 100644 vendor/github.com/stretchr/objx/.codeclimate.yml create mode 100644 vendor/github.com/stretchr/objx/.gitignore create mode 100644 vendor/github.com/stretchr/objx/LICENSE create mode 100644 vendor/github.com/stretchr/objx/README.md create mode 100644 vendor/github.com/stretchr/objx/Taskfile.yml create mode 100644 vendor/github.com/stretchr/objx/accessors.go create mode 100644 vendor/github.com/stretchr/objx/conversions.go create mode 100644 vendor/github.com/stretchr/objx/doc.go create mode 100644 vendor/github.com/stretchr/objx/map.go create mode 100644 vendor/github.com/stretchr/objx/mutations.go create mode 100644 vendor/github.com/stretchr/objx/security.go create mode 100644 vendor/github.com/stretchr/objx/tests.go create mode 100644 vendor/github.com/stretchr/objx/type_specific.go create mode 100644 vendor/github.com/stretchr/objx/type_specific_codegen.go create mode 100644 vendor/github.com/stretchr/objx/value.go create mode 100644 vendor/github.com/stretchr/testify/mock/doc.go create mode 100644 vendor/github.com/stretchr/testify/mock/mock.go diff --git a/go.mod b/go.mod index 0bf9d3f72..cd1ab6c9a 100644 --- a/go.mod +++ b/go.mod @@ -20,11 +20,13 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/mariomac/guara v0.0.0-20230621100729-42bd7716e524 github.com/mariomac/pipes v0.10.0 + github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.19.0 github.com/prometheus/client_model v0.6.0 github.com/prometheus/common v0.48.0 github.com/prometheus/procfs v0.12.0 github.com/shirou/gopsutil v3.21.11+incompatible + github.com/shirou/gopsutil/v3 v3.24.2 github.com/stretchr/testify v1.9.0 github.com/vishvananda/netlink v1.1.0 github.com/vladimirvivien/gexe v0.2.0 @@ -112,6 +114,7 @@ require ( github.com/knadh/koanf/providers/confmap v0.1.0 // indirect github.com/knadh/koanf/v2 v2.1.0 // indirect github.com/leodido/go-urn v1.2.4 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -123,10 +126,12 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/rs/cors v1.10.1 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/tklauser/go-sysconf v0.3.13 // indirect github.com/tklauser/numcpus v0.7.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect diff --git a/go.sum b/go.sum index f883b971a..cf6a3e789 100644 --- a/go.sum +++ b/go.sum @@ -98,6 +98,7 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -154,6 +155,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mariomac/guara v0.0.0-20230621100729-42bd7716e524 h1:24nnoPrOI7cw2YZTWqDHqIchSJ07thcQDIUNnL6V+2o= @@ -190,6 +193,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= @@ -204,11 +209,19 @@ github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y= +github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk= +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/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -217,10 +230,13 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= @@ -353,8 +369,12 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= diff --git a/pkg/internal/infraolly/process/cache.go b/pkg/internal/infraolly/process/cache.go index cc65ad3e3..41ff229fb 100644 --- a/pkg/internal/infraolly/process/cache.go +++ b/pkg/internal/infraolly/process/cache.go @@ -11,7 +11,7 @@ import ( type cacheEntry struct { process *linuxProcess - lastSample *Sample // The last event we generated for this process, so we can re-use metadata which doesn't change + lastSample *Sample // The last event we generated for this process, so we can re-use metadata that doesn't change } // removeUntilLen removes the oldest entries until the cache reaches the given length. diff --git a/pkg/internal/infraolly/process/harvester_unix.go b/pkg/internal/infraolly/process/harvester.go similarity index 76% rename from pkg/internal/infraolly/process/harvester_unix.go rename to pkg/internal/infraolly/process/harvester.go index 495487cc6..f17e691c4 100644 --- a/pkg/internal/infraolly/process/harvester_unix.go +++ b/pkg/internal/infraolly/process/harvester.go @@ -1,7 +1,5 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -//go:build linux || darwin -// +build linux darwin // Package process provides all the tools and functionality for sampling processes. It is divided in three main // components: @@ -12,12 +10,12 @@ package process import ( "fmt" - - "github.com/newrelic/infrastructure-agent/pkg/log" - "github.com/newrelic/infrastructure-agent/pkg/metrics/types" + "log/slog" ) -var mplog = log.WithComponent("ProcessSampler") +func mplog() *slog.Logger { + return slog.With("component", "process.Sampler") +} var errProcessWithoutRSS = fmt.Errorf("process with zero rss") @@ -27,6 +25,18 @@ type Harvester interface { // Pids return the IDs of all the processes that are currently running Pids() ([]int32, error) // Do performs the actual harvesting operation, returning a process sample containing all the metrics data - // for the last elapsedSeconds - Do(pid int32, elapsedSeconds float64) (*Sample, error) + Do(pid int32) (*Sample, error) } + +type RunMode string + +const ( + RunModeRoot = "root" + RunModePrivileged = "privileged" +) + +type Config struct { + RunMode RunMode + DisableZeroRSSFilter bool + StripCommandLine bool +} \ No newline at end of file diff --git a/pkg/internal/infraolly/process/harvester_linux.go b/pkg/internal/infraolly/process/harvester_linux.go index f87f50551..a29a5777e 100644 --- a/pkg/internal/infraolly/process/harvester_linux.go +++ b/pkg/internal/infraolly/process/harvester_linux.go @@ -8,30 +8,25 @@ package process import ( + "log/slog" + "github.com/hashicorp/golang-lru/v2/simplelru" - "github.com/newrelic/infrastructure-agent/internal/agent" - "github.com/newrelic/infrastructure-agent/pkg/config" - "github.com/newrelic/infrastructure-agent/pkg/metrics" - "github.com/newrelic/infrastructure-agent/pkg/metrics/acquire" - "github.com/newrelic/infrastructure-agent/pkg/metrics/types" "github.com/pkg/errors" "github.com/shirou/gopsutil/v3/process" - "github.com/sirupsen/logrus" ) -func newHarvester(ctx agent.AgentContext, cache *simplelru.LRU[int32, *cacheEntry]) *linuxHarvester { - cfg := ctx.Config() +func newHarvester(cfg *Config, cache *simplelru.LRU[int32, *cacheEntry]) *linuxHarvester { // If not config, assuming root mode as default - privileged := cfg == nil || cfg.RunMode == config.ModeRoot || cfg.RunMode == config.ModePrivileged + privileged := cfg == nil || cfg.RunMode == RunModeRoot || cfg.RunMode == RunModePrivileged disableZeroRSSFilter := cfg != nil && cfg.DisableZeroRSSFilter - stripCommandLine := (cfg != nil && cfg.StripCommandLine) || (cfg == nil && config.DefaultStripCommandLine) + stripCommandLine := cfg != nil && cfg.StripCommandLine return &linuxHarvester{ privileged: privileged, disableZeroRSSFilter: disableZeroRSSFilter, stripCommandLine: stripCommandLine, - serviceForPid: ctx.GetServiceForPid, cache: cache, + log: mplog(), } } @@ -40,8 +35,8 @@ type linuxHarvester struct { privileged bool disableZeroRSSFilter bool stripCommandLine bool - cache *cache - serviceForPid func(int) (string, bool) + cache *simplelru.LRU[int32, *cacheEntry] + log *slog.Logger } var _ Harvester = (*linuxHarvester)(nil) // static interface assertion @@ -51,10 +46,10 @@ func (*linuxHarvester) Pids() ([]int32, error) { return process.Pids() } -// Returns a sample of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the +// Do returns a sample of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the // time since this process was sampled for the last time. If the process has been sampled for the first time, this value // will be ignored -func (ps *linuxHarvester) Do(pid int32, elapsedSeconds float64) (*Sample, error) { +func (ps *linuxHarvester) Do(pid int32) (*Sample, error) { // Reuses process information that does not vary cached, hasCachedSample := ps.cache.Get(pid) @@ -74,7 +69,7 @@ func (ps *linuxHarvester) Do(pid int32, elapsedSeconds float64) (*Sample, error) } // Creates a fresh process sample and populates it with the metrics data - sample := metrics.NewProcessSample(pid) + sample := NewSample(pid) if err := ps.populateStaticData(sample, cached.process); err != nil { return nil, errors.Wrap(err, "can't populate static attributes") @@ -89,16 +84,10 @@ func (ps *linuxHarvester) Do(pid int32, elapsedSeconds float64) (*Sample, error) return nil, errors.Wrap(err, "can't fetch gauge data") } - if err := ps.populateIOCounters(sample, cached.lastSample, cached.process, elapsedSeconds); err != nil { + if err := ps.populateIOCounters(sample, cached.process); err != nil { return nil, errors.Wrap(err, "can't fetch deltas") } - // This must happen every time, even if we already had a cached sample for the process, because - // the available process name metadata may have changed underneath us (if we pick up a new - // service/PID association, etc) - sample.ProcessDisplayName = ps.determineProcessDisplayName(sample) - - sample.Type("ProcessSample") cached.lastSample = sample return sample, nil @@ -116,10 +105,10 @@ func (ps *linuxHarvester) populateStaticData(sample *Sample, process Snapshot) e sample.User, err = process.Username() if err != nil { - mplog.WithError(err).WithField("processID", sample.ProcessID).Debug("Can't get Username for process.") + ps.log.Debug("can't get username for process", "pid", sample.ProcessID, "error", err) } - sample.CommandName = process.Command() + sample.Command = process.Command() sample.ParentProcessID = process.Ppid() return nil @@ -146,13 +135,10 @@ func (ps *linuxHarvester) populateGauges(sample *Sample, process Snapshot) error } if ps.privileged { - fds, err := process.NumFDs() + sample.FdCount, err = process.NumFDs() if err != nil { return err } - if fds >= 0 { - sample.FdCount = &fds - } } // Extra status data @@ -166,49 +152,14 @@ func (ps *linuxHarvester) populateGauges(sample *Sample, process Snapshot) error // populateIOCounters fills the sample with the IO counters data. For the "X per second" metrics, it requires the // last process sample for comparative purposes -func (ps *linuxHarvester) populateIOCounters(sample, lastSample *Sample, source Snapshot, elapsedSeconds float64) error { +func (ps *linuxHarvester) populateIOCounters(sample *Sample, source Snapshot) error { ioCounters, err := source.IOCounters() if err != nil { return err } - if ioCounters != nil { - // Delta - if lastSample != nil && lastSample.LastIOCounters != nil { - lastCounters := lastSample.LastIOCounters - - mplog.WithField(config.TracesFieldName, config.FeatureTrace).Tracef("ReadCount: %d, WriteCount: %d, ReadBytes: %d, WriteBytes: %d", ioCounters.ReadCount, ioCounters.WriteCount, ioCounters.ReadBytes, ioCounters.WriteBytes) - ioReadCountPerSecond := acquire.CalculateSafeDelta(ioCounters.ReadCount, lastCounters.ReadCount, elapsedSeconds) - ioWriteCountPerSecond := acquire.CalculateSafeDelta(ioCounters.WriteCount, lastCounters.WriteCount, elapsedSeconds) - ioReadBytesPerSecond := acquire.CalculateSafeDelta(ioCounters.ReadBytes, lastCounters.ReadBytes, elapsedSeconds) - ioWriteBytesPerSecond := acquire.CalculateSafeDelta(ioCounters.WriteBytes, lastCounters.WriteBytes, elapsedSeconds) - - sample.IOReadCountPerSecond = &ioReadCountPerSecond - sample.IOWriteCountPerSecond = &ioWriteCountPerSecond - sample.IOReadBytesPerSecond = &ioReadBytesPerSecond - sample.IOWriteBytesPerSecond = &ioWriteBytesPerSecond - } - - // Cumulative - sample.IOTotalReadCount = &ioCounters.ReadCount - sample.IOTotalWriteCount = &ioCounters.WriteCount - sample.IOTotalReadBytes = &ioCounters.ReadBytes - sample.IOTotalWriteBytes = &ioCounters.WriteBytes - - sample.LastIOCounters = ioCounters - } + sample.IOReadCount = ioCounters.ReadCount + sample.IOWriteCount = ioCounters.WriteCount + sample.IOReadBytes = ioCounters.ReadBytes + sample.IOWriteBytes = ioCounters.WriteBytes return nil } - -// determineProcessDisplayName generates a human-friendly name for this process. By default, we use the command name. -// If we know of a service for this pid, that'll be the name. -func (ps *linuxHarvester) determineProcessDisplayName(sample *Sample) string { - displayName := sample.CommandName - if serviceName, ok := ps.serviceForPid(int(sample.ProcessID)); ok && len(serviceName) > 0 { - mplog.WithFieldsF(func() logrus.Fields { - return logrus.Fields{"serviceName": serviceName, "displayName": displayName, "ProcessID": sample.ProcessID} - }).Debug("Using service name as display name.") - displayName = serviceName - } - - return displayName -} diff --git a/pkg/internal/infraolly/process/harvester_linux_test.go b/pkg/internal/infraolly/process/harvester_linux_test.go index 98b094e85..5369936d9 100644 --- a/pkg/internal/infraolly/process/harvester_linux_test.go +++ b/pkg/internal/infraolly/process/harvester_linux_test.go @@ -13,9 +13,6 @@ import ( "time" "github.com/hashicorp/golang-lru/v2/simplelru" - "github.com/newrelic/infrastructure-agent/internal/agent/mocks" - "github.com/newrelic/infrastructure-agent/internal/testhelpers" - "github.com/newrelic/infrastructure-agent/pkg/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -87,7 +84,7 @@ func TestLinuxHarvester_Do(t *testing.T) { require.NotNil(t, sample) assert.Equal(t, int32(os.Getpid()), sample.ProcessID) - assert.Equal(t, "process.test", sample.CommandName) + assert.Equal(t, "process.test", sample.Command) assert.Contains(t, sample.CmdLine, os.Args[0]) assert.NotEmpty(t, sample.User) assert.Contains(t, "RSD", sample.Status, @@ -206,8 +203,8 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentCmd(t *testing.T) { require.NoError(t, err) // The sample is updated - assert.NotEmpty(t, sample.CommandName) - assert.NotEqual(t, "something old", sample.CommandName) + assert.NotEmpty(t, sample.Command) + assert.NotEqual(t, "something old", sample.Command) } func TestLinuxHarvester_Do_InvalidateCache_DifferentPid(t *testing.T) { @@ -247,7 +244,7 @@ func TestLinuxHarvester_GetServiceForPid(t *testing.T) { require.NoError(t, err) require.NotNil(t, sample) assert.Equal(t, "MyServiceIdentifier", sample.ProcessDisplayName) - assert.Equal(t, "process.test", sample.CommandName) + assert.Equal(t, "process.test", sample.Command) assert.Contains(t, sample.CmdLine, os.Args[0]) } @@ -267,7 +264,7 @@ func TestLinuxHarvester_GetServiceForPid_OnEmptyUseCommandName(t *testing.T) { // It returns the corresponding process names require.NoError(t, err) require.NotNil(t, sample) - assert.Equal(t, sample.CommandName, sample.ProcessDisplayName) - assert.Equal(t, "process.test", sample.CommandName) + assert.Equal(t, sample.Command, sample.ProcessDisplayName) + assert.Equal(t, "process.test", sample.Command) assert.Contains(t, sample.CmdLine, os.Args[0]) } diff --git a/pkg/internal/infraolly/process/harvester_unix_mock_test.go b/pkg/internal/infraolly/process/harvester_mock_test.go similarity index 93% rename from pkg/internal/infraolly/process/harvester_unix_mock_test.go rename to pkg/internal/infraolly/process/harvester_mock_test.go index 04910f47a..9f8d8ca6d 100644 --- a/pkg/internal/infraolly/process/harvester_unix_mock_test.go +++ b/pkg/internal/infraolly/process/harvester_mock_test.go @@ -6,7 +6,6 @@ package process import ( - "github.com/newrelic/infrastructure-agent/pkg/metrics/types" "github.com/stretchr/testify/mock" ) diff --git a/pkg/internal/infraolly/process/process_retriever_cached_darwin.go b/pkg/internal/infraolly/process/process_retriever_cached_darwin.go index e21582b56..da059c8d0 100644 --- a/pkg/internal/infraolly/process/process_retriever_cached_darwin.go +++ b/pkg/internal/infraolly/process/process_retriever_cached_darwin.go @@ -5,7 +5,6 @@ package process import ( "fmt" - "github.com/newrelic/infrastructure-agent/pkg/helpers" "github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/process" "os/exec" diff --git a/pkg/internal/infraolly/process/sample.go b/pkg/internal/infraolly/process/sample.go index 0b2030d79..dbb769759 100644 --- a/pkg/internal/infraolly/process/sample.go +++ b/pkg/internal/infraolly/process/sample.go @@ -1,36 +1,27 @@ package process -import "github.com/shirou/gopsutil/process" - type Sample struct { - ProcessDisplayName string `json:"processDisplayName"` - ProcessID int32 `json:"processId"` - CommandName string `json:"commandName"` - User string `json:"userName,omitempty"` - MemoryRSSBytes int64 `json:"memoryResidentSizeBytes"` - MemoryVMSBytes int64 `json:"memoryVirtualSizeBytes"` - CPUPercent float64 `json:"cpuPercent"` - CPUUserPercent float64 `json:"cpuUserPercent"` - CPUSystemPercent float64 `json:"cpuSystemPercent"` - ContainerImage string `json:"containerImage,omitempty"` - ContainerImageName string `json:"containerImageName,omitempty"` - ContainerName string `json:"containerName,omitempty"` - ContainerID string `json:"containerId,omitempty"` - Contained string `json:"contained,omitempty"` - CmdLine string `json:"commandLine,omitempty"` - Status string `json:"state,omitempty"` - ParentProcessID int32 `json:"parentProcessId,omitempty"` - ThreadCount int32 `json:"threadCount,omitempty"` - FdCount *int32 `json:"fileDescriptorCount,omitempty"` - IOReadCountPerSecond *float64 `json:"ioReadCountPerSecond,omitempty"` - IOWriteCountPerSecond *float64 `json:"ioWriteCountPerSecond,omitempty"` - IOReadBytesPerSecond *float64 `json:"ioReadBytesPerSecond,omitempty"` - IOWriteBytesPerSecond *float64 `json:"ioWriteBytesPerSecond,omitempty"` - IOTotalReadCount *uint64 `json:"ioTotalReadCount,omitempty"` - IOTotalWriteCount *uint64 `json:"ioTotalWriteCount,omitempty"` - IOTotalReadBytes *uint64 `json:"ioTotalReadBytes,omitempty"` - IOTotalWriteBytes *uint64 `json:"ioTotalWriteBytes,omitempty"` - // Auxiliary values, not to be reported - LastIOCounters *process.IOCountersStat `json:"-"` - ContainerLabels map[string]string `json:"-"` + ProcessID int32 + Command string + User string + MemoryRSSBytes int64 + MemoryVMSBytes int64 + CPUPercent float64 + CPUUserPercent float64 + CPUSystemPercent float64 + CmdLine string + Status string + ParentProcessID int32 + ThreadCount int32 + FdCount int32 + IOReadCount uint64 + IOWriteCount uint64 + IOReadBytes uint64 + IOWriteBytes uint64 +} + +func NewSample(pid int32) *Sample { + return &Sample{ + ProcessID: pid, + } } diff --git a/pkg/internal/infraolly/process/sampler_darwin.go b/pkg/internal/infraolly/process/sampler_darwin.go index 56ae8f3f9..089c92396 100644 --- a/pkg/internal/infraolly/process/sampler_darwin.go +++ b/pkg/internal/infraolly/process/sampler_darwin.go @@ -9,12 +9,7 @@ import ( "strings" "time" - "github.com/newrelic/infrastructure-agent/internal/agent" - "github.com/newrelic/infrastructure-agent/pkg/config" - "github.com/newrelic/infrastructure-agent/pkg/metrics" - "github.com/newrelic/infrastructure-agent/pkg/metrics/sampler" - "github.com/newrelic/infrastructure-agent/pkg/metrics/types" - "github.com/newrelic/infrastructure-agent/pkg/sample" + ) // processSampler is an implementation of the metrics_sender.Sampler interface, which returns runtime information about diff --git a/pkg/internal/infraolly/process/sampler_darwin_test.go b/pkg/internal/infraolly/process/sampler_darwin_test.go index d7a5fd170..f5e06bb2e 100644 --- a/pkg/internal/infraolly/process/sampler_darwin_test.go +++ b/pkg/internal/infraolly/process/sampler_darwin_test.go @@ -6,11 +6,6 @@ import ( "errors" "testing" - "github.com/newrelic/infrastructure-agent/pkg/metrics" - - "github.com/newrelic/infrastructure-agent/internal/agent/mocks" - "github.com/newrelic/infrastructure-agent/pkg/config" - "github.com/newrelic/infrastructure-agent/pkg/metrics/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) diff --git a/pkg/internal/infraolly/process/sampler_linux.go b/pkg/internal/infraolly/process/sampler_linux.go index a99dc3820..e04959330 100644 --- a/pkg/internal/infraolly/process/sampler_linux.go +++ b/pkg/internal/infraolly/process/sampler_linux.go @@ -11,12 +11,7 @@ import ( "time" "github.com/hashicorp/golang-lru/v2/simplelru" - "github.com/newrelic/infrastructure-agent/internal/agent" - "github.com/newrelic/infrastructure-agent/pkg/config" - "github.com/newrelic/infrastructure-agent/pkg/metrics" - "github.com/newrelic/infrastructure-agent/pkg/metrics/sampler" - "github.com/newrelic/infrastructure-agent/pkg/metrics/types" - "github.com/newrelic/infrastructure-agent/pkg/sample" + ) // processSampler is an implementation of the metrics_sender.Sampler interface, which returns runtime information about diff --git a/pkg/internal/infraolly/process/sampler_linux_test.go b/pkg/internal/infraolly/process/sampler_linux_test.go index cacab82b9..5b4426d8b 100644 --- a/pkg/internal/infraolly/process/sampler_linux_test.go +++ b/pkg/internal/infraolly/process/sampler_linux_test.go @@ -12,18 +12,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/newrelic/infrastructure-agent/internal/agent" - "github.com/newrelic/infrastructure-agent/internal/agent/mocks" - agentTypes "github.com/newrelic/infrastructure-agent/internal/agent/types" - "github.com/newrelic/infrastructure-agent/pkg/config" - "github.com/newrelic/infrastructure-agent/pkg/entity" - "github.com/newrelic/infrastructure-agent/pkg/entity/host" - "github.com/newrelic/infrastructure-agent/pkg/metrics" - "github.com/newrelic/infrastructure-agent/pkg/metrics/types" - "github.com/newrelic/infrastructure-agent/pkg/plugins/ids" - "github.com/newrelic/infrastructure-agent/pkg/sample" - "github.com/newrelic/infrastructure-agent/pkg/sysinfo" - "github.com/newrelic/infrastructure-agent/pkg/sysinfo/hostname" ) func TestProcessSampler_DockerDecorator(t *testing.T) { diff --git a/pkg/internal/infraolly/process/sampler_unix_test.go b/pkg/internal/infraolly/process/sampler_unix_test.go index 995c3d81f..5ebf1489f 100644 --- a/pkg/internal/infraolly/process/sampler_unix_test.go +++ b/pkg/internal/infraolly/process/sampler_unix_test.go @@ -6,10 +6,6 @@ package process -import ( - "github.com/newrelic/infrastructure-agent/pkg/metrics" - "github.com/newrelic/infrastructure-agent/pkg/metrics/types" -) type fakeContainerSampler struct{} diff --git a/pkg/internal/infraolly/process/snapshot_darwin.go b/pkg/internal/infraolly/process/snapshot_darwin.go index a5706e399..d9f5a6f7b 100644 --- a/pkg/internal/infraolly/process/snapshot_darwin.go +++ b/pkg/internal/infraolly/process/snapshot_darwin.go @@ -10,7 +10,6 @@ import ( "github.com/shirou/gopsutil/v3/process" - "github.com/newrelic/infrastructure-agent/pkg/helpers" ) // darwinProcess is an implementation of the process.Snapshot interface for darwin hosts. diff --git a/pkg/internal/infraolly/process/snapshot_linux.go b/pkg/internal/infraolly/process/snapshot_linux.go index a75a8ba59..865e1796c 100644 --- a/pkg/internal/infraolly/process/snapshot_linux.go +++ b/pkg/internal/infraolly/process/snapshot_linux.go @@ -11,7 +11,6 @@ import ( "strings" "time" - "github.com/newrelic/infrastructure-agent/pkg/helpers" "github.com/pkg/errors" "github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/process" @@ -76,7 +75,7 @@ func getLinuxProcess(pid int32, previous *linuxProcess, privileged bool) (*linux // Reusing information from the last snapshot for the same process // If the name or the PPID changed from the previous, we'll consider this sample is just // a new process that shares the PID with an old one. - // if a process with the same CommandName but different CmdLine or User name + // if a process with the same Command but different CmdLine or User name // occupies the same PID, the cache won't refresh the CmdLine and Username. if previous == nil || procStats.command != previous.Command() || procStats.ppid != previous.Ppid() { gops, err = process.NewProcess(pid) diff --git a/pkg/internal/infraolly/process/snapshot_linux_test.go b/pkg/internal/infraolly/process/snapshot_linux_test.go index fd57d3e33..5736e60f8 100644 --- a/pkg/internal/infraolly/process/snapshot_linux_test.go +++ b/pkg/internal/infraolly/process/snapshot_linux_test.go @@ -12,8 +12,6 @@ import ( "path" "testing" - "github.com/newrelic/infrastructure-agent/pkg/helpers" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/vendor/github.com/lufia/plan9stats/.gitignore b/vendor/github.com/lufia/plan9stats/.gitignore new file mode 100644 index 000000000..f1c181ec9 --- /dev/null +++ b/vendor/github.com/lufia/plan9stats/.gitignore @@ -0,0 +1,12 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out diff --git a/vendor/github.com/lufia/plan9stats/LICENSE b/vendor/github.com/lufia/plan9stats/LICENSE new file mode 100644 index 000000000..a6d47e807 --- /dev/null +++ b/vendor/github.com/lufia/plan9stats/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2019, KADOTA, Kyohei +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/lufia/plan9stats/README.md b/vendor/github.com/lufia/plan9stats/README.md new file mode 100644 index 000000000..a21700c0c --- /dev/null +++ b/vendor/github.com/lufia/plan9stats/README.md @@ -0,0 +1,2 @@ +# plan9stats +A module for retrieving statistics of Plan 9 diff --git a/vendor/github.com/lufia/plan9stats/cpu.go b/vendor/github.com/lufia/plan9stats/cpu.go new file mode 100644 index 000000000..a101b9119 --- /dev/null +++ b/vendor/github.com/lufia/plan9stats/cpu.go @@ -0,0 +1,288 @@ +package stats + +import ( + "bufio" + "bytes" + "context" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "time" +) + +// CPUType represents /dev/cputype. +type CPUType struct { + Name string + Clock int // clock rate in MHz +} + +func ReadCPUType(ctx context.Context, opts ...Option) (*CPUType, error) { + cfg := newConfig(opts...) + var c CPUType + if err := readCPUType(cfg.rootdir, &c); err != nil { + return nil, err + } + return &c, nil +} + +type SysStats struct { + ID int + NumCtxSwitch int64 + NumInterrupt int64 + NumSyscall int64 + NumFault int64 + NumTLBFault int64 + NumTLBPurge int64 + LoadAvg int64 // in units of milli-CPUs and is decayed over time + Idle int // percentage + Interrupt int // percentage +} + +// ReadSysStats reads system statistics from /dev/sysstat. +func ReadSysStats(ctx context.Context, opts ...Option) ([]*SysStats, error) { + cfg := newConfig(opts...) + file := filepath.Join(cfg.rootdir, "/dev/sysstat") + f, err := os.Open(file) + if err != nil { + return nil, err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + var stats []*SysStats + for scanner.Scan() { + a := strings.Fields(scanner.Text()) + if len(a) != 10 { + continue + } + var ( + p intParser + stat SysStats + ) + stat.ID = p.ParseInt(a[0], 10) + stat.NumCtxSwitch = p.ParseInt64(a[1], 10) + stat.NumInterrupt = p.ParseInt64(a[2], 10) + stat.NumSyscall = p.ParseInt64(a[3], 10) + stat.NumFault = p.ParseInt64(a[4], 10) + stat.NumTLBFault = p.ParseInt64(a[5], 10) + stat.NumTLBPurge = p.ParseInt64(a[6], 10) + stat.LoadAvg = p.ParseInt64(a[7], 10) + stat.Idle = p.ParseInt(a[8], 10) + stat.Interrupt = p.ParseInt(a[9], 10) + if err := p.Err(); err != nil { + return nil, err + } + stats = append(stats, &stat) + } + if err := scanner.Err(); err != nil { + return nil, err + } + return stats, nil +} + +func readCPUType(rootdir string, c *CPUType) error { + file := filepath.Join(rootdir, "/dev/cputype") + b, err := ioutil.ReadFile(file) + if err != nil { + return err + } + b = bytes.TrimSpace(b) + i := bytes.LastIndexByte(b, ' ') + if i < 0 { + return fmt.Errorf("%s: invalid format", file) + } + clock, err := strconv.Atoi(string(b[i+1:])) + if err != nil { + return err + } + c.Name = string(b[:i]) + c.Clock = clock + return nil +} + +// Time represents /dev/time. +type Time struct { + Unix time.Duration + UnixNano time.Duration + Ticks int64 // clock ticks + Freq int64 //cloc frequency +} + +// Uptime returns uptime. +func (t *Time) Uptime() time.Duration { + v := float64(t.Ticks) / float64(t.Freq) + return time.Duration(v*1000_000_000) * time.Nanosecond +} + +func ReadTime(ctx context.Context, opts ...Option) (*Time, error) { + cfg := newConfig(opts...) + file := filepath.Join(cfg.rootdir, "/dev/time") + var t Time + if err := readTime(file, &t); err != nil { + return nil, err + } + return &t, nil +} + +// ProcStatus represents a /proc/n/status. +type ProcStatus struct { + Name string + User string + State string + Times CPUTime + MemUsed int64 // in units of 1024 bytes + BasePriority uint32 // 0(low) to 19(high) + Priority uint32 // 0(low) to 19(high) +} + +// CPUTime represents /dev/cputime or a part of /proc/n/status. +type CPUTime struct { + User time.Duration // the time in user mode (millisecconds) + Sys time.Duration + Real time.Duration + ChildUser time.Duration // exited children and descendants time in user mode + ChildSys time.Duration + ChildReal time.Duration +} + +// CPUStats emulates Linux's /proc/stat. +type CPUStats struct { + User time.Duration + Sys time.Duration + Idle time.Duration +} + +func ReadCPUStats(ctx context.Context, opts ...Option) (*CPUStats, error) { + cfg := newConfig(opts...) + a, err := ReadSysStats(ctx, opts...) + if err != nil { + return nil, err + } + + dir := filepath.Join(cfg.rootdir, "/proc") + d, err := os.Open(dir) + if err != nil { + return nil, err + } + defer d.Close() + + names, err := d.Readdirnames(0) + if err != nil { + return nil, err + } + var up uint32parser + pids := make([]uint32, len(names)) + for i, s := range names { + pids[i] = up.Parse(s) + } + if up.err != nil { + return nil, err + } + sort.Slice(pids, func(i, j int) bool { + return pids[i] < pids[j] + }) + + var stat CPUStats + for _, pid := range pids { + s := strconv.FormatUint(uint64(pid), 10) + file := filepath.Join(dir, s, "status") + var p ProcStatus + if err := readProcStatus(file, &p); err != nil { + return nil, err + } + stat.User += p.Times.User + stat.Sys += p.Times.Sys + } + + var t Time + file := filepath.Join(cfg.rootdir, "/dev/time") + if err := readTime(file, &t); err != nil { + return nil, err + } + // In multi-processor host, Idle should multiple by number of cores. + u := t.Uptime() * time.Duration(len(a)) + stat.Idle = u - stat.User - stat.Sys + return &stat, nil +} + +func readProcStatus(file string, p *ProcStatus) error { + b, err := ioutil.ReadFile(file) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + fields := strings.Fields(string(b)) + if len(fields) != 12 { + return errors.New("invalid format") + } + p.Name = string(fields[0]) + p.User = string(fields[1]) + p.State = string(fields[2]) + var up uint32parser + p.Times.User = time.Duration(up.Parse(fields[3])) * time.Millisecond + p.Times.Sys = time.Duration(up.Parse(fields[4])) * time.Millisecond + p.Times.Real = time.Duration(up.Parse(fields[5])) * time.Millisecond + p.Times.ChildUser = time.Duration(up.Parse(fields[6])) * time.Millisecond + p.Times.ChildSys = time.Duration(up.Parse(fields[7])) * time.Millisecond + p.Times.ChildReal = time.Duration(up.Parse(fields[8])) * time.Millisecond + p.MemUsed, err = strconv.ParseInt(fields[9], 10, 64) + if err != nil { + return err + } + p.BasePriority = up.Parse(fields[10]) + p.Priority = up.Parse(fields[11]) + return up.err +} + +func readTime(file string, t *Time) error { + b, err := ioutil.ReadFile(file) + if err != nil { + return err + } + fields := strings.Fields(string(b)) + if len(fields) != 4 { + return errors.New("invalid format") + } + n, err := strconv.ParseInt(fields[0], 10, 32) + if err != nil { + return err + } + t.Unix = time.Duration(n) * time.Second + v, err := strconv.ParseInt(fields[1], 10, 64) + if err != nil { + return err + } + t.UnixNano = time.Duration(v) * time.Nanosecond + t.Ticks, err = strconv.ParseInt(fields[2], 10, 64) + if err != nil { + return err + } + t.Freq, err = strconv.ParseInt(fields[3], 10, 64) + if err != nil { + return err + } + return nil +} + +type uint32parser struct { + err error +} + +func (p *uint32parser) Parse(s string) uint32 { + if p.err != nil { + return 0 + } + n, err := strconv.ParseUint(s, 10, 32) + if err != nil { + p.err = err + return 0 + } + return uint32(n) +} diff --git a/vendor/github.com/lufia/plan9stats/doc.go b/vendor/github.com/lufia/plan9stats/doc.go new file mode 100644 index 000000000..10e398e7a --- /dev/null +++ b/vendor/github.com/lufia/plan9stats/doc.go @@ -0,0 +1,2 @@ +// Package stats provides statistic utilities for Plan 9. +package stats diff --git a/vendor/github.com/lufia/plan9stats/host.go b/vendor/github.com/lufia/plan9stats/host.go new file mode 100644 index 000000000..957e90348 --- /dev/null +++ b/vendor/github.com/lufia/plan9stats/host.go @@ -0,0 +1,303 @@ +package stats + +import ( + "bufio" + "bytes" + "context" + "fmt" + "io/ioutil" + "net" + "os" + "path/filepath" + "strconv" + "strings" +) + +var ( + delim = []byte{' '} +) + +// Host represents host status. +type Host struct { + Sysname string + Storages []*Storage + Interfaces []*Interface +} + +// MemStats represents the memory statistics. +type MemStats struct { + Total int64 // total memory in byte + PageSize int64 // a page size in byte + KernelPages int64 + UserPages Gauge + SwapPages Gauge + + Malloced Gauge // kernel malloced data in byte + Graphics Gauge // kernel graphics data in byte +} + +// Gauge is used/available gauge. +type Gauge struct { + Used int64 + Avail int64 +} + +func (g Gauge) Free() int64 { + return g.Avail - g.Used +} + +// ReadMemStats reads memory statistics from /dev/swap. +func ReadMemStats(ctx context.Context, opts ...Option) (*MemStats, error) { + cfg := newConfig(opts...) + swap := filepath.Join(cfg.rootdir, "/dev/swap") + f, err := os.Open(swap) + if err != nil { + return nil, err + } + defer f.Close() + + var stat MemStats + m := map[string]interface{}{ + "memory": &stat.Total, + "pagesize": &stat.PageSize, + "kernel": &stat.KernelPages, + "user": &stat.UserPages, + "swap": &stat.SwapPages, + "kernel malloc": &stat.Malloced, + "kernel draw": &stat.Graphics, + } + scanner := bufio.NewScanner(f) + for scanner.Scan() { + fields := bytes.SplitN(scanner.Bytes(), delim, 2) + if len(fields) < 2 { + continue + } + switch key := string(fields[1]); key { + case "memory", "pagesize", "kernel": + v := m[key].(*int64) + n, err := strconv.ParseInt(string(fields[0]), 10, 64) + if err != nil { + return nil, err + } + *v = n + case "user", "swap", "kernel malloc", "kernel draw": + v := m[key].(*Gauge) + if err := parseGauge(string(fields[0]), v); err != nil { + return nil, err + } + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + return &stat, nil +} + +func parseGauge(s string, r *Gauge) error { + a := strings.SplitN(s, "/", 2) + if len(a) != 2 { + return fmt.Errorf("can't parse ratio: %s", s) + } + var p intParser + u := p.ParseInt64(a[0], 10) + n := p.ParseInt64(a[1], 10) + if err := p.Err(); err != nil { + return err + } + r.Used = u + r.Avail = n + return nil +} + +type Storage struct { + Name string + Model string + Capacity int64 +} + +type Interface struct { + Name string + Addr string +} + +const ( + numEther = 8 // see ether(3) + numIpifc = 16 // see ip(3) +) + +// ReadInterfaces reads network interfaces from etherN. +func ReadInterfaces(ctx context.Context, opts ...Option) ([]*Interface, error) { + cfg := newConfig(opts...) + var a []*Interface + for i := 0; i < numEther; i++ { + p, err := readInterface(cfg.rootdir, i) + if os.IsNotExist(err) { + continue + } + if err != nil { + return nil, err + } + a = append(a, p) + } + return a, nil +} + +func readInterface(netroot string, i int) (*Interface, error) { + ether := fmt.Sprintf("ether%d", i) + dir := filepath.Join(netroot, ether) + info, err := os.Stat(dir) + if err != nil { + return nil, err + } + if !info.IsDir() { + return nil, fmt.Errorf("%s: is not directory", dir) + } + + addr, err := ioutil.ReadFile(filepath.Join(dir, "addr")) + if err != nil { + return nil, err + } + return &Interface{ + Name: ether, + Addr: string(addr), + }, nil +} + +var ( + netdirs = []string{"/net", "/net.alt"} +) + +// ReadHost reads host status. +func ReadHost(ctx context.Context, opts ...Option) (*Host, error) { + cfg := newConfig(opts...) + var h Host + name, err := readSysname(cfg.rootdir) + if err != nil { + return nil, err + } + h.Sysname = name + + a, err := readStorages(cfg.rootdir) + if err != nil { + return nil, err + } + h.Storages = a + + for _, s := range netdirs { + netroot := filepath.Join(cfg.rootdir, s) + ifaces, err := ReadInterfaces(ctx, WithRootDir(netroot)) + if err != nil { + return nil, err + } + h.Interfaces = append(h.Interfaces, ifaces...) + } + return &h, nil +} + +func readSysname(rootdir string) (string, error) { + file := filepath.Join(rootdir, "/dev/sysname") + b, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + return string(bytes.TrimSpace(b)), nil +} + +func readStorages(rootdir string) ([]*Storage, error) { + sdctl := filepath.Join(rootdir, "/dev/sdctl") + f, err := os.Open(sdctl) + if err != nil { + return nil, err + } + defer f.Close() + + var a []*Storage + scanner := bufio.NewScanner(f) + for scanner.Scan() { + fields := bytes.Split(scanner.Bytes(), delim) + if len(fields) == 0 { + continue + } + exp := string(fields[0]) + "*" + if !strings.HasPrefix(exp, "sd") { + continue + } + dir := filepath.Join(rootdir, "/dev", exp) + m, err := filepath.Glob(dir) + if err != nil { + return nil, err + } + for _, dir := range m { + s, err := readStorage(dir) + if err != nil { + return nil, err + } + a = append(a, s) + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + return a, nil +} + +func readStorage(dir string) (*Storage, error) { + ctl := filepath.Join(dir, "ctl") + f, err := os.Open(ctl) + if err != nil { + return nil, err + } + defer f.Close() + + var s Storage + s.Name = filepath.Base(dir) + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Bytes() + switch { + case bytes.HasPrefix(line, []byte("inquiry")): + s.Model = string(bytes.TrimSpace(line[7:])) + case bytes.HasPrefix(line, []byte("geometry")): + fields := bytes.Split(line, delim) + if len(fields) < 3 { + continue + } + var p intParser + sec := p.ParseInt64(string(fields[1]), 10) + size := p.ParseInt64(string(fields[2]), 10) + if err := p.Err(); err != nil { + return nil, err + } + s.Capacity = sec * size + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + return &s, nil +} + +type IPStats struct { + ID int // number of interface in ipifc dir + Device string // associated physical device + MTU int // max transfer unit + Sendra6 uint8 // on == send router adv + Recvra6 uint8 // on == recv router adv + + Pktin int64 // packets read + Pktout int64 // packets written + Errin int64 // read errors + Errout int64 // write errors +} + +type Iplifc struct { + IP net.IP + Mask net.IPMask + Net net.IP // ip & mask + PerfLifetime int64 // preferred lifetime + ValidLifetime int64 // valid lifetime +} + +type Ipv6rp struct { + // TODO(lufia): see ip(2) +} diff --git a/vendor/github.com/lufia/plan9stats/int.go b/vendor/github.com/lufia/plan9stats/int.go new file mode 100644 index 000000000..db133c43e --- /dev/null +++ b/vendor/github.com/lufia/plan9stats/int.go @@ -0,0 +1,31 @@ +package stats + +import ( + "strconv" +) + +type intParser struct { + err error +} + +func (p *intParser) ParseInt(s string, base int) int { + if p.err != nil { + return 0 + } + var n int64 + n, p.err = strconv.ParseInt(s, base, 0) + return int(n) +} + +func (p *intParser) ParseInt64(s string, base int) int64 { + if p.err != nil { + return 0 + } + var n int64 + n, p.err = strconv.ParseInt(s, base, 64) + return n +} + +func (p *intParser) Err() error { + return p.err +} diff --git a/vendor/github.com/lufia/plan9stats/opts.go b/vendor/github.com/lufia/plan9stats/opts.go new file mode 100644 index 000000000..05b7d036a --- /dev/null +++ b/vendor/github.com/lufia/plan9stats/opts.go @@ -0,0 +1,21 @@ +package stats + +type Config struct { + rootdir string +} + +type Option func(*Config) + +func newConfig(opts ...Option) *Config { + var cfg Config + for _, opt := range opts { + opt(&cfg) + } + return &cfg +} + +func WithRootDir(dir string) Option { + return func(cfg *Config) { + cfg.rootdir = dir + } +} diff --git a/vendor/github.com/lufia/plan9stats/stats.go b/vendor/github.com/lufia/plan9stats/stats.go new file mode 100644 index 000000000..d4ecdcfa0 --- /dev/null +++ b/vendor/github.com/lufia/plan9stats/stats.go @@ -0,0 +1,88 @@ +package stats + +import ( + "bufio" + "context" + "os" + "path/filepath" + "strings" +) + +type InterfaceStats struct { + PacketsReceived int64 // in packets + Link int // link status + PacketsSent int64 // out packets + NumCRCErr int // input CRC errors + NumOverflows int // packet overflows + NumSoftOverflows int // software overflow + NumFramingErr int // framing errors + NumBufferingErr int // buffering errors + NumOutputErr int // output errors + Promiscuous int // number of promiscuous opens + Mbps int // megabits per sec + Addr string +} + +func ReadInterfaceStats(ctx context.Context, opts ...Option) (*InterfaceStats, error) { + cfg := newConfig(opts...) + file := filepath.Join(cfg.rootdir, "stats") + f, err := os.Open(file) + if err != nil { + return nil, err + } + defer f.Close() + + var stats InterfaceStats + scanner := bufio.NewScanner(f) + for scanner.Scan() { + s := strings.TrimSpace(scanner.Text()) + a := strings.SplitN(s, ":", 2) + if len(a) != 2 { + continue + } + var p intParser + v := strings.TrimSpace(a[1]) + switch a[0] { + case "in": + stats.PacketsReceived = p.ParseInt64(v, 10) + case "link": + stats.Link = p.ParseInt(v, 10) + case "out": + stats.PacketsSent = p.ParseInt64(v, 10) + case "crc": + stats.NumCRCErr = p.ParseInt(v, 10) + case "overflows": + stats.NumOverflows = p.ParseInt(v, 10) + case "soft overflows": + stats.NumSoftOverflows = p.ParseInt(v, 10) + case "framing errs": + stats.NumFramingErr = p.ParseInt(v, 10) + case "buffer errs": + stats.NumBufferingErr = p.ParseInt(v, 10) + case "output errs": + stats.NumOutputErr = p.ParseInt(v, 10) + case "prom": + stats.Promiscuous = p.ParseInt(v, 10) + case "mbps": + stats.Mbps = p.ParseInt(v, 10) + case "addr": + stats.Addr = v + } + if err := p.Err(); err != nil { + return nil, err + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + return &stats, nil +} + +type TCPStats struct { + MaxConn int + MaxSegment int + ActiveOpens int + PassiveOpens int + EstablishedResets int + CurrentEstablished int +} diff --git a/vendor/github.com/power-devops/perfstat/LICENSE b/vendor/github.com/power-devops/perfstat/LICENSE new file mode 100644 index 000000000..ec4e5d39d --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2020 Power DevOps + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/vendor/github.com/power-devops/perfstat/c_helpers.c b/vendor/github.com/power-devops/perfstat/c_helpers.c new file mode 100644 index 000000000..49ba1ad7e --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/c_helpers.c @@ -0,0 +1,159 @@ +#include "c_helpers.h" + +GETFUNC(cpu) +GETFUNC(disk) +GETFUNC(diskadapter) +GETFUNC(diskpath) +GETFUNC(fcstat) +GETFUNC(logicalvolume) +GETFUNC(memory_page) +GETFUNC(netadapter) +GETFUNC(netbuffer) +GETFUNC(netinterface) +GETFUNC(pagingspace) +GETFUNC(process) +GETFUNC(thread) +GETFUNC(volumegroup) + +double get_partition_mhz(perfstat_partition_config_t pinfo) { + return pinfo.processorMHz; +} + +char *get_ps_hostname(perfstat_pagingspace_t *ps) { + return ps->u.nfs_paging.hostname; +} + +char *get_ps_filename(perfstat_pagingspace_t *ps) { + return ps->u.nfs_paging.filename; +} + +char *get_ps_vgname(perfstat_pagingspace_t *ps) { + return ps->u.lv_paging.vgname; +} + +time_t boottime() +{ + register struct utmpx *utmp; + + setutxent(); + while ( (utmp = getutxent()) != NULL ) { + if (utmp->ut_type == BOOT_TIME) { + return utmp->ut_tv.tv_sec; + } + } + endutxent(); + return -1; +} + +struct fsinfo *get_filesystem_stat(struct fsinfo *fs_all, int n) { + if (!fs_all) return NULL; + return &(fs_all[n]); +} + +int get_mounts(struct vmount **vmountpp) { + int size; + struct vmount *vm; + int nmounts; + + size = BUFSIZ; + + while (1) { + if ((vm = (struct vmount *)malloc((size_t)size)) == NULL) { + perror("malloc failed"); + exit(-1); + } + if ((nmounts = mntctl(MCTL_QUERY, size, (caddr_t)vm)) > 0) { + *vmountpp = vm; + return nmounts; + } else if (nmounts == 0) { + size = *(int *)vm; + free((void *)vm); + } else { + free((void *)vm); + return -1; + } + } +} + +void fill_fsinfo(struct statfs statbuf, struct fsinfo *fs) { + fsblkcnt_t freeblks, totblks, usedblks; + fsblkcnt_t tinodes, ninodes, ifree; + uint cfactor; + + if (statbuf.f_blocks == -1) { + fs->totalblks = 0; + fs->freeblks = 0; + fs->totalinodes = 0; + fs->freeinodes = 0; + return; + } + + cfactor = statbuf.f_bsize / 512; + fs->freeblks = statbuf.f_bavail * cfactor; + fs->totalblks = statbuf.f_blocks * cfactor; + + fs->freeinodes = statbuf.f_ffree; + fs->totalinodes = statbuf.f_files; + + if (fs->freeblks < 0) + fs->freeblks = 0; +} + +int getfsinfo(char *fsname, char *devname, char *host, char *options, int flags, int fstype, struct fsinfo *fs) { + struct statfs statbuf; + int devname_size = strlen(devname); + int fsname_size = strlen(fsname); + char buf[BUFSIZ]; + char *p; + + if (fs == NULL) { + return 1; + } + + for (p = strtok(options, ","); p != NULL; p = strtok(NULL, ",")) + if (strcmp(p, "ignore") == 0) + return 0; + + if (*host != 0 && strcmp(host, "-") != 0) { + sprintf(buf, "%s:%s", host, devname); + devname = buf; + } + fs->devname = (char *)calloc(devname_size+1, 1); + fs->fsname = (char *)calloc(fsname_size+1, 1); + strncpy(fs->devname, devname, devname_size); + strncpy(fs->fsname, fsname, fsname_size); + fs->flags = flags; + fs->fstype = fstype; + + if (statfs(fsname,&statbuf) < 0) { + return 1; + } + + fill_fsinfo(statbuf, fs); + return 0; +} + +struct fsinfo *get_all_fs(int *rc) { + struct vmount *mnt; + struct fsinfo *fs_all; + int nmounts; + + *rc = -1; + if ((nmounts = get_mounts(&mnt)) <= 0) { + perror("Can't get mount table info"); + return NULL; + } + + fs_all = (struct fsinfo *)calloc(sizeof(struct fsinfo), nmounts); + while ((*rc)++, nmounts--) { + getfsinfo(vmt2dataptr(mnt, VMT_STUB), + vmt2dataptr(mnt, VMT_OBJECT), + vmt2dataptr(mnt, VMT_HOST), + vmt2dataptr(mnt, VMT_ARGS), + mnt->vmt_flags, + mnt->vmt_gfstype, + &fs_all[*rc]); + mnt = (struct vmount *)((char *)mnt + mnt->vmt_length); + } + return fs_all; +} diff --git a/vendor/github.com/power-devops/perfstat/c_helpers.h b/vendor/github.com/power-devops/perfstat/c_helpers.h new file mode 100644 index 000000000..b66bc53c3 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/c_helpers.h @@ -0,0 +1,58 @@ +#ifndef C_HELPERS_H +#define C_HELPERS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GETFUNC(TYPE) perfstat_##TYPE##_t *get_##TYPE##_stat(perfstat_##TYPE##_t *b, int n) { \ + if (!b) return NULL; \ + return &(b[n]); \ +} + +#define GETFUNC_EXT(TYPE) extern perfstat_##TYPE##_t *get_##TYPE##_stat(perfstat_##TYPE##_t *, int); + +GETFUNC_EXT(cpu) +GETFUNC_EXT(disk) +GETFUNC_EXT(diskadapter) +GETFUNC_EXT(diskpath) +GETFUNC_EXT(fcstat) +GETFUNC_EXT(logicalvolume) +GETFUNC_EXT(memory_page) +GETFUNC_EXT(netadapter) +GETFUNC_EXT(netbuffer) +GETFUNC_EXT(netinterface) +GETFUNC_EXT(pagingspace) +GETFUNC_EXT(process) +GETFUNC_EXT(thread) +GETFUNC_EXT(volumegroup) + +struct fsinfo { + char *devname; + char *fsname; + int flags; + int fstype; + unsigned long totalblks; + unsigned long freeblks; + unsigned long totalinodes; + unsigned long freeinodes; +}; + +extern double get_partition_mhz(perfstat_partition_config_t); +extern char *get_ps_hostname(perfstat_pagingspace_t *); +extern char *get_ps_filename(perfstat_pagingspace_t *); +extern char *get_ps_vgname(perfstat_pagingspace_t *); +extern time_t boottime(); +struct fsinfo *get_filesystem_stat(struct fsinfo *, int); +int get_mounts(struct vmount **); +void fill_statfs(struct statfs, struct fsinfo *); +int getfsinfo(char *, char *, char *, char *, int, int, struct fsinfo *); +struct fsinfo *get_all_fs(int *); + +#endif diff --git a/vendor/github.com/power-devops/perfstat/config.go b/vendor/github.com/power-devops/perfstat/config.go new file mode 100644 index 000000000..de7230d28 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/config.go @@ -0,0 +1,18 @@ +// +build aix + +package perfstat + +/* +#cgo LDFLAGS: -lperfstat + +#include +*/ +import "C" + +func EnableLVMStat() { + C.perfstat_config(C.PERFSTAT_ENABLE|C.PERFSTAT_LV|C.PERFSTAT_VG, nil) +} + +func DisableLVMStat() { + C.perfstat_config(C.PERFSTAT_DISABLE|C.PERFSTAT_LV|C.PERFSTAT_VG, nil) +} diff --git a/vendor/github.com/power-devops/perfstat/cpustat.go b/vendor/github.com/power-devops/perfstat/cpustat.go new file mode 100644 index 000000000..902727fb8 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/cpustat.go @@ -0,0 +1,98 @@ +// +build aix + +package perfstat + +/* +#cgo LDFLAGS: -lperfstat + +#include +#include +#include + +#include "c_helpers.h" +*/ +import "C" + +import ( + "fmt" + "runtime" + "time" + "unsafe" +) + +func CpuStat() ([]CPU, error) { + var cpustat *C.perfstat_cpu_t + var cpu C.perfstat_id_t + + ncpu := runtime.NumCPU() + + cpustat_len := C.sizeof_perfstat_cpu_t * C.ulong(ncpu) + cpustat = (*C.perfstat_cpu_t)(C.malloc(cpustat_len)) + defer C.free(unsafe.Pointer(cpustat)) + C.strcpy(&cpu.name[0], C.CString(C.FIRST_CPU)) + r := C.perfstat_cpu(&cpu, cpustat, C.sizeof_perfstat_cpu_t, C.int(ncpu)) + if r <= 0 { + return nil, fmt.Errorf("error perfstat_cpu()") + } + c := make([]CPU, r) + for i := 0; i < int(r); i++ { + n := C.get_cpu_stat(cpustat, C.int(i)) + if n != nil { + c[i] = perfstatcpu2cpu(n) + } + } + return c, nil +} + +func CpuTotalStat() (*CPUTotal, error) { + var cpustat *C.perfstat_cpu_total_t + + cpustat = (*C.perfstat_cpu_total_t)(C.malloc(C.sizeof_perfstat_cpu_total_t)) + defer C.free(unsafe.Pointer(cpustat)) + r := C.perfstat_cpu_total(nil, cpustat, C.sizeof_perfstat_cpu_total_t, 1) + if r <= 0 { + return nil, fmt.Errorf("error perfstat_cpu_total()") + } + c := perfstatcputotal2cputotal(cpustat) + return &c, nil +} + +func CpuUtilStat(intvl time.Duration) (*CPUUtil, error) { + var cpuutil *C.perfstat_cpu_util_t + var newt *C.perfstat_cpu_total_t + var oldt *C.perfstat_cpu_total_t + var data C.perfstat_rawdata_t + + oldt = (*C.perfstat_cpu_total_t)(C.malloc(C.sizeof_perfstat_cpu_total_t)) + newt = (*C.perfstat_cpu_total_t)(C.malloc(C.sizeof_perfstat_cpu_total_t)) + cpuutil = (*C.perfstat_cpu_util_t)(C.malloc(C.sizeof_perfstat_cpu_util_t)) + defer C.free(unsafe.Pointer(oldt)) + defer C.free(unsafe.Pointer(newt)) + defer C.free(unsafe.Pointer(cpuutil)) + + r := C.perfstat_cpu_total(nil, oldt, C.sizeof_perfstat_cpu_total_t, 1) + if r <= 0 { + return nil, fmt.Errorf("error perfstat_cpu_total()") + } + + time.Sleep(intvl) + + r = C.perfstat_cpu_total(nil, newt, C.sizeof_perfstat_cpu_total_t, 1) + if r <= 0 { + return nil, fmt.Errorf("error perfstat_cpu_total()") + } + + data._type = C.UTIL_CPU_TOTAL + data.curstat = unsafe.Pointer(newt) + data.prevstat = unsafe.Pointer(oldt) + data.sizeof_data = C.sizeof_perfstat_cpu_total_t + data.cur_elems = 1 + data.prev_elems = 1 + + r = C.perfstat_cpu_util(&data, cpuutil, C.sizeof_perfstat_cpu_util_t, 1) + if r <= 0 { + return nil, fmt.Errorf("error perfstat_cpu_util()") + } + u := perfstatcpuutil2cpuutil(cpuutil) + return &u, nil +} diff --git a/vendor/github.com/power-devops/perfstat/diskstat.go b/vendor/github.com/power-devops/perfstat/diskstat.go new file mode 100644 index 000000000..fc70dfaa4 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/diskstat.go @@ -0,0 +1,137 @@ +// +build aix + +package perfstat + +/* +#cgo LDFLAGS: -lperfstat + +#include +#include +#include +#include "c_helpers.h" +*/ +import "C" + +import ( + "fmt" + "unsafe" +) + +func DiskTotalStat() (*DiskTotal, error) { + var disk C.perfstat_disk_total_t + + rc := C.perfstat_disk_total(nil, &disk, C.sizeof_perfstat_disk_total_t, 1) + if rc != 1 { + return nil, fmt.Errorf("perfstat_disk_total() error") + } + d := perfstatdisktotal2disktotal(disk) + return &d, nil +} + +func DiskAdapterStat() ([]DiskAdapter, error) { + var adapter *C.perfstat_diskadapter_t + var adptname C.perfstat_id_t + + numadpt := C.perfstat_diskadapter(nil, nil, C.sizeof_perfstat_diskadapter_t, 0) + if numadpt <= 0 { + return nil, fmt.Errorf("perfstat_diskadapter() error") + } + + adapter_len := C.sizeof_perfstat_diskadapter_t * C.ulong(numadpt) + adapter = (*C.perfstat_diskadapter_t)(C.malloc(adapter_len)) + defer C.free(unsafe.Pointer(adapter)) + C.strcpy(&adptname.name[0], C.CString(C.FIRST_DISKADAPTER)) + r := C.perfstat_diskadapter(&adptname, adapter, C.sizeof_perfstat_diskadapter_t, numadpt) + if r < 0 { + return nil, fmt.Errorf("perfstat_diskadapter() error") + } + da := make([]DiskAdapter, r) + for i := 0; i < int(r); i++ { + d := C.get_diskadapter_stat(adapter, C.int(i)) + if d != nil { + da[i] = perfstatdiskadapter2diskadapter(d) + } + } + return da, nil +} + +func DiskStat() ([]Disk, error) { + var disk *C.perfstat_disk_t + var diskname C.perfstat_id_t + + numdisk := C.perfstat_disk(nil, nil, C.sizeof_perfstat_disk_t, 0) + if numdisk <= 0 { + return nil, fmt.Errorf("perfstat_disk() error") + } + + disk_len := C.sizeof_perfstat_disk_t * C.ulong(numdisk) + disk = (*C.perfstat_disk_t)(C.malloc(disk_len)) + defer C.free(unsafe.Pointer(disk)) + C.strcpy(&diskname.name[0], C.CString(C.FIRST_DISK)) + r := C.perfstat_disk(&diskname, disk, C.sizeof_perfstat_disk_t, numdisk) + if r < 0 { + return nil, fmt.Errorf("perfstat_disk() error") + } + d := make([]Disk, r) + for i := 0; i < int(r); i++ { + ds := C.get_disk_stat(disk, C.int(i)) + if ds != nil { + d[i] = perfstatdisk2disk(ds) + } + } + return d, nil +} + +func DiskPathStat() ([]DiskPath, error) { + var diskpath *C.perfstat_diskpath_t + var pathname C.perfstat_id_t + + numpaths := C.perfstat_diskpath(nil, nil, C.sizeof_perfstat_diskpath_t, 0) + if numpaths <= 0 { + return nil, fmt.Errorf("perfstat_diskpath() error") + } + + path_len := C.sizeof_perfstat_diskpath_t * C.ulong(numpaths) + diskpath = (*C.perfstat_diskpath_t)(C.malloc(path_len)) + defer C.free(unsafe.Pointer(diskpath)) + C.strcpy(&pathname.name[0], C.CString(C.FIRST_DISKPATH)) + r := C.perfstat_diskpath(&pathname, diskpath, C.sizeof_perfstat_diskpath_t, numpaths) + if r < 0 { + return nil, fmt.Errorf("perfstat_diskpath() error") + } + d := make([]DiskPath, r) + for i := 0; i < int(r); i++ { + p := C.get_diskpath_stat(diskpath, C.int(i)) + if p != nil { + d[i] = perfstatdiskpath2diskpath(p) + } + } + return d, nil +} + +func FCAdapterStat() ([]FCAdapter, error) { + var fcstat *C.perfstat_fcstat_t + var fcname C.perfstat_id_t + + numadpt := C.perfstat_fcstat(nil, nil, C.sizeof_perfstat_fcstat_t, 0) + if numadpt <= 0 { + return nil, fmt.Errorf("perfstat_fcstat() error") + } + + fcstat_len := C.sizeof_perfstat_fcstat_t * C.ulong(numadpt) + fcstat = (*C.perfstat_fcstat_t)(C.malloc(fcstat_len)) + defer C.free(unsafe.Pointer(fcstat)) + C.strcpy(&fcname.name[0], C.CString(C.FIRST_NETINTERFACE)) + r := C.perfstat_fcstat(&fcname, fcstat, C.sizeof_perfstat_fcstat_t, numadpt) + if r < 0 { + return nil, fmt.Errorf("perfstat_fcstat() error") + } + fca := make([]FCAdapter, r) + for i := 0; i < int(r); i++ { + f := C.get_fcstat_stat(fcstat, C.int(i)) + if f != nil { + fca[i] = perfstatfcstat2fcadapter(f) + } + } + return fca, nil +} diff --git a/vendor/github.com/power-devops/perfstat/doc.go b/vendor/github.com/power-devops/perfstat/doc.go new file mode 100644 index 000000000..85eaf3e7e --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/doc.go @@ -0,0 +1,315 @@ +// +build !aix + +// Copyright 2020 Power-Devops.com. All rights reserved. +// Use of this source code is governed by the license +// that can be found in the LICENSE file. +/* +Package perfstat is Go interface to IBM AIX libperfstat. +To use it you need AIX with installed bos.perf.libperfstat. You can check, if is installed using the following command: + + $ lslpp -L bos.perf.perfstat + +The package is written using Go 1.14.7 and AIX 7.2 TL5. It should work with earlier TLs of AIX 7.2, but I +can't guarantee that perfstat structures in the TLs have all the same fields as the structures in AIX 7.2 TL5. + +For documentation of perfstat on AIX and using it in programs refer to the official IBM documentation: +https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat.html +*/ +package perfstat + +import ( + "fmt" + "time" +) + +// EnableLVMStat() switches on LVM (logical volumes and volume groups) performance statistics. +// With this enabled you can use fields KBReads, KBWrites, and IOCnt +// in LogicalVolume and VolumeGroup data types. +func EnableLVMStat() {} + +// DisableLVMStat() switchess of LVM (logical volumes and volume groups) performance statistics. +// This is the default state. In this case LogicalVolume and VolumeGroup data types are +// populated with informations about LVM structures, but performance statistics fields +// (KBReads, KBWrites, IOCnt) are empty. +func DisableLVMStat() {} + +// CpuStat() returns array of CPU structures with information about +// logical CPUs on the system. +// IBM documentation: +// * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_int_cpu.html +// * https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cpu.html +func CpuStat() ([]CPU, error) { + return nil, fmt.Errorf("not implemented") +} + +// CpuTotalStat() returns general information about CPUs on the system. +// IBM documentation: +// * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_glob_cpu.html +// * https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cputot.html +func CpuTotalStat() (*CPUTotal, error) { + return nil, fmt.Errorf("not implemented") +} + +// CpuUtilStat() calculates CPU utilization. +// IBM documentation: +// * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_cpu_util.html +// * https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cpu_util.html +func CpuUtilStat(intvl time.Duration) (*CPUUtil, error) { + return nil, fmt.Errorf("not implemented") +} + +func DiskTotalStat() (*DiskTotal, error) { + return nil, fmt.Errorf("not implemented") +} + +func DiskAdapterStat() ([]DiskAdapter, error) { + return nil, fmt.Errorf("not implemented") +} + +func DiskStat() ([]Disk, error) { + return nil, fmt.Errorf("not implemented") +} + +func DiskPathStat() ([]DiskPath, error) { + return nil, fmt.Errorf("not implemented") +} + +func FCAdapterStat() ([]FCAdapter, error) { + return nil, fmt.Errorf("not implemented") +} + +func PartitionStat() (*PartitionConfig, error) { + return nil, fmt.Errorf("not implemented") +} + +func LogicalVolumeStat() ([]LogicalVolume, error) { + return nil, fmt.Errorf("not implemented") +} + +func VolumeGroupStat() ([]VolumeGroup, error) { + return nil, fmt.Errorf("not implemented") +} + +func MemoryTotalStat() (*MemoryTotal, error) { + return nil, fmt.Errorf("not implemented") +} + +func MemoryPageStat() ([]MemoryPage, error) { + return nil, fmt.Errorf("not implemented") +} + +func PagingSpaceStat() ([]PagingSpace, error) { + return nil, fmt.Errorf("not implemented") +} + +func NetIfaceTotalStat() (*NetIfaceTotal, error) { + return nil, fmt.Errorf("not implemented") +} + +func NetBufferStat() ([]NetBuffer, error) { + return nil, fmt.Errorf("not implemented") +} + +func NetIfaceStat() ([]NetIface, error) { + return nil, fmt.Errorf("not implemented") +} + +func NetAdapterStat() ([]NetAdapter, error) { + return nil, fmt.Errorf("not implemented") +} + +func ProcessStat() ([]Process, error) { + return nil, fmt.Errorf("not implemented") +} + +func ThreadStat() ([]Thread, error) { + return nil, fmt.Errorf("not implemented") +} + +func Sysconf(name int32) (int64, error) { + return 0, fmt.Errorf("not implemented") +} + +func GetCPUImplementation() string { + return "" +} + +func POWER9OrNewer() bool { + return false +} + +func POWER9() bool { + return false +} + +func POWER8OrNewer() bool { + return false +} + +func POWER8() bool { + return false +} + +func POWER7OrNewer() bool { + return false +} + +func POWER7() bool { + return false +} + +func HasTransactionalMemory() bool { + return false +} + +func Is64Bit() bool { + return false +} + +func IsSMP() bool { + return false +} + +func HasVMX() bool { + return false +} + +func HasVSX() bool { + return false +} + +func HasDFP() bool { + return false +} + +func HasNxGzip() bool { + return false +} + +func PksCapable() bool { + return false +} + +func PksEnabled() bool { + return false +} + +func CPUMode() string { + return "" +} + +func KernelBits() int { + return 0 +} + +func IsLPAR() bool { + return false +} + +func CpuAddCapable() bool { + return false +} + +func CpuRemoveCapable() bool { + return false +} + +func MemoryAddCapable() bool { + return false +} + +func MemoryRemoveCapable() bool { + return false +} + +func DLparCapable() bool { + return false +} + +func IsNUMA() bool { + return false +} + +func KernelKeys() bool { + return false +} + +func RecoveryMode() bool { + return false +} + +func EnhancedAffinity() bool { + return false +} + +func VTpmEnabled() bool { + return false +} + +func IsVIOS() bool { + return false +} + +func MLSEnabled() bool { + return false +} + +func SPLparCapable() bool { + return false +} + +func SPLparEnabled() bool { + return false +} + +func DedicatedLpar() bool { + return false +} + +func SPLparCapped() bool { + return false +} + +func SPLparDonating() bool { + return false +} + +func SmtCapable() bool { + return false +} + +func SmtEnabled() bool { + return false +} + +func VrmCapable() bool { + return false +} + +func VrmEnabled() bool { + return false +} + +func AmeEnabled() bool { + return false +} + +func EcoCapable() bool { + return false +} + +func EcoEnabled() bool { + return false +} + +func BootTime() (uint64, error) { + return 0, fmt.Errorf("Not implemented") +} + +func UptimeSeconds() (uint64, error) { + return 0, fmt.Errorf("Not implemented") +} + +func FileSystemStat() ([]FileSystem, error) { + return nil, fmt.Errorf("Not implemented") +} diff --git a/vendor/github.com/power-devops/perfstat/fsstat.go b/vendor/github.com/power-devops/perfstat/fsstat.go new file mode 100644 index 000000000..27f4c06c1 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/fsstat.go @@ -0,0 +1,31 @@ +// +build aix + +package perfstat + +/* +#include "c_helpers.h" +*/ +import "C" + +import ( + "fmt" +) + +func FileSystemStat() ([]FileSystem, error) { + var fsinfo *C.struct_fsinfo + var nmounts C.int + + fsinfo = C.get_all_fs(&nmounts) + if nmounts <= 0 { + return nil, fmt.Errorf("No mounts found") + } + + fs := make([]FileSystem, nmounts) + for i := 0; i < int(nmounts); i++ { + f := C.get_filesystem_stat(fsinfo, C.int(i)) + if f != nil { + fs[i] = fsinfo2filesystem(f) + } + } + return fs, nil +} diff --git a/vendor/github.com/power-devops/perfstat/helpers.go b/vendor/github.com/power-devops/perfstat/helpers.go new file mode 100644 index 000000000..e8d699766 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/helpers.go @@ -0,0 +1,764 @@ +// +build aix + +package perfstat + +/* +#cgo LDFLAGS: -lperfstat + +#include +#include + +#include "c_helpers.h" +*/ +import "C" + +func perfstatcpu2cpu(n *C.perfstat_cpu_t) CPU { + var c CPU + c.Name = C.GoString(&n.name[0]) + c.User = int64(n.user) + c.Sys = int64(n.sys) + c.Idle = int64(n.idle) + c.Wait = int64(n.wait) + c.PSwitch = int64(n.pswitch) + c.Syscall = int64(n.syscall) + c.Sysread = int64(n.sysread) + c.Syswrite = int64(n.syswrite) + c.Sysfork = int64(n.sysfork) + c.Sysexec = int64(n.sysexec) + c.Readch = int64(n.readch) + c.Writech = int64(n.writech) + c.Bread = int64(n.bread) + c.Bwrite = int64(n.bwrite) + c.Lread = int64(n.lread) + c.Lwrite = int64(n.lwrite) + c.Phread = int64(n.phread) + c.Phwrite = int64(n.phwrite) + c.Iget = int64(n.iget) + c.Namei = int64(n.namei) + c.Dirblk = int64(n.dirblk) + c.Msg = int64(n.msg) + c.Sema = int64(n.sema) + c.MinFaults = int64(n.minfaults) + c.MajFaults = int64(n.majfaults) + c.PUser = int64(n.puser) + c.PSys = int64(n.psys) + c.PIdle = int64(n.pidle) + c.PWait = int64(n.pwait) + c.RedispSD0 = int64(n.redisp_sd0) + c.RedispSD1 = int64(n.redisp_sd1) + c.RedispSD2 = int64(n.redisp_sd2) + c.RedispSD3 = int64(n.redisp_sd3) + c.RedispSD4 = int64(n.redisp_sd4) + c.RedispSD5 = int64(n.redisp_sd5) + c.MigrationPush = int64(n.migration_push) + c.MigrationS3grq = int64(n.migration_S3grq) + c.MigrationS3pul = int64(n.migration_S3pul) + c.InvolCSwitch = int64(n.invol_cswitch) + c.VolCSwitch = int64(n.vol_cswitch) + c.RunQueue = int64(n.runque) + c.Bound = int64(n.bound) + c.DecrIntrs = int64(n.decrintrs) + c.MpcRIntrs = int64(n.mpcrintrs) + c.MpcSIntrs = int64(n.mpcsintrs) + c.SoftIntrs = int64(n.softintrs) + c.DevIntrs = int64(n.devintrs) + c.PhantIntrs = int64(n.phantintrs) + c.IdleDonatedPurr = int64(n.idle_donated_purr) + c.IdleDonatedSpurr = int64(n.idle_donated_spurr) + c.BusyDonatedPurr = int64(n.busy_donated_purr) + c.BusyDonatedSpurr = int64(n.busy_donated_spurr) + c.IdleStolenPurr = int64(n.idle_stolen_purr) + c.IdleStolenSpurr = int64(n.idle_stolen_spurr) + c.BusyStolenPurr = int64(n.busy_stolen_purr) + c.BusyStolenSpurr = int64(n.busy_stolen_spurr) + c.Hpi = int64(n.hpi) + c.Hpit = int64(n.hpit) + c.PUserSpurr = int64(n.puser_spurr) + c.PSysSpurr = int64(n.psys_spurr) + c.PIdleSpurr = int64(n.pidle_spurr) + c.PWaitSpurr = int64(n.pwait_spurr) + c.SpurrFlag = int32(n.spurrflag) + c.LocalDispatch = int64(n.localdispatch) + c.NearDispatch = int64(n.neardispatch) + c.FarDispatch = int64(n.fardispatch) + c.CSwitches = int64(n.cswitches) + c.Version = int64(n.version) + c.TbLast = int64(n.tb_last) + c.State = int(n.state) + c.VtbLast = int64(n.vtb_last) + c.ICountLast = int64(n.icount_last) + return c +} + +func perfstatcputotal2cputotal(n *C.perfstat_cpu_total_t) CPUTotal { + var c CPUTotal + c.NCpus = int(n.ncpus) + c.NCpusCfg = int(n.ncpus_cfg) + c.Description = C.GoString(&n.description[0]) + c.ProcessorHz = int64(n.processorHZ) + c.User = int64(n.user) + c.Sys = int64(n.sys) + c.Idle = int64(n.idle) + c.Wait = int64(n.wait) + c.PSwitch = int64(n.pswitch) + c.Syscall = int64(n.syscall) + c.Sysread = int64(n.sysread) + c.Syswrite = int64(n.syswrite) + c.Sysfork = int64(n.sysfork) + c.Sysexec = int64(n.sysexec) + c.Readch = int64(n.readch) + c.Writech = int64(n.writech) + c.DevIntrs = int64(n.devintrs) + c.SoftIntrs = int64(n.softintrs) + c.Lbolt = int64(n.lbolt) + c.LoadAvg1 = (float32(n.loadavg[0]) / (1 << C.SBITS)) + c.LoadAvg5 = (float32(n.loadavg[1]) / (1 << C.SBITS)) + c.LoadAvg15 = (float32(n.loadavg[2]) / (1 << C.SBITS)) + c.RunQueue = int64(n.runque) + c.SwpQueue = int64(n.swpque) + c.Bread = int64(n.bread) + c.Bwrite = int64(n.bwrite) + c.Lread = int64(n.lread) + c.Lwrite = int64(n.lwrite) + c.Phread = int64(n.phread) + c.Phwrite = int64(n.phwrite) + c.RunOcc = int64(n.runocc) + c.SwpOcc = int64(n.swpocc) + c.Iget = int64(n.iget) + c.Namei = int64(n.namei) + c.Dirblk = int64(n.dirblk) + c.Msg = int64(n.msg) + c.Sema = int64(n.sema) + c.RcvInt = int64(n.rcvint) + c.XmtInt = int64(n.xmtint) + c.MdmInt = int64(n.mdmint) + c.TtyRawInch = int64(n.tty_rawinch) + c.TtyCanInch = int64(n.tty_caninch) + c.TtyRawOutch = int64(n.tty_rawoutch) + c.Ksched = int64(n.ksched) + c.Koverf = int64(n.koverf) + c.Kexit = int64(n.kexit) + c.Rbread = int64(n.rbread) + c.Rcread = int64(n.rcread) + c.Rbwrt = int64(n.rbwrt) + c.Rcwrt = int64(n.rcwrt) + c.Traps = int64(n.traps) + c.NCpusHigh = int64(n.ncpus_high) + c.PUser = int64(n.puser) + c.PSys = int64(n.psys) + c.PIdle = int64(n.pidle) + c.PWait = int64(n.pwait) + c.DecrIntrs = int64(n.decrintrs) + c.MpcRIntrs = int64(n.mpcrintrs) + c.MpcSIntrs = int64(n.mpcsintrs) + c.PhantIntrs = int64(n.phantintrs) + c.IdleDonatedPurr = int64(n.idle_donated_purr) + c.IdleDonatedSpurr = int64(n.idle_donated_spurr) + c.BusyDonatedPurr = int64(n.busy_donated_purr) + c.BusyDonatedSpurr = int64(n.busy_donated_spurr) + c.IdleStolenPurr = int64(n.idle_stolen_purr) + c.IdleStolenSpurr = int64(n.idle_stolen_spurr) + c.BusyStolenPurr = int64(n.busy_stolen_purr) + c.BusyStolenSpurr = int64(n.busy_stolen_spurr) + c.IOWait = int32(n.iowait) + c.PhysIO = int32(n.physio) + c.TWait = int64(n.twait) + c.Hpi = int64(n.hpi) + c.Hpit = int64(n.hpit) + c.PUserSpurr = int64(n.puser_spurr) + c.PSysSpurr = int64(n.psys_spurr) + c.PIdleSpurr = int64(n.pidle_spurr) + c.PWaitSpurr = int64(n.pwait_spurr) + c.SpurrFlag = int(n.spurrflag) + c.Version = int64(n.version) + c.TbLast = int64(n.tb_last) + c.PurrCoalescing = int64(n.purr_coalescing) + c.SpurrCoalescing = int64(n.spurr_coalescing) + return c +} + +func perfstatcpuutil2cpuutil(n *C.perfstat_cpu_util_t) CPUUtil { + var c CPUUtil + + c.Version = int64(n.version) + c.CpuID = C.GoString(&n.cpu_id[0]) + c.Entitlement = float32(n.entitlement) + c.UserPct = float32(n.user_pct) + c.KernPct = float32(n.kern_pct) + c.IdlePct = float32(n.idle_pct) + c.WaitPct = float32(n.wait_pct) + c.PhysicalBusy = float32(n.physical_busy) + c.PhysicalConsumed = float32(n.physical_consumed) + c.FreqPct = float32(n.freq_pct) + c.EntitlementPct = float32(n.entitlement_pct) + c.BusyPct = float32(n.busy_pct) + c.IdleDonatedPct = float32(n.idle_donated_pct) + c.BusyDonatedPct = float32(n.busy_donated_pct) + c.IdleStolenPct = float32(n.idle_stolen_pct) + c.BusyStolenPct = float32(n.busy_stolen_pct) + c.LUserPct = float32(n.l_user_pct) + c.LKernPct = float32(n.l_kern_pct) + c.LIdlePct = float32(n.l_idle_pct) + c.LWaitPct = float32(n.l_wait_pct) + c.DeltaTime = int64(n.delta_time) + + return c +} + +func perfstatdisktotal2disktotal(n C.perfstat_disk_total_t) DiskTotal { + var d DiskTotal + + d.Number = int32(n.number) + d.Size = int64(n.size) + d.Free = int64(n.free) + d.XRate = int64(n.xrate) + d.Xfers = int64(n.xfers) + d.Wblks = int64(n.wblks) + d.Rblks = int64(n.rblks) + d.Time = int64(n.time) + d.Version = int64(n.version) + d.Rserv = int64(n.rserv) + d.MinRserv = int64(n.min_rserv) + d.MaxRserv = int64(n.max_rserv) + d.RTimeOut = int64(n.rtimeout) + d.RFailed = int64(n.rfailed) + d.Wserv = int64(n.wserv) + d.MinWserv = int64(n.min_wserv) + d.MaxWserv = int64(n.max_wserv) + d.WTimeOut = int64(n.wtimeout) + d.WFailed = int64(n.wfailed) + d.WqDepth = int64(n.wq_depth) + d.WqTime = int64(n.wq_time) + d.WqMinTime = int64(n.wq_min_time) + d.WqMaxTime = int64(n.wq_max_time) + + return d +} + +func perfstatdiskadapter2diskadapter(n *C.perfstat_diskadapter_t) DiskAdapter { + var d DiskAdapter + + d.Name = C.GoString(&n.name[0]) + d.Description = C.GoString(&n.description[0]) + d.Number = int32(n.number) + d.Size = int64(n.size) + d.Free = int64(n.free) + d.XRate = int64(n.xrate) + d.Xfers = int64(n.xfers) + d.Rblks = int64(n.rblks) + d.Wblks = int64(n.wblks) + d.Time = int64(n.time) + d.Version = int64(n.version) + d.AdapterType = int64(n.adapter_type) + d.DkBSize = int64(n.dk_bsize) + d.DkRserv = int64(n.dk_rserv) + d.DkWserv = int64(n.dk_wserv) + d.MinRserv = int64(n.min_rserv) + d.MaxRserv = int64(n.max_rserv) + d.MinWserv = int64(n.min_wserv) + d.MaxWserv = int64(n.max_wserv) + d.WqDepth = int64(n.wq_depth) + d.WqSampled = int64(n.wq_sampled) + d.WqTime = int64(n.wq_time) + d.WqMinTime = int64(n.wq_min_time) + d.WqMaxTime = int64(n.wq_max_time) + d.QFull = int64(n.q_full) + d.QSampled = int64(n.q_sampled) + + return d +} + +func perfstatpartitionconfig2partitionconfig(n C.perfstat_partition_config_t) PartitionConfig { + var p PartitionConfig + p.Version = int64(n.version) + p.Name = C.GoString(&n.partitionname[0]) + p.Node = C.GoString(&n.nodename[0]) + p.Conf.SmtCapable = (n.conf[0] & (1 << 7)) > 0 + p.Conf.SmtEnabled = (n.conf[0] & (1 << 6)) > 0 + p.Conf.LparCapable = (n.conf[0] & (1 << 5)) > 0 + p.Conf.LparEnabled = (n.conf[0] & (1 << 4)) > 0 + p.Conf.SharedCapable = (n.conf[0] & (1 << 3)) > 0 + p.Conf.SharedEnabled = (n.conf[0] & (1 << 2)) > 0 + p.Conf.DLparCapable = (n.conf[0] & (1 << 1)) > 0 + p.Conf.Capped = (n.conf[0] & (1 << 0)) > 0 + p.Conf.Kernel64bit = (n.conf[1] & (1 << 7)) > 0 + p.Conf.PoolUtilAuthority = (n.conf[1] & (1 << 6)) > 0 + p.Conf.DonateCapable = (n.conf[1] & (1 << 5)) > 0 + p.Conf.DonateEnabled = (n.conf[1] & (1 << 4)) > 0 + p.Conf.AmsCapable = (n.conf[1] & (1 << 3)) > 0 + p.Conf.AmsEnabled = (n.conf[1] & (1 << 2)) > 0 + p.Conf.PowerSave = (n.conf[1] & (1 << 1)) > 0 + p.Conf.AmeEnabled = (n.conf[1] & (1 << 0)) > 0 + p.Conf.SharedExtended = (n.conf[2] & (1 << 7)) > 0 + p.Number = int32(n.partitionnum) + p.GroupID = int32(n.groupid) + p.ProcessorFamily = C.GoString(&n.processorFamily[0]) + p.ProcessorModel = C.GoString(&n.processorModel[0]) + p.MachineID = C.GoString(&n.machineID[0]) + p.ProcessorMhz = float64(C.get_partition_mhz(n)) + p.NumProcessors.Online = int64(n.numProcessors.online) + p.NumProcessors.Max = int64(n.numProcessors.max) + p.NumProcessors.Min = int64(n.numProcessors.min) + p.NumProcessors.Desired = int64(n.numProcessors.desired) + p.OSName = C.GoString(&n.OSName[0]) + p.OSVersion = C.GoString(&n.OSVersion[0]) + p.OSBuild = C.GoString(&n.OSBuild[0]) + p.LCpus = int32(n.lcpus) + p.SmtThreads = int32(n.smtthreads) + p.Drives = int32(n.drives) + p.NetworkAdapters = int32(n.nw_adapters) + p.CpuCap.Online = int64(n.cpucap.online) + p.CpuCap.Max = int64(n.cpucap.max) + p.CpuCap.Min = int64(n.cpucap.min) + p.CpuCap.Desired = int64(n.cpucap.desired) + p.Weightage = int32(n.cpucap_weightage) + p.EntCapacity = int32(n.entitled_proc_capacity) + p.VCpus.Online = int64(n.vcpus.online) + p.VCpus.Max = int64(n.vcpus.max) + p.VCpus.Min = int64(n.vcpus.min) + p.VCpus.Desired = int64(n.vcpus.desired) + p.PoolID = int32(n.processor_poolid) + p.ActiveCpusInPool = int32(n.activecpusinpool) + p.PoolWeightage = int32(n.cpupool_weightage) + p.SharedPCpu = int32(n.sharedpcpu) + p.MaxPoolCap = int32(n.maxpoolcap) + p.EntPoolCap = int32(n.entpoolcap) + p.Mem.Online = int64(n.mem.online) + p.Mem.Max = int64(n.mem.max) + p.Mem.Min = int64(n.mem.min) + p.Mem.Desired = int64(n.mem.desired) + p.MemWeightage = int32(n.mem_weightage) + p.TotalIOMemoryEntitlement = int64(n.totiomement) + p.MemPoolID = int32(n.mempoolid) + p.HyperPgSize = int64(n.hyperpgsize) + p.ExpMem.Online = int64(n.exp_mem.online) + p.ExpMem.Max = int64(n.exp_mem.max) + p.ExpMem.Min = int64(n.exp_mem.min) + p.ExpMem.Desired = int64(n.exp_mem.desired) + p.TargetMemExpFactor = int64(n.targetmemexpfactor) + p.TargetMemExpSize = int64(n.targetmemexpsize) + p.SubProcessorMode = int32(n.subprocessor_mode) + return p +} + +func perfstatmemorytotal2memorytotal(n C.perfstat_memory_total_t) MemoryTotal { + var m MemoryTotal + m.VirtualTotal = int64(n.virt_total) + m.RealTotal = int64(n.real_total) + m.RealFree = int64(n.real_free) + m.RealPinned = int64(n.real_pinned) + m.RealInUse = int64(n.real_inuse) + m.BadPages = int64(n.pgbad) + m.PageFaults = int64(n.pgexct) + m.PageIn = int64(n.pgins) + m.PageOut = int64(n.pgouts) + m.PgSpIn = int64(n.pgspins) + m.PgSpOut = int64(n.pgspouts) + m.Scans = int64(n.scans) + m.Cycles = int64(n.cycles) + m.PgSteals = int64(n.pgsteals) + m.NumPerm = int64(n.numperm) + m.PgSpTotal = int64(n.pgsp_total) + m.PgSpFree = int64(n.pgsp_free) + m.PgSpRsvd = int64(n.pgsp_rsvd) + m.RealSystem = int64(n.real_system) + m.RealUser = int64(n.real_user) + m.RealProcess = int64(n.real_process) + m.VirtualActive = int64(n.virt_active) + m.IOME = int64(n.iome) + m.IOMU = int64(n.iomu) + m.IOHWM = int64(n.iohwm) + m.PMem = int64(n.pmem) + m.CompressedTotal = int64(n.comprsd_total) + m.CompressedWSegPg = int64(n.comprsd_wseg_pgs) + m.CPgIn = int64(n.cpgins) + m.CPgOut = int64(n.cpgouts) + m.TrueSize = int64(n.true_size) + m.ExpandedMemory = int64(n.expanded_memory) + m.CompressedWSegSize = int64(n.comprsd_wseg_size) + m.TargetCPoolSize = int64(n.target_cpool_size) + m.MaxCPoolSize = int64(n.max_cpool_size) + m.MinUCPoolSize = int64(n.min_ucpool_size) + m.CPoolSize = int64(n.cpool_size) + m.UCPoolSize = int64(n.ucpool_size) + m.CPoolInUse = int64(n.cpool_inuse) + m.UCPoolInUse = int64(n.ucpool_inuse) + m.Version = int64(n.version) + m.RealAvailable = int64(n.real_avail) + m.BytesCoalesced = int64(n.bytes_coalesced) + m.BytesCoalescedMemPool = int64(n.bytes_coalesced_mempool) + + return m +} + +func perfstatnetinterfacetotal2netifacetotal(n C.perfstat_netinterface_total_t) NetIfaceTotal { + var i NetIfaceTotal + + i.Number = int32(n.number) + i.IPackets = int64(n.ipackets) + i.IBytes = int64(n.ibytes) + i.IErrors = int64(n.ierrors) + i.OPackets = int64(n.opackets) + i.OBytes = int64(n.obytes) + i.OErrors = int64(n.oerrors) + i.Collisions = int64(n.collisions) + i.XmitDrops = int64(n.xmitdrops) + i.Version = int64(n.version) + + return i +} + +func perfstatdisk2disk(n *C.perfstat_disk_t) Disk { + var d Disk + + d.Name = C.GoString(&n.name[0]) + d.Description = C.GoString(&n.description[0]) + d.VGName = C.GoString(&n.vgname[0]) + d.Size = int64(n.size) + d.Free = int64(n.free) + d.BSize = int64(n.bsize) + d.XRate = int64(n.xrate) + d.Xfers = int64(n.xfers) + d.Wblks = int64(n.wblks) + d.Rblks = int64(n.rblks) + d.QDepth = int64(n.qdepth) + d.Time = int64(n.time) + d.Adapter = C.GoString(&n.adapter[0]) + d.PathsCount = int32(n.paths_count) + d.QFull = int64(n.q_full) + d.Rserv = int64(n.rserv) + d.RTimeOut = int64(n.rtimeout) + d.Rfailed = int64(n.rfailed) + d.MinRserv = int64(n.min_rserv) + d.MaxRserv = int64(n.max_rserv) + d.Wserv = int64(n.wserv) + d.WTimeOut = int64(n.wtimeout) + d.Wfailed = int64(n.wfailed) + d.MinWserv = int64(n.min_wserv) + d.MaxWserv = int64(n.max_wserv) + d.WqDepth = int64(n.wq_depth) + d.WqSampled = int64(n.wq_sampled) + d.WqTime = int64(n.wq_time) + d.WqMinTime = int64(n.wq_min_time) + d.WqMaxTime = int64(n.wq_max_time) + d.QSampled = int64(n.q_sampled) + d.Version = int64(n.version) + d.PseudoDisk = (n.dk_type[0] & (1 << 7)) > 0 + d.VTDisk = (n.dk_type[0] & (1 << 6)) > 0 + + return d +} + +func perfstatdiskpath2diskpath(n *C.perfstat_diskpath_t) DiskPath { + var d DiskPath + + d.Name = C.GoString(&n.name[0]) + d.XRate = int64(n.xrate) + d.Xfers = int64(n.xfers) + d.Rblks = int64(n.rblks) + d.Wblks = int64(n.wblks) + d.Time = int64(n.time) + d.Adapter = C.GoString(&n.adapter[0]) + d.QFull = int64(n.q_full) + d.Rserv = int64(n.rserv) + d.RTimeOut = int64(n.rtimeout) + d.Rfailed = int64(n.rfailed) + d.MinRserv = int64(n.min_rserv) + d.MaxRserv = int64(n.max_rserv) + d.Wserv = int64(n.wserv) + d.WTimeOut = int64(n.wtimeout) + d.Wfailed = int64(n.wfailed) + d.MinWserv = int64(n.min_wserv) + d.MaxWserv = int64(n.max_wserv) + d.WqDepth = int64(n.wq_depth) + d.WqSampled = int64(n.wq_sampled) + d.WqTime = int64(n.wq_time) + d.WqMinTime = int64(n.wq_min_time) + d.WqMaxTime = int64(n.wq_max_time) + d.QSampled = int64(n.q_sampled) + d.Version = int64(n.version) + + return d +} + +func perfstatfcstat2fcadapter(n *C.perfstat_fcstat_t) FCAdapter { + var f FCAdapter + + f.Version = int64(n.version) + f.Name = C.GoString(&n.name[0]) + f.State = int32(n.state) + f.InputRequests = int64(n.InputRequests) + f.OutputRequests = int64(n.OutputRequests) + f.InputBytes = int64(n.InputBytes) + f.OutputBytes = int64(n.OutputBytes) + f.EffMaxTransfer = int64(n.EffMaxTransfer) + f.NoDMAResourceCnt = int64(n.NoDMAResourceCnt) + f.NoCmdResourceCnt = int64(n.NoCmdResourceCnt) + f.AttentionType = int32(n.AttentionType) + f.SecondsSinceLastReset = int64(n.SecondsSinceLastReset) + f.TxFrames = int64(n.TxFrames) + f.TxWords = int64(n.TxWords) + f.RxFrames = int64(n.RxFrames) + f.RxWords = int64(n.RxWords) + f.LIPCount = int64(n.LIPCount) + f.NOSCount = int64(n.NOSCount) + f.ErrorFrames = int64(n.ErrorFrames) + f.DumpedFrames = int64(n.DumpedFrames) + f.LinkFailureCount = int64(n.LinkFailureCount) + f.LossofSyncCount = int64(n.LossofSyncCount) + f.LossofSignal = int64(n.LossofSignal) + f.PrimitiveSeqProtocolErrCount = int64(n.PrimitiveSeqProtocolErrCount) + f.InvalidTxWordCount = int64(n.InvalidTxWordCount) + f.InvalidCRCCount = int64(n.InvalidCRCCount) + f.PortFcId = int64(n.PortFcId) + f.PortSpeed = int64(n.PortSpeed) + f.PortType = C.GoString(&n.PortType[0]) + f.PortWWN = int64(n.PortWWN) + f.PortSupportedSpeed = int64(n.PortSupportedSpeed) + f.AdapterType = int(n.adapter_type) + f.VfcName = C.GoString(&n.vfc_name[0]) + f.ClientPartName = C.GoString(&n.client_part_name[0]) + + return f +} + +func perfstatlogicalvolume2logicalvolume(n *C.perfstat_logicalvolume_t) LogicalVolume { + var l LogicalVolume + + l.Name = C.GoString(&n.name[0]) + l.VGName = C.GoString(&n.vgname[0]) + l.OpenClose = int64(n.open_close) + l.State = int64(n.state) + l.MirrorPolicy = int64(n.mirror_policy) + l.MirrorWriteConsistency = int64(n.mirror_write_consistency) + l.WriteVerify = int64(n.write_verify) + l.PPsize = int64(n.ppsize) + l.LogicalPartitions = int64(n.logical_partitions) + l.Mirrors = int32(n.mirrors) + l.IOCnt = int64(n.iocnt) + l.KBReads = int64(n.kbreads) + l.KBWrites = int64(n.kbwrites) + l.Version = int64(n.version) + + return l +} + +func perfstatvolumegroup2volumegroup(n *C.perfstat_volumegroup_t) VolumeGroup { + var v VolumeGroup + + v.Name = C.GoString(&n.name[0]) + v.TotalDisks = int64(n.total_disks) + v.ActiveDisks = int64(n.active_disks) + v.TotalLogicalVolumes = int64(n.total_logical_volumes) + v.OpenedLogicalVolumes = int64(n.opened_logical_volumes) + v.IOCnt = int64(n.iocnt) + v.KBReads = int64(n.kbreads) + v.KBWrites = int64(n.kbwrites) + v.Version = int64(n.version) + v.VariedState = int(n.variedState) + + return v +} + +func perfstatmemorypage2memorypage(n *C.perfstat_memory_page_t) MemoryPage { + var m MemoryPage + + m.PSize = int64(n.psize) + m.RealTotal = int64(n.real_total) + m.RealFree = int64(n.real_free) + m.RealPinned = int64(n.real_pinned) + m.RealInUse = int64(n.real_inuse) + m.PgExct = int64(n.pgexct) + m.PgIns = int64(n.pgins) + m.PgOuts = int64(n.pgouts) + m.PgSpIns = int64(n.pgspins) + m.PgSpOuts = int64(n.pgspouts) + m.Scans = int64(n.scans) + m.Cycles = int64(n.cycles) + m.PgSteals = int64(n.pgsteals) + m.NumPerm = int64(n.numperm) + m.NumPgSp = int64(n.numpgsp) + m.RealSystem = int64(n.real_system) + m.RealUser = int64(n.real_user) + m.RealProcess = int64(n.real_process) + m.VirtActive = int64(n.virt_active) + m.ComprsdTotal = int64(n.comprsd_total) + m.ComprsdWsegPgs = int64(n.comprsd_wseg_pgs) + m.CPgIns = int64(n.cpgins) + m.CPgOuts = int64(n.cpgouts) + m.CPoolInUse = int64(n.cpool_inuse) + m.UCPoolSize = int64(n.ucpool_size) + m.ComprsdWsegSize = int64(n.comprsd_wseg_size) + m.Version = int64(n.version) + m.RealAvail = int64(n.real_avail) + + return m +} + +func perfstatnetbuffer2netbuffer(n *C.perfstat_netbuffer_t) NetBuffer { + var b NetBuffer + + b.Name = C.GoString(&n.name[0]) + b.InUse = int64(n.inuse) + b.Calls = int64(n.calls) + b.Delayed = int64(n.delayed) + b.Free = int64(n.free) + b.Failed = int64(n.failed) + b.HighWatermark = int64(n.highwatermark) + b.Freed = int64(n.freed) + b.Version = int64(n.version) + + return b +} + +func perfstatnetinterface2netiface(n *C.perfstat_netinterface_t) NetIface { + var i NetIface + + i.Name = C.GoString(&n.name[0]) + i.Description = C.GoString(&n.description[0]) + i.Type = uint8(n._type) + i.MTU = int64(n.mtu) + i.IPackets = int64(n.ipackets) + i.IBytes = int64(n.ibytes) + i.IErrors = int64(n.ierrors) + i.OPackets = int64(n.opackets) + i.OBytes = int64(n.obytes) + i.OErrors = int64(n.oerrors) + i.Collisions = int64(n.collisions) + i.Bitrate = int64(n.bitrate) + i.XmitDrops = int64(n.xmitdrops) + i.Version = int64(n.version) + i.IfIqDrops = int64(n.if_iqdrops) + i.IfArpDrops = int64(n.if_arpdrops) + + return i +} + +func perfstatnetadapter2netadapter(n *C.perfstat_netadapter_t) NetAdapter { + var i NetAdapter + + i.Version = int64(n.version) + i.Name = C.GoString(&n.name[0]) + i.TxPackets = int64(n.tx_packets) + i.TxBytes = int64(n.tx_bytes) + i.TxInterrupts = int64(n.tx_interrupts) + i.TxErrors = int64(n.tx_errors) + i.TxPacketsDropped = int64(n.tx_packets_dropped) + i.TxQueueSize = int64(n.tx_queue_size) + i.TxQueueLen = int64(n.tx_queue_len) + i.TxQueueOverflow = int64(n.tx_queue_overflow) + i.TxBroadcastPackets = int64(n.tx_broadcast_packets) + i.TxMulticastPackets = int64(n.tx_multicast_packets) + i.TxCarrierSense = int64(n.tx_carrier_sense) + i.TxDMAUnderrun = int64(n.tx_DMA_underrun) + i.TxLostCTSErrors = int64(n.tx_lost_CTS_errors) + i.TxMaxCollisionErrors = int64(n.tx_max_collision_errors) + i.TxLateCollisionErrors = int64(n.tx_late_collision_errors) + i.TxDeferred = int64(n.tx_deferred) + i.TxTimeoutErrors = int64(n.tx_timeout_errors) + i.TxSingleCollisionCount = int64(n.tx_single_collision_count) + i.TxMultipleCollisionCount = int64(n.tx_multiple_collision_count) + i.RxPackets = int64(n.rx_packets) + i.RxBytes = int64(n.rx_bytes) + i.RxInterrupts = int64(n.rx_interrupts) + i.RxErrors = int64(n.rx_errors) + i.RxPacketsDropped = int64(n.rx_packets_dropped) + i.RxBadPackets = int64(n.rx_bad_packets) + i.RxMulticastPackets = int64(n.rx_multicast_packets) + i.RxBroadcastPackets = int64(n.rx_broadcast_packets) + i.RxCRCErrors = int64(n.rx_CRC_errors) + i.RxDMAOverrun = int64(n.rx_DMA_overrun) + i.RxAlignmentErrors = int64(n.rx_alignment_errors) + i.RxNoResourceErrors = int64(n.rx_noresource_errors) + i.RxCollisionErrors = int64(n.rx_collision_errors) + i.RxPacketTooShortErrors = int64(n.rx_packet_tooshort_errors) + i.RxPacketTooLongErrors = int64(n.rx_packet_toolong_errors) + i.RxPacketDiscardedByAdapter = int64(n.rx_packets_discardedbyadapter) + i.AdapterType = int32(n.adapter_type) + + return i +} + +func perfstatpagingspace2pagingspace(n *C.perfstat_pagingspace_t) PagingSpace { + var i PagingSpace + + i.Name = C.GoString(&n.name[0]) + i.Type = uint8(n._type) + i.VGName = C.GoString(C.get_ps_vgname(n)) + i.Hostname = C.GoString(C.get_ps_hostname(n)) + i.Filename = C.GoString(C.get_ps_filename(n)) + i.LPSize = int64(n.lp_size) + i.MBSize = int64(n.mb_size) + i.MBUsed = int64(n.mb_used) + i.IOPending = int64(n.io_pending) + i.Active = uint8(n.active) + i.Automatic = uint8(n.automatic) + i.Version = int64(n.version) + + return i +} + +func perfstatprocess2process(n *C.perfstat_process_t) Process { + var i Process + + i.Version = int64(n.version) + i.PID = int64(n.pid) + i.ProcessName = C.GoString(&n.proc_name[0]) + i.Priority = int32(n.proc_priority) + i.NumThreads = int64(n.num_threads) + i.UID = int64(n.proc_uid) + i.ClassID = int64(n.proc_classid) + i.Size = int64(n.proc_size) + i.RealMemData = int64(n.proc_real_mem_data) + i.RealMemText = int64(n.proc_real_mem_text) + i.VirtMemData = int64(n.proc_virt_mem_data) + i.VirtMemText = int64(n.proc_virt_mem_text) + i.SharedLibDataSize = int64(n.shared_lib_data_size) + i.HeapSize = int64(n.heap_size) + i.RealInUse = int64(n.real_inuse) + i.VirtInUse = int64(n.virt_inuse) + i.Pinned = int64(n.pinned) + i.PgSpInUse = int64(n.pgsp_inuse) + i.FilePages = int64(n.filepages) + i.RealInUseMap = int64(n.real_inuse_map) + i.VirtInUseMap = int64(n.virt_inuse_map) + i.PinnedInUseMap = int64(n.pinned_inuse_map) + i.UCpuTime = float64(n.ucpu_time) + i.SCpuTime = float64(n.scpu_time) + i.LastTimeBase = int64(n.last_timebase) + i.InBytes = int64(n.inBytes) + i.OutBytes = int64(n.outBytes) + i.InOps = int64(n.inOps) + i.OutOps = int64(n.outOps) + + return i +} + +func perfstatthread2thread(n *C.perfstat_thread_t) Thread { + var i Thread + + i.TID = int64(n.tid) + i.PID = int64(n.pid) + i.CpuID = int64(n.cpuid) + i.UCpuTime = float64(n.ucpu_time) + i.SCpuTime = float64(n.scpu_time) + i.LastTimeBase = int64(n.last_timebase) + i.Version = int64(n.version) + + return i +} + +func fsinfo2filesystem(n *C.struct_fsinfo) FileSystem { + var i FileSystem + + i.Device = C.GoString(n.devname) + i.MountPoint = C.GoString(n.fsname) + i.FSType = int(n.fstype) + i.Flags = int(n.flags) + i.TotalBlocks = int64(n.totalblks) + i.FreeBlocks = int64(n.freeblks) + i.TotalInodes = int64(n.totalinodes) + i.FreeInodes = int64(n.freeinodes) + + return i +} diff --git a/vendor/github.com/power-devops/perfstat/lparstat.go b/vendor/github.com/power-devops/perfstat/lparstat.go new file mode 100644 index 000000000..0ce35e3c5 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/lparstat.go @@ -0,0 +1,26 @@ +// +build aix + +package perfstat + +/* +#cgo LDFLAGS: -lperfstat + +#include +*/ +import "C" + +import ( + "fmt" +) + +func PartitionStat() (*PartitionConfig, error) { + var part C.perfstat_partition_config_t + + rc := C.perfstat_partition_config(nil, &part, C.sizeof_perfstat_partition_config_t, 1) + if rc != 1 { + return nil, fmt.Errorf("perfstat_partition_config() error") + } + p := perfstatpartitionconfig2partitionconfig(part) + return &p, nil + +} diff --git a/vendor/github.com/power-devops/perfstat/lvmstat.go b/vendor/github.com/power-devops/perfstat/lvmstat.go new file mode 100644 index 000000000..eb2064c80 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/lvmstat.go @@ -0,0 +1,72 @@ +// +build aix + +package perfstat + +/* +#cgo LDFLAGS: -lperfstat + +#include +#include +#include +#include "c_helpers.h" +*/ +import "C" + +import ( + "fmt" + "unsafe" +) + +func LogicalVolumeStat() ([]LogicalVolume, error) { + var lv *C.perfstat_logicalvolume_t + var lvname C.perfstat_id_t + + numlvs := C.perfstat_logicalvolume(nil, nil, C.sizeof_perfstat_logicalvolume_t, 0) + if numlvs <= 0 { + return nil, fmt.Errorf("perfstat_logicalvolume() error") + } + + lv_len := C.sizeof_perfstat_logicalvolume_t * C.ulong(numlvs) + lv = (*C.perfstat_logicalvolume_t)(C.malloc(lv_len)) + defer C.free(unsafe.Pointer(lv)) + C.strcpy(&lvname.name[0], C.CString("")) + r := C.perfstat_logicalvolume(&lvname, lv, C.sizeof_perfstat_logicalvolume_t, numlvs) + if r < 0 { + return nil, fmt.Errorf("perfstat_logicalvolume() error") + } + lvs := make([]LogicalVolume, r) + for i := 0; i < int(r); i++ { + l := C.get_logicalvolume_stat(lv, C.int(i)) + if l != nil { + lvs[i] = perfstatlogicalvolume2logicalvolume(l) + } + } + return lvs, nil +} + +func VolumeGroupStat() ([]VolumeGroup, error) { + var vg *C.perfstat_volumegroup_t + var vgname C.perfstat_id_t + + numvgs := C.perfstat_volumegroup(nil, nil, C.sizeof_perfstat_volumegroup_t, 0) + if numvgs <= 0 { + return nil, fmt.Errorf("perfstat_volumegroup() error") + } + + vg_len := C.sizeof_perfstat_volumegroup_t * C.ulong(numvgs) + vg = (*C.perfstat_volumegroup_t)(C.malloc(vg_len)) + defer C.free(unsafe.Pointer(vg)) + C.strcpy(&vgname.name[0], C.CString("")) + r := C.perfstat_volumegroup(&vgname, vg, C.sizeof_perfstat_volumegroup_t, numvgs) + if r < 0 { + return nil, fmt.Errorf("perfstat_volumegroup() error") + } + vgs := make([]VolumeGroup, r) + for i := 0; i < int(r); i++ { + v := C.get_volumegroup_stat(vg, C.int(i)) + if v != nil { + vgs[i] = perfstatvolumegroup2volumegroup(v) + } + } + return vgs, nil +} diff --git a/vendor/github.com/power-devops/perfstat/memstat.go b/vendor/github.com/power-devops/perfstat/memstat.go new file mode 100644 index 000000000..d211a73aa --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/memstat.go @@ -0,0 +1,84 @@ +// +build aix + +package perfstat + +/* +#cgo LDFLAGS: -lperfstat + +#include +#include +#include + +#include "c_helpers.h" +*/ +import "C" + +import ( + "fmt" + "unsafe" +) + +func MemoryTotalStat() (*MemoryTotal, error) { + var memory C.perfstat_memory_total_t + + rc := C.perfstat_memory_total(nil, &memory, C.sizeof_perfstat_memory_total_t, 1) + if rc != 1 { + return nil, fmt.Errorf("perfstat_memory_total() error") + } + m := perfstatmemorytotal2memorytotal(memory) + return &m, nil +} + +func MemoryPageStat() ([]MemoryPage, error) { + var mempage *C.perfstat_memory_page_t + var fps C.perfstat_psize_t + + numps := C.perfstat_memory_page(nil, nil, C.sizeof_perfstat_memory_page_t, 0) + if numps < 1 { + return nil, fmt.Errorf("perfstat_memory_page() error") + } + + mp_len := C.sizeof_perfstat_memory_page_t * C.ulong(numps) + mempage = (*C.perfstat_memory_page_t)(C.malloc(mp_len)) + defer C.free(unsafe.Pointer(mempage)) + fps.psize = C.FIRST_PSIZE + r := C.perfstat_memory_page(&fps, mempage, C.sizeof_perfstat_memory_page_t, numps) + if r < 1 { + return nil, fmt.Errorf("perfstat_memory_page() error") + } + ps := make([]MemoryPage, r) + for i := 0; i < int(r); i++ { + p := C.get_memory_page_stat(mempage, C.int(i)) + if p != nil { + ps[i] = perfstatmemorypage2memorypage(p) + } + } + return ps, nil +} + +func PagingSpaceStat() ([]PagingSpace, error) { + var pspace *C.perfstat_pagingspace_t + var fps C.perfstat_id_t + + numps := C.perfstat_pagingspace(nil, nil, C.sizeof_perfstat_pagingspace_t, 0) + if numps <= 0 { + return nil, fmt.Errorf("perfstat_pagingspace() error") + } + + ps_len := C.sizeof_perfstat_pagingspace_t * C.ulong(numps) + pspace = (*C.perfstat_pagingspace_t)(C.malloc(ps_len)) + defer C.free(unsafe.Pointer(pspace)) + C.strcpy(&fps.name[0], C.CString(C.FIRST_PAGINGSPACE)) + r := C.perfstat_pagingspace(&fps, pspace, C.sizeof_perfstat_pagingspace_t, numps) + if r < 1 { + return nil, fmt.Errorf("perfstat_pagingspace() error") + } + ps := make([]PagingSpace, r) + for i := 0; i < int(r); i++ { + p := C.get_pagingspace_stat(pspace, C.int(i)) + if p != nil { + ps[i] = perfstatpagingspace2pagingspace(p) + } + } + return ps, nil +} diff --git a/vendor/github.com/power-devops/perfstat/netstat.go b/vendor/github.com/power-devops/perfstat/netstat.go new file mode 100644 index 000000000..4070da211 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/netstat.go @@ -0,0 +1,117 @@ +// +build aix + +package perfstat + +/* +#cgo LDFLAGS: -lperfstat + +#include +#include +#include + +#include "c_helpers.h" +*/ +import "C" + +import ( + "fmt" + "unsafe" +) + +func NetIfaceTotalStat() (*NetIfaceTotal, error) { + var nif C.perfstat_netinterface_total_t + + rc := C.perfstat_netinterface_total(nil, &nif, C.sizeof_perfstat_netinterface_total_t, 1) + if rc != 1 { + return nil, fmt.Errorf("perfstat_netinterface_total() error") + } + n := perfstatnetinterfacetotal2netifacetotal(nif) + return &n, nil +} + +func NetBufferStat() ([]NetBuffer, error) { + var nbuf *C.perfstat_netbuffer_t + var first C.perfstat_id_t + + numbuf := C.perfstat_netbuffer(nil, nil, C.sizeof_perfstat_netbuffer_t, 0) + if numbuf < 1 { + return nil, fmt.Errorf("perfstat_netbuffer() error") + } + + nblen := C.sizeof_perfstat_netbuffer_t * C.ulong(numbuf) + nbuf = (*C.perfstat_netbuffer_t)(C.malloc(nblen)) + defer C.free(unsafe.Pointer(nbuf)) + C.strcpy(&first.name[0], C.CString(C.FIRST_NETBUFFER)) + r := C.perfstat_netbuffer(&first, nbuf, C.sizeof_perfstat_netbuffer_t, numbuf) + if r < 0 { + return nil, fmt.Errorf("perfstat_netbuffer() error") + } + nb := make([]NetBuffer, r) + for i := 0; i < int(r); i++ { + b := C.get_netbuffer_stat(nbuf, C.int(i)) + if b != nil { + nb[i] = perfstatnetbuffer2netbuffer(b) + } + } + return nb, nil +} + +func NetIfaceStat() ([]NetIface, error) { + var nif *C.perfstat_netinterface_t + var first C.perfstat_id_t + + numif := C.perfstat_netinterface(nil, nil, C.sizeof_perfstat_netinterface_t, 0) + if numif < 0 { + return nil, fmt.Errorf("perfstat_netinterface() error") + } + if numif == 0 { + return []NetIface{}, fmt.Errorf("no network interfaces found") + } + + iflen := C.sizeof_perfstat_netinterface_t * C.ulong(numif) + nif = (*C.perfstat_netinterface_t)(C.malloc(iflen)) + defer C.free(unsafe.Pointer(nif)) + C.strcpy(&first.name[0], C.CString(C.FIRST_NETINTERFACE)) + r := C.perfstat_netinterface(&first, nif, C.sizeof_perfstat_netinterface_t, numif) + if r < 0 { + return nil, fmt.Errorf("perfstat_netinterface() error") + } + ifs := make([]NetIface, r) + for i := 0; i < int(r); i++ { + b := C.get_netinterface_stat(nif, C.int(i)) + if b != nil { + ifs[i] = perfstatnetinterface2netiface(b) + } + } + return ifs, nil +} + +func NetAdapterStat() ([]NetAdapter, error) { + var adapters *C.perfstat_netadapter_t + var first C.perfstat_id_t + + numad := C.perfstat_netadapter(nil, nil, C.sizeof_perfstat_netadapter_t, 0) + if numad < 0 { + return nil, fmt.Errorf("perfstat_netadater() error") + } + if numad == 0 { + return []NetAdapter{}, fmt.Errorf("no network adapters found") + } + + adplen := C.sizeof_perfstat_netadapter_t * C.ulong(numad) + adapters = (*C.perfstat_netadapter_t)(C.malloc(adplen)) + defer C.free(unsafe.Pointer(adapters)) + C.strcpy(&first.name[0], C.CString(C.FIRST_NETINTERFACE)) + r := C.perfstat_netadapter(&first, adapters, C.sizeof_perfstat_netadapter_t, numad) + if r < 0 { + return nil, fmt.Errorf("perfstat_netadapter() error") + } + ads := make([]NetAdapter, r) + for i := 0; i < int(r); i++ { + b := C.get_netadapter_stat(adapters, C.int(i)) + if b != nil { + ads[i] = perfstatnetadapter2netadapter(b) + } + } + return ads, nil +} diff --git a/vendor/github.com/power-devops/perfstat/procstat.go b/vendor/github.com/power-devops/perfstat/procstat.go new file mode 100644 index 000000000..ecafebd8d --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/procstat.go @@ -0,0 +1,75 @@ +// +build aix + +package perfstat + +/* +#cgo LDFLAGS: -lperfstat + +#include +#include +#include + +#include "c_helpers.h" +*/ +import "C" + +import ( + "fmt" + "unsafe" +) + +func ProcessStat() ([]Process, error) { + var proc *C.perfstat_process_t + var first C.perfstat_id_t + + numproc := C.perfstat_process(nil, nil, C.sizeof_perfstat_process_t, 0) + if numproc < 1 { + return nil, fmt.Errorf("perfstat_process() error") + } + + plen := C.sizeof_perfstat_process_t * C.ulong(numproc) + proc = (*C.perfstat_process_t)(C.malloc(plen)) + defer C.free(unsafe.Pointer(proc)) + C.strcpy(&first.name[0], C.CString("")) + r := C.perfstat_process(&first, proc, C.sizeof_perfstat_process_t, numproc) + if r < 0 { + return nil, fmt.Errorf("perfstat_process() error") + } + + ps := make([]Process, r) + for i := 0; i < int(r); i++ { + p := C.get_process_stat(proc, C.int(i)) + if p != nil { + ps[i] = perfstatprocess2process(p) + } + } + return ps, nil +} + +func ThreadStat() ([]Thread, error) { + var thread *C.perfstat_thread_t + var first C.perfstat_id_t + + numthr := C.perfstat_thread(nil, nil, C.sizeof_perfstat_thread_t, 0) + if numthr < 1 { + return nil, fmt.Errorf("perfstat_thread() error") + } + + thlen := C.sizeof_perfstat_thread_t * C.ulong(numthr) + thread = (*C.perfstat_thread_t)(C.malloc(thlen)) + defer C.free(unsafe.Pointer(thread)) + C.strcpy(&first.name[0], C.CString("")) + r := C.perfstat_thread(&first, thread, C.sizeof_perfstat_thread_t, numthr) + if r < 0 { + return nil, fmt.Errorf("perfstat_thread() error") + } + + th := make([]Thread, r) + for i := 0; i < int(r); i++ { + t := C.get_thread_stat(thread, C.int(i)) + if t != nil { + th[i] = perfstatthread2thread(t) + } + } + return th, nil +} diff --git a/vendor/github.com/power-devops/perfstat/sysconf.go b/vendor/github.com/power-devops/perfstat/sysconf.go new file mode 100644 index 000000000..c7454d03d --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/sysconf.go @@ -0,0 +1,195 @@ +// +build aix + +package perfstat + +/* +#include +*/ +import "C" + +import "fmt" + +const ( + SC_ARG_MAX = 0 + SC_CHILD_MAX = 1 + SC_CLK_TCK = 2 + SC_NGROUPS_MAX = 3 + SC_OPEN_MAX = 4 + SC_STREAM_MAX = 5 + SC_TZNAME_MAX = 6 + SC_JOB_CONTROL = 7 + SC_SAVED_IDS = 8 + SC_VERSION = 9 + SC_POSIX_ARG_MAX = 10 + SC_POSIX_CHILD_MAX = 11 + SC_POSIX_LINK_MAX = 12 + SC_POSIX_MAX_CANON = 13 + SC_POSIX_MAX_INPUT = 14 + SC_POSIX_NAME_MAX = 15 + SC_POSIX_NGROUPS_MAX = 16 + SC_POSIX_OPEN_MAX = 17 + SC_POSIX_PATH_MAX = 18 + SC_POSIX_PIPE_BUF = 19 + SC_POSIX_SSIZE_MAX = 20 + SC_POSIX_STREAM_MAX = 21 + SC_POSIX_TZNAME_MAX = 22 + SC_BC_BASE_MAX = 23 + SC_BC_DIM_MAX = 24 + SC_BC_SCALE_MAX = 25 + SC_BC_STRING_MAX = 26 + SC_EQUIV_CLASS_MAX = 27 + SC_EXPR_NEST_MAX = 28 + SC_LINE_MAX = 29 + SC_RE_DUP_MAX = 30 + SC_2_VERSION = 31 + SC_2_C_DEV = 32 + SC_2_FORT_DEV = 33 + SC_2_FORT_RUN = 34 + SC_2_LOCALEDEF = 35 + SC_2_SW_DEV = 36 + SC_POSIX2_BC_BASE_MAX = 37 + SC_POSIX2_BC_DIM_MAX = 38 + SC_POSIX2_BC_SCALE_MAX = 39 + SC_POSIX2_BC_STRING_MAX = 40 + SC_POSIX2_BC_EQUIV_CLASS_MAX = 41 + SC_POSIX2_BC_EXPR_NEST_MAX = 42 + SC_POSIX2_BC_LINE_MAX = 43 + SC_POSIX2_BC_RE_DUP_MAX = 44 + SC_PASS_MAX = 45 + SC_XOPEN_VERSION = 46 + SC_ATEXIT_MAX = 47 + SC_PAGE_SIZE = 48 + SC_PAGESIZE = SC_PAGE_SIZE + SC_AES_OS_VERSION = 49 + SC_COLL_WEIGHTS_MAX = 50 + SC_2_C_WIND = 51 + SC_2_C_VERSION = 52 + SC_2_UPE = 53 + SC_2_CHAR_TERM = 54 + SC_XOPEN_SHM = 55 + SC_XOPEN_CRYPT = 56 + SC_XOPEN_ENH_I18N = 57 + SC_IOV_MAX = 58 + SC_THREAD_SAFE_FUNCTIONS = 59 + SC_THREADS = 60 + SC_THREAD_ATTR_STACKADDR = 61 + SC_THREAD_ATTR_STACKSIZE = 62 + SC_THREAD_FORKALL = 63 + SC_THREAD_PRIORITY_SCHEDULING = 64 + SC_THREAD_PRIO_INHERIT = 65 + SC_THREAD_PRIO_PROTECT = 66 + SC_THREAD_PROCESS_SHARED = 67 + SC_THREAD_KEYS_MAX = 68 + SC_THREAD_DATAKEYS_MAX = SC_THREAD_KEYS_MAX + SC_THREAD_STACK_MIN = 69 + SC_THREAD_THREADS_MAX = 70 + SC_NPROCESSORS_CONF = 71 + SC_NPROCESSORS_ONLN = 72 + SC_XOPEN_UNIX = 73 + SC_AIO_LISTIO_MAX = 75 + SC_AIO_MAX = 76 + SC_AIO_PRIO_DELTA_MAX = 77 + SC_ASYNCHRONOUS_IO = 78 + SC_DELAYTIMER_MAX = 79 + SC_FSYNC = 80 + SC_GETGR_R_SIZE_MAX = 81 + SC_GETPW_R_SIZE_MAX = 82 + SC_LOGIN_NAME_MAX = 83 + SC_MAPPED_FILES = 84 + SC_MEMLOCK = 85 + SC_MEMLOCK_RANGE = 86 + SC_MEMORY_PROTECTION = 87 + SC_MESSAGE_PASSING = 88 + SC_MQ_OPEN_MAX = 89 + SC_MQ_PRIO_MAX = 90 + SC_PRIORITIZED_IO = 91 + SC_PRIORITY_SCHEDULING = 92 + SC_REALTIME_SIGNALS = 93 + SC_RTSIG_MAX = 94 + SC_SEMAPHORES = 95 + SC_SEM_NSEMS_MAX = 96 + SC_SEM_VALUE_MAX = 97 + SC_SHARED_MEMORY_OBJECTS = 98 + SC_SIGQUEUE_MAX = 99 + SC_SYNCHRONIZED_IO = 100 + SC_THREAD_DESTRUCTOR_ITERATIONS = 101 + SC_TIMERS = 102 + SC_TIMER_MAX = 103 + SC_TTY_NAME_MAX = 104 + SC_XBS5_ILP32_OFF32 = 105 + SC_XBS5_ILP32_OFFBIG = 106 + SC_XBS5_LP64_OFF64 = 107 + SC_XBS5_LPBIG_OFFBIG = 108 + SC_XOPEN_XCU_VERSION = 109 + SC_XOPEN_REALTIME = 110 + SC_XOPEN_REALTIME_THREADS = 111 + SC_XOPEN_LEGACY = 112 + SC_REENTRANT_FUNCTIONS = SC_THREAD_SAFE_FUNCTIONS + SC_PHYS_PAGES = 113 + SC_AVPHYS_PAGES = 114 + SC_LPAR_ENABLED = 115 + SC_LARGE_PAGESIZE = 116 + SC_AIX_KERNEL_BITMODE = 117 + SC_AIX_REALMEM = 118 + SC_AIX_HARDWARE_BITMODE = 119 + SC_AIX_MP_CAPABLE = 120 + SC_V6_ILP32_OFF32 = 121 + SC_V6_ILP32_OFFBIG = 122 + SC_V6_LP64_OFF64 = 123 + SC_V6_LPBIG_OFFBIG = 124 + SC_XOPEN_STREAMS = 125 + SC_HOST_NAME_MAX = 126 + SC_REGEXP = 127 + SC_SHELL = 128 + SC_SYMLOOP_MAX = 129 + SC_ADVISORY_INFO = 130 + SC_FILE_LOCKING = 131 + SC_2_PBS = 132 + SC_2_PBS_ACCOUNTING = 133 + SC_2_PBS_CHECKPOINT = 134 + SC_2_PBS_LOCATE = 135 + SC_2_PBS_MESSAGE = 136 + SC_2_PBS_TRACK = 137 + SC_BARRIERS = 138 + SC_CLOCK_SELECTION = 139 + SC_CPUTIME = 140 + SC_MONOTONIC_CLOCK = 141 + SC_READER_WRITER_LOCKS = 142 + SC_SPAWN = 143 + SC_SPIN_LOCKS = 144 + SC_SPORADIC_SERVER = 145 + SC_THREAD_CPUTIME = 146 + SC_THREAD_SPORADIC_SERVER = 147 + SC_TIMEOUTS = 148 + SC_TRACE = 149 + SC_TRACE_EVENT_FILTER = 150 + SC_TRACE_INHERIT = 151 + SC_TRACE_LOG = 152 + SC_TYPED_MEMORY_OBJECTS = 153 + SC_IPV6 = 154 + SC_RAW_SOCKETS = 155 + SC_SS_REPL_MAX = 156 + SC_TRACE_EVENT_NAME_MAX = 157 + SC_TRACE_NAME_MAX = 158 + SC_TRACE_SYS_MAX = 159 + SC_TRACE_USER_EVENT_MAX = 160 + SC_AIX_UKEYS = 161 + SC_AIX_ENHANCED_AFFINITY = 162 + SC_V7_ILP32_OFF32 = 163 + SC_V7_ILP32_OFFBIG = 164 + SC_V7_LP64_OFF64 = 165 + SC_V7_LPBIG_OFFBIG = 166 + SC_THREAD_ROBUST_PRIO_INHERIT = 167 + SC_THREAD_ROBUST_PRIO_PROTECT = 168 + SC_XOPEN_UUCP = 169 + SC_XOPEN_ARMOR = 170 +) + +func Sysconf(name int32) (int64, error) { + r := C.sysconf(C.int(name)) + if r == -1 { + return 0, fmt.Errorf("sysconf error") + } else { + return int64(r), nil + } +} diff --git a/vendor/github.com/power-devops/perfstat/systemcfg.go b/vendor/github.com/power-devops/perfstat/systemcfg.go new file mode 100644 index 000000000..6287eb46a --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/systemcfg.go @@ -0,0 +1,635 @@ +// +build aix + +package perfstat + +import "golang.org/x/sys/unix" + +// function Getsystemcfg() is defined in golang.org/x/sys/unix +// we define here just missing constants for the function and some helpers + +// Calls to getsystemcfg() +const ( + SC_ARCH = 1 /* processor architecture */ + SC_IMPL = 2 /* processor implementation */ + SC_VERS = 3 /* processor version */ + SC_WIDTH = 4 /* width (32 || 64) */ + SC_NCPUS = 5 /* 1 = UP, n = n-way MP */ + SC_L1C_ATTR = 6 /* L1 cache attributes (bit flags) */ + SC_L1C_ISZ = 7 /* size of L1 instruction cache */ + SC_L1C_DSZ = 8 /* size of L1 data cache */ + SC_L1C_ICA = 9 /* L1 instruction cache associativity */ + SC_L1C_DCA = 10 /* L1 data cache associativity */ + SC_L1C_IBS = 11 /* L1 instruction cache block size */ + SC_L1C_DBS = 12 /* L1 data cache block size */ + SC_L1C_ILS = 13 /* L1 instruction cache line size */ + SC_L1C_DLS = 14 /* L1 data cache line size */ + SC_L2C_SZ = 15 /* size of L2 cache, 0 = No L2 cache */ + SC_L2C_AS = 16 /* L2 cache associativity */ + SC_TLB_ATTR = 17 /* TLB attributes (bit flags) */ + SC_ITLB_SZ = 18 /* entries in instruction TLB */ + SC_DTLB_SZ = 19 /* entries in data TLB */ + SC_ITLB_ATT = 20 /* instruction tlb associativity */ + SC_DTLB_ATT = 21 /* data tlb associativity */ + SC_RESRV_SZ = 22 /* size of reservation */ + SC_PRI_LC = 23 /* spin lock count in supevisor mode */ + SC_PRO_LC = 24 /* spin lock count in problem state */ + SC_RTC_TYPE = 25 /* RTC type */ + SC_VIRT_AL = 26 /* 1 if hardware aliasing is supported */ + SC_CAC_CONG = 27 /* number of page bits for cache synonym */ + SC_MOD_ARCH = 28 /* used by system for model determination */ + SC_MOD_IMPL = 29 /* used by system for model determination */ + SC_XINT = 30 /* used by system for time base conversion */ + SC_XFRAC = 31 /* used by system for time base conversion */ + SC_KRN_ATTR = 32 /* kernel attributes, see below */ + SC_PHYSMEM = 33 /* bytes of OS available memory */ + SC_SLB_ATTR = 34 /* SLB attributes */ + SC_SLB_SZ = 35 /* size of slb (0 = no slb) */ + SC_ORIG_NCPUS = 36 /* original number of CPUs */ + SC_MAX_NCPUS = 37 /* max cpus supported by this AIX image */ + SC_MAX_REALADDR = 38 /* max supported real memory address +1 */ + SC_ORIG_ENT_CAP = 39 /* configured entitled processor capacity at boot required by cross-partition LPAR tools. */ + SC_ENT_CAP = 40 /* entitled processor capacity */ + SC_DISP_WHE = 41 /* Dispatch wheel time period (TB units) */ + SC_CAPINC = 42 /* delta by which capacity can change */ + SC_VCAPW = 43 /* priority weight for idle capacity distribution */ + SC_SPLP_STAT = 44 /* State of SPLPAR enablement: 0x1 => 1=SPLPAR capable; 0=not, 0x2 => SPLPAR enabled 0=dedicated, 1=shared */ + SC_SMT_STAT = 45 /* State of SMT enablement: 0x1 = SMT Capable 0=no/1=yes, 0x2 = SMT Enabled 0=no/1=yes, 0x4 = SMT threads bound true 0=no/1=yes */ + SC_SMT_TC = 46 /* Number of SMT Threads per Physical CPU */ + SC_VMX_VER = 47 /* RPA defined VMX version: 0 = VMX not available or disabled, 1 = VMX capable, 2 = VMX and VSX capable */ + SC_LMB_SZ = 48 /* Size of an LMB on this system. */ + SC_MAX_XCPU = 49 /* Number of exclusive cpus on line */ + SC_EC_LVL = 50 /* Kernel error checking level */ + SC_AME_STAT = 51 /* AME status */ + SC_ECO_STAT = 52 /* extended cache options */ + SC_DFP_STAT = 53 /* RPA defined DFP version, 0=none/disabled */ + SC_VRM_STAT = 54 /* VRM Capable/enabled */ + SC_PHYS_IMP = 55 /* physical processor implementation */ + SC_PHYS_VER = 56 /* physical processor version */ + SC_SPCM_STATUS = 57 + SC_SPCM_MAX = 58 + SC_TM_VER = 59 /* Transaction Memory version, 0 - not capable */ + SC_NX_CAP = 60 /* NX GZIP capable */ + SC_PKS_STATE = 61 /* Platform KeyStore */ +) + +/* kernel attributes */ +/* bit 0/1 meaning */ +/* -----------------------------------------*/ +/* 31 32-bit kernel / 64-bit kernel */ +/* 30 non-LPAR / LPAR */ +/* 29 old 64bit ABI / 64bit Large ABI */ +/* 28 non-NUMA / NUMA */ +/* 27 UP / MP */ +/* 26 no DR CPU add / DR CPU add support */ +/* 25 no DR CPU rm / DR CPU rm support */ +/* 24 no DR MEM add / DR MEM add support */ +/* 23 no DR MEM rm / DR MEM rm support */ +/* 22 kernel keys disabled / enabled */ +/* 21 no recovery / recovery enabled */ +/* 20 non-MLS / MLS enabled */ +/* 19 enhanced affinity indicator */ +/* 18 non-vTPM / vTPM enabled */ +/* 17 non-VIOS / VIOS */ + +// Values for architecture field +const ( + ARCH_POWER_RS = 0x0001 /* Power Classic architecture */ + ARCH_POWER_PC = 0x0002 /* Power PC architecture */ + ARCH_IA64 = 0x0003 /* Intel IA64 architecture */ +) + +// Values for implementation field for POWER_PC Architectures +const ( + IMPL_POWER_RS1 = 0x00001 /* RS1 class CPU */ + IMPL_POWER_RSC = 0x00002 /* RSC class CPU */ + IMPL_POWER_RS2 = 0x00004 /* RS2 class CPU */ + IMPL_POWER_601 = 0x00008 /* 601 class CPU */ + IMPL_POWER_603 = 0x00020 /* 603 class CPU */ + IMPL_POWER_604 = 0x00010 /* 604 class CPU */ + IMPL_POWER_620 = 0x00040 /* 620 class CPU */ + IMPL_POWER_630 = 0x00080 /* 630 class CPU */ + IMPL_POWER_A35 = 0x00100 /* A35 class CPU */ + IMPL_POWER_RS64II = 0x0200 /* RS64-II class CPU */ + IMPL_POWER_RS64III = 0x0400 /* RS64-III class CPU */ + IMPL_POWER4 = 0x0800 /* 4 class CPU */ + IMPL_POWER_RS64IV = IMPL_POWER4 /* 4 class CPU */ + IMPL_POWER_MPC7450 = 0x1000 /* MPC7450 class CPU */ + IMPL_POWER5 = 0x2000 /* 5 class CPU */ + IMPL_POWER6 = 0x4000 /* 6 class CPU */ + IMPL_POWER7 = 0x8000 /* 7 class CPU */ + IMPL_POWER8 = 0x10000 /* 8 class CPU */ + IMPL_POWER9 = 0x20000 /* 9 class CPU */ +) + +// Values for implementation field for IA64 Architectures +const ( + IMPL_IA64_M1 = 0x0001 /* IA64 M1 class CPU (Itanium) */ + IMPL_IA64_M2 = 0x0002 /* IA64 M2 class CPU */ +) + +// Values for the version field +const ( + PV_601 = 0x010001 /* Power PC 601 */ + PV_601A = 0x010002 /* Power PC 601 */ + PV_603 = 0x060000 /* Power PC 603 */ + PV_604 = 0x050000 /* Power PC 604 */ + PV_620 = 0x070000 /* Power PC 620 */ + PV_630 = 0x080000 /* Power PC 630 */ + PV_A35 = 0x090000 /* Power PC A35 */ + PV_RS64II = 0x0A0000 /* Power PC RS64II */ + PV_RS64III = 0x0B0000 /* Power PC RS64III */ + PV_4 = 0x0C0000 /* Power PC 4 */ + PV_RS64IV = PV_4 /* Power PC 4 */ + PV_MPC7450 = 0x0D0000 /* Power PC MPC7450 */ + PV_4_2 = 0x0E0000 /* Power PC 4 */ + PV_4_3 = 0x0E0001 /* Power PC 4 */ + PV_5 = 0x0F0000 /* Power PC 5 */ + PV_5_2 = 0x0F0001 /* Power PC 5 */ + PV_5_3 = 0x0F0002 /* Power PC 5 */ + PV_6 = 0x100000 /* Power PC 6 */ + PV_6_1 = 0x100001 /* Power PC 6 DD1.x */ + PV_7 = 0x200000 /* Power PC 7 */ + PV_8 = 0x300000 /* Power PC 8 */ + PV_9 = 0x400000 /* Power PC 9 */ + PV_5_Compat = 0x0F8000 /* Power PC 5 */ + PV_6_Compat = 0x108000 /* Power PC 6 */ + PV_7_Compat = 0x208000 /* Power PC 7 */ + PV_8_Compat = 0x308000 /* Power PC 8 */ + PV_9_Compat = 0x408000 /* Power PC 9 */ + PV_RESERVED_2 = 0x0A0000 /* source compatability */ + PV_RESERVED_3 = 0x0B0000 /* source compatability */ + PV_RS2 = 0x040000 /* Power RS2 */ + PV_RS1 = 0x020000 /* Power RS1 */ + PV_RSC = 0x030000 /* Power RSC */ + PV_M1 = 0x008000 /* Intel IA64 M1 */ + PV_M2 = 0x008001 /* Intel IA64 M2 */ +) + +// Values for rtc_type +const ( + RTC_POWER = 1 /* rtc as defined by Power Arch. */ + RTC_POWER_PC = 2 /* rtc as defined by Power PC Arch. */ + RTC_IA64 = 3 /* rtc as defined by IA64 Arch. */ +) + +const NX_GZIP_PRESENT = 0x00000001 + +const ( + PKS_STATE_CAPABLE = 1 + PKS_STATE_ENABLED = 2 +) + +// Macros for identifying physical processor +const ( + PPI4_1 = 0x35 + PPI4_2 = 0x38 + PPI4_3 = 0x39 + PPI4_4 = 0x3C + PPI4_5 = 0x44 + PPI5_1 = 0x3A + PPI5_2 = 0x3B + PPI6_1 = 0x3E + PPI7_1 = 0x3F + PPI7_2 = 0x4A + PPI8_1 = 0x4B + PPI8_2 = 0x4D + PPI9 = 0x4E +) + +// Macros for kernel attributes +const ( + KERN_TYPE = 0x1 + KERN_LPAR = 0x2 + KERN_64BIT_LARGE_ABI = 0x4 + KERN_NUMA = 0x8 + KERN_UPMP = 0x10 + KERN_DR_CPU_ADD = 0x20 + KERN_DR_CPU_RM = 0x40 + KERN_DR_MEM_ADD = 0x80 + KERN_DR_MEM_RM = 0x100 + KERN_KKEY_ENABLED = 0x200 + KERN_RECOVERY = 0x400 + KERN_MLS = 0x800 + KERN_ENH_AFFINITY = 0x1000 + KERN_VTPM = 0x2000 + KERN_VIOS = 0x4000 +) + +// macros for SPLPAR environment. +const ( + SPLPAR_CAPABLE = 0x1 + SPLPAR_ENABLED = 0x2 + SPLPAR_DONATE_CAPABLE = 0x4 +) + +// Macros for SMT status determination +const ( + SMT_CAPABLE = 0x1 + SMT_ENABLE = 0x2 + SMT_BOUND = 0x4 + SMT_ORDER = 0x8 +) + +// Macros for VRM status determination +const ( + VRM_CAPABLE = 0x1 + VRM_ENABLE = 0x2 + CMOX_CAPABLE = 0x4 +) + +// Macros for AME status determination +const AME_ENABLE = 0x1 + +// Macros for extended cache options +const ( + ECO_CAPABLE = 0x1 + ECO_ENABLE = 0x2 +) + +// These define blocks of values for model_arch and model_impl that are reserved for OEM use. +const ( + MODEL_ARCH_RSPC = 2 + MODEL_ARCH_CHRP = 3 + MODEL_ARCH_IA64 = 4 + MODEL_ARCH_OEM_START = 1024 + MODEL_ARCH_OEM_END = 2047 + MODEL_IMPL_RS6K_UP_MCA = 1 + MODEL_IMPL_RS6K_SMP_MCA = 2 + MODEL_IMPL_RSPC_UP_PCI = 3 + MODEL_IMPL_RSPC_SMP_PCI = 4 + MODEL_IMPL_CHRP_UP_PCI = 5 + MODEL_IMPL_CHRP_SMP_PCI = 6 + MODEL_IMPL_IA64_COM = 7 + MODEL_IMPL_IA64_SOFTSDV = 8 + MODEL_IMPL_MAMBO_SIM = 9 + MODEL_IMPL_POWER_KVM = 10 + MODEL_IMPL_OEM_START = 1024 + MODEL_IMPL_OEM_END = 2047 +) + +// example determining processor compatibilty mode on AIX: +// impl := unix.Getsystemcfg(SC_IMPL) +// if impl&IMPL_POWER8 != 0 { +// // we are running on POWER8 +// } +// if impl&IMPL_POWER9 != 0 { +// // we are running on POWER9 +// } + +func GetCPUImplementation() string { + impl := unix.Getsystemcfg(SC_IMPL) + switch { + case impl&IMPL_POWER4 != 0: + return "POWER4" + case impl&IMPL_POWER5 != 0: + return "POWER5" + case impl&IMPL_POWER6 != 0: + return "POWER6" + case impl&IMPL_POWER7 != 0: + return "POWER7" + case impl&IMPL_POWER8 != 0: + return "POWER8" + case impl&IMPL_POWER9 != 0: + return "POWER9" + default: + return "Unknown" + } +} + +func POWER9OrNewer() bool { + impl := unix.Getsystemcfg(SC_IMPL) + if impl&IMPL_POWER9 != 0 { + return true + } + return false +} + +func POWER9() bool { + impl := unix.Getsystemcfg(SC_IMPL) + if impl&IMPL_POWER9 != 0 { + return true + } + return false +} + +func POWER8OrNewer() bool { + impl := unix.Getsystemcfg(SC_IMPL) + if impl&IMPL_POWER9 != 0 || impl&IMPL_POWER8 != 0 { + return true + } + return false +} + +func POWER8() bool { + impl := unix.Getsystemcfg(SC_IMPL) + if impl&IMPL_POWER8 != 0 { + return true + } + return false +} + +func POWER7OrNewer() bool { + impl := unix.Getsystemcfg(SC_IMPL) + if impl&IMPL_POWER9 != 0 || impl&IMPL_POWER8 != 0 || impl&IMPL_POWER7 != 0 { + return true + } + return false +} + +func POWER7() bool { + impl := unix.Getsystemcfg(SC_IMPL) + if impl&IMPL_POWER7 != 0 { + return true + } + return false +} + +func HasTransactionalMemory() bool { + impl := unix.Getsystemcfg(SC_TM_VER) + if impl > 0 { + return true + } + return false +} + +func Is64Bit() bool { + impl := unix.Getsystemcfg(SC_WIDTH) + if impl == 64 { + return true + } + return false +} + +func IsSMP() bool { + impl := unix.Getsystemcfg(SC_NCPUS) + if impl > 1 { + return true + } + return false +} + +func HasVMX() bool { + impl := unix.Getsystemcfg(SC_VMX_VER) + if impl > 0 { + return true + } + return false +} + +func HasVSX() bool { + impl := unix.Getsystemcfg(SC_VMX_VER) + if impl > 1 { + return true + } + return false +} + +func HasDFP() bool { + impl := unix.Getsystemcfg(SC_DFP_STAT) + if impl > 1 { + return true + } + return false +} + +func HasNxGzip() bool { + impl := unix.Getsystemcfg(SC_NX_CAP) + if impl&NX_GZIP_PRESENT > 0 { + return true + } + return false +} + +func PksCapable() bool { + impl := unix.Getsystemcfg(SC_PKS_STATE) + if impl&PKS_STATE_CAPABLE > 0 { + return true + } + return false +} + +func PksEnabled() bool { + impl := unix.Getsystemcfg(SC_PKS_STATE) + if impl&PKS_STATE_ENABLED > 0 { + return true + } + return false +} + +func CPUMode() string { + impl := unix.Getsystemcfg(SC_VERS) + switch impl { + case PV_9, PV_9_Compat: + return "POWER9" + case PV_8, PV_8_Compat: + return "POWER8" + case PV_7, PV_7_Compat: + return "POWER7" + default: + return "Unknown" + } +} + +func KernelBits() int { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_TYPE == KERN_TYPE { + return 64 + } + return 32 +} + +func IsLPAR() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_LPAR == KERN_LPAR { + return true + } + return false +} + +func CpuAddCapable() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_DR_CPU_ADD == KERN_DR_CPU_ADD { + return true + } + return false +} + +func CpuRemoveCapable() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_DR_CPU_RM == KERN_DR_CPU_RM { + return true + } + return false +} + +func MemoryAddCapable() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_DR_MEM_ADD == KERN_DR_MEM_ADD { + return true + } + return false +} + +func MemoryRemoveCapable() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_DR_MEM_RM == KERN_DR_MEM_RM { + return true + } + return false +} + +func DLparCapable() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&(KERN_DR_CPU_ADD|KERN_DR_CPU_RM|KERN_DR_MEM_ADD|KERN_DR_MEM_RM) > 0 { + return true + } + return false +} + +func IsNUMA() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_NUMA > 0 { + return true + } + return false +} + +func KernelKeys() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_KKEY_ENABLED > 0 { + return true + } + return false +} + +func RecoveryMode() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_RECOVERY > 0 { + return true + } + return false +} + +func EnhancedAffinity() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_ENH_AFFINITY > 0 { + return true + } + return false +} + +func VTpmEnabled() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_VTPM > 0 { + return true + } + return false +} + +func IsVIOS() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_VIOS > 0 { + return true + } + return false +} + +func MLSEnabled() bool { + impl := unix.Getsystemcfg(SC_KRN_ATTR) + if impl&KERN_MLS > 0 { + return true + } + return false +} + +func SPLparCapable() bool { + impl := unix.Getsystemcfg(SC_SPLP_STAT) + if impl&SPLPAR_CAPABLE > 0 { + return true + } + return false +} + +func SPLparEnabled() bool { + impl := unix.Getsystemcfg(SC_SPLP_STAT) + if impl&SPLPAR_ENABLED > 0 { + return true + } + return false +} + +func DedicatedLpar() bool { + return !SPLparEnabled() +} + +func SPLparCapped() bool { + impl := unix.Getsystemcfg(SC_VCAPW) + if impl == 0 { + return true + } + return false +} + +func SPLparDonating() bool { + impl := unix.Getsystemcfg(SC_SPLP_STAT) + if impl&SPLPAR_DONATE_CAPABLE > 0 { + return true + } + return false +} + +func SmtCapable() bool { + impl := unix.Getsystemcfg(SC_SMT_STAT) + if impl&SMT_CAPABLE > 0 { + return true + } + return false +} + +func SmtEnabled() bool { + impl := unix.Getsystemcfg(SC_SMT_STAT) + if impl&SMT_ENABLE > 0 { + return true + } + return false +} + +func VrmCapable() bool { + impl := unix.Getsystemcfg(SC_VRM_STAT) + if impl&VRM_CAPABLE > 0 { + return true + } + return false +} + +func VrmEnabled() bool { + impl := unix.Getsystemcfg(SC_VRM_STAT) + if impl&VRM_ENABLE > 0 { + return true + } + return false +} + +func AmeEnabled() bool { + impl := unix.Getsystemcfg(SC_AME_STAT) + if impl&AME_ENABLE > 0 { + return true + } + return false +} + +func EcoCapable() bool { + impl := unix.Getsystemcfg(SC_ECO_STAT) + if impl&ECO_CAPABLE > 0 { + return true + } + return false +} + +func EcoEnabled() bool { + impl := unix.Getsystemcfg(SC_ECO_STAT) + if impl&ECO_ENABLE > 0 { + return true + } + return false +} diff --git a/vendor/github.com/power-devops/perfstat/types_cpu.go b/vendor/github.com/power-devops/perfstat/types_cpu.go new file mode 100644 index 000000000..84425e92f --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/types_cpu.go @@ -0,0 +1,186 @@ +package perfstat + +type CPU struct { + Name string /* logical processor name (cpu0, cpu1, ..) */ + User int64 /* raw number of clock ticks spent in user mode */ + Sys int64 /* raw number of clock ticks spent in system mode */ + Idle int64 /* raw number of clock ticks spent idle */ + Wait int64 /* raw number of clock ticks spent waiting for I/O */ + PSwitch int64 /* number of context switches (changes of currently running process) */ + Syscall int64 /* number of system calls executed */ + Sysread int64 /* number of read system calls executed */ + Syswrite int64 /* number of write system calls executed */ + Sysfork int64 /* number of fork system call executed */ + Sysexec int64 /* number of exec system call executed */ + Readch int64 /* number of characters tranferred with read system call */ + Writech int64 /* number of characters tranferred with write system call */ + Bread int64 /* number of block reads */ + Bwrite int64 /* number of block writes */ + Lread int64 /* number of logical read requests */ + Lwrite int64 /* number of logical write requests */ + Phread int64 /* number of physical reads (reads on raw device) */ + Phwrite int64 /* number of physical writes (writes on raw device) */ + Iget int64 /* number of inode lookups */ + Namei int64 /* number of vnode lookup from a path name */ + Dirblk int64 /* number of 512-byte block reads by the directory search routine to locate an entry for a file */ + Msg int64 /* number of IPC message operations */ + Sema int64 /* number of IPC semaphore operations */ + MinFaults int64 /* number of page faults with no I/O */ + MajFaults int64 /* number of page faults with disk I/O */ + PUser int64 /* raw number of physical processor tics in user mode */ + PSys int64 /* raw number of physical processor tics in system mode */ + PIdle int64 /* raw number of physical processor tics idle */ + PWait int64 /* raw number of physical processor tics waiting for I/O */ + RedispSD0 int64 /* number of thread redispatches within the scheduler affinity domain 0 */ + RedispSD1 int64 /* number of thread redispatches within the scheduler affinity domain 1 */ + RedispSD2 int64 /* number of thread redispatches within the scheduler affinity domain 2 */ + RedispSD3 int64 /* number of thread redispatches within the scheduler affinity domain 3 */ + RedispSD4 int64 /* number of thread redispatches within the scheduler affinity domain 4 */ + RedispSD5 int64 /* number of thread redispatches within the scheduler affinity domain 5 */ + MigrationPush int64 /* number of thread migrations from the local runque to another queue due to starvation load balancing */ + MigrationS3grq int64 /* number of thread migrations from the global runque to the local runque resulting in a move accross scheduling domain 3 */ + MigrationS3pul int64 /* number of thread migrations from another processor's runque resulting in a move accross scheduling domain 3 */ + InvolCSwitch int64 /* number of involuntary thread context switches */ + VolCSwitch int64 /* number of voluntary thread context switches */ + RunQueue int64 /* number of threads on the runque */ + Bound int64 /* number of bound threads */ + DecrIntrs int64 /* number of decrementer tics interrupts */ + MpcRIntrs int64 /* number of mpc's received interrupts */ + MpcSIntrs int64 /* number of mpc's sent interrupts */ + DevIntrs int64 /* number of device interrupts */ + SoftIntrs int64 /* number of offlevel handlers called */ + PhantIntrs int64 /* number of phantom interrupts */ + IdleDonatedPurr int64 /* number of idle cycles donated by a dedicated partition enabled for donation */ + IdleDonatedSpurr int64 /* number of idle spurr cycles donated by a dedicated partition enabled for donation */ + BusyDonatedPurr int64 /* number of busy cycles donated by a dedicated partition enabled for donation */ + BusyDonatedSpurr int64 /* number of busy spurr cycles donated by a dedicated partition enabled for donation */ + IdleStolenPurr int64 /* number of idle cycles stolen by the hypervisor from a dedicated partition */ + IdleStolenSpurr int64 /* number of idle spurr cycles stolen by the hypervisor from a dedicated partition */ + BusyStolenPurr int64 /* number of busy cycles stolen by the hypervisor from a dedicated partition */ + BusyStolenSpurr int64 /* number of busy spurr cycles stolen by the hypervisor from a dedicated partition */ + Hpi int64 /* number of hypervisor page-ins */ + Hpit int64 /* Time spent in hypervisor page-ins (in nanoseconds)*/ + PUserSpurr int64 /* number of spurr cycles spent in user mode */ + PSysSpurr int64 /* number of spurr cycles spent in kernel mode */ + PIdleSpurr int64 /* number of spurr cycles spent in idle mode */ + PWaitSpurr int64 /* number of spurr cycles spent in wait mode */ + SpurrFlag int32 /* set if running in spurr mode */ + LocalDispatch int64 /* number of local thread dispatches on this logical CPU */ + NearDispatch int64 /* number of near thread dispatches on this logical CPU */ + FarDispatch int64 /* number of far thread dispatches on this logical CPU */ + CSwitches int64 /* Context switches */ + Version int64 /* version number (1, 2, etc.,) */ + TbLast int64 /* timebase counter */ + State int /* Show whether the CPU is offline or online */ + VtbLast int64 /* Last virtual timebase read */ + ICountLast int64 /* Last instruction count read */ +} + +type CPUTotal struct { + NCpus int /* number of active logical processors */ + NCpusCfg int /* number of configured processors */ + Description string /* processor description (type/official name) */ + ProcessorHz int64 /* processor speed in Hz */ + User int64 /* raw total number of clock ticks spent in user mode */ + Sys int64 /* raw total number of clock ticks spent in system mode */ + Idle int64 /* raw total number of clock ticks spent idle */ + Wait int64 /* raw total number of clock ticks spent waiting for I/O */ + PSwitch int64 /* number of process switches (change in currently running process) */ + Syscall int64 /* number of system calls executed */ + Sysread int64 /* number of read system calls executed */ + Syswrite int64 /* number of write system calls executed */ + Sysfork int64 /* number of forks system calls executed */ + Sysexec int64 /* number of execs system calls executed */ + Readch int64 /* number of characters tranferred with read system call */ + Writech int64 /* number of characters tranferred with write system call */ + DevIntrs int64 /* number of device interrupts */ + SoftIntrs int64 /* number of software interrupts */ + Lbolt int64 /* number of ticks since last reboot */ + LoadAvg1 float32 /* times the average number of runnables processes during the last 1, 5 and 15 minutes. */ + LoadAvg5 float32 /* times the average number of runnables processes during the last 1, 5 and 15 minutes. */ + LoadAvg15 float32 /* times the average number of runnables processes during the last 1, 5 and 15 minutes. */ + RunQueue int64 /* length of the run queue (processes ready) */ + SwpQueue int64 /* length of the swap queue (processes waiting to be paged in) */ + Bread int64 /* number of blocks read */ + Bwrite int64 /* number of blocks written */ + Lread int64 /* number of logical read requests */ + Lwrite int64 /* number of logical write requests */ + Phread int64 /* number of physical reads (reads on raw devices) */ + Phwrite int64 /* number of physical writes (writes on raw devices) */ + RunOcc int64 /* updated whenever runque is updated, i.e. the runqueue is occupied. This can be used to compute the simple average of ready processes */ + SwpOcc int64 /* updated whenever swpque is updated. i.e. the swpqueue is occupied. This can be used to compute the simple average processes waiting to be paged in */ + Iget int64 /* number of inode lookups */ + Namei int64 /* number of vnode lookup from a path name */ + Dirblk int64 /* number of 512-byte block reads by the directory search routine to locate an entry for a file */ + Msg int64 /* number of IPC message operations */ + Sema int64 /* number of IPC semaphore operations */ + RcvInt int64 /* number of tty receive interrupts */ + XmtInt int64 /* number of tyy transmit interrupts */ + MdmInt int64 /* number of modem interrupts */ + TtyRawInch int64 /* number of raw input characters */ + TtyCanInch int64 /* number of canonical input characters (always zero) */ + TtyRawOutch int64 /* number of raw output characters */ + Ksched int64 /* number of kernel processes created */ + Koverf int64 /* kernel process creation attempts where: -the user has forked to their maximum limit -the configuration limit of processes has been reached */ + Kexit int64 /* number of kernel processes that became zombies */ + Rbread int64 /* number of remote read requests */ + Rcread int64 /* number of cached remote reads */ + Rbwrt int64 /* number of remote writes */ + Rcwrt int64 /* number of cached remote writes */ + Traps int64 /* number of traps */ + NCpusHigh int64 /* index of highest processor online */ + PUser int64 /* raw number of physical processor tics in user mode */ + PSys int64 /* raw number of physical processor tics in system mode */ + PIdle int64 /* raw number of physical processor tics idle */ + PWait int64 /* raw number of physical processor tics waiting for I/O */ + DecrIntrs int64 /* number of decrementer tics interrupts */ + MpcRIntrs int64 /* number of mpc's received interrupts */ + MpcSIntrs int64 /* number of mpc's sent interrupts */ + PhantIntrs int64 /* number of phantom interrupts */ + IdleDonatedPurr int64 /* number of idle cycles donated by a dedicated partition enabled for donation */ + IdleDonatedSpurr int64 /* number of idle spurr cycles donated by a dedicated partition enabled for donation */ + BusyDonatedPurr int64 /* number of busy cycles donated by a dedicated partition enabled for donation */ + BusyDonatedSpurr int64 /* number of busy spurr cycles donated by a dedicated partition enabled for donation */ + IdleStolenPurr int64 /* number of idle cycles stolen by the hypervisor from a dedicated partition */ + IdleStolenSpurr int64 /* number of idle spurr cycles stolen by the hypervisor from a dedicated partition */ + BusyStolenPurr int64 /* number of busy cycles stolen by the hypervisor from a dedicated partition */ + BusyStolenSpurr int64 /* number of busy spurr cycles stolen by the hypervisor from a dedicated partition */ + IOWait int32 /* number of processes that are asleep waiting for buffered I/O */ + PhysIO int32 /* number of processes waiting for raw I/O */ + TWait int64 /* number of threads that are waiting for filesystem direct(cio) */ + Hpi int64 /* number of hypervisor page-ins */ + Hpit int64 /* Time spent in hypervisor page-ins (in nanoseconds) */ + PUserSpurr int64 /* number of spurr cycles spent in user mode */ + PSysSpurr int64 /* number of spurr cycles spent in kernel mode */ + PIdleSpurr int64 /* number of spurr cycles spent in idle mode */ + PWaitSpurr int64 /* number of spurr cycles spent in wait mode */ + SpurrFlag int /* set if running in spurr mode */ + Version int64 /* version number (1, 2, etc.,) */ + TbLast int64 /*time base counter */ + PurrCoalescing int64 /* If the calling partition is authorized to see pool wide statistics then PURR cycles consumed to coalesce data else set to zero.*/ + SpurrCoalescing int64 /* If the calling partition is authorized to see pool wide statistics then SPURR cycles consumed to coalesce data else set to zero. */ +} + +type CPUUtil struct { + Version int64 + CpuID string /* holds the id of the cpu */ + Entitlement float32 /* Partition's entitlement */ + UserPct float32 /* % of utilization in user mode */ + KernPct float32 /* % of utilization in kernel mode */ + IdlePct float32 /* % of utilization in idle mode */ + WaitPct float32 /* % of utilization in wait mode */ + PhysicalBusy float32 /* physical cpus busy */ + PhysicalConsumed float32 /* total cpus consumed by the partition */ + FreqPct float32 /* Average freq% over the last interval */ + EntitlementPct float32 /* % of entitlement used */ + BusyPct float32 /* % of entitlement busy */ + IdleDonatedPct float32 /* % idle cycles donated */ + BusyDonatedPct float32 /* % of busy cycles donated */ + IdleStolenPct float32 /* % idle cycles stolen */ + BusyStolenPct float32 /* % busy cycles stolen */ + LUserPct float32 /* % of utilization in user mode, in terms of logical processor ticks */ + LKernPct float32 /* % of utilization in kernel mode, in terms of logical processor ticks*/ + LIdlePct float32 /* % of utilization in idle mode, in terms of logical processor ticks */ + LWaitPct float32 /* % of utilization in wait mode, in terms of logical processor ticks */ + DeltaTime int64 /* delta time in milliseconds, for which utilization is evaluated */ +} diff --git a/vendor/github.com/power-devops/perfstat/types_disk.go b/vendor/github.com/power-devops/perfstat/types_disk.go new file mode 100644 index 000000000..ca1493d87 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/types_disk.go @@ -0,0 +1,176 @@ +package perfstat + +type DiskTotal struct { + Number int32 /* total number of disks */ + Size int64 /* total size of all disks (in MB) */ + Free int64 /* free portion of all disks (in MB) */ + XRate int64 /* __rxfers: total number of transfers from disk */ + Xfers int64 /* total number of transfers to/from disk */ + Wblks int64 /* 512 bytes blocks written to all disks */ + Rblks int64 /* 512 bytes blocks read from all disks */ + Time int64 /* amount of time disks are active */ + Version int64 /* version number (1, 2, etc.,) */ + Rserv int64 /* Average read or receive service time */ + MinRserv int64 /* min read or receive service time */ + MaxRserv int64 /* max read or receive service time */ + RTimeOut int64 /* number of read request timeouts */ + RFailed int64 /* number of failed read requests */ + Wserv int64 /* Average write or send service time */ + MinWserv int64 /* min write or send service time */ + MaxWserv int64 /* max write or send service time */ + WTimeOut int64 /* number of write request timeouts */ + WFailed int64 /* number of failed write requests */ + WqDepth int64 /* instantaneous wait queue depth (number of requests waiting to be sent to disk) */ + WqTime int64 /* accumulated wait queueing time */ + WqMinTime int64 /* min wait queueing time */ + WqMaxTime int64 /* max wait queueing time */ +} + +// Disk Adapter Types +const ( + DA_SCSI = 0 /* 0 ==> SCSI, SAS, other legacy adapter types */ + DA_VSCSI /* 1 ==> Virtual SCSI/SAS Adapter */ + DA_FCA /* 2 ==> Fiber Channel Adapter */ +) + +type DiskAdapter struct { + Name string /* name of the adapter (from ODM) */ + Description string /* adapter description (from ODM) */ + Number int32 /* number of disks connected to adapter */ + Size int64 /* total size of all disks (in MB) */ + Free int64 /* free portion of all disks (in MB) */ + XRate int64 /* __rxfers: total number of reads via adapter */ + Xfers int64 /* total number of transfers via adapter */ + Rblks int64 /* 512 bytes blocks written via adapter */ + Wblks int64 /* 512 bytes blocks read via adapter */ + Time int64 /* amount of time disks are active */ + Version int64 /* version number (1, 2, etc.,) */ + AdapterType int64 /* 0 ==> SCSI, SAS, other legacy adapter types, 1 ==> Virtual SCSI/SAS Adapter, 2 ==> Fiber Channel Adapter */ + DkBSize int64 /* Number of Bytes in a block for this disk*/ + DkRxfers int64 /* Number of transfers from disk */ + DkRserv int64 /* read or receive service time */ + DkWserv int64 /* write or send service time */ + MinRserv int64 /* Minimum read service time */ + MaxRserv int64 /* Maximum read service time */ + MinWserv int64 /* Minimum Write service time */ + MaxWserv int64 /* Maximum write service time */ + WqDepth int64 /* driver wait queue depth */ + WqSampled int64 /* accumulated sampled dk_wq_depth */ + WqTime int64 /* accumulated wait queueing time */ + WqMinTime int64 /* minimum wait queueing time */ + WqMaxTime int64 /* maximum wait queueing time */ + QFull int64 /* "Service" queue full occurrence count (number of times the adapter/devices connected to the adapter is not accepting any more request) */ + QSampled int64 /* accumulated sampled */ +} + +type Disk struct { + Name string /* name of the disk */ + Description string /* disk description (from ODM) */ + VGName string /* volume group name (from ODM) */ + Size int64 /* size of the disk (in MB) */ + Free int64 /* free portion of the disk (in MB) */ + BSize int64 /* disk block size (in bytes) */ + XRate int64 /* number of transfers from disk */ + Xfers int64 /* number of transfers to/from disk */ + Wblks int64 /* number of blocks written to disk */ + Rblks int64 /* number of blocks read from disk */ + QDepth int64 /* instantaneous "service" queue depth (number of requests sent to disk and not completed yet) */ + Time int64 /* amount of time disk is active */ + Adapter string /* disk adapter name */ + PathsCount int32 /* number of paths to this disk */ + QFull int64 /* "service" queue full occurrence count (number of times the disk is not accepting any more request) */ + Rserv int64 /* read or receive service time */ + RTimeOut int64 /* number of read request timeouts */ + Rfailed int64 /* number of failed read requests */ + MinRserv int64 /* min read or receive service time */ + MaxRserv int64 /* max read or receive service time */ + Wserv int64 /* write or send service time */ + WTimeOut int64 /* number of write request timeouts */ + Wfailed int64 /* number of failed write requests */ + MinWserv int64 /* min write or send service time */ + MaxWserv int64 /* max write or send service time */ + WqDepth int64 /* instantaneous wait queue depth (number of requests waiting to be sent to disk) */ + WqSampled int64 /* accumulated sampled dk_wq_depth */ + WqTime int64 /* accumulated wait queueing time */ + WqMinTime int64 /* min wait queueing time */ + WqMaxTime int64 /* max wait queueing time */ + QSampled int64 /* accumulated sampled dk_q_depth */ + Version int64 /* version number (1, 2, etc.,) */ + PseudoDisk bool /*Indicates whether pseudo or physical disk */ + VTDisk bool /* 1- Virtual Target Disk, 0 - Others */ +} + +type DiskPath struct { + Name string /* name of the path */ + XRate int64 /* __rxfers: number of reads via the path */ + Xfers int64 /* number of transfers via the path */ + Rblks int64 /* 512 bytes blocks written via the path */ + Wblks int64 /* 512 bytes blocks read via the path */ + Time int64 /* amount of time disks are active */ + Adapter string /* disk adapter name (from ODM) */ + QFull int64 /* "service" queue full occurrence count (number of times the disk is not accepting any more request) */ + Rserv int64 /* read or receive service time */ + RTimeOut int64 /* number of read request timeouts */ + Rfailed int64 /* number of failed read requests */ + MinRserv int64 /* min read or receive service time */ + MaxRserv int64 /* max read or receive service time */ + Wserv int64 /* write or send service time */ + WTimeOut int64 /* number of write request timeouts */ + Wfailed int64 /* number of failed write requests */ + MinWserv int64 /* min write or send service time */ + MaxWserv int64 /* max write or send service time */ + WqDepth int64 /* instantaneous wait queue depth (number of requests waiting to be sent to disk) */ + WqSampled int64 /* accumulated sampled dk_wq_depth */ + WqTime int64 /* accumulated wait queueing time */ + WqMinTime int64 /* min wait queueing time */ + WqMaxTime int64 /* max wait queueing time */ + QSampled int64 /* accumulated sampled dk_q_depth */ + Version int64 /* version number (1, 2, etc.,) */ +} + +const ( + FC_DOWN = 0 // FC Adapter state is DOWN + FC_UP = 1 // FC Adapter state is UP +) + +const ( + FCT_FCHBA = 0 // FC type - real Fiber Channel Adapter + FCT_VFC = 1 // FC type - virtual Fiber Channel +) + +type FCAdapter struct { + Version int64 /* version number (1, 2, etc.,) */ + Name string /* name of the adapter */ + State int32 /* FC Adapter state UP or DOWN */ + InputRequests int64 /* Number of Input Requests*/ + OutputRequests int64 /* Number of Output Requests */ + InputBytes int64 /* Number of Input Bytes */ + OutputBytes int64 /* Number of Output Bytes */ + EffMaxTransfer int64 /* Adapter's Effective Maximum Transfer Value */ + NoDMAResourceCnt int64 /* Count of DMA failures due to no DMA Resource available */ + NoCmdResourceCnt int64 /* Count of failures to allocate a command due to no command resource available */ + AttentionType int32 /* Link up or down Indicator */ + SecondsSinceLastReset int64 /* Displays the seconds since last reset of the statistics on the adapter */ + TxFrames int64 /* Number of frames transmitted */ + TxWords int64 /* Fiber Channel Kbytes transmitted */ + RxFrames int64 /* Number of Frames Received */ + RxWords int64 /* Fiber Channel Kbytes Received */ + LIPCount int64 /* Count of LIP (Loop Initialization Protocol) Events received in case we have FC-AL */ + NOSCount int64 /* Count of NOS (Not_Operational) Events. This indicates a link failure state. */ + ErrorFrames int64 /* Number of frames received with the CRC Error */ + DumpedFrames int64 /* Number of lost frames */ + LinkFailureCount int64 /* Count of Link failures */ + LossofSyncCount int64 /* Count of loss of sync */ + LossofSignal int64 /* Count of loss of Signal */ + PrimitiveSeqProtocolErrCount int64 /* number of times a primitive sequence was in error */ + InvalidTxWordCount int64 /* Count of Invalid Transmission words received */ + InvalidCRCCount int64 /* Count of CRC Errors in a Received Frame */ + PortFcId int64 /* SCSI Id of the adapter */ + PortSpeed int64 /* Speed of Adapter in GBIT */ + PortType string /* Type of connection. The Possible Values are Fabric, Private Loop, Point-to-Point, unknown */ + PortWWN int64 /* World Wide Port name */ + PortSupportedSpeed int64 /* Supported Port Speed in GBIT */ + AdapterType int /* 0 - Fiber Chanel, 1 - Virtual Fiber Chanel Adapter */ + VfcName string /* name of the Virtual Fiber Chanel(VFC) adapter */ + ClientPartName string /* name of the client partition */ +} diff --git a/vendor/github.com/power-devops/perfstat/types_fs.go b/vendor/github.com/power-devops/perfstat/types_fs.go new file mode 100644 index 000000000..0be048a38 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/types_fs.go @@ -0,0 +1,195 @@ +package perfstat + +import ( + "strings" +) + +type FileSystem struct { + Device string /* name of the mounted device */ + MountPoint string /* where the device is mounted */ + FSType int /* File system type, see the constants below */ + Flags int /* Flags of the file system */ + TotalBlocks int64 /* number of 512 bytes blocks in the filesystem */ + FreeBlocks int64 /* number of free 512 bytes block in the filesystem */ + TotalInodes int64 /* total number of inodes in the filesystem */ + FreeInodes int64 /* number of free inodes in the filesystem */ +} + +func (f *FileSystem) TypeString() string { + switch f.FSType { + case FS_JFS2: + return "jfs2" + case FS_NAMEFS: + return "namefs" + case FS_NFS: + return "nfs" + case FS_JFS: + return "jfs" + case FS_CDROM: + return "cdrfs" + case FS_PROCFS: + return "procfs" + case FS_SFS: + return "sfs" + case FS_CACHEFS: + return "cachefs" + case FS_NFS3: + return "nfs3" + case FS_AUTOFS: + return "autofs" + case FS_POOLFS: + return "poolfs" + case FS_VXFS: + return "vxfs" + case FS_VXODM: + return "vxodm" + case FS_UDF: + return "udfs" + case FS_NFS4: + return "nfs4" + case FS_RFS4: + return "rfs4" + case FS_CIFS: + return "cifs" + case FS_PMEMFS: + return "pmemfs" + case FS_AHAFS: + return "ahafs" + case FS_STNFS: + return "stnfs" + case FS_ASMFS: + return "asmfs" + } + return "unknown" +} + +func (f *FileSystem) FlagsString() string { + var flags []string + + switch { + case f.Flags&VFS_READONLY != 0: + flags = append(flags, "ro") + case f.Flags&VFS_REMOVABLE != 0: + flags = append(flags, "removable") + case f.Flags&VFS_DEVMOUNT != 0: + flags = append(flags, "local") + case f.Flags&VFS_REMOTE != 0: + flags = append(flags, "remote") + case f.Flags&VFS_SYSV_MOUNT != 0: + flags = append(flags, "sysv") + case f.Flags&VFS_UNMOUNTING != 0: + flags = append(flags, "unmounting") + case f.Flags&VFS_NOSUID != 0: + flags = append(flags, "nosuid") + case f.Flags&VFS_NODEV != 0: + flags = append(flags, "nodev") + case f.Flags&VFS_NOINTEG != 0: + flags = append(flags, "nointeg") + case f.Flags&VFS_NOMANAGER != 0: + flags = append(flags, "nomanager") + case f.Flags&VFS_NOCASE != 0: + flags = append(flags, "nocase") + case f.Flags&VFS_UPCASE != 0: + flags = append(flags, "upcase") + case f.Flags&VFS_NBC != 0: + flags = append(flags, "nbc") + case f.Flags&VFS_MIND != 0: + flags = append(flags, "mind") + case f.Flags&VFS_RBR != 0: + flags = append(flags, "rbr") + case f.Flags&VFS_RBW != 0: + flags = append(flags, "rbw") + case f.Flags&VFS_DISCONNECTED != 0: + flags = append(flags, "disconnected") + case f.Flags&VFS_SHUTDOWN != 0: + flags = append(flags, "shutdown") + case f.Flags&VFS_VMOUNTOK != 0: + flags = append(flags, "vmountok") + case f.Flags&VFS_SUSER != 0: + flags = append(flags, "suser") + case f.Flags&VFS_SOFT_MOUNT != 0: + flags = append(flags, "soft") + case f.Flags&VFS_UNMOUNTED != 0: + flags = append(flags, "unmounted") + case f.Flags&VFS_DEADMOUNT != 0: + flags = append(flags, "deadmount") + case f.Flags&VFS_SNAPSHOT != 0: + flags = append(flags, "snapshot") + case f.Flags&VFS_VCM_ON != 0: + flags = append(flags, "vcm_on") + case f.Flags&VFS_VCM_MONITOR != 0: + flags = append(flags, "vcm_monitor") + case f.Flags&VFS_ATIMEOFF != 0: + flags = append(flags, "noatime") + case f.Flags&VFS_READMOSTLY != 0: + flags = append(flags, "readmostly") + case f.Flags&VFS_CIOR != 0: + flags = append(flags, "cior") + case f.Flags&VFS_CIO != 0: + flags = append(flags, "cio") + case f.Flags&VFS_DIO != 0: + flags = append(flags, "dio") + } + + return strings.Join(flags, ",") +} + +// Filesystem types +const ( + FS_JFS2 = 0 /* AIX physical fs "jfs2" */ + FS_NAMEFS = 1 /* AIX pseudo fs "namefs" */ + FS_NFS = 2 /* SUN Network File System "nfs" */ + FS_JFS = 3 /* AIX R3 physical fs "jfs" */ + FS_CDROM = 5 /* CDROM File System "cdrom" */ + FS_PROCFS = 6 /* PROCFS File System "proc" */ + FS_SFS = 16 /* AIX Special FS (STREAM mounts) */ + FS_CACHEFS = 17 /* Cachefs file system */ + FS_NFS3 = 18 /* NFSv3 file system */ + FS_AUTOFS = 19 /* Automount file system */ + FS_POOLFS = 20 /* Pool file system */ + FS_VXFS = 32 /* THRPGIO File System "vxfs" */ + FS_VXODM = 33 /* For Veritas File System */ + FS_UDF = 34 /* UDFS file system */ + FS_NFS4 = 35 /* NFSv4 file system */ + FS_RFS4 = 36 /* NFSv4 Pseudo file system */ + FS_CIFS = 37 /* AIX SMBFS (CIFS client) */ + FS_PMEMFS = 38 /* MCR Async Mobility pseudo file system */ + FS_AHAFS = 39 /* AHAFS File System "aha" */ + FS_STNFS = 40 /* Short-Term NFS */ + FS_ASMFS = 41 /* Oracle ASM FS */ +) + +// Filesystem flags +const ( + VFS_READONLY = 0x00000001 /* rdonly access to vfs */ + VFS_REMOVABLE = 0x00000002 /* removable (diskette) media */ + VFS_DEVMOUNT = 0x00000004 /* physical device mount */ + VFS_REMOTE = 0x00000008 /* file system is on network */ + VFS_SYSV_MOUNT = 0x00000010 /* System V style mount */ + VFS_UNMOUNTING = 0x00000020 /* originated by unmount() */ + VFS_NOSUID = 0x00000040 /* don't maintain suid-ness across this mount */ + VFS_NODEV = 0x00000080 /* don't allow device access across this mount */ + VFS_NOINTEG = 0x00000100 /* no integrity mount option */ + VFS_NOMANAGER = 0x00000200 /* mount managed fs w/o manager */ + VFS_NOCASE = 0x00000400 /* do not map dir names */ + VFS_UPCASE = 0x00000800 /* map dir names to uppercase */ + VFS_NBC = 0x00001000 /* NBC cached file in this vfs */ + VFS_MIND = 0x00002000 /* multi-segment .indirect */ + VFS_RBR = 0x00004000 /* Release-behind when reading */ + VFS_RBW = 0x00008000 /* Release-behind when writing */ + VFS_DISCONNECTED = 0x00010000 /* file mount not in use */ + VFS_SHUTDOWN = 0x00020000 /* forced unmount for shutdown */ + VFS_VMOUNTOK = 0x00040000 /* dir/file mnt permission flag */ + VFS_SUSER = 0x00080000 /* client-side suser perm. flag */ + VFS_SOFT_MOUNT = 0x00100000 /* file-over-file or directory over directory "soft" mount */ + VFS_UNMOUNTED = 0x00200000 /* unmount completed, stale vnodes are left in the vfs */ + VFS_DEADMOUNT = 0x00400000 /* softmount vfs should be disconnected at last vnode free */ + VFS_SNAPSHOT = 0x00800000 /* snapshot mount */ + VFS_VCM_ON = 0x01000000 /* VCM is currently active */ + VFS_VCM_MONITOR = 0x02000000 /* VCM monitoring is active */ + VFS_ATIMEOFF = 0x04000000 /* no atime updates during i/o */ + VFS_READMOSTLY = 0x10000000 /* ROFS allows open for write */ + VFS_CIOR = 0x20000000 /* O_CIOR mount */ + VFS_CIO = 0x40000000 /* O_CIO mount */ + VFS_DIO = 0x80000000 /* O_DIRECT mount */ +) diff --git a/vendor/github.com/power-devops/perfstat/types_lpar.go b/vendor/github.com/power-devops/perfstat/types_lpar.go new file mode 100644 index 000000000..2d3c32fa8 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/types_lpar.go @@ -0,0 +1,68 @@ +package perfstat + +type PartitionType struct { + SmtCapable bool /* OS supports SMT mode */ + SmtEnabled bool /* SMT mode is on */ + LparCapable bool /* OS supports logical partitioning */ + LparEnabled bool /* logical partitioning is on */ + SharedCapable bool /* OS supports shared processor LPAR */ + SharedEnabled bool /* partition runs in shared mode */ + DLparCapable bool /* OS supports dynamic LPAR */ + Capped bool /* partition is capped */ + Kernel64bit bool /* kernel is 64 bit */ + PoolUtilAuthority bool /* pool utilization available */ + DonateCapable bool /* capable of donating cycles */ + DonateEnabled bool /* enabled for donating cycles */ + AmsCapable bool /* 1 = AMS(Active Memory Sharing) capable, 0 = Not AMS capable */ + AmsEnabled bool /* 1 = AMS(Active Memory Sharing) enabled, 0 = Not AMS enabled */ + PowerSave bool /*1= Power saving mode is enabled*/ + AmeEnabled bool /* Active Memory Expansion is enabled */ + SharedExtended bool +} + +type PartitionValue struct { + Online int64 + Max int64 + Min int64 + Desired int64 +} + +type PartitionConfig struct { + Version int64 /* Version number */ + Name string /* Partition Name */ + Node string /* Node Name */ + Conf PartitionType /* Partition Properties */ + Number int32 /* Partition Number */ + GroupID int32 /* Group ID */ + ProcessorFamily string /* Processor Type */ + ProcessorModel string /* Processor Model */ + MachineID string /* Machine ID */ + ProcessorMhz float64 /* Processor Clock Speed in MHz */ + NumProcessors PartitionValue /* Number of Configured Physical Processors in frame*/ + OSName string /* Name of Operating System */ + OSVersion string /* Version of operating System */ + OSBuild string /* Build of Operating System */ + LCpus int32 /* Number of Logical CPUs */ + SmtThreads int32 /* Number of SMT Threads */ + Drives int32 /* Total Number of Drives */ + NetworkAdapters int32 /* Total Number of Network Adapters */ + CpuCap PartitionValue /* Min, Max and Online CPU Capacity */ + Weightage int32 /* Variable Processor Capacity Weightage */ + EntCapacity int32 /* number of processor units this partition is entitled to receive */ + VCpus PartitionValue /* Min, Max and Online Virtual CPUs */ + PoolID int32 /* Shared Pool ID of physical processors, to which this partition belongs*/ + ActiveCpusInPool int32 /* Count of physical CPUs in the shared processor pool, to which this partition belongs */ + PoolWeightage int32 /* Pool Weightage */ + SharedPCpu int32 /* Number of physical processors allocated for shared processor use */ + MaxPoolCap int32 /* Maximum processor capacity of partition's pool */ + EntPoolCap int32 /* Entitled processor capacity of partition's pool */ + Mem PartitionValue /* Min, Max and Online Memory */ + MemWeightage int32 /* Variable Memory Capacity Weightage */ + TotalIOMemoryEntitlement int64 /* I/O Memory Entitlement of the partition in bytes */ + MemPoolID int32 /* AMS pool id of the pool the LPAR belongs to */ + HyperPgSize int64 /* Hypervisor page size in KB*/ + ExpMem PartitionValue /* Min, Max and Online Expanded Memory */ + TargetMemExpFactor int64 /* Target Memory Expansion Factor scaled by 100 */ + TargetMemExpSize int64 /* Expanded Memory Size in MB */ + SubProcessorMode int32 /* Split core mode, its value can be 0,1,2 or 4. 0 for unsupported, 1 for capable but not enabled, 2 or 4 for enabled*/ +} diff --git a/vendor/github.com/power-devops/perfstat/types_lvm.go b/vendor/github.com/power-devops/perfstat/types_lvm.go new file mode 100644 index 000000000..8f7176a61 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/types_lvm.go @@ -0,0 +1,31 @@ +package perfstat + +type LogicalVolume struct { + Name string /* logical volume name */ + VGName string /* volume group name */ + OpenClose int64 /* LVM_QLVOPEN, etc. (see lvm.h) */ + State int64 /* LVM_UNDEF, etc. (see lvm.h) */ + MirrorPolicy int64 /* LVM_PARALLEL, etc. (see lvm.h) */ + MirrorWriteConsistency int64 /* LVM_CONSIST, etc. (see lvm.h) */ + WriteVerify int64 /* LVM_VERIFY, etc. (see lvm.h) */ + PPsize int64 /* physical partition size in MB */ + LogicalPartitions int64 /* total number of logical paritions configured for this logical volume */ + Mirrors int32 /* number of physical mirrors for each logical partition */ + IOCnt int64 /* Number of read and write requests */ + KBReads int64 /* Number of Kilobytes read */ + KBWrites int64 /* Number of Kilobytes written */ + Version int64 /* version number (1, 2, etc.,) */ +} + +type VolumeGroup struct { + Name string /* volume group name */ + TotalDisks int64 /* number of physical volumes in the volume group */ + ActiveDisks int64 /* number of active physical volumes in the volume group */ + TotalLogicalVolumes int64 /* number of logical volumes in the volume group */ + OpenedLogicalVolumes int64 /* number of logical volumes opened in the volume group */ + IOCnt int64 /* Number of read and write requests */ + KBReads int64 /* Number of Kilobytes read */ + KBWrites int64 /* Number of Kilobytes written */ + Version int64 /* version number (1, 2, etc.,) */ + VariedState int /* Indicates volume group available or not */ +} diff --git a/vendor/github.com/power-devops/perfstat/types_memory.go b/vendor/github.com/power-devops/perfstat/types_memory.go new file mode 100644 index 000000000..096d29ad2 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/types_memory.go @@ -0,0 +1,101 @@ +package perfstat + +type MemoryTotal struct { + VirtualTotal int64 /* total virtual memory (in 4KB pages) */ + RealTotal int64 /* total real memory (in 4KB pages) */ + RealFree int64 /* free real memory (in 4KB pages) */ + RealPinned int64 /* real memory which is pinned (in 4KB pages) */ + RealInUse int64 /* real memory which is in use (in 4KB pages) */ + BadPages int64 /* number of bad pages */ + PageFaults int64 /* number of page faults */ + PageIn int64 /* number of pages paged in */ + PageOut int64 /* number of pages paged out */ + PgSpIn int64 /* number of page ins from paging space */ + PgSpOut int64 /* number of page outs from paging space */ + Scans int64 /* number of page scans by clock */ + Cycles int64 /* number of page replacement cycles */ + PgSteals int64 /* number of page steals */ + NumPerm int64 /* number of frames used for files (in 4KB pages) */ + PgSpTotal int64 /* total paging space (in 4KB pages) */ + PgSpFree int64 /* free paging space (in 4KB pages) */ + PgSpRsvd int64 /* reserved paging space (in 4KB pages) */ + RealSystem int64 /* real memory used by system segments (in 4KB pages). */ + RealUser int64 /* real memory used by non-system segments (in 4KB pages). */ + RealProcess int64 /* real memory used by process segments (in 4KB pages). */ + VirtualActive int64 /* Active virtual pages. Virtual pages are considered active if they have been accessed */ + IOME int64 /* I/O memory entitlement of the partition in bytes*/ + IOMU int64 /* I/O memory entitlement of the partition in use in bytes*/ + IOHWM int64 /* High water mark of I/O memory entitlement used in bytes*/ + PMem int64 /* Amount of physical mmeory currently backing partition's logical memory in bytes*/ + CompressedTotal int64 /* Total numbers of pages in compressed pool (in 4KB pages) */ + CompressedWSegPg int64 /* Number of compressed working storage pages */ + CPgIn int64 /* number of page ins to compressed pool */ + CPgOut int64 /* number of page outs from compressed pool */ + TrueSize int64 /* True Memory Size in 4KB pages */ + ExpandedMemory int64 /* Expanded Memory Size in 4KB pages */ + CompressedWSegSize int64 /* Total size of the compressed working storage pages in the pool */ + TargetCPoolSize int64 /* Target Compressed Pool Size in bytes */ + MaxCPoolSize int64 /* Max Size of Compressed Pool in bytes */ + MinUCPoolSize int64 /* Min Size of Uncompressed Pool in bytes */ + CPoolSize int64 /* Compressed Pool size in bytes */ + UCPoolSize int64 /* Uncompressed Pool size in bytes */ + CPoolInUse int64 /* Compressed Pool Used in bytes */ + UCPoolInUse int64 /* Uncompressed Pool Used in bytes */ + Version int64 /* version number (1, 2, etc.,) */ + RealAvailable int64 /* number of pages (in 4KB pages) of memory available without paging out working segments */ + BytesCoalesced int64 /* The number of bytes of the calling partition.s logical real memory coalesced because they contained duplicated data */ + BytesCoalescedMemPool int64 /* number of bytes of logical real memory coalesced because they contained duplicated data in the calling partition.s memory */ +} + +type MemoryPage struct { + PSize int64 /* page size in bytes */ + RealTotal int64 /* number of real memory frames of this page size */ + RealFree int64 /* number of pages on free list */ + RealPinned int64 /* number of pages pinned */ + RealInUse int64 /* number of pages in use */ + PgExct int64 /* number of page faults */ + PgIns int64 /* number of pages paged in */ + PgOuts int64 /* number of pages paged out */ + PgSpIns int64 /* number of page ins from paging space */ + PgSpOuts int64 /* number of page outs from paging space */ + Scans int64 /* number of page scans by clock */ + Cycles int64 /* number of page replacement cycles */ + PgSteals int64 /* number of page steals */ + NumPerm int64 /* number of frames used for files */ + NumPgSp int64 /* number of pages with allocated paging space */ + RealSystem int64 /* number of pages used by system segments. */ + RealUser int64 /* number of pages used by non-system segments. */ + RealProcess int64 /* number of pages used by process segments. */ + VirtActive int64 /* Active virtual pages. */ + ComprsdTotal int64 /* Number of pages of this size compressed */ + ComprsdWsegPgs int64 /* Number of compressed working storage pages */ + CPgIns int64 /* number of page ins of this page size to compressed pool */ + CPgOuts int64 /* number of page outs of this page size from compressed pool */ + CPoolInUse int64 /* Compressed Size of this page size in Compressed Pool */ + UCPoolSize int64 /* Uncompressed Pool size in bytes of this page size */ + ComprsdWsegSize int64 /* Total size of the compressed working storage pages in the pool */ + Version int64 /* version number (1, 2, etc.,) */ + RealAvail int64 /* number of pages (in 4KB pages) of memory available without paging out working segments */ +} + +// paging space types +const ( + LV_PAGING = 1 + NFS_PAGING = 2 + UNKNOWN_PAGING = 3 +) + +type PagingSpace struct { + Name string /* Paging space name */ + Type uint8 /* type of paging device (LV_PAGING or NFS_PAGING) */ + VGName string /* volume group name */ + Hostname string /* host name of paging server */ + Filename string /* swap file name on server */ + LPSize int64 /* size in number of logical partitions */ + MBSize int64 /* size in megabytes */ + MBUsed int64 /* portion used in megabytes */ + IOPending int64 /* number of pending I/O */ + Active uint8 /* indicates if active (1 if so, 0 if not) */ + Automatic uint8 /* indicates if automatic (1 if so, 0 if not) */ + Version int64 /* version number (1, 2, etc.,) */ +} diff --git a/vendor/github.com/power-devops/perfstat/types_network.go b/vendor/github.com/power-devops/perfstat/types_network.go new file mode 100644 index 000000000..e69d0041d --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/types_network.go @@ -0,0 +1,163 @@ +package perfstat + +// Network Interface types +const ( + IFT_OTHER = 0x1 + IFT_1822 = 0x2 /* old-style arpanet imp */ + IFT_HDH1822 = 0x3 /* HDH arpanet imp */ + IFT_X25DDN = 0x4 /* x25 to imp */ + IFT_X25 = 0x5 /* PDN X25 interface (RFC877) */ + IFT_ETHER = 0x6 /* Ethernet CSMACD */ + IFT_ISO88023 = 0x7 /* CMSA CD */ + IFT_ISO88024 = 0x8 /* Token Bus */ + IFT_ISO88025 = 0x9 /* Token Ring */ + IFT_ISO88026 = 0xa /* MAN */ + IFT_STARLAN = 0xb + IFT_P10 = 0xc /* Proteon 10MBit ring */ + IFT_P80 = 0xd /* Proteon 10MBit ring */ + IFT_HY = 0xe /* Hyperchannel */ + IFT_FDDI = 0xf + IFT_LAPB = 0x10 + IFT_SDLC = 0x11 + IFT_T1 = 0x12 + IFT_CEPT = 0x13 /* E1 - european T1 */ + IFT_ISDNBASIC = 0x14 + IFT_ISDNPRIMARY = 0x15 + IFT_PTPSERIAL = 0x16 /* Proprietary PTP serial */ + IFT_PPP = 0x17 /* RFC 1331 */ + IFT_LOOP = 0x18 /* loopback */ + IFT_EON = 0x19 /* ISO over IP */ + IFT_XETHER = 0x1a /* obsolete 3MB experimental ethernet */ + IFT_NSIP = 0x1b /* XNS over IP */ + IFT_SLIP = 0x1c /* IP over generic TTY */ + IFT_ULTRA = 0x1d /* Ultra Technologies */ + IFT_DS3 = 0x1e /* Generic T3 */ + IFT_SIP = 0x1f /* SMDS */ + IFT_FRELAY = 0x20 /* Frame Relay DTE only */ + IFT_RS232 = 0x21 + IFT_PARA = 0x22 /* parallel-port */ + IFT_ARCNET = 0x23 + IFT_ARCNETPLUS = 0x24 + IFT_ATM = 0x25 /* ATM cells */ + IFT_MIOX25 = 0x26 + IFT_SONET = 0x27 /* SONET or SDH */ + IFT_X25PLE = 0x28 + IFT_ISO88022LLC = 0x29 + IFT_LOCALTALK = 0x2a + IFT_SMDSDXI = 0x2b + IFT_FRELAYDCE = 0x2c /* Frame Relay DCE */ + IFT_V35 = 0x2d + IFT_HSSI = 0x2e + IFT_HIPPI = 0x2f + IFT_MODEM = 0x30 /* Generic Modem */ + IFT_AAL5 = 0x31 /* AAL5 over ATM */ + IFT_SONETPATH = 0x32 + IFT_SONETVT = 0x33 + IFT_SMDSICIP = 0x34 /* SMDS InterCarrier Interface */ + IFT_PROPVIRTUAL = 0x35 /* Proprietary Virtual/internal */ + IFT_PROPMUX = 0x36 /* Proprietary Multiplexing */ + IFT_VIPA = 0x37 /* Virtual Interface */ + IFT_SN = 0x38 /* Federation Switch */ + IFT_SP = 0x39 /* SP switch */ + IFT_FCS = 0x3a /* IP over Fiber Channel */ + IFT_TUNNEL = 0x3b + IFT_GIFTUNNEL = 0x3c /* IPv4 over IPv6 tunnel */ + IFT_HF = 0x3d /* Support for PERCS HFI*/ + IFT_CLUSTER = 0x3e /* cluster pseudo network interface */ + IFT_FB = 0xc7 /* IP over Infiniband. Number by IANA */ +) + +type NetIfaceTotal struct { + Number int32 /* number of network interfaces */ + IPackets int64 /* number of packets received on interface */ + IBytes int64 /* number of bytes received on interface */ + IErrors int64 /* number of input errors on interface */ + OPackets int64 /* number of packets sent on interface */ + OBytes int64 /* number of bytes sent on interface */ + OErrors int64 /* number of output errors on interface */ + Collisions int64 /* number of collisions on csma interface */ + XmitDrops int64 /* number of packets not transmitted */ + Version int64 /* version number (1, 2, etc.,) */ +} + +type NetIface struct { + Name string /* name of the interface */ + Description string /* interface description (from ODM, similar to lscfg output) */ + Type uint8 /* ethernet, tokenring, etc. interpretation can be done using /usr/include/net/if_types.h */ + MTU int64 /* network frame size */ + IPackets int64 /* number of packets received on interface */ + IBytes int64 /* number of bytes received on interface */ + IErrors int64 /* number of input errors on interface */ + OPackets int64 /* number of packets sent on interface */ + OBytes int64 /* number of bytes sent on interface */ + OErrors int64 /* number of output errors on interface */ + Collisions int64 /* number of collisions on csma interface */ + Bitrate int64 /* adapter rating in bit per second */ + XmitDrops int64 /* number of packets not transmitted */ + Version int64 /* version number (1, 2, etc.,) */ + IfIqDrops int64 /* Dropped on input, this interface */ + IfArpDrops int64 /* Dropped because no arp response */ +} + +type NetBuffer struct { + Name string /* size in ascii, always power of 2 (ex: "32", "64", "128") */ + InUse int64 /* number of buffer currently allocated */ + Calls int64 /* number of buffer allocations since last reset */ + Delayed int64 /* number of delayed allocations */ + Free int64 /* number of free calls */ + Failed int64 /* number of failed allocations */ + HighWatermark int64 /* high threshold for number of buffer allocated */ + Freed int64 /* number of buffers freed */ + Version int64 /* version number (1, 2, etc.,) */ +} + +// Network adapter types +const ( + NET_PHY = 0 /* physical device */ + NET_SEA = 1 /* shared ethernet adapter */ + NET_VIR = 2 /* virtual device */ + NET_HEA = 3 /* host ethernet adapter */ + NET_EC = 4 /* etherchannel */ + NET_VLAN = 5 /* vlan pseudo device */ +) + +type NetAdapter struct { + Version int64 /* version number (1,2, etc) */ + Name string /* name of the adapter */ + TxPackets int64 /* Transmit Packets on interface */ + TxBytes int64 /* Transmit Bytes on interface */ + TxInterrupts int64 /* Transfer Interrupts */ + TxErrors int64 /* Transmit Errors */ + TxPacketsDropped int64 /* Packets Dropped at the time of Data Transmission */ + TxQueueSize int64 /* Maximum Packets on Software Transmit Queue */ + TxQueueLen int64 /* Transmission Queue Length */ + TxQueueOverflow int64 /* Transmission Queue Overflow */ + TxBroadcastPackets int64 /* Number of Broadcast Packets Transmitted */ + TxMulticastPackets int64 /* Number of Multicast packets Transmitted */ + TxCarrierSense int64 /* Lost Carrier Sense signal count */ + TxDMAUnderrun int64 /* Count of DMA Under-runs for Transmission */ + TxLostCTSErrors int64 /* The number of unsuccessful transmissions due to the loss of the Clear-to-Send signal error */ + TxMaxCollisionErrors int64 /* Maximum Collision Errors at Transmission */ + TxLateCollisionErrors int64 /* Late Collision Errors at Transmission */ + TxDeferred int64 /* The number of packets deferred for Transmission. */ + TxTimeoutErrors int64 /* Time Out Errors for Transmission */ + TxSingleCollisionCount int64 /* Count of Single Collision error at Transmission */ + TxMultipleCollisionCount int64 /* Count of Multiple Collision error at Transmission */ + RxPackets int64 /* Receive Packets on interface */ + RxBytes int64 /* Receive Bytes on interface */ + RxInterrupts int64 /* Receive Interrupts */ + RxErrors int64 /* Input errors on interface */ + RxPacketsDropped int64 /* The number of packets accepted by the device driver for transmission which were not (for any reason) given to the device. */ + RxBadPackets int64 /* Count of Bad Packets Received. */ + RxMulticastPackets int64 /* Number of MultiCast Packets Received */ + RxBroadcastPackets int64 /* Number of Broadcast Packets Received */ + RxCRCErrors int64 /* Count of Packets Received with CRC errors */ + RxDMAOverrun int64 /* Count of DMA over-runs for Data Receival. */ + RxAlignmentErrors int64 /* Packets Received with Alignment Error */ + RxNoResourceErrors int64 /* Packets Received with No Resource Errors */ + RxCollisionErrors int64 /* Packets Received with Collision errors */ + RxPacketTooShortErrors int64 /* Count of Short Packets Received. */ + RxPacketTooLongErrors int64 /* Count of Too Long Packets Received. */ + RxPacketDiscardedByAdapter int64 /* Count of Received Packets discarded by Adapter. */ + AdapterType int32 /* 0 - Physical, 1 - SEA, 2 - Virtual, 3 -HEA */ +} diff --git a/vendor/github.com/power-devops/perfstat/types_process.go b/vendor/github.com/power-devops/perfstat/types_process.go new file mode 100644 index 000000000..325c70b07 --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/types_process.go @@ -0,0 +1,43 @@ +package perfstat + +type Process struct { + Version int64 /* version number (1, 2, etc.,) */ + PID int64 /* Process ID */ + ProcessName string /* Name of The Process */ + Priority int32 /* Process Priority */ + NumThreads int64 /* Thread Count */ + UID int64 /* Owner Info */ + ClassID int64 /* WLM Class Name */ + Size int64 /* Virtual Size of the Process in KB(Exclusive Usage, Leaving all Shared Library Text & Shared File Pages, Shared Memory, Memory Mapped) */ + RealMemData int64 /* Real Memory used for Data in KB */ + RealMemText int64 /* Real Memory used for Text in KB */ + VirtMemData int64 /* Virtual Memory used to Data in KB */ + VirtMemText int64 /* Virtual Memory used for Text in KB */ + SharedLibDataSize int64 /* Data Size from Shared Library in KB */ + HeapSize int64 /* Heap Size in KB */ + RealInUse int64 /* The Real memory in use(in KB) by the process including all kind of segments (excluding system segments). This includes Text, Data, Shared Library Text, Shared Library Data, File Pages, Shared Memory & Memory Mapped */ + VirtInUse int64 /* The Virtual memory in use(in KB) by the process including all kind of segments (excluding system segments). This includes Text, Data, Shared Library Text, Shared Library Data, File Pages, Shared Memory & Memory Mapped */ + Pinned int64 /* Pinned Memory(in KB) for this process inclusive of all segments */ + PgSpInUse int64 /* Paging Space used(in KB) inclusive of all segments */ + FilePages int64 /* File Pages used(in KB) including shared pages */ + RealInUseMap int64 /* Real memory used(in KB) for Shared Memory and Memory Mapped regions */ + VirtInUseMap int64 /* Virtual Memory used(in KB) for Shared Memory and Memory Mapped regions */ + PinnedInUseMap int64 /* Pinned memory(in KB) for Shared Memory and Memory Mapped regions */ + UCpuTime float64 /* User Mode CPU time will be in percentage or milliseconds based on, whether it is filled by perfstat_process_util or perfstat_process respectively. */ + SCpuTime float64 /* System Mode CPU time will be in percentage or milliseconds based on, whether it is filled by perfstat_process_util or perfstat_process respectively. */ + LastTimeBase int64 /* Timebase Counter */ + InBytes int64 /* Bytes Read from Disk */ + OutBytes int64 /* Bytes Written to Disk */ + InOps int64 /* In Operations from Disk */ + OutOps int64 /* Out Operations from Disk */ +} + +type Thread struct { + TID int64 /* thread identifier */ + PID int64 /* process identifier */ + CpuID int64 /* processor on which I'm bound */ + UCpuTime float64 /* User Mode CPU time will be in percentage or milliseconds based on, whether it is filled by perfstat_thread_util or perfstat_thread respectively. */ + SCpuTime float64 /* System Mode CPU time will be in percentage or milliseconds based on, whether it is filled by perfstat_thread_util or perfstat_thread respectively. */ + LastTimeBase int64 /* Timebase Counter */ + Version int64 +} diff --git a/vendor/github.com/power-devops/perfstat/uptime.go b/vendor/github.com/power-devops/perfstat/uptime.go new file mode 100644 index 000000000..2bd3e568d --- /dev/null +++ b/vendor/github.com/power-devops/perfstat/uptime.go @@ -0,0 +1,35 @@ +// +build aix + +package perfstat + +/* +#include "c_helpers.h" +*/ +import "C" + +import ( + "fmt" + "time" +) + +func timeSince(ts uint64) uint64 { + return uint64(time.Now().Unix()) - ts +} + +// BootTime() returns the time of the last boot in UNIX seconds +func BootTime() (uint64, error) { + sec := C.boottime() + if sec == -1 { + return 0, fmt.Errorf("Can't determine boot time") + } + return uint64(sec), nil +} + +// UptimeSeconds() calculates uptime in seconds +func UptimeSeconds() (uint64, error) { + boot, err := BootTime() + if err != nil { + return 0, err + } + return timeSince(boot), nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/LICENSE b/vendor/github.com/shirou/gopsutil/v3/LICENSE new file mode 100644 index 000000000..6f06adcbf --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/LICENSE @@ -0,0 +1,61 @@ +gopsutil is distributed under BSD license reproduced below. + +Copyright (c) 2014, WAKAYAMA Shirou +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the gopsutil authors nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +------- +internal/common/binary.go in the gopsutil is copied and modified from golang/encoding/binary.go. + + + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/github.com/shirou/gopsutil/v3/common/env.go b/vendor/github.com/shirou/gopsutil/v3/common/env.go new file mode 100644 index 000000000..4b5f4980c --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/common/env.go @@ -0,0 +1,23 @@ +package common + +type EnvKeyType string + +// EnvKey is a context key that can be used to set programmatically the environment +// gopsutil relies on to perform calls against the OS. +// Example of use: +// +// ctx := context.WithValue(context.Background(), common.EnvKey, EnvMap{common.HostProcEnvKey: "/myproc"}) +// avg, err := load.AvgWithContext(ctx) +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" +) + +type EnvMap map[EnvKeyType]string diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu.go new file mode 100644 index 000000000..83bc23d45 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu.go @@ -0,0 +1,200 @@ +package cpu + +import ( + "context" + "encoding/json" + "fmt" + "math" + "runtime" + "strconv" + "strings" + "sync" + "time" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +// TimesStat contains the amounts of time the CPU has spent performing different +// kinds of work. Time units are in seconds. It is based on linux /proc/stat file. +type TimesStat struct { + CPU string `json:"cpu"` + User float64 `json:"user"` + System float64 `json:"system"` + Idle float64 `json:"idle"` + Nice float64 `json:"nice"` + Iowait float64 `json:"iowait"` + Irq float64 `json:"irq"` + Softirq float64 `json:"softirq"` + Steal float64 `json:"steal"` + Guest float64 `json:"guest"` + GuestNice float64 `json:"guestNice"` +} + +type InfoStat struct { + CPU int32 `json:"cpu"` + VendorID string `json:"vendorId"` + Family string `json:"family"` + Model string `json:"model"` + Stepping int32 `json:"stepping"` + PhysicalID string `json:"physicalId"` + CoreID string `json:"coreId"` + Cores int32 `json:"cores"` + ModelName string `json:"modelName"` + Mhz float64 `json:"mhz"` + CacheSize int32 `json:"cacheSize"` + Flags []string `json:"flags"` + Microcode string `json:"microcode"` +} + +type lastPercent struct { + sync.Mutex + lastCPUTimes []TimesStat + lastPerCPUTimes []TimesStat +} + +var ( + lastCPUPercent lastPercent + invoke common.Invoker = common.Invoke{} +) + +func init() { + lastCPUPercent.Lock() + lastCPUPercent.lastCPUTimes, _ = Times(false) + lastCPUPercent.lastPerCPUTimes, _ = Times(true) + lastCPUPercent.Unlock() +} + +// Counts returns the number of physical or logical cores in the system +func Counts(logical bool) (int, error) { + return CountsWithContext(context.Background(), logical) +} + +func (c TimesStat) String() string { + v := []string{ + `"cpu":"` + c.CPU + `"`, + `"user":` + strconv.FormatFloat(c.User, 'f', 1, 64), + `"system":` + strconv.FormatFloat(c.System, 'f', 1, 64), + `"idle":` + strconv.FormatFloat(c.Idle, 'f', 1, 64), + `"nice":` + strconv.FormatFloat(c.Nice, 'f', 1, 64), + `"iowait":` + strconv.FormatFloat(c.Iowait, 'f', 1, 64), + `"irq":` + strconv.FormatFloat(c.Irq, 'f', 1, 64), + `"softirq":` + strconv.FormatFloat(c.Softirq, 'f', 1, 64), + `"steal":` + strconv.FormatFloat(c.Steal, 'f', 1, 64), + `"guest":` + strconv.FormatFloat(c.Guest, 'f', 1, 64), + `"guestNice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64), + } + + return `{` + strings.Join(v, ",") + `}` +} + +// Deprecated: Total returns the total number of seconds in a CPUTimesStat +// Please do not use this internal function. +func (c TimesStat) Total() float64 { + total := c.User + c.System + c.Idle + c.Nice + c.Iowait + c.Irq + + c.Softirq + c.Steal + c.Guest + c.GuestNice + + return total +} + +func (c InfoStat) String() string { + s, _ := json.Marshal(c) + return string(s) +} + +func getAllBusy(t TimesStat) (float64, float64) { + tot := t.Total() + if runtime.GOOS == "linux" { + tot -= t.Guest // Linux 2.6.24+ + tot -= t.GuestNice // Linux 3.2.0+ + } + + busy := tot - t.Idle - t.Iowait + + return tot, busy +} + +func calculateBusy(t1, t2 TimesStat) float64 { + t1All, t1Busy := getAllBusy(t1) + t2All, t2Busy := getAllBusy(t2) + + if t2Busy <= t1Busy { + return 0 + } + if t2All <= t1All { + return 100 + } + return math.Min(100, math.Max(0, (t2Busy-t1Busy)/(t2All-t1All)*100)) +} + +func calculateAllBusy(t1, t2 []TimesStat) ([]float64, error) { + // Make sure the CPU measurements have the same length. + if len(t1) != len(t2) { + return nil, fmt.Errorf( + "received two CPU counts: %d != %d", + len(t1), len(t2), + ) + } + + ret := make([]float64, len(t1)) + for i, t := range t2 { + ret[i] = calculateBusy(t1[i], t) + } + return ret, nil +} + +// Percent calculates the percentage of cpu used either per CPU or combined. +// If an interval of 0 is given it will compare the current cpu times against the last call. +// Returns one value per cpu, or a single value if percpu is set to false. +func Percent(interval time.Duration, percpu bool) ([]float64, error) { + return PercentWithContext(context.Background(), interval, percpu) +} + +func PercentWithContext(ctx context.Context, interval time.Duration, percpu bool) ([]float64, error) { + if interval <= 0 { + return percentUsedFromLastCallWithContext(ctx, percpu) + } + + // Get CPU usage at the start of the interval. + cpuTimes1, err := TimesWithContext(ctx, percpu) + if err != nil { + return nil, err + } + + if err := common.Sleep(ctx, interval); err != nil { + return nil, err + } + + // And at the end of the interval. + cpuTimes2, err := TimesWithContext(ctx, percpu) + if err != nil { + return nil, err + } + + return calculateAllBusy(cpuTimes1, cpuTimes2) +} + +func percentUsedFromLastCall(percpu bool) ([]float64, error) { + return percentUsedFromLastCallWithContext(context.Background(), percpu) +} + +func percentUsedFromLastCallWithContext(ctx context.Context, percpu bool) ([]float64, error) { + cpuTimes, err := TimesWithContext(ctx, percpu) + if err != nil { + return nil, err + } + lastCPUPercent.Lock() + defer lastCPUPercent.Unlock() + var lastTimes []TimesStat + if percpu { + lastTimes = lastCPUPercent.lastPerCPUTimes + lastCPUPercent.lastPerCPUTimes = cpuTimes + } else { + lastTimes = lastCPUPercent.lastCPUTimes + lastCPUPercent.lastCPUTimes = cpuTimes + } + + if lastTimes == nil { + return nil, fmt.Errorf("error getting times for cpu percent. lastTimes was nil") + } + return calculateAllBusy(lastTimes, cpuTimes) +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix.go new file mode 100644 index 000000000..1439d1d79 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix.go @@ -0,0 +1,16 @@ +//go:build aix +// +build aix + +package cpu + +import ( + "context" +) + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix_cgo.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix_cgo.go new file mode 100644 index 000000000..9c1e70b17 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix_cgo.go @@ -0,0 +1,66 @@ +//go:build aix && cgo +// +build aix,cgo + +package cpu + +import ( + "context" + + "github.com/power-devops/perfstat" +) + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + var ret []TimesStat + if percpu { + cpus, err := perfstat.CpuStat() + if err != nil { + return nil, err + } + for _, c := range cpus { + ct := &TimesStat{ + CPU: c.Name, + Idle: float64(c.Idle), + User: float64(c.User), + System: float64(c.Sys), + Iowait: float64(c.Wait), + } + ret = append(ret, *ct) + } + } else { + c, err := perfstat.CpuUtilTotalStat() + if err != nil { + return nil, err + } + ct := &TimesStat{ + CPU: "cpu-total", + Idle: float64(c.IdlePct), + User: float64(c.UserPct), + System: float64(c.KernPct), + Iowait: float64(c.WaitPct), + } + ret = append(ret, *ct) + } + return ret, nil +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + c, err := perfstat.CpuTotalStat() + if err != nil { + return nil, err + } + info := InfoStat{ + CPU: 0, + Mhz: float64(c.ProcessorHz / 1000000), + Cores: int32(c.NCpusCfg), + } + result := []InfoStat{info} + return result, nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + c, err := perfstat.CpuTotalStat() + if err != nil { + return 0, err + } + return c.NCpusCfg, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix_nocgo.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix_nocgo.go new file mode 100644 index 000000000..a77b4dbb7 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix_nocgo.go @@ -0,0 +1,92 @@ +//go:build aix && !cgo +// +build aix,!cgo + +package cpu + +import ( + "context" + "strconv" + "strings" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + if percpu { + return []TimesStat{}, common.ErrNotImplementedError + } else { + out, err := invoke.CommandWithContext(ctx, "sar", "-u", "10", "1") + if err != nil { + return nil, err + } + lines := strings.Split(string(out), "\n") + if len(lines) < 5 { + return []TimesStat{}, common.ErrNotImplementedError + } + + ret := TimesStat{CPU: "cpu-total"} + h := strings.Fields(lines[len(lines)-3]) // headers + v := strings.Fields(lines[len(lines)-2]) // values + for i, header := range h { + if t, err := strconv.ParseFloat(v[i], 64); err == nil { + switch header { + case `%usr`: + ret.User = t + case `%sys`: + ret.System = t + case `%wio`: + ret.Iowait = t + case `%idle`: + ret.Idle = t + } + } + } + + return []TimesStat{ret}, nil + } +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + out, err := invoke.CommandWithContext(ctx, "prtconf") + if err != nil { + return nil, err + } + + ret := InfoStat{} + for _, line := range strings.Split(string(out), "\n") { + if strings.HasPrefix(line, "Number Of Processors:") { + p := strings.Fields(line) + if len(p) > 3 { + if t, err := strconv.ParseUint(p[3], 10, 64); err == nil { + ret.Cores = int32(t) + } + } + } else if strings.HasPrefix(line, "Processor Clock Speed:") { + p := strings.Fields(line) + if len(p) > 4 { + if t, err := strconv.ParseFloat(p[3], 64); err == nil { + switch strings.ToUpper(p[4]) { + case "MHZ": + ret.Mhz = t + case "GHZ": + ret.Mhz = t * 1000.0 + case "KHZ": + ret.Mhz = t / 1000.0 + default: + ret.Mhz = t + } + } + } + break + } + } + return []InfoStat{ret}, nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + info, err := InfoWithContext(ctx) + if err == nil { + return int(info[0].Cores), nil + } + return 0, err +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin.go new file mode 100644 index 000000000..41f395e5e --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin.go @@ -0,0 +1,117 @@ +//go:build darwin +// +build darwin + +package cpu + +import ( + "context" + "strconv" + "strings" + + "github.com/shoenig/go-m1cpu" + "github.com/tklauser/go-sysconf" + "golang.org/x/sys/unix" +) + +// sys/resource.h +const ( + CPUser = 0 + cpNice = 1 + cpSys = 2 + cpIntr = 3 + cpIdle = 4 + cpUStates = 5 +) + +// default value. from time.h +var ClocksPerSec = float64(128) + +func init() { + clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK) + // ignore errors + if err == nil { + ClocksPerSec = float64(clkTck) + } +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + if percpu { + return perCPUTimes() + } + + return allCPUTimes() +} + +// Returns only one CPUInfoStat on FreeBSD +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + var ret []InfoStat + + c := InfoStat{} + c.ModelName, _ = unix.Sysctl("machdep.cpu.brand_string") + family, _ := unix.SysctlUint32("machdep.cpu.family") + c.Family = strconv.FormatUint(uint64(family), 10) + model, _ := unix.SysctlUint32("machdep.cpu.model") + c.Model = strconv.FormatUint(uint64(model), 10) + stepping, _ := unix.SysctlUint32("machdep.cpu.stepping") + c.Stepping = int32(stepping) + features, err := unix.Sysctl("machdep.cpu.features") + if err == nil { + for _, v := range strings.Fields(features) { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } + leaf7Features, err := unix.Sysctl("machdep.cpu.leaf7_features") + if err == nil { + for _, v := range strings.Fields(leaf7Features) { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } + extfeatures, err := unix.Sysctl("machdep.cpu.extfeatures") + if err == nil { + for _, v := range strings.Fields(extfeatures) { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } + cores, _ := unix.SysctlUint32("machdep.cpu.core_count") + c.Cores = int32(cores) + cacheSize, _ := unix.SysctlUint32("machdep.cpu.cache.size") + 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 + } + } + + return append(ret, c), nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + var cpuArgument string + if logical { + cpuArgument = "hw.logicalcpu" + } else { + cpuArgument = "hw.physicalcpu" + } + + count, err := unix.SysctlUint32(cpuArgument) + if err != nil { + return 0, err + } + + return int(count), nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin_cgo.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin_cgo.go new file mode 100644 index 000000000..1d5f0772e --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin_cgo.go @@ -0,0 +1,111 @@ +//go:build darwin && cgo +// +build darwin,cgo + +package cpu + +/* +#include +#include +#include +#include +#include +#include +#include +#if TARGET_OS_MAC +#include +#endif +#include +#include +*/ +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 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin_nocgo.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin_nocgo.go new file mode 100644 index 000000000..e067e99f9 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_darwin_nocgo.go @@ -0,0 +1,14 @@ +//go:build darwin && !cgo +// +build darwin,!cgo + +package cpu + +import "github.com/shirou/gopsutil/v3/internal/common" + +func perCPUTimes() ([]TimesStat, error) { + return []TimesStat{}, common.ErrNotImplementedError +} + +func allCPUTimes() ([]TimesStat, error) { + return []TimesStat{}, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_dragonfly.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_dragonfly.go new file mode 100644 index 000000000..fef53e5dc --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_dragonfly.go @@ -0,0 +1,156 @@ +package cpu + +import ( + "context" + "fmt" + "reflect" + "regexp" + "runtime" + "strconv" + "strings" + "unsafe" + + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/tklauser/go-sysconf" + "golang.org/x/sys/unix" +) + +var ( + ClocksPerSec = float64(128) + cpuMatch = regexp.MustCompile(`^CPU:`) + originMatch = regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`) + featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`) + featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`) + cpuEnd = regexp.MustCompile(`^Trying to mount root`) + cpuTimesSize int + emptyTimes cpuTimes +) + +func init() { + clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK) + // ignore errors + if err == nil { + ClocksPerSec = float64(clkTck) + } +} + +func timeStat(name string, t *cpuTimes) *TimesStat { + return &TimesStat{ + User: float64(t.User) / ClocksPerSec, + Nice: float64(t.Nice) / ClocksPerSec, + System: float64(t.Sys) / ClocksPerSec, + Idle: float64(t.Idle) / ClocksPerSec, + Irq: float64(t.Intr) / ClocksPerSec, + CPU: name, + } +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + if percpu { + buf, err := unix.SysctlRaw("kern.cp_times") + if err != nil { + return nil, err + } + + // We can't do this in init due to the conflict with cpu.init() + if cpuTimesSize == 0 { + cpuTimesSize = int(reflect.TypeOf(cpuTimes{}).Size()) + } + + ncpus := len(buf) / cpuTimesSize + ret := make([]TimesStat, 0, ncpus) + for i := 0; i < ncpus; i++ { + times := (*cpuTimes)(unsafe.Pointer(&buf[i*cpuTimesSize])) + if *times == emptyTimes { + // CPU not present + continue + } + ret = append(ret, *timeStat(fmt.Sprintf("cpu%d", len(ret)), times)) + } + return ret, nil + } + + buf, err := unix.SysctlRaw("kern.cp_time") + if err != nil { + return nil, err + } + + times := (*cpuTimes)(unsafe.Pointer(&buf[0])) + return []TimesStat{*timeStat("cpu-total", times)}, nil +} + +// Returns only one InfoStat on DragonflyBSD. The information regarding core +// count, however is accurate and it is assumed that all InfoStat attributes +// are the same across CPUs. +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + const dmesgBoot = "/var/run/dmesg.boot" + + c, err := parseDmesgBoot(dmesgBoot) + if err != nil { + return nil, err + } + + var u32 uint32 + if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil { + return nil, err + } + c.Mhz = float64(u32) + + var num int + var buf string + if buf, err = unix.Sysctl("hw.cpu_topology.tree"); err != nil { + return nil, err + } + num = strings.Count(buf, "CHIP") + c.Cores = int32(strings.Count(string(buf), "CORE") / num) + + if c.ModelName, err = unix.Sysctl("hw.model"); err != nil { + return nil, err + } + + ret := make([]InfoStat, num) + for i := 0; i < num; i++ { + ret[i] = c + } + + return ret, nil +} + +func parseDmesgBoot(fileName string) (InfoStat, error) { + c := InfoStat{} + lines, _ := common.ReadLines(fileName) + for _, line := range lines { + if matches := cpuEnd.FindStringSubmatch(line); matches != nil { + break + } else if matches := originMatch.FindStringSubmatch(line); matches != nil { + c.VendorID = matches[1] + t, err := strconv.ParseInt(matches[2], 10, 32) + if err != nil { + return c, fmt.Errorf("unable to parse DragonflyBSD CPU stepping information from %q: %v", line, err) + } + c.Stepping = int32(t) + } else if matches := featuresMatch.FindStringSubmatch(line); matches != nil { + for _, v := range strings.Split(matches[1], ",") { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil { + for _, v := range strings.Split(matches[1], ",") { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } + } + + return c, nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_dragonfly_amd64.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_dragonfly_amd64.go new file mode 100644 index 000000000..57e14528d --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_dragonfly_amd64.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint64 + Nice uint64 + Sys uint64 + Intr uint64 + Idle uint64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_fallback.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_fallback.go new file mode 100644 index 000000000..089f603c8 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_fallback.go @@ -0,0 +1,31 @@ +//go:build !darwin && !linux && !freebsd && !openbsd && !netbsd && !solaris && !windows && !dragonfly && !plan9 && !aix +// +build !darwin,!linux,!freebsd,!openbsd,!netbsd,!solaris,!windows,!dragonfly,!plan9,!aix + +package cpu + +import ( + "context" + "runtime" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + return []TimesStat{}, common.ErrNotImplementedError +} + +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + return []InfoStat{}, common.ErrNotImplementedError +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd.go new file mode 100644 index 000000000..d3f47353c --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd.go @@ -0,0 +1,168 @@ +package cpu + +import ( + "context" + "fmt" + "reflect" + "regexp" + "runtime" + "strconv" + "strings" + "unsafe" + + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/tklauser/go-sysconf" + "golang.org/x/sys/unix" +) + +var ( + ClocksPerSec = float64(128) + cpuMatch = regexp.MustCompile(`^CPU:`) + originMatch = regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Family\s*=\s*(.+)\s+Model\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`) + featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`) + featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`) + cpuEnd = regexp.MustCompile(`^Trying to mount root`) + cpuCores = regexp.MustCompile(`FreeBSD/SMP: (\d*) package\(s\) x (\d*) core\(s\)`) + cpuTimesSize int + emptyTimes cpuTimes +) + +func init() { + clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK) + // ignore errors + if err == nil { + ClocksPerSec = float64(clkTck) + } +} + +func timeStat(name string, t *cpuTimes) *TimesStat { + return &TimesStat{ + User: float64(t.User) / ClocksPerSec, + Nice: float64(t.Nice) / ClocksPerSec, + System: float64(t.Sys) / ClocksPerSec, + Idle: float64(t.Idle) / ClocksPerSec, + Irq: float64(t.Intr) / ClocksPerSec, + CPU: name, + } +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + if percpu { + buf, err := unix.SysctlRaw("kern.cp_times") + if err != nil { + return nil, err + } + + // We can't do this in init due to the conflict with cpu.init() + if cpuTimesSize == 0 { + cpuTimesSize = int(reflect.TypeOf(cpuTimes{}).Size()) + } + + ncpus := len(buf) / cpuTimesSize + ret := make([]TimesStat, 0, ncpus) + for i := 0; i < ncpus; i++ { + times := (*cpuTimes)(unsafe.Pointer(&buf[i*cpuTimesSize])) + if *times == emptyTimes { + // CPU not present + continue + } + ret = append(ret, *timeStat(fmt.Sprintf("cpu%d", len(ret)), times)) + } + return ret, nil + } + + buf, err := unix.SysctlRaw("kern.cp_time") + if err != nil { + return nil, err + } + + times := (*cpuTimes)(unsafe.Pointer(&buf[0])) + return []TimesStat{*timeStat("cpu-total", times)}, nil +} + +// Returns only one InfoStat on FreeBSD. The information regarding core +// count, however is accurate and it is assumed that all InfoStat attributes +// are the same across CPUs. +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + const dmesgBoot = "/var/run/dmesg.boot" + + c, num, err := parseDmesgBoot(dmesgBoot) + if err != nil { + return nil, err + } + + var u32 uint32 + if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil { + return nil, err + } + c.Mhz = float64(u32) + + if u32, err = unix.SysctlUint32("hw.ncpu"); err != nil { + return nil, err + } + c.Cores = int32(u32) + + if c.ModelName, err = unix.Sysctl("hw.model"); err != nil { + return nil, err + } + + ret := make([]InfoStat, num) + for i := 0; i < num; i++ { + ret[i] = c + } + + return ret, nil +} + +func parseDmesgBoot(fileName string) (InfoStat, int, error) { + c := InfoStat{} + lines, _ := common.ReadLines(fileName) + cpuNum := 1 // default cpu num is 1 + for _, line := range lines { + if matches := cpuEnd.FindStringSubmatch(line); matches != nil { + break + } else if matches := originMatch.FindStringSubmatch(line); matches != nil { + c.VendorID = matches[1] + c.Family = matches[3] + c.Model = matches[4] + t, err := strconv.ParseInt(matches[5], 10, 32) + if err != nil { + return c, 0, fmt.Errorf("unable to parse FreeBSD CPU stepping information from %q: %v", line, err) + } + c.Stepping = int32(t) + } else if matches := featuresMatch.FindStringSubmatch(line); matches != nil { + for _, v := range strings.Split(matches[1], ",") { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil { + for _, v := range strings.Split(matches[1], ",") { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if matches := cpuCores.FindStringSubmatch(line); matches != nil { + t, err := strconv.ParseInt(matches[1], 10, 32) + if err != nil { + return c, 0, fmt.Errorf("unable to parse FreeBSD CPU Nums from %q: %v", line, err) + } + cpuNum = int(t) + t2, err := strconv.ParseInt(matches[2], 10, 32) + if err != nil { + return c, 0, fmt.Errorf("unable to parse FreeBSD CPU cores from %q: %v", line, err) + } + c.Cores = int32(t2) + } + } + + return c, cpuNum, nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_386.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_386.go new file mode 100644 index 000000000..8b7f4c321 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_386.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint32 + Nice uint32 + Sys uint32 + Intr uint32 + Idle uint32 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_amd64.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_amd64.go new file mode 100644 index 000000000..57e14528d --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_amd64.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint64 + Nice uint64 + Sys uint64 + Intr uint64 + Idle uint64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_arm.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_arm.go new file mode 100644 index 000000000..8b7f4c321 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_arm.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint32 + Nice uint32 + Sys uint32 + Intr uint32 + Idle uint32 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_arm64.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_arm64.go new file mode 100644 index 000000000..57e14528d --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_freebsd_arm64.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint64 + Nice uint64 + Sys uint64 + Intr uint64 + Idle uint64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_linux.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_linux.go new file mode 100644 index 000000000..da467e2dd --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_linux.go @@ -0,0 +1,479 @@ +//go:build linux +// +build linux + +package cpu + +import ( + "context" + "errors" + "fmt" + "path/filepath" + "strconv" + "strings" + + "github.com/tklauser/go-sysconf" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +var ClocksPerSec = float64(100) + +var armModelToModelName = map[uint64]string{ + 0x810: "ARM810", + 0x920: "ARM920", + 0x922: "ARM922", + 0x926: "ARM926", + 0x940: "ARM940", + 0x946: "ARM946", + 0x966: "ARM966", + 0xa20: "ARM1020", + 0xa22: "ARM1022", + 0xa26: "ARM1026", + 0xb02: "ARM11 MPCore", + 0xb36: "ARM1136", + 0xb56: "ARM1156", + 0xb76: "ARM1176", + 0xc05: "Cortex-A5", + 0xc07: "Cortex-A7", + 0xc08: "Cortex-A8", + 0xc09: "Cortex-A9", + 0xc0d: "Cortex-A17", + 0xc0f: "Cortex-A15", + 0xc0e: "Cortex-A17", + 0xc14: "Cortex-R4", + 0xc15: "Cortex-R5", + 0xc17: "Cortex-R7", + 0xc18: "Cortex-R8", + 0xc20: "Cortex-M0", + 0xc21: "Cortex-M1", + 0xc23: "Cortex-M3", + 0xc24: "Cortex-M4", + 0xc27: "Cortex-M7", + 0xc60: "Cortex-M0+", + 0xd01: "Cortex-A32", + 0xd02: "Cortex-A34", + 0xd03: "Cortex-A53", + 0xd04: "Cortex-A35", + 0xd05: "Cortex-A55", + 0xd06: "Cortex-A65", + 0xd07: "Cortex-A57", + 0xd08: "Cortex-A72", + 0xd09: "Cortex-A73", + 0xd0a: "Cortex-A75", + 0xd0b: "Cortex-A76", + 0xd0c: "Neoverse-N1", + 0xd0d: "Cortex-A77", + 0xd0e: "Cortex-A76AE", + 0xd13: "Cortex-R52", + 0xd20: "Cortex-M23", + 0xd21: "Cortex-M33", + 0xd40: "Neoverse-V1", + 0xd41: "Cortex-A78", + 0xd42: "Cortex-A78AE", + 0xd43: "Cortex-A65AE", + 0xd44: "Cortex-X1", + 0xd46: "Cortex-A510", + 0xd47: "Cortex-A710", + 0xd48: "Cortex-X2", + 0xd49: "Neoverse-N2", + 0xd4a: "Neoverse-E1", + 0xd4b: "Cortex-A78C", + 0xd4c: "Cortex-X1C", + 0xd4d: "Cortex-A715", + 0xd4e: "Cortex-X3", +} + +func init() { + clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK) + // ignore errors + if err == nil { + ClocksPerSec = float64(clkTck) + } +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + filename := common.HostProcWithContext(ctx, "stat") + lines := []string{} + if percpu { + statlines, err := common.ReadLines(filename) + if err != nil || len(statlines) < 2 { + return []TimesStat{}, nil + } + for _, line := range statlines[1:] { + if !strings.HasPrefix(line, "cpu") { + break + } + lines = append(lines, line) + } + } else { + lines, _ = common.ReadLinesOffsetN(filename, 0, 1) + } + + ret := make([]TimesStat, 0, len(lines)) + + for _, line := range lines { + ct, err := parseStatLine(line) + if err != nil { + continue + } + ret = append(ret, *ct) + + } + return ret, nil +} + +func sysCPUPath(ctx context.Context, cpu int32, relPath string) string { + return common.HostSysWithContext(ctx, fmt.Sprintf("devices/system/cpu/cpu%d", cpu), relPath) +} + +func finishCPUInfo(ctx context.Context, c *InfoStat) { + var lines []string + var err error + var value float64 + + if len(c.CoreID) == 0 { + lines, err = common.ReadLines(sysCPUPath(ctx, c.CPU, "topology/core_id")) + if err == nil { + c.CoreID = lines[0] + } + } + + // override the value of c.Mhz with cpufreq/cpuinfo_max_freq regardless + // of the value from /proc/cpuinfo because we want to report the maximum + // clock-speed of the CPU for c.Mhz, matching the behaviour of Windows + lines, err = common.ReadLines(sysCPUPath(ctx, c.CPU, "cpufreq/cpuinfo_max_freq")) + // if we encounter errors below such as there are no cpuinfo_max_freq file, + // we just ignore. so let Mhz is 0. + if err != nil || len(lines) == 0 { + return + } + value, err = strconv.ParseFloat(lines[0], 64) + if err != nil { + return + } + c.Mhz = value / 1000.0 // value is in kHz + if c.Mhz > 9999 { + c.Mhz = c.Mhz / 1000.0 // value in Hz + } +} + +// CPUInfo on linux will return 1 item per physical thread. +// +// CPUs have three levels of counting: sockets, cores, threads. +// Cores with HyperThreading count as having 2 threads per core. +// Sockets often come with many physical CPU cores. +// For example a single socket board with two cores each with HT will +// return 4 CPUInfoStat structs on Linux and the "Cores" field set to 1. +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + filename := common.HostProcWithContext(ctx, "cpuinfo") + lines, _ := common.ReadLines(filename) + + var ret []InfoStat + var processorName string + + c := InfoStat{CPU: -1, Cores: 1} + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) < 2 { + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.TrimSpace(fields[1]) + + switch key { + case "Processor": + processorName = value + case "processor", "cpu number": + if c.CPU >= 0 { + finishCPUInfo(ctx, &c) + ret = append(ret, c) + } + c = InfoStat{Cores: 1, ModelName: processorName} + t, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return ret, err + } + c.CPU = int32(t) + case "vendorId", "vendor_id": + c.VendorID = value + if strings.Contains(value, "S390") { + processorName = "S390" + } + case "CPU implementer": + if v, err := strconv.ParseUint(value, 0, 8); err == nil { + switch v { + case 0x41: + c.VendorID = "ARM" + case 0x42: + c.VendorID = "Broadcom" + case 0x43: + c.VendorID = "Cavium" + case 0x44: + c.VendorID = "DEC" + case 0x46: + c.VendorID = "Fujitsu" + case 0x48: + c.VendorID = "HiSilicon" + case 0x49: + c.VendorID = "Infineon" + case 0x4d: + c.VendorID = "Motorola/Freescale" + case 0x4e: + c.VendorID = "NVIDIA" + case 0x50: + c.VendorID = "APM" + case 0x51: + c.VendorID = "Qualcomm" + case 0x56: + c.VendorID = "Marvell" + case 0x61: + c.VendorID = "Apple" + case 0x69: + c.VendorID = "Intel" + case 0xc0: + c.VendorID = "Ampere" + } + } + case "cpu family": + c.Family = value + case "model", "CPU part": + c.Model = value + // if CPU is arm based, model name is found via model number. refer to: arch/arm64/kernel/cpuinfo.c + if c.VendorID == "ARM" { + if v, err := strconv.ParseUint(c.Model, 0, 16); err == nil { + modelName, exist := armModelToModelName[v] + if exist { + c.ModelName = modelName + } else { + c.ModelName = "Undefined" + } + } + } + case "Model Name", "model name", "cpu": + c.ModelName = value + if strings.Contains(value, "POWER") { + c.Model = strings.Split(value, " ")[0] + c.Family = "POWER" + c.VendorID = "IBM" + } + case "stepping", "revision", "CPU revision": + val := value + + if key == "revision" { + val = strings.Split(value, ".")[0] + } + + t, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return ret, err + } + c.Stepping = int32(t) + case "cpu MHz", "clock", "cpu MHz dynamic": + // treat this as the fallback value, thus we ignore error + if t, err := strconv.ParseFloat(strings.Replace(value, "MHz", "", 1), 64); err == nil { + c.Mhz = t + } + case "cache size": + t, err := strconv.ParseInt(strings.Replace(value, " KB", "", 1), 10, 64) + if err != nil { + return ret, err + } + c.CacheSize = int32(t) + case "physical id": + c.PhysicalID = value + case "core id": + c.CoreID = value + case "flags", "Features": + c.Flags = strings.FieldsFunc(value, func(r rune) bool { + return r == ',' || r == ' ' + }) + case "microcode": + c.Microcode = value + } + } + if c.CPU >= 0 { + finishCPUInfo(ctx, &c) + ret = append(ret, c) + } + return ret, nil +} + +func parseStatLine(line string) (*TimesStat, error) { + fields := strings.Fields(line) + + if len(fields) < 8 { + return nil, errors.New("stat does not contain cpu info") + } + + if !strings.HasPrefix(fields[0], "cpu") { + return nil, errors.New("not contain cpu") + } + + cpu := fields[0] + if cpu == "cpu" { + cpu = "cpu-total" + } + user, err := strconv.ParseFloat(fields[1], 64) + if err != nil { + return nil, err + } + nice, err := strconv.ParseFloat(fields[2], 64) + if err != nil { + return nil, err + } + system, err := strconv.ParseFloat(fields[3], 64) + if err != nil { + return nil, err + } + idle, err := strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, err + } + iowait, err := strconv.ParseFloat(fields[5], 64) + if err != nil { + return nil, err + } + irq, err := strconv.ParseFloat(fields[6], 64) + if err != nil { + return nil, err + } + softirq, err := strconv.ParseFloat(fields[7], 64) + if err != nil { + return nil, err + } + + ct := &TimesStat{ + CPU: cpu, + User: user / ClocksPerSec, + Nice: nice / ClocksPerSec, + System: system / ClocksPerSec, + Idle: idle / ClocksPerSec, + Iowait: iowait / ClocksPerSec, + Irq: irq / ClocksPerSec, + Softirq: softirq / ClocksPerSec, + } + if len(fields) > 8 { // Linux >= 2.6.11 + steal, err := strconv.ParseFloat(fields[8], 64) + if err != nil { + return nil, err + } + ct.Steal = steal / ClocksPerSec + } + if len(fields) > 9 { // Linux >= 2.6.24 + guest, err := strconv.ParseFloat(fields[9], 64) + if err != nil { + return nil, err + } + ct.Guest = guest / ClocksPerSec + } + if len(fields) > 10 { // Linux >= 3.2.0 + guestNice, err := strconv.ParseFloat(fields[10], 64) + if err != nil { + return nil, err + } + ct.GuestNice = guestNice / ClocksPerSec + } + + return ct, nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + if logical { + ret := 0 + // https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_pslinux.py#L599 + procCpuinfo := common.HostProcWithContext(ctx, "cpuinfo") + lines, err := common.ReadLines(procCpuinfo) + if err == nil { + for _, line := range lines { + line = strings.ToLower(line) + if strings.HasPrefix(line, "processor") { + _, err = strconv.Atoi(strings.TrimSpace(line[strings.IndexByte(line, ':')+1:])) + if err == nil { + ret++ + } + } + } + } + if ret == 0 { + procStat := common.HostProcWithContext(ctx, "stat") + lines, err = common.ReadLines(procStat) + if err != nil { + return 0, err + } + for _, line := range lines { + if len(line) >= 4 && strings.HasPrefix(line, "cpu") && '0' <= line[3] && line[3] <= '9' { // `^cpu\d` regexp matching + ret++ + } + } + } + return ret, nil + } + // physical cores + // https://github.com/giampaolo/psutil/blob/8415355c8badc9c94418b19bdf26e622f06f0cce/psutil/_pslinux.py#L615-L628 + threadSiblingsLists := make(map[string]bool) + // These 2 files are the same but */core_cpus_list is newer while */thread_siblings_list is deprecated and may disappear in the future. + // https://www.kernel.org/doc/Documentation/admin-guide/cputopology.rst + // https://github.com/giampaolo/psutil/pull/1727#issuecomment-707624964 + // https://lkml.org/lkml/2019/2/26/41 + for _, glob := range []string{"devices/system/cpu/cpu[0-9]*/topology/core_cpus_list", "devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list"} { + if files, err := filepath.Glob(common.HostSysWithContext(ctx, glob)); err == nil { + for _, file := range files { + lines, err := common.ReadLines(file) + if err != nil || len(lines) != 1 { + continue + } + threadSiblingsLists[lines[0]] = true + } + ret := len(threadSiblingsLists) + if ret != 0 { + return ret, nil + } + } + } + // https://github.com/giampaolo/psutil/blob/122174a10b75c9beebe15f6c07dcf3afbe3b120d/psutil/_pslinux.py#L631-L652 + filename := common.HostProcWithContext(ctx, "cpuinfo") + lines, err := common.ReadLines(filename) + if err != nil { + return 0, err + } + mapping := make(map[int]int) + currentInfo := make(map[string]int) + for _, line := range lines { + line = strings.ToLower(strings.TrimSpace(line)) + if line == "" { + // new section + id, okID := currentInfo["physical id"] + cores, okCores := currentInfo["cpu cores"] + if okID && okCores { + mapping[id] = cores + } + currentInfo = make(map[string]int) + continue + } + fields := strings.Split(line, ":") + if len(fields) < 2 { + continue + } + fields[0] = strings.TrimSpace(fields[0]) + if fields[0] == "physical id" || fields[0] == "cpu cores" { + val, err := strconv.Atoi(strings.TrimSpace(fields[1])) + if err != nil { + continue + } + currentInfo[fields[0]] = val + } + } + ret := 0 + for _, v := range mapping { + ret += v + } + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd.go new file mode 100644 index 000000000..1f66be342 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd.go @@ -0,0 +1,119 @@ +//go:build netbsd +// +build netbsd + +package cpu + +import ( + "context" + "fmt" + "runtime" + "unsafe" + + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/tklauser/go-sysconf" + "golang.org/x/sys/unix" +) + +const ( + // sys/sysctl.h + ctlKern = 1 // "high kernel": proc, limits + ctlHw = 6 // CTL_HW + kernCpTime = 51 // KERN_CPTIME +) + +var ClocksPerSec = float64(100) + +func init() { + clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK) + // ignore errors + if err == nil { + ClocksPerSec = float64(clkTck) + } +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) (ret []TimesStat, err error) { + if !percpu { + mib := []int32{ctlKern, kernCpTime} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return ret, err + } + times := (*cpuTimes)(unsafe.Pointer(&buf[0])) + stat := TimesStat{ + CPU: "cpu-total", + User: float64(times.User), + Nice: float64(times.Nice), + System: float64(times.Sys), + Idle: float64(times.Idle), + Irq: float64(times.Intr), + } + return []TimesStat{stat}, nil + } + + ncpu, err := unix.SysctlUint32("hw.ncpu") + if err != nil { + return + } + + var i uint32 + for i = 0; i < ncpu; i++ { + mib := []int32{ctlKern, kernCpTime, int32(i)} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return ret, err + } + + stats := (*cpuTimes)(unsafe.Pointer(&buf[0])) + ret = append(ret, TimesStat{ + CPU: fmt.Sprintf("cpu%d", i), + User: float64(stats.User), + Nice: float64(stats.Nice), + System: float64(stats.Sys), + Idle: float64(stats.Idle), + Irq: float64(stats.Intr), + }) + } + + return ret, nil +} + +// Returns only one (minimal) CPUInfoStat on NetBSD +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + var ret []InfoStat + var err error + + c := InfoStat{} + + mhz, err := unix.Sysctl("machdep.dmi.processor-frequency") + if err != nil { + return nil, err + } + _, err = fmt.Sscanf(mhz, "%f", &c.Mhz) + if err != nil { + return nil, err + } + + ncpu, err := unix.SysctlUint32("hw.ncpuonline") + if err != nil { + return nil, err + } + c.Cores = int32(ncpu) + + if c.ModelName, err = unix.Sysctl("machdep.dmi.processor-version"); err != nil { + return nil, err + } + + return append(ret, c), nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd_amd64.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd_amd64.go new file mode 100644 index 000000000..57e14528d --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd_amd64.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint64 + Nice uint64 + Sys uint64 + Intr uint64 + Idle uint64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd_arm64.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd_arm64.go new file mode 100644 index 000000000..57e14528d --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_netbsd_arm64.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint64 + Nice uint64 + Sys uint64 + Intr uint64 + Idle uint64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd.go new file mode 100644 index 000000000..fe3329030 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd.go @@ -0,0 +1,137 @@ +//go:build openbsd +// +build openbsd + +package cpu + +import ( + "context" + "fmt" + "runtime" + "unsafe" + + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/tklauser/go-sysconf" + "golang.org/x/sys/unix" +) + +const ( + // sys/sched.h + cpuOnline = 0x0001 // CPUSTATS_ONLINE + + // sys/sysctl.h + ctlKern = 1 // "high kernel": proc, limits + ctlHw = 6 // CTL_HW + smt = 24 // HW_SMT + kernCpTime = 40 // KERN_CPTIME + kernCPUStats = 85 // KERN_CPUSTATS +) + +var ClocksPerSec = float64(128) + +type cpuStats struct { + // cs_time[CPUSTATES] + User uint64 + Nice uint64 + Sys uint64 + Spin uint64 + Intr uint64 + Idle uint64 + + // cs_flags + Flags uint64 +} + +func init() { + clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK) + // ignore errors + if err == nil { + ClocksPerSec = float64(clkTck) + } +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) (ret []TimesStat, err error) { + if !percpu { + mib := []int32{ctlKern, kernCpTime} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return ret, err + } + times := (*cpuTimes)(unsafe.Pointer(&buf[0])) + stat := TimesStat{ + CPU: "cpu-total", + User: float64(times.User) / ClocksPerSec, + Nice: float64(times.Nice) / ClocksPerSec, + System: float64(times.Sys) / ClocksPerSec, + Idle: float64(times.Idle) / ClocksPerSec, + Irq: float64(times.Intr) / ClocksPerSec, + } + return []TimesStat{stat}, nil + } + + ncpu, err := unix.SysctlUint32("hw.ncpu") + if err != nil { + return + } + + var i uint32 + for i = 0; i < ncpu; i++ { + mib := []int32{ctlKern, kernCPUStats, int32(i)} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return ret, err + } + + stats := (*cpuStats)(unsafe.Pointer(&buf[0])) + if (stats.Flags & cpuOnline) == 0 { + continue + } + ret = append(ret, TimesStat{ + CPU: fmt.Sprintf("cpu%d", i), + User: float64(stats.User) / ClocksPerSec, + Nice: float64(stats.Nice) / ClocksPerSec, + System: float64(stats.Sys) / ClocksPerSec, + Idle: float64(stats.Idle) / ClocksPerSec, + Irq: float64(stats.Intr) / ClocksPerSec, + }) + } + + return ret, nil +} + +// Returns only one (minimal) CPUInfoStat on OpenBSD +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + var ret []InfoStat + var err error + + c := InfoStat{} + + mhz, err := unix.SysctlUint32("hw.cpuspeed") + if err != nil { + return nil, err + } + c.Mhz = float64(mhz) + + ncpu, err := unix.SysctlUint32("hw.ncpuonline") + if err != nil { + return nil, err + } + c.Cores = int32(ncpu) + + if c.ModelName, err = unix.Sysctl("hw.model"); err != nil { + return nil, err + } + + return append(ret, c), nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_386.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_386.go new file mode 100644 index 000000000..5e878399a --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_386.go @@ -0,0 +1,10 @@ +package cpu + +type cpuTimes struct { + User uint32 + Nice uint32 + Sys uint32 + Spin uint32 + Intr uint32 + Idle uint32 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_amd64.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_amd64.go new file mode 100644 index 000000000..d659058cd --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_amd64.go @@ -0,0 +1,10 @@ +package cpu + +type cpuTimes struct { + User uint64 + Nice uint64 + Sys uint64 + Spin uint64 + Intr uint64 + Idle uint64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_arm.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_arm.go new file mode 100644 index 000000000..5e878399a --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_arm.go @@ -0,0 +1,10 @@ +package cpu + +type cpuTimes struct { + User uint32 + Nice uint32 + Sys uint32 + Spin uint32 + Intr uint32 + Idle uint32 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_arm64.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_arm64.go new file mode 100644 index 000000000..d659058cd --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_arm64.go @@ -0,0 +1,10 @@ +package cpu + +type cpuTimes struct { + User uint64 + Nice uint64 + Sys uint64 + Spin uint64 + Intr uint64 + Idle uint64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_riscv64.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_riscv64.go new file mode 100644 index 000000000..d659058cd --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_openbsd_riscv64.go @@ -0,0 +1,10 @@ +package cpu + +type cpuTimes struct { + User uint64 + Nice uint64 + Sys uint64 + Spin uint64 + Intr uint64 + Idle uint64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_plan9.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_plan9.go new file mode 100644 index 000000000..a2e99d8c0 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_plan9.go @@ -0,0 +1,50 @@ +//go:build plan9 +// +build plan9 + +package cpu + +import ( + "context" + "os" + "runtime" + + stats "github.com/lufia/plan9stats" + "github.com/shirou/gopsutil/v3/internal/common" +) + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + // BUG: percpu flag is not supported yet. + root := os.Getenv("HOST_ROOT") + c, err := stats.ReadCPUType(ctx, stats.WithRootDir(root)) + if err != nil { + return nil, err + } + s, err := stats.ReadCPUStats(ctx, stats.WithRootDir(root)) + if err != nil { + return nil, err + } + return []TimesStat{ + { + CPU: c.Name, + User: s.User.Seconds(), + System: s.Sys.Seconds(), + Idle: s.Idle.Seconds(), + }, + }, nil +} + +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + return []InfoStat{}, common.ErrNotImplementedError +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_solaris.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_solaris.go new file mode 100644 index 000000000..4231ad168 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_solaris.go @@ -0,0 +1,269 @@ +package cpu + +import ( + "context" + "errors" + "fmt" + "regexp" + "runtime" + "sort" + "strconv" + "strings" + + "github.com/tklauser/go-sysconf" +) + +var ClocksPerSec = float64(128) + +func init() { + clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK) + // ignore errors + if err == nil { + ClocksPerSec = float64(clkTck) + } +} + +// sum all values in a float64 map with float64 keys +func msum(x map[float64]float64) float64 { + total := 0.0 + for _, y := range x { + total += y + } + return total +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +var kstatSplit = regexp.MustCompile(`[:\s]+`) + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + kstatSysOut, err := invoke.CommandWithContext(ctx, "kstat", "-p", "cpu_stat:*:*:/^idle$|^user$|^kernel$|^iowait$|^swap$/") + if err != nil { + return nil, fmt.Errorf("cannot execute kstat: %s", err) + } + cpu := make(map[float64]float64) + idle := make(map[float64]float64) + user := make(map[float64]float64) + kern := make(map[float64]float64) + iowt := make(map[float64]float64) + // swap := make(map[float64]float64) + for _, line := range strings.Split(string(kstatSysOut), "\n") { + fields := kstatSplit.Split(line, -1) + if fields[0] != "cpu_stat" { + continue + } + cpuNumber, err := strconv.ParseFloat(fields[1], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse cpu number: %s", err) + } + cpu[cpuNumber] = cpuNumber + switch fields[3] { + case "idle": + idle[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse idle: %s", err) + } + case "user": + user[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse user: %s", err) + } + case "kernel": + kern[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse kernel: %s", err) + } + case "iowait": + iowt[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse iowait: %s", err) + } + //not sure how this translates, don't report, add to kernel, something else? + /*case "swap": + swap[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse swap: %s", err) + } */ + } + } + ret := make([]TimesStat, 0, len(cpu)) + if percpu { + for _, c := range cpu { + ct := &TimesStat{ + CPU: fmt.Sprintf("cpu%d", int(cpu[c])), + Idle: idle[c] / ClocksPerSec, + User: user[c] / ClocksPerSec, + System: kern[c] / ClocksPerSec, + Iowait: iowt[c] / ClocksPerSec, + } + ret = append(ret, *ct) + } + } else { + ct := &TimesStat{ + CPU: "cpu-total", + Idle: msum(idle) / ClocksPerSec, + User: msum(user) / ClocksPerSec, + System: msum(kern) / ClocksPerSec, + Iowait: msum(iowt) / ClocksPerSec, + } + ret = append(ret, *ct) + } + return ret, nil +} + +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + psrInfoOut, err := invoke.CommandWithContext(ctx, "psrinfo", "-p", "-v") + if err != nil { + return nil, fmt.Errorf("cannot execute psrinfo: %s", err) + } + + procs, err := parseProcessorInfo(string(psrInfoOut)) + if err != nil { + return nil, fmt.Errorf("error parsing psrinfo output: %s", err) + } + + isaInfoOut, err := invoke.CommandWithContext(ctx, "isainfo", "-b", "-v") + if err != nil { + return nil, fmt.Errorf("cannot execute isainfo: %s", err) + } + + flags, err := parseISAInfo(string(isaInfoOut)) + if err != nil { + return nil, fmt.Errorf("error parsing isainfo output: %s", err) + } + + result := make([]InfoStat, 0, len(flags)) + for _, proc := range procs { + procWithFlags := proc + procWithFlags.Flags = flags + result = append(result, procWithFlags) + } + + return result, nil +} + +var flagsMatch = regexp.MustCompile(`[\w\.]+`) + +func parseISAInfo(cmdOutput string) ([]string, error) { + words := flagsMatch.FindAllString(cmdOutput, -1) + + // Sanity check the output + if len(words) < 4 || words[1] != "bit" || words[3] != "applications" { + return nil, errors.New("attempted to parse invalid isainfo output") + } + + flags := make([]string, len(words)-4) + for i, val := range words[4:] { + flags[i] = val + } + sort.Strings(flags) + + return flags, nil +} + +var psrInfoMatch = regexp.MustCompile(`The physical processor has (?:([\d]+) virtual processors? \(([\d-]+)\)|([\d]+) cores and ([\d]+) virtual processors[^\n]+)\n(?:\s+ The core has.+\n)*\s+.+ \((\w+) ([\S]+) family (.+) model (.+) step (.+) clock (.+) MHz\)\n[\s]*(.*)`) + +const ( + psrNumCoresOffset = 1 + psrNumCoresHTOffset = 3 + psrNumHTOffset = 4 + psrVendorIDOffset = 5 + psrFamilyOffset = 7 + psrModelOffset = 8 + psrStepOffset = 9 + psrClockOffset = 10 + psrModelNameOffset = 11 +) + +func parseProcessorInfo(cmdOutput string) ([]InfoStat, error) { + matches := psrInfoMatch.FindAllStringSubmatch(cmdOutput, -1) + + var infoStatCount int32 + result := make([]InfoStat, 0, len(matches)) + for physicalIndex, physicalCPU := range matches { + var step int32 + var clock float64 + + if physicalCPU[psrStepOffset] != "" { + stepParsed, err := strconv.ParseInt(physicalCPU[psrStepOffset], 10, 32) + if err != nil { + return nil, fmt.Errorf("cannot parse value %q for step as 32-bit integer: %s", physicalCPU[9], err) + } + step = int32(stepParsed) + } + + if physicalCPU[psrClockOffset] != "" { + clockParsed, err := strconv.ParseInt(physicalCPU[psrClockOffset], 10, 64) + if err != nil { + return nil, fmt.Errorf("cannot parse value %q for clock as 32-bit integer: %s", physicalCPU[10], err) + } + clock = float64(clockParsed) + } + + var err error + var numCores int64 + var numHT int64 + switch { + case physicalCPU[psrNumCoresOffset] != "": + numCores, err = strconv.ParseInt(physicalCPU[psrNumCoresOffset], 10, 32) + if err != nil { + return nil, fmt.Errorf("cannot parse value %q for core count as 32-bit integer: %s", physicalCPU[1], err) + } + + for i := 0; i < int(numCores); i++ { + result = append(result, InfoStat{ + CPU: infoStatCount, + PhysicalID: strconv.Itoa(physicalIndex), + CoreID: strconv.Itoa(i), + Cores: 1, + VendorID: physicalCPU[psrVendorIDOffset], + ModelName: physicalCPU[psrModelNameOffset], + Family: physicalCPU[psrFamilyOffset], + Model: physicalCPU[psrModelOffset], + Stepping: step, + Mhz: clock, + }) + infoStatCount++ + } + case physicalCPU[psrNumCoresHTOffset] != "": + numCores, err = strconv.ParseInt(physicalCPU[psrNumCoresHTOffset], 10, 32) + if err != nil { + return nil, fmt.Errorf("cannot parse value %q for core count as 32-bit integer: %s", physicalCPU[3], err) + } + + numHT, err = strconv.ParseInt(physicalCPU[psrNumHTOffset], 10, 32) + if err != nil { + return nil, fmt.Errorf("cannot parse value %q for hyperthread count as 32-bit integer: %s", physicalCPU[4], err) + } + + for i := 0; i < int(numCores); i++ { + result = append(result, InfoStat{ + CPU: infoStatCount, + PhysicalID: strconv.Itoa(physicalIndex), + CoreID: strconv.Itoa(i), + Cores: int32(numHT) / int32(numCores), + VendorID: physicalCPU[psrVendorIDOffset], + ModelName: physicalCPU[psrModelNameOffset], + Family: physicalCPU[psrFamilyOffset], + Model: physicalCPU[psrModelOffset], + Stepping: step, + Mhz: clock, + }) + infoStatCount++ + } + default: + return nil, errors.New("values for cores with and without hyperthreading are both set") + } + } + return result, nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_windows.go b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_windows.go new file mode 100644 index 000000000..e10612fd1 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/cpu/cpu_windows.go @@ -0,0 +1,229 @@ +//go:build windows +// +build windows + +package cpu + +import ( + "context" + "fmt" + "unsafe" + + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/yusufpapurcu/wmi" + "golang.org/x/sys/windows" +) + +var ( + procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo") +) + +type win32_Processor struct { + Family uint16 + Manufacturer string + Name string + NumberOfLogicalProcessors uint32 + NumberOfCores uint32 + ProcessorID *string + Stepping *string + MaxClockSpeed uint32 +} + +// SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION +// defined in windows api doc with the following +// https://docs.microsoft.com/en-us/windows/desktop/api/winternl/nf-winternl-ntquerysysteminformation#system_processor_performance_information +// additional fields documented here +// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/processor_performance.htm +type win32_SystemProcessorPerformanceInformation struct { + IdleTime int64 // idle time in 100ns (this is not a filetime). + KernelTime int64 // kernel time in 100ns. kernel time includes idle time. (this is not a filetime). + UserTime int64 // usertime in 100ns (this is not a filetime). + DpcTime int64 // dpc time in 100ns (this is not a filetime). + InterruptTime int64 // interrupt time in 100ns + InterruptCount uint32 +} + +const ( + ClocksPerSec = 10000000.0 + + // systemProcessorPerformanceInformationClass information class to query with NTQuerySystemInformation + // https://processhacker.sourceforge.io/doc/ntexapi_8h.html#ad5d815b48e8f4da1ef2eb7a2f18a54e0 + win32_SystemProcessorPerformanceInformationClass = 8 + + // size of systemProcessorPerformanceInfoSize in memory + win32_SystemProcessorPerformanceInfoSize = uint32(unsafe.Sizeof(win32_SystemProcessorPerformanceInformation{})) +) + +// Times returns times stat per cpu and combined for all CPUs +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + if percpu { + return perCPUTimes() + } + + var ret []TimesStat + var lpIdleTime common.FILETIME + var lpKernelTime common.FILETIME + var lpUserTime common.FILETIME + r, _, _ := common.ProcGetSystemTimes.Call( + uintptr(unsafe.Pointer(&lpIdleTime)), + uintptr(unsafe.Pointer(&lpKernelTime)), + uintptr(unsafe.Pointer(&lpUserTime))) + if r == 0 { + return ret, windows.GetLastError() + } + + LOT := float64(0.0000001) + HIT := (LOT * 4294967296.0) + idle := ((HIT * float64(lpIdleTime.DwHighDateTime)) + (LOT * float64(lpIdleTime.DwLowDateTime))) + user := ((HIT * float64(lpUserTime.DwHighDateTime)) + (LOT * float64(lpUserTime.DwLowDateTime))) + kernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime))) + system := (kernel - idle) + + ret = append(ret, TimesStat{ + CPU: "cpu-total", + Idle: float64(idle), + User: float64(user), + System: float64(system), + }) + return ret, nil +} + +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + var ret []InfoStat + var dst []win32_Processor + q := wmi.CreateQuery(&dst, "") + if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil { + return ret, err + } + + var procID string + for i, l := range dst { + procID = "" + if l.ProcessorID != nil { + procID = *l.ProcessorID + } + + cpu := InfoStat{ + CPU: int32(i), + Family: fmt.Sprintf("%d", l.Family), + VendorID: l.Manufacturer, + ModelName: l.Name, + Cores: int32(l.NumberOfLogicalProcessors), + PhysicalID: procID, + Mhz: float64(l.MaxClockSpeed), + Flags: []string{}, + } + ret = append(ret, cpu) + } + + return ret, nil +} + +// perCPUTimes returns times stat per cpu, per core and overall for all CPUs +func perCPUTimes() ([]TimesStat, error) { + var ret []TimesStat + stats, err := perfInfo() + if err != nil { + return nil, err + } + for core, v := range stats { + c := TimesStat{ + CPU: fmt.Sprintf("cpu%d", core), + User: float64(v.UserTime) / ClocksPerSec, + System: float64(v.KernelTime-v.IdleTime) / ClocksPerSec, + Idle: float64(v.IdleTime) / ClocksPerSec, + Irq: float64(v.InterruptTime) / ClocksPerSec, + } + ret = append(ret, c) + } + return ret, nil +} + +// makes call to Windows API function to retrieve performance information for each core +func perfInfo() ([]win32_SystemProcessorPerformanceInformation, error) { + // Make maxResults large for safety. + // We can't invoke the api call with a results array that's too small. + // If we have more than 2056 cores on a single host, then it's probably the future. + maxBuffer := 2056 + // buffer for results from the windows proc + resultBuffer := make([]win32_SystemProcessorPerformanceInformation, maxBuffer) + // size of the buffer in memory + bufferSize := uintptr(win32_SystemProcessorPerformanceInfoSize) * uintptr(maxBuffer) + // size of the returned response + var retSize uint32 + + // Invoke windows api proc. + // The returned err from the windows dll proc will always be non-nil even when successful. + // See https://godoc.org/golang.org/x/sys/windows#LazyProc.Call for more information + retCode, _, err := common.ProcNtQuerySystemInformation.Call( + win32_SystemProcessorPerformanceInformationClass, // System Information Class -> SystemProcessorPerformanceInformation + uintptr(unsafe.Pointer(&resultBuffer[0])), // pointer to first element in result buffer + bufferSize, // size of the buffer in memory + uintptr(unsafe.Pointer(&retSize)), // pointer to the size of the returned results the windows proc will set this + ) + + // check return code for errors + if retCode != 0 { + return nil, fmt.Errorf("call to NtQuerySystemInformation returned %d. err: %s", retCode, err.Error()) + } + + // calculate the number of returned elements based on the returned size + numReturnedElements := retSize / win32_SystemProcessorPerformanceInfoSize + + // trim results to the number of returned elements + resultBuffer = resultBuffer[:numReturnedElements] + + return resultBuffer, nil +} + +// SystemInfo is an equivalent representation of SYSTEM_INFO in the Windows API. +// https://msdn.microsoft.com/en-us/library/ms724958%28VS.85%29.aspx?f=255&MSPPError=-2147217396 +// https://github.com/elastic/go-windows/blob/bb1581babc04d5cb29a2bfa7a9ac6781c730c8dd/kernel32.go#L43 +type systemInfo struct { + wProcessorArchitecture uint16 + wReserved uint16 + dwPageSize uint32 + lpMinimumApplicationAddress uintptr + lpMaximumApplicationAddress uintptr + dwActiveProcessorMask uintptr + dwNumberOfProcessors uint32 + dwProcessorType uint32 + dwAllocationGranularity uint32 + wProcessorLevel uint16 + wProcessorRevision uint16 +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + if logical { + // https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L97 + ret := windows.GetActiveProcessorCount(windows.ALL_PROCESSOR_GROUPS) + if ret != 0 { + return int(ret), nil + } + var systemInfo systemInfo + _, _, err := procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo))) + if systemInfo.dwNumberOfProcessors == 0 { + return 0, err + } + return int(systemInfo.dwNumberOfProcessors), nil + } + // physical cores https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L499 + // for the time being, try with unreliable and slow WMI call… + var dst []win32_Processor + q := wmi.CreateQuery(&dst, "") + if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil { + return 0, err + } + var count uint32 + for _, d := range dst { + count += d.NumberOfCores + } + return int(count), nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/binary.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/binary.go new file mode 100644 index 000000000..5e8d43db8 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/binary.go @@ -0,0 +1,637 @@ +package common + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package binary implements simple translation between numbers and byte +// sequences and encoding and decoding of varints. +// +// Numbers are translated by reading and writing fixed-size values. +// A fixed-size value is either a fixed-size arithmetic +// type (int8, uint8, int16, float32, complex64, ...) +// or an array or struct containing only fixed-size values. +// +// The varint functions encode and decode single integer values using +// a variable-length encoding; smaller values require fewer bytes. +// For a specification, see +// http://code.google.com/apis/protocolbuffers/docs/encoding.html. +// +// This package favors simplicity over efficiency. Clients that require +// high-performance serialization, especially for large data structures, +// should look at more advanced solutions such as the encoding/gob +// package or protocol buffers. + +import ( + "errors" + "io" + "math" + "reflect" +) + +// A ByteOrder specifies how to convert byte sequences into +// 16-, 32-, or 64-bit unsigned integers. +type ByteOrder interface { + Uint16([]byte) uint16 + Uint32([]byte) uint32 + Uint64([]byte) uint64 + PutUint16([]byte, uint16) + PutUint32([]byte, uint32) + PutUint64([]byte, uint64) + String() string +} + +// LittleEndian is the little-endian implementation of ByteOrder. +var LittleEndian littleEndian + +// BigEndian is the big-endian implementation of ByteOrder. +var BigEndian bigEndian + +type littleEndian struct{} + +func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 } + +func (littleEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v) + b[1] = byte(v >> 8) +} + +func (littleEndian) Uint32(b []byte) uint32 { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func (littleEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) +} + +func (littleEndian) Uint64(b []byte) uint64 { + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +func (littleEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) +} + +func (littleEndian) String() string { return "LittleEndian" } + +func (littleEndian) GoString() string { return "binary.LittleEndian" } + +type bigEndian struct{} + +func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8 } + +func (bigEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v >> 8) + b[1] = byte(v) +} + +func (bigEndian) Uint32(b []byte) uint32 { + return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 +} + +func (bigEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) +} + +func (bigEndian) Uint64(b []byte) uint64 { + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +func (bigEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} + +func (bigEndian) String() string { return "BigEndian" } + +func (bigEndian) GoString() string { return "binary.BigEndian" } + +// Read reads structured binary data from r into data. +// Data must be a pointer to a fixed-size value or a slice +// of fixed-size values. +// Bytes read from r are decoded using the specified byte order +// and written to successive fields of the data. +// When reading into structs, the field data for fields with +// blank (_) field names is skipped; i.e., blank field names +// may be used for padding. +// When reading into a struct, all non-blank fields must be exported. +func Read(r io.Reader, order ByteOrder, data interface{}) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + var b [8]byte + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } + if _, err := io.ReadFull(r, bs); err != nil { + return err + } + switch data := data.(type) { + case *int8: + *data = int8(b[0]) + case *uint8: + *data = b[0] + case *int16: + *data = int16(order.Uint16(bs)) + case *uint16: + *data = order.Uint16(bs) + case *int32: + *data = int32(order.Uint32(bs)) + case *uint32: + *data = order.Uint32(bs) + case *int64: + *data = int64(order.Uint64(bs)) + case *uint64: + *data = order.Uint64(bs) + case []int8: + for i, x := range bs { // Easier to loop over the input for 8-bit values. + data[i] = int8(x) + } + case []uint8: + copy(data, bs) + case []int16: + for i := range data { + data[i] = int16(order.Uint16(bs[2*i:])) + } + case []uint16: + for i := range data { + data[i] = order.Uint16(bs[2*i:]) + } + case []int32: + for i := range data { + data[i] = int32(order.Uint32(bs[4*i:])) + } + case []uint32: + for i := range data { + data[i] = order.Uint32(bs[4*i:]) + } + case []int64: + for i := range data { + data[i] = int64(order.Uint64(bs[8*i:])) + } + case []uint64: + for i := range data { + data[i] = order.Uint64(bs[8*i:]) + } + } + return nil + } + + // Fallback to reflect-based decoding. + v := reflect.ValueOf(data) + size := -1 + switch v.Kind() { + case reflect.Ptr: + v = v.Elem() + size = dataSize(v) + case reflect.Slice: + size = dataSize(v) + } + if size < 0 { + return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String()) + } + d := &decoder{order: order, buf: make([]byte, size)} + if _, err := io.ReadFull(r, d.buf); err != nil { + return err + } + d.value(v) + return nil +} + +// Write writes the binary representation of data into w. +// Data must be a fixed-size value or a slice of fixed-size +// values, or a pointer to such data. +// Bytes written to w are encoded using the specified byte order +// and read from successive fields of the data. +// When writing structs, zero values are written for fields +// with blank (_) field names. +func Write(w io.Writer, order ByteOrder, data interface{}) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + var b [8]byte + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } + switch v := data.(type) { + case *int8: + bs = b[:1] + b[0] = byte(*v) + case int8: + bs = b[:1] + b[0] = byte(v) + case []int8: + for i, x := range v { + bs[i] = byte(x) + } + case *uint8: + bs = b[:1] + b[0] = *v + case uint8: + bs = b[:1] + b[0] = byte(v) + case []uint8: + bs = v + case *int16: + bs = b[:2] + order.PutUint16(bs, uint16(*v)) + case int16: + bs = b[:2] + order.PutUint16(bs, uint16(v)) + case []int16: + for i, x := range v { + order.PutUint16(bs[2*i:], uint16(x)) + } + case *uint16: + bs = b[:2] + order.PutUint16(bs, *v) + case uint16: + bs = b[:2] + order.PutUint16(bs, v) + case []uint16: + for i, x := range v { + order.PutUint16(bs[2*i:], x) + } + case *int32: + bs = b[:4] + order.PutUint32(bs, uint32(*v)) + case int32: + bs = b[:4] + order.PutUint32(bs, uint32(v)) + case []int32: + for i, x := range v { + order.PutUint32(bs[4*i:], uint32(x)) + } + case *uint32: + bs = b[:4] + order.PutUint32(bs, *v) + case uint32: + bs = b[:4] + order.PutUint32(bs, v) + case []uint32: + for i, x := range v { + order.PutUint32(bs[4*i:], x) + } + case *int64: + bs = b[:8] + order.PutUint64(bs, uint64(*v)) + case int64: + bs = b[:8] + order.PutUint64(bs, uint64(v)) + case []int64: + for i, x := range v { + order.PutUint64(bs[8*i:], uint64(x)) + } + case *uint64: + bs = b[:8] + order.PutUint64(bs, *v) + case uint64: + bs = b[:8] + order.PutUint64(bs, v) + case []uint64: + for i, x := range v { + order.PutUint64(bs[8*i:], x) + } + } + _, err := w.Write(bs) + return err + } + + // Fallback to reflect-based encoding. + v := reflect.Indirect(reflect.ValueOf(data)) + size := dataSize(v) + if size < 0 { + return errors.New("binary.Write: invalid type " + reflect.TypeOf(data).String()) + } + buf := make([]byte, size) + e := &encoder{order: order, buf: buf} + e.value(v) + _, err := w.Write(buf) + return err +} + +// Size returns how many bytes Write would generate to encode the value v, which +// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data. +// If v is neither of these, Size returns -1. +func Size(v interface{}) int { + return dataSize(reflect.Indirect(reflect.ValueOf(v))) +} + +// dataSize returns the number of bytes the actual data represented by v occupies in memory. +// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice +// it returns the length of the slice times the element size and does not count the memory +// occupied by the header. If the type of v is not acceptable, dataSize returns -1. +func dataSize(v reflect.Value) int { + if v.Kind() == reflect.Slice { + if s := sizeof(v.Type().Elem()); s >= 0 { + return s * v.Len() + } + return -1 + } + return sizeof(v.Type()) +} + +// sizeof returns the size >= 0 of variables for the given type or -1 if the type is not acceptable. +func sizeof(t reflect.Type) int { + switch t.Kind() { + case reflect.Array: + if s := sizeof(t.Elem()); s >= 0 { + return s * t.Len() + } + + case reflect.Struct: + sum := 0 + for i, n := 0, t.NumField(); i < n; i++ { + s := sizeof(t.Field(i).Type) + if s < 0 { + return -1 + } + sum += s + } + return sum + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.Ptr: + return int(t.Size()) + } + + return -1 +} + +type coder struct { + order ByteOrder + buf []byte +} + +type ( + decoder coder + encoder coder +) + +func (d *decoder) uint8() uint8 { + x := d.buf[0] + d.buf = d.buf[1:] + return x +} + +func (e *encoder) uint8(x uint8) { + e.buf[0] = x + e.buf = e.buf[1:] +} + +func (d *decoder) uint16() uint16 { + x := d.order.Uint16(d.buf[0:2]) + d.buf = d.buf[2:] + return x +} + +func (e *encoder) uint16(x uint16) { + e.order.PutUint16(e.buf[0:2], x) + e.buf = e.buf[2:] +} + +func (d *decoder) uint32() uint32 { + x := d.order.Uint32(d.buf[0:4]) + d.buf = d.buf[4:] + return x +} + +func (e *encoder) uint32(x uint32) { + e.order.PutUint32(e.buf[0:4], x) + e.buf = e.buf[4:] +} + +func (d *decoder) uint64() uint64 { + x := d.order.Uint64(d.buf[0:8]) + d.buf = d.buf[8:] + return x +} + +func (e *encoder) uint64(x uint64) { + e.order.PutUint64(e.buf[0:8], x) + e.buf = e.buf[8:] +} + +func (d *decoder) int8() int8 { return int8(d.uint8()) } + +func (e *encoder) int8(x int8) { e.uint8(uint8(x)) } + +func (d *decoder) int16() int16 { return int16(d.uint16()) } + +func (e *encoder) int16(x int16) { e.uint16(uint16(x)) } + +func (d *decoder) int32() int32 { return int32(d.uint32()) } + +func (e *encoder) int32(x int32) { e.uint32(uint32(x)) } + +func (d *decoder) int64() int64 { return int64(d.uint64()) } + +func (e *encoder) int64(x int64) { e.uint64(uint64(x)) } + +func (d *decoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // Note: Calling v.CanSet() below is an optimization. + // It would be sufficient to check the field name, + // but creating the StructField info for each field is + // costly (run "go test -bench=ReadStruct" and compare + // results when making changes to this code). + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + d.value(v) + } else { + d.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Int8: + v.SetInt(int64(d.int8())) + case reflect.Int16: + v.SetInt(int64(d.int16())) + case reflect.Int32: + v.SetInt(int64(d.int32())) + case reflect.Int64: + v.SetInt(d.int64()) + + case reflect.Uint8: + v.SetUint(uint64(d.uint8())) + case reflect.Uint16: + v.SetUint(uint64(d.uint16())) + case reflect.Uint32: + v.SetUint(uint64(d.uint32())) + case reflect.Uint64: + v.SetUint(d.uint64()) + + case reflect.Float32: + v.SetFloat(float64(math.Float32frombits(d.uint32()))) + case reflect.Float64: + v.SetFloat(math.Float64frombits(d.uint64())) + + case reflect.Complex64: + v.SetComplex(complex( + float64(math.Float32frombits(d.uint32())), + float64(math.Float32frombits(d.uint32())), + )) + case reflect.Complex128: + v.SetComplex(complex( + math.Float64frombits(d.uint64()), + math.Float64frombits(d.uint64()), + )) + } +} + +func (e *encoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // see comment for corresponding code in decoder.value() + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + e.value(v) + } else { + e.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch v.Type().Kind() { + case reflect.Int8: + e.int8(int8(v.Int())) + case reflect.Int16: + e.int16(int16(v.Int())) + case reflect.Int32: + e.int32(int32(v.Int())) + case reflect.Int64: + e.int64(v.Int()) + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch v.Type().Kind() { + case reflect.Uint8: + e.uint8(uint8(v.Uint())) + case reflect.Uint16: + e.uint16(uint16(v.Uint())) + case reflect.Uint32: + e.uint32(uint32(v.Uint())) + case reflect.Uint64: + e.uint64(v.Uint()) + } + + case reflect.Float32, reflect.Float64: + switch v.Type().Kind() { + case reflect.Float32: + e.uint32(math.Float32bits(float32(v.Float()))) + case reflect.Float64: + e.uint64(math.Float64bits(v.Float())) + } + + case reflect.Complex64, reflect.Complex128: + switch v.Type().Kind() { + case reflect.Complex64: + x := v.Complex() + e.uint32(math.Float32bits(float32(real(x)))) + e.uint32(math.Float32bits(float32(imag(x)))) + case reflect.Complex128: + x := v.Complex() + e.uint64(math.Float64bits(real(x))) + e.uint64(math.Float64bits(imag(x))) + } + } +} + +func (d *decoder) skip(v reflect.Value) { + d.buf = d.buf[dataSize(v):] +} + +func (e *encoder) skip(v reflect.Value) { + n := dataSize(v) + for i := range e.buf[0:n] { + e.buf[i] = 0 + } + e.buf = e.buf[n:] +} + +// intDataSize returns the size of the data required to represent the data when encoded. +// It returns zero if the type cannot be implemented by the fast path in Read or Write. +func intDataSize(data interface{}) int { + switch data := data.(type) { + case int8, *int8, *uint8: + return 1 + case []int8: + return len(data) + case []uint8: + return len(data) + case int16, *int16, *uint16: + return 2 + case []int16: + return 2 * len(data) + case []uint16: + return 2 * len(data) + case int32, *int32, *uint32: + return 4 + case []int32: + return 4 * len(data) + case []uint32: + return 4 * len(data) + case int64, *int64, *uint64: + return 8 + case []int64: + return 8 * len(data) + case []uint64: + return 8 * len(data) + } + return 0 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/common.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/common.go new file mode 100644 index 000000000..5e25e507b --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/common.go @@ -0,0 +1,464 @@ +package common + +// +// gopsutil is a port of psutil(http://pythonhosted.org/psutil/). +// This covers these architectures. +// - linux (amd64, arm) +// - freebsd (amd64) +// - windows (amd64) + +import ( + "bufio" + "bytes" + "context" + "errors" + "fmt" + "io" + "net/url" + "os" + "os/exec" + "path" + "path/filepath" + "reflect" + "runtime" + "strconv" + "strings" + "time" + + "github.com/shirou/gopsutil/v3/common" +) + +var ( + Timeout = 3 * time.Second + ErrTimeout = errors.New("command timed out") +) + +type Invoker interface { + Command(string, ...string) ([]byte, error) + CommandWithContext(context.Context, string, ...string) ([]byte, error) +} + +type Invoke struct{} + +func (i Invoke) Command(name string, arg ...string) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), Timeout) + defer cancel() + return i.CommandWithContext(ctx, name, arg...) +} + +func (i Invoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) { + cmd := exec.CommandContext(ctx, name, arg...) + + var buf bytes.Buffer + cmd.Stdout = &buf + cmd.Stderr = &buf + + if err := cmd.Start(); err != nil { + return buf.Bytes(), err + } + + if err := cmd.Wait(); err != nil { + return buf.Bytes(), err + } + + return buf.Bytes(), nil +} + +type FakeInvoke struct { + Suffix string // Suffix species expected file name suffix such as "fail" + Error error // If Error specified, return the error. +} + +// Command in FakeInvoke returns from expected file if exists. +func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) { + if i.Error != nil { + return []byte{}, i.Error + } + + arch := runtime.GOOS + + commandName := filepath.Base(name) + + fname := strings.Join(append([]string{commandName}, arg...), "") + fname = url.QueryEscape(fname) + fpath := path.Join("testdata", arch, fname) + if i.Suffix != "" { + fpath += "_" + i.Suffix + } + if PathExists(fpath) { + return os.ReadFile(fpath) + } + return []byte{}, fmt.Errorf("could not find testdata: %s", fpath) +} + +func (i FakeInvoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) { + return i.Command(name, arg...) +} + +var ErrNotImplementedError = errors.New("not implemented yet") + +// ReadFile reads contents from a file +func ReadFile(filename string) (string, error) { + content, err := os.ReadFile(filename) + if err != nil { + return "", err + } + + return string(content), nil +} + +// ReadLines reads contents from a file and splits them by new lines. +// A convenience wrapper to ReadLinesOffsetN(filename, 0, -1). +func ReadLines(filename string) ([]string, error) { + return ReadLinesOffsetN(filename, 0, -1) +} + +// ReadLine reads a file and returns the first occurrence of a line that is prefixed with prefix. +func ReadLine(filename string, prefix string) (string, error) { + f, err := os.Open(filename) + if err != nil { + return "", err + } + defer f.Close() + r := bufio.NewReader(f) + for { + line, err := r.ReadString('\n') + if err != nil { + if err == io.EOF { + break + } + return "", err + } + if strings.HasPrefix(line, prefix) { + return line, nil + } + } + + return "", nil +} + +// ReadLinesOffsetN reads contents from file and splits them by new line. +// The offset tells at which line number to start. +// The count determines the number of lines to read (starting from offset): +// n >= 0: at most n lines +// n < 0: whole file +func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) { + f, err := os.Open(filename) + if err != nil { + return []string{""}, err + } + defer f.Close() + + var ret []string + + r := bufio.NewReader(f) + for i := 0; i < n+int(offset) || n < 0; i++ { + line, err := r.ReadString('\n') + if err != nil { + if err == io.EOF && len(line) > 0 { + ret = append(ret, strings.Trim(line, "\n")) + } + break + } + if i < int(offset) { + continue + } + ret = append(ret, strings.Trim(line, "\n")) + } + + return ret, nil +} + +func IntToString(orig []int8) string { + ret := make([]byte, len(orig)) + size := -1 + for i, o := range orig { + if o == 0 { + size = i + break + } + ret[i] = byte(o) + } + if size == -1 { + size = len(orig) + } + + return string(ret[0:size]) +} + +func UintToString(orig []uint8) string { + ret := make([]byte, len(orig)) + size := -1 + for i, o := range orig { + if o == 0 { + size = i + break + } + ret[i] = byte(o) + } + if size == -1 { + size = len(orig) + } + + return string(ret[0:size]) +} + +func ByteToString(orig []byte) string { + n := -1 + l := -1 + for i, b := range orig { + // skip left side null + if l == -1 && b == 0 { + continue + } + if l == -1 { + l = i + } + + if b == 0 { + break + } + n = i + 1 + } + if n == -1 { + return string(orig) + } + return string(orig[l:n]) +} + +// ReadInts reads contents from single line file and returns them as []int32. +func ReadInts(filename string) ([]int64, error) { + f, err := os.Open(filename) + if err != nil { + return []int64{}, err + } + defer f.Close() + + var ret []int64 + + r := bufio.NewReader(f) + + // The int files that this is concerned with should only be one liners. + line, err := r.ReadString('\n') + if err != nil { + return []int64{}, err + } + + i, err := strconv.ParseInt(strings.Trim(line, "\n"), 10, 32) + if err != nil { + return []int64{}, err + } + ret = append(ret, i) + + return ret, nil +} + +// Parse Hex to uint32 without error +func HexToUint32(hex string) uint32 { + vv, _ := strconv.ParseUint(hex, 16, 32) + return uint32(vv) +} + +// Parse to int32 without error +func mustParseInt32(val string) int32 { + vv, _ := strconv.ParseInt(val, 10, 32) + return int32(vv) +} + +// Parse to uint64 without error +func mustParseUint64(val string) uint64 { + vv, _ := strconv.ParseInt(val, 10, 64) + return uint64(vv) +} + +// Parse to Float64 without error +func mustParseFloat64(val string) float64 { + vv, _ := strconv.ParseFloat(val, 64) + return vv +} + +// StringsHas checks the target string slice contains src or not +func StringsHas(target []string, src string) bool { + for _, t := range target { + if strings.TrimSpace(t) == src { + return true + } + } + return false +} + +// StringsContains checks the src in any string of the target string slice +func StringsContains(target []string, src string) bool { + for _, t := range target { + if strings.Contains(t, src) { + return true + } + } + return false +} + +// IntContains checks the src in any int of the target int slice. +func IntContains(target []int, src int) bool { + for _, t := range target { + if src == t { + return true + } + } + return false +} + +// get struct attributes. +// This method is used only for debugging platform dependent code. +func attributes(m interface{}) map[string]reflect.Type { + typ := reflect.TypeOf(m) + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + + attrs := make(map[string]reflect.Type) + if typ.Kind() != reflect.Struct { + return nil + } + + for i := 0; i < typ.NumField(); i++ { + p := typ.Field(i) + if !p.Anonymous { + attrs[p.Name] = p.Type + } + } + + return attrs +} + +func PathExists(filename string) bool { + if _, err := os.Stat(filename); err == nil { + return true + } + return false +} + +// PathExistsWithContents returns the filename exists and it is not empty +func PathExistsWithContents(filename string) bool { + info, err := os.Stat(filename) + if err != nil { + return false + } + return info.Size() > 4 && !info.IsDir() // at least 4 bytes +} + +// GetEnvWithContext retrieves the environment variable key. If it does not exist it returns the default. +// The context may optionally contain a map superseding os.EnvKey. +func GetEnvWithContext(ctx context.Context, key string, dfault string, combineWith ...string) string { + var value string + if env, ok := ctx.Value(common.EnvKey).(common.EnvMap); ok { + value = env[common.EnvKeyType(key)] + } + if value == "" { + value = os.Getenv(key) + } + if value == "" { + value = dfault + } + + return combine(value, combineWith) +} + +// GetEnv retrieves the environment variable key. If it does not exist it returns the default. +func GetEnv(key string, dfault string, combineWith ...string) string { + value := os.Getenv(key) + if value == "" { + value = dfault + } + + return combine(value, combineWith) +} + +func combine(value string, combineWith []string) string { + switch len(combineWith) { + case 0: + return value + case 1: + return filepath.Join(value, combineWith[0]) + default: + all := make([]string, len(combineWith)+1) + all[0] = value + copy(all[1:], combineWith) + return filepath.Join(all...) + } +} + +func HostProc(combineWith ...string) string { + return GetEnv("HOST_PROC", "/proc", combineWith...) +} + +func HostSys(combineWith ...string) string { + return GetEnv("HOST_SYS", "/sys", combineWith...) +} + +func HostEtc(combineWith ...string) string { + return GetEnv("HOST_ETC", "/etc", combineWith...) +} + +func HostVar(combineWith ...string) string { + return GetEnv("HOST_VAR", "/var", combineWith...) +} + +func HostRun(combineWith ...string) string { + return GetEnv("HOST_RUN", "/run", combineWith...) +} + +func HostDev(combineWith ...string) string { + return GetEnv("HOST_DEV", "/dev", combineWith...) +} + +func HostRoot(combineWith ...string) string { + return GetEnv("HOST_ROOT", "/", combineWith...) +} + +func HostProcWithContext(ctx context.Context, combineWith ...string) string { + return GetEnvWithContext(ctx, "HOST_PROC", "/proc", combineWith...) +} + +func HostProcMountInfoWithContext(ctx context.Context, combineWith ...string) string { + return GetEnvWithContext(ctx, "HOST_PROC_MOUNTINFO", "", combineWith...) +} + +func HostSysWithContext(ctx context.Context, combineWith ...string) string { + return GetEnvWithContext(ctx, "HOST_SYS", "/sys", combineWith...) +} + +func HostEtcWithContext(ctx context.Context, combineWith ...string) string { + return GetEnvWithContext(ctx, "HOST_ETC", "/etc", combineWith...) +} + +func HostVarWithContext(ctx context.Context, combineWith ...string) string { + return GetEnvWithContext(ctx, "HOST_VAR", "/var", combineWith...) +} + +func HostRunWithContext(ctx context.Context, combineWith ...string) string { + return GetEnvWithContext(ctx, "HOST_RUN", "/run", combineWith...) +} + +func HostDevWithContext(ctx context.Context, combineWith ...string) string { + return GetEnvWithContext(ctx, "HOST_DEV", "/dev", combineWith...) +} + +func HostRootWithContext(ctx context.Context, combineWith ...string) string { + return GetEnvWithContext(ctx, "HOST_ROOT", "/", combineWith...) +} + +// getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running +// sysctl commands (see DoSysctrl). +func getSysctrlEnv(env []string) []string { + foundLC := false + for i, line := range env { + if strings.HasPrefix(line, "LC_ALL") { + env[i] = "LC_ALL=C" + foundLC = true + } + } + if !foundLC { + env = append(env, "LC_ALL=C") + } + return env +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/common_darwin.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_darwin.go new file mode 100644 index 000000000..f1a784597 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_darwin.go @@ -0,0 +1,66 @@ +//go:build darwin +// +build darwin + +package common + +import ( + "context" + "os" + "os/exec" + "strings" + "unsafe" + + "golang.org/x/sys/unix" +) + +func DoSysctrlWithContext(ctx context.Context, mib string) ([]string, error) { + cmd := exec.CommandContext(ctx, "sysctl", "-n", mib) + cmd.Env = getSysctrlEnv(os.Environ()) + out, err := cmd.Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} + +func CallSyscall(mib []int32) ([]byte, uint64, error) { + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := unix.Syscall6( + 202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146 + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = unix.Syscall6( + 202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146 + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/common_freebsd.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_freebsd.go new file mode 100644 index 000000000..f590e2e67 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_freebsd.go @@ -0,0 +1,82 @@ +//go:build freebsd || openbsd +// +build freebsd openbsd + +package common + +import ( + "fmt" + "os" + "os/exec" + "strings" + "unsafe" + + "golang.org/x/sys/unix" +) + +func SysctlUint(mib string) (uint64, error) { + buf, err := unix.SysctlRaw(mib) + if err != nil { + return 0, err + } + if len(buf) == 8 { // 64 bit + return *(*uint64)(unsafe.Pointer(&buf[0])), nil + } + if len(buf) == 4 { // 32bit + t := *(*uint32)(unsafe.Pointer(&buf[0])) + return uint64(t), nil + } + return 0, fmt.Errorf("unexpected size: %s, %d", mib, len(buf)) +} + +func DoSysctrl(mib string) ([]string, error) { + cmd := exec.Command("sysctl", "-n", mib) + cmd.Env = getSysctrlEnv(os.Environ()) + out, err := cmd.Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} + +func CallSyscall(mib []int32) ([]byte, uint64, error) { + mibptr := unsafe.Pointer(&mib[0]) + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/common_linux.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_linux.go new file mode 100644 index 000000000..a429e16a2 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_linux.go @@ -0,0 +1,353 @@ +//go:build linux +// +build linux + +package common + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "sync" + "sync/atomic" + "syscall" + "time" +) + +// cachedBootTime must be accessed via atomic.Load/StoreUint64 +var cachedBootTime uint64 + +func DoSysctrl(mib string) ([]string, error) { + cmd := exec.Command("sysctl", "-n", mib) + cmd.Env = getSysctrlEnv(os.Environ()) + out, err := cmd.Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} + +func NumProcs() (uint64, error) { + return NumProcsWithContext(context.Background()) +} + +func NumProcsWithContext(ctx context.Context) (uint64, error) { + f, err := os.Open(HostProcWithContext(ctx)) + if err != nil { + return 0, err + } + defer f.Close() + + list, err := f.Readdirnames(-1) + if err != nil { + return 0, err + } + var cnt uint64 + + for _, v := range list { + if _, err = strconv.ParseUint(v, 10, 64); err == nil { + cnt++ + } + } + + return cnt, nil +} + +func BootTimeWithContext(ctx context.Context, enableCache bool) (uint64, error) { + if enableCache { + t := atomic.LoadUint64(&cachedBootTime) + if t != 0 { + return t, nil + } + } + + system, role, err := VirtualizationWithContext(ctx) + if err != nil { + return 0, err + } + + useStatFile := true + if system == "lxc" && role == "guest" { + // if lxc, /proc/uptime is used. + useStatFile = false + } else if system == "docker" && role == "guest" { + // also docker, guest + useStatFile = false + } + + if useStatFile { + t, err := readBootTimeStat(ctx) + if err != nil { + return 0, err + } + if enableCache { + atomic.StoreUint64(&cachedBootTime, t) + } + } + + filename := HostProcWithContext(ctx, "uptime") + lines, err := ReadLines(filename) + if err != nil { + return handleBootTimeFileReadErr(err) + } + if len(lines) != 1 { + return 0, fmt.Errorf("wrong uptime format") + } + f := strings.Fields(lines[0]) + b, err := strconv.ParseFloat(f[0], 64) + if err != nil { + return 0, err + } + currentTime := float64(time.Now().UnixNano()) / float64(time.Second) + t := currentTime - b + + if enableCache { + atomic.StoreUint64(&cachedBootTime, uint64(t)) + } + + return uint64(t), nil +} + +func handleBootTimeFileReadErr(err error) (uint64, error) { + if os.IsPermission(err) { + var info syscall.Sysinfo_t + err := syscall.Sysinfo(&info) + if err != nil { + return 0, err + } + + currentTime := time.Now().UnixNano() / int64(time.Second) + t := currentTime - int64(info.Uptime) + return uint64(t), nil + } + return 0, err +} + +func readBootTimeStat(ctx context.Context) (uint64, error) { + filename := HostProcWithContext(ctx, "stat") + line, err := ReadLine(filename, "btime") + if err != nil { + return handleBootTimeFileReadErr(err) + } + if strings.HasPrefix(line, "btime") { + f := strings.Fields(line) + if len(f) != 2 { + return 0, fmt.Errorf("wrong btime format") + } + b, err := strconv.ParseInt(f[1], 10, 64) + if err != nil { + return 0, err + } + t := uint64(b) + return t, nil + } + return 0, fmt.Errorf("could not find btime") +} + +func Virtualization() (string, string, error) { + return VirtualizationWithContext(context.Background()) +} + +// required variables for concurrency safe virtualization caching +var ( + cachedVirtMap map[string]string + cachedVirtMutex sync.RWMutex + cachedVirtOnce sync.Once +) + +func VirtualizationWithContext(ctx context.Context) (string, string, error) { + var system, role string + + // if cached already, return from cache + cachedVirtMutex.RLock() // unlock won't be deferred so concurrent reads don't wait for long + if cachedVirtMap != nil { + cachedSystem, cachedRole := cachedVirtMap["system"], cachedVirtMap["role"] + cachedVirtMutex.RUnlock() + return cachedSystem, cachedRole, nil + } + cachedVirtMutex.RUnlock() + + filename := HostProcWithContext(ctx, "xen") + if PathExists(filename) { + system = "xen" + role = "guest" // assume guest + + if PathExists(filepath.Join(filename, "capabilities")) { + contents, err := ReadLines(filepath.Join(filename, "capabilities")) + if err == nil { + if StringsContains(contents, "control_d") { + role = "host" + } + } + } + } + + filename = HostProcWithContext(ctx, "modules") + if PathExists(filename) { + contents, err := ReadLines(filename) + if err == nil { + if StringsContains(contents, "kvm") { + system = "kvm" + role = "host" + } else if StringsContains(contents, "hv_util") { + system = "hyperv" + role = "guest" + } else if StringsContains(contents, "vboxdrv") { + system = "vbox" + role = "host" + } else if StringsContains(contents, "vboxguest") { + system = "vbox" + role = "guest" + } else if StringsContains(contents, "vmware") { + system = "vmware" + role = "guest" + } + } + } + + filename = HostProcWithContext(ctx, "cpuinfo") + if PathExists(filename) { + contents, err := ReadLines(filename) + if err == nil { + if StringsContains(contents, "QEMU Virtual CPU") || + StringsContains(contents, "Common KVM processor") || + StringsContains(contents, "Common 32-bit KVM processor") { + system = "kvm" + role = "guest" + } + } + } + + filename = HostProcWithContext(ctx, "bus/pci/devices") + if PathExists(filename) { + contents, err := ReadLines(filename) + if err == nil { + if StringsContains(contents, "virtio-pci") { + role = "guest" + } + } + } + + filename = HostProcWithContext(ctx) + if PathExists(filepath.Join(filename, "bc", "0")) { + system = "openvz" + role = "host" + } else if PathExists(filepath.Join(filename, "vz")) { + system = "openvz" + role = "guest" + } + + // not use dmidecode because it requires root + if PathExists(filepath.Join(filename, "self", "status")) { + contents, err := ReadLines(filepath.Join(filename, "self", "status")) + if err == nil { + if StringsContains(contents, "s_context:") || + StringsContains(contents, "VxID:") { + system = "linux-vserver" + } + // TODO: guest or host + } + } + + if PathExists(filepath.Join(filename, "1", "environ")) { + contents, err := ReadFile(filepath.Join(filename, "1", "environ")) + + if err == nil { + if strings.Contains(contents, "container=lxc") { + system = "lxc" + role = "guest" + } + } + } + + if PathExists(filepath.Join(filename, "self", "cgroup")) { + contents, err := ReadLines(filepath.Join(filename, "self", "cgroup")) + if err == nil { + if StringsContains(contents, "lxc") { + system = "lxc" + role = "guest" + } else if StringsContains(contents, "docker") { + system = "docker" + role = "guest" + } else if StringsContains(contents, "machine-rkt") { + system = "rkt" + role = "guest" + } else if PathExists("/usr/bin/lxc-version") { + system = "lxc" + role = "host" + } + } + } + + if PathExists(HostEtcWithContext(ctx, "os-release")) { + p, _, err := GetOSReleaseWithContext(ctx) + if err == nil && p == "coreos" { + system = "rkt" // Is it true? + role = "host" + } + } + + if PathExists(HostRootWithContext(ctx, ".dockerenv")) { + system = "docker" + role = "guest" + } + + // before returning for the first time, cache the system and role + cachedVirtOnce.Do(func() { + cachedVirtMutex.Lock() + defer cachedVirtMutex.Unlock() + cachedVirtMap = map[string]string{ + "system": system, + "role": role, + } + }) + + return system, role, nil +} + +func GetOSRelease() (platform string, version string, err error) { + return GetOSReleaseWithContext(context.Background()) +} + +func GetOSReleaseWithContext(ctx context.Context) (platform string, version string, err error) { + contents, err := ReadLines(HostEtcWithContext(ctx, "os-release")) + if err != nil { + return "", "", nil // return empty + } + for _, line := range contents { + field := strings.Split(line, "=") + if len(field) < 2 { + continue + } + switch field[0] { + case "ID": // use ID for lowercase + platform = trimQuotes(field[1]) + case "VERSION_ID": + version = trimQuotes(field[1]) + } + } + + // cleanup amazon ID + if platform == "amzn" { + platform = "amazon" + } + + return platform, version, nil +} + +// Remove quotes of the source string +func trimQuotes(s string) string { + if len(s) >= 2 { + if s[0] == '"' && s[len(s)-1] == '"' { + return s[1 : len(s)-1] + } + } + return s +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/common_netbsd.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_netbsd.go new file mode 100644 index 000000000..efbc710a5 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_netbsd.go @@ -0,0 +1,66 @@ +//go:build netbsd +// +build netbsd + +package common + +import ( + "os" + "os/exec" + "strings" + "unsafe" + + "golang.org/x/sys/unix" +) + +func DoSysctrl(mib string) ([]string, error) { + cmd := exec.Command("sysctl", "-n", mib) + cmd.Env = getSysctrlEnv(os.Environ()) + out, err := cmd.Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} + +func CallSyscall(mib []int32) ([]byte, uint64, error) { + mibptr := unsafe.Pointer(&mib[0]) + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/common_openbsd.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_openbsd.go new file mode 100644 index 000000000..58d76f334 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_openbsd.go @@ -0,0 +1,66 @@ +//go:build openbsd +// +build openbsd + +package common + +import ( + "os" + "os/exec" + "strings" + "unsafe" + + "golang.org/x/sys/unix" +) + +func DoSysctrl(mib string) ([]string, error) { + cmd := exec.Command("sysctl", "-n", mib) + cmd.Env = getSysctrlEnv(os.Environ()) + out, err := cmd.Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} + +func CallSyscall(mib []int32) ([]byte, uint64, error) { + mibptr := unsafe.Pointer(&mib[0]) + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/common_unix.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_unix.go new file mode 100644 index 000000000..4af7e5c2a --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_unix.go @@ -0,0 +1,62 @@ +//go:build linux || freebsd || darwin || openbsd +// +build linux freebsd darwin openbsd + +package common + +import ( + "context" + "errors" + "os/exec" + "strconv" + "strings" +) + +func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ...string) ([]string, error) { + var cmd []string + if pid == 0 { // will get from all processes. + cmd = []string{"-a", "-n", "-P"} + } else { + cmd = []string{"-a", "-n", "-P", "-p", strconv.Itoa(int(pid))} + } + cmd = append(cmd, args...) + out, err := invoke.CommandWithContext(ctx, "lsof", cmd...) + if err != nil { + if errors.Is(err, exec.ErrNotFound) { + return []string{}, err + } + // if no pid found, lsof returns code 1. + if err.Error() == "exit status 1" && len(out) == 0 { + return []string{}, nil + } + } + lines := strings.Split(string(out), "\n") + + var ret []string + for _, l := range lines[1:] { + if len(l) == 0 { + continue + } + ret = append(ret, l) + } + 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 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/common_windows.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_windows.go new file mode 100644 index 000000000..301b2315b --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/common_windows.go @@ -0,0 +1,304 @@ +//go:build windows +// +build windows + +package common + +import ( + "context" + "fmt" + "path/filepath" + "reflect" + "strings" + "syscall" + "unsafe" + + "github.com/yusufpapurcu/wmi" + "golang.org/x/sys/windows" +) + +// for double values +type PDH_FMT_COUNTERVALUE_DOUBLE struct { + CStatus uint32 + DoubleValue float64 +} + +// for 64 bit integer values +type PDH_FMT_COUNTERVALUE_LARGE struct { + CStatus uint32 + LargeValue int64 +} + +// for long values +type PDH_FMT_COUNTERVALUE_LONG struct { + CStatus uint32 + LongValue int32 + padding [4]byte +} + +// windows system const +const ( + ERROR_SUCCESS = 0 + ERROR_FILE_NOT_FOUND = 2 + DRIVE_REMOVABLE = 2 + DRIVE_FIXED = 3 + HKEY_LOCAL_MACHINE = 0x80000002 + RRF_RT_REG_SZ = 0x00000002 + RRF_RT_REG_DWORD = 0x00000010 + PDH_FMT_LONG = 0x00000100 + PDH_FMT_DOUBLE = 0x00000200 + PDH_FMT_LARGE = 0x00000400 + PDH_INVALID_DATA = 0xc0000bc6 + PDH_INVALID_HANDLE = 0xC0000bbc + PDH_NO_DATA = 0x800007d5 + + STATUS_BUFFER_OVERFLOW = 0x80000005 + STATUS_BUFFER_TOO_SMALL = 0xC0000023 + STATUS_INFO_LENGTH_MISMATCH = 0xC0000004 +) + +const ( + ProcessBasicInformation = 0 + ProcessWow64Information = 26 + ProcessQueryInformation = windows.PROCESS_DUP_HANDLE | windows.PROCESS_QUERY_INFORMATION + + SystemExtendedHandleInformationClass = 64 +) + +var ( + Modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + ModNt = windows.NewLazySystemDLL("ntdll.dll") + ModPdh = windows.NewLazySystemDLL("pdh.dll") + ModPsapi = windows.NewLazySystemDLL("psapi.dll") + + ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes") + ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation") + ProcRtlGetNativeSystemInformation = ModNt.NewProc("RtlGetNativeSystemInformation") + ProcRtlNtStatusToDosError = ModNt.NewProc("RtlNtStatusToDosError") + ProcNtQueryInformationProcess = ModNt.NewProc("NtQueryInformationProcess") + ProcNtReadVirtualMemory = ModNt.NewProc("NtReadVirtualMemory") + ProcNtWow64QueryInformationProcess64 = ModNt.NewProc("NtWow64QueryInformationProcess64") + ProcNtWow64ReadVirtualMemory64 = ModNt.NewProc("NtWow64ReadVirtualMemory64") + + PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery") + PdhAddEnglishCounterW = ModPdh.NewProc("PdhAddEnglishCounterW") + PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData") + PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue") + PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery") + + procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW") +) + +type FILETIME struct { + DwLowDateTime uint32 + DwHighDateTime uint32 +} + +// borrowed from net/interface_windows.go +func BytePtrToString(p *uint8) string { + a := (*[10000]uint8)(unsafe.Pointer(p)) + i := 0 + for a[i] != 0 { + i++ + } + return string(a[:i]) +} + +// CounterInfo struct is used to track a windows performance counter +// copied from https://github.com/mackerelio/mackerel-agent/ +type CounterInfo struct { + PostName string + CounterName string + Counter windows.Handle +} + +// CreateQuery with a PdhOpenQuery call +// copied from https://github.com/mackerelio/mackerel-agent/ +func CreateQuery() (windows.Handle, error) { + var query windows.Handle + r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query))) + if r != 0 { + return 0, err + } + return query, nil +} + +// CreateCounter with a PdhAddEnglishCounterW call +func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, error) { + var counter windows.Handle + r, _, err := PdhAddEnglishCounterW.Call( + uintptr(query), + uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cname))), + 0, + uintptr(unsafe.Pointer(&counter))) + if r != 0 { + return nil, err + } + return &CounterInfo{ + PostName: pname, + CounterName: cname, + Counter: counter, + }, nil +} + +// GetCounterValue get counter value from handle +// adapted from https://github.com/mackerelio/mackerel-agent/ +func GetCounterValue(counter windows.Handle) (float64, error) { + var value PDH_FMT_COUNTERVALUE_DOUBLE + r, _, err := PdhGetFormattedCounterValue.Call(uintptr(counter), PDH_FMT_DOUBLE, uintptr(0), uintptr(unsafe.Pointer(&value))) + if r != 0 && r != PDH_INVALID_DATA { + return 0.0, err + } + return value.DoubleValue, nil +} + +type Win32PerformanceCounter struct { + PostName string + CounterName string + Query windows.Handle + Counter windows.Handle +} + +func NewWin32PerformanceCounter(postName, counterName string) (*Win32PerformanceCounter, error) { + query, err := CreateQuery() + if err != nil { + return nil, err + } + counter := Win32PerformanceCounter{ + Query: query, + PostName: postName, + CounterName: counterName, + } + r, _, err := PdhAddEnglishCounterW.Call( + uintptr(counter.Query), + uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(counter.CounterName))), + 0, + uintptr(unsafe.Pointer(&counter.Counter)), + ) + if r != 0 { + return nil, err + } + return &counter, nil +} + +func (w *Win32PerformanceCounter) GetValue() (float64, error) { + r, _, err := PdhCollectQueryData.Call(uintptr(w.Query)) + if r != 0 && err != nil { + if r == PDH_NO_DATA { + return 0.0, fmt.Errorf("%w: this counter has not data", err) + } + return 0.0, err + } + + return GetCounterValue(w.Counter) +} + +func ProcessorQueueLengthCounter() (*Win32PerformanceCounter, error) { + return NewWin32PerformanceCounter("processor_queue_length", `\System\Processor Queue Length`) +} + +// WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging +func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error { + if _, ok := ctx.Deadline(); !ok { + ctxTimeout, cancel := context.WithTimeout(ctx, Timeout) + defer cancel() + ctx = ctxTimeout + } + + errChan := make(chan error, 1) + go func() { + errChan <- wmi.Query(query, dst, connectServerArgs...) + }() + + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-errChan: + return err + } +} + +// Convert paths using native DOS format like: +// +// "\Device\HarddiskVolume1\Windows\systemew\file.txt" +// +// into: +// +// "C:\Windows\systemew\file.txt" +func ConvertDOSPath(p string) string { + rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`) + + for d := 'A'; d <= 'Z'; d++ { + szDeviceName := string(d) + ":" + szTarget := make([]uint16, 512) + ret, _, _ := procQueryDosDeviceW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(szDeviceName))), + uintptr(unsafe.Pointer(&szTarget[0])), + uintptr(len(szTarget))) + if ret != 0 && windows.UTF16ToString(szTarget[:]) == rawDrive { + return filepath.Join(szDeviceName, p[len(rawDrive):]) + } + } + return p +} + +type NtStatus uint32 + +func (s NtStatus) Error() error { + if s == 0 { + return nil + } + return fmt.Errorf("NtStatus 0x%08x", uint32(s)) +} + +func (s NtStatus) IsError() bool { + return s>>30 == 3 +} + +type SystemExtendedHandleTableEntryInformation struct { + Object uintptr + UniqueProcessId uintptr + HandleValue uintptr + GrantedAccess uint32 + CreatorBackTraceIndex uint16 + ObjectTypeIndex uint16 + HandleAttributes uint32 + Reserved uint32 +} + +type SystemExtendedHandleInformation struct { + NumberOfHandles uintptr + Reserved uintptr + Handles [1]SystemExtendedHandleTableEntryInformation +} + +// CallWithExpandingBuffer https://github.com/hillu/go-ntdll +func CallWithExpandingBuffer(fn func() NtStatus, buf *[]byte, resultLength *uint32) NtStatus { + for { + if st := fn(); st == STATUS_BUFFER_OVERFLOW || st == STATUS_BUFFER_TOO_SMALL || st == STATUS_INFO_LENGTH_MISMATCH { + if int(*resultLength) <= cap(*buf) { + (*reflect.SliceHeader)(unsafe.Pointer(buf)).Len = int(*resultLength) + } else { + *buf = make([]byte, int(*resultLength)) + } + continue + } else { + if !st.IsError() { + *buf = (*buf)[:int(*resultLength)] + } + return st + } + } +} + +func NtQuerySystemInformation( + SystemInformationClass uint32, + SystemInformation *byte, + SystemInformationLength uint32, + ReturnLength *uint32, +) NtStatus { + r0, _, _ := ProcNtQuerySystemInformation.Call( + uintptr(SystemInformationClass), + uintptr(unsafe.Pointer(SystemInformation)), + uintptr(SystemInformationLength), + uintptr(unsafe.Pointer(ReturnLength))) + return NtStatus(r0) +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/endian.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/endian.go new file mode 100644 index 000000000..147cfdc4b --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/endian.go @@ -0,0 +1,10 @@ +package common + +import "unsafe" + +// IsLittleEndian checks if the current platform uses little-endian. +// copied from https://github.com/ntrrg/ntgo/blob/v0.8.0/runtime/infrastructure.go#L16 (MIT License) +func IsLittleEndian() bool { + var x int16 = 0x0011 + return *(*byte)(unsafe.Pointer(&x)) == 0x11 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/sleep.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/sleep.go new file mode 100644 index 000000000..9bed2419e --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/sleep.go @@ -0,0 +1,21 @@ +package common + +import ( + "context" + "time" +) + +// Sleep awaits for provided interval. +// Can be interrupted by context cancelation. +func Sleep(ctx context.Context, interval time.Duration) error { + timer := time.NewTimer(interval) + select { + case <-ctx.Done(): + if !timer.Stop() { + <-timer.C + } + return ctx.Err() + case <-timer.C: + return nil + } +} diff --git a/vendor/github.com/shirou/gopsutil/v3/internal/common/warnings.go b/vendor/github.com/shirou/gopsutil/v3/internal/common/warnings.go new file mode 100644 index 000000000..a4aaadaf5 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/internal/common/warnings.go @@ -0,0 +1,30 @@ +package common + +import "fmt" + +type Warnings struct { + List []error + Verbose bool +} + +func (w *Warnings) Add(err error) { + w.List = append(w.List, err) +} + +func (w *Warnings) Reference() error { + if len(w.List) > 0 { + return w + } + return nil +} + +func (w *Warnings) Error() string { + if w.Verbose { + str := "" + for i, e := range w.List { + str += fmt.Sprintf("\tError %d: %s\n", i, e.Error()) + } + return str + } + return fmt.Sprintf("Number of warnings: %v", len(w.List)) +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem.go new file mode 100644 index 000000000..edaf268bb --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem.go @@ -0,0 +1,120 @@ +package mem + +import ( + "encoding/json" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +var invoke common.Invoker = common.Invoke{} + +// Memory usage statistics. Total, Available and Used contain numbers of bytes +// for human consumption. +// +// The other fields in this struct contain kernel specific values. +type VirtualMemoryStat struct { + // Total amount of RAM on this system + Total uint64 `json:"total"` + + // RAM available for programs to allocate + // + // This value is computed from the kernel specific values. + Available uint64 `json:"available"` + + // RAM used by programs + // + // This value is computed from the kernel specific values. + Used uint64 `json:"used"` + + // Percentage of RAM used by programs + // + // This value is computed from the kernel specific values. + UsedPercent float64 `json:"usedPercent"` + + // This is the kernel's notion of free memory; RAM chips whose bits nobody + // cares about the value of right now. For a human consumable number, + // Available is what you really want. + Free uint64 `json:"free"` + + // OS X / BSD specific numbers: + // http://www.macyourself.com/2010/02/17/what-is-free-wired-active-and-inactive-system-memory-ram/ + Active uint64 `json:"active"` + Inactive uint64 `json:"inactive"` + Wired uint64 `json:"wired"` + + // FreeBSD specific numbers: + // https://reviews.freebsd.org/D8467 + Laundry uint64 `json:"laundry"` + + // Linux specific numbers + // https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-proc-meminfo.html + // https://www.kernel.org/doc/Documentation/filesystems/proc.txt + // https://www.kernel.org/doc/Documentation/vm/overcommit-accounting + // https://www.kernel.org/doc/Documentation/vm/transhuge.txt + Buffers uint64 `json:"buffers"` + Cached uint64 `json:"cached"` + WriteBack uint64 `json:"writeBack"` + Dirty uint64 `json:"dirty"` + WriteBackTmp uint64 `json:"writeBackTmp"` + Shared uint64 `json:"shared"` + Slab uint64 `json:"slab"` + Sreclaimable uint64 `json:"sreclaimable"` + Sunreclaim uint64 `json:"sunreclaim"` + PageTables uint64 `json:"pageTables"` + SwapCached uint64 `json:"swapCached"` + CommitLimit uint64 `json:"commitLimit"` + CommittedAS uint64 `json:"committedAS"` + HighTotal uint64 `json:"highTotal"` + HighFree uint64 `json:"highFree"` + LowTotal uint64 `json:"lowTotal"` + LowFree uint64 `json:"lowFree"` + SwapTotal uint64 `json:"swapTotal"` + SwapFree uint64 `json:"swapFree"` + Mapped uint64 `json:"mapped"` + VmallocTotal uint64 `json:"vmallocTotal"` + VmallocUsed uint64 `json:"vmallocUsed"` + VmallocChunk uint64 `json:"vmallocChunk"` + HugePagesTotal uint64 `json:"hugePagesTotal"` + HugePagesFree uint64 `json:"hugePagesFree"` + HugePagesRsvd uint64 `json:"hugePagesRsvd"` + HugePagesSurp uint64 `json:"hugePagesSurp"` + HugePageSize uint64 `json:"hugePageSize"` + AnonHugePages uint64 `json:"anonHugePages"` +} + +type SwapMemoryStat struct { + Total uint64 `json:"total"` + Used uint64 `json:"used"` + Free uint64 `json:"free"` + UsedPercent float64 `json:"usedPercent"` + Sin uint64 `json:"sin"` + Sout uint64 `json:"sout"` + PgIn uint64 `json:"pgIn"` + PgOut uint64 `json:"pgOut"` + PgFault uint64 `json:"pgFault"` + + // Linux specific numbers + // https://www.kernel.org/doc/Documentation/cgroup-v2.txt + PgMajFault uint64 `json:"pgMajFault"` +} + +func (m VirtualMemoryStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +func (m SwapMemoryStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +type SwapDevice struct { + Name string `json:"name"` + UsedBytes uint64 `json:"usedBytes"` + FreeBytes uint64 `json:"freeBytes"` +} + +func (m SwapDevice) String() string { + s, _ := json.Marshal(m) + return string(s) +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_aix.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_aix.go new file mode 100644 index 000000000..22a6a4e92 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_aix.go @@ -0,0 +1,16 @@ +//go:build aix +// +build aix + +package mem + +import ( + "context" +) + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_aix_cgo.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_aix_cgo.go new file mode 100644 index 000000000..67e11dff8 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_aix_cgo.go @@ -0,0 +1,51 @@ +//go:build aix && cgo +// +build aix,cgo + +package mem + +import ( + "context" + + "github.com/power-devops/perfstat" +) + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + m, err := perfstat.MemoryTotalStat() + if err != nil { + return nil, err + } + pagesize := uint64(4096) + ret := VirtualMemoryStat{ + Total: uint64(m.RealTotal) * pagesize, + Available: uint64(m.RealAvailable) * pagesize, + Free: uint64(m.RealFree) * pagesize, + Used: uint64(m.RealInUse) * pagesize, + UsedPercent: 100 * float64(m.RealInUse) / float64(m.RealTotal), + Active: uint64(m.VirtualActive) * pagesize, + SwapTotal: uint64(m.PgSpTotal) * pagesize, + SwapFree: uint64(m.PgSpFree) * pagesize, + } + return &ret, nil +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + m, err := perfstat.MemoryTotalStat() + if err != nil { + return nil, err + } + pagesize := uint64(4096) + swapUsed := uint64(m.PgSpTotal-m.PgSpFree-m.PgSpRsvd) * pagesize + swapTotal := uint64(m.PgSpTotal) * pagesize + ret := SwapMemoryStat{ + Total: swapTotal, + Free: uint64(m.PgSpFree) * pagesize, + Used: swapUsed, + UsedPercent: float64(100*swapUsed) / float64(swapTotal), + Sin: uint64(m.PgSpIn), + Sout: uint64(m.PgSpOut), + PgIn: uint64(m.PageIn), + PgOut: uint64(m.PageOut), + PgFault: uint64(m.PageFaults), + } + return &ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_aix_nocgo.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_aix_nocgo.go new file mode 100644 index 000000000..cc6a76d2f --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_aix_nocgo.go @@ -0,0 +1,78 @@ +//go:build aix && !cgo +// +build aix,!cgo + +package mem + +import ( + "context" + "strconv" + "strings" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + vmem, swap, err := callSVMon(ctx) + if err != nil { + return nil, err + } + if vmem.Total == 0 { + return nil, common.ErrNotImplementedError + } + vmem.SwapTotal = swap.Total + vmem.SwapFree = swap.Free + return vmem, nil +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + _, swap, err := callSVMon(ctx) + if err != nil { + return nil, err + } + if swap.Total == 0 { + return nil, common.ErrNotImplementedError + } + return swap, nil +} + +func callSVMon(ctx context.Context) (*VirtualMemoryStat, *SwapMemoryStat, error) { + out, err := invoke.CommandWithContext(ctx, "svmon", "-G") + if err != nil { + return nil, nil, err + } + + pagesize := uint64(4096) + vmem := &VirtualMemoryStat{} + swap := &SwapMemoryStat{} + for _, line := range strings.Split(string(out), "\n") { + if strings.HasPrefix(line, "memory") { + p := strings.Fields(line) + if len(p) > 2 { + if t, err := strconv.ParseUint(p[1], 10, 64); err == nil { + vmem.Total = t * pagesize + } + if t, err := strconv.ParseUint(p[2], 10, 64); err == nil { + vmem.Used = t * pagesize + if vmem.Total > 0 { + vmem.UsedPercent = 100 * float64(vmem.Used) / float64(vmem.Total) + } + } + if t, err := strconv.ParseUint(p[3], 10, 64); err == nil { + vmem.Free = t * pagesize + } + } + } else if strings.HasPrefix(line, "pg space") { + p := strings.Fields(line) + if len(p) > 3 { + if t, err := strconv.ParseUint(p[2], 10, 64); err == nil { + swap.Total = t * pagesize + } + if t, err := strconv.ParseUint(p[3], 10, 64); err == nil { + swap.Free = swap.Total - t*pagesize + } + } + break + } + } + return vmem, swap, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_bsd.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_bsd.go new file mode 100644 index 000000000..ef867d742 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_bsd.go @@ -0,0 +1,87 @@ +//go:build freebsd || openbsd || netbsd +// +build freebsd openbsd netbsd + +package mem + +import ( + "context" + "fmt" + "strconv" + "strings" +) + +const swapCommand = "swapctl" + +// swapctl column indexes +const ( + nameCol = 0 + totalKiBCol = 1 + usedKiBCol = 2 +) + +func SwapDevices() ([]*SwapDevice, error) { + return SwapDevicesWithContext(context.Background()) +} + +func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) { + output, err := invoke.CommandWithContext(ctx, swapCommand, "-lk") + if err != nil { + return nil, fmt.Errorf("could not execute %q: %w", swapCommand, err) + } + + return parseSwapctlOutput(string(output)) +} + +func parseSwapctlOutput(output string) ([]*SwapDevice, error) { + lines := strings.Split(output, "\n") + if len(lines) == 0 { + return nil, fmt.Errorf("could not parse output of %q: no lines in %q", swapCommand, output) + } + + // Check header headerFields are as expected. + header := lines[0] + header = strings.ToLower(header) + header = strings.ReplaceAll(header, ":", "") + headerFields := strings.Fields(header) + if len(headerFields) < usedKiBCol { + return nil, fmt.Errorf("couldn't parse %q: too few fields in header %q", swapCommand, header) + } + if headerFields[nameCol] != "device" { + return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[nameCol], "device") + } + if headerFields[totalKiBCol] != "1kb-blocks" && headerFields[totalKiBCol] != "1k-blocks" { + return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[totalKiBCol], "1kb-blocks") + } + if headerFields[usedKiBCol] != "used" { + return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[usedKiBCol], "used") + } + + var swapDevices []*SwapDevice + for _, line := range lines[1:] { + if line == "" { + continue // the terminal line is typically empty + } + fields := strings.Fields(line) + if len(fields) < usedKiBCol { + return nil, fmt.Errorf("couldn't parse %q: too few fields", swapCommand) + } + + totalKiB, err := strconv.ParseUint(fields[totalKiBCol], 10, 64) + if err != nil { + return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapCommand, err) + } + + usedKiB, err := strconv.ParseUint(fields[usedKiBCol], 10, 64) + if err != nil { + return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapCommand, err) + } + + swapDevices = append(swapDevices, &SwapDevice{ + Name: fields[nameCol], + UsedBytes: usedKiB * 1024, + FreeBytes: (totalKiB - usedKiB) * 1024, + }) + } + + return swapDevices, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin.go new file mode 100644 index 000000000..a05a0faba --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin.go @@ -0,0 +1,72 @@ +//go:build darwin +// +build darwin + +package mem + +import ( + "context" + "fmt" + "unsafe" + + "golang.org/x/sys/unix" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +func getHwMemsize() (uint64, error) { + total, err := unix.SysctlUint64("hw.memsize") + if err != nil { + return 0, err + } + return total, nil +} + +// xsw_usage in sys/sysctl.h +type swapUsage struct { + Total uint64 + Avail uint64 + Used uint64 + Pagesize int32 + Encrypted bool +} + +// SwapMemory returns swapinfo. +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + // https://github.com/yanllearnn/go-osstat/blob/ae8a279d26f52ec946a03698c7f50a26cfb427e3/memory/memory_darwin.go + var ret *SwapMemoryStat + + value, err := unix.SysctlRaw("vm.swapusage") + if err != nil { + return ret, err + } + if len(value) != 32 { + return ret, fmt.Errorf("unexpected output of sysctl vm.swapusage: %v (len: %d)", value, len(value)) + } + swap := (*swapUsage)(unsafe.Pointer(&value[0])) + + u := float64(0) + if swap.Total != 0 { + u = ((float64(swap.Total) - float64(swap.Avail)) / float64(swap.Total)) * 100.0 + } + + ret = &SwapMemoryStat{ + Total: swap.Total, + Used: swap.Used, + Free: swap.Avail, + UsedPercent: u, + } + + return ret, nil +} + +func SwapDevices() ([]*SwapDevice, error) { + return SwapDevicesWithContext(context.Background()) +} + +func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) { + return nil, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin_cgo.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin_cgo.go new file mode 100644 index 000000000..e5da7dcdb --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin_cgo.go @@ -0,0 +1,58 @@ +//go:build darwin && cgo +// +build darwin,cgo + +package mem + +/* +#include +#include +*/ +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 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin_nocgo.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin_nocgo.go new file mode 100644 index 000000000..c93931680 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_darwin_nocgo.go @@ -0,0 +1,89 @@ +//go:build darwin && !cgo +// +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 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_fallback.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_fallback.go new file mode 100644 index 000000000..697fd8709 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_fallback.go @@ -0,0 +1,34 @@ +//go:build !darwin && !linux && !freebsd && !openbsd && !solaris && !windows && !plan9 && !aix && !netbsd +// +build !darwin,!linux,!freebsd,!openbsd,!solaris,!windows,!plan9,!aix,!netbsd + +package mem + +import ( + "context" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + return nil, common.ErrNotImplementedError +} + +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + return nil, common.ErrNotImplementedError +} + +func SwapDevices() ([]*SwapDevice, error) { + return SwapDevicesWithContext(context.Background()) +} + +func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) { + return nil, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_freebsd.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_freebsd.go new file mode 100644 index 000000000..9a56785b3 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_freebsd.go @@ -0,0 +1,167 @@ +//go:build freebsd +// +build freebsd + +package mem + +import ( + "context" + "errors" + "unsafe" + + "github.com/shirou/gopsutil/v3/internal/common" + "golang.org/x/sys/unix" +) + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + pageSize, err := common.SysctlUint("vm.stats.vm.v_page_size") + if err != nil { + return nil, err + } + physmem, err := common.SysctlUint("hw.physmem") + if err != nil { + return nil, err + } + + free, err := common.SysctlUint("vm.stats.vm.v_free_count") + if err != nil { + return nil, err + } + active, err := common.SysctlUint("vm.stats.vm.v_active_count") + if err != nil { + return nil, err + } + inactive, err := common.SysctlUint("vm.stats.vm.v_inactive_count") + if err != nil { + return nil, err + } + buffers, err := common.SysctlUint("vfs.bufspace") + if err != nil { + return nil, err + } + wired, err := common.SysctlUint("vm.stats.vm.v_wire_count") + if err != nil { + return nil, err + } + var cached, laundry uint64 + osreldate, _ := common.SysctlUint("kern.osreldate") + if osreldate < 1102000 { + cached, err = common.SysctlUint("vm.stats.vm.v_cache_count") + if err != nil { + return nil, err + } + } else { + laundry, err = common.SysctlUint("vm.stats.vm.v_laundry_count") + if err != nil { + return nil, err + } + } + + p := pageSize + ret := &VirtualMemoryStat{ + Total: physmem, + Free: free * p, + Active: active * p, + Inactive: inactive * p, + Cached: cached * p, + Buffers: buffers, + Wired: wired * p, + Laundry: laundry * p, + } + + ret.Available = ret.Inactive + ret.Cached + ret.Free + ret.Laundry + ret.Used = ret.Total - ret.Available + ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 + + return ret, nil +} + +// Return swapinfo +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +// Constants from vm/vm_param.h +// nolint: golint +const ( + XSWDEV_VERSION11 = 1 + XSWDEV_VERSION = 2 +) + +// Types from vm/vm_param.h +type xswdev struct { + Version uint32 // Version is the version + Dev uint64 // Dev is the device identifier + Flags int32 // Flags is the swap flags applied to the device + NBlks int32 // NBlks is the total number of blocks + Used int32 // Used is the number of blocks used +} + +// xswdev11 is a compatibility for under FreeBSD 11 +// sys/vm/swap_pager.c +type xswdev11 struct { + Version uint32 // Version is the version + Dev uint32 // Dev is the device identifier + Flags int32 // Flags is the swap flags applied to the device + NBlks int32 // NBlks is the total number of blocks + Used int32 // Used is the number of blocks used +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + // FreeBSD can have multiple swap devices so we total them up + i, err := common.SysctlUint("vm.nswapdev") + if err != nil { + return nil, err + } + + if i == 0 { + return nil, errors.New("no swap devices found") + } + + c := int(i) + + i, err = common.SysctlUint("vm.stats.vm.v_page_size") + if err != nil { + return nil, err + } + pageSize := i + + var buf []byte + s := &SwapMemoryStat{} + for n := 0; n < c; n++ { + buf, err = unix.SysctlRaw("vm.swap_info", n) + if err != nil { + return nil, err + } + + // first, try to parse with version 2 + xsw := (*xswdev)(unsafe.Pointer(&buf[0])) + if xsw.Version == XSWDEV_VERSION11 { + // this is version 1, so try to parse again + xsw := (*xswdev11)(unsafe.Pointer(&buf[0])) + if xsw.Version != XSWDEV_VERSION11 { + return nil, errors.New("xswdev version mismatch(11)") + } + s.Total += uint64(xsw.NBlks) + s.Used += uint64(xsw.Used) + } else if xsw.Version != XSWDEV_VERSION { + return nil, errors.New("xswdev version mismatch") + } else { + s.Total += uint64(xsw.NBlks) + s.Used += uint64(xsw.Used) + } + + } + + if s.Total != 0 { + s.UsedPercent = float64(s.Used) / float64(s.Total) * 100 + } + s.Total *= pageSize + s.Used *= pageSize + s.Free = s.Total - s.Used + + return s, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_linux.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_linux.go new file mode 100644 index 000000000..214a91e47 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_linux.go @@ -0,0 +1,532 @@ +//go:build linux +// +build linux + +package mem + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "io" + "math" + "os" + "strconv" + "strings" + + "golang.org/x/sys/unix" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +type VirtualMemoryExStat struct { + ActiveFile uint64 `json:"activefile"` + InactiveFile uint64 `json:"inactivefile"` + ActiveAnon uint64 `json:"activeanon"` + InactiveAnon uint64 `json:"inactiveanon"` + Unevictable uint64 `json:"unevictable"` +} + +func (v VirtualMemoryExStat) String() string { + s, _ := json.Marshal(v) + return string(s) +} + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + vm, _, err := fillFromMeminfoWithContext(ctx) + if err != nil { + return nil, err + } + return vm, nil +} + +func VirtualMemoryEx() (*VirtualMemoryExStat, error) { + return VirtualMemoryExWithContext(context.Background()) +} + +func VirtualMemoryExWithContext(ctx context.Context) (*VirtualMemoryExStat, error) { + _, vmEx, err := fillFromMeminfoWithContext(ctx) + if err != nil { + return nil, err + } + return vmEx, nil +} + +func fillFromMeminfoWithContext(ctx context.Context) (*VirtualMemoryStat, *VirtualMemoryExStat, error) { + filename := common.HostProcWithContext(ctx, "meminfo") + lines, _ := common.ReadLines(filename) + + // flag if MemAvailable is in /proc/meminfo (kernel 3.14+) + memavail := false + activeFile := false // "Active(file)" not available: 2.6.28 / Dec 2008 + inactiveFile := false // "Inactive(file)" not available: 2.6.28 / Dec 2008 + sReclaimable := false // "Sreclaimable:" not available: 2.6.19 / Nov 2006 + + ret := &VirtualMemoryStat{} + retEx := &VirtualMemoryExStat{} + + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) != 2 { + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.TrimSpace(fields[1]) + value = strings.Replace(value, " kB", "", -1) + + switch key { + case "MemTotal": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.Total = t * 1024 + case "MemFree": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.Free = t * 1024 + case "MemAvailable": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + memavail = true + ret.Available = t * 1024 + case "Buffers": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.Buffers = t * 1024 + case "Cached": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.Cached = t * 1024 + case "Active": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.Active = t * 1024 + case "Inactive": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.Inactive = t * 1024 + case "Active(anon)": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + retEx.ActiveAnon = t * 1024 + case "Inactive(anon)": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + retEx.InactiveAnon = t * 1024 + case "Active(file)": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + activeFile = true + retEx.ActiveFile = t * 1024 + case "Inactive(file)": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + inactiveFile = true + retEx.InactiveFile = t * 1024 + case "Unevictable": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + retEx.Unevictable = t * 1024 + case "Writeback": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.WriteBack = t * 1024 + case "WritebackTmp": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.WriteBackTmp = t * 1024 + case "Dirty": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.Dirty = t * 1024 + case "Shmem": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.Shared = t * 1024 + case "Slab": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.Slab = t * 1024 + case "SReclaimable": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + sReclaimable = true + ret.Sreclaimable = t * 1024 + case "SUnreclaim": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.Sunreclaim = t * 1024 + case "PageTables": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.PageTables = t * 1024 + case "SwapCached": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.SwapCached = t * 1024 + case "CommitLimit": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.CommitLimit = t * 1024 + case "Committed_AS": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.CommittedAS = t * 1024 + case "HighTotal": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.HighTotal = t * 1024 + case "HighFree": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.HighFree = t * 1024 + case "LowTotal": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.LowTotal = t * 1024 + case "LowFree": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.LowFree = t * 1024 + case "SwapTotal": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.SwapTotal = t * 1024 + case "SwapFree": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.SwapFree = t * 1024 + case "Mapped": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.Mapped = t * 1024 + case "VmallocTotal": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.VmallocTotal = t * 1024 + case "VmallocUsed": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.VmallocUsed = t * 1024 + case "VmallocChunk": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.VmallocChunk = t * 1024 + case "HugePages_Total": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.HugePagesTotal = t + case "HugePages_Free": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.HugePagesFree = t + case "HugePages_Rsvd": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.HugePagesRsvd = t + case "HugePages_Surp": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.HugePagesSurp = t + case "Hugepagesize": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.HugePageSize = t * 1024 + case "AnonHugePages": + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx, err + } + ret.AnonHugePages = t * 1024 + } + } + + ret.Cached += ret.Sreclaimable + + if !memavail { + if activeFile && inactiveFile && sReclaimable { + ret.Available = calculateAvailVmem(ctx, ret, retEx) + } else { + ret.Available = ret.Cached + ret.Free + } + } + + ret.Used = ret.Total - ret.Free - ret.Buffers - ret.Cached + ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 + + return ret, retEx, nil +} + +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + sysinfo := &unix.Sysinfo_t{} + + if err := unix.Sysinfo(sysinfo); err != nil { + return nil, err + } + ret := &SwapMemoryStat{ + Total: uint64(sysinfo.Totalswap) * uint64(sysinfo.Unit), + Free: uint64(sysinfo.Freeswap) * uint64(sysinfo.Unit), + } + ret.Used = ret.Total - ret.Free + // check Infinity + if ret.Total != 0 { + ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0 + } else { + ret.UsedPercent = 0 + } + filename := common.HostProcWithContext(ctx, "vmstat") + lines, _ := common.ReadLines(filename) + for _, l := range lines { + fields := strings.Fields(l) + if len(fields) < 2 { + continue + } + switch fields[0] { + case "pswpin": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.Sin = value * 4 * 1024 + case "pswpout": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.Sout = value * 4 * 1024 + case "pgpgin": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.PgIn = value * 4 * 1024 + case "pgpgout": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.PgOut = value * 4 * 1024 + case "pgfault": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.PgFault = value * 4 * 1024 + case "pgmajfault": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.PgMajFault = value * 4 * 1024 + } + } + return ret, nil +} + +// calculateAvailVmem is a fallback under kernel 3.14 where /proc/meminfo does not provide +// "MemAvailable:" column. It reimplements an algorithm from the link below +// https://github.com/giampaolo/psutil/pull/890 +func calculateAvailVmem(ctx context.Context, ret *VirtualMemoryStat, retEx *VirtualMemoryExStat) uint64 { + var watermarkLow uint64 + + fn := common.HostProcWithContext(ctx, "zoneinfo") + lines, err := common.ReadLines(fn) + if err != nil { + return ret.Free + ret.Cached // fallback under kernel 2.6.13 + } + + pagesize := uint64(os.Getpagesize()) + watermarkLow = 0 + + for _, line := range lines { + fields := strings.Fields(line) + + if strings.HasPrefix(fields[0], "low") { + lowValue, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + lowValue = 0 + } + watermarkLow += lowValue + } + } + + watermarkLow *= pagesize + + availMemory := ret.Free - watermarkLow + pageCache := retEx.ActiveFile + retEx.InactiveFile + pageCache -= uint64(math.Min(float64(pageCache/2), float64(watermarkLow))) + availMemory += pageCache + availMemory += ret.Sreclaimable - uint64(math.Min(float64(ret.Sreclaimable/2.0), float64(watermarkLow))) + + if availMemory < 0 { + availMemory = 0 + } + + return availMemory +} + +const swapsFilename = "swaps" + +// swaps file column indexes +const ( + nameCol = 0 + // typeCol = 1 + totalCol = 2 + usedCol = 3 + // priorityCol = 4 +) + +func SwapDevices() ([]*SwapDevice, error) { + return SwapDevicesWithContext(context.Background()) +} + +func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) { + swapsFilePath := common.HostProcWithContext(ctx, swapsFilename) + f, err := os.Open(swapsFilePath) + if err != nil { + return nil, err + } + defer f.Close() + + return parseSwapsFile(ctx, f) +} + +func parseSwapsFile(ctx context.Context, r io.Reader) ([]*SwapDevice, error) { + swapsFilePath := common.HostProcWithContext(ctx, swapsFilename) + scanner := bufio.NewScanner(r) + if !scanner.Scan() { + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("couldn't read file %q: %w", swapsFilePath, err) + } + return nil, fmt.Errorf("unexpected end-of-file in %q", swapsFilePath) + + } + + // Check header headerFields are as expected + headerFields := strings.Fields(scanner.Text()) + if len(headerFields) < usedCol { + return nil, fmt.Errorf("couldn't parse %q: too few fields in header", swapsFilePath) + } + if headerFields[nameCol] != "Filename" { + return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[nameCol], "Filename") + } + if headerFields[totalCol] != "Size" { + return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[totalCol], "Size") + } + if headerFields[usedCol] != "Used" { + return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[usedCol], "Used") + } + + var swapDevices []*SwapDevice + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + if len(fields) < usedCol { + return nil, fmt.Errorf("couldn't parse %q: too few fields", swapsFilePath) + } + + totalKiB, err := strconv.ParseUint(fields[totalCol], 10, 64) + if err != nil { + return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapsFilePath, err) + } + + usedKiB, err := strconv.ParseUint(fields[usedCol], 10, 64) + if err != nil { + return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapsFilePath, err) + } + + swapDevices = append(swapDevices, &SwapDevice{ + Name: fields[nameCol], + UsedBytes: usedKiB * 1024, + FreeBytes: (totalKiB - usedKiB) * 1024, + }) + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("couldn't read file %q: %w", swapsFilePath, err) + } + + return swapDevices, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_netbsd.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_netbsd.go new file mode 100644 index 000000000..d1f54ecaf --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_netbsd.go @@ -0,0 +1,87 @@ +//go:build netbsd +// +build netbsd + +package mem + +import ( + "context" + "errors" + "fmt" + + "golang.org/x/sys/unix" +) + +func GetPageSize() (uint64, error) { + return GetPageSizeWithContext(context.Background()) +} + +func GetPageSizeWithContext(ctx context.Context) (uint64, error) { + uvmexp, err := unix.SysctlUvmexp("vm.uvmexp2") + if err != nil { + return 0, err + } + return uint64(uvmexp.Pagesize), nil +} + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + uvmexp, err := unix.SysctlUvmexp("vm.uvmexp2") + if err != nil { + return nil, err + } + p := uint64(uvmexp.Pagesize) + + ret := &VirtualMemoryStat{ + Total: uint64(uvmexp.Npages) * p, + Free: uint64(uvmexp.Free) * p, + Active: uint64(uvmexp.Active) * p, + Inactive: uint64(uvmexp.Inactive) * p, + Cached: 0, // not available + Wired: uint64(uvmexp.Wired) * p, + } + + ret.Available = ret.Inactive + ret.Cached + ret.Free + ret.Used = ret.Total - ret.Available + ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 + + // Get buffers from vm.bufmem sysctl + ret.Buffers, err = unix.SysctlUint64("vm.bufmem") + if err != nil { + return nil, err + } + + return ret, nil +} + +// Return swapctl summary info +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + out, err := invoke.CommandWithContext(ctx, "swapctl", "-sk") + if err != nil { + return &SwapMemoryStat{}, nil + } + + line := string(out) + var total, used, free uint64 + + _, err = fmt.Sscanf(line, + "total: %d 1K-blocks allocated, %d used, %d available", + &total, &used, &free) + if err != nil { + return nil, errors.New("failed to parse swapctl output") + } + + percent := float64(used) / float64(total) * 100 + return &SwapMemoryStat{ + Total: total * 1024, + Used: used * 1024, + Free: free * 1024, + UsedPercent: percent, + }, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd.go new file mode 100644 index 000000000..e37d5abe0 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd.go @@ -0,0 +1,100 @@ +//go:build openbsd +// +build openbsd + +package mem + +import ( + "bytes" + "context" + "encoding/binary" + "errors" + "fmt" + + "github.com/shirou/gopsutil/v3/internal/common" + "golang.org/x/sys/unix" +) + +func GetPageSize() (uint64, error) { + return GetPageSizeWithContext(context.Background()) +} + +func GetPageSizeWithContext(ctx context.Context) (uint64, error) { + uvmexp, err := unix.SysctlUvmexp("vm.uvmexp") + if err != nil { + return 0, err + } + return uint64(uvmexp.Pagesize), nil +} + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + uvmexp, err := unix.SysctlUvmexp("vm.uvmexp") + if err != nil { + return nil, err + } + p := uint64(uvmexp.Pagesize) + + ret := &VirtualMemoryStat{ + Total: uint64(uvmexp.Npages) * p, + Free: uint64(uvmexp.Free) * p, + Active: uint64(uvmexp.Active) * p, + Inactive: uint64(uvmexp.Inactive) * p, + Cached: 0, // not available + Wired: uint64(uvmexp.Wired) * p, + } + + ret.Available = ret.Inactive + ret.Cached + ret.Free + ret.Used = ret.Total - ret.Available + ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 + + mib := []int32{CTLVfs, VfsGeneric, VfsBcacheStat} + buf, length, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + if length < sizeOfBcachestats { + return nil, fmt.Errorf("short syscall ret %d bytes", length) + } + var bcs Bcachestats + br := bytes.NewReader(buf) + err = common.Read(br, binary.LittleEndian, &bcs) + if err != nil { + return nil, err + } + ret.Buffers = uint64(bcs.Numbufpages) * p + + return ret, nil +} + +// Return swapctl summary info +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + out, err := invoke.CommandWithContext(ctx, "swapctl", "-sk") + if err != nil { + return &SwapMemoryStat{}, nil + } + + line := string(out) + var total, used, free uint64 + + _, err = fmt.Sscanf(line, + "total: %d 1K-blocks allocated, %d used, %d available", + &total, &used, &free) + if err != nil { + return nil, errors.New("failed to parse swapctl output") + } + + percent := float64(used) / float64(total) * 100 + return &SwapMemoryStat{ + Total: total * 1024, + Used: used * 1024, + Free: free * 1024, + UsedPercent: percent, + }, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_386.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_386.go new file mode 100644 index 000000000..de2b26ca4 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_386.go @@ -0,0 +1,38 @@ +//go:build openbsd && 386 +// +build openbsd,386 + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs mem/types_openbsd.go + +package mem + +const ( + CTLVfs = 10 + VfsGeneric = 0 + VfsBcacheStat = 3 +) + +const ( + sizeOfBcachestats = 0x90 +) + +type Bcachestats struct { + Numbufs int64 + Numbufpages int64 + Numdirtypages int64 + Numcleanpages int64 + Pendingwrites int64 + Pendingreads int64 + Numwrites int64 + Numreads int64 + Cachehits int64 + Busymapped int64 + Dmapages int64 + Highpages int64 + Delwribufs int64 + Kvaslots int64 + Avail int64 + Highflips int64 + Highflops int64 + Dmaflips int64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_amd64.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_amd64.go new file mode 100644 index 000000000..d187abf01 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_amd64.go @@ -0,0 +1,32 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_openbsd.go + +package mem + +const ( + CTLVfs = 10 + VfsGeneric = 0 + VfsBcacheStat = 3 +) + +const ( + sizeOfBcachestats = 0x78 +) + +type Bcachestats struct { + Numbufs int64 + Numbufpages int64 + Numdirtypages int64 + Numcleanpages int64 + Pendingwrites int64 + Pendingreads int64 + Numwrites int64 + Numreads int64 + Cachehits int64 + Busymapped int64 + Dmapages int64 + Highpages int64 + Delwribufs int64 + Kvaslots int64 + Avail int64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_arm.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_arm.go new file mode 100644 index 000000000..2488f1851 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_arm.go @@ -0,0 +1,38 @@ +//go:build openbsd && arm +// +build openbsd,arm + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs mem/types_openbsd.go + +package mem + +const ( + CTLVfs = 10 + VfsGeneric = 0 + VfsBcacheStat = 3 +) + +const ( + sizeOfBcachestats = 0x90 +) + +type Bcachestats struct { + Numbufs int64 + Numbufpages int64 + Numdirtypages int64 + Numcleanpages int64 + Pendingwrites int64 + Pendingreads int64 + Numwrites int64 + Numreads int64 + Cachehits int64 + Busymapped int64 + Dmapages int64 + Highpages int64 + Delwribufs int64 + Kvaslots int64 + Avail int64 + Highflips int64 + Highflops int64 + Dmaflips int64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_arm64.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_arm64.go new file mode 100644 index 000000000..3661b16fb --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_arm64.go @@ -0,0 +1,38 @@ +//go:build openbsd && arm64 +// +build openbsd,arm64 + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs mem/types_openbsd.go + +package mem + +const ( + CTLVfs = 10 + VfsGeneric = 0 + VfsBcacheStat = 3 +) + +const ( + sizeOfBcachestats = 0x90 +) + +type Bcachestats struct { + Numbufs int64 + Numbufpages int64 + Numdirtypages int64 + Numcleanpages int64 + Pendingwrites int64 + Pendingreads int64 + Numwrites int64 + Numreads int64 + Cachehits int64 + Busymapped int64 + Dmapages int64 + Highpages int64 + Delwribufs int64 + Kvaslots int64 + Avail int64 + Highflips int64 + Highflops int64 + Dmaflips int64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_riscv64.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_riscv64.go new file mode 100644 index 000000000..7a7b48038 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_openbsd_riscv64.go @@ -0,0 +1,38 @@ +//go:build openbsd && riscv64 +// +build openbsd,riscv64 + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs mem/types_openbsd.go + +package mem + +const ( + CTLVfs = 10 + VfsGeneric = 0 + VfsBcacheStat = 3 +) + +const ( + sizeOfBcachestats = 0x90 +) + +type Bcachestats struct { + Numbufs int64 + Numbufpages int64 + Numdirtypages int64 + Numcleanpages int64 + Pendingwrites int64 + Pendingreads int64 + Numwrites int64 + Numreads int64 + Cachehits int64 + Busymapped int64 + Dmapages int64 + Highpages int64 + Delwribufs int64 + Kvaslots int64 + Avail int64 + Highflips int64 + Highflops int64 + Dmaflips int64 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_plan9.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_plan9.go new file mode 100644 index 000000000..b5259f844 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_plan9.go @@ -0,0 +1,68 @@ +//go:build plan9 +// +build plan9 + +package mem + +import ( + "context" + "os" + + stats "github.com/lufia/plan9stats" + "github.com/shirou/gopsutil/v3/internal/common" +) + +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + root := os.Getenv("HOST_ROOT") + m, err := stats.ReadMemStats(ctx, stats.WithRootDir(root)) + if err != nil { + return nil, err + } + u := 0.0 + if m.SwapPages.Avail != 0 { + u = float64(m.SwapPages.Used) / float64(m.SwapPages.Avail) * 100.0 + } + return &SwapMemoryStat{ + Total: uint64(m.SwapPages.Avail * m.PageSize), + Used: uint64(m.SwapPages.Used * m.PageSize), + Free: uint64(m.SwapPages.Free() * m.PageSize), + UsedPercent: u, + }, nil +} + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + root := os.Getenv("HOST_ROOT") + m, err := stats.ReadMemStats(ctx, stats.WithRootDir(root)) + if err != nil { + return nil, err + } + u := 0.0 + if m.UserPages.Avail != 0 { + u = float64(m.UserPages.Used) / float64(m.UserPages.Avail) * 100.0 + } + return &VirtualMemoryStat{ + Total: uint64(m.Total), + Available: uint64(m.UserPages.Free() * m.PageSize), + Used: uint64(m.UserPages.Used * m.PageSize), + UsedPercent: u, + Free: uint64(m.UserPages.Free() * m.PageSize), + + SwapTotal: uint64(m.SwapPages.Avail * m.PageSize), + SwapFree: uint64(m.SwapPages.Free() * m.PageSize), + }, nil +} + +func SwapDevices() ([]*SwapDevice, error) { + return SwapDevicesWithContext(context.Background()) +} + +func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) { + return nil, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_solaris.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_solaris.go new file mode 100644 index 000000000..c911267e1 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_solaris.go @@ -0,0 +1,213 @@ +//go:build solaris +// +build solaris + +package mem + +import ( + "context" + "errors" + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/tklauser/go-sysconf" +) + +// VirtualMemory for Solaris is a minimal implementation which only returns +// what Nomad needs. It does take into account global vs zone, however. +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + result := &VirtualMemoryStat{} + + zoneName, err := zoneName() + if err != nil { + return nil, err + } + + if zoneName == "global" { + cap, err := globalZoneMemoryCapacity() + if err != nil { + return nil, err + } + result.Total = cap + freemem, err := globalZoneFreeMemory(ctx) + if err != nil { + return nil, err + } + result.Available = freemem + result.Free = freemem + result.Used = result.Total - result.Free + } else { + cap, err := nonGlobalZoneMemoryCapacity() + if err != nil { + return nil, err + } + result.Total = cap + } + + return result, nil +} + +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + return nil, common.ErrNotImplementedError +} + +func zoneName() (string, error) { + ctx := context.Background() + out, err := invoke.CommandWithContext(ctx, "zonename") + if err != nil { + return "", err + } + + return strings.TrimSpace(string(out)), nil +} + +var globalZoneMemoryCapacityMatch = regexp.MustCompile(`[Mm]emory size: (\d+) Megabytes`) + +func globalZoneMemoryCapacity() (uint64, error) { + ctx := context.Background() + out, err := invoke.CommandWithContext(ctx, "prtconf") + if err != nil { + return 0, err + } + + match := globalZoneMemoryCapacityMatch.FindAllStringSubmatch(string(out), -1) + if len(match) != 1 { + return 0, errors.New("memory size not contained in output of prtconf") + } + + totalMB, err := strconv.ParseUint(match[0][1], 10, 64) + if err != nil { + return 0, err + } + + return totalMB * 1024 * 1024, nil +} + +func globalZoneFreeMemory(ctx context.Context) (uint64, error) { + output, err := invoke.CommandWithContext(ctx, "pagesize") + if err != nil { + return 0, err + } + + pagesize, err := strconv.ParseUint(strings.TrimSpace(string(output)), 10, 64) + if err != nil { + return 0, err + } + + free, err := sysconf.Sysconf(sysconf.SC_AVPHYS_PAGES) + if err != nil { + return 0, err + } + + return uint64(free) * pagesize, nil +} + +var kstatMatch = regexp.MustCompile(`(\S+)\s+(\S*)`) + +func nonGlobalZoneMemoryCapacity() (uint64, error) { + ctx := context.Background() + out, err := invoke.CommandWithContext(ctx, "kstat", "-p", "-c", "zone_memory_cap", "memory_cap:*:*:physcap") + if err != nil { + return 0, err + } + + kstats := kstatMatch.FindAllStringSubmatch(string(out), -1) + if len(kstats) != 1 { + return 0, fmt.Errorf("expected 1 kstat, found %d", len(kstats)) + } + + memSizeBytes, err := strconv.ParseUint(kstats[0][2], 10, 64) + if err != nil { + return 0, err + } + + return memSizeBytes, nil +} + +const swapCommand = "swap" + +// The blockSize as reported by `swap -l`. See https://docs.oracle.com/cd/E23824_01/html/821-1459/fsswap-52195.html +const blockSize = 512 + +// swapctl column indexes +const ( + nameCol = 0 + // devCol = 1 + // swaploCol = 2 + totalBlocksCol = 3 + freeBlocksCol = 4 +) + +func SwapDevices() ([]*SwapDevice, error) { + return SwapDevicesWithContext(context.Background()) +} + +func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) { + output, err := invoke.CommandWithContext(ctx, swapCommand, "-l") + if err != nil { + return nil, fmt.Errorf("could not execute %q: %w", swapCommand, err) + } + + return parseSwapsCommandOutput(string(output)) +} + +func parseSwapsCommandOutput(output string) ([]*SwapDevice, error) { + lines := strings.Split(output, "\n") + if len(lines) == 0 { + return nil, fmt.Errorf("could not parse output of %q: no lines in %q", swapCommand, output) + } + + // Check header headerFields are as expected. + headerFields := strings.Fields(lines[0]) + if len(headerFields) < freeBlocksCol { + return nil, fmt.Errorf("couldn't parse %q: too few fields in header %q", swapCommand, lines[0]) + } + if headerFields[nameCol] != "swapfile" { + return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[nameCol], "swapfile") + } + if headerFields[totalBlocksCol] != "blocks" { + return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[totalBlocksCol], "blocks") + } + if headerFields[freeBlocksCol] != "free" { + return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[freeBlocksCol], "free") + } + + var swapDevices []*SwapDevice + for _, line := range lines[1:] { + if line == "" { + continue // the terminal line is typically empty + } + fields := strings.Fields(line) + if len(fields) < freeBlocksCol { + return nil, fmt.Errorf("couldn't parse %q: too few fields", swapCommand) + } + + totalBlocks, err := strconv.ParseUint(fields[totalBlocksCol], 10, 64) + if err != nil { + return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapCommand, err) + } + + freeBlocks, err := strconv.ParseUint(fields[freeBlocksCol], 10, 64) + if err != nil { + return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapCommand, err) + } + + swapDevices = append(swapDevices, &SwapDevice{ + Name: fields[nameCol], + UsedBytes: (totalBlocks - freeBlocks) * blockSize, + FreeBytes: freeBlocks * blockSize, + }) + } + + return swapDevices, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/mem/mem_windows.go b/vendor/github.com/shirou/gopsutil/v3/mem/mem_windows.go new file mode 100644 index 000000000..8c7fb1a13 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/mem/mem_windows.go @@ -0,0 +1,166 @@ +//go:build windows +// +build windows + +package mem + +import ( + "context" + "sync" + "syscall" + "unsafe" + + "github.com/shirou/gopsutil/v3/internal/common" + "golang.org/x/sys/windows" +) + +var ( + procEnumPageFilesW = common.ModPsapi.NewProc("EnumPageFilesW") + procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo") + procGetPerformanceInfo = common.ModPsapi.NewProc("GetPerformanceInfo") + procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx") +) + +type memoryStatusEx struct { + cbSize uint32 + dwMemoryLoad uint32 + ullTotalPhys uint64 // in bytes + ullAvailPhys uint64 + ullTotalPageFile uint64 + ullAvailPageFile uint64 + ullTotalVirtual uint64 + ullAvailVirtual uint64 + ullAvailExtendedVirtual uint64 +} + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + var memInfo memoryStatusEx + memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) + mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) + if mem == 0 { + return nil, windows.GetLastError() + } + + ret := &VirtualMemoryStat{ + Total: memInfo.ullTotalPhys, + Available: memInfo.ullAvailPhys, + Free: memInfo.ullAvailPhys, + UsedPercent: float64(memInfo.dwMemoryLoad), + } + + ret.Used = ret.Total - ret.Available + return ret, nil +} + +type performanceInformation struct { + cb uint32 + commitTotal uint64 + commitLimit uint64 + commitPeak uint64 + physicalTotal uint64 + physicalAvailable uint64 + systemCache uint64 + kernelTotal uint64 + kernelPaged uint64 + kernelNonpaged uint64 + pageSize uint64 + handleCount uint32 + processCount uint32 + threadCount uint32 +} + +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + var perfInfo performanceInformation + perfInfo.cb = uint32(unsafe.Sizeof(perfInfo)) + mem, _, _ := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&perfInfo)), uintptr(perfInfo.cb)) + if mem == 0 { + return nil, windows.GetLastError() + } + tot := perfInfo.commitLimit * perfInfo.pageSize + used := perfInfo.commitTotal * perfInfo.pageSize + free := tot - used + var usedPercent float64 + if tot == 0 { + usedPercent = 0 + } else { + usedPercent = float64(used) / float64(tot) * 100 + } + ret := &SwapMemoryStat{ + Total: tot, + Used: used, + Free: free, + UsedPercent: usedPercent, + } + + return ret, nil +} + +var ( + pageSize uint64 + pageSizeOnce sync.Once +) + +type systemInfo struct { + wProcessorArchitecture uint16 + wReserved uint16 + dwPageSize uint32 + lpMinimumApplicationAddress uintptr + lpMaximumApplicationAddress uintptr + dwActiveProcessorMask uintptr + dwNumberOfProcessors uint32 + dwProcessorType uint32 + dwAllocationGranularity uint32 + wProcessorLevel uint16 + wProcessorRevision uint16 +} + +// system type as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-enum_page_file_information +type enumPageFileInformation struct { + cb uint32 + reserved uint32 + totalSize uint64 + totalInUse uint64 + peakUsage uint64 +} + +func SwapDevices() ([]*SwapDevice, error) { + return SwapDevicesWithContext(context.Background()) +} + +func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) { + pageSizeOnce.Do(func() { + var sysInfo systemInfo + procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&sysInfo))) + pageSize = uint64(sysInfo.dwPageSize) + }) + + // the following system call invokes the supplied callback function once for each page file before returning + // see https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumpagefilesw + var swapDevices []*SwapDevice + result, _, _ := procEnumPageFilesW.Call(windows.NewCallback(pEnumPageFileCallbackW), uintptr(unsafe.Pointer(&swapDevices))) + if result == 0 { + return nil, windows.GetLastError() + } + + return swapDevices, nil +} + +// system callback as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/nc-psapi-penum_page_file_callbackw +func pEnumPageFileCallbackW(swapDevices *[]*SwapDevice, enumPageFileInfo *enumPageFileInformation, lpFilenamePtr *[syscall.MAX_LONG_PATH]uint16) *bool { + *swapDevices = append(*swapDevices, &SwapDevice{ + Name: syscall.UTF16ToString((*lpFilenamePtr)[:]), + UsedBytes: enumPageFileInfo.totalInUse * pageSize, + FreeBytes: (enumPageFileInfo.totalSize - enumPageFileInfo.totalInUse) * pageSize, + }) + + // return true to continue enumerating page files + ret := true + return &ret +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net.go b/vendor/github.com/shirou/gopsutil/v3/net/net.go new file mode 100644 index 000000000..0f3a62f39 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net.go @@ -0,0 +1,273 @@ +package net + +import ( + "context" + "encoding/json" + "net" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +var invoke common.Invoker = common.Invoke{} + +type IOCountersStat struct { + Name string `json:"name"` // interface name + BytesSent uint64 `json:"bytesSent"` // number of bytes sent + BytesRecv uint64 `json:"bytesRecv"` // number of bytes received + PacketsSent uint64 `json:"packetsSent"` // number of packets sent + PacketsRecv uint64 `json:"packetsRecv"` // number of packets received + Errin uint64 `json:"errin"` // total number of errors while receiving + Errout uint64 `json:"errout"` // total number of errors while sending + Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped + Dropout uint64 `json:"dropout"` // total number of outgoing packets which were dropped (always 0 on OSX and BSD) + Fifoin uint64 `json:"fifoin"` // total number of FIFO buffers errors while receiving + Fifoout uint64 `json:"fifoout"` // total number of FIFO buffers errors while sending +} + +// Addr is implemented compatibility to psutil +type Addr struct { + IP string `json:"ip"` + Port uint32 `json:"port"` +} + +type ConnectionStat struct { + Fd uint32 `json:"fd"` + Family uint32 `json:"family"` + Type uint32 `json:"type"` + Laddr Addr `json:"localaddr"` + Raddr Addr `json:"remoteaddr"` + Status string `json:"status"` + Uids []int32 `json:"uids"` + Pid int32 `json:"pid"` +} + +// System wide stats about different network protocols +type ProtoCountersStat struct { + Protocol string `json:"protocol"` + Stats map[string]int64 `json:"stats"` +} + +// NetInterfaceAddr is designed for represent interface addresses +type InterfaceAddr struct { + Addr string `json:"addr"` +} + +// InterfaceAddrList is a list of InterfaceAddr +type InterfaceAddrList []InterfaceAddr + +type InterfaceStat struct { + Index int `json:"index"` + MTU int `json:"mtu"` // maximum transmission unit + Name string `json:"name"` // e.g., "en0", "lo0", "eth0.100" + HardwareAddr string `json:"hardwareAddr"` // IEEE MAC-48, EUI-48 and EUI-64 form + Flags []string `json:"flags"` // e.g., FlagUp, FlagLoopback, FlagMulticast + Addrs InterfaceAddrList `json:"addrs"` +} + +// InterfaceStatList is a list of InterfaceStat +type InterfaceStatList []InterfaceStat + +type FilterStat struct { + ConnTrackCount int64 `json:"connTrackCount"` + ConnTrackMax int64 `json:"connTrackMax"` +} + +// ConntrackStat has conntrack summary info +type ConntrackStat struct { + Entries uint32 `json:"entries"` // Number of entries in the conntrack table + Searched uint32 `json:"searched"` // Number of conntrack table lookups performed + Found uint32 `json:"found"` // Number of searched entries which were successful + New uint32 `json:"new"` // Number of entries added which were not expected before + Invalid uint32 `json:"invalid"` // Number of packets seen which can not be tracked + Ignore uint32 `json:"ignore"` // Packets seen which are already connected to an entry + Delete uint32 `json:"delete"` // Number of entries which were removed + DeleteList uint32 `json:"deleteList"` // Number of entries which were put to dying list + Insert uint32 `json:"insert"` // Number of entries inserted into the list + InsertFailed uint32 `json:"insertFailed"` // # insertion attempted but failed (same entry exists) + Drop uint32 `json:"drop"` // Number of packets dropped due to conntrack failure. + EarlyDrop uint32 `json:"earlyDrop"` // Dropped entries to make room for new ones, if maxsize reached + IcmpError uint32 `json:"icmpError"` // Subset of invalid. Packets that can't be tracked d/t error + ExpectNew uint32 `json:"expectNew"` // Entries added after an expectation was already present + ExpectCreate uint32 `json:"expectCreate"` // Expectations added + ExpectDelete uint32 `json:"expectDelete"` // Expectations deleted + SearchRestart uint32 `json:"searchRestart"` // Conntrack table lookups restarted due to hashtable resizes +} + +func NewConntrackStat(e uint32, s uint32, f uint32, n uint32, inv uint32, ign uint32, del uint32, dlst uint32, ins uint32, insfail uint32, drop uint32, edrop uint32, ie uint32, en uint32, ec uint32, ed uint32, sr uint32) *ConntrackStat { + return &ConntrackStat{ + Entries: e, + Searched: s, + Found: f, + New: n, + Invalid: inv, + Ignore: ign, + Delete: del, + DeleteList: dlst, + Insert: ins, + InsertFailed: insfail, + Drop: drop, + EarlyDrop: edrop, + IcmpError: ie, + ExpectNew: en, + ExpectCreate: ec, + ExpectDelete: ed, + SearchRestart: sr, + } +} + +type ConntrackStatList struct { + items []*ConntrackStat +} + +func NewConntrackStatList() *ConntrackStatList { + return &ConntrackStatList{ + items: []*ConntrackStat{}, + } +} + +func (l *ConntrackStatList) Append(c *ConntrackStat) { + l.items = append(l.items, c) +} + +func (l *ConntrackStatList) Items() []ConntrackStat { + items := make([]ConntrackStat, len(l.items)) + for i, el := range l.items { + items[i] = *el + } + return items +} + +// Summary returns a single-element list with totals from all list items. +func (l *ConntrackStatList) Summary() []ConntrackStat { + summary := NewConntrackStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + for _, cs := range l.items { + summary.Entries += cs.Entries + summary.Searched += cs.Searched + summary.Found += cs.Found + summary.New += cs.New + summary.Invalid += cs.Invalid + summary.Ignore += cs.Ignore + summary.Delete += cs.Delete + summary.DeleteList += cs.DeleteList + summary.Insert += cs.Insert + summary.InsertFailed += cs.InsertFailed + summary.Drop += cs.Drop + summary.EarlyDrop += cs.EarlyDrop + summary.IcmpError += cs.IcmpError + summary.ExpectNew += cs.ExpectNew + summary.ExpectCreate += cs.ExpectCreate + summary.ExpectDelete += cs.ExpectDelete + summary.SearchRestart += cs.SearchRestart + } + return []ConntrackStat{*summary} +} + +func (n IOCountersStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (n ConnectionStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (n ProtoCountersStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (a Addr) String() string { + s, _ := json.Marshal(a) + return string(s) +} + +func (n InterfaceStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (l InterfaceStatList) String() string { + s, _ := json.Marshal(l) + return string(s) +} + +func (n InterfaceAddr) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (n ConntrackStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func Interfaces() (InterfaceStatList, error) { + return InterfacesWithContext(context.Background()) +} + +func InterfacesWithContext(ctx context.Context) (InterfaceStatList, error) { + is, err := net.Interfaces() + if err != nil { + return nil, err + } + ret := make(InterfaceStatList, 0, len(is)) + for _, ifi := range is { + + var flags []string + if ifi.Flags&net.FlagUp != 0 { + flags = append(flags, "up") + } + if ifi.Flags&net.FlagBroadcast != 0 { + flags = append(flags, "broadcast") + } + if ifi.Flags&net.FlagLoopback != 0 { + flags = append(flags, "loopback") + } + if ifi.Flags&net.FlagPointToPoint != 0 { + flags = append(flags, "pointtopoint") + } + if ifi.Flags&net.FlagMulticast != 0 { + flags = append(flags, "multicast") + } + + r := InterfaceStat{ + Index: ifi.Index, + Name: ifi.Name, + MTU: ifi.MTU, + HardwareAddr: ifi.HardwareAddr.String(), + Flags: flags, + } + addrs, err := ifi.Addrs() + if err == nil { + r.Addrs = make(InterfaceAddrList, 0, len(addrs)) + for _, addr := range addrs { + r.Addrs = append(r.Addrs, InterfaceAddr{ + Addr: addr.String(), + }) + } + + } + ret = append(ret, r) + } + + return ret, nil +} + +func getIOCountersAll(n []IOCountersStat) ([]IOCountersStat, error) { + r := IOCountersStat{ + Name: "all", + } + for _, nic := range n { + r.BytesRecv += nic.BytesRecv + r.PacketsRecv += nic.PacketsRecv + r.Errin += nic.Errin + r.Dropin += nic.Dropin + r.BytesSent += nic.BytesSent + r.PacketsSent += nic.PacketsSent + r.Errout += nic.Errout + r.Dropout += nic.Dropout + } + + return []IOCountersStat{r}, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_aix.go b/vendor/github.com/shirou/gopsutil/v3/net/net_aix.go new file mode 100644 index 000000000..81feaa8d7 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_aix.go @@ -0,0 +1,330 @@ +//go:build aix +// +build aix + +package net + +import ( + "context" + "fmt" + "regexp" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +// IOCountersByFile exists just for compatibility with Linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return nil, common.ErrNotImplementedError +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return nil, common.ErrNotImplementedError +} + +func parseNetstatNetLine(line string) (ConnectionStat, error) { + f := strings.Fields(line) + if len(f) < 5 { + return ConnectionStat{}, fmt.Errorf("wrong line,%s", line) + } + + var netType, netFamily uint32 + switch f[0] { + case "tcp", "tcp4": + netType = syscall.SOCK_STREAM + netFamily = syscall.AF_INET + case "udp", "udp4": + netType = syscall.SOCK_DGRAM + netFamily = syscall.AF_INET + case "tcp6": + netType = syscall.SOCK_STREAM + netFamily = syscall.AF_INET6 + case "udp6": + netType = syscall.SOCK_DGRAM + netFamily = syscall.AF_INET6 + default: + return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0]) + } + + laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily) + if err != nil { + return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4]) + } + + n := ConnectionStat{ + Fd: uint32(0), // not supported + Family: uint32(netFamily), + Type: uint32(netType), + Laddr: laddr, + Raddr: raddr, + Pid: int32(0), // not supported + } + if len(f) == 6 { + n.Status = f[5] + } + + return n, nil +} + +var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`) + +// This function only works for netstat returning addresses with a "." +// before the port (0.0.0.0.22 instead of 0.0.0.0:22). +func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) { + parse := func(l string) (Addr, error) { + matches := portMatch.FindStringSubmatch(l) + if matches == nil { + return Addr{}, fmt.Errorf("wrong addr, %s", l) + } + host := matches[1] + port := matches[2] + if host == "*" { + switch family { + case syscall.AF_INET: + host = "0.0.0.0" + case syscall.AF_INET6: + host = "::" + default: + return Addr{}, fmt.Errorf("unknown family, %d", family) + } + } + lport, err := strconv.Atoi(port) + if err != nil { + return Addr{}, err + } + return Addr{IP: host, Port: uint32(lport)}, nil + } + + laddr, err = parse(local) + if remote != "*.*" { // remote addr exists + raddr, err = parse(remote) + if err != nil { + return laddr, raddr, err + } + } + + return laddr, raddr, err +} + +func parseNetstatUnixLine(f []string) (ConnectionStat, error) { + if len(f) < 8 { + return ConnectionStat{}, fmt.Errorf("wrong number of fields: expected >=8 got %d", len(f)) + } + + var netType uint32 + + switch f[1] { + case "dgram": + netType = syscall.SOCK_DGRAM + case "stream": + netType = syscall.SOCK_STREAM + default: + return ConnectionStat{}, fmt.Errorf("unknown type: %s", f[1]) + } + + // Some Unix Socket don't have any address associated + addr := "" + if len(f) == 9 { + addr = f[8] + } + + c := ConnectionStat{ + Fd: uint32(0), // not supported + Family: uint32(syscall.AF_UNIX), + Type: uint32(netType), + Laddr: Addr{ + IP: addr, + }, + Status: "NONE", + Pid: int32(0), // not supported + } + + return c, nil +} + +// Return true if proto is the corresponding to the kind parameter +// Only for Inet lines +func hasCorrectInetProto(kind, proto string) bool { + switch kind { + case "all", "inet": + return true + case "unix": + return false + case "inet4": + return !strings.HasSuffix(proto, "6") + case "inet6": + return strings.HasSuffix(proto, "6") + case "tcp": + return proto == "tcp" || proto == "tcp4" || proto == "tcp6" + case "tcp4": + return proto == "tcp" || proto == "tcp4" + case "tcp6": + return proto == "tcp6" + case "udp": + return proto == "udp" || proto == "udp4" || proto == "udp6" + case "udp4": + return proto == "udp" || proto == "udp4" + case "udp6": + return proto == "udp6" + } + return false +} + +func parseNetstatA(output string, kind string) ([]ConnectionStat, error) { + var ret []ConnectionStat + lines := strings.Split(string(output), "\n") + + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) < 1 { + continue + } + + if strings.HasPrefix(fields[0], "f1") { + // Unix lines + if len(fields) < 2 { + // every unix connections have two lines + continue + } + + c, err := parseNetstatUnixLine(fields) + if err != nil { + return nil, fmt.Errorf("failed to parse Unix Address (%s): %s", line, err) + } + + ret = append(ret, c) + + } else if strings.HasPrefix(fields[0], "tcp") || strings.HasPrefix(fields[0], "udp") { + // Inet lines + if !hasCorrectInetProto(kind, fields[0]) { + continue + } + + // On AIX, netstat display some connections with "*.*" as local addresses + // Skip them as they aren't real connections. + if fields[3] == "*.*" { + continue + } + + c, err := parseNetstatNetLine(line) + if err != nil { + return nil, fmt.Errorf("failed to parse Inet Address (%s): %s", line, err) + } + + ret = append(ret, c) + } else { + // Header lines + continue + } + } + + return ret, nil +} + +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + args := []string{"-na"} + switch strings.ToLower(kind) { + default: + fallthrough + case "": + kind = "all" + case "all": + // nothing to add + case "inet", "inet4", "inet6": + args = append(args, "-finet") + case "tcp", "tcp4", "tcp6": + args = append(args, "-finet") + case "udp", "udp4", "udp6": + args = append(args, "-finet") + case "unix": + args = append(args, "-funix") + } + + out, err := invoke.CommandWithContext(ctx, "netstat", args...) + if err != nil { + return nil, err + } + + ret, err := parseNetstatA(string(out), kind) + if err != nil { + return nil, err + } + + return ret, nil +} + +func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { + return ConnectionsMaxWithContext(context.Background(), kind, max) +} + +func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +// Return a list of network connections opened, omitting `Uids`. +// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be +// removed from the API in the future. +func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { + return ConnectionsWithoutUidsWithContext(context.Background(), kind) +} + +func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) +} + +func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max) +} + +func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0) +} + +func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max) +} + +func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_aix_cgo.go b/vendor/github.com/shirou/gopsutil/v3/net/net_aix_cgo.go new file mode 100644 index 000000000..8c34f881c --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_aix_cgo.go @@ -0,0 +1,36 @@ +//go:build aix && cgo +// +build aix,cgo + +package net + +import ( + "context" + + "github.com/power-devops/perfstat" +) + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + ifs, err := perfstat.NetIfaceStat() + if err != nil { + return nil, err + } + + iocounters := make([]IOCountersStat, 0, len(ifs)) + for _, netif := range ifs { + n := IOCountersStat{ + Name: netif.Name, + BytesSent: uint64(netif.OBytes), + BytesRecv: uint64(netif.IBytes), + PacketsSent: uint64(netif.OPackets), + PacketsRecv: uint64(netif.IPackets), + Errin: uint64(netif.OErrors), + Errout: uint64(netif.IErrors), + Dropout: uint64(netif.XmitDrops), + } + iocounters = append(iocounters, n) + } + if pernic == false { + return getIOCountersAll(iocounters) + } + return iocounters, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_aix_nocgo.go b/vendor/github.com/shirou/gopsutil/v3/net/net_aix_nocgo.go new file mode 100644 index 000000000..e3fce9021 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_aix_nocgo.go @@ -0,0 +1,95 @@ +//go:build aix && !cgo +// +build aix,!cgo + +package net + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +func parseNetstatI(output string) ([]IOCountersStat, error) { + lines := strings.Split(string(output), "\n") + ret := make([]IOCountersStat, 0, len(lines)-1) + exists := make([]string, 0, len(ret)) + + // Check first line is header + if len(lines) > 0 && strings.Fields(lines[0])[0] != "Name" { + return nil, fmt.Errorf("not a 'netstat -i' output") + } + + for _, line := range lines[1:] { + values := strings.Fields(line) + if len(values) < 1 || values[0] == "Name" { + continue + } + if common.StringsHas(exists, values[0]) { + // skip if already get + continue + } + exists = append(exists, values[0]) + + if len(values) < 9 { + continue + } + + base := 1 + // sometimes Address is omitted + if len(values) < 10 { + base = 0 + } + + parsed := make([]uint64, 0, 5) + vv := []string{ + values[base+3], // Ipkts == PacketsRecv + values[base+4], // Ierrs == Errin + values[base+5], // Opkts == PacketsSent + values[base+6], // Oerrs == Errout + values[base+8], // Drops == Dropout + } + + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + n := IOCountersStat{ + Name: values[0], + PacketsRecv: parsed[0], + Errin: parsed[1], + PacketsSent: parsed[2], + Errout: parsed[3], + Dropout: parsed[4], + } + ret = append(ret, n) + } + return ret, nil +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + out, err := invoke.CommandWithContext(ctx, "netstat", "-idn") + if err != nil { + return nil, err + } + + iocounters, err := parseNetstatI(string(out)) + if err != nil { + return nil, err + } + if pernic == false { + return getIOCountersAll(iocounters) + } + return iocounters, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_darwin.go b/vendor/github.com/shirou/gopsutil/v3/net/net_darwin.go new file mode 100644 index 000000000..8a7b63744 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_darwin.go @@ -0,0 +1,291 @@ +//go:build darwin +// +build darwin + +package net + +import ( + "context" + "errors" + "fmt" + "os/exec" + "regexp" + "strconv" + "strings" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +var ( + errNetstatHeader = errors.New("Can't parse header of netstat output") + netstatLinkRegexp = regexp.MustCompile(`^$`) +) + +const endOfLine = "\n" + +func parseNetstatLine(line string) (stat *IOCountersStat, linkID *uint, err error) { + var ( + numericValue uint64 + columns = strings.Fields(line) + ) + + if columns[0] == "Name" { + err = errNetstatHeader + return + } + + // try to extract the numeric value from + if subMatch := netstatLinkRegexp.FindStringSubmatch(columns[2]); len(subMatch) == 2 { + numericValue, err = strconv.ParseUint(subMatch[1], 10, 64) + if err != nil { + return + } + linkIDUint := uint(numericValue) + linkID = &linkIDUint + } + + base := 1 + numberColumns := len(columns) + // sometimes Address is omitted + if numberColumns < 12 { + base = 0 + } + if numberColumns < 11 || numberColumns > 13 { + err = fmt.Errorf("Line %q do have an invalid number of columns %d", line, numberColumns) + return + } + + parsed := make([]uint64, 0, 7) + vv := []string{ + columns[base+3], // Ipkts == PacketsRecv + columns[base+4], // Ierrs == Errin + columns[base+5], // Ibytes == BytesRecv + columns[base+6], // Opkts == PacketsSent + columns[base+7], // Oerrs == Errout + columns[base+8], // Obytes == BytesSent + } + if len(columns) == 12 { + vv = append(vv, columns[base+10]) + } + + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + if numericValue, err = strconv.ParseUint(target, 10, 64); err != nil { + return + } + parsed = append(parsed, numericValue) + } + + stat = &IOCountersStat{ + Name: strings.Trim(columns[0], "*"), // remove the * that sometimes is on right on interface + PacketsRecv: parsed[0], + Errin: parsed[1], + BytesRecv: parsed[2], + PacketsSent: parsed[3], + Errout: parsed[4], + BytesSent: parsed[5], + } + if len(parsed) == 7 { + stat.Dropout = parsed[6] + } + return +} + +type netstatInterface struct { + linkID *uint + stat *IOCountersStat +} + +func parseNetstatOutput(output string) ([]netstatInterface, error) { + var ( + err error + lines = strings.Split(strings.Trim(output, endOfLine), endOfLine) + ) + + // number of interfaces is number of lines less one for the header + numberInterfaces := len(lines) - 1 + + interfaces := make([]netstatInterface, numberInterfaces) + // no output beside header + if numberInterfaces == 0 { + return interfaces, nil + } + + for index := 0; index < numberInterfaces; index++ { + nsIface := netstatInterface{} + if nsIface.stat, nsIface.linkID, err = parseNetstatLine(lines[index+1]); err != nil { + return nil, err + } + interfaces[index] = nsIface + } + return interfaces, nil +} + +// map that hold the name of a network interface and the number of usage +type mapInterfaceNameUsage map[string]uint + +func newMapInterfaceNameUsage(ifaces []netstatInterface) mapInterfaceNameUsage { + output := make(mapInterfaceNameUsage) + for index := range ifaces { + if ifaces[index].linkID != nil { + ifaceName := ifaces[index].stat.Name + usage, ok := output[ifaceName] + if ok { + output[ifaceName] = usage + 1 + } else { + output[ifaceName] = 1 + } + } + } + return output +} + +func (min mapInterfaceNameUsage) isTruncated() bool { + for _, usage := range min { + if usage > 1 { + return true + } + } + return false +} + +func (min mapInterfaceNameUsage) notTruncated() []string { + output := make([]string, 0) + for ifaceName, usage := range min { + if usage == 1 { + output = append(output, ifaceName) + } + } + return output +} + +// example of `netstat -ibdnW` output on yosemite +// Name Mtu Network Address Ipkts Ierrs Ibytes Opkts Oerrs Obytes Coll Drop +// lo0 16384 869107 0 169411755 869107 0 169411755 0 0 +// lo0 16384 ::1/128 ::1 869107 - 169411755 869107 - 169411755 - - +// lo0 16384 127 127.0.0.1 869107 - 169411755 869107 - 169411755 - - +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + var ( + ret []IOCountersStat + retIndex int + ) + + netstat, err := exec.LookPath("netstat") + if err != nil { + return nil, err + } + + // try to get all interface metrics, and hope there won't be any truncated + out, err := invoke.CommandWithContext(ctx, netstat, "-ibdnW") + if err != nil { + return nil, err + } + + nsInterfaces, err := parseNetstatOutput(string(out)) + if err != nil { + return nil, err + } + + ifaceUsage := newMapInterfaceNameUsage(nsInterfaces) + notTruncated := ifaceUsage.notTruncated() + ret = make([]IOCountersStat, len(notTruncated)) + + if !ifaceUsage.isTruncated() { + // no truncated interface name, return stats of all interface with + for index := range nsInterfaces { + if nsInterfaces[index].linkID != nil { + ret[retIndex] = *nsInterfaces[index].stat + retIndex++ + } + } + } else { + // duplicated interface, list all interfaces + if out, err = invoke.CommandWithContext(ctx, "ifconfig", "-l"); err != nil { + return nil, err + } + interfaceNames := strings.Fields(strings.TrimRight(string(out), endOfLine)) + + // for each of the interface name, run netstat if we don't have any stats yet + for _, interfaceName := range interfaceNames { + truncated := true + for index := range nsInterfaces { + if nsInterfaces[index].linkID != nil && nsInterfaces[index].stat.Name == interfaceName { + // handle the non truncated name to avoid execute netstat for them again + ret[retIndex] = *nsInterfaces[index].stat + retIndex++ + truncated = false + break + } + } + if truncated { + // run netstat with -I$ifacename + if out, err = invoke.CommandWithContext(ctx, netstat, "-ibdnWI"+interfaceName); err != nil { + return nil, err + } + parsedIfaces, err := parseNetstatOutput(string(out)) + if err != nil { + return nil, err + } + if len(parsedIfaces) == 0 { + // interface had been removed since `ifconfig -l` had been executed + continue + } + for index := range parsedIfaces { + if parsedIfaces[index].linkID != nil { + ret = append(ret, *parsedIfaces[index].stat) + break + } + } + } + } + } + + if pernic == false { + return getIOCountersAll(ret) + } + return ret, nil +} + +// IOCountersByFile exists just for compatibility with Linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersWithContext(ctx, pernic) +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return nil, common.ErrNotImplementedError +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + +// ProtoCounters returns network statistics for the entire system +// If protocols is empty then all protocols are returned, otherwise +// just the protocols in the list are returned. +// Not Implemented for Darwin +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return nil, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_fallback.go b/vendor/github.com/shirou/gopsutil/v3/net/net_fallback.go new file mode 100644 index 000000000..e136be1ba --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_fallback.go @@ -0,0 +1,93 @@ +//go:build !aix && !darwin && !linux && !freebsd && !openbsd && !windows && !solaris +// +build !aix,!darwin,!linux,!freebsd,!openbsd,!windows,!solaris + +package net + +import ( + "context" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + return []IOCountersStat{}, common.ErrNotImplementedError +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return []FilterStat{}, common.ErrNotImplementedError +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return []ProtoCountersStat{}, common.ErrNotImplementedError +} + +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { + return ConnectionsMaxWithContext(context.Background(), kind, max) +} + +func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +// Return a list of network connections opened, omitting `Uids`. +// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be +// removed from the API in the future. +func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { + return ConnectionsWithoutUidsWithContext(context.Background(), kind) +} + +func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) +} + +func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max) +} + +func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0) +} + +func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max) +} + +func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_freebsd.go b/vendor/github.com/shirou/gopsutil/v3/net/net_freebsd.go new file mode 100644 index 000000000..bf8baf094 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_freebsd.go @@ -0,0 +1,128 @@ +//go:build freebsd +// +build freebsd + +package net + +import ( + "context" + "strconv" + "strings" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + out, err := invoke.CommandWithContext(ctx, "netstat", "-ibdnW") + if err != nil { + return nil, err + } + + lines := strings.Split(string(out), "\n") + ret := make([]IOCountersStat, 0, len(lines)-1) + exists := make([]string, 0, len(ret)) + + for _, line := range lines { + values := strings.Fields(line) + if len(values) < 1 || values[0] == "Name" { + continue + } + if common.StringsHas(exists, values[0]) { + // skip if already get + continue + } + exists = append(exists, values[0]) + + if len(values) < 12 { + continue + } + base := 1 + // sometimes Address is omitted + if len(values) < 13 { + base = 0 + } + + parsed := make([]uint64, 0, 8) + vv := []string{ + values[base+3], // PacketsRecv + values[base+4], // Errin + values[base+5], // Dropin + values[base+6], // BytesRecvn + values[base+7], // PacketSent + values[base+8], // Errout + values[base+9], // BytesSent + values[base+11], // Dropout + } + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + n := IOCountersStat{ + Name: values[0], + PacketsRecv: parsed[0], + Errin: parsed[1], + Dropin: parsed[2], + BytesRecv: parsed[3], + PacketsSent: parsed[4], + Errout: parsed[5], + BytesSent: parsed[6], + Dropout: parsed[7], + } + ret = append(ret, n) + } + + if pernic == false { + return getIOCountersAll(ret) + } + + return ret, nil +} + +// IOCountersByFile exists just for compatibility with Linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return nil, common.ErrNotImplementedError +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + +// ProtoCounters returns network statistics for the entire system +// If protocols is empty then all protocols are returned, otherwise +// just the protocols in the list are returned. +// Not Implemented for FreeBSD +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return nil, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_linux.go b/vendor/github.com/shirou/gopsutil/v3/net/net_linux.go new file mode 100644 index 000000000..6e8ce67fb --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_linux.go @@ -0,0 +1,910 @@ +//go:build linux +// +build linux + +package net + +import ( + "bytes" + "context" + "encoding/hex" + "errors" + "fmt" + "io" + "net" + "os" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +const ( // Conntrack Column numbers + ctENTRIES = iota + ctSEARCHED + ctFOUND + ctNEW + ctINVALID + ctIGNORE + ctDELETE + ctDELETE_LIST + ctINSERT + ctINSERT_FAILED + ctDROP + ctEARLY_DROP + ctICMP_ERROR + CT_EXPEctNEW + ctEXPECT_CREATE + CT_EXPEctDELETE + ctSEARCH_RESTART +) + +// NetIOCounters returns network I/O statistics for every network +// interface installed on the system. If pernic argument is false, +// return only sum of all information (which name is 'all'). If true, +// every network interface installed on the system is returned +// separately. +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + filename := common.HostProcWithContext(ctx, "net/dev") + return IOCountersByFileWithContext(ctx, pernic, filename) +} + +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + lines, err := common.ReadLines(filename) + if err != nil { + return nil, err + } + + parts := make([]string, 2) + + statlen := len(lines) - 1 + + ret := make([]IOCountersStat, 0, statlen) + + for _, line := range lines[2:] { + separatorPos := strings.LastIndex(line, ":") + if separatorPos == -1 { + continue + } + parts[0] = line[0:separatorPos] + parts[1] = line[separatorPos+1:] + + interfaceName := strings.TrimSpace(parts[0]) + if interfaceName == "" { + continue + } + + fields := strings.Fields(strings.TrimSpace(parts[1])) + bytesRecv, err := strconv.ParseUint(fields[0], 10, 64) + if err != nil { + return ret, err + } + packetsRecv, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + return ret, err + } + errIn, err := strconv.ParseUint(fields[2], 10, 64) + if err != nil { + return ret, err + } + dropIn, err := strconv.ParseUint(fields[3], 10, 64) + if err != nil { + return ret, err + } + fifoIn, err := strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return ret, err + } + bytesSent, err := strconv.ParseUint(fields[8], 10, 64) + if err != nil { + return ret, err + } + packetsSent, err := strconv.ParseUint(fields[9], 10, 64) + if err != nil { + return ret, err + } + errOut, err := strconv.ParseUint(fields[10], 10, 64) + if err != nil { + return ret, err + } + dropOut, err := strconv.ParseUint(fields[11], 10, 64) + if err != nil { + return ret, err + } + fifoOut, err := strconv.ParseUint(fields[12], 10, 64) + if err != nil { + return ret, err + } + + nic := IOCountersStat{ + Name: interfaceName, + BytesRecv: bytesRecv, + PacketsRecv: packetsRecv, + Errin: errIn, + Dropin: dropIn, + Fifoin: fifoIn, + BytesSent: bytesSent, + PacketsSent: packetsSent, + Errout: errOut, + Dropout: dropOut, + Fifoout: fifoOut, + } + ret = append(ret, nic) + } + + if !pernic { + return getIOCountersAll(ret) + } + + return ret, nil +} + +var netProtocols = []string{ + "ip", + "icmp", + "icmpmsg", + "tcp", + "udp", + "udplite", +} + +// ProtoCounters returns network statistics for the entire system +// If protocols is empty then all protocols are returned, otherwise +// just the protocols in the list are returned. +// Available protocols: +// [ip,icmp,icmpmsg,tcp,udp,udplite] +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + if len(protocols) == 0 { + protocols = netProtocols + } + + stats := make([]ProtoCountersStat, 0, len(protocols)) + protos := make(map[string]bool, len(protocols)) + for _, p := range protocols { + protos[p] = true + } + + filename := common.HostProcWithContext(ctx, "net/snmp") + lines, err := common.ReadLines(filename) + if err != nil { + return nil, err + } + + linecount := len(lines) + for i := 0; i < linecount; i++ { + line := lines[i] + r := strings.IndexRune(line, ':') + if r == -1 { + return nil, errors.New(filename + " is not formatted correctly, expected ':'.") + } + proto := strings.ToLower(line[:r]) + if !protos[proto] { + // skip protocol and data line + i++ + continue + } + + // Read header line + statNames := strings.Split(line[r+2:], " ") + + // Read data line + i++ + statValues := strings.Split(lines[i][r+2:], " ") + if len(statNames) != len(statValues) { + return nil, errors.New(filename + " is not formatted correctly, expected same number of columns.") + } + stat := ProtoCountersStat{ + Protocol: proto, + Stats: make(map[string]int64, len(statNames)), + } + for j := range statNames { + value, err := strconv.ParseInt(statValues[j], 10, 64) + if err != nil { + return nil, err + } + stat.Stats[statNames[j]] = value + } + stats = append(stats, stat) + } + return stats, nil +} + +// NetFilterCounters returns iptables conntrack statistics +// the currently in use conntrack count and the max. +// If the file does not exist or is invalid it will return nil. +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + countfile := common.HostProcWithContext(ctx, "sys/net/netfilter/nf_conntrack_count") + maxfile := common.HostProcWithContext(ctx, "sys/net/netfilter/nf_conntrack_max") + + count, err := common.ReadInts(countfile) + if err != nil { + return nil, err + } + stats := make([]FilterStat, 0, 1) + + max, err := common.ReadInts(maxfile) + if err != nil { + return nil, err + } + + payload := FilterStat{ + ConnTrackCount: count[0], + ConnTrackMax: max[0], + } + + stats = append(stats, payload) + return stats, nil +} + +// ConntrackStats returns more detailed info about the conntrack table +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +// ConntrackStatsWithContext returns more detailed info about the conntrack table +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return conntrackStatsFromFile(common.HostProcWithContext(ctx, "net/stat/nf_conntrack"), percpu) +} + +// conntrackStatsFromFile returns more detailed info about the conntrack table +// from `filename` +// If 'percpu' is false, the result will contain exactly one item with totals/summary +func conntrackStatsFromFile(filename string, percpu bool) ([]ConntrackStat, error) { + lines, err := common.ReadLines(filename) + if err != nil { + return nil, err + } + + statlist := NewConntrackStatList() + + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) == 17 && fields[0] != "entries" { + statlist.Append(NewConntrackStat( + common.HexToUint32(fields[ctENTRIES]), + common.HexToUint32(fields[ctSEARCHED]), + common.HexToUint32(fields[ctFOUND]), + common.HexToUint32(fields[ctNEW]), + common.HexToUint32(fields[ctINVALID]), + common.HexToUint32(fields[ctIGNORE]), + common.HexToUint32(fields[ctDELETE]), + common.HexToUint32(fields[ctDELETE_LIST]), + common.HexToUint32(fields[ctINSERT]), + common.HexToUint32(fields[ctINSERT_FAILED]), + common.HexToUint32(fields[ctDROP]), + common.HexToUint32(fields[ctEARLY_DROP]), + common.HexToUint32(fields[ctICMP_ERROR]), + common.HexToUint32(fields[CT_EXPEctNEW]), + common.HexToUint32(fields[ctEXPECT_CREATE]), + common.HexToUint32(fields[CT_EXPEctDELETE]), + common.HexToUint32(fields[ctSEARCH_RESTART]), + )) + } + } + + if percpu { + return statlist.Items(), nil + } + return statlist.Summary(), nil +} + +// http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h +var tcpStatuses = map[string]string{ + "01": "ESTABLISHED", + "02": "SYN_SENT", + "03": "SYN_RECV", + "04": "FIN_WAIT1", + "05": "FIN_WAIT2", + "06": "TIME_WAIT", + "07": "CLOSE", + "08": "CLOSE_WAIT", + "09": "LAST_ACK", + "0A": "LISTEN", + "0B": "CLOSING", +} + +type netConnectionKindType struct { + family uint32 + sockType uint32 + filename string +} + +var kindTCP4 = netConnectionKindType{ + family: syscall.AF_INET, + sockType: syscall.SOCK_STREAM, + filename: "tcp", +} + +var kindTCP6 = netConnectionKindType{ + family: syscall.AF_INET6, + sockType: syscall.SOCK_STREAM, + filename: "tcp6", +} + +var kindUDP4 = netConnectionKindType{ + family: syscall.AF_INET, + sockType: syscall.SOCK_DGRAM, + filename: "udp", +} + +var kindUDP6 = netConnectionKindType{ + family: syscall.AF_INET6, + sockType: syscall.SOCK_DGRAM, + filename: "udp6", +} + +var kindUNIX = netConnectionKindType{ + family: syscall.AF_UNIX, + filename: "unix", +} + +var netConnectionKindMap = map[string][]netConnectionKindType{ + "all": {kindTCP4, kindTCP6, kindUDP4, kindUDP6, kindUNIX}, + "tcp": {kindTCP4, kindTCP6}, + "tcp4": {kindTCP4}, + "tcp6": {kindTCP6}, + "udp": {kindUDP4, kindUDP6}, + "udp4": {kindUDP4}, + "udp6": {kindUDP6}, + "unix": {kindUNIX}, + "inet": {kindTCP4, kindTCP6, kindUDP4, kindUDP6}, + "inet4": {kindTCP4, kindUDP4}, + "inet6": {kindTCP6, kindUDP6}, +} + +type inodeMap struct { + pid int32 + fd uint32 +} + +type connTmp struct { + fd uint32 + family uint32 + sockType uint32 + laddr Addr + raddr Addr + status string + pid int32 + boundPid int32 + path string +} + +// Return a list of network connections opened. +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsPidWithContext(ctx, kind, 0) +} + +// Return a list of network connections opened returning at most `max` +// connections for each running process. +func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { + return ConnectionsMaxWithContext(context.Background(), kind, max) +} + +func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithContext(ctx, kind, 0, max) +} + +// Return a list of network connections opened, omitting `Uids`. +// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be +// removed from the API in the future. +func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { + return ConnectionsWithoutUidsWithContext(context.Background(), kind) +} + +func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) +} + +func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max) +} + +// Return a list of network connections opened by a process. +func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithContext(ctx, kind, pid, 0) +} + +func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0) +} + +// Return up to `max` network connections opened by a process. +func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, false) +} + +func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, true) +} + +func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int, skipUids bool) ([]ConnectionStat, error) { + tmap, ok := netConnectionKindMap[kind] + if !ok { + return nil, fmt.Errorf("invalid kind, %s", kind) + } + root := common.HostProcWithContext(ctx) + var err error + var inodes map[string][]inodeMap + if pid == 0 { + inodes, err = getProcInodesAllWithContext(ctx, root, max) + } else { + inodes, err = getProcInodes(root, pid, max) + if len(inodes) == 0 { + // no connection for the pid + return []ConnectionStat{}, nil + } + } + if err != nil { + return nil, fmt.Errorf("cound not get pid(s), %d: %w", pid, err) + } + return statsFromInodesWithContext(ctx, root, pid, tmap, inodes, skipUids) +} + +func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap, skipUids bool) ([]ConnectionStat, error) { + return statsFromInodesWithContext(context.Background(), root, pid, tmap, inodes, skipUids) +} + +func statsFromInodesWithContext(ctx context.Context, root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap, skipUids bool) ([]ConnectionStat, error) { + dupCheckMap := make(map[string]struct{}) + var ret []ConnectionStat + + var err error + for _, t := range tmap { + var path string + var connKey string + var ls []connTmp + if pid == 0 { + path = fmt.Sprintf("%s/net/%s", root, t.filename) + } else { + path = fmt.Sprintf("%s/%d/net/%s", root, pid, t.filename) + } + switch t.family { + case syscall.AF_INET, syscall.AF_INET6: + ls, err = processInetWithContext(ctx, path, t, inodes, pid) + case syscall.AF_UNIX: + ls, err = processUnix(path, t, inodes, pid) + } + if err != nil { + return nil, err + } + for _, c := range ls { + // Build TCP key to id the connection uniquely + // socket type, src ip, src port, dst ip, dst port and state should be enough + // to prevent duplications. + connKey = fmt.Sprintf("%d-%s:%d-%s:%d-%s", c.sockType, c.laddr.IP, c.laddr.Port, c.raddr.IP, c.raddr.Port, c.status) + if _, ok := dupCheckMap[connKey]; ok { + continue + } + + conn := ConnectionStat{ + Fd: c.fd, + Family: c.family, + Type: c.sockType, + Laddr: c.laddr, + Raddr: c.raddr, + Status: c.status, + Pid: c.pid, + } + if c.pid == 0 { + conn.Pid = c.boundPid + } else { + conn.Pid = c.pid + } + + if !skipUids { + // fetch process owner Real, effective, saved set, and filesystem UIDs + proc := process{Pid: conn.Pid} + conn.Uids, _ = proc.getUids(ctx) + } + + ret = append(ret, conn) + dupCheckMap[connKey] = struct{}{} + } + + } + + return ret, nil +} + +// getProcInodes returns fd of the pid. +func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, error) { + ret := make(map[string][]inodeMap) + + dir := fmt.Sprintf("%s/%d/fd", root, pid) + f, err := os.Open(dir) + if err != nil { + return ret, err + } + defer f.Close() + dirEntries, err := readDir(f, max) + if err != nil { + return ret, err + } + for _, dirEntry := range dirEntries { + inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, dirEntry.Name()) + + inode, err := os.Readlink(inodePath) + if err != nil { + continue + } + if !strings.HasPrefix(inode, "socket:[") { + continue + } + // the process is using a socket + l := len(inode) + inode = inode[8 : l-1] + _, ok := ret[inode] + if !ok { + ret[inode] = make([]inodeMap, 0) + } + fd, err := strconv.Atoi(dirEntry.Name()) + if err != nil { + continue + } + + i := inodeMap{ + pid: pid, + fd: uint32(fd), + } + ret[inode] = append(ret[inode], i) + } + return ret, nil +} + +// Pids retunres all pids. +// Note: this is a copy of process_linux.Pids() +// FIXME: Import process occures import cycle. +// move to common made other platform breaking. Need consider. +func Pids() ([]int32, error) { + return PidsWithContext(context.Background()) +} + +func PidsWithContext(ctx context.Context) ([]int32, error) { + var ret []int32 + + d, err := os.Open(common.HostProcWithContext(ctx)) + if err != nil { + return nil, err + } + defer d.Close() + + fnames, err := d.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, fname := range fnames { + pid, err := strconv.ParseInt(fname, 10, 32) + if err != nil { + // if not numeric name, just skip + continue + } + ret = append(ret, int32(pid)) + } + + return ret, nil +} + +// Note: the following is based off process_linux structs and methods +// we need these to fetch the owner of a process ID +// FIXME: Import process occures import cycle. +// see remarks on pids() +type process struct { + Pid int32 `json:"pid"` + uids []int32 +} + +// Uids returns user ids of the process as a slice of the int +func (p *process) getUids(ctx context.Context) ([]int32, error) { + err := p.fillFromStatus(ctx) + if err != nil { + return []int32{}, err + } + return p.uids, nil +} + +// Get status from /proc/(pid)/status +func (p *process) fillFromStatus(ctx context.Context) error { + pid := p.Pid + statPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "status") + contents, err := os.ReadFile(statPath) + if err != nil { + return err + } + lines := strings.Split(string(contents), "\n") + for _, line := range lines { + tabParts := strings.SplitN(line, "\t", 2) + if len(tabParts) < 2 { + continue + } + value := tabParts[1] + switch strings.TrimRight(tabParts[0], ":") { + case "Uid": + p.uids = make([]int32, 0, 4) + for _, i := range strings.Split(value, "\t") { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.uids = append(p.uids, int32(v)) + } + } + } + return nil +} + +func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) { + return getProcInodesAllWithContext(context.Background(), root, max) +} + +func getProcInodesAllWithContext(ctx context.Context, root string, max int) (map[string][]inodeMap, error) { + pids, err := PidsWithContext(ctx) + if err != nil { + return nil, err + } + ret := make(map[string][]inodeMap) + + for _, pid := range pids { + t, err := getProcInodes(root, pid, max) + if err != nil { + // skip if permission error or no longer exists + if os.IsPermission(err) || os.IsNotExist(err) || errors.Is(err, io.EOF) { + continue + } + return ret, err + } + if len(t) == 0 { + continue + } + // TODO: update ret. + ret = updateMap(ret, t) + } + return ret, nil +} + +// decodeAddress decode addresse represents addr in proc/net/* +// ex: +// "0500000A:0016" -> "10.0.0.5", 22 +// "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53 +func decodeAddress(family uint32, src string) (Addr, error) { + return decodeAddressWithContext(context.Background(), family, src) +} + +func decodeAddressWithContext(ctx context.Context, family uint32, src string) (Addr, error) { + t := strings.Split(src, ":") + if len(t) != 2 { + return Addr{}, fmt.Errorf("does not contain port, %s", src) + } + addr := t[0] + port, err := strconv.ParseUint(t[1], 16, 16) + if err != nil { + return Addr{}, fmt.Errorf("invalid port, %s", src) + } + decoded, err := hex.DecodeString(addr) + if err != nil { + return Addr{}, fmt.Errorf("decode error, %w", err) + } + var ip net.IP + + if family == syscall.AF_INET { + if common.IsLittleEndian() { + ip = net.IP(ReverseWithContext(ctx, decoded)) + } else { + ip = net.IP(decoded) + } + } else { // IPv6 + ip, err = parseIPv6HexStringWithContext(ctx, decoded) + if err != nil { + return Addr{}, err + } + } + return Addr{ + IP: ip.String(), + Port: uint32(port), + }, nil +} + +// Reverse reverses array of bytes. +func Reverse(s []byte) []byte { + return ReverseWithContext(context.Background(), s) +} + +func ReverseWithContext(ctx context.Context, s []byte) []byte { + for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// parseIPv6HexString parse array of bytes to IPv6 string +func parseIPv6HexString(src []byte) (net.IP, error) { + return parseIPv6HexStringWithContext(context.Background(), src) +} + +func parseIPv6HexStringWithContext(ctx context.Context, src []byte) (net.IP, error) { + if len(src) != 16 { + return nil, fmt.Errorf("invalid IPv6 string") + } + + buf := make([]byte, 0, 16) + for i := 0; i < len(src); i += 4 { + r := ReverseWithContext(ctx, src[i:i+4]) + buf = append(buf, r...) + } + return net.IP(buf), nil +} + +func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { + return processInetWithContext(context.Background(), file, kind, inodes, filterPid) +} + +func processInetWithContext(ctx context.Context, file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { + if strings.HasSuffix(file, "6") && !common.PathExists(file) { + // IPv6 not supported, return empty. + return []connTmp{}, nil + } + + // Read the contents of the /proc file with a single read sys call. + // This minimizes duplicates in the returned connections + // For more info: + // https://github.com/shirou/gopsutil/pull/361 + contents, err := os.ReadFile(file) + if err != nil { + return nil, err + } + + lines := bytes.Split(contents, []byte("\n")) + + var ret []connTmp + // skip first line + for _, line := range lines[1:] { + l := strings.Fields(string(line)) + if len(l) < 10 { + continue + } + laddr := l[1] + raddr := l[2] + status := l[3] + inode := l[9] + pid := int32(0) + fd := uint32(0) + i, exists := inodes[inode] + if exists { + pid = i[0].pid + fd = i[0].fd + } + if filterPid > 0 && filterPid != pid { + continue + } + if kind.sockType == syscall.SOCK_STREAM { + status = tcpStatuses[status] + } else { + status = "NONE" + } + la, err := decodeAddressWithContext(ctx, kind.family, laddr) + if err != nil { + continue + } + ra, err := decodeAddressWithContext(ctx, kind.family, raddr) + if err != nil { + continue + } + + ret = append(ret, connTmp{ + fd: fd, + family: kind.family, + sockType: kind.sockType, + laddr: la, + raddr: ra, + status: status, + pid: pid, + }) + } + + return ret, nil +} + +func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { + // Read the contents of the /proc file with a single read sys call. + // This minimizes duplicates in the returned connections + // For more info: + // https://github.com/shirou/gopsutil/pull/361 + contents, err := os.ReadFile(file) + if err != nil { + return nil, err + } + + lines := bytes.Split(contents, []byte("\n")) + + var ret []connTmp + // skip first line + for _, line := range lines[1:] { + tokens := strings.Fields(string(line)) + if len(tokens) < 6 { + continue + } + st, err := strconv.Atoi(tokens[4]) + if err != nil { + return nil, err + } + + inode := tokens[6] + + var pairs []inodeMap + pairs, exists := inodes[inode] + if !exists { + pairs = []inodeMap{ + {}, + } + } + for _, pair := range pairs { + if filterPid > 0 && filterPid != pair.pid { + continue + } + var path string + if len(tokens) == 8 { + path = tokens[len(tokens)-1] + } + ret = append(ret, connTmp{ + fd: pair.fd, + family: kind.family, + sockType: uint32(st), + laddr: Addr{ + IP: path, + }, + pid: pair.pid, + status: "NONE", + path: path, + }) + } + } + + return ret, nil +} + +func updateMap(src map[string][]inodeMap, add map[string][]inodeMap) map[string][]inodeMap { + for key, value := range add { + a, exists := src[key] + if !exists { + src[key] = value + continue + } + src[key] = append(a, value...) + } + return src +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_linux_111.go b/vendor/github.com/shirou/gopsutil/v3/net/net_linux_111.go new file mode 100644 index 000000000..bd5c95871 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_linux_111.go @@ -0,0 +1,12 @@ +//go:build !go1.16 +// +build !go1.16 + +package net + +import ( + "os" +) + +func readDir(f *os.File, max int) ([]os.FileInfo, error) { + return f.Readdir(max) +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_linux_116.go b/vendor/github.com/shirou/gopsutil/v3/net/net_linux_116.go new file mode 100644 index 000000000..a45072e92 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_linux_116.go @@ -0,0 +1,12 @@ +//go:build go1.16 +// +build go1.16 + +package net + +import ( + "os" +) + +func readDir(f *os.File, max int) ([]os.DirEntry, error) { + return f.ReadDir(max) +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_openbsd.go b/vendor/github.com/shirou/gopsutil/v3/net/net_openbsd.go new file mode 100644 index 000000000..cf48f53e7 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_openbsd.go @@ -0,0 +1,319 @@ +//go:build openbsd +// +build openbsd + +package net + +import ( + "context" + "fmt" + "os/exec" + "regexp" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`) + +func ParseNetstat(output string, mode string, + iocs map[string]IOCountersStat) error { + lines := strings.Split(output, "\n") + + exists := make([]string, 0, len(lines)-1) + + columns := 6 + if mode == "ind" { + columns = 10 + } + for _, line := range lines { + values := strings.Fields(line) + if len(values) < 1 || values[0] == "Name" { + continue + } + if common.StringsHas(exists, values[0]) { + // skip if already get + continue + } + + if len(values) < columns { + continue + } + base := 1 + // sometimes Address is omitted + if len(values) < columns { + base = 0 + } + + parsed := make([]uint64, 0, 8) + var vv []string + if mode == "inb" { + vv = []string{ + values[base+3], // BytesRecv + values[base+4], // BytesSent + } + } else { + vv = []string{ + values[base+3], // Ipkts + values[base+4], // Ierrs + values[base+5], // Opkts + values[base+6], // Oerrs + values[base+8], // Drops + } + } + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return err + } + parsed = append(parsed, t) + } + exists = append(exists, values[0]) + + n, present := iocs[values[0]] + if !present { + n = IOCountersStat{Name: values[0]} + } + if mode == "inb" { + n.BytesRecv = parsed[0] + n.BytesSent = parsed[1] + } else { + n.PacketsRecv = parsed[0] + n.Errin = parsed[1] + n.PacketsSent = parsed[2] + n.Errout = parsed[3] + n.Dropin = parsed[4] + n.Dropout = parsed[4] + } + + iocs[n.Name] = n + } + return nil +} + +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + netstat, err := exec.LookPath("netstat") + if err != nil { + return nil, err + } + out, err := invoke.CommandWithContext(ctx, netstat, "-inb") + if err != nil { + return nil, err + } + out2, err := invoke.CommandWithContext(ctx, netstat, "-ind") + if err != nil { + return nil, err + } + iocs := make(map[string]IOCountersStat) + + lines := strings.Split(string(out), "\n") + ret := make([]IOCountersStat, 0, len(lines)-1) + + err = ParseNetstat(string(out), "inb", iocs) + if err != nil { + return nil, err + } + err = ParseNetstat(string(out2), "ind", iocs) + if err != nil { + return nil, err + } + + for _, ioc := range iocs { + ret = append(ret, ioc) + } + + if pernic == false { + return getIOCountersAll(ret) + } + + return ret, nil +} + +// IOCountersByFile exists just for compatibility with Linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return nil, common.ErrNotImplementedError +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + +// ProtoCounters returns network statistics for the entire system +// If protocols is empty then all protocols are returned, otherwise +// just the protocols in the list are returned. +// Not Implemented for OpenBSD +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return nil, common.ErrNotImplementedError +} + +func parseNetstatLine(line string) (ConnectionStat, error) { + f := strings.Fields(line) + if len(f) < 5 { + return ConnectionStat{}, fmt.Errorf("wrong line,%s", line) + } + + var netType, netFamily uint32 + switch f[0] { + case "tcp": + netType = syscall.SOCK_STREAM + netFamily = syscall.AF_INET + case "udp": + netType = syscall.SOCK_DGRAM + netFamily = syscall.AF_INET + case "tcp6": + netType = syscall.SOCK_STREAM + netFamily = syscall.AF_INET6 + case "udp6": + netType = syscall.SOCK_DGRAM + netFamily = syscall.AF_INET6 + default: + return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0]) + } + + laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily) + if err != nil { + return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4]) + } + + n := ConnectionStat{ + Fd: uint32(0), // not supported + Family: uint32(netFamily), + Type: uint32(netType), + Laddr: laddr, + Raddr: raddr, + Pid: int32(0), // not supported + } + if len(f) == 6 { + n.Status = f[5] + } + + return n, nil +} + +func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) { + parse := func(l string) (Addr, error) { + matches := portMatch.FindStringSubmatch(l) + if matches == nil { + return Addr{}, fmt.Errorf("wrong addr, %s", l) + } + host := matches[1] + port := matches[2] + if host == "*" { + switch family { + case syscall.AF_INET: + host = "0.0.0.0" + case syscall.AF_INET6: + host = "::" + default: + return Addr{}, fmt.Errorf("unknown family, %d", family) + } + } + lport, err := strconv.Atoi(port) + if err != nil { + return Addr{}, err + } + return Addr{IP: host, Port: uint32(lport)}, nil + } + + laddr, err = parse(local) + if remote != "*.*" { // remote addr exists + raddr, err = parse(remote) + if err != nil { + return laddr, raddr, err + } + } + + return laddr, raddr, err +} + +// Return a list of network connections opened. +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + var ret []ConnectionStat + + args := []string{"-na"} + switch strings.ToLower(kind) { + default: + fallthrough + case "": + fallthrough + case "all": + fallthrough + case "inet": + // nothing to add + case "inet4": + args = append(args, "-finet") + case "inet6": + args = append(args, "-finet6") + case "tcp": + args = append(args, "-ptcp") + case "tcp4": + args = append(args, "-ptcp", "-finet") + case "tcp6": + args = append(args, "-ptcp", "-finet6") + case "udp": + args = append(args, "-pudp") + case "udp4": + args = append(args, "-pudp", "-finet") + case "udp6": + args = append(args, "-pudp", "-finet6") + case "unix": + return ret, common.ErrNotImplementedError + } + + netstat, err := exec.LookPath("netstat") + if err != nil { + return nil, err + } + out, err := invoke.CommandWithContext(ctx, netstat, args...) + if err != nil { + return nil, err + } + lines := strings.Split(string(out), "\n") + for _, line := range lines { + if !(strings.HasPrefix(line, "tcp") || strings.HasPrefix(line, "udp")) { + continue + } + n, err := parseNetstatLine(line) + if err != nil { + continue + } + + ret = append(ret, n) + } + + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_solaris.go b/vendor/github.com/shirou/gopsutil/v3/net/net_solaris.go new file mode 100644 index 000000000..79d8ac30e --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_solaris.go @@ -0,0 +1,144 @@ +//go:build solaris +// +build solaris + +package net + +import ( + "context" + "fmt" + "regexp" + "runtime" + "strconv" + "strings" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +// NetIOCounters returnes network I/O statistics for every network +// interface installed on the system. If pernic argument is false, +// return only sum of all information (which name is 'all'). If true, +// every network interface installed on the system is returned +// separately. +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +var kstatSplit = regexp.MustCompile(`[:\s]+`) + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + // collect all the net class's links with below statistics + filterstr := "/^(?!vnic)/::phys:/^rbytes64$|^ipackets64$|^idrops64$|^ierrors$|^obytes64$|^opackets64$|^odrops64$|^oerrors$/" + if runtime.GOOS == "illumos" { + filterstr = "/[^vnic]/::mac:/^rbytes64$|^ipackets64$|^idrops64$|^ierrors$|^obytes64$|^opackets64$|^odrops64$|^oerrors$/" + } + kstatSysOut, err := invoke.CommandWithContext(ctx, "kstat", "-c", "net", "-p", filterstr) + if err != nil { + return nil, fmt.Errorf("cannot execute kstat: %w", err) + } + + lines := strings.Split(strings.TrimSpace(string(kstatSysOut)), "\n") + if len(lines) == 0 { + return nil, fmt.Errorf("no interface found") + } + rbytes64arr := make(map[string]uint64) + ipackets64arr := make(map[string]uint64) + idrops64arr := make(map[string]uint64) + ierrorsarr := make(map[string]uint64) + obytes64arr := make(map[string]uint64) + opackets64arr := make(map[string]uint64) + odrops64arr := make(map[string]uint64) + oerrorsarr := make(map[string]uint64) + + for _, line := range lines { + fields := kstatSplit.Split(line, -1) + interfaceName := fields[0] + instance := fields[1] + switch fields[3] { + case "rbytes64": + rbytes64arr[interfaceName+instance], err = strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, fmt.Errorf("cannot parse rbytes64: %w", err) + } + case "ipackets64": + ipackets64arr[interfaceName+instance], err = strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, fmt.Errorf("cannot parse ipackets64: %w", err) + } + case "idrops64": + idrops64arr[interfaceName+instance], err = strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, fmt.Errorf("cannot parse idrops64: %w", err) + } + case "ierrors": + ierrorsarr[interfaceName+instance], err = strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, fmt.Errorf("cannot parse ierrors: %w", err) + } + case "obytes64": + obytes64arr[interfaceName+instance], err = strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, fmt.Errorf("cannot parse obytes64: %w", err) + } + case "opackets64": + opackets64arr[interfaceName+instance], err = strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, fmt.Errorf("cannot parse opackets64: %w", err) + } + case "odrops64": + odrops64arr[interfaceName+instance], err = strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, fmt.Errorf("cannot parse odrops64: %w", err) + } + case "oerrors": + oerrorsarr[interfaceName+instance], err = strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, fmt.Errorf("cannot parse oerrors: %w", err) + } + } + } + ret := make([]IOCountersStat, 0) + for k := range rbytes64arr { + nic := IOCountersStat{ + Name: k, + BytesRecv: rbytes64arr[k], + PacketsRecv: ipackets64arr[k], + Errin: ierrorsarr[k], + Dropin: idrops64arr[k], + BytesSent: obytes64arr[k], + PacketsSent: opackets64arr[k], + Errout: oerrorsarr[k], + Dropout: odrops64arr[k], + } + ret = append(ret, nic) + } + + if !pernic { + return getIOCountersAll(ret) + } + + return ret, nil +} + +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return []FilterStat{}, common.ErrNotImplementedError +} + +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return []ProtoCountersStat{}, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_unix.go b/vendor/github.com/shirou/gopsutil/v3/net/net_unix.go new file mode 100644 index 000000000..cb846e28a --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_unix.go @@ -0,0 +1,224 @@ +//go:build freebsd || darwin +// +build freebsd darwin + +package net + +import ( + "context" + "fmt" + "net" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +// Return a list of network connections opened. +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsPidWithContext(ctx, kind, 0) +} + +// Return a list of network connections opened returning at most `max` +// connections for each running process. +func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { + return ConnectionsMaxWithContext(context.Background(), kind, max) +} + +func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +// Return a list of network connections opened by a process. +func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + var ret []ConnectionStat + + args := []string{"-i"} + switch strings.ToLower(kind) { + default: + fallthrough + case "": + fallthrough + case "all": + fallthrough + case "inet": + args = append(args, "tcp", "-i", "udp") + case "inet4": + args = append(args, "4") + case "inet6": + args = append(args, "6") + case "tcp": + args = append(args, "tcp") + case "tcp4": + args = append(args, "4tcp") + case "tcp6": + args = append(args, "6tcp") + case "udp": + args = append(args, "udp") + case "udp4": + args = append(args, "4udp") + case "udp6": + args = append(args, "6udp") + case "unix": + args = []string{"-U"} + } + + r, err := common.CallLsofWithContext(ctx, invoke, pid, args...) + if err != nil { + return nil, err + } + for _, rr := range r { + if strings.HasPrefix(rr, "COMMAND") { + continue + } + n, err := parseNetLine(rr) + if err != nil { + continue + } + + ret = append(ret, n) + } + + return ret, nil +} + +var constMap = map[string]int{ + "unix": syscall.AF_UNIX, + "TCP": syscall.SOCK_STREAM, + "UDP": syscall.SOCK_DGRAM, + "IPv4": syscall.AF_INET, + "IPv6": syscall.AF_INET6, +} + +func parseNetLine(line string) (ConnectionStat, error) { + f := strings.Fields(line) + if len(f) < 8 { + return ConnectionStat{}, fmt.Errorf("wrong line,%s", line) + } + + if len(f) == 8 { + f = append(f, f[7]) + f[7] = "unix" + } + + pid, err := strconv.Atoi(f[1]) + if err != nil { + return ConnectionStat{}, err + } + fd, err := strconv.Atoi(strings.Trim(f[3], "u")) + if err != nil { + return ConnectionStat{}, fmt.Errorf("unknown fd, %s", f[3]) + } + netFamily, ok := constMap[f[4]] + if !ok { + return ConnectionStat{}, fmt.Errorf("unknown family, %s", f[4]) + } + netType, ok := constMap[f[7]] + if !ok { + return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[7]) + } + + var laddr, raddr Addr + if f[7] == "unix" { + laddr.IP = f[8] + } else { + laddr, raddr, err = parseNetAddr(f[8]) + if err != nil { + return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s", f[8]) + } + } + + n := ConnectionStat{ + Fd: uint32(fd), + Family: uint32(netFamily), + Type: uint32(netType), + Laddr: laddr, + Raddr: raddr, + Pid: int32(pid), + } + if len(f) == 10 { + n.Status = strings.Trim(f[9], "()") + } + + return n, nil +} + +func parseNetAddr(line string) (laddr Addr, raddr Addr, err error) { + parse := func(l string) (Addr, error) { + host, port, err := net.SplitHostPort(l) + if err != nil { + return Addr{}, fmt.Errorf("wrong addr, %s", l) + } + lport, err := strconv.Atoi(port) + if err != nil { + return Addr{}, err + } + return Addr{IP: host, Port: uint32(lport)}, nil + } + + addrs := strings.Split(line, "->") + if len(addrs) == 0 { + return laddr, raddr, fmt.Errorf("wrong netaddr, %s", line) + } + laddr, err = parse(addrs[0]) + if len(addrs) == 2 { // remote addr exists + raddr, err = parse(addrs[1]) + if err != nil { + return laddr, raddr, err + } + } + + return laddr, raddr, err +} + +// Return up to `max` network connections opened by a process. +func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +// Return a list of network connections opened, omitting `Uids`. +// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be +// removed from the API in the future. +func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { + return ConnectionsWithoutUidsWithContext(context.Background(), kind) +} + +func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) +} + +func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max) +} + +func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0) +} + +func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max) +} + +func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/net/net_windows.go b/vendor/github.com/shirou/gopsutil/v3/net/net_windows.go new file mode 100644 index 000000000..5d384342f --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/net/net_windows.go @@ -0,0 +1,779 @@ +//go:build windows +// +build windows + +package net + +import ( + "context" + "fmt" + "net" + "os" + "syscall" + "unsafe" + + "github.com/shirou/gopsutil/v3/internal/common" + "golang.org/x/sys/windows" +) + +var ( + modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") + procGetExtendedTCPTable = modiphlpapi.NewProc("GetExtendedTcpTable") + procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable") + procGetIfEntry2 = modiphlpapi.NewProc("GetIfEntry2") +) + +const ( + TCPTableBasicListener = iota + TCPTableBasicConnections + TCPTableBasicAll + TCPTableOwnerPIDListener + TCPTableOwnerPIDConnections + TCPTableOwnerPIDAll + TCPTableOwnerModuleListener + TCPTableOwnerModuleConnections + TCPTableOwnerModuleAll +) + +type netConnectionKindType struct { + family uint32 + sockType uint32 + filename string +} + +var kindTCP4 = netConnectionKindType{ + family: syscall.AF_INET, + sockType: syscall.SOCK_STREAM, + filename: "tcp", +} + +var kindTCP6 = netConnectionKindType{ + family: syscall.AF_INET6, + sockType: syscall.SOCK_STREAM, + filename: "tcp6", +} + +var kindUDP4 = netConnectionKindType{ + family: syscall.AF_INET, + sockType: syscall.SOCK_DGRAM, + filename: "udp", +} + +var kindUDP6 = netConnectionKindType{ + family: syscall.AF_INET6, + sockType: syscall.SOCK_DGRAM, + filename: "udp6", +} + +var netConnectionKindMap = map[string][]netConnectionKindType{ + "all": {kindTCP4, kindTCP6, kindUDP4, kindUDP6}, + "tcp": {kindTCP4, kindTCP6}, + "tcp4": {kindTCP4}, + "tcp6": {kindTCP6}, + "udp": {kindUDP4, kindUDP6}, + "udp4": {kindUDP4}, + "udp6": {kindUDP6}, + "inet": {kindTCP4, kindTCP6, kindUDP4, kindUDP6}, + "inet4": {kindTCP4, kindUDP4}, + "inet6": {kindTCP6, kindUDP6}, +} + +// https://github.com/microsoft/ethr/blob/aecdaf923970e5a9b4c461b4e2e3963d781ad2cc/plt_windows.go#L114-L170 +type guid struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + +const ( + maxStringSize = 256 + maxPhysAddressLength = 32 + pad0for64_4for32 = 0 +) + +type mibIfRow2 struct { + InterfaceLuid uint64 + InterfaceIndex uint32 + InterfaceGuid guid + Alias [maxStringSize + 1]uint16 + Description [maxStringSize + 1]uint16 + PhysicalAddressLength uint32 + PhysicalAddress [maxPhysAddressLength]uint8 + PermanentPhysicalAddress [maxPhysAddressLength]uint8 + Mtu uint32 + Type uint32 + TunnelType uint32 + MediaType uint32 + PhysicalMediumType uint32 + AccessType uint32 + DirectionType uint32 + InterfaceAndOperStatusFlags uint32 + OperStatus uint32 + AdminStatus uint32 + MediaConnectState uint32 + NetworkGuid guid + ConnectionType uint32 + padding1 [pad0for64_4for32]byte + TransmitLinkSpeed uint64 + ReceiveLinkSpeed uint64 + InOctets uint64 + InUcastPkts uint64 + InNUcastPkts uint64 + InDiscards uint64 + InErrors uint64 + InUnknownProtos uint64 + InUcastOctets uint64 + InMulticastOctets uint64 + InBroadcastOctets uint64 + OutOctets uint64 + OutUcastPkts uint64 + OutNUcastPkts uint64 + OutDiscards uint64 + OutErrors uint64 + OutUcastOctets uint64 + OutMulticastOctets uint64 + OutBroadcastOctets uint64 + OutQLen uint64 +} + +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + ifs, err := net.Interfaces() + if err != nil { + return nil, err + } + var counters []IOCountersStat + + err = procGetIfEntry2.Find() + if err == nil { // Vista+, uint64 values (issue#693) + for _, ifi := range ifs { + c := IOCountersStat{ + Name: ifi.Name, + } + + row := mibIfRow2{InterfaceIndex: uint32(ifi.Index)} + ret, _, err := procGetIfEntry2.Call(uintptr(unsafe.Pointer(&row))) + if ret != 0 { + return nil, os.NewSyscallError("GetIfEntry2", err) + } + c.BytesSent = uint64(row.OutOctets) + c.BytesRecv = uint64(row.InOctets) + c.PacketsSent = uint64(row.OutUcastPkts) + c.PacketsRecv = uint64(row.InUcastPkts) + c.Errin = uint64(row.InErrors) + c.Errout = uint64(row.OutErrors) + c.Dropin = uint64(row.InDiscards) + c.Dropout = uint64(row.OutDiscards) + + counters = append(counters, c) + } + } else { // WinXP fallback, uint32 values + for _, ifi := range ifs { + c := IOCountersStat{ + Name: ifi.Name, + } + + row := windows.MibIfRow{Index: uint32(ifi.Index)} + err = windows.GetIfEntry(&row) + if err != nil { + return nil, os.NewSyscallError("GetIfEntry", err) + } + c.BytesSent = uint64(row.OutOctets) + c.BytesRecv = uint64(row.InOctets) + c.PacketsSent = uint64(row.OutUcastPkts) + c.PacketsRecv = uint64(row.InUcastPkts) + c.Errin = uint64(row.InErrors) + c.Errout = uint64(row.OutErrors) + c.Dropin = uint64(row.InDiscards) + c.Dropout = uint64(row.OutDiscards) + + counters = append(counters, c) + } + } + + if !pernic { + return getIOCountersAll(counters) + } + return counters, nil +} + +// IOCountersByFile exists just for compatibility with Linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + +// Return a list of network connections +// Available kind: +// +// reference to netConnectionKindMap +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsPidWithContext(ctx, kind, 0) +} + +// ConnectionsPid Return a list of network connections opened by a process +func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + tmap, ok := netConnectionKindMap[kind] + if !ok { + return nil, fmt.Errorf("invalid kind, %s", kind) + } + return getProcInet(tmap, pid) +} + +func getProcInet(kinds []netConnectionKindType, pid int32) ([]ConnectionStat, error) { + stats := make([]ConnectionStat, 0) + + for _, kind := range kinds { + s, err := getNetStatWithKind(kind) + if err != nil { + continue + } + + if pid == 0 { + stats = append(stats, s...) + } else { + for _, ns := range s { + if ns.Pid != pid { + continue + } + stats = append(stats, ns) + } + } + } + + return stats, nil +} + +func getNetStatWithKind(kindType netConnectionKindType) ([]ConnectionStat, error) { + if kindType.filename == "" { + return nil, fmt.Errorf("kind filename must be required") + } + + switch kindType.filename { + case kindTCP4.filename: + return getTCPConnections(kindTCP4.family) + case kindTCP6.filename: + return getTCPConnections(kindTCP6.family) + case kindUDP4.filename: + return getUDPConnections(kindUDP4.family) + case kindUDP6.filename: + return getUDPConnections(kindUDP6.family) + } + + return nil, fmt.Errorf("invalid kind filename, %s", kindType.filename) +} + +// Return a list of network connections opened returning at most `max` +// connections for each running process. +func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { + return ConnectionsMaxWithContext(context.Background(), kind, max) +} + +func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +// Return a list of network connections opened, omitting `Uids`. +// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be +// removed from the API in the future. +func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { + return ConnectionsWithoutUidsWithContext(context.Background(), kind) +} + +func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) +} + +func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max) +} + +func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0) +} + +func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max) +} + +func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return nil, common.ErrNotImplementedError +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + +// ProtoCounters returns network statistics for the entire system +// If protocols is empty then all protocols are returned, otherwise +// just the protocols in the list are returned. +// Not Implemented for Windows +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return nil, common.ErrNotImplementedError +} + +func getTableUintptr(family uint32, buf []byte) uintptr { + var ( + pmibTCPTable pmibTCPTableOwnerPidAll + pmibTCP6Table pmibTCP6TableOwnerPidAll + + p uintptr + ) + switch family { + case kindTCP4.family: + if len(buf) > 0 { + pmibTCPTable = (*mibTCPTableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibTCPTable)) + } else { + p = uintptr(unsafe.Pointer(pmibTCPTable)) + } + case kindTCP6.family: + if len(buf) > 0 { + pmibTCP6Table = (*mibTCP6TableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibTCP6Table)) + } else { + p = uintptr(unsafe.Pointer(pmibTCP6Table)) + } + } + return p +} + +func getTableInfo(filename string, table interface{}) (index, step, length int) { + switch filename { + case kindTCP4.filename: + index = int(unsafe.Sizeof(table.(pmibTCPTableOwnerPidAll).DwNumEntries)) + step = int(unsafe.Sizeof(table.(pmibTCPTableOwnerPidAll).Table)) + length = int(table.(pmibTCPTableOwnerPidAll).DwNumEntries) + case kindTCP6.filename: + index = int(unsafe.Sizeof(table.(pmibTCP6TableOwnerPidAll).DwNumEntries)) + step = int(unsafe.Sizeof(table.(pmibTCP6TableOwnerPidAll).Table)) + length = int(table.(pmibTCP6TableOwnerPidAll).DwNumEntries) + case kindUDP4.filename: + index = int(unsafe.Sizeof(table.(pmibUDPTableOwnerPid).DwNumEntries)) + step = int(unsafe.Sizeof(table.(pmibUDPTableOwnerPid).Table)) + length = int(table.(pmibUDPTableOwnerPid).DwNumEntries) + case kindUDP6.filename: + index = int(unsafe.Sizeof(table.(pmibUDP6TableOwnerPid).DwNumEntries)) + step = int(unsafe.Sizeof(table.(pmibUDP6TableOwnerPid).Table)) + length = int(table.(pmibUDP6TableOwnerPid).DwNumEntries) + } + + return +} + +func getTCPConnections(family uint32) ([]ConnectionStat, error) { + var ( + p uintptr + buf []byte + size uint32 + + pmibTCPTable pmibTCPTableOwnerPidAll + pmibTCP6Table pmibTCP6TableOwnerPidAll + ) + + if family == 0 { + return nil, fmt.Errorf("faimly must be required") + } + + for { + switch family { + case kindTCP4.family: + if len(buf) > 0 { + pmibTCPTable = (*mibTCPTableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibTCPTable)) + } else { + p = uintptr(unsafe.Pointer(pmibTCPTable)) + } + case kindTCP6.family: + if len(buf) > 0 { + pmibTCP6Table = (*mibTCP6TableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibTCP6Table)) + } else { + p = uintptr(unsafe.Pointer(pmibTCP6Table)) + } + } + + err := getExtendedTcpTable(p, + &size, + true, + family, + tcpTableOwnerPidAll, + 0) + if err == nil { + break + } + if err != windows.ERROR_INSUFFICIENT_BUFFER { + return nil, err + } + buf = make([]byte, size) + } + + var ( + index, step int + length int + ) + + stats := make([]ConnectionStat, 0) + switch family { + case kindTCP4.family: + index, step, length = getTableInfo(kindTCP4.filename, pmibTCPTable) + case kindTCP6.family: + index, step, length = getTableInfo(kindTCP6.filename, pmibTCP6Table) + } + + if length == 0 { + return nil, nil + } + + for i := 0; i < length; i++ { + switch family { + case kindTCP4.family: + mibs := (*mibTCPRowOwnerPid)(unsafe.Pointer(&buf[index])) + ns := mibs.convertToConnectionStat() + stats = append(stats, ns) + case kindTCP6.family: + mibs := (*mibTCP6RowOwnerPid)(unsafe.Pointer(&buf[index])) + ns := mibs.convertToConnectionStat() + stats = append(stats, ns) + } + + index += step + } + return stats, nil +} + +func getUDPConnections(family uint32) ([]ConnectionStat, error) { + var ( + p uintptr + buf []byte + size uint32 + + pmibUDPTable pmibUDPTableOwnerPid + pmibUDP6Table pmibUDP6TableOwnerPid + ) + + if family == 0 { + return nil, fmt.Errorf("faimly must be required") + } + + for { + switch family { + case kindUDP4.family: + if len(buf) > 0 { + pmibUDPTable = (*mibUDPTableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibUDPTable)) + } else { + p = uintptr(unsafe.Pointer(pmibUDPTable)) + } + case kindUDP6.family: + if len(buf) > 0 { + pmibUDP6Table = (*mibUDP6TableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibUDP6Table)) + } else { + p = uintptr(unsafe.Pointer(pmibUDP6Table)) + } + } + + err := getExtendedUdpTable( + p, + &size, + true, + family, + udpTableOwnerPid, + 0, + ) + if err == nil { + break + } + if err != windows.ERROR_INSUFFICIENT_BUFFER { + return nil, err + } + buf = make([]byte, size) + } + + var index, step, length int + + stats := make([]ConnectionStat, 0) + switch family { + case kindUDP4.family: + index, step, length = getTableInfo(kindUDP4.filename, pmibUDPTable) + case kindUDP6.family: + index, step, length = getTableInfo(kindUDP6.filename, pmibUDP6Table) + } + + if length == 0 { + return nil, nil + } + + for i := 0; i < length; i++ { + switch family { + case kindUDP4.family: + mibs := (*mibUDPRowOwnerPid)(unsafe.Pointer(&buf[index])) + ns := mibs.convertToConnectionStat() + stats = append(stats, ns) + case kindUDP6.family: + mibs := (*mibUDP6RowOwnerPid)(unsafe.Pointer(&buf[index])) + ns := mibs.convertToConnectionStat() + stats = append(stats, ns) + } + + index += step + } + return stats, nil +} + +// tcpStatuses https://msdn.microsoft.com/en-us/library/windows/desktop/bb485761(v=vs.85).aspx +var tcpStatuses = map[mibTCPState]string{ + 1: "CLOSED", + 2: "LISTEN", + 3: "SYN_SENT", + 4: "SYN_RECEIVED", + 5: "ESTABLISHED", + 6: "FIN_WAIT_1", + 7: "FIN_WAIT_2", + 8: "CLOSE_WAIT", + 9: "CLOSING", + 10: "LAST_ACK", + 11: "TIME_WAIT", + 12: "DELETE", +} + +func getExtendedTcpTable(pTcpTable uintptr, pdwSize *uint32, bOrder bool, ulAf uint32, tableClass tcpTableClass, reserved uint32) (errcode error) { + r1, _, _ := syscall.Syscall6(procGetExtendedTCPTable.Addr(), 6, pTcpTable, uintptr(unsafe.Pointer(pdwSize)), getUintptrFromBool(bOrder), uintptr(ulAf), uintptr(tableClass), uintptr(reserved)) + if r1 != 0 { + errcode = syscall.Errno(r1) + } + return +} + +func getExtendedUdpTable(pUdpTable uintptr, pdwSize *uint32, bOrder bool, ulAf uint32, tableClass udpTableClass, reserved uint32) (errcode error) { + r1, _, _ := syscall.Syscall6(procGetExtendedUDPTable.Addr(), 6, pUdpTable, uintptr(unsafe.Pointer(pdwSize)), getUintptrFromBool(bOrder), uintptr(ulAf), uintptr(tableClass), uintptr(reserved)) + if r1 != 0 { + errcode = syscall.Errno(r1) + } + return +} + +func getUintptrFromBool(b bool) uintptr { + if b { + return 1 + } + return 0 +} + +const anySize = 1 + +// type MIB_TCP_STATE int32 +type mibTCPState int32 + +type tcpTableClass int32 + +const ( + tcpTableBasicListener tcpTableClass = iota + tcpTableBasicConnections + tcpTableBasicAll + tcpTableOwnerPidListener + tcpTableOwnerPidConnections + tcpTableOwnerPidAll + tcpTableOwnerModuleListener + tcpTableOwnerModuleConnections + tcpTableOwnerModuleAll +) + +type udpTableClass int32 + +const ( + udpTableBasic udpTableClass = iota + udpTableOwnerPid + udpTableOwnerModule +) + +// TCP + +type mibTCPRowOwnerPid struct { + DwState uint32 + DwLocalAddr uint32 + DwLocalPort uint32 + DwRemoteAddr uint32 + DwRemotePort uint32 + DwOwningPid uint32 +} + +func (m *mibTCPRowOwnerPid) convertToConnectionStat() ConnectionStat { + ns := ConnectionStat{ + Family: kindTCP4.family, + Type: kindTCP4.sockType, + Laddr: Addr{ + IP: parseIPv4HexString(m.DwLocalAddr), + Port: uint32(decodePort(m.DwLocalPort)), + }, + Raddr: Addr{ + IP: parseIPv4HexString(m.DwRemoteAddr), + Port: uint32(decodePort(m.DwRemotePort)), + }, + Pid: int32(m.DwOwningPid), + Status: tcpStatuses[mibTCPState(m.DwState)], + } + + return ns +} + +type mibTCPTableOwnerPid struct { + DwNumEntries uint32 + Table [anySize]mibTCPRowOwnerPid +} + +type mibTCP6RowOwnerPid struct { + UcLocalAddr [16]byte + DwLocalScopeId uint32 + DwLocalPort uint32 + UcRemoteAddr [16]byte + DwRemoteScopeId uint32 + DwRemotePort uint32 + DwState uint32 + DwOwningPid uint32 +} + +func (m *mibTCP6RowOwnerPid) convertToConnectionStat() ConnectionStat { + ns := ConnectionStat{ + Family: kindTCP6.family, + Type: kindTCP6.sockType, + Laddr: Addr{ + IP: parseIPv6HexString(m.UcLocalAddr), + Port: uint32(decodePort(m.DwLocalPort)), + }, + Raddr: Addr{ + IP: parseIPv6HexString(m.UcRemoteAddr), + Port: uint32(decodePort(m.DwRemotePort)), + }, + Pid: int32(m.DwOwningPid), + Status: tcpStatuses[mibTCPState(m.DwState)], + } + + return ns +} + +type mibTCP6TableOwnerPid struct { + DwNumEntries uint32 + Table [anySize]mibTCP6RowOwnerPid +} + +type ( + pmibTCPTableOwnerPidAll *mibTCPTableOwnerPid + pmibTCP6TableOwnerPidAll *mibTCP6TableOwnerPid +) + +// UDP + +type mibUDPRowOwnerPid struct { + DwLocalAddr uint32 + DwLocalPort uint32 + DwOwningPid uint32 +} + +func (m *mibUDPRowOwnerPid) convertToConnectionStat() ConnectionStat { + ns := ConnectionStat{ + Family: kindUDP4.family, + Type: kindUDP4.sockType, + Laddr: Addr{ + IP: parseIPv4HexString(m.DwLocalAddr), + Port: uint32(decodePort(m.DwLocalPort)), + }, + Pid: int32(m.DwOwningPid), + } + + return ns +} + +type mibUDPTableOwnerPid struct { + DwNumEntries uint32 + Table [anySize]mibUDPRowOwnerPid +} + +type mibUDP6RowOwnerPid struct { + UcLocalAddr [16]byte + DwLocalScopeId uint32 + DwLocalPort uint32 + DwOwningPid uint32 +} + +func (m *mibUDP6RowOwnerPid) convertToConnectionStat() ConnectionStat { + ns := ConnectionStat{ + Family: kindUDP6.family, + Type: kindUDP6.sockType, + Laddr: Addr{ + IP: parseIPv6HexString(m.UcLocalAddr), + Port: uint32(decodePort(m.DwLocalPort)), + }, + Pid: int32(m.DwOwningPid), + } + + return ns +} + +type mibUDP6TableOwnerPid struct { + DwNumEntries uint32 + Table [anySize]mibUDP6RowOwnerPid +} + +type ( + pmibUDPTableOwnerPid *mibUDPTableOwnerPid + pmibUDP6TableOwnerPid *mibUDP6TableOwnerPid +) + +func decodePort(port uint32) uint16 { + return syscall.Ntohs(uint16(port)) +} + +func parseIPv4HexString(addr uint32) string { + return fmt.Sprintf("%d.%d.%d.%d", addr&255, addr>>8&255, addr>>16&255, addr>>24&255) +} + +func parseIPv6HexString(addr [16]byte) string { + var ret [16]byte + for i := 0; i < 16; i++ { + ret[i] = uint8(addr[i]) + } + + // convert []byte to net.IP + ip := net.IP(ret[:]) + return ip.String() +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process.go b/vendor/github.com/shirou/gopsutil/v3/process/process.go new file mode 100644 index 000000000..1bb27abf8 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process.go @@ -0,0 +1,627 @@ +package process + +import ( + "context" + "encoding/json" + "errors" + "runtime" + "sort" + "sync" + "time" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/shirou/gopsutil/v3/mem" + "github.com/shirou/gopsutil/v3/net" +) + +var ( + invoke common.Invoker = common.Invoke{} + ErrorNoChildren = errors.New("process does not have children") + ErrorProcessNotRunning = errors.New("process does not exist") + ErrorNotPermitted = errors.New("operation not permitted") +) + +type Process struct { + Pid int32 `json:"pid"` + name string + status string + parent int32 + parentMutex sync.RWMutex // for windows ppid cache + numCtxSwitches *NumCtxSwitchesStat + uids []int32 + gids []int32 + groups []int32 + numThreads int32 + memInfo *MemoryInfoStat + sigInfo *SignalInfoStat + createTime int64 + + lastCPUTimes *cpu.TimesStat + lastCPUTime time.Time + + tgid int32 +} + +// Process status +const ( + // Running marks a task a running or runnable (on the run queue) + Running = "running" + // Blocked marks a task waiting on a short, uninterruptible operation (usually I/O) + Blocked = "blocked" + // Idle marks a task sleeping for more than about 20 seconds + Idle = "idle" + // Lock marks a task waiting to acquire a lock + Lock = "lock" + // Sleep marks task waiting for short, interruptible operation + Sleep = "sleep" + // Stop marks a stopped process + Stop = "stop" + // Wait marks an idle interrupt thread (or paging in pre 2.6.xx Linux) + Wait = "wait" + // Zombie marks a defunct process, terminated but not reaped by its parent + Zombie = "zombie" + + // Solaris states. See https://github.com/collectd/collectd/blob/1da3305c10c8ff9a63081284cf3d4bb0f6daffd8/src/processes.c#L2115 + Daemon = "daemon" + Detached = "detached" + System = "system" + Orphan = "orphan" + + UnknownState = "" +) + +type OpenFilesStat struct { + Path string `json:"path"` + Fd uint64 `json:"fd"` +} + +type MemoryInfoStat struct { + RSS uint64 `json:"rss"` // bytes + VMS uint64 `json:"vms"` // bytes + HWM uint64 `json:"hwm"` // bytes + Data uint64 `json:"data"` // bytes + Stack uint64 `json:"stack"` // bytes + Locked uint64 `json:"locked"` // bytes + Swap uint64 `json:"swap"` // bytes +} + +type SignalInfoStat struct { + PendingProcess uint64 `json:"pending_process"` + PendingThread uint64 `json:"pending_thread"` + Blocked uint64 `json:"blocked"` + Ignored uint64 `json:"ignored"` + Caught uint64 `json:"caught"` +} + +type RlimitStat struct { + Resource int32 `json:"resource"` + Soft uint64 `json:"soft"` + Hard uint64 `json:"hard"` + Used uint64 `json:"used"` +} + +type IOCountersStat struct { + ReadCount uint64 `json:"readCount"` + WriteCount uint64 `json:"writeCount"` + ReadBytes uint64 `json:"readBytes"` + WriteBytes uint64 `json:"writeBytes"` +} + +type NumCtxSwitchesStat struct { + Voluntary int64 `json:"voluntary"` + Involuntary int64 `json:"involuntary"` +} + +type PageFaultsStat struct { + MinorFaults uint64 `json:"minorFaults"` + MajorFaults uint64 `json:"majorFaults"` + ChildMinorFaults uint64 `json:"childMinorFaults"` + ChildMajorFaults uint64 `json:"childMajorFaults"` +} + +// Resource limit constants are from /usr/include/x86_64-linux-gnu/bits/resource.h +// from libc6-dev package in Ubuntu 16.10 +const ( + RLIMIT_CPU int32 = 0 + RLIMIT_FSIZE int32 = 1 + RLIMIT_DATA int32 = 2 + RLIMIT_STACK int32 = 3 + RLIMIT_CORE int32 = 4 + RLIMIT_RSS int32 = 5 + RLIMIT_NPROC int32 = 6 + RLIMIT_NOFILE int32 = 7 + RLIMIT_MEMLOCK int32 = 8 + RLIMIT_AS int32 = 9 + RLIMIT_LOCKS int32 = 10 + RLIMIT_SIGPENDING int32 = 11 + RLIMIT_MSGQUEUE int32 = 12 + RLIMIT_NICE int32 = 13 + RLIMIT_RTPRIO int32 = 14 + RLIMIT_RTTIME int32 = 15 +) + +func (p Process) String() string { + s, _ := json.Marshal(p) + return string(s) +} + +func (o OpenFilesStat) String() string { + s, _ := json.Marshal(o) + return string(s) +} + +func (m MemoryInfoStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +func (r RlimitStat) String() string { + s, _ := json.Marshal(r) + return string(s) +} + +func (i IOCountersStat) String() string { + s, _ := json.Marshal(i) + return string(s) +} + +func (p NumCtxSwitchesStat) String() string { + s, _ := json.Marshal(p) + return string(s) +} + +var enableBootTimeCache bool + +// EnableBootTimeCache change cache behavior of BootTime. If true, cache BootTime value. Default is false. +func EnableBootTimeCache(enable bool) { + enableBootTimeCache = enable +} + +// Pids returns a slice of process ID list which are running now. +func Pids() ([]int32, error) { + return PidsWithContext(context.Background()) +} + +func PidsWithContext(ctx context.Context) ([]int32, error) { + pids, err := pidsWithContext(ctx) + sort.Slice(pids, func(i, j int) bool { return pids[i] < pids[j] }) + return pids, err +} + +// Processes returns a slice of pointers to Process structs for all +// currently running processes. +func Processes() ([]*Process, error) { + return ProcessesWithContext(context.Background()) +} + +// NewProcess creates a new Process instance, it only stores the pid and +// checks that the process exists. Other method on Process can be used +// to get more information about the process. An error will be returned +// if the process does not exist. +func NewProcess(pid int32) (*Process, error) { + return NewProcessWithContext(context.Background(), pid) +} + +func NewProcessWithContext(ctx context.Context, pid int32) (*Process, error) { + p := &Process{ + Pid: pid, + } + + exists, err := PidExistsWithContext(ctx, pid) + if err != nil { + return p, err + } + if !exists { + return p, ErrorProcessNotRunning + } + p.CreateTimeWithContext(ctx) + return p, nil +} + +func PidExists(pid int32) (bool, error) { + return PidExistsWithContext(context.Background(), pid) +} + +// Background returns true if the process is in background, false otherwise. +func (p *Process) Background() (bool, error) { + return p.BackgroundWithContext(context.Background()) +} + +func (p *Process) BackgroundWithContext(ctx context.Context) (bool, error) { + fg, err := p.ForegroundWithContext(ctx) + if err != nil { + return false, err + } + return !fg, err +} + +// If interval is 0, return difference from last call(non-blocking). +// If interval > 0, wait interval sec and return difference between start and end. +func (p *Process) Percent(interval time.Duration) (float64, error) { + return p.PercentWithContext(context.Background(), interval) +} + +func (p *Process) PercentWithContext(ctx context.Context, interval time.Duration) (float64, error) { + cpuTimes, err := p.TimesWithContext(ctx) + if err != nil { + return 0, err + } + now := time.Now() + + if interval > 0 { + p.lastCPUTimes = cpuTimes + p.lastCPUTime = now + if err := common.Sleep(ctx, interval); err != nil { + return 0, err + } + cpuTimes, err = p.TimesWithContext(ctx) + now = time.Now() + if err != nil { + return 0, err + } + } else { + if p.lastCPUTimes == nil { + // invoked first time + p.lastCPUTimes = cpuTimes + p.lastCPUTime = now + return 0, nil + } + } + + numcpu := runtime.NumCPU() + delta := (now.Sub(p.lastCPUTime).Seconds()) * float64(numcpu) + ret := calculatePercent(p.lastCPUTimes, cpuTimes, delta, numcpu) + p.lastCPUTimes = cpuTimes + p.lastCPUTime = now + return ret, nil +} + +// IsRunning returns whether the process is still running or not. +func (p *Process) IsRunning() (bool, error) { + return p.IsRunningWithContext(context.Background()) +} + +func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) { + createTime, err := p.CreateTimeWithContext(ctx) + if err != nil { + return false, err + } + p2, err := NewProcessWithContext(ctx, p.Pid) + if errors.Is(err, ErrorProcessNotRunning) { + return false, nil + } + createTime2, err := p2.CreateTimeWithContext(ctx) + if err != nil { + return false, err + } + return createTime == createTime2, nil +} + +// CreateTime returns created time of the process in milliseconds since the epoch, in UTC. +func (p *Process) CreateTime() (int64, error) { + return p.CreateTimeWithContext(context.Background()) +} + +func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) { + if p.createTime != 0 { + return p.createTime, nil + } + createTime, err := p.createTimeWithContext(ctx) + p.createTime = createTime + return p.createTime, err +} + +func calculatePercent(t1, t2 *cpu.TimesStat, delta float64, numcpu int) float64 { + if delta == 0 { + return 0 + } + delta_proc := t2.Total() - t1.Total() + overall_percent := ((delta_proc / delta) * 100) * float64(numcpu) + return overall_percent +} + +// MemoryPercent returns how many percent of the total RAM this process uses +func (p *Process) MemoryPercent() (float32, error) { + return p.MemoryPercentWithContext(context.Background()) +} + +func (p *Process) MemoryPercentWithContext(ctx context.Context) (float32, error) { + machineMemory, err := mem.VirtualMemoryWithContext(ctx) + if err != nil { + return 0, err + } + total := machineMemory.Total + + processMemory, err := p.MemoryInfoWithContext(ctx) + if err != nil { + return 0, err + } + used := processMemory.RSS + + return (100 * float32(used) / float32(total)), nil +} + +// CPUPercent returns how many percent of the CPU time this process uses +func (p *Process) CPUPercent() (float64, error) { + return p.CPUPercentWithContext(context.Background()) +} + +func (p *Process) CPUPercentWithContext(ctx context.Context) (float64, error) { + crt_time, err := p.createTimeWithContext(ctx) + if err != nil { + return 0, err + } + + cput, err := p.TimesWithContext(ctx) + if err != nil { + return 0, err + } + + created := time.Unix(0, crt_time*int64(time.Millisecond)) + totalTime := time.Since(created).Seconds() + if totalTime <= 0 { + return 0, nil + } + + return 100 * cput.Total() / totalTime, nil +} + +// Groups returns all group IDs(include supplementary groups) of the process as a slice of the int +func (p *Process) Groups() ([]int32, error) { + return p.GroupsWithContext(context.Background()) +} + +// Ppid returns Parent Process ID of the process. +func (p *Process) Ppid() (int32, error) { + return p.PpidWithContext(context.Background()) +} + +// Name returns name of the process. +func (p *Process) Name() (string, error) { + return p.NameWithContext(context.Background()) +} + +// Exe returns executable path of the process. +func (p *Process) Exe() (string, error) { + return p.ExeWithContext(context.Background()) +} + +// Cmdline returns the command line arguments of the process as a string with +// each argument separated by 0x20 ascii character. +func (p *Process) Cmdline() (string, error) { + return p.CmdlineWithContext(context.Background()) +} + +// CmdlineSlice returns the command line arguments of the process as a slice with each +// element being an argument. +func (p *Process) CmdlineSlice() ([]string, error) { + return p.CmdlineSliceWithContext(context.Background()) +} + +// Cwd returns current working directory of the process. +func (p *Process) Cwd() (string, error) { + return p.CwdWithContext(context.Background()) +} + +// Parent returns parent Process of the process. +func (p *Process) Parent() (*Process, error) { + return p.ParentWithContext(context.Background()) +} + +// ParentWithContext returns parent Process of the process. +func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) { + ppid, err := p.PpidWithContext(ctx) + if err != nil { + return nil, err + } + return NewProcessWithContext(ctx, ppid) +} + +// Status returns the process status. +// Return value could be one of these. +// R: Running S: Sleep T: Stop I: Idle +// Z: Zombie W: Wait L: Lock +// The character is same within all supported platforms. +func (p *Process) Status() ([]string, error) { + return p.StatusWithContext(context.Background()) +} + +// Foreground returns true if the process is in foreground, false otherwise. +func (p *Process) Foreground() (bool, error) { + return p.ForegroundWithContext(context.Background()) +} + +// Uids returns user ids of the process as a slice of the int +func (p *Process) Uids() ([]int32, error) { + return p.UidsWithContext(context.Background()) +} + +// Gids returns group ids of the process as a slice of the int +func (p *Process) Gids() ([]int32, error) { + return p.GidsWithContext(context.Background()) +} + +// Terminal returns a terminal which is associated with the process. +func (p *Process) Terminal() (string, error) { + return p.TerminalWithContext(context.Background()) +} + +// Nice returns a nice value (priority). +func (p *Process) Nice() (int32, error) { + return p.NiceWithContext(context.Background()) +} + +// IOnice returns process I/O nice value (priority). +func (p *Process) IOnice() (int32, error) { + return p.IOniceWithContext(context.Background()) +} + +// Rlimit returns Resource Limits. +func (p *Process) Rlimit() ([]RlimitStat, error) { + return p.RlimitWithContext(context.Background()) +} + +// RlimitUsage returns Resource Limits. +// If gatherUsed is true, the currently used value will be gathered and added +// to the resulting RlimitStat. +func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) { + return p.RlimitUsageWithContext(context.Background(), gatherUsed) +} + +// IOCounters returns IO Counters. +func (p *Process) IOCounters() (*IOCountersStat, error) { + return p.IOCountersWithContext(context.Background()) +} + +// NumCtxSwitches returns the number of the context switches of the process. +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return p.NumCtxSwitchesWithContext(context.Background()) +} + +// NumFDs returns the number of File Descriptors used by the process. +func (p *Process) NumFDs() (int32, error) { + return p.NumFDsWithContext(context.Background()) +} + +// NumThreads returns the number of threads used by the process. +func (p *Process) NumThreads() (int32, error) { + return p.NumThreadsWithContext(context.Background()) +} + +func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) { + return p.ThreadsWithContext(context.Background()) +} + +// Times returns CPU times of the process. +func (p *Process) Times() (*cpu.TimesStat, error) { + return p.TimesWithContext(context.Background()) +} + +// CPUAffinity returns CPU affinity of the process. +func (p *Process) CPUAffinity() ([]int32, error) { + return p.CPUAffinityWithContext(context.Background()) +} + +// MemoryInfo returns generic process memory information, +// such as RSS and VMS. +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + return p.MemoryInfoWithContext(context.Background()) +} + +// MemoryInfoEx returns platform-specific process memory information. +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return p.MemoryInfoExWithContext(context.Background()) +} + +// PageFaults returns the process's page fault counters. +func (p *Process) PageFaults() (*PageFaultsStat, error) { + return p.PageFaultsWithContext(context.Background()) +} + +// Children returns the children of the process represented as a slice +// of pointers to Process type. +func (p *Process) Children() ([]*Process, error) { + return p.ChildrenWithContext(context.Background()) +} + +// OpenFiles returns a slice of OpenFilesStat opend by the process. +// OpenFilesStat includes a file path and file descriptor. +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return p.OpenFilesWithContext(context.Background()) +} + +// Connections returns a slice of net.ConnectionStat used by the process. +// This returns all kind of the connection. This means TCP, UDP or UNIX. +func (p *Process) Connections() ([]net.ConnectionStat, error) { + return p.ConnectionsWithContext(context.Background()) +} + +// ConnectionsMax returns a slice of net.ConnectionStat used by the process at most `max`. +func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) { + return p.ConnectionsMaxWithContext(context.Background(), max) +} + +// MemoryMaps get memory maps from /proc/(pid)/smaps +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + return p.MemoryMapsWithContext(context.Background(), grouped) +} + +// Tgid returns thread group id of the process. +func (p *Process) Tgid() (int32, error) { + return p.TgidWithContext(context.Background()) +} + +// SendSignal sends a unix.Signal to the process. +func (p *Process) SendSignal(sig Signal) error { + return p.SendSignalWithContext(context.Background(), sig) +} + +// Suspend sends SIGSTOP to the process. +func (p *Process) Suspend() error { + return p.SuspendWithContext(context.Background()) +} + +// Resume sends SIGCONT to the process. +func (p *Process) Resume() error { + return p.ResumeWithContext(context.Background()) +} + +// Terminate sends SIGTERM to the process. +func (p *Process) Terminate() error { + return p.TerminateWithContext(context.Background()) +} + +// Kill sends SIGKILL to the process. +func (p *Process) Kill() error { + return p.KillWithContext(context.Background()) +} + +// Username returns a username of the process. +func (p *Process) Username() (string, error) { + return p.UsernameWithContext(context.Background()) +} + +// Environ returns the environment variables of the process. +func (p *Process) Environ() ([]string, error) { + return p.EnvironWithContext(context.Background()) +} + +// convertStatusChar as reported by the ps command across different platforms. +func convertStatusChar(letter string) string { + // Sources + // Darwin: http://www.mywebuniversity.com/Man_Pages/Darwin/man_ps.html + // FreeBSD: https://www.freebsd.org/cgi/man.cgi?ps + // Linux https://man7.org/linux/man-pages/man1/ps.1.html + // OpenBSD: https://man.openbsd.org/ps.1#state + // Solaris: https://github.com/collectd/collectd/blob/1da3305c10c8ff9a63081284cf3d4bb0f6daffd8/src/processes.c#L2115 + switch letter { + case "A": + return Daemon + case "D", "U": + return Blocked + case "E": + return Detached + case "I": + return Idle + case "L": + return Lock + case "O": + return Orphan + case "R": + return Running + case "S": + return Sleep + case "T", "t": + // "t" is used by Linux to signal stopped by the debugger during tracing + return Stop + case "W": + return Wait + case "Y": + return System + case "Z": + return Zombie + default: + return UnknownState + } +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_bsd.go b/vendor/github.com/shirou/gopsutil/v3/process/process_bsd.go new file mode 100644 index 000000000..263829ffa --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_bsd.go @@ -0,0 +1,76 @@ +//go:build darwin || freebsd || openbsd +// +build darwin freebsd openbsd + +package process + +import ( + "bytes" + "context" + "encoding/binary" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/internal/common" +) + +type MemoryInfoExStat struct{} + +type MemoryMapsStat struct{} + +func (p *Process) TgidWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) { + return nil, common.ErrNotImplementedError +} + +func parseKinfoProc(buf []byte) (KinfoProc, error) { + var k KinfoProc + br := bytes.NewReader(buf) + err := common.Read(br, binary.LittleEndian, &k) + return k, err +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_darwin.go b/vendor/github.com/shirou/gopsutil/v3/process/process_darwin.go new file mode 100644 index 000000000..176661cbd --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_darwin.go @@ -0,0 +1,325 @@ +//go:build darwin +// +build darwin + +package process + +import ( + "context" + "fmt" + "path/filepath" + "strconv" + "strings" + + "github.com/tklauser/go-sysconf" + "golang.org/x/sys/unix" + + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/shirou/gopsutil/v3/net" +) + +// copied from sys/sysctl.h +const ( + CTLKern = 1 // "high kernel": proc, limits + KernProc = 14 // struct: process entries + KernProcPID = 1 // by process id + KernProcProc = 8 // only return procs + KernProcAll = 0 // everything + 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 +} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + var ret []int32 + + kprocs, err := unix.SysctlKinfoProcSlice("kern.proc.all") + if err != nil { + return ret, err + } + + for _, proc := range kprocs { + ret = append(ret, int32(proc.Proc.P_pid)) + } + + return ret, nil +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.Eproc.Ppid, nil +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + name := common.ByteToString(k.Proc.P_comm[:]) + + if len(name) >= 15 { + cmdName, err := p.cmdNameWithContext(ctx) + if err != nil { + return "", err + } + if len(cmdName) > 0 { + extendedName := filepath.Base(cmdName) + if strings.HasPrefix(extendedName, p.name) { + name = extendedName + } + } + } + + return name, nil +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.Proc.P_starttime.Sec*1000 + int64(k.Proc.P_starttime.Usec)/1000, nil +} + +func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) { + r, err := callPsWithContext(ctx, "state", p.Pid, false, false) + if err != nil { + return []string{""}, err + } + status := convertStatusChar(r[0][0][0:1]) + return []string{status}, err +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + // see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details + pid := p.Pid + out, err := invoke.CommandWithContext(ctx, "ps", "-o", "stat=", "-p", strconv.Itoa(int(pid))) + if err != nil { + return false, err + } + return strings.IndexByte(string(out), '+') != -1, nil +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + // See: http://unix.superglobalmegacorp.com/Net2/newsrc/sys/ucred.h.html + userEffectiveUID := int32(k.Eproc.Ucred.Uid) + + return []int32{userEffectiveUID}, nil +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + gids := make([]int32, 0, 3) + gids = append(gids, int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Pcred.P_svgid)) + + return gids, nil +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError + // k, err := p.getKProc() + // if err != nil { + // return nil, err + // } + + // groups := make([]int32, k.Eproc.Ucred.Ngroups) + // for i := int16(0); i < k.Eproc.Ucred.Ngroups; i++ { + // groups[i] = int32(k.Eproc.Ucred.Groups[i]) + // } + + // return groups, nil +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError + /* + k, err := p.getKProc() + if err != nil { + return "", err + } + + ttyNr := uint64(k.Eproc.Tdev) + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + + return termmap[ttyNr], nil + */ +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + return int32(k.Proc.P_nice), nil +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + 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.Atoi(_t[0]) + if err != nil { + return ret, err + } + t += hour * 60 * 60 * clockTicks + + mins, err := strconv.Atoi(_t[1]) + if err != nil { + return ret, err + } + t += mins * 60 * clockTicks + _tmp = _t[2] + case 2: + mins, err := strconv.Atoi(_t[0]) + if err != nil { + return ret, err + } + t += 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.Atoi(_t[0]) + t += h * clockTicks + h, err = strconv.Atoi(_t[1]) + t += h + return float64(t) / float64(clockTicks), nil +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + if err != nil { + return nil, err + } + ret := make([]*Process, 0, len(pids)) + for _, pid := range pids { + np, err := NewProcessWithContext(ctx, pid) + if err != nil { + return nil, err + } + ret = append(ret, np) + } + return ret, nil +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return net.ConnectionsPidWithContext(ctx, "all", p.Pid) +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return net.ConnectionsPidMaxWithContext(ctx, "all", p.Pid, max) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + out := []*Process{} + + pids, err := PidsWithContext(ctx) + if err != nil { + return out, err + } + + for _, pid := range pids { + p, err := NewProcessWithContext(ctx, pid) + if err != nil { + continue + } + out = append(out, p) + } + + return out, nil +} + +// Returns a proc as defined here: +// http://unix.superglobalmegacorp.com/Net2/newsrc/sys/kinfo_proc.h.html +func (p *Process) getKProc() (*unix.KinfoProc, error) { + return unix.SysctlKinfoProc("kern.proc.pid", int(p.Pid)) +} + +// call ps command. +// Return value deletes Header line(you must not input wrong arg). +// And splited by Space. Caller have responsibility to manage. +// If passed arg pid is 0, get information from all process. +func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool, nameOption bool) ([][]string, error) { + var cmd []string + if pid == 0 { // will get from all processes. + cmd = []string{"-ax", "-o", arg} + } else if threadOption { + cmd = []string{"-x", "-o", arg, "-M", "-p", strconv.Itoa(int(pid))} + } else { + cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))} + } + if nameOption { + cmd = append(cmd, "-c") + } + out, err := invoke.CommandWithContext(ctx, "ps", cmd...) + if err != nil { + return [][]string{}, err + } + lines := strings.Split(string(out), "\n") + + var ret [][]string + for _, l := range lines[1:] { + var lr []string + if nameOption { + lr = append(lr, l) + } else { + for _, r := range strings.Split(l, " ") { + if r == "" { + continue + } + lr = append(lr, strings.TrimSpace(r)) + } + } + if len(lr) != 0 { + ret = append(ret, lr) + } + } + + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_amd64.go b/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_amd64.go new file mode 100644 index 000000000..b353e5eac --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_amd64.go @@ -0,0 +1,236 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_darwin.go + +package process + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int32 + Pad_cgo_0 [4]byte +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type UGid_t uint32 + +type KinfoProc struct { + Proc ExternProc + Eproc Eproc +} + +type Eproc struct { + Paddr *uint64 + Sess *Session + Pcred Upcred + Ucred Uucred + Pad_cgo_0 [4]byte + Vm Vmspace + Ppid int32 + Pgid int32 + Jobc int16 + Pad_cgo_1 [2]byte + Tdev int32 + Tpgid int32 + Pad_cgo_2 [4]byte + Tsess *Session + Wmesg [8]int8 + Xsize int32 + Xrssize int16 + Xccount int16 + Xswrss int16 + Pad_cgo_3 [2]byte + Flag int32 + Login [12]int8 + Spare [4]int32 + Pad_cgo_4 [4]byte +} + +type Proc struct{} + +type Session struct{} + +type ucred struct { + Link _Ctype_struct___0 + Ref uint64 + Posix Posix_cred + Label *Label + Audit Au_session +} + +type Uucred struct { + Ref int32 + UID uint32 + Ngroups int16 + Pad_cgo_0 [2]byte + Groups [16]uint32 +} + +type Upcred struct { + Pc_lock [72]int8 + Pc_ucred *ucred + P_ruid uint32 + P_svuid uint32 + P_rgid uint32 + P_svgid uint32 + P_refcnt int32 + Pad_cgo_0 [4]byte +} + +type Vmspace struct { + Dummy int32 + Pad_cgo_0 [4]byte + Dummy2 *int8 + Dummy3 [5]int32 + Pad_cgo_1 [4]byte + Dummy4 [3]*int8 +} + +type Sigacts struct{} + +type ExternProc struct { + P_un [16]byte + P_vmspace uint64 + P_sigacts uint64 + Pad_cgo_0 [3]byte + P_flag int32 + P_stat int8 + P_pid int32 + P_oppid int32 + P_dupfd int32 + Pad_cgo_1 [4]byte + User_stack uint64 + Exit_thread uint64 + P_debugger int32 + Sigwait int32 + P_estcpu uint32 + P_cpticks int32 + P_pctcpu uint32 + Pad_cgo_2 [4]byte + P_wchan uint64 + P_wmesg uint64 + P_swtime uint32 + P_slptime uint32 + P_realtimer Itimerval + P_rtime Timeval + P_uticks uint64 + P_sticks uint64 + P_iticks uint64 + P_traceflag int32 + Pad_cgo_3 [4]byte + P_tracep uint64 + P_siglist int32 + Pad_cgo_4 [4]byte + P_textvp uint64 + P_holdcnt int32 + P_sigmask uint32 + P_sigignore uint32 + P_sigcatch uint32 + P_priority uint8 + P_usrpri uint8 + P_nice int8 + P_comm [17]int8 + Pad_cgo_5 [4]byte + P_pgrp uint64 + P_addr uint64 + P_xstat uint16 + P_acflag uint16 + Pad_cgo_6 [4]byte + P_ru uint64 +} + +type Itimerval struct { + Interval Timeval + Value Timeval +} + +type Vnode struct{} + +type Pgrp struct{} + +type UserStruct struct{} + +type Au_session struct { + Aia_p *AuditinfoAddr + Mask AuMask +} + +type Posix_cred struct { + UID uint32 + Ruid uint32 + Svuid uint32 + Ngroups int16 + Pad_cgo_0 [2]byte + Groups [16]uint32 + Rgid uint32 + Svgid uint32 + Gmuid uint32 + Flags int32 +} + +type Label struct{} + +type AuditinfoAddr struct { + Auid uint32 + Mask AuMask + Termid AuTidAddr + Asid int32 + Flags uint64 +} + +type AuMask struct { + Success uint32 + Failure uint32 +} + +type AuTidAddr struct { + Port int32 + Type uint32 + Addr [4]uint32 +} + +type UcredQueue struct { + Next *ucred + Prev **ucred +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_arm64.go b/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_arm64.go new file mode 100644 index 000000000..cbd6bdc79 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_arm64.go @@ -0,0 +1,213 @@ +//go:build darwin && arm64 +// +build darwin,arm64 + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs process/types_darwin.go + +package process + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int32 + Pad_cgo_0 [4]byte +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type UGid_t uint32 + +type KinfoProc struct { + Proc ExternProc + Eproc Eproc +} + +type Eproc struct { + Paddr *Proc + Sess *Session + Pcred Upcred + Ucred Uucred + Vm Vmspace + Ppid int32 + Pgid int32 + Jobc int16 + Tdev int32 + Tpgid int32 + Tsess *Session + Wmesg [8]int8 + Xsize int32 + Xrssize int16 + Xccount int16 + Xswrss int16 + Flag int32 + Login [12]int8 + Spare [4]int32 + Pad_cgo_0 [4]byte +} + +type Proc struct{} + +type Session struct{} + +type ucred struct{} + +type Uucred struct { + Ref int32 + UID uint32 + Ngroups int16 + Groups [16]uint32 +} + +type Upcred struct { + Pc_lock [72]int8 + Pc_ucred *ucred + P_ruid uint32 + P_svuid uint32 + P_rgid uint32 + P_svgid uint32 + P_refcnt int32 + Pad_cgo_0 [4]byte +} + +type Vmspace struct { + Dummy int32 + Dummy2 *int8 + Dummy3 [5]int32 + Dummy4 [3]*int8 +} + +type Sigacts struct{} + +type ExternProc struct { + P_un [16]byte + P_vmspace uint64 + P_sigacts uint64 + Pad_cgo_0 [3]byte + P_flag int32 + P_stat int8 + P_pid int32 + P_oppid int32 + P_dupfd int32 + Pad_cgo_1 [4]byte + User_stack uint64 + Exit_thread uint64 + P_debugger int32 + Sigwait int32 + P_estcpu uint32 + P_cpticks int32 + P_pctcpu uint32 + Pad_cgo_2 [4]byte + P_wchan uint64 + P_wmesg uint64 + P_swtime uint32 + P_slptime uint32 + P_realtimer Itimerval + P_rtime Timeval + P_uticks uint64 + P_sticks uint64 + P_iticks uint64 + P_traceflag int32 + Pad_cgo_3 [4]byte + P_tracep uint64 + P_siglist int32 + Pad_cgo_4 [4]byte + P_textvp uint64 + P_holdcnt int32 + P_sigmask uint32 + P_sigignore uint32 + P_sigcatch uint32 + P_priority uint8 + P_usrpri uint8 + P_nice int8 + P_comm [17]int8 + Pad_cgo_5 [4]byte + P_pgrp uint64 + P_addr uint64 + P_xstat uint16 + P_acflag uint16 + Pad_cgo_6 [4]byte + P_ru uint64 +} + +type Itimerval struct { + Interval Timeval + Value Timeval +} + +type Vnode struct{} + +type Pgrp struct{} + +type UserStruct struct{} + +type Au_session struct { + Aia_p *AuditinfoAddr + Mask AuMask +} + +type Posix_cred struct{} + +type Label struct{} + +type AuditinfoAddr struct { + Auid uint32 + Mask AuMask + Termid AuTidAddr + Asid int32 + Flags uint64 +} +type AuMask struct { + Success uint32 + Failure uint32 +} +type AuTidAddr struct { + Port int32 + Type uint32 + Addr [4]uint32 +} + +type UcredQueue struct { + Next *ucred + Prev **ucred +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_cgo.go b/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_cgo.go new file mode 100644 index 000000000..858f08e7a --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_cgo.go @@ -0,0 +1,222 @@ +//go:build darwin && cgo +// +build darwin,cgo + +package process + +// #include +// #include +// #include +// #include +// #include +// #include +// #include +import "C" + +import ( + "bytes" + "context" + "fmt" + "strings" + "syscall" + "unsafe" + + "github.com/shirou/gopsutil/v3/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 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_nocgo.go b/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_nocgo.go new file mode 100644 index 000000000..bc1d357df --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_darwin_nocgo.go @@ -0,0 +1,127 @@ +//go:build darwin && !cgo +// +build darwin,!cgo + +package process + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/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: %s", err) + } + txtFound := 0 + lines := strings.Split(string(out), "\n") + for i := 1; i < len(lines); i++ { + if lines[i] == "ftxt" { + txtFound++ + if txtFound == 2 { + return lines[i-1][1:], 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.Atoi(r[0][0]) + if err != nil { + return nil, err + } + vms, err := strconv.Atoi(r[0][1]) + if err != nil { + return nil, err + } + pagein, err := strconv.Atoi(r[0][2]) + if err != nil { + return nil, err + } + + ret := &MemoryInfoStat{ + RSS: uint64(rss) * 1024, + VMS: uint64(vms) * 1024, + Swap: uint64(pagein), + } + + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_fallback.go b/vendor/github.com/shirou/gopsutil/v3/process/process_fallback.go new file mode 100644 index 000000000..1a5d0c4b4 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_fallback.go @@ -0,0 +1,203 @@ +//go:build !darwin && !linux && !freebsd && !openbsd && !windows && !solaris && !plan9 +// +build !darwin,!linux,!freebsd,!openbsd,!windows,!solaris,!plan9 + +package process + +import ( + "context" + "syscall" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/shirou/gopsutil/v3/net" +) + +type Signal = syscall.Signal + +type MemoryMapsStat struct { + Path string `json:"path"` + Rss uint64 `json:"rss"` + Size uint64 `json:"size"` + Pss uint64 `json:"pss"` + SharedClean uint64 `json:"sharedClean"` + SharedDirty uint64 `json:"sharedDirty"` + PrivateClean uint64 `json:"privateClean"` + PrivateDirty uint64 `json:"privateDirty"` + Referenced uint64 `json:"referenced"` + Anonymous uint64 `json:"anonymous"` + Swap uint64 `json:"swap"` +} + +type MemoryInfoExStat struct{} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + return nil, common.ErrNotImplementedError +} + +func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { + return false, common.ErrNotImplementedError +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) TgidWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) { + return []string{""}, common.ErrNotImplementedError +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + return false, common.ErrNotImplementedError +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) SendSignalWithContext(ctx context.Context, sig Signal) error { + return common.ErrNotImplementedError +} + +func (p *Process) SuspendWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} + +func (p *Process) ResumeWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} + +func (p *Process) TerminateWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} + +func (p *Process) KillWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} + +func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) { + return nil, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd.go b/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd.go new file mode 100644 index 000000000..40b10e14f --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd.go @@ -0,0 +1,342 @@ +//go:build freebsd +// +build freebsd + +package process + +import ( + "bytes" + "context" + "path/filepath" + "strconv" + "strings" + + cpu "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/internal/common" + net "github.com/shirou/gopsutil/v3/net" + "golang.org/x/sys/unix" +) + +func pidsWithContext(ctx context.Context) ([]int32, error) { + var ret []int32 + procs, err := ProcessesWithContext(ctx) + if err != nil { + return ret, nil + } + + for _, p := range procs { + ret = append(ret, p.Pid) + } + + return ret, nil +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.Ppid, nil +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + name := common.IntToString(k.Comm[:]) + + if len(name) >= 15 { + cmdlineSlice, err := p.CmdlineSliceWithContext(ctx) + if err != nil { + return "", err + } + if len(cmdlineSlice) > 0 { + extendedName := filepath.Base(cmdlineSlice[0]) + if strings.HasPrefix(extendedName, p.name) { + name = extendedName + } + } + } + + return name, nil +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + mib := []int32{CTLKern, KernProc, KernProcPathname, p.Pid} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return "", err + } + + return strings.Trim(string(buf), "\x00"), nil +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return "", err + } + ret := strings.FieldsFunc(string(buf), func(r rune) bool { + if r == '\u0000' { + return true + } + return false + }) + + return strings.Join(ret, " "), nil +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + if len(buf) == 0 { + return nil, nil + } + if buf[len(buf)-1] == 0 { + buf = buf[:len(buf)-1] + } + parts := bytes.Split(buf, []byte{0}) + var strParts []string + for _, p := range parts { + strParts = append(strParts, string(p)) + } + + return strParts, nil +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + return int64(k.Start.Sec)*1000 + int64(k.Start.Usec)/1000, nil +} + +func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) { + k, err := p.getKProc() + if err != nil { + return []string{""}, err + } + var s string + switch k.Stat { + case SIDL: + s = Idle + case SRUN: + s = Running + case SSLEEP: + s = Sleep + case SSTOP: + s = Stop + case SZOMB: + s = Zombie + case SWAIT: + s = Wait + case SLOCK: + s = Lock + } + + return []string{s}, nil +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + // see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details + pid := p.Pid + out, err := invoke.CommandWithContext(ctx, "ps", "-o", "stat=", "-p", strconv.Itoa(int(pid))) + if err != nil { + return false, err + } + return strings.IndexByte(string(out), '+') != -1, nil +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + uids := make([]int32, 0, 3) + + uids = append(uids, int32(k.Ruid), int32(k.Uid), int32(k.Svuid)) + + return uids, nil +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + gids := make([]int32, 0, 3) + gids = append(gids, int32(k.Rgid), int32(k.Ngroups), int32(k.Svgid)) + + return gids, nil +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + groups := make([]int32, k.Ngroups) + for i := int16(0); i < k.Ngroups; i++ { + groups[i] = int32(k.Groups[i]) + } + + return groups, nil +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + ttyNr := uint64(k.Tdev) + + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + + return termmap[ttyNr], nil +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + return int32(k.Nice), nil +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + return &IOCountersStat{ + ReadCount: uint64(k.Rusage.Inblock), + WriteCount: uint64(k.Rusage.Oublock), + }, nil +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.Numthreads, nil +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + return &cpu.TimesStat{ + CPU: "cpu", + User: float64(k.Rusage.Utime.Sec) + float64(k.Rusage.Utime.Usec)/1000000, + System: float64(k.Rusage.Stime.Sec) + float64(k.Rusage.Stime.Usec)/1000000, + }, nil +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + v, err := unix.Sysctl("vm.stats.vm.v_page_size") + if err != nil { + return nil, err + } + pageSize := common.LittleEndian.Uint16([]byte(v)) + + return &MemoryInfoStat{ + RSS: uint64(k.Rssize) * uint64(pageSize), + VMS: uint64(k.Size), + }, nil +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + if err != nil { + return nil, err + } + ret := make([]*Process, 0, len(pids)) + for _, pid := range pids { + np, err := NewProcessWithContext(ctx, pid) + if err != nil { + return nil, err + } + ret = append(ret, np) + } + return ret, nil +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return net.ConnectionsPidWithContext(ctx, "all", p.Pid) +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return net.ConnectionsPidMaxWithContext(ctx, "all", p.Pid, max) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + results := []*Process{} + + mib := []int32{CTLKern, KernProc, KernProcProc, 0} + buf, length, err := common.CallSyscall(mib) + if err != nil { + return results, err + } + + // get kinfo_proc size + count := int(length / uint64(sizeOfKinfoProc)) + + // parse buf to procs + for i := 0; i < count; i++ { + b := buf[i*sizeOfKinfoProc : (i+1)*sizeOfKinfoProc] + k, err := parseKinfoProc(b) + if err != nil { + continue + } + p, err := NewProcessWithContext(ctx, int32(k.Pid)) + if err != nil { + continue + } + + results = append(results, p) + } + + return results, nil +} + +func (p *Process) getKProc() (*KinfoProc, error) { + mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} + + buf, length, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + if length != sizeOfKinfoProc { + return nil, err + } + + k, err := parseKinfoProc(buf) + if err != nil { + return nil, err + } + return &k, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_386.go b/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_386.go new file mode 100644 index 000000000..08ab333b4 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_386.go @@ -0,0 +1,192 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 14 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 7 +) + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x488 + sizeOfKinfoProc = 0x300 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SWAIT = 6 + SLOCK = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type Timespec struct { + Sec int32 + Nsec int32 +} + +type Timeval struct { + Sec int32 + Usec int32 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int32 + Ixrss int32 + Idrss int32 + Isrss int32 + Minflt int32 + Majflt int32 + Nswap int32 + Inblock int32 + Oublock int32 + Msgsnd int32 + Msgrcv int32 + Nsignals int32 + Nvcsw int32 + Nivcsw int32 +} + +type Rlimit struct { + Cur int64 + Max int64 +} + +type KinfoProc struct { + Structsize int32 + Layout int32 + Args int32 /* pargs */ + Paddr int32 /* proc */ + Addr int32 /* user */ + Tracep int32 /* vnode */ + Textvp int32 /* vnode */ + Fd int32 /* filedesc */ + Vmspace int32 /* vmspace */ + Wchan int32 + Pid int32 + Ppid int32 + Pgid int32 + Tpgid int32 + Sid int32 + Tsid int32 + Jobc int16 + Spare_short1 int16 + Tdev uint32 + Siglist [16]byte /* sigset */ + Sigmask [16]byte /* sigset */ + Sigignore [16]byte /* sigset */ + Sigcatch [16]byte /* sigset */ + Uid uint32 + Ruid uint32 + Svuid uint32 + Rgid uint32 + Svgid uint32 + Ngroups int16 + Spare_short2 int16 + Groups [16]uint32 + Size uint32 + Rssize int32 + Swrss int32 + Tsize int32 + Dsize int32 + Ssize int32 + Xstat uint16 + Acflag uint16 + Pctcpu uint32 + Estcpu uint32 + Slptime uint32 + Swtime uint32 + Cow uint32 + Runtime uint64 + Start Timeval + Childtime Timeval + Flag int32 + Kiflag int32 + Traceflag int32 + Stat int8 + Nice int8 + Lock int8 + Rqindex int8 + Oncpu uint8 + Lastcpu uint8 + Tdname [17]int8 + Wmesg [9]int8 + Login [18]int8 + Lockname [9]int8 + Comm [20]int8 + Emul [17]int8 + Loginclass [18]int8 + Sparestrings [50]int8 + Spareints [7]int32 + Flag2 int32 + Fibnum int32 + Cr_flags uint32 + Jid int32 + Numthreads int32 + Tid int32 + Pri Priority + Rusage Rusage + Rusage_ch Rusage + Pcb int32 /* pcb */ + Kstack int32 + Udata int32 + Tdaddr int32 /* thread */ + Spareptrs [6]int32 + Sparelongs [12]int32 + Sflag int32 + Tdflags int32 +} + +type Priority struct { + Class uint8 + Level uint8 + Native uint8 + User uint8 +} + +type KinfoVmentry struct { + Structsize int32 + Type int32 + Start uint64 + End uint64 + Offset uint64 + Vn_fileid uint64 + Vn_fsid uint32 + Flags int32 + Resident int32 + Private_resident int32 + Protection int32 + Ref_count int32 + Shadow_count int32 + Vn_type int32 + Vn_size uint64 + Vn_rdev uint32 + Vn_mode uint16 + Status uint16 + X_kve_ispare [12]int32 + Path [1024]int8 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_amd64.go b/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_amd64.go new file mode 100644 index 000000000..560e627d2 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_amd64.go @@ -0,0 +1,192 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 14 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 7 +) + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x488 + sizeOfKinfoProc = 0x440 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SWAIT = 6 + SLOCK = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur int64 + Max int64 +} + +type KinfoProc struct { + Structsize int32 + Layout int32 + Args int64 /* pargs */ + Paddr int64 /* proc */ + Addr int64 /* user */ + Tracep int64 /* vnode */ + Textvp int64 /* vnode */ + Fd int64 /* filedesc */ + Vmspace int64 /* vmspace */ + Wchan int64 + Pid int32 + Ppid int32 + Pgid int32 + Tpgid int32 + Sid int32 + Tsid int32 + Jobc int16 + Spare_short1 int16 + Tdev uint32 + Siglist [16]byte /* sigset */ + Sigmask [16]byte /* sigset */ + Sigignore [16]byte /* sigset */ + Sigcatch [16]byte /* sigset */ + Uid uint32 + Ruid uint32 + Svuid uint32 + Rgid uint32 + Svgid uint32 + Ngroups int16 + Spare_short2 int16 + Groups [16]uint32 + Size uint64 + Rssize int64 + Swrss int64 + Tsize int64 + Dsize int64 + Ssize int64 + Xstat uint16 + Acflag uint16 + Pctcpu uint32 + Estcpu uint32 + Slptime uint32 + Swtime uint32 + Cow uint32 + Runtime uint64 + Start Timeval + Childtime Timeval + Flag int64 + Kiflag int64 + Traceflag int32 + Stat int8 + Nice int8 + Lock int8 + Rqindex int8 + Oncpu uint8 + Lastcpu uint8 + Tdname [17]int8 + Wmesg [9]int8 + Login [18]int8 + Lockname [9]int8 + Comm [20]int8 + Emul [17]int8 + Loginclass [18]int8 + Sparestrings [50]int8 + Spareints [7]int32 + Flag2 int32 + Fibnum int32 + Cr_flags uint32 + Jid int32 + Numthreads int32 + Tid int32 + Pri Priority + Rusage Rusage + Rusage_ch Rusage + Pcb int64 /* pcb */ + Kstack int64 + Udata int64 + Tdaddr int64 /* thread */ + Spareptrs [6]int64 + Sparelongs [12]int64 + Sflag int64 + Tdflags int64 +} + +type Priority struct { + Class uint8 + Level uint8 + Native uint8 + User uint8 +} + +type KinfoVmentry struct { + Structsize int32 + Type int32 + Start uint64 + End uint64 + Offset uint64 + Vn_fileid uint64 + Vn_fsid uint32 + Flags int32 + Resident int32 + Private_resident int32 + Protection int32 + Ref_count int32 + Shadow_count int32 + Vn_type int32 + Vn_size uint64 + Vn_rdev uint32 + Vn_mode uint16 + Status uint16 + X_kve_ispare [12]int32 + Path [1024]int8 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm.go b/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm.go new file mode 100644 index 000000000..81ae0b9a8 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm.go @@ -0,0 +1,192 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 14 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 7 +) + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x488 + sizeOfKinfoProc = 0x440 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SWAIT = 6 + SLOCK = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int32 + Ixrss int32 + Idrss int32 + Isrss int32 + Minflt int32 + Majflt int32 + Nswap int32 + Inblock int32 + Oublock int32 + Msgsnd int32 + Msgrcv int32 + Nsignals int32 + Nvcsw int32 + Nivcsw int32 +} + +type Rlimit struct { + Cur int32 + Max int32 +} + +type KinfoProc struct { + Structsize int32 + Layout int32 + Args int32 /* pargs */ + Paddr int32 /* proc */ + Addr int32 /* user */ + Tracep int32 /* vnode */ + Textvp int32 /* vnode */ + Fd int32 /* filedesc */ + Vmspace int32 /* vmspace */ + Wchan int32 + Pid int32 + Ppid int32 + Pgid int32 + Tpgid int32 + Sid int32 + Tsid int32 + Jobc int16 + Spare_short1 int16 + Tdev uint32 + Siglist [16]byte /* sigset */ + Sigmask [16]byte /* sigset */ + Sigignore [16]byte /* sigset */ + Sigcatch [16]byte /* sigset */ + Uid uint32 + Ruid uint32 + Svuid uint32 + Rgid uint32 + Svgid uint32 + Ngroups int16 + Spare_short2 int16 + Groups [16]uint32 + Size uint32 + Rssize int32 + Swrss int32 + Tsize int32 + Dsize int32 + Ssize int32 + Xstat uint16 + Acflag uint16 + Pctcpu uint32 + Estcpu uint32 + Slptime uint32 + Swtime uint32 + Cow uint32 + Runtime uint64 + Start Timeval + Childtime Timeval + Flag int32 + Kiflag int32 + Traceflag int32 + Stat int8 + Nice int8 + Lock int8 + Rqindex int8 + Oncpu uint8 + Lastcpu uint8 + Tdname [17]int8 + Wmesg [9]int8 + Login [18]int8 + Lockname [9]int8 + Comm [20]int8 + Emul [17]int8 + Loginclass [18]int8 + Sparestrings [50]int8 + Spareints [4]int32 + Flag2 int32 + Fibnum int32 + Cr_flags uint32 + Jid int32 + Numthreads int32 + Tid int32 + Pri Priority + Rusage Rusage + Rusage_ch Rusage + Pcb int32 /* pcb */ + Kstack int32 + Udata int32 + Tdaddr int32 /* thread */ + Spareptrs [6]int64 + Sparelongs [12]int64 + Sflag int64 + Tdflags int64 +} + +type Priority struct { + Class uint8 + Level uint8 + Native uint8 + User uint8 +} + +type KinfoVmentry struct { + Structsize int32 + Type int32 + Start uint64 + End uint64 + Offset uint64 + Vn_fileid uint64 + Vn_fsid uint32 + Flags int32 + Resident int32 + Private_resident int32 + Protection int32 + Ref_count int32 + Shadow_count int32 + Vn_type int32 + Vn_size uint64 + Vn_rdev uint32 + Vn_mode uint16 + Status uint16 + X_kve_ispare [12]int32 + Path [1024]int8 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm64.go b/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm64.go new file mode 100644 index 000000000..effd470a0 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm64.go @@ -0,0 +1,202 @@ +//go:build freebsd && arm64 +// +build freebsd,arm64 + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs process/types_freebsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 14 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 7 +) + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x488 + sizeOfKinfoProc = 0x440 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SWAIT = 6 + SLOCK = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur int64 + Max int64 +} + +type KinfoProc struct { + Structsize int32 + Layout int32 + Args *int64 /* pargs */ + Paddr *int64 /* proc */ + Addr *int64 /* user */ + Tracep *int64 /* vnode */ + Textvp *int64 /* vnode */ + Fd *int64 /* filedesc */ + Vmspace *int64 /* vmspace */ + Wchan *byte + Pid int32 + Ppid int32 + Pgid int32 + Tpgid int32 + Sid int32 + Tsid int32 + Jobc int16 + Spare_short1 int16 + Tdev_freebsd11 uint32 + Siglist [16]byte /* sigset */ + Sigmask [16]byte /* sigset */ + Sigignore [16]byte /* sigset */ + Sigcatch [16]byte /* sigset */ + Uid uint32 + Ruid uint32 + Svuid uint32 + Rgid uint32 + Svgid uint32 + Ngroups int16 + Spare_short2 int16 + Groups [16]uint32 + Size uint64 + Rssize int64 + Swrss int64 + Tsize int64 + Dsize int64 + Ssize int64 + Xstat uint16 + Acflag uint16 + Pctcpu uint32 + Estcpu uint32 + Slptime uint32 + Swtime uint32 + Cow uint32 + Runtime uint64 + Start Timeval + Childtime Timeval + Flag int64 + Kiflag int64 + Traceflag int32 + Stat uint8 + Nice int8 + Lock uint8 + Rqindex uint8 + Oncpu_old uint8 + Lastcpu_old uint8 + Tdname [17]uint8 + Wmesg [9]uint8 + Login [18]uint8 + Lockname [9]uint8 + Comm [20]int8 + Emul [17]uint8 + Loginclass [18]uint8 + Moretdname [4]uint8 + Sparestrings [46]uint8 + Spareints [2]int32 + Tdev uint64 + Oncpu int32 + Lastcpu int32 + Tracer int32 + Flag2 int32 + Fibnum int32 + Cr_flags uint32 + Jid int32 + Numthreads int32 + Tid int32 + Pri Priority + Rusage Rusage + Rusage_ch Rusage + Pcb *int64 /* pcb */ + Kstack *byte + Udata *byte + Tdaddr *int64 /* thread */ + Spareptrs [6]*byte + Sparelongs [12]int64 + Sflag int64 + Tdflags int64 +} + +type Priority struct { + Class uint8 + Level uint8 + Native uint8 + User uint8 +} + +type KinfoVmentry struct { + Structsize int32 + Type int32 + Start uint64 + End uint64 + Offset uint64 + Vn_fileid uint64 + Vn_fsid_freebsd11 uint32 + Flags int32 + Resident int32 + Private_resident int32 + Protection int32 + Ref_count int32 + Shadow_count int32 + Vn_type int32 + Vn_size uint64 + Vn_rdev_freebsd11 uint32 + Vn_mode uint16 + Status uint16 + Vn_fsid uint64 + Vn_rdev uint64 + X_kve_ispare [8]int32 + Path [1024]uint8 +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_linux.go b/vendor/github.com/shirou/gopsutil/v3/process/process_linux.go new file mode 100644 index 000000000..557435b34 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_linux.go @@ -0,0 +1,1187 @@ +//go:build linux +// +build linux + +package process + +import ( + "bufio" + "bytes" + "context" + "encoding/json" + "fmt" + "math" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/tklauser/go-sysconf" + "golang.org/x/sys/unix" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/shirou/gopsutil/v3/net" +) + +var pageSize = uint64(os.Getpagesize()) + +const prioProcess = 0 // linux/resource.h + +var clockTicks = 100 // default value + +func init() { + clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK) + // ignore errors + if err == nil { + clockTicks = int(clkTck) + } +} + +// MemoryInfoExStat is different between OSes +type MemoryInfoExStat struct { + RSS uint64 `json:"rss"` // bytes + VMS uint64 `json:"vms"` // bytes + Shared uint64 `json:"shared"` // bytes + Text uint64 `json:"text"` // bytes + Lib uint64 `json:"lib"` // bytes + Data uint64 `json:"data"` // bytes + Dirty uint64 `json:"dirty"` // bytes +} + +func (m MemoryInfoExStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +type MemoryMapsStat struct { + Path string `json:"path"` + Rss uint64 `json:"rss"` + Size uint64 `json:"size"` + Pss uint64 `json:"pss"` + SharedClean uint64 `json:"sharedClean"` + SharedDirty uint64 `json:"sharedDirty"` + PrivateClean uint64 `json:"privateClean"` + PrivateDirty uint64 `json:"privateDirty"` + Referenced uint64 `json:"referenced"` + Anonymous uint64 `json:"anonymous"` + Swap uint64 `json:"swap"` +} + +// String returns JSON value of the process. +func (m MemoryMapsStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + _, ppid, _, _, _, _, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return -1, err + } + return ppid, nil +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + if p.name == "" { + if err := p.fillNameWithContext(ctx); err != nil { + return "", err + } + } + return p.name, nil +} + +func (p *Process) TgidWithContext(ctx context.Context) (int32, error) { + if p.tgid == 0 { + if err := p.fillFromStatusWithContext(ctx); err != nil { + return 0, err + } + } + return p.tgid, nil +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + return p.fillFromExeWithContext(ctx) +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + return p.fillFromCmdlineWithContext(ctx) +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + return p.fillSliceFromCmdlineWithContext(ctx) +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + _, _, _, createTime, _, _, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return 0, err + } + return createTime, nil +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return p.fillFromCwdWithContext(ctx) +} + +func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return []string{""}, err + } + return []string{p.status}, nil +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + // see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details + pid := p.Pid + statPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "stat") + contents, err := os.ReadFile(statPath) + if err != nil { + return false, err + } + fields := strings.Fields(string(contents)) + if len(fields) < 8 { + return false, fmt.Errorf("insufficient data in %s", statPath) + } + pgid := fields[4] + tpgid := fields[7] + return pgid == tpgid, nil +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return []int32{}, err + } + return p.uids, nil +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return []int32{}, err + } + return p.gids, nil +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return []int32{}, err + } + return p.groups, nil +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + t, _, _, _, _, _, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return "", err + } + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + terminal := termmap[t] + return terminal, nil +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + _, _, _, _, _, nice, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return 0, err + } + return nice, nil +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + return p.RlimitUsageWithContext(ctx, false) +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + rlimits, err := p.fillFromLimitsWithContext(ctx) + if !gatherUsed || err != nil { + return rlimits, err + } + + _, _, _, _, rtprio, nice, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return nil, err + } + if err := p.fillFromStatusWithContext(ctx); err != nil { + return nil, err + } + + for i := range rlimits { + rs := &rlimits[i] + switch rs.Resource { + case RLIMIT_CPU: + times, err := p.TimesWithContext(ctx) + if err != nil { + return nil, err + } + rs.Used = uint64(times.User + times.System) + case RLIMIT_DATA: + rs.Used = uint64(p.memInfo.Data) + case RLIMIT_STACK: + rs.Used = uint64(p.memInfo.Stack) + case RLIMIT_RSS: + rs.Used = uint64(p.memInfo.RSS) + case RLIMIT_NOFILE: + n, err := p.NumFDsWithContext(ctx) + if err != nil { + return nil, err + } + rs.Used = uint64(n) + case RLIMIT_MEMLOCK: + rs.Used = uint64(p.memInfo.Locked) + case RLIMIT_AS: + rs.Used = uint64(p.memInfo.VMS) + case RLIMIT_LOCKS: + // TODO we can get the used value from /proc/$pid/locks. But linux doesn't enforce it, so not a high priority. + case RLIMIT_SIGPENDING: + rs.Used = p.sigInfo.PendingProcess + case RLIMIT_NICE: + // The rlimit for nice is a little unusual, in that 0 means the niceness cannot be decreased beyond the current value, but it can be increased. + // So effectively: if rs.Soft == 0 { rs.Soft = rs.Used } + rs.Used = uint64(nice) + case RLIMIT_RTPRIO: + rs.Used = uint64(rtprio) + } + } + + return rlimits, err +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + return p.fillFromIOWithContext(ctx) +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return nil, err + } + return p.numCtxSwitches, nil +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + _, fnames, err := p.fillFromfdListWithContext(ctx) + return int32(len(fnames)), err +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return 0, err + } + return p.numThreads, nil +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + ret := make(map[int32]*cpu.TimesStat) + taskPath := common.HostProcWithContext(ctx, strconv.Itoa(int(p.Pid)), "task") + + tids, err := readPidsFromDir(taskPath) + if err != nil { + return nil, err + } + + for _, tid := range tids { + _, _, cpuTimes, _, _, _, _, err := p.fillFromTIDStatWithContext(ctx, tid) + if err != nil { + return nil, err + } + ret[tid] = cpuTimes + } + + return ret, nil +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + _, _, cpuTimes, _, _, _, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return nil, err + } + return cpuTimes, nil +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + meminfo, _, err := p.fillFromStatmWithContext(ctx) + if err != nil { + return nil, err + } + return meminfo, nil +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + _, memInfoEx, err := p.fillFromStatmWithContext(ctx) + if err != nil { + return nil, err + } + return memInfoEx, nil +} + +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + _, _, _, _, _, _, pageFaults, err := p.fillFromStatWithContext(ctx) + if err != nil { + return nil, err + } + return pageFaults, nil +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + 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) + if err != nil { + return nil, err + } + ret = append(ret, np) + } + return ret, nil +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + _, ofs, err := p.fillFromfdWithContext(ctx) + if err != nil { + return nil, err + } + ret := make([]OpenFilesStat, len(ofs)) + for i, o := range ofs { + ret[i] = *o + } + + return ret, nil +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return net.ConnectionsPidWithContext(ctx, "all", p.Pid) +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return net.ConnectionsPidMaxWithContext(ctx, "all", p.Pid, max) +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + pid := p.Pid + var ret []MemoryMapsStat + smapsPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "smaps") + if grouped { + ret = make([]MemoryMapsStat, 1) + // If smaps_rollup exists (require kernel >= 4.15), then we will use it + // for pre-summed memory information for a process. + smapsRollupPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "smaps_rollup") + if _, err := os.Stat(smapsRollupPath); !os.IsNotExist(err) { + smapsPath = smapsRollupPath + } + } + contents, err := os.ReadFile(smapsPath) + if err != nil { + return nil, err + } + lines := strings.Split(string(contents), "\n") + + // function of parsing a block + getBlock := func(firstLine []string, block []string) (MemoryMapsStat, error) { + m := MemoryMapsStat{} + m.Path = firstLine[len(firstLine)-1] + + for _, line := range block { + if strings.Contains(line, "VmFlags") { + continue + } + field := strings.Split(line, ":") + if len(field) < 2 { + continue + } + v := strings.Trim(field[1], "kB") // remove last "kB" + v = strings.TrimSpace(v) + t, err := strconv.ParseUint(v, 10, 64) + if err != nil { + return m, err + } + + switch field[0] { + case "Size": + m.Size = t + case "Rss": + m.Rss = t + case "Pss": + m.Pss = t + case "Shared_Clean": + m.SharedClean = t + case "Shared_Dirty": + m.SharedDirty = t + case "Private_Clean": + m.PrivateClean = t + case "Private_Dirty": + m.PrivateDirty = t + case "Referenced": + m.Referenced = t + case "Anonymous": + m.Anonymous = t + case "Swap": + m.Swap = t + } + } + return m, nil + } + + var firstLine []string + blocks := make([]string, 0, 16) + + for i, line := range lines { + fields := strings.Fields(line) + if (len(fields) > 0 && !strings.HasSuffix(fields[0], ":")) || i == len(lines)-1 { + // new block section + if len(firstLine) > 0 && len(blocks) > 0 { + g, err := getBlock(firstLine, blocks) + if err != nil { + return &ret, err + } + if grouped { + ret[0].Size += g.Size + ret[0].Rss += g.Rss + ret[0].Pss += g.Pss + ret[0].SharedClean += g.SharedClean + ret[0].SharedDirty += g.SharedDirty + ret[0].PrivateClean += g.PrivateClean + ret[0].PrivateDirty += g.PrivateDirty + ret[0].Referenced += g.Referenced + ret[0].Anonymous += g.Anonymous + ret[0].Swap += g.Swap + } else { + ret = append(ret, g) + } + } + // starts new block + blocks = make([]string, 0, 16) + firstLine = fields + } else { + blocks = append(blocks, line) + } + } + + return &ret, nil +} + +func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) { + environPath := common.HostProcWithContext(ctx, strconv.Itoa(int(p.Pid)), "environ") + + environContent, err := os.ReadFile(environPath) + if err != nil { + return nil, err + } + + return strings.Split(string(environContent), "\000"), nil +} + +/** +** Internal functions +**/ + +func limitToUint(val string) (uint64, error) { + if val == "unlimited" { + return math.MaxUint64, nil + } + res, err := strconv.ParseUint(val, 10, 64) + if err != nil { + return 0, err + } + return res, nil +} + +// Get num_fds from /proc/(pid)/limits +func (p *Process) fillFromLimitsWithContext(ctx context.Context) ([]RlimitStat, error) { + pid := p.Pid + limitsFile := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "limits") + d, err := os.Open(limitsFile) + if err != nil { + return nil, err + } + defer d.Close() + + var limitStats []RlimitStat + + limitsScanner := bufio.NewScanner(d) + for limitsScanner.Scan() { + var statItem RlimitStat + + str := strings.Fields(limitsScanner.Text()) + + // Remove the header line + if strings.Contains(str[len(str)-1], "Units") { + continue + } + + // Assert that last item is a Hard limit + statItem.Hard, err = limitToUint(str[len(str)-1]) + if err != nil { + // On error remove last item and try once again since it can be unit or header line + str = str[:len(str)-1] + statItem.Hard, err = limitToUint(str[len(str)-1]) + if err != nil { + return nil, err + } + } + // Remove last item from string + str = str[:len(str)-1] + + // Now last item is a Soft limit + statItem.Soft, err = limitToUint(str[len(str)-1]) + if err != nil { + return nil, err + } + // Remove last item from string + str = str[:len(str)-1] + + // The rest is a stats name + resourceName := strings.Join(str, " ") + switch resourceName { + case "Max cpu time": + statItem.Resource = RLIMIT_CPU + case "Max file size": + statItem.Resource = RLIMIT_FSIZE + case "Max data size": + statItem.Resource = RLIMIT_DATA + case "Max stack size": + statItem.Resource = RLIMIT_STACK + case "Max core file size": + statItem.Resource = RLIMIT_CORE + case "Max resident set": + statItem.Resource = RLIMIT_RSS + case "Max processes": + statItem.Resource = RLIMIT_NPROC + case "Max open files": + statItem.Resource = RLIMIT_NOFILE + case "Max locked memory": + statItem.Resource = RLIMIT_MEMLOCK + case "Max address space": + statItem.Resource = RLIMIT_AS + case "Max file locks": + statItem.Resource = RLIMIT_LOCKS + case "Max pending signals": + statItem.Resource = RLIMIT_SIGPENDING + case "Max msgqueue size": + statItem.Resource = RLIMIT_MSGQUEUE + case "Max nice priority": + statItem.Resource = RLIMIT_NICE + case "Max realtime priority": + statItem.Resource = RLIMIT_RTPRIO + case "Max realtime timeout": + statItem.Resource = RLIMIT_RTTIME + default: + continue + } + + limitStats = append(limitStats, statItem) + } + + if err := limitsScanner.Err(); err != nil { + return nil, err + } + + return limitStats, nil +} + +// Get list of /proc/(pid)/fd files +func (p *Process) fillFromfdListWithContext(ctx context.Context) (string, []string, error) { + pid := p.Pid + statPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "fd") + d, err := os.Open(statPath) + if err != nil { + return statPath, []string{}, err + } + defer d.Close() + fnames, err := d.Readdirnames(-1) + return statPath, fnames, err +} + +// Get num_fds from /proc/(pid)/fd +func (p *Process) fillFromfdWithContext(ctx context.Context) (int32, []*OpenFilesStat, error) { + statPath, fnames, err := p.fillFromfdListWithContext(ctx) + if err != nil { + return 0, nil, err + } + numFDs := int32(len(fnames)) + + var openfiles []*OpenFilesStat + for _, fd := range fnames { + fpath := filepath.Join(statPath, fd) + filepath, err := os.Readlink(fpath) + if err != nil { + continue + } + t, err := strconv.ParseUint(fd, 10, 64) + if err != nil { + return numFDs, openfiles, err + } + o := &OpenFilesStat{ + Path: filepath, + Fd: t, + } + openfiles = append(openfiles, o) + } + + return numFDs, openfiles, nil +} + +// Get cwd from /proc/(pid)/cwd +func (p *Process) fillFromCwdWithContext(ctx context.Context) (string, error) { + pid := p.Pid + cwdPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "cwd") + cwd, err := os.Readlink(cwdPath) + if err != nil { + return "", err + } + return string(cwd), nil +} + +// Get exe from /proc/(pid)/exe +func (p *Process) fillFromExeWithContext(ctx context.Context) (string, error) { + pid := p.Pid + exePath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "exe") + exe, err := os.Readlink(exePath) + if err != nil { + return "", err + } + return string(exe), nil +} + +// Get cmdline from /proc/(pid)/cmdline +func (p *Process) fillFromCmdlineWithContext(ctx context.Context) (string, error) { + pid := p.Pid + cmdPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "cmdline") + cmdline, err := os.ReadFile(cmdPath) + if err != nil { + return "", err + } + ret := strings.FieldsFunc(string(cmdline), func(r rune) bool { + return r == '\u0000' + }) + + return strings.Join(ret, " "), nil +} + +func (p *Process) fillSliceFromCmdlineWithContext(ctx context.Context) ([]string, error) { + pid := p.Pid + cmdPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "cmdline") + cmdline, err := os.ReadFile(cmdPath) + if err != nil { + return nil, err + } + if len(cmdline) == 0 { + return nil, nil + } + + cmdline = bytes.TrimRight(cmdline, "\x00") + + parts := bytes.Split(cmdline, []byte{0}) + var strParts []string + for _, p := range parts { + strParts = append(strParts, string(p)) + } + + return strParts, nil +} + +// Get IO status from /proc/(pid)/io +func (p *Process) fillFromIOWithContext(ctx context.Context) (*IOCountersStat, error) { + pid := p.Pid + ioPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "io") + ioline, err := os.ReadFile(ioPath) + if err != nil { + return nil, err + } + lines := strings.Split(string(ioline), "\n") + ret := &IOCountersStat{} + + for _, line := range lines { + field := strings.Fields(line) + if len(field) < 2 { + continue + } + t, err := strconv.ParseUint(field[1], 10, 64) + if err != nil { + return nil, err + } + param := strings.TrimSuffix(field[0], ":") + switch param { + case "syscr": + ret.ReadCount = t + case "syscw": + ret.WriteCount = t + case "read_bytes": + ret.ReadBytes = t + case "write_bytes": + ret.WriteBytes = t + } + } + + return ret, nil +} + +// Get memory info from /proc/(pid)/statm +func (p *Process) fillFromStatmWithContext(ctx context.Context) (*MemoryInfoStat, *MemoryInfoExStat, error) { + pid := p.Pid + memPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "statm") + contents, err := os.ReadFile(memPath) + if err != nil { + return nil, nil, err + } + fields := strings.Split(string(contents), " ") + + vms, err := strconv.ParseUint(fields[0], 10, 64) + if err != nil { + return nil, nil, err + } + rss, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + return nil, nil, err + } + memInfo := &MemoryInfoStat{ + RSS: rss * pageSize, + VMS: vms * pageSize, + } + + shared, err := strconv.ParseUint(fields[2], 10, 64) + if err != nil { + return nil, nil, err + } + text, err := strconv.ParseUint(fields[3], 10, 64) + if err != nil { + return nil, nil, err + } + lib, err := strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, nil, err + } + dirty, err := strconv.ParseUint(fields[5], 10, 64) + if err != nil { + return nil, nil, err + } + + memInfoEx := &MemoryInfoExStat{ + RSS: rss * pageSize, + VMS: vms * pageSize, + Shared: shared * pageSize, + Text: text * pageSize, + Lib: lib * pageSize, + Dirty: dirty * pageSize, + } + + return memInfo, memInfoEx, nil +} + +// Get name from /proc/(pid)/comm or /proc/(pid)/status +func (p *Process) fillNameWithContext(ctx context.Context) error { + err := p.fillFromCommWithContext(ctx) + if err == nil && p.name != "" && len(p.name) < 15 { + return nil + } + return p.fillFromStatusWithContext(ctx) +} + +// Get name from /proc/(pid)/comm +func (p *Process) fillFromCommWithContext(ctx context.Context) error { + pid := p.Pid + statPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "comm") + contents, err := os.ReadFile(statPath) + if err != nil { + return err + } + + p.name = strings.TrimSuffix(string(contents), "\n") + return nil +} + +// Get various status from /proc/(pid)/status +func (p *Process) fillFromStatus() error { + return p.fillFromStatusWithContext(context.Background()) +} + +func (p *Process) fillFromStatusWithContext(ctx context.Context) error { + pid := p.Pid + statPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "status") + contents, err := os.ReadFile(statPath) + if err != nil { + return err + } + lines := strings.Split(string(contents), "\n") + p.numCtxSwitches = &NumCtxSwitchesStat{} + p.memInfo = &MemoryInfoStat{} + p.sigInfo = &SignalInfoStat{} + for _, line := range lines { + tabParts := strings.SplitN(line, "\t", 2) + if len(tabParts) < 2 { + continue + } + value := tabParts[1] + switch strings.TrimRight(tabParts[0], ":") { + case "Name": + p.name = strings.Trim(value, " \t") + if len(p.name) >= 15 { + cmdlineSlice, err := p.CmdlineSliceWithContext(ctx) + if err != nil { + return err + } + if len(cmdlineSlice) > 0 { + extendedName := filepath.Base(cmdlineSlice[0]) + if strings.HasPrefix(extendedName, p.name) { + p.name = extendedName + } + } + } + // Ensure we have a copy and not reference into slice + p.name = string([]byte(p.name)) + case "State": + p.status = convertStatusChar(value[0:1]) + // Ensure we have a copy and not reference into slice + p.status = string([]byte(p.status)) + case "PPid", "Ppid": + pval, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + p.parent = int32(pval) + case "Tgid": + pval, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + p.tgid = int32(pval) + case "Uid": + p.uids = make([]int32, 0, 4) + for _, i := range strings.Split(value, "\t") { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.uids = append(p.uids, int32(v)) + } + case "Gid": + p.gids = make([]int32, 0, 4) + for _, i := range strings.Split(value, "\t") { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.gids = append(p.gids, int32(v)) + } + case "Groups": + groups := strings.Fields(value) + p.groups = make([]int32, 0, len(groups)) + for _, i := range groups { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.groups = append(p.groups, int32(v)) + } + case "Threads": + v, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + p.numThreads = int32(v) + case "voluntary_ctxt_switches": + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + p.numCtxSwitches.Voluntary = v + case "nonvoluntary_ctxt_switches": + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + p.numCtxSwitches.Involuntary = v + case "VmRSS": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.RSS = v * 1024 + case "VmSize": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.VMS = v * 1024 + case "VmSwap": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.Swap = v * 1024 + case "VmHWM": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.HWM = v * 1024 + case "VmData": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.Data = v * 1024 + case "VmStk": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.Stack = v * 1024 + case "VmLck": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.Locked = v * 1024 + case "SigPnd": + if len(value) > 16 { + value = value[len(value)-16:] + } + v, err := strconv.ParseUint(value, 16, 64) + if err != nil { + return err + } + p.sigInfo.PendingThread = v + case "ShdPnd": + if len(value) > 16 { + value = value[len(value)-16:] + } + v, err := strconv.ParseUint(value, 16, 64) + if err != nil { + return err + } + p.sigInfo.PendingProcess = v + case "SigBlk": + if len(value) > 16 { + value = value[len(value)-16:] + } + v, err := strconv.ParseUint(value, 16, 64) + if err != nil { + return err + } + p.sigInfo.Blocked = v + case "SigIgn": + if len(value) > 16 { + value = value[len(value)-16:] + } + v, err := strconv.ParseUint(value, 16, 64) + if err != nil { + return err + } + p.sigInfo.Ignored = v + case "SigCgt": + if len(value) > 16 { + value = value[len(value)-16:] + } + v, err := strconv.ParseUint(value, 16, 64) + if err != nil { + return err + } + p.sigInfo.Caught = v + } + + } + return nil +} + +func (p *Process) fillFromTIDStat(tid int32) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, *PageFaultsStat, error) { + return p.fillFromTIDStatWithContext(context.Background(), tid) +} + +func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, *PageFaultsStat, error) { + pid := p.Pid + var statPath string + + if tid == -1 { + statPath = common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "stat") + } else { + statPath = common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "task", strconv.Itoa(int(tid)), "stat") + } + + contents, err := os.ReadFile(statPath) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + // Indexing from one, as described in `man proc` about the file /proc/[pid]/stat + fields := splitProcStat(contents) + + terminal, err := strconv.ParseUint(fields[7], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + + ppid, err := strconv.ParseInt(fields[4], 10, 32) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + utime, err := strconv.ParseFloat(fields[14], 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + + stime, err := strconv.ParseFloat(fields[15], 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + + // There is no such thing as iotime in stat file. As an approximation, we + // will use delayacct_blkio_ticks (aggregated block I/O delays, as per Linux + // docs). Note: I am assuming at least Linux 2.6.18 + var iotime float64 + if len(fields) > 42 { + iotime, err = strconv.ParseFloat(fields[42], 64) + if err != nil { + iotime = 0 // Ancient linux version, most likely + } + } else { + iotime = 0 // e.g. SmartOS containers + } + + cpuTimes := &cpu.TimesStat{ + CPU: "cpu", + User: utime / float64(clockTicks), + System: stime / float64(clockTicks), + Iowait: iotime / float64(clockTicks), + } + + bootTime, _ := common.BootTimeWithContext(ctx, enableBootTimeCache) + t, err := strconv.ParseUint(fields[22], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + ctime := (t / uint64(clockTicks)) + uint64(bootTime) + createTime := int64(ctime * 1000) + + rtpriority, err := strconv.ParseInt(fields[18], 10, 32) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + if rtpriority < 0 { + rtpriority = rtpriority*-1 - 1 + } else { + rtpriority = 0 + } + + // p.Nice = mustParseInt32(fields[18]) + // use syscall instead of parse Stat file + snice, _ := unix.Getpriority(prioProcess, int(pid)) + nice := int32(snice) // FIXME: is this true? + + minFault, err := strconv.ParseUint(fields[10], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + cMinFault, err := strconv.ParseUint(fields[11], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + majFault, err := strconv.ParseUint(fields[12], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + cMajFault, err := strconv.ParseUint(fields[13], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + + faults := &PageFaultsStat{ + MinorFaults: minFault, + MajorFaults: majFault, + ChildMinorFaults: cMinFault, + ChildMajorFaults: cMajFault, + } + + return terminal, int32(ppid), cpuTimes, createTime, uint32(rtpriority), nice, faults, nil +} + +func (p *Process) fillFromStatWithContext(ctx context.Context) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, *PageFaultsStat, error) { + return p.fillFromTIDStatWithContext(ctx, -1) +} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + return readPidsFromDir(common.HostProcWithContext(ctx)) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + out := []*Process{} + + pids, err := PidsWithContext(ctx) + if err != nil { + return out, err + } + + for _, pid := range pids { + p, err := NewProcessWithContext(ctx, pid) + if err != nil { + continue + } + out = append(out, p) + } + + return out, nil +} + +func readPidsFromDir(path string) ([]int32, error) { + var ret []int32 + + d, err := os.Open(path) + if err != nil { + return nil, err + } + defer d.Close() + + fnames, err := d.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, fname := range fnames { + pid, err := strconv.ParseInt(fname, 10, 32) + if err != nil { + // if not numeric name, just skip + continue + } + ret = append(ret, int32(pid)) + } + + return ret, nil +} + +func splitProcStat(content []byte) []string { + nameStart := bytes.IndexByte(content, '(') + nameEnd := bytes.LastIndexByte(content, ')') + restFields := strings.Fields(string(content[nameEnd+2:])) // +2 skip ') ' + name := content[nameStart+1 : nameEnd] + pid := strings.TrimSpace(string(content[:nameStart])) + fields := make([]string, 3, len(restFields)+3) + fields[1] = string(pid) + fields[2] = string(name) + fields = append(fields, restFields...) + return fields +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd.go b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd.go new file mode 100644 index 000000000..a58c5eb11 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd.go @@ -0,0 +1,387 @@ +//go:build openbsd +// +build openbsd + +package process + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "io" + "path/filepath" + "strconv" + "strings" + "unsafe" + + cpu "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/internal/common" + mem "github.com/shirou/gopsutil/v3/mem" + net "github.com/shirou/gopsutil/v3/net" + "golang.org/x/sys/unix" +) + +func pidsWithContext(ctx context.Context) ([]int32, error) { + var ret []int32 + procs, err := ProcessesWithContext(ctx) + if err != nil { + return ret, nil + } + + for _, p := range procs { + ret = append(ret, p.Pid) + } + + return ret, nil +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.Ppid, nil +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + name := common.IntToString(k.Comm[:]) + + if len(name) >= 15 { + cmdlineSlice, err := p.CmdlineSliceWithContext(ctx) + if err != nil { + return "", err + } + if len(cmdlineSlice) > 0 { + extendedName := filepath.Base(cmdlineSlice[0]) + if strings.HasPrefix(extendedName, p.name) { + name = extendedName + } + } + } + + return name, nil +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + mib := []int32{CTLKern, KernProcArgs, p.Pid, KernProcArgv} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + + /* From man sysctl(2): + The buffer pointed to by oldp is filled with an array of char + pointers followed by the strings themselves. The last char + pointer is a NULL pointer. */ + var strParts []string + r := bytes.NewReader(buf) + baseAddr := uintptr(unsafe.Pointer(&buf[0])) + for { + argvp, err := readPtr(r) + if err != nil { + return nil, err + } + if argvp == 0 { // check for a NULL pointer + break + } + offset := argvp - baseAddr + length := uintptr(bytes.IndexByte(buf[offset:], 0)) + str := string(buf[offset : offset+length]) + strParts = append(strParts, str) + } + + return strParts, nil +} + +// readPtr reads a pointer data from a given reader. WARNING: only little +// endian architectures are supported. +func readPtr(r io.Reader) (uintptr, error) { + switch sizeofPtr { + case 4: + var p uint32 + if err := binary.Read(r, binary.LittleEndian, &p); err != nil { + return 0, err + } + return uintptr(p), nil + case 8: + var p uint64 + if err := binary.Read(r, binary.LittleEndian, &p); err != nil { + return 0, err + } + return uintptr(p), nil + default: + return 0, fmt.Errorf("unsupported pointer size") + } +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + argv, err := p.CmdlineSliceWithContext(ctx) + if err != nil { + return "", err + } + return strings.Join(argv, " "), nil +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) { + k, err := p.getKProc() + if err != nil { + return []string{""}, err + } + var s string + switch k.Stat { + case SIDL: + case SRUN: + case SONPROC: + s = Running + case SSLEEP: + s = Sleep + case SSTOP: + s = Stop + case SDEAD: + s = Zombie + } + + return []string{s}, nil +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + // see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details + pid := p.Pid + out, err := invoke.CommandWithContext(ctx, "ps", "-o", "stat=", "-p", strconv.Itoa(int(pid))) + if err != nil { + return false, err + } + return strings.IndexByte(string(out), '+') != -1, nil +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + uids := make([]int32, 0, 3) + + uids = append(uids, int32(k.Ruid), int32(k.Uid), int32(k.Svuid)) + + return uids, nil +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + gids := make([]int32, 0, 3) + gids = append(gids, int32(k.Rgid), int32(k.Ngroups), int32(k.Svgid)) + + return gids, nil +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + groups := make([]int32, k.Ngroups) + for i := int16(0); i < k.Ngroups; i++ { + groups[i] = int32(k.Groups[i]) + } + + return groups, nil +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + ttyNr := uint64(k.Tdev) + + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + + return termmap[ttyNr], nil +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + return int32(k.Nice), nil +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + return &IOCountersStat{ + ReadCount: uint64(k.Uru_inblock), + WriteCount: uint64(k.Uru_oublock), + }, nil +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + /* not supported, just return 1 */ + return 1, nil +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + return &cpu.TimesStat{ + CPU: "cpu", + User: float64(k.Uutime_sec) + float64(k.Uutime_usec)/1000000, + System: float64(k.Ustime_sec) + float64(k.Ustime_usec)/1000000, + }, nil +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + pageSize, err := mem.GetPageSizeWithContext(ctx) + if err != nil { + return nil, err + } + + return &MemoryInfoStat{ + RSS: uint64(k.Vm_rssize) * pageSize, + VMS: uint64(k.Vm_tsize) + uint64(k.Vm_dsize) + + uint64(k.Vm_ssize), + }, nil +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + if err != nil { + return nil, err + } + ret := make([]*Process, 0, len(pids)) + for _, pid := range pids { + np, err := NewProcessWithContext(ctx, pid) + if err != nil { + return nil, err + } + ret = append(ret, np) + } + return ret, nil +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + results := []*Process{} + + buf, length, err := callKernProcSyscall(KernProcAll, 0) + if err != nil { + return results, err + } + + // get kinfo_proc size + count := int(length / uint64(sizeOfKinfoProc)) + + // parse buf to procs + for i := 0; i < count; i++ { + b := buf[i*sizeOfKinfoProc : (i+1)*sizeOfKinfoProc] + k, err := parseKinfoProc(b) + if err != nil { + continue + } + p, err := NewProcessWithContext(ctx, int32(k.Pid)) + if err != nil { + continue + } + + results = append(results, p) + } + + return results, nil +} + +func (p *Process) getKProc() (*KinfoProc, error) { + buf, length, err := callKernProcSyscall(KernProcPID, p.Pid) + if err != nil { + return nil, err + } + if length != sizeOfKinfoProc { + return nil, err + } + + k, err := parseKinfoProc(buf) + if err != nil { + return nil, err + } + return &k, nil +} + +func callKernProcSyscall(op int32, arg int32) ([]byte, uint64, error) { + mib := []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, 0} + mibptr := unsafe.Pointer(&mib[0]) + miblen := uint64(len(mib)) + length := uint64(0) + _, _, err := unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return nil, length, err + } + + count := int32(length / uint64(sizeOfKinfoProc)) + mib = []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, count} + mibptr = unsafe.Pointer(&mib[0]) + miblen = uint64(len(mib)) + // get proc info itself + buf := make([]byte, length) + _, _, err = unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_386.go b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_386.go new file mode 100644 index 000000000..f4ed02491 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_386.go @@ -0,0 +1,202 @@ +//go:build openbsd && 386 +// +build openbsd,386 + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs process/types_openbsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 66 + KernProcAll = 0 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 55 + KernProcArgv = 1 + KernProcEnv = 3 +) + +const ( + ArgMax = 256 * 1024 +) + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x38 + sizeOfKinfoProc = 0x264 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SDEAD = 6 + SONPROC = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int32 +} + +type Timeval struct { + Sec int64 + Usec int32 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int32 + Ixrss int32 + Idrss int32 + Isrss int32 + Minflt int32 + Majflt int32 + Nswap int32 + Inblock int32 + Oublock int32 + Msgsnd int32 + Msgrcv int32 + Nsignals int32 + Nvcsw int32 + Nivcsw int32 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type KinfoProc struct { + Forw uint64 + Back uint64 + Paddr uint64 + Addr uint64 + Fd uint64 + Stats uint64 + Limit uint64 + Vmspace uint64 + Sigacts uint64 + Sess uint64 + Tsess uint64 + Ru uint64 + Eflag int32 + Exitsig int32 + Flag int32 + Pid int32 + Ppid int32 + Sid int32 + X_pgid int32 + Tpgid int32 + Uid uint32 + Ruid uint32 + Gid uint32 + Rgid uint32 + Groups [16]uint32 + Ngroups int16 + Jobc int16 + Tdev uint32 + Estcpu uint32 + Rtime_sec uint32 + Rtime_usec uint32 + Cpticks int32 + Pctcpu uint32 + Swtime uint32 + Slptime uint32 + Schedflags int32 + Uticks uint64 + Sticks uint64 + Iticks uint64 + Tracep uint64 + Traceflag int32 + Holdcnt int32 + Siglist int32 + Sigmask uint32 + Sigignore uint32 + Sigcatch uint32 + Stat int8 + Priority uint8 + Usrpri uint8 + Nice uint8 + Xstat uint16 + Acflag uint16 + Comm [24]int8 + Wmesg [8]int8 + Wchan uint64 + Login [32]int8 + Vm_rssize int32 + Vm_tsize int32 + Vm_dsize int32 + Vm_ssize int32 + Uvalid int64 + Ustart_sec uint64 + Ustart_usec uint32 + Uutime_sec uint32 + Uutime_usec uint32 + Ustime_sec uint32 + Ustime_usec uint32 + Uru_maxrss uint64 + Uru_ixrss uint64 + Uru_idrss uint64 + Uru_isrss uint64 + Uru_minflt uint64 + Uru_majflt uint64 + Uru_nswap uint64 + Uru_inblock uint64 + Uru_oublock uint64 + Uru_msgsnd uint64 + Uru_msgrcv uint64 + Uru_nsignals uint64 + Uru_nvcsw uint64 + Uru_nivcsw uint64 + Uctime_sec uint32 + Uctime_usec uint32 + Psflags int32 + Spare int32 + Svuid uint32 + Svgid uint32 + Emul [8]int8 + Rlim_rss_cur uint64 + Cpuid uint64 + Vm_map_size uint64 + Tid int32 + Rtableid uint32 +} + +type Priority struct{} + +type KinfoVmentry struct { + Start uint32 + End uint32 + Guard uint32 + Fspace uint32 + Fspace_augment uint32 + Offset uint64 + Wired_count int32 + Etype int32 + Protection int32 + Max_protection int32 + Advice int32 + Inheritance int32 + Flags uint8 + Pad_cgo_0 [3]byte +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_amd64.go b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_amd64.go new file mode 100644 index 000000000..8607422b5 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_amd64.go @@ -0,0 +1,200 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_openbsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 66 + KernProcAll = 0 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 55 + KernProcArgv = 1 + KernProcEnv = 3 +) + +const ( + ArgMax = 256 * 1024 +) + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x50 + sizeOfKinfoProc = 0x268 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SDEAD = 6 + SONPROC = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type KinfoProc struct { + Forw uint64 + Back uint64 + Paddr uint64 + Addr uint64 + Fd uint64 + Stats uint64 + Limit uint64 + Vmspace uint64 + Sigacts uint64 + Sess uint64 + Tsess uint64 + Ru uint64 + Eflag int32 + Exitsig int32 + Flag int32 + Pid int32 + Ppid int32 + Sid int32 + X_pgid int32 + Tpgid int32 + Uid uint32 + Ruid uint32 + Gid uint32 + Rgid uint32 + Groups [16]uint32 + Ngroups int16 + Jobc int16 + Tdev uint32 + Estcpu uint32 + Rtime_sec uint32 + Rtime_usec uint32 + Cpticks int32 + Pctcpu uint32 + Swtime uint32 + Slptime uint32 + Schedflags int32 + Uticks uint64 + Sticks uint64 + Iticks uint64 + Tracep uint64 + Traceflag int32 + Holdcnt int32 + Siglist int32 + Sigmask uint32 + Sigignore uint32 + Sigcatch uint32 + Stat int8 + Priority uint8 + Usrpri uint8 + Nice uint8 + Xstat uint16 + Acflag uint16 + Comm [24]int8 + Wmesg [8]int8 + Wchan uint64 + Login [32]int8 + Vm_rssize int32 + Vm_tsize int32 + Vm_dsize int32 + Vm_ssize int32 + Uvalid int64 + Ustart_sec uint64 + Ustart_usec uint32 + Uutime_sec uint32 + Uutime_usec uint32 + Ustime_sec uint32 + Ustime_usec uint32 + Pad_cgo_0 [4]byte + Uru_maxrss uint64 + Uru_ixrss uint64 + Uru_idrss uint64 + Uru_isrss uint64 + Uru_minflt uint64 + Uru_majflt uint64 + Uru_nswap uint64 + Uru_inblock uint64 + Uru_oublock uint64 + Uru_msgsnd uint64 + Uru_msgrcv uint64 + Uru_nsignals uint64 + Uru_nvcsw uint64 + Uru_nivcsw uint64 + Uctime_sec uint32 + Uctime_usec uint32 + Psflags int32 + Spare int32 + Svuid uint32 + Svgid uint32 + Emul [8]int8 + Rlim_rss_cur uint64 + Cpuid uint64 + Vm_map_size uint64 + Tid int32 + Rtableid uint32 +} + +type Priority struct{} + +type KinfoVmentry struct { + Start uint64 + End uint64 + Guard uint64 + Fspace uint64 + Fspace_augment uint64 + Offset uint64 + Wired_count int32 + Etype int32 + Protection int32 + Max_protection int32 + Advice int32 + Inheritance int32 + Flags uint8 + Pad_cgo_0 [7]byte +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm.go b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm.go new file mode 100644 index 000000000..b94429f2e --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm.go @@ -0,0 +1,202 @@ +//go:build openbsd && arm +// +build openbsd,arm + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs process/types_openbsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 66 + KernProcAll = 0 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 55 + KernProcArgv = 1 + KernProcEnv = 3 +) + +const ( + ArgMax = 256 * 1024 +) + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x38 + sizeOfKinfoProc = 0x264 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SDEAD = 6 + SONPROC = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int32 +} + +type Timeval struct { + Sec int64 + Usec int32 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int32 + Ixrss int32 + Idrss int32 + Isrss int32 + Minflt int32 + Majflt int32 + Nswap int32 + Inblock int32 + Oublock int32 + Msgsnd int32 + Msgrcv int32 + Nsignals int32 + Nvcsw int32 + Nivcsw int32 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type KinfoProc struct { + Forw uint64 + Back uint64 + Paddr uint64 + Addr uint64 + Fd uint64 + Stats uint64 + Limit uint64 + Vmspace uint64 + Sigacts uint64 + Sess uint64 + Tsess uint64 + Ru uint64 + Eflag int32 + Exitsig int32 + Flag int32 + Pid int32 + Ppid int32 + Sid int32 + X_pgid int32 + Tpgid int32 + Uid uint32 + Ruid uint32 + Gid uint32 + Rgid uint32 + Groups [16]uint32 + Ngroups int16 + Jobc int16 + Tdev uint32 + Estcpu uint32 + Rtime_sec uint32 + Rtime_usec uint32 + Cpticks int32 + Pctcpu uint32 + Swtime uint32 + Slptime uint32 + Schedflags int32 + Uticks uint64 + Sticks uint64 + Iticks uint64 + Tracep uint64 + Traceflag int32 + Holdcnt int32 + Siglist int32 + Sigmask uint32 + Sigignore uint32 + Sigcatch uint32 + Stat int8 + Priority uint8 + Usrpri uint8 + Nice uint8 + Xstat uint16 + Acflag uint16 + Comm [24]int8 + Wmesg [8]int8 + Wchan uint64 + Login [32]int8 + Vm_rssize int32 + Vm_tsize int32 + Vm_dsize int32 + Vm_ssize int32 + Uvalid int64 + Ustart_sec uint64 + Ustart_usec uint32 + Uutime_sec uint32 + Uutime_usec uint32 + Ustime_sec uint32 + Ustime_usec uint32 + Uru_maxrss uint64 + Uru_ixrss uint64 + Uru_idrss uint64 + Uru_isrss uint64 + Uru_minflt uint64 + Uru_majflt uint64 + Uru_nswap uint64 + Uru_inblock uint64 + Uru_oublock uint64 + Uru_msgsnd uint64 + Uru_msgrcv uint64 + Uru_nsignals uint64 + Uru_nvcsw uint64 + Uru_nivcsw uint64 + Uctime_sec uint32 + Uctime_usec uint32 + Psflags int32 + Spare int32 + Svuid uint32 + Svgid uint32 + Emul [8]int8 + Rlim_rss_cur uint64 + Cpuid uint64 + Vm_map_size uint64 + Tid int32 + Rtableid uint32 +} + +type Priority struct{} + +type KinfoVmentry struct { + Start uint32 + End uint32 + Guard uint32 + Fspace uint32 + Fspace_augment uint32 + Offset uint64 + Wired_count int32 + Etype int32 + Protection int32 + Max_protection int32 + Advice int32 + Inheritance int32 + Flags uint8 + Pad_cgo_0 [3]byte +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm64.go b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm64.go new file mode 100644 index 000000000..a3291b8ca --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm64.go @@ -0,0 +1,203 @@ +//go:build openbsd && arm64 +// +build openbsd,arm64 + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs process/types_openbsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 66 + KernProcAll = 0 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 55 + KernProcArgv = 1 + KernProcEnv = 3 +) + +const ( + ArgMax = 256 * 1024 +) + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x50 + sizeOfKinfoProc = 0x270 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SDEAD = 6 + SONPROC = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type KinfoProc struct { + Forw uint64 + Back uint64 + Paddr uint64 + Addr uint64 + Fd uint64 + Stats uint64 + Limit uint64 + Vmspace uint64 + Sigacts uint64 + Sess uint64 + Tsess uint64 + Ru uint64 + Eflag int32 + Exitsig int32 + Flag int32 + Pid int32 + Ppid int32 + Sid int32 + X_pgid int32 + Tpgid int32 + Uid uint32 + Ruid uint32 + Gid uint32 + Rgid uint32 + Groups [16]uint32 + Ngroups int16 + Jobc int16 + Tdev uint32 + Estcpu uint32 + Rtime_sec uint32 + Rtime_usec uint32 + Cpticks int32 + Pctcpu uint32 + Swtime uint32 + Slptime uint32 + Schedflags int32 + Uticks uint64 + Sticks uint64 + Iticks uint64 + Tracep uint64 + Traceflag int32 + Holdcnt int32 + Siglist int32 + Sigmask uint32 + Sigignore uint32 + Sigcatch uint32 + Stat int8 + Priority uint8 + Usrpri uint8 + Nice uint8 + Xstat uint16 + Acflag uint16 + Comm [24]int8 + Wmesg [8]uint8 + Wchan uint64 + Login [32]uint8 + Vm_rssize int32 + Vm_tsize int32 + Vm_dsize int32 + Vm_ssize int32 + Uvalid int64 + Ustart_sec uint64 + Ustart_usec uint32 + Uutime_sec uint32 + Uutime_usec uint32 + Ustime_sec uint32 + Ustime_usec uint32 + Uru_maxrss uint64 + Uru_ixrss uint64 + Uru_idrss uint64 + Uru_isrss uint64 + Uru_minflt uint64 + Uru_majflt uint64 + Uru_nswap uint64 + Uru_inblock uint64 + Uru_oublock uint64 + Uru_msgsnd uint64 + Uru_msgrcv uint64 + Uru_nsignals uint64 + Uru_nvcsw uint64 + Uru_nivcsw uint64 + Uctime_sec uint32 + Uctime_usec uint32 + Psflags uint32 + Spare int32 + Svuid uint32 + Svgid uint32 + Emul [8]uint8 + Rlim_rss_cur uint64 + Cpuid uint64 + Vm_map_size uint64 + Tid int32 + Rtableid uint32 + Pledge uint64 +} + +type Priority struct{} + +type KinfoVmentry struct { + Start uint64 + End uint64 + Guard uint64 + Fspace uint64 + Fspace_augment uint64 + Offset uint64 + Wired_count int32 + Etype int32 + Protection int32 + Max_protection int32 + Advice int32 + Inheritance int32 + Flags uint8 + Pad_cgo_0 [7]byte +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_riscv64.go b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_riscv64.go new file mode 100644 index 000000000..076f095ea --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_riscv64.go @@ -0,0 +1,204 @@ +//go:build openbsd && riscv64 +// +build openbsd,riscv64 + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs process/types_openbsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 66 + KernProcAll = 0 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 55 + KernProcArgv = 1 + KernProcEnv = 3 +) + +const ( + ArgMax = 256 * 1024 +) + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x50 + sizeOfKinfoProc = 0x288 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SDEAD = 6 + SONPROC = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type KinfoProc struct { + Forw uint64 + Back uint64 + Paddr uint64 + Addr uint64 + Fd uint64 + Stats uint64 + Limit uint64 + Vmspace uint64 + Sigacts uint64 + Sess uint64 + Tsess uint64 + Ru uint64 + Eflag int32 + Exitsig int32 + Flag int32 + Pid int32 + Ppid int32 + Sid int32 + X_pgid int32 + Tpgid int32 + Uid uint32 + Ruid uint32 + Gid uint32 + Rgid uint32 + Groups [16]uint32 + Ngroups int16 + Jobc int16 + Tdev uint32 + Estcpu uint32 + Rtime_sec uint32 + Rtime_usec uint32 + Cpticks int32 + Pctcpu uint32 + Swtime uint32 + Slptime uint32 + Schedflags int32 + Uticks uint64 + Sticks uint64 + Iticks uint64 + Tracep uint64 + Traceflag int32 + Holdcnt int32 + Siglist int32 + Sigmask uint32 + Sigignore uint32 + Sigcatch uint32 + Stat int8 + Priority uint8 + Usrpri uint8 + Nice uint8 + Xstat uint16 + Spare uint16 + Comm [24]int8 + Wmesg [8]uint8 + Wchan uint64 + Login [32]uint8 + Vm_rssize int32 + Vm_tsize int32 + Vm_dsize int32 + Vm_ssize int32 + Uvalid int64 + Ustart_sec uint64 + Ustart_usec uint32 + Uutime_sec uint32 + Uutime_usec uint32 + Ustime_sec uint32 + Ustime_usec uint32 + Uru_maxrss uint64 + Uru_ixrss uint64 + Uru_idrss uint64 + Uru_isrss uint64 + Uru_minflt uint64 + Uru_majflt uint64 + Uru_nswap uint64 + Uru_inblock uint64 + Uru_oublock uint64 + Uru_msgsnd uint64 + Uru_msgrcv uint64 + Uru_nsignals uint64 + Uru_nvcsw uint64 + Uru_nivcsw uint64 + Uctime_sec uint32 + Uctime_usec uint32 + Psflags uint32 + Acflag uint32 + Svuid uint32 + Svgid uint32 + Emul [8]uint8 + Rlim_rss_cur uint64 + Cpuid uint64 + Vm_map_size uint64 + Tid int32 + Rtableid uint32 + Pledge uint64 + Name [24]uint8 +} + +type Priority struct{} + +type KinfoVmentry struct { + Start uint64 + End uint64 + Guard uint64 + Fspace uint64 + Fspace_augment uint64 + Offset uint64 + Wired_count int32 + Etype int32 + Protection int32 + Max_protection int32 + Advice int32 + Inheritance int32 + Flags uint8 + Pad_cgo_0 [7]byte +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_plan9.go b/vendor/github.com/shirou/gopsutil/v3/process/process_plan9.go new file mode 100644 index 000000000..bc4bc062a --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_plan9.go @@ -0,0 +1,203 @@ +//go:build plan9 +// +build plan9 + +package process + +import ( + "context" + "syscall" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/shirou/gopsutil/v3/net" +) + +type Signal = syscall.Note + +type MemoryMapsStat struct { + Path string `json:"path"` + Rss uint64 `json:"rss"` + Size uint64 `json:"size"` + Pss uint64 `json:"pss"` + SharedClean uint64 `json:"sharedClean"` + SharedDirty uint64 `json:"sharedDirty"` + PrivateClean uint64 `json:"privateClean"` + PrivateDirty uint64 `json:"privateDirty"` + Referenced uint64 `json:"referenced"` + Anonymous uint64 `json:"anonymous"` + Swap uint64 `json:"swap"` +} + +type MemoryInfoExStat struct{} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + return nil, common.ErrNotImplementedError +} + +func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { + return false, common.ErrNotImplementedError +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) TgidWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) { + return []string{""}, common.ErrNotImplementedError +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + return false, common.ErrNotImplementedError +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) SendSignalWithContext(ctx context.Context, sig Signal) error { + return common.ErrNotImplementedError +} + +func (p *Process) SuspendWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} + +func (p *Process) ResumeWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} + +func (p *Process) TerminateWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} + +func (p *Process) KillWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} + +func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) { + return nil, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_posix.go b/vendor/github.com/shirou/gopsutil/v3/process/process_posix.go new file mode 100644 index 000000000..a01f9ecfc --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_posix.go @@ -0,0 +1,185 @@ +//go:build linux || freebsd || openbsd || darwin || solaris +// +build linux freebsd openbsd darwin solaris + +package process + +import ( + "context" + "errors" + "fmt" + "os" + "os/user" + "path/filepath" + "strconv" + "strings" + "syscall" + + "golang.org/x/sys/unix" + + "github.com/shirou/gopsutil/v3/internal/common" +) + +type Signal = syscall.Signal + +// POSIX +func getTerminalMap() (map[uint64]string, error) { + ret := make(map[uint64]string) + var termfiles []string + + d, err := os.Open("/dev") + if err != nil { + return nil, err + } + defer d.Close() + + devnames, err := d.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, devname := range devnames { + if strings.HasPrefix(devname, "/dev/tty") { + termfiles = append(termfiles, "/dev/tty/"+devname) + } + } + + var ptsnames []string + ptsd, err := os.Open("/dev/pts") + if err != nil { + ptsnames, _ = filepath.Glob("/dev/ttyp*") + if ptsnames == nil { + return nil, err + } + } + defer ptsd.Close() + + if ptsnames == nil { + defer ptsd.Close() + ptsnames, err = ptsd.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, ptsname := range ptsnames { + termfiles = append(termfiles, "/dev/pts/"+ptsname) + } + } else { + termfiles = ptsnames + } + + for _, name := range termfiles { + stat := unix.Stat_t{} + if err = unix.Stat(name, &stat); err != nil { + return nil, err + } + rdev := uint64(stat.Rdev) + ret[rdev] = strings.Replace(name, "/dev", "", -1) + } + return ret, nil +} + +// isMount is a port of python's os.path.ismount() +// https://github.com/python/cpython/blob/08ff4369afca84587b1c82034af4e9f64caddbf2/Lib/posixpath.py#L186-L216 +// https://docs.python.org/3/library/os.path.html#os.path.ismount +func isMount(path string) bool { + // Check symlinkness with os.Lstat; unix.DT_LNK is not portable + fileInfo, err := os.Lstat(path) + if err != nil { + return false + } + if fileInfo.Mode()&os.ModeSymlink != 0 { + return false + } + var stat1 unix.Stat_t + if err := unix.Lstat(path, &stat1); err != nil { + return false + } + parent := filepath.Join(path, "..") + var stat2 unix.Stat_t + if err := unix.Lstat(parent, &stat2); err != nil { + return false + } + return stat1.Dev != stat2.Dev || stat1.Ino == stat2.Ino +} + +func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { + if pid <= 0 { + return false, fmt.Errorf("invalid pid %v", pid) + } + proc, err := os.FindProcess(int(pid)) + if err != nil { + return false, err + } + + if isMount(common.HostProcWithContext(ctx)) { // if //proc exists and is mounted, check if //proc/ folder exists + _, err := os.Stat(common.HostProcWithContext(ctx, strconv.Itoa(int(pid)))) + if os.IsNotExist(err) { + return false, nil + } + return err == nil, err + } + + // procfs does not exist or is not mounted, check PID existence by signalling the pid + err = proc.Signal(syscall.Signal(0)) + if err == nil { + return true, nil + } + if errors.Is(err, os.ErrProcessDone) { + return false, nil + } + var errno syscall.Errno + if !errors.As(err, &errno) { + return false, err + } + switch errno { + case syscall.ESRCH: + return false, nil + case syscall.EPERM: + return true, nil + } + + return false, err +} + +func (p *Process) SendSignalWithContext(ctx context.Context, sig syscall.Signal) error { + process, err := os.FindProcess(int(p.Pid)) + if err != nil { + return err + } + + err = process.Signal(sig) + if err != nil { + return err + } + + return nil +} + +func (p *Process) SuspendWithContext(ctx context.Context) error { + return p.SendSignalWithContext(ctx, unix.SIGSTOP) +} + +func (p *Process) ResumeWithContext(ctx context.Context) error { + return p.SendSignalWithContext(ctx, unix.SIGCONT) +} + +func (p *Process) TerminateWithContext(ctx context.Context) error { + return p.SendSignalWithContext(ctx, unix.SIGTERM) +} + +func (p *Process) KillWithContext(ctx context.Context) error { + return p.SendSignalWithContext(ctx, unix.SIGKILL) +} + +func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { + uids, err := p.UidsWithContext(ctx) + if err != nil { + return "", err + } + if len(uids) > 0 { + u, err := user.LookupId(strconv.Itoa(int(uids[0]))) + if err != nil { + return "", err + } + return u.Username, nil + } + return "", nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_solaris.go b/vendor/github.com/shirou/gopsutil/v3/process/process_solaris.go new file mode 100644 index 000000000..dd4bd4760 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_solaris.go @@ -0,0 +1,303 @@ +package process + +import ( + "bytes" + "context" + "os" + "strconv" + "strings" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/shirou/gopsutil/v3/net" +) + +type MemoryMapsStat struct { + Path string `json:"path"` + Rss uint64 `json:"rss"` + Size uint64 `json:"size"` + Pss uint64 `json:"pss"` + SharedClean uint64 `json:"sharedClean"` + SharedDirty uint64 `json:"sharedDirty"` + PrivateClean uint64 `json:"privateClean"` + PrivateDirty uint64 `json:"privateDirty"` + Referenced uint64 `json:"referenced"` + Anonymous uint64 `json:"anonymous"` + Swap uint64 `json:"swap"` +} + +type MemoryInfoExStat struct{} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + return readPidsFromDir(common.HostProcWithContext(ctx)) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + out := []*Process{} + + pids, err := PidsWithContext(ctx) + if err != nil { + return out, err + } + + for _, pid := range pids { + p, err := NewProcessWithContext(ctx, pid) + if err != nil { + continue + } + out = append(out, p) + } + + return out, nil +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) TgidWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + exe, err := p.fillFromPathAOutWithContext(ctx) + if os.IsNotExist(err) { + exe, err = p.fillFromExecnameWithContext(ctx) + } + return exe, err +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + return p.fillFromCmdlineWithContext(ctx) +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + return p.fillSliceFromCmdlineWithContext(ctx) +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return p.fillFromPathCwdWithContext(ctx) +} + +func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) { + return []string{""}, common.ErrNotImplementedError +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + return false, common.ErrNotImplementedError +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + _, fnames, err := p.fillFromfdListWithContext(ctx) + return int32(len(fnames)), err +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) { + return nil, common.ErrNotImplementedError +} + +/** +** Internal functions +**/ + +func (p *Process) fillFromfdListWithContext(ctx context.Context) (string, []string, error) { + pid := p.Pid + statPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "fd") + d, err := os.Open(statPath) + if err != nil { + return statPath, []string{}, err + } + defer d.Close() + fnames, err := d.Readdirnames(-1) + return statPath, fnames, err +} + +func (p *Process) fillFromPathCwdWithContext(ctx context.Context) (string, error) { + pid := p.Pid + cwdPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "path", "cwd") + cwd, err := os.Readlink(cwdPath) + if err != nil { + return "", err + } + return cwd, nil +} + +func (p *Process) fillFromPathAOutWithContext(ctx context.Context) (string, error) { + pid := p.Pid + cwdPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "path", "a.out") + exe, err := os.Readlink(cwdPath) + if err != nil { + return "", err + } + return exe, nil +} + +func (p *Process) fillFromExecnameWithContext(ctx context.Context) (string, error) { + pid := p.Pid + execNamePath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "execname") + exe, err := os.ReadFile(execNamePath) + if err != nil { + return "", err + } + return string(exe), nil +} + +func (p *Process) fillFromCmdlineWithContext(ctx context.Context) (string, error) { + pid := p.Pid + cmdPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "cmdline") + cmdline, err := os.ReadFile(cmdPath) + if err != nil { + return "", err + } + ret := strings.FieldsFunc(string(cmdline), func(r rune) bool { + if r == '\u0000' { + return true + } + return false + }) + + return strings.Join(ret, " "), nil +} + +func (p *Process) fillSliceFromCmdlineWithContext(ctx context.Context) ([]string, error) { + pid := p.Pid + cmdPath := common.HostProcWithContext(ctx, strconv.Itoa(int(pid)), "cmdline") + cmdline, err := os.ReadFile(cmdPath) + if err != nil { + return nil, err + } + if len(cmdline) == 0 { + return nil, nil + } + if cmdline[len(cmdline)-1] == 0 { + cmdline = cmdline[:len(cmdline)-1] + } + parts := bytes.Split(cmdline, []byte{0}) + var strParts []string + for _, p := range parts { + strParts = append(strParts, string(p)) + } + + return strParts, nil +} + +func readPidsFromDir(path string) ([]int32, error) { + var ret []int32 + + d, err := os.Open(path) + if err != nil { + return nil, err + } + defer d.Close() + + fnames, err := d.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, fname := range fnames { + pid, err := strconv.ParseInt(fname, 10, 32) + if err != nil { + // if not numeric name, just skip + continue + } + ret = append(ret, int32(pid)) + } + + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_windows.go b/vendor/github.com/shirou/gopsutil/v3/process/process_windows.go new file mode 100644 index 000000000..f2053d985 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_windows.go @@ -0,0 +1,1165 @@ +//go:build windows +// +build windows + +package process + +import ( + "bufio" + "context" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "reflect" + "strings" + "syscall" + "time" + "unicode/utf16" + "unsafe" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/internal/common" + "github.com/shirou/gopsutil/v3/net" + "golang.org/x/sys/windows" +) + +type Signal = syscall.Signal + +var ( + modntdll = windows.NewLazySystemDLL("ntdll.dll") + procNtResumeProcess = modntdll.NewProc("NtResumeProcess") + procNtSuspendProcess = modntdll.NewProc("NtSuspendProcess") + + modpsapi = windows.NewLazySystemDLL("psapi.dll") + procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") + procGetProcessImageFileNameW = modpsapi.NewProc("GetProcessImageFileNameW") + + advapi32 = windows.NewLazySystemDLL("advapi32.dll") + procLookupPrivilegeValue = advapi32.NewProc("LookupPrivilegeValueW") + procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges") + + procQueryFullProcessImageNameW = common.Modkernel32.NewProc("QueryFullProcessImageNameW") + procGetPriorityClass = common.Modkernel32.NewProc("GetPriorityClass") + procGetProcessIoCounters = common.Modkernel32.NewProc("GetProcessIoCounters") + procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo") + + processorArchitecture uint +) + +const processQueryInformation = windows.PROCESS_QUERY_LIMITED_INFORMATION + +type systemProcessorInformation struct { + ProcessorArchitecture uint16 + ProcessorLevel uint16 + ProcessorRevision uint16 + Reserved uint16 + ProcessorFeatureBits uint16 +} + +type systemInfo struct { + wProcessorArchitecture uint16 + wReserved uint16 + dwpageSize uint32 + lpMinimumApplicationAddress uintptr + lpMaximumApplicationAddress uintptr + dwActiveProcessorMask uintptr + dwNumberOfProcessors uint32 + dwProcessorType uint32 + dwAllocationGranularity uint32 + wProcessorLevel uint16 + wProcessorRevision uint16 +} + +// Memory_info_ex is different between OSes +type MemoryInfoExStat struct{} + +type MemoryMapsStat struct{} + +// ioCounters is an equivalent representation of IO_COUNTERS in the Windows API. +// https://docs.microsoft.com/windows/win32/api/winnt/ns-winnt-io_counters +type ioCounters struct { + ReadOperationCount uint64 + WriteOperationCount uint64 + OtherOperationCount uint64 + ReadTransferCount uint64 + WriteTransferCount uint64 + OtherTransferCount uint64 +} + +type processBasicInformation32 struct { + Reserved1 uint32 + PebBaseAddress uint32 + Reserved2 uint32 + Reserved3 uint32 + UniqueProcessId uint32 + Reserved4 uint32 +} + +type processBasicInformation64 struct { + Reserved1 uint64 + PebBaseAddress uint64 + Reserved2 uint64 + Reserved3 uint64 + UniqueProcessId uint64 + Reserved4 uint64 +} + +type processEnvironmentBlock32 struct { + Reserved1 [2]uint8 + BeingDebugged uint8 + Reserved2 uint8 + Reserved3 [2]uint32 + Ldr uint32 + ProcessParameters uint32 + // More fields which we don't use so far +} + +type processEnvironmentBlock64 struct { + Reserved1 [2]uint8 + BeingDebugged uint8 + Reserved2 uint8 + _ [4]uint8 // padding, since we are 64 bit, the next pointer is 64 bit aligned (when compiling for 32 bit, this is not the case without manual padding) + Reserved3 [2]uint64 + Ldr uint64 + ProcessParameters uint64 + // More fields which we don't use so far +} + +type rtlUserProcessParameters32 struct { + Reserved1 [16]uint8 + ConsoleHandle uint32 + ConsoleFlags uint32 + StdInputHandle uint32 + StdOutputHandle uint32 + StdErrorHandle uint32 + CurrentDirectoryPathNameLength uint16 + _ uint16 // Max Length + CurrentDirectoryPathAddress uint32 + CurrentDirectoryHandle uint32 + DllPathNameLength uint16 + _ uint16 // Max Length + DllPathAddress uint32 + ImagePathNameLength uint16 + _ uint16 // Max Length + ImagePathAddress uint32 + CommandLineLength uint16 + _ uint16 // Max Length + CommandLineAddress uint32 + EnvironmentAddress uint32 + // More fields which we don't use so far +} + +type rtlUserProcessParameters64 struct { + Reserved1 [16]uint8 + ConsoleHandle uint64 + ConsoleFlags uint64 + StdInputHandle uint64 + StdOutputHandle uint64 + StdErrorHandle uint64 + CurrentDirectoryPathNameLength uint16 + _ uint16 // Max Length + _ uint32 // Padding + CurrentDirectoryPathAddress uint64 + CurrentDirectoryHandle uint64 + DllPathNameLength uint16 + _ uint16 // Max Length + _ uint32 // Padding + DllPathAddress uint64 + ImagePathNameLength uint16 + _ uint16 // Max Length + _ uint32 // Padding + ImagePathAddress uint64 + CommandLineLength uint16 + _ uint16 // Max Length + _ uint32 // Padding + CommandLineAddress uint64 + EnvironmentAddress uint64 + // More fields which we don't use so far +} + +type winLUID struct { + LowPart winDWord + HighPart winLong +} + +// LUID_AND_ATTRIBUTES +type winLUIDAndAttributes struct { + Luid winLUID + Attributes winDWord +} + +// TOKEN_PRIVILEGES +type winTokenPrivileges struct { + PrivilegeCount winDWord + Privileges [1]winLUIDAndAttributes +} + +type ( + winLong int32 + winDWord uint32 +) + +func init() { + var systemInfo systemInfo + + procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo))) + processorArchitecture = uint(systemInfo.wProcessorArchitecture) + + // enable SeDebugPrivilege https://github.com/midstar/proci/blob/6ec79f57b90ba3d9efa2a7b16ef9c9369d4be875/proci_windows.go#L80-L119 + handle, err := syscall.GetCurrentProcess() + if err != nil { + return + } + + var token syscall.Token + err = syscall.OpenProcessToken(handle, 0x0028, &token) + if err != nil { + return + } + defer token.Close() + + tokenPrivileges := winTokenPrivileges{PrivilegeCount: 1} + lpName := syscall.StringToUTF16("SeDebugPrivilege") + ret, _, _ := procLookupPrivilegeValue.Call( + 0, + uintptr(unsafe.Pointer(&lpName[0])), + uintptr(unsafe.Pointer(&tokenPrivileges.Privileges[0].Luid))) + if ret == 0 { + return + } + + tokenPrivileges.Privileges[0].Attributes = 0x00000002 // SE_PRIVILEGE_ENABLED + + procAdjustTokenPrivileges.Call( + uintptr(token), + 0, + uintptr(unsafe.Pointer(&tokenPrivileges)), + uintptr(unsafe.Sizeof(tokenPrivileges)), + 0, + 0) +} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + // inspired by https://gist.github.com/henkman/3083408 + // and https://github.com/giampaolo/psutil/blob/1c3a15f637521ba5c0031283da39c733fda53e4c/psutil/arch/windows/process_info.c#L315-L329 + var ret []int32 + var read uint32 = 0 + var psSize uint32 = 1024 + const dwordSize uint32 = 4 + + for { + ps := make([]uint32, psSize) + if err := windows.EnumProcesses(ps, &read); err != nil { + return nil, err + } + if uint32(len(ps)) == read/dwordSize { // ps buffer was too small to host every results, retry with a bigger one + psSize += 1024 + continue + } + for _, pid := range ps[:read/dwordSize] { + ret = append(ret, int32(pid)) + } + return ret, nil + + } +} + +func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { + if pid == 0 { // special case for pid 0 System Idle Process + return true, nil + } + if pid < 0 { + return false, fmt.Errorf("invalid pid %v", pid) + } + if pid%4 != 0 { + // OpenProcess will succeed even on non-existing pid here https://devblogs.microsoft.com/oldnewthing/20080606-00/?p=22043 + // so we list every pid just to be sure and be future-proof + pids, err := PidsWithContext(ctx) + if err != nil { + return false, err + } + for _, i := range pids { + if i == pid { + return true, err + } + } + return false, err + } + h, err := windows.OpenProcess(windows.SYNCHRONIZE, false, uint32(pid)) + if err == windows.ERROR_ACCESS_DENIED { + return true, nil + } + if err == windows.ERROR_INVALID_PARAMETER { + return false, nil + } + if err != nil { + return false, err + } + defer windows.CloseHandle(h) + event, err := windows.WaitForSingleObject(h, 0) + return event == uint32(windows.WAIT_TIMEOUT), err +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + // if cached already, return from cache + cachedPpid := p.getPpid() + if cachedPpid != 0 { + return cachedPpid, nil + } + + ppid, _, _, err := getFromSnapProcess(p.Pid) + if err != nil { + return 0, err + } + + // no errors and not cached already, so cache it + p.setPpid(ppid) + + return ppid, nil +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + if p.Pid == 0 { + return "System Idle Process", nil + } + if p.Pid == 4 { + return "System", nil + } + + exe, err := p.ExeWithContext(ctx) + if err != nil { + return "", fmt.Errorf("could not get Name: %s", err) + } + + return filepath.Base(exe), nil +} + +func (p *Process) TgidWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid)) + if err != nil { + return "", err + } + defer windows.CloseHandle(c) + buf := make([]uint16, syscall.MAX_LONG_PATH) + size := uint32(syscall.MAX_LONG_PATH) + if err := procQueryFullProcessImageNameW.Find(); err == nil { // Vista+ + ret, _, err := procQueryFullProcessImageNameW.Call( + uintptr(c), + uintptr(0), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&size))) + if ret == 0 { + return "", err + } + return windows.UTF16ToString(buf[:]), nil + } + // XP fallback + ret, _, err := procGetProcessImageFileNameW.Call(uintptr(c), uintptr(unsafe.Pointer(&buf[0])), uintptr(size)) + if ret == 0 { + return "", err + } + return common.ConvertDOSPath(windows.UTF16ToString(buf[:])), nil +} + +func (p *Process) CmdlineWithContext(_ context.Context) (string, error) { + cmdline, err := getProcessCommandLine(p.Pid) + if err != nil { + return "", fmt.Errorf("could not get CommandLine: %s", err) + } + return cmdline, nil +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + cmdline, err := p.CmdlineWithContext(ctx) + if err != nil { + return nil, err + } + return strings.Split(cmdline, " "), nil +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + ru, err := getRusage(p.Pid) + if err != nil { + return 0, fmt.Errorf("could not get CreationDate: %s", err) + } + + return ru.CreationTime.Nanoseconds() / 1000000, nil +} + +func (p *Process) CwdWithContext(_ context.Context) (string, error) { + h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(p.Pid)) + if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER { + return "", nil + } + if err != nil { + return "", err + } + defer syscall.CloseHandle(syscall.Handle(h)) + + procIs32Bits := is32BitProcess(h) + + if procIs32Bits { + userProcParams, err := getUserProcessParams32(h) + if err != nil { + return "", err + } + if userProcParams.CurrentDirectoryPathNameLength > 0 { + cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CurrentDirectoryPathAddress), uint(userProcParams.CurrentDirectoryPathNameLength)) + if len(cwd) != int(userProcParams.CurrentDirectoryPathNameLength) { + return "", errors.New("cannot read current working directory") + } + + return convertUTF16ToString(cwd), nil + } + } else { + userProcParams, err := getUserProcessParams64(h) + if err != nil { + return "", err + } + if userProcParams.CurrentDirectoryPathNameLength > 0 { + cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CurrentDirectoryPathAddress, uint(userProcParams.CurrentDirectoryPathNameLength)) + if len(cwd) != int(userProcParams.CurrentDirectoryPathNameLength) { + return "", errors.New("cannot read current working directory") + } + + return convertUTF16ToString(cwd), nil + } + } + + // if we reach here, we have no cwd + return "", nil +} + +func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) { + return []string{""}, common.ErrNotImplementedError +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + return false, common.ErrNotImplementedError +} + +func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { + pid := p.Pid + c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid)) + if err != nil { + return "", err + } + defer windows.CloseHandle(c) + + var token syscall.Token + err = syscall.OpenProcessToken(syscall.Handle(c), syscall.TOKEN_QUERY, &token) + if err != nil { + return "", err + } + defer token.Close() + tokenUser, err := token.GetTokenUser() + if err != nil { + return "", err + } + + user, domain, _, err := tokenUser.User.Sid.LookupAccount("") + return domain + "\\" + user, err +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +// priorityClasses maps a win32 priority class to its WMI equivalent Win32_Process.Priority +// https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getpriorityclass +// https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-process +var priorityClasses = map[int]int32{ + 0x00008000: 10, // ABOVE_NORMAL_PRIORITY_CLASS + 0x00004000: 6, // BELOW_NORMAL_PRIORITY_CLASS + 0x00000080: 13, // HIGH_PRIORITY_CLASS + 0x00000040: 4, // IDLE_PRIORITY_CLASS + 0x00000020: 8, // NORMAL_PRIORITY_CLASS + 0x00000100: 24, // REALTIME_PRIORITY_CLASS +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid)) + if err != nil { + return 0, err + } + defer windows.CloseHandle(c) + ret, _, err := procGetPriorityClass.Call(uintptr(c)) + if ret == 0 { + return 0, err + } + priority, ok := priorityClasses[int(ret)] + if !ok { + return 0, fmt.Errorf("unknown priority class %v", ret) + } + return priority, nil +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid)) + if err != nil { + return nil, err + } + defer windows.CloseHandle(c) + var ioCounters ioCounters + ret, _, err := procGetProcessIoCounters.Call(uintptr(c), uintptr(unsafe.Pointer(&ioCounters))) + if ret == 0 { + return nil, err + } + stats := &IOCountersStat{ + ReadCount: ioCounters.ReadOperationCount, + ReadBytes: ioCounters.ReadTransferCount, + WriteCount: ioCounters.WriteOperationCount, + WriteBytes: ioCounters.WriteTransferCount, + } + + return stats, nil +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + ppid, ret, _, err := getFromSnapProcess(p.Pid) + if err != nil { + return 0, err + } + + // if no errors and not cached already, cache ppid + p.parent = ppid + if 0 == p.getPpid() { + p.setPpid(ppid) + } + + return ret, nil +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + sysTimes, err := getProcessCPUTimes(p.Pid) + if err != nil { + return nil, err + } + + // User and kernel times are represented as a FILETIME structure + // which contains a 64-bit value representing the number of + // 100-nanosecond intervals since January 1, 1601 (UTC): + // http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx + // To convert it into a float representing the seconds that the + // process has executed in user/kernel mode I borrowed the code + // below from psutil's _psutil_windows.c, and in turn from Python's + // Modules/posixmodule.c + + user := float64(sysTimes.UserTime.HighDateTime)*429.4967296 + float64(sysTimes.UserTime.LowDateTime)*1e-7 + kernel := float64(sysTimes.KernelTime.HighDateTime)*429.4967296 + float64(sysTimes.KernelTime.LowDateTime)*1e-7 + + return &cpu.TimesStat{ + User: user, + System: kernel, + }, nil +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + mem, err := getMemoryInfo(p.Pid) + if err != nil { + return nil, err + } + + ret := &MemoryInfoStat{ + RSS: uint64(mem.WorkingSetSize), + VMS: uint64(mem.PagefileUsage), + } + + return ret, nil +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + out := []*Process{} + snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, uint32(0)) + if err != nil { + return out, err + } + defer windows.CloseHandle(snap) + var pe32 windows.ProcessEntry32 + pe32.Size = uint32(unsafe.Sizeof(pe32)) + if err := windows.Process32First(snap, &pe32); err != nil { + return out, err + } + for { + if pe32.ParentProcessID == uint32(p.Pid) { + p, err := NewProcessWithContext(ctx, int32(pe32.ProcessID)) + if err == nil { + out = append(out, p) + } + } + if err = windows.Process32Next(snap, &pe32); err != nil { + break + } + } + return out, nil +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + files := make([]OpenFilesStat, 0) + fileExists := make(map[string]bool) + + process, err := windows.OpenProcess(common.ProcessQueryInformation, false, uint32(p.Pid)) + if err != nil { + return nil, err + } + + buffer := make([]byte, 1024) + var size uint32 + + st := common.CallWithExpandingBuffer( + func() common.NtStatus { + return common.NtQuerySystemInformation( + common.SystemExtendedHandleInformationClass, + &buffer[0], + uint32(len(buffer)), + &size, + ) + }, + &buffer, + &size, + ) + if st.IsError() { + return nil, st.Error() + } + + handlesList := (*common.SystemExtendedHandleInformation)(unsafe.Pointer(&buffer[0])) + handles := make([]common.SystemExtendedHandleTableEntryInformation, int(handlesList.NumberOfHandles)) + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&handles)) + hdr.Data = uintptr(unsafe.Pointer(&handlesList.Handles[0])) + + currentProcess, err := windows.GetCurrentProcess() + if err != nil { + return nil, err + } + + for _, handle := range handles { + var file uintptr + if int32(handle.UniqueProcessId) != p.Pid { + continue + } + if windows.DuplicateHandle(process, windows.Handle(handle.HandleValue), currentProcess, (*windows.Handle)(&file), + 0, true, windows.DUPLICATE_SAME_ACCESS) != nil { + continue + } + // release the new handle + defer windows.CloseHandle(windows.Handle(file)) + + fileType, err := windows.GetFileType(windows.Handle(file)) + if err != nil || fileType != windows.FILE_TYPE_DISK { + continue + } + + var fileName string + ch := make(chan struct{}) + + go func() { + var buf [syscall.MAX_LONG_PATH]uint16 + n, err := windows.GetFinalPathNameByHandle(windows.Handle(file), &buf[0], syscall.MAX_LONG_PATH, 0) + if err != nil { + return + } + + fileName = string(utf16.Decode(buf[:n])) + ch <- struct{}{} + }() + + select { + case <-time.NewTimer(100 * time.Millisecond).C: + continue + case <-ch: + fileInfo, err := os.Stat(fileName) + if err != nil || fileInfo.IsDir() { + continue + } + + if _, exists := fileExists[fileName]; !exists { + files = append(files, OpenFilesStat{ + Path: fileName, + Fd: uint64(file), + }) + fileExists[fileName] = true + } + case <-ctx.Done(): + return files, ctx.Err() + } + } + + return files, nil +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return net.ConnectionsPidWithContext(ctx, "all", p.Pid) +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) SendSignalWithContext(ctx context.Context, sig syscall.Signal) error { + return common.ErrNotImplementedError +} + +func (p *Process) SuspendWithContext(ctx context.Context) error { + c, err := windows.OpenProcess(windows.PROCESS_SUSPEND_RESUME, false, uint32(p.Pid)) + if err != nil { + return err + } + defer windows.CloseHandle(c) + + r1, _, _ := procNtSuspendProcess.Call(uintptr(c)) + if r1 != 0 { + // See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 + return fmt.Errorf("NtStatus='0x%.8X'", r1) + } + + return nil +} + +func (p *Process) ResumeWithContext(ctx context.Context) error { + c, err := windows.OpenProcess(windows.PROCESS_SUSPEND_RESUME, false, uint32(p.Pid)) + if err != nil { + return err + } + defer windows.CloseHandle(c) + + r1, _, _ := procNtResumeProcess.Call(uintptr(c)) + if r1 != 0 { + // See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 + return fmt.Errorf("NtStatus='0x%.8X'", r1) + } + + return nil +} + +func (p *Process) TerminateWithContext(ctx context.Context) error { + proc, err := windows.OpenProcess(windows.PROCESS_TERMINATE, false, uint32(p.Pid)) + if err != nil { + return err + } + err = windows.TerminateProcess(proc, 0) + windows.CloseHandle(proc) + return err +} + +func (p *Process) KillWithContext(ctx context.Context) error { + process, err := os.FindProcess(int(p.Pid)) + if err != nil { + return err + } + return process.Kill() +} + +func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) { + envVars, err := getProcessEnvironmentVariables(p.Pid, ctx) + if err != nil { + return nil, fmt.Errorf("could not get environment variables: %s", err) + } + return envVars, nil +} + +// retrieve Ppid in a thread-safe manner +func (p *Process) getPpid() int32 { + p.parentMutex.RLock() + defer p.parentMutex.RUnlock() + return p.parent +} + +// cache Ppid in a thread-safe manner (WINDOWS ONLY) +// see https://psutil.readthedocs.io/en/latest/#psutil.Process.ppid +func (p *Process) setPpid(ppid int32) { + p.parentMutex.Lock() + defer p.parentMutex.Unlock() + p.parent = ppid +} + +func getFromSnapProcess(pid int32) (int32, int32, string, error) { + snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, uint32(pid)) + if err != nil { + return 0, 0, "", err + } + defer windows.CloseHandle(snap) + var pe32 windows.ProcessEntry32 + pe32.Size = uint32(unsafe.Sizeof(pe32)) + if err = windows.Process32First(snap, &pe32); err != nil { + return 0, 0, "", err + } + for { + if pe32.ProcessID == uint32(pid) { + szexe := windows.UTF16ToString(pe32.ExeFile[:]) + return int32(pe32.ParentProcessID), int32(pe32.Threads), szexe, nil + } + if err = windows.Process32Next(snap, &pe32); err != nil { + break + } + } + return 0, 0, "", fmt.Errorf("couldn't find pid: %d", pid) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + out := []*Process{} + + pids, err := PidsWithContext(ctx) + if err != nil { + return out, fmt.Errorf("could not get Processes %s", err) + } + + for _, pid := range pids { + p, err := NewProcessWithContext(ctx, pid) + if err != nil { + continue + } + out = append(out, p) + } + + return out, nil +} + +func getRusage(pid int32) (*windows.Rusage, error) { + var CPU windows.Rusage + + c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid)) + if err != nil { + return nil, err + } + defer windows.CloseHandle(c) + + if err := windows.GetProcessTimes(c, &CPU.CreationTime, &CPU.ExitTime, &CPU.KernelTime, &CPU.UserTime); err != nil { + return nil, err + } + + return &CPU, nil +} + +func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) { + var mem PROCESS_MEMORY_COUNTERS + c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid)) + if err != nil { + return mem, err + } + defer windows.CloseHandle(c) + if err := getProcessMemoryInfo(c, &mem); err != nil { + return mem, err + } + + return mem, err +} + +func getProcessMemoryInfo(h windows.Handle, mem *PROCESS_MEMORY_COUNTERS) (err error) { + r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(mem)), uintptr(unsafe.Sizeof(*mem))) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +type SYSTEM_TIMES struct { + CreateTime syscall.Filetime + ExitTime syscall.Filetime + KernelTime syscall.Filetime + UserTime syscall.Filetime +} + +func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) { + var times SYSTEM_TIMES + + h, err := windows.OpenProcess(processQueryInformation, false, uint32(pid)) + if err != nil { + return times, err + } + defer windows.CloseHandle(h) + + err = syscall.GetProcessTimes( + syscall.Handle(h), + ×.CreateTime, + ×.ExitTime, + ×.KernelTime, + ×.UserTime, + ) + + return times, err +} + +func getUserProcessParams32(handle windows.Handle) (rtlUserProcessParameters32, error) { + pebAddress, err := queryPebAddress(syscall.Handle(handle), true) + if err != nil { + return rtlUserProcessParameters32{}, fmt.Errorf("cannot locate process PEB: %w", err) + } + + buf := readProcessMemory(syscall.Handle(handle), true, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock32{}))) + if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock32{})) { + return rtlUserProcessParameters32{}, fmt.Errorf("cannot read process PEB") + } + peb := (*processEnvironmentBlock32)(unsafe.Pointer(&buf[0])) + userProcessAddress := uint64(peb.ProcessParameters) + buf = readProcessMemory(syscall.Handle(handle), true, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters32{}))) + if len(buf) != int(unsafe.Sizeof(rtlUserProcessParameters32{})) { + return rtlUserProcessParameters32{}, fmt.Errorf("cannot read user process parameters") + } + return *(*rtlUserProcessParameters32)(unsafe.Pointer(&buf[0])), nil +} + +func getUserProcessParams64(handle windows.Handle) (rtlUserProcessParameters64, error) { + pebAddress, err := queryPebAddress(syscall.Handle(handle), false) + if err != nil { + return rtlUserProcessParameters64{}, fmt.Errorf("cannot locate process PEB: %w", err) + } + + buf := readProcessMemory(syscall.Handle(handle), false, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock64{}))) + if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock64{})) { + return rtlUserProcessParameters64{}, fmt.Errorf("cannot read process PEB") + } + peb := (*processEnvironmentBlock64)(unsafe.Pointer(&buf[0])) + userProcessAddress := peb.ProcessParameters + buf = readProcessMemory(syscall.Handle(handle), false, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters64{}))) + if len(buf) != int(unsafe.Sizeof(rtlUserProcessParameters64{})) { + return rtlUserProcessParameters64{}, fmt.Errorf("cannot read user process parameters") + } + return *(*rtlUserProcessParameters64)(unsafe.Pointer(&buf[0])), nil +} + +func is32BitProcess(h windows.Handle) bool { + const ( + PROCESSOR_ARCHITECTURE_INTEL = 0 + PROCESSOR_ARCHITECTURE_ARM = 5 + PROCESSOR_ARCHITECTURE_ARM64 = 12 + PROCESSOR_ARCHITECTURE_IA64 = 6 + PROCESSOR_ARCHITECTURE_AMD64 = 9 + ) + + var procIs32Bits bool + switch processorArchitecture { + case PROCESSOR_ARCHITECTURE_INTEL, PROCESSOR_ARCHITECTURE_ARM: + procIs32Bits = true + case PROCESSOR_ARCHITECTURE_ARM64, PROCESSOR_ARCHITECTURE_IA64, PROCESSOR_ARCHITECTURE_AMD64: + var wow64 uint + + ret, _, _ := common.ProcNtQueryInformationProcess.Call( + uintptr(h), + uintptr(common.ProcessWow64Information), + uintptr(unsafe.Pointer(&wow64)), + uintptr(unsafe.Sizeof(wow64)), + uintptr(0), + ) + if int(ret) >= 0 { + if wow64 != 0 { + procIs32Bits = true + } + } else { + // if the OS does not support the call, we fallback into the bitness of the app + if unsafe.Sizeof(wow64) == 4 { + procIs32Bits = true + } + } + + default: + // for other unknown platforms, we rely on process platform + if unsafe.Sizeof(processorArchitecture) == 8 { + procIs32Bits = false + } else { + procIs32Bits = true + } + } + return procIs32Bits +} + +func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, error) { + h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid)) + if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER { + return nil, nil + } + if err != nil { + return nil, err + } + defer syscall.CloseHandle(syscall.Handle(h)) + + procIs32Bits := is32BitProcess(h) + + var processParameterBlockAddress uint64 + + if procIs32Bits { + peb, err := getUserProcessParams32(h) + if err != nil { + return nil, err + } + processParameterBlockAddress = uint64(peb.EnvironmentAddress) + } else { + peb, err := getUserProcessParams64(h) + if err != nil { + return nil, err + } + processParameterBlockAddress = peb.EnvironmentAddress + } + envvarScanner := bufio.NewScanner(&processReader{ + processHandle: h, + is32BitProcess: procIs32Bits, + offset: processParameterBlockAddress, + }) + envvarScanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + // Check for UTF-16 zero character + for i := 0; i < len(data)-1; i += 2 { + if data[i] == 0 && data[i+1] == 0 { + return i + 2, data[0:i], nil + } + } + if atEOF { + return len(data), data, nil + } + // Request more data + return 0, nil, nil + }) + var envVars []string + for envvarScanner.Scan() { + entry := envvarScanner.Bytes() + if len(entry) == 0 { + break // Block is finished + } + envVars = append(envVars, convertUTF16ToString(entry)) + select { + case <-ctx.Done(): + break + default: + continue + } + } + if err := envvarScanner.Err(); err != nil { + return nil, err + } + return envVars, nil +} + +type processReader struct { + processHandle windows.Handle + is32BitProcess bool + offset uint64 +} + +func (p *processReader) Read(buf []byte) (int, error) { + processMemory := readProcessMemory(syscall.Handle(p.processHandle), p.is32BitProcess, p.offset, uint(len(buf))) + if len(processMemory) == 0 { + return 0, io.EOF + } + copy(buf, processMemory) + p.offset += uint64(len(processMemory)) + return len(processMemory), nil +} + +func getProcessCommandLine(pid int32) (string, error) { + h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid)) + if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER { + return "", nil + } + if err != nil { + return "", err + } + defer syscall.CloseHandle(syscall.Handle(h)) + + procIs32Bits := is32BitProcess(h) + + if procIs32Bits { + userProcParams, err := getUserProcessParams32(h) + if err != nil { + return "", err + } + if userProcParams.CommandLineLength > 0 { + cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CommandLineAddress), uint(userProcParams.CommandLineLength)) + if len(cmdLine) != int(userProcParams.CommandLineLength) { + return "", errors.New("cannot read cmdline") + } + + return convertUTF16ToString(cmdLine), nil + } + } else { + userProcParams, err := getUserProcessParams64(h) + if err != nil { + return "", err + } + if userProcParams.CommandLineLength > 0 { + cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CommandLineAddress, uint(userProcParams.CommandLineLength)) + if len(cmdLine) != int(userProcParams.CommandLineLength) { + return "", errors.New("cannot read cmdline") + } + + return convertUTF16ToString(cmdLine), nil + } + } + + // if we reach here, we have no command line + return "", nil +} + +func convertUTF16ToString(src []byte) string { + srcLen := len(src) / 2 + + codePoints := make([]uint16, srcLen) + + srcIdx := 0 + for i := 0; i < srcLen; i++ { + codePoints[i] = uint16(src[srcIdx]) | uint16(src[srcIdx+1])<<8 + srcIdx += 2 + } + return syscall.UTF16ToString(codePoints) +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_windows_32bit.go b/vendor/github.com/shirou/gopsutil/v3/process/process_windows_32bit.go new file mode 100644 index 000000000..db4d45334 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_windows_32bit.go @@ -0,0 +1,108 @@ +//go:build (windows && 386) || (windows && arm) +// +build windows,386 windows,arm + +package process + +import ( + "errors" + "syscall" + "unsafe" + + "github.com/shirou/gopsutil/v3/internal/common" + "golang.org/x/sys/windows" +) + +type PROCESS_MEMORY_COUNTERS struct { + CB uint32 + PageFaultCount uint32 + PeakWorkingSetSize uint32 + WorkingSetSize uint32 + QuotaPeakPagedPoolUsage uint32 + QuotaPagedPoolUsage uint32 + QuotaPeakNonPagedPoolUsage uint32 + QuotaNonPagedPoolUsage uint32 + PagefileUsage uint32 + PeakPagefileUsage uint32 +} + +func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, error) { + if is32BitProcess { + // we are on a 32-bit process reading an external 32-bit process + var info processBasicInformation32 + + ret, _, _ := common.ProcNtQueryInformationProcess.Call( + uintptr(procHandle), + uintptr(common.ProcessBasicInformation), + uintptr(unsafe.Pointer(&info)), + uintptr(unsafe.Sizeof(info)), + uintptr(0), + ) + if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS { + return uint64(info.PebBaseAddress), nil + } else { + return 0, windows.NTStatus(ret) + } + } else { + // we are on a 32-bit process reading an external 64-bit process + if common.ProcNtWow64QueryInformationProcess64.Find() == nil { // avoid panic + var info processBasicInformation64 + + ret, _, _ := common.ProcNtWow64QueryInformationProcess64.Call( + uintptr(procHandle), + uintptr(common.ProcessBasicInformation), + uintptr(unsafe.Pointer(&info)), + uintptr(unsafe.Sizeof(info)), + uintptr(0), + ) + if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS { + return info.PebBaseAddress, nil + } else { + return 0, windows.NTStatus(ret) + } + } else { + return 0, errors.New("can't find API to query 64 bit process from 32 bit") + } + } +} + +func readProcessMemory(h syscall.Handle, is32BitProcess bool, address uint64, size uint) []byte { + if is32BitProcess { + var read uint + + buffer := make([]byte, size) + + ret, _, _ := common.ProcNtReadVirtualMemory.Call( + uintptr(h), + uintptr(address), + uintptr(unsafe.Pointer(&buffer[0])), + uintptr(size), + uintptr(unsafe.Pointer(&read)), + ) + if int(ret) >= 0 && read > 0 { + return buffer[:read] + } + } else { + // reading a 64-bit process from a 32-bit one + if common.ProcNtWow64ReadVirtualMemory64.Find() == nil { // avoid panic + var read uint64 + + buffer := make([]byte, size) + + ret, _, _ := common.ProcNtWow64ReadVirtualMemory64.Call( + uintptr(h), + uintptr(address&0xFFFFFFFF), // the call expects a 64-bit value + uintptr(address>>32), + uintptr(unsafe.Pointer(&buffer[0])), + uintptr(size), // the call expects a 64-bit value + uintptr(0), // but size is 32-bit so pass zero as the high dword + uintptr(unsafe.Pointer(&read)), + ) + if int(ret) >= 0 && read > 0 { + return buffer[:uint(read)] + } + } + } + + // if we reach here, an error happened + return nil +} diff --git a/vendor/github.com/shirou/gopsutil/v3/process/process_windows_64bit.go b/vendor/github.com/shirou/gopsutil/v3/process/process_windows_64bit.go new file mode 100644 index 000000000..74c6212cf --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/v3/process/process_windows_64bit.go @@ -0,0 +1,79 @@ +//go:build (windows && amd64) || (windows && arm64) +// +build windows,amd64 windows,arm64 + +package process + +import ( + "syscall" + "unsafe" + + "github.com/shirou/gopsutil/v3/internal/common" + "golang.org/x/sys/windows" +) + +type PROCESS_MEMORY_COUNTERS struct { + CB uint32 + PageFaultCount uint32 + PeakWorkingSetSize uint64 + WorkingSetSize uint64 + QuotaPeakPagedPoolUsage uint64 + QuotaPagedPoolUsage uint64 + QuotaPeakNonPagedPoolUsage uint64 + QuotaNonPagedPoolUsage uint64 + PagefileUsage uint64 + PeakPagefileUsage uint64 +} + +func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, error) { + if is32BitProcess { + // we are on a 64-bit process reading an external 32-bit process + var wow64 uint + + ret, _, _ := common.ProcNtQueryInformationProcess.Call( + uintptr(procHandle), + uintptr(common.ProcessWow64Information), + uintptr(unsafe.Pointer(&wow64)), + uintptr(unsafe.Sizeof(wow64)), + uintptr(0), + ) + if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS { + return uint64(wow64), nil + } else { + return 0, windows.NTStatus(ret) + } + } else { + // we are on a 64-bit process reading an external 64-bit process + var info processBasicInformation64 + + ret, _, _ := common.ProcNtQueryInformationProcess.Call( + uintptr(procHandle), + uintptr(common.ProcessBasicInformation), + uintptr(unsafe.Pointer(&info)), + uintptr(unsafe.Sizeof(info)), + uintptr(0), + ) + if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS { + return info.PebBaseAddress, nil + } else { + return 0, windows.NTStatus(ret) + } + } +} + +func readProcessMemory(procHandle syscall.Handle, _ bool, address uint64, size uint) []byte { + var read uint + + buffer := make([]byte, size) + + ret, _, _ := common.ProcNtReadVirtualMemory.Call( + uintptr(procHandle), + uintptr(address), + uintptr(unsafe.Pointer(&buffer[0])), + uintptr(size), + uintptr(unsafe.Pointer(&read)), + ) + if int(ret) >= 0 && read > 0 { + return buffer[:read] + } + return nil +} diff --git a/vendor/github.com/shoenig/go-m1cpu/.golangci.yaml b/vendor/github.com/shoenig/go-m1cpu/.golangci.yaml new file mode 100644 index 000000000..dc6fefb97 --- /dev/null +++ b/vendor/github.com/shoenig/go-m1cpu/.golangci.yaml @@ -0,0 +1,12 @@ +run: + timeout: 5m +linters: + enable: + - gofmt + - errcheck + - errname + - errorlint + - bodyclose + - durationcheck + - whitespace + diff --git a/vendor/github.com/shoenig/go-m1cpu/LICENSE b/vendor/github.com/shoenig/go-m1cpu/LICENSE new file mode 100644 index 000000000..e87a115e4 --- /dev/null +++ b/vendor/github.com/shoenig/go-m1cpu/LICENSE @@ -0,0 +1,363 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. "Contributor" + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. "Contributor Version" + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + +1.6. "Executable Form" + + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + + means a work that combines Covered Software with other material, in a + separate file or files, that is not Covered Software. + +1.8. "License" + + means this document. + +1.9. "Licensable" + + means having the right to grant, to the maximum extent possible, whether + at the time of the initial grant or subsequently, any and all of the + rights conveyed by this License. + +1.10. "Modifications" + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. "Patent Claims" of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. "Secondary License" + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. "Source Code Form" + + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of + its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, + judicial order, or regulation then You must: (a) comply with the terms of + this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be placed in a + text file included with all distributions of the Covered Software under + this License. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing + basis, if such Contributor fails to notify You of the non-compliance by + some reasonable means prior to 60 days after You have come back into + compliance. Moreover, Your grants from a particular Contributor are + reinstated on an ongoing basis if such Contributor notifies You of the + non-compliance by some reasonable means, this is the first time You have + received notice of non-compliance with this License from such + Contributor, and You become compliant prior to 30 days after Your receipt + of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, + without warranty of any kind, either expressed, implied, or statutory, + including, without limitation, warranties that the Covered Software is free + of defects, merchantable, fit for a particular purpose or non-infringing. + The entire risk as to the quality and performance of the Covered Software + is with You. Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary servicing, + repair, or correction. This disclaimer of warranty constitutes an essential + part of this License. No use of any Covered Software is authorized under + this License except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts + of a jurisdiction where the defendant maintains its principal place of + business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. Nothing + in this Section shall prevent a party's ability to bring cross-claims or + counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall not + be used to construe this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this License must be + attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + + This Source Code Form is "Incompatible + With Secondary Licenses", as defined by + the Mozilla Public License, v. 2.0. + diff --git a/vendor/github.com/shoenig/go-m1cpu/Makefile b/vendor/github.com/shoenig/go-m1cpu/Makefile new file mode 100644 index 000000000..28d786397 --- /dev/null +++ b/vendor/github.com/shoenig/go-m1cpu/Makefile @@ -0,0 +1,12 @@ +SHELL = bash + +default: test + +.PHONY: test +test: + @echo "--> Running Tests ..." + @go test -v -race ./... + +vet: + @echo "--> Vet Go sources ..." + @go vet ./... diff --git a/vendor/github.com/shoenig/go-m1cpu/README.md b/vendor/github.com/shoenig/go-m1cpu/README.md new file mode 100644 index 000000000..399657acf --- /dev/null +++ b/vendor/github.com/shoenig/go-m1cpu/README.md @@ -0,0 +1,66 @@ +# m1cpu + +[![Go Reference](https://pkg.go.dev/badge/github.com/shoenig/go-m1cpu.svg)](https://pkg.go.dev/github.com/shoenig/go-m1cpu) +[![MPL License](https://img.shields.io/github/license/shoenig/go-m1cpu?color=g&style=flat-square)](https://github.com/shoenig/go-m1cpu/blob/main/LICENSE) +[![Run CI Tests](https://github.com/shoenig/go-m1cpu/actions/workflows/ci.yaml/badge.svg)](https://github.com/shoenig/go-m1cpu/actions/workflows/ci.yaml) + +The `go-m1cpu` module is a library for inspecting Apple Silicon CPUs in Go. + +Use the `m1cpu` Go package for looking up the CPU frequency for Apple M1 and M2 CPUs. + +# Install + +```shell +go get github.com/shoenig/go-m1cpu@latest +``` + +# CGO + +This package requires the use of [CGO](https://go.dev/blog/cgo). + +Extracting the CPU properties is done via Apple's [IOKit](https://developer.apple.com/documentation/iokit?language=objc) +framework, which is accessible only through system C libraries. + +# Example + +Simple Go program to print Apple Silicon M1/M2 CPU speeds. + +```go +package main + +import ( + "fmt" + + "github.com/shoenig/go-m1cpu" +) + +func main() { + fmt.Println("Apple Silicon", m1cpu.IsAppleSilicon()) + + fmt.Println("pCore GHz", m1cpu.PCoreGHz()) + fmt.Println("eCore GHz", m1cpu.ECoreGHz()) + + fmt.Println("pCore Hz", m1cpu.PCoreHz()) + fmt.Println("eCore Hz", m1cpu.ECoreHz()) +} +``` + +Using `go test` to print out available information. + +``` +âžœ go test -v -run Show +=== RUN Test_Show + cpu_test.go:42: pCore Hz 3504000000 + cpu_test.go:43: eCore Hz 2424000000 + cpu_test.go:44: pCore GHz 3.504 + cpu_test.go:45: eCore GHz 2.424 + cpu_test.go:46: pCore count 8 + cpu_test.go:47: eCoreCount 4 + cpu_test.go:50: pCore Caches 196608 131072 16777216 + cpu_test.go:53: eCore Caches 131072 65536 4194304 +--- PASS: Test_Show (0.00s) +``` + +# License + +Open source under the [MPL](LICENSE) diff --git a/vendor/github.com/shoenig/go-m1cpu/cpu.go b/vendor/github.com/shoenig/go-m1cpu/cpu.go new file mode 100644 index 000000000..502a8cce9 --- /dev/null +++ b/vendor/github.com/shoenig/go-m1cpu/cpu.go @@ -0,0 +1,213 @@ +//go:build darwin && arm64 && cgo + +package m1cpu + +// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit +// #include +// #include +// #include +// #include +// +// #if !defined(MAC_OS_VERSION_12_0) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0 +// #define kIOMainPortDefault kIOMasterPortDefault +// #endif +// +// #define HzToGHz(hz) ((hz) / 1000000000.0) +// +// UInt64 global_pCoreHz; +// UInt64 global_eCoreHz; +// int global_pCoreCount; +// int global_eCoreCount; +// int global_pCoreL1InstCacheSize; +// int global_eCoreL1InstCacheSize; +// int global_pCoreL1DataCacheSize; +// int global_eCoreL1DataCacheSize; +// int global_pCoreL2CacheSize; +// int global_eCoreL2CacheSize; +// char global_brand[32]; +// +// UInt64 getFrequency(CFTypeRef typeRef) { +// CFDataRef cfData = typeRef; +// +// CFIndex size = CFDataGetLength(cfData); +// UInt8 buf[size]; +// CFDataGetBytes(cfData, CFRangeMake(0, size), buf); +// +// UInt8 b1 = buf[size-5]; +// UInt8 b2 = buf[size-6]; +// UInt8 b3 = buf[size-7]; +// UInt8 b4 = buf[size-8]; +// +// UInt64 pCoreHz = 0x00000000FFFFFFFF & ((b1<<24) | (b2 << 16) | (b3 << 8) | (b4)); +// return pCoreHz; +// } +// +// int sysctl_int(const char * name) { +// int value = -1; +// size_t size = 8; +// sysctlbyname(name, &value, &size, NULL, 0); +// return value; +// } +// +// void sysctl_string(const char * name, char * dest) { +// size_t size = 32; +// sysctlbyname(name, dest, &size, NULL, 0); +// } +// +// void initialize() { +// global_pCoreCount = sysctl_int("hw.perflevel0.physicalcpu"); +// global_eCoreCount = sysctl_int("hw.perflevel1.physicalcpu"); +// global_pCoreL1InstCacheSize = sysctl_int("hw.perflevel0.l1icachesize"); +// global_eCoreL1InstCacheSize = sysctl_int("hw.perflevel1.l1icachesize"); +// global_pCoreL1DataCacheSize = sysctl_int("hw.perflevel0.l1dcachesize"); +// global_eCoreL1DataCacheSize = sysctl_int("hw.perflevel1.l1dcachesize"); +// global_pCoreL2CacheSize = sysctl_int("hw.perflevel0.l2cachesize"); +// global_eCoreL2CacheSize = sysctl_int("hw.perflevel1.l2cachesize"); +// sysctl_string("machdep.cpu.brand_string", global_brand); +// +// CFMutableDictionaryRef matching = IOServiceMatching("AppleARMIODevice"); +// io_iterator_t iter; +// IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iter); +// +// const size_t bufsize = 512; +// io_object_t obj; +// while ((obj = IOIteratorNext(iter))) { +// char class[bufsize]; +// IOObjectGetClass(obj, class); +// char name[bufsize]; +// IORegistryEntryGetName(obj, name); +// +// if (strncmp(name, "pmgr", bufsize) == 0) { +// CFTypeRef pCoreRef = IORegistryEntryCreateCFProperty(obj, CFSTR("voltage-states5-sram"), kCFAllocatorDefault, 0); +// CFTypeRef eCoreRef = IORegistryEntryCreateCFProperty(obj, CFSTR("voltage-states1-sram"), kCFAllocatorDefault, 0); +// +// long long pCoreHz = getFrequency(pCoreRef); +// long long eCoreHz = getFrequency(eCoreRef); +// +// global_pCoreHz = pCoreHz; +// global_eCoreHz = eCoreHz; +// return; +// } +// } +// } +// +// UInt64 eCoreHz() { +// return global_eCoreHz; +// } +// +// UInt64 pCoreHz() { +// return global_pCoreHz; +// } +// +// Float64 eCoreGHz() { +// return HzToGHz(global_eCoreHz); +// } +// +// Float64 pCoreGHz() { +// return HzToGHz(global_pCoreHz); +// } +// +// int pCoreCount() { +// return global_pCoreCount; +// } +// +// int eCoreCount() { +// return global_eCoreCount; +// } +// +// int pCoreL1InstCacheSize() { +// return global_pCoreL1InstCacheSize; +// } +// +// int pCoreL1DataCacheSize() { +// return global_pCoreL1DataCacheSize; +// } +// +// int pCoreL2CacheSize() { +// return global_pCoreL2CacheSize; +// } +// +// int eCoreL1InstCacheSize() { +// return global_eCoreL1InstCacheSize; +// } +// +// int eCoreL1DataCacheSize() { +// return global_eCoreL1DataCacheSize; +// } +// +// int eCoreL2CacheSize() { +// return global_eCoreL2CacheSize; +// } +// +// char * modelName() { +// return global_brand; +// } +import "C" + +func init() { + C.initialize() +} + +// IsAppleSilicon returns true on this platform. +func IsAppleSilicon() bool { + return true +} + +// PCoreHZ returns the max frequency in Hertz of the P-Core of an Apple Silicon CPU. +func PCoreHz() uint64 { + return uint64(C.pCoreHz()) +} + +// ECoreHZ returns the max frequency in Hertz of the E-Core of an Apple Silicon CPU. +func ECoreHz() uint64 { + return uint64(C.eCoreHz()) +} + +// PCoreGHz returns the max frequency in Gigahertz of the P-Core of an Apple Silicon CPU. +func PCoreGHz() float64 { + return float64(C.pCoreGHz()) +} + +// ECoreGHz returns the max frequency in Gigahertz of the E-Core of an Apple Silicon CPU. +func ECoreGHz() float64 { + return float64(C.eCoreGHz()) +} + +// PCoreCount returns the number of physical P (performance) cores. +func PCoreCount() int { + return int(C.pCoreCount()) +} + +// ECoreCount returns the number of physical E (efficiency) cores. +func ECoreCount() int { + return int(C.eCoreCount()) +} + +// PCoreCacheSize returns the sizes of the P (performance) core cache sizes +// in the order of +// +// - L1 instruction cache +// - L1 data cache +// - L2 cache +func PCoreCache() (int, int, int) { + return int(C.pCoreL1InstCacheSize()), + int(C.pCoreL1DataCacheSize()), + int(C.pCoreL2CacheSize()) +} + +// ECoreCacheSize returns the sizes of the E (efficiency) core cache sizes +// in the order of +// +// - L1 instruction cache +// - L1 data cache +// - L2 cache +func ECoreCache() (int, int, int) { + return int(C.eCoreL1InstCacheSize()), + int(C.eCoreL1DataCacheSize()), + int(C.eCoreL2CacheSize()) +} + +// ModelName returns the model name of the CPU. +func ModelName() string { + return C.GoString(C.modelName()) +} diff --git a/vendor/github.com/shoenig/go-m1cpu/incompatible.go b/vendor/github.com/shoenig/go-m1cpu/incompatible.go new file mode 100644 index 000000000..d425025aa --- /dev/null +++ b/vendor/github.com/shoenig/go-m1cpu/incompatible.go @@ -0,0 +1,53 @@ +//go:build !darwin || !arm64 || !cgo + +package m1cpu + +// IsAppleSilicon return false on this platform. +func IsAppleSilicon() bool { + return false +} + +// PCoreHZ requires darwin/arm64 +func PCoreHz() uint64 { + panic("m1cpu: not a darwin/arm64 system") +} + +// ECoreHZ requires darwin/arm64 +func ECoreHz() uint64 { + panic("m1cpu: not a darwin/arm64 system") +} + +// PCoreGHz requires darwin/arm64 +func PCoreGHz() float64 { + panic("m1cpu: not a darwin/arm64 system") +} + +// ECoreGHz requires darwin/arm64 +func ECoreGHz() float64 { + panic("m1cpu: not a darwin/arm64 system") +} + +// PCoreCount requires darwin/arm64 +func PCoreCount() int { + panic("m1cpu: not a darwin/arm64 system") +} + +// ECoreCount requires darwin/arm64 +func ECoreCount() int { + panic("m1cpu: not a darwin/arm64 system") +} + +// PCoreCacheSize requires darwin/arm64 +func PCoreCache() (int, int, int) { + panic("m1cpu: not a darwin/arm64 system") +} + +// ECoreCacheSize requires darwin/arm64 +func ECoreCache() (int, int, int) { + panic("m1cpu: not a darwin/arm64 system") +} + +// ModelName requires darwin/arm64 +func ModelName() string { + panic("m1cpu: not a darwin/arm64 system") +} diff --git a/vendor/github.com/stretchr/objx/.codeclimate.yml b/vendor/github.com/stretchr/objx/.codeclimate.yml new file mode 100644 index 000000000..559fa399c --- /dev/null +++ b/vendor/github.com/stretchr/objx/.codeclimate.yml @@ -0,0 +1,21 @@ +engines: + gofmt: + enabled: true + golint: + enabled: true + govet: + enabled: true + +exclude_patterns: +- ".github/" +- "vendor/" +- "codegen/" +- "*.yml" +- ".*.yml" +- "*.md" +- "Gopkg.*" +- "doc.go" +- "type_specific_codegen_test.go" +- "type_specific_codegen.go" +- ".gitignore" +- "LICENSE" diff --git a/vendor/github.com/stretchr/objx/.gitignore b/vendor/github.com/stretchr/objx/.gitignore new file mode 100644 index 000000000..ea58090bd --- /dev/null +++ b/vendor/github.com/stretchr/objx/.gitignore @@ -0,0 +1,11 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out diff --git a/vendor/github.com/stretchr/objx/LICENSE b/vendor/github.com/stretchr/objx/LICENSE new file mode 100644 index 000000000..44d4d9d5a --- /dev/null +++ b/vendor/github.com/stretchr/objx/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2014 Stretchr, Inc. +Copyright (c) 2017-2018 objx contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/stretchr/objx/README.md b/vendor/github.com/stretchr/objx/README.md new file mode 100644 index 000000000..78dc1f8b0 --- /dev/null +++ b/vendor/github.com/stretchr/objx/README.md @@ -0,0 +1,80 @@ +# Objx +[![Build Status](https://travis-ci.org/stretchr/objx.svg?branch=master)](https://travis-ci.org/stretchr/objx) +[![Go Report Card](https://goreportcard.com/badge/github.com/stretchr/objx)](https://goreportcard.com/report/github.com/stretchr/objx) +[![Maintainability](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/maintainability)](https://codeclimate.com/github/stretchr/objx/maintainability) +[![Test Coverage](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/test_coverage)](https://codeclimate.com/github/stretchr/objx/test_coverage) +[![Sourcegraph](https://sourcegraph.com/github.com/stretchr/objx/-/badge.svg)](https://sourcegraph.com/github.com/stretchr/objx) +[![GoDoc](https://pkg.go.dev/badge/github.com/stretchr/objx?utm_source=godoc)](https://pkg.go.dev/github.com/stretchr/objx) + +Objx - Go package for dealing with maps, slices, JSON and other data. + +Get started: + +- Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date) +- Check out the API Documentation http://pkg.go.dev/github.com/stretchr/objx + +## Overview +Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc. + +### Pattern +Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going: + + m, err := objx.FromJSON(json) + +NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, the rest will be optimistic and try to figure things out without panicking. + +Use `Get` to access the value you're interested in. You can use dot and array +notation too: + + m.Get("places[0].latlng") + +Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. + + if m.Get("code").IsStr() { // Your code... } + +Or you can just assume the type, and use one of the strong type methods to extract the real value: + + m.Get("code").Int() + +If there's no value there (or if it's the wrong type) then a default value will be returned, or you can be explicit about the default value. + + Get("code").Int(-1) + +If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, manipulating and selecting that data. You can find out more by exploring the index below. + +### Reading data +A simple example of how to use Objx: + + // Use MustFromJSON to make an objx.Map from some JSON + m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) + + // Get the details + name := m.Get("name").Str() + age := m.Get("age").Int() + + // Get their nickname (or use their name if they don't have one) + nickname := m.Get("nickname").Str(name) + +### Ranging +Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For example, to `range` the data, do what you would expect: + + m := objx.MustFromJSON(json) + for key, value := range m { + // Your code... + } + +## Installation +To install Objx, use go get: + + go get github.com/stretchr/objx + +### Staying up to date +To update Objx to the latest version, run: + + go get -u github.com/stretchr/objx + +### Supported go versions +We currently support the three recent major Go versions. + +## Contributing +Please feel free to submit issues, fork the repository and send pull requests! diff --git a/vendor/github.com/stretchr/objx/Taskfile.yml b/vendor/github.com/stretchr/objx/Taskfile.yml new file mode 100644 index 000000000..8a79e8d67 --- /dev/null +++ b/vendor/github.com/stretchr/objx/Taskfile.yml @@ -0,0 +1,27 @@ +version: '3' + +tasks: + default: + deps: [test] + + lint: + desc: Checks code style + cmds: + - gofmt -d -s *.go + - go vet ./... + silent: true + + lint-fix: + desc: Fixes code style + cmds: + - gofmt -w -s *.go + + test: + desc: Runs go tests + cmds: + - go test -race ./... + + test-coverage: + desc: Runs go tests and calculates test coverage + cmds: + - go test -race -coverprofile=c.out ./... diff --git a/vendor/github.com/stretchr/objx/accessors.go b/vendor/github.com/stretchr/objx/accessors.go new file mode 100644 index 000000000..72f1d1c1c --- /dev/null +++ b/vendor/github.com/stretchr/objx/accessors.go @@ -0,0 +1,197 @@ +package objx + +import ( + "reflect" + "regexp" + "strconv" + "strings" +) + +const ( + // PathSeparator is the character used to separate the elements + // of the keypath. + // + // For example, `location.address.city` + PathSeparator string = "." + + // arrayAccessRegexString is the regex used to extract the array number + // from the access path + arrayAccessRegexString = `^(.+)\[([0-9]+)\]$` + + // mapAccessRegexString is the regex used to extract the map key + // from the access path + mapAccessRegexString = `^([^\[]*)\[([^\]]+)\](.*)$` +) + +// arrayAccessRegex is the compiled arrayAccessRegexString +var arrayAccessRegex = regexp.MustCompile(arrayAccessRegexString) + +// mapAccessRegex is the compiled mapAccessRegexString +var mapAccessRegex = regexp.MustCompile(mapAccessRegexString) + +// Get gets the value using the specified selector and +// returns it inside a new Obj object. +// +// If it cannot find the value, Get will return a nil +// value inside an instance of Obj. +// +// Get can only operate directly on map[string]interface{} and []interface. +// +// # Example +// +// To access the title of the third chapter of the second book, do: +// +// o.Get("books[1].chapters[2].title") +func (m Map) Get(selector string) *Value { + rawObj := access(m, selector, nil, false) + return &Value{data: rawObj} +} + +// Set sets the value using the specified selector and +// returns the object on which Set was called. +// +// Set can only operate directly on map[string]interface{} and []interface +// +// # Example +// +// To set the title of the third chapter of the second book, do: +// +// o.Set("books[1].chapters[2].title","Time to Go") +func (m Map) Set(selector string, value interface{}) Map { + access(m, selector, value, true) + return m +} + +// getIndex returns the index, which is hold in s by two branches. +// It also returns s without the index part, e.g. name[1] will return (1, name). +// If no index is found, -1 is returned +func getIndex(s string) (int, string) { + arrayMatches := arrayAccessRegex.FindStringSubmatch(s) + if len(arrayMatches) > 0 { + // Get the key into the map + selector := arrayMatches[1] + // Get the index into the array at the key + // We know this can't fail because arrayMatches[2] is an int for sure + index, _ := strconv.Atoi(arrayMatches[2]) + return index, selector + } + return -1, s +} + +// getKey returns the key which is held in s by two brackets. +// It also returns the next selector. +func getKey(s string) (string, string) { + selSegs := strings.SplitN(s, PathSeparator, 2) + thisSel := selSegs[0] + nextSel := "" + + if len(selSegs) > 1 { + nextSel = selSegs[1] + } + + mapMatches := mapAccessRegex.FindStringSubmatch(s) + if len(mapMatches) > 0 { + if _, err := strconv.Atoi(mapMatches[2]); err != nil { + thisSel = mapMatches[1] + nextSel = "[" + mapMatches[2] + "]" + mapMatches[3] + + if thisSel == "" { + thisSel = mapMatches[2] + nextSel = mapMatches[3] + } + + if nextSel == "" { + selSegs = []string{"", ""} + } else if nextSel[0] == '.' { + nextSel = nextSel[1:] + } + } + } + + return thisSel, nextSel +} + +// access accesses the object using the selector and performs the +// appropriate action. +func access(current interface{}, selector string, value interface{}, isSet bool) interface{} { + thisSel, nextSel := getKey(selector) + + indexes := []int{} + for strings.Contains(thisSel, "[") { + prevSel := thisSel + index := -1 + index, thisSel = getIndex(thisSel) + indexes = append(indexes, index) + if prevSel == thisSel { + break + } + } + + if curMap, ok := current.(Map); ok { + current = map[string]interface{}(curMap) + } + // get the object in question + switch current.(type) { + case map[string]interface{}: + curMSI := current.(map[string]interface{}) + if nextSel == "" && isSet { + curMSI[thisSel] = value + return nil + } + + _, ok := curMSI[thisSel].(map[string]interface{}) + if !ok { + _, ok = curMSI[thisSel].(Map) + } + + if (curMSI[thisSel] == nil || !ok) && len(indexes) == 0 && isSet { + curMSI[thisSel] = map[string]interface{}{} + } + + current = curMSI[thisSel] + default: + current = nil + } + + // do we need to access the item of an array? + if len(indexes) > 0 { + num := len(indexes) + for num > 0 { + num-- + index := indexes[num] + indexes = indexes[:num] + if array, ok := interSlice(current); ok { + if index < len(array) { + current = array[index] + } else { + current = nil + break + } + } + } + } + + if nextSel != "" { + current = access(current, nextSel, value, isSet) + } + return current +} + +func interSlice(slice interface{}) ([]interface{}, bool) { + if array, ok := slice.([]interface{}); ok { + return array, ok + } + + s := reflect.ValueOf(slice) + if s.Kind() != reflect.Slice { + return nil, false + } + + ret := make([]interface{}, s.Len()) + + for i := 0; i < s.Len(); i++ { + ret[i] = s.Index(i).Interface() + } + + return ret, true +} diff --git a/vendor/github.com/stretchr/objx/conversions.go b/vendor/github.com/stretchr/objx/conversions.go new file mode 100644 index 000000000..01c63d7d3 --- /dev/null +++ b/vendor/github.com/stretchr/objx/conversions.go @@ -0,0 +1,280 @@ +package objx + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "net/url" + "strconv" +) + +// SignatureSeparator is the character that is used to +// separate the Base64 string from the security signature. +const SignatureSeparator = "_" + +// URLValuesSliceKeySuffix is the character that is used to +// specify a suffix for slices parsed by URLValues. +// If the suffix is set to "[i]", then the index of the slice +// is used in place of i +// Ex: Suffix "[]" would have the form a[]=b&a[]=c +// OR Suffix "[i]" would have the form a[0]=b&a[1]=c +// OR Suffix "" would have the form a=b&a=c +var urlValuesSliceKeySuffix = "[]" + +const ( + URLValuesSliceKeySuffixEmpty = "" + URLValuesSliceKeySuffixArray = "[]" + URLValuesSliceKeySuffixIndex = "[i]" +) + +// SetURLValuesSliceKeySuffix sets the character that is used to +// specify a suffix for slices parsed by URLValues. +// If the suffix is set to "[i]", then the index of the slice +// is used in place of i +// Ex: Suffix "[]" would have the form a[]=b&a[]=c +// OR Suffix "[i]" would have the form a[0]=b&a[1]=c +// OR Suffix "" would have the form a=b&a=c +func SetURLValuesSliceKeySuffix(s string) error { + if s == URLValuesSliceKeySuffixEmpty || s == URLValuesSliceKeySuffixArray || s == URLValuesSliceKeySuffixIndex { + urlValuesSliceKeySuffix = s + return nil + } + + return errors.New("objx: Invalid URLValuesSliceKeySuffix provided.") +} + +// JSON converts the contained object to a JSON string +// representation +func (m Map) JSON() (string, error) { + for k, v := range m { + m[k] = cleanUp(v) + } + + result, err := json.Marshal(m) + if err != nil { + err = errors.New("objx: JSON encode failed with: " + err.Error()) + } + return string(result), err +} + +func cleanUpInterfaceArray(in []interface{}) []interface{} { + result := make([]interface{}, len(in)) + for i, v := range in { + result[i] = cleanUp(v) + } + return result +} + +func cleanUpInterfaceMap(in map[interface{}]interface{}) Map { + result := Map{} + for k, v := range in { + result[fmt.Sprintf("%v", k)] = cleanUp(v) + } + return result +} + +func cleanUpStringMap(in map[string]interface{}) Map { + result := Map{} + for k, v := range in { + result[k] = cleanUp(v) + } + return result +} + +func cleanUpMSIArray(in []map[string]interface{}) []Map { + result := make([]Map, len(in)) + for i, v := range in { + result[i] = cleanUpStringMap(v) + } + return result +} + +func cleanUpMapArray(in []Map) []Map { + result := make([]Map, len(in)) + for i, v := range in { + result[i] = cleanUpStringMap(v) + } + return result +} + +func cleanUp(v interface{}) interface{} { + switch v := v.(type) { + case []interface{}: + return cleanUpInterfaceArray(v) + case []map[string]interface{}: + return cleanUpMSIArray(v) + case map[interface{}]interface{}: + return cleanUpInterfaceMap(v) + case Map: + return cleanUpStringMap(v) + case []Map: + return cleanUpMapArray(v) + default: + return v + } +} + +// MustJSON converts the contained object to a JSON string +// representation and panics if there is an error +func (m Map) MustJSON() string { + result, err := m.JSON() + if err != nil { + panic(err.Error()) + } + return result +} + +// Base64 converts the contained object to a Base64 string +// representation of the JSON string representation +func (m Map) Base64() (string, error) { + var buf bytes.Buffer + + jsonData, err := m.JSON() + if err != nil { + return "", err + } + + encoder := base64.NewEncoder(base64.StdEncoding, &buf) + _, _ = encoder.Write([]byte(jsonData)) + _ = encoder.Close() + + return buf.String(), nil +} + +// MustBase64 converts the contained object to a Base64 string +// representation of the JSON string representation and panics +// if there is an error +func (m Map) MustBase64() string { + result, err := m.Base64() + if err != nil { + panic(err.Error()) + } + return result +} + +// SignedBase64 converts the contained object to a Base64 string +// representation of the JSON string representation and signs it +// using the provided key. +func (m Map) SignedBase64(key string) (string, error) { + base64, err := m.Base64() + if err != nil { + return "", err + } + + sig := HashWithKey(base64, key) + return base64 + SignatureSeparator + sig, nil +} + +// MustSignedBase64 converts the contained object to a Base64 string +// representation of the JSON string representation and signs it +// using the provided key and panics if there is an error +func (m Map) MustSignedBase64(key string) string { + result, err := m.SignedBase64(key) + if err != nil { + panic(err.Error()) + } + return result +} + +/* + URL Query + ------------------------------------------------ +*/ + +// URLValues creates a url.Values object from an Obj. This +// function requires that the wrapped object be a map[string]interface{} +func (m Map) URLValues() url.Values { + vals := make(url.Values) + + m.parseURLValues(m, vals, "") + + return vals +} + +func (m Map) parseURLValues(queryMap Map, vals url.Values, key string) { + useSliceIndex := false + if urlValuesSliceKeySuffix == "[i]" { + useSliceIndex = true + } + + for k, v := range queryMap { + val := &Value{data: v} + switch { + case val.IsObjxMap(): + if key == "" { + m.parseURLValues(val.ObjxMap(), vals, k) + } else { + m.parseURLValues(val.ObjxMap(), vals, key+"["+k+"]") + } + case val.IsObjxMapSlice(): + sliceKey := k + if key != "" { + sliceKey = key + "[" + k + "]" + } + + if useSliceIndex { + for i, sv := range val.MustObjxMapSlice() { + sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" + m.parseURLValues(sv, vals, sk) + } + } else { + sliceKey = sliceKey + urlValuesSliceKeySuffix + for _, sv := range val.MustObjxMapSlice() { + m.parseURLValues(sv, vals, sliceKey) + } + } + case val.IsMSISlice(): + sliceKey := k + if key != "" { + sliceKey = key + "[" + k + "]" + } + + if useSliceIndex { + for i, sv := range val.MustMSISlice() { + sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" + m.parseURLValues(New(sv), vals, sk) + } + } else { + sliceKey = sliceKey + urlValuesSliceKeySuffix + for _, sv := range val.MustMSISlice() { + m.parseURLValues(New(sv), vals, sliceKey) + } + } + case val.IsStrSlice(), val.IsBoolSlice(), + val.IsFloat32Slice(), val.IsFloat64Slice(), + val.IsIntSlice(), val.IsInt8Slice(), val.IsInt16Slice(), val.IsInt32Slice(), val.IsInt64Slice(), + val.IsUintSlice(), val.IsUint8Slice(), val.IsUint16Slice(), val.IsUint32Slice(), val.IsUint64Slice(): + + sliceKey := k + if key != "" { + sliceKey = key + "[" + k + "]" + } + + if useSliceIndex { + for i, sv := range val.StringSlice() { + sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" + vals.Set(sk, sv) + } + } else { + sliceKey = sliceKey + urlValuesSliceKeySuffix + vals[sliceKey] = val.StringSlice() + } + + default: + if key == "" { + vals.Set(k, val.String()) + } else { + vals.Set(key+"["+k+"]", val.String()) + } + } + } +} + +// URLQuery gets an encoded URL query representing the given +// Obj. This function requires that the wrapped object be a +// map[string]interface{} +func (m Map) URLQuery() (string, error) { + return m.URLValues().Encode(), nil +} diff --git a/vendor/github.com/stretchr/objx/doc.go b/vendor/github.com/stretchr/objx/doc.go new file mode 100644 index 000000000..b170af74b --- /dev/null +++ b/vendor/github.com/stretchr/objx/doc.go @@ -0,0 +1,66 @@ +/* +Package objx provides utilities for dealing with maps, slices, JSON and other data. + +# Overview + +Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes +a powerful `Get` method (among others) that allows you to easily and quickly get +access to data within the map, without having to worry too much about type assertions, +missing data, default values etc. + +# Pattern + +Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. +Call one of the `objx.` functions to create your `objx.Map` to get going: + + m, err := objx.FromJSON(json) + +NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, +the rest will be optimistic and try to figure things out without panicking. + +Use `Get` to access the value you're interested in. You can use dot and array +notation too: + + m.Get("places[0].latlng") + +Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. + + if m.Get("code").IsStr() { // Your code... } + +Or you can just assume the type, and use one of the strong type methods to extract the real value: + + m.Get("code").Int() + +If there's no value there (or if it's the wrong type) then a default value will be returned, +or you can be explicit about the default value. + + Get("code").Int(-1) + +If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, +manipulating and selecting that data. You can find out more by exploring the index below. + +# Reading data + +A simple example of how to use Objx: + + // Use MustFromJSON to make an objx.Map from some JSON + m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) + + // Get the details + name := m.Get("name").Str() + age := m.Get("age").Int() + + // Get their nickname (or use their name if they don't have one) + nickname := m.Get("nickname").Str(name) + +# Ranging + +Since `objx.Map` is a `map[string]interface{}` you can treat it as such. +For example, to `range` the data, do what you would expect: + + m := objx.MustFromJSON(json) + for key, value := range m { + // Your code... + } +*/ +package objx diff --git a/vendor/github.com/stretchr/objx/map.go b/vendor/github.com/stretchr/objx/map.go new file mode 100644 index 000000000..ab9f9ae67 --- /dev/null +++ b/vendor/github.com/stretchr/objx/map.go @@ -0,0 +1,214 @@ +package objx + +import ( + "encoding/base64" + "encoding/json" + "errors" + "io/ioutil" + "net/url" + "strings" +) + +// MSIConvertable is an interface that defines methods for converting your +// custom types to a map[string]interface{} representation. +type MSIConvertable interface { + // MSI gets a map[string]interface{} (msi) representing the + // object. + MSI() map[string]interface{} +} + +// Map provides extended functionality for working with +// untyped data, in particular map[string]interface (msi). +type Map map[string]interface{} + +// Value returns the internal value instance +func (m Map) Value() *Value { + return &Value{data: m} +} + +// Nil represents a nil Map. +var Nil = New(nil) + +// New creates a new Map containing the map[string]interface{} in the data argument. +// If the data argument is not a map[string]interface, New attempts to call the +// MSI() method on the MSIConvertable interface to create one. +func New(data interface{}) Map { + if _, ok := data.(map[string]interface{}); !ok { + if converter, ok := data.(MSIConvertable); ok { + data = converter.MSI() + } else { + return nil + } + } + return Map(data.(map[string]interface{})) +} + +// MSI creates a map[string]interface{} and puts it inside a new Map. +// +// The arguments follow a key, value pattern. +// +// Returns nil if any key argument is non-string or if there are an odd number of arguments. +// +// # Example +// +// To easily create Maps: +// +// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true)) +// +// // creates an Map equivalent to +// m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}} +func MSI(keyAndValuePairs ...interface{}) Map { + newMap := Map{} + keyAndValuePairsLen := len(keyAndValuePairs) + if keyAndValuePairsLen%2 != 0 { + return nil + } + for i := 0; i < keyAndValuePairsLen; i = i + 2 { + key := keyAndValuePairs[i] + value := keyAndValuePairs[i+1] + + // make sure the key is a string + keyString, keyStringOK := key.(string) + if !keyStringOK { + return nil + } + newMap[keyString] = value + } + return newMap +} + +// ****** Conversion Constructors + +// MustFromJSON creates a new Map containing the data specified in the +// jsonString. +// +// Panics if the JSON is invalid. +func MustFromJSON(jsonString string) Map { + o, err := FromJSON(jsonString) + if err != nil { + panic("objx: MustFromJSON failed with error: " + err.Error()) + } + return o +} + +// MustFromJSONSlice creates a new slice of Map containing the data specified in the +// jsonString. Works with jsons with a top level array +// +// Panics if the JSON is invalid. +func MustFromJSONSlice(jsonString string) []Map { + slice, err := FromJSONSlice(jsonString) + if err != nil { + panic("objx: MustFromJSONSlice failed with error: " + err.Error()) + } + return slice +} + +// FromJSON creates a new Map containing the data specified in the +// jsonString. +// +// Returns an error if the JSON is invalid. +func FromJSON(jsonString string) (Map, error) { + var m Map + err := json.Unmarshal([]byte(jsonString), &m) + if err != nil { + return Nil, err + } + return m, nil +} + +// FromJSONSlice creates a new slice of Map containing the data specified in the +// jsonString. Works with jsons with a top level array +// +// Returns an error if the JSON is invalid. +func FromJSONSlice(jsonString string) ([]Map, error) { + var slice []Map + err := json.Unmarshal([]byte(jsonString), &slice) + if err != nil { + return nil, err + } + return slice, nil +} + +// FromBase64 creates a new Obj containing the data specified +// in the Base64 string. +// +// The string is an encoded JSON string returned by Base64 +func FromBase64(base64String string) (Map, error) { + decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String)) + decoded, err := ioutil.ReadAll(decoder) + if err != nil { + return nil, err + } + return FromJSON(string(decoded)) +} + +// MustFromBase64 creates a new Obj containing the data specified +// in the Base64 string and panics if there is an error. +// +// The string is an encoded JSON string returned by Base64 +func MustFromBase64(base64String string) Map { + result, err := FromBase64(base64String) + if err != nil { + panic("objx: MustFromBase64 failed with error: " + err.Error()) + } + return result +} + +// FromSignedBase64 creates a new Obj containing the data specified +// in the Base64 string. +// +// The string is an encoded JSON string returned by SignedBase64 +func FromSignedBase64(base64String, key string) (Map, error) { + parts := strings.Split(base64String, SignatureSeparator) + if len(parts) != 2 { + return nil, errors.New("objx: Signed base64 string is malformed") + } + + sig := HashWithKey(parts[0], key) + if parts[1] != sig { + return nil, errors.New("objx: Signature for base64 data does not match") + } + return FromBase64(parts[0]) +} + +// MustFromSignedBase64 creates a new Obj containing the data specified +// in the Base64 string and panics if there is an error. +// +// The string is an encoded JSON string returned by Base64 +func MustFromSignedBase64(base64String, key string) Map { + result, err := FromSignedBase64(base64String, key) + if err != nil { + panic("objx: MustFromSignedBase64 failed with error: " + err.Error()) + } + return result +} + +// FromURLQuery generates a new Obj by parsing the specified +// query. +// +// For queries with multiple values, the first value is selected. +func FromURLQuery(query string) (Map, error) { + vals, err := url.ParseQuery(query) + if err != nil { + return nil, err + } + m := Map{} + for k, vals := range vals { + m[k] = vals[0] + } + return m, nil +} + +// MustFromURLQuery generates a new Obj by parsing the specified +// query. +// +// For queries with multiple values, the first value is selected. +// +// Panics if it encounters an error +func MustFromURLQuery(query string) Map { + o, err := FromURLQuery(query) + if err != nil { + panic("objx: MustFromURLQuery failed with error: " + err.Error()) + } + return o +} diff --git a/vendor/github.com/stretchr/objx/mutations.go b/vendor/github.com/stretchr/objx/mutations.go new file mode 100644 index 000000000..c3400a3f7 --- /dev/null +++ b/vendor/github.com/stretchr/objx/mutations.go @@ -0,0 +1,77 @@ +package objx + +// Exclude returns a new Map with the keys in the specified []string +// excluded. +func (m Map) Exclude(exclude []string) Map { + excluded := make(Map) + for k, v := range m { + if !contains(exclude, k) { + excluded[k] = v + } + } + return excluded +} + +// Copy creates a shallow copy of the Obj. +func (m Map) Copy() Map { + copied := Map{} + for k, v := range m { + copied[k] = v + } + return copied +} + +// Merge blends the specified map with a copy of this map and returns the result. +// +// Keys that appear in both will be selected from the specified map. +// This method requires that the wrapped object be a map[string]interface{} +func (m Map) Merge(merge Map) Map { + return m.Copy().MergeHere(merge) +} + +// MergeHere blends the specified map with this map and returns the current map. +// +// Keys that appear in both will be selected from the specified map. The original map +// will be modified. This method requires that +// the wrapped object be a map[string]interface{} +func (m Map) MergeHere(merge Map) Map { + for k, v := range merge { + m[k] = v + } + return m +} + +// Transform builds a new Obj giving the transformer a chance +// to change the keys and values as it goes. This method requires that +// the wrapped object be a map[string]interface{} +func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map { + newMap := Map{} + for k, v := range m { + modifiedKey, modifiedVal := transformer(k, v) + newMap[modifiedKey] = modifiedVal + } + return newMap +} + +// TransformKeys builds a new map using the specified key mapping. +// +// Unspecified keys will be unaltered. +// This method requires that the wrapped object be a map[string]interface{} +func (m Map) TransformKeys(mapping map[string]string) Map { + return m.Transform(func(key string, value interface{}) (string, interface{}) { + if newKey, ok := mapping[key]; ok { + return newKey, value + } + return key, value + }) +} + +// Checks if a string slice contains a string +func contains(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} diff --git a/vendor/github.com/stretchr/objx/security.go b/vendor/github.com/stretchr/objx/security.go new file mode 100644 index 000000000..692be8e2a --- /dev/null +++ b/vendor/github.com/stretchr/objx/security.go @@ -0,0 +1,12 @@ +package objx + +import ( + "crypto/sha1" + "encoding/hex" +) + +// HashWithKey hashes the specified string using the security key +func HashWithKey(data, key string) string { + d := sha1.Sum([]byte(data + ":" + key)) + return hex.EncodeToString(d[:]) +} diff --git a/vendor/github.com/stretchr/objx/tests.go b/vendor/github.com/stretchr/objx/tests.go new file mode 100644 index 000000000..d9e0b479a --- /dev/null +++ b/vendor/github.com/stretchr/objx/tests.go @@ -0,0 +1,17 @@ +package objx + +// Has gets whether there is something at the specified selector +// or not. +// +// If m is nil, Has will always return false. +func (m Map) Has(selector string) bool { + if m == nil { + return false + } + return !m.Get(selector).IsNil() +} + +// IsNil gets whether the data is nil or not. +func (v *Value) IsNil() bool { + return v == nil || v.data == nil +} diff --git a/vendor/github.com/stretchr/objx/type_specific.go b/vendor/github.com/stretchr/objx/type_specific.go new file mode 100644 index 000000000..80f88d9fa --- /dev/null +++ b/vendor/github.com/stretchr/objx/type_specific.go @@ -0,0 +1,346 @@ +package objx + +/* + MSI (map[string]interface{} and []map[string]interface{}) +*/ + +// MSI gets the value as a map[string]interface{}, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) MSI(optionalDefault ...map[string]interface{}) map[string]interface{} { + if s, ok := v.data.(map[string]interface{}); ok { + return s + } + if s, ok := v.data.(Map); ok { + return map[string]interface{}(s) + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustMSI gets the value as a map[string]interface{}. +// +// Panics if the object is not a map[string]interface{}. +func (v *Value) MustMSI() map[string]interface{} { + if s, ok := v.data.(Map); ok { + return map[string]interface{}(s) + } + return v.data.(map[string]interface{}) +} + +// MSISlice gets the value as a []map[string]interface{}, returns the optionalDefault +// value or nil if the value is not a []map[string]interface{}. +func (v *Value) MSISlice(optionalDefault ...[]map[string]interface{}) []map[string]interface{} { + if s, ok := v.data.([]map[string]interface{}); ok { + return s + } + + s := v.ObjxMapSlice() + if s == nil { + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil + } + + result := make([]map[string]interface{}, len(s)) + for i := range s { + result[i] = s[i].Value().MSI() + } + return result +} + +// MustMSISlice gets the value as a []map[string]interface{}. +// +// Panics if the object is not a []map[string]interface{}. +func (v *Value) MustMSISlice() []map[string]interface{} { + if s := v.MSISlice(); s != nil { + return s + } + + return v.data.([]map[string]interface{}) +} + +// IsMSI gets whether the object contained is a map[string]interface{} or not. +func (v *Value) IsMSI() bool { + _, ok := v.data.(map[string]interface{}) + if !ok { + _, ok = v.data.(Map) + } + return ok +} + +// IsMSISlice gets whether the object contained is a []map[string]interface{} or not. +func (v *Value) IsMSISlice() bool { + _, ok := v.data.([]map[string]interface{}) + if !ok { + _, ok = v.data.([]Map) + if !ok { + s, ok := v.data.([]interface{}) + if ok { + for i := range s { + switch s[i].(type) { + case Map: + case map[string]interface{}: + default: + return false + } + } + return true + } + } + } + return ok +} + +// EachMSI calls the specified callback for each object +// in the []map[string]interface{}. +// +// Panics if the object is the wrong type. +func (v *Value) EachMSI(callback func(int, map[string]interface{}) bool) *Value { + for index, val := range v.MustMSISlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereMSI uses the specified decider function to select items +// from the []map[string]interface{}. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereMSI(decider func(int, map[string]interface{}) bool) *Value { + var selected []map[string]interface{} + v.EachMSI(func(index int, val map[string]interface{}) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupMSI uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]map[string]interface{}. +func (v *Value) GroupMSI(grouper func(int, map[string]interface{}) string) *Value { + groups := make(map[string][]map[string]interface{}) + v.EachMSI(func(index int, val map[string]interface{}) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]map[string]interface{}, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceMSI uses the specified function to replace each map[string]interface{}s +// by iterating each item. The data in the returned result will be a +// []map[string]interface{} containing the replaced items. +func (v *Value) ReplaceMSI(replacer func(int, map[string]interface{}) map[string]interface{}) *Value { + arr := v.MustMSISlice() + replaced := make([]map[string]interface{}, len(arr)) + v.EachMSI(func(index int, val map[string]interface{}) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectMSI uses the specified collector function to collect a value +// for each of the map[string]interface{}s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectMSI(collector func(int, map[string]interface{}) interface{}) *Value { + arr := v.MustMSISlice() + collected := make([]interface{}, len(arr)) + v.EachMSI(func(index int, val map[string]interface{}) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + ObjxMap ((Map) and [](Map)) +*/ + +// ObjxMap gets the value as a (Map), returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) ObjxMap(optionalDefault ...(Map)) Map { + if s, ok := v.data.((Map)); ok { + return s + } + if s, ok := v.data.(map[string]interface{}); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return New(nil) +} + +// MustObjxMap gets the value as a (Map). +// +// Panics if the object is not a (Map). +func (v *Value) MustObjxMap() Map { + if s, ok := v.data.(map[string]interface{}); ok { + return s + } + return v.data.((Map)) +} + +// ObjxMapSlice gets the value as a [](Map), returns the optionalDefault +// value or nil if the value is not a [](Map). +func (v *Value) ObjxMapSlice(optionalDefault ...[](Map)) [](Map) { + if s, ok := v.data.([]Map); ok { + return s + } + + if s, ok := v.data.([]map[string]interface{}); ok { + result := make([]Map, len(s)) + for i := range s { + result[i] = s[i] + } + return result + } + + s, ok := v.data.([]interface{}) + if !ok { + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil + } + + result := make([]Map, len(s)) + for i := range s { + switch s[i].(type) { + case Map: + result[i] = s[i].(Map) + case map[string]interface{}: + result[i] = New(s[i]) + default: + return nil + } + } + return result +} + +// MustObjxMapSlice gets the value as a [](Map). +// +// Panics if the object is not a [](Map). +func (v *Value) MustObjxMapSlice() [](Map) { + if s := v.ObjxMapSlice(); s != nil { + return s + } + return v.data.([](Map)) +} + +// IsObjxMap gets whether the object contained is a (Map) or not. +func (v *Value) IsObjxMap() bool { + _, ok := v.data.((Map)) + if !ok { + _, ok = v.data.(map[string]interface{}) + } + return ok +} + +// IsObjxMapSlice gets whether the object contained is a [](Map) or not. +func (v *Value) IsObjxMapSlice() bool { + _, ok := v.data.([](Map)) + if !ok { + _, ok = v.data.([]map[string]interface{}) + if !ok { + s, ok := v.data.([]interface{}) + if ok { + for i := range s { + switch s[i].(type) { + case Map: + case map[string]interface{}: + default: + return false + } + } + return true + } + } + } + + return ok +} + +// EachObjxMap calls the specified callback for each object +// in the [](Map). +// +// Panics if the object is the wrong type. +func (v *Value) EachObjxMap(callback func(int, Map) bool) *Value { + for index, val := range v.MustObjxMapSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereObjxMap uses the specified decider function to select items +// from the [](Map). The object contained in the result will contain +// only the selected items. +func (v *Value) WhereObjxMap(decider func(int, Map) bool) *Value { + var selected [](Map) + v.EachObjxMap(func(index int, val Map) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupObjxMap uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][](Map). +func (v *Value) GroupObjxMap(grouper func(int, Map) string) *Value { + groups := make(map[string][](Map)) + v.EachObjxMap(func(index int, val Map) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([](Map), 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceObjxMap uses the specified function to replace each (Map)s +// by iterating each item. The data in the returned result will be a +// [](Map) containing the replaced items. +func (v *Value) ReplaceObjxMap(replacer func(int, Map) Map) *Value { + arr := v.MustObjxMapSlice() + replaced := make([](Map), len(arr)) + v.EachObjxMap(func(index int, val Map) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectObjxMap uses the specified collector function to collect a value +// for each of the (Map)s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectObjxMap(collector func(int, Map) interface{}) *Value { + arr := v.MustObjxMapSlice() + collected := make([]interface{}, len(arr)) + v.EachObjxMap(func(index int, val Map) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} diff --git a/vendor/github.com/stretchr/objx/type_specific_codegen.go b/vendor/github.com/stretchr/objx/type_specific_codegen.go new file mode 100644 index 000000000..45850456e --- /dev/null +++ b/vendor/github.com/stretchr/objx/type_specific_codegen.go @@ -0,0 +1,2261 @@ +package objx + +/* + Inter (interface{} and []interface{}) +*/ + +// Inter gets the value as a interface{}, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Inter(optionalDefault ...interface{}) interface{} { + if s, ok := v.data.(interface{}); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInter gets the value as a interface{}. +// +// Panics if the object is not a interface{}. +func (v *Value) MustInter() interface{} { + return v.data.(interface{}) +} + +// InterSlice gets the value as a []interface{}, returns the optionalDefault +// value or nil if the value is not a []interface{}. +func (v *Value) InterSlice(optionalDefault ...[]interface{}) []interface{} { + if s, ok := v.data.([]interface{}); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInterSlice gets the value as a []interface{}. +// +// Panics if the object is not a []interface{}. +func (v *Value) MustInterSlice() []interface{} { + return v.data.([]interface{}) +} + +// IsInter gets whether the object contained is a interface{} or not. +func (v *Value) IsInter() bool { + _, ok := v.data.(interface{}) + return ok +} + +// IsInterSlice gets whether the object contained is a []interface{} or not. +func (v *Value) IsInterSlice() bool { + _, ok := v.data.([]interface{}) + return ok +} + +// EachInter calls the specified callback for each object +// in the []interface{}. +// +// Panics if the object is the wrong type. +func (v *Value) EachInter(callback func(int, interface{}) bool) *Value { + for index, val := range v.MustInterSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInter uses the specified decider function to select items +// from the []interface{}. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInter(decider func(int, interface{}) bool) *Value { + var selected []interface{} + v.EachInter(func(index int, val interface{}) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInter uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]interface{}. +func (v *Value) GroupInter(grouper func(int, interface{}) string) *Value { + groups := make(map[string][]interface{}) + v.EachInter(func(index int, val interface{}) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]interface{}, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInter uses the specified function to replace each interface{}s +// by iterating each item. The data in the returned result will be a +// []interface{} containing the replaced items. +func (v *Value) ReplaceInter(replacer func(int, interface{}) interface{}) *Value { + arr := v.MustInterSlice() + replaced := make([]interface{}, len(arr)) + v.EachInter(func(index int, val interface{}) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInter uses the specified collector function to collect a value +// for each of the interface{}s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInter(collector func(int, interface{}) interface{}) *Value { + arr := v.MustInterSlice() + collected := make([]interface{}, len(arr)) + v.EachInter(func(index int, val interface{}) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Bool (bool and []bool) +*/ + +// Bool gets the value as a bool, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Bool(optionalDefault ...bool) bool { + if s, ok := v.data.(bool); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return false +} + +// MustBool gets the value as a bool. +// +// Panics if the object is not a bool. +func (v *Value) MustBool() bool { + return v.data.(bool) +} + +// BoolSlice gets the value as a []bool, returns the optionalDefault +// value or nil if the value is not a []bool. +func (v *Value) BoolSlice(optionalDefault ...[]bool) []bool { + if s, ok := v.data.([]bool); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustBoolSlice gets the value as a []bool. +// +// Panics if the object is not a []bool. +func (v *Value) MustBoolSlice() []bool { + return v.data.([]bool) +} + +// IsBool gets whether the object contained is a bool or not. +func (v *Value) IsBool() bool { + _, ok := v.data.(bool) + return ok +} + +// IsBoolSlice gets whether the object contained is a []bool or not. +func (v *Value) IsBoolSlice() bool { + _, ok := v.data.([]bool) + return ok +} + +// EachBool calls the specified callback for each object +// in the []bool. +// +// Panics if the object is the wrong type. +func (v *Value) EachBool(callback func(int, bool) bool) *Value { + for index, val := range v.MustBoolSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereBool uses the specified decider function to select items +// from the []bool. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereBool(decider func(int, bool) bool) *Value { + var selected []bool + v.EachBool(func(index int, val bool) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupBool uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]bool. +func (v *Value) GroupBool(grouper func(int, bool) string) *Value { + groups := make(map[string][]bool) + v.EachBool(func(index int, val bool) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]bool, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceBool uses the specified function to replace each bools +// by iterating each item. The data in the returned result will be a +// []bool containing the replaced items. +func (v *Value) ReplaceBool(replacer func(int, bool) bool) *Value { + arr := v.MustBoolSlice() + replaced := make([]bool, len(arr)) + v.EachBool(func(index int, val bool) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectBool uses the specified collector function to collect a value +// for each of the bools in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectBool(collector func(int, bool) interface{}) *Value { + arr := v.MustBoolSlice() + collected := make([]interface{}, len(arr)) + v.EachBool(func(index int, val bool) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Str (string and []string) +*/ + +// Str gets the value as a string, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Str(optionalDefault ...string) string { + if s, ok := v.data.(string); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return "" +} + +// MustStr gets the value as a string. +// +// Panics if the object is not a string. +func (v *Value) MustStr() string { + return v.data.(string) +} + +// StrSlice gets the value as a []string, returns the optionalDefault +// value or nil if the value is not a []string. +func (v *Value) StrSlice(optionalDefault ...[]string) []string { + if s, ok := v.data.([]string); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustStrSlice gets the value as a []string. +// +// Panics if the object is not a []string. +func (v *Value) MustStrSlice() []string { + return v.data.([]string) +} + +// IsStr gets whether the object contained is a string or not. +func (v *Value) IsStr() bool { + _, ok := v.data.(string) + return ok +} + +// IsStrSlice gets whether the object contained is a []string or not. +func (v *Value) IsStrSlice() bool { + _, ok := v.data.([]string) + return ok +} + +// EachStr calls the specified callback for each object +// in the []string. +// +// Panics if the object is the wrong type. +func (v *Value) EachStr(callback func(int, string) bool) *Value { + for index, val := range v.MustStrSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereStr uses the specified decider function to select items +// from the []string. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereStr(decider func(int, string) bool) *Value { + var selected []string + v.EachStr(func(index int, val string) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupStr uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]string. +func (v *Value) GroupStr(grouper func(int, string) string) *Value { + groups := make(map[string][]string) + v.EachStr(func(index int, val string) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]string, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceStr uses the specified function to replace each strings +// by iterating each item. The data in the returned result will be a +// []string containing the replaced items. +func (v *Value) ReplaceStr(replacer func(int, string) string) *Value { + arr := v.MustStrSlice() + replaced := make([]string, len(arr)) + v.EachStr(func(index int, val string) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectStr uses the specified collector function to collect a value +// for each of the strings in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectStr(collector func(int, string) interface{}) *Value { + arr := v.MustStrSlice() + collected := make([]interface{}, len(arr)) + v.EachStr(func(index int, val string) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int (int and []int) +*/ + +// Int gets the value as a int, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int(optionalDefault ...int) int { + if s, ok := v.data.(int); ok { + return s + } + if s, ok := v.data.(float64); ok { + if float64(int(s)) == s { + return int(s) + } + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt gets the value as a int. +// +// Panics if the object is not a int. +func (v *Value) MustInt() int { + if s, ok := v.data.(float64); ok { + if float64(int(s)) == s { + return int(s) + } + } + return v.data.(int) +} + +// IntSlice gets the value as a []int, returns the optionalDefault +// value or nil if the value is not a []int. +func (v *Value) IntSlice(optionalDefault ...[]int) []int { + if s, ok := v.data.([]int); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustIntSlice gets the value as a []int. +// +// Panics if the object is not a []int. +func (v *Value) MustIntSlice() []int { + return v.data.([]int) +} + +// IsInt gets whether the object contained is a int or not. +func (v *Value) IsInt() bool { + _, ok := v.data.(int) + return ok +} + +// IsIntSlice gets whether the object contained is a []int or not. +func (v *Value) IsIntSlice() bool { + _, ok := v.data.([]int) + return ok +} + +// EachInt calls the specified callback for each object +// in the []int. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt(callback func(int, int) bool) *Value { + for index, val := range v.MustIntSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt uses the specified decider function to select items +// from the []int. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt(decider func(int, int) bool) *Value { + var selected []int + v.EachInt(func(index int, val int) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int. +func (v *Value) GroupInt(grouper func(int, int) string) *Value { + groups := make(map[string][]int) + v.EachInt(func(index int, val int) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt uses the specified function to replace each ints +// by iterating each item. The data in the returned result will be a +// []int containing the replaced items. +func (v *Value) ReplaceInt(replacer func(int, int) int) *Value { + arr := v.MustIntSlice() + replaced := make([]int, len(arr)) + v.EachInt(func(index int, val int) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt uses the specified collector function to collect a value +// for each of the ints in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt(collector func(int, int) interface{}) *Value { + arr := v.MustIntSlice() + collected := make([]interface{}, len(arr)) + v.EachInt(func(index int, val int) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int8 (int8 and []int8) +*/ + +// Int8 gets the value as a int8, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int8(optionalDefault ...int8) int8 { + if s, ok := v.data.(int8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt8 gets the value as a int8. +// +// Panics if the object is not a int8. +func (v *Value) MustInt8() int8 { + return v.data.(int8) +} + +// Int8Slice gets the value as a []int8, returns the optionalDefault +// value or nil if the value is not a []int8. +func (v *Value) Int8Slice(optionalDefault ...[]int8) []int8 { + if s, ok := v.data.([]int8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt8Slice gets the value as a []int8. +// +// Panics if the object is not a []int8. +func (v *Value) MustInt8Slice() []int8 { + return v.data.([]int8) +} + +// IsInt8 gets whether the object contained is a int8 or not. +func (v *Value) IsInt8() bool { + _, ok := v.data.(int8) + return ok +} + +// IsInt8Slice gets whether the object contained is a []int8 or not. +func (v *Value) IsInt8Slice() bool { + _, ok := v.data.([]int8) + return ok +} + +// EachInt8 calls the specified callback for each object +// in the []int8. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt8(callback func(int, int8) bool) *Value { + for index, val := range v.MustInt8Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt8 uses the specified decider function to select items +// from the []int8. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt8(decider func(int, int8) bool) *Value { + var selected []int8 + v.EachInt8(func(index int, val int8) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt8 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int8. +func (v *Value) GroupInt8(grouper func(int, int8) string) *Value { + groups := make(map[string][]int8) + v.EachInt8(func(index int, val int8) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int8, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt8 uses the specified function to replace each int8s +// by iterating each item. The data in the returned result will be a +// []int8 containing the replaced items. +func (v *Value) ReplaceInt8(replacer func(int, int8) int8) *Value { + arr := v.MustInt8Slice() + replaced := make([]int8, len(arr)) + v.EachInt8(func(index int, val int8) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt8 uses the specified collector function to collect a value +// for each of the int8s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt8(collector func(int, int8) interface{}) *Value { + arr := v.MustInt8Slice() + collected := make([]interface{}, len(arr)) + v.EachInt8(func(index int, val int8) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int16 (int16 and []int16) +*/ + +// Int16 gets the value as a int16, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int16(optionalDefault ...int16) int16 { + if s, ok := v.data.(int16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt16 gets the value as a int16. +// +// Panics if the object is not a int16. +func (v *Value) MustInt16() int16 { + return v.data.(int16) +} + +// Int16Slice gets the value as a []int16, returns the optionalDefault +// value or nil if the value is not a []int16. +func (v *Value) Int16Slice(optionalDefault ...[]int16) []int16 { + if s, ok := v.data.([]int16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt16Slice gets the value as a []int16. +// +// Panics if the object is not a []int16. +func (v *Value) MustInt16Slice() []int16 { + return v.data.([]int16) +} + +// IsInt16 gets whether the object contained is a int16 or not. +func (v *Value) IsInt16() bool { + _, ok := v.data.(int16) + return ok +} + +// IsInt16Slice gets whether the object contained is a []int16 or not. +func (v *Value) IsInt16Slice() bool { + _, ok := v.data.([]int16) + return ok +} + +// EachInt16 calls the specified callback for each object +// in the []int16. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt16(callback func(int, int16) bool) *Value { + for index, val := range v.MustInt16Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt16 uses the specified decider function to select items +// from the []int16. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt16(decider func(int, int16) bool) *Value { + var selected []int16 + v.EachInt16(func(index int, val int16) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt16 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int16. +func (v *Value) GroupInt16(grouper func(int, int16) string) *Value { + groups := make(map[string][]int16) + v.EachInt16(func(index int, val int16) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int16, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt16 uses the specified function to replace each int16s +// by iterating each item. The data in the returned result will be a +// []int16 containing the replaced items. +func (v *Value) ReplaceInt16(replacer func(int, int16) int16) *Value { + arr := v.MustInt16Slice() + replaced := make([]int16, len(arr)) + v.EachInt16(func(index int, val int16) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt16 uses the specified collector function to collect a value +// for each of the int16s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt16(collector func(int, int16) interface{}) *Value { + arr := v.MustInt16Slice() + collected := make([]interface{}, len(arr)) + v.EachInt16(func(index int, val int16) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int32 (int32 and []int32) +*/ + +// Int32 gets the value as a int32, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int32(optionalDefault ...int32) int32 { + if s, ok := v.data.(int32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt32 gets the value as a int32. +// +// Panics if the object is not a int32. +func (v *Value) MustInt32() int32 { + return v.data.(int32) +} + +// Int32Slice gets the value as a []int32, returns the optionalDefault +// value or nil if the value is not a []int32. +func (v *Value) Int32Slice(optionalDefault ...[]int32) []int32 { + if s, ok := v.data.([]int32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt32Slice gets the value as a []int32. +// +// Panics if the object is not a []int32. +func (v *Value) MustInt32Slice() []int32 { + return v.data.([]int32) +} + +// IsInt32 gets whether the object contained is a int32 or not. +func (v *Value) IsInt32() bool { + _, ok := v.data.(int32) + return ok +} + +// IsInt32Slice gets whether the object contained is a []int32 or not. +func (v *Value) IsInt32Slice() bool { + _, ok := v.data.([]int32) + return ok +} + +// EachInt32 calls the specified callback for each object +// in the []int32. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt32(callback func(int, int32) bool) *Value { + for index, val := range v.MustInt32Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt32 uses the specified decider function to select items +// from the []int32. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt32(decider func(int, int32) bool) *Value { + var selected []int32 + v.EachInt32(func(index int, val int32) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt32 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int32. +func (v *Value) GroupInt32(grouper func(int, int32) string) *Value { + groups := make(map[string][]int32) + v.EachInt32(func(index int, val int32) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int32, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt32 uses the specified function to replace each int32s +// by iterating each item. The data in the returned result will be a +// []int32 containing the replaced items. +func (v *Value) ReplaceInt32(replacer func(int, int32) int32) *Value { + arr := v.MustInt32Slice() + replaced := make([]int32, len(arr)) + v.EachInt32(func(index int, val int32) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt32 uses the specified collector function to collect a value +// for each of the int32s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt32(collector func(int, int32) interface{}) *Value { + arr := v.MustInt32Slice() + collected := make([]interface{}, len(arr)) + v.EachInt32(func(index int, val int32) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int64 (int64 and []int64) +*/ + +// Int64 gets the value as a int64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int64(optionalDefault ...int64) int64 { + if s, ok := v.data.(int64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt64 gets the value as a int64. +// +// Panics if the object is not a int64. +func (v *Value) MustInt64() int64 { + return v.data.(int64) +} + +// Int64Slice gets the value as a []int64, returns the optionalDefault +// value or nil if the value is not a []int64. +func (v *Value) Int64Slice(optionalDefault ...[]int64) []int64 { + if s, ok := v.data.([]int64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt64Slice gets the value as a []int64. +// +// Panics if the object is not a []int64. +func (v *Value) MustInt64Slice() []int64 { + return v.data.([]int64) +} + +// IsInt64 gets whether the object contained is a int64 or not. +func (v *Value) IsInt64() bool { + _, ok := v.data.(int64) + return ok +} + +// IsInt64Slice gets whether the object contained is a []int64 or not. +func (v *Value) IsInt64Slice() bool { + _, ok := v.data.([]int64) + return ok +} + +// EachInt64 calls the specified callback for each object +// in the []int64. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt64(callback func(int, int64) bool) *Value { + for index, val := range v.MustInt64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt64 uses the specified decider function to select items +// from the []int64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt64(decider func(int, int64) bool) *Value { + var selected []int64 + v.EachInt64(func(index int, val int64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int64. +func (v *Value) GroupInt64(grouper func(int, int64) string) *Value { + groups := make(map[string][]int64) + v.EachInt64(func(index int, val int64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt64 uses the specified function to replace each int64s +// by iterating each item. The data in the returned result will be a +// []int64 containing the replaced items. +func (v *Value) ReplaceInt64(replacer func(int, int64) int64) *Value { + arr := v.MustInt64Slice() + replaced := make([]int64, len(arr)) + v.EachInt64(func(index int, val int64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt64 uses the specified collector function to collect a value +// for each of the int64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt64(collector func(int, int64) interface{}) *Value { + arr := v.MustInt64Slice() + collected := make([]interface{}, len(arr)) + v.EachInt64(func(index int, val int64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint (uint and []uint) +*/ + +// Uint gets the value as a uint, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint(optionalDefault ...uint) uint { + if s, ok := v.data.(uint); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint gets the value as a uint. +// +// Panics if the object is not a uint. +func (v *Value) MustUint() uint { + return v.data.(uint) +} + +// UintSlice gets the value as a []uint, returns the optionalDefault +// value or nil if the value is not a []uint. +func (v *Value) UintSlice(optionalDefault ...[]uint) []uint { + if s, ok := v.data.([]uint); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUintSlice gets the value as a []uint. +// +// Panics if the object is not a []uint. +func (v *Value) MustUintSlice() []uint { + return v.data.([]uint) +} + +// IsUint gets whether the object contained is a uint or not. +func (v *Value) IsUint() bool { + _, ok := v.data.(uint) + return ok +} + +// IsUintSlice gets whether the object contained is a []uint or not. +func (v *Value) IsUintSlice() bool { + _, ok := v.data.([]uint) + return ok +} + +// EachUint calls the specified callback for each object +// in the []uint. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint(callback func(int, uint) bool) *Value { + for index, val := range v.MustUintSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint uses the specified decider function to select items +// from the []uint. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint(decider func(int, uint) bool) *Value { + var selected []uint + v.EachUint(func(index int, val uint) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint. +func (v *Value) GroupUint(grouper func(int, uint) string) *Value { + groups := make(map[string][]uint) + v.EachUint(func(index int, val uint) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint uses the specified function to replace each uints +// by iterating each item. The data in the returned result will be a +// []uint containing the replaced items. +func (v *Value) ReplaceUint(replacer func(int, uint) uint) *Value { + arr := v.MustUintSlice() + replaced := make([]uint, len(arr)) + v.EachUint(func(index int, val uint) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint uses the specified collector function to collect a value +// for each of the uints in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint(collector func(int, uint) interface{}) *Value { + arr := v.MustUintSlice() + collected := make([]interface{}, len(arr)) + v.EachUint(func(index int, val uint) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint8 (uint8 and []uint8) +*/ + +// Uint8 gets the value as a uint8, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint8(optionalDefault ...uint8) uint8 { + if s, ok := v.data.(uint8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint8 gets the value as a uint8. +// +// Panics if the object is not a uint8. +func (v *Value) MustUint8() uint8 { + return v.data.(uint8) +} + +// Uint8Slice gets the value as a []uint8, returns the optionalDefault +// value or nil if the value is not a []uint8. +func (v *Value) Uint8Slice(optionalDefault ...[]uint8) []uint8 { + if s, ok := v.data.([]uint8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint8Slice gets the value as a []uint8. +// +// Panics if the object is not a []uint8. +func (v *Value) MustUint8Slice() []uint8 { + return v.data.([]uint8) +} + +// IsUint8 gets whether the object contained is a uint8 or not. +func (v *Value) IsUint8() bool { + _, ok := v.data.(uint8) + return ok +} + +// IsUint8Slice gets whether the object contained is a []uint8 or not. +func (v *Value) IsUint8Slice() bool { + _, ok := v.data.([]uint8) + return ok +} + +// EachUint8 calls the specified callback for each object +// in the []uint8. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint8(callback func(int, uint8) bool) *Value { + for index, val := range v.MustUint8Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint8 uses the specified decider function to select items +// from the []uint8. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint8(decider func(int, uint8) bool) *Value { + var selected []uint8 + v.EachUint8(func(index int, val uint8) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint8 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint8. +func (v *Value) GroupUint8(grouper func(int, uint8) string) *Value { + groups := make(map[string][]uint8) + v.EachUint8(func(index int, val uint8) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint8, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint8 uses the specified function to replace each uint8s +// by iterating each item. The data in the returned result will be a +// []uint8 containing the replaced items. +func (v *Value) ReplaceUint8(replacer func(int, uint8) uint8) *Value { + arr := v.MustUint8Slice() + replaced := make([]uint8, len(arr)) + v.EachUint8(func(index int, val uint8) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint8 uses the specified collector function to collect a value +// for each of the uint8s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint8(collector func(int, uint8) interface{}) *Value { + arr := v.MustUint8Slice() + collected := make([]interface{}, len(arr)) + v.EachUint8(func(index int, val uint8) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint16 (uint16 and []uint16) +*/ + +// Uint16 gets the value as a uint16, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint16(optionalDefault ...uint16) uint16 { + if s, ok := v.data.(uint16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint16 gets the value as a uint16. +// +// Panics if the object is not a uint16. +func (v *Value) MustUint16() uint16 { + return v.data.(uint16) +} + +// Uint16Slice gets the value as a []uint16, returns the optionalDefault +// value or nil if the value is not a []uint16. +func (v *Value) Uint16Slice(optionalDefault ...[]uint16) []uint16 { + if s, ok := v.data.([]uint16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint16Slice gets the value as a []uint16. +// +// Panics if the object is not a []uint16. +func (v *Value) MustUint16Slice() []uint16 { + return v.data.([]uint16) +} + +// IsUint16 gets whether the object contained is a uint16 or not. +func (v *Value) IsUint16() bool { + _, ok := v.data.(uint16) + return ok +} + +// IsUint16Slice gets whether the object contained is a []uint16 or not. +func (v *Value) IsUint16Slice() bool { + _, ok := v.data.([]uint16) + return ok +} + +// EachUint16 calls the specified callback for each object +// in the []uint16. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint16(callback func(int, uint16) bool) *Value { + for index, val := range v.MustUint16Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint16 uses the specified decider function to select items +// from the []uint16. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint16(decider func(int, uint16) bool) *Value { + var selected []uint16 + v.EachUint16(func(index int, val uint16) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint16 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint16. +func (v *Value) GroupUint16(grouper func(int, uint16) string) *Value { + groups := make(map[string][]uint16) + v.EachUint16(func(index int, val uint16) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint16, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint16 uses the specified function to replace each uint16s +// by iterating each item. The data in the returned result will be a +// []uint16 containing the replaced items. +func (v *Value) ReplaceUint16(replacer func(int, uint16) uint16) *Value { + arr := v.MustUint16Slice() + replaced := make([]uint16, len(arr)) + v.EachUint16(func(index int, val uint16) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint16 uses the specified collector function to collect a value +// for each of the uint16s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint16(collector func(int, uint16) interface{}) *Value { + arr := v.MustUint16Slice() + collected := make([]interface{}, len(arr)) + v.EachUint16(func(index int, val uint16) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint32 (uint32 and []uint32) +*/ + +// Uint32 gets the value as a uint32, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint32(optionalDefault ...uint32) uint32 { + if s, ok := v.data.(uint32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint32 gets the value as a uint32. +// +// Panics if the object is not a uint32. +func (v *Value) MustUint32() uint32 { + return v.data.(uint32) +} + +// Uint32Slice gets the value as a []uint32, returns the optionalDefault +// value or nil if the value is not a []uint32. +func (v *Value) Uint32Slice(optionalDefault ...[]uint32) []uint32 { + if s, ok := v.data.([]uint32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint32Slice gets the value as a []uint32. +// +// Panics if the object is not a []uint32. +func (v *Value) MustUint32Slice() []uint32 { + return v.data.([]uint32) +} + +// IsUint32 gets whether the object contained is a uint32 or not. +func (v *Value) IsUint32() bool { + _, ok := v.data.(uint32) + return ok +} + +// IsUint32Slice gets whether the object contained is a []uint32 or not. +func (v *Value) IsUint32Slice() bool { + _, ok := v.data.([]uint32) + return ok +} + +// EachUint32 calls the specified callback for each object +// in the []uint32. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint32(callback func(int, uint32) bool) *Value { + for index, val := range v.MustUint32Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint32 uses the specified decider function to select items +// from the []uint32. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint32(decider func(int, uint32) bool) *Value { + var selected []uint32 + v.EachUint32(func(index int, val uint32) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint32 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint32. +func (v *Value) GroupUint32(grouper func(int, uint32) string) *Value { + groups := make(map[string][]uint32) + v.EachUint32(func(index int, val uint32) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint32, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint32 uses the specified function to replace each uint32s +// by iterating each item. The data in the returned result will be a +// []uint32 containing the replaced items. +func (v *Value) ReplaceUint32(replacer func(int, uint32) uint32) *Value { + arr := v.MustUint32Slice() + replaced := make([]uint32, len(arr)) + v.EachUint32(func(index int, val uint32) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint32 uses the specified collector function to collect a value +// for each of the uint32s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint32(collector func(int, uint32) interface{}) *Value { + arr := v.MustUint32Slice() + collected := make([]interface{}, len(arr)) + v.EachUint32(func(index int, val uint32) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint64 (uint64 and []uint64) +*/ + +// Uint64 gets the value as a uint64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint64(optionalDefault ...uint64) uint64 { + if s, ok := v.data.(uint64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint64 gets the value as a uint64. +// +// Panics if the object is not a uint64. +func (v *Value) MustUint64() uint64 { + return v.data.(uint64) +} + +// Uint64Slice gets the value as a []uint64, returns the optionalDefault +// value or nil if the value is not a []uint64. +func (v *Value) Uint64Slice(optionalDefault ...[]uint64) []uint64 { + if s, ok := v.data.([]uint64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint64Slice gets the value as a []uint64. +// +// Panics if the object is not a []uint64. +func (v *Value) MustUint64Slice() []uint64 { + return v.data.([]uint64) +} + +// IsUint64 gets whether the object contained is a uint64 or not. +func (v *Value) IsUint64() bool { + _, ok := v.data.(uint64) + return ok +} + +// IsUint64Slice gets whether the object contained is a []uint64 or not. +func (v *Value) IsUint64Slice() bool { + _, ok := v.data.([]uint64) + return ok +} + +// EachUint64 calls the specified callback for each object +// in the []uint64. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint64(callback func(int, uint64) bool) *Value { + for index, val := range v.MustUint64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint64 uses the specified decider function to select items +// from the []uint64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint64(decider func(int, uint64) bool) *Value { + var selected []uint64 + v.EachUint64(func(index int, val uint64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint64. +func (v *Value) GroupUint64(grouper func(int, uint64) string) *Value { + groups := make(map[string][]uint64) + v.EachUint64(func(index int, val uint64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint64 uses the specified function to replace each uint64s +// by iterating each item. The data in the returned result will be a +// []uint64 containing the replaced items. +func (v *Value) ReplaceUint64(replacer func(int, uint64) uint64) *Value { + arr := v.MustUint64Slice() + replaced := make([]uint64, len(arr)) + v.EachUint64(func(index int, val uint64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint64 uses the specified collector function to collect a value +// for each of the uint64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint64(collector func(int, uint64) interface{}) *Value { + arr := v.MustUint64Slice() + collected := make([]interface{}, len(arr)) + v.EachUint64(func(index int, val uint64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uintptr (uintptr and []uintptr) +*/ + +// Uintptr gets the value as a uintptr, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uintptr(optionalDefault ...uintptr) uintptr { + if s, ok := v.data.(uintptr); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUintptr gets the value as a uintptr. +// +// Panics if the object is not a uintptr. +func (v *Value) MustUintptr() uintptr { + return v.data.(uintptr) +} + +// UintptrSlice gets the value as a []uintptr, returns the optionalDefault +// value or nil if the value is not a []uintptr. +func (v *Value) UintptrSlice(optionalDefault ...[]uintptr) []uintptr { + if s, ok := v.data.([]uintptr); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUintptrSlice gets the value as a []uintptr. +// +// Panics if the object is not a []uintptr. +func (v *Value) MustUintptrSlice() []uintptr { + return v.data.([]uintptr) +} + +// IsUintptr gets whether the object contained is a uintptr or not. +func (v *Value) IsUintptr() bool { + _, ok := v.data.(uintptr) + return ok +} + +// IsUintptrSlice gets whether the object contained is a []uintptr or not. +func (v *Value) IsUintptrSlice() bool { + _, ok := v.data.([]uintptr) + return ok +} + +// EachUintptr calls the specified callback for each object +// in the []uintptr. +// +// Panics if the object is the wrong type. +func (v *Value) EachUintptr(callback func(int, uintptr) bool) *Value { + for index, val := range v.MustUintptrSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUintptr uses the specified decider function to select items +// from the []uintptr. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUintptr(decider func(int, uintptr) bool) *Value { + var selected []uintptr + v.EachUintptr(func(index int, val uintptr) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUintptr uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uintptr. +func (v *Value) GroupUintptr(grouper func(int, uintptr) string) *Value { + groups := make(map[string][]uintptr) + v.EachUintptr(func(index int, val uintptr) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uintptr, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUintptr uses the specified function to replace each uintptrs +// by iterating each item. The data in the returned result will be a +// []uintptr containing the replaced items. +func (v *Value) ReplaceUintptr(replacer func(int, uintptr) uintptr) *Value { + arr := v.MustUintptrSlice() + replaced := make([]uintptr, len(arr)) + v.EachUintptr(func(index int, val uintptr) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUintptr uses the specified collector function to collect a value +// for each of the uintptrs in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUintptr(collector func(int, uintptr) interface{}) *Value { + arr := v.MustUintptrSlice() + collected := make([]interface{}, len(arr)) + v.EachUintptr(func(index int, val uintptr) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Float32 (float32 and []float32) +*/ + +// Float32 gets the value as a float32, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Float32(optionalDefault ...float32) float32 { + if s, ok := v.data.(float32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustFloat32 gets the value as a float32. +// +// Panics if the object is not a float32. +func (v *Value) MustFloat32() float32 { + return v.data.(float32) +} + +// Float32Slice gets the value as a []float32, returns the optionalDefault +// value or nil if the value is not a []float32. +func (v *Value) Float32Slice(optionalDefault ...[]float32) []float32 { + if s, ok := v.data.([]float32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustFloat32Slice gets the value as a []float32. +// +// Panics if the object is not a []float32. +func (v *Value) MustFloat32Slice() []float32 { + return v.data.([]float32) +} + +// IsFloat32 gets whether the object contained is a float32 or not. +func (v *Value) IsFloat32() bool { + _, ok := v.data.(float32) + return ok +} + +// IsFloat32Slice gets whether the object contained is a []float32 or not. +func (v *Value) IsFloat32Slice() bool { + _, ok := v.data.([]float32) + return ok +} + +// EachFloat32 calls the specified callback for each object +// in the []float32. +// +// Panics if the object is the wrong type. +func (v *Value) EachFloat32(callback func(int, float32) bool) *Value { + for index, val := range v.MustFloat32Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereFloat32 uses the specified decider function to select items +// from the []float32. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereFloat32(decider func(int, float32) bool) *Value { + var selected []float32 + v.EachFloat32(func(index int, val float32) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupFloat32 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]float32. +func (v *Value) GroupFloat32(grouper func(int, float32) string) *Value { + groups := make(map[string][]float32) + v.EachFloat32(func(index int, val float32) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]float32, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceFloat32 uses the specified function to replace each float32s +// by iterating each item. The data in the returned result will be a +// []float32 containing the replaced items. +func (v *Value) ReplaceFloat32(replacer func(int, float32) float32) *Value { + arr := v.MustFloat32Slice() + replaced := make([]float32, len(arr)) + v.EachFloat32(func(index int, val float32) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectFloat32 uses the specified collector function to collect a value +// for each of the float32s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectFloat32(collector func(int, float32) interface{}) *Value { + arr := v.MustFloat32Slice() + collected := make([]interface{}, len(arr)) + v.EachFloat32(func(index int, val float32) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Float64 (float64 and []float64) +*/ + +// Float64 gets the value as a float64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Float64(optionalDefault ...float64) float64 { + if s, ok := v.data.(float64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustFloat64 gets the value as a float64. +// +// Panics if the object is not a float64. +func (v *Value) MustFloat64() float64 { + return v.data.(float64) +} + +// Float64Slice gets the value as a []float64, returns the optionalDefault +// value or nil if the value is not a []float64. +func (v *Value) Float64Slice(optionalDefault ...[]float64) []float64 { + if s, ok := v.data.([]float64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustFloat64Slice gets the value as a []float64. +// +// Panics if the object is not a []float64. +func (v *Value) MustFloat64Slice() []float64 { + return v.data.([]float64) +} + +// IsFloat64 gets whether the object contained is a float64 or not. +func (v *Value) IsFloat64() bool { + _, ok := v.data.(float64) + return ok +} + +// IsFloat64Slice gets whether the object contained is a []float64 or not. +func (v *Value) IsFloat64Slice() bool { + _, ok := v.data.([]float64) + return ok +} + +// EachFloat64 calls the specified callback for each object +// in the []float64. +// +// Panics if the object is the wrong type. +func (v *Value) EachFloat64(callback func(int, float64) bool) *Value { + for index, val := range v.MustFloat64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereFloat64 uses the specified decider function to select items +// from the []float64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereFloat64(decider func(int, float64) bool) *Value { + var selected []float64 + v.EachFloat64(func(index int, val float64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupFloat64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]float64. +func (v *Value) GroupFloat64(grouper func(int, float64) string) *Value { + groups := make(map[string][]float64) + v.EachFloat64(func(index int, val float64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]float64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceFloat64 uses the specified function to replace each float64s +// by iterating each item. The data in the returned result will be a +// []float64 containing the replaced items. +func (v *Value) ReplaceFloat64(replacer func(int, float64) float64) *Value { + arr := v.MustFloat64Slice() + replaced := make([]float64, len(arr)) + v.EachFloat64(func(index int, val float64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectFloat64 uses the specified collector function to collect a value +// for each of the float64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectFloat64(collector func(int, float64) interface{}) *Value { + arr := v.MustFloat64Slice() + collected := make([]interface{}, len(arr)) + v.EachFloat64(func(index int, val float64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Complex64 (complex64 and []complex64) +*/ + +// Complex64 gets the value as a complex64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Complex64(optionalDefault ...complex64) complex64 { + if s, ok := v.data.(complex64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustComplex64 gets the value as a complex64. +// +// Panics if the object is not a complex64. +func (v *Value) MustComplex64() complex64 { + return v.data.(complex64) +} + +// Complex64Slice gets the value as a []complex64, returns the optionalDefault +// value or nil if the value is not a []complex64. +func (v *Value) Complex64Slice(optionalDefault ...[]complex64) []complex64 { + if s, ok := v.data.([]complex64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustComplex64Slice gets the value as a []complex64. +// +// Panics if the object is not a []complex64. +func (v *Value) MustComplex64Slice() []complex64 { + return v.data.([]complex64) +} + +// IsComplex64 gets whether the object contained is a complex64 or not. +func (v *Value) IsComplex64() bool { + _, ok := v.data.(complex64) + return ok +} + +// IsComplex64Slice gets whether the object contained is a []complex64 or not. +func (v *Value) IsComplex64Slice() bool { + _, ok := v.data.([]complex64) + return ok +} + +// EachComplex64 calls the specified callback for each object +// in the []complex64. +// +// Panics if the object is the wrong type. +func (v *Value) EachComplex64(callback func(int, complex64) bool) *Value { + for index, val := range v.MustComplex64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereComplex64 uses the specified decider function to select items +// from the []complex64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereComplex64(decider func(int, complex64) bool) *Value { + var selected []complex64 + v.EachComplex64(func(index int, val complex64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupComplex64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]complex64. +func (v *Value) GroupComplex64(grouper func(int, complex64) string) *Value { + groups := make(map[string][]complex64) + v.EachComplex64(func(index int, val complex64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]complex64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceComplex64 uses the specified function to replace each complex64s +// by iterating each item. The data in the returned result will be a +// []complex64 containing the replaced items. +func (v *Value) ReplaceComplex64(replacer func(int, complex64) complex64) *Value { + arr := v.MustComplex64Slice() + replaced := make([]complex64, len(arr)) + v.EachComplex64(func(index int, val complex64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectComplex64 uses the specified collector function to collect a value +// for each of the complex64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectComplex64(collector func(int, complex64) interface{}) *Value { + arr := v.MustComplex64Slice() + collected := make([]interface{}, len(arr)) + v.EachComplex64(func(index int, val complex64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Complex128 (complex128 and []complex128) +*/ + +// Complex128 gets the value as a complex128, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Complex128(optionalDefault ...complex128) complex128 { + if s, ok := v.data.(complex128); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustComplex128 gets the value as a complex128. +// +// Panics if the object is not a complex128. +func (v *Value) MustComplex128() complex128 { + return v.data.(complex128) +} + +// Complex128Slice gets the value as a []complex128, returns the optionalDefault +// value or nil if the value is not a []complex128. +func (v *Value) Complex128Slice(optionalDefault ...[]complex128) []complex128 { + if s, ok := v.data.([]complex128); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustComplex128Slice gets the value as a []complex128. +// +// Panics if the object is not a []complex128. +func (v *Value) MustComplex128Slice() []complex128 { + return v.data.([]complex128) +} + +// IsComplex128 gets whether the object contained is a complex128 or not. +func (v *Value) IsComplex128() bool { + _, ok := v.data.(complex128) + return ok +} + +// IsComplex128Slice gets whether the object contained is a []complex128 or not. +func (v *Value) IsComplex128Slice() bool { + _, ok := v.data.([]complex128) + return ok +} + +// EachComplex128 calls the specified callback for each object +// in the []complex128. +// +// Panics if the object is the wrong type. +func (v *Value) EachComplex128(callback func(int, complex128) bool) *Value { + for index, val := range v.MustComplex128Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereComplex128 uses the specified decider function to select items +// from the []complex128. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereComplex128(decider func(int, complex128) bool) *Value { + var selected []complex128 + v.EachComplex128(func(index int, val complex128) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupComplex128 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]complex128. +func (v *Value) GroupComplex128(grouper func(int, complex128) string) *Value { + groups := make(map[string][]complex128) + v.EachComplex128(func(index int, val complex128) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]complex128, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceComplex128 uses the specified function to replace each complex128s +// by iterating each item. The data in the returned result will be a +// []complex128 containing the replaced items. +func (v *Value) ReplaceComplex128(replacer func(int, complex128) complex128) *Value { + arr := v.MustComplex128Slice() + replaced := make([]complex128, len(arr)) + v.EachComplex128(func(index int, val complex128) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectComplex128 uses the specified collector function to collect a value +// for each of the complex128s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectComplex128(collector func(int, complex128) interface{}) *Value { + arr := v.MustComplex128Slice() + collected := make([]interface{}, len(arr)) + v.EachComplex128(func(index int, val complex128) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} diff --git a/vendor/github.com/stretchr/objx/value.go b/vendor/github.com/stretchr/objx/value.go new file mode 100644 index 000000000..4e5f9b77e --- /dev/null +++ b/vendor/github.com/stretchr/objx/value.go @@ -0,0 +1,159 @@ +package objx + +import ( + "fmt" + "strconv" +) + +// Value provides methods for extracting interface{} data in various +// types. +type Value struct { + // data contains the raw data being managed by this Value + data interface{} +} + +// Data returns the raw data contained by this Value +func (v *Value) Data() interface{} { + return v.data +} + +// String returns the value always as a string +func (v *Value) String() string { + switch { + case v.IsNil(): + return "" + case v.IsStr(): + return v.Str() + case v.IsBool(): + return strconv.FormatBool(v.Bool()) + case v.IsFloat32(): + return strconv.FormatFloat(float64(v.Float32()), 'f', -1, 32) + case v.IsFloat64(): + return strconv.FormatFloat(v.Float64(), 'f', -1, 64) + case v.IsInt(): + return strconv.FormatInt(int64(v.Int()), 10) + case v.IsInt8(): + return strconv.FormatInt(int64(v.Int8()), 10) + case v.IsInt16(): + return strconv.FormatInt(int64(v.Int16()), 10) + case v.IsInt32(): + return strconv.FormatInt(int64(v.Int32()), 10) + case v.IsInt64(): + return strconv.FormatInt(v.Int64(), 10) + case v.IsUint(): + return strconv.FormatUint(uint64(v.Uint()), 10) + case v.IsUint8(): + return strconv.FormatUint(uint64(v.Uint8()), 10) + case v.IsUint16(): + return strconv.FormatUint(uint64(v.Uint16()), 10) + case v.IsUint32(): + return strconv.FormatUint(uint64(v.Uint32()), 10) + case v.IsUint64(): + return strconv.FormatUint(v.Uint64(), 10) + } + return fmt.Sprintf("%#v", v.Data()) +} + +// StringSlice returns the value always as a []string +func (v *Value) StringSlice(optionalDefault ...[]string) []string { + switch { + case v.IsStrSlice(): + return v.MustStrSlice() + case v.IsBoolSlice(): + slice := v.MustBoolSlice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatBool(iv) + } + return vals + case v.IsFloat32Slice(): + slice := v.MustFloat32Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatFloat(float64(iv), 'f', -1, 32) + } + return vals + case v.IsFloat64Slice(): + slice := v.MustFloat64Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatFloat(iv, 'f', -1, 64) + } + return vals + case v.IsIntSlice(): + slice := v.MustIntSlice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt8Slice(): + slice := v.MustInt8Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt16Slice(): + slice := v.MustInt16Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt32Slice(): + slice := v.MustInt32Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt64Slice(): + slice := v.MustInt64Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(iv, 10) + } + return vals + case v.IsUintSlice(): + slice := v.MustUintSlice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint8Slice(): + slice := v.MustUint8Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint16Slice(): + slice := v.MustUint16Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint32Slice(): + slice := v.MustUint32Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint64Slice(): + slice := v.MustUint64Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(iv, 10) + } + return vals + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + + return []string{} +} diff --git a/vendor/github.com/stretchr/testify/mock/doc.go b/vendor/github.com/stretchr/testify/mock/doc.go new file mode 100644 index 000000000..d6b3c844c --- /dev/null +++ b/vendor/github.com/stretchr/testify/mock/doc.go @@ -0,0 +1,44 @@ +// Package mock provides a system by which it is possible to mock your objects +// and verify calls are happening as expected. +// +// # Example Usage +// +// The mock package provides an object, Mock, that tracks activity on another object. It is usually +// embedded into a test object as shown below: +// +// type MyTestObject struct { +// // add a Mock object instance +// mock.Mock +// +// // other fields go here as normal +// } +// +// When implementing the methods of an interface, you wire your functions up +// to call the Mock.Called(args...) method, and return the appropriate values. +// +// For example, to mock a method that saves the name and age of a person and returns +// the year of their birth or an error, you might write this: +// +// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { +// args := o.Called(firstname, lastname, age) +// return args.Int(0), args.Error(1) +// } +// +// The Int, Error and Bool methods are examples of strongly typed getters that take the argument +// index position. Given this argument list: +// +// (12, true, "Something") +// +// You could read them out strongly typed like this: +// +// args.Int(0) +// args.Bool(1) +// args.String(2) +// +// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: +// +// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) +// +// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those +// cases you should check for nil first. +package mock diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go new file mode 100644 index 000000000..213bde2ea --- /dev/null +++ b/vendor/github.com/stretchr/testify/mock/mock.go @@ -0,0 +1,1241 @@ +package mock + +import ( + "errors" + "fmt" + "path" + "reflect" + "regexp" + "runtime" + "strings" + "sync" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/pmezard/go-difflib/difflib" + "github.com/stretchr/objx" + + "github.com/stretchr/testify/assert" +) + +// regex for GCCGO functions +var gccgoRE = regexp.MustCompile(`\.pN\d+_`) + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Logf(format string, args ...interface{}) + Errorf(format string, args ...interface{}) + FailNow() +} + +/* + Call +*/ + +// Call represents a method call and is used for setting expectations, +// as well as recording activity. +type Call struct { + Parent *Mock + + // The name of the method that was or will be called. + Method string + + // Holds the arguments of the method. + Arguments Arguments + + // Holds the arguments that should be returned when + // this method is called. + ReturnArguments Arguments + + // Holds the caller info for the On() call + callerInfo []string + + // The number of times to return the return arguments when setting + // expectations. 0 means to always return the value. + Repeatability int + + // Amount of times this call has been called + totalCalls int + + // Call to this method can be optional + optional bool + + // Holds a channel that will be used to block the Return until it either + // receives a message or is closed. nil means it returns immediately. + WaitFor <-chan time.Time + + waitTime time.Duration + + // Holds a handler used to manipulate arguments content that are passed by + // reference. It's useful when mocking methods such as unmarshalers or + // decoders. + RunFn func(Arguments) + + // PanicMsg holds msg to be used to mock panic on the function call + // if the PanicMsg is set to a non nil string the function call will panic + // irrespective of other settings + PanicMsg *string + + // Calls which must be satisfied before this call can be + requires []*Call +} + +func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments ...interface{}) *Call { + return &Call{ + Parent: parent, + Method: methodName, + Arguments: methodArguments, + ReturnArguments: make([]interface{}, 0), + callerInfo: callerInfo, + Repeatability: 0, + WaitFor: nil, + RunFn: nil, + PanicMsg: nil, + } +} + +func (c *Call) lock() { + c.Parent.mutex.Lock() +} + +func (c *Call) unlock() { + c.Parent.mutex.Unlock() +} + +// Return specifies the return arguments for the expectation. +// +// Mock.On("DoSomething").Return(errors.New("failed")) +func (c *Call) Return(returnArguments ...interface{}) *Call { + c.lock() + defer c.unlock() + + c.ReturnArguments = returnArguments + + return c +} + +// Panic specifies if the function call should fail and the panic message +// +// Mock.On("DoSomething").Panic("test panic") +func (c *Call) Panic(msg string) *Call { + c.lock() + defer c.unlock() + + c.PanicMsg = &msg + + return c +} + +// Once indicates that the mock should only return the value once. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() +func (c *Call) Once() *Call { + return c.Times(1) +} + +// Twice indicates that the mock should only return the value twice. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() +func (c *Call) Twice() *Call { + return c.Times(2) +} + +// Times indicates that the mock should only return the indicated number +// of times. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) +func (c *Call) Times(i int) *Call { + c.lock() + defer c.unlock() + c.Repeatability = i + return c +} + +// WaitUntil sets the channel that will block the mock's return until its closed +// or a message is received. +// +// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) +func (c *Call) WaitUntil(w <-chan time.Time) *Call { + c.lock() + defer c.unlock() + c.WaitFor = w + return c +} + +// After sets how long to block until the call returns +// +// Mock.On("MyMethod", arg1, arg2).After(time.Second) +func (c *Call) After(d time.Duration) *Call { + c.lock() + defer c.unlock() + c.waitTime = d + return c +} + +// Run sets a handler to be called before returning. It can be used when +// mocking a method (such as an unmarshaler) that takes a pointer to a struct and +// sets properties in such struct +// +// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) { +// arg := args.Get(0).(*map[string]interface{}) +// arg["foo"] = "bar" +// }) +func (c *Call) Run(fn func(args Arguments)) *Call { + c.lock() + defer c.unlock() + c.RunFn = fn + return c +} + +// Maybe allows the method call to be optional. Not calling an optional method +// will not cause an error while asserting expectations +func (c *Call) Maybe() *Call { + c.lock() + defer c.unlock() + c.optional = true + return c +} + +// On chains a new expectation description onto the mocked interface. This +// allows syntax like. +// +// Mock. +// On("MyMethod", 1).Return(nil). +// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) +// +//go:noinline +func (c *Call) On(methodName string, arguments ...interface{}) *Call { + return c.Parent.On(methodName, arguments...) +} + +// Unset removes a mock handler from being called. +// +// test.On("func", mock.Anything).Unset() +func (c *Call) Unset() *Call { + var unlockOnce sync.Once + + for _, arg := range c.Arguments { + if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { + panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) + } + } + + c.lock() + defer unlockOnce.Do(c.unlock) + + foundMatchingCall := false + + // in-place filter slice for calls to be removed - iterate from 0'th to last skipping unnecessary ones + var index int // write index + for _, call := range c.Parent.ExpectedCalls { + if call.Method == c.Method { + _, diffCount := call.Arguments.Diff(c.Arguments) + if diffCount == 0 { + foundMatchingCall = true + // Remove from ExpectedCalls - just skip it + continue + } + } + c.Parent.ExpectedCalls[index] = call + index++ + } + // trim slice up to last copied index + c.Parent.ExpectedCalls = c.Parent.ExpectedCalls[:index] + + if !foundMatchingCall { + unlockOnce.Do(c.unlock) + c.Parent.fail("\n\nmock: Could not find expected call\n-----------------------------\n\n%s\n\n", + callString(c.Method, c.Arguments, true), + ) + } + + return c +} + +// NotBefore indicates that the mock should only be called after the referenced +// calls have been called as expected. The referenced calls may be from the +// same mock instance and/or other mock instances. +// +// Mock.On("Do").Return(nil).Notbefore( +// Mock.On("Init").Return(nil) +// ) +func (c *Call) NotBefore(calls ...*Call) *Call { + c.lock() + defer c.unlock() + + for _, call := range calls { + if call.Parent == nil { + panic("not before calls must be created with Mock.On()") + } + } + + c.requires = append(c.requires, calls...) + return c +} + +// Mock is the workhorse used to track activity on another object. +// For an example of its usage, refer to the "Example Usage" section at the top +// of this document. +type Mock struct { + // Represents the calls that are expected of + // an object. + ExpectedCalls []*Call + + // Holds the calls that were made to this mocked object. + Calls []Call + + // test is An optional variable that holds the test struct, to be used when an + // invalid mock call was made. + test TestingT + + // TestData holds any data that might be useful for testing. Testify ignores + // this data completely allowing you to do whatever you like with it. + testData objx.Map + + mutex sync.Mutex +} + +// String provides a %v format string for Mock. +// Note: this is used implicitly by Arguments.Diff if a Mock is passed. +// It exists because go's default %v formatting traverses the struct +// without acquiring the mutex, which is detected by go test -race. +func (m *Mock) String() string { + return fmt.Sprintf("%[1]T<%[1]p>", m) +} + +// TestData holds any data that might be useful for testing. Testify ignores +// this data completely allowing you to do whatever you like with it. +func (m *Mock) TestData() objx.Map { + if m.testData == nil { + m.testData = make(objx.Map) + } + + return m.testData +} + +/* + Setting expectations +*/ + +// Test sets the test struct variable of the mock object +func (m *Mock) Test(t TestingT) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.test = t +} + +// fail fails the current test with the given formatted format and args. +// In case that a test was defined, it uses the test APIs for failing a test, +// otherwise it uses panic. +func (m *Mock) fail(format string, args ...interface{}) { + m.mutex.Lock() + defer m.mutex.Unlock() + + if m.test == nil { + panic(fmt.Sprintf(format, args...)) + } + m.test.Errorf(format, args...) + m.test.FailNow() +} + +// On starts a description of an expectation of the specified method +// being called. +// +// Mock.On("MyMethod", arg1, arg2) +func (m *Mock) On(methodName string, arguments ...interface{}) *Call { + for _, arg := range arguments { + if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { + panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) + } + } + + m.mutex.Lock() + defer m.mutex.Unlock() + c := newCall(m, methodName, assert.CallerInfo(), arguments...) + m.ExpectedCalls = append(m.ExpectedCalls, c) + return c +} + +// /* +// Recording and responding to activity +// */ + +func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) { + var expectedCall *Call + + for i, call := range m.ExpectedCalls { + if call.Method == method { + _, diffCount := call.Arguments.Diff(arguments) + if diffCount == 0 { + expectedCall = call + if call.Repeatability > -1 { + return i, call + } + } + } + } + + return -1, expectedCall +} + +type matchCandidate struct { + call *Call + mismatch string + diffCount int +} + +func (c matchCandidate) isBetterMatchThan(other matchCandidate) bool { + if c.call == nil { + return false + } + if other.call == nil { + return true + } + + if c.diffCount > other.diffCount { + return false + } + if c.diffCount < other.diffCount { + return true + } + + if c.call.Repeatability > 0 && other.call.Repeatability <= 0 { + return true + } + return false +} + +func (m *Mock) findClosestCall(method string, arguments ...interface{}) (*Call, string) { + var bestMatch matchCandidate + + for _, call := range m.expectedCalls() { + if call.Method == method { + + errInfo, tempDiffCount := call.Arguments.Diff(arguments) + tempCandidate := matchCandidate{ + call: call, + mismatch: errInfo, + diffCount: tempDiffCount, + } + if tempCandidate.isBetterMatchThan(bestMatch) { + bestMatch = tempCandidate + } + } + } + + return bestMatch.call, bestMatch.mismatch +} + +func callString(method string, arguments Arguments, includeArgumentValues bool) string { + var argValsString string + if includeArgumentValues { + var argVals []string + for argIndex, arg := range arguments { + if _, ok := arg.(*FunctionalOptionsArgument); ok { + argVals = append(argVals, fmt.Sprintf("%d: %s", argIndex, arg)) + continue + } + argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg)) + } + argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t")) + } + + return fmt.Sprintf("%s(%s)%s", method, arguments.String(), argValsString) +} + +// Called tells the mock object that a method has been called, and gets an array +// of arguments to return. Panics if the call is unexpected (i.e. not preceded by +// appropriate .On .Return() calls) +// If Call.WaitFor is set, blocks until the channel is closed or receives a message. +func (m *Mock) Called(arguments ...interface{}) Arguments { + // get the calling function's name + pc, _, _, ok := runtime.Caller(1) + if !ok { + panic("Couldn't get the caller information") + } + functionPath := runtime.FuncForPC(pc).Name() + // Next four lines are required to use GCCGO function naming conventions. + // For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock + // uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree + // With GCCGO we need to remove interface information starting from pN
. + if gccgoRE.MatchString(functionPath) { + functionPath = gccgoRE.Split(functionPath, -1)[0] + } + parts := strings.Split(functionPath, ".") + functionName := parts[len(parts)-1] + return m.MethodCalled(functionName, arguments...) +} + +// MethodCalled tells the mock object that the given method has been called, and gets +// an array of arguments to return. Panics if the call is unexpected (i.e. not preceded +// by appropriate .On .Return() calls) +// If Call.WaitFor is set, blocks until the channel is closed or receives a message. +func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Arguments { + m.mutex.Lock() + // TODO: could combine expected and closes in single loop + found, call := m.findExpectedCall(methodName, arguments...) + + if found < 0 { + // expected call found, but it has already been called with repeatable times + if call != nil { + m.mutex.Unlock() + m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(\"%s\").Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo()) + } + // we have to fail here - because we don't know what to do + // as the return arguments. This is because: + // + // a) this is a totally unexpected call to this method, + // b) the arguments are not what was expected, or + // c) the developer has forgotten to add an accompanying On...Return pair. + closestCall, mismatch := m.findClosestCall(methodName, arguments...) + m.mutex.Unlock() + + if closestCall != nil { + m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s", + callString(methodName, arguments, true), + callString(methodName, closestCall.Arguments, true), + diffArguments(closestCall.Arguments, arguments), + strings.TrimSpace(mismatch), + ) + } else { + m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo()) + } + } + + for _, requirement := range call.requires { + if satisfied, _ := requirement.Parent.checkExpectation(requirement); !satisfied { + m.mutex.Unlock() + m.fail("mock: Unexpected Method Call\n-----------------------------\n\n%s\n\nMust not be called before%s:\n\n%s", + callString(call.Method, call.Arguments, true), + func() (s string) { + if requirement.totalCalls > 0 { + s = " another call of" + } + if call.Parent != requirement.Parent { + s += " method from another mock instance" + } + return + }(), + callString(requirement.Method, requirement.Arguments, true), + ) + } + } + + if call.Repeatability == 1 { + call.Repeatability = -1 + } else if call.Repeatability > 1 { + call.Repeatability-- + } + call.totalCalls++ + + // add the call + m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments...)) + m.mutex.Unlock() + + // block if specified + if call.WaitFor != nil { + <-call.WaitFor + } else { + time.Sleep(call.waitTime) + } + + m.mutex.Lock() + panicMsg := call.PanicMsg + m.mutex.Unlock() + if panicMsg != nil { + panic(*panicMsg) + } + + m.mutex.Lock() + runFn := call.RunFn + m.mutex.Unlock() + + if runFn != nil { + runFn(arguments) + } + + m.mutex.Lock() + returnArgs := call.ReturnArguments + m.mutex.Unlock() + + return returnArgs +} + +/* + Assertions +*/ + +type assertExpectationiser interface { + AssertExpectations(TestingT) bool +} + +// AssertExpectationsForObjects asserts that everything specified with On and Return +// of the specified objects was in fact called as expected. +// +// Calls may have occurred in any order. +func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + for _, obj := range testObjects { + if m, ok := obj.(*Mock); ok { + t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)") + obj = m + } + m := obj.(assertExpectationiser) + if !m.AssertExpectations(t) { + t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m)) + return false + } + } + return true +} + +// AssertExpectations asserts that everything specified with On and Return was +// in fact called as expected. Calls may have occurred in any order. +func (m *Mock) AssertExpectations(t TestingT) bool { + if s, ok := t.(interface{ Skipped() bool }); ok && s.Skipped() { + return true + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + + m.mutex.Lock() + defer m.mutex.Unlock() + var failedExpectations int + + // iterate through each expectation + expectedCalls := m.expectedCalls() + for _, expectedCall := range expectedCalls { + satisfied, reason := m.checkExpectation(expectedCall) + if !satisfied { + failedExpectations++ + t.Logf(reason) + } + } + + if failedExpectations != 0 { + t.Errorf("FAIL: %d out of %d expectation(s) were met.\n\tThe code you are testing needs to make %d more call(s).\n\tat: %s", len(expectedCalls)-failedExpectations, len(expectedCalls), failedExpectations, assert.CallerInfo()) + } + + return failedExpectations == 0 +} + +func (m *Mock) checkExpectation(call *Call) (bool, string) { + if !call.optional && !m.methodWasCalled(call.Method, call.Arguments) && call.totalCalls == 0 { + return false, fmt.Sprintf("FAIL:\t%s(%s)\n\t\tat: %s", call.Method, call.Arguments.String(), call.callerInfo) + } + if call.Repeatability > 0 { + return false, fmt.Sprintf("FAIL:\t%s(%s)\n\t\tat: %s", call.Method, call.Arguments.String(), call.callerInfo) + } + return true, fmt.Sprintf("PASS:\t%s(%s)", call.Method, call.Arguments.String()) +} + +// AssertNumberOfCalls asserts that the method was called expectedCalls times. +func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + var actualCalls int + for _, call := range m.calls() { + if call.Method == methodName { + actualCalls++ + } + } + return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls)) +} + +// AssertCalled asserts that the method was called. +// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. +func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + if !m.methodWasCalled(methodName, arguments) { + var calledWithArgs []string + for _, call := range m.calls() { + calledWithArgs = append(calledWithArgs, fmt.Sprintf("%v", call.Arguments)) + } + if len(calledWithArgs) == 0 { + return assert.Fail(t, "Should have called with given arguments", + fmt.Sprintf("Expected %q to have been called with:\n%v\nbut no actual calls happened", methodName, arguments)) + } + return assert.Fail(t, "Should have called with given arguments", + fmt.Sprintf("Expected %q to have been called with:\n%v\nbut actual calls were:\n %v", methodName, arguments, strings.Join(calledWithArgs, "\n"))) + } + return true +} + +// AssertNotCalled asserts that the method was not called. +// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. +func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + if m.methodWasCalled(methodName, arguments) { + return assert.Fail(t, "Should not have called with given arguments", + fmt.Sprintf("Expected %q to not have been called with:\n%v\nbut actually it was.", methodName, arguments)) + } + return true +} + +// IsMethodCallable checking that the method can be called +// If the method was called more than `Repeatability` return false +func (m *Mock) IsMethodCallable(t TestingT, methodName string, arguments ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + + for _, v := range m.ExpectedCalls { + if v.Method != methodName { + continue + } + if len(arguments) != len(v.Arguments) { + continue + } + if v.Repeatability < v.totalCalls { + continue + } + if isArgsEqual(v.Arguments, arguments) { + return true + } + } + return false +} + +// isArgsEqual compares arguments +func isArgsEqual(expected Arguments, args []interface{}) bool { + if len(expected) != len(args) { + return false + } + for i, v := range args { + if !reflect.DeepEqual(expected[i], v) { + return false + } + } + return true +} + +func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool { + for _, call := range m.calls() { + if call.Method == methodName { + + _, differences := Arguments(expected).Diff(call.Arguments) + + if differences == 0 { + // found the expected call + return true + } + + } + } + // we didn't find the expected call + return false +} + +func (m *Mock) expectedCalls() []*Call { + return append([]*Call{}, m.ExpectedCalls...) +} + +func (m *Mock) calls() []Call { + return append([]Call{}, m.Calls...) +} + +/* + Arguments +*/ + +// Arguments holds an array of method arguments or return values. +type Arguments []interface{} + +const ( + // Anything is used in Diff and Assert when the argument being tested + // shouldn't be taken into consideration. + Anything = "mock.Anything" +) + +// AnythingOfTypeArgument contains the type of an argument +// for use when type checking. Used in Diff and Assert. +// +// Deprecated: this is an implementation detail that must not be used. Use [AnythingOfType] instead. +type AnythingOfTypeArgument = anythingOfTypeArgument + +// anythingOfTypeArgument is a string that contains the type of an argument +// for use when type checking. Used in Diff and Assert. +type anythingOfTypeArgument string + +// AnythingOfType returns a special value containing the +// name of the type to check for. The type name will be matched against the type name returned by [reflect.Type.String]. +// +// Used in Diff and Assert. +// +// For example: +// +// Assert(t, AnythingOfType("string"), AnythingOfType("int")) +func AnythingOfType(t string) AnythingOfTypeArgument { + return anythingOfTypeArgument(t) +} + +// IsTypeArgument is a struct that contains the type of an argument +// for use when type checking. This is an alternative to AnythingOfType. +// Used in Diff and Assert. +type IsTypeArgument struct { + t reflect.Type +} + +// IsType returns an IsTypeArgument object containing the type to check for. +// You can provide a zero-value of the type to check. This is an +// alternative to AnythingOfType. Used in Diff and Assert. +// +// For example: +// Assert(t, IsType(""), IsType(0)) +func IsType(t interface{}) *IsTypeArgument { + return &IsTypeArgument{t: reflect.TypeOf(t)} +} + +// FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument +// for use when type checking. +type FunctionalOptionsArgument struct { + value interface{} +} + +// String returns the string representation of FunctionalOptionsArgument +func (f *FunctionalOptionsArgument) String() string { + var name string + tValue := reflect.ValueOf(f.value) + if tValue.Len() > 0 { + name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() + } + + return strings.Replace(fmt.Sprintf("%#v", f.value), "[]interface {}", name, 1) +} + +// FunctionalOptions returns an FunctionalOptionsArgument object containing the functional option type +// and the values to check of +// +// For example: +// Assert(t, FunctionalOptions("[]foo.FunctionalOption", foo.Opt1(), foo.Opt2())) +func FunctionalOptions(value ...interface{}) *FunctionalOptionsArgument { + return &FunctionalOptionsArgument{ + value: value, + } +} + +// argumentMatcher performs custom argument matching, returning whether or +// not the argument is matched by the expectation fixture function. +type argumentMatcher struct { + // fn is a function which accepts one argument, and returns a bool. + fn reflect.Value +} + +func (f argumentMatcher) Matches(argument interface{}) bool { + expectType := f.fn.Type().In(0) + expectTypeNilSupported := false + switch expectType.Kind() { + case reflect.Interface, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Ptr: + expectTypeNilSupported = true + } + + argType := reflect.TypeOf(argument) + var arg reflect.Value + if argType == nil { + arg = reflect.New(expectType).Elem() + } else { + arg = reflect.ValueOf(argument) + } + + if argType == nil && !expectTypeNilSupported { + panic(errors.New("attempting to call matcher with nil for non-nil expected type")) + } + if argType == nil || argType.AssignableTo(expectType) { + result := f.fn.Call([]reflect.Value{arg}) + return result[0].Bool() + } + return false +} + +func (f argumentMatcher) String() string { + return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).String()) +} + +// MatchedBy can be used to match a mock call based on only certain properties +// from a complex struct or some calculation. It takes a function that will be +// evaluated with the called argument and will return true when there's a match +// and false otherwise. +// +// Example: +// m.On("Do", MatchedBy(func(req *http.Request) bool { return req.Host == "example.com" })) +// +// |fn|, must be a function accepting a single argument (of the expected type) +// which returns a bool. If |fn| doesn't match the required signature, +// MatchedBy() panics. +func MatchedBy(fn interface{}) argumentMatcher { + fnType := reflect.TypeOf(fn) + + if fnType.Kind() != reflect.Func { + panic(fmt.Sprintf("assert: arguments: %s is not a func", fn)) + } + if fnType.NumIn() != 1 { + panic(fmt.Sprintf("assert: arguments: %s does not take exactly one argument", fn)) + } + if fnType.NumOut() != 1 || fnType.Out(0).Kind() != reflect.Bool { + panic(fmt.Sprintf("assert: arguments: %s does not return a bool", fn)) + } + + return argumentMatcher{fn: reflect.ValueOf(fn)} +} + +// Get Returns the argument at the specified index. +func (args Arguments) Get(index int) interface{} { + if index+1 > len(args) { + panic(fmt.Sprintf("assert: arguments: Cannot call Get(%d) because there are %d argument(s).", index, len(args))) + } + return args[index] +} + +// Is gets whether the objects match the arguments specified. +func (args Arguments) Is(objects ...interface{}) bool { + for i, obj := range args { + if obj != objects[i] { + return false + } + } + return true +} + +// Diff gets a string describing the differences between the arguments +// and the specified objects. +// +// Returns the diff string and number of differences found. +func (args Arguments) Diff(objects []interface{}) (string, int) { + // TODO: could return string as error and nil for No difference + + output := "\n" + var differences int + + maxArgCount := len(args) + if len(objects) > maxArgCount { + maxArgCount = len(objects) + } + + for i := 0; i < maxArgCount; i++ { + var actual, expected interface{} + var actualFmt, expectedFmt string + + if len(objects) <= i { + actual = "(Missing)" + actualFmt = "(Missing)" + } else { + actual = objects[i] + actualFmt = fmt.Sprintf("(%[1]T=%[1]v)", actual) + } + + if len(args) <= i { + expected = "(Missing)" + expectedFmt = "(Missing)" + } else { + expected = args[i] + expectedFmt = fmt.Sprintf("(%[1]T=%[1]v)", expected) + } + + if matcher, ok := expected.(argumentMatcher); ok { + var matches bool + func() { + defer func() { + if r := recover(); r != nil { + actualFmt = fmt.Sprintf("panic in argument matcher: %v", r) + } + }() + matches = matcher.Matches(actual) + }() + if matches { + output = fmt.Sprintf("%s\t%d: PASS: %s matched by %s\n", output, i, actualFmt, matcher) + } else { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher) + } + } else { + switch expected := expected.(type) { + case anythingOfTypeArgument: + // type checking + if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) + } + case *IsTypeArgument: + actualT := reflect.TypeOf(actual) + if actualT != expected.t { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt) + } + case *FunctionalOptionsArgument: + t := expected.value + + var name string + tValue := reflect.ValueOf(t) + if tValue.Len() > 0 { + name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() + } + + tName := reflect.TypeOf(t).Name() + if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) + } else { + if ef, af := assertOpts(t, actual); ef == "" && af == "" { + // match + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) + } else { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef) + } + } + + default: + if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { + // match + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) + } else { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) + } + } + } + + } + + if differences == 0 { + return "No differences.", differences + } + + return output, differences +} + +// Assert compares the arguments with the specified objects and fails if +// they do not exactly match. +func (args Arguments) Assert(t TestingT, objects ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + // get the differences + diff, diffCount := args.Diff(objects) + + if diffCount == 0 { + return true + } + + // there are differences... report them... + t.Logf(diff) + t.Errorf("%sArguments do not match.", assert.CallerInfo()) + + return false +} + +// String gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +// +// If no index is provided, String() returns a complete string representation +// of the arguments. +func (args Arguments) String(indexOrNil ...int) string { + if len(indexOrNil) == 0 { + // normal String() method - return a string representation of the args + var argsStr []string + for _, arg := range args { + argsStr = append(argsStr, fmt.Sprintf("%T", arg)) // handles nil nicely + } + return strings.Join(argsStr, ",") + } else if len(indexOrNil) == 1 { + // Index has been specified - get the argument at that index + index := indexOrNil[0] + var s string + var ok bool + if s, ok = args.Get(index).(string); !ok { + panic(fmt.Sprintf("assert: arguments: String(%d) failed because object wasn't correct type: %s", index, args.Get(index))) + } + return s + } + + panic(fmt.Sprintf("assert: arguments: Wrong number of arguments passed to String. Must be 0 or 1, not %d", len(indexOrNil))) +} + +// Int gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +func (args Arguments) Int(index int) int { + var s int + var ok bool + if s, ok = args.Get(index).(int); !ok { + panic(fmt.Sprintf("assert: arguments: Int(%d) failed because object wasn't correct type: %v", index, args.Get(index))) + } + return s +} + +// Error gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +func (args Arguments) Error(index int) error { + obj := args.Get(index) + var s error + var ok bool + if obj == nil { + return nil + } + if s, ok = obj.(error); !ok { + panic(fmt.Sprintf("assert: arguments: Error(%d) failed because object wasn't correct type: %v", index, args.Get(index))) + } + return s +} + +// Bool gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +func (args Arguments) Bool(index int) bool { + var s bool + var ok bool + if s, ok = args.Get(index).(bool); !ok { + panic(fmt.Sprintf("assert: arguments: Bool(%d) failed because object wasn't correct type: %v", index, args.Get(index))) + } + return s +} + +func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { + t := reflect.TypeOf(v) + k := t.Kind() + + if k == reflect.Ptr { + t = t.Elem() + k = t.Kind() + } + return t, k +} + +func diffArguments(expected Arguments, actual Arguments) string { + if len(expected) != len(actual) { + return fmt.Sprintf("Provided %v arguments, mocked for %v arguments", len(expected), len(actual)) + } + + for x := range expected { + if diffString := diff(expected[x], actual[x]); diffString != "" { + return fmt.Sprintf("Difference found in argument %v:\n\n%s", x, diffString) + } + } + + return "" +} + +// diff returns a diff of both values as long as both are of the same type and +// are a struct, map, slice or array. Otherwise it returns an empty string. +func diff(expected interface{}, actual interface{}) string { + if expected == nil || actual == nil { + return "" + } + + et, ek := typeAndKind(expected) + at, _ := typeAndKind(actual) + + if et != at { + return "" + } + + if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array { + return "" + } + + e := spewConfig.Sdump(expected) + a := spewConfig.Sdump(actual) + + diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(e), + B: difflib.SplitLines(a), + FromFile: "Expected", + FromDate: "", + ToFile: "Actual", + ToDate: "", + Context: 1, + }) + + return diff +} + +var spewConfig = spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + DisableCapacities: true, + SortKeys: true, +} + +type tHelper interface { + Helper() +} + +func assertOpts(expected, actual interface{}) (expectedFmt, actualFmt string) { + expectedOpts := reflect.ValueOf(expected) + actualOpts := reflect.ValueOf(actual) + var expectedNames []string + for i := 0; i < expectedOpts.Len(); i++ { + expectedNames = append(expectedNames, funcName(expectedOpts.Index(i).Interface())) + } + var actualNames []string + for i := 0; i < actualOpts.Len(); i++ { + actualNames = append(actualNames, funcName(actualOpts.Index(i).Interface())) + } + if !assert.ObjectsAreEqual(expectedNames, actualNames) { + expectedFmt = fmt.Sprintf("%v", expectedNames) + actualFmt = fmt.Sprintf("%v", actualNames) + return + } + + for i := 0; i < expectedOpts.Len(); i++ { + expectedOpt := expectedOpts.Index(i).Interface() + actualOpt := actualOpts.Index(i).Interface() + + expectedFunc := expectedNames[i] + actualFunc := actualNames[i] + if expectedFunc != actualFunc { + expectedFmt = expectedFunc + actualFmt = actualFunc + return + } + + ot := reflect.TypeOf(expectedOpt) + var expectedValues []reflect.Value + var actualValues []reflect.Value + if ot.NumIn() == 0 { + return + } + + for i := 0; i < ot.NumIn(); i++ { + vt := ot.In(i).Elem() + expectedValues = append(expectedValues, reflect.New(vt)) + actualValues = append(actualValues, reflect.New(vt)) + } + + reflect.ValueOf(expectedOpt).Call(expectedValues) + reflect.ValueOf(actualOpt).Call(actualValues) + + for i := 0; i < ot.NumIn(); i++ { + if !assert.ObjectsAreEqual(expectedValues[i].Interface(), actualValues[i].Interface()) { + expectedFmt = fmt.Sprintf("%s %+v", expectedNames[i], expectedValues[i].Interface()) + actualFmt = fmt.Sprintf("%s %+v", expectedNames[i], actualValues[i].Interface()) + return + } + } + } + + return "", "" +} + +func funcName(opt interface{}) string { + n := runtime.FuncForPC(reflect.ValueOf(opt).Pointer()).Name() + return strings.TrimSuffix(path.Base(n), path.Ext(n)) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 3598ada36..da5cffd92 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -314,6 +314,9 @@ github.com/knadh/koanf/v2 # github.com/leodido/go-urn v1.2.4 ## explicit; go 1.16 github.com/leodido/go-urn +# github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 +## explicit; go 1.16 +github.com/lufia/plan9stats # github.com/mailru/easyjson v0.7.7 ## explicit; go 1.12 github.com/mailru/easyjson/buffer @@ -370,6 +373,9 @@ github.com/pkg/errors # github.com/pmezard/go-difflib v1.0.0 ## explicit github.com/pmezard/go-difflib/difflib +# github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c +## explicit; go 1.14 +github.com/power-devops/perfstat # github.com/prometheus/client_golang v1.19.0 ## explicit; go 1.20 github.com/prometheus/client_golang/prometheus @@ -398,12 +404,27 @@ github.com/shirou/gopsutil/internal/common github.com/shirou/gopsutil/mem github.com/shirou/gopsutil/net github.com/shirou/gopsutil/process +# github.com/shirou/gopsutil/v3 v3.24.2 +## explicit; go 1.15 +github.com/shirou/gopsutil/v3/common +github.com/shirou/gopsutil/v3/cpu +github.com/shirou/gopsutil/v3/internal/common +github.com/shirou/gopsutil/v3/mem +github.com/shirou/gopsutil/v3/net +github.com/shirou/gopsutil/v3/process +# github.com/shoenig/go-m1cpu v0.1.6 +## explicit; go 1.20 +github.com/shoenig/go-m1cpu # github.com/spf13/pflag v1.0.5 ## explicit; go 1.12 github.com/spf13/pflag +# github.com/stretchr/objx v0.5.2 +## explicit; go 1.20 +github.com/stretchr/objx # github.com/stretchr/testify v1.9.0 ## explicit; go 1.17 github.com/stretchr/testify/assert +github.com/stretchr/testify/mock github.com/stretchr/testify/require # github.com/tklauser/go-sysconf v0.3.13 ## explicit; go 1.18 From ef731c8c45c308ef529792c8491fae118ec0a1cc Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Mon, 27 May 2024 12:33:14 +0200 Subject: [PATCH 03/27] refactored harvester_linux_test.go --- pkg/internal/infraolly/process/harvester.go | 11 +- .../infraolly/process/harvester_linux.go | 9 +- .../infraolly/process/harvester_linux_test.go | 183 ++++-------------- pkg/internal/infraolly/process/sample.go | 34 ++-- 4 files changed, 63 insertions(+), 174 deletions(-) diff --git a/pkg/internal/infraolly/process/harvester.go b/pkg/internal/infraolly/process/harvester.go index f17e691c4..7be39f1fe 100644 --- a/pkg/internal/infraolly/process/harvester.go +++ b/pkg/internal/infraolly/process/harvester.go @@ -31,12 +31,13 @@ type Harvester interface { type RunMode string const ( - RunModeRoot = "root" - RunModePrivileged = "privileged" + RunModeRoot = "root" + RunModePrivileged = "privileged" + RunModeUnprivileged = "unprivileged" ) type Config struct { - RunMode RunMode + RunMode RunMode DisableZeroRSSFilter bool - StripCommandLine bool -} \ No newline at end of file + FullCommandLine bool +} diff --git a/pkg/internal/infraolly/process/harvester_linux.go b/pkg/internal/infraolly/process/harvester_linux.go index a29a5777e..392763d4d 100644 --- a/pkg/internal/infraolly/process/harvester_linux.go +++ b/pkg/internal/infraolly/process/harvester_linux.go @@ -1,5 +1,6 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 + // Package process provides all the tools and functionality for sampling processes. It is divided in three main // components: // - Snapshot: provides OS-level information of a process at a given spot @@ -15,11 +16,11 @@ import ( "github.com/shirou/gopsutil/v3/process" ) -func newHarvester(cfg *Config, cache *simplelru.LRU[int32, *cacheEntry]) *linuxHarvester { +func newHarvester(cfg Config, cache *simplelru.LRU[int32, *cacheEntry]) *linuxHarvester { // If not config, assuming root mode as default - privileged := cfg == nil || cfg.RunMode == RunModeRoot || cfg.RunMode == RunModePrivileged - disableZeroRSSFilter := cfg != nil && cfg.DisableZeroRSSFilter - stripCommandLine := cfg != nil && cfg.StripCommandLine + privileged := cfg.RunMode == RunModeRoot || cfg.RunMode == RunModePrivileged + disableZeroRSSFilter := cfg.DisableZeroRSSFilter + stripCommandLine := !cfg.FullCommandLine return &linuxHarvester{ privileged: privileged, diff --git a/pkg/internal/infraolly/process/harvester_linux_test.go b/pkg/internal/infraolly/process/harvester_linux_test.go index 5369936d9..43f267e5f 100644 --- a/pkg/internal/infraolly/process/harvester_linux_test.go +++ b/pkg/internal/infraolly/process/harvester_linux_test.go @@ -7,77 +7,51 @@ import ( "math" "os" "os/exec" - "os/user" "strings" "testing" "time" "github.com/hashicorp/golang-lru/v2/simplelru" + "github.com/mariomac/guara/pkg/test" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) func TestLinuxHarvester_IsPrivileged(t *testing.T) { cases := []struct { - mode string + mode RunMode privileged bool }{ - {mode: config.ModeRoot, privileged: true}, - {mode: config.ModePrivileged, privileged: true}, - {mode: config.ModeUnprivileged, privileged: false}, + {mode: RunModeRoot, privileged: true}, + {mode: RunModePrivileged, privileged: true}, + {mode: RunModeUnprivileged, privileged: false}, } for _, c := range cases { t.Run(fmt.Sprint("mode ", c.mode), func(t *testing.T) { - ctx := new(mocks.AgentContext) - ctx.On("Config").Return(&config.Config{RunMode: c.mode}) - ctx.On("GetServiceForPid", mock.Anything).Return("", false) - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(ctx, &cache) + h := newHarvester(Config{RunMode: c.mode}, cache) // If not privileged, it is expected to not report neither FDs nor IO counters - sample, err := h.Do(int32(os.Getpid()), 100) + sample, err := h.Do(int32(os.Getpid())) require.NoError(t, err) if c.privileged { - assert.NotNil(t, sample.FdCount) - assert.NotNil(t, sample.IOTotalReadCount) + assert.NotZero(t, sample.FdCount) + assert.NotZero(t, sample.IOReadCount) } else { - assert.Nil(t, sample.FdCount) - assert.Nil(t, sample.IOTotalReadCount) + assert.Zero(t, sample.FdCount) + assert.Zero(t, sample.IOReadCount) } }) } } -func TestLinuxHarvester_Pids(t *testing.T) { - // Given a process harvester - ctx := new(mocks.AgentContext) - ctx.On("Config").Return(&config.Config{}) - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(ctx, &cache) - - // When th Pids are retrieved - pids, err := h.Pids() - - // A pids list is returned - require.NoError(t, err) - require.NotEmpty(t, pids) - - // And it contains the pids of the running processes (e.g. current testing executable) - require.Contains(t, pids, int32(os.Getpid())) -} - func TestLinuxHarvester_Do(t *testing.T) { // Given a process harvester - ctx := new(mocks.AgentContext) - ctx.On("Config").Return(&config.Config{}) - ctx.On("GetServiceForPid", mock.Anything).Return("", false) cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(ctx, &cache) + h := newHarvester(Config{}, cache) // When retrieving for a given process sample (e.g. the current testing executable) - sample, err := h.Do(int32(os.Getpid()), 0) + sample, err := h.Do(int32(os.Getpid())) // It returns the corresponding process sample with valid data require.NoError(t, err) @@ -89,65 +63,31 @@ func TestLinuxHarvester_Do(t *testing.T) { assert.NotEmpty(t, sample.User) assert.Contains(t, "RSD", sample.Status, "process status must be R (running), S (interruptible sleep) or D (uninterruptible sleep)") - assert.True(t, sample.MemoryVMSBytes > 0) - assert.True(t, sample.ThreadCount > 0) - assert.Equal(t, "process.test", sample.ProcessDisplayName) - assert.Equal(t, "ProcessSample", sample.EventType) -} - -func TestLinuxHarvester_Do_Privileged(t *testing.T) { - current, err := user.Current() - require.NoError(t, err) - if current.Username != "root" { - t.Skip("this test requires privileges. Current user: ", current.Username) - } - - // Given a process harvester running in privileged mode - ctx := new(mocks.AgentContext) - ctx.On("Config").Return(&config.Config{RunMode: config.ModeRoot}) - ctx.On("GetServiceForPid", mock.Anything).Return("", false) - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(ctx, &cache) - - // When retrieving for a given process sample (e.g. the current testing executable) - sample, err := h.Do(int32(os.Getpid()), 0) - - // It returns the corresponding process sample with valid data - require.NoError(t, err) - require.NotNil(t, sample) - - assert.NotNil(t, sample.FdCount) - - // And when the process sample is retrieved again - sample, err = h.Do(int32(os.Getpid()), 0) - require.NoError(t, err) - require.NotNil(t, sample) - - // Per second deltas are returned - assert.NotNil(t, sample.IOReadBytesPerSecond) - assert.NotNil(t, sample.IOReadCountPerSecond) - assert.NotNil(t, sample.IOWriteBytesPerSecond) - assert.NotNil(t, sample.IOWriteCountPerSecond) + assert.NotZero(t, sample.MemoryVMSBytes) + assert.NotZero(t, sample.MemoryRSSBytes) + assert.NotZero(t, sample.CPUPercent) + assert.NotZero(t, sample.CPUUserPercent) + assert.NotZero(t, sample.CPUSystemPercent) + assert.NotZero(t, sample.ParentProcessID) + assert.NotZero(t, sample.ThreadCount) + assert.NotZero(t, sample.FdCount) + assert.NotZero(t, sample.ThreadCount) } -func TestLinuxHarvester_Do_DisableStripCommandLine(t *testing.T) { +func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { cmd := exec.Command("/bin/sleep", "1m") require.NoError(t, cmd.Start()) defer func() { _ = cmd.Process.Kill() }() - // Given a process harvester - ctx := new(mocks.AgentContext) - // configure to not strip the command line - ctx.On("Config").Return(&config.Config{StripCommandLine: false}) - ctx.On("GetServiceForPid", mock.Anything).Return("", false) + // Given a process harvester configured to showw the full command line cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(ctx, &cache) + h := newHarvester(Config{FullCommandLine: true}, cache) - testhelpers.Eventually(t, 5*time.Second, func(t require.TestingT) { + test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process sample (e.g. the current testing executable) - sample, err := h.Do(int32(cmd.Process.Pid), 0) + sample, err := h.Do(int32(cmd.Process.Pid)) // It returns the corresponding Command line without stripping arguments require.NoError(t, err) @@ -158,7 +98,7 @@ func TestLinuxHarvester_Do_DisableStripCommandLine(t *testing.T) { }) } -func TestLinuxHarvester_Do_EnableStripCommandLine(t *testing.T) { +func TestLinuxHarvester_Do_StripCommandLine(t *testing.T) { cmd := exec.Command("/bin/sleep", "1m") require.NoError(t, cmd.Start()) defer func() { @@ -166,16 +106,12 @@ func TestLinuxHarvester_Do_EnableStripCommandLine(t *testing.T) { }() // Given a process harvester - ctx := new(mocks.AgentContext) - // configure to not strip the command line - ctx.On("Config").Return(&config.Config{StripCommandLine: true}) - ctx.On("GetServiceForPid", mock.Anything).Return("", false) cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(ctx, &cache) + h := newHarvester(Config{FullCommandLine: true}, cache) - testhelpers.Eventually(t, 5*time.Second, func(t require.TestingT) { + test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process sample (e.g. the current testing executable) - sample, err := h.Do(int32(cmd.Process.Pid), 0) + sample, err := h.Do(int32(cmd.Process.Pid)) // It returns the corresponding Command line without stripping arguments require.NoError(t, err) @@ -189,17 +125,13 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentCmd(t *testing.T) { currentPid := int32(os.Getpid()) // Given a process harvester - ctx := new(mocks.AgentContext) - ctx.On("Config").Return(&config.Config{}) - ctx.On("GetServiceForPid", mock.Anything).Return("", false) - // That has cached an old process sharing the PID with a new process cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) cache.Add(currentPid, &cacheEntry{process: &linuxProcess{cmdLine: "something old"}}) - h := newHarvester(ctx, &cache) + h := newHarvester(Config{}, cache) // When the process is harvested - sample, err := h.Do(currentPid, 0) + sample, err := h.Do(currentPid) require.NoError(t, err) // The sample is updated @@ -211,60 +143,15 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentPid(t *testing.T) { currentPid := int32(os.Getpid()) // Given a process harvester - ctx := new(mocks.AgentContext) - ctx.On("Config").Return(&config.Config{}) - ctx.On("GetServiceForPid", mock.Anything).Return("", false) - // That has cached an old process sharing the PID with a new process cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) cache.Add(currentPid, &cacheEntry{process: &linuxProcess{stats: procStats{ppid: -1}}}) - h := newHarvester(ctx, &cache) + h := newHarvester(Config{}, cache) // When the process is harvested - sample, err := h.Do(currentPid, 0) + sample, err := h.Do(currentPid) require.NoError(t, err) // The sample is updated assert.NotEqual(t, -1, sample.ParentProcessID) } - -func TestLinuxHarvester_GetServiceForPid(t *testing.T) { - // Given a process harvester - ctx := new(mocks.AgentContext) - ctx.On("Config").Return(&config.Config{}) - // That matches a given PID with an existing service name - ctx.On("GetServiceForPid", os.Getpid()).Return("MyServiceIdentifier", true) - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(ctx, &cache) - - // When retrieving the process sampler - sample, err := h.Do(int32(os.Getpid()), 0) - - // It returns the corresponding process names - require.NoError(t, err) - require.NotNil(t, sample) - assert.Equal(t, "MyServiceIdentifier", sample.ProcessDisplayName) - assert.Equal(t, "process.test", sample.Command) - assert.Contains(t, sample.CmdLine, os.Args[0]) -} - -func TestLinuxHarvester_GetServiceForPid_OnEmptyUseCommandName(t *testing.T) { - - // Given a process harvester - ctx := new(mocks.AgentContext) - ctx.On("Config").Return(&config.Config{}) - // That matches a given PID with an existing service name that is EMPTY - ctx.On("GetServiceForPid", os.Getpid()).Return("", true) - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(ctx, &cache) - - // When retrieving the process sampler - sample, err := h.Do(int32(os.Getpid()), 0) - - // It returns the corresponding process names - require.NoError(t, err) - require.NotNil(t, sample) - assert.Equal(t, sample.Command, sample.ProcessDisplayName) - assert.Equal(t, "process.test", sample.Command) - assert.Contains(t, sample.CmdLine, os.Args[0]) -} diff --git a/pkg/internal/infraolly/process/sample.go b/pkg/internal/infraolly/process/sample.go index dbb769759..8326200c7 100644 --- a/pkg/internal/infraolly/process/sample.go +++ b/pkg/internal/infraolly/process/sample.go @@ -1,23 +1,23 @@ package process type Sample struct { - ProcessID int32 - Command string - User string - MemoryRSSBytes int64 - MemoryVMSBytes int64 - CPUPercent float64 - CPUUserPercent float64 - CPUSystemPercent float64 - CmdLine string - Status string - ParentProcessID int32 - ThreadCount int32 - FdCount int32 - IOReadCount uint64 - IOWriteCount uint64 - IOReadBytes uint64 - IOWriteBytes uint64 + ProcessID int32 + Command string + User string + MemoryRSSBytes int64 + MemoryVMSBytes int64 + CPUPercent float64 + CPUUserPercent float64 + CPUSystemPercent float64 + CmdLine string + Status string + ParentProcessID int32 + ThreadCount int32 + FdCount int32 + IOReadCount uint64 + IOWriteCount uint64 + IOReadBytes uint64 + IOWriteBytes uint64 } func NewSample(pid int32) *Sample { From bdee025e54f9668ac21c5b5e4529e66b76e0fac4 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Mon, 27 May 2024 12:43:42 +0200 Subject: [PATCH 04/27] removed unneeded darwin files --- .../infraolly/process/harvester_mock_test.go | 40 - pkg/internal/infraolly/process/process.go | 42 - .../infraolly/process/process_mock_test.go | 144 - .../process_retriever_cached_darwin.go | 428 -- .../process_retriever_cached_darwin_test.go | 4919 ----------------- .../infraolly/process/sampler_darwin.go | 170 - .../infraolly/process/sampler_darwin_test.go | 150 - .../infraolly/process/sampler_unix_test.go | 28 - .../infraolly/process/snapshot_darwin.go | 247 - .../infraolly/process/snapshot_darwin_test.go | 431 -- 10 files changed, 6599 deletions(-) delete mode 100644 pkg/internal/infraolly/process/harvester_mock_test.go delete mode 100644 pkg/internal/infraolly/process/process.go delete mode 100644 pkg/internal/infraolly/process/process_mock_test.go delete mode 100644 pkg/internal/infraolly/process/process_retriever_cached_darwin.go delete mode 100644 pkg/internal/infraolly/process/process_retriever_cached_darwin_test.go delete mode 100644 pkg/internal/infraolly/process/sampler_darwin.go delete mode 100644 pkg/internal/infraolly/process/sampler_darwin_test.go delete mode 100644 pkg/internal/infraolly/process/sampler_unix_test.go delete mode 100644 pkg/internal/infraolly/process/snapshot_darwin.go delete mode 100644 pkg/internal/infraolly/process/snapshot_darwin_test.go diff --git a/pkg/internal/infraolly/process/harvester_mock_test.go b/pkg/internal/infraolly/process/harvester_mock_test.go deleted file mode 100644 index 9f8d8ca6d..000000000 --- a/pkg/internal/infraolly/process/harvester_mock_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020 New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 -//go:build linux || darwin -// +build linux darwin - -package process - -import ( - "github.com/stretchr/testify/mock" -) - -type HarvesterMock struct { - mock.Mock -} - -func (h *HarvesterMock) Pids() ([]int32, error) { - args := h.Called() - - return args.Get(0).([]int32), args.Error(1) -} - -func (h *HarvesterMock) ShouldReturnPids(pids []int32, err error) { - h. - On("Pids"). - Once(). - Return(pids, err) -} - -func (h *HarvesterMock) Do(pid int32, elapsedSeconds float64) (*Sample, error) { - args := h.Called(pid, elapsedSeconds) - - return args.Get(0).(*Sample), args.Error(1) -} - -func (h *HarvesterMock) ShouldDo(pid int32, elapsedSeconds float64, sample *Sample, err error) { - h. - On("Do", pid, elapsedSeconds). - Once(). - Return(sample, err) -} diff --git a/pkg/internal/infraolly/process/process.go b/pkg/internal/infraolly/process/process.go deleted file mode 100644 index d23b1c963..000000000 --- a/pkg/internal/infraolly/process/process.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package process - -import ( - "github.com/shirou/gopsutil/v3/cpu" - "github.com/shirou/gopsutil/v3/process" -) - -// Process it's an interface to abstract gopsutil process so we can mock it and test not coupling to infra -type Process interface { - Username() (string, error) - Name() (string, error) - Cmdline() (string, error) - ProcessId() int32 - Parent() (Process, error) - NumThreads() (int32, error) - Status() ([]string, error) - MemoryInfo() (*process.MemoryInfoStat, error) - CPUPercent() (float64, error) - Times() (*cpu.TimesStat, error) -} - -// ProcessWrapper is necessary to implement the interface as gopsutil process is not exporting Pid() -type ProcessWrapper struct { - *process.Process -} - -// ProcessId returns the Pid of the process -func (p *ProcessWrapper) ProcessId() int32 { - return p.Process.Pid -} - -// Parent return the process' parent -func (p *ProcessWrapper) Parent() (Process, error) { - par, err := p.Process.Parent() - if err != nil { - return &ProcessWrapper{}, err - } - return &ProcessWrapper{par}, nil -} diff --git a/pkg/internal/infraolly/process/process_mock_test.go b/pkg/internal/infraolly/process/process_mock_test.go deleted file mode 100644 index 3a155081d..000000000 --- a/pkg/internal/infraolly/process/process_mock_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package process - -import ( - "github.com/shirou/gopsutil/v3/cpu" - "github.com/shirou/gopsutil/v3/process" - "github.com/stretchr/testify/mock" -) - -type ProcessMock struct { - mock.Mock -} - -func (s *ProcessMock) Username() (string, error) { - args := s.Called() - - return args.String(0), args.Error(1) -} - -func (s *ProcessMock) ShouldReturnUsername(username string, err error) { - s. - On("Username"). - Once(). - Return(username, err) -} - -func (s *ProcessMock) Name() (string, error) { - args := s.Called() - - return args.String(0), args.Error(1) -} - -func (s *ProcessMock) ShouldReturnName(name string, err error) { - s. - On("Name"). - Once(). - Return(name, err) -} - -func (s *ProcessMock) Cmdline() (string, error) { - args := s.Called() - - return args.String(0), args.Error(1) -} - -func (s *ProcessMock) ShouldReturnCmdLine(cmdLine string, err error) { - s. - On("Cmdline"). - Once(). - Return(cmdLine, err) -} - -func (s *ProcessMock) ProcessId() int32 { - args := s.Called() - - return args.Get(0).(int32) -} - -func (s *ProcessMock) ShouldReturnProcessId(processId int32) { - s.ShouldReturnProcessIdMultipleTimes(processId, 1) -} - -func (s *ProcessMock) ShouldReturnProcessIdMultipleTimes(processId int32, times int) { - s. - On("ProcessId"). - Times(times). - Return(processId) -} - -func (s *ProcessMock) Parent() (Process, error) { - args := s.Called() - - return args.Get(0).(Process), args.Error(1) -} - -func (s *ProcessMock) ShouldReturnParent(process Process, err error) { - s. - On("Parent"). - Once(). - Return(process, err) -} - -func (s *ProcessMock) NumThreads() (int32, error) { - args := s.Called() - - return args.Get(0).(int32), args.Error(1) -} - -func (s *ProcessMock) ShouldReturnNumThreads(num int32, err error) { - s. - On("NumThreads"). - Once(). - Return(num, err) -} - -func (s *ProcessMock) Status() ([]string, error) { - args := s.Called() - - return args.Get(0).([]string), args.Error(1) -} -func (s *ProcessMock) ShouldReturnStatus(status []string, err error) { - s. - On("Status"). - Once(). - Return(status, err) -} - -func (s *ProcessMock) MemoryInfo() (*process.MemoryInfoStat, error) { - args := s.Called() - - return args.Get(0).(*process.MemoryInfoStat), args.Error(1) -} -func (s *ProcessMock) ShouldReturnMemoryInfo(memInfo *process.MemoryInfoStat, err error) { - s. - On("MemoryInfo"). - Once(). - Return(memInfo, err) -} - -func (s *ProcessMock) CPUPercent() (float64, error) { - args := s.Called() - - return args.Get(0).(float64), args.Error(1) -} -func (s *ProcessMock) ShouldReturnCPUPercent(percent float64, err error) { - s. - On("CPUPercent"). - Once(). - Return(percent, err) -} - -func (s *ProcessMock) Times() (*cpu.TimesStat, error) { - args := s.Called() - - return args.Get(0).(*cpu.TimesStat), args.Error(1) -} -func (s *ProcessMock) ShouldReturnTimes(times *cpu.TimesStat, err error) { - s. - On("Times"). - Once(). - Return(times, err) -} diff --git a/pkg/internal/infraolly/process/process_retriever_cached_darwin.go b/pkg/internal/infraolly/process/process_retriever_cached_darwin.go deleted file mode 100644 index da059c8d0..000000000 --- a/pkg/internal/infraolly/process/process_retriever_cached_darwin.go +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package process - -import ( - "fmt" - "github.com/shirou/gopsutil/v3/cpu" - "github.com/shirou/gopsutil/v3/process" - "os/exec" - "strconv" - "strings" - "sync" - "time" -) - -const ( - ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) -) - -type CommandRunner func(command string, stdin string, arguments ...string) (string, error) - -var commandRunner CommandRunner = helpers.RunCommand - -// ProcessRetrieverCached acts as a process.ProcessRetriever and retrieves a process.Process from its pid -// it uses an in-memory cache to store the information of all running processes with a short ttl enough to -// read information of all processes with just 2 calls to ps -// it uses c&p parts of code of gopsutil which was the 1st approach but makes too may system calls -type ProcessRetrieverCached struct { - cache cache -} - -func NewProcessRetrieverCached(ttl time.Duration) *ProcessRetrieverCached { - return &ProcessRetrieverCached{cache: cache{ttl: ttl}} -} - -// ProcessById returns a process.Process by pid or error if not found -func (s *ProcessRetrieverCached) ProcessById(pid int32) (Process, error) { - procs, err := s.processesFromCache() - if err != nil { - return nil, err - } - if proc, ok := procs[pid]; ok { - return &proc, nil - } - - return nil, fmt.Errorf("cannot find process with pid %v", pid) -} - -// processesFromCache returns all processes running. These will be retrieved and cached for cache.ttl time -func (s *ProcessRetrieverCached) processesFromCache() (map[int32]psItem, error) { - s.cache.Lock() - defer s.cache.Unlock() - - if s.cache.expired() { - psBin, err := exec.LookPath("ps") - if err != nil { - return nil, err - } - // it's easier to get the thread num per process from different call - processesThreads, err := s.getProcessThreads(psBin) - if err != nil { - return nil, err - } - // it's easier to get the thread num per process from different call - fullCmd, err := s.getProcessFullCmd(psBin) - if err != nil { - return nil, err - } - //get all processes and inject numThreads - items, err := s.retrieveProcesses(psBin) - if err != nil { - return nil, err - } - items = addThreadsAndCmdToPsItems(items, processesThreads, fullCmd) - s.cache.update(items) - } - - return s.cache.items, nil -} - -func addThreadsAndCmdToPsItems(items map[int32]psItem, processesThreads map[int32]int32, processesCmd map[int32]string) map[int32]psItem { - itemsWithAllInfo := make(map[int32]psItem) - for pid, item := range items { - if numThreads, ok := processesThreads[pid]; ok { - item.numThreads = numThreads - } - if cmd, ok := processesCmd[pid]; ok { - item.cmdLine = cmd - } - itemsWithAllInfo[pid] = item - } - return itemsWithAllInfo -} - -func (s *ProcessRetrieverCached) retrieveProcesses(psBin string) (map[int32]psItem, error) { - - // get all processes info - args := []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"} - out, err := commandRunner(psBin, "", args...) - if err != nil { - return nil, err - } - - lines := strings.Split(out, "\n") - items := make(map[int32]psItem) - for _, line := range lines[1:] { - var lineItems []string - for _, lineItem := range strings.Split(line, " ") { - if lineItem == "" { - continue - } - lineItems = append(lineItems, strings.TrimSpace(lineItem)) - } - if len(lineItems) > 10 { - pid, _ := strconv.Atoi(lineItems[0]) - ppid, _ := strconv.Atoi(lineItems[1]) - user := lineItems[2] - state := lineItems[3] - utime := lineItems[4] - stime := lineItems[5] - etime := lineItems[6] - rss, _ := strconv.ParseInt(lineItems[7], 10, 64) - vsize, _ := strconv.ParseInt(lineItems[8], 10, 64) - pagein, _ := strconv.ParseInt(lineItems[9], 10, 64) - command := strings.Join(lineItems[10:], " ") - - item := psItem{ - pid: int32(pid), - ppid: int32(ppid), - username: user, - state: []string{convertStateToGopsutilState(state[0:1])}, - utime: utime, - stime: stime, - etime: etime, - rss: rss, - vsize: vsize, - pagein: pagein, - command: command, - } - items[int32(pid)] = item - } else { - mplog.WithField("ps_output", out).Error("ps output is expected to have >10 columns") - } - } - return items, nil -} - -// convertStateToGopsutilState converts ps state to gopsutil v3 state -// C&P from https://github.com/shirou/gopsutil/blob/v3.21.11/v3/process/process.go#L575 -func convertStateToGopsutilState(letter string) string { - // Sources - // Darwin: http://www.mywebuniversity.com/Man_Pages/Darwin/man_ps.html - // FreeBSD: https://www.freebsd.org/cgi/man.cgi?ps - // Linux https://man7.org/linux/man-pages/man1/ps.1.html - // OpenBSD: https://man.openbsd.org/ps.1#state - // Solaris: https://github.com/collectd/collectd/blob/1da3305c10c8ff9a63081284cf3d4bb0f6daffd8/src/processes.c#L2115 - switch letter { - case "A": - return process.Daemon - case "D", "U": - return process.Blocked - case "E": - return process.Detached - case "I": - return process.Idle - case "L": - return process.Lock - case "O": - return process.Orphan - case "R": - return process.Running - case "S": - return process.Sleep - case "T", "t": - // "t" is used by Linux to signal stopped by the debugger during tracing - return process.Stop - case "W": - return process.Wait - case "Y": - return process.System - case "Z": - return process.Zombie - default: - return process.UnknownState - } -} - -func (s *ProcessRetrieverCached) getProcessThreads(psBin string) (map[int32]int32, error) { - // get all processes info with threads - args := []string{"ax", "-M", "-c"} - out, err := commandRunner(psBin, "", args...) - if err != nil { - return nil, err - } - - lines := strings.Split(out, "\n") - processThreads := make(map[int32]int32) - for _, line := range lines[1:] { - if len(line) > 0 && line[0] != ' ' { - //we exclude main process for simplicity - continue - } - for _, lineItem := range strings.Split(line, " ") { - if lineItem == "" { - continue - } - pidAsInt, err := strconv.Atoi(strings.TrimSpace(lineItem)) - if err != nil { - mplog.Warnf("pid %v doesn't look like an int", pidAsInt) - continue - } - pid := int32(pidAsInt) - if _, ok := processThreads[pid]; !ok { - processThreads[pid] = 1 //main process already included - } - processThreads[pid]++ - //we are only interested in pid so break and process next line - break - } - } - - return processThreads, nil -} - -// getProcessFullCmd retrieves the full process command line w/o arguments (as commands can have spaces in mac :( ) -func (s *ProcessRetrieverCached) getProcessFullCmd(psBin string) (map[int32]string, error) { - // get all processes info with threads - args := []string{"ax", "-o", "pid,command"} - out, err := commandRunner(psBin, "", args...) - if err != nil { - return nil, err - } - - lines := strings.Split(out, "\n") - processThreads := make(map[int32]string) - for _, line := range lines[1:] { - var lineItems []string - for _, lineItem := range strings.Split(line, " ") { - if lineItem == "" { - continue - } - lineItems = append(lineItems, strings.TrimSpace(lineItem)) - } - if len(lineItems) > 1 { - pidAsInt, _ := strconv.Atoi(lineItems[0]) - cmd := strings.Join(lineItems[1:], " ") - pid := int32(pidAsInt) - if _, ok := processThreads[pid]; !ok { - processThreads[pid] = cmd - } - } - } - - return processThreads, nil -} - -// createTime retrieves ceate time from ps output etime field -// it is a c&p of gopsutil process.CreateTimeWithContext -func createTime(etime string) (int64, error) { - elapsedSegments := strings.Split(strings.Replace(etime, "-", ":", 1), ":") - var elapsedDurations []time.Duration - for i := len(elapsedSegments) - 1; i >= 0; i-- { - p, err := strconv.ParseInt(elapsedSegments[i], 10, 0) - if err != nil { - return 0, err - } - elapsedDurations = append(elapsedDurations, time.Duration(p)) - } - - var elapsed = elapsedDurations[0] * time.Second - if len(elapsedDurations) > 1 { - elapsed += elapsedDurations[1] * time.Minute - } - if len(elapsedDurations) > 2 { - elapsed += elapsedDurations[2] * time.Hour - } - if len(elapsedDurations) > 3 { - elapsed += elapsedDurations[3] * time.Hour * 24 - } - - start := time.Now().Add(-elapsed) - return start.Unix() * 1000, nil -} - -// times retrieves ceate time from ps output utime and stime fields -// it is a c&p of gopsutil process.TimesWithContext -func times(utime string, stime string) (*cpu.TimesStat, error) { - uCpuTimes, err := convertCPUTimes(utime) - if err != nil { - return nil, err - } - sCpuTimes, err := convertCPUTimes(stime) - if err != nil { - return nil, err - } - - ret := &cpu.TimesStat{ - CPU: "cpu", - User: uCpuTimes, - System: sCpuTimes, - } - return ret, nil -} - -// convertCPUTimes converts ps format cputime to time units that are in USER_HZ or Jiffies -// it is a c&p of gopsutil process.convertCPUTimes -func convertCPUTimes(s string) (ret float64, err error) { - var t int - var _tmp string - if strings.Contains(s, ":") { - _t := strings.Split(s, ":") - hour, err := strconv.Atoi(_t[0]) - if err != nil { - return ret, err - } - t += hour * 60 * 100 - _tmp = _t[1] - } else { - _tmp = s - } - - _t := strings.Split(_tmp, ".") - if err != nil { - return ret, err - } - h, _ := strconv.Atoi(_t[0]) - t += h * 100 - h, _ = strconv.Atoi(_t[1]) - t += h - return float64(t) / ClockTicks, nil -} - -// psItem stores the information of a process and implements process.Process -type psItem struct { - pid int32 - ppid int32 - numThreads int32 - username string - state []string - command string - cmdLine string - utime string - stime string - etime string - rss int64 - vsize int64 - pagein int64 -} - -func (p *psItem) Username() (string, error) { - return p.username, nil -} - -func (p *psItem) Name() (string, error) { - return p.command, nil -} - -func (p *psItem) Cmdline() (string, error) { - return p.cmdLine, nil -} - -func (p *psItem) ProcessId() int32 { - return p.pid -} - -func (p *psItem) Parent() (Process, error) { - return &psItem{pid: p.ppid}, nil -} - -func (p *psItem) NumThreads() (int32, error) { - return p.numThreads, nil -} - -func (p *psItem) Status() ([]string, error) { - return p.state, nil -} - -func (p *psItem) MemoryInfo() (*process.MemoryInfoStat, error) { - return &process.MemoryInfoStat{ - RSS: uint64(p.rss) * 1024, - VMS: uint64(p.vsize) * 1024, - Swap: uint64(p.pagein), - }, nil -} - -// CPUPercent returns how many percent of the CPU time this process uses -// it is a c&p of gopsutil process.CPUPercent -func (p *psItem) CPUPercent() (float64, error) { - crt_time, err := createTime(p.etime) - if err != nil { - return 0, err - } - - cput, err := p.Times() - if err != nil { - return 0, err - } - - created := time.Unix(0, crt_time*int64(time.Millisecond)) - totalTime := time.Since(created).Seconds() - if totalTime <= 0 { - return 0, nil - } - - return 100 * cput.Total() / totalTime, nil -} - -func (p *psItem) Times() (*cpu.TimesStat, error) { - return times(p.utime, p.stime) -} - -// cache in-memory cache not to call ps for every process -type cache struct { - ttl time.Duration - sync.Mutex - items map[int32]psItem - createdAt time.Time -} - -func (c *cache) expired() bool { - return c == nil || c.createdAt.IsZero() || time.Since(c.createdAt) > c.ttl -} - -func (c *cache) update(items map[int32]psItem) { - c.items = items - c.createdAt = time.Now() -} diff --git a/pkg/internal/infraolly/process/process_retriever_cached_darwin_test.go b/pkg/internal/infraolly/process/process_retriever_cached_darwin_test.go deleted file mode 100644 index 503b3998f..000000000 --- a/pkg/internal/infraolly/process/process_retriever_cached_darwin_test.go +++ /dev/null @@ -1,4919 +0,0 @@ -// Copyright New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package process - -import ( - "errors" - "github.com/shirou/gopsutil/v3/cpu" - process2 "github.com/shirou/gopsutil/v3/process" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "math" - "testing" - "time" -) - -var psOut = []string{ - `PID PPID USER STAT UTIME STIME ELAPSED RSS VSZ PAGEIN COMMAND - 1 0 root Ss 3:56.38 18:41.21 07-21:03:49 12000 4481064 0 launchd - 68 1 joe S 0:20.99 0:38.18 07-21:03:41 920 4471000 0 Google Chrome - 73 1 root Ss 2:06.17 4:13.62 07-21:03:41 3108 4477816 0 fseventsd - 74 48 pam Ss 0:00.02 0:00.09 07-21:03:41 64 4322064 0 systemstats`, - - `PID PPID USER STAT UTIME STIME ELAPSED RSS VSZ PAGEIN COMMAND - 1 0 root Ss 3:58.38 18:51.21 07-21:04:49 12200 4482064 0 launchd - 68 1 joe Ss 0:23.99 0:48.18 07-21:04:41 910 4473000 0 Google Chrome - 74 48 pam Ss 0:00.10 0:20.09 07-21:04:41 84 4324064 0 systemstats`, -} - -var psCmdOut = []string{ - `PID COMMAND - 1 /sbin/launchd - 68 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/94.0.4606.61/Helpers/chrome_crashpad_handler --monitor-self-annotation=ptype=crashpad-handler --url=https://clients2.google.com/cr/report --annotation=channel= --annotation=plat=OS X --annotation=prod=Chrome_Mac --annotation=ver=94.0.4606.61 --handshake-fd=6 - 73 /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/Support/fseventsd - 74 /usr/sbin/systemstats --daemon`, - - `PID COMMAND - 1 /sbin/launchd - 68 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/94.0.4606.61/Helpers/chrome_crashpad_handler --monitor-self-annotation=ptype=crashpad-handler --url=https://clients2.google.com/cr/report --annotation=channel= --annotation=plat=OS X --annotation=prod=Chrome_Mac --annotation=ver=94.0.4606.61 --handshake-fd=6 - 74 /usr/sbin/systemstats --daemon`, -} - -var psThreadsOut = []string{ - `USER PID TT %CPU STAT PRI STIME UTIME COMMAND -root 1 ?? 0.0 S 31T 0:00.36 0:00.08 launchd - 1 0.0 S 20T 0:00.12 0:00.01 - 1 0.0 S 37T 0:00.00 0:00.00 - 1 0.0 S 37T 0:00.00 0:00.00 -joe 68 ?? 0.0 S 4T 0:01.13 0:00.30 syslogd - 68 0.0 S 4T 0:00.00 0:00.00 -root 73 ?? 0.0 S 4T 0:01.13 0:00.30 fseventsd -pam 74 0.0 S 4T 0:00.00 0:00.00 systemstats`, - - `USER PID TT %CPU STAT PRI STIME UTIME COMMAND -root 1 ?? 0.0 S 31T 0:00.36 0:00.08 launchd - 1 0.0 S 20T 0:00.12 0:00.01 - 1 0.0 S 37T 0:00.00 0:00.00 - 1 0.0 S 37T 0:00.00 0:00.00 -joe 68 ?? 0.0 S 4T 0:01.13 0:00.30 syslogd - 68 0.0 S 4T 0:00.00 0:00.00 -pam 74 0.0 S 4T 0:00.00 0:00.00 systemstats`, -} - -// commandRunnerMock mocks CommandRunner and so we can mock ps results -type commandRunnerMock struct { - mock.Mock -} - -func (c *commandRunnerMock) run(command string, stdin string, arguments ...string) (string, error) { - args := c.Called(command, stdin, arguments) - - return args.String(0), args.Error(1) -} - -func (c *commandRunnerMock) ShouldRunCommand(command string, stdin string, arguments []string, output string, err error) { - c. - On("run", command, stdin, arguments). - Once(). - Return(output, err) -} - -func (c *commandRunnerMock) ShouldRunCommandMultipleTimes(command string, stdin string, arguments []string, output string, err error) { - c. - On("run", command, stdin, arguments). - Return(output, err) -} - -func Test_ProcessRetrieverCached_InvalidPsOutputShouldNotBreakTheInternet(t *testing.T) { - - tests := []struct { - name string - psOut string - psThreadsOut string - psCmdOut string - }{ - { - name: "empty content in ps", - psOut: "", - psThreadsOut: psThreadsOut[0], - psCmdOut: psCmdOut[0], - }, - { - name: "empty content in ps threads", - psOut: psOut[0], - psThreadsOut: "", - psCmdOut: psCmdOut[0], - }, - { - name: "empty content in ps cmd", - psOut: psOut[0], - psThreadsOut: psThreadsOut[0], - psCmdOut: "", - }, - { - name: "some invalid data", - psOut: "some invalid data\nin\nmultiple lines", - }, - { - name: "some missing columns", - psOut: `PID PPID USER STAT RSS VSZ PAGEIN COMMAND - 1 0 root Ss 12000 4481064 0 /sbin/launchd - 68 1 joe S 920 4471000 0 /usr/sbin/syslogd - 73 1 root Ss 3108 4477816 0 /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/Support/fseventsd - 74 48 pam Ss 64 4322064 0 /usr/sbin/systemstats --daemon`, - }, - } - - ttl := time.Second * 0 - ret := NewProcessRetrieverCached(ttl) - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cmdRunMock := &commandRunnerMock{} - commandRunner = cmdRunMock.run - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, tt.psThreadsOut, nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, tt.psCmdOut, nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, tt.psOut, nil) - _, err := ret.ProcessById(999) - assert.EqualError(t, err, "cannot find process with pid 999") - //mocked objects assertions - mock.AssertExpectationsForObjects(t, cmdRunMock) - }) - } -} - -func Test_ProcessRetrieverCached_ProcessById_PsErrorOnThreads(t *testing.T) { - expectedError := errors.New("this is an error") - cmdRunMock := &commandRunnerMock{} - commandRunner = cmdRunMock.run - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], expectedError) - - ttl := time.Second * 0 - ret := NewProcessRetrieverCached(ttl) - _, err := ret.ProcessById(68) - assert.Equal(t, expectedError, err) - - //mocked objects assertions - mock.AssertExpectationsForObjects(t, cmdRunMock) -} - -func Test_ProcessRetrieverCached_ProcessById_PsErrorOnPsInfo(t *testing.T) { - expectedError := errors.New("this is an error") - cmdRunMock := &commandRunnerMock{} - commandRunner = cmdRunMock.run - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[0], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], expectedError) - - ttl := time.Second * 0 - ret := NewProcessRetrieverCached(ttl) - _, err := ret.ProcessById(68) - assert.Equal(t, expectedError, err) - - //mocked objects assertions - mock.AssertExpectationsForObjects(t, cmdRunMock) -} - -func Test_ProcessRetrieverCached_ProcessById_NonExistingProcess(t *testing.T) { - cmdRunMock := &commandRunnerMock{} - commandRunner = cmdRunMock.run - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[0], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], nil) - - ttl := time.Second * 0 - ret := NewProcessRetrieverCached(ttl) - _, err := ret.ProcessById(99999999) - assert.EqualError(t, err, "cannot find process with pid 99999999") - - //mocked objects assertions - mock.AssertExpectationsForObjects(t, cmdRunMock) -} - -func Test_ProcessRetrieverCached_ProcessById_ExistingProcess(t *testing.T) { - cmdRunMock := &commandRunnerMock{} - commandRunner = cmdRunMock.run - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[0], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], nil) - - ttl := time.Second * 10 - ret := NewProcessRetrieverCached(ttl) - process, err := ret.ProcessById(68) - assert.Nil(t, err) - assert.Equal(t, int32(68), process.ProcessId()) - assert.Equal(t, "Google Chrome", noError(process.Name())) - assert.Equal(t, "/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/94.0.4606.61/Helpers/chrome_crashpad_handler --monitor-self-annotation=ptype=crashpad-handler --url=https://clients2.google.com/cr/report --annotation=channel= --annotation=plat=OS X --annotation=prod=Chrome_Mac --annotation=ver=94.0.4606.61 --handshake-fd=6", - noError(process.Cmdline())) - assert.Equal(t, "joe", noError(process.Username())) - assert.Equal(t, int32(1), noError(process.Parent()).(Process).ProcessId()) - assert.Equal(t, []string{process2.Sleep}, noError(process.Status())) - assert.Equal(t, &cpu.TimesStat{CPU: "cpu", User: 20.99, System: 38.18}, noError(process.Times())) - assert.Equal(t, &process2.MemoryInfoStat{RSS: uint64(920) * 1024, VMS: uint64(4471000) * 1024, Swap: uint64(0)}, noError(process.MemoryInfo())) - assert.Equal(t, int32(2), noError(process.NumThreads())) - assert.Equal(t, 0.00869, math.Round(noError(process.CPUPercent()).(float64)*100000)/100000) - - //mocked objects assertions - mock.AssertExpectationsForObjects(t, cmdRunMock) -} - -func Test_ProcessRetrieverCached_processesFromCache_reuseCacheIfTtlNotExpired(t *testing.T) { - - cmdRunMock := &commandRunnerMock{} - commandRunner = cmdRunMock.run - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[0], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], nil) - - ttl := time.Second * 10 - ret := NewProcessRetrieverCached(ttl) - itemsFirstCall, err := ret.processesFromCache() - assert.Nil(t, err) - itemsSecondCall, err := ret.processesFromCache() - assert.Nil(t, err) - assert.Equal(t, itemsFirstCall, itemsSecondCall) - - //mocked objects assertions - mock.AssertExpectationsForObjects(t, cmdRunMock) -} - -func Test_ProcessRetrieverCached_processesFromCache_cleanCacheIfTtlExpired(t *testing.T) { - - cmdRunMock := &commandRunnerMock{} - commandRunner = cmdRunMock.run - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[0], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[0], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut[1], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-o", "pid,command"}, psCmdOut[1], nil) - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[1], nil) - - ttl := time.Second * 0 - ret := NewProcessRetrieverCached(ttl) - itemsFirstCall, err := ret.processesFromCache() - assert.Nil(t, err) - itemsSecondCall, err := ret.processesFromCache() - assert.Nil(t, err) - assert.Len(t, itemsFirstCall, 4) - assert.Len(t, itemsSecondCall, 3) - assert.Equal(t, itemsSecondCall[74].stime, "0:20.09") - - //mocked objects assertions - mock.AssertExpectationsForObjects(t, cmdRunMock) -} - -func Test_addThreadsAndCmdToPsItems(t *testing.T) { - - tests := []struct { - name string - items map[int32]psItem - processesThreads map[int32]int32 - processesCmd map[int32]string - expectedItems map[int32]psItem - }{ - { - name: "empty items", - items: map[int32]psItem{}, - processesThreads: map[int32]int32{}, - processesCmd: map[int32]string{}, - expectedItems: map[int32]psItem{}, - }, - { - name: "empty items but info in threads and cmd", - items: map[int32]psItem{}, - processesThreads: map[int32]int32{1: 12, 343: 23}, - processesCmd: map[int32]string{1: "/some/command"}, - expectedItems: map[int32]psItem{}, - }, - { - name: "non items should not change original", - items: map[int32]psItem{1: {pid: 1, command: "some_command"}, 2: {pid: 2, command: "another_command"}}, - processesThreads: map[int32]int32{1: 12, 2: 4, 5: 343}, - processesCmd: map[int32]string{1: "/bin/some_command", 5: "already_dead_command", 2: "/bin/another_command"}, - expectedItems: map[int32]psItem{ - 1: { - pid: 1, - command: "some_command", - numThreads: 12, - cmdLine: "/bin/some_command", - }, 2: { - pid: 2, - command: "another_command", - numThreads: 4, - cmdLine: "/bin/another_command", - }}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - origItems := copyItems(tt.items) - fullInfoItems := addThreadsAndCmdToPsItems(tt.items, tt.processesThreads, tt.processesCmd) - assert.Equal(t, tt.expectedItems, fullInfoItems) - assert.Equal(t, tt.items, origItems) - }) - } -} - -func copyItems(origItems map[int32]psItem) map[int32]psItem { - dest := make(map[int32]psItem) - for pid, item := range origItems { - dest[pid] = item - } - return dest -} - -func Test_ProcessRetrieverCached_retrieveProcesses(t *testing.T) { - expected := map[int32]psItem{ - - 1: { - pid: 1, - ppid: 0, - username: "root", - state: []string{process2.Sleep}, - utime: "3:56.38", - stime: "18:41.21", - etime: "07-21:03:49", - rss: 12000, - vsize: 4481064, - pagein: 0, - numThreads: 0, - command: "launchd", - cmdLine: "", - }, - 68: { - pid: 68, - ppid: 1, - username: "joe", - state: []string{process2.Sleep}, - utime: "0:20.99", - stime: "0:38.18", - etime: "07-21:03:41", - rss: 920, - vsize: 4471000, - pagein: 0, - numThreads: 0, - command: "Google Chrome", - cmdLine: "", - }, - 73: { - pid: 73, - ppid: 1, - username: "root", - state: []string{process2.Sleep}, - utime: "2:06.17", - stime: "4:13.62", - etime: "07-21:03:41", - rss: 3108, - vsize: 4477816, - pagein: 0, - numThreads: 0, - command: "fseventsd", - cmdLine: "", - }, - 74: { - pid: 74, - ppid: 48, - username: "pam", - state: []string{process2.Sleep}, - utime: "0:00.02", - stime: "0:00.09", - etime: "07-21:03:41", - rss: 64, - vsize: 4322064, - pagein: 0, - numThreads: 0, - command: "systemstats", - cmdLine: "", - }, - } - - cmdRunMock := &commandRunnerMock{} - commandRunner = cmdRunMock.run - cmdRunMock.ShouldRunCommand("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut[0], nil) - - ttl := time.Second * 10 - ret := NewProcessRetrieverCached(ttl) - psItems, err := ret.retrieveProcesses("/bin/ps") - - assert.Nil(t, err) - assert.Len(t, psItems, 4) - for pid, expectedPsItem := range expected { - assert.Equal(t, psItems[pid], expectedPsItem) - } - //mocked objects assertions - mock.AssertExpectationsForObjects(t, cmdRunMock) -} - -func benchmark_ProcessRetrieverCached_getProcessThreads(psThreadsOut string, b *testing.B) { - cmdRunMock := &commandRunnerMock{} - commandRunner = cmdRunMock.run - cmdRunMock.ShouldRunCommandMultipleTimes("/bin/ps", "", []string{"ax", "-M", "-c"}, psThreadsOut, nil) - - ttl := time.Second * 10 - ret := NewProcessRetrieverCached(ttl) - - // run the Fib function b.N times - for n := 0; n < b.N; n++ { - ret.getProcessThreads("/bin/ps") - } -} - -func Benchmark_ProcessRetrieverCached_getProcessThreads10(b *testing.B) { - benchmark_ProcessRetrieverCached_getProcessThreads(psOutThreads10, b) -} -func Benchmark_ProcessRetrieverCached_getProcessThreads100(b *testing.B) { - benchmark_ProcessRetrieverCached_getProcessThreads(psOutThreads100, b) -} -func Benchmark_ProcessRetrieverCached_getProcessThreads1000(b *testing.B) { - benchmark_ProcessRetrieverCached_getProcessThreads(psOutThreads500, b) -} - -func benchmark_ProcessRetrieverCached_retrieveProcesses(psOut string, b *testing.B) { - cmdRunMock := &commandRunnerMock{} - commandRunner = cmdRunMock.run - cmdRunMock.ShouldRunCommandMultipleTimes("/bin/ps", "", []string{"ax", "-c", "-o", "pid,ppid,user,state,utime,stime,etime,rss,vsize,pagein,command"}, psOut, nil) - - ttl := time.Second * 0 - ret := NewProcessRetrieverCached(ttl) - - // run the Fib function b.N times - for n := 0; n < b.N; n++ { - ret.retrieveProcesses("/bin/ps") - } -} - -func Benchmark_ProcessRetrieverCached_retrieveProcesses10(b *testing.B) { - benchmark_ProcessRetrieverCached_retrieveProcesses(psOut10, b) -} -func Benchmark_ProcessRetrieverCached_retrieveProcesses100(b *testing.B) { - benchmark_ProcessRetrieverCached_retrieveProcesses(psOut100, b) -} -func Benchmark_ProcessRetrieverCached_retrieveProcesses1000(b *testing.B) { - benchmark_ProcessRetrieverCached_retrieveProcesses(psOut500, b) -} - -func noError(i interface{}, _ error) interface{} { - return i -} - -var psOut10 = `PID PPID USER STAT UTIME STIME ELAPSED RSS VSZ PAGEIN COMMAND - 1 0 root Ss 4:11.67 20:07.54 08-14:58:33 14376 4480016 0 /sbin/launchd - 68 1 root Ss 0:22.59 0:41.16 08-14:58:25 852 4471000 0 /usr/bin/some_command with some parameters - 69 1 root Ss 0:21.18 0:38.61 08-14:58:25 4416 4503784 0 /usr/bin/some_command with some parameters - 72 1 root Ss 0:01.86 0:04.56 08-14:58:25 464 4403040 0 /usr/bin/some_command with some parameters - 73 1 root Ss 2:15.80 4:35.94 08-14:58:25 2976 4477292 0 /usr/bin/some_command with some parameters - 74 1 root Ss 0:00.02 0:00.10 08-14:58:25 64 4322064 0 /usr/bin/some_command with some parameters - 75 1 root Ss 0:13.98 0:11.16 08-14:58:25 3572 4504692 0 /usr/bin/some_command with some parameters - 81 1 root Ss 0:00.02 0:00.10 08-14:58:25 72 4312320 0 /usr/bin/some_command with some parameters - 82 1 root Ss 2:38.05 1:57.49 08-14:58:25 4396 4515104 0 /usr/bin/some_command with some parameters - 84 1 root Ss 0:22.27 0:41.25 08-14:58:25 3224 4506768 0 /usr/bin/some_command with some parameters` - -var psOut100 = `PID PPID USER STAT UTIME STIME ELAPSED RSS VSZ PAGEIN COMMAND - 1 0 root Ss 4:11.67 20:07.54 08-14:58:33 14376 4480016 0 /sbin/launchd - 68 1 root Ss 0:22.59 0:41.16 08-14:58:25 852 4471000 0 /usr/bin/some_command with some parameters - 69 1 root Ss 0:21.18 0:38.61 08-14:58:25 4416 4503784 0 /usr/bin/some_command with some parameters - 72 1 root Ss 0:01.86 0:04.56 08-14:58:25 464 4403040 0 /usr/bin/some_command with some parameters - 73 1 root Ss 2:15.80 4:35.94 08-14:58:25 2976 4477292 0 /usr/bin/some_command with some parameters - 74 1 root Ss 0:00.02 0:00.10 08-14:58:25 64 4322064 0 /usr/bin/some_command with some parameters - 75 1 root Ss 0:13.98 0:11.16 08-14:58:25 3572 4504692 0 /usr/bin/some_command with some parameters - 81 1 root Ss 0:00.02 0:00.10 08-14:58:25 72 4312320 0 /usr/bin/some_command with some parameters - 82 1 root Ss 2:38.05 1:57.49 08-14:58:25 4396 4515104 0 /usr/bin/some_command with some parameters - 84 1 root Ss 0:22.27 0:41.25 08-14:58:25 3224 4506768 0 /usr/bin/some_command with some parameters - 86 1 root Ss 0:39.19 1:53.93 08-14:58:25 4808 4503136 0 /usr/bin/some_command with some parameters - 88 1 root Ss 21:00.91 24:52.86 08-14:58:25 17640 4579384 0 /usr/bin/some_command with some parameters - 91 1 root Ss 0:10.63 0:15.49 08-14:58:25 1632 4515168 0 /usr/bin/some_command with some parameters - 93 1 root Ss 2:11.10 2:51.66 08-14:58:25 10816 4586644 0 /usr/bin/some_command with some parameters - 99 1 root Ss 0:03.12 0:04.29 08-14:58:25 1584 4469432 0 /usr/bin/some_command with some parameters - 103 1 root Ss 10:21.66 15:23.52 08-14:58:25 12592 4814408 0 /usr/bin/some_command with some parameters - 104 1 root RNs 224:31.63 95:24.02 08-14:58:25 148944 10665428 0 /usr/bin/some_command with some parameters - 106 1 root Ss 0:08.78 0:09.93 08-14:58:25 2148 4522824 0 /usr/bin/some_command with some parameters - 107 1 root Ss 0:41.81 0:24.94 08-14:58:25 2188 4500416 0 /usr/bin/some_command with some parameters - 114 1 root Ss 0:00.00 0:00.03 08-14:58:25 8 4409456 0 /usr/bin/some_command with some parameters - 115 1 root Ss 2:07.05 0:56.41 08-14:58:25 7844 4509284 0 /usr/bin/some_command with some parameters - 116 1 root Ss 11:16.42 11:07.60 08-14:58:25 8596 4532768 0 /usr/bin/some_command with some parameters - 117 1 root Ss 0:11.28 0:11.58 08-14:58:25 5508 4517024 0 /usr/bin/some_command with some parameters - 118 1 root Ss 0:00.11 0:00.24 08-14:58:25 232 5017516 0 /usr/bin/some_command with some parameters - 119 1 root Ss 4:09.83 4:01.65 08-14:58:25 6028 4502240 0 /usr/bin/some_command with some parameters - 120 1 _timed Ss 0:01.47 0:07.47 08-14:58:25 1244 4500700 0 /usr/bin/some_command with some parameters - 123 1 root Ss 0:15.88 0:05.03 08-14:58:25 4320 4504936 0 /usr/bin/some_command with some parameters - 124 1 root Ss 0:00.00 0:00.05 08-14:58:25 36 4403280 0 /usr/bin/some_command with some parameters - 126 1 _locationd Ss 1:05.54 1:07.10 08-14:58:25 5500 4522344 0 /usr/bin/some_command with some parameters - 128 1 root Ss 0:00.00 0:00.05 08-14:58:25 56 4436712 0 /usr/bin/some_command with some parameters - 129 1 _displaypolicyd Ss 0:00.20 0:02.01 08-14:58:25 380 4472456 0 /usr/bin/some_command with some parameters - 132 1 root Ss 1:03.47 0:34.12 08-14:58:25 7100 4506616 0 /usr/bin/some_command with some parameters - 135 1 _distnote Ss 0:05.55 0:03.92 08-14:58:25 892 4367444 0 /usr/bin/some_command with some parameters - 139 1 root SNs 0:56.36 1:31.65 08-14:58:25 3392 4403952 0 /usr/bin/some_command with some parameters - 140 1 root Ss 0:00.09 0:00.46 08-14:58:25 1128 4468904 0 /usr/bin/some_command with some parameters - 141 1 root Ss 0:00.18 0:01.91 08-14:58:25 1268 4502020 0 /usr/bin/some_command with some parameters - 142 1 root Ss 0:00.00 0:00.03 08-14:58:25 80 4411744 0 /usr/bin/some_command with some parameters - 144 1 root Ss 0:23.44 0:34.43 08-14:58:25 4004 4504992 0 /usr/bin/some_command with some parameters - 145 1 root Ss 0:33.93 1:17.70 08-14:58:25 1984 4469284 0 /usr/bin/some_command with some parameters - 147 1 root Ss 0:41.57 0:49.94 08-14:58:25 3380 4501864 0 /usr/bin/some_command with some parameters - 148 1 root Ss 0:01.39 0:03.99 08-14:58:25 1356 4502052 0 /usr/bin/some_command with some parameters - 151 1 root Ss 4:33.07 1:27.82 08-14:58:25 8772 4544432 0 /usr/bin/some_command with some parameters - 152 1 root Ss 0:49.94 1:25.03 08-14:58:25 2808 4469628 0 /usr/bin/some_command with some parameters - 153 1 root Ss 0:02.71 0:05.10 08-14:58:25 1540 4517692 0 /usr/bin/some_command with some parameters - 156 1 _analyticsd Ss 0:08.73 0:11.37 08-14:58:25 4536 4511792 0 /usr/bin/some_command with some parameters - 191 1 root Ss 1:12.18 0:45.09 08-14:58:25 7972 4508044 0 /usr/bin/some_command with some parameters - 195 1 root Ss 0:16.43 0:14.18 08-14:58:24 4872 4503448 0 /usr/bin/some_command with some parameters - 199 1 root S 0:00.32 0:00.52 08-14:58:24 92 4367444 0 /usr/bin/some_command with some parameters - 206 1 root Ss 0:19.05 0:15.72 08-14:58:24 6592 4511260 0 /usr/bin/some_command with some parameters - 208 1 _trustd Ss 3:19.93 0:39.78 08-14:58:24 6948 4518924 0 /usr/bin/some_command with some parameters - 215 1 _networkd Ss 0:56.22 2:25.35 08-14:58:24 5692 4512388 0 /usr/bin/some_command with some parameters - 232 1 _mdnsresponder Ss 0:50.06 1:34.61 08-14:58:22 5168 4515992 0 /usr/bin/some_command with some parameters - 248 1 root Ss 0:03.37 0:05.61 08-14:58:22 288 4501552 0 /usr/bin/some_command with some parameters - 250 1 root Ss 0:00.18 0:01.24 08-14:58:22 896 4506016 0 /usr/bin/some_command with some parameters - 252 1 root Ss 0:00.00 0:00.01 08-14:58:22 12 4400400 0 /usr/bin/some_command with some parameters - 254 1 root Ss 0:00.14 0:00.62 08-14:58:22 548 4468908 0 /usr/bin/some_command with some parameters - 255 1 root Ss 3:23.68 2:37.34 08-14:58:22 9636 4514220 0 /usr/bin/some_command with some parameters - 256 1 _coreaudiod Ss 19:18.84 12:29.64 08-14:58:22 6700 4531492 0 /usr/bin/some_command with some parameters - 257 1 _nsurlsessiond Ss 0:06.68 0:19.85 08-14:58:22 4332 4521772 0 /usr/bin/some_command with some parameters - 263 1 root Ss 0:02.09 0:07.21 08-14:58:22 1436 4503404 0 /usr/bin/some_command with some parameters - 264 1 _cmiodalassistants Ss 5:01.51 2:35.11 08-14:58:22 8700 4556188 0 /usr/bin/some_command with some parameters - 269 1 root Ss 1:39.02 1:56.92 08-14:58:22 2132 4502064 0 /usr/bin/some_command with some parameters - 271 1 _coreaudiod S 0:00.24 0:00.52 08-14:58:22 92 4367408 0 /usr/bin/some_command with some parameters - 272 1 root Ss 0:00.14 0:01.46 08-14:58:22 456 4501676 0 /usr/bin/some_command with some parameters - 279 1 _locationd S 0:00.29 0:00.57 08-14:58:22 96 4367444 0 /usr/bin/some_command with some parameters - 300 1 root Ss 0:01.46 0:05.79 08-14:58:21 2236 4514480 0 /usr/bin/some_command with some parameters - 307 1 _softwareupdate S 0:00.24 0:00.55 08-14:58:21 92 4367408 0 /usr/bin/some_command with some parameters - 313 1 root Ss 0:00.64 0:01.36 08-14:58:21 128 4524496 0 /usr/bin/some_command with some parameters - 322 1 root Ss 0:01.54 0:04.68 08-14:58:21 5552 4516592 0 /usr/bin/some_command with some parameters - 337 1 root Ss 0:00.24 0:01.86 08-14:58:19 428 4501512 0 /usr/bin/some_command with some parameters - 397 1 root Ss 102:37.16 33:46.59 08-14:58:17 135092 4975516 0 /usr/bin/some_command with some parameters - 398 1 _nsurlsessiond S 0:00.23 0:00.54 08-14:58:16 92 4367408 0 /usr/bin/some_command with some parameters - 419 1 root Ss 37:27.33 9:27.48 08-14:58:10 61484 8032180 0 /usr/bin/some_command with some parameters - 422 1 _driverkit Ss 0:01.03 0:02.65 08-14:58:10 708 4810472 0 /usr/bin/some_command with some parameters - 423 1 _driverkit Ss 0:00.33 0:01.21 08-14:58:10 676 4810472 0 /usr/bin/some_command with some parameters - 425 1 _driverkit Ss 0:00.00 0:00.03 08-14:58:10 8 4801256 0 /usr/bin/some_command with some parameters - 427 1 _driverkit Ss 0:21.25 0:48.49 08-14:58:10 996 4810488 0 /usr/bin/some_command with some parameters - 428 1 _driverkit Ss 0:00.00 0:00.02 08-14:58:10 8 4801256 0 /usr/bin/some_command with some parameters - 430 1 _driverkit Ss 0:00.02 0:00.08 08-14:58:10 8 4808424 0 /usr/bin/some_command with some parameters - 432 1 _driverkit Ss 0:00.31 0:00.95 08-14:58:10 676 4810472 0 /usr/bin/some_command with some parameters - 434 1 _driverkit Ss 0:00.02 0:00.04 08-14:58:10 20 4816640 0 /usr/bin/some_command with some parameters - 435 1 _driverkit Ss 0:00.00 0:00.00 08-14:58:10 8 4800252 0 /usr/bin/some_command with some parameters - 437 1 _spotlight S 0:00.43 0:00.65 08-14:58:09 256 4367444 0 /usr/bin/some_command with some parameters - 460 1 root Ss 0:00.13 0:00.95 08-14:58:06 196 4384904 0 /usr/bin/some_command with some parameters - 474 1 _windowserver S 0:00.22 0:00.52 08-14:57:56 92 4367408 0 /usr/bin/some_command with some parameters - 481 1 _appinstalld S 0:00.22 0:00.50 08-14:57:54 92 4367408 0 /usr/bin/some_command with some parameters - 492 1 root Ss 2:58.80 4:36.41 08-14:57:50 9684 4585224 0 /usr/bin/some_command with some parameters - 501 1 _appleevents Ss 0:02.48 0:03.54 08-14:57:47 2896 4501716 0 /usr/bin/some_command with some parameters - 503 1 root Ss 0:00.01 0:00.05 08-14:57:47 148 4436644 0 /usr/bin/some_command with some parameters - 508 1 root Ss 69:22.80 284:56.76 08-14:57:47 29604 4491692 0 /usr/bin/some_command with some parameters - 515 82 root S 0:00.43 0:02.99 08-14:57:41 764 4502752 0 /usr/bin/some_command with some parameters - 528 1 root Ss 0:00.15 0:01.08 08-14:57:39 1104 4502164 0 /usr/bin/some_command with some parameters - 541 1 _appleevents S 0:00.21 0:00.50 08-14:57:36 92 4367408 0 /usr/bin/some_command with some parameters - 555 1 root Ss 0:00.89 0:02.12 08-14:57:33 2816 4501864 0 /usr/bin/some_command with some parameters - 558 1 someuser S 0:22.12 0:10.91 08-14:57:32 2344 4368112 0 /usr/bin/some_command with some parameters - 583 1 root Ss 0:00.29 0:00.83 08-14:57:31 864 4500984 0 /usr/bin/some_command with some parameters - 631 1 root Ss 0:00.04 0:00.02 08-14:57:28 32 4418196 0 /usr/bin/some_command with some parameters - 638 1 someuser S 0:49.92 1:27.51 08-14:57:28 2968 4469948 0 /usr/bin/some_command with some parameters - 673 1 someuser Ss 0:24.13 0:25.01 08-14:57:27 19868 4782072 0 /usr/bin/some_command with some parameters` - -var psOut500 = `PID PPID USER STAT UTIME STIME ELAPSED RSS VSZ PAGEIN COMMAND - 1 0 root Ss 4:11.67 20:07.54 08-14:58:33 14376 4480016 0 /sbin/launchd - 68 1 root Ss 0:22.59 0:41.16 08-14:58:25 852 4471000 0 /usr/bin/some_command with some parameters - 69 1 root Ss 0:21.18 0:38.61 08-14:58:25 4416 4503784 0 /usr/bin/some_command with some parameters - 72 1 root Ss 0:01.86 0:04.56 08-14:58:25 464 4403040 0 /usr/bin/some_command with some parameters - 73 1 root Ss 2:15.80 4:35.94 08-14:58:25 2976 4477292 0 /usr/bin/some_command with some parameters - 74 1 root Ss 0:00.02 0:00.10 08-14:58:25 64 4322064 0 /usr/bin/some_command with some parameters - 75 1 root Ss 0:13.98 0:11.16 08-14:58:25 3572 4504692 0 /usr/bin/some_command with some parameters - 81 1 root Ss 0:00.02 0:00.10 08-14:58:25 72 4312320 0 /usr/bin/some_command with some parameters - 82 1 root Ss 2:38.05 1:57.49 08-14:58:25 4396 4515104 0 /usr/bin/some_command with some parameters - 84 1 root Ss 0:22.27 0:41.25 08-14:58:25 3224 4506768 0 /usr/bin/some_command with some parameters - 86 1 root Ss 0:39.19 1:53.93 08-14:58:25 4808 4503136 0 /usr/bin/some_command with some parameters - 88 1 root Ss 21:00.91 24:52.86 08-14:58:25 17640 4579384 0 /usr/bin/some_command with some parameters - 91 1 root Ss 0:10.63 0:15.49 08-14:58:25 1632 4515168 0 /usr/bin/some_command with some parameters - 93 1 root Ss 2:11.10 2:51.66 08-14:58:25 10816 4586644 0 /usr/bin/some_command with some parameters - 99 1 root Ss 0:03.12 0:04.29 08-14:58:25 1584 4469432 0 /usr/bin/some_command with some parameters - 103 1 root Ss 10:21.66 15:23.52 08-14:58:25 12592 4814408 0 /usr/bin/some_command with some parameters - 104 1 root RNs 224:31.63 95:24.02 08-14:58:25 148944 10665428 0 /usr/bin/some_command with some parameters - 106 1 root Ss 0:08.78 0:09.93 08-14:58:25 2148 4522824 0 /usr/bin/some_command with some parameters - 107 1 root Ss 0:41.81 0:24.94 08-14:58:25 2188 4500416 0 /usr/bin/some_command with some parameters - 114 1 root Ss 0:00.00 0:00.03 08-14:58:25 8 4409456 0 /usr/bin/some_command with some parameters - 115 1 root Ss 2:07.05 0:56.41 08-14:58:25 7844 4509284 0 /usr/bin/some_command with some parameters - 116 1 root Ss 11:16.42 11:07.60 08-14:58:25 8596 4532768 0 /usr/bin/some_command with some parameters - 117 1 root Ss 0:11.28 0:11.58 08-14:58:25 5508 4517024 0 /usr/bin/some_command with some parameters - 118 1 root Ss 0:00.11 0:00.24 08-14:58:25 232 5017516 0 /usr/bin/some_command with some parameters - 119 1 root Ss 4:09.83 4:01.65 08-14:58:25 6028 4502240 0 /usr/bin/some_command with some parameters - 120 1 _timed Ss 0:01.47 0:07.47 08-14:58:25 1244 4500700 0 /usr/bin/some_command with some parameters - 123 1 root Ss 0:15.88 0:05.03 08-14:58:25 4320 4504936 0 /usr/bin/some_command with some parameters - 124 1 root Ss 0:00.00 0:00.05 08-14:58:25 36 4403280 0 /usr/bin/some_command with some parameters - 126 1 _locationd Ss 1:05.54 1:07.10 08-14:58:25 5500 4522344 0 /usr/bin/some_command with some parameters - 128 1 root Ss 0:00.00 0:00.05 08-14:58:25 56 4436712 0 /usr/bin/some_command with some parameters - 129 1 _displaypolicyd Ss 0:00.20 0:02.01 08-14:58:25 380 4472456 0 /usr/bin/some_command with some parameters - 132 1 root Ss 1:03.47 0:34.12 08-14:58:25 7100 4506616 0 /usr/bin/some_command with some parameters - 135 1 _distnote Ss 0:05.55 0:03.92 08-14:58:25 892 4367444 0 /usr/bin/some_command with some parameters - 139 1 root SNs 0:56.36 1:31.65 08-14:58:25 3392 4403952 0 /usr/bin/some_command with some parameters - 140 1 root Ss 0:00.09 0:00.46 08-14:58:25 1128 4468904 0 /usr/bin/some_command with some parameters - 141 1 root Ss 0:00.18 0:01.91 08-14:58:25 1268 4502020 0 /usr/bin/some_command with some parameters - 142 1 root Ss 0:00.00 0:00.03 08-14:58:25 80 4411744 0 /usr/bin/some_command with some parameters - 144 1 root Ss 0:23.44 0:34.43 08-14:58:25 4004 4504992 0 /usr/bin/some_command with some parameters - 145 1 root Ss 0:33.93 1:17.70 08-14:58:25 1984 4469284 0 /usr/bin/some_command with some parameters - 147 1 root Ss 0:41.57 0:49.94 08-14:58:25 3380 4501864 0 /usr/bin/some_command with some parameters - 148 1 root Ss 0:01.39 0:03.99 08-14:58:25 1356 4502052 0 /usr/bin/some_command with some parameters - 151 1 root Ss 4:33.07 1:27.82 08-14:58:25 8772 4544432 0 /usr/bin/some_command with some parameters - 152 1 root Ss 0:49.94 1:25.03 08-14:58:25 2808 4469628 0 /usr/bin/some_command with some parameters - 153 1 root Ss 0:02.71 0:05.10 08-14:58:25 1540 4517692 0 /usr/bin/some_command with some parameters - 156 1 _analyticsd Ss 0:08.73 0:11.37 08-14:58:25 4536 4511792 0 /usr/bin/some_command with some parameters - 191 1 root Ss 1:12.18 0:45.09 08-14:58:25 7972 4508044 0 /usr/bin/some_command with some parameters - 195 1 root Ss 0:16.43 0:14.18 08-14:58:24 4872 4503448 0 /usr/bin/some_command with some parameters - 199 1 root S 0:00.32 0:00.52 08-14:58:24 92 4367444 0 /usr/bin/some_command with some parameters - 206 1 root Ss 0:19.05 0:15.72 08-14:58:24 6592 4511260 0 /usr/bin/some_command with some parameters - 208 1 _trustd Ss 3:19.93 0:39.78 08-14:58:24 6948 4518924 0 /usr/bin/some_command with some parameters - 215 1 _networkd Ss 0:56.22 2:25.35 08-14:58:24 5692 4512388 0 /usr/bin/some_command with some parameters - 232 1 _mdnsresponder Ss 0:50.06 1:34.61 08-14:58:22 5168 4515992 0 /usr/bin/some_command with some parameters - 248 1 root Ss 0:03.37 0:05.61 08-14:58:22 288 4501552 0 /usr/bin/some_command with some parameters - 250 1 root Ss 0:00.18 0:01.24 08-14:58:22 896 4506016 0 /usr/bin/some_command with some parameters - 252 1 root Ss 0:00.00 0:00.01 08-14:58:22 12 4400400 0 /usr/bin/some_command with some parameters - 254 1 root Ss 0:00.14 0:00.62 08-14:58:22 548 4468908 0 /usr/bin/some_command with some parameters - 255 1 root Ss 3:23.68 2:37.34 08-14:58:22 9636 4514220 0 /usr/bin/some_command with some parameters - 256 1 _coreaudiod Ss 19:18.84 12:29.64 08-14:58:22 6700 4531492 0 /usr/bin/some_command with some parameters - 257 1 _nsurlsessiond Ss 0:06.68 0:19.85 08-14:58:22 4332 4521772 0 /usr/bin/some_command with some parameters - 263 1 root Ss 0:02.09 0:07.21 08-14:58:22 1436 4503404 0 /usr/bin/some_command with some parameters - 264 1 _cmiodalassistants Ss 5:01.51 2:35.11 08-14:58:22 8700 4556188 0 /usr/bin/some_command with some parameters - 269 1 root Ss 1:39.02 1:56.92 08-14:58:22 2132 4502064 0 /usr/bin/some_command with some parameters - 271 1 _coreaudiod S 0:00.24 0:00.52 08-14:58:22 92 4367408 0 /usr/bin/some_command with some parameters - 272 1 root Ss 0:00.14 0:01.46 08-14:58:22 456 4501676 0 /usr/bin/some_command with some parameters - 279 1 _locationd S 0:00.29 0:00.57 08-14:58:22 96 4367444 0 /usr/bin/some_command with some parameters - 300 1 root Ss 0:01.46 0:05.79 08-14:58:21 2236 4514480 0 /usr/bin/some_command with some parameters - 307 1 _softwareupdate S 0:00.24 0:00.55 08-14:58:21 92 4367408 0 /usr/bin/some_command with some parameters - 313 1 root Ss 0:00.64 0:01.36 08-14:58:21 128 4524496 0 /usr/bin/some_command with some parameters - 322 1 root Ss 0:01.54 0:04.68 08-14:58:21 5552 4516592 0 /usr/bin/some_command with some parameters - 337 1 root Ss 0:00.24 0:01.86 08-14:58:19 428 4501512 0 /usr/bin/some_command with some parameters - 397 1 root Ss 102:37.16 33:46.59 08-14:58:17 135092 4975516 0 /usr/bin/some_command with some parameters - 398 1 _nsurlsessiond S 0:00.23 0:00.54 08-14:58:16 92 4367408 0 /usr/bin/some_command with some parameters - 419 1 root Ss 37:27.33 9:27.48 08-14:58:10 61484 8032180 0 /usr/bin/some_command with some parameters - 422 1 _driverkit Ss 0:01.03 0:02.65 08-14:58:10 708 4810472 0 /usr/bin/some_command with some parameters - 423 1 _driverkit Ss 0:00.33 0:01.21 08-14:58:10 676 4810472 0 /usr/bin/some_command with some parameters - 425 1 _driverkit Ss 0:00.00 0:00.03 08-14:58:10 8 4801256 0 /usr/bin/some_command with some parameters - 427 1 _driverkit Ss 0:21.25 0:48.49 08-14:58:10 996 4810488 0 /usr/bin/some_command with some parameters - 428 1 _driverkit Ss 0:00.00 0:00.02 08-14:58:10 8 4801256 0 /usr/bin/some_command with some parameters - 430 1 _driverkit Ss 0:00.02 0:00.08 08-14:58:10 8 4808424 0 /usr/bin/some_command with some parameters - 432 1 _driverkit Ss 0:00.31 0:00.95 08-14:58:10 676 4810472 0 /usr/bin/some_command with some parameters - 434 1 _driverkit Ss 0:00.02 0:00.04 08-14:58:10 20 4816640 0 /usr/bin/some_command with some parameters - 435 1 _driverkit Ss 0:00.00 0:00.00 08-14:58:10 8 4800252 0 /usr/bin/some_command with some parameters - 437 1 _spotlight S 0:00.43 0:00.65 08-14:58:09 256 4367444 0 /usr/bin/some_command with some parameters - 460 1 root Ss 0:00.13 0:00.95 08-14:58:06 196 4384904 0 /usr/bin/some_command with some parameters - 474 1 _windowserver S 0:00.22 0:00.52 08-14:57:56 92 4367408 0 /usr/bin/some_command with some parameters - 481 1 _appinstalld S 0:00.22 0:00.50 08-14:57:54 92 4367408 0 /usr/bin/some_command with some parameters - 492 1 root Ss 2:58.80 4:36.41 08-14:57:50 9684 4585224 0 /usr/bin/some_command with some parameters - 501 1 _appleevents Ss 0:02.48 0:03.54 08-14:57:47 2896 4501716 0 /usr/bin/some_command with some parameters - 503 1 root Ss 0:00.01 0:00.05 08-14:57:47 148 4436644 0 /usr/bin/some_command with some parameters - 508 1 root Ss 69:22.80 284:56.76 08-14:57:47 29604 4491692 0 /usr/bin/some_command with some parameters - 515 82 root S 0:00.43 0:02.99 08-14:57:41 764 4502752 0 /usr/bin/some_command with some parameters - 528 1 root Ss 0:00.15 0:01.08 08-14:57:39 1104 4502164 0 /usr/bin/some_command with some parameters - 541 1 _appleevents S 0:00.21 0:00.50 08-14:57:36 92 4367408 0 /usr/bin/some_command with some parameters - 555 1 root Ss 0:00.89 0:02.12 08-14:57:33 2816 4501864 0 /usr/bin/some_command with some parameters - 558 1 someuser S 0:22.12 0:10.91 08-14:57:32 2344 4368112 0 /usr/bin/some_command with some parameters - 583 1 root Ss 0:00.29 0:00.83 08-14:57:31 864 4500984 0 /usr/bin/some_command with some parameters - 631 1 root Ss 0:00.04 0:00.02 08-14:57:28 32 4418196 0 /usr/bin/some_command with some parameters - 638 1 someuser S 0:49.92 1:27.51 08-14:57:28 2968 4469948 0 /usr/bin/some_command with some parameters - 673 1 someuser Ss 0:24.13 0:25.01 08-14:57:27 19868 4782072 0 /usr/bin/some_command with some parameters - 677 1 _windowserver Ss 273:54.03 183:35.46 08-14:57:27 84708 11097548 0 /usr/bin/some_command with some parameters - 735 1 _securityagent S 0:00.20 0:00.48 08-14:57:23 92 4367408 0 /usr/bin/some_command with some parameters - 762 1 root Ss 0:00.07 0:00.24 08-14:57:22 344 4468988 0 /usr/bin/some_command with some parameters - 860 1 root Ss 0:37.02 0:09.34 08-14:57:16 1792 4501828 0 /usr/bin/some_command with some parameters - 978 2828 someuser S 0:03.61 0:04.06 01-00:22:51 19400 30027412 0 /usr/bin/some_command with some parameters - 2054 1 someuser S 0:21.49 0:26.70 08-14:56:26 5044 4508552 0 /usr/bin/some_command with some parameters - 2059 1 someuser S 1:38.52 0:24.47 08-14:56:25 6868 4522040 0 /usr/bin/some_command with some parameters - 2142 1 _appstore S 0:00.20 0:00.47 08-14:55:24 92 4367408 0 /usr/bin/some_command with some parameters - 2155 1 _assetcache S 0:00.20 0:00.46 08-14:55:20 92 4367408 0 /usr/bin/some_command with some parameters - 2156 1 someuser SN 0:13.00 0:20.89 08-14:55:07 3424 4508336 0 /usr/bin/some_command with some parameters - 2157 1 _spotlight SN 0:12.93 0:20.55 08-14:55:07 2916 4509148 0 /usr/bin/some_command with some parameters - 2165 1 _spotlight S 0:00.06 0:00.14 08-14:55:03 200 4453292 0 /usr/bin/some_command with some parameters - 2316 1 someuser S 0:14.93 0:17.03 08-14:54:54 5132 4504040 0 /usr/bin/some_command with some parameters - 2324 1 someuser S 0:02.52 0:03.70 08-14:54:53 3316 4476480 0 /usr/bin/some_command with some parameters - 2325 1 someuser S 0:02.67 0:10.56 08-14:54:53 3468 4513940 0 /usr/bin/some_command with some parameters - 2328 1 someuser S 0:09.30 0:22.60 08-14:54:53 5180 4551980 0 /usr/bin/some_command with some parameters - 2329 1 someuser S 0:01.98 0:04.14 08-14:54:53 2624 4501644 0 /usr/bin/some_command with some parameters - 2330 1 someuser S 1:32.26 0:32.06 08-14:54:53 14524 5183340 0 /usr/bin/some_command with some parameters - 2331 1 someuser S 0:04.71 0:07.80 08-14:54:53 7152 4509632 0 /usr/bin/some_command with some parameters - 2332 1 someuser S 0:13.12 0:12.46 08-14:54:53 9296 4526744 0 /usr/bin/some_command with some parameters - 2334 1 someuser S 0:22.69 0:16.18 08-14:54:53 5976 4699464 0 /usr/bin/some_command with some parameters - 2348 1 someuser S 0:02.28 0:07.72 08-14:54:51 3436 4517492 0 /usr/bin/some_command with some parameters - 2349 1 someuser S 0:00.90 0:01.91 08-14:54:50 2592 4502988 0 /usr/bin/some_command with some parameters - 2350 1 someuser S 0:27.60 0:32.25 08-14:54:50 18180 5390300 0 /usr/bin/some_command with some parameters - 2361 1 someuser S 0:00.02 0:00.13 08-14:54:47 188 4429328 0 /usr/bin/some_command with some parameters - 2363 1 someuser S 0:04.66 0:09.83 08-14:54:44 6200 4520040 0 /usr/bin/some_command with some parameters - 2364 1 someuser S 0:42.31 0:18.23 08-14:54:44 1880 4502788 0 /usr/bin/some_command with some parameters - 2367 1 someuser S 0:00.27 0:01.99 08-14:54:44 748 4502224 0 /usr/bin/some_command with some parameters - 2369 1 someuser Ss 0:09.77 0:13.68 08-14:54:43 5776 4982052 0 /usr/bin/some_command with some parameters - 2371 1 someuser S 0:22.72 0:23.78 08-14:54:43 4388 4502616 0 /usr/bin/some_command with some parameters - 2383 1 someuser S 0:00.30 0:00.68 08-14:54:42 128 4524688 0 /usr/bin/some_command with some parameters - 2389 1 someuser S 0:02.71 0:06.13 08-14:54:41 5080 4555908 0 /usr/bin/some_command with some parameters - 2391 1 someuser S 0:00.52 0:03.02 08-14:54:40 3400 4502792 0 /usr/bin/some_command with some parameters - 2397 1 someuser S 0:01.87 0:04.29 08-14:54:37 2140 4502668 0 /usr/bin/some_command with some parameters - 2399 1 someuser Ss 0:40.92 0:24.48 08-14:54:37 9528 5349444 0 /usr/bin/some_command with some parameters - 2402 1 root Ss 0:00.64 0:01.66 08-14:54:37 3036 4475828 0 /usr/bin/some_command with some parameters - 2411 1 someuser S 0:13.29 0:30.20 08-14:54:34 5852 4558504 0 /usr/bin/some_command with some parameters - 2412 1 someuser S 0:20.66 0:27.38 08-14:54:33 4020 4502560 0 /usr/bin/some_command with some parameters - 2414 1 someuser S 0:01.99 0:05.92 08-14:54:33 2620 4516816 0 /usr/bin/some_command with some parameters - 2417 1 someuser S 0:01.15 0:04.26 08-14:54:32 2556 4802024 0 /usr/bin/some_command with some parameters - 2420 1 someuser S 0:15.13 0:16.16 08-14:54:30 3516 4503164 0 /usr/bin/some_command with some parameters - 2421 1 someuser S 0:00.68 0:01.09 08-14:54:30 524 4468844 0 /usr/bin/some_command with some parameters - 2425 1 someuser S 0:21.08 0:36.98 08-14:54:30 5288 4513816 0 /usr/bin/some_command with some parameters - 2430 1 someuser S 0:01.20 0:05.17 08-14:54:28 3080 4514368 0 /usr/bin/some_command with some parameters - 2441 1 someuser S 0:02.28 0:09.12 08-14:54:25 2728 4501860 0 /usr/bin/some_command with some parameters - 2448 1 someuser S 0:00.05 0:00.11 08-14:54:25 632 4463028 0 /usr/bin/some_command with some parameters - 2456 1 _reportmemoryexception S 0:00.20 0:00.44 08-14:54:20 92 4367408 0 /usr/bin/some_command with some parameters - 2458 1 root Ss 0:00.02 0:00.08 08-14:54:19 648 4468844 0 /usr/bin/some_command with some parameters - 2478 1 _applepay S 0:00.20 0:00.44 08-14:54:10 92 4367408 0 /usr/bin/some_command with some parameters - 2532 1 _fpsd Ss 0:00.49 0:01.17 08-14:52:15 88 4389624 0 /usr/bin/some_command with some parameters - 2555 1 666 S 0:00.43 0:00.53 08-14:51:26 236 4367444 0 /usr/bin/some_command with some parameters - 2556 1 newrelic S 0:00.42 0:00.52 08-14:51:25 236 4367444 0 /usr/bin/some_command with some parameters - 2730 1 newrelic SN 0:11.91 0:18.84 08-14:48:59 1856 4507356 0 /usr/bin/some_command with some parameters - 2731 1 666 SN 0:11.94 0:18.87 08-14:48:59 1856 4507352 0 /usr/bin/some_command with some parameters - 2736 1 666 S 0:21.02 0:21.51 08-14:48:58 1416 4507568 0 /usr/bin/some_command with some parameters - 2737 1 newrelic S 0:21.17 0:21.43 08-14:48:58 1416 4507572 0 /usr/bin/some_command with some parameters - 2827 1 someuser S 30:58.89 10:27.94 08-14:42:42 81748 5816616 0 /usr/bin/some_command with some parameters - 2828 1 someuser S 90:43.25 36:25.00 08-14:42:41 243936 6135992 0 /usr/bin/some_command with some parameters - 2832 1 someuser S 12:42.13 7:03.75 08-14:42:40 61980 43791692 0 /usr/bin/some_command with some parameters - 2834 1 someuser S 11:17.00 10:16.84 08-14:42:40 33144 10090544 0 /usr/bin/some_command with some parameters - 2836 1 someuser S 31:11.18 15:32.71 08-14:42:40 61296 5733652 0 /usr/bin/some_command with some parameters - 2838 1 someuser S 244:03.96 87:55.31 08-14:42:39 347524 6441364 0 /usr/bin/some_command with some parameters - 2839 1 someuser T 0:00.00 0:00.00 08-14:42:38 4 4260072 0 /usr/bin/some_command with some parameters - 2840 1 someuser S 0:06.80 0:11.98 08-14:42:37 6368 5391612 0 /usr/bin/some_command with some parameters - 2842 1 someuser S 1:45.21 1:09.08 08-14:42:37 16584 5192932 0 /usr/bin/some_command with some parameters - 2843 1 someuser S 0:35.97 0:29.42 08-14:42:36 5276 5154488 0 /usr/bin/some_command with some parameters - 2844 1 someuser S 2:16.88 1:55.23 08-14:42:36 21520 5473872 0 /usr/bin/some_command with some parameters - 2848 1 someuser S 0:18.41 0:19.18 08-14:42:36 6056 5188224 0 /usr/bin/some_command with some parameters - 2861 1 someuser S 0:00.05 0:00.37 08-14:42:35 260 4464252 0 /usr/bin/some_command with some parameters - 2872 1 someuser S 0:01.13 0:05.69 08-14:42:34 1212 4510572 0 /usr/bin/some_command with some parameters - 2882 1 root Ss 0:00.05 0:00.17 08-14:42:33 132 4428416 0 /usr/bin/some_command with some parameters - 2885 1 someuser S 0:00.05 0:00.35 08-14:42:33 248 4464252 0 /usr/bin/some_command with some parameters - 2889 1 someuser S 0:04.63 0:08.78 08-14:42:30 4540 4534984 0 /usr/bin/some_command with some parameters - 2892 2832 someuser S 42:37.65 16:27.26 08-14:42:29 29456 5027544 0 /usr/bin/some_command with some parameters - 2899 2832 someuser S 1:22.11 1:04.56 08-14:42:29 10328 4714188 0 /usr/bin/some_command with some parameters - 2913 1 someuser S 0:00.05 0:00.33 08-14:42:28 256 4472508 0 /usr/bin/some_command with some parameters - 2915 2832 someuser S 116:23.29 14:11.11 08-14:42:27 224592 47242100 0 /usr/bin/some_command with some parameters - 2924 2834 someuser S 3:57.25 1:44.85 08-14:42:27 7540 5165308 0 /usr/bin/some_command with some parameters - 2925 2834 someuser S 0:34.10 0:28.53 08-14:42:27 8392 4703840 0 /usr/bin/some_command with some parameters - 2928 1 someuser S 0:00.14 0:01.05 08-14:42:27 708 4669756 0 /usr/bin/some_command with some parameters - 2930 1 someuser S 0:00.93 0:01.42 08-14:42:25 1736 4501280 0 /usr/bin/some_command with some parameters - 2948 1 root Ss 0:04.21 0:10.50 08-14:42:24 1664 4501036 0 /usr/bin/some_command with some parameters - 2949 2827 someuser S 0:00.01 0:00.02 08-14:42:24 8 4311692 0 /usr/bin/some_command with some parameters - 2984 2828 someuser S 58:50.54 27:06.48 08-14:42:20 76684 7939928 0 /usr/bin/some_command with some parameters - 2986 2828 someuser S 29:43.51 13:05.10 08-14:42:20 42148 4812112 0 /usr/bin/some_command with some parameters - 2991 2828 someuser S 1:00.32 0:37.67 08-14:42:19 5548 4747448 0 /usr/bin/some_command with some parameters - 2997 2828 someuser S 2:23.76 0:43.92 08-14:42:18 32048 29994144 0 /usr/bin/some_command with some parameters - 2998 2828 someuser S 0:05.01 0:05.43 08-14:42:18 4672 29984524 0 /usr/bin/some_command with some parameters - 2999 2828 someuser S 3:23.90 1:52.55 08-14:42:18 36272 30024356 0 /usr/bin/some_command with some parameters - 3016 1 someuser S 0:00.01 0:00.12 08-14:42:17 52 4469476 0 /usr/bin/some_command with some parameters - 3033 2836 someuser S 8:11.46 2:45.88 08-14:42:12 7912 4855280 0 /usr/bin/some_command with some parameters - 3059 1 someuser Ss 0:04.76 0:06.36 08-14:42:08 552 4470796 0 /usr/bin/some_command with some parameters - 3062 1 someuser S 0:01.74 0:05.01 08-14:42:08 1552 4515872 0 /usr/bin/some_command with some parameters - 3063 1 someuser S 1:30.60 0:37.89 08-14:42:08 7528 4506740 0 /usr/bin/some_command with some parameters - 3071 1 someuser S< 0:00.03 0:00.20 08-14:42:08 128 4501628 0 /usr/bin/some_command with some parameters - 3073 1 someuser S 17:32.46 11:44.81 08-14:42:08 25632 5352928 0 /usr/bin/some_command with some parameters - 3080 1 someuser S 0:01.60 0:05.41 08-14:42:08 3480 4968212 0 /usr/bin/some_command with some parameters - 3083 1 someuser S 0:06.43 0:09.02 08-14:42:08 2016 4500992 0 /usr/bin/some_command with some parameters - 3088 1 someuser S 3:32.35 3:59.93 08-14:42:08 16712 6625084 0 /usr/bin/some_command with some parameters - 3091 1 someuser S 0:00.08 0:00.23 08-14:42:08 580 4469020 0 /usr/bin/some_command with some parameters - 3093 2836 someuser S 0:00.51 0:01.06 08-14:42:07 1160 4719516 0 /usr/bin/some_command with some parameters - 3094 2836 someuser S 1:02.52 0:53.95 08-14:42:07 10076 4738620 0 /usr/bin/some_command with some parameters - 3095 2836 someuser S 21:28.85 5:06.94 08-14:42:07 36928 47125408 0 /usr/bin/some_command with some parameters - 3146 1 someuser S 0:31.08 0:29.05 08-14:42:01 9736 5409664 0 /usr/bin/some_command with some parameters - 3181 1 someuser S 12:19.70 10:55.35 08-14:41:57 21268 5190672 0 /usr/bin/some_command with some parameters - 3211 3073 someuser S 17:41.68 6:32.44 08-14:41:54 1456 4732408 0 /usr/bin/some_command with some parameters - 3288 3073 someuser S 2:51.70 1:11.08 08-14:41:52 6856 4686332 0 /usr/bin/some_command with some parameters - 3312 2828 someuser S 4:06.39 0:56.98 08-14:41:49 18672 34530964 0 /usr/bin/some_command with some parameters - 3337 2828 someuser S 3:42.05 1:53.09 08-14:41:48 42672 30220176 0 /usr/bin/some_command with some parameters - 3543 2834 someuser S 1:34.97 0:32.71 08-14:41:36 3084 8908360 0 /usr/bin/some_command with some parameters - 3544 2834 someuser S 1:04.96 0:30.34 08-14:41:36 9656 8995708 0 /usr/bin/some_command with some parameters - 3545 2834 someuser S 0:33.10 0:16.46 08-14:41:36 10096 8986332 0 /usr/bin/some_command with some parameters - 3564 2834 someuser S 13:31.51 3:18.03 08-14:41:35 52736 9309328 0 /usr/bin/some_command with some parameters - 3566 2834 someuser S 0:00.88 0:02.21 08-14:41:35 2744 8995380 0 /usr/bin/some_command with some parameters - 3569 2834 someuser S 17:07.58 3:54.01 08-14:41:34 89788 9460448 0 /usr/bin/some_command with some parameters - 3571 2834 someuser S 0:00.91 0:02.28 08-14:41:34 2904 8994356 0 /usr/bin/some_command with some parameters - 3623 3073 someuser S 52:17.67 11:34.11 08-14:41:18 51392 5076344 0 /usr/bin/some_command with some parameters - 3656 1 someuser S 0:00.02 0:00.14 08-14:41:01 2204 4461572 0 /usr/bin/some_command with some parameters - 3732 1 root S 7:12.99 1:10.20 08-14:40:36 1584 4476304 0 /usr/bin/some_command with some parameters - 3736 1 someuser S 4:22.99 1:24.41 08-14:40:36 2716 4512484 0 /usr/bin/some_command with some parameters - 3742 1 root Ss 7:29.21 6:04.37 08-14:40:36 3888 4494528 0 /usr/bin/some_command with some parameters - 3743 1 someuser S 0:13.27 0:13.21 08-14:40:36 4040 4733128 0 /usr/bin/some_command with some parameters - 3747 1 root Ss 8:05.21 2:59.27 08-14:40:36 3408 4483984 0 /usr/bin/some_command with some parameters - 3769 1 root Ss 1:26.36 3:15.75 08-14:40:33 788 4477924 0 /usr/bin/some_command with some parameters - 3811 1 _driverkit Ss 0:12.28 0:11.01 08-14:40:30 1120 4826876 0 /usr/bin/some_command with some parameters - 3813 1 _driverkit Ss 0:00.00 0:00.01 08-14:40:30 8 4808448 0 /usr/bin/some_command with some parameters - 3834 1 someuser S 0:05.59 0:17.21 08-14:40:26 5236 4660040 0 /usr/bin/some_command with some parameters - 3857 1 someuser S 0:00.07 0:00.37 08-14:34:54 140 4469332 0 /usr/bin/some_command with some parameters - 4074 1 root Ss 0:34.56 0:54.92 08-13:54:53 2040 4505404 0 /usr/bin/some_command with some parameters - 4168 1 someuser S 0:00.23 0:01.53 08-09:51:17 1164 4501232 0 /usr/bin/some_command with some parameters - 5222 2828 someuser S 16:02.55 4:18.96 08-04:00:15 117528 34562148 0 /usr/bin/some_command with some parameters - 5252 2828 someuser S 0:14.30 0:19.63 08-04:00:13 3400 4736224 0 /usr/bin/some_command with some parameters - 5347 1 _fpsd Ss 0:00.04 0:00.17 08-03:59:11 1036 4444676 0 /usr/bin/some_command with some parameters - 5407 1 someuser S 0:00.30 0:00.89 08-03:58:41 284 4504764 0 /usr/bin/some_command with some parameters - 6280 1 nobody S 0:00.18 0:00.40 08-03:18:55 92 4367408 0 /usr/bin/some_command with some parameters - 6305 1 someuser S 282:50.93 82:22.21 08-03:17:56 2292124 18217416 0 /usr/bin/some_command with some parameters - 6351 6305 someuser S 0:11.88 0:24.93 08-03:17:53 404 4403508 0 /usr/bin/some_command with some parameters - 6365 6305 someuser S 0:04.48 0:04.98 08-03:17:47 2524 4892876 0 /usr/bin/some_command with some parameters - 6368 6305 someuser S 0:00.73 0:02.15 08-03:17:46 1916 4730504 0 /usr/bin/some_command with some parameters - 6774 1 someuser S 1:14.28 0:54.60 08-03:15:45 11608 6030076 0 /usr/bin/some_command with some parameters - 6796 1 someuser Ss 0:01.93 0:06.99 08-03:15:42 5352 5006176 0 /usr/bin/some_command with some parameters - 6947 1 someuser Ss 0:23.99 0:26.77 08-03:12:59 14192 4400464 0 /usr/bin/some_command with some parameters - 7649 2838 someuser S 0:02.69 0:08.67 07-22:38:33 4660 4716376 0 /usr/bin/some_command with some parameters - 7651 1 someuser S 0:01.60 0:05.57 07-22:38:31 4752 5132776 0 /usr/bin/some_command with some parameters - 7961 1 someuser S 0:02.56 0:00.59 08-02:58:43 432 4371696 0 /usr/bin/some_command with some parameters - 9260 25257 someuser S 3:51.10 15:40.43 02-01:11:16 558980 5146896 0 /usr/bin/some_command with some parameters -12403 1 someuser S 0:03.22 0:07.88 08-02:12:17 4604 4767956 0 /usr/bin/some_command with some parameters -13175 1 someuser S 0:22.98 0:34.21 07-22:17:23 6344 5200676 0 /usr/bin/some_command with some parameters -13178 13175 someuser S 5:12.88 4:16.86 07-22:17:23 21612 6243308 0 /usr/bin/some_command with some parameters -13179 13175 someuser S 0:03.66 0:04.15 07-22:17:23 4196 5014868 0 /usr/bin/some_command with some parameters -13201 13178 someuser S 0:04.69 0:05.74 07-22:17:18 4668 5177360 0 /usr/bin/some_command with some parameters -13207 13201 someuser S 0:16.94 0:25.36 07-22:17:17 2108 4446856 0 /usr/bin/some_command with some parameters -13208 13201 someuser S 0:13.28 0:12.44 07-22:17:17 7412 5051112 0 /usr/bin/some_command with some parameters -13209 13201 someuser S 3:11.69 5:39.34 07-22:17:17 7780 5011776 0 /usr/bin/some_command with some parameters -13210 13201 someuser S 0:35.56 0:49.66 07-22:17:17 9224 5048820 0 /usr/bin/some_command with some parameters -13213 13207 someuser Z 0:00.00 0:00.00 07-22:17:16 0 /usr/bin/some_command with some parameters -13219 13210 someuser S 62:01.45 273:27.39 07-22:17:13 8684 6685852 0 /usr/bin/some_command with some parameters -13565 2832 someuser S 0:07.72 0:10.21 08-02:05:51 3224 4704560 0 /usr/bin/some_command with some parameters -15552 2828 someuser S 0:21.59 0:09.03 22:23:01 52380 34493004 0 /usr/bin/some_command with some parameters -20135 2828 someuser S 0:07.21 0:10.85 08-01:34:55 21200 30212992 0 /usr/bin/some_command with some parameters -22878 1 someuser S 0:03.53 0:06.54 07-16:03:13 4464 4715080 0 /usr/bin/some_command with some parameters -23677 1 root Ss 0:08.84 0:10.65 05-21:56:12 6440 4508224 0 /usr/bin/some_command with some parameters -25255 1 someuser S 0:41.36 6:19.76 07-03:55:41 352 4338244 0 /usr/bin/some_command with some parameters -25257 1 someuser S 6:47.93 10:51.33 07-03:55:41 4000 4557808 0 /usr/bin/some_command with some parameters -25320 25257 someuser S 0:03.25 0:05.20 07-03:55:33 600 4329640 0 /usr/bin/some_command with some parameters -27923 1 root SNs 0:00.08 0:00.25 01-22:35:02 1204 4469016 0 /usr/bin/some_command with some parameters -29226 1 someuser S 0:23.33 0:58.42 05-21:48:02 6600 4613240 0 /usr/bin/some_command with some parameters -29631 2828 someuser S 0:46.31 0:35.11 04-19:33:24 41232 30292244 0 /usr/bin/some_command with some parameters -29686 2828 someuser S 5:01.84 1:31.59 04-19:33:06 65136 30334312 0 /usr/bin/some_command with some parameters -29894 2828 someuser S 0:13.88 0:05.57 01-22:27:32 6896 30011808 0 /usr/bin/some_command with some parameters -31499 2828 someuser S 0:42.96 0:37.98 04-19:17:27 51460 30313196 0 /usr/bin/some_command with some parameters -31632 2828 someuser S 5:00.31 1:03.01 04-19:16:15 42316 30307640 0 /usr/bin/some_command with some parameters -32179 1 someuser Ss 0:46.91 0:33.35 07-00:25:38 9824 5290628 0 /usr/bin/some_command with some parameters -32424 2828 someuser S 0:04.34 0:02.46 01-22:11:47 9352 30013604 0 /usr/bin/some_command with some parameters -33878 1 someuser S 1:11.98 2:05.21 04-19:05:27 12044 5461036 0 /usr/bin/some_command with some parameters -33945 25257 someuser S 4:06.13 49:50.93 04-19:05:09 1084480 5695004 0 /usr/bin/some_command with some parameters -37665 1 someuser S 0:09.40 0:08.78 19:56:02 18148 4738308 0 /usr/bin/some_command with some parameters -37728 2828 someuser S 0:01.70 0:01.72 01-21:26:00 6720 29984528 0 /usr/bin/some_command with some parameters -38532 1 someuser S 0:00.18 0:00.34 19:12:29 4148 4507568 0 /usr/bin/some_command with some parameters -38747 1 root Ss 0:01.51 0:01.02 19:02:32 6952 4507708 0 /usr/bin/some_command with some parameters -40037 1 someuser S 0:11.66 0:17.17 06-19:11:03 6660 5376404 0 /usr/bin/some_command with some parameters -40686 2828 someuser S 0:05.48 0:06.76 01-21:05:59 20752 30001376 0 /usr/bin/some_command with some parameters -40698 2828 someuser S 0:10.13 0:05.07 01-21:05:52 12364 30248656 0 /usr/bin/some_command with some parameters -40707 2828 someuser S 0:01.26 0:02.23 01-21:05:49 16620 29990580 0 /usr/bin/some_command with some parameters -41159 2828 someuser S 0:01.59 0:01.43 01-21:01:44 4488 30007584 0 /usr/bin/some_command with some parameters -41458 1 root Ss 0:00.23 0:00.58 17:31:36 8844 4502512 0 /usr/bin/some_command with some parameters -41491 1 root Ss 0:01.84 0:03.01 17:31:03 2428 4462264 0 /usr/bin/some_command with some parameters -41501 1 someuser S 0:00.25 0:00.38 17:01:05 7196 4507212 0 /usr/bin/some_command with some parameters -41507 1 someuser Ss 0:02.07 0:01.29 17:01:04 39712 5001496 0 /usr/bin/some_command with some parameters -41513 1 root Ss 0:00.07 0:00.32 17:01:04 4624 4506916 0 /usr/bin/some_command with some parameters -41520 1 root Ss 0:00.10 0:00.09 17:01:03 3408 4501336 0 /usr/bin/some_command with some parameters -41747 1 someuser S 0:00.56 0:01.09 15:16:53 25600 4553952 0 /usr/bin/some_command with some parameters -41837 1 root Ss 0:03.48 0:01.11 13:14:29 9504 4508288 0 /usr/bin/some_command with some parameters -41852 1 root Ss 0:00.06 0:00.15 11:14:25 2240 4469368 0 /usr/bin/some_command with some parameters -41855 1 root Ss 0:00.12 0:00.10 11:14:23 4268 4469548 0 /usr/bin/some_command with some parameters -41869 1 someuser S 0:00.34 0:00.27 11:14:21 3032 4501416 0 /usr/bin/some_command with some parameters -41875 1 someuser S 0:00.17 0:00.15 11:14:20 7516 4501204 0 /usr/bin/some_command with some parameters -41878 1 someuser S 0:00.51 0:00.58 11:14:20 34728 4939584 0 /usr/bin/some_command with some parameters -41886 1 root Ss 0:06.23 0:00.47 11:14:12 2740 4558804 0 /usr/bin/some_command with some parameters -41890 1 root Ss 0:00.65 0:02.81 11:14:11 4040 4470284 0 /usr/bin/some_command with some parameters -41897 1 root Ss 0:00.07 0:00.17 11:14:08 10828 4442748 0 /usr/bin/some_command with some parameters -41908 1 someuser Ss 0:00.17 0:00.39 11:13:56 2316 4472152 0 /usr/bin/some_command with some parameters -41912 1 root Ss 0:00.04 0:00.20 11:13:39 3524 4469036 0 /usr/bin/some_command with some parameters -41926 1 root Ss 0:01.47 0:03.00 09:13:37 3596 4504084 0 /usr/bin/some_command with some parameters -42029 1 _netbios SNs 0:00.06 0:00.18 07:11:50 3056 4469768 0 /usr/bin/some_command with some parameters -42082 1 someuser S 0:00.38 0:00.55 05:10:33 10624 4709112 0 /usr/bin/some_command with some parameters -42094 1 _driverkit Ss 0:00.00 0:00.01 04:06:08 1240 4802280 0 /usr/bin/some_command with some parameters -42095 1 _driverkit Ss 0:00.00 0:00.01 04:06:08 1248 4803304 0 /usr/bin/some_command with some parameters -42096 1 _driverkit Ss 0:00.29 0:00.72 04:06:08 1676 4810472 0 /usr/bin/some_command with some parameters -42097 1 _driverkit Ss 0:00.01 0:00.03 04:06:08 1276 4807400 0 /usr/bin/some_command with some parameters -42098 1 _driverkit Ss 0:00.00 0:00.01 04:06:08 1236 4801256 0 /usr/bin/some_command with some parameters -42100 1 _driverkit Ss 0:00.00 0:00.01 04:06:08 1248 4826876 0 /usr/bin/some_command with some parameters -42115 1 root Ss 0:00.01 0:00.04 04:06:07 1168 4419408 0 /usr/bin/some_command with some parameters -42121 1 someuser S 0:00.71 0:00.98 04:06:07 18876 4672452 0 /usr/bin/some_command with some parameters -42139 1 someuser S 0:00.18 0:00.28 04:06:03 12144 4512888 0 /usr/bin/some_command with some parameters -42155 1 someuser S 0:00.07 0:00.17 04:06:02 5080 4635844 0 /usr/bin/some_command with some parameters -42306 1 _spotlight S 0:00.93 0:00.47 04:05:57 3272 4503812 0 /usr/bin/some_command with some parameters -42930 1 newrelic S 0:00.66 0:00.20 04:05:33 3060 4503644 0 /usr/bin/some_command with some parameters -42931 1 666 S 0:00.75 0:00.26 04:05:33 3308 4503780 0 /usr/bin/some_command with some parameters -42958 1 someuser S 0:02.07 0:07.75 06-18:12:41 4012 4560160 0 /usr/bin/some_command with some parameters -43266 1 someuser S 0:00.28 0:00.53 04:05:22 10800 5133048 0 /usr/bin/some_command with some parameters -43267 1 someuser S 0:00.31 0:00.30 04:05:22 5900 4521780 0 /usr/bin/some_command with some parameters -43686 1 someuser S 0:00.31 0:00.19 04:05:06 6176 4500888 0 /usr/bin/some_command with some parameters -43718 2828 someuser S 0:21.05 0:06.89 04:04:51 61656 29988608 0 /usr/bin/some_command with some parameters -43719 1 _gamecontrollerd Ss 0:35.23 0:34.34 04:04:50 6484 4501660 0 /usr/bin/some_command with some parameters -43720 1 _coreaudiod Ss 0:00.27 0:00.28 04:04:49 1784 4470408 0 /usr/bin/some_command with some parameters -43724 1 someuser S 0:00.35 0:00.50 04:04:40 13356 4555412 0 /usr/bin/some_command with some parameters -43725 1 someuser S 0:00.35 0:00.27 04:04:39 7656 4504552 0 /usr/bin/some_command with some parameters -43726 1 someuser S 0:00.05 0:00.10 04:04:38 4756 4469172 0 /usr/bin/some_command with some parameters -43728 2828 someuser S 0:01.16 0:00.50 04:04:36 33280 29981708 0 /usr/bin/some_command with some parameters -43729 2828 someuser S 0:00.96 0:00.56 04:04:32 39820 30201408 0 /usr/bin/some_command with some parameters -43731 1 root Ss 0:00.07 0:00.12 04:04:31 5112 4501764 0 /usr/bin/some_command with some parameters -43865 1 someuser Ss 0:00.20 0:00.26 04:01:01 11512 4513268 0 /usr/bin/some_command with some parameters -43867 1 someuser S 0:00.11 0:00.14 04:01:01 2668 4501528 0 /usr/bin/some_command with some parameters -43868 1 root Ss 4:07.09 1:17.30 04:01:01 9064 4510512 0 /usr/bin/some_command with some parameters -43869 1 someuser S 0:00.09 0:00.10 04:01:01 6516 4503240 0 /usr/bin/some_command with some parameters -43871 1 someuser S 0:00.27 0:00.39 04:01:01 9324 4510864 0 /usr/bin/some_command with some parameters -43873 1 root Ss 0:00.05 0:00.03 04:01:00 2604 4469424 0 /usr/bin/some_command with some parameters -43874 1 _fpsd Ss 0:00.02 0:00.03 04:01:00 2580 4462948 0 /usr/bin/some_command with some parameters -43880 1 root Ss 0:00.01 0:00.02 04:01:00 1172 4428420 0 /usr/bin/some_command with some parameters -43881 1 someuser S 0:00.03 0:00.04 04:01:00 5300 4501132 0 /usr/bin/some_command with some parameters -43882 1 someuser S 0:00.95 0:00.91 04:01:00 14520 5154420 0 /usr/bin/some_command with some parameters -43883 1 root Ss 0:00.10 0:00.15 04:01:00 5856 4501964 0 /usr/bin/some_command with some parameters -43889 1 someuser S 0:00.19 0:00.44 04:00:59 12500 4559048 0 /usr/bin/some_command with some parameters -43890 1 someuser S 0:00.01 0:00.04 04:00:59 3476 4469156 0 /usr/bin/some_command with some parameters -43892 1 root Ss 0:00.13 0:00.08 04:00:59 6208 4505372 0 /usr/bin/some_command with some parameters -43893 139 root SN 6:02.86 1:53.28 04:00:59 56592 4579620 0 /usr/bin/some_command with some parameters -43895 1 someuser S 0:00.42 0:01.13 04:00:59 10948 4525368 0 /usr/bin/some_command with some parameters -43896 1 someuser S 0:00.03 0:00.03 04:00:58 4056 4460904 0 /usr/bin/some_command with some parameters -43898 1 someuser S 0:00.21 0:00.57 04:00:58 10428 4517276 0 /usr/bin/some_command with some parameters -43901 1 someuser S 0:00.15 0:00.36 04:00:57 10512 4983912 0 /usr/bin/some_command with some parameters -43904 1 someuser S 0:00.28 0:00.88 04:00:57 15404 4508360 0 /usr/bin/some_command with some parameters -43907 1 someuser S 0:00.07 0:00.07 04:00:56 8780 4501212 0 /usr/bin/some_command with some parameters -43908 1 _installcoordinationd Ss 0:00.03 0:00.04 04:00:56 3548 4461340 0 /usr/bin/some_command with some parameters -43910 1 root Ss 0:00.08 0:00.05 04:00:56 2204 4469212 0 /usr/bin/some_command with some parameters -43916 1 root Ss 0:00.01 0:00.02 04:00:55 2456 4428548 0 /usr/bin/some_command with some parameters -43918 1 root Ss 0:00.17 0:00.22 04:00:51 4868 4502444 0 /usr/bin/some_command with some parameters -43936 1 someuser S 0:13.05 0:02.34 04:00:45 102644 4904648 0 /usr/bin/some_command with some parameters -43941 1 someuser S 0:00.03 0:00.06 04:00:45 6680 4505292 0 /usr/bin/some_command with some parameters -43942 1 root Ss 0:00.01 0:00.05 04:00:44 1216 4387164 0 /usr/bin/some_command with some parameters -43956 1 root Ss 0:00.11 0:00.16 04:00:12 7552 4501572 0 /usr/bin/some_command with some parameters -43957 1 root Ss 0:00.01 0:00.03 04:00:12 2224 4469360 0 /usr/bin/some_command with some parameters -43966 1 someuser S 0:00.22 0:00.41 03:59:56 9244 4502860 0 /usr/bin/some_command with some parameters -43971 1 someuser S 0:00.46 0:00.84 03:59:39 14664 4514540 0 /usr/bin/some_command with some parameters -43973 1 someuser S 0:00.07 0:00.08 03:59:38 6744 4501644 0 /usr/bin/some_command with some parameters -43974 1 someuser S 0:00.12 0:00.19 03:59:38 9636 4534772 0 /usr/bin/some_command with some parameters -43975 1 someuser S 0:00.03 0:00.11 03:59:38 2436 4501720 0 /usr/bin/some_command with some parameters -43976 1 someuser S 0:00.07 0:00.21 03:59:38 6548 4504276 0 /usr/bin/some_command with some parameters -43977 1 _assetcache Ss 0:00.04 0:00.06 03:59:38 4788 4462272 0 /usr/bin/some_command with some parameters -43978 1 root Ss 0:00.04 0:00.03 03:59:38 3144 4472376 0 /usr/bin/some_command with some parameters -43983 1 root SNs 0:00.00 0:00.01 03:59:21 396 4383348 0 /usr/bin/some_command with some parameters -43984 1 root Ss 0:00.00 0:00.01 03:59:21 1200 4418664 0 /usr/bin/some_command with some parameters -44067 1 someuser S 0:00.05 0:00.08 03:59:06 6920 5019924 0 /usr/bin/some_command with some parameters -44068 1 someuser S 0:00.38 0:00.92 03:59:06 12356 4511004 0 /usr/bin/some_command with some parameters -44070 1 someuser Ss 0:00.02 0:00.04 03:59:05 7044 4526648 0 /usr/bin/some_command with some parameters -44072 1 someuser S 0:00.38 0:00.88 03:59:05 25828 4555100 0 /usr/bin/some_command with some parameters -44073 1 someuser S 0:00.02 0:00.04 03:59:05 4544 4451212 0 /usr/bin/some_command with some parameters -44074 1 someuser S 0:00.05 0:00.05 03:59:05 6052 4486044 0 /usr/bin/some_command with some parameters -44075 1 someuser Ss 0:00.01 0:00.01 03:59:04 2360 4404832 0 /usr/bin/some_command with some parameters -44076 1 someuser S 0:00.02 0:00.04 03:59:04 6496 4489536 0 /usr/bin/some_command with some parameters -44083 1 someuser S 0:00.07 0:00.08 03:58:50 7508 4505784 0 /usr/bin/some_command with some parameters -44084 1 someuser S 0:00.01 0:00.01 03:58:50 3188 4484560 0 /usr/bin/some_command with some parameters -44085 1 someuser S 0:00.13 0:00.07 03:58:50 5084 4502116 0 /usr/bin/some_command with some parameters -44086 1 root Ss 0:00.08 0:00.18 03:58:50 4188 4475624 0 /usr/bin/some_command with some parameters -44090 1 someuser S 0:00.16 0:00.33 03:58:49 12008 4712592 0 /usr/bin/some_command with some parameters -44098 1 someuser Ss 0:34.74 0:36.48 03:58:36 4884 4544372 0 /usr/bin/some_command with some parameters -44099 1 root Ss 0:00.06 0:00.06 03:58:35 5176 4501788 0 /usr/bin/some_command with some parameters -44100 1 someuser S 0:00.24 0:00.58 03:58:35 13316 4717696 0 /usr/bin/some_command with some parameters -44101 1 root Ss 0:00.04 0:00.01 03:58:35 1204 4414572 0 /usr/bin/some_command with some parameters -44103 1 someuser S 0:00.17 0:00.38 03:58:19 12108 4984140 0 /usr/bin/some_command with some parameters -44153 1 root Ss 0:00.20 0:00.23 03:52:58 2776 4471612 0 /usr/bin/some_command with some parameters -44167 1 root Ss 0:00.33 0:00.21 03:52:37 3912 4514384 0 /usr/bin/some_command with some parameters -44185 1 someuser Ss 0:00.74 0:00.41 03:51:04 38244 4967236 0 /usr/bin/some_command with some parameters -44520 1 root Ss 0:21.43 0:01.69 03:37:59 3624 4478804 0 /usr/bin/some_command with some parameters -44805 1 someuser Ss 0:00.78 0:00.37 03:25:11 40244 5132076 0 /usr/bin/some_command with some parameters -44913 1 root Ss 0:00.02 0:00.02 03:23:40 992 4409696 0 /usr/bin/some_command with some parameters -45056 1 root Ss 0:00.01 0:00.03 03:16:33 3544 4457836 0 /usr/bin/some_command with some parameters -45060 1 root Ss 0:00.03 0:00.02 03:16:16 2480 4484908 0 /usr/bin/some_command with some parameters -45062 1 root Ss 0:00.00 0:00.01 03:16:16 1616 4428404 0 /usr/bin/some_command with some parameters -45063 1 root Ss 0:00.01 0:00.02 03:16:16 3164 4493296 0 /usr/bin/some_command with some parameters -45064 1 someuser S 0:00.03 0:00.02 03:16:16 5876 4469764 0 /usr/bin/some_command with some parameters -45065 1 someuser Ss 0:00.01 0:00.04 03:16:15 8272 4482376 0 /usr/bin/some_command with some parameters -45066 1 root Ss 0:00.00 0:00.01 03:16:15 2820 4452456 0 /usr/bin/some_command with some parameters -45067 1 root Ss 0:00.00 0:00.01 03:16:15 2868 4452456 0 /usr/bin/some_command with some parameters -45068 1 root Ss 0:00.03 0:00.02 03:16:15 2740 4493316 0 /usr/bin/some_command with some parameters -45069 1 someuser Ss 0:00.02 0:00.02 03:16:15 9216 4457000 0 /usr/bin/some_command with some parameters -45070 1 someuser Ss 0:00.08 0:00.17 03:16:14 31604 4614864 0 /usr/bin/some_command with some parameters -45071 1 someuser Ss 0:00.02 0:00.02 03:16:14 9204 4490792 0 /usr/bin/some_command with some parameters -45073 1 root Ss 0:00.01 0:00.02 03:16:05 2704 4434688 0 /usr/bin/some_command with some parameters -45096 1 _appstore Ss 0:00.20 0:00.47 03:15:29 11272 4512396 0 /usr/bin/some_command with some parameters -45097 1 someuser S 0:00.07 0:00.09 03:15:29 5152 4461828 0 /usr/bin/some_command with some parameters -45098 1 root Ss 0:00.06 0:00.03 03:15:28 4412 4502452 0 /usr/bin/some_command with some parameters -45101 1 someuser S 0:00.24 0:00.10 03:15:12 8556 4505324 0 /usr/bin/some_command with some parameters -45104 1 root Ss 0:00.01 0:00.02 03:14:56 2960 4436732 0 /usr/bin/some_command with some parameters -45105 1 root Ss 0:00.01 0:00.02 03:14:56 1996 4436764 0 /usr/bin/some_command with some parameters -45106 1 root Ss 0:00.02 0:00.03 03:14:55 4512 4448868 0 /usr/bin/some_command with some parameters -45111 1 _applepay Ss 0:00.06 0:00.11 03:14:39 4036 4465524 0 /usr/bin/some_command with some parameters -45174 1 someuser S 0:00.36 0:00.38 03:10:31 2328 4469464 0 /usr/bin/some_command with some parameters -45206 2828 someuser S 0:13.66 0:09.04 08-00:49:53 12592 29970564 0 /usr/bin/some_command with some parameters -45624 1 someuser S 0:00.01 0:00.03 02:54:41 6752 4436088 0 /usr/bin/some_command with some parameters -45782 1 someuser S 0:00.02 0:00.04 02:49:03 2348 4500936 0 /usr/bin/some_command with some parameters -45792 2828 someuser S 0:02.15 0:01.37 02:48:40 50716 29998868 0 /usr/bin/some_command with some parameters -45933 1 someuser S 0:00.11 0:00.11 02:43:47 5596 4500920 0 /usr/bin/some_command with some parameters -45982 1 _iconservices Ss 0:00.03 0:00.01 02:40:31 1884 4426912 0 /usr/bin/some_command with some parameters -46122 25257 someuser S 3:58.32 55:53.41 05-19:37:35 1090964 5687776 0 /usr/bin/some_command with some parameters -46396 1 someuser S 0:03.56 0:04.82 05-19:35:48 3460 4508436 0 /usr/bin/some_command with some parameters -46645 2828 someuser S 0:00.85 0:00.34 02:17:06 44544 30006608 0 /usr/bin/some_command with some parameters -46738 2828 someuser S 0:02.09 0:01.33 02:16:11 58224 30072984 0 /usr/bin/some_command with some parameters -47353 2828 someuser S 0:09.82 0:01.73 01:56:34 54452 30222164 0 /usr/bin/some_command with some parameters -47355 2828 someuser S 0:04.39 0:00.96 01:56:29 47800 30005420 0 /usr/bin/some_command with some parameters -49788 1 root Ss 0:00.03 0:00.07 01:06:50 7128 4470308 0 /usr/bin/some_command with some parameters -51166 1 _softwareupdate Ss 1:26.27 1:40.78 06-16:20:59 7132 4600944 0 /usr/bin/some_command with some parameters -51168 1 root Ss 0:00.25 0:01.68 06-16:20:58 644 4504988 0 /usr/bin/some_command with some parameters -51169 1 _atsserver Ss 0:00.25 0:00.74 06-16:20:58 788 4470832 0 /usr/bin/some_command with some parameters -51368 1 someuser S 0:04.07 0:09.22 06-15:27:09 6624 4537084 0 /usr/bin/some_command with some parameters -52356 2828 someuser S 0:01.51 0:00.48 31:56 58868 34199560 0 /usr/bin/some_command with some parameters -52359 2828 someuser S 0:06.29 0:01.36 31:53 55940 30230764 0 /usr/bin/some_command with some parameters -53270 1 root Ss 0:00.03 0:00.04 30:48 3076 4460200 0 /usr/bin/some_command with some parameters -53628 1 root Ss 0:00.01 0:00.02 29:30 3176 4425516 0 /usr/bin/some_command with some parameters -53631 1 root Ss 0:00.03 0:00.02 29:29 1476 4424424 0 /usr/bin/some_command with some parameters -53753 1 someuser S 0:00.06 0:00.06 23:38 4072 4476468 0 /usr/bin/some_command with some parameters -53792 1 root Ss 0:00.03 0:00.08 21:02 3104 4479136 0 /usr/bin/some_command with some parameters -53793 1 root Ss 0:00.00 0:00.01 21:02 2372 4405596 0 /usr/bin/some_command with some parameters -53835 2838 someuser S 2:02.77 0:49.41 20:45 40960 4694608 0 /usr/bin/some_command with some parameters -53836 2838 someuser S 1:14.02 0:24.45 20:43 29924 4802016 0 /usr/bin/some_command with some parameters -53837 1 someuser Ss 0:00.12 0:00.13 20:43 5212 4550420 0 /usr/bin/some_command with some parameters -53838 1 someuser Ss 0:00.03 0:00.13 20:43 2288 4892308 0 /usr/bin/some_command with some parameters -53839 1 someuser Ss 0:00.04 0:00.24 20:42 1752 4849368 0 /usr/bin/some_command with some parameters -53885 2828 someuser S 0:01.36 0:00.27 17:24 53856 30073136 0 /usr/bin/some_command with some parameters -53929 2828 someuser S 0:01.62 0:00.37 14:25 49896 30006408 0 /usr/bin/some_command with some parameters -53931 2828 someuser S 0:00.09 0:00.04 14:20 20312 29973136 0 /usr/bin/some_command with some parameters -54166 1 someuser S 0:00.17 0:00.23 12:08 14300 4862340 0 /usr/bin/some_command with some parameters -54402 1 someuser S 0:00.08 0:00.05 02:40 9952 4477448 0 /usr/bin/some_command with some parameters -54840 1 someuser S 0:00.05 0:00.02 00:14 5132 4444152 0 /usr/bin/some_command with some parameters -55706 1 root Ss 0:00.01 0:00.06 01-19:23:23 264 4452756 0 /usr/bin/some_command with some parameters -56786 2828 someuser S 2:44.32 0:46.63 01-19:17:22 101724 30335308 0 /usr/bin/some_command with some parameters -67087 1 someuser S< 0:00.02 0:00.13 05-12:33:55 128 4502220 0 /usr/bin/some_command with some parameters -70071 1 root Ss 0:00.09 0:00.22 01-03:34:12 2100 4485696 0 /usr/bin/some_command with some parameters -70682 1 _usbmuxd Ss 0:00.05 0:00.15 01-03:32:05 1596 4464252 0 /usr/bin/some_command with some parameters -70696 1 someuser S 0:00.47 0:01.53 01-03:32:04 6708 4703432 0 /usr/bin/some_command with some parameters -70752 1 someuser S 0:00.07 0:00.32 01-03:31:59 2964 4507376 0 /usr/bin/some_command with some parameters -70896 1 _driverkit Ss 0:00.00 0:00.02 01-03:30:59 220 4800232 0 /usr/bin/some_command with some parameters -70898 1 _driverkit Ss 0:31.53 1:25.95 01-03:30:59 756 4810996 0 /usr/bin/some_command with some parameters -70899 1 _driverkit Ss 0:24.49 0:36.97 01-03:30:59 684 4810496 0 /usr/bin/some_command with some parameters -71311 1 root Ss 0:14.93 0:25.21 01-03:27:30 11168 4506632 0 /usr/bin/some_command with some parameters -75951 2828 someuser S 0:07.09 0:05.92 05-23:52:24 8116 29977444 0 /usr/bin/some_command with some parameters -76232 2828 someuser S 0:32.61 0:25.42 05-23:50:45 22396 30003880 0 /usr/bin/some_command with some parameters -79317 2828 someuser S 0:12.80 0:09.77 05-01:26:20 7100 30014992 0 /usr/bin/some_command with some parameters -79623 2828 someuser S 0:27.32 0:15.83 01-02:17:54 39500 34189824 0 /usr/bin/some_command with some parameters -79636 2828 someuser S 0:23.39 0:15.08 01-02:17:50 38148 34197540 0 /usr/bin/some_command with some parameters -79637 2828 someuser S 0:00.31 0:00.60 01-02:17:50 6348 29973680 0 /usr/bin/some_command with some parameters -79692 2828 someuser S 0:41.06 0:19.53 01-02:17:38 105872 30086076 0 /usr/bin/some_command with some parameters -79727 1 someuser S 13:34.28 13:44.94 06-20:37:35 26636 5376912 0 /usr/bin/some_command with some parameters -79738 2828 someuser S 0:51.34 0:25.93 01-02:17:16 62400 30105596 0 /usr/bin/some_command with some parameters -80172 2828 someuser S 0:10.71 0:04.62 01-02:13:44 30536 30002272 0 /usr/bin/some_command with some parameters -87090 6305 someuser ?Es 0:00.00 0:00.00 07-23:41:16 0 /usr/bin/some_command with some parameters -87324 87090 someuser Z 0:00.00 0:00.00 07-23:41:12 0 /usr/bin/some_command with some parameters -87339 2828 someuser S 2:04.19 0:29.69 01-01:22:11 47436 34221640 0 /usr/bin/some_command with some parameters -89436 1 someuser S 0:05.20 0:06.94 05-23:11:51 5904 4538312 0 /usr/bin/some_command with some parameters -89517 2828 someuser S 20:37.55 3:52.48 04-00:35:47 209016 63653976 0 /usr/bin/some_command with some parameters -92412 2828 someuser S 1:40.63 0:34.70 01-00:57:02 79664 42633048 0 /usr/bin/some_command with some parameters -96559 1 someuser S 0:27.56 0:15.84 04-23:43:59 18544 5430400 0 /usr/bin/some_command with some parameters -97411 2828 someuser S 0:02.14 0:02.34 02-02:46:46 4928 29984652 0 /usr/bin/some_command with some parameters -98939 2828 someuser S 0:34.14 0:09.05 01-00:34:12 51468 30021300 0 /usr/bin/some_command with some parameters -99779 2828 someuser S 0:12.27 0:04.10 01-00:29:06 24548 30019412 0 /usr/bin/some_command with some parameters -99817 6305 someuser S 0:00.38 0:01.25 04-23:18:44 2604 42554704 0 /usr/bin/some_command with some parameters -99889 2828 someuser S 0:05.75 0:05.06 01-00:28:17 27912 30287484 0 /usr/bin/some_command with some parameters - 2956 2949 root Ss 0:00.02 0:00.03 08-14:42:23 12 4469120 0 /usr/bin/some_command with some parameters - 2959 2956 someuser S 0:00.26 0:00.30 08-14:42:22 8 4370964 0 /usr/bin/some_command with some parameters - 6945 2959 someuser S+ 0:00.01 0:00.05 08-03:13:00 112 4297872 0 /usr/bin/some_command with some parameters - 6948 6947 someuser Ss+ 0:00.38 0:00.58 08-03:12:59 200 4362720 0 /usr/bin/some_command with some parameters - 6999 6947 someuser Ss+ 0:00.29 0:00.52 08-03:12:57 8 4348436 0 /usr/bin/some_command with some parameters - 7049 6947 someuser Ss+ 0:00.20 0:00.20 08-03:12:56 8 4338196 0 /usr/bin/some_command with some parameters -11147 6947 someuser Ss+ 0:00.46 0:00.42 08-02:35:38 8 4338196 0 /usr/bin/some_command with some parameters -65815 6947 someuser Ss+ 0:00.67 0:01.10 08-00:18:51 3016 4362844 0 /usr/bin/some_command with some parameters - 1393 6947 someuser Ss+ 0:00.31 0:00.36 07-23:15:08 8 4338064 0 /usr/bin/some_command with some parameters -26136 6305 someuser Ss+ 0:00.33 0:00.54 07-03:31:53 228 4370964 0 /usr/bin/some_command with some parameters -42855 6947 someuser Ss 0:00.33 0:00.61 01-20:49:21 3192 4355472 0 /usr/bin/some_command with some parameters -54887 42855 root R+ 0:00.00 0:00.01 00:01 1076 4269016 0 /usr/bin/some_command with some parameters` - -var psOutThreads10 = `USER PID TT %CPU STAT PRI STIME UTIME COMMAND -root 1 ?? 0.0 S 31T 0:00.39 0:00.09 /usr/bin/some_command with some parameters - 1 0.0 S 20T 0:00.15 0:00.04 - 1 0.6 S 37T 0:00.01 0:00.00 -root 68 ?? 0.0 S 4T 0:01.24 0:00.33 /usr/bin/some_command with some parameters - 68 0.0 S 4T 0:00.00 0:00.00 - 68 0.0 S 4T 0:00.00 0:00.00 - 68 0.0 S 4T 0:00.00 0:00.00 -root 69 ?? 0.0 S 31T 0:00.20 0:00.08 /usr/bin/some_command with some parameters - 69 0.0 S 31T 0:00.00 0:00.00 - 69 0.0 S 31T 0:00.00 0:00.00 - 69 0.0 S 4T 0:00.01 0:00.00 -root 72 ?? 0.0 S 20T 0:00.98 0:00.94 /usr/bin/some_command with some parameters - 72 0.0 S 20T 0:00.00 0:00.00 -root 73 ?? 0.0 S 49T 0:27.84 1:22.66 /usr/bin/some_command with some parameters - 73 0.0 S 31T 0:00.00 0:00.00 - 73 0.0 S 31T 0:00.02 0:00.01 - 73 0.0 S 50R 0:16.31 0:03.31 - 73 0.0 S 31T 0:01.93 0:07.66 - 73 0.0 S 31T 0:13.32 0:04.10 - 73 0.0 S 31T 0:57.43 0:05.99 - 73 0.0 S 31T 0:13.38 0:01.60 - 73 0.0 S 31T 0:09.00 0:00.96 - 73 0.0 S 31T 0:02.29 0:00.64 - 73 0.0 S 31T 0:08.64 0:01.02 - 73 0.0 S 31T 0:00.01 0:00.00 - 73 0.0 S 31T 0:00.00 0:00.00 - 73 0.0 S 31T 0:00.00 0:00.00 -root 74 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters - 74 0.0 S 20T 0:00.04 0:00.01 -root 75 ?? 0.0 S 4T 0:04.35 0:07.60 /usr/bin/some_command with some parameters - 75 0.0 S 4T 0:00.00 0:00.00 - 75 0.0 S 4T 0:00.00 0:00.00 -root 81 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters - 81 0.0 S 20T 0:00.05 0:00.01 -root 82 ?? 0.0 S 4T 0:06.92 0:05.91 /usr/bin/some_command with some parameters - 82 0.0 S 4T 0:00.00 0:00.00 - 82 0.0 S 4T 0:00.00 0:00.00 -root 84 ?? 0.0 S 31T 0:04.14 0:02.10 /usr/bin/some_command with some parameters - 84 0.0 S 31T 0:00.12 0:00.03 - 84 0.0 S 31T 0:00.25 0:00.13 - 84 0.0 S 31T 0:00.65 0:00.28 - 84 0.0 S 31T 0:00.21 0:00.10 - 84 0.0 S 31T 0:00.33 0:00.18 - 84 0.0 S 31T 0:00.37 0:00.16 - 84 0.0 S 31T 0:00.01 0:00.00 -root 86 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 86 0.0 S 37T 0:00.01 0:00.01 - 86 0.0 S 37T 0:00.00 0:00.00 -root 88 ?? 0.0 S 20T 0:10.90 0:10.20 /usr/bin/some_command with some parameters - 88 0.0 S 20T 24:38.78 20:49.45 - 88 0.0 S 20T 0:00.00 0:00.00 - 88 0.0 S 20T 0:00.00 0:00.00 -root 91 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 91 0.0 S 31T 0:00.00 0:00.00 - 91 0.0 S 31T 0:00.00 0:00.00 - 91 0.0 S 31T 0:00.00 0:00.00 -root 93 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 93 0.0 S 20T 0:00.01 0:00.01 - 93 0.0 S 20T 0:00.01 0:00.00 - 93 0.0 S 20T 0:00.00 0:00.00 -root 99 ?? 0.0 S 31T 0:00.04 0:00.01 /usr/bin/some_command with some parameters - 99 0.0 S 97R 0:00.16 0:00.06 - 99 0.0 S 31T 0:00.04 0:00.06 -` - -var psOutThreads100 = `USER PID TT %CPU STAT PRI STIME UTIME COMMAND -root 1 ?? 0.0 S 31T 0:00.39 0:00.09 /usr/bin/some_command with some parameters - 1 0.0 S 20T 0:00.15 0:00.04 - 1 0.6 S 37T 0:00.01 0:00.00 -root 68 ?? 0.0 S 4T 0:01.24 0:00.33 /usr/bin/some_command with some parameters - 68 0.0 S 4T 0:00.00 0:00.00 - 68 0.0 S 4T 0:00.00 0:00.00 - 68 0.0 S 4T 0:00.00 0:00.00 -root 69 ?? 0.0 S 31T 0:00.20 0:00.08 /usr/bin/some_command with some parameters - 69 0.0 S 31T 0:00.00 0:00.00 - 69 0.0 S 31T 0:00.00 0:00.00 - 69 0.0 S 4T 0:00.01 0:00.00 -root 72 ?? 0.0 S 20T 0:00.98 0:00.94 /usr/bin/some_command with some parameters - 72 0.0 S 20T 0:00.00 0:00.00 -root 73 ?? 0.0 S 49T 0:27.84 1:22.66 /usr/bin/some_command with some parameters - 73 0.0 S 31T 0:00.00 0:00.00 - 73 0.0 S 31T 0:00.02 0:00.01 - 73 0.0 S 50R 0:16.31 0:03.31 - 73 0.0 S 31T 0:01.93 0:07.66 - 73 0.0 S 31T 0:13.32 0:04.10 - 73 0.0 S 31T 0:57.43 0:05.99 - 73 0.0 S 31T 0:13.38 0:01.60 - 73 0.0 S 31T 0:09.00 0:00.96 - 73 0.0 S 31T 0:02.29 0:00.64 - 73 0.0 S 31T 0:08.64 0:01.02 - 73 0.0 S 31T 0:00.01 0:00.00 - 73 0.0 S 31T 0:00.00 0:00.00 - 73 0.0 S 31T 0:00.00 0:00.00 -root 74 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters - 74 0.0 S 20T 0:00.04 0:00.01 -root 75 ?? 0.0 S 4T 0:04.35 0:07.60 /usr/bin/some_command with some parameters - 75 0.0 S 4T 0:00.00 0:00.00 - 75 0.0 S 4T 0:00.00 0:00.00 -root 81 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters - 81 0.0 S 20T 0:00.05 0:00.01 -root 82 ?? 0.0 S 4T 0:06.92 0:05.91 /usr/bin/some_command with some parameters - 82 0.0 S 4T 0:00.00 0:00.00 - 82 0.0 S 4T 0:00.00 0:00.00 -root 84 ?? 0.0 S 31T 0:04.14 0:02.10 /usr/bin/some_command with some parameters - 84 0.0 S 31T 0:00.12 0:00.03 - 84 0.0 S 31T 0:00.25 0:00.13 - 84 0.0 S 31T 0:00.65 0:00.28 - 84 0.0 S 31T 0:00.21 0:00.10 - 84 0.0 S 31T 0:00.33 0:00.18 - 84 0.0 S 31T 0:00.37 0:00.16 - 84 0.0 S 31T 0:00.01 0:00.00 -root 86 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 86 0.0 S 37T 0:00.01 0:00.01 - 86 0.0 S 37T 0:00.00 0:00.00 -root 88 ?? 0.0 S 20T 0:10.90 0:10.20 /usr/bin/some_command with some parameters - 88 0.0 S 20T 24:38.78 20:49.45 - 88 0.0 S 20T 0:00.00 0:00.00 - 88 0.0 S 20T 0:00.00 0:00.00 -root 91 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 91 0.0 S 31T 0:00.00 0:00.00 - 91 0.0 S 31T 0:00.00 0:00.00 - 91 0.0 S 31T 0:00.00 0:00.00 -root 93 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 93 0.0 S 20T 0:00.01 0:00.01 - 93 0.0 S 20T 0:00.01 0:00.00 - 93 0.0 S 20T 0:00.00 0:00.00 -root 99 ?? 0.0 S 31T 0:00.04 0:00.01 /usr/bin/some_command with some parameters - 99 0.0 S 97R 0:00.16 0:00.06 - 99 0.0 S 31T 0:00.04 0:00.06 -root 103 ?? 0.0 S 31T 0:00.15 0:00.09 /usr/bin/some_command with some parameters - 103 0.0 S 50R 0:24.25 0:09.17 - 103 0.0 S 31T 0:00.00 0:00.00 - 103 0.0 S 4T 0:00.03 0:00.02 - 103 0.0 S 37T 0:00.00 0:00.00 - 103 0.0 S 20T 0:00.01 0:00.00 - 103 0.0 S 31T 0:00.00 0:00.00 - 103 0.0 S 37T 0:00.00 0:00.00 - 103 0.0 S 4T 0:00.00 0:00.00 -root 104 ?? 0.0 S 4T 0:01.01 0:09.28 /usr/bin/some_command with some parameters - 104 0.0 S 4T 2:14.81 5:02.47 - 104 0.0 S 4T 0:00.44 0:01.08 - 104 0.0 S 4T 0:12.12 2:53.73 - 104 0.0 S 4T 0:00.02 0:00.01 - 104 0.0 S 4T 0:15.99 0:06.45 - 104 0.0 S 4T 1:28.59 2:46.98 - 104 0.0 S 4T 0:04.30 0:12.81 - 104 0.0 S 4T 0:08.71 0:17.49 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:59.96 6:08.35 - 104 0.0 S 4T 0:23.11 0:36.48 - 104 0.0 S 4T 1:34.48 2:09.53 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:44.10 0:19.85 - 104 0.0 S 4T 0:00.52 0:00.17 - 104 0.0 S 4T 2:14.01 5:01.32 - 104 0.0 S 4T 2:13.19 5:01.40 - 104 0.0 S 4T 2:13.78 5:03.91 - 104 0.0 S 4T 2:15.10 5:04.41 - 104 0.0 S 4T 2:12.32 5:03.10 - 104 0.0 S 4T 0:12.37 2:53.81 - 104 0.0 S 4T 0:12.10 2:53.81 - 104 0.0 S 4T 2:14.30 5:02.53 - 104 0.0 S 4T 2:10.42 5:00.82 - 104 0.0 S 4T 2:14.03 5:02.43 - 104 0.0 S 4T 2:11.48 5:03.86 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:08.54 0:06.59 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:05.34 0:01.55 - 104 0.0 S 4T 0:03.66 0:01.06 - 104 0.0 S 4T 0:00.18 0:00.03 - 104 0.0 S 4T 0:37.36 0:06.80 - 104 0.0 S 4T 0:01.95 0:01.44 - 104 0.0 S 4T 0:37.35 0:40.82 - 104 0.0 S 4T 0:12.91 0:09.45 - 104 0.0 S 4T 0:02.40 0:00.68 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:08.22 0:07.64 - 104 0.0 S 4T 0:00.17 0:00.02 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.10 0:00.03 - 104 0.0 S 4T 0:01.59 0:01.74 - 104 0.0 S 4T 0:01.66 0:00.43 - 104 0.0 S 4T 0:00.98 0:00.21 - 104 0.0 S 4T 0:01.56 0:00.92 - 104 0.0 S 4T 0:00.02 0:00.00 - 104 0.0 S 4T 0:00.38 0:00.14 - 104 0.0 S 4T 0:00.92 0:00.25 - 104 0.0 S 4T 0:17.54 0:09.81 - 104 0.0 S 4T 0:05.13 0:01.18 - 104 0.0 S 4T 0:11.10 1:14.80 - 104 0.0 S 4T 2:27.08 11:01.59 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.76 0:00.28 - 104 0.3 S 4T 21:15.35 38:57.00 - 104 0.0 S 4T 0:01.84 0:00.31 - 104 0.0 S 4T 0:17.74 0:28.64 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:15.35 0:45.24 - 104 0.0 S 4T 0:00.70 0:00.30 - 104 0.0 S 4T 0:00.62 0:00.27 - 104 0.0 S 4T 0:00.64 0:00.31 - 104 0.0 S 4T 0:00.55 0:00.28 - 104 0.0 S 4T 0:02.64 0:04.64 - 104 0.0 S 4T 0:03.42 0:01.28 - 104 0.0 S 4T 0:05.43 0:02.17 - 104 0.0 S 4T 0:00.18 0:00.05 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.18 0:00.05 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:33.60 0:37.38 - 104 0.0 S 4T 0:24.14 0:08.43 - 104 0.0 S 4T 2:35.92 4:31.62 - 104 0.0 S 4T 0:04.54 0:03.01 - 104 0.0 S 4T 1:29.91 1:00.41 - 104 0.0 S 4T 0:04.88 0:01.94 - 104 0.0 S 4T 0:07.19 0:05.44 - 104 0.0 S 4T 0:02.68 0:00.83 - 104 0.0 S 4T 0:56.96 1:49.87 - 104 0.0 S 4T 0:12.03 0:07.99 - 104 0.0 S 4T 0:15.67 0:12.93 - 104 0.0 S 4T 0:25.82 1:14.97 - 104 0.0 S 4T 0:01.66 0:02.08 - 104 0.0 S 4T 0:08.01 0:40.99 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:02.42 0:02.15 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:01.54 0:01.59 - 104 0.0 S 4T 0:01.13 0:00.29 - 104 0.0 S 4T 0:02.33 0:00.85 - 104 0.0 S 4T 0:20.38 0:20.06 - 104 0.0 S 4T 1:52.14 25:52.43 - 104 0.0 S 4T 2:07.78 2:26.81 - 104 0.0 S 4T 4:04.54 4:24.85 - 104 0.0 S 4T 0:00.01 0:00.00 - 104 0.0 S 4T 0:00.88 0:02.39 - 104 0.0 S 4T 0:00.98 0:02.32 - 104 0.0 S 4T 0:00.83 0:02.10 - 104 0.3 S 4T 0:00.87 0:02.00 - 104 0.0 S 4T 0:00.62 0:01.89 - 104 0.0 S 4T 0:00.24 0:00.51 - 104 0.0 S 4T 0:00.85 0:04.37 - 104 0.0 S 4T 0:00.01 0:00.01 - 104 0.1 S 4T 0:00.11 0:00.17 - 104 0.0 S 4T 0:00.07 0:00.11 - 104 0.0 S 4T 0:00.02 0:00.03 - 104 0.0 S 4T 0:00.01 0:00.01 - 104 0.0 S 4T 0:00.00 0:00.00 -root 106 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 106 0.0 S 31T 0:00.00 0:00.00 -root 107 ?? 0.0 S 31T 0:24.80 0:41.84 /usr/bin/some_command with some parameters - 107 0.0 S 37T 0:00.00 0:00.00 -root 114 ?? 0.0 S 37T 0:00.03 0:00.00 /usr/bin/some_command with some parameters -root 115 ?? 0.0 S 4T 0:00.78 0:00.70 /usr/bin/some_command with some parameters - 115 0.0 S 4T 0:00.00 0:00.01 - 115 0.0 S 4T 0:00.00 0:00.00 - 115 0.0 S 4T 0:00.00 0:00.00 - 115 0.0 S 4T 0:00.01 0:00.02 -root 116 ?? 0.0 S 20T 0:00.42 0:00.96 /usr/bin/some_command with some parameters - 116 0.0 S 31T 0:00.00 0:00.00 - 116 0.0 S 20T 0:00.00 0:00.00 - 116 0.0 S 31T 0:00.20 0:00.07 - 116 0.0 S 31T 0:00.00 0:00.00 - 116 0.0 S 31T 0:00.00 0:00.00 - 116 0.0 S 31T 0:00.00 0:00.00 -root 117 ?? 0.0 S 31T 0:06.27 0:08.00 /usr/bin/some_command with some parameters - 117 0.0 S 31T 0:00.00 0:00.00 - 117 0.0 S 31T 0:00.01 0:00.00 -root 118 ?? 0.0 S 20T 0:00.02 0:00.01 /usr/bin/some_command with some parameters - 118 0.0 S 20T 0:00.22 0:00.11 - 118 0.0 S 20T 0:00.00 0:00.00 - 118 0.0 S 20T 0:00.00 0:00.00 - 118 0.0 S 20T 0:00.00 0:00.00 - 118 0.0 S 20T 0:00.00 0:00.00 -root 119 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 119 0.0 S 4T 0:00.00 0:00.00 - 119 0.0 S 4T 0:00.01 0:00.01 -_timed 120 ?? 0.0 S 31T 0:03.13 0:00.61 /usr/bin/some_command with some parameters - 120 0.0 S 4T 0:00.02 0:00.00 -root 123 ?? 0.0 S 31T 0:01.98 0:06.46 /usr/bin/some_command with some parameters - 123 0.0 S 31T 0:00.17 0:00.03 - 123 0.0 S 31T 0:00.00 0:00.00 -root 124 ?? 0.0 S 20T 0:00.00 0:00.00 auditd -l - 124 0.0 S 20T 0:00.04 0:00.00 -_locationd 126 ?? 0.0 S 4T 0:00.62 0:00.12 /usr/bin/some_command with some parameters - 126 0.0 S 4T 0:00.04 0:00.05 - 126 0.0 S 4T 0:00.01 0:00.00 - 126 0.0 S 4T 0:00.00 0:00.00 -root 128 ?? 0.0 S 20T 0:00.00 0:00.00 autofsd - 128 0.0 S 20T 0:00.00 0:00.00 -_displaypolicyd 129 ?? 0.0 S 20T 0:00.43 0:00.05 /usr/bin/some_command with some parameters - 129 0.0 S 20T 0:00.00 0:00.00 - 129 0.0 S 20T 0:00.16 0:00.08 - 129 0.0 S 20T 0:00.93 0:00.02 - 129 0.0 S 20T 0:00.05 0:00.00 - 129 0.0 S 20T 0:00.01 0:00.00 -root 132 ?? 0.0 S 4T 0:00.05 0:00.06 /usr/bin/some_command with some parameters - 132 0.0 S 4T 0:00.00 0:00.00 - 132 0.0 S 4T 0:00.00 0:00.00 -_distnote 135 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 135 0.0 S 31T 0:00.76 0:01.09 -root 139 ?? 0.0 S 20T 0:00.06 0:00.02 /usr/bin/some_command with some parameters - 139 0.0 S 20T 1:27.75 0:55.96 - 139 0.0 S 20T 0:04.08 0:00.52 -root 140 ?? 0.0 S 31T 0:00.05 0:00.00 /usr/bin/some_command with some parameters - 140 0.0 S 31T 0:00.00 0:00.00 -root 141 ?? 0.0 S 4T 0:00.07 0:00.03 /usr/bin/some_command with some parameters - 141 0.0 S 4T 0:00.00 0:00.00 - 141 0.0 S 4T 0:00.00 0:00.00 -root 142 ?? 0.0 S 31T 0:00.02 0:00.00 /usr/bin/some_command with some parameters - 142 0.0 S 31T 0:00.01 0:00.00 - 142 0.0 S 31T 0:00.00 0:00.00 -root 144 ?? 0.0 S 31T 0:19.66 0:16.97 /usr/bin/some_command with some parameters - 144 0.0 S 31T 0:11.46 0:04.77 - 144 0.0 S 37T 0:00.03 0:00.01 -root 145 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 145 0.0 S 37T 0:00.08 0:00.04 -root 147 ?? 0.0 S 31T 0:00.30 0:00.25 /usr/bin/some_command with some parameters - 147 0.0 S 31T 0:00.00 0:00.00 -root 148 ?? 0.0 S 55R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 148 0.0 S 19T 0:00.00 0:00.00 - 148 0.0 S 31T 0:00.00 0:00.00 - 148 0.0 S 31T 0:00.01 0:00.00 -root 151 ?? 0.0 S 31T 0:00.19 0:00.27 /usr/bin/some_command with some parameters - 151 0.0 S 31T 0:00.43 0:00.77 - 151 0.0 S 31T 0:00.01 0:00.00 -root 152 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 152 0.0 S 4T 0:00.00 0:00.00 - 152 0.0 S 4T 0:00.00 0:00.00 -root 153 ?? 0.0 S 31T 0:04.59 0:02.48 /usr/bin/some_command with some parameters - 153 0.0 S 31T 0:00.16 0:00.16 - 153 0.0 S 31T 0:00.07 0:00.01 - 153 0.0 S 31T 0:00.01 0:00.00 -_analyticsd 156 ?? 0.0 S 31T 0:00.23 0:00.16 /usr/bin/some_command with some parameters - 156 0.0 S 31T 0:00.01 0:00.00 -root 191 ?? 0.0 S 4T 0:00.05 0:00.04 /usr/bin/some_command with some parameters - 191 0.0 S 4T 0:00.00 0:00.01 - 191 0.0 S 4T 0:00.00 0:00.00 - 191 0.0 S 4T 0:00.00 0:00.00 - 191 0.0 S 4T 0:00.00 0:00.00 -root 195 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 195 0.0 S 4T 0:00.27 0:00.03 - 195 0.0 S 4T 0:00.03 0:00.06 -root 199 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters - 199 0.0 S 31T 0:00.51 0:00.29 -root 206 ?? 0.0 S 4T 0:01.29 0:01.82 /usr/bin/some_command with some parameters - 206 0.0 S 4T 0:00.00 0:00.00 -_trustd 208 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 208 0.0 S 4T 0:00.00 0:00.00 -_networkd 215 ?? 0.0 S 4T 0:01.52 0:00.19 /usr/bin/some_command with some parameters - 215 0.0 S 4T 0:00.01 0:00.00 - 215 0.0 S 4T 0:00.00 0:00.00 -_mdnsresponder 232 ?? 0.0 S 31T 0:02.58 0:03.05 /usr/bin/some_command with some parameters - 232 0.0 S 31T 1:14.24 0:37.44 - 232 0.0 S 37T 0:00.00 0:00.00 -root 248 ?? 0.0 S 31T 0:05.23 0:03.32 /usr/bin/some_command with some parameters - 248 0.0 S 31T 0:00.00 0:00.00 - 248 0.0 S 37T 0:00.00 0:00.00 -root 250 ?? 0.0 S 4T 0:00.14 0:00.05 /usr/bin/some_command with some parameters - 250 0.0 S 4T 0:00.00 0:00.00 -root 252 ?? 0.0 S 31T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 252 0.0 S 31T 0:00.00 0:00.00 -root 254 ?? 0.0 S 20T 0:00.49 0:00.12 /usr/bin/some_command with some parameters - 254 0.0 S 20T 0:00.00 0:00.00 -root 255 ?? 0.0 S 31T 0:25.65 0:13.33 /usr/bin/some_command with some parameters - 255 0.0 S 31T 0:00.25 0:00.03 - 255 0.0 S 31T 0:20.97 0:06.65 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:02.38 0:01.51 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:00.00 0:00.00 -_coreaudiod 256 ?? 0.0 S 63T 0:00.26 0:00.11 /usr/bin/some_command with some parameters - 256 0.0 S 19T 0:00.00 0:00.00 - 256 0.0 S 31T 0:00.00 0:00.00 - 256 0.0 S 31T 0:00.95 0:00.05 - 256 0.3 S 97R 0:02.08 0:05.95 - 256 0.3 S 97R 0:02.04 0:05.63 - 256 0.3 S 61T 0:00.70 0:00.25 - 256 0.0 S 31T 0:00.00 0:00.00 - 256 0.8 S 61R 0:00.40 0:00.27 -_nsurlsessiond 257 ?? 0.0 S 4T 0:00.38 0:00.15 /usr/bin/some_command with some parameters - 257 0.0 S 4T 0:00.00 0:00.00 -root 263 ?? 0.0 S 4T 0:00.69 0:00.11 /usr/bin/some_command with some parameters - 263 0.0 S 4T 0:00.01 0:00.00 - 263 0.0 S 4T 0:00.00 0:00.00 -_cmiodalassistants 264 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 264 1.1 S 97R 2:00.26 4:45.36 - 264 0.1 S 31T 0:01.94 0:01.28 - 264 0.0 S 31T 0:00.00 0:00.00 -root 269 ?? 0.0 S 20T 1:55.12 1:38.27 /usr/bin/some_command with some parameters - 269 0.0 S 20T 0:00.94 0:00.31 - 269 0.0 S 20T 0:00.00 0:00.00 -_coreaudiod 271 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters - 271 0.0 S 31T 0:00.52 0:00.21 -root 272 ?? 0.0 S 4T 0:00.06 0:00.01 /usr/bin/some_command with some parameters - 272 0.0 S 4T 0:00.00 0:00.00 -_locationd 279 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters - 279 0.0 S 31T 0:00.56 0:00.26 -root 300 ?? 0.0 S 4T 0:00.05 0:00.01 /usr/bin/some_command with some parameters - 300 0.0 S 4T 0:00.07 0:00.01 -_softwareupdate 307 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters - 307 0.0 S 31T 0:00.54 0:00.21 -root 313 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 313 0.0 S 31T 0:00.00 0:00.00 -root 322 ?? 0.0 S 20T 0:00.03 0:00.03 /usr/bin/some_command with some parameters - 322 0.0 S 20T 0:00.00 0:00.00 - 322 0.0 S 20T 0:00.00 0:00.00 -root 337 ?? 0.0 S 4T 0:00.08 0:00.03 /usr/bin/some_command with some parameters - 337 0.0 S 4T 0:00.00 0:00.00 -root 397 ?? 0.0 S 31T 0:14.02 0:12.57 /usr/bin/some_command with some parameters - 397 0.0 S 31T 0:00.00 0:00.00 - 397 0.0 S 31T 0:00.00 0:00.00 - 397 0.0 S 31T 1:32.16 7:28.27 - 397 0.0 R 31T 1:32.29 7:28.11 - 397 0.0 S 31T 1:33.32 7:28.98 - 397 0.0 S 31T 1:32.30 7:28.34 - 397 0.0 S 31T 1:32.30 7:28.44 - 397 0.0 S 31T 1:32.33 7:27.11 - 397 0.0 S 31T 0:10.06 0:22.50 - 397 0.0 S 31T 0:00.25 0:01.70 - 397 0.0 S 31T 2:20.84 5:59.52 - 397 0.0 S 31T 0:00.29 0:00.12 - 397 0.0 S 31T 5:05.54 5:08.26 - 397 0.0 S 4T 0:00.16 0:00.34 - 397 0.0 S 31T 0:00.04 0:00.02 - 397 0.0 S 31T 0:43.11 1:10.88 - 397 0.0 S 31T 0:00.72 0:00.56 - 397 0.0 S 31T 0:13.98 0:26.76 - 397 0.0 S 31T 0:01.40 0:01.34 - 397 0.0 S 31T 0:02.07 0:01.84 - 397 0.0 S 31T 0:00.59 0:00.76 - 397 0.0 S 31T 0:00.37 0:00.67 - 397 0.0 S 31T 0:00.15 0:00.13 - 397 0.0 S 31T 0:00.02 0:00.02 - 397 0.0 R 54R 0:00.01 0:00.02 - 397 0.0 S 31T 0:00.03 0:00.01 -_nsurlsessiond 398 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 398 0.0 S 31T 0:00.53 0:00.21 -root 419 ?? 0.0 S 31T 0:00.71 0:00.84 /usr/bin/some_command with some parameters - 419 0.0 S 31T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.01 0:00.00 - 419 0.0 S 31T 0:00.01 0:00.01 - 419 0.0 S 37T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.01 - 419 0.0 S 31T 0:00.00 0:00.00 -_driverkit 422 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 422 0.0 S 63R 0:00.09 0:00.03 -_driverkit 423 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 423 0.0 S 63R 0:00.00 0:00.00 -_driverkit 425 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 425 0.0 S 31T 0:00.00 0:00.00 -_driverkit 427 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 427 0.0 S 63R 0:03.41 0:01.46 -_driverkit 428 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 428 0.0 S 31T 0:00.00 0:00.00 -_driverkit 430 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 430 0.0 S 63R 0:00.00 0:00.00 -_driverkit 432 ?? 0.0 S 63R 0:00.94 0:00.30 /usr/bin/some_command with some parameters - 432 0.0 S 31T 0:00.00 0:00.00 -_driverkit 434 ?? 0.0 S 63R 0:00.04 0:00.02 /usr/bin/some_command with some parameters - 434 0.0 S 31T 0:00.00 0:00.00 -_driverkit 435 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 435 0.0 S 31T 0:00.00 0:00.00 -_spotlight 437 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 437 0.0 S 31T 0:00.13 0:00.08 -root 460 ?? 0.0 S 63R 0:00.88 0:00.12 /usr/bin/some_command with some parameters - 460 0.0 S 31T 0:00.00 0:00.00 -_windowserver 474 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 474 0.0 S 31T 0:00.52 0:00.20 -_appinstalld 481 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 481 0.0 S 31T 0:00.50 0:00.19 -root 492 ?? 0.0 S 51R 0:17.61 0:22.47 /usr/bin/some_command with some parameters - 492 0.1 S 55R 4:03.41 2:28.94 - 492 0.0 S 37T 0:03.98 0:03.88 - 492 0.0 S 51T 0:00.00 0:00.00 - 492 0.0 S 37T 0:00.00 0:00.00 - 492 0.0 S 31T 0:00.00 0:00.00 - 492 0.0 S 51R 0:00.00 0:00.00 -_appleevents 501 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 501 0.0 S 31T 0:00.00 0:00.00 -root 503 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 503 0.0 S 4T 0:00.00 0:00.00 -root 508 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 508 23.4 S 4T 0:06.35 0:01.50 - 508 0.0 S 4T 0:00.60 0:00.14 -root 515 ?? 0.0 S 4T 0:00.12 0:00.03 /usr/bin/some_command with some parameters - 515 0.0 S 4T 0:00.00 0:00.00 - 515 0.0 S 4T 0:00.00 0:00.00 -root 528 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 528 0.0 S 4T 0:00.00 0:00.00 -_appleevents 541 ?? 0.0 S 31T 0:00.01 0:00.02 /usr/bin/some_command with some parameters - 541 0.0 S 31T 0:00.50 0:00.19 -root 555 ?? 0.0 S 4T 0:00.04 0:00.01 /usr/bin/some_command with some parameters - 555 0.0 S 4T 0:00.00 0:00.00 -someuser 558 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 558 0.0 S 20T 0:00.86 0:01.75 -root 583 ?? 0.0 S 4T 0:00.06 0:00.01 /usr/bin/some_command with some parameters - 583 0.0 S 4T 0:00.00 0:00.00 -root 631 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 631 0.0 S 20T 0:00.01 0:00.00 -someuser 638 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 638 0.0 S 4T 0:00.01 0:00.00 - 638 0.0 S 4T 0:00.00 0:00.00 -someuser 673 ?? 0.0 S 47T 0:10.83 0:08.96 /usr/bin/some_command with some parameters - 673 0.0 S 19T 0:00.00 0:00.00 - 673 0.0 S 31T 0:00.00 0:00.00 - 673 0.0 S 37T 0:00.00 0:00.00 -_windowserver 677 ?? 26.8 S 79R 100:32.78 206:51.42 /usr/bin/some_command with some parameters - 677 2.8 S 79R 42:18.54 27:27.26 - 677 0.0 S 37T 0:00.00 0:00.00 - 677 0.0 S 79R 0:00.00 0:00.00 - 677 0.0 S 31T 0:00.01 0:00.00 - 677 0.0 S 31T 0:00.16 0:00.20 - 677 0.0 S 31T 0:28.55 0:11.03 - 677 1.7 U 31T 0:23.73 0:19.24 - 677 1.9 S 31T 0:14.59 0:11.88 - 677 0.0 S 79R 0:13.48 0:11.02 - 677 0.6 S 79R 0:01.43 0:00.84 - 677 0.2 S 79T 0:04.30 0:03.51 - 677 21.0 S 79T 0:04.35 0:03.53 - 677 1.2 S 79R 0:00.53 0:00.29 - 677 0.0 S 31T 0:00.00 0:00.00 - 677 1.2 S 79R 0:00.09 0:00.05 -_securityagent 735 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 735 0.0 S 31T 0:00.47 0:00.18 -root 762 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 762 0.0 S 4T 0:00.01 0:00.00 -root 860 ?? 0.0 S 31T 0:01.12 0:01.52 /usr/bin/some_command with some parameters - 860 0.0 S 31T 0:00.58 0:00.39 - 860 0.0 S 31T 0:00.00 0:00.01 -someuser 978 ?? 0.0 S 31T 0:03.65 0:02.88 /usr/bin/some_command with some parameters - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 0T 0:00.02 0:00.02 - 978 0.0 S 31T 0:00.13 0:00.06 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.10 0:00.02 - 978 0.0 S 31T 0:00.02 0:00.00 - 978 0.0 S 31T 0:00.01 0:00.00 -someuser 2054 ?? 0.0 S 4T 0:01.04 0:01.49 /usr/bin/some_command with some parameters - 2054 0.0 S 4T 0:00.01 0:00.00 - 2054 0.0 S 4T 0:00.00 0:00.00 -` - -var psOutThreads500 = `USER PID TT %CPU STAT PRI STIME UTIME COMMAND -root 1 ?? 0.0 S 31T 0:00.39 0:00.09 /usr/bin/some_command with some parameters - 1 0.0 S 20T 0:00.15 0:00.04 - 1 0.6 S 37T 0:00.01 0:00.00 -root 68 ?? 0.0 S 4T 0:01.24 0:00.33 /usr/bin/some_command with some parameters - 68 0.0 S 4T 0:00.00 0:00.00 - 68 0.0 S 4T 0:00.00 0:00.00 - 68 0.0 S 4T 0:00.00 0:00.00 -root 69 ?? 0.0 S 31T 0:00.20 0:00.08 /usr/bin/some_command with some parameters - 69 0.0 S 31T 0:00.00 0:00.00 - 69 0.0 S 31T 0:00.00 0:00.00 - 69 0.0 S 4T 0:00.01 0:00.00 -root 72 ?? 0.0 S 20T 0:00.98 0:00.94 /usr/bin/some_command with some parameters - 72 0.0 S 20T 0:00.00 0:00.00 -root 73 ?? 0.0 S 49T 0:27.84 1:22.66 /usr/bin/some_command with some parameters - 73 0.0 S 31T 0:00.00 0:00.00 - 73 0.0 S 31T 0:00.02 0:00.01 - 73 0.0 S 50R 0:16.31 0:03.31 - 73 0.0 S 31T 0:01.93 0:07.66 - 73 0.0 S 31T 0:13.32 0:04.10 - 73 0.0 S 31T 0:57.43 0:05.99 - 73 0.0 S 31T 0:13.38 0:01.60 - 73 0.0 S 31T 0:09.00 0:00.96 - 73 0.0 S 31T 0:02.29 0:00.64 - 73 0.0 S 31T 0:08.64 0:01.02 - 73 0.0 S 31T 0:00.01 0:00.00 - 73 0.0 S 31T 0:00.00 0:00.00 - 73 0.0 S 31T 0:00.00 0:00.00 -root 74 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters - 74 0.0 S 20T 0:00.04 0:00.01 -root 75 ?? 0.0 S 4T 0:04.35 0:07.60 /usr/bin/some_command with some parameters - 75 0.0 S 4T 0:00.00 0:00.00 - 75 0.0 S 4T 0:00.00 0:00.00 -root 81 ?? 0.0 S 20T 0:00.05 0:00.02 /usr/bin/some_command with some parameters - 81 0.0 S 20T 0:00.05 0:00.01 -root 82 ?? 0.0 S 4T 0:06.92 0:05.91 /usr/bin/some_command with some parameters - 82 0.0 S 4T 0:00.00 0:00.00 - 82 0.0 S 4T 0:00.00 0:00.00 -root 84 ?? 0.0 S 31T 0:04.14 0:02.10 /usr/bin/some_command with some parameters - 84 0.0 S 31T 0:00.12 0:00.03 - 84 0.0 S 31T 0:00.25 0:00.13 - 84 0.0 S 31T 0:00.65 0:00.28 - 84 0.0 S 31T 0:00.21 0:00.10 - 84 0.0 S 31T 0:00.33 0:00.18 - 84 0.0 S 31T 0:00.37 0:00.16 - 84 0.0 S 31T 0:00.01 0:00.00 -root 86 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 86 0.0 S 37T 0:00.01 0:00.01 - 86 0.0 S 37T 0:00.00 0:00.00 -root 88 ?? 0.0 S 20T 0:10.90 0:10.20 /usr/bin/some_command with some parameters - 88 0.0 S 20T 24:38.78 20:49.45 - 88 0.0 S 20T 0:00.00 0:00.00 - 88 0.0 S 20T 0:00.00 0:00.00 -root 91 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 91 0.0 S 31T 0:00.00 0:00.00 - 91 0.0 S 31T 0:00.00 0:00.00 - 91 0.0 S 31T 0:00.00 0:00.00 -root 93 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 93 0.0 S 20T 0:00.01 0:00.01 - 93 0.0 S 20T 0:00.01 0:00.00 - 93 0.0 S 20T 0:00.00 0:00.00 -root 99 ?? 0.0 S 31T 0:00.04 0:00.01 /usr/bin/some_command with some parameters - 99 0.0 S 97R 0:00.16 0:00.06 - 99 0.0 S 31T 0:00.04 0:00.06 -root 103 ?? 0.0 S 31T 0:00.15 0:00.09 /usr/bin/some_command with some parameters - 103 0.0 S 50R 0:24.25 0:09.17 - 103 0.0 S 31T 0:00.00 0:00.00 - 103 0.0 S 4T 0:00.03 0:00.02 - 103 0.0 S 37T 0:00.00 0:00.00 - 103 0.0 S 20T 0:00.01 0:00.00 - 103 0.0 S 31T 0:00.00 0:00.00 - 103 0.0 S 37T 0:00.00 0:00.00 - 103 0.0 S 4T 0:00.00 0:00.00 -root 104 ?? 0.0 S 4T 0:01.01 0:09.28 /usr/bin/some_command with some parameters - 104 0.0 S 4T 2:14.81 5:02.47 - 104 0.0 S 4T 0:00.44 0:01.08 - 104 0.0 S 4T 0:12.12 2:53.73 - 104 0.0 S 4T 0:00.02 0:00.01 - 104 0.0 S 4T 0:15.99 0:06.45 - 104 0.0 S 4T 1:28.59 2:46.98 - 104 0.0 S 4T 0:04.30 0:12.81 - 104 0.0 S 4T 0:08.71 0:17.49 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:59.96 6:08.35 - 104 0.0 S 4T 0:23.11 0:36.48 - 104 0.0 S 4T 1:34.48 2:09.53 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:44.10 0:19.85 - 104 0.0 S 4T 0:00.52 0:00.17 - 104 0.0 S 4T 2:14.01 5:01.32 - 104 0.0 S 4T 2:13.19 5:01.40 - 104 0.0 S 4T 2:13.78 5:03.91 - 104 0.0 S 4T 2:15.10 5:04.41 - 104 0.0 S 4T 2:12.32 5:03.10 - 104 0.0 S 4T 0:12.37 2:53.81 - 104 0.0 S 4T 0:12.10 2:53.81 - 104 0.0 S 4T 2:14.30 5:02.53 - 104 0.0 S 4T 2:10.42 5:00.82 - 104 0.0 S 4T 2:14.03 5:02.43 - 104 0.0 S 4T 2:11.48 5:03.86 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:08.54 0:06.59 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:05.34 0:01.55 - 104 0.0 S 4T 0:03.66 0:01.06 - 104 0.0 S 4T 0:00.18 0:00.03 - 104 0.0 S 4T 0:37.36 0:06.80 - 104 0.0 S 4T 0:01.95 0:01.44 - 104 0.0 S 4T 0:37.35 0:40.82 - 104 0.0 S 4T 0:12.91 0:09.45 - 104 0.0 S 4T 0:02.40 0:00.68 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:08.22 0:07.64 - 104 0.0 S 4T 0:00.17 0:00.02 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.10 0:00.03 - 104 0.0 S 4T 0:01.59 0:01.74 - 104 0.0 S 4T 0:01.66 0:00.43 - 104 0.0 S 4T 0:00.98 0:00.21 - 104 0.0 S 4T 0:01.56 0:00.92 - 104 0.0 S 4T 0:00.02 0:00.00 - 104 0.0 S 4T 0:00.38 0:00.14 - 104 0.0 S 4T 0:00.92 0:00.25 - 104 0.0 S 4T 0:17.54 0:09.81 - 104 0.0 S 4T 0:05.13 0:01.18 - 104 0.0 S 4T 0:11.10 1:14.80 - 104 0.0 S 4T 2:27.08 11:01.59 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.76 0:00.28 - 104 0.3 S 4T 21:15.35 38:57.00 - 104 0.0 S 4T 0:01.84 0:00.31 - 104 0.0 S 4T 0:17.74 0:28.64 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:15.35 0:45.24 - 104 0.0 S 4T 0:00.70 0:00.30 - 104 0.0 S 4T 0:00.62 0:00.27 - 104 0.0 S 4T 0:00.64 0:00.31 - 104 0.0 S 4T 0:00.55 0:00.28 - 104 0.0 S 4T 0:02.64 0:04.64 - 104 0.0 S 4T 0:03.42 0:01.28 - 104 0.0 S 4T 0:05.43 0:02.17 - 104 0.0 S 4T 0:00.18 0:00.05 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:00.18 0:00.05 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:33.60 0:37.38 - 104 0.0 S 4T 0:24.14 0:08.43 - 104 0.0 S 4T 2:35.92 4:31.62 - 104 0.0 S 4T 0:04.54 0:03.01 - 104 0.0 S 4T 1:29.91 1:00.41 - 104 0.0 S 4T 0:04.88 0:01.94 - 104 0.0 S 4T 0:07.19 0:05.44 - 104 0.0 S 4T 0:02.68 0:00.83 - 104 0.0 S 4T 0:56.96 1:49.87 - 104 0.0 S 4T 0:12.03 0:07.99 - 104 0.0 S 4T 0:15.67 0:12.93 - 104 0.0 S 4T 0:25.82 1:14.97 - 104 0.0 S 4T 0:01.66 0:02.08 - 104 0.0 S 4T 0:08.01 0:40.99 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:02.42 0:02.15 - 104 0.0 S 4T 0:00.00 0:00.00 - 104 0.0 S 4T 0:01.54 0:01.59 - 104 0.0 S 4T 0:01.13 0:00.29 - 104 0.0 S 4T 0:02.33 0:00.85 - 104 0.0 S 4T 0:20.38 0:20.06 - 104 0.0 S 4T 1:52.14 25:52.43 - 104 0.0 S 4T 2:07.78 2:26.81 - 104 0.0 S 4T 4:04.54 4:24.85 - 104 0.0 S 4T 0:00.01 0:00.00 - 104 0.0 S 4T 0:00.88 0:02.39 - 104 0.0 S 4T 0:00.98 0:02.32 - 104 0.0 S 4T 0:00.83 0:02.10 - 104 0.3 S 4T 0:00.87 0:02.00 - 104 0.0 S 4T 0:00.62 0:01.89 - 104 0.0 S 4T 0:00.24 0:00.51 - 104 0.0 S 4T 0:00.85 0:04.37 - 104 0.0 S 4T 0:00.01 0:00.01 - 104 0.1 S 4T 0:00.11 0:00.17 - 104 0.0 S 4T 0:00.07 0:00.11 - 104 0.0 S 4T 0:00.02 0:00.03 - 104 0.0 S 4T 0:00.01 0:00.01 - 104 0.0 S 4T 0:00.00 0:00.00 -root 106 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 106 0.0 S 31T 0:00.00 0:00.00 -root 107 ?? 0.0 S 31T 0:24.80 0:41.84 /usr/bin/some_command with some parameters - 107 0.0 S 37T 0:00.00 0:00.00 -root 114 ?? 0.0 S 37T 0:00.03 0:00.00 /usr/bin/some_command with some parameters -root 115 ?? 0.0 S 4T 0:00.78 0:00.70 /usr/bin/some_command with some parameters - 115 0.0 S 4T 0:00.00 0:00.01 - 115 0.0 S 4T 0:00.00 0:00.00 - 115 0.0 S 4T 0:00.00 0:00.00 - 115 0.0 S 4T 0:00.01 0:00.02 -root 116 ?? 0.0 S 20T 0:00.42 0:00.96 /usr/bin/some_command with some parameters - 116 0.0 S 31T 0:00.00 0:00.00 - 116 0.0 S 20T 0:00.00 0:00.00 - 116 0.0 S 31T 0:00.20 0:00.07 - 116 0.0 S 31T 0:00.00 0:00.00 - 116 0.0 S 31T 0:00.00 0:00.00 - 116 0.0 S 31T 0:00.00 0:00.00 -root 117 ?? 0.0 S 31T 0:06.27 0:08.00 /usr/bin/some_command with some parameters - 117 0.0 S 31T 0:00.00 0:00.00 - 117 0.0 S 31T 0:00.01 0:00.00 -root 118 ?? 0.0 S 20T 0:00.02 0:00.01 /usr/bin/some_command with some parameters - 118 0.0 S 20T 0:00.22 0:00.11 - 118 0.0 S 20T 0:00.00 0:00.00 - 118 0.0 S 20T 0:00.00 0:00.00 - 118 0.0 S 20T 0:00.00 0:00.00 - 118 0.0 S 20T 0:00.00 0:00.00 -root 119 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 119 0.0 S 4T 0:00.00 0:00.00 - 119 0.0 S 4T 0:00.01 0:00.01 -_timed 120 ?? 0.0 S 31T 0:03.13 0:00.61 /usr/bin/some_command with some parameters - 120 0.0 S 4T 0:00.02 0:00.00 -root 123 ?? 0.0 S 31T 0:01.98 0:06.46 /usr/bin/some_command with some parameters - 123 0.0 S 31T 0:00.17 0:00.03 - 123 0.0 S 31T 0:00.00 0:00.00 -root 124 ?? 0.0 S 20T 0:00.00 0:00.00 auditd -l - 124 0.0 S 20T 0:00.04 0:00.00 -_locationd 126 ?? 0.0 S 4T 0:00.62 0:00.12 /usr/bin/some_command with some parameters - 126 0.0 S 4T 0:00.04 0:00.05 - 126 0.0 S 4T 0:00.01 0:00.00 - 126 0.0 S 4T 0:00.00 0:00.00 -root 128 ?? 0.0 S 20T 0:00.00 0:00.00 autofsd - 128 0.0 S 20T 0:00.00 0:00.00 -_displaypolicyd 129 ?? 0.0 S 20T 0:00.43 0:00.05 /usr/bin/some_command with some parameters - 129 0.0 S 20T 0:00.00 0:00.00 - 129 0.0 S 20T 0:00.16 0:00.08 - 129 0.0 S 20T 0:00.93 0:00.02 - 129 0.0 S 20T 0:00.05 0:00.00 - 129 0.0 S 20T 0:00.01 0:00.00 -root 132 ?? 0.0 S 4T 0:00.05 0:00.06 /usr/bin/some_command with some parameters - 132 0.0 S 4T 0:00.00 0:00.00 - 132 0.0 S 4T 0:00.00 0:00.00 -_distnote 135 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 135 0.0 S 31T 0:00.76 0:01.09 -root 139 ?? 0.0 S 20T 0:00.06 0:00.02 /usr/bin/some_command with some parameters - 139 0.0 S 20T 1:27.75 0:55.96 - 139 0.0 S 20T 0:04.08 0:00.52 -root 140 ?? 0.0 S 31T 0:00.05 0:00.00 /usr/bin/some_command with some parameters - 140 0.0 S 31T 0:00.00 0:00.00 -root 141 ?? 0.0 S 4T 0:00.07 0:00.03 /usr/bin/some_command with some parameters - 141 0.0 S 4T 0:00.00 0:00.00 - 141 0.0 S 4T 0:00.00 0:00.00 -root 142 ?? 0.0 S 31T 0:00.02 0:00.00 /usr/bin/some_command with some parameters - 142 0.0 S 31T 0:00.01 0:00.00 - 142 0.0 S 31T 0:00.00 0:00.00 -root 144 ?? 0.0 S 31T 0:19.66 0:16.97 /usr/bin/some_command with some parameters - 144 0.0 S 31T 0:11.46 0:04.77 - 144 0.0 S 37T 0:00.03 0:00.01 -root 145 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 145 0.0 S 37T 0:00.08 0:00.04 -root 147 ?? 0.0 S 31T 0:00.30 0:00.25 /usr/bin/some_command with some parameters - 147 0.0 S 31T 0:00.00 0:00.00 -root 148 ?? 0.0 S 55R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 148 0.0 S 19T 0:00.00 0:00.00 - 148 0.0 S 31T 0:00.00 0:00.00 - 148 0.0 S 31T 0:00.01 0:00.00 -root 151 ?? 0.0 S 31T 0:00.19 0:00.27 /usr/bin/some_command with some parameters - 151 0.0 S 31T 0:00.43 0:00.77 - 151 0.0 S 31T 0:00.01 0:00.00 -root 152 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 152 0.0 S 4T 0:00.00 0:00.00 - 152 0.0 S 4T 0:00.00 0:00.00 -root 153 ?? 0.0 S 31T 0:04.59 0:02.48 /usr/bin/some_command with some parameters - 153 0.0 S 31T 0:00.16 0:00.16 - 153 0.0 S 31T 0:00.07 0:00.01 - 153 0.0 S 31T 0:00.01 0:00.00 -_analyticsd 156 ?? 0.0 S 31T 0:00.23 0:00.16 /usr/bin/some_command with some parameters - 156 0.0 S 31T 0:00.01 0:00.00 -root 191 ?? 0.0 S 4T 0:00.05 0:00.04 /usr/bin/some_command with some parameters - 191 0.0 S 4T 0:00.00 0:00.01 - 191 0.0 S 4T 0:00.00 0:00.00 - 191 0.0 S 4T 0:00.00 0:00.00 - 191 0.0 S 4T 0:00.00 0:00.00 -root 195 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 195 0.0 S 4T 0:00.27 0:00.03 - 195 0.0 S 4T 0:00.03 0:00.06 -root 199 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters - 199 0.0 S 31T 0:00.51 0:00.29 -root 206 ?? 0.0 S 4T 0:01.29 0:01.82 /usr/bin/some_command with some parameters - 206 0.0 S 4T 0:00.00 0:00.00 -_trustd 208 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 208 0.0 S 4T 0:00.00 0:00.00 -_networkd 215 ?? 0.0 S 4T 0:01.52 0:00.19 /usr/bin/some_command with some parameters - 215 0.0 S 4T 0:00.01 0:00.00 - 215 0.0 S 4T 0:00.00 0:00.00 -_mdnsresponder 232 ?? 0.0 S 31T 0:02.58 0:03.05 /usr/bin/some_command with some parameters - 232 0.0 S 31T 1:14.24 0:37.44 - 232 0.0 S 37T 0:00.00 0:00.00 -root 248 ?? 0.0 S 31T 0:05.23 0:03.32 /usr/bin/some_command with some parameters - 248 0.0 S 31T 0:00.00 0:00.00 - 248 0.0 S 37T 0:00.00 0:00.00 -root 250 ?? 0.0 S 4T 0:00.14 0:00.05 /usr/bin/some_command with some parameters - 250 0.0 S 4T 0:00.00 0:00.00 -root 252 ?? 0.0 S 31T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 252 0.0 S 31T 0:00.00 0:00.00 -root 254 ?? 0.0 S 20T 0:00.49 0:00.12 /usr/bin/some_command with some parameters - 254 0.0 S 20T 0:00.00 0:00.00 -root 255 ?? 0.0 S 31T 0:25.65 0:13.33 /usr/bin/some_command with some parameters - 255 0.0 S 31T 0:00.25 0:00.03 - 255 0.0 S 31T 0:20.97 0:06.65 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:02.38 0:01.51 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:00.00 0:00.00 - 255 0.0 S 31T 0:00.00 0:00.00 -_coreaudiod 256 ?? 0.0 S 63T 0:00.26 0:00.11 /usr/bin/some_command with some parameters - 256 0.0 S 19T 0:00.00 0:00.00 - 256 0.0 S 31T 0:00.00 0:00.00 - 256 0.0 S 31T 0:00.95 0:00.05 - 256 0.3 S 97R 0:02.08 0:05.95 - 256 0.3 S 97R 0:02.04 0:05.63 - 256 0.3 S 61T 0:00.70 0:00.25 - 256 0.0 S 31T 0:00.00 0:00.00 - 256 0.8 S 61R 0:00.40 0:00.27 -_nsurlsessiond 257 ?? 0.0 S 4T 0:00.38 0:00.15 /usr/bin/some_command with some parameters - 257 0.0 S 4T 0:00.00 0:00.00 -root 263 ?? 0.0 S 4T 0:00.69 0:00.11 /usr/bin/some_command with some parameters - 263 0.0 S 4T 0:00.01 0:00.00 - 263 0.0 S 4T 0:00.00 0:00.00 -_cmiodalassistants 264 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 264 1.1 S 97R 2:00.26 4:45.36 - 264 0.1 S 31T 0:01.94 0:01.28 - 264 0.0 S 31T 0:00.00 0:00.00 -root 269 ?? 0.0 S 20T 1:55.12 1:38.27 /usr/bin/some_command with some parameters - 269 0.0 S 20T 0:00.94 0:00.31 - 269 0.0 S 20T 0:00.00 0:00.00 -_coreaudiod 271 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters - 271 0.0 S 31T 0:00.52 0:00.21 -root 272 ?? 0.0 S 4T 0:00.06 0:00.01 /usr/bin/some_command with some parameters - 272 0.0 S 4T 0:00.00 0:00.00 -_locationd 279 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters - 279 0.0 S 31T 0:00.56 0:00.26 -root 300 ?? 0.0 S 4T 0:00.05 0:00.01 /usr/bin/some_command with some parameters - 300 0.0 S 4T 0:00.07 0:00.01 -_softwareupdate 307 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters - 307 0.0 S 31T 0:00.54 0:00.21 -root 313 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 313 0.0 S 31T 0:00.00 0:00.00 -root 322 ?? 0.0 S 20T 0:00.03 0:00.03 /usr/bin/some_command with some parameters - 322 0.0 S 20T 0:00.00 0:00.00 - 322 0.0 S 20T 0:00.00 0:00.00 -root 337 ?? 0.0 S 4T 0:00.08 0:00.03 /usr/bin/some_command with some parameters - 337 0.0 S 4T 0:00.00 0:00.00 -root 397 ?? 0.0 S 31T 0:14.02 0:12.57 /usr/bin/some_command with some parameters - 397 0.0 S 31T 0:00.00 0:00.00 - 397 0.0 S 31T 0:00.00 0:00.00 - 397 0.0 S 31T 1:32.16 7:28.27 - 397 0.0 R 31T 1:32.29 7:28.11 - 397 0.0 S 31T 1:33.32 7:28.98 - 397 0.0 S 31T 1:32.30 7:28.34 - 397 0.0 S 31T 1:32.30 7:28.44 - 397 0.0 S 31T 1:32.33 7:27.11 - 397 0.0 S 31T 0:10.06 0:22.50 - 397 0.0 S 31T 0:00.25 0:01.70 - 397 0.0 S 31T 2:20.84 5:59.52 - 397 0.0 S 31T 0:00.29 0:00.12 - 397 0.0 S 31T 5:05.54 5:08.26 - 397 0.0 S 4T 0:00.16 0:00.34 - 397 0.0 S 31T 0:00.04 0:00.02 - 397 0.0 S 31T 0:43.11 1:10.88 - 397 0.0 S 31T 0:00.72 0:00.56 - 397 0.0 S 31T 0:13.98 0:26.76 - 397 0.0 S 31T 0:01.40 0:01.34 - 397 0.0 S 31T 0:02.07 0:01.84 - 397 0.0 S 31T 0:00.59 0:00.76 - 397 0.0 S 31T 0:00.37 0:00.67 - 397 0.0 S 31T 0:00.15 0:00.13 - 397 0.0 S 31T 0:00.02 0:00.02 - 397 0.0 R 54R 0:00.01 0:00.02 - 397 0.0 S 31T 0:00.03 0:00.01 -_nsurlsessiond 398 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 398 0.0 S 31T 0:00.53 0:00.21 -root 419 ?? 0.0 S 31T 0:00.71 0:00.84 /usr/bin/some_command with some parameters - 419 0.0 S 31T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.01 0:00.00 - 419 0.0 S 31T 0:00.01 0:00.01 - 419 0.0 S 37T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.00 - 419 0.0 S 4T 0:00.00 0:00.01 - 419 0.0 S 31T 0:00.00 0:00.00 -_driverkit 422 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 422 0.0 S 63R 0:00.09 0:00.03 -_driverkit 423 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 423 0.0 S 63R 0:00.00 0:00.00 -_driverkit 425 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 425 0.0 S 31T 0:00.00 0:00.00 -_driverkit 427 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 427 0.0 S 63R 0:03.41 0:01.46 -_driverkit 428 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 428 0.0 S 31T 0:00.00 0:00.00 -_driverkit 430 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 430 0.0 S 63R 0:00.00 0:00.00 -_driverkit 432 ?? 0.0 S 63R 0:00.94 0:00.30 /usr/bin/some_command with some parameters - 432 0.0 S 31T 0:00.00 0:00.00 -_driverkit 434 ?? 0.0 S 63R 0:00.04 0:00.02 /usr/bin/some_command with some parameters - 434 0.0 S 31T 0:00.00 0:00.00 -_driverkit 435 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 435 0.0 S 31T 0:00.00 0:00.00 -_spotlight 437 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 437 0.0 S 31T 0:00.13 0:00.08 -root 460 ?? 0.0 S 63R 0:00.88 0:00.12 /usr/bin/some_command with some parameters - 460 0.0 S 31T 0:00.00 0:00.00 -_windowserver 474 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 474 0.0 S 31T 0:00.52 0:00.20 -_appinstalld 481 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 481 0.0 S 31T 0:00.50 0:00.19 -root 492 ?? 0.0 S 51R 0:17.61 0:22.47 /usr/bin/some_command with some parameters - 492 0.1 S 55R 4:03.41 2:28.94 - 492 0.0 S 37T 0:03.98 0:03.88 - 492 0.0 S 51T 0:00.00 0:00.00 - 492 0.0 S 37T 0:00.00 0:00.00 - 492 0.0 S 31T 0:00.00 0:00.00 - 492 0.0 S 51R 0:00.00 0:00.00 -_appleevents 501 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 501 0.0 S 31T 0:00.00 0:00.00 -root 503 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 503 0.0 S 4T 0:00.00 0:00.00 -root 508 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 508 23.4 S 4T 0:06.35 0:01.50 - 508 0.0 S 4T 0:00.60 0:00.14 -root 515 ?? 0.0 S 4T 0:00.12 0:00.03 /usr/bin/some_command with some parameters - 515 0.0 S 4T 0:00.00 0:00.00 - 515 0.0 S 4T 0:00.00 0:00.00 -root 528 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 528 0.0 S 4T 0:00.00 0:00.00 -_appleevents 541 ?? 0.0 S 31T 0:00.01 0:00.02 /usr/bin/some_command with some parameters - 541 0.0 S 31T 0:00.50 0:00.19 -root 555 ?? 0.0 S 4T 0:00.04 0:00.01 /usr/bin/some_command with some parameters - 555 0.0 S 4T 0:00.00 0:00.00 -someuser 558 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 558 0.0 S 20T 0:00.86 0:01.75 -root 583 ?? 0.0 S 4T 0:00.06 0:00.01 /usr/bin/some_command with some parameters - 583 0.0 S 4T 0:00.00 0:00.00 -root 631 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 631 0.0 S 20T 0:00.01 0:00.00 -someuser 638 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 638 0.0 S 4T 0:00.01 0:00.00 - 638 0.0 S 4T 0:00.00 0:00.00 -someuser 673 ?? 0.0 S 47T 0:10.83 0:08.96 /usr/bin/some_command with some parameters - 673 0.0 S 19T 0:00.00 0:00.00 - 673 0.0 S 31T 0:00.00 0:00.00 - 673 0.0 S 37T 0:00.00 0:00.00 -_windowserver 677 ?? 26.8 S 79R 100:32.78 206:51.42 /usr/bin/some_command with some parameters - 677 2.8 S 79R 42:18.54 27:27.26 - 677 0.0 S 37T 0:00.00 0:00.00 - 677 0.0 S 79R 0:00.00 0:00.00 - 677 0.0 S 31T 0:00.01 0:00.00 - 677 0.0 S 31T 0:00.16 0:00.20 - 677 0.0 S 31T 0:28.55 0:11.03 - 677 1.7 U 31T 0:23.73 0:19.24 - 677 1.9 S 31T 0:14.59 0:11.88 - 677 0.0 S 79R 0:13.48 0:11.02 - 677 0.6 S 79R 0:01.43 0:00.84 - 677 0.2 S 79T 0:04.30 0:03.51 - 677 21.0 S 79T 0:04.35 0:03.53 - 677 1.2 S 79R 0:00.53 0:00.29 - 677 0.0 S 31T 0:00.00 0:00.00 - 677 1.2 S 79R 0:00.09 0:00.05 -_securityagent 735 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 735 0.0 S 31T 0:00.47 0:00.18 -root 762 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 762 0.0 S 4T 0:00.01 0:00.00 -root 860 ?? 0.0 S 31T 0:01.12 0:01.52 /usr/bin/some_command with some parameters - 860 0.0 S 31T 0:00.58 0:00.39 - 860 0.0 S 31T 0:00.00 0:00.01 -someuser 978 ?? 0.0 S 31T 0:03.65 0:02.88 /usr/bin/some_command with some parameters - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 0T 0:00.02 0:00.02 - 978 0.0 S 31T 0:00.13 0:00.06 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.00 0:00.00 - 978 0.0 S 31T 0:00.10 0:00.02 - 978 0.0 S 31T 0:00.02 0:00.00 - 978 0.0 S 31T 0:00.01 0:00.00 -someuser 2054 ?? 0.0 S 4T 0:01.04 0:01.49 /usr/bin/some_command with some parameters - 2054 0.0 S 4T 0:00.01 0:00.00 - 2054 0.0 S 4T 0:00.00 0:00.00 -someuser 2059 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2059 0.0 S 4T 0:00.01 0:00.01 - 2059 0.0 S 4T 0:00.00 0:00.00 -_appstore 2142 ?? 0.0 S 31T 0:00.01 0:00.02 /usr/bin/some_command with some parameters - 2142 0.0 S 31T 0:00.46 0:00.18 -_assetcache 2155 ?? 0.0 S 31T 0:00.00 0:00.03 /usr/bin/some_command with some parameters - 2155 0.0 S 31T 0:00.45 0:00.18 -someuser 2156 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2156 0.0 S 4T 0:00.00 0:00.00 -_spotlight 2157 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2157 0.0 S 4T 0:00.00 0:00.00 -_spotlight 2165 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 2165 0.0 S 4T 0:00.00 0:00.00 -someuser 2316 ?? 0.0 S 31T 0:04.73 0:05.82 /usr/bin/some_command with some parameters - 2316 0.0 S 20T 0:00.00 0:00.00 -someuser 2324 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2324 0.0 S 37T 0:00.00 0:00.00 - 2324 0.0 S 37T 0:00.00 0:00.00 -someuser 2325 ?? 0.0 S 31T 0:00.52 0:00.11 /usr/bin/some_command with some parameters - 2325 0.0 S 31T 0:00.00 0:00.00 - 2325 0.0 S 31T 0:00.00 0:00.00 - 2325 0.0 S 31T 0:00.05 0:00.00 - 2325 0.0 S 20T 0:00.00 0:00.00 - 2325 0.0 S 20T 0:00.00 0:00.00 -someuser 2328 ?? 0.0 S 4T 0:01.96 0:01.06 /usr/bin/some_command with some parameters - 2328 0.0 S 4T 0:00.04 0:00.01 - 2328 0.0 S 4T 0:00.00 0:00.00 - 2328 0.0 S 4T 0:00.00 0:00.00 - 2328 0.0 S 4T 0:00.00 0:00.00 -someuser 2329 ?? 0.0 S 31T 0:01.99 0:00.36 /usr/bin/some_command with some parameters - 2329 0.0 S 31T 0:00.00 0:00.00 -someuser 2330 ?? 0.0 S 31T 0:22.30 1:25.11 /usr/bin/some_command with some parameters - 2330 0.0 S 19T 0:00.00 0:00.00 - 2330 0.0 S 31T 0:00.31 0:00.10 - 2330 0.0 S 31T 0:00.00 0:00.00 - 2330 0.0 S 31T 0:00.00 0:00.00 - 2330 0.0 S 31T 0:00.00 0:00.00 - 2330 0.0 S 31T 0:00.00 0:00.00 -someuser 2331 ?? 0.0 S 31T 0:00.06 0:00.04 /usr/bin/some_command with some parameters - 2331 0.0 S 37T 0:00.00 0:00.00 -someuser 2332 ?? 0.0 S 31T 0:00.71 0:00.34 /usr/bin/some_command with some parameters - 2332 0.0 S 31T 0:00.02 0:00.00 - 2332 0.0 S 31T 0:00.00 0:00.00 -someuser 2334 ?? 0.0 S 31T 0:02.77 0:01.40 /usr/bin/some_command with some parameters - 2334 0.0 S 31T 0:00.01 0:00.00 - 2334 0.0 S 37T 0:00.31 0:00.09 - 2334 0.0 S 31T 0:00.00 0:00.00 - 2334 0.0 S 37T 0:00.00 0:00.00 -someuser 2348 ?? 0.0 S 31T 0:02.24 0:00.66 /usr/bin/some_command with some parameters - 2348 0.0 S 31T 0:00.07 0:00.01 - 2348 0.0 S 31T 0:00.00 0:00.00 - 2348 0.0 S 31T 0:00.00 0:00.00 - 2348 0.0 S 31T 0:00.00 0:00.00 - 2348 0.0 S 31T 0:00.00 0:00.00 -someuser 2349 ?? 0.0 S 31T 0:01.18 0:00.45 /usr/bin/some_command with some parameters - 2349 0.0 S 37T 0:00.00 0:00.00 -someuser 2350 ?? 0.0 S 46T 0:14.08 0:12.57 /usr/bin/some_command with some parameters - 2350 0.0 S 46T 0:00.00 0:00.00 - 2350 0.0 S 46T 0:00.59 0:00.20 - 2350 0.0 S 46T 0:00.00 0:00.00 -someuser 2361 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2361 0.0 S 37T 0:00.02 0:00.00 -someuser 2363 ?? 0.0 S 31T 0:00.11 0:00.11 /usr/bin/some_command with some parameters - 2363 0.0 S 31T 0:00.00 0:00.00 - 2363 0.0 S 31T 0:00.00 0:00.00 -someuser 2364 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2364 0.0 S 4T 0:00.05 0:00.07 -someuser 2367 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2367 0.0 S 4T 0:00.00 0:00.00 -someuser 2369 ?? 0.0 S 4T 0:05.78 0:05.65 /usr/bin/some_command with some parameters - 2369 0.0 S 4T 0:00.98 0:00.44 - 2369 0.0 S 4T 0:00.00 0:00.00 - 2369 0.0 S 4T 0:00.00 0:00.00 -someuser 2371 ?? 0.0 S 4T 0:12.60 0:13.23 /usr/bin/some_command with some parameters - 2371 0.0 S 4T 0:00.00 0:00.00 - 2371 0.0 S 4T 0:00.00 0:00.00 - 2371 0.0 S 4T 0:00.00 0:00.00 - 2371 0.0 S 4T 0:00.00 0:00.00 - 2371 0.0 S 4T 0:00.00 0:00.00 -someuser 2383 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2383 0.0 S 31T 0:00.00 0:00.00 -someuser 2389 ?? 0.0 S 31T 0:00.23 0:00.08 /usr/bin/some_command with some parameters - 2389 0.0 S 31T 0:00.00 0:00.00 - 2389 0.0 S 31T 0:00.13 0:00.05 -someuser 2391 ?? 0.0 S 31T 0:00.99 0:00.22 /usr/bin/some_command with some parameters - 2391 0.0 S 4T 0:00.00 0:00.00 -someuser 2397 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2397 0.0 S 4T 0:00.00 0:00.00 -someuser 2399 ?? 0.0 S 46T 0:14.24 0:30.95 /usr/bin/some_command with some parameters - 2399 0.0 S 46T 0:01.32 0:00.87 - 2399 0.0 S 37T 0:00.00 0:00.00 - 2399 0.0 S 46T 0:00.00 0:00.00 -root 2402 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2402 0.0 S 31T 0:00.00 0:00.00 -someuser 2411 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2411 0.0 S 4T 0:00.02 0:00.01 - 2411 0.0 S 4T 0:00.00 0:00.00 -someuser 2412 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2412 0.0 S 4T 0:00.00 0:00.00 - 2412 0.0 S 4T 0:00.00 0:00.00 -someuser 2414 ?? 0.0 S 4T 0:04.02 0:01.69 /usr/bin/some_command with some parameters - 2414 0.0 S 4T 0:00.56 0:00.10 - 2414 0.0 S 4T 0:00.00 0:00.00 - 2414 0.0 S 4T 0:00.00 0:00.00 -someuser 2417 ?? 0.0 S 31T 0:00.80 0:00.29 /usr/bin/some_command with some parameters - 2417 0.0 S 31T 0:00.00 0:00.00 - 2417 0.0 S 31T 0:00.00 0:00.00 -someuser 2420 ?? 0.0 S 4T 0:05.78 0:05.41 /usr/bin/some_command with some parameters - 2420 0.0 S 4T 0:00.00 0:00.00 - 2420 0.0 S 4T 0:00.00 0:00.00 -someuser 2421 ?? 0.0 S 31T 0:00.03 0:00.01 /usr/bin/some_command with some parameters - 2421 0.0 S 31T 0:00.00 0:00.00 -someuser 2425 ?? 0.0 S 31T 0:18.03 0:12.80 /usr/bin/some_command with some parameters - 2425 0.0 S 31T 0:13.33 0:05.22 - 2425 0.0 S 31T 0:00.02 0:00.00 -someuser 2430 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2430 0.0 S 4T 0:00.03 0:00.01 - 2430 0.0 S 4T 0:00.00 0:00.00 -someuser 2441 ?? 0.0 S 4T 0:00.11 0:00.04 /usr/bin/some_command with some parameters - 2441 0.0 S 4T 0:00.00 0:00.00 - 2441 0.0 S 4T 0:00.00 0:00.00 -someuser 2448 ?? 0.0 S 4T 0:00.02 0:00.04 /usr/bin/some_command with some parameters - 2448 0.0 S 4T 0:00.01 0:00.00 -_reportmemoryexception 2456 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 2456 0.0 S 31T 0:00.44 0:00.17 -root 2458 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 2458 0.0 S 4T 0:00.00 0:00.00 -_applepay 2478 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 2478 0.0 S 31T 0:00.44 0:00.17 -_fpsd 2532 ?? 0.0 S 31T 0:00.01 0:00.02 /usr/bin/some_command with some parameters - 2532 0.0 S 31T 0:01.16 0:00.47 -666 2555 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 2555 0.0 S 31T 0:00.11 0:00.08 -newrelic 2556 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 2556 0.0 S 31T 0:00.10 0:00.08 -newrelic 2730 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2730 0.0 S 4T 0:00.00 0:00.00 -666 2731 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2731 0.0 S 4T 0:00.00 0:00.00 -666 2736 ?? 0.0 S 4T 0:01.24 0:01.51 /usr/bin/some_command with some parameters - 2736 0.0 S 4T 0:00.03 0:00.00 -newrelic 2737 ?? 0.0 S 4T 0:01.24 0:01.51 /usr/bin/some_command with some parameters - 2737 0.0 S 4T 0:00.03 0:00.00 -someuser 2827 ?? 3.6 S 47T 3:24.32 12:09.34 /usr/bin/some_command with some parameters - 2827 0.0 S 47T 0:36.63 0:21.76 - 2827 0.0 S 31T 0:04.50 0:05.66 - 2827 0.6 S 47T 0:00.03 0:00.08 - 2827 0.1 S 47T 0:00.15 0:00.10 - 2827 0.0 S 37T 0:00.02 0:00.07 - 2827 0.0 S 47T 0:00.03 0:00.08 - 2827 0.2 S 47T 0:00.02 0:00.06 - 2827 0.2 S 37T 0:00.03 0:00.08 - 2827 0.3 S 37T 0:00.08 0:00.26 - 2827 0.0 S 31T 0:00.00 0:00.00 - 2827 0.0 S 31T 0:00.00 0:00.00 -someuser 2828 ?? 0.0 S 46T 16:03.60 66:28.44 /usr/bin/some_command with some parameters - 2828 0.0 S 31T 0:00.00 0:00.00 - 2828 0.0 S 31T 0:00.46 0:00.34 - 2828 0.0 S 31T 0:00.92 0:01.03 - 2828 0.0 S 31T 10:41.96 10:41.22 - 2828 0.0 S 31T 0:07.12 0:19.95 - 2828 0.0 S 31T 0:00.08 0:00.02 - 2828 0.0 S 31T 0:00.00 0:00.00 - 2828 0.0 S 31T 0:00.01 0:00.00 - 2828 0.0 S 31T 0:10.27 0:00.81 - 2828 0.0 S 31T 0:25.33 2:56.06 - 2828 0.0 S 31T 0:00.00 0:00.00 - 2828 0.0 S 0T 0:00.00 0:00.00 - 2828 0.0 S 31T 0:00.01 0:00.00 - 2828 0.0 S 31T 0:00.28 0:00.22 - 2828 0.0 S 31T 0:00.24 0:00.14 - 2828 0.0 S 46T 0:00.06 0:00.02 - 2828 0.0 S 46T 3:19.50 2:30.69 - 2828 0.0 S 31T 0:01.52 0:01.64 - 2828 0.0 S 31T 0:00.01 0:00.00 - 2828 0.0 S 31T 0:00.31 0:00.05 - 2828 0.0 S 31T 0:00.22 0:00.05 - 2828 0.0 S 19T 0:00.00 0:00.00 - 2828 0.0 S 31T 0:00.03 0:00.01 - 2828 0.0 S 31T 0:01.38 0:00.91 - 2828 0.0 S 31T 0:00.01 0:00.00 - 2828 0.0 S 31T 0:00.01 0:00.00 - 2828 0.0 S 31T 0:00.01 0:00.00 - 2828 0.0 S 31T 0:00.01 0:00.00 - 2828 0.0 S 31T 0:00.01 0:00.00 - 2828 0.0 S 31T 0:00.02 0:00.00 - 2828 0.0 S 31T 0:00.01 0:00.00 - 2828 0.0 S 31T 0:00.13 0:00.15 - 2828 0.0 S 31T 0:00.01 0:00.00 - 2828 0.0 S 0T 0:00.00 0:00.00 - 2828 0.0 S 31T 0:00.00 0:00.00 - 2828 0.0 S 31T 0:00.00 0:00.00 -someuser 2832 ?? 0.2 S 46T 2:47.01 8:47.93 /usr/bin/some_command with some parameters - 2832 0.0 S 31T 0:02.83 0:02.86 - 2832 0.0 S 0T 0:00.16 0:00.11 - 2832 0.2 S 31T 1:48.95 1:18.65 - 2832 0.0 S 31T 0:00.00 0:00.00 - 2832 0.0 S 31T 0:00.00 0:00.00 - 2832 0.0 S 31T 0:04.27 0:09.82 - 2832 0.0 S 31T 0:03.52 0:09.53 - 2832 0.0 S 31T 0:06.21 0:11.86 - 2832 0.0 S 31T 0:00.00 0:00.00 - 2832 0.0 S 31T 0:01.21 0:00.11 - 2832 0.0 S 31T 0:01.20 0:00.11 - 2832 0.0 S 31T 0:01.21 0:00.11 - 2832 0.0 S 31T 0:01.21 0:00.11 - 2832 0.0 S 31T 0:00.00 0:00.00 - 2832 0.0 S 31T 0:00.09 0:00.02 - 2832 0.0 S 31T 0:00.00 0:00.00 - 2832 0.0 S 31T 0:00.01 0:00.00 - 2832 0.0 S 31T 0:00.01 0:00.00 - 2832 0.0 S 31T 0:00.03 0:00.02 - 2832 0.0 S 31T 0:09.04 0:02.71 - 2832 0.0 S 31T 0:00.02 0:00.00 - 2832 0.0 S 46T 0:53.22 0:30.59 - 2832 0.0 S 31T 0:00.00 0:00.00 - 2832 0.0 S 31T 0:00.37 0:00.41 - 2832 0.0 S 0T 0:00.00 0:00.00 - 2832 0.0 S 31T 0:00.00 0:00.00 - 2832 0.0 S 31T 0:00.05 0:00.02 - 2832 0.0 S 31T 0:00.03 0:00.01 - 2832 0.0 S 31T 0:00.00 0:00.00 - 2832 0.0 S 31T 0:00.00 0:00.00 -someuser 2834 ?? 0.0 S 46T 1:31.25 3:58.95 /usr/bin/some_command with some parameters - 2834 0.0 S 31T 0:07.01 0:12.77 - 2834 0.0 S 0T 0:00.12 0:00.11 - 2834 0.0 S 31T 1:24.23 1:37.63 - 2834 0.0 S 31T 0:00.02 0:00.01 - 2834 0.0 S 31T 0:00.00 0:00.00 - 2834 0.0 S 31T 0:00.98 0:00.66 - 2834 0.0 S 31T 0:00.87 0:00.73 - 2834 0.0 S 31T 0:00.98 0:00.75 - 2834 0.0 S 31T 0:00.00 0:00.00 - 2834 0.0 S 31T 0:00.00 0:00.00 - 2834 0.0 S 31T 0:00.11 0:00.04 - 2834 0.0 S 31T 0:00.00 0:00.00 - 2834 0.0 S 31T 0:00.03 0:00.02 - 2834 0.0 S 31T 0:00.01 0:00.00 - 2834 0.0 S 31T 0:00.02 0:00.00 - 2834 0.0 S 31T 0:05.78 0:01.83 - 2834 0.0 S 31T 0:00.04 0:00.02 - 2834 0.0 S 31T 0:01.42 0:00.09 - 2834 0.0 S 31T 0:01.47 0:00.09 - 2834 0.0 S 31T 0:01.44 0:00.09 - 2834 0.0 S 31T 0:01.45 0:00.09 - 2834 0.0 S 46T 0:34.34 0:22.84 - 2834 0.0 S 31T 0:00.03 0:00.02 - 2834 0.0 S 0T 0:00.00 0:00.00 - 2834 0.0 S 31T 0:00.26 0:00.42 - 2834 0.0 S 31T 0:17.54 0:11.36 - 2834 0.0 S 31T 0:10.92 0:07.12 - 2834 0.0 S 37T 0:00.00 0:00.00 -someuser 2836 ?? 0.0 S 28T 1:13.22 2:38.49 /usr/bin/some_command with some parameters - 2836 0.0 S 28T 0:00.00 0:00.00 - 2836 0.0 S 28T 0:00.23 0:00.26 - 2836 0.0 S 0T 0:00.97 0:00.65 - 2836 0.0 S 28T 0:41.17 0:53.42 - 2836 0.0 S 28T 0:00.04 0:00.03 - 2836 0.0 S 28T 0:00.10 0:00.04 - 2836 0.0 S 28T 0:00.03 0:00.02 - 2836 0.0 S 28T 0:00.00 0:00.00 - 2836 0.0 S 28T 0:00.27 0:00.03 - 2836 0.0 S 0T 0:00.73 0:02.01 - 2836 0.0 S 28T 0:00.03 0:00.10 - 2836 0.0 S 28T 2:59.40 5:19.92 - 2836 0.0 S 28T 0:05.51 0:16.63 - 2836 0.0 S 28T 0:00.00 0:00.00 - 2836 0.0 S 28T 0:01.00 0:01.60 - 2836 0.0 S 28T 0:00.01 0:00.01 - 2836 0.0 S 28T 1:31.26 4:01.24 - 2836 0.0 S 28T 0:00.03 0:00.01 - 2836 0.0 S 28T 0:00.00 0:00.00 - 2836 0.0 S 28T 0:01.24 0:00.39 - 2836 0.0 S 28T 2:58.51 0:48.56 - 2836 0.0 S 28T 0:00.63 0:00.03 - 2836 0.0 S 28T 0:50.04 1:15.25 - 2836 0.0 S 28T 0:00.35 0:00.08 - 2836 0.0 S 28T 0:03.12 0:00.91 - 2836 0.0 S 28T 0:01.97 0:00.30 - 2836 0.0 S 28T 0:00.06 0:00.01 - 2836 0.0 S 28T 0:00.04 0:00.01 - 2836 0.0 S 28T 0:02.95 0:00.93 - 2836 0.0 S 28T 0:04.52 0:01.20 - 2836 0.0 S 28T 0:01.26 0:00.90 - 2836 0.0 S 28T 0:35.10 0:24.68 - 2836 0.0 S 28T 0:00.04 0:00.02 - 2836 0.0 S 28T 0:28.24 0:16.75 - 2836 0.0 S 28T 0:05.82 0:01.66 - 2836 0.0 S 28T 0:00.31 0:00.46 - 2836 0.0 S 28T 0:00.05 0:00.01 - 2836 0.0 S 28T 0:00.04 0:00.02 - 2836 0.0 S 19T 0:00.00 0:00.00 - 2836 0.0 S 28T 0:18.64 2:15.96 - 2836 0.0 S 28T 0:00.02 0:00.01 - 2836 0.0 S 28T 0:00.00 0:00.00 - 2836 0.0 S 28T 0:00.00 0:00.00 -someuser 2838 ?? 1.6 S 46T 17:08.54 22:01.14 /usr/bin/some_command with some parameters - 2838 0.0 S 31T 0:01.57 0:00.80 - 2838 0.0 S 31T 0:00.00 0:00.00 - 2838 0.0 S 31T 0:03.78 0:01.50 - 2838 0.0 S 31T 0:10.84 0:06.58 - 2838 0.1 S 31T 1:18.21 0:15.73 - 2838 0.1 S 31T 1:17.68 0:15.27 - 2838 0.1 S 31T 1:17.53 0:14.74 - 2838 0.0 S 31T 0:01.98 0:00.98 - 2838 0.0 S 31T 0:09.71 0:06.75 - 2838 0.1 S 31T 1:19.00 0:17.83 - 2838 0.0 S 46T 2:51.36 1:51.45 - 2838 0.0 S 31T 0:48.84 1:23.12 - 2838 0.0 S 31T 0:08.61 0:03.85 - 2838 1.2 S 31T 6:48.39 2:04.46 - 2838 0.0 S 19T 0:00.00 0:00.00 - 2838 0.0 S 31T 0:00.00 0:00.00 - 2838 0.1 S 31T 0:58.76 0:08.79 - 2838 0.1 S 31T 0:58.62 0:08.80 - 2838 0.1 S 31T 0:56.48 0:09.06 - 2838 0.1 S 31T 0:09.79 0:01.50 - 2838 0.1 S 31T 0:03.29 0:00.54 - 2838 0.1 S 31T 0:03.26 0:00.56 - 2838 0.1 S 31T 0:03.28 0:00.56 - 2838 0.0 S 31T 0:00.03 0:00.02 - 2838 0.7 S 31T 0:07.26 0:09.31 - 2838 0.1 S 31T 0:03.25 0:00.47 - 2838 0.0 S 31R 0:00.16 0:00.13 - 2838 7.0 S 46R 0:03.09 2:03.34 - 2838 8.4 S 31T 0:03.83 1:56.14 - 2838 2.2 S 31T 0:07.17 0:37.92 - 2838 0.4 S 57T 0:01.45 0:06.86 - 2838 1.8 S 31T 0:34.69 0:11.36 - 2838 0.0 S 31T 0:00.58 0:00.89 - 2838 0.5 S 31T 0:01.81 0:19.41 - 2838 6.7 S 46R 0:09.32 1:57.44 - 2838 0.0 S 97R 0:00.96 0:00.84 - 2838 0.7 S 46R 0:00.63 0:13.44 - 2838 0.1 S 97R 0:00.94 0:02.07 - 2838 0.1 S 31T 0:00.33 0:05.60 - 2838 4.1 S 31T 0:02.02 0:51.42 - 2838 4.0 S 31T 0:02.02 0:51.34 - 2838 4.0 S 31T 0:02.02 0:51.43 - 2838 4.0 S 31T 0:02.01 0:51.39 - 2838 4.0 S 31T 0:02.02 0:51.36 - 2838 0.0 S 31T 0:00.04 0:00.03 - 2838 0.0 S 31T 0:00.63 0:16.54 - 2838 0.0 S 31T 0:00.63 0:16.53 - 2838 0.4 S 31T 0:02.25 0:22.82 - 2838 0.0 S 31T 0:00.63 0:16.52 - 2838 4.0 S 31T 0:02.03 0:51.44 - 2838 0.0 S 31T 0:00.11 0:00.05 - 2838 0.0 S 31T 0:00.11 0:00.05 - 2838 0.0 S 31T 0:00.11 0:00.05 - 2838 0.0 S 31T 0:00.11 0:00.05 - 2838 0.0 S 31T 0:00.11 0:00.05 - 2838 0.0 S 31T 0:00.11 0:00.05 - 2838 2.8 S 31T 0:01.00 0:42.64 - 2838 2.9 S 31T 0:01.00 0:42.74 - 2838 2.9 S 31T 0:01.00 0:42.65 - 2838 2.9 S 31T 0:00.98 0:42.73 - 2838 2.9 S 31T 0:01.00 0:42.64 - 2838 2.8 S 31T 0:01.01 0:42.55 - 2838 2.8 S 46T 0:02.01 0:03.06 - 2838 2.4 S 20T 0:00.91 0:01.40 - 2838 0.0 S 37T 0:00.76 0:01.30 - 2838 3.0 S 46T 0:00.52 0:00.80 - 2838 1.0 S 46T 0:00.38 0:00.67 - 2838 0.0 S 31T 0:00.46 0:00.80 - 2838 0.0 S 31T 0:00.00 0:00.00 -someuser 2839 ?? 0.0 S 46T 0:00.00 0:00.00 /usr/bin/some_command with some parameters -someuser 2840 ?? 0.0 S 4T 0:07.76 0:04.96 /usr/bin/some_command with some parameters - 2840 0.0 S 4T 0:01.14 0:00.37 - 2840 0.0 S 4T 0:00.00 0:00.00 - 2840 0.0 S 4T 0:00.00 0:00.00 -someuser 2842 ?? 0.0 S 46T 0:41.47 1:23.77 /usr/bin/some_command with some parameters - 2842 0.0 S 19T 0:00.00 0:00.00 - 2842 0.0 S 46T 0:01.57 0:00.92 - 2842 0.0 S 37T 0:00.00 0:00.00 -someuser 2843 ?? 0.0 S 47T 0:22.58 0:33.09 /usr/bin/some_command with some parameters - 2843 0.0 S 47T 0:01.30 0:00.59 - 2843 0.0 S 37T 0:00.00 0:00.00 -someuser 2844 ?? 0.0 S 46T 0:29.59 0:51.01 /usr/bin/some_command with some parameters - 2844 0.0 S 46T 0:03.21 0:01.96 - 2844 0.0 S 31T 0:00.04 0:00.00 - 2844 0.0 S 20T 0:00.00 0:00.00 - 2844 0.0 S 20T 0:00.00 0:00.00 -someuser 2848 ?? 0.0 S 4T 0:10.12 0:13.79 /usr/bin/some_command with some parameters - 2848 0.0 S 4T 0:00.88 0:00.35 - 2848 0.0 S 4T 0:01.33 0:00.58 - 2848 0.0 S 4T 0:00.00 0:00.00 - 2848 0.0 S 4T 0:00.00 0:00.00 -someuser 2861 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 2861 0.0 S 31T 0:00.33 0:00.04 - 2861 0.0 S 31T 0:00.03 0:00.00 - 2861 0.0 S 31T 0:00.00 0:00.00 -someuser 2872 ?? 0.0 S 4T 0:00.27 0:00.11 /usr/bin/some_command with some parameters - 2872 0.0 S 4T 0:00.04 0:00.01 - 2872 0.0 S 4T 0:00.00 0:00.00 -root 2882 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 2882 0.0 S 4T 0:00.02 0:00.00 -someuser 2885 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 2885 0.0 S 31T 0:00.32 0:00.05 - 2885 0.0 S 31T 0:00.02 0:00.00 - 2885 0.0 S 31T 0:00.00 0:00.00 -someuser 2889 ?? 0.0 S 31T 0:02.32 0:01.03 /usr/bin/some_command with some parameters - 2889 0.0 S 31T 0:00.00 0:00.00 -someuser 2892 ?? 5.5 S 31T 11:25.69 27:31.32 /usr/bin/some_command with some parameters - 2892 0.0 S 31T 0:00.34 0:00.41 - 2892 0.0 S 31T 0:15.36 0:16.26 - 2892 0.0 S 31T 0:04.59 0:06.11 - 2892 0.0 S 0T 0:00.14 0:00.10 - 2892 0.6 S 31T 2:39.79 3:07.42 - 2892 2.0 S 31T 2:16.48 12:19.61 - 2892 0.0 S 31T 0:00.00 0:00.00 - 2892 0.0 S 31T 0:00.00 0:00.00 - 2892 0.0 S 31T 0:00.00 0:00.00 -someuser 2899 ?? 0.0 S 31T 0:00.02 0:00.05 /usr/bin/some_command with some parameters - 2899 0.0 S 31T 0:00.17 0:00.16 - 2899 0.0 S 31T 0:32.75 1:16.03 - 2899 0.0 S 31T 0:00.09 0:00.02 - 2899 0.0 S 0T 0:02.16 0:00.21 - 2899 0.0 S 31T 0:00.01 0:00.00 - 2899 0.0 S 31T 0:00.01 0:00.00 - 2899 0.0 S 31T 0:00.00 0:00.00 -someuser 2913 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 2913 0.0 S 31T 0:00.31 0:00.04 - 2913 0.0 S 31T 0:00.01 0:00.00 - 2913 0.0 S 31T 0:00.00 0:00.00 -someuser 2915 ?? 0.3 S 31T 3:51.63 59:54.10 /usr/bin/some_command with some parameters - 2915 0.0 S 31T 0:19.84 0:22.22 - 2915 0.0 S 0T 0:00.11 0:00.10 - 2915 0.6 S 31T 2:43.61 3:46.09 - 2915 0.0 S 31T 0:00.01 0:00.00 - 2915 2.8 S 31T 3:57.14 19:14.88 - 2915 0.0 S 31T 0:00.94 0:01.53 - 2915 0.0 S 31T 0:07.65 0:39.99 - 2915 0.0 S 31T 0:07.57 0:39.59 - 2915 0.0 S 31T 0:07.61 0:39.10 - 2915 0.0 S 31T 0:07.67 0:40.79 - 2915 0.0 S 31T 0:00.11 0:00.48 - 2915 0.0 S 31T 0:00.24 0:00.46 - 2915 0.0 S 31T 0:00.06 0:00.02 - 2915 0.0 S 31T 0:00.03 0:00.55 - 2915 0.0 S 31T 0:00.03 0:00.48 - 2915 0.0 S 31T 0:00.01 0:00.22 - 2915 0.0 S 31T 0:00.01 0:00.10 - 2915 0.0 S 31T 0:00.00 0:00.06 -someuser 2924 ?? 0.0 S 31T 0:23.34 0:27.91 /usr/bin/some_command with some parameters - 2924 0.0 S 31T 0:00.19 0:00.41 - 2924 0.0 S 31T 0:00.06 0:00.01 - 2924 0.0 S 31T 0:00.14 0:00.11 - 2924 0.0 S 0T 0:00.14 0:00.10 - 2924 0.0 S 31T 0:20.16 0:24.92 - 2924 0.3 S 31T 1:00.63 3:05.41 - 2924 0.0 S 31T 0:00.00 0:00.00 -someuser 2925 ?? 0.0 S 31T 0:00.02 0:00.06 /usr/bin/some_command with some parameters - 2925 0.0 S 31T 0:00.10 0:00.08 - 2925 0.0 S 31T 0:20.11 0:31.57 - 2925 0.0 S 31T 0:00.09 0:00.02 - 2925 0.0 S 0T 0:00.83 0:00.11 - 2925 0.0 S 31T 0:00.15 0:00.05 - 2925 0.0 S 31T 0:00.00 0:00.00 -someuser 2928 ?? 0.0 S 31T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 2928 0.0 S 31T 0:00.54 0:00.06 - 2928 0.0 S 31T 0:00.01 0:00.00 - 2928 0.0 S 31T 0:00.00 0:00.00 -someuser 2930 ?? 0.0 S 31T 0:00.54 0:00.82 /usr/bin/some_command with some parameters - 2930 0.0 S 31T 0:00.00 0:00.00 -root 2948 ?? 0.0 S 31T 0:03.08 0:01.19 /usr/bin/some_command with some parameters - 2948 0.0 S 31T 0:00.00 0:00.00 -someuser 2949 ?? 0.0 S 31T 0:00.02 0:00.01 /usr/bin/some_command with some parameters -someuser 2984 ?? 0.0 S 31T 18:41.03 38:08.48 /usr/bin/some_command with some parameters - 2984 0.0 S 31T 0:00.32 0:00.40 - 2984 0.0 S 31T 0:00.01 0:00.00 - 2984 0.0 S 31T 2:54.77 3:38.20 - 2984 0.0 S 31T 3:30.37 14:49.00 - 2984 0.0 S 31T 0:07.78 0:16.90 - 2984 0.0 S 31T 0:00.82 0:00.48 - 2984 0.0 S 31T 0:05.01 0:10.98 - 2984 0.0 S 54T 0:06.15 0:14.80 - 2984 0.0 S 0T 0:00.36 0:00.38 - 2984 0.0 S 31T 0:00.00 0:00.00 -someuser 2986 ?? 0.0 S 31T 0:00.07 0:00.13 /usr/bin/some_command with some parameters - 2986 0.0 S 31T 0:00.68 0:00.88 - 2986 0.1 S 31T 8:41.43 28:57.47 - 2986 0.0 S 31T 0:00.08 0:00.03 - 2986 0.0 S 31T 0:00.12 0:00.08 - 2986 0.0 S 0T 0:04.88 0:00.32 - 2986 0.0 S 0T 0:03.61 0:00.24 - 2986 0.0 S 31T 0:00.01 0:00.01 - 2986 0.0 S 31T 0:00.06 0:00.01 - 2986 0.0 S 31T 0:00.07 0:00.01 - 2986 0.0 S 31T 0:00.01 0:00.00 - 2986 0.0 S 31T 0:00.00 0:00.00 - 2986 0.0 S 31T 0:00.00 0:00.00 -someuser 2991 ?? 0.0 S 31T 0:18.98 0:40.44 /usr/bin/some_command with some parameters - 2991 0.0 S 31T 0:00.00 0:00.00 - 2991 0.0 S 0T 0:00.13 0:00.10 - 2991 0.0 S 31T 0:09.96 0:11.54 - 2991 0.0 S 31T 0:00.09 0:00.02 - 2991 0.0 S 31T 0:00.00 0:00.00 - 2991 0.0 S 31T 0:00.01 0:00.00 - 2991 0.0 S 31T 0:00.00 0:00.00 -someuser 2997 ?? 0.0 S 31T 0:20.21 1:53.46 /usr/bin/some_command with some parameters - 2997 0.0 S 31T 0:00.00 0:00.00 - 2997 0.0 S 0T 0:00.13 0:00.10 - 2997 0.0 S 31T 0:15.84 0:25.18 - 2997 0.0 S 31T 0:00.01 0:00.00 - 2997 0.0 S 31T 0:00.10 0:00.26 - 2997 0.0 S 31T 0:00.00 0:00.00 - 2997 0.0 S 31T 0:00.02 0:00.01 - 2997 0.0 S 31T 0:00.01 0:00.01 - 2997 0.0 S 31T 0:00.00 0:00.01 - 2997 0.0 S 31T 0:00.01 0:00.02 - 2997 0.0 S 31T 0:00.00 0:00.00 - 2997 0.0 S 31T 0:00.23 0:00.13 - 2997 0.0 S 31T 0:00.00 0:00.00 - 2997 0.0 S 31T 0:00.01 0:00.00 -someuser 2998 ?? 0.0 S 31T 0:02.59 0:03.34 /usr/bin/some_command with some parameters - 2998 0.0 S 31T 0:00.01 0:00.00 - 2998 0.0 S 0T 0:00.15 0:00.10 - 2998 0.0 S 31T 0:01.21 0:01.23 - 2998 0.0 S 31T 0:00.01 0:00.00 - 2998 0.0 S 31T 0:00.01 0:00.00 - 2998 0.0 S 31T 0:00.00 0:00.00 - 2998 0.0 S 31T 0:00.00 0:00.00 - 2998 0.0 S 31T 0:00.00 0:00.00 - 2998 0.0 S 31T 0:00.00 0:00.00 - 2998 0.0 S 31T 0:00.00 0:00.00 - 2998 0.0 S 31T 0:00.00 0:00.00 - 2998 0.0 S 31T 0:00.27 0:00.12 - 2998 0.0 S 31T 0:00.11 0:00.03 - 2998 0.0 S 31T 0:00.00 0:00.00 -someuser 2999 ?? 0.0 S 31T 0:46.82 1:58.19 /usr/bin/some_command with some parameters - 2999 0.0 S 31T 0:00.00 0:00.00 - 2999 0.0 S 0T 0:00.14 0:00.09 - 2999 0.0 S 31T 0:15.19 0:20.96 - 2999 0.0 S 31T 0:00.01 0:00.02 - 2999 0.0 S 31T 0:00.37 0:01.38 - 2999 0.0 S 31T 0:00.00 0:00.00 - 2999 0.0 S 31T 0:00.03 0:00.04 - 2999 0.0 S 31T 0:00.03 0:00.04 - 2999 0.0 S 31T 0:00.02 0:00.04 - 2999 0.0 S 31T 0:00.03 0:00.05 - 2999 0.0 S 31T 0:00.00 0:00.00 - 2999 0.0 S 31T 0:00.01 0:00.04 - 2999 0.0 S 31T 0:00.48 0:00.15 - 2999 0.0 S 31T 0:00.00 0:00.00 - 2999 0.0 S 31T 0:00.00 0:00.00 -someuser 3016 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 3016 0.0 S 20T 0:00.00 0:00.00 -someuser 3033 ?? 0.0 S 31T 1:29.64 4:29.00 /usr/bin/some_command with some parameters - 3033 0.0 S 31T 0:00.72 0:00.47 - 3033 0.0 S 31T 0:00.01 0:00.00 - 3033 0.0 S 31T 0:00.15 0:00.11 - 3033 0.0 S 0T 0:00.12 0:00.09 - 3033 0.0 S 31T 0:40.64 0:51.82 - 3033 0.0 S 31T 0:33.94 2:49.91 - 3033 0.0 S 31T 0:00.00 0:00.00 -someuser 3059 ?? 0.0 S 31T 0:04.49 0:00.80 gpg-agent --homedir /usr/bin/some_command with some parameters -someuser 3062 ?? 0.0 S 31T 0:00.62 0:00.24 /usr/bin/some_command with some parameters - 3062 0.0 S 19T 0:00.00 0:00.00 - 3062 0.0 S 31T 0:00.01 0:00.00 -someuser 3063 ?? 0.0 S 4T 0:00.14 0:00.12 /usr/bin/some_command with some parameters - 3063 0.0 S 4T 0:00.01 0:00.01 - 3063 0.0 S 4T 0:00.01 0:00.02 - 3063 0.0 S 4T 0:00.00 0:00.00 - 3063 0.0 S 4T 0:00.00 0:00.00 -someuser 3071 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 3071 0.0 S 20T 0:00.00 0:00.00 -someuser 3073 ?? 0.2 S 20T 5:16.24 10:35.13 /usr/bin/some_command with some parameters - 3073 0.0 S 20T 0:00.08 0:00.02 - 3073 0.0 S 20T 0:00.16 0:00.12 - 3073 0.2 S 20T 4:03.29 3:27.79 - 3073 0.0 S 20T 0:00.00 0:00.00 - 3073 0.0 S 20T 0:01.45 0:21.67 - 3073 0.0 S 20T 0:01.43 0:21.43 - 3073 0.0 S 20T 0:01.41 0:21.23 - 3073 0.0 S 20T 0:00.00 0:00.00 - 3073 0.0 S 20T 0:00.00 0:00.00 - 3073 0.0 S 20T 0:00.00 0:00.00 - 3073 0.0 S 20T 0:00.00 0:00.00 - 3073 0.0 S 20T 0:00.00 0:00.00 - 3073 0.0 S 20T 0:00.09 0:00.02 - 3073 0.0 S 20T 0:00.00 0:00.00 - 3073 0.0 S 20T 0:00.01 0:00.00 - 3073 0.0 S 20T 0:00.00 0:00.00 - 3073 0.0 S 20T 0:00.01 0:00.00 - 3073 0.0 S 20T 0:00.27 0:00.11 - 3073 0.0 S 20T 0:01.36 0:00.77 - 3073 0.0 S 20T 0:00.01 0:00.00 - 3073 0.0 S 20T 0:00.31 0:00.44 - 3073 0.0 S 20T 0:00.00 0:00.00 - 3073 0.1 S 20T 0:00.25 0:00.32 - 3073 0.0 S 20T 0:00.22 0:00.28 - 3073 0.1 S 20T 0:00.26 0:00.34 - 3073 0.0 S 20T 0:00.00 0:00.00 - 3073 0.0 S 20T 0:00.00 0:00.00 - 3073 0.0 S 20T 0:00.00 0:00.00 -someuser 3080 ?? 0.0 S 46T 0:03.17 0:01.25 /usr/bin/some_command with some parameters - 3080 0.0 S 46T 0:00.45 0:00.09 - 3080 0.0 S 37T 0:00.00 0:00.00 -someuser 3083 ?? 0.0 S 4T 0:06.89 0:05.98 /usr/bin/some_command with some parameters - 3083 0.0 S 4T 0:00.00 0:00.00 -someuser 3088 ?? 0.0 S 20T 0:16.14 0:15.99 /usr/bin/some_command with some parameters - 3088 0.0 S 20T 0:00.00 0:00.00 - 3088 0.0 S 20T 0:29.70 0:28.62 - 3088 0.0 S 20T 0:00.01 0:00.01 - 3088 0.0 S 20T 0:00.01 0:00.01 - 3088 0.0 S 20T 0:00.00 0:00.00 - 3088 0.0 S 20T 0:00.00 0:00.00 - 3088 0.0 S 20T 0:08.88 0:44.56 - 3088 0.0 S 20T 0:05.83 0:08.89 - 3088 0.0 S 20T 0:00.38 0:00.11 - 3088 0.0 S 20T 0:41.72 0:28.30 - 3088 0.0 S 20T 0:00.13 0:00.10 - 3088 0.0 S 20T 0:03.64 0:01.80 - 3088 0.0 S 20T 0:01.36 0:00.56 - 3088 0.0 S 20T 0:00.00 0:00.00 - 3088 0.0 S 20T 0:01.67 0:00.69 - 3088 0.0 S 20T 0:00.00 0:00.00 - 3088 0.0 S 20T 0:02.85 0:05.42 - 3088 0.0 S 20T 0:30.76 0:26.13 - 3088 0.0 S 20T 0:00.03 0:00.01 - 3088 0.0 S 20T 0:00.00 0:00.01 - 3088 0.0 S 20T 0:00.16 0:00.11 - 3088 0.0 S 20T 0:00.04 0:00.05 - 3088 0.0 S 20T 0:02.61 0:01.24 - 3088 0.0 S 20T 0:01.54 0:00.83 - 3088 0.0 S 20T 0:00.74 0:00.29 - 3088 0.0 S 20T 0:01.15 0:00.46 - 3088 0.0 S 20T 0:02.32 0:01.10 - 3088 0.0 S 20T 0:03.38 0:01.60 - 3088 0.0 S 20T 0:01.86 0:00.84 - 3088 0.0 S 20T 0:01.69 0:00.88 - 3088 0.0 S 20T 0:02.14 0:01.09 - 3088 0.0 S 20T 0:00.45 0:00.19 - 3088 0.0 S 20T 0:01.06 0:00.48 - 3088 0.0 S 20T 0:00.01 0:00.00 - 3088 0.0 S 20T 0:00.00 0:00.01 - 3088 0.0 S 20T 0:00.02 0:00.01 - 3088 0.0 S 20T 0:00.01 0:00.00 - 3088 0.0 S 20T 0:00.00 0:00.00 -someuser 3091 ?? 0.0 S 4T 0:00.01 0:00.04 /usr/bin/some_command with some parameters - 3091 0.0 S 4T 0:00.02 0:00.00 -someuser 3093 ?? 0.0 S 31T 0:00.28 0:00.17 /usr/bin/some_command with some parameters - 3093 0.0 S 31T 0:00.01 0:00.00 - 3093 0.0 S 0T 0:00.13 0:00.10 - 3093 0.0 S 31T 0:00.27 0:00.10 - 3093 0.0 S 31T 0:00.03 0:00.02 - 3093 0.0 S 31T 0:00.00 0:00.00 -someuser 3094 ?? 0.0 S 31T 0:00.03 0:00.07 /usr/bin/some_command with some parameters - 3094 0.0 S 31T 0:00.09 0:00.07 - 3094 0.0 S 31T 0:31.44 0:58.88 - 3094 0.0 S 31T 0:00.08 0:00.03 - 3094 0.0 S 31T 0:00.00 0:00.00 - 3094 0.0 S 0T 0:00.12 0:00.01 - 3094 0.0 S 31T 0:00.27 0:00.04 -someuser 3095 ?? 0.0 S 31T 1:29.01 11:00.31 /usr/bin/some_command with some parameters - 3095 0.0 S 31T 0:00.06 0:00.05 - 3095 0.0 S 0T 0:00.12 0:00.10 - 3095 0.0 S 31T 0:43.62 1:04.66 - 3095 0.0 S 31T 0:00.01 0:00.00 - 3095 0.0 S 31T 1:00.96 4:59.78 - 3095 0.0 S 31T 0:00.81 0:00.80 - 3095 0.0 S 31T 0:02.30 0:07.20 - 3095 0.0 S 31T 0:02.30 0:07.27 - 3095 0.0 S 31T 0:02.39 0:07.26 - 3095 0.0 S 31T 0:02.31 0:07.21 - 3095 0.0 S 31T 0:00.01 0:00.02 - 3095 0.0 S 31T 0:00.11 0:00.04 - 3095 0.0 S 31T 0:00.01 0:00.00 - 3095 0.0 S 31T 0:00.36 0:00.14 - 3095 0.0 S 31T 0:00.05 0:00.01 -someuser 3146 ?? 0.0 S 46T 0:20.20 0:26.52 /usr/bin/some_command with some parameters - 3146 0.0 S 46T 0:01.17 0:00.58 - 3146 0.0 S 31T 0:00.00 0:00.00 - 3146 0.0 S 31T 0:00.00 0:00.00 -someuser 3181 ?? 0.3 S 46T 9:32.32 10:30.99 /usr/bin/some_command with some parameters - 3181 0.0 S 46T 0:01.30 0:00.81 - 3181 0.0 S 46T 0:00.00 0:00.00 - 3181 0.0 S 46T 0:00.00 0:00.00 - 3181 0.0 S 46T 0:00.00 0:00.00 - 3181 0.0 S 46T 0:00.00 0:00.00 - 3181 0.0 S 46T 0:00.00 0:00.00 - 3181 0.0 S 46T 0:00.00 0:00.00 - 3181 0.0 S 46T 0:00.00 0:00.00 - 3181 0.0 S 46T 0:00.00 0:00.00 - 3181 0.0 S 37T 0:00.00 0:00.00 - 3181 0.0 S 31T 0:00.00 0:00.00 -someuser 3211 ?? 0.0 S 20T 0:00.13 0:00.17 /usr/bin/some_command with some parameters - 3211 0.0 S 20T 0:00.10 0:00.01 - 3211 0.0 S 20T 0:00.18 0:00.12 - 3211 0.0 S 20T 0:00.15 0:00.12 - 3211 0.0 S 20T 2:46.82 1:29.37 - 3211 0.0 S 20T 3:44.57 16:11.83 - 3211 0.0 S 20T 0:00.00 0:00.00 -someuser 3288 ?? 0.0 S 20T 0:00.05 0:00.04 /usr/bin/some_command with some parameters - 3288 0.0 S 20T 0:00.07 0:00.02 - 3288 0.0 S 20T 0:00.15 0:00.12 - 3288 0.1 S 20T 1:07.39 2:51.00 - 3288 0.0 S 20T 0:00.09 0:00.02 - 3288 0.0 S 20T 0:00.02 0:00.01 - 3288 0.0 S 20T 0:00.00 0:00.00 -someuser 3312 ?? 0.0 S 31T 0:33.92 2:38.45 /usr/bin/some_command with some parameters - 3312 0.0 S 31T 0:00.01 0:00.00 - 3312 0.0 S 0T 0:00.12 0:00.09 - 3312 0.0 S 31T 0:05.15 0:04.22 - 3312 0.0 S 31T 0:00.01 0:00.00 - 3312 0.0 S 31T 0:04.08 0:15.29 - 3312 0.0 S 31T 0:00.00 0:00.00 - 3312 0.0 S 31T 0:00.16 0:00.52 - 3312 0.0 S 31T 0:00.28 0:00.54 - 3312 0.0 S 31T 0:00.21 0:00.71 - 3312 0.0 S 31T 0:00.44 0:00.74 - 3312 0.0 S 31T 0:00.01 0:00.03 - 3312 0.0 S 31T 0:00.85 0:00.21 - 3312 0.0 S 31T 0:00.00 0:00.00 - 3312 0.0 S 31T 0:01.40 0:00.32 - 3312 0.0 S 31T 0:00.03 0:00.02 - 3312 0.0 S 31T 0:00.02 0:00.01 -someuser 3337 ?? 0.0 S 31T 0:51.88 2:16.56 /usr/bin/some_command with some parameters - 3337 0.0 S 31T 0:00.01 0:00.00 - 3337 0.0 S 0T 0:00.13 0:00.09 - 3337 0.0 S 31T 0:16.44 0:19.64 - 3337 0.0 S 31T 0:00.01 0:00.00 - 3337 0.0 S 31T 0:00.65 0:01.07 - 3337 0.0 S 31T 0:00.00 0:00.00 - 3337 0.0 S 31T 0:00.07 0:00.05 - 3337 0.0 S 31T 0:00.06 0:00.04 - 3337 0.0 S 31T 0:00.07 0:00.05 - 3337 0.0 S 31T 0:00.05 0:00.04 - 3337 0.0 S 31T 0:00.00 0:00.00 - 3337 0.0 S 31T 0:00.50 0:00.20 - 3337 0.0 S 31T 0:00.42 0:00.16 - 3337 0.0 S 31T 0:00.13 0:00.15 - 3337 0.0 S 31T 0:00.04 0:00.03 -someuser 3543 ?? 0.0 S 31T 0:31.51 1:34.86 /usr/bin/some_command with some parameters - 3543 0.0 S 31T 0:00.13 0:00.01 - 3543 0.0 S 31T 0:00.15 0:00.11 - 3543 0.0 S 0T 0:00.15 0:00.11 - 3543 0.0 S 31T 0:00.00 0:00.00 - 3543 0.0 S 31T 0:00.05 0:00.03 - 3543 0.0 S 31T 0:00.05 0:00.02 - 3543 0.0 S 31T 0:00.02 0:00.01 - 3543 0.0 S 31T 0:00.00 0:00.00 - 3543 0.0 S 31T 0:00.00 0:00.00 -someuser 3544 ?? 0.0 S 31T 0:16.69 0:54.27 /usr/bin/some_command with some parameters - 3544 0.0 S 31T 0:00.03 0:00.00 - 3544 0.0 S 0T 0:00.12 0:00.11 - 3544 0.0 S 31T 0:10.40 0:09.03 - 3544 0.0 S 31T 0:00.01 0:00.00 - 3544 0.0 S 31T 0:00.15 0:00.09 - 3544 0.0 S 31T 0:00.00 0:00.00 - 3544 0.0 S 31T 0:00.00 0:00.00 - 3544 0.0 S 31T 0:00.01 0:00.00 - 3544 0.0 S 31T 0:00.01 0:00.00 - 3544 0.0 S 31T 0:00.00 0:00.00 - 3544 0.0 S 31T 0:00.00 0:00.00 - 3544 0.0 S 31T 0:00.00 0:00.00 - 3544 0.0 S 31T 0:00.01 0:00.01 - 3544 0.0 S 31T 0:00.00 0:00.00 - 3544 0.0 S 31T 0:00.00 0:00.00 -someuser 3545 ?? 0.0 S 31T 0:08.99 0:27.17 /usr/bin/some_command with some parameters - 3545 0.0 S 31T 0:00.03 0:00.00 - 3545 0.0 S 0T 0:00.13 0:00.10 - 3545 0.0 S 31T 0:06.04 0:05.41 - 3545 0.0 S 31T 0:00.01 0:00.00 - 3545 0.0 S 31T 0:00.09 0:00.05 - 3545 0.0 S 31T 0:00.00 0:00.00 - 3545 0.0 S 31T 0:00.00 0:00.00 - 3545 0.0 S 31T 0:00.00 0:00.00 - 3545 0.0 S 31T 0:00.00 0:00.00 - 3545 0.0 S 31T 0:00.00 0:00.00 - 3545 0.0 S 31T 0:00.00 0:00.00 - 3545 0.0 S 31T 0:00.00 0:00.00 - 3545 0.0 S 31T 0:00.00 0:00.00 - 3545 0.0 S 31T 0:00.00 0:00.00 -someuser 3564 ?? 0.2 S 31T 1:56.38 11:04.14 /usr/bin/some_command with some parameters - 3564 0.0 S 31T 0:00.02 0:00.00 - 3564 0.0 S 0T 0:00.12 0:00.10 - 3564 0.0 S 31T 0:30.11 0:36.49 - 3564 0.0 S 31T 0:00.00 0:00.00 - 3564 0.0 S 31T 0:18.17 0:51.64 - 3564 0.0 S 31T 0:00.00 0:00.00 - 3564 0.0 S 31T 0:00.01 0:00.01 - 3564 0.0 S 31T 0:00.02 0:00.01 - 3564 0.0 S 31T 0:00.02 0:00.02 - 3564 0.0 S 31T 0:00.02 0:00.02 - 3564 0.0 S 31T 0:00.00 0:00.00 - 3564 0.0 S 31T 0:00.00 0:00.00 - 3564 0.0 S 31T 0:00.00 0:00.00 - 3564 0.0 S 31T 0:00.00 0:00.00 - 3564 0.0 S 31T 0:00.00 0:00.00 - 3564 0.0 S 31T 0:00.02 0:00.02 - 3564 0.0 S 31T 0:00.00 0:00.00 -someuser 3566 ?? 0.0 S 31T 0:00.89 0:00.49 /usr/bin/some_command with some parameters - 3566 0.0 S 31T 0:00.07 0:00.01 - 3566 0.0 S 0T 0:00.14 0:00.11 - 3566 0.0 S 31T 0:00.22 0:00.04 - 3566 0.0 S 31T 0:00.01 0:00.00 - 3566 0.0 S 31T 0:00.01 0:00.00 - 3566 0.0 S 31T 0:00.00 0:00.00 - 3566 0.0 S 31T 0:00.00 0:00.00 - 3566 0.0 S 31T 0:00.00 0:00.00 - 3566 0.0 S 31T 0:00.00 0:00.00 - 3566 0.0 S 31T 0:00.00 0:00.00 - 3566 0.0 S 31T 0:00.00 0:00.00 - 3566 0.0 S 31T 0:00.00 0:00.00 - 3566 0.0 S 31T 0:00.16 0:00.12 - 3566 0.0 S 31T 0:00.00 0:00.00 -someuser 3569 ?? 0.1 S 31T 2:18.42 13:40.07 /usr/bin/some_command with some parameters - 3569 0.0 S 31T 0:00.02 0:00.00 - 3569 0.0 S 0T 0:00.12 0:00.10 - 3569 0.0 S 31T 0:30.67 0:38.85 - 3569 0.0 S 31T 0:00.01 0:00.00 - 3569 0.0 S 31T 0:21.88 1:15.72 - 3569 0.0 S 31T 0:00.00 0:00.00 - 3569 0.0 S 31T 0:00.15 0:00.27 - 3569 0.0 S 31T 0:00.14 0:00.26 - 3569 0.0 S 31T 0:00.13 0:00.29 - 3569 0.0 S 31T 0:00.13 0:00.32 - 3569 0.0 S 31T 0:00.00 0:00.00 - 3569 0.0 S 31T 0:00.00 0:00.00 - 3569 0.0 S 31T 0:00.00 0:00.00 - 3569 0.0 S 31T 0:00.00 0:00.00 - 3569 0.0 S 31T 0:00.00 0:00.00 - 3569 0.0 S 31T 0:00.00 0:00.00 - 3569 0.0 S 31T 0:00.00 0:00.00 - 3569 0.0 S 31T 0:00.00 0:00.00 - 3569 0.0 S 31T 0:00.00 0:00.00 - 3569 0.0 S 31T 0:00.03 0:00.02 - 3569 0.0 S 31T 0:00.00 0:00.00 -someuser 3571 ?? 0.0 S 31T 0:00.97 0:00.52 /usr/bin/some_command with some parameters - 3571 0.0 S 31T 0:00.07 0:00.01 - 3571 0.0 S 0T 0:00.14 0:00.11 - 3571 0.0 S 31T 0:00.23 0:00.05 - 3571 0.0 S 31T 0:00.01 0:00.00 - 3571 0.0 S 31T 0:00.01 0:00.00 - 3571 0.0 S 31T 0:00.00 0:00.00 - 3571 0.0 S 31T 0:00.00 0:00.00 - 3571 0.0 S 31T 0:00.00 0:00.00 - 3571 0.0 S 31T 0:00.00 0:00.00 - 3571 0.0 S 31T 0:00.00 0:00.00 - 3571 0.0 S 31T 0:00.00 0:00.00 - 3571 0.0 S 31T 0:00.00 0:00.00 - 3571 0.0 S 31T 0:00.15 0:00.12 - 3571 0.0 S 31T 0:00.00 0:00.00 -someuser 3623 ?? 1.6 S 20T 3:07.53 26:05.10 /usr/bin/some_command with some parameters - 3623 0.0 S 20T 0:00.02 0:00.00 - 3623 0.0 S 20T 0:00.14 0:00.12 - 3623 0.1 S 20T 2:59.93 3:58.42 - 3623 0.0 S 20T 0:00.00 0:00.00 - 3623 0.0 S 20T 3:50.67 16:36.39 - 3623 0.0 S 20T 0:00.00 0:00.00 - 3623 0.0 S 20T 0:07.04 0:08.13 - 3623 0.0 S 20T 0:07.15 0:08.16 - 3623 0.0 S 20T 0:07.15 0:08.21 - 3623 0.0 S 20T 0:07.13 0:08.20 - 3623 0.0 S 20T 0:00.00 0:00.00 - 3623 0.0 S 20T 0:00.02 0:00.01 - 3623 0.0 S 20T 0:00.01 0:00.00 - 3623 0.0 S 20T 0:00.01 0:00.00 - 3623 0.0 S 20T 0:00.01 0:00.00 - 3623 0.0 S 20T 0:00.01 0:00.00 - 3623 0.0 S 20T 0:00.77 0:00.26 - 3623 0.0 S 20T 0:00.00 0:00.00 - 3623 0.0 S 20T 0:00.10 0:00.56 - 3623 0.0 S 20T 0:00.11 0:00.62 - 3623 0.0 S 20T 0:00.09 0:00.60 - 3623 0.0 S 20T 0:00.11 0:00.66 - 3623 0.0 S 20T 0:00.11 0:00.62 - 3623 0.0 S 20T 0:00.14 0:00.70 - 3623 0.0 S 20T 0:00.06 0:00.35 - 3623 0.0 S 20T 0:00.05 0:00.30 - 3623 0.0 S 20T 0:00.00 0:00.00 -someuser 3656 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 3656 0.0 S 4T 0:00.00 0:00.00 -root 3732 ?? 0.0 S 20T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 3732 0.4 S 20T 0:57.98 7:08.01 - 3732 0.0 S 20T 0:00.00 0:00.00 - 3732 0.0 S 20T 0:00.46 0:00.25 - 3732 0.0 S 20T 0:00.00 0:00.00 - 3732 0.0 S 20T 0:00.00 0:00.00 - 3732 0.0 S 20T 0:11.95 0:05.79 -someuser 3736 ?? 0.0 S 20T 0:00.33 0:00.26 /usr/bin/some_command with some parameters - 3736 0.0 S 20T 1:11.05 4:16.13 - 3736 0.0 S 20T 0:00.00 0:00.00 - 3736 0.0 S 20T 0:00.46 0:00.25 - 3736 0.0 S 20T 0:00.00 0:00.00 - 3736 0.0 S 20T 0:11.65 0:06.09 - 3736 0.0 S 20T 0:00.00 0:00.00 - 3736 0.0 S 20T 0:00.00 0:00.00 - 3736 0.0 S 20T 0:00.00 0:00.00 -root 3742 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 3742 0.0 S 31T 1:32.58 3:35.36 - 3742 0.0 S 31T 0:05.20 0:00.21 - 3742 0.0 S 31T 0:00.50 0:00.28 - 3742 0.0 S 31T 0:00.00 0:00.00 - 3742 0.0 S 31T 0:00.03 0:00.02 - 3742 0.0 S 31T 0:17.45 0:06.37 - 3742 0.0 S 31T 0:04.79 0:02.33 - 3742 0.0 S 31T 0:28.30 0:10.24 - 3742 0.0 S 31T 0:05.11 0:02.47 - 3742 0.0 S 31T 1:33.15 2:44.53 - 3742 0.0 S 31T 0:00.00 0:00.00 - 3742 0.0 S 31T 1:13.45 0:32.12 - 3742 0.0 S 31T 0:00.06 0:00.03 - 3742 0.0 S 31T 0:00.05 0:00.01 - 3742 0.0 S 31T 0:00.05 0:00.01 - 3742 0.0 S 31T 0:00.65 0:01.64 - 3742 0.0 S 31T 0:00.11 0:00.11 - 3742 0.0 S 31T 0:00.00 0:00.00 - 3742 0.0 S 31T 0:00.00 0:00.00 - 3742 0.0 S 31T 0:00.00 0:00.00 - 3742 0.0 S 31T 0:00.00 0:00.00 -someuser 3743 ?? 0.0 S 46T 0:09.36 0:11.95 /usr/bin/some_command with some parameters - 3743 0.0 S 31T 0:00.00 0:00.00 - 3743 0.0 S 31T 0:00.00 0:00.00 - 3743 0.0 S 31T 0:00.00 0:00.00 - 3743 0.0 S 31T 0:00.00 0:00.00 - 3743 0.0 S 46T 0:01.34 0:00.57 - 3743 0.0 S 31T 0:00.00 0:00.00 - 3743 0.0 S 37T 0:00.00 0:00.00 -root 3747 ?? 0.0 S 20T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 3747 0.0 R 20T 1:30.26 5:34.22 - 3747 0.0 S 20T 0:00.00 0:00.00 - 3747 0.0 S 20T 0:00.66 0:00.29 - 3747 0.0 S 20T 0:00.00 0:00.00 - 3747 0.0 S 20T 0:20.98 0:10.77 - 3747 0.0 S 20T 0:00.28 0:00.10 - 3747 0.0 S 20T 0:00.25 0:00.07 - 3747 0.0 S 20T 0:00.23 0:00.07 - 3747 0.0 S 20T 0:03.57 0:06.72 - 3747 0.0 S 20T 0:00.00 0:00.00 - 3747 0.0 S 20T 0:01.41 0:01.08 - 3747 0.0 S 20T 0:00.75 0:01.43 - 3747 0.0 S 20T 0:11.93 0:27.25 - 3747 0.0 S 20T 0:00.00 0:00.00 - 3747 0.0 S 20T 0:00.00 0:00.00 - 3747 0.0 S 20T 0:00.01 0:00.00 - 3747 0.0 S 20T 0:00.01 0:00.00 -root 3769 ?? 0.0 S 31T 0:53.06 0:37.06 /usr/bin/some_command with some parameters - 3769 0.0 S 31T 0:00.42 0:00.27 - 3769 0.0 S 31T 0:00.00 0:00.00 - 3769 0.0 S 31T 1:28.59 0:29.68 - 3769 0.0 S 31T 0:00.00 0:00.00 - 3769 0.0 S 31T 0:00.00 0:00.00 - 3769 0.0 S 31T 0:15.11 0:07.92 - 3769 0.0 S 31T 0:00.00 0:00.00 - 3769 0.0 S 31T 0:13.86 0:07.25 -_driverkit 3811 ?? 0.0 S 63R 0:11.05 0:12.34 /usr/bin/some_command with some parameters - 3811 0.0 S 31T 0:00.00 0:00.00 -_driverkit 3813 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 3813 0.0 S 31T 0:00.00 0:00.00 -someuser 3834 ?? 0.0 S 20T 0:10.13 0:04.30 /usr/bin/some_command with some parameters - 3834 0.0 S 20T 0:00.00 0:00.00 - 3834 0.0 S 20T 0:00.00 0:00.00 - 3834 0.0 S 20T 0:00.00 0:00.00 - 3834 0.0 S 20T 0:04.18 0:00.63 - 3834 0.0 S 20T 0:00.50 0:00.12 - 3834 0.0 S 20T 0:00.00 0:00.00 - 3834 0.0 S 20T 0:00.00 0:00.00 -someuser 3857 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 3857 0.0 S 4T 0:00.00 0:00.00 -root 4074 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 4074 0.0 S 4T 0:08.20 0:08.17 - 4074 0.0 S 4T 0:11.99 0:12.76 - 4074 0.0 S 4T 0:00.47 0:00.32 - 4074 0.0 S 4T 0:00.15 0:00.06 - 4074 0.0 S 4T 0:00.09 0:00.04 - 4074 0.0 S 4T 0:00.00 0:00.00 -someuser 4168 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 4168 0.0 S 4T 0:00.00 0:00.00 -someuser 5222 ?? 0.0 S 31T 2:04.89 9:15.88 /usr/bin/some_command with some parameters - 5222 0.0 S 31T 0:00.14 0:00.16 - 5222 0.0 S 0T 0:00.12 0:00.09 - 5222 0.0 S 31T 0:16.89 0:21.20 - 5222 0.0 S 31T 0:00.03 0:00.02 - 5222 0.0 S 31T 0:07.50 0:29.90 - 5222 0.0 S 31T 0:01.60 0:02.46 - 5222 0.0 S 31T 0:00.60 0:01.87 - 5222 0.0 S 31T 0:00.58 0:01.99 - 5222 0.0 S 31T 0:00.59 0:01.92 - 5222 0.0 S 31T 0:00.58 0:01.89 - 5222 0.0 S 31T 0:00.03 0:00.07 - 5222 0.0 S 31T 0:00.03 0:00.01 - 5222 0.0 S 31T 0:00.43 0:00.16 - 5222 0.0 S 31T 0:00.40 0:00.15 - 5222 0.0 S 31T 0:00.01 0:00.01 - 5222 0.0 S 31T 0:00.01 0:00.01 - 5222 0.0 S 31T 0:00.00 0:00.00 - 5222 0.0 S 31T 0:00.01 0:00.01 -someuser 5252 ?? 0.0 S 47T 0:01.94 0:04.23 /usr/bin/some_command with some parameters - 5252 0.0 S 31T 0:00.30 0:00.22 - 5252 0.0 S 31T 0:00.44 0:00.21 - 5252 0.0 S 0T 0:00.13 0:00.09 - 5252 0.0 S 31T 0:00.37 0:00.17 - 5252 0.0 S 19T 0:00.00 0:00.00 - 5252 0.0 S 31T 0:00.05 0:00.02 - 5252 0.0 S 31T 0:00.01 0:00.00 - 5252 0.0 S 31T 0:00.00 0:00.00 -_fpsd 5347 ?? 0.0 S 31T 0:00.17 0:00.04 /usr/bin/some_command with some parameters -someuser 5407 ?? 0.0 S 31T 0:00.45 0:00.24 /usr/bin/some_command with some parameters - 5407 0.0 S 37T 0:00.00 0:00.00 -nobody 6280 ?? 0.0 S 31T 0:00.00 0:00.02 /usr/bin/some_command with some parameters - 6280 0.0 S 31T 0:00.40 0:00.16 -someuser 6305 ?? 1.3 S 46T 14:38.38 21:54.25 /usr/bin/some_command with some parameters - 6305 0.0 S 31T 0:00.58 0:03.33 - 6305 0.0 S 31T 1:56.75 2:30.84 - 6305 0.0 S 31T 0:00.19 0:05.45 - 6305 0.0 S 31T 0:55.35 9:47.97 - 6305 0.0 S 31T 0:02.03 0:12.22 - 6305 0.2 S 31T 0:08.09 1:37.46 - 6305 0.0 S 31T 0:19.19 0:24.56 - 6305 0.0 S 31T 0:00.54 0:03.37 - 6305 0.0 S 31T 0:00.43 0:00.52 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:00.00 0:00.06 - 6305 0.0 S 31T 1:14.17 18:13.85 - 6305 0.0 S 31T 0:19.65 1:18.76 - 6305 0.0 S 31T 0:03.51 0:09.72 - 6305 0.0 S 31T 0:00.22 0:00.13 - 6305 0.0 S 31T 0:32.14 0:20.07 - 6305 0.0 S 31T 0:00.03 0:00.00 - 6305 0.0 S 31T 0:00.02 0:00.01 - 6305 0.0 S 31T 0:00.10 0:00.08 - 6305 0.0 S 31T 1:56.78 2:30.94 - 6305 0.0 S 31T 1:56.75 2:31.11 - 6305 0.0 S 31T 1:56.49 2:31.40 - 6305 0.0 S 31T 1:56.60 2:30.67 - 6305 0.0 S 31T 1:56.62 2:30.67 - 6305 0.0 S 31T 1:56.76 2:31.07 - 6305 0.0 S 31T 1:56.71 2:30.79 - 6305 0.0 S 31T 1:56.79 2:30.63 - 6305 0.0 S 31T 1:56.53 2:30.88 - 6305 0.0 S 31T 0:00.11 0:04.80 - 6305 0.0 S 31T 0:00.07 0:03.24 - 6305 0.0 S 31T 0:00.05 0:02.38 - 6305 0.0 S 46T 2:56.91 2:01.51 - 6305 3.3 S 31T 12:58.58 72:36.81 - 6305 0.0 S 31T 0:55.46 9:47.97 - 6305 0.0 S 31T 0:55.40 9:47.91 - 6305 0.0 S 31T 0:01.74 0:02.82 - 6305 0.3 S 31T 4:43.67 12:23.04 - 6305 0.0 S 31T 0:01.57 0:03.01 - 6305 0.2 S 31T 1:10.77 2:20.80 - 6305 0.0 S 31T 0:02.30 0:02.43 - 6305 0.0 S 31T 0:03.38 0:02.56 - 6305 0.0 S 31T 0:03.83 0:02.92 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:14.64 0:17.93 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.4 S 31T 2:45.00 6:39.29 - 6305 0.0 S 31T 0:00.03 0:01.13 - 6305 0.0 S 31T 0:00.04 0:01.79 - 6305 0.0 S 31T 0:00.03 0:01.41 - 6305 0.0 S 31T 0:00.02 0:01.08 - 6305 0.0 S 31T 0:00.26 0:02.26 - 6305 0.0 S 31T 0:00.21 0:00.05 - 6305 0.0 S 0T 0:02.77 0:01.00 - 6305 0.0 S 31T 0:00.75 0:00.83 - 6305 0.0 S 31T 0:00.02 0:00.01 - 6305 0.0 S 31T 0:00.24 0:00.09 - 6305 0.0 S 31T 0:00.05 0:00.02 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:00.03 0:00.00 - 6305 0.0 S 0T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:00.07 0:00.03 - 6305 0.0 S 31T 0:01.45 0:01.30 - 6305 0.0 S 31T 0:00.08 0:00.96 - 6305 0.0 S 31T 0:00.02 0:00.83 - 6305 0.0 S 31T 0:00.01 0:00.63 - 6305 0.0 S 31T 0:00.01 0:00.47 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:01.03 0:00.80 - 6305 0.0 S 31T 0:00.88 0:00.72 - 6305 0.0 S 31T 0:01.09 0:00.83 - 6305 0.0 S 31T 0:00.99 0:00.78 - 6305 0.0 S 31T 0:00.01 0:00.32 - 6305 0.0 S 31T 0:00.15 0:00.00 - 6305 0.0 S 31T 0:00.09 0:00.53 - 6305 0.0 S 31T 0:00.22 0:00.13 - 6305 0.0 S 31T 0:00.13 0:00.48 - 6305 0.0 S 31T 0:01.48 0:31.13 - 6305 0.0 S 31T 0:00.19 0:00.00 - 6305 0.0 S 31T 0:00.09 0:00.08 - 6305 0.0 S 31T 0:07.05 0:09.79 - 6305 0.0 S 31T 0:00.48 0:00.30 - 6305 0.0 S 31T 0:00.05 0:00.09 - 6305 0.0 S 31T 0:00.26 0:00.56 - 6305 0.0 S 31T 0:00.05 0:00.23 - 6305 0.0 S 31T 0:00.02 0:00.09 - 6305 0.0 S 31T 0:00.38 0:01.15 - 6305 0.0 S 31T 0:00.01 0:00.04 - 6305 0.0 S 31T 0:00.07 0:00.90 - 6305 0.0 S 31T 0:00.02 0:00.08 - 6305 0.0 S 31T 0:00.00 0:00.10 - 6305 0.0 S 31T 0:00.06 0:00.13 - 6305 0.0 S 31T 0:00.01 0:00.08 - 6305 0.0 S 31T 0:00.00 0:00.01 - 6305 0.0 S 31T 0:00.04 0:00.59 - 6305 0.0 S 31T 0:00.00 0:00.01 - 6305 0.0 S 31T 0:00.19 0:00.27 - 6305 0.0 S 31T 0:00.13 0:05.79 - 6305 0.0 S 31T 0:02.17 0:17.73 - 6305 0.0 S 31T 0:02.58 0:19.51 - 6305 0.0 S 31T 0:01.45 0:15.04 - 6305 0.0 S 31T 0:02.03 0:13.55 - 6305 0.0 S 31T 0:01.58 0:12.37 - 6305 0.0 S 31T 0:01.38 0:15.01 - 6305 0.0 S 31T 0:01.20 0:10.89 - 6305 0.0 S 31T 0:00.01 0:00.06 - 6305 0.0 S 31T 0:01.75 0:15.56 - 6305 0.0 S 31T 0:01.38 0:14.93 - 6305 0.0 S 31T 0:01.38 0:13.37 - 6305 0.0 S 31T 0:01.05 0:12.57 - 6305 0.0 S 31T 0:01.17 0:13.21 - 6305 0.0 S 31T 0:01.30 0:14.13 - 6305 0.0 S 31T 0:00.77 0:10.35 - 6305 0.0 S 31T 0:01.16 0:11.87 - 6305 0.0 S 31T 0:01.37 0:14.07 - 6305 0.0 S 31T 0:00.99 0:11.23 - 6305 0.0 S 31T 0:00.88 0:09.94 - 6305 0.0 S 31T 0:00.96 0:11.20 - 6305 0.0 S 31T 0:01.04 0:13.12 - 6305 0.0 S 31T 0:01.21 0:11.56 - 6305 0.0 S 31T 0:01.20 0:11.02 - 6305 0.0 S 31T 0:01.29 0:15.79 - 6305 0.0 S 31T 0:00.63 0:08.23 - 6305 0.0 S 31T 0:01.32 0:11.65 - 6305 0.0 S 31T 0:01.34 0:11.90 - 6305 0.0 S 31T 0:00.94 0:11.78 - 6305 0.0 S 31T 0:00.01 0:00.01 - 6305 0.0 S 31T 0:00.86 0:09.48 - 6305 0.0 S 31T 0:00.67 0:10.34 - 6305 0.0 S 31T 0:00.85 0:09.31 - 6305 0.0 S 31T 0:00.97 0:11.96 - 6305 0.0 S 31T 0:00.69 0:07.93 - 6305 0.0 S 31T 0:00.66 0:09.77 - 6305 0.0 S 31T 0:00.46 0:07.58 - 6305 0.0 S 31T 0:00.75 0:08.26 - 6305 0.0 S 31T 0:00.72 0:09.74 - 6305 0.0 S 31T 0:00.81 0:11.11 - 6305 0.0 S 31T 0:00.79 0:07.88 - 6305 0.0 S 31T 0:00.44 0:06.56 - 6305 0.0 S 31T 0:00.36 0:05.39 - 6305 0.0 S 31T 0:00.61 0:07.06 - 6305 0.0 S 31T 0:00.87 0:08.89 - 6305 0.0 S 31T 0:00.62 0:08.71 - 6305 0.0 S 31T 0:00.72 0:07.11 - 6305 0.0 S 31T 0:00.55 0:09.69 - 6305 0.0 S 31T 0:00.53 0:08.28 - 6305 0.0 S 31T 0:00.58 0:06.60 - 6305 0.0 S 31T 0:00.59 0:08.23 - 6305 0.0 S 31T 0:00.68 0:07.10 - 6305 0.0 S 31T 0:00.42 0:08.30 - 6305 0.0 S 31T 0:00.69 0:08.53 - 6305 0.0 S 31T 0:00.56 0:06.80 - 6305 0.0 S 31T 0:00.43 0:08.04 - 6305 0.0 S 31T 0:00.43 0:06.37 - 6305 0.0 S 31T 0:00.65 0:08.07 - 6305 0.0 S 31T 0:00.64 0:06.97 - 6305 0.0 S 31T 0:00.59 0:06.51 - 6305 0.0 S 31T 0:00.31 0:05.59 - 6305 0.0 S 31T 0:00.40 0:04.95 - 6305 0.0 S 31T 0:00.52 0:06.14 - 6305 0.0 S 31T 0:00.98 0:06.32 - 6305 0.0 S 31T 0:00.54 0:07.47 - 6305 0.0 S 31T 0:00.37 0:05.68 - 6305 0.0 S 31T 0:00.45 0:08.21 - 6305 0.0 S 31T 0:00.34 0:04.88 - 6305 0.0 S 31T 0:00.52 0:04.65 - 6305 0.0 S 31T 0:00.50 0:05.71 - 6305 0.0 S 31T 0:00.43 0:05.11 - 6305 0.0 S 31T 0:00.20 0:03.59 - 6305 0.0 S 31T 0:00.18 0:01.24 - 6305 0.0 S 31T 0:00.20 0:01.65 - 6305 0.0 S 31T 0:00.16 0:01.14 - 6305 0.0 S 31T 0:00.24 0:02.09 - 6305 0.0 S 31T 0:00.12 0:01.40 - 6305 0.0 S 31T 0:00.11 0:01.56 - 6305 0.0 S 31T 0:00.09 0:01.35 - 6305 0.0 S 31T 0:00.09 0:01.50 - 6305 0.0 S 31T 0:00.07 0:01.12 - 6305 0.0 S 31T 0:00.17 0:02.92 - 6305 0.0 S 31T 0:00.19 0:01.78 - 6305 0.0 S 31T 0:00.04 0:01.05 - 6305 0.0 S 31T 0:00.07 0:01.62 - 6305 0.0 S 31T 0:00.08 0:01.47 - 6305 0.0 S 31T 0:00.56 0:02.55 - 6305 0.0 S 31T 0:00.05 0:00.76 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:00.00 0:00.01 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:00.01 0:00.01 - 6305 0.0 S 31T 0:00.03 0:00.44 - 6305 0.0 S 31T 0:00.12 0:01.07 - 6305 0.0 S 31T 0:00.03 0:00.31 - 6305 0.0 S 31T 0:01.55 0:07.47 - 6305 0.0 S 31T 0:00.95 0:05.26 - 6305 1.2 S 31T 0:00.24 0:01.77 - 6305 0.0 S 31T 0:00.23 0:01.26 - 6305 0.0 S 31T 0:00.17 0:01.56 - 6305 0.0 S 31T 0:00.48 0:03.07 - 6305 0.0 S 31T 0:00.01 0:00.08 - 6305 0.0 S 31T 0:00.26 0:01.80 - 6305 1.5 S 31T 0:00.17 0:01.06 - 6305 0.0 S 31T 0:00.02 0:00.02 - 6305 0.0 S 31T 0:00.02 0:00.14 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 31T 0:00.00 0:00.00 - 6305 0.0 S 37T 0:00.00 0:00.00 - 6305 0.0 S 4T 0:00.00 0:00.00 - 6305 0.0 S 37T 0:00.01 0:00.01 - 6305 0.0 S 46T 0:00.00 0:00.00 -someuser 6351 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 6351 0.0 S 31T 0:24.98 0:11.90 - 6351 0.0 S 31T 0:00.00 0:00.00 -someuser 6365 ?? 0.0 S 31T 0:02.68 0:02.63 /usr/bin/some_command with some parameters - 6365 0.0 S 31T 0:00.61 0:00.45 - 6365 0.0 S 31T 0:00.01 0:00.00 - 6365 0.0 S 31T 0:00.14 0:00.11 - 6365 0.0 S 0T 0:00.12 0:00.10 - 6365 0.0 S 31T 0:00.32 0:00.16 - 6365 0.0 S 31T 0:00.53 0:00.98 - 6365 0.0 S 31T 0:00.00 0:00.00 -someuser 6368 ?? 0.0 S 31T 0:00.03 0:00.04 /usr/bin/some_command with some parameters - 6368 0.0 S 31T 0:00.04 0:00.02 - 6368 0.0 S 0T 0:00.14 0:00.10 - 6368 0.0 S 31T 0:00.73 0:00.28 - 6368 0.0 S 31T 0:00.16 0:00.03 - 6368 0.0 S 31T 0:00.01 0:00.01 - 6368 0.0 S 31T 0:00.00 0:00.00 -someuser 6774 ?? 0.0 S 46T 0:24.25 0:42.34 /usr/bin/some_command with some parameters - 6774 0.0 S 31T 0:04.32 0:04.53 - 6774 0.0 S 46T 0:00.00 0:00.00 - 6774 0.0 S 46T 0:00.00 0:00.00 - 6774 0.0 S 31T 0:00.00 0:00.00 -someuser 6796 ?? 0.0 S 46T 0:04.38 0:01.51 /usr/bin/some_command with some parameters - 6796 0.0 S 46T 0:00.47 0:00.09 - 6796 0.0 S 31T 0:00.00 0:00.00 - 6796 0.0 S 37T 0:00.00 0:00.00 -someuser 6947 ?? 0.0 S 31T 0:27.15 0:24.09 tmux -someuser 7649 ?? 0.0 S 46T 0:04.70 0:02.03 /usr/bin/some_command with some parameters - 7649 0.0 S 46T 0:00.43 0:00.09 - 7649 0.0 S 19T 0:00.00 0:00.00 - 7649 0.0 S 31T 0:00.00 0:00.00 - 7649 0.0 S 37T 0:00.00 0:00.00 -someuser 7651 ?? 0.0 S 46T 0:03.32 0:01.25 /usr/bin/some_command with some parameters - 7651 0.0 S 46T 0:00.49 0:00.09 - 7651 0.0 S 37T 0:00.00 0:00.00 - 7651 0.0 S 37T 0:00.00 0:00.00 -someuser 7961 ?? 0.0 S 20T 0:00.59 0:02.56 /usr/bin/some_command with some parameters -someuser 9260 ?? 0.0 S 42T 0:03.22 0:04.05 /usr/bin/some_command with some parameters - 9260 0.0 S 31T 0:42.22 0:05.66 - 9260 0.0 S 31T 0:01.14 0:23.88 - 9260 0.0 S 31T 0:00.12 0:00.14 - 9260 0.0 S 31T 0:00.01 0:00.00 - 9260 0.0 S 31T 12:00.97 1:03.81 - 9260 0.0 S 31T 0:00.00 0:00.00 - 9260 0.0 S 31T 0:05.92 0:03.89 - 9260 0.0 S 31T 0:00.00 0:00.00 - 9260 0.0 S 31T 0:02.90 0:01.01 - 9260 0.0 S 31T 0:09.95 0:03.94 - 9260 0.0 S 31T 0:00.00 0:00.00 - 9260 0.0 S 31T 0:00.00 0:00.00 - 9260 0.0 S 31T 0:00.62 0:00.40 - 9260 0.0 S 31T 0:01.02 0:01.37 - 9260 0.0 S 31T 0:08.48 0:02.01 - 9260 0.0 S 31T 0:00.00 0:00.00 - 9260 0.0 S 31T 0:01.28 0:00.30 - 9260 0.0 S 31T 0:01.11 0:01.11 - 9260 0.0 S 31T 0:00.01 0:00.01 - 9260 0.0 S 31T 0:00.03 0:00.01 - 9260 0.0 S 31T 0:09.80 0:02.43 - 9260 0.0 S 31T 0:00.27 0:00.06 - 9260 0.0 S 31T 0:00.00 0:00.00 - 9260 0.0 S 19T 0:00.00 0:00.00 - 9260 0.0 S 31T 0:01.46 0:34.31 - 9260 0.0 S 31T 0:12.38 0:14.26 - 9260 0.0 S 31T 0:12.50 0:14.27 - 9260 0.0 S 31T 0:12.40 0:14.23 - 9260 0.0 S 20T 0:01.37 0:00.59 - 9260 0.0 S 20T 0:01.22 0:00.53 - 9260 0.0 S 20T 0:00.07 0:00.03 - 9260 0.0 S 61T 0:00.00 0:00.00 - 9260 0.0 S 31T 0:00.00 0:00.00 -someuser 12403 ?? 0.0 S 31T 0:04.96 0:02.66 /usr/bin/some_command with some parameters - 12403 0.0 S 37T 0:00.65 0:00.15 - 12403 0.0 S 37T 0:00.00 0:00.00 -someuser 13175 ?? 0.0 S 46T 0:10.60 0:14.64 /usr/bin/some_command with some parameters - 13175 0.0 S 4T 0:00.01 0:00.01 - 13175 0.0 S 31T 0:00.06 0:00.07 - 13175 0.0 S 31T 0:00.00 0:00.00 - 13175 0.0 S 31T 0:00.00 0:00.00 - 13175 0.0 S 46T 0:01.09 0:00.50 - 13175 0.0 S 31T 0:00.05 0:00.03 - 13175 0.0 S 4T 0:00.06 0:00.05 - 13175 0.0 S 37T 0:00.00 0:00.00 - 13175 0.0 S 37T 0:00.00 0:00.00 -someuser 13178 ?? 0.0 S 31T 0:15.15 0:20.39 /usr/bin/some_command with some parameters - 13178 0.0 S 31T 0:44.89 0:26.59 - 13178 0.0 S 31T 0:00.00 0:00.00 - 13178 0.0 S 31T 0:15.04 0:20.25 - 13178 0.0 S 31T 0:15.16 0:20.54 - 13178 0.0 S 31T 0:00.00 0:00.00 - 13178 0.0 S 31T 0:14.73 0:19.94 - 13178 0.0 S 31T 0:00.00 0:00.00 - 13178 0.0 S 31T 0:00.00 0:00.00 - 13178 0.0 S 31T 0:15.60 0:21.09 - 13178 0.0 S 31T 0:00.00 0:00.00 - 13178 0.0 S 31T 0:14.89 0:20.23 - 13178 0.0 S 31T 0:12.79 0:17.16 - 13178 0.0 S 31T 0:15.57 0:20.91 - 13178 0.0 S 31T 0:15.23 0:20.53 - 13178 0.0 S 31T 0:12.72 0:17.05 - 13178 0.0 S 31T 0:14.85 0:20.27 - 13178 0.0 S 31T 0:15.46 0:20.75 - 13178 0.0 S 31T 0:08.81 0:11.94 - 13178 0.0 S 20T 0:00.04 0:00.00 - 13178 0.3 S 31T 0:14.74 0:19.94 - 13178 0.2 S 31T 0:11.98 0:16.27 -someuser 13179 ?? 0.0 S 31T 0:00.02 0:00.01 /usr/bin/some_command with some parameters - 13179 0.0 S 31T 0:00.25 0:00.12 - 13179 0.0 S 31T 0:00.00 0:00.00 - 13179 0.0 S 31T 0:00.29 0:00.27 - 13179 0.0 S 31T 0:00.00 0:00.00 - 13179 0.0 S 31T 0:00.33 0:00.29 - 13179 0.0 S 31T 0:00.00 0:00.00 - 13179 0.0 S 31T 0:00.30 0:00.29 - 13179 0.0 S 31T 0:00.24 0:00.21 - 13179 0.0 S 31T 0:00.32 0:00.26 - 13179 0.0 S 31T 0:00.29 0:00.27 - 13179 0.0 S 31T 0:00.31 0:00.27 - 13179 0.0 S 31T 0:00.29 0:00.28 - 13179 0.0 S 31T 0:00.22 0:00.24 - 13179 0.0 S 31T 0:00.29 0:00.27 - 13179 0.0 S 31T 0:00.29 0:00.26 - 13179 0.0 S 31T 0:00.25 0:00.24 - 13179 0.0 S 31T 0:00.27 0:00.24 - 13179 0.0 S 31T 0:00.21 0:00.16 -someuser 13201 ?? 0.0 S 31T 0:00.42 0:00.33 /usr/bin/some_command with some parameters - 13201 0.0 S 31T 0:00.35 0:00.18 - 13201 0.0 S 31T 0:00.00 0:00.00 - 13201 0.0 S 31T 0:00.07 0:00.01 - 13201 0.0 S 31T 0:00.01 0:00.00 - 13201 0.0 S 31T 0:00.00 0:00.00 - 13201 0.0 S 31T 0:00.00 0:00.00 - 13201 0.0 S 31T 0:00.00 0:00.00 - 13201 0.0 S 31T 0:00.00 0:00.00 - 13201 0.0 S 31T 0:00.00 0:00.00 - 13201 0.0 S 31T 0:00.00 0:00.00 - 13201 0.0 S 31T 0:00.34 0:00.30 - 13201 0.0 S 31T 0:00.00 0:00.00 - 13201 0.0 S 31T 0:00.38 0:00.34 - 13201 0.0 S 31T 0:00.40 0:00.32 - 13201 0.0 S 31T 0:00.36 0:00.30 - 13201 0.0 S 31T 0:00.39 0:00.34 - 13201 0.0 S 31T 0:00.36 0:00.31 - 13201 0.0 S 31T 0:00.37 0:00.32 - 13201 0.0 S 31T 0:00.39 0:00.32 - 13201 0.0 S 31T 0:00.44 0:00.35 - 13201 0.0 S 31T 0:00.36 0:00.32 - 13201 0.0 S 31T 0:00.32 0:00.31 - 13201 0.0 S 31T 0:00.41 0:00.34 - 13201 0.0 S 31T 0:00.38 0:00.31 -someuser 13207 ?? 0.0 S 31T 0:12.54 0:15.84 com.docker.vpnkit --ethernet fd:3 --diagnostics fd:4 --pcap fd:5 --vsock-path vms/0/connect --host-names host.docker.internal,docker.for.mac.host.internal,docker.for.mac.localhost --listen-backlog 32 --mtu 1500 --allowed-bind-addresses 0.0.0.0 --http /usr/bin/some_command with some parameters - 13207 0.0 S 31T 0:03.22 0:00.28 - 13207 0.0 S 31T 0:03.23 0:00.28 - 13207 0.0 S 31T 0:03.22 0:00.28 - 13207 0.0 S 31T 0:03.22 0:00.28 -someuser 13208 ?? 0.0 S 31T 0:00.05 0:00.02 docker serve --address unix:///Users/someuser/.docker/run/docker-cli-api.sock - 13208 0.0 S 31T 0:01.57 0:00.98 - 13208 0.0 S 31T 0:00.81 0:00.91 - 13208 0.0 S 31T 0:00.86 0:00.97 - 13208 0.0 S 31T 0:00.76 0:00.85 - 13208 0.0 S 31T 0:00.79 0:00.92 - 13208 0.0 S 31T 0:00.76 0:00.86 - 13208 0.0 S 31T 0:00.81 0:00.94 - 13208 0.0 S 31T 0:00.75 0:00.87 - 13208 0.0 S 31T 0:00.80 0:00.92 - 13208 0.0 S 31T 0:00.75 0:00.89 - 13208 0.0 S 31T 0:00.00 0:00.00 - 13208 0.0 S 31T 0:00.72 0:00.90 - 13208 0.0 S 31T 0:00.00 0:00.00 - 13208 0.0 S 31T 0:00.84 0:00.91 - 13208 0.0 S 31T 0:00.73 0:00.79 - 13208 0.0 S 31T 0:00.87 0:00.96 - 13208 0.0 S 31T 0:00.59 0:00.63 -someuser 13209 ?? 0.0 S 31T 0:21.43 0:12.21 vpnkit-bridge --disable wsl2-cross-distro-service,wsl2-bootstrap-expose-ports,transfused --addr listen://1999 host - 13209 0.0 S 31T 1:09.44 0:39.38 - 13209 0.0 S 31T 0:19.72 0:11.04 - 13209 0.1 S 31T 0:20.30 0:11.48 - 13209 0.0 S 31T 0:20.49 0:11.54 - 13209 0.0 S 31T 0:19.68 0:11.18 - 13209 0.0 S 31T 0:00.00 0:00.00 - 13209 0.0 S 31T 0:18.70 0:10.39 - 13209 0.0 S 31T 0:20.89 0:11.77 - 13209 0.0 S 31T 0:19.80 0:11.19 - 13209 0.0 S 31T 0:19.19 0:10.81 - 13209 0.0 S 31T 0:15.30 0:08.80 - 13209 0.0 S 31T 0:21.64 0:12.16 - 13209 0.0 S 31T 0:18.95 0:10.76 - 13209 0.0 S 31T 0:20.61 0:11.57 - 13209 0.0 S 31T 0:14.18 0:07.93 -someuser 13210 ?? 0.0 S 31T 0:02.99 0:02.18 com.docker.driver.amd64-linux -addr fd:3 -debug -native-api - 13210 0.0 S 31T 0:08.70 0:04.96 - 13210 0.0 S 31T 0:03.44 0:02.47 - 13210 0.0 S 31T 0:02.62 0:01.93 - 13210 0.0 S 31T 0:02.40 0:01.88 - 13210 0.0 S 31T 0:00.00 0:00.00 - 13210 0.0 S 31T 0:02.96 0:02.22 - 13210 0.0 S 31T 0:02.72 0:02.04 - 13210 0.0 S 31T 0:02.96 0:02.15 - 13210 0.0 S 31T 0:03.01 0:02.23 - 13210 0.0 S 31T 0:00.00 0:00.00 - 13210 0.0 S 31T 0:00.00 0:00.00 - 13210 0.0 S 31T 0:03.18 0:02.38 - 13210 0.0 S 31T 0:02.95 0:02.21 - 13210 0.0 S 31T 0:03.37 0:02.49 - 13210 0.0 S 31T 0:00.00 0:00.00 - 13210 0.0 S 31T 0:02.96 0:02.23 - 13210 0.0 S 31T 0:02.66 0:02.06 - 13210 0.0 S 31T 0:02.78 0:02.15 -someuser 13219 ?? 0.0 S 31T 0:00.03 0:00.01 com.docker.hyperkit -A -u -F vms/0/hyperkit.pid -c 6 -m 2048M -s 0:0,hostbridge -s 31,lpc -s 1:0,virtio-vpnkit,path=vpnkit.eth.sock,uuid=254b47b9-08d1-4825-812d-21a5c072a954 -U e5194447-4cb5-4962-9f8a-6926b08ac2b9 -s 2:0,virtio-blk,/Users/someuser/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw -s 3,virtio-sock,guest_cid=3,path=vms/0,guest_forwards=2376;1525 -s 4,virtio-rnd -l com1,null,asl,log=vms/0/console-ring -f kexec,/Applications/Docker.app/Contents/Resources/linuxkit/kernel,/Applications/Docker.app/Contents/Resources/linuxkit/initrd.img,earlyprintk=serial page_poison=1 vsyscall=emulate panic=1 nospec_store_bypass_disable noibrs noibpb no_stf_barrier mitigations=off console=ttyS0 console=ttyS1 vpnkit.connect=connect://2/1999 - 13219 0.0 S 31T 0:00.24 0:00.08 - 13219 0.0 S 31T 0:47.37 0:14.79 - 13219 0.6 S 31T 18:03.18 5:13.88 - 13219 0.0 S 31T 0:01.02 0:00.21 - 13219 0.0 S 31T 0:03.91 0:01.07 - 13219 0.0 S 31T 0:04.41 0:00.07 - 13219 0.0 S 31T 1:26.07 0:15.54 - 13219 0.0 S 31T 1:22.96 0:18.63 - 13219 1.3 S 31T 40:46.89 8:59.16 - 13219 0.6 S 31T 42:32.76 9:23.27 - 13219 0.9 S 31T 41:26.00 9:08.48 - 13219 3.9 S 31T 40:25.64 9:00.49 - 13219 1.4 S 31T 37:32.72 8:13.76 - 13219 1.5 S 31T 49:41.46 11:22.61 - 13219 0.0 S 31T 0:00.00 0:00.00 -someuser 13565 ?? 0.0 S 47T 0:01.95 0:03.59 /usr/bin/some_command with some parameters - 13565 0.0 S 31T 0:00.29 0:00.20 - 13565 0.0 S 31T 0:00.44 0:00.19 - 13565 0.0 S 0T 0:00.12 0:00.10 - 13565 0.0 S 31T 0:00.14 0:00.06 - 13565 0.0 S 19T 0:00.00 0:00.00 - 13565 0.0 S 61T 0:00.00 0:00.00 -someuser 15552 ?? 0.0 S 31T 0:06.78 0:11.04 /usr/bin/some_command with some parameters - 15552 0.0 S 31T 0:00.00 0:00.00 - 15552 0.0 S 0T 0:00.01 0:00.01 - 15552 0.0 S 31T 0:00.65 0:00.39 - 15552 0.0 S 31T 0:00.01 0:00.01 - 15552 0.0 S 31T 0:00.06 0:00.26 - 15552 0.0 S 31T 0:00.00 0:00.00 - 15552 0.0 S 31T 0:00.01 0:00.28 - 15552 0.0 S 31T 0:00.01 0:00.32 - 15552 0.0 S 31T 0:00.01 0:00.30 - 15552 0.0 S 31T 0:00.01 0:00.27 - 15552 0.0 S 31T 0:00.00 0:00.00 - 15552 0.0 S 31T 0:00.05 0:00.31 - 15552 0.0 S 31T 0:00.11 0:00.02 - 15552 0.0 S 31T 0:00.05 0:00.02 - 15552 0.0 S 31T 0:00.10 0:00.03 -someuser 20135 ?? 0.0 S 31T 0:06.37 0:04.95 /usr/bin/some_command with some parameters - 20135 0.0 S 31T 0:00.01 0:00.00 - 20135 0.0 S 0T 0:00.13 0:00.09 - 20135 0.0 S 31T 0:01.40 0:00.74 - 20135 0.0 S 31T 0:00.00 0:00.00 - 20135 0.0 S 31T 0:00.22 0:00.17 - 20135 0.0 S 31T 0:00.00 0:00.00 - 20135 0.0 S 31T 0:00.00 0:00.00 - 20135 0.0 S 31T 0:00.01 0:00.00 - 20135 0.0 S 31T 0:00.01 0:00.00 - 20135 0.0 S 31T 0:00.01 0:00.00 - 20135 0.0 S 31T 0:00.00 0:00.00 - 20135 0.0 S 31T 0:00.44 0:00.13 - 20135 0.0 S 31T 0:00.03 0:00.01 - 20135 0.0 S 31T 0:00.01 0:00.00 -someuser 22878 ?? 0.0 S 31T 0:03.45 0:01.37 /usr/bin/some_command with some parameters - 22878 0.0 S 37T 0:00.47 0:00.09 - 22878 0.0 S 37T 0:00.00 0:00.00 -root 23677 ?? 0.0 S 4T 0:00.03 0:00.03 /usr/bin/some_command with some parameters - 23677 0.0 S 4T 0:00.00 0:00.00 -someuser 25255 ?? 0.1 S 42T 6:20.26 0:41.43 /usr/bin/some_command with some parameters -someuser 25257 ?? 0.0 S 42T 0:33.20 0:39.82 /usr/bin/some_command with some parameters - 25257 0.0 S 31T 3:27.77 0:49.18 - 25257 0.0 S 31T 0:05.84 0:06.98 - 25257 0.0 S 31T 0:00.00 0:00.00 - 25257 0.0 S 31T 0:00.00 0:00.00 - 25257 0.0 S 31T 0:04.04 0:04.28 - 25257 0.2 S 31T 5:43.56 4:03.97 - 25257 0.0 S 31T 0:00.07 0:00.03 - 25257 0.0 S 31T 0:00.12 0:00.09 - 25257 0.0 S 31T 0:14.99 0:08.07 - 25257 0.0 S 31T 0:00.51 0:01.78 - 25257 0.0 S 31T 0:05.98 0:07.04 - 25257 0.0 S 31T 0:06.15 0:06.98 - 25257 0.0 S 31T 0:05.36 0:06.37 - 25257 0.0 S 31T 0:05.34 0:06.34 - 25257 0.0 S 19T 0:00.00 0:00.00 - 25257 0.0 S 31T 0:05.13 0:06.33 - 25257 0.0 S 31T 0:03.31 0:04.24 - 25257 0.0 S 31T 0:03.52 0:04.25 - 25257 0.0 S 61T 0:00.00 0:00.00 - 25257 0.0 S 20T 0:00.00 0:00.00 -someuser 25320 ?? 0.0 S 31T 0:01.42 0:00.23 /usr/bin/some_command with some parameters - 25320 0.0 S 31T 0:03.79 0:03.03 -root 27923 ?? 0.0 S 31T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 27923 0.0 S 4T 0:00.00 0:00.00 -someuser 29226 ?? 0.0 S 4T 0:00.37 0:00.03 /usr/bin/some_command with some parameters - 29226 0.0 S 4T 0:00.01 0:00.00 - 29226 0.0 S 4T 0:00.00 0:00.00 -someuser 29631 ?? 0.0 S 31T 0:21.95 0:26.43 /usr/bin/some_command with some parameters - 29631 0.0 S 31T 0:00.00 0:00.00 - 29631 0.0 S 0T 0:00.08 0:00.04 - 29631 0.0 S 31T 0:01.79 0:01.89 - 29631 0.0 S 31T 0:00.01 0:00.01 - 29631 0.0 S 31T 0:00.30 0:01.31 - 29631 0.0 S 31T 0:00.00 0:00.00 - 29631 0.0 S 31T 0:00.06 0:00.18 - 29631 0.0 S 31T 0:00.03 0:00.22 - 29631 0.0 S 31T 0:00.02 0:00.16 - 29631 0.0 S 31T 0:00.05 0:00.18 - 29631 0.0 S 31T 0:00.01 0:00.04 - 29631 0.0 S 31T 0:00.41 0:00.08 - 29631 0.0 S 31T 0:00.05 0:00.01 - 29631 0.0 S 31T 0:00.06 0:00.03 -someuser 29686 ?? 0.0 S 31T 0:37.11 1:54.63 /usr/bin/some_command with some parameters - 29686 0.0 S 31T 0:00.00 0:00.00 - 29686 0.0 S 0T 0:00.06 0:00.04 - 29686 0.0 S 31T 0:03.42 0:03.91 - 29686 0.0 S 31T 0:00.01 0:00.02 - 29686 0.0 S 31T 0:00.59 0:04.69 - 29686 0.0 S 31T 0:00.00 0:00.00 - 29686 0.0 S 31T 0:00.02 0:00.08 - 29686 0.0 S 31T 0:00.02 0:00.08 - 29686 0.0 S 31T 0:00.02 0:00.08 - 29686 0.0 S 31T 0:00.03 0:00.08 - 29686 0.0 S 31T 0:00.00 0:00.00 - 29686 0.0 S 31T 0:00.22 0:00.07 - 29686 0.0 S 31T 0:00.18 0:00.07 - 29686 0.0 S 31T 0:00.00 0:00.00 -someuser 29894 ?? 0.0 S 31T 0:03.26 0:07.72 /usr/bin/some_command with some parameters - 29894 0.0 S 31T 0:00.00 0:00.00 - 29894 0.0 S 0T 0:00.04 0:00.03 - 29894 0.0 S 31T 0:00.43 0:00.21 - 29894 0.0 S 31T 0:00.00 0:00.00 - 29894 0.0 S 31T 0:00.10 0:00.46 - 29894 0.0 S 31T 0:00.00 0:00.00 - 29894 0.0 S 31T 0:00.01 0:00.06 - 29894 0.0 S 31T 0:00.01 0:00.07 - 29894 0.0 S 31T 0:00.02 0:00.12 - 29894 0.0 S 31T 0:00.01 0:00.07 - 29894 0.0 S 31T 0:00.00 0:00.00 - 29894 0.0 S 31T 0:00.21 0:00.04 - 29894 0.0 S 31T 0:00.09 0:00.05 - 29894 0.0 S 31T 0:00.02 0:00.01 -someuser 31499 ?? 0.0 S 31T 0:24.21 0:26.45 /usr/bin/some_command with some parameters - 31499 0.0 S 31T 0:00.00 0:00.00 - 31499 0.0 S 0T 0:00.05 0:00.04 - 31499 0.0 S 31T 0:01.51 0:01.19 - 31499 0.0 S 31T 0:00.01 0:00.01 - 31499 0.0 S 31T 0:00.22 0:00.86 - 31499 0.0 S 31T 0:00.00 0:00.00 - 31499 0.0 S 31T 0:00.02 0:00.24 - 31499 0.0 S 31T 0:00.06 0:00.30 - 31499 0.0 S 31T 0:00.02 0:00.24 - 31499 0.0 S 31T 0:00.04 0:00.39 - 31499 0.0 S 31T 0:00.00 0:00.00 - 31499 0.0 S 31T 0:00.44 0:00.08 - 31499 0.0 S 31T 0:00.07 0:00.02 - 31499 0.0 S 31T 0:00.07 0:00.04 -someuser 31632 ?? 0.0 S 31T 0:33.04 3:00.11 /usr/bin/some_command with some parameters - 31632 0.0 S 31T 0:00.00 0:00.00 - 31632 0.0 S 0T 0:00.07 0:00.04 - 31632 0.0 S 31T 0:07.05 0:08.22 - 31632 0.0 S 31T 0:00.01 0:00.01 - 31632 0.0 S 31T 0:07.97 0:34.81 - 31632 0.0 S 31T 0:00.00 0:00.00 - 31632 0.0 S 31T 0:00.38 0:05.44 - 31632 0.0 S 31T 0:00.40 0:05.95 - 31632 0.0 S 31T 0:00.39 0:05.04 - 31632 0.0 S 31T 0:00.42 0:05.37 - 31632 0.0 S 31T 0:00.00 0:00.00 - 31632 0.0 S 31T 0:00.44 0:00.07 - 31632 0.0 S 31T 0:00.12 0:00.04 - 31632 0.0 S 31T 0:00.07 0:00.06 - 31632 0.0 S 31T 0:00.00 0:00.00 -someuser 32179 ?? 0.0 S 4T 0:11.92 0:31.08 /usr/bin/some_command with some parameters - 32179 0.0 S 4T 0:01.70 0:00.46 - 32179 0.0 S 4T 0:00.00 0:00.00 -someuser 32424 ?? 0.0 S 31T 0:01.39 0:02.07 /usr/bin/some_command with some parameters - 32424 0.0 S 31T 0:00.00 0:00.00 - 32424 0.0 S 0T 0:00.04 0:00.03 - 32424 0.0 S 31T 0:00.30 0:00.14 - 32424 0.0 S 31T 0:00.00 0:00.00 - 32424 0.0 S 31T 0:00.02 0:00.08 - 32424 0.0 S 31T 0:00.00 0:00.00 - 32424 0.0 S 31T 0:00.00 0:00.00 - 32424 0.0 S 31T 0:00.00 0:00.01 - 32424 0.0 S 31T 0:00.00 0:00.01 - 32424 0.0 S 31T 0:00.00 0:00.01 - 32424 0.0 S 31T 0:00.00 0:00.00 - 32424 0.0 S 31T 0:00.16 0:00.04 - 32424 0.0 S 31T 0:00.05 0:00.04 - 32424 0.0 S 31T 0:00.01 0:00.00 -someuser 33878 ?? 0.0 S 4T 0:25.60 0:38.00 /usr/bin/some_command with some parameters - 33878 0.0 S 4T 1:25.59 0:20.82 - 33878 0.0 S 4T 0:00.97 0:00.64 - 33878 0.0 S 3T 0:00.22 0:00.19 - 33878 0.0 S 4T 0:09.85 0:10.18 - 33878 0.0 S 4T 0:00.69 0:01.10 - 33878 0.0 S 4T 0:00.65 0:00.32 - 33878 0.0 S 4T 0:00.58 0:00.42 - 33878 0.0 S 4T 0:00.00 0:00.00 - 33878 0.0 S 4T 0:00.00 0:00.00 -someuser 33945 ?? 0.0 S 42T 0:02.18 0:02.60 /usr/bin/some_command with some parameters - 33945 0.0 S 31T 0:21.66 0:03.84 - 33945 0.0 S 31T 0:00.88 0:12.15 - 33945 0.0 S 31T 0:00.23 0:00.19 - 33945 0.0 S 31T 0:00.01 0:00.00 - 33945 0.0 S 31T 17:48.57 0:24.29 - 33945 0.0 S 31T 30:13.52 2:32.90 - 33945 0.0 S 31T 0:00.00 0:00.00 - 33945 0.0 S 31T 0:08.96 0:05.76 - 33945 0.0 S 31T 0:00.00 0:00.00 - 33945 0.0 S 31T 0:04.72 0:01.54 - 33945 0.0 S 31T 0:16.20 0:06.16 - 33945 0.0 S 31T 0:01.72 0:00.32 - 33945 0.0 S 31T 0:00.00 0:00.00 - 33945 0.0 S 31T 0:01.13 0:00.61 - 33945 0.0 S 31T 0:01.56 0:02.05 - 33945 0.0 S 31T 0:19.88 0:15.23 - 33945 0.0 S 31T 0:09.60 0:09.37 - 33945 0.0 S 31T 0:00.00 0:00.00 - 33945 0.0 S 31T 0:00.00 0:00.00 - 33945 0.0 S 31T 0:03.13 0:01.01 - 33945 0.0 S 31T 0:02.45 0:01.37 - 33945 0.0 S 31T 0:00.00 0:00.00 - 33945 0.0 S 31T 0:00.00 0:00.00 - 33945 0.0 S 31T 0:13.29 0:03.91 - 33945 0.0 S 31T 0:00.47 0:00.10 - 33945 0.0 S 31T 0:00.00 0:00.00 - 33945 0.0 S 19T 0:00.00 0:00.00 - 33945 0.0 S 31T 0:00.14 0:00.07 - 33945 0.0 S 31T 0:00.23 0:02.59 - 33945 0.0 S 31T 0:00.00 0:00.00 -someuser 37665 ?? 0.0 S 46T 0:09.04 0:10.03 /usr/bin/some_command with some parameters - 37665 0.0 S 46T 0:00.13 0:00.07 - 37665 0.0 S 37T 0:00.00 0:00.00 - 37665 0.0 S 31T 0:00.00 0:00.00 - 37665 0.0 S 37T 0:00.00 0:00.00 -someuser 37728 ?? 0.0 S 31T 0:00.97 0:01.21 /usr/bin/some_command with some parameters - 37728 0.0 S 31T 0:00.00 0:00.00 - 37728 0.0 S 0T 0:00.03 0:00.02 - 37728 0.0 S 31T 0:00.42 0:00.39 - 37728 0.0 S 31T 0:00.00 0:00.00 - 37728 0.0 S 31T 0:00.00 0:00.00 - 37728 0.0 S 31T 0:00.00 0:00.00 - 37728 0.0 S 31T 0:00.00 0:00.00 - 37728 0.0 S 31T 0:00.00 0:00.00 - 37728 0.0 S 31T 0:00.00 0:00.00 - 37728 0.0 S 31T 0:00.00 0:00.00 - 37728 0.0 S 31T 0:00.00 0:00.00 - 37728 0.0 S 31T 0:00.09 0:00.03 - 37728 0.0 S 31T 0:00.04 0:00.02 - 37728 0.0 S 31T 0:00.00 0:00.00 -someuser 38532 ?? 0.0 S 4T 0:00.07 0:00.05 /usr/bin/some_command with some parameters - 38532 0.0 S 4T 0:00.03 0:00.01 -root 38747 ?? 0.0 S 4T 0:00.06 0:00.06 /usr/bin/some_command with some parameters - 38747 0.0 S 4T 0:00.00 0:00.00 -someuser 40037 ?? 0.0 S 4T 0:09.30 0:08.43 /usr/bin/some_command with some parameters - 40037 0.0 S 4T 0:00.00 0:00.00 - 40037 0.0 S 4T 0:01.34 0:00.54 - 40037 0.0 S 4T 0:00.01 0:00.00 - 40037 0.0 S 4T 0:00.07 0:00.02 - 40037 0.0 S 4T 0:00.00 0:00.00 - 40037 0.0 S 4T 0:00.00 0:00.00 -someuser 40686 ?? 0.0 S 31T 0:03.71 0:02.99 /usr/bin/some_command with some parameters - 40686 0.0 S 31T 0:00.00 0:00.00 - 40686 0.0 S 0T 0:00.03 0:00.02 - 40686 0.0 S 31T 0:00.70 0:00.36 - 40686 0.0 S 31T 0:00.00 0:00.00 - 40686 0.0 S 31T 0:00.03 0:00.09 - 40686 0.0 S 31T 0:00.10 0:00.02 - 40686 0.0 S 31T 0:00.00 0:00.01 - 40686 0.0 S 31T 0:00.00 0:00.00 - 40686 0.0 S 31T 0:00.00 0:00.00 - 40686 0.0 S 31T 0:00.00 0:00.00 - 40686 0.0 S 31T 0:00.00 0:00.01 - 40686 0.0 S 31T 0:00.12 0:00.03 - 40686 0.0 S 31T 0:00.02 0:00.01 - 40686 0.0 S 31T 0:00.01 0:00.01 - 40686 0.0 S 31T 0:00.03 0:00.01 -someuser 40698 ?? 0.0 S 31T 0:03.41 0:05.44 /usr/bin/some_command with some parameters - 40698 0.0 S 31T 0:00.00 0:00.00 - 40698 0.0 S 0T 0:00.03 0:00.02 - 40698 0.0 S 31T 0:00.37 0:00.17 - 40698 0.0 S 31T 0:00.00 0:00.00 - 40698 0.0 S 31T 0:00.05 0:00.17 - 40698 0.0 S 31T 0:00.00 0:00.00 - 40698 0.0 S 31T 0:00.00 0:00.01 - 40698 0.0 S 31T 0:00.00 0:00.01 - 40698 0.0 S 31T 0:00.00 0:00.01 - 40698 0.0 S 31T 0:00.00 0:00.02 - 40698 0.0 S 31T 0:00.00 0:00.00 - 40698 0.0 S 31T 0:00.00 0:00.00 - 40698 0.0 S 31T 0:00.24 0:00.04 - 40698 0.0 S 31T 0:00.02 0:00.00 - 40698 0.0 S 31T 0:00.01 0:00.00 -someuser 40707 ?? 0.0 S 31T 0:01.40 0:00.84 /usr/bin/some_command with some parameters - 40707 0.0 S 31T 0:00.00 0:00.00 - 40707 0.0 S 0T 0:00.04 0:00.02 - 40707 0.0 S 31T 0:00.28 0:00.12 - 40707 0.0 S 31T 0:00.00 0:00.00 - 40707 0.0 S 31T 0:00.03 0:00.05 - 40707 0.0 S 31T 0:00.00 0:00.00 - 40707 0.0 S 31T 0:00.00 0:00.00 - 40707 0.0 S 31T 0:00.00 0:00.00 - 40707 0.0 S 31T 0:00.00 0:00.01 - 40707 0.0 S 31T 0:00.00 0:00.00 - 40707 0.0 S 31T 0:00.00 0:00.00 - 40707 0.0 S 31T 0:00.10 0:00.03 - 40707 0.0 S 31T 0:00.02 0:00.00 - 40707 0.0 S 31T 0:00.01 0:00.01 -someuser 41159 ?? 0.0 S 31T 0:00.78 0:00.57 /usr/bin/some_command with some parameters - 41159 0.0 S 31T 0:00.00 0:00.00 - 41159 0.0 S 0T 0:00.03 0:00.02 - 41159 0.0 S 31T 0:00.21 0:00.09 - 41159 0.0 S 31T 0:00.00 0:00.00 - 41159 0.0 S 31T 0:00.02 0:00.07 - 41159 0.0 S 31T 0:00.00 0:00.00 - 41159 0.0 S 31T 0:00.00 0:00.01 - 41159 0.0 S 31T 0:00.00 0:00.00 - 41159 0.0 S 31T 0:00.00 0:00.00 - 41159 0.0 S 31T 0:00.00 0:00.00 - 41159 0.0 S 31T 0:00.00 0:00.00 - 41159 0.0 S 31T 0:00.11 0:00.03 - 41159 0.0 S 31T 0:00.04 0:00.03 - 41159 0.0 S 31T 0:00.01 0:00.00 -root 41458 ?? 0.0 S 31T 0:00.24 0:00.05 /usr/bin/some_command with some parameters - 41458 0.0 S 4T 0:00.00 0:00.00 -root 41491 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 41491 0.0 S 4T 0:00.00 0:00.00 -someuser 41501 ?? 0.0 S 4T 0:00.04 0:00.04 /usr/bin/some_command with some parameters - 41501 0.0 S 4T 0:00.02 0:00.01 -someuser 41507 ?? 0.0 S 4T 0:00.61 0:01.35 /usr/bin/some_command with some parameters - 41507 0.0 S 4T 0:00.05 0:00.04 - 41507 0.0 S 4T 0:00.04 0:00.04 - 41507 0.0 S 4T 0:00.02 0:00.02 - 41507 0.0 S 4T 0:00.01 0:00.01 - 41507 0.0 S 4T 0:00.00 0:00.00 -root 41513 ?? 0.0 S 4T 0:00.02 0:00.02 /usr/bin/some_command with some parameters - 41513 0.0 S 4T 0:00.00 0:00.00 -root 41520 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 41520 0.0 S 4T 0:00.00 0:00.00 -someuser 41747 ?? 0.0 S 4T 0:00.34 0:00.10 /usr/bin/some_command with some parameters - 41747 0.0 S 4T 0:00.00 0:00.00 - 41747 0.0 S 4T 0:00.00 0:00.00 -root 41837 ?? 0.0 S 4T 0:00.24 0:00.19 /usr/bin/some_command with some parameters - 41837 0.0 S 4T 0:00.00 0:00.00 - 41837 0.0 S 4T 0:00.01 0:00.00 -root 41852 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 41852 0.0 S 4T 0:00.00 0:00.00 -root 41855 ?? 0.0 S 4T 0:00.02 0:00.05 /usr/bin/some_command with some parameters - 41855 0.0 S 4T 0:00.00 0:00.00 -someuser 41869 ?? 0.0 S 4T 0:00.01 0:00.04 /usr/bin/some_command with some parameters - 41869 0.0 S 4T 0:00.01 0:00.00 -someuser 41875 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 41875 0.0 S 4T 0:00.00 0:00.00 -someuser 41878 ?? 0.0 S 46R 0:00.18 0:00.08 /usr/bin/some_command with some parameters - 41878 0.0 S 20T 0:00.00 0:00.00 -root 41886 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 41886 0.0 S 4T 0:00.00 0:00.01 -root 41890 ?? 0.0 S 4T 0:00.08 0:00.02 /usr/bin/some_command with some parameters - 41890 0.0 S 4T 0:00.01 0:00.00 - 41890 0.0 S 4T 0:00.00 0:00.00 - 41890 0.0 S 4T 0:00.00 0:00.00 -root 41897 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 41897 0.0 S 4T 0:00.15 0:00.04 -someuser 41908 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 41908 0.0 S 37T 0:00.00 0:00.00 - 41908 0.0 S 37T 0:00.00 0:00.00 - 41908 0.0 S 31T 0:00.00 0:00.00 -root 41912 ?? 0.0 S 4T 0:00.06 0:00.03 /usr/bin/some_command with some parameters - 41912 0.0 S 4T 0:00.00 0:00.00 -root 41926 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 41926 0.0 S 20T 0:00.00 0:00.00 - 41926 0.0 S 31T 0:01.15 0:00.50 - 41926 0.0 S 31T 0:00.01 0:00.01 -_netbios 42029 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 42029 0.0 S 4T 0:00.00 0:00.00 -someuser 42082 ?? 0.0 S 20T 0:00.34 0:00.31 /usr/bin/some_command with some parameters - 42082 0.0 S 20T 0:00.03 0:00.01 - 42082 0.0 S 20T 0:00.00 0:00.00 - 42082 0.0 S 20T 0:00.00 0:00.00 -_driverkit 42094 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 42094 0.0 S 31T 0:00.00 0:00.00 -_driverkit 42095 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 42095 0.0 S 31T 0:00.00 0:00.00 -_driverkit 42096 ?? 0.0 S 63R 0:00.75 0:00.30 /usr/bin/some_command with some parameters - 42096 0.0 S 31T 0:00.00 0:00.00 -_driverkit 42097 ?? 0.0 S 63R 0:00.02 0:00.00 /usr/bin/some_command with some parameters - 42097 0.0 S 31T 0:00.00 0:00.00 -_driverkit 42098 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 42098 0.0 S 31T 0:00.00 0:00.00 -_driverkit 42100 ?? 0.0 S 63R 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 42100 0.0 S 31T 0:00.00 0:00.00 -root 42115 ?? 0.0 S 4T 0:00.03 0:00.01 /usr/bin/some_command with some parameters - 42115 0.0 S 4T 0:00.01 0:00.00 -someuser 42121 ?? 0.0 S 4T 0:00.17 0:00.09 /usr/bin/some_command with some parameters - 42121 0.0 S 4T 0:00.00 0:00.00 -someuser 42139 ?? 0.0 S 4T 0:00.10 0:00.09 /usr/bin/some_command with some parameters - 42139 0.0 S 4T 0:00.00 0:00.00 -someuser 42155 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 42155 0.0 S 4T 0:00.00 0:00.00 -_spotlight 42306 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 42306 0.0 S 4T 0:00.00 0:00.00 -newrelic 42930 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 42930 0.0 S 4T 0:00.00 0:00.00 -666 42931 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 42931 0.0 S 4T 0:00.00 0:00.00 -someuser 42958 ?? 0.0 S 31T 0:04.06 0:01.29 /usr/bin/some_command with some parameters - 42958 0.0 S 19T 0:00.00 0:00.00 - 42958 0.0 S 37T 0:00.32 0:00.07 - 42958 0.0 S 60R 0:00.00 0:00.00 - 42958 0.0 S 55R 0:00.00 0:00.00 - 42958 0.0 S 31T 0:00.00 0:00.00 - 42958 0.0 S 31T 0:00.00 0:00.00 -someuser 43266 ?? 0.0 S 4T 0:00.35 0:00.23 /usr/bin/some_command with some parameters - 43266 0.0 S 4T 0:00.04 0:00.01 - 43266 0.0 S 4T 0:00.00 0:00.00 - 43266 0.0 S 4T 0:00.00 0:00.00 -someuser 43267 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 43267 0.0 S 4T 0:00.02 0:00.02 -someuser 43686 ?? 0.0 S 4T 0:00.07 0:00.18 /usr/bin/some_command with some parameters - 43686 0.0 S 4T 0:00.00 0:00.00 -someuser 43718 ?? 0.0 S 31T 0:03.94 0:13.45 /usr/bin/some_command with some parameters - 43718 0.0 S 31T 0:00.00 0:00.00 - 43718 0.0 S 0T 0:00.01 0:00.01 - 43718 0.0 S 31T 0:00.91 0:01.19 - 43718 0.0 S 31T 0:00.00 0:00.00 - 43718 0.0 S 31T 0:00.00 0:00.00 - 43718 0.0 S 31T 0:00.00 0:00.00 - 43718 0.0 S 31T 0:00.00 0:00.00 - 43718 0.0 S 31T 0:00.00 0:00.00 - 43718 0.0 S 31T 0:00.00 0:00.00 - 43718 0.0 S 31T 0:00.00 0:00.00 - 43718 0.0 S 31T 0:00.00 0:00.00 - 43718 0.0 S 31T 0:00.01 0:00.00 - 43718 0.0 S 31T 0:00.50 0:00.19 - 43718 0.0 S 31T 0:00.05 0:00.04 - 43718 0.0 S 31T 0:00.02 0:00.01 - 43718 0.0 S 31T 0:00.02 0:00.01 - 43718 0.0 S 31T 0:00.02 0:00.02 - 43718 0.0 S 31T 0:00.02 0:00.02 - 43718 0.0 S 31T 0:00.02 0:00.01 - 43718 0.0 S 31T 0:00.03 0:00.01 - 43718 0.0 S 31T 0:00.01 0:00.00 - 43718 0.0 S 31T 0:00.01 0:00.00 - 43718 0.0 S 31T 0:00.00 0:00.00 -_gamecontrollerd 43719 ?? 0.0 S 4T 0:19.83 0:24.56 /usr/bin/some_command with some parameters - 43719 0.0 S 4T 0:00.24 0:00.19 - 43719 0.0 S 4T 0:00.04 0:00.03 - 43719 0.0 S 4T 0:00.02 0:00.01 - 43719 0.0 S 4T 0:00.00 0:00.00 -_coreaudiod 43720 ?? 0.0 S 4T 0:00.03 0:00.01 Core Audio Driver (ZoomAudioDevice.driver) - 43720 0.0 S 4T 0:00.23 0:00.25 - 43720 0.0 S 4T 0:00.00 0:00.00 -someuser 43724 ?? 0.0 S 20T 0:00.28 0:00.28 /usr/bin/some_command with some parameters - 43724 0.0 S 20T 0:00.02 0:00.01 - 43724 0.0 S 20T 0:00.00 0:00.00 - 43724 0.0 S 20T 0:00.00 0:00.00 -someuser 43725 ?? 0.0 S 31T 0:00.05 0:00.13 /usr/bin/some_command with some parameters - 43725 0.0 S 31T 0:00.00 0:00.00 -someuser 43726 ?? 0.0 S 4T 0:00.04 0:00.04 /usr/bin/some_command with some parameters - 43726 0.0 S 4T 0:00.00 0:00.00 -someuser 43728 ?? 0.0 S 31T 0:00.31 0:00.77 /usr/bin/some_command with some parameters - 43728 0.0 S 31T 0:00.00 0:00.00 - 43728 0.0 S 0T 0:00.01 0:00.01 - 43728 0.0 S 31T 0:00.08 0:00.06 - 43728 0.0 S 31T 0:00.00 0:00.00 - 43728 0.0 S 31T 0:00.00 0:00.00 - 43728 0.0 S 31T 0:00.00 0:00.00 - 43728 0.0 S 31T 0:00.00 0:00.00 - 43728 0.0 S 31T 0:00.00 0:00.00 - 43728 0.0 S 31T 0:00.00 0:00.00 - 43728 0.0 S 31T 0:00.00 0:00.00 - 43728 0.0 S 31T 0:00.00 0:00.00 - 43728 0.0 S 31T 0:00.01 0:00.00 - 43728 0.0 S 31T 0:00.01 0:00.01 - 43728 0.0 S 31T 0:00.01 0:00.00 -someuser 43729 ?? 0.0 S 31T 0:00.39 0:00.76 /usr/bin/some_command with some parameters - 43729 0.0 S 31T 0:00.00 0:00.00 - 43729 0.0 S 0T 0:00.01 0:00.01 - 43729 0.0 S 31T 0:00.07 0:00.05 - 43729 0.0 S 31T 0:00.00 0:00.00 - 43729 0.0 S 31T 0:00.00 0:00.00 - 43729 0.0 S 31T 0:00.00 0:00.00 - 43729 0.0 S 31T 0:00.00 0:00.00 - 43729 0.0 S 31T 0:00.00 0:00.00 - 43729 0.0 S 31T 0:00.00 0:00.00 - 43729 0.0 S 31T 0:00.00 0:00.00 - 43729 0.0 S 31T 0:00.00 0:00.00 - 43729 0.0 S 31T 0:00.02 0:00.00 - 43729 0.0 S 31T 0:00.01 0:00.01 - 43729 0.0 S 31T 0:00.01 0:00.00 -root 43731 ?? 0.0 S 31T 0:00.07 0:00.04 /usr/bin/some_command with some parameters - 43731 0.0 S 31T 0:00.01 0:00.00 -someuser 43865 ?? 0.0 S 4T 0:00.20 0:00.09 /usr/bin/some_command with some parameters - 43865 0.0 S 4T 0:00.00 0:00.00 -someuser 43867 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 43867 0.0 S 4T 0:00.00 0:00.00 -root 43868 ?? 0.0 S 31T 0:07.98 0:13.93 /usr/bin/some_command with some parameters - 43868 0.0 S 19T 0:00.00 0:00.00 - 43868 0.0 S 31T 0:00.00 0:00.00 -someuser 43869 ?? 0.0 S 4T 0:00.05 0:00.06 /usr/bin/some_command with some parameters - 43869 0.0 S 4T 0:00.00 0:00.00 -someuser 43871 ?? 0.0 S 4T 0:00.04 0:00.04 /usr/bin/some_command with some parameters - 43871 0.0 S 4T 0:00.00 0:00.00 - 43871 0.0 S 4T 0:00.00 0:00.00 -root 43873 ?? 0.0 S 4T 0:00.02 0:00.04 /usr/bin/some_command with some parameters - 43873 0.0 S 4T 0:00.01 0:00.00 -_fpsd 43874 ?? 0.0 S 4T 0:00.03 0:00.02 /usr/bin/some_command with some parameters - 43874 0.0 S 4T 0:00.00 0:00.00 -root 43880 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 43880 0.0 S 4T 0:00.00 0:00.00 -someuser 43881 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 43881 0.0 S 4T 0:00.00 0:00.00 -someuser 43882 ?? 0.0 S 46T 0:00.73 0:00.88 /usr/bin/some_command with some parameters - 43882 0.0 S 46T 0:00.08 0:00.03 - 43882 0.0 S 37T 0:00.00 0:00.00 -root 43883 ?? 0.0 S 31T 0:00.05 0:00.04 /usr/bin/some_command with some parameters - 43883 0.0 S 31T 0:00.01 0:00.00 -someuser 43889 ?? 0.0 S 31T 0:00.29 0:00.16 /usr/bin/some_command with some parameters - 43889 0.0 S 37T 0:00.03 0:00.01 - 43889 0.0 S 37T 0:00.00 0:00.00 -someuser 43890 ?? 0.0 S 4T 0:00.02 0:00.01 /usr/bin/some_command with some parameters - 43890 0.0 S 4T 0:00.01 0:00.00 -root 43892 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 43892 0.0 S 4T 0:00.00 0:00.00 -root 43893 ?? 0.0 S 20T 0:00.19 0:00.38 /usr/bin/some_command with some parameters - 43893 0.0 S 20T 0:00.09 0:00.07 - 43893 0.0 S 20T 0:00.17 0:00.20 - 43893 0.0 S 20T 0:00.05 0:00.06 - 43893 0.0 S 20T 0:00.12 0:00.19 - 43893 0.0 S 20T 0:00.05 0:00.08 - 43893 0.0 S 20T 0:00.04 0:00.05 - 43893 0.0 S 20T 0:00.42 0:00.30 - 43893 0.0 S 20T 0:00.17 0:00.44 - 43893 0.0 S 20T 0:03.44 0:14.51 - 43893 0.0 S 20T 0:00.00 0:00.00 - 43893 0.0 S 20T 0:00.28 0:00.93 - 43893 0.0 S 20T 0:00.01 0:00.00 - 43893 0.0 S 20T 0:01.11 0:02.00 - 43893 0.0 S 20T 0:00.02 0:00.01 - 43893 1.8 S 20T 1:03.41 4:28.47 - 43893 0.0 S 20T 0:03.14 0:02.00 - 43893 0.0 S 20T 0:38.76 1:20.10 - 43893 0.0 S 20T 0:00.00 0:00.00 - 43893 0.0 S 20T 0:00.00 0:00.00 - 43893 0.0 S 20T 0:00.00 0:00.00 - 43893 0.0 S 20T 0:00.01 0:00.01 -someuser 43895 ?? 0.0 S 4T 0:00.21 0:00.08 /usr/bin/some_command with some parameters - 43895 0.0 S 4T 0:00.01 0:00.00 - 43895 0.0 S 4T 0:00.00 0:00.00 - 43895 0.0 S 4T 0:00.00 0:00.00 -someuser 43896 ?? 0.0 S 31T 0:00.01 0:00.02 /usr/bin/some_command with some parameters - 43896 0.0 S 31T 0:00.02 0:00.01 -someuser 43898 ?? 0.0 S 4T 0:00.27 0:00.10 /usr/bin/some_command with some parameters - 43898 0.0 S 4T 0:00.00 0:00.00 -someuser 43901 ?? 0.0 S 46T 0:00.21 0:00.12 /usr/bin/some_command with some parameters - 43901 0.0 S 46T 0:00.03 0:00.01 - 43901 0.0 S 37T 0:00.00 0:00.00 -someuser 43904 ?? 0.0 S 4T 0:00.20 0:00.10 /usr/bin/some_command with some parameters - 43904 0.0 S 4T 0:00.01 0:00.00 -someuser 43907 ?? 0.0 S 4T 0:00.04 0:00.05 /usr/bin/some_command with some parameters - 43907 0.0 S 4T 0:00.01 0:00.00 -_installcoordinationd 43908 ?? 0.0 S 4T 0:00.02 0:00.03 /usr/bin/some_command with some parameters - 43908 0.0 S 4T 0:00.00 0:00.00 -root 43910 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 43910 0.0 S 4T 0:00.04 0:00.07 -root 43916 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 43916 0.0 S 20T 0:00.01 0:00.00 -root 43918 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 43918 0.0 S 4T 0:00.01 0:00.00 -someuser 43936 ?? 0.0 S 31T 0:01.79 0:12.34 /usr/bin/some_command with some parameters - 43936 0.0 S 37T 0:00.00 0:00.00 -someuser 43941 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 43941 0.0 S 4T 0:00.00 0:00.00 -root 43942 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 43942 0.0 S 4T 0:00.01 0:00.00 -root 43956 ?? 0.0 S 4T 0:00.06 0:00.08 /usr/bin/some_command with some parameters - 43956 0.0 S 4T 0:00.01 0:00.00 -root 43957 ?? 0.0 S 31T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 43957 0.0 S 37T 0:00.00 0:00.00 -someuser 43966 ?? 0.0 S 4T 0:00.06 0:00.05 /usr/bin/some_command with some parameters - 43966 0.0 S 4T 0:00.00 0:00.00 -someuser 43971 ?? 0.0 S 4T 0:00.03 0:00.01 /usr/bin/some_command with some parameters - 43971 0.0 S 4T 0:00.00 0:00.00 -someuser 43973 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 43973 0.0 S 4T 0:00.00 0:00.00 -someuser 43974 ?? 0.0 S 4T 0:00.02 0:00.01 /usr/bin/some_command with some parameters - 43974 0.0 S 4T 0:00.00 0:00.00 -someuser 43975 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 43975 0.0 S 4T 0:00.00 0:00.00 -someuser 43976 ?? 0.0 S 4T 0:00.02 0:00.03 /usr/bin/some_command with some parameters - 43976 0.0 S 4T 0:00.00 0:00.00 - 43976 0.0 S 4T 0:00.00 0:00.00 -_assetcache 43977 ?? 0.0 S 4T 0:00.03 0:00.03 /usr/bin/some_command with some parameters - 43977 0.0 S 4T 0:00.00 0:00.00 -root 43978 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 43978 0.0 S 4T 0:00.00 0:00.00 -root 43983 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 43983 0.0 S 20T 0:00.00 0:00.00 -root 43984 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 43984 0.0 S 4T 0:00.00 0:00.00 -someuser 44067 ?? 0.0 S 4T 0:00.02 0:00.03 /usr/bin/some_command with some parameters - 44067 0.0 S 4T 0:00.00 0:00.00 - 44067 0.0 S 4T 0:00.00 0:00.00 -someuser 44068 ?? 0.0 S 4T 0:00.17 0:00.07 /usr/bin/some_command with some parameters - 44068 0.0 S 4T 0:00.01 0:00.01 - 44068 0.0 S 4T 0:00.08 0:00.03 - 44068 0.0 S 4T 0:00.03 0:00.01 -someuser 44070 ?? 0.0 S 4T 0:00.02 0:00.01 /usr/bin/some_command with some parameters - 44070 0.0 S 4T 0:00.00 0:00.00 -someuser 44072 ?? 0.0 S 4T 0:00.43 0:00.17 /usr/bin/some_command with some parameters - 44072 0.0 S 4T 0:00.01 0:00.01 - 44072 0.0 S 4T 0:00.00 0:00.00 - 44072 0.0 S 4T 0:00.00 0:00.00 - 44072 0.0 S 4T 0:00.00 0:00.00 - 44072 0.0 S 4T 0:00.00 0:00.00 -someuser 44073 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 44073 0.0 S 4T 0:00.01 0:00.01 -someuser 44074 ?? 0.0 S 4T 0:00.02 0:00.01 /usr/bin/some_command with some parameters - 44074 0.0 S 4T 0:00.00 0:00.00 -someuser 44075 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 44075 0.0 S 4T 0:00.00 0:00.00 -someuser 44076 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 44076 0.0 S 4T 0:00.00 0:00.00 -someuser 44083 ?? 0.0 S 4T 0:00.03 0:00.04 /usr/bin/some_command with some parameters - 44083 0.0 S 4T 0:00.01 0:00.00 -someuser 44084 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 44084 0.0 S 4T 0:00.00 0:00.00 -someuser 44085 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 44085 0.0 S 4T 0:00.00 0:00.00 -root 44086 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 44086 0.0 S 4T 0:00.02 0:00.01 -someuser 44090 ?? 0.0 S 31T 0:00.16 0:00.09 /usr/bin/some_command with some parameters - 44090 0.0 S 37T 0:00.03 0:00.01 - 44090 0.0 S 37T 0:00.00 0:00.00 -someuser 44098 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 44098 0.0 S 4T 0:00.00 0:00.00 -root 44099 ?? 0.0 S 4T 0:00.02 0:00.04 /usr/bin/some_command with some parameters - 44099 0.0 S 4T 0:00.00 0:00.00 - 44099 0.0 S 4T 0:00.00 0:00.00 -someuser 44100 ?? 0.0 S 4T 0:00.23 0:00.13 /usr/bin/some_command with some parameters - 44100 0.0 S 4T 0:00.04 0:00.01 - 44100 0.0 S 4T 0:00.00 0:00.00 - 44100 0.0 S 4T 0:00.00 0:00.00 -root 44101 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 44101 0.0 S 4T 0:00.00 0:00.00 -someuser 44103 ?? 0.0 S 46T 0:00.20 0:00.13 /usr/bin/some_command with some parameters - 44103 0.0 S 46T 0:00.03 0:00.00 - 44103 0.0 S 37T 0:00.00 0:00.00 -root 44153 ?? 0.0 S 31T 0:00.02 0:00.01 /usr/bin/some_command with some parameters - 44153 0.0 S 31T 0:00.00 0:00.00 - 44153 0.0 S 4T 0:00.00 0:00.00 -root 44167 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 44167 0.0 S 31T 0:00.00 0:00.00 -someuser 44185 ?? 0.0 S 4T 0:00.30 0:00.67 /usr/bin/some_command with some parameters - 44185 0.0 S 4T 0:00.01 0:00.00 - 44185 0.0 S 4T 0:00.01 0:00.00 - 44185 0.0 S 4T 0:00.00 0:00.00 -root 44520 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 44520 0.0 S 4T 0:00.01 0:00.04 -someuser 44805 ?? 0.0 S 4T 0:00.24 0:00.39 /usr/bin/some_command with some parameters - 44805 0.0 S 4T 0:00.05 0:00.05 - 44805 0.0 S 4T 0:00.00 0:00.00 - 44805 0.0 S 4T 0:00.02 0:00.13 - 44805 0.0 S 4T 0:00.01 0:00.00 - 44805 0.0 S 4T 0:00.00 0:00.01 - 44805 0.0 S 4T 0:00.00 0:00.00 - 44805 0.0 S 4T 0:00.00 0:00.00 -root 44913 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 44913 0.0 S 20T 0:00.01 0:00.00 -root 45056 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 45056 0.0 S 4T 0:00.00 0:00.00 -root 45060 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 45060 0.0 S 4T 0:00.00 0:00.00 -root 45062 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 45062 0.0 S 4T 0:00.00 0:00.00 -root 45063 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 45063 0.0 S 31T 0:00.00 0:00.00 -someuser 45064 ?? 0.0 S 4T 0:00.02 0:00.03 /usr/bin/some_command with some parameters - 45064 0.0 S 4T 0:00.00 0:00.00 -someuser 45065 ?? 0.0 S 31T 0:00.03 0:00.01 /usr/bin/some_command with some parameters - 45065 0.0 S 31T 0:00.00 0:00.00 -root 45066 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 45066 0.0 S 4T 0:00.00 0:00.00 -root 45067 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 45067 0.0 S 4T 0:00.00 0:00.00 -root 45068 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 45068 0.0 S 4T 0:00.00 0:00.00 -someuser 45069 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 45069 0.0 S 31T 0:00.00 0:00.00 -someuser 45070 ?? 0.0 S 31T 0:00.16 0:00.07 /usr/bin/some_command with some parameters - 45070 0.0 S 37T 0:00.00 0:00.00 - 45070 0.0 S 31T 0:00.00 0:00.00 -someuser 45071 ?? 0.0 S 31T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 45071 0.0 S 31T 0:00.00 0:00.00 -root 45073 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 45073 0.0 S 4T 0:00.00 0:00.00 -_appstore 45096 ?? 0.0 S 4T 0:00.18 0:00.07 /usr/bin/some_command with some parameters - 45096 0.0 S 4T 0:00.00 0:00.00 -someuser 45097 ?? 0.0 S 4T 0:00.03 0:00.05 /usr/bin/some_command with some parameters - 45097 0.0 S 4T 0:00.01 0:00.00 -root 45098 ?? 0.0 S 4T 0:00.01 0:00.05 /usr/bin/some_command with some parameters - 45098 0.0 S 4T 0:00.00 0:00.00 -someuser 45101 ?? 0.0 S 4T 0:00.02 0:00.04 /usr/bin/some_command with some parameters - 45101 0.0 S 4T 0:00.00 0:00.00 -root 45104 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 45104 0.0 S 4T 0:00.00 0:00.00 -root 45105 ?? 0.0 S 20T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 45105 0.0 S 4T 0:00.01 0:00.00 -root 45106 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 45106 0.0 S 4T 0:00.00 0:00.00 -_applepay 45111 ?? 0.0 S 50T 0:00.03 0:00.03 /usr/bin/some_command with some parameters - 45111 0.0 S 31T 0:00.00 0:00.00 -someuser 45174 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 45174 0.0 S 4T 0:00.02 0:00.02 -someuser 45206 ?? 0.0 S 31T 0:04.32 0:10.39 /usr/bin/some_command with some parameters - 45206 0.0 S 31T 0:00.00 0:00.00 - 45206 0.0 S 0T 0:00.12 0:00.09 - 45206 0.0 S 31T 0:02.48 0:02.72 - 45206 0.0 S 31T 0:00.00 0:00.00 - 45206 0.0 S 31T 0:00.01 0:00.00 - 45206 0.0 S 31T 0:00.00 0:00.00 - 45206 0.0 S 31T 0:00.00 0:00.00 - 45206 0.0 S 31T 0:00.00 0:00.00 - 45206 0.0 S 31T 0:00.00 0:00.00 - 45206 0.0 S 31T 0:00.00 0:00.00 - 45206 0.0 S 31T 0:00.00 0:00.00 - 45206 0.0 S 31T 0:00.14 0:00.03 - 45206 0.0 S 31T 0:00.00 0:00.00 - 45206 0.0 S 31T 0:00.02 0:00.00 -someuser 45624 ?? 0.0 S 4T 0:00.03 0:00.01 /usr/bin/some_command with some parameters - 45624 0.0 S 4T 0:00.00 0:00.00 -someuser 45782 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 45782 0.0 S 4T 0:00.00 0:00.00 -someuser 45792 ?? 0.0 S 31T 0:00.69 0:01.23 /usr/bin/some_command with some parameters - 45792 0.0 S 31T 0:00.05 0:00.02 - 45792 0.0 S 31T 0:00.00 0:00.00 - 45792 0.0 S 0T 0:00.00 0:00.00 - 45792 0.0 S 31T 0:00.16 0:00.09 - 45792 0.0 S 31T 0:00.00 0:00.00 - 45792 0.0 S 31T 0:00.01 0:00.03 - 45792 0.0 S 31T 0:00.02 0:00.01 - 45792 0.0 S 31T 0:00.00 0:00.00 - 45792 0.0 S 31T 0:00.00 0:00.00 - 45792 0.0 S 31T 0:00.00 0:00.01 - 45792 0.0 S 31T 0:00.00 0:00.00 - 45792 0.0 S 31T 0:00.00 0:00.00 - 45792 0.0 S 31T 0:00.01 0:00.00 - 45792 0.0 S 31T 0:00.01 0:00.01 -someuser 45933 ?? 0.0 S 20T 0:00.04 0:00.02 /usr/bin/some_command with some parameters - 45933 0.0 S 20T 0:00.00 0:00.00 -_iconservices 45982 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 45982 0.0 S 4T 0:00.00 0:00.00 -someuser 46122 ?? 0.0 S 42T 0:03.35 0:04.46 /usr/bin/some_command with some parameters - 46122 0.0 S 31T 0:21.72 0:06.13 - 46122 0.0 S 31T 0:00.09 0:00.09 - 46122 0.0 S 31T 0:00.24 0:00.26 - 46122 0.0 S 31T 0:00.01 0:00.00 - 46122 1.0 S 31T 24:06.44 0:28.11 - 46122 1.5 S 31T 29:27.74 2:20.05 - 46122 0.0 S 31T 0:00.00 0:00.00 - 46122 0.0 S 31T 0:12.63 0:07.90 - 46122 0.0 S 31T 0:00.00 0:00.00 - 46122 0.0 S 31T 0:07.66 0:02.06 - 46122 0.0 S 31T 0:27.64 0:10.88 - 46122 0.0 S 31T 0:02.82 0:00.63 - 46122 0.0 S 31T 0:00.00 0:00.00 - 46122 0.0 S 31T 0:00.14 0:00.07 - 46122 0.0 S 31T 0:00.21 0:00.26 - 46122 0.0 S 31T 0:21.79 0:11.83 - 46122 0.0 S 31T 0:06.17 0:05.60 - 46122 0.0 S 31T 0:00.00 0:00.00 - 46122 0.0 S 31T 0:00.00 0:00.00 - 46122 0.0 S 31T 0:03.87 0:00.84 - 46122 0.0 S 31T 0:04.06 0:03.00 - 46122 0.0 S 31T 0:00.01 0:00.00 - 46122 0.0 S 31T 0:00.00 0:00.00 - 46122 0.0 S 31T 0:26.09 0:06.42 - 46122 0.0 S 31T 0:00.98 0:00.22 - 46122 0.0 S 31T 0:00.00 0:00.00 - 46122 0.0 S 19T 0:00.00 0:00.00 - 46122 0.0 S 31T 0:00.14 0:00.07 - 46122 0.0 S 31T 0:12.82 0:10.30 - 46122 0.0 S 31T 0:00.00 0:00.00 -someuser 46396 ?? 0.0 S 4T 0:00.05 0:00.04 /usr/bin/some_command with some parameters - 46396 0.0 S 4T 0:00.00 0:00.00 -someuser 46645 ?? 0.0 S 31T 0:00.23 0:00.60 /usr/bin/some_command with some parameters - 46645 0.0 S 31T 0:00.02 0:00.01 - 46645 0.0 S 31T 0:00.00 0:00.00 - 46645 0.0 S 0T 0:00.00 0:00.00 - 46645 0.0 S 31T 0:00.06 0:00.03 - 46645 0.0 S 31T 0:00.00 0:00.00 - 46645 0.0 S 31T 0:00.02 0:00.04 - 46645 0.0 S 31T 0:00.00 0:00.00 - 46645 0.0 S 31T 0:00.00 0:00.01 - 46645 0.0 S 31T 0:00.00 0:00.01 - 46645 0.0 S 31T 0:00.00 0:00.02 - 46645 0.0 S 31T 0:00.00 0:00.02 - 46645 0.0 S 31T 0:00.00 0:00.00 - 46645 0.0 S 31T 0:00.00 0:00.00 - 46645 0.0 S 31T 0:00.00 0:00.01 -someuser 46738 ?? 0.0 S 31T 0:00.59 0:01.17 /usr/bin/some_command with some parameters - 46738 0.0 S 31T 0:00.05 0:00.02 - 46738 0.0 S 31T 0:00.00 0:00.00 - 46738 0.0 S 0T 0:00.00 0:00.00 - 46738 0.0 S 31T 0:00.14 0:00.08 - 46738 0.0 S 31T 0:00.00 0:00.00 - 46738 0.0 S 31T 0:00.02 0:00.06 - 46738 0.0 S 31T 0:00.03 0:00.01 - 46738 0.0 S 31T 0:00.00 0:00.00 - 46738 0.0 S 31T 0:00.00 0:00.01 - 46738 0.0 S 31T 0:00.00 0:00.00 - 46738 0.0 S 31T 0:00.00 0:00.00 - 46738 0.0 S 31T 0:00.00 0:00.00 - 46738 0.0 S 31T 0:00.00 0:00.00 - 46738 0.0 S 31T 0:00.03 0:00.02 -someuser 47353 ?? 0.0 S 31T 0:00.88 0:04.50 /usr/bin/some_command with some parameters - 47353 0.0 S 31T 0:00.07 0:00.03 - 47353 0.0 S 31T 0:00.00 0:00.00 - 47353 0.0 S 0T 0:00.00 0:00.00 - 47353 0.0 S 31T 0:00.25 0:00.23 - 47353 0.0 S 31T 0:00.00 0:00.00 - 47353 0.0 S 31T 0:00.23 0:01.09 - 47353 0.0 S 31T 0:00.00 0:00.00 - 47353 0.0 S 31T 0:00.01 0:00.03 - 47353 0.0 S 31T 0:00.02 0:00.03 - 47353 0.0 S 31T 0:00.01 0:00.03 - 47353 0.0 S 31T 0:00.01 0:00.03 - 47353 0.0 S 31T 0:00.00 0:00.00 - 47353 0.0 S 31T 0:00.00 0:00.00 - 47353 0.0 S 31T 0:00.02 0:00.02 -someuser 47355 ?? 0.0 S 31T 0:00.46 0:02.51 /usr/bin/some_command with some parameters - 47355 0.0 S 31T 0:00.04 0:00.02 - 47355 0.0 S 31T 0:00.00 0:00.00 - 47355 0.0 S 0T 0:00.00 0:00.00 - 47355 0.0 S 31T 0:00.09 0:00.05 - 47355 0.0 S 31T 0:00.00 0:00.00 - 47355 0.0 S 31T 0:00.04 0:00.18 - 47355 0.0 S 31T 0:00.00 0:00.00 - 47355 0.0 S 31T 0:00.01 0:00.04 - 47355 0.0 S 31T 0:00.00 0:00.01 - 47355 0.0 S 31T 0:00.00 0:00.03 - 47355 0.0 S 31T 0:00.00 0:00.02 - 47355 0.0 S 31T 0:00.00 0:00.00 - 47355 0.0 S 31T 0:00.01 0:00.00 - 47355 0.0 S 31T 0:00.02 0:00.02 -root 49788 ?? 0.0 S 4T 0:00.03 0:00.01 /usr/bin/some_command with some parameters - 49788 0.0 S 4T 0:00.03 0:00.01 -_softwareupdate 51166 ?? 0.0 S 4T 0:08.83 0:00.85 /usr/bin/some_command with some parameters - 51166 0.0 S 4T 0:00.58 0:00.03 - 51166 0.0 S 4T 0:00.00 0:00.00 - 51166 0.0 S 4T 0:00.04 0:00.02 - 51166 0.0 S 4T 0:00.01 0:00.00 -root 51168 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 51168 0.0 S 4T 0:00.00 0:00.00 -_atsserver 51169 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 51169 0.0 S 4T 0:00.00 0:00.00 -someuser 51368 ?? 0.0 S 4T 0:00.82 0:00.33 /usr/bin/some_command with some parameters - 51368 0.0 S 4T 0:00.01 0:00.00 -someuser 52356 ?? 0.0 S 31T 0:00.36 0:00.97 /usr/bin/some_command with some parameters - 52356 0.0 S 31T 0:00.03 0:00.01 - 52356 0.0 S 31T 0:00.00 0:00.00 - 52356 0.0 S 0T 0:00.00 0:00.00 - 52356 0.0 S 31T 0:00.05 0:00.04 - 52356 0.0 S 31T 0:00.00 0:00.00 - 52356 0.0 S 31T 0:00.01 0:00.03 - 52356 0.0 S 31T 0:00.01 0:00.00 - 52356 0.0 S 31T 0:00.00 0:00.00 - 52356 0.0 S 31T 0:00.00 0:00.00 - 52356 0.0 S 31T 0:00.00 0:00.01 - 52356 0.0 S 31T 0:00.00 0:00.00 - 52356 0.0 S 31T 0:00.00 0:00.00 - 52356 0.0 S 31T 0:00.01 0:00.00 - 52356 0.0 S 31T 0:00.01 0:00.01 -someuser 52359 ?? 0.0 S 31T 0:00.57 0:03.45 /usr/bin/some_command with some parameters - 52359 0.0 S 31T 0:00.04 0:00.02 - 52359 0.0 S 31T 0:00.00 0:00.00 - 52359 0.0 S 0T 0:00.00 0:00.00 - 52359 0.0 S 31T 0:00.26 0:00.39 - 52359 0.0 S 31T 0:00.00 0:00.00 - 52359 0.0 S 31T 0:00.44 0:01.68 - 52359 0.0 S 31T 0:00.00 0:00.00 - 52359 0.0 S 31T 0:00.01 0:00.02 - 52359 0.0 S 31T 0:00.00 0:00.02 - 52359 0.0 S 31T 0:00.01 0:00.03 - 52359 0.0 S 31T 0:00.01 0:00.01 - 52359 0.0 S 31T 0:00.00 0:00.00 - 52359 0.0 S 31T 0:00.01 0:00.00 - 52359 0.0 S 31T 0:00.00 0:00.00 -root 53270 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 53270 0.0 S 4T 0:00.00 0:00.00 -root 53628 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 53628 0.0 S 4T 0:00.00 0:00.00 -root 53631 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 53631 0.0 S 4T 0:00.00 0:00.00 -someuser 53753 ?? 0.0 S 4T 0:00.05 0:00.06 /usr/bin/some_command with some parameters - 53753 0.0 S 4T 0:00.00 0:00.00 -root 53792 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 53792 0.0 S 19T 0:00.00 0:00.00 - 53792 0.0 S 31T 0:00.00 0:00.00 -root 53793 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 53793 0.0 S 4T 0:00.00 0:00.00 -someuser 53835 ?? 0.0 S 46T 0:00.04 0:00.06 /usr/bin/some_command with some parameters - 53835 0.0 S 46T 0:00.00 0:00.00 - 53835 24.1 S 31T 1:21.34 3:38.44 - 53835 0.1 S 20T 0:00.12 0:00.08 - 53835 0.1 S 20T 0:00.06 0:00.04 - 53835 0.2 S 46T 0:00.03 0:00.02 -someuser 53836 ?? 0.4 S 46T 0:04.23 0:05.61 /usr/bin/some_command with some parameters - 53836 0.0 S 46T 0:00.00 0:00.00 - 53836 6.1 S 31T 0:16.08 1:47.22 - 53836 0.5 S 37T 0:00.90 0:00.16 - 53836 0.4 S 46T 0:00.48 0:00.09 - 53836 0.0 S 31T 0:00.00 0:00.00 -someuser 53837 ?? 0.0 S 4T 0:00.01 0:00.03 /usr/bin/some_command with some parameters - 53837 0.0 S 4T 0:00.00 0:00.00 - 53837 0.0 S 4T 0:00.00 0:00.00 -someuser 53838 ?? 0.0 S 4T 0:00.12 0:00.01 /usr/bin/some_command with some parameters - 53838 0.0 S 4T 0:00.00 0:00.00 -someuser 53839 ?? 0.0 S 4T 0:00.24 0:00.02 /usr/bin/some_command with some parameters - 53839 0.0 S 4T 0:00.00 0:00.00 -someuser 53885 ?? 0.0 S 31T 0:00.20 0:00.89 /usr/bin/some_command with some parameters - 53885 0.0 S 31T 0:00.02 0:00.01 - 53885 0.0 S 31T 0:00.00 0:00.00 - 53885 0.0 S 0T 0:00.00 0:00.00 - 53885 0.0 S 31T 0:00.04 0:00.04 - 53885 0.0 S 31T 0:00.00 0:00.00 - 53885 0.0 S 31T 0:00.02 0:00.06 - 53885 0.0 S 31T 0:00.01 0:00.00 - 53885 0.0 S 31T 0:00.00 0:00.00 - 53885 0.0 S 31T 0:00.00 0:00.00 - 53885 0.0 S 31T 0:00.00 0:00.02 - 53885 0.0 S 31T 0:00.00 0:00.00 - 53885 0.0 S 31T 0:00.00 0:00.00 - 53885 0.0 S 31T 0:00.00 0:00.00 - 53885 0.0 S 31T 0:00.00 0:00.01 -someuser 53929 ?? 0.0 S 31T 0:00.20 0:01.08 /usr/bin/some_command with some parameters - 53929 0.0 S 31T 0:00.01 0:00.00 - 53929 0.0 S 31T 0:00.00 0:00.00 - 53929 0.0 S 0T 0:00.00 0:00.00 - 53929 0.0 S 31T 0:00.05 0:00.07 - 53929 0.0 S 31T 0:00.00 0:00.00 - 53929 0.0 S 31T 0:00.06 0:00.22 - 53929 0.0 S 31T 0:00.00 0:00.00 - 53929 0.0 S 31T 0:00.00 0:00.02 - 53929 0.0 S 31T 0:00.00 0:00.03 - 53929 0.0 S 31T 0:00.00 0:00.01 - 53929 0.0 S 31T 0:00.00 0:00.02 - 53929 0.0 S 31T 0:00.00 0:00.00 - 53929 0.0 S 31T 0:00.00 0:00.00 - 53929 0.0 S 31T 0:00.01 0:00.00 -someuser 53931 ?? 0.0 S 31T 0:00.03 0:00.08 /usr/bin/some_command with some parameters - 53931 0.0 S 31T 0:00.00 0:00.00 - 53931 0.0 S 31T 0:00.00 0:00.00 - 53931 0.0 S 31T 0:00.00 0:00.00 - 53931 0.0 S 0T 0:00.00 0:00.00 - 53931 0.0 S 31T 0:00.00 0:00.00 - 53931 0.0 S 31T 0:00.00 0:00.00 - 53931 0.0 S 31T 0:00.00 0:00.00 - 53931 0.0 S 31T 0:00.00 0:00.00 - 53931 0.0 S 31T 0:00.00 0:00.00 - 53931 0.0 S 31T 0:00.00 0:00.00 - 53931 0.0 S 31T 0:00.00 0:00.00 - 53931 0.0 S 31T 0:00.00 0:00.00 - 53931 0.0 S 31T 0:00.00 0:00.00 -someuser 54166 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 54166 0.0 S 4T 0:00.00 0:00.00 - 54166 0.0 S 4T 0:00.00 0:00.00 - 54166 0.0 S 4T 0:00.00 0:00.00 -someuser 55004 ?? 0.0 S 4T 0:00.78 0:02.70 /usr/bin/some_command with some parameters - 55004 0.0 S 4T 0:00.05 0:00.03 - 55004 0.0 S 4T 0:00.00 0:00.00 - 55004 0.0 S 4T 0:00.00 0:00.00 -someuser 55005 ?? 0.0 S 4T 0:00.03 0:00.03 /usr/bin/some_command with some parameters - 55005 0.0 S 4T 0:00.00 0:00.00 - 55005 0.0 S 4T 0:00.00 0:00.00 -someuser 55006 ?? 0.0 S 4T 0:00.03 0:00.04 /usr/bin/some_command with some parameters - 55006 0.0 S 4T 0:00.01 0:00.00 -someuser 55007 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 55007 0.0 S 4T 0:00.00 0:00.00 -someuser 55008 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 55008 0.0 S 4T 0:00.00 0:00.00 -someuser 55010 ?? 0.0 S 4T 0:00.03 0:00.04 /usr/bin/some_command with some parameters - 55010 0.0 S 4T 0:00.00 0:00.00 -someuser 55011 ?? 0.0 S 4T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 55011 0.0 S 4T 0:00.00 0:00.00 -_spotlight 55287 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 55287 0.0 S 4T 0:00.00 0:00.04 - 55287 0.0 S 4T 0:00.00 0:00.00 -someuser 55291 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 55291 0.0 S 4T 0:00.00 0:00.04 - 55291 0.0 S 4T 0:00.10 0:00.09 - 55291 0.0 S 4T 0:00.00 0:00.00 -someuser 55294 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 55294 0.0 S 4T 0:00.00 0:00.04 - 55294 0.0 S 4T 0:00.05 0:00.07 - 55294 0.0 S 4T 0:00.00 0:00.00 -someuser 55348 ?? 0.0 S 4T 0:00.01 0:00.01 /usr/bin/some_command with some parameters - 55348 0.0 S 4T 0:00.00 0:00.04 - 55348 0.0 S 4T 0:00.00 0:00.00 - 55348 0.0 S 4T 0:00.00 0:00.00 -someuser 55349 ?? 0.0 S 4T 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 55349 0.0 S 4T 0:00.00 0:00.04 - 55349 0.0 S 4T 0:00.00 0:00.00 - 55349 0.0 S 4T 0:00.00 0:00.00 -root 55706 ?? 0.0 S 31T 0:00.05 0:00.01 /usr/bin/some_command with some parameters - 55706 0.0 S 20T 0:00.00 0:00.00 -someuser 56786 ?? 0.0 S 31T 0:21.59 1:26.39 /usr/bin/some_command with some parameters - 56786 0.0 S 31T 0:00.00 0:00.00 - 56786 0.0 S 0T 0:00.04 0:00.02 - 56786 0.0 S 31T 0:01.50 0:01.81 - 56786 0.0 S 31T 0:00.01 0:00.01 - 56786 0.0 S 31T 0:00.10 0:00.47 - 56786 0.0 S 31T 0:00.02 0:00.01 - 56786 0.0 S 31T 0:00.00 0:00.02 - 56786 0.0 S 31T 0:00.01 0:00.03 - 56786 0.0 S 31T 0:00.00 0:00.01 - 56786 0.0 S 31T 0:00.00 0:00.01 - 56786 0.0 S 31T 0:00.00 0:00.00 - 56786 0.0 S 31T 0:00.09 0:00.03 - 56786 0.0 S 31T 0:00.02 0:00.04 - 56786 0.0 S 31T 0:00.19 0:00.07 - 56786 0.0 S 31T 0:00.01 0:00.08 - 56786 0.0 S 31T 0:00.00 0:00.00 - 56786 0.0 S 31T 0:00.00 0:00.00 - 56786 0.0 S 31T 0:00.00 0:00.00 - 56786 0.0 S 31T 0:00.00 0:00.00 -someuser 67087 ?? 0.0 S 20T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 67087 0.0 S 20T 0:00.00 0:00.00 -root 70071 ?? 0.0 S 37T 0:00.20 0:00.09 /usr/bin/some_command with some parameters - 70071 0.0 S 31T 0:00.02 0:00.01 - 70071 0.0 S 20T 0:00.00 0:00.00 -_usbmuxd 70682 ?? 0.0 S 31T 0:00.09 0:00.04 /usr/bin/some_command with some parameters - 70682 0.0 S 31T 0:00.06 0:00.01 - 70682 0.0 S 31T 0:00.00 0:00.00 -someuser 70696 ?? 0.0 S 4T 0:00.79 0:00.35 /usr/bin/some_command with some parameters - 70696 0.0 S 4T 0:00.00 0:00.00 - 70696 0.0 S 4T 0:00.00 0:00.00 - 70696 0.0 S 4T 0:00.12 0:00.02 - 70696 0.0 S 4T 0:00.00 0:00.00 - 70696 0.0 S 4T 0:00.00 0:00.00 -someuser 70752 ?? 0.0 S 4T 0:00.07 0:00.03 /usr/bin/some_command with some parameters - 70752 0.0 S 4T 0:00.00 0:00.00 - 70752 0.0 S 4T 0:00.00 0:00.00 - 70752 0.0 S 4T 0:00.01 0:00.00 -_driverkit 70896 ?? 0.0 S 63R 0:00.01 0:00.00 /usr/bin/some_command with some parameters - 70896 0.0 S 31T 0:00.00 0:00.00 -_driverkit 70898 ?? 0.0 S 31T 0:00.00 0:00.00 /usr/bin/some_command with some parameters - 70898 0.0 S 37T 0:00.14 0:00.05 - 70898 0.0 S 63R 0:00.03 0:00.01 -_driverkit 70899 ?? 0.0 S 63R 0:38.05 0:25.27 /usr/bin/some_command with some parameters - 70899 0.0 S 31T 0:00.00 0:00.00 -root 71311 ?? 0.0 S 31T 0:00.37 0:00.25 /usr/bin/some_command with some parameters - 71311 0.0 S 19T 0:00.00 0:00.00 - 71311 0.0 S 4T 0:00.00 0:00.00 - 71311 0.0 S 4T 0:00.00 0:00.00 -someuser 75951 ?? 0.0 S 31T 0:03.06 0:05.41 /usr/bin/some_command with some parameters - 75951 0.0 S 31T 0:00.00 0:00.00 - 75951 0.0 S 0T 0:00.09 0:00.06 - 75951 0.0 S 31T 0:01.48 0:01.12 - 75951 0.0 S 31T 0:00.00 0:00.00 - 75951 0.0 S 31T 0:00.03 0:00.10 - 75951 0.0 S 31T 0:00.00 0:00.00 - 75951 0.0 S 31T 0:00.00 0:00.00 - 75951 0.0 S 31T 0:00.00 0:00.01 - 75951 0.0 S 31T 0:00.00 0:00.00 - 75951 0.0 S 31T 0:00.00 0:00.00 - 75951 0.0 S 31T 0:00.00 0:00.00 - 75951 0.0 S 31T 0:00.12 0:00.03 - 75951 0.0 S 31T 0:00.08 0:00.02 - 75951 0.0 S 31T 0:00.00 0:00.00 -someuser 76232 ?? 0.0 S 31T 0:15.22 0:22.98 /usr/bin/some_command with some parameters - 76232 0.0 S 31T 0:00.00 0:00.00 - 76232 0.0 S 0T 0:00.08 0:00.06 - 76232 0.0 S 31T 0:02.42 0:02.74 - 76232 0.0 S 31T 0:00.01 0:00.02 - 76232 0.0 S 31T 0:00.10 0:00.34 - 76232 0.0 S 31T 0:00.00 0:00.00 - 76232 0.0 S 31T 0:00.01 0:00.02 - 76232 0.0 S 31T 0:00.01 0:00.01 - 76232 0.0 S 31T 0:00.00 0:00.02 - 76232 0.0 S 31T 0:00.01 0:00.01 - 76232 0.0 S 31T 0:00.00 0:00.00 - 76232 0.0 S 31T 0:00.01 0:00.04 - 76232 0.0 S 31T 0:00.17 0:00.04 - 76232 0.0 S 31T 0:00.00 0:00.00 - 76232 0.0 S 31T 0:00.09 0:00.03 -someuser 79317 ?? 0.0 S 31T 0:05.42 0:07.07 /usr/bin/some_command with some parameters - 79317 0.0 S 31T 0:00.00 0:00.00 - 79317 0.0 S 0T 0:00.08 0:00.05 - 79317 0.0 S 31T 0:01.01 0:00.60 - 79317 0.0 S 31T 0:00.00 0:00.00 - 79317 0.0 S 31T 0:00.06 0:00.25 - 79317 0.0 S 31T 0:00.00 0:00.00 - 79317 0.0 S 31T 0:00.00 0:00.03 - 79317 0.0 S 31T 0:00.00 0:00.01 - 79317 0.0 S 31T 0:00.00 0:00.01 - 79317 0.0 S 31T 0:00.00 0:00.02 - 79317 0.0 S 31T 0:00.00 0:00.00 - 79317 0.0 S 31T 0:00.34 0:00.04 - 79317 0.0 S 31T 0:00.01 0:00.00 - 79317 0.0 S 31T 0:00.20 0:00.04 - 79317 0.0 S 31T 0:00.02 0:00.01 -someuser 79623 ?? 0.0 S 31T 0:10.60 0:19.96 /usr/bin/some_command with some parameters - 79623 0.0 S 31T 0:00.00 0:00.00 - 79623 0.0 S 0T 0:00.02 0:00.02 - 79623 0.0 S 31T 0:01.10 0:01.15 - 79623 0.0 S 31T 0:00.00 0:00.00 - 79623 0.0 S 31T 0:00.00 0:00.00 - 79623 0.0 S 31T 0:00.00 0:00.00 - 79623 0.0 S 31T 0:00.00 0:00.00 - 79623 0.0 S 31T 0:00.00 0:00.00 - 79623 0.0 S 31T 0:00.00 0:00.00 - 79623 0.0 S 31T 0:00.00 0:00.00 - 79623 0.0 S 31T 0:00.00 0:00.00 - 79623 0.0 S 31T 0:00.02 0:00.01 - 79623 0.0 S 31T 0:00.04 0:00.01 - 79623 0.0 S 31T 0:00.17 0:00.06 - 79623 0.0 S 31T 0:00.02 0:00.01 - 79623 0.0 S 31T 0:00.00 0:00.00 -someuser 79636 ?? 0.0 S 31T 0:10.19 0:18.21 /usr/bin/some_command with some parameters - 79636 0.0 S 31T 0:00.00 0:00.00 - 79636 0.0 S 0T 0:00.02 0:00.02 - 79636 0.0 S 31T 0:01.04 0:01.12 - 79636 0.0 S 31T 0:00.00 0:00.00 - 79636 0.0 S 31T 0:00.00 0:00.00 - 79636 0.0 S 31T 0:00.00 0:00.00 - 79636 0.0 S 31T 0:00.00 0:00.00 - 79636 0.0 S 31T 0:00.00 0:00.00 - 79636 0.0 S 31T 0:00.00 0:00.00 - 79636 0.0 S 31T 0:00.00 0:00.00 - 79636 0.0 S 31T 0:00.00 0:00.00 - 79636 0.0 S 31T 0:00.01 0:00.01 - 79636 0.0 S 31T 0:00.03 0:00.01 - 79636 0.0 S 31T 0:00.15 0:00.06 - 79636 0.0 S 31T 0:00.01 0:00.00 - 79636 0.0 S 31T 0:00.00 0:00.00 -someuser 79637 ?? 0.0 S 31T 0:00.26 0:00.20 /usr/bin/some_command with some parameters - 79637 0.0 S 31T 0:00.00 0:00.00 - 79637 0.0 S 31T 0:00.03 0:00.02 - 79637 0.0 S 0T 0:00.02 0:00.02 - 79637 0.0 S 31T 0:00.17 0:00.05 - 79637 0.0 S 31T 0:00.00 0:00.00 - 79637 0.0 S 31T 0:00.00 0:00.00 - 79637 0.0 S 31T 0:00.00 0:00.00 - 79637 0.0 S 31T 0:00.00 0:00.00 - 79637 0.0 S 31T 0:00.00 0:00.00 - 79637 0.0 S 31T 0:00.00 0:00.00 - 79637 0.0 S 31T 0:00.00 0:00.00 - 79637 0.0 S 31T 0:00.00 0:00.00 - 79637 0.0 S 31T 0:00.04 0:00.01 - 79637 0.0 S 31T 0:00.00 0:00.00 -someuser 79692 ?? 0.0 S 31T 0:11.25 0:18.01 /usr/bin/some_command with some parameters - 79692 0.0 S 31T 0:00.00 0:00.00 - 79692 0.0 S 0T 0:00.02 0:00.02 - 79692 0.0 S 31T 0:01.06 0:01.05 - 79692 0.0 S 31T 0:00.01 0:00.02 - 79692 0.0 S 31T 0:00.04 0:00.17 - 79692 0.0 S 31T 0:00.00 0:00.00 - 79692 0.0 S 31T 0:00.00 0:00.00 - 79692 0.0 S 31T 0:00.00 0:00.00 - 79692 0.0 S 31T 0:00.00 0:00.01 - 79692 0.0 S 31T 0:00.00 0:00.01 - 79692 0.0 S 31T 0:00.00 0:00.00 - 79692 0.0 S 31T 0:00.10 0:00.01 - 79692 0.0 S 31T 0:00.14 0:00.05 - 79692 0.0 S 31T 0:00.10 0:00.05 -someuser 79727 ?? 0.0 S 4T 0:47.45 1:38.46 /usr/bin/some_command with some parameters - 79727 0.0 S 4T 0:00.85 0:00.52 - 79727 0.0 S 4T 0:00.28 0:00.22 - 79727 0.0 S 4T 0:00.08 0:00.07 - 79727 0.0 S 4T 0:00.00 0:00.00 -someuser 79738 ?? 0.0 S 31T 0:12.99 0:22.71 /usr/bin/some_command with some parameters - 79738 0.0 S 31T 0:00.00 0:00.00 - 79738 0.0 S 0T 0:00.02 0:00.02 - 79738 0.0 S 31T 0:01.21 0:01.39 - 79738 0.0 S 31T 0:00.01 0:00.01 - 79738 0.0 S 31T 0:00.37 0:00.69 - 79738 0.0 S 31T 0:00.00 0:00.00 - 79738 0.0 S 31T 0:00.07 0:00.06 - 79738 0.0 S 31T 0:00.07 0:00.06 - 79738 0.0 S 31T 0:00.07 0:00.07 - 79738 0.0 S 31T 0:00.07 0:00.07 - 79738 0.0 S 31T 0:00.00 0:00.00 - 79738 0.0 S 31T 0:00.05 0:00.01 - 79738 0.0 S 31T 0:00.17 0:00.06 - 79738 0.0 S 31T 0:00.04 0:00.06 -someuser 80172 ?? 0.0 S 31T 0:03.00 0:06.88 /usr/bin/some_command with some parameters - 80172 0.0 S 31T 0:00.00 0:00.00 - 80172 0.0 S 0T 0:00.02 0:00.02 - 80172 0.0 S 31T 0:00.30 0:00.20 - 80172 0.0 S 31T 0:00.00 0:00.00 - 80172 0.0 S 31T 0:00.06 0:00.15 - 80172 0.0 S 31T 0:00.00 0:00.00 - 80172 0.0 S 31T 0:00.00 0:00.00 - 80172 0.0 S 31T 0:00.01 0:00.01 - 80172 0.0 S 31T 0:00.01 0:00.01 - 80172 0.0 S 31T 0:00.00 0:00.01 - 80172 0.0 S 31T 0:00.00 0:00.00 - 80172 0.0 S 31T 0:00.11 0:00.02 - 80172 0.0 S 31T 0:00.09 0:00.03 - 80172 0.0 S 31T 0:00.00 0:00.00 -someuser 87339 ?? 0.0 S 31T 0:16.87 1:17.95 /usr/bin/some_command with some parameters - 87339 0.0 S 31T 0:00.00 0:00.00 - 87339 0.0 S 0T 0:00.02 0:00.02 - 87339 0.0 S 31T 0:03.27 0:03.72 - 87339 0.0 S 31T 0:00.00 0:00.00 - 87339 0.0 S 31T 0:01.53 0:09.72 - 87339 0.0 S 31T 0:00.00 0:00.00 - 87339 0.0 S 31T 0:00.07 0:00.24 - 87339 0.0 S 31T 0:00.09 0:00.27 - 87339 0.0 S 31T 0:00.08 0:00.26 - 87339 0.0 S 31T 0:00.08 0:00.26 - 87339 0.0 S 31T 0:00.00 0:00.00 - 87339 0.0 S 31T 0:00.09 0:00.03 - 87339 0.0 S 31T 0:00.02 0:00.01 - 87339 0.0 S 31T 0:00.17 0:00.06 - 87339 0.0 S 31T 0:00.02 0:00.01 - 87339 0.0 S 31T 0:00.00 0:00.00 -someuser 89436 ?? 0.0 S 4T 0:00.04 0:00.02 /usr/bin/some_command with some parameters - 89436 0.0 S 4T 0:00.00 0:00.00 -someuser 89517 ?? 0.0 S 31T 1:21.10 12:08.73 /usr/bin/some_command with some parameters - 89517 0.0 S 31T 0:00.00 0:00.00 - 89517 0.0 S 0T 0:00.05 0:00.03 - 89517 0.0 S 31T 0:24.50 0:33.51 - 89517 0.0 S 31T 0:00.00 0:00.00 - 89517 0.0 S 31T 1:10.56 3:32.25 - 89517 0.0 S 31T 0:00.00 0:00.00 - 89517 0.0 S 31T 0:00.14 0:00.17 - 89517 0.0 S 31T 0:00.10 0:00.15 - 89517 0.0 S 31T 0:00.21 0:00.23 - 89517 0.0 S 31T 0:00.10 0:00.18 - 89517 0.0 S 31T 0:00.00 0:00.00 - 89517 0.0 S 31T 0:00.04 0:00.03 - 89517 0.0 S 31T 0:00.18 0:00.15 - 89517 0.0 S 31T 0:00.20 0:00.16 - 89517 0.0 S 31T 0:00.20 0:00.15 - 89517 0.0 S 31T 0:00.18 0:00.15 - 89517 0.0 S 31T 0:00.17 0:00.12 - 89517 0.0 S 31T 0:00.18 0:00.12 - 89517 0.0 S 31T 0:00.18 0:00.12 - 89517 0.0 S 31T 0:00.22 0:00.12 - 89517 0.0 S 31T 0:00.14 0:00.02 - 89517 0.0 S 31T 0:00.12 0:00.05 - 89517 0.0 S 31T 0:00.10 0:00.17 -someuser 92412 ?? 0.0 S 31T 0:19.36 1:03.93 /usr/bin/some_command with some parameters - 92412 0.0 S 31T 0:00.00 0:00.00 - 92412 0.0 S 0T 0:00.02 0:00.02 - 92412 0.0 S 31T 0:02.58 0:02.93 - 92412 0.0 S 31T 0:00.00 0:00.00 - 92412 0.0 S 31T 0:01.08 0:06.32 - 92412 0.0 S 31T 0:00.00 0:00.00 - 92412 0.0 S 31T 0:00.06 0:00.17 - 92412 0.0 S 31T 0:00.04 0:00.18 - 92412 0.0 S 31T 0:00.04 0:00.17 - 92412 0.0 S 31T 0:00.05 0:00.17 - 92412 0.0 S 31T 0:00.00 0:00.00 - 92412 0.0 S 31T 0:00.11 0:00.02 - 92412 0.0 S 31T 0:00.03 0:00.02 - 92412 0.0 S 31T 0:00.03 0:00.02 - 92412 0.0 S 31T 0:00.01 0:00.02 - 92412 0.0 S 31T 0:00.20 0:00.07 - 92412 0.0 S 31T 0:00.01 0:00.00 - 92412 0.0 S 31T 0:00.00 0:00.00 -someuser 96559 ?? 0.0 S 46T 0:13.24 0:26.60 /usr/bin/some_command with some parameters - 96559 0.0 S 46T 0:00.60 0:00.29 - 96559 0.0 S 31T 0:00.00 0:00.00 -someuser 97411 ?? 0.0 S 31T 0:01.26 0:01.54 /usr/bin/some_command with some parameters - 97411 0.0 S 31T 0:00.00 0:00.00 - 97411 0.0 S 0T 0:00.04 0:00.03 - 97411 0.0 S 31T 0:00.64 0:00.45 - 97411 0.0 S 31T 0:00.00 0:00.00 - 97411 0.0 S 31T 0:00.00 0:00.00 - 97411 0.0 S 31T 0:00.00 0:00.00 - 97411 0.0 S 31T 0:00.00 0:00.00 - 97411 0.0 S 31T 0:00.00 0:00.00 - 97411 0.0 S 31T 0:00.00 0:00.00 - 97411 0.0 S 31T 0:00.00 0:00.00 - 97411 0.0 S 31T 0:00.00 0:00.00 - 97411 0.0 S 31T 0:00.19 0:00.04 - 97411 0.0 S 31T 0:00.01 0:00.01 - 97411 0.0 S 31T 0:00.00 0:00.00 -someuser 98939 ?? 0.0 S 31T 0:05.17 0:23.65 /usr/bin/some_command with some parameters - 98939 0.0 S 31T 0:00.00 0:00.00 - 98939 0.0 S 0T 0:00.02 0:00.02 - 98939 0.0 S 31T 0:00.81 0:00.64 - 98939 0.0 S 31T 0:00.00 0:00.00 - 98939 0.0 S 31T 0:00.88 0:02.66 - 98939 0.0 S 31T 0:00.00 0:00.00 - 98939 0.0 S 31T 0:00.02 0:00.04 - 98939 0.0 S 31T 0:00.01 0:00.05 - 98939 0.0 S 31T 0:00.04 0:00.07 - 98939 0.0 S 31T 0:00.02 0:00.03 - 98939 0.0 S 31T 0:00.00 0:00.01 - 98939 0.0 S 31T 0:00.12 0:00.02 - 98939 0.0 S 31T 0:00.14 0:00.05 - 98939 0.0 S 31T 0:00.08 0:00.15 - 98939 0.0 S 31T 0:00.01 0:00.07 -someuser 99779 ?? 0.0 S 31T 0:02.61 0:08.14 /usr/bin/some_command with some parameters - 99779 0.0 S 31T 0:00.00 0:00.00 - 99779 0.0 S 0T 0:00.02 0:00.02 - 99779 0.0 S 31T 0:00.47 0:00.22 - 99779 0.0 S 31T 0:00.00 0:00.00 - 99779 0.0 S 31T 0:00.09 0:00.32 - 99779 0.0 S 31T 0:00.00 0:00.00 - 99779 0.0 S 31T 0:00.00 0:00.00 - 99779 0.0 S 31T 0:00.00 0:00.00 - 99779 0.0 S 31T 0:00.00 0:00.00 - 99779 0.0 S 31T 0:00.00 0:00.01 - 99779 0.0 S 31T 0:00.00 0:00.00 - 99779 0.0 S 31T 0:00.08 0:00.02 - 99779 0.0 S 31T 0:00.07 0:00.02 - 99779 0.0 S 31T 0:00.00 0:00.00 -someuser 99817 ?? 0.0 S 31T 0:00.60 0:00.17 /usr/bin/some_command with some parameters - 99817 0.0 S 31T 0:00.00 0:00.00 - 99817 0.0 S 31T 0:00.07 0:00.05 - 99817 0.0 S 0T 0:00.06 0:00.05 - 99817 0.0 S 31T 0:00.14 0:00.04 - 99817 0.0 S 31T 0:00.00 0:00.00 - 99817 0.0 S 31T 0:00.12 0:00.05 - 99817 0.0 S 31T 0:00.00 0:00.00 - 99817 0.0 S 31T 0:00.00 0:00.00 - 99817 0.0 S 31T 0:00.00 0:00.00 - 99817 0.0 S 31T 0:00.00 0:00.00 - 99817 0.0 S 31T 0:00.00 0:00.00 - 99817 0.0 S 31T 0:00.00 0:00.00 - 99817 0.0 S 31T 0:00.00 0:00.00 -someuser 99889 ?? 0.0 S 31T 0:02.90 0:03.20 /usr/bin/some_command with some parameters - 99889 0.0 S 31T 0:00.00 0:00.00 - 99889 0.0 S 0T 0:00.02 0:00.01 - 99889 0.0 S 31T 0:00.47 0:00.24 - 99889 0.0 S 31T 0:00.00 0:00.00 - 99889 0.0 S 31T 0:00.05 0:00.15 - 99889 0.0 S 31T 0:00.06 0:00.01 - 99889 0.0 S 31T 0:00.00 0:00.00 - 99889 0.0 S 31T 0:00.01 0:00.01 - 99889 0.0 S 31T 0:00.00 0:00.01 - 99889 0.0 S 31T 0:00.00 0:00.02 - 99889 0.0 S 31T 0:00.00 0:00.00 - 99889 0.0 S 31T 0:00.09 0:00.03 - 99889 0.0 S 31T 0:00.02 0:00.01 - 99889 0.0 S 31T 0:00.01 0:00.01 -root 2956 s000 0.0 S 31T 0:00.03 0:00.02 /usr/bin/some_command with some parameters - 2956 0.0 S 31T 0:00.00 0:00.00 -someuser 2959 s000 0.0 S 31T 0:00.30 0:00.26 -bash -someuser 6945 s000 0.0 S 31T 0:00.05 0:00.01 tmux -someuser 6948 s004 0.0 S 31T 0:00.58 0:00.38 -bash -someuser 6999 s005 0.0 S 31T 0:00.52 0:00.29 -bash -someuser 7049 s006 0.0 S 31T 0:00.20 0:00.20 -bash -someuser 11147 s007 0.0 S 31T 0:00.42 0:00.46 -bash -someuser 65815 s008 0.0 S 31T 0:01.10 0:00.67 -bash -someuser 1393 s010 0.0 S 31T 0:00.36 0:00.31 -bash -someuser 26136 s014 0.0 S 31T 0:00.54 0:00.33 /usr/bin/some_command with some parameters -someuser 42855 s026 0.0 S 31T 0:00.64 0:00.36 -bash -root 55350 s026 0.0 R 31T 0:00.00 0:00.00 ps -ax -M -` diff --git a/pkg/internal/infraolly/process/sampler_darwin.go b/pkg/internal/infraolly/process/sampler_darwin.go deleted file mode 100644 index 089c92396..000000000 --- a/pkg/internal/infraolly/process/sampler_darwin.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2020 New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 -package process - -import ( - "encoding/json" - "errors" - "fmt" - "strings" - "time" - - -) - -// processSampler is an implementation of the metrics_sender.Sampler interface, which returns runtime information about -// the currently running processes -type processSampler struct { - harvest Harvester - containerSamplers []metrics.ContainerSampler - lastRun time.Time - hasAlreadyRun bool - interval time.Duration -} - -var ( - _ sampler.Sampler = (*processSampler)(nil) // static interface assertion - containerNotRunningErrs = map[string]struct{}{} -) - -// NewProcessSampler creates and returns a new process Sampler, given an agent context. -func NewProcessSampler(ctx agent.AgentContext) sampler.Sampler { - hasConfig := ctx != nil && ctx.Config() != nil - - ttlSecs := config.DefaultContainerCacheMetadataLimit - apiVersion := "" - interval := config.FREQ_INTERVAL_FLOOR_PROCESS_METRICS - dockerContainerdNamespace := "" - if hasConfig { - cfg := ctx.Config() - ttlSecs = cfg.ContainerMetadataCacheLimit - apiVersion = cfg.DockerApiVersion - dockerContainerdNamespace = cfg.DockerContainerdNamespace - interval = cfg.MetricsProcessSampleRate - } - harvester := newHarvester(ctx) - containerSamplers := metrics.GetContainerSamplers(time.Duration(ttlSecs)*time.Second, apiVersion, dockerContainerdNamespace) - - return &processSampler{ - harvest: harvester, - containerSamplers: containerSamplers, - interval: time.Second * time.Duration(interval), - } - -} - -func (ps *processSampler) OnStartup() {} - -func (ps *processSampler) Name() string { - return "ProcessSampler" -} - -func (ps *processSampler) Interval() time.Duration { - return ps.interval -} - -func (ps *processSampler) Disabled() bool { - return ps.Interval() <= config.FREQ_DISABLE_SAMPLING -} - -// Sample returns samples for all the running processes, decorated with container runtime information, if applies. -func (ps *processSampler) Sample() (results sample.EventBatch, err error) { - var elapsedMs int64 - var elapsedSeconds float64 - now := time.Now() - if ps.hasAlreadyRun { - elapsedMs = (now.UnixNano() - ps.lastRun.UnixNano()) / 1000000 - } - elapsedSeconds = float64(elapsedMs) / 1000 - ps.lastRun = now - - pids, err := ps.harvest.Pids() - if err != nil { - return nil, err - } - - var containerDecorators []metrics.ProcessDecorator - - for _, containerSampler := range ps.containerSamplers { - if !containerSampler.Enabled() { - continue - } - - decorator, err := containerSampler.NewDecorator() - if err != nil { - // ensure containerDecorator is set to nil if error - decorator = nil - if id := containerIDFromNotRunningErr(err); id != "" { - if _, ok := containerNotRunningErrs[id]; !ok { - containerNotRunningErrs[id] = struct{}{} - mplog.WithError(err).Warn("instantiating container sampler process decorator") - } - } else { - mplog.WithError(err).Warn("instantiating container sampler process decorator") - if strings.Contains(err.Error(), "client is newer than server") { - mplog.WithError(err).Error("Only docker api version from 1.24 upwards are officially supported. You can still use the docker_api_version configuration to work with older versions. You can check https://docs.docker.com/develop/sdk/ what api version maps with each docker version.") - } - } - } else { - containerDecorators = append(containerDecorators, decorator) - } - } - - for _, pid := range pids { - var processSample *Sample - var err error - - processSample, err = ps.harvest.Do(pid, elapsedSeconds) - if err != nil { - procLog := mplog.WithError(err) - if errors.Is(err, errProcessWithoutRSS) { - procLog = procLog.WithField(config.TracesFieldName, config.ProcessTrace) - } - - procLog.WithField("pid", pid).Debug("Skipping process.") - continue - } - - for _, containerDecorator := range containerDecorators { - if containerDecorator != nil { - containerDecorator.Decorate(processSample) - } - } - - results = append(results, ps.normalizeSample(processSample)) - } - - ps.hasAlreadyRun = true - return results, nil -} - -func (ps *processSampler) normalizeSample(s *Sample) sample.Event { - if len(s.ContainerLabels) > 0 { - sb, err := json.Marshal(s) - if err == nil { - bm := &types.FlatProcessSample{} - if err = json.Unmarshal(sb, bm); err == nil { - for name, value := range s.ContainerLabels { - key := fmt.Sprintf("containerLabel_%s", name) - (*bm)[key] = value - } - return bm - } - } else { - mplog.WithError(err).WithField("sample", fmt.Sprintf("%+v", s)).Debug("normalizeSample can't operate on the sample.") - } - } - return s -} - -func containerIDFromNotRunningErr(err error) string { - prefix := "Error response from daemon: Container " - suffix := " is not running" - msg := err.Error() - i := strings.Index(msg, prefix) - j := strings.Index(msg, suffix) - if i == -1 || j == -1 { - return "" - } - return msg[len(prefix):j] -} diff --git a/pkg/internal/infraolly/process/sampler_darwin_test.go b/pkg/internal/infraolly/process/sampler_darwin_test.go deleted file mode 100644 index f5e06bb2e..000000000 --- a/pkg/internal/infraolly/process/sampler_darwin_test.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2020 New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 -package process - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func TestProcessSampler_Sample(t *testing.T) { - ctx := new(mocks.AgentContext) - cfg := &config.Config{RunMode: config.ModeRoot} - ctx.On("Config").Times(3).Return(cfg) - - harvester := &HarvesterMock{} - sampler := NewProcessSampler(ctx).(*processSampler) - sampler.harvest = harvester - - samples := []*Sample{ - { - ProcessDisplayName: "proc 1", - ProcessID: 1, - }, - { - ProcessDisplayName: "proc 2", - ProcessID: 2, - }, - { - ProcessDisplayName: "proc 3", - ProcessID: 3, - }, - } - var pids []int32 - for _, s := range samples { - pids = append(pids, s.ProcessID) - } - - harvester.ShouldReturnPids(pids, nil) - for _, s := range samples { - harvester.ShouldDo(s.ProcessID, 0, s, nil) - } - - eventBatch, err := sampler.Sample() - assert.Nil(t, err) - assert.Len(t, eventBatch, len(samples)) - for i, e := range eventBatch { - assert.Equal(t, samples[i], e) - } - - mock.AssertExpectationsForObjects(t, ctx, harvester) -} - -func TestProcessSampler_Sample_ErrorOnProcessShouldNotStop(t *testing.T) { - ctx := new(mocks.AgentContext) - cfg := &config.Config{RunMode: config.ModeRoot} - ctx.On("Config").Times(3).Return(cfg) - - harvester := &HarvesterMock{} - sampler := NewProcessSampler(ctx).(*processSampler) - sampler.harvest = harvester - - samples := []struct { - pid int32 - name string - err error - }{ - { - name: "proc 1", - pid: 1, - }, - { - name: "proc 2", - pid: 2, - err: errors.New("some error"), - }, - { - name: "proc 3", - pid: 3, - }, - } - var pids []int32 - for _, s := range samples { - pids = append(pids, s.pid) - } - - harvester.ShouldReturnPids(pids, nil) - for _, s := range samples { - harvester.ShouldDo(s.pid, 0, &Sample{ProcessID: s.pid, ProcessDisplayName: s.name}, s.err) - } - - eventBatch, err := sampler.Sample() - assert.Nil(t, err) - assert.Len(t, eventBatch, 2) - assert.Equal(t, int32(1), eventBatch[0].(*Sample).ProcessID) - assert.Equal(t, int32(3), eventBatch[1].(*Sample).ProcessID) - - mock.AssertExpectationsForObjects(t, ctx, harvester) -} - -func TestProcessSampler_Sample_DockerDecorator(t *testing.T) { - ctx := new(mocks.AgentContext) - cfg := &config.Config{RunMode: config.ModeRoot} - ctx.On("Config").Times(3).Return(cfg) - - harvester := &HarvesterMock{} - sampler := NewProcessSampler(ctx).(*processSampler) - sampler.harvest = harvester - sampler.containerSamplers = []metrics.ContainerSampler{&fakeContainerSampler{}} - - samples := []*Sample{ - { - ProcessDisplayName: "proc 1", - ProcessID: 1, - }, - { - ProcessDisplayName: "proc 2", - ProcessID: 2, - }, - { - ProcessDisplayName: "proc 3", - ProcessID: 3, - }, - } - var pids []int32 - for _, s := range samples { - pids = append(pids, s.ProcessID) - } - - harvester.ShouldReturnPids(pids, nil) - for _, s := range samples { - harvester.ShouldDo(s.ProcessID, 0, s, nil) - } - - eventBatch, err := sampler.Sample() - assert.Nil(t, err) - assert.Len(t, eventBatch, len(samples)) - for i, e := range eventBatch { - flatProcessSample := e.(*types.FlatProcessSample) - assert.Equal(t, float64(samples[i].ProcessID), (*flatProcessSample)["processId"]) - assert.Equal(t, samples[i].ProcessDisplayName, (*flatProcessSample)["processDisplayName"]) - assert.Equal(t, "decorated", (*flatProcessSample)["containerImage"]) - assert.Equal(t, "value1", (*flatProcessSample)["containerLabel_label1"]) - assert.Equal(t, "value2", (*flatProcessSample)["containerLabel_label2"]) - } - - mock.AssertExpectationsForObjects(t, ctx, harvester) -} diff --git a/pkg/internal/infraolly/process/sampler_unix_test.go b/pkg/internal/infraolly/process/sampler_unix_test.go deleted file mode 100644 index 5ebf1489f..000000000 --- a/pkg/internal/infraolly/process/sampler_unix_test.go +++ /dev/null @@ -1,28 +0,0 @@ -//go:build !windows -// +build !windows - -// Copyright New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package process - - -type fakeContainerSampler struct{} - -func (cs *fakeContainerSampler) Enabled() bool { - return true -} - -func (*fakeContainerSampler) NewDecorator() (metrics.ProcessDecorator, error) { //nolint:ireturn - return &fakeDecorator{}, nil -} - -type fakeDecorator struct{} - -func (pd *fakeDecorator) Decorate(process *Sample) { - process.ContainerImage = "decorated" - process.ContainerLabels = map[string]string{ - "label1": "value1", - "label2": "value2", - } -} diff --git a/pkg/internal/infraolly/process/snapshot_darwin.go b/pkg/internal/infraolly/process/snapshot_darwin.go deleted file mode 100644 index d9f5a6f7b..000000000 --- a/pkg/internal/infraolly/process/snapshot_darwin.go +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2020 New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 -package process - -import ( - "os" - "runtime" - "strings" - "time" - - "github.com/shirou/gopsutil/v3/process" - -) - -// darwinProcess is an implementation of the process.Snapshot interface for darwin hosts. -type darwinProcess struct { - // if privileged == false, some operations will be avoided: FD and IO count - privileged bool - - stats procStats - process Process - lastCPU CPUInfo - lastTime time.Time - - // data that will be reused between samples of the same process - pid int32 - user string - cmdLine string -} - -// needed to calculate RSS -var pageSize int64 - -func init() { - pageSize = int64(os.Getpagesize()) - if pageSize <= 0 { - pageSize = 4096 // default value - } -} - -var _ Snapshot = (*darwinProcess)(nil) // static interface assertion - -// getDarwinProcess returns a darwin process snapshot, trying to reuse the data from a previous snapshot of the same -// process. -func getDarwinProcess(proc Process, privileged bool) (*darwinProcess, error) { - - stats, err := collectProcStats(proc) - if err != nil { - return nil, err - } - - return &darwinProcess{ - privileged: privileged, - pid: proc.ProcessId(), - process: proc, - stats: stats, - }, nil -} - -func (pw *darwinProcess) Pid() int32 { - return pw.pid -} - -func (pw *darwinProcess) Username() (string, error) { - var err error - if pw.user == "" { // caching user - pw.user, err = pw.process.Username() - if err != nil { - return "", err - } - } - return pw.user, nil -} - -func (pw *darwinProcess) IOCounters() (*process.IOCountersStat, error) { - //Not implemented in darwin yet - return nil, nil -} - -// NumFDs returns the number of file descriptors. It returns -1 (and nil error) if the Agent does not have privileges to -// access this information. -func (pw *darwinProcess) NumFDs() (int32, error) { - //Not implemented in darwin yet - return -1, nil -} - -// /////////////////////////// -// Data to be derived from /proc//stat in linux systems. In darwin this structure will be populated -// if no error happens retrieving the information from process and will allow to cache some process vallues -// to avoid calling multiple times to same method -// /////////////////////////// -type procStats struct { - command string - ppid int32 - numThreads int32 - state string - vmRSS int64 - vmSize int64 - cpu CPUInfo -} - -// collectProcStats will gather information about the process and will return procStats struct with the necessary information -// not to call process methods more than once per iteration. It will return error if any of the expected -// items returns an error. -func collectProcStats(p Process) (procStats, error) { - var s procStats - name, err := p.Name() - if err != nil { - return s, err - } - - var ppid int32 - var parent Process - if p.ProcessId() != 1 { - parent, err = p.Parent() - if err == nil { - ppid = parent.ProcessId() - } - } - numThreads, err := p.NumThreads() - if err != nil { - return s, err - } - status, err := p.Status() - if err != nil { - return s, err - } - memInfo, err := p.MemoryInfo() - if err != nil { - return s, err - } - cpuPercent, err := p.CPUPercent() - if err != nil { - return s, err - } - times, err := p.Times() - if err != nil { - return s, err - } - - // unit64 to int64 conversion so there are options to lose data if rss > 9,223 PetaBytes - rss := int64(memInfo.RSS) - if rss > 0 { - s.vmRSS = rss - } - vms := int64(memInfo.VMS) - if vms > 0 { - s.vmSize = vms - } - - s.command = name - s.ppid = ppid - s.numThreads = numThreads - if len(status) > 0 { - s.state = status[0] - } - s.cpu = CPUInfo{ - Percent: cpuPercent, - User: times.User, - System: times.System, - } - - return s, nil -} - -func (pw *darwinProcess) CPUTimes() (CPUInfo, error) { - now := time.Now() - - if pw.lastTime.IsZero() { - // invoked first time - pw.lastCPU = pw.stats.cpu - pw.lastTime = now - return pw.stats.cpu, nil - } - - // Calculate CPU percent from user time, system time, and last harvested cpu counters - numcpu := runtime.NumCPU() - delta := (now.Sub(pw.lastTime).Seconds()) * float64(numcpu) - pw.stats.cpu.Percent = calculatePercent(pw.lastCPU, pw.stats.cpu, delta, numcpu) - pw.lastCPU = pw.stats.cpu - pw.lastTime = now - - return pw.stats.cpu, nil -} - -func calculatePercent(t1, t2 CPUInfo, delta float64, numcpu int) float64 { - if delta <= 0 { - return 0 - } - deltaProc := t2.User + t2.System - t1.User - t1.System - overallPercent := ((deltaProc / delta) * 100) * float64(numcpu) - return overallPercent -} - -func (pw *darwinProcess) Ppid() int32 { - return pw.stats.ppid -} - -func (pw *darwinProcess) NumThreads() int32 { - return pw.stats.numThreads -} - -func (pw *darwinProcess) Status() string { - return pw.stats.state -} - -func (pw *darwinProcess) VmRSS() int64 { - return pw.stats.vmRSS -} - -func (pw *darwinProcess) VmSize() int64 { - return pw.stats.vmSize -} - -func (pw *darwinProcess) Command() string { - return pw.stats.command -} - -// CmdLine is taken from ps. As commands can have spaces, it's difficult parse parameters -// so no params for now -func (pw *darwinProcess) CmdLine(withArgs bool) (string, error) { - if pw.cmdLine != "" { - return pw.cmdLine, nil - } - - procCmdline, err := pw.process.Cmdline() - if err != nil { - return "", nil - } - - if len(procCmdline) == 0 { - return "", nil // zombie process - } - - // Ignoring dash on session commands - if procCmdline[0] == '-' { - procCmdline = procCmdline[1:] - } - - if !withArgs { - parts := strings.Split(procCmdline, " ") - procCmdline = parts[0] - } - - pw.cmdLine = helpers.SanitizeCommandLine(procCmdline) - return pw.cmdLine, nil -} diff --git a/pkg/internal/infraolly/process/snapshot_darwin_test.go b/pkg/internal/infraolly/process/snapshot_darwin_test.go deleted file mode 100644 index 77122dee0..000000000 --- a/pkg/internal/infraolly/process/snapshot_darwin_test.go +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright 2020 New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 -package process - -import ( - "errors" - "testing" - - "github.com/shirou/gopsutil/v3/cpu" - "github.com/shirou/gopsutil/v3/process" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_collectProcStats_NameError(t *testing.T) { - proc := &ProcessMock{} - expectedError := errors.New("some error") - - proc.ShouldReturnName("", expectedError) - - stats, err := collectProcStats(proc) - - assert.Equal(t, expectedError, err) - assert.Equal(t, procStats{}, stats) - //mocked objects assertions - mock.AssertExpectationsForObjects(t, proc) -} - -func Test_collectProcStats_NumThreadsError(t *testing.T) { - proc := &ProcessMock{} - expectedError := errors.New("some error") - - proc.ShouldReturnName("some name", nil) - proc.ShouldReturnProcessId(1) - proc.ShouldReturnNumThreads(0, expectedError) - - stats, err := collectProcStats(proc) - - assert.Equal(t, expectedError, err) - assert.Equal(t, procStats{}, stats) - //mocked objects assertions - mock.AssertExpectationsForObjects(t, proc) -} - -func Test_collectProcStats_StatusError(t *testing.T) { - proc := &ProcessMock{} - expectedError := errors.New("some error") - - proc.ShouldReturnName("some name", nil) - proc.ShouldReturnProcessId(1) - proc.ShouldReturnNumThreads(4, nil) - proc.ShouldReturnStatus([]string{}, expectedError) - - stats, err := collectProcStats(proc) - - assert.Equal(t, expectedError, err) - assert.Equal(t, procStats{}, stats) - //mocked objects assertions - mock.AssertExpectationsForObjects(t, proc) -} - -func Test_collectProcStats_MemoryInfoError(t *testing.T) { - proc := &ProcessMock{} - expectedError := errors.New("some error") - - proc.ShouldReturnName("some name", nil) - proc.ShouldReturnProcessId(1) - proc.ShouldReturnNumThreads(4, nil) - proc.ShouldReturnStatus([]string{"some status"}, nil) - proc.ShouldReturnMemoryInfo(&process.MemoryInfoStat{}, expectedError) - - stats, err := collectProcStats(proc) - - assert.Equal(t, expectedError, err) - assert.Equal(t, procStats{}, stats) - //mocked objects assertions - mock.AssertExpectationsForObjects(t, proc) -} - -func Test_collectProcStats_CpuPercentError(t *testing.T) { - proc := &ProcessMock{} - expectedError := errors.New("some error") - - proc.ShouldReturnName("some name", nil) - proc.ShouldReturnProcessId(1) - proc.ShouldReturnNumThreads(4, nil) - proc.ShouldReturnStatus([]string{"some status"}, nil) - proc.ShouldReturnMemoryInfo(&process.MemoryInfoStat{}, nil) - proc.ShouldReturnCPUPercent(0, expectedError) - - stats, err := collectProcStats(proc) - - assert.Equal(t, expectedError, err) - assert.Equal(t, procStats{}, stats) - //mocked objects assertions - mock.AssertExpectationsForObjects(t, proc) -} - -func Test_collectProcStats_CpuTimesError(t *testing.T) { - proc := &ProcessMock{} - expectedError := errors.New("some error") - - proc.ShouldReturnName("some name", nil) - proc.ShouldReturnProcessId(1) - proc.ShouldReturnNumThreads(4, nil) - proc.ShouldReturnStatus([]string{"some status"}, nil) - proc.ShouldReturnMemoryInfo(&process.MemoryInfoStat{}, nil) - proc.ShouldReturnCPUPercent(0, nil) - proc.ShouldReturnTimes(&cpu.TimesStat{}, expectedError) - - stats, err := collectProcStats(proc) - - assert.Equal(t, expectedError, err) - assert.Equal(t, procStats{}, stats) - //mocked objects assertions - mock.AssertExpectationsForObjects(t, proc) -} - -func Test_collectProcStats_NoErrorsInitProcess(t *testing.T) { - tests := []struct { - name string - command string - processId int32 - numThreads int32 - status string - memStat *process.MemoryInfoStat - cpuPercent float64 - timesStat *cpu.TimesStat - expected procStats - }{ - { - name: "invalid rss", - command: "some command", - processId: 1, - numThreads: 3, - status: "some status", - memStat: &process.MemoryInfoStat{ - RSS: 0, - VMS: 232, - }, - cpuPercent: 10, - timesStat: &cpu.TimesStat{User: 2, System: 8}, - expected: procStats{ - command: "some command", - ppid: 0, - numThreads: 3, - state: "some status", - vmRSS: 0, - vmSize: 232, - cpu: CPUInfo{ - Percent: 10, - User: 2, - System: 8, - }, - }, - }, - { - name: "invalid vmsize", - command: "some command", - processId: 1, - numThreads: 3, - status: "some status", - memStat: &process.MemoryInfoStat{ - RSS: 45, - VMS: 0, - }, - cpuPercent: 10, - timesStat: &cpu.TimesStat{User: 2, System: 8}, - expected: procStats{ - command: "some command", - ppid: 0, - numThreads: 3, - state: "some status", - vmRSS: 45, - vmSize: 0, - cpu: CPUInfo{ - Percent: 10, - User: 2, - System: 8, - }, - }, - }, - { - name: "happy path", - command: "some command", - processId: 1, - numThreads: 3, - status: "some status", - memStat: &process.MemoryInfoStat{ - RSS: 45, - VMS: 22, - }, - cpuPercent: 10, - timesStat: &cpu.TimesStat{User: 2, System: 8}, - expected: procStats{ - command: "some command", - ppid: 0, - numThreads: 3, - state: "some status", - vmRSS: 45, - vmSize: 22, - cpu: CPUInfo{ - Percent: 10, - User: 2, - System: 8, - }, - }, - }, - } - - proc := &ProcessMock{} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - proc.ShouldReturnName(tt.command, nil) - proc.ShouldReturnProcessId(tt.processId) - proc.ShouldReturnNumThreads(tt.numThreads, nil) - proc.ShouldReturnStatus([]string{tt.status}, nil) - proc.ShouldReturnMemoryInfo(tt.memStat, nil) - proc.ShouldReturnCPUPercent(tt.cpuPercent, nil) - proc.ShouldReturnTimes(tt.timesStat, nil) - - stats, err := collectProcStats(proc) - - assert.Nil(t, err) - assert.Equal(t, tt.expected, stats) - //mocked objects assertions - mock.AssertExpectationsForObjects(t, proc) - }) - } -} - -func Test_collectProcStats_NoErrorsProcessWithParent(t *testing.T) { - tests := []struct { - name string - command string - processId int32 - parentProcessId int32 - parentProcessErr error - numThreads int32 - status string - memStat *process.MemoryInfoStat - cpuPercent float64 - timesStat *cpu.TimesStat - expected procStats - }{ - { - name: "parent process ok", - command: "some command", - processId: 16, - parentProcessId: 11, - parentProcessErr: nil, - numThreads: 3, - status: "some status", - memStat: &process.MemoryInfoStat{ - RSS: 0, - VMS: 232, - }, - cpuPercent: 10, - timesStat: &cpu.TimesStat{User: 2, System: 8}, - expected: procStats{ - command: "some command", - ppid: 11, - numThreads: 3, - state: "some status", - vmRSS: 0, - vmSize: 232, - cpu: CPUInfo{ - Percent: 10, - User: 2, - System: 8, - }, - }, - }, - { - name: "error getting parent process", - command: "some command", - processId: 16, - parentProcessId: 11, - parentProcessErr: errors.New("some error"), - numThreads: 3, - status: "some status", - memStat: &process.MemoryInfoStat{ - RSS: 0, - VMS: 232, - }, - cpuPercent: 10, - timesStat: &cpu.TimesStat{User: 2, System: 8}, - expected: procStats{ - command: "some command", - ppid: 0, - numThreads: 3, - state: "some status", - vmRSS: 0, - vmSize: 232, - cpu: CPUInfo{ - Percent: 10, - User: 2, - System: 8, - }, - }, - }, - } - - parentProc := &ProcessMock{} - proc := &ProcessMock{} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - proc.ShouldReturnName(tt.command, nil) - proc.ShouldReturnParent(parentProc, tt.parentProcessErr) - if tt.parentProcessErr == nil { - parentProc.ShouldReturnProcessId(tt.parentProcessId) - } - proc.ShouldReturnProcessId(tt.processId) - proc.ShouldReturnNumThreads(tt.numThreads, nil) - proc.ShouldReturnStatus([]string{tt.status}, nil) - proc.ShouldReturnMemoryInfo(tt.memStat, nil) - proc.ShouldReturnCPUPercent(tt.cpuPercent, nil) - proc.ShouldReturnTimes(tt.timesStat, nil) - - stats, err := collectProcStats(proc) - - assert.Nil(t, err) - assert.Equal(t, tt.expected, stats) - //mocked objects assertions - mock.AssertExpectationsForObjects(t, proc) - }) - } -} - -func Test_calculatePercent(t *testing.T) { - tests := []struct { - name string - t1 CPUInfo - t2 CPUInfo - delta float64 - numcpu int - expectedPercent float64 - }{ - { - name: "delta 0", - expectedPercent: 0, - }, - { - name: "bad delta", - delta: -1, - expectedPercent: 0, - }, - { - name: "good delta", - delta: 10, - numcpu: 2, - t1: CPUInfo{ - User: 24, - System: 33, - }, - t2: CPUInfo{ - User: 42, - System: 55, - }, - expectedPercent: ((44 / 10) * 100) * 2, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - percent := calculatePercent(tt.t1, tt.t2, tt.delta, tt.numcpu) - assert.Equal(t, tt.expectedPercent, percent) - }) - } -} - -//nolint:exhaustruct -func Test_Calculate_Process_CmdLine(t *testing.T) { - t.Parallel() - tests := []struct { - name string - cmdLine string - args bool - expected string - }{ - { - name: "empty", - cmdLine: "", - expected: "", - }, - { - name: "ignoring dash on session commands", - cmdLine: "-zsh", - expected: "zsh", - }, - { - name: "no arguments & args enabled", - cmdLine: "/sbin/launchd", - args: true, - expected: "/sbin/launchd", - }, - { - name: "no arguments & args disabled", - cmdLine: "/sbin/launchd", - args: false, - expected: "/sbin/launchd", - }, - { - name: "arguments & args enabled", - cmdLine: "/sbin/launchd -arg_a=1 -arg_b 2", - args: true, - expected: "/sbin/launchd -arg_a=1 -arg_b 2", - }, - { - name: "arguments & args disabled", - cmdLine: "/sbin/launchd -arg_a=1 -arg_b 2", - args: false, - expected: "/sbin/launchd", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - process := &ProcessMock{} - process.ShouldReturnCmdLine(tt.cmdLine, nil) - darwinProcess := darwinProcess{ - process: process, - } - - result, err := darwinProcess.CmdLine(tt.args) - assert.NoError(t, err) - assert.Equal(t, tt.expected, result) - }) - } -} From ba134ab9aee71e2f12f2b9b85e85ad57ceb7273e Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Mon, 27 May 2024 16:09:29 +0200 Subject: [PATCH 05/27] first refactor version without any integration --- pkg/internal/infraolly/process/cache.go | 1 - pkg/internal/infraolly/process/harvester.go | 171 +++++++- .../infraolly/process/harvester_linux.go | 166 ------- ...vester_linux_test.go => harvester_test.go} | 14 +- pkg/internal/infraolly/process/sample.go | 8 + pkg/internal/infraolly/process/sampler.go | 63 +++ .../infraolly/process/sampler_linux.go | 175 -------- .../infraolly/process/sampler_linux_test.go | 187 -------- pkg/internal/infraolly/process/snapshot.go | 408 ++++++++++++++++-- .../infraolly/process/snapshot_linux.go | 379 ---------------- .../infraolly/process/snapshot_mock_test.go | 169 -------- ...napshot_linux_test.go => snapshot_test.go} | 22 +- 12 files changed, 636 insertions(+), 1127 deletions(-) delete mode 100644 pkg/internal/infraolly/process/harvester_linux.go rename pkg/internal/infraolly/process/{harvester_linux_test.go => harvester_test.go} (92%) create mode 100644 pkg/internal/infraolly/process/sampler.go delete mode 100644 pkg/internal/infraolly/process/sampler_linux.go delete mode 100644 pkg/internal/infraolly/process/sampler_linux_test.go delete mode 100644 pkg/internal/infraolly/process/snapshot_linux.go delete mode 100644 pkg/internal/infraolly/process/snapshot_mock_test.go rename pkg/internal/infraolly/process/{snapshot_linux_test.go => snapshot_test.go} (93%) diff --git a/pkg/internal/infraolly/process/cache.go b/pkg/internal/infraolly/process/cache.go index 41ff229fb..688a84b9e 100644 --- a/pkg/internal/infraolly/process/cache.go +++ b/pkg/internal/infraolly/process/cache.go @@ -1,7 +1,6 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 //go:build linux -// +build linux package process diff --git a/pkg/internal/infraolly/process/harvester.go b/pkg/internal/infraolly/process/harvester.go index 7be39f1fe..3621fa1e3 100644 --- a/pkg/internal/infraolly/process/harvester.go +++ b/pkg/internal/infraolly/process/harvester.go @@ -1,20 +1,27 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +//go:build linux + // Package process provides all the tools and functionality for sampling processes. It is divided in three main // components: -// - Snapshot: provides OS-level information of a process at a given spot -// - Harvester: manages process Snapshots to create actual Process Samples with the actual metrics. +// - *linuxProcess: provides OS-level information of a process at a given spot +// - Harvester: manages process *linuxProcesss to create actual Process Samples with the actual metrics. // - Sampler: uses the harvester to coordinate the creation of the Process Samples dataset, as being reported to NR package process import ( "fmt" "log/slog" + "time" + + "github.com/hashicorp/golang-lru/v2/simplelru" + "github.com/pkg/errors" + "github.com/shirou/gopsutil/v3/process" ) -func mplog() *slog.Logger { - return slog.With("component", "process.Sampler") +func hlog() *slog.Logger { + return slog.With("component", "process.Harvester") } var errProcessWithoutRSS = fmt.Errorf("process with zero rss") @@ -39,5 +46,159 @@ const ( type Config struct { RunMode RunMode DisableZeroRSSFilter bool - FullCommandLine bool + FullCommandLine bool + + ProcFSRoot string + Rate time.Duration +} + +func newHarvester(cfg Config, cache *simplelru.LRU[int32, *cacheEntry]) *linuxHarvester { + // If not config, assuming root mode as default + privileged := cfg.RunMode == RunModeRoot || cfg.RunMode == RunModePrivileged + disableZeroRSSFilter := cfg.DisableZeroRSSFilter + stripCommandLine := !cfg.FullCommandLine + + return &linuxHarvester{ + procFSRoot: cfg.ProcFSRoot, + privileged: privileged, + disableZeroRSSFilter: disableZeroRSSFilter, + stripCommandLine: stripCommandLine, + cache: cache, + log: hlog(), + } +} + +// linuxHarvester is a Harvester implementation that uses various linux sources and manages process caches +type linuxHarvester struct { + procFSRoot string + privileged bool + disableZeroRSSFilter bool + stripCommandLine bool + cache *simplelru.LRU[int32, *cacheEntry] + log *slog.Logger +} + +var _ Harvester = (*linuxHarvester)(nil) // static interface assertion + +// Pids returns a slice of process IDs that are running now +func (*linuxHarvester) Pids() ([]int32, error) { + return process.Pids() +} + +// Do returns a sample of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the +// time since this process was sampled for the last time. If the process has been sampled for the first time, this value +// will be ignored +func (ps *linuxHarvester) Do(pid int32) (*Sample, error) { + // Reuses process information that does not vary + cached, hasCachedSample := ps.cache.Get(pid) + + // If cached is nil, the linux process will be created from fresh data + if !hasCachedSample { + cached = &cacheEntry{} + } + var err error + cached.process, err = getLinuxProcess(ps.procFSRoot, pid, cached.process, ps.privileged) + if err != nil { + return nil, errors.Wrap(err, "can't create process") + } + + // We don't need to report processes which are not using memory. This filters out certain kernel processes. + if !ps.disableZeroRSSFilter && cached.process.VMRSS() == 0 { + return nil, errProcessWithoutRSS + } + + // Creates a fresh process sample and populates it with the metrics data + sample := NewSample(pid) + + if err := ps.populateStaticData(sample, cached.process); err != nil { + return nil, errors.Wrap(err, "can't populate static attributes") + } + + // As soon as we have successfully stored the static (reusable) values, we can cache the entry + if !hasCachedSample { + ps.cache.Add(pid, cached) + } + + if err := ps.populateGauges(sample, cached.process); err != nil { + return nil, errors.Wrap(err, "can't fetch gauge data") + } + + if err := ps.populateIOCounters(sample, cached.process); err != nil { + return nil, errors.Wrap(err, "can't fetch deltas") + } + + cached.lastSample = sample + + return sample, nil +} + +// populateStaticData populates the sample with the process data won't vary during the process life cycle +func (ps *linuxHarvester) populateStaticData(sample *Sample, process *linuxProcess) error { + var err error + sample.CmdLine, err = process.CmdLine(!ps.stripCommandLine) + if err != nil { + return errors.Wrap(err, "acquiring command line") + } + + sample.ProcessID = process.Pid() + + sample.User, err = process.Username() + if err != nil { + ps.log.Debug("can't get username for process", "pid", sample.ProcessID, "error", err) + } + + sample.Command = process.Command() + sample.ParentProcessID = process.Ppid() + + return nil +} + +// populateGauges populates the sample with gauge data that represents the process state at a given point +func (ps *linuxHarvester) populateGauges(sample *Sample, process *linuxProcess) error { + var err error + + cpuTimes, err := process.CPUTimes() + if err != nil { + return err + } + sample.CPUPercent = cpuTimes.Percent + + totalCPU := cpuTimes.User + cpuTimes.System + + if totalCPU > 0 { + sample.CPUUserPercent = (cpuTimes.User / totalCPU) * sample.CPUPercent + sample.CPUSystemPercent = (cpuTimes.System / totalCPU) * sample.CPUPercent + } else { + sample.CPUUserPercent = 0 + sample.CPUSystemPercent = 0 + } + + if ps.privileged { + sample.FdCount, err = process.NumFDs() + if err != nil { + return err + } + } + + // Extra status data + sample.Status = process.Status() + sample.ThreadCount = process.NumThreads() + sample.MemoryVMSBytes = process.VMSize() + sample.MemoryRSSBytes = process.VMRSS() + + return nil +} + +// populateIOCounters fills the sample with the IO counters data. For the "X per second" metrics, it requires the +// last process sample for comparative purposes +func (ps *linuxHarvester) populateIOCounters(sample *Sample, source *linuxProcess) error { + ioCounters, err := source.IOCounters() + if err != nil { + return err + } + sample.IOReadCount = ioCounters.ReadCount + sample.IOWriteCount = ioCounters.WriteCount + sample.IOReadBytes = ioCounters.ReadBytes + sample.IOWriteBytes = ioCounters.WriteBytes + return nil } diff --git a/pkg/internal/infraolly/process/harvester_linux.go b/pkg/internal/infraolly/process/harvester_linux.go deleted file mode 100644 index 392763d4d..000000000 --- a/pkg/internal/infraolly/process/harvester_linux.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2020 New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package process provides all the tools and functionality for sampling processes. It is divided in three main -// components: -// - Snapshot: provides OS-level information of a process at a given spot -// - Harvester: manages process Snapshots to create actual Process Samples with the actual metrics. -// - Sampler: uses the harvester to coordinate the creation of the Process Samples dataset, as being reported to NR -package process - -import ( - "log/slog" - - "github.com/hashicorp/golang-lru/v2/simplelru" - "github.com/pkg/errors" - "github.com/shirou/gopsutil/v3/process" -) - -func newHarvester(cfg Config, cache *simplelru.LRU[int32, *cacheEntry]) *linuxHarvester { - // If not config, assuming root mode as default - privileged := cfg.RunMode == RunModeRoot || cfg.RunMode == RunModePrivileged - disableZeroRSSFilter := cfg.DisableZeroRSSFilter - stripCommandLine := !cfg.FullCommandLine - - return &linuxHarvester{ - privileged: privileged, - disableZeroRSSFilter: disableZeroRSSFilter, - stripCommandLine: stripCommandLine, - cache: cache, - log: mplog(), - } -} - -// linuxHarvester is a Harvester implementation that uses various linux sources and manages process caches -type linuxHarvester struct { - privileged bool - disableZeroRSSFilter bool - stripCommandLine bool - cache *simplelru.LRU[int32, *cacheEntry] - log *slog.Logger -} - -var _ Harvester = (*linuxHarvester)(nil) // static interface assertion - -// Pids returns a slice of process IDs that are running now -func (*linuxHarvester) Pids() ([]int32, error) { - return process.Pids() -} - -// Do returns a sample of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the -// time since this process was sampled for the last time. If the process has been sampled for the first time, this value -// will be ignored -func (ps *linuxHarvester) Do(pid int32) (*Sample, error) { - // Reuses process information that does not vary - cached, hasCachedSample := ps.cache.Get(pid) - - // If cached is nil, the linux process will be created from fresh data - if !hasCachedSample { - cached = &cacheEntry{} - } - var err error - cached.process, err = getLinuxProcess(pid, cached.process, ps.privileged) - if err != nil { - return nil, errors.Wrap(err, "can't create process") - } - - // We don't need to report processes which are not using memory. This filters out certain kernel processes. - if !ps.disableZeroRSSFilter && cached.process.VmRSS() == 0 { - return nil, errProcessWithoutRSS - } - - // Creates a fresh process sample and populates it with the metrics data - sample := NewSample(pid) - - if err := ps.populateStaticData(sample, cached.process); err != nil { - return nil, errors.Wrap(err, "can't populate static attributes") - } - - // As soon as we have successfully stored the static (reusable) values, we can cache the entry - if !hasCachedSample { - ps.cache.Add(pid, cached) - } - - if err := ps.populateGauges(sample, cached.process); err != nil { - return nil, errors.Wrap(err, "can't fetch gauge data") - } - - if err := ps.populateIOCounters(sample, cached.process); err != nil { - return nil, errors.Wrap(err, "can't fetch deltas") - } - - cached.lastSample = sample - - return sample, nil -} - -// populateStaticData populates the sample with the process data won't vary during the process life cycle -func (ps *linuxHarvester) populateStaticData(sample *Sample, process Snapshot) error { - var err error - sample.CmdLine, err = process.CmdLine(!ps.stripCommandLine) - if err != nil { - return errors.Wrap(err, "acquiring command line") - } - - sample.ProcessID = process.Pid() - - sample.User, err = process.Username() - if err != nil { - ps.log.Debug("can't get username for process", "pid", sample.ProcessID, "error", err) - } - - sample.Command = process.Command() - sample.ParentProcessID = process.Ppid() - - return nil -} - -// populateGauges populates the sample with gauge data that represents the process state at a given point -func (ps *linuxHarvester) populateGauges(sample *Sample, process Snapshot) error { - var err error - - cpuTimes, err := process.CPUTimes() - if err != nil { - return err - } - sample.CPUPercent = cpuTimes.Percent - - totalCPU := cpuTimes.User + cpuTimes.System - - if totalCPU > 0 { - sample.CPUUserPercent = (cpuTimes.User / totalCPU) * sample.CPUPercent - sample.CPUSystemPercent = (cpuTimes.System / totalCPU) * sample.CPUPercent - } else { - sample.CPUUserPercent = 0 - sample.CPUSystemPercent = 0 - } - - if ps.privileged { - sample.FdCount, err = process.NumFDs() - if err != nil { - return err - } - } - - // Extra status data - sample.Status = process.Status() - sample.ThreadCount = process.NumThreads() - sample.MemoryVMSBytes = process.VmSize() - sample.MemoryRSSBytes = process.VmRSS() - - return nil -} - -// populateIOCounters fills the sample with the IO counters data. For the "X per second" metrics, it requires the -// last process sample for comparative purposes -func (ps *linuxHarvester) populateIOCounters(sample *Sample, source Snapshot) error { - ioCounters, err := source.IOCounters() - if err != nil { - return err - } - sample.IOReadCount = ioCounters.ReadCount - sample.IOWriteCount = ioCounters.WriteCount - sample.IOReadBytes = ioCounters.ReadBytes - sample.IOWriteBytes = ioCounters.WriteBytes - return nil -} diff --git a/pkg/internal/infraolly/process/harvester_linux_test.go b/pkg/internal/infraolly/process/harvester_test.go similarity index 92% rename from pkg/internal/infraolly/process/harvester_linux_test.go rename to pkg/internal/infraolly/process/harvester_test.go index 43f267e5f..99d2d86ea 100644 --- a/pkg/internal/infraolly/process/harvester_linux_test.go +++ b/pkg/internal/infraolly/process/harvester_test.go @@ -1,5 +1,7 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +//go:build linux + package process import ( @@ -29,7 +31,7 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { for _, c := range cases { t.Run(fmt.Sprint("mode ", c.mode), func(t *testing.T) { cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(Config{RunMode: c.mode}, cache) + h := newHarvester(Config{ProcFSRoot: "/proc", RunMode: c.mode}, cache) // If not privileged, it is expected to not report neither FDs nor IO counters sample, err := h.Do(int32(os.Getpid())) @@ -48,7 +50,7 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { func TestLinuxHarvester_Do(t *testing.T) { // Given a process harvester cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(Config{}, cache) + h := newHarvester(Config{ProcFSRoot: "/proc"}, cache) // When retrieving for a given process sample (e.g. the current testing executable) sample, err := h.Do(int32(os.Getpid())) @@ -83,7 +85,7 @@ func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { // Given a process harvester configured to showw the full command line cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(Config{FullCommandLine: true}, cache) + h := newHarvester(Config{ProcFSRoot: "/proc", FullCommandLine: true}, cache) test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process sample (e.g. the current testing executable) @@ -107,7 +109,7 @@ func TestLinuxHarvester_Do_StripCommandLine(t *testing.T) { // Given a process harvester cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(Config{FullCommandLine: true}, cache) + h := newHarvester(Config{ProcFSRoot: "/proc", FullCommandLine: true}, cache) test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process sample (e.g. the current testing executable) @@ -128,7 +130,7 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentCmd(t *testing.T) { // That has cached an old process sharing the PID with a new process cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) cache.Add(currentPid, &cacheEntry{process: &linuxProcess{cmdLine: "something old"}}) - h := newHarvester(Config{}, cache) + h := newHarvester(Config{ProcFSRoot: "/proc"}, cache) // When the process is harvested sample, err := h.Do(currentPid) @@ -146,7 +148,7 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentPid(t *testing.T) { // That has cached an old process sharing the PID with a new process cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) cache.Add(currentPid, &cacheEntry{process: &linuxProcess{stats: procStats{ppid: -1}}}) - h := newHarvester(Config{}, cache) + h := newHarvester(Config{ProcFSRoot: "/proc"}, cache) // When the process is harvested sample, err := h.Do(currentPid) diff --git a/pkg/internal/infraolly/process/sample.go b/pkg/internal/infraolly/process/sample.go index 8326200c7..45548d418 100644 --- a/pkg/internal/infraolly/process/sample.go +++ b/pkg/internal/infraolly/process/sample.go @@ -1,5 +1,13 @@ +//go:build linux + package process +import "log/slog" + +func pslog() *slog.Logger { + return slog.With("component", "process.Sampler") +} + type Sample struct { ProcessID int32 Command string diff --git a/pkg/internal/infraolly/process/sampler.go b/pkg/internal/infraolly/process/sampler.go new file mode 100644 index 000000000..ef1626bca --- /dev/null +++ b/pkg/internal/infraolly/process/sampler.go @@ -0,0 +1,63 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +//go:build linux + +package process + +import ( + "log/slog" + "math" + "time" + + "github.com/hashicorp/golang-lru/v2/simplelru" + "github.com/mariomac/pipes/pipe" +) + +// processSampler returns runtime information about the currently running processes +type processSampler struct { + harvest Harvester + interval time.Duration + cache *simplelru.LRU[int32, *cacheEntry] + log *slog.Logger +} + +// NewProcessSampler creates and returns a new process Sampler, given an agent context. +func NewProcessSampler(cfg Config) pipe.StartFunc[[]Sample] { + // we purge entries explicitly so size is unbounded + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + harvest := newHarvester(cfg, cache) + + return (&processSampler{ + harvest: harvest, + cache: cache, + interval: cfg.Rate, + log: pslog(), + }).Run +} + +func (ps *processSampler) Run(out chan<- []Sample) { + _ = out +} + +// Sample returns samples for all the running processes, decorated with Docker runtime information, if applies. +func (ps *processSampler) Sample() ([]*Sample, error) { + pids, err := ps.harvest.Pids() + if err != nil { + return nil, err + } + results := make([]*Sample, 0, len(pids)) + + for _, pid := range pids { + processSample, err := ps.harvest.Do(pid) + if err != nil { + ps.log.Debug("skipping process", "pid", pid, "error", err) + continue + } + + results = append(results, processSample) + } + + removeUntilLen(ps.cache, len(pids)) + + return results, nil +} diff --git a/pkg/internal/infraolly/process/sampler_linux.go b/pkg/internal/infraolly/process/sampler_linux.go deleted file mode 100644 index e04959330..000000000 --- a/pkg/internal/infraolly/process/sampler_linux.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2020 New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 -package process - -import ( - "encoding/json" - "errors" - "fmt" - "math" - "strings" - "time" - - "github.com/hashicorp/golang-lru/v2/simplelru" - -) - -// processSampler is an implementation of the metrics_sender.Sampler interface, which returns runtime information about -// the currently running processes -type processSampler struct { - harvest Harvester - containerSamplers []metrics.ContainerSampler - lastRun time.Time - hasAlreadyRun bool - interval time.Duration - cache *cache -} - -var ( - _ sampler.Sampler = (*processSampler)(nil) // static interface assertion - containerNotRunningErrs = map[string]struct{}{} -) - -// NewProcessSampler creates and returns a new process Sampler, given an agent context. -func NewProcessSampler(ctx agent.AgentContext) sampler.Sampler { - hasConfig := ctx != nil && ctx.Config() != nil - - ttlSecs := config.DefaultContainerCacheMetadataLimit - apiVersion := "" - dockerContainerdNamespace := "" - interval := config.FREQ_INTERVAL_FLOOR_PROCESS_METRICS - if hasConfig { - cfg := ctx.Config() - ttlSecs = cfg.ContainerMetadataCacheLimit - apiVersion = cfg.DockerApiVersion - dockerContainerdNamespace = cfg.DockerContainerdNamespace - interval = cfg.MetricsProcessSampleRate - } - // we purge entries explicitly so size is unbounded - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - harvest := newHarvester(ctx, &cache) - containerSamplers := metrics.GetContainerSamplers(time.Duration(ttlSecs)*time.Second, apiVersion, dockerContainerdNamespace) - - return &processSampler{ - harvest: harvest, - containerSamplers: containerSamplers, - cache: &cache, - interval: time.Second * time.Duration(interval), - } -} - -func (ps *processSampler) OnStartup() {} - -func (ps *processSampler) Name() string { - return "ProcessSampler" -} - -func (ps *processSampler) Interval() time.Duration { - return ps.interval -} - -func (ps *processSampler) Disabled() bool { - return ps.Interval() <= config.FREQ_DISABLE_SAMPLING -} - -// Sample returns samples for all the running processes, decorated with Docker runtime information, if applies. -func (ps *processSampler) Sample() (results sample.EventBatch, err error) { - var elapsedMs int64 - var elapsedSeconds float64 - now := time.Now() - if ps.hasAlreadyRun { - elapsedMs = (now.UnixNano() - ps.lastRun.UnixNano()) / 1000000 - } - elapsedSeconds = float64(elapsedMs) / 1000 - ps.lastRun = now - - pids, err := ps.harvest.Pids() - if err != nil { - return nil, err - } - - var containerDecorators []metrics.ProcessDecorator - - for _, containerSampler := range ps.containerSamplers { - if !containerSampler.Enabled() { - continue - } - - decorator, err := containerSampler.NewDecorator() - if err != nil { - if id := containerIDFromNotRunningErr(err); id != "" { - if _, ok := containerNotRunningErrs[id]; !ok { - containerNotRunningErrs[id] = struct{}{} - - mplog.WithError(err).Warn("instantiating container sampler process decorator") - } - } else { - mplog.WithError(err).Warn("instantiating container sampler process decorator") - if strings.Contains(err.Error(), "client is newer than server") { - mplog.WithError(err).Error("Only docker api version from 1.24 upwards are officially supported. You can still use the docker_api_version configuration to work with older versions. You can check https://docs.docker.com/develop/sdk/ what api version maps with each docker version.") - } - } - } else { - containerDecorators = append(containerDecorators, decorator) - } - } - - for _, pid := range pids { - var processSample *Sample - var err error - - processSample, err = ps.harvest.Do(pid, elapsedSeconds) - if err != nil { - procLog := mplog.WithError(err) - if errors.Is(err, errProcessWithoutRSS) { - procLog = procLog.WithField(config.TracesFieldName, config.ProcessTrace) - } - - procLog.WithField("pid", pid).Debug("Skipping process.") - continue - } - - for _, containerDecorator := range containerDecorators { - if containerDecorator != nil { - containerDecorator.Decorate(processSample) - } - } - - results = append(results, ps.normalizeSample(processSample)) - } - - ps.cache.items.RemoveUntilLen(len(pids)) - ps.hasAlreadyRun = true - return results, nil -} - -func (ps *processSampler) normalizeSample(s *Sample) sample.Event { - if len(s.ContainerLabels) > 0 { - sb, err := json.Marshal(s) - if err == nil { - bm := &types.FlatProcessSample{} - if err = json.Unmarshal(sb, bm); err == nil { - for name, value := range s.ContainerLabels { - key := fmt.Sprintf("containerLabel_%s", name) - (*bm)[key] = value - } - return bm - } - } else { - mplog.WithError(err).WithField("sample", fmt.Sprintf("%+v", s)).Debug("normalizeSample can't operate on the sample.") - } - } - return s -} - -func containerIDFromNotRunningErr(err error) string { - prefix := "Error response from daemon: Container " - suffix := " is not running" - msg := err.Error() - i := strings.Index(msg, prefix) - j := strings.Index(msg, suffix) - if i == -1 || j == -1 { - return "" - } - return msg[len(prefix):j] -} diff --git a/pkg/internal/infraolly/process/sampler_linux_test.go b/pkg/internal/infraolly/process/sampler_linux_test.go deleted file mode 100644 index 5b4426d8b..000000000 --- a/pkg/internal/infraolly/process/sampler_linux_test.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2020 New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 -package process - -import ( - "context" - "errors" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - -) - -func TestProcessSampler_DockerDecorator(t *testing.T) { - // Given a Process Sampler - ctx := new(mocks.AgentContext) - ctx.On("Config").Return(&config.Config{}) - ctx.On("GetServiceForPid", mock.Anything).Return("", false) - ps := NewProcessSampler(ctx).(*processSampler) - ps.harvest = &harvesterMock{samples: map[int32]*Sample{ - 1: { - ProcessID: 1, - ProcessDisplayName: "Hello", - }, - 2: { - ProcessID: 2, - ProcessDisplayName: "Bye", - }, - }} - ps.containerSamplers = []metrics.ContainerSampler{&fakeContainerSampler{}} - - // When asking for the process samples - samples, err := ps.Sample() - require.NoError(t, err) - - // They are returned, decorated and normalized - require.Len(t, samples, 2) - - for i := range samples { - flatProcessSample := samples[i].(*types.FlatProcessSample) - switch int32((*flatProcessSample)["processId"].(float64)) { - case 1: - assert.Equal(t, "Hello", (*flatProcessSample)["processDisplayName"]) - case 2: - assert.Equal(t, "Bye", (*flatProcessSample)["processDisplayName"]) - default: - assert.Failf(t, fmt.Sprintf("Unknown process: %#v", *flatProcessSample), "") - } - assert.Equal(t, "decorated", (*flatProcessSample)["containerImage"]) - assert.Equal(t, "value1", (*flatProcessSample)["containerLabel_label1"]) - assert.Equal(t, "value2", (*flatProcessSample)["containerLabel_label2"]) - } -} - -type harvesterMock struct { - samples map[int32]*Sample -} - -func (hm *harvesterMock) Pids() ([]int32, error) { - keys := make([]int32, 0) - for k := range hm.samples { - keys = append(keys, k) - } - return keys, nil -} - -func (hm *harvesterMock) Do(pid int32, _ float64) (*Sample, error) { - return hm.samples[pid], nil -} - -func BenchmarkProcessSampler(b *testing.B) { - pm := NewProcessSampler(&dummyAgentContext{}) - - for i := 0; i < b.N; i++ { - _, _ = pm.Sample() - } -} - -// Tests procs monitor without the Docker container metadata cache -func BenchmarkProcessSampler_NoCache(b *testing.B) { - pm := NewProcessSampler(&dummyAgentContext{ - cfg: &config.Config{ - ContainerMetadataCacheLimit: -5, - }}) - - for i := 0; i < b.N; i++ { - _, _ = pm.Sample() - } -} - -// DummyAgentContext replaces mock agent context because mocks management can have impact in benchmarks -type dummyAgentContext struct { - agent.AgentContext - cfg *config.Config -} - -func (*dummyAgentContext) Context() context.Context { - return context.TODO() -} - -func (*dummyAgentContext) ActiveEntitiesChannel() chan string { - return nil -} - -func (*dummyAgentContext) AddReconnecting(agent.Plugin) {} - -func (*dummyAgentContext) EntityKey() string { - return "" -} - -func (*dummyAgentContext) CacheServicePids(source string, pidMap map[int]string) {} - -func (d *dummyAgentContext) Config() *config.Config { - return d.cfg -} - -func (*dummyAgentContext) GetServiceForPid(pid int) (service string, ok bool) { - return "", false -} - -func (*dummyAgentContext) HostnameResolver() hostname.Resolver { - return nil -} - -func (*dummyAgentContext) Reconnect() {} - -func (*dummyAgentContext) SendData(agentTypes.PluginOutput) {} - -func (*dummyAgentContext) SendEvent(event sample.Event, entityKey entity.Key) {} - -func (*dummyAgentContext) Unregister(ids.PluginID) {} - -func (*dummyAgentContext) Version() string { - return "" -} - -func (dummyAgentContext) IDLookup() host.IDLookup { - idLookupTable := make(host.IDLookup) - idLookupTable[sysinfo.HOST_SOURCE_HOSTNAME_SHORT] = "short_hostname" - return idLookupTable -} - -func Test_checkContainerNotRunning(t *testing.T) { - type args struct { - err error - } - tests := []struct { - name string - args args - want string - }{ - { - name: "match", - args: args{err: errors.New("Error response from daemon: Container e9c57d578de9e487f6f703d04b1b237b1ff3d926d9cc2a4adfcbe8e1946e841f is not running")}, - want: "e9c57d578de9e487f6f703d04b1b237b1ff3d926d9cc2a4adfcbe8e1946e841f", - }, - { - name: "match2", - args: args{err: errors.New("Error response from daemon: Container cb33a2dfaa4b25dddcd509b434bc6cd6c088a4e39a2611776d45fdb02b763039 is not running")}, - want: "cb33a2dfaa4b25dddcd509b434bc6cd6c088a4e39a2611776d45fdb02b763039", - }, - { - name: "nomatch", - args: args{err: errors.New("not legit")}, - want: "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := containerIDFromNotRunningErr(tt.args.err); got != tt.want { - t.Errorf("check() = %v, want %v", got, tt.want) - } - }) - } -} - -func Benchmark_checkContainerNotRunning(b *testing.B) { - err := errors.New("Error response from daemon: Container e9c57d578de9e487f6f703d04b1b237b1ff3d926d9cc2a4adfcbe8e1946e841f is not running") - for i := 0; i < b.N; i++ { - if id := containerIDFromNotRunningErr(err); id != "e9c57d578de9e487f6f703d04b1b237b1ff3d926d9cc2a4adfcbe8e1946e841f" { - b.Fatalf("check() = %s, want %s", id, "e9c57d578de9e487f6f703d04b1b237b1ff3d926d9cc2a4adfcbe8e1946e841f") - } - } -} diff --git a/pkg/internal/infraolly/process/snapshot.go b/pkg/internal/infraolly/process/snapshot.go index 8d07dc212..757e1780c 100644 --- a/pkg/internal/infraolly/process/snapshot.go +++ b/pkg/internal/infraolly/process/snapshot.go @@ -1,9 +1,23 @@ +//go:build linux + // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 package process import ( + "fmt" + "os" + "path" + "runtime" + "strconv" + "strings" + "time" + + "github.com/pkg/errors" + "github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/process" + + "github.com/grafana/beyla/pkg/internal/helpers" ) // CPUInfo represents CPU usage statistics at a given point @@ -16,32 +30,370 @@ type CPUInfo struct { System float64 } -// Snapshot represents the status of a process at a given time. Instances of Snapshot must not be -// reused for different samples -type Snapshot interface { - // Pid returns the Process ID - Pid() int32 - // Ppid returns the Parent Process ID - Ppid() int32 - // Status returns the state of the process: R (running or runnable), D (uninterruptible sleep), S (interruptible - // sleep), Z (defunct/zombie) or T (stopped) - Status() string - // Command returns the process Command name - Command() string - // CmdLine returns the process invoking command line, with or without arguments - CmdLine(withArgs bool) (string, error) - // Username returns the name of the process owner user - Username() (string, error) - // CPUTimes returns the CPU consumption percentages for the process - CPUTimes() (CPUInfo, error) - // IOCounters returns the I/O statistics for the process - IOCounters() (*process.IOCountersStat, error) - // NumThreads returns the number of threads that are being used by the process - NumThreads() int32 - // NumFDs returns the number of File Descriptors that are open by the process - NumFDs() (int32, error) - // VmRSS returns the Resident Set Size (memory in RAM) of the process - VmRSS() int64 - // VmSize returns the total memory of the process (RSS + virtual memory) - VmSize() int64 +// linuxProcess is an implementation of the process.Snapshot interface for linux hosts. It is designed to be highly +// optimized and avoid unnecessary/duplicated system calls. +type linuxProcess struct { + // if privileged == false, some operations will be avoided: FD and IO count. + privileged bool + + stats procStats + process *process.Process + lastCPU CPUInfo + lastTime time.Time + + procFSRoot string + + // data that will be reused between samples of the same process. + pid int32 + user string + cmdLine string +} + +// needed to calculate RSS. +var pageSize int64 + +// needed to calculate CPU times. +var clockTicks int64 + +// for testing getting username from getent. +var getEntCommand = helpers.RunCommand //nolint:gochecknoglobals + +var ( + errMalformedGetentEntry = errors.New("malformed getent entry") + errInvalidUidsForProcess = errors.New("invalid uids for process") +) + +func init() { + pageSize = int64(os.Getpagesize()) + if pageSize <= 0 { + pageSize = 4096 // default value + } + + clockTicks = int64(cpu.ClocksPerSec) + if clockTicks <= 0 { + clockTicks = 100 // default value + } +} + +// getLinuxProcess returns a linux process snapshot, trying to reuse the data from a previous snapshot of the same +// process. +func getLinuxProcess(procFSRoot string, pid int32, previous *linuxProcess, privileged bool) (*linuxProcess, error) { + var gops *process.Process + var err error + + procStats, err := readProcStat(procFSRoot, pid) + if err != nil { + return nil, err + } + + // Reusing information from the last snapshot for the same process + // If the name or the PPID changed from the previous, we'll consider this sample is just + // a new process that shares the PID with an old one. + // if a process with the same Command but different CmdLine or User name + // occupies the same PID, the cache won't refresh the CmdLine and Username. + if previous == nil || procStats.command != previous.Command() || procStats.ppid != previous.Ppid() { + gops, err = process.NewProcess(pid) + if err != nil { + return nil, err + } + return &linuxProcess{ + privileged: privileged, + pid: pid, + process: gops, + stats: procStats, + procFSRoot: procFSRoot, + }, nil + } + + // Otherwise, instead of creating a new process snapshot, we just reuse the previous one, with updated data + previous.stats = procStats + + return previous, nil +} + +func (pw *linuxProcess) Pid() int32 { + return pw.pid +} + +func (pw *linuxProcess) Username() (string, error) { + var err error + if pw.user == "" { // caching user + // try to get it from gopsutil and return it if ok + pw.user, err = pw.process.Username() + if err == nil { + return pw.user, nil + } + + // get the uid to be retrieved from getent + uid, err := pw.uid() + if err != nil { + return "", err + } + + // try to get it using getent + pw.user, err = usernameFromGetent(uid) + if err != nil { + return "", err + } + } + return pw.user, nil +} + +func (pw *linuxProcess) uid() (int32, error) { + uuids, err := pw.process.Uids() + if err != nil { + return 0, fmt.Errorf("error getting process uids: %w", err) //nolint:wrapcheck + } + + if len(uuids) == 0 { + return 0, errInvalidUidsForProcess //nolint:wrapcheck + } + + return uuids[0], nil +} + +// usernameFromGetent returns the username using getent https://man7.org/linux/man-pages/man1/getent.1.html +// getent passwd format example: +// deleteme:x:63367:63367:Dynamic User:/:/usr/sbin/nologin +func usernameFromGetent(uid int32) (string, error) { + out, err := getEntCommand("/usr/bin/getent", "", []string{"passwd", fmt.Sprintf("%d", uid)}...) + if err != nil { + return "", err + } + + if sepIdx := strings.Index(out, ":"); sepIdx > 0 { + return out[0:sepIdx], nil + } + + return "", errMalformedGetentEntry //nolint:wrapcheck +} + +func (pw *linuxProcess) IOCounters() (*process.IOCountersStat, error) { + if !pw.privileged { + return nil, nil + } + return pw.process.IOCounters() +} + +// NumFDs returns the number of file descriptors. It returns -1 (and nil error) if the Agent does not have privileges to +// access this information. +func (pw *linuxProcess) NumFDs() (int32, error) { + if !pw.privileged { + return -1, nil + } + pid := pw.process.Pid + statPath := path.Join(pw.procFSRoot, strconv.Itoa(int(pid)), "fd") + d, err := os.Open(statPath) + if err != nil { + return 0, err + } + defer d.Close() + fnames, err := d.Readdirnames(-1) + return int32(len(fnames)), err +} + +///////////////////////////// +// Data to be derived from /proc//stat +///////////////////////////// + +type procStats struct { + command string + ppid int32 + numThreads int32 + state string + vmRSS int64 + vmSize int64 + cpu CPUInfo +} + +// /proc//stat standard field indices according to: http://man7.org/linux/man-pages/man5/proc.5.html +// because the first two fields are treated separately those indices are smaller with 2 elements than in the doc. +const ( + statState = 0 + statPPID = 1 + statUtime = 11 + statStime = 12 + statNumThreads = 17 + statVsize = 20 + statRss = 21 +) + +// readProcStat will gather information about the pid from /proc//stat file. +func readProcStat(procFSRoot string, pid int32) (procStats, error) { + statPath := path.Join(procFSRoot, strconv.Itoa(int(pid)), "stat") + + content, err := os.ReadFile(statPath) + if err != nil { + return procStats{}, err + } + + return parseProcStat(string(content)) +} + +// parseProcStat is used to parse the content of the /proc//stat file. +func parseProcStat(content string) (procStats, error) { + stats := procStats{} + + i := strings.Index(content, "(") + if i == -1 { + return stats, fmt.Errorf("could not find command name start symbol '(' for stats: %s", content) + } + // Drop the first first field which is the pid. + content = content[i+1:] + + i = strings.Index(content, ")") + if i == -1 { + return stats, fmt.Errorf("could not find command name end symbol ')' for stats: %s", content) + } + + // Command Name found as the second field inside the brackets. + stats.command = content[:i] + + fields := strings.Fields(content[i+1:]) + + // Process State + stats.state = fields[statState] + + // Parent PID + ppid, err := strconv.ParseInt(fields[statPPID], 10, 32) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + stats.ppid = int32(ppid) + + // User time + utime, err := strconv.ParseInt(fields[statUtime], 10, 64) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + stats.cpu.User = float64(utime) / float64(clockTicks) + + // System time + stime, err := strconv.ParseInt(fields[statStime], 10, 64) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + stats.cpu.System = float64(stime) / float64(clockTicks) + + // Number of threads + nthreads, err := strconv.ParseInt(fields[statNumThreads], 10, 32) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + stats.numThreads = int32(nthreads) + + // VM Memory size + stats.vmSize, err = strconv.ParseInt(fields[statVsize], 10, 64) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + + // VM RSS size + stats.vmRSS, err = strconv.ParseInt(fields[statRss], 10, 64) + if err != nil { + return stats, errors.Wrapf(err, "for stats: %s", string(content)) + } + stats.vmRSS *= pageSize + + return stats, nil +} + +func (pw *linuxProcess) CPUTimes() (CPUInfo, error) { + now := time.Now() + + if pw.lastTime.IsZero() { + // invoked first time + pw.lastCPU = pw.stats.cpu + pw.lastTime = now + return pw.stats.cpu, nil + } + + // Calculate CPU percent from user time, system time, and last harvested cpu counters + numcpu := runtime.NumCPU() + delta := (now.Sub(pw.lastTime).Seconds()) * float64(numcpu) + pw.stats.cpu.Percent = calculatePercent(pw.lastCPU, pw.stats.cpu, delta, numcpu) + pw.lastCPU = pw.stats.cpu + pw.lastTime = now + + return pw.stats.cpu, nil +} + +func calculatePercent(t1, t2 CPUInfo, delta float64, numcpu int) float64 { + if delta == 0 { + return 0 + } + deltaProc := t2.User + t2.System - t1.User - t1.System + overallPercent := ((deltaProc / delta) * 100) * float64(numcpu) + return overallPercent +} + +func (pw *linuxProcess) Ppid() int32 { + return pw.stats.ppid +} + +func (pw *linuxProcess) NumThreads() int32 { + return pw.stats.numThreads +} + +func (pw *linuxProcess) Status() string { + return pw.stats.state +} + +func (pw *linuxProcess) VMRSS() int64 { + return pw.stats.vmRSS +} + +func (pw *linuxProcess) VMSize() int64 { + return pw.stats.vmSize +} + +func (pw *linuxProcess) Command() string { + return pw.stats.command +} + +////////////////////////// +// Data to be derived from /proc//cmdline: command line, and command line without arguments +////////////////////////// + +func (pw *linuxProcess) CmdLine(withArgs bool) (string, error) { + if pw.cmdLine != "" { + return pw.cmdLine, nil + } + + cmdPath := path.Join(pw.procFSRoot, strconv.Itoa(int(pw.pid)), "cmdline") + procCmdline, err := os.ReadFile(cmdPath) + if err != nil { + procCmdline = nil // we can't be sure internal libraries return nil on error + } + + if len(procCmdline) == 0 { + return "", nil // zombie process + } + + // Ignoring dash on session commands + if procCmdline[0] == '-' { + procCmdline = procCmdline[1:] + } + + cmdLineBytes := make([]byte, 0, len(procCmdline)) + for i := 0; i < len(procCmdline); i++ { + if procCmdline[i] == 0 { + // ignoring the trailing zero that ends /proc//cmdline, but adding the last character if the file + // does not end in zero + if withArgs && i < len(procCmdline)-1 { + cmdLineBytes = append(cmdLineBytes, ' ') + } else { + break + } + } else { + cmdLineBytes = append(cmdLineBytes, procCmdline[i]) + } + } + pw.cmdLine = sanitizeCommandLine(string(cmdLineBytes)) + return pw.cmdLine, nil +} + +// sanitizeCommandLine cleans the command line to remove wrappers like quotation marks. +func sanitizeCommandLine(cmd string) string { + return strings.Trim(cmd, " \t\n\v\f\r\"'`") } diff --git a/pkg/internal/infraolly/process/snapshot_linux.go b/pkg/internal/infraolly/process/snapshot_linux.go deleted file mode 100644 index 865e1796c..000000000 --- a/pkg/internal/infraolly/process/snapshot_linux.go +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2020 New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 -package process - -import ( - "fmt" - "io/ioutil" - "os" - "runtime" - "strconv" - "strings" - "time" - - "github.com/pkg/errors" - "github.com/shirou/gopsutil/v3/cpu" - "github.com/shirou/gopsutil/v3/process" -) - -// linuxProcess is an implementation of the process.Snapshot interface for linux hosts. It is designed to be highly -// optimized and avoid unnecessary/duplicated system calls. -type linuxProcess struct { - // if privileged == false, some operations will be avoided: FD and IO count. - privileged bool - - stats procStats - process *process.Process - lastCPU CPUInfo - lastTime time.Time - - // data that will be reused between samples of the same process. - pid int32 - user string - cmdLine string -} - -// needed to calculate RSS. -var pageSize int64 - -// needed to calculate CPU times. -var clockTicks int64 - -// for testing getting username from getent. -var getEntCommand = helpers.RunCommand //nolint:gochecknoglobals - -var ( - errMalformedGetentEntry = errors.New("malformed getent entry") - errInvalidUidsForProcess = errors.New("invalid uids for process") -) - -func init() { - pageSize = int64(os.Getpagesize()) - if pageSize <= 0 { - pageSize = 4096 // default value - } - - clockTicks = int64(cpu.ClocksPerSec) - if clockTicks <= 0 { - clockTicks = 100 // default value - } -} - -var _ Snapshot = (*linuxProcess)(nil) // static interface assertion - -// getLinuxProcess returns a linux process snapshot, trying to reuse the data from a previous snapshot of the same -// process. -func getLinuxProcess(pid int32, previous *linuxProcess, privileged bool) (*linuxProcess, error) { - var gops *process.Process - var err error - - procStats, err := readProcStat(pid) - if err != nil { - return nil, err - } - - // Reusing information from the last snapshot for the same process - // If the name or the PPID changed from the previous, we'll consider this sample is just - // a new process that shares the PID with an old one. - // if a process with the same Command but different CmdLine or User name - // occupies the same PID, the cache won't refresh the CmdLine and Username. - if previous == nil || procStats.command != previous.Command() || procStats.ppid != previous.Ppid() { - gops, err = process.NewProcess(pid) - if err != nil { - return nil, err - } - return &linuxProcess{ - privileged: privileged, - pid: pid, - process: gops, - stats: procStats, - }, nil - } - - // Otherwise, instead of creating a new process snapshot, we just reuse the previous one, with updated data - previous.stats = procStats - - return previous, nil -} - -func (pw *linuxProcess) Pid() int32 { - return pw.pid -} - -func (pw *linuxProcess) Username() (string, error) { - var err error - if pw.user == "" { // caching user - // try to get it from gopsutil and return it if ok - pw.user, err = pw.process.Username() - if err == nil { - return pw.user, nil - } - - // get the uid to be retrieved from getent - uid, err := pw.uid() - if err != nil { - return "", err - } - - // try to get it using getent - pw.user, err = usernameFromGetent(uid) - if err != nil { - return "", err - } - } - return pw.user, nil -} - -func (pw *linuxProcess) uid() (int32, error) { - uuids, err := pw.process.Uids() - if err != nil { - return 0, fmt.Errorf("error getting process uids: %w", err) //nolint:wrapcheck - } - - if len(uuids) == 0 { - return 0, errInvalidUidsForProcess //nolint:wrapcheck - } - - return uuids[0], nil -} - -// usernameFromGetent returns the username using getent https://man7.org/linux/man-pages/man1/getent.1.html -// getent passwd format example: -// deleteme:x:63367:63367:Dynamic User:/:/usr/sbin/nologin -func usernameFromGetent(uid int32) (string, error) { - out, err := getEntCommand("/usr/bin/getent", "", []string{"passwd", fmt.Sprintf("%d", uid)}...) - if err != nil { - return "", err - } - - if sepIdx := strings.Index(out, ":"); sepIdx > 0 { - return out[0:sepIdx], nil - } - - return "", errMalformedGetentEntry //nolint:wrapcheck -} - -func (pw *linuxProcess) IOCounters() (*process.IOCountersStat, error) { - if !pw.privileged { - return nil, nil - } - return pw.process.IOCounters() -} - -// NumFDs returns the number of file descriptors. It returns -1 (and nil error) if the Agent does not have privileges to -// access this information. -func (pw *linuxProcess) NumFDs() (int32, error) { - if !pw.privileged { - return -1, nil - } - pid := pw.process.Pid - statPath := helpers.HostProc(strconv.Itoa(int(pid)), "fd") - d, err := os.Open(statPath) - if err != nil { - return 0, err - } - defer d.Close() - fnames, err := d.Readdirnames(-1) - return int32(len(fnames)), nil -} - -///////////////////////////// -// Data to be derived from /proc//stat -///////////////////////////// - -type procStats struct { - command string - ppid int32 - numThreads int32 - state string - vmRSS int64 - vmSize int64 - cpu CPUInfo -} - -// /proc//stat standard field indices according to: http://man7.org/linux/man-pages/man5/proc.5.html -// because the first two fields are treated separately those indices are smaller with 2 elements than in the doc. -const ( - statState = 0 - statPPID = 1 - statUtime = 11 - statStime = 12 - statNumThreads = 17 - statVsize = 20 - statRss = 21 -) - -// readProcStat will gather information about the pid from /proc//stat file. -func readProcStat(pid int32) (procStats, error) { - statPath := helpers.HostProc(strconv.Itoa(int(pid)), "stat") - - content, err := ioutil.ReadFile(statPath) - if err != nil { - return procStats{}, err - } - - return parseProcStat(string(content)) -} - -// parseProcStat is used to parse the content of the /proc//stat file. -func parseProcStat(content string) (procStats, error) { - stats := procStats{} - - i := strings.Index(content, "(") - if i == -1 { - return stats, fmt.Errorf("could not find command name start symbol '(' for stats: %s", content) - } - // Drop the first first field which is the pid. - content = content[i+1:] - - i = strings.Index(content, ")") - if i == -1 { - return stats, fmt.Errorf("could not find command name end symbol ')' for stats: %s", content) - } - - // Command Name found as the second field inside the brackets. - stats.command = content[:i] - - fields := strings.Fields(content[i+1:]) - - // Process State - stats.state = fields[statState] - - // Parent PID - ppid, err := strconv.ParseInt(fields[statPPID], 10, 32) - if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) - } - stats.ppid = int32(ppid) - - // User time - utime, err := strconv.ParseInt(fields[statUtime], 10, 64) - if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) - } - stats.cpu.User = float64(utime) / float64(clockTicks) - - // System time - stime, err := strconv.ParseInt(fields[statStime], 10, 64) - if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) - } - stats.cpu.System = float64(stime) / float64(clockTicks) - - // Number of threads - nthreads, err := strconv.ParseInt(fields[statNumThreads], 10, 32) - if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) - } - stats.numThreads = int32(nthreads) - - // VM Memory size - stats.vmSize, err = strconv.ParseInt(fields[statVsize], 10, 64) - if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) - } - - // VM RSS size - stats.vmRSS, err = strconv.ParseInt(fields[statRss], 10, 64) - if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) - } - stats.vmRSS *= pageSize - - return stats, nil -} - -func (pw *linuxProcess) CPUTimes() (CPUInfo, error) { - now := time.Now() - - if pw.lastTime.IsZero() { - // invoked first time - pw.lastCPU = pw.stats.cpu - pw.lastTime = now - return pw.stats.cpu, nil - } - - // Calculate CPU percent from user time, system time, and last harvested cpu counters - numcpu := runtime.NumCPU() - delta := (now.Sub(pw.lastTime).Seconds()) * float64(numcpu) - pw.stats.cpu.Percent = calculatePercent(pw.lastCPU, pw.stats.cpu, delta, numcpu) - pw.lastCPU = pw.stats.cpu - pw.lastTime = now - - return pw.stats.cpu, nil -} - -func calculatePercent(t1, t2 CPUInfo, delta float64, numcpu int) float64 { - if delta == 0 { - return 0 - } - deltaProc := t2.User + t2.System - t1.User - t1.System - overallPercent := ((deltaProc / delta) * 100) * float64(numcpu) - return overallPercent -} - -func (pw *linuxProcess) Ppid() int32 { - return pw.stats.ppid -} - -func (pw *linuxProcess) NumThreads() int32 { - return pw.stats.numThreads -} - -func (pw *linuxProcess) Status() string { - return pw.stats.state -} - -func (pw *linuxProcess) VmRSS() int64 { - return pw.stats.vmRSS -} - -func (pw *linuxProcess) VmSize() int64 { - return pw.stats.vmSize -} - -func (pw *linuxProcess) Command() string { - return pw.stats.command -} - -////////////////////////// -// Data to be derived from /proc//cmdline: command line, and command line without arguments -////////////////////////// - -func (pw *linuxProcess) CmdLine(withArgs bool) (string, error) { - if pw.cmdLine != "" { - return pw.cmdLine, nil - } - - cmdPath := helpers.HostProc(strconv.Itoa(int(pw.pid)), "cmdline") - procCmdline, err := ioutil.ReadFile(cmdPath) - if err != nil { - procCmdline = nil // we can't be sure internal libraries return nil on error - } - - if len(procCmdline) == 0 { - return "", nil // zombie process - } - - // Ignoring dash on session commands - if procCmdline[0] == '-' { - procCmdline = procCmdline[1:] - } - - cmdLineBytes := make([]byte, 0, len(procCmdline)) - for i := 0; i < len(procCmdline); i++ { - if procCmdline[i] == 0 { - // ignoring the trailing zero that ends /proc//cmdline, but adding the last character if the file - // does not end in zero - if withArgs && i < len(procCmdline)-1 { - cmdLineBytes = append(cmdLineBytes, ' ') - } else { - break - } - } else { - cmdLineBytes = append(cmdLineBytes, procCmdline[i]) - } - } - pw.cmdLine = helpers.SanitizeCommandLine(string(cmdLineBytes)) - return pw.cmdLine, nil -} diff --git a/pkg/internal/infraolly/process/snapshot_mock_test.go b/pkg/internal/infraolly/process/snapshot_mock_test.go deleted file mode 100644 index 2728cd9e5..000000000 --- a/pkg/internal/infraolly/process/snapshot_mock_test.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package process - -import ( - "github.com/shirou/gopsutil/v3/process" - "github.com/stretchr/testify/mock" -) - -type SnapshotMock struct { - mock.Mock -} - -func (s *SnapshotMock) Pid() int32 { - args := s.Called() - - return args.Get(0).(int32) -} - -func (s *SnapshotMock) ShouldReturnPid(pid int32) { - s. - On("Pid"). - Once(). - Return(pid) -} - -func (s *SnapshotMock) Ppid() int32 { - args := s.Called() - - return args.Get(0).(int32) -} - -func (s *SnapshotMock) ShouldReturnPpid(ppid int32) { - s. - On("Ppid"). - Once(). - Return(ppid) -} - -func (s *SnapshotMock) Status() string { - args := s.Called() - - return args.String(0) -} - -func (s *SnapshotMock) ShouldReturnStatus(status string) { - s. - On("Status"). - Once(). - Return(status) -} - -func (s *SnapshotMock) Command() string { - args := s.Called() - - return args.String(0) -} - -func (s *SnapshotMock) ShouldReturnCommand(command string) { - s. - On("Command"). - Once(). - Return(command) -} - -func (s *SnapshotMock) CmdLine(withArgs bool) (string, error) { - args := s.Called(withArgs) - - return args.String(0), args.Error(1) -} - -func (s *SnapshotMock) ShouldReturnCmdLine(withArgs bool, cmd string, err error) { - s. - On("CmdLine", withArgs). - Once(). - Return(cmd, err) -} - -func (s *SnapshotMock) Username() (string, error) { - args := s.Called() - - return args.String(0), args.Error(1) -} - -func (s *SnapshotMock) ShouldReturnUsername(cmd string, err error) { - s. - On("Username"). - Once(). - Return(cmd, err) -} - -func (s *SnapshotMock) CPUTimes() (CPUInfo, error) { - args := s.Called() - - return args.Get(0).(CPUInfo), args.Error(1) -} - -func (s *SnapshotMock) ShouldReturnCPUTimes(cpu CPUInfo, err error) { - s. - On("CPUTimes"). - Once(). - Return(cpu, err) -} - -func (s *SnapshotMock) IOCounters() (*process.IOCountersStat, error) { - args := s.Called() - - return args.Get(0).(*process.IOCountersStat), args.Error(1) -} - -func (s *SnapshotMock) ShouldReturnIOCounters(io *process.IOCountersStat, err error) { - s. - On("IOCounters"). - Once(). - Return(io, err) -} - -func (s *SnapshotMock) NumThreads() int32 { - args := s.Called() - - return args.Get(0).(int32) -} - -func (s *SnapshotMock) ShouldReturnNumThreads(num int32) { - s. - On("NumThreads"). - Once(). - Return(num) -} - -func (s *SnapshotMock) NumFDs() (int32, error) { - args := s.Called() - - return args.Get(0).(int32), args.Error(1) -} - -func (s *SnapshotMock) ShouldReturnNumFDs(num int32, err error) { - s. - On("NumFDs"). - Once(). - Return(num, err) -} - -func (s *SnapshotMock) VmRSS() int64 { - args := s.Called() - - return args.Get(0).(int64) -} - -func (s *SnapshotMock) ShouldReturnVmRSS(rss int64) { - s. - On("VmRSS"). - Once(). - Return(rss) -} - -func (s *SnapshotMock) VmSize() int64 { - args := s.Called() - - return args.Get(0).(int64) -} - -func (s *SnapshotMock) ShouldReturnVmSize(size int64) { - s. - On("VmSize"). - Once(). - Return(size) -} diff --git a/pkg/internal/infraolly/process/snapshot_linux_test.go b/pkg/internal/infraolly/process/snapshot_test.go similarity index 93% rename from pkg/internal/infraolly/process/snapshot_linux_test.go rename to pkg/internal/infraolly/process/snapshot_test.go index 5736e60f8..eb3bdb86a 100644 --- a/pkg/internal/infraolly/process/snapshot_linux_test.go +++ b/pkg/internal/infraolly/process/snapshot_test.go @@ -1,25 +1,26 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -// -//nolint:goerr113 +//go:build linux + package process import ( "errors" "fmt" - "io/ioutil" "os" "path" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/grafana/beyla/pkg/internal/helpers" ) func TestLinuxProcess_CmdLine(t *testing.T) { hostProc := os.Getenv("HOST_PROC") defer os.Setenv("HOST_PROC", hostProc) - tmpDir, err := ioutil.TempDir("", "proc") + tmpDir, err := os.MkdirTemp("", "proc") require.NoError(t, err) processDir := path.Join(tmpDir, "12345") require.NoError(t, os.MkdirAll(processDir, 0o755)) @@ -40,7 +41,7 @@ func TestLinuxProcess_CmdLine(t *testing.T) { }, } for _, tc := range testCases { - require.NoError(t, ioutil.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) + require.NoError(t, os.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) lp := linuxProcess{pid: 12345} actual, err := lp.CmdLine(true) assert.NoError(t, err) @@ -51,7 +52,7 @@ func TestLinuxProcess_CmdLine(t *testing.T) { func TestLinuxProcess_CmdLine_NoArgs(t *testing.T) { hostProc := os.Getenv("HOST_PROC") defer os.Setenv("HOST_PROC", hostProc) - tmpDir, err := ioutil.TempDir("", "proc") + tmpDir, err := os.MkdirTemp("", "proc") require.NoError(t, err) processDir := path.Join(tmpDir, "12345") require.NoError(t, os.MkdirAll(processDir, 0o755)) @@ -72,7 +73,7 @@ func TestLinuxProcess_CmdLine_NoArgs(t *testing.T) { }, } for _, tc := range testCases { - require.NoError(t, ioutil.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) + require.NoError(t, os.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) lp := linuxProcess{pid: 12345} actual, err := lp.CmdLine(false) assert.NoError(t, err) @@ -85,7 +86,7 @@ func TestLinuxProcess_CmdLine_NoArgs(t *testing.T) { func TestLinuxProcess_CmdLine_NotStandard(t *testing.T) { hostProc := os.Getenv("HOST_PROC") defer os.Setenv("HOST_PROC", hostProc) - tmpDir, err := ioutil.TempDir("", "proc") + tmpDir, err := os.MkdirTemp("", "proc") require.NoError(t, err) processDir := path.Join(tmpDir, "12345") require.NoError(t, os.MkdirAll(processDir, 0o755)) @@ -103,7 +104,7 @@ func TestLinuxProcess_CmdLine_NotStandard(t *testing.T) { }, } for _, tc := range testCases { - require.NoError(t, ioutil.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) + require.NoError(t, os.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) lp := linuxProcess{pid: 12345} // Testing both the cases with and without command line stripping @@ -227,11 +228,10 @@ func Test_usernameFromGetent(t *testing.T) { //nolint:paralleltest }, } - //nolint:paralleltest for i := range testCases { testCase := testCases[i] t.Run(testCase.name, func(t *testing.T) { - getEntCommand = func(command string, stdin string, args ...string) (string, error) { + getEntCommand = func(_, _ string, _ ...string) (string, error) { return testCase.getEntResult, testCase.getEntError } defer func() { From c0070239af230b90d2cde1eb0d7184b6e58e7c75 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Tue, 28 May 2024 11:05:01 +0200 Subject: [PATCH 06/27] renamings --- pkg/internal/infraolly/process/cache.go | 2 +- .../process/{sampler.go => collect.go} | 22 ++--- .../process/{harvester.go => harvest.go} | 93 ++++++++++--------- .../{harvester_test.go => harvest_test.go} | 78 ++++++++-------- pkg/internal/infraolly/process/snapshot.go | 2 +- .../process/{sample.go => status.go} | 8 +- 6 files changed, 103 insertions(+), 102 deletions(-) rename pkg/internal/infraolly/process/{sampler.go => collect.go} (58%) rename pkg/internal/infraolly/process/{harvester.go => harvest.go} (61%) rename pkg/internal/infraolly/process/{harvester_test.go => harvest_test.go} (66%) rename pkg/internal/infraolly/process/{sample.go => status.go} (80%) diff --git a/pkg/internal/infraolly/process/cache.go b/pkg/internal/infraolly/process/cache.go index 688a84b9e..a8b4824ee 100644 --- a/pkg/internal/infraolly/process/cache.go +++ b/pkg/internal/infraolly/process/cache.go @@ -10,7 +10,7 @@ import ( type cacheEntry struct { process *linuxProcess - lastSample *Sample // The last event we generated for this process, so we can re-use metadata that doesn't change + last *Status // The last event we generated for this process, so we can re-use metadata that doesn't change } // removeUntilLen removes the oldest entries until the cache reaches the given length. diff --git a/pkg/internal/infraolly/process/sampler.go b/pkg/internal/infraolly/process/collect.go similarity index 58% rename from pkg/internal/infraolly/process/sampler.go rename to pkg/internal/infraolly/process/collect.go index ef1626bca..e90b799bb 100644 --- a/pkg/internal/infraolly/process/sampler.go +++ b/pkg/internal/infraolly/process/collect.go @@ -13,21 +13,21 @@ import ( "github.com/mariomac/pipes/pipe" ) -// processSampler returns runtime information about the currently running processes -type processSampler struct { +// Collector returns runtime information about the currently running processes +type Collector struct { harvest Harvester interval time.Duration cache *simplelru.LRU[int32, *cacheEntry] log *slog.Logger } -// NewProcessSampler creates and returns a new process Sampler, given an agent context. -func NewProcessSampler(cfg Config) pipe.StartFunc[[]Sample] { +// NewCollector creates and returns a new process Collector, given an agent context. +func NewCollector(cfg Config) pipe.StartFunc[[]Status] { // we purge entries explicitly so size is unbounded cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) harvest := newHarvester(cfg, cache) - return (&processSampler{ + return (&Collector{ harvest: harvest, cache: cache, interval: cfg.Rate, @@ -35,26 +35,26 @@ func NewProcessSampler(cfg Config) pipe.StartFunc[[]Sample] { }).Run } -func (ps *processSampler) Run(out chan<- []Sample) { +func (ps *Collector) Run(out chan<- []Status) { _ = out } -// Sample returns samples for all the running processes, decorated with Docker runtime information, if applies. -func (ps *processSampler) Sample() ([]*Sample, error) { +// Collect returns the status for all the running processes, decorated with Docker runtime information, if applies. +func (ps *Collector) Collect() ([]*Status, error) { pids, err := ps.harvest.Pids() if err != nil { return nil, err } - results := make([]*Sample, 0, len(pids)) + results := make([]*Status, 0, len(pids)) for _, pid := range pids { - processSample, err := ps.harvest.Do(pid) + status, err := ps.harvest.Do(pid) if err != nil { ps.log.Debug("skipping process", "pid", pid, "error", err) continue } - results = append(results, processSample) + results = append(results, status) } removeUntilLen(ps.cache, len(pids)) diff --git a/pkg/internal/infraolly/process/harvester.go b/pkg/internal/infraolly/process/harvest.go similarity index 61% rename from pkg/internal/infraolly/process/harvester.go rename to pkg/internal/infraolly/process/harvest.go index 3621fa1e3..b7e600f06 100644 --- a/pkg/internal/infraolly/process/harvester.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -5,9 +5,10 @@ // Package process provides all the tools and functionality for sampling processes. It is divided in three main // components: -// - *linuxProcess: provides OS-level information of a process at a given spot -// - Harvester: manages process *linuxProcesss to create actual Process Samples with the actual metrics. -// - Sampler: uses the harvester to coordinate the creation of the Process Samples dataset, as being reported to NR +// - Status: provides OS-level information of a process at a given spot +// - Harvester: fetches and creates actual Process Status from system +// - Collector: uses input from the application pipeline to fetch information for all the processes from +// the instrumented applications, and forwards it to the next stage of the Process' pipeline. package process import ( @@ -26,13 +27,13 @@ func hlog() *slog.Logger { var errProcessWithoutRSS = fmt.Errorf("process with zero rss") -// Harvester manages sampling for individual processes. It is used by the Process Sampler to get information about the +// Harvester manages sampling for individual processes. It is used by the Process Collector to get information about the // existing processes. type Harvester interface { // Pids return the IDs of all the processes that are currently running Pids() ([]int32, error) - // Do performs the actual harvesting operation, returning a process sample containing all the metrics data - Do(pid int32) (*Sample, error) + // Do performs the actual harvesting operation, returning a process status containing all the metrics data + Do(pid int32) (*Status, error) } type RunMode string @@ -85,15 +86,15 @@ func (*linuxHarvester) Pids() ([]int32, error) { return process.Pids() } -// Do returns a sample of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the -// time since this process was sampled for the last time. If the process has been sampled for the first time, this value +// Do returns a status of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the +// time since this process was statusd for the last time. If the process has been statusd for the first time, this value // will be ignored -func (ps *linuxHarvester) Do(pid int32) (*Sample, error) { +func (ps *linuxHarvester) Do(pid int32) (*Status, error) { // Reuses process information that does not vary - cached, hasCachedSample := ps.cache.Get(pid) + cached, hasCachedEntry := ps.cache.Get(pid) // If cached is nil, the linux process will be created from fresh data - if !hasCachedSample { + if !hasCachedEntry { cached = &cacheEntry{} } var err error @@ -107,98 +108,98 @@ func (ps *linuxHarvester) Do(pid int32) (*Sample, error) { return nil, errProcessWithoutRSS } - // Creates a fresh process sample and populates it with the metrics data - sample := NewSample(pid) + // Creates a fresh process status and populates it with the metrics data + status := NewStatus(pid) - if err := ps.populateStaticData(sample, cached.process); err != nil { + if err := ps.populateStaticData(status, cached.process); err != nil { return nil, errors.Wrap(err, "can't populate static attributes") } // As soon as we have successfully stored the static (reusable) values, we can cache the entry - if !hasCachedSample { + if !hasCachedEntry { ps.cache.Add(pid, cached) } - if err := ps.populateGauges(sample, cached.process); err != nil { + if err := ps.populateGauges(status, cached.process); err != nil { return nil, errors.Wrap(err, "can't fetch gauge data") } - if err := ps.populateIOCounters(sample, cached.process); err != nil { + if err := ps.populateIOCounters(status, cached.process); err != nil { return nil, errors.Wrap(err, "can't fetch deltas") } - cached.lastSample = sample + cached.last = status - return sample, nil + return status, nil } -// populateStaticData populates the sample with the process data won't vary during the process life cycle -func (ps *linuxHarvester) populateStaticData(sample *Sample, process *linuxProcess) error { +// populateStaticData populates the status with the process data won't vary during the process life cycle +func (ps *linuxHarvester) populateStaticData(status *Status, process *linuxProcess) error { var err error - sample.CmdLine, err = process.CmdLine(!ps.stripCommandLine) + status.CmdLine, err = process.CmdLine(!ps.stripCommandLine) if err != nil { return errors.Wrap(err, "acquiring command line") } - sample.ProcessID = process.Pid() + status.ProcessID = process.Pid() - sample.User, err = process.Username() + status.User, err = process.Username() if err != nil { - ps.log.Debug("can't get username for process", "pid", sample.ProcessID, "error", err) + ps.log.Debug("can't get username for process", "pid", status.ProcessID, "error", err) } - sample.Command = process.Command() - sample.ParentProcessID = process.Ppid() + status.Command = process.Command() + status.ParentProcessID = process.Ppid() return nil } -// populateGauges populates the sample with gauge data that represents the process state at a given point -func (ps *linuxHarvester) populateGauges(sample *Sample, process *linuxProcess) error { +// populateGauges populates the status with gauge data that represents the process state at a given point +func (ps *linuxHarvester) populateGauges(status *Status, process *linuxProcess) error { var err error cpuTimes, err := process.CPUTimes() if err != nil { return err } - sample.CPUPercent = cpuTimes.Percent + status.CPUPercent = cpuTimes.Percent totalCPU := cpuTimes.User + cpuTimes.System if totalCPU > 0 { - sample.CPUUserPercent = (cpuTimes.User / totalCPU) * sample.CPUPercent - sample.CPUSystemPercent = (cpuTimes.System / totalCPU) * sample.CPUPercent + status.CPUUserPercent = (cpuTimes.User / totalCPU) * status.CPUPercent + status.CPUSystemPercent = (cpuTimes.System / totalCPU) * status.CPUPercent } else { - sample.CPUUserPercent = 0 - sample.CPUSystemPercent = 0 + status.CPUUserPercent = 0 + status.CPUSystemPercent = 0 } if ps.privileged { - sample.FdCount, err = process.NumFDs() + status.FdCount, err = process.NumFDs() if err != nil { return err } } // Extra status data - sample.Status = process.Status() - sample.ThreadCount = process.NumThreads() - sample.MemoryVMSBytes = process.VMSize() - sample.MemoryRSSBytes = process.VMRSS() + status.Status = process.Status() + status.ThreadCount = process.NumThreads() + status.MemoryVMSBytes = process.VMSize() + status.MemoryRSSBytes = process.VMRSS() return nil } -// populateIOCounters fills the sample with the IO counters data. For the "X per second" metrics, it requires the -// last process sample for comparative purposes -func (ps *linuxHarvester) populateIOCounters(sample *Sample, source *linuxProcess) error { +// populateIOCounters fills the status with the IO counters data. For the "X per second" metrics, it requires the +// last process status for comparative purposes +func (ps *linuxHarvester) populateIOCounters(status *Status, source *linuxProcess) error { ioCounters, err := source.IOCounters() if err != nil { return err } - sample.IOReadCount = ioCounters.ReadCount - sample.IOWriteCount = ioCounters.WriteCount - sample.IOReadBytes = ioCounters.ReadBytes - sample.IOWriteBytes = ioCounters.WriteBytes + status.IOReadCount = ioCounters.ReadCount + status.IOWriteCount = ioCounters.WriteCount + status.IOReadBytes = ioCounters.ReadBytes + status.IOWriteBytes = ioCounters.WriteBytes return nil } diff --git a/pkg/internal/infraolly/process/harvester_test.go b/pkg/internal/infraolly/process/harvest_test.go similarity index 66% rename from pkg/internal/infraolly/process/harvester_test.go rename to pkg/internal/infraolly/process/harvest_test.go index 99d2d86ea..6b21a6dc9 100644 --- a/pkg/internal/infraolly/process/harvester_test.go +++ b/pkg/internal/infraolly/process/harvest_test.go @@ -34,14 +34,14 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { h := newHarvester(Config{ProcFSRoot: "/proc", RunMode: c.mode}, cache) // If not privileged, it is expected to not report neither FDs nor IO counters - sample, err := h.Do(int32(os.Getpid())) + status, err := h.Do(int32(os.Getpid())) require.NoError(t, err) if c.privileged { - assert.NotZero(t, sample.FdCount) - assert.NotZero(t, sample.IOReadCount) + assert.NotZero(t, status.FdCount) + assert.NotZero(t, status.IOReadCount) } else { - assert.Zero(t, sample.FdCount) - assert.Zero(t, sample.IOReadCount) + assert.Zero(t, status.FdCount) + assert.Zero(t, status.IOReadCount) } }) } @@ -52,28 +52,28 @@ func TestLinuxHarvester_Do(t *testing.T) { cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) h := newHarvester(Config{ProcFSRoot: "/proc"}, cache) - // When retrieving for a given process sample (e.g. the current testing executable) - sample, err := h.Do(int32(os.Getpid())) + // When retrieving for a given process status (e.g. the current testing executable) + status, err := h.Do(int32(os.Getpid())) - // It returns the corresponding process sample with valid data + // It returns the corresponding process status with valid data require.NoError(t, err) - require.NotNil(t, sample) + require.NotNil(t, status) - assert.Equal(t, int32(os.Getpid()), sample.ProcessID) - assert.Equal(t, "process.test", sample.Command) - assert.Contains(t, sample.CmdLine, os.Args[0]) - assert.NotEmpty(t, sample.User) - assert.Contains(t, "RSD", sample.Status, + assert.Equal(t, int32(os.Getpid()), status.ProcessID) + assert.Equal(t, "process.test", status.Command) + assert.Contains(t, status.CmdLine, os.Args[0]) + assert.NotEmpty(t, status.User) + assert.Contains(t, "RSD", status.Status, "process status must be R (running), S (interruptible sleep) or D (uninterruptible sleep)") - assert.NotZero(t, sample.MemoryVMSBytes) - assert.NotZero(t, sample.MemoryRSSBytes) - assert.NotZero(t, sample.CPUPercent) - assert.NotZero(t, sample.CPUUserPercent) - assert.NotZero(t, sample.CPUSystemPercent) - assert.NotZero(t, sample.ParentProcessID) - assert.NotZero(t, sample.ThreadCount) - assert.NotZero(t, sample.FdCount) - assert.NotZero(t, sample.ThreadCount) + assert.NotZero(t, status.MemoryVMSBytes) + assert.NotZero(t, status.MemoryRSSBytes) + assert.NotZero(t, status.CPUPercent) + assert.NotZero(t, status.CPUUserPercent) + assert.NotZero(t, status.CPUSystemPercent) + assert.NotZero(t, status.ParentProcessID) + assert.NotZero(t, status.ThreadCount) + assert.NotZero(t, status.FdCount) + assert.NotZero(t, status.ThreadCount) } func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { @@ -88,15 +88,15 @@ func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { h := newHarvester(Config{ProcFSRoot: "/proc", FullCommandLine: true}, cache) test.Eventually(t, 5*time.Second, func(t require.TestingT) { - // When retrieving for a given process sample (e.g. the current testing executable) - sample, err := h.Do(int32(cmd.Process.Pid)) + // When retrieving for a given process status (e.g. the current testing executable) + status, err := h.Do(int32(cmd.Process.Pid)) // It returns the corresponding Command line without stripping arguments require.NoError(t, err) - require.NotNil(t, sample) + require.NotNil(t, status) - assert.False(t, strings.HasSuffix(sample.CmdLine, "sleep"), "%q should have arguments", sample.CmdLine) - assert.Contains(t, sample.CmdLine, "sleep") + assert.False(t, strings.HasSuffix(status.CmdLine, "sleep"), "%q should have arguments", status.CmdLine) + assert.Contains(t, status.CmdLine, "sleep") }) } @@ -112,14 +112,14 @@ func TestLinuxHarvester_Do_StripCommandLine(t *testing.T) { h := newHarvester(Config{ProcFSRoot: "/proc", FullCommandLine: true}, cache) test.Eventually(t, 5*time.Second, func(t require.TestingT) { - // When retrieving for a given process sample (e.g. the current testing executable) - sample, err := h.Do(int32(cmd.Process.Pid)) + // When retrieving for a given process status (e.g. the current testing executable) + status, err := h.Do(int32(cmd.Process.Pid)) // It returns the corresponding Command line without stripping arguments require.NoError(t, err) - require.NotNil(t, sample) + require.NotNil(t, status) - assert.True(t, strings.HasSuffix(sample.CmdLine, "sleep"), "%q should not have arguments", sample.CmdLine) + assert.True(t, strings.HasSuffix(status.CmdLine, "sleep"), "%q should not have arguments", status.CmdLine) }) } @@ -133,12 +133,12 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentCmd(t *testing.T) { h := newHarvester(Config{ProcFSRoot: "/proc"}, cache) // When the process is harvested - sample, err := h.Do(currentPid) + status, err := h.Do(currentPid) require.NoError(t, err) - // The sample is updated - assert.NotEmpty(t, sample.Command) - assert.NotEqual(t, "something old", sample.Command) + // The status is updated + assert.NotEmpty(t, status.Command) + assert.NotEqual(t, "something old", status.Command) } func TestLinuxHarvester_Do_InvalidateCache_DifferentPid(t *testing.T) { @@ -151,9 +151,9 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentPid(t *testing.T) { h := newHarvester(Config{ProcFSRoot: "/proc"}, cache) // When the process is harvested - sample, err := h.Do(currentPid) + status, err := h.Do(currentPid) require.NoError(t, err) - // The sample is updated - assert.NotEqual(t, -1, sample.ParentProcessID) + // The status is updated + assert.NotEqual(t, -1, status.ParentProcessID) } diff --git a/pkg/internal/infraolly/process/snapshot.go b/pkg/internal/infraolly/process/snapshot.go index 757e1780c..f7348dc24 100644 --- a/pkg/internal/infraolly/process/snapshot.go +++ b/pkg/internal/infraolly/process/snapshot.go @@ -43,7 +43,7 @@ type linuxProcess struct { procFSRoot string - // data that will be reused between samples of the same process. + // data that will be reused between harvests of the same process. pid int32 user string cmdLine string diff --git a/pkg/internal/infraolly/process/sample.go b/pkg/internal/infraolly/process/status.go similarity index 80% rename from pkg/internal/infraolly/process/sample.go rename to pkg/internal/infraolly/process/status.go index 45548d418..829a3f0c7 100644 --- a/pkg/internal/infraolly/process/sample.go +++ b/pkg/internal/infraolly/process/status.go @@ -5,10 +5,10 @@ package process import "log/slog" func pslog() *slog.Logger { - return slog.With("component", "process.Sampler") + return slog.With("component", "process.Collector") } -type Sample struct { +type Status struct { ProcessID int32 Command string User string @@ -28,8 +28,8 @@ type Sample struct { IOWriteBytes uint64 } -func NewSample(pid int32) *Sample { - return &Sample{ +func NewStatus(pid int32) *Status { + return &Status{ ProcessID: pid, } } From 0d349497b7a40ce38dba476d3034b68768cee31e Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Wed, 29 May 2024 17:18:42 +0200 Subject: [PATCH 07/27] Collector provider --- pkg/internal/infraolly/process/collect.go | 76 +++++++++++++------ pkg/internal/infraolly/process/harvest.go | 2 +- .../infraolly/process/harvest_test.go | 12 +-- 3 files changed, 58 insertions(+), 32 deletions(-) diff --git a/pkg/internal/infraolly/process/collect.go b/pkg/internal/infraolly/process/collect.go index e90b799bb..bf666377a 100644 --- a/pkg/internal/infraolly/process/collect.go +++ b/pkg/internal/infraolly/process/collect.go @@ -5,52 +5,78 @@ package process import ( + "context" "log/slog" "math" "time" "github.com/hashicorp/golang-lru/v2/simplelru" "github.com/mariomac/pipes/pipe" + + "github.com/grafana/beyla/pkg/internal/request" ) // Collector returns runtime information about the currently running processes type Collector struct { - harvest Harvester - interval time.Duration - cache *simplelru.LRU[int32, *cacheEntry] - log *slog.Logger + ctx context.Context + cfg *Config + harvest Harvester + cache *simplelru.LRU[int32, *cacheEntry] + log *slog.Logger } -// NewCollector creates and returns a new process Collector, given an agent context. -func NewCollector(cfg Config) pipe.StartFunc[[]Status] { - // we purge entries explicitly so size is unbounded - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - harvest := newHarvester(cfg, cache) - - return (&Collector{ - harvest: harvest, - cache: cache, - interval: cfg.Rate, - log: pslog(), - }).Run +// NewCollectorProvider creates and returns a new process Collector, given an agent context. +func NewCollectorProvider(ctx context.Context, cfg *Config) pipe.MiddleProvider[[]request.Span, []*Status] { + return func() (pipe.MiddleFunc[[]request.Span, []*Status], error) { + // we purge entries explicitly so size is unbounded + cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + harvest := newHarvester(cfg, cache) + + return (&Collector{ + ctx: ctx, + cfg: cfg, + harvest: harvest, + cache: cache, + log: pslog(), + }).Run, nil + } } -func (ps *Collector) Run(out chan<- []Status) { - _ = out +func (ps *Collector) Run(in <-chan []request.Span, out chan<- []*Status) { + // TODO: set app metadata as key for later decoration? (e.g. K8s metadata, svc.ID) + pids := map[int32]struct{}{} + collectTicker := time.NewTicker(ps.cfg.Rate) + for { + select { + case <-ps.ctx.Done(): + ps.log.Debug("exiting") + case spans := <-in: + // updating PIDs map with spans information + for i := range spans { + pids[int32(spans[i].Pid.UserPID)] = struct{}{} + } + case <-collectTicker.C: + ps.log.Debug("start process collection") + procs, removed := ps.Collect(pids) + for _, rp := range removed { + pids[rp] = struct{}{} + } + out <- procs + } + } } // Collect returns the status for all the running processes, decorated with Docker runtime information, if applies. -func (ps *Collector) Collect() ([]*Status, error) { - pids, err := ps.harvest.Pids() - if err != nil { - return nil, err - } +// It also returns the PIDs that have to be removed from the map, as they do not exist anymore +func (ps *Collector) Collect(pids map[int32]struct{}) ([]*Status, []int32) { results := make([]*Status, 0, len(pids)) - for _, pid := range pids { + var removed []int32 + for pid := range pids { status, err := ps.harvest.Do(pid) if err != nil { ps.log.Debug("skipping process", "pid", pid, "error", err) + removed = append(removed, pid) continue } @@ -59,5 +85,5 @@ func (ps *Collector) Collect() ([]*Status, error) { removeUntilLen(ps.cache, len(pids)) - return results, nil + return results, removed } diff --git a/pkg/internal/infraolly/process/harvest.go b/pkg/internal/infraolly/process/harvest.go index b7e600f06..2b1fd0cf0 100644 --- a/pkg/internal/infraolly/process/harvest.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -53,7 +53,7 @@ type Config struct { Rate time.Duration } -func newHarvester(cfg Config, cache *simplelru.LRU[int32, *cacheEntry]) *linuxHarvester { +func newHarvester(cfg *Config, cache *simplelru.LRU[int32, *cacheEntry]) *linuxHarvester { // If not config, assuming root mode as default privileged := cfg.RunMode == RunModeRoot || cfg.RunMode == RunModePrivileged disableZeroRSSFilter := cfg.DisableZeroRSSFilter diff --git a/pkg/internal/infraolly/process/harvest_test.go b/pkg/internal/infraolly/process/harvest_test.go index 6b21a6dc9..132a0cfa4 100644 --- a/pkg/internal/infraolly/process/harvest_test.go +++ b/pkg/internal/infraolly/process/harvest_test.go @@ -31,7 +31,7 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { for _, c := range cases { t.Run(fmt.Sprint("mode ", c.mode), func(t *testing.T) { cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(Config{ProcFSRoot: "/proc", RunMode: c.mode}, cache) + h := newHarvester(&Config{ProcFSRoot: "/proc", RunMode: c.mode}, cache) // If not privileged, it is expected to not report neither FDs nor IO counters status, err := h.Do(int32(os.Getpid())) @@ -50,7 +50,7 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { func TestLinuxHarvester_Do(t *testing.T) { // Given a process harvester cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(Config{ProcFSRoot: "/proc"}, cache) + h := newHarvester(&Config{ProcFSRoot: "/proc"}, cache) // When retrieving for a given process status (e.g. the current testing executable) status, err := h.Do(int32(os.Getpid())) @@ -85,7 +85,7 @@ func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { // Given a process harvester configured to showw the full command line cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(Config{ProcFSRoot: "/proc", FullCommandLine: true}, cache) + h := newHarvester(&Config{ProcFSRoot: "/proc", FullCommandLine: true}, cache) test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process status (e.g. the current testing executable) @@ -109,7 +109,7 @@ func TestLinuxHarvester_Do_StripCommandLine(t *testing.T) { // Given a process harvester cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(Config{ProcFSRoot: "/proc", FullCommandLine: true}, cache) + h := newHarvester(&Config{ProcFSRoot: "/proc", FullCommandLine: true}, cache) test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process status (e.g. the current testing executable) @@ -130,7 +130,7 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentCmd(t *testing.T) { // That has cached an old process sharing the PID with a new process cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) cache.Add(currentPid, &cacheEntry{process: &linuxProcess{cmdLine: "something old"}}) - h := newHarvester(Config{ProcFSRoot: "/proc"}, cache) + h := newHarvester(&Config{ProcFSRoot: "/proc"}, cache) // When the process is harvested status, err := h.Do(currentPid) @@ -148,7 +148,7 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentPid(t *testing.T) { // That has cached an old process sharing the PID with a new process cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) cache.Add(currentPid, &cacheEntry{process: &linuxProcess{stats: procStats{ppid: -1}}}) - h := newHarvester(Config{ProcFSRoot: "/proc"}, cache) + h := newHarvester(&Config{ProcFSRoot: "/proc"}, cache) // When the process is harvested status, err := h.Do(currentPid) From 50fe1c44493d396211b67aa9d6e771c1db7870b4 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Thu, 30 May 2024 12:11:27 +0200 Subject: [PATCH 08/27] connecting pipelines --- pkg/beyla/config.go | 10 +++ pkg/internal/export/otel/metrics.go | 21 +++--- pkg/internal/export/prom/prom.go | 18 ++++-- pkg/internal/infraolly/infra_pipe.go | 64 +++++++++++++++++++ pkg/internal/infraolly/process/collect.go | 11 ++-- pkg/internal/infraolly/process/harvest.go | 3 +- .../infraolly/process/harvest_test.go | 8 +-- pkg/internal/infraolly/process/snapshot.go | 4 +- pkg/internal/infraolly/process/status.go | 2 +- 9 files changed, 112 insertions(+), 29 deletions(-) create mode 100644 pkg/internal/infraolly/infra_pipe.go diff --git a/pkg/beyla/config.go b/pkg/beyla/config.go index 47de0fe38..272d64a50 100644 --- a/pkg/beyla/config.go +++ b/pkg/beyla/config.go @@ -17,6 +17,7 @@ import ( "github.com/grafana/beyla/pkg/internal/export/prom" "github.com/grafana/beyla/pkg/internal/filter" "github.com/grafana/beyla/pkg/internal/imetrics" + "github.com/grafana/beyla/pkg/internal/infraolly/process" "github.com/grafana/beyla/pkg/internal/traces" "github.com/grafana/beyla/pkg/services" "github.com/grafana/beyla/pkg/transform" @@ -99,6 +100,11 @@ var DefaultConfig = Config{ }, Routes: &transform.RoutesConfig{}, NetworkFlows: defaultNetworkConfig, + Processes: process.Config{ + RunMode: process.RunModeRoot, + ProcFSRoot: "/proc", + Rate: 5 * time.Second, + }, } type Config struct { @@ -149,6 +155,10 @@ type Config struct { ProfilePort int `yaml:"profile_port" env:"BEYLA_PROFILE_PORT"` InternalMetrics imetrics.Config `yaml:"internal_metrics"` + // Processes metrics for application. They will be only enabled if there is a metrics exporter enabled, + // and both the "application" and "application_process" features are enabled + Processes process.Config `yaml:"processes"` + // Grafana Agent specific configuration TracesReceiver TracesReceiverConfig `yaml:"-"` } diff --git a/pkg/internal/export/otel/metrics.go b/pkg/internal/export/otel/metrics.go index 720136e45..bfb2e0d26 100644 --- a/pkg/internal/export/otel/metrics.go +++ b/pkg/internal/export/otel/metrics.go @@ -55,6 +55,7 @@ const ( FeatureApplication = "application" FeatureSpan = "application_span" FeatureGraph = "application_service_graph" + FeatureProcess = "application_processes" ) type MetricsConfig struct { @@ -130,24 +131,24 @@ func (m *MetricsConfig) GuessProtocol() Protocol { // Reason to disable linting: it requires to be a value despite it is considered a "heavy struct". // This method is invoked only once during startup time so it doesn't have a noticeable performance impact. // nolint:gocritic -func (m MetricsConfig) EndpointEnabled() bool { +func (m *MetricsConfig) EndpointEnabled() bool { return m.CommonEndpoint != "" || m.MetricsEndpoint != "" || m.Grafana.MetricsEnabled() } -func (m MetricsConfig) SpanMetricsEnabled() bool { +func (m *MetricsConfig) SpanMetricsEnabled() bool { return slices.Contains(m.Features, FeatureSpan) } -func (m MetricsConfig) ServiceGraphMetricsEnabled() bool { +func (m *MetricsConfig) ServiceGraphMetricsEnabled() bool { return slices.Contains(m.Features, FeatureGraph) } -func (m MetricsConfig) OTelMetricsEnabled() bool { +func (m *MetricsConfig) AppMetricsEnabled() bool { return slices.Contains(m.Features, FeatureApplication) } -func (m MetricsConfig) Enabled() bool { - return m.EndpointEnabled() && (m.OTelMetricsEnabled() || m.SpanMetricsEnabled() || m.ServiceGraphMetricsEnabled()) +func (m *MetricsConfig) Enabled() bool { + return m.EndpointEnabled() && (m.AppMetricsEnabled() || m.SpanMetricsEnabled() || m.ServiceGraphMetricsEnabled()) } // MetricsReporter implements the graph node that receives request.Span @@ -273,7 +274,7 @@ func newMetricsReporter( } func (mr *MetricsReporter) otelMetricOptions(mlog *slog.Logger) []metric.Option { - if !mr.cfg.OTelMetricsEnabled() { + if !mr.cfg.AppMetricsEnabled() { return []metric.Option{} } @@ -316,7 +317,7 @@ func (mr *MetricsReporter) graphMetricOptions(mlog *slog.Logger) []metric.Option } func (mr *MetricsReporter) setupOtelMeters(m *Metrics, meter instrument.Meter) error { - if !mr.cfg.OTelMetricsEnabled() { + if !mr.cfg.AppMetricsEnabled() { return nil } @@ -447,7 +448,7 @@ func (mr *MetricsReporter) newMetricSet(service svc.ID) (*Metrics, error) { // TODO: set ExplicitBucketBoundaries here and in prometheus from the previous specification meter := m.provider.Meter(reporterName) var err error - if mr.cfg.OTelMetricsEnabled() { + if mr.cfg.AppMetricsEnabled() { err = mr.setupOtelMeters(&m, meter) if err != nil { return nil, err @@ -650,7 +651,7 @@ func (r *Metrics) record(span *request.Span, mr *MetricsReporter) { t := span.Timings() duration := t.End.Sub(t.RequestStart).Seconds() - if mr.cfg.OTelMetricsEnabled() { + if mr.cfg.AppMetricsEnabled() { switch span.Type { case request.EventTypeHTTP: // TODO: for more accuracy, there must be a way to set the metric time from the actual span end time diff --git a/pkg/internal/export/prom/prom.go b/pkg/internal/export/prom/prom.go index 8a5dfcea0..f30938824 100644 --- a/pkg/internal/export/prom/prom.go +++ b/pkg/internal/export/prom/prom.go @@ -112,21 +112,25 @@ type PrometheusConfig struct { Registry *prometheus.Registry `yaml:"-"` } -func (p PrometheusConfig) SpanMetricsEnabled() bool { +func (p *PrometheusConfig) SpanMetricsEnabled() bool { return slices.Contains(p.Features, otel.FeatureSpan) } -func (p PrometheusConfig) OTelMetricsEnabled() bool { +func (p *PrometheusConfig) AppMetricsEnabled() bool { return slices.Contains(p.Features, otel.FeatureApplication) } -func (p PrometheusConfig) ServiceGraphMetricsEnabled() bool { +func (p *PrometheusConfig) ServiceGraphMetricsEnabled() bool { return slices.Contains(p.Features, otel.FeatureGraph) } +func (p *PrometheusConfig) EndpointEnabled() bool { + return p.Port != 0 || p.Registry != nil +} + // nolint:gocritic -func (p PrometheusConfig) Enabled() bool { - return (p.Port != 0 || p.Registry != nil) && (p.OTelMetricsEnabled() || p.SpanMetricsEnabled() || p.ServiceGraphMetricsEnabled()) +func (p *PrometheusConfig) Enabled() bool { + return p.EndpointEnabled() && (p.AppMetricsEnabled() || p.SpanMetricsEnabled() || p.ServiceGraphMetricsEnabled()) } type metricsReporter struct { @@ -364,7 +368,7 @@ func newReporter( registeredMetrics = append(registeredMetrics, mr.beylaInfo) } - if cfg.OTelMetricsEnabled() { + if cfg.AppMetricsEnabled() { registeredMetrics = append(registeredMetrics, mr.httpClientRequestSize, mr.httpClientDuration, @@ -422,7 +426,7 @@ func (r *metricsReporter) observe(span *request.Span) { t := span.Timings() r.beylaInfo.WithLabelValues(span.ServiceID.SDKLanguage.String()).Set(1.0) duration := t.End.Sub(t.RequestStart).Seconds() - if r.cfg.OTelMetricsEnabled() { + if r.cfg.AppMetricsEnabled() { switch span.Type { case request.EventTypeHTTP: r.httpDuration.WithLabelValues( diff --git a/pkg/internal/infraolly/infra_pipe.go b/pkg/internal/infraolly/infra_pipe.go new file mode 100644 index 000000000..f65c87d08 --- /dev/null +++ b/pkg/internal/infraolly/infra_pipe.go @@ -0,0 +1,64 @@ +package infraolly + +import ( + "context" + "fmt" + "slices" + + "github.com/mariomac/pipes/pipe" + + "github.com/grafana/beyla/pkg/beyla" + "github.com/grafana/beyla/pkg/internal/export/otel" + "github.com/grafana/beyla/pkg/internal/infraolly/process" + "github.com/grafana/beyla/pkg/internal/request" +) + +// SubPipeline is actually a part of the Application Observability pipeline. +// Its management is moved here because it's only activated if the process +// metrics are activated. +type subPipeline struct { + Collector pipe.Start[[]*process.Status] + OtelExport pipe.Final[[]*process.Status] + PromExport pipe.Final[[]*process.Status] +} + +func collector(sp *subPipeline) *pipe.Start[[]*process.Status] { return &sp.Collector } +func otelExport(sp *subPipeline) *pipe.Final[[]*process.Status] { return &sp.OtelExport } +func promExport(sp *subPipeline) *pipe.Final[[]*process.Status] { return &sp.PromExport } + +func (sp *subPipeline) Connect() { + sp.Collector.SendTo(sp.OtelExport, sp.PromExport) +} + +// the sub-pipe is enabled only if there is a metrics exporter enabled, +// and both the "application" and "application_process" features are enabled +func isSubPipeEnabled(cfg *beyla.Config) bool { + return (cfg.Metrics.EndpointEnabled() && cfg.Metrics.AppMetricsEnabled() && + slices.Contains(cfg.Metrics.Features, otel.FeatureProcess)) || + (cfg.Prometheus.EndpointEnabled() && cfg.Prometheus.AppMetricsEnabled() && + slices.Contains(cfg.Prometheus.Features, otel.FeatureProcess)) +} + +// SubPipelineProvider returns a Final node that actually has a pipeline inside. +// It is manually connected through a channel +func SubPipelineProvider(ctx context.Context, cfg *beyla.Config) pipe.FinalProvider[[]request.Span] { + return func() (pipe.FinalFunc[[]request.Span], error) { + if !isSubPipeEnabled(cfg) { + return pipe.IgnoreFinal[[]request.Span](), nil + } + connectorChan := make(chan []request.Span, cfg.ChannelBufferLen) + var connector <-chan []request.Span = connectorChan + nb := pipe.NewBuilder(&subPipeline{}, pipe.ChannelBufferLen(cfg.ChannelBufferLen)) + pipe.AddStartProvider(nb, collector, process.NewCollectorProvider(ctx, &connector, &cfg.Processes)) + + runner, err := nb.Build() + if err != nil { + return nil, fmt.Errorf("creating process subpipeline: %w", err) + } + return func(in <-chan []request.Span) { + connector = in + runner.Start() + <-ctx.Done() + }, nil + } +} diff --git a/pkg/internal/infraolly/process/collect.go b/pkg/internal/infraolly/process/collect.go index bf666377a..904d5803f 100644 --- a/pkg/internal/infraolly/process/collect.go +++ b/pkg/internal/infraolly/process/collect.go @@ -23,11 +23,12 @@ type Collector struct { harvest Harvester cache *simplelru.LRU[int32, *cacheEntry] log *slog.Logger + newPids *<-chan []request.Span } // NewCollectorProvider creates and returns a new process Collector, given an agent context. -func NewCollectorProvider(ctx context.Context, cfg *Config) pipe.MiddleProvider[[]request.Span, []*Status] { - return func() (pipe.MiddleFunc[[]request.Span, []*Status], error) { +func NewCollectorProvider(ctx context.Context, input *<-chan []request.Span, cfg *Config) pipe.StartProvider[[]*Status] { + return func() (pipe.StartFunc[[]*Status], error) { // we purge entries explicitly so size is unbounded cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) harvest := newHarvester(cfg, cache) @@ -38,19 +39,21 @@ func NewCollectorProvider(ctx context.Context, cfg *Config) pipe.MiddleProvider[ harvest: harvest, cache: cache, log: pslog(), + newPids: input, }).Run, nil } } -func (ps *Collector) Run(in <-chan []request.Span, out chan<- []*Status) { +func (ps *Collector) Run(out chan<- []*Status) { // TODO: set app metadata as key for later decoration? (e.g. K8s metadata, svc.ID) pids := map[int32]struct{}{} collectTicker := time.NewTicker(ps.cfg.Rate) + newPids := *ps.newPids for { select { case <-ps.ctx.Done(): ps.log.Debug("exiting") - case spans := <-in: + case spans := <-newPids: // updating PIDs map with spans information for i := range spans { pids[int32(spans[i].Pid.UserPID)] = struct{}{} diff --git a/pkg/internal/infraolly/process/harvest.go b/pkg/internal/infraolly/process/harvest.go index 2b1fd0cf0..129aa4f3c 100644 --- a/pkg/internal/infraolly/process/harvest.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -47,6 +47,7 @@ const ( type Config struct { RunMode RunMode DisableZeroRSSFilter bool + // TODO: replace configuration option by attributes-aware set FullCommandLine bool ProcFSRoot string @@ -136,7 +137,7 @@ func (ps *linuxHarvester) Do(pid int32) (*Status, error) { // populateStaticData populates the status with the process data won't vary during the process life cycle func (ps *linuxHarvester) populateStaticData(status *Status, process *linuxProcess) error { var err error - status.CmdLine, err = process.CmdLine(!ps.stripCommandLine) + status.CommandLine, err = process.CmdLine(!ps.stripCommandLine) if err != nil { return errors.Wrap(err, "acquiring command line") } diff --git a/pkg/internal/infraolly/process/harvest_test.go b/pkg/internal/infraolly/process/harvest_test.go index 132a0cfa4..399b87517 100644 --- a/pkg/internal/infraolly/process/harvest_test.go +++ b/pkg/internal/infraolly/process/harvest_test.go @@ -61,7 +61,7 @@ func TestLinuxHarvester_Do(t *testing.T) { assert.Equal(t, int32(os.Getpid()), status.ProcessID) assert.Equal(t, "process.test", status.Command) - assert.Contains(t, status.CmdLine, os.Args[0]) + assert.Contains(t, status.CommandLine, os.Args[0]) assert.NotEmpty(t, status.User) assert.Contains(t, "RSD", status.Status, "process status must be R (running), S (interruptible sleep) or D (uninterruptible sleep)") @@ -95,8 +95,8 @@ func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { require.NoError(t, err) require.NotNil(t, status) - assert.False(t, strings.HasSuffix(status.CmdLine, "sleep"), "%q should have arguments", status.CmdLine) - assert.Contains(t, status.CmdLine, "sleep") + assert.False(t, strings.HasSuffix(status.CommandLine, "sleep"), "%q should have arguments", status.CommandLine) + assert.Contains(t, status.CommandLine, "sleep") }) } @@ -119,7 +119,7 @@ func TestLinuxHarvester_Do_StripCommandLine(t *testing.T) { require.NoError(t, err) require.NotNil(t, status) - assert.True(t, strings.HasSuffix(status.CmdLine, "sleep"), "%q should not have arguments", status.CmdLine) + assert.True(t, strings.HasSuffix(status.CommandLine, "sleep"), "%q should not have arguments", status.CommandLine) }) } diff --git a/pkg/internal/infraolly/process/snapshot.go b/pkg/internal/infraolly/process/snapshot.go index f7348dc24..0c6a4d2cc 100644 --- a/pkg/internal/infraolly/process/snapshot.go +++ b/pkg/internal/infraolly/process/snapshot.go @@ -89,8 +89,8 @@ func getLinuxProcess(procFSRoot string, pid int32, previous *linuxProcess, privi // Reusing information from the last snapshot for the same process // If the name or the PPID changed from the previous, we'll consider this sample is just // a new process that shares the PID with an old one. - // if a process with the same Command but different CmdLine or User name - // occupies the same PID, the cache won't refresh the CmdLine and Username. + // if a process with the same Command but different CommandLine or User name + // occupies the same PID, the cache won't refresh the CommandLine and Username. if previous == nil || procStats.command != previous.Command() || procStats.ppid != previous.Ppid() { gops, err = process.NewProcess(pid) if err != nil { diff --git a/pkg/internal/infraolly/process/status.go b/pkg/internal/infraolly/process/status.go index 829a3f0c7..2a392be84 100644 --- a/pkg/internal/infraolly/process/status.go +++ b/pkg/internal/infraolly/process/status.go @@ -17,7 +17,7 @@ type Status struct { CPUPercent float64 CPUUserPercent float64 CPUSystemPercent float64 - CmdLine string + CommandLine string Status string ParentProcessID int32 ThreadCount int32 From 87cbabb3b98b6e22ae8df3753f79596adf346b8c Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Thu, 30 May 2024 12:47:19 +0200 Subject: [PATCH 09/27] basic integration --- pkg/internal/export/otel/metrics.go | 2 +- pkg/internal/export/otel/metrics_test.go | 18 +++++++++--------- pkg/internal/infraolly/infra_pipe.go | 10 ++++++++++ pkg/internal/infraolly/process/cache.go | 5 ++--- pkg/internal/infraolly/process/collect.go | 1 - pkg/internal/infraolly/process/harvest.go | 12 +++++------- pkg/internal/infraolly/process/harvest_test.go | 1 - pkg/internal/infraolly/process/snapshot.go | 3 +-- .../infraolly/process/snapshot_test.go | 1 - pkg/internal/infraolly/process/status.go | 2 -- pkg/internal/pipe/instrumenter.go | 8 +++++++- test/integration/docker-compose-python.yml | 3 ++- test/integration/suites_test.go | 4 ++-- 13 files changed, 39 insertions(+), 31 deletions(-) diff --git a/pkg/internal/export/otel/metrics.go b/pkg/internal/export/otel/metrics.go index bfb2e0d26..10a2b21c9 100644 --- a/pkg/internal/export/otel/metrics.go +++ b/pkg/internal/export/otel/metrics.go @@ -55,7 +55,7 @@ const ( FeatureApplication = "application" FeatureSpan = "application_span" FeatureGraph = "application_service_graph" - FeatureProcess = "application_processes" + FeatureProcess = "application_process" ) type MetricsConfig struct { diff --git a/pkg/internal/export/otel/metrics_test.go b/pkg/internal/export/otel/metrics_test.go index bb98c63ac..fd1ef5fae 100644 --- a/pkg/internal/export/otel/metrics_test.go +++ b/pkg/internal/export/otel/metrics_test.go @@ -341,19 +341,19 @@ func TestMetricSetupHTTP_DoNotOverrideEnv(t *testing.T) { } func TestMetricsConfig_Enabled(t *testing.T) { - assert.True(t, MetricsConfig{Features: []string{FeatureApplication, FeatureNetwork}, CommonEndpoint: "foo"}.Enabled()) - assert.True(t, MetricsConfig{Features: []string{FeatureApplication}, MetricsEndpoint: "foo"}.Enabled()) - assert.True(t, MetricsConfig{Features: []string{FeatureNetwork, FeatureApplication}, Grafana: &GrafanaOTLP{Submit: []string{"traces", "metrics"}, InstanceID: "33221"}}.Enabled()) + assert.True(t, (&MetricsConfig{Features: []string{FeatureApplication, FeatureNetwork}, CommonEndpoint: "foo"}).Enabled()) + assert.True(t, (&MetricsConfig{Features: []string{FeatureApplication}, MetricsEndpoint: "foo"}).Enabled()) + assert.True(t, (&MetricsConfig{Features: []string{FeatureNetwork, FeatureApplication}, Grafana: &GrafanaOTLP{Submit: []string{"traces", "metrics"}, InstanceID: "33221"}}).Enabled()) } func TestMetricsConfig_Disabled(t *testing.T) { - assert.False(t, MetricsConfig{Features: []string{FeatureApplication}}.Enabled()) - assert.False(t, MetricsConfig{Features: []string{FeatureApplication}, Grafana: &GrafanaOTLP{Submit: []string{"traces"}, InstanceID: "33221"}}.Enabled()) - assert.False(t, MetricsConfig{Features: []string{FeatureApplication}, Grafana: &GrafanaOTLP{Submit: []string{"metrics"}}}.Enabled()) + assert.False(t, (&MetricsConfig{Features: []string{FeatureApplication}}).Enabled()) + assert.False(t, (&MetricsConfig{Features: []string{FeatureApplication}, Grafana: &GrafanaOTLP{Submit: []string{"traces"}, InstanceID: "33221"}}).Enabled()) + assert.False(t, (&MetricsConfig{Features: []string{FeatureApplication}, Grafana: &GrafanaOTLP{Submit: []string{"metrics"}}}).Enabled()) // application feature is not enabled - assert.False(t, MetricsConfig{CommonEndpoint: "foo"}.Enabled()) - assert.False(t, MetricsConfig{MetricsEndpoint: "foo", Features: []string{FeatureNetwork}}.Enabled()) - assert.False(t, MetricsConfig{Grafana: &GrafanaOTLP{Submit: []string{"traces", "metrics"}, InstanceID: "33221"}}.Enabled()) + assert.False(t, (&MetricsConfig{CommonEndpoint: "foo"}).Enabled()) + assert.False(t, (&MetricsConfig{MetricsEndpoint: "foo", Features: []string{FeatureNetwork}}).Enabled()) + assert.False(t, (&MetricsConfig{Grafana: &GrafanaOTLP{Submit: []string{"traces", "metrics"}, InstanceID: "33221"}}).Enabled()) } func (f *fakeInternalMetrics) OTELMetricExport(len int) { diff --git a/pkg/internal/infraolly/infra_pipe.go b/pkg/internal/infraolly/infra_pipe.go index f65c87d08..e4d271311 100644 --- a/pkg/internal/infraolly/infra_pipe.go +++ b/pkg/internal/infraolly/infra_pipe.go @@ -50,6 +50,16 @@ func SubPipelineProvider(ctx context.Context, cfg *beyla.Config) pipe.FinalProvi var connector <-chan []request.Span = connectorChan nb := pipe.NewBuilder(&subPipeline{}, pipe.ChannelBufferLen(cfg.ChannelBufferLen)) pipe.AddStartProvider(nb, collector, process.NewCollectorProvider(ctx, &connector, &cfg.Processes)) + pipe.AddFinal(nb, otelExport, func(_ <-chan []*process.Status) { + + }) + pipe.AddFinal(nb, promExport, func(in <-chan []*process.Status) { + for ps := range in { + for _, p := range ps { + fmt.Printf("%#v\n", *p) + } + } + }) runner, err := nb.Build() if err != nil { diff --git a/pkg/internal/infraolly/process/cache.go b/pkg/internal/infraolly/process/cache.go index a8b4824ee..7805f88d0 100644 --- a/pkg/internal/infraolly/process/cache.go +++ b/pkg/internal/infraolly/process/cache.go @@ -1,6 +1,5 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -//go:build linux package process @@ -9,8 +8,8 @@ import ( ) type cacheEntry struct { - process *linuxProcess - last *Status // The last event we generated for this process, so we can re-use metadata that doesn't change + process *linuxProcess + last *Status // The last event we generated for this process, so we can re-use metadata that doesn't change } // removeUntilLen removes the oldest entries until the cache reaches the given length. diff --git a/pkg/internal/infraolly/process/collect.go b/pkg/internal/infraolly/process/collect.go index 904d5803f..444f6137a 100644 --- a/pkg/internal/infraolly/process/collect.go +++ b/pkg/internal/infraolly/process/collect.go @@ -1,6 +1,5 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -//go:build linux package process diff --git a/pkg/internal/infraolly/process/harvest.go b/pkg/internal/infraolly/process/harvest.go index 129aa4f3c..04a6d9a7d 100644 --- a/pkg/internal/infraolly/process/harvest.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -1,14 +1,12 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -//go:build linux - // Package process provides all the tools and functionality for sampling processes. It is divided in three main // components: -// - Status: provides OS-level information of a process at a given spot -// - Harvester: fetches and creates actual Process Status from system -// - Collector: uses input from the application pipeline to fetch information for all the processes from -// the instrumented applications, and forwards it to the next stage of the Process' pipeline. +// - Status: provides OS-level information of a process at a given spot +// - Harvester: fetches and creates actual Process Status from system +// - Collector: uses input from the application pipeline to fetch information for all the processes from +// the instrumented applications, and forwards it to the next stage of the Process' pipeline. package process import ( @@ -48,7 +46,7 @@ type Config struct { RunMode RunMode DisableZeroRSSFilter bool // TODO: replace configuration option by attributes-aware set - FullCommandLine bool + FullCommandLine bool ProcFSRoot string Rate time.Duration diff --git a/pkg/internal/infraolly/process/harvest_test.go b/pkg/internal/infraolly/process/harvest_test.go index 399b87517..0a989c4bf 100644 --- a/pkg/internal/infraolly/process/harvest_test.go +++ b/pkg/internal/infraolly/process/harvest_test.go @@ -1,6 +1,5 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -//go:build linux package process diff --git a/pkg/internal/infraolly/process/snapshot.go b/pkg/internal/infraolly/process/snapshot.go index 0c6a4d2cc..6d51d769e 100644 --- a/pkg/internal/infraolly/process/snapshot.go +++ b/pkg/internal/infraolly/process/snapshot.go @@ -1,7 +1,6 @@ -//go:build linux - // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 + package process import ( diff --git a/pkg/internal/infraolly/process/snapshot_test.go b/pkg/internal/infraolly/process/snapshot_test.go index eb3bdb86a..626979961 100644 --- a/pkg/internal/infraolly/process/snapshot_test.go +++ b/pkg/internal/infraolly/process/snapshot_test.go @@ -1,6 +1,5 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -//go:build linux package process diff --git a/pkg/internal/infraolly/process/status.go b/pkg/internal/infraolly/process/status.go index 2a392be84..93861623b 100644 --- a/pkg/internal/infraolly/process/status.go +++ b/pkg/internal/infraolly/process/status.go @@ -1,5 +1,3 @@ -//go:build linux - package process import "log/slog" diff --git a/pkg/internal/pipe/instrumenter.go b/pkg/internal/pipe/instrumenter.go index 446a7c04c..36cb77d53 100644 --- a/pkg/internal/pipe/instrumenter.go +++ b/pkg/internal/pipe/instrumenter.go @@ -14,6 +14,7 @@ import ( "github.com/grafana/beyla/pkg/internal/export/prom" "github.com/grafana/beyla/pkg/internal/filter" "github.com/grafana/beyla/pkg/internal/imetrics" + "github.com/grafana/beyla/pkg/internal/infraolly" "github.com/grafana/beyla/pkg/internal/pipe/global" "github.com/grafana/beyla/pkg/internal/request" "github.com/grafana/beyla/pkg/internal/traces" @@ -41,6 +42,8 @@ type nodesMap struct { Prometheus pipe.Final[[]request.Span] Printer pipe.Final[[]request.Span] Noop pipe.Final[[]request.Span] + + ProcessReport pipe.Final[[]request.Span] } // Connect must specify how the above nodes are connected. Nodes that are disabled @@ -51,7 +54,7 @@ func (n *nodesMap) Connect() { n.Routes.SendTo(n.Kubernetes) n.Kubernetes.SendTo(n.NameResolver) n.NameResolver.SendTo(n.AttributeFilter) - n.AttributeFilter.SendTo(n.AlloyTraces, n.Metrics, n.Traces, n.Prometheus, n.Printer, n.Noop) + n.AttributeFilter.SendTo(n.AlloyTraces, n.Metrics, n.Traces, n.Prometheus, n.Printer, n.Noop, n.ProcessReport) } // accessor functions to each field. Grouped here for code brevity during the pipeline build @@ -66,6 +69,7 @@ func otelTraces(n *nodesMap) *pipe.Final[[]request.Span] { re func printer(n *nodesMap) *pipe.Final[[]request.Span] { return &n.Printer } func prometheus(n *nodesMap) *pipe.Final[[]request.Span] { return &n.Prometheus } func noop(n *nodesMap) *pipe.Final[[]request.Span] { return &n.Noop } +func process(n *nodesMap) *pipe.Final[[]request.Span] { return &n.ProcessReport } // builder with injectable instantiators for unit testing type graphFunctions struct { @@ -119,6 +123,8 @@ func newGraphBuilder(ctx context.Context, config *beyla.Config, ctxInfo *global. pipe.AddFinalProvider(gnb, noop, debug.NoopNode(config.Noop)) pipe.AddFinalProvider(gnb, printer, debug.PrinterNode(config.Printer)) + pipe.AddFinalProvider(gnb, process, infraolly.SubPipelineProvider(ctx, config)) + // The returned builder later invokes its "Build" function that, given // the contents of the nodesMap struct, will instantiate // and interconnect each node according to the SendTo invocations in the diff --git a/test/integration/docker-compose-python.yml b/test/integration/docker-compose-python.yml index 6e97994c8..fd5484dd4 100644 --- a/test/integration/docker-compose-python.yml +++ b/test/integration/docker-compose-python.yml @@ -24,7 +24,7 @@ services: image: hatest-autoinstrumenter privileged: true # in some environments (not GH Pull Requests) you can set it to false and then cap_add: [ SYS_ADMIN ] network_mode: "service:testserver" - pid: "service:testserver" + pid: "host" environment: BEYLA_CONFIG_PATH: "/configs/instrumenter-config-java.yml" GOCOVERDIR: "/coverage" @@ -41,6 +41,7 @@ services: BEYLA_METRICS_REPORT_PEER: "true" BEYLA_HOSTNAME: "beyla" BEYLA_BPF_HTTP_REQUEST_TIMEOUT: "5s" + BEYLA_OTEL_METRICS_FEATURES: "application,application_process" depends_on: testserver: condition: service_started diff --git a/test/integration/suites_test.go b/test/integration/suites_test.go index 91f321967..fb2928a7e 100644 --- a/test/integration/suites_test.go +++ b/test/integration/suites_test.go @@ -401,8 +401,8 @@ func TestSuite_Python(t *testing.T) { t.Run("Python RED metrics", testREDMetricsPythonHTTP) t.Run("Python RED metrics with timeouts", testREDMetricsTimeoutPythonHTTP) t.Run("BPF pinning folder mounted", testBPFPinningMounted) - require.NoError(t, compose.Close()) - t.Run("BPF pinning folder unmounted", testBPFPinningUnmounted) + //require.NoError(t, compose.Close()) + //t.Run("BPF pinning folder unmounted", testBPFPinningUnmounted) } func TestSuite_PythonTLS(t *testing.T) { From dcc653fcc2a8d2722a57b2aeeff07ad02371b9f5 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Thu, 30 May 2024 16:58:20 +0200 Subject: [PATCH 10/27] some process metrics verification --- pkg/internal/infraolly/infra_pipe.go | 5 ++- pkg/internal/infraolly/process/collect.go | 42 +++++++++++------- pkg/internal/infraolly/process/harvest.go | 50 +++++++++++----------- test/integration/docker-compose-python.yml | 7 ++- test/integration/suites_test.go | 10 ++--- 5 files changed, 63 insertions(+), 51 deletions(-) diff --git a/pkg/internal/infraolly/infra_pipe.go b/pkg/internal/infraolly/infra_pipe.go index e4d271311..88a7a3e58 100644 --- a/pkg/internal/infraolly/infra_pipe.go +++ b/pkg/internal/infraolly/infra_pipe.go @@ -50,8 +50,9 @@ func SubPipelineProvider(ctx context.Context, cfg *beyla.Config) pipe.FinalProvi var connector <-chan []request.Span = connectorChan nb := pipe.NewBuilder(&subPipeline{}, pipe.ChannelBufferLen(cfg.ChannelBufferLen)) pipe.AddStartProvider(nb, collector, process.NewCollectorProvider(ctx, &connector, &cfg.Processes)) - pipe.AddFinal(nb, otelExport, func(_ <-chan []*process.Status) { - + pipe.AddFinal(nb, otelExport, func(in <-chan []*process.Status) { + for range in { + } }) pipe.AddFinal(nb, promExport, func(in <-chan []*process.Status) { for ps := range in { diff --git a/pkg/internal/infraolly/process/collect.go b/pkg/internal/infraolly/process/collect.go index 444f6137a..b23ae4996 100644 --- a/pkg/internal/infraolly/process/collect.go +++ b/pkg/internal/infraolly/process/collect.go @@ -17,12 +17,13 @@ import ( // Collector returns runtime information about the currently running processes type Collector struct { - ctx context.Context - cfg *Config - harvest Harvester - cache *simplelru.LRU[int32, *cacheEntry] - log *slog.Logger - newPids *<-chan []request.Span + userPids bool + ctx context.Context + cfg *Config + harvest *Harvester + cache *simplelru.LRU[int32, *cacheEntry] + log *slog.Logger + newPids *<-chan []request.Span } // NewCollectorProvider creates and returns a new process Collector, given an agent context. @@ -33,12 +34,13 @@ func NewCollectorProvider(ctx context.Context, input *<-chan []request.Span, cfg harvest := newHarvester(cfg, cache) return (&Collector{ - ctx: ctx, - cfg: cfg, - harvest: harvest, - cache: cache, - log: pslog(), - newPids: input, + ctx: ctx, + cfg: cfg, + harvest: harvest, + cache: cache, + log: pslog(), + newPids: input, + userPids: cfg.PidMode == PidModeUser, }).Run, nil } } @@ -47,6 +49,7 @@ func (ps *Collector) Run(out chan<- []*Status) { // TODO: set app metadata as key for later decoration? (e.g. K8s metadata, svc.ID) pids := map[int32]struct{}{} collectTicker := time.NewTicker(ps.cfg.Rate) + defer collectTicker.Stop() newPids := *ps.newPids for { select { @@ -54,14 +57,20 @@ func (ps *Collector) Run(out chan<- []*Status) { ps.log.Debug("exiting") case spans := <-newPids: // updating PIDs map with spans information - for i := range spans { - pids[int32(spans[i].Pid.UserPID)] = struct{}{} + if ps.userPids { + for i := range spans { + pids[int32(spans[i].Pid.UserPID)] = struct{}{} + } + } else { + for i := range spans { + pids[int32(spans[i].Pid.HostPID)] = struct{}{} + } } case <-collectTicker.C: ps.log.Debug("start process collection") procs, removed := ps.Collect(pids) for _, rp := range removed { - pids[rp] = struct{}{} + delete(pids, rp) } out <- procs } @@ -78,6 +87,7 @@ func (ps *Collector) Collect(pids map[int32]struct{}) ([]*Status, []int32) { status, err := ps.harvest.Do(pid) if err != nil { ps.log.Debug("skipping process", "pid", pid, "error", err) + ps.harvest.cache.Remove(pid) removed = append(removed, pid) continue } @@ -85,7 +95,7 @@ func (ps *Collector) Collect(pids map[int32]struct{}) ([]*Status, []int32) { results = append(results, status) } - removeUntilLen(ps.cache, len(pids)) + removeUntilLen(ps.cache, len(results)) return results, removed } diff --git a/pkg/internal/infraolly/process/harvest.go b/pkg/internal/infraolly/process/harvest.go index 04a6d9a7d..16075d0a4 100644 --- a/pkg/internal/infraolly/process/harvest.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -15,7 +15,6 @@ import ( "time" "github.com/hashicorp/golang-lru/v2/simplelru" - "github.com/pkg/errors" "github.com/shirou/gopsutil/v3/process" ) @@ -25,15 +24,6 @@ func hlog() *slog.Logger { var errProcessWithoutRSS = fmt.Errorf("process with zero rss") -// Harvester manages sampling for individual processes. It is used by the Process Collector to get information about the -// existing processes. -type Harvester interface { - // Pids return the IDs of all the processes that are currently running - Pids() ([]int32, error) - // Do performs the actual harvesting operation, returning a process status containing all the metrics data - Do(pid int32) (*Status, error) -} - type RunMode string const ( @@ -42,6 +32,13 @@ const ( RunModeUnprivileged = "unprivileged" ) +type PidMode string + +const ( + PidModeHost = "host" + PidModeUser = "user" +) + type Config struct { RunMode RunMode DisableZeroRSSFilter bool @@ -50,15 +47,17 @@ type Config struct { ProcFSRoot string Rate time.Duration + + PidMode PidMode } -func newHarvester(cfg *Config, cache *simplelru.LRU[int32, *cacheEntry]) *linuxHarvester { +func newHarvester(cfg *Config, cache *simplelru.LRU[int32, *cacheEntry]) *Harvester { // If not config, assuming root mode as default privileged := cfg.RunMode == RunModeRoot || cfg.RunMode == RunModePrivileged disableZeroRSSFilter := cfg.DisableZeroRSSFilter stripCommandLine := !cfg.FullCommandLine - return &linuxHarvester{ + return &Harvester{ procFSRoot: cfg.ProcFSRoot, privileged: privileged, disableZeroRSSFilter: disableZeroRSSFilter, @@ -68,8 +67,8 @@ func newHarvester(cfg *Config, cache *simplelru.LRU[int32, *cacheEntry]) *linuxH } } -// linuxHarvester is a Harvester implementation that uses various linux sources and manages process caches -type linuxHarvester struct { +// Harvester is a Harvester implementation that uses various linux sources and manages process caches +type Harvester struct { procFSRoot string privileged bool disableZeroRSSFilter bool @@ -78,17 +77,16 @@ type linuxHarvester struct { log *slog.Logger } -var _ Harvester = (*linuxHarvester)(nil) // static interface assertion - // Pids returns a slice of process IDs that are running now -func (*linuxHarvester) Pids() ([]int32, error) { +func (*Harvester) Pids() ([]int32, error) { return process.Pids() } // Do returns a status of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the // time since this process was statusd for the last time. If the process has been statusd for the first time, this value // will be ignored -func (ps *linuxHarvester) Do(pid int32) (*Status, error) { +func (ps *Harvester) Do(pid int32) (*Status, error) { + ps.log.Debug("harvesting pid", "pid", pid) // Reuses process information that does not vary cached, hasCachedEntry := ps.cache.Get(pid) @@ -99,7 +97,7 @@ func (ps *linuxHarvester) Do(pid int32) (*Status, error) { var err error cached.process, err = getLinuxProcess(ps.procFSRoot, pid, cached.process, ps.privileged) if err != nil { - return nil, errors.Wrap(err, "can't create process") + return nil, fmt.Errorf("can't create process: %w", err) } // We don't need to report processes which are not using memory. This filters out certain kernel processes. @@ -111,7 +109,7 @@ func (ps *linuxHarvester) Do(pid int32) (*Status, error) { status := NewStatus(pid) if err := ps.populateStaticData(status, cached.process); err != nil { - return nil, errors.Wrap(err, "can't populate static attributes") + return nil, fmt.Errorf("can't populate static attributes: %w", err) } // As soon as we have successfully stored the static (reusable) values, we can cache the entry @@ -120,11 +118,11 @@ func (ps *linuxHarvester) Do(pid int32) (*Status, error) { } if err := ps.populateGauges(status, cached.process); err != nil { - return nil, errors.Wrap(err, "can't fetch gauge data") + return nil, fmt.Errorf("can't fetch gauge data: %w", err) } if err := ps.populateIOCounters(status, cached.process); err != nil { - return nil, errors.Wrap(err, "can't fetch deltas") + return nil, fmt.Errorf("can't fetch deltas: %w", err) } cached.last = status @@ -133,11 +131,11 @@ func (ps *linuxHarvester) Do(pid int32) (*Status, error) { } // populateStaticData populates the status with the process data won't vary during the process life cycle -func (ps *linuxHarvester) populateStaticData(status *Status, process *linuxProcess) error { +func (ps *Harvester) populateStaticData(status *Status, process *linuxProcess) error { var err error status.CommandLine, err = process.CmdLine(!ps.stripCommandLine) if err != nil { - return errors.Wrap(err, "acquiring command line") + return fmt.Errorf("acquiring command line: %w", err) } status.ProcessID = process.Pid() @@ -154,7 +152,7 @@ func (ps *linuxHarvester) populateStaticData(status *Status, process *linuxProce } // populateGauges populates the status with gauge data that represents the process state at a given point -func (ps *linuxHarvester) populateGauges(status *Status, process *linuxProcess) error { +func (ps *Harvester) populateGauges(status *Status, process *linuxProcess) error { var err error cpuTimes, err := process.CPUTimes() @@ -191,7 +189,7 @@ func (ps *linuxHarvester) populateGauges(status *Status, process *linuxProcess) // populateIOCounters fills the status with the IO counters data. For the "X per second" metrics, it requires the // last process status for comparative purposes -func (ps *linuxHarvester) populateIOCounters(status *Status, source *linuxProcess) error { +func (ps *Harvester) populateIOCounters(status *Status, source *linuxProcess) error { ioCounters, err := source.IOCounters() if err != nil { return err diff --git a/test/integration/docker-compose-python.yml b/test/integration/docker-compose-python.yml index fd5484dd4..8adb000a6 100644 --- a/test/integration/docker-compose-python.yml +++ b/test/integration/docker-compose-python.yml @@ -23,7 +23,7 @@ services: - ../../testoutput/run:/var/run/beyla image: hatest-autoinstrumenter privileged: true # in some environments (not GH Pull Requests) you can set it to false and then cap_add: [ SYS_ADMIN ] - network_mode: "service:testserver" + network_mode: "host" pid: "host" environment: BEYLA_CONFIG_PATH: "/configs/instrumenter-config-java.yml" @@ -41,10 +41,13 @@ services: BEYLA_METRICS_REPORT_PEER: "true" BEYLA_HOSTNAME: "beyla" BEYLA_BPF_HTTP_REQUEST_TIMEOUT: "5s" - BEYLA_OTEL_METRICS_FEATURES: "application,application_process" + BEYLA_PROMETHEUS_FEATURES: "application,application_process,application_span,application_service_graph" + BEYLA_PROMETHEUS_PORT: "6060" depends_on: testserver: condition: service_started + ports: + - "6060:6060" # OpenTelemetry Collector otelcol: diff --git a/test/integration/suites_test.go b/test/integration/suites_test.go index fb2928a7e..e05b9d148 100644 --- a/test/integration/suites_test.go +++ b/test/integration/suites_test.go @@ -398,11 +398,11 @@ func TestSuite_Python(t *testing.T) { compose.Env = append(compose.Env, `BEYLA_OPEN_PORT=8380`, `BEYLA_EXECUTABLE_NAME=`, `TEST_SERVICE_PORTS=8381:8380`) require.NoError(t, err) require.NoError(t, compose.Up()) - t.Run("Python RED metrics", testREDMetricsPythonHTTP) - t.Run("Python RED metrics with timeouts", testREDMetricsTimeoutPythonHTTP) - t.Run("BPF pinning folder mounted", testBPFPinningMounted) - //require.NoError(t, compose.Close()) - //t.Run("BPF pinning folder unmounted", testBPFPinningUnmounted) + //t.Run("Python RED metrics", testREDMetricsPythonHTTP) + //t.Run("Python RED metrics with timeouts", testREDMetricsTimeoutPythonHTTP) + //t.Run("BPF pinning folder mounted", testBPFPinningMounted) + ////require.NoError(t, compose.Close()) + ////t.Run("BPF pinning folder unmounted", testBPFPinningUnmounted) } func TestSuite_PythonTLS(t *testing.T) { From 10f4ee23b1550ff1493dbd6ff4157ed01de3ead9 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Fri, 31 May 2024 12:32:58 +0200 Subject: [PATCH 11/27] WIP generifying expirer --- pkg/internal/export/attributes/attr_defs.go | 18 +++ pkg/internal/export/attributes/metric.go | 5 + pkg/internal/export/attributes/names/attrs.go | 18 +++ pkg/internal/export/otel/common.go | 2 +- pkg/internal/export/otel/metrics.go | 2 +- pkg/internal/export/otel/traces.go | 2 +- pkg/internal/infraolly/export/otel/metrics.go | 111 ++++++++++++++++++ pkg/internal/infraolly/getters/proc_status.go | 58 +++++++++ pkg/internal/infraolly/process/harvest.go | 19 +-- pkg/internal/infraolly/process/snapshot.go | 5 +- pkg/internal/netolly/export/otel/expirer.go | 74 +++++++++--- pkg/internal/netolly/export/otel/metrics.go | 2 +- 12 files changed, 285 insertions(+), 31 deletions(-) create mode 100644 pkg/internal/infraolly/export/otel/metrics.go create mode 100644 pkg/internal/infraolly/getters/proc_status.go diff --git a/pkg/internal/export/attributes/attr_defs.go b/pkg/internal/export/attributes/attr_defs.go index f97ead734..9e69960e3 100644 --- a/pkg/internal/export/attributes/attr_defs.go +++ b/pkg/internal/export/attributes/attr_defs.go @@ -150,6 +150,21 @@ func getDefinitions(groups AttrGroups) map[Section]AttrReportGroup { }, } + var processAttributes = AttrReportGroup{ + Attributes: map[attr.Name]Default{ + attr.ProcCommand: true, + attr.ProcCPUState: true, + attr.ProcOwner: true, + attr.ProcParentPid: true, + attr.ProcPid: true, + attr.ProcCommandLine: false, + // TODO: not yet supported attributes + //attr.ProcCommandArgs: true, + //attr.ProcExecName: true, + //attr.ProcExecPath: true, + }, + } + return map[Section]AttrReportGroup{ BeylaNetworkFlow.Section: { SubGroups: []*AttrReportGroup{&networkCIDR, &networkKubeAttributes}, @@ -208,6 +223,9 @@ func getDefinitions(groups AttrGroups) map[Section]AttrReportGroup { attr.IncludeDBStatement: false, }, }, + ProcessCPUUtilization.Section: { + SubGroups: []*AttrReportGroup{&processAttributes}, + }, } } diff --git a/pkg/internal/export/attributes/metric.go b/pkg/internal/export/attributes/metric.go index 9b0a2210d..e336dd20f 100644 --- a/pkg/internal/export/attributes/metric.go +++ b/pkg/internal/export/attributes/metric.go @@ -61,6 +61,11 @@ var ( Prom: "sql_client_duration_seconds", OTEL: "sql.client.duration", } + ProcessCPUUtilization = Name{ + Section: "process.cpu.utilization", + Prom: "process_cpu_utilization_ratio", + OTEL: "process.cpu.utilization", + } ) // normalizeMetric will facilitate the user-input in the attributes.enable section. diff --git a/pkg/internal/export/attributes/names/attrs.go b/pkg/internal/export/attributes/names/attrs.go index dc839ef34..ae4de7639 100644 --- a/pkg/internal/export/attributes/names/attrs.go +++ b/pkg/internal/export/attributes/names/attrs.go @@ -91,6 +91,24 @@ var ( K8sDstNodeName = Name("k8s.dst.node.name") ) +// Process Metrics following OTEL 1.26 experimental conventions +// https://opentelemetry.io/docs/specs/semconv/resource/process/ +// https://opentelemetry.io/docs/specs/semconv/system/process-metrics/ + +const ( + ProcCommand = Name(semconv.ProcessCommandKey) + ProcCommandLine = Name(semconv.ProcessCommandLineKey) + ProcCPUState = Name("process.cpu.state") + ProcOwner = Name(semconv.ProcessOwnerKey) + ProcParentPid = Name(semconv.ProcessParentPIDKey) + ProcPid = Name(semconv.ProcessPIDKey) + + // TODO: not yet supported attributes + ProcCommandArgs = Name(semconv.ProcessCommandArgsKey) + ProcExecName = Name(semconv.ProcessExecutableNameKey) + ProcExecPath = Name(semconv.ProcessExecutablePathKey) +) + // other beyla-specific attributes var ( // TargetInstance is a Prometheus-only attribute. diff --git a/pkg/internal/export/otel/common.go b/pkg/internal/export/otel/common.go index e0af1057c..a77221ebf 100644 --- a/pkg/internal/export/otel/common.go +++ b/pkg/internal/export/otel/common.go @@ -58,7 +58,7 @@ var DefaultBuckets = Buckets{ RequestSizeHistogram: []float64{0, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192}, } -func getResourceAttrs(service svc.ID) *resource.Resource { +func ResourceAttrs(service svc.ID) *resource.Resource { attrs := []attribute.KeyValue{ semconv.ServiceName(service.Name), semconv.ServiceInstanceID(service.Instance), diff --git a/pkg/internal/export/otel/metrics.go b/pkg/internal/export/otel/metrics.go index 10a2b21c9..7b32b2839 100644 --- a/pkg/internal/export/otel/metrics.go +++ b/pkg/internal/export/otel/metrics.go @@ -424,7 +424,7 @@ func (mr *MetricsReporter) setupGraphMeters(m *Metrics, meter instrument.Meter) func (mr *MetricsReporter) newMetricSet(service svc.ID) (*Metrics, error) { mlog := mlog().With("service", service) mlog.Debug("creating new Metrics reporter") - resources := getResourceAttrs(service) + resources := ResourceAttrs(service) opts := []metric.Option{ metric.WithResource(resources), diff --git a/pkg/internal/export/otel/traces.go b/pkg/internal/export/otel/traces.go index 67686f233..9a85561ae 100644 --- a/pkg/internal/export/otel/traces.go +++ b/pkg/internal/export/otel/traces.go @@ -323,7 +323,7 @@ func GenerateTraces(span *request.Span, userAttrs map[attr.Name]struct{}) ptrace traces := ptrace.NewTraces() rs := traces.ResourceSpans().AppendEmpty() ss := rs.ScopeSpans().AppendEmpty() - resourceAttrs := attrsToMap(getResourceAttrs(span.ServiceID).Attributes()) + resourceAttrs := attrsToMap(ResourceAttrs(span.ServiceID).Attributes()) resourceAttrs.PutStr(string(semconv.OTelLibraryNameKey), reporterName) resourceAttrs.CopyTo(rs.Resource().Attributes()) diff --git a/pkg/internal/infraolly/export/otel/metrics.go b/pkg/internal/infraolly/export/otel/metrics.go new file mode 100644 index 000000000..f6648f7f5 --- /dev/null +++ b/pkg/internal/infraolly/export/otel/metrics.go @@ -0,0 +1,111 @@ +package otel + +import ( + "context" + "fmt" + "log/slog" + "slices" + "time" + + "github.com/google/uuid" + "github.com/mariomac/pipes/pipe" + "go.opentelemetry.io/otel/attribute" + metric2 "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.19.0" + + "github.com/grafana/beyla/pkg/internal/export/attributes" + "github.com/grafana/beyla/pkg/internal/export/expire" + "github.com/grafana/beyla/pkg/internal/export/otel" + "github.com/grafana/beyla/pkg/internal/netolly/ebpf" + otel2 "github.com/grafana/beyla/pkg/internal/netolly/export/otel" + "github.com/grafana/beyla/pkg/internal/pipe/global" + "github.com/grafana/beyla/pkg/internal/svc" +) + +var timeNow = time.Now + +type MetricsConfig struct { + Metrics *otel.MetricsConfig + AttributeSelectors attributes.Selection +} + +func (mc MetricsConfig) Enabled() bool { + return mc.Metrics != nil && mc.Metrics.EndpointEnabled() && slices.Contains(mc.Metrics.Features, otel.FeatureProcess) +} + +func mlog() *slog.Logger { + return slog.With("component", "otel.ProcessMetricsExporter") +} + +func newMeterProvider(res *resource.Resource, exporter *metric.Exporter, interval time.Duration) (*metric.MeterProvider, error) { + meterProvider := metric.NewMeterProvider( + metric.WithResource(res), + metric.WithReader(metric.NewPeriodicReader(*exporter, metric.WithInterval(interval))), + ) + return meterProvider, nil +} + +type metricsExporter struct { + metrics *otel2.Expirer + clock *expire.CachedClock +} + +func ProcessMetricsExporterProvider(ctxInfo *global.ContextInfo, cfg *MetricsConfig) (pipe.FinalFunc[[]*ebpf.Record], error) { + if !cfg.Enabled() { + // This node is not going to be instantiated. Let the pipes library just ignore it. + return pipe.IgnoreFinal[[]*ebpf.Record](), nil + } + log := mlog() + log.Debug("instantiating network metrics exporter provider") + exporter, err := otel.InstantiateMetricsExporter(context.Background(), cfg.Metrics, log) + if err != nil { + log.Error("", "error", err) + return nil, err + } + + provider, err := newMeterProvider(otel.ResourceAttrs(), &exporter, cfg.Metrics.Interval) + + if err != nil { + log.Error("", "error", err) + return nil, err + } + + attrProv, err := attributes.NewAttrSelector(ctxInfo.MetricAttributeGroups, cfg.AttributeSelectors) + if err != nil { + return nil, fmt.Errorf("network OTEL exporter attributes enable: %w", err) + } + attrs := attributes.OpenTelemetryGetters( + ebpf.RecordGetters, + attrProv.For(attributes.BeylaNetworkFlow)) + + clock := expire.NewCachedClock(timeNow) + expirer := otel2.NewExpirer(attrs, clock.Time, cfg.Metrics.TTL) + ebpfEvents := provider.Meter("network_ebpf_events") + + _, err = ebpfEvents.Int64ObservableCounter( + attributes.BeylaNetworkFlow.OTEL, + metric2.WithDescription("total bytes_sent value of network flows observed by probe since its launch"), + metric2.WithUnit("{bytes}"), + metric2.WithInt64Callback(expirer.Collect), + ) + if err != nil { + log.Error("creating observable counter", "error", err) + return nil, err + } + log.Debug("restricting attributes not in this list", "attributes", cfg.AttributeSelectors) + return (&metricsExporter{ + metrics: expirer, + clock: clock, + }).Do, nil +} + +func (me *metricsExporter) Do(in <-chan []*ebpf.Record) { + for i := range in { + me.clock.Update() + for _, v := range i { + me.metrics.CounterForRecord(v).val.Add(int64(v.Metrics.Bytes)) + } + } +} diff --git a/pkg/internal/infraolly/getters/proc_status.go b/pkg/internal/infraolly/getters/proc_status.go new file mode 100644 index 000000000..4527bcf5e --- /dev/null +++ b/pkg/internal/infraolly/getters/proc_status.go @@ -0,0 +1,58 @@ +package getters + +import ( + "go.opentelemetry.io/otel/attribute" + + "github.com/grafana/beyla/pkg/internal/export/attributes" + attr "github.com/grafana/beyla/pkg/internal/export/attributes/names" + "github.com/grafana/beyla/pkg/internal/infraolly/process" +) + +func procStatusOTELGettersUser(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { + if name == attr.ProcCPUState { + return func(_ *process.Status) attribute.KeyValue { + return attribute.Key(attr.ProcCPUState).String("user") + }, true + } + return procStatusOTELGetters(name) +} +func procStatusOTELGettersSystem(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { + if name == attr.ProcCPUState { + return func(_ *process.Status) attribute.KeyValue { + return attribute.Key(attr.ProcCPUState).String("system") + }, true + } + return procStatusOTELGetters(name) +} +func procStatusOTELGettersWait(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { + if name == attr.ProcCPUState { + return func(_ *process.Status) attribute.KeyValue { + return attribute.Key(attr.ProcCPUState).String("wait") + }, true + } + return procStatusOTELGetters(name) +} + +// nolint:cyclop +func procStatusOTELGetters(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { + var g attributes.Getter[*process.Status, attribute.KeyValue] + switch name { + case attr.ProcCommand: + g = func(s *process.Status) attribute.KeyValue { return attribute.Key(attr.ProcCommand).String(s.Command) } + case attr.ProcCommandLine: + g = func(s *process.Status) attribute.KeyValue { + return attribute.Key(attr.ProcCommand).String(s.CommandLine) + } + case attr.ProcOwner: + g = func(s *process.Status) attribute.KeyValue { return attribute.Key(attr.ProcOwner).String(s.User) } + case attr.ProcParentPid: + g = func(s *process.Status) attribute.KeyValue { + return attribute.Key(attr.ProcParentPid).Int(int(s.ParentProcessID)) + } + case attr.ProcPid: + g = func(s *process.Status) attribute.KeyValue { + return attribute.Key(attr.ProcParentPid).Int(int(s.ProcessID)) + } + } + return g, g != nil +} diff --git a/pkg/internal/infraolly/process/harvest.go b/pkg/internal/infraolly/process/harvest.go index 16075d0a4..cc106da0f 100644 --- a/pkg/internal/infraolly/process/harvest.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -12,6 +12,7 @@ package process import ( "fmt" "log/slog" + "os" "time" "github.com/hashicorp/golang-lru/v2/simplelru" @@ -42,11 +43,8 @@ const ( type Config struct { RunMode RunMode DisableZeroRSSFilter bool - // TODO: replace configuration option by attributes-aware set - FullCommandLine bool - ProcFSRoot string - Rate time.Duration + Rate time.Duration PidMode PidMode } @@ -55,13 +53,17 @@ func newHarvester(cfg *Config, cache *simplelru.LRU[int32, *cacheEntry]) *Harves // If not config, assuming root mode as default privileged := cfg.RunMode == RunModeRoot || cfg.RunMode == RunModePrivileged disableZeroRSSFilter := cfg.DisableZeroRSSFilter - stripCommandLine := !cfg.FullCommandLine + + // we need to use the same method to override HOST_PROC that is used by gopsutil library + hostProc, ok := os.LookupEnv("HOST_PROC") + if !ok { + hostProc = "/proc" + } return &Harvester{ - procFSRoot: cfg.ProcFSRoot, + procFSRoot: hostProc, privileged: privileged, disableZeroRSSFilter: disableZeroRSSFilter, - stripCommandLine: stripCommandLine, cache: cache, log: hlog(), } @@ -72,7 +74,6 @@ type Harvester struct { procFSRoot string privileged bool disableZeroRSSFilter bool - stripCommandLine bool cache *simplelru.LRU[int32, *cacheEntry] log *slog.Logger } @@ -133,7 +134,7 @@ func (ps *Harvester) Do(pid int32) (*Status, error) { // populateStaticData populates the status with the process data won't vary during the process life cycle func (ps *Harvester) populateStaticData(status *Status, process *linuxProcess) error { var err error - status.CommandLine, err = process.CmdLine(!ps.stripCommandLine) + status.CommandLine, err = process.CmdLine() if err != nil { return fmt.Errorf("acquiring command line: %w", err) } diff --git a/pkg/internal/infraolly/process/snapshot.go b/pkg/internal/infraolly/process/snapshot.go index 6d51d769e..efd467609 100644 --- a/pkg/internal/infraolly/process/snapshot.go +++ b/pkg/internal/infraolly/process/snapshot.go @@ -354,10 +354,11 @@ func (pw *linuxProcess) Command() string { // Data to be derived from /proc//cmdline: command line, and command line without arguments ////////////////////////// -func (pw *linuxProcess) CmdLine(withArgs bool) (string, error) { +func (pw *linuxProcess) CmdLine() (string, error) { if pw.cmdLine != "" { return pw.cmdLine, nil } + pw.process.Cmdline() cmdPath := path.Join(pw.procFSRoot, strconv.Itoa(int(pw.pid)), "cmdline") procCmdline, err := os.ReadFile(cmdPath) @@ -379,7 +380,7 @@ func (pw *linuxProcess) CmdLine(withArgs bool) (string, error) { if procCmdline[i] == 0 { // ignoring the trailing zero that ends /proc//cmdline, but adding the last character if the file // does not end in zero - if withArgs && i < len(procCmdline)-1 { + if i < len(procCmdline)-1 { cmdLineBytes = append(cmdLineBytes, ' ') } else { break diff --git a/pkg/internal/netolly/export/otel/expirer.go b/pkg/internal/netolly/export/otel/expirer.go index 56cd16bab..5c0b976b7 100644 --- a/pkg/internal/netolly/export/otel/expirer.go +++ b/pkg/internal/netolly/export/otel/expirer.go @@ -11,7 +11,6 @@ import ( "github.com/grafana/beyla/pkg/internal/export/attributes" "github.com/grafana/beyla/pkg/internal/export/expire" - "github.com/grafana/beyla/pkg/internal/netolly/ebpf" ) var timeNow = time.Now @@ -20,11 +19,16 @@ func plog() *slog.Logger { return slog.With("component", "otel.Expirer") } +type attribGetter interface { + Attributes() attribute.Set +} + // Expirer drops metrics from labels that haven't been updated during a given timeout // TODO: generify and move to a common section for using it also in AppO11y, supporting more OTEL metrics -type Expirer struct { - attrs []attributes.Field[*ebpf.Record, string] - entries *expire.ExpiryMap[*Counter] +type Expirer[RT attribGetter] struct { + attrs []attributes.Field[RT, string] + counters *expire.ExpiryMap[*Counter] + gauges *expire.ExpiryMap[*Gauge] } type Counter struct { @@ -32,21 +36,41 @@ type Counter struct { val atomic.Int64 } +type Gauge struct { + attributes attribute.Set + val atomic.Value +} + +func NewGauge(attributes attribute.Set, initVal float64) *Gauge { + val := atomic.Value{} + val.Store(initVal) + return &Gauge{attributes: attributes, val: val} +} + +func (g *Gauge) Load() float64 { + return g.val.Load().(float64) +} + +func (g *Gauge) Set(val float64) { + g.val.Store(val) +} + // NewExpirer creates a metric that wraps a Counter. Its labeled instances are dropped // if they haven't been updated during the last timeout period -func NewExpirer(attrs []attributes.Field[*ebpf.Record, string], clock expire.Clock, expireTime time.Duration) *Expirer { - return &Expirer{ - attrs: attrs, - entries: expire.NewExpiryMap[*Counter](clock, expireTime), +func NewExpirer[RT attribGetter](attrs []attributes.Field[RT, string], clock expire.Clock, expireTime time.Duration) *Expirer[RT] { + return &Expirer[RT]{ + attrs: attrs, + counters: expire.NewExpiryMap[*Counter](clock, expireTime), + gauges: expire.NewExpiryMap[*Gauge](clock, expireTime), } } -// ForRecord returns the Counter for the given eBPF record. If that record +// CounterForRecord returns the Counter for the given eBPF record. If that record // s accessed for the first time, a new Counter is created. // If not, a cached copy is returned and the "last access" cache time is updated. -func (ex *Expirer) ForRecord(m *ebpf.Record) *Counter { +func (ex *Expirer[RT]) CounterForRecord(m RT) *Counter { recordAttrs, attrValues := ex.recordAttributes(m) - return ex.entries.GetOrCreate(attrValues, func() *Counter { + return ex.counters.GetOrCreate(attrValues, func() *Counter { plog().With("labelValues", attrValues).Debug("storing new metric label set") return &Counter{ attributes: recordAttrs, @@ -54,20 +78,38 @@ func (ex *Expirer) ForRecord(m *ebpf.Record) *Counter { }) } -func (ex *Expirer) Collect(_ context.Context, observer metric.Int64Observer) error { +func (ex *Expirer[RT]) GaugeForRecord(m RT) *Gauge { + recordAttrs, attrValues := ex.recordAttributes(m) + return ex. +} + + + +type observer[VT any] interface { + Observe(value VT, options ...metric.ObserveOption) +} + +func (ex *Expirer[RT]) Collect(_ context.Context, observer observer[]) error { log := plog() log.Debug("invoking metrics collection") - old := ex.entries.DeleteExpired() - log.With("labelValues", old).Debug("deleting old OTEL metric") + if old := ex.counters.DeleteExpired(); len(old) > 0 { + log.With("labelValues", old).Debug("deleting old OTEL counter") + } + if old := ex.gauges.DeleteExpired(); len(old) > 0 { + log.With("labelValues", old).Debug("deleting old OTEL gauges") + } - for _, v := range ex.entries.All() { + for _, v := range ex.counters.All() { + observer.Observe(v.val.Load(), metric.WithAttributeSet(v.attributes)) + } + for _, v := range ex.gauges.All() { observer.Observe(v.val.Load(), metric.WithAttributeSet(v.attributes)) } return nil } -func (ex *Expirer) recordAttributes(m *ebpf.Record) (attribute.Set, []string) { +func (ex *Expirer[RT]) recordAttributes(m RT) (attribute.Set, []string) { keyVals := make([]attribute.KeyValue, 0, len(ex.attrs)) vals := make([]string, 0, len(ex.attrs)) diff --git a/pkg/internal/netolly/export/otel/metrics.go b/pkg/internal/netolly/export/otel/metrics.go index 9e71b9c1a..f755fa2bf 100644 --- a/pkg/internal/netolly/export/otel/metrics.go +++ b/pkg/internal/netolly/export/otel/metrics.go @@ -117,7 +117,7 @@ func (me *metricsExporter) Do(in <-chan []*ebpf.Record) { for i := range in { me.clock.Update() for _, v := range i { - me.metrics.ForRecord(v).val.Add(int64(v.Metrics.Bytes)) + me.metrics.CounterForRecord(v).val.Add(int64(v.Metrics.Bytes)) } } } From 4e12f03015a7ecfd04c47fe61100fc45193f1de8 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Fri, 31 May 2024 15:08:49 +0200 Subject: [PATCH 12/27] Generified OTEL metrics expirer --- pkg/beyla/config.go | 5 +- pkg/internal/export/attributes/attr_defs.go | 6 +- pkg/internal/infraolly/export/otel/metrics.go | 2 + pkg/internal/infraolly/getters/proc_status.go | 11 +- pkg/internal/infraolly/infra_pipe.go | 3 +- .../infraolly/process/harvest_test.go | 12 +- pkg/internal/infraolly/process/snapshot.go | 1 - .../infraolly/process/snapshot_test.go | 14 ++- pkg/internal/netolly/export/otel/expirer.go | 105 ++++++++++-------- pkg/internal/netolly/export/otel/metrics.go | 6 +- .../netolly/export/otel/metrics_test.go | 5 +- test/integration/docker-compose-python.yml | 8 +- test/integration/suites_test.go | 10 +- 13 files changed, 102 insertions(+), 86 deletions(-) diff --git a/pkg/beyla/config.go b/pkg/beyla/config.go index 272d64a50..3036aec11 100644 --- a/pkg/beyla/config.go +++ b/pkg/beyla/config.go @@ -101,9 +101,8 @@ var DefaultConfig = Config{ Routes: &transform.RoutesConfig{}, NetworkFlows: defaultNetworkConfig, Processes: process.Config{ - RunMode: process.RunModeRoot, - ProcFSRoot: "/proc", - Rate: 5 * time.Second, + RunMode: process.RunModeRoot, + Rate: 5 * time.Second, }, } diff --git a/pkg/internal/export/attributes/attr_defs.go b/pkg/internal/export/attributes/attr_defs.go index 9e69960e3..97f676078 100644 --- a/pkg/internal/export/attributes/attr_defs.go +++ b/pkg/internal/export/attributes/attr_defs.go @@ -159,9 +159,9 @@ func getDefinitions(groups AttrGroups) map[Section]AttrReportGroup { attr.ProcPid: true, attr.ProcCommandLine: false, // TODO: not yet supported attributes - //attr.ProcCommandArgs: true, - //attr.ProcExecName: true, - //attr.ProcExecPath: true, + // attr.ProcCommandArgs: true, + // attr.ProcExecName: true, + // attr.ProcExecPath: true, }, } diff --git a/pkg/internal/infraolly/export/otel/metrics.go b/pkg/internal/infraolly/export/otel/metrics.go index f6648f7f5..bc449833f 100644 --- a/pkg/internal/infraolly/export/otel/metrics.go +++ b/pkg/internal/infraolly/export/otel/metrics.go @@ -1,5 +1,6 @@ package otel +/* import ( "context" "fmt" @@ -109,3 +110,4 @@ func (me *metricsExporter) Do(in <-chan []*ebpf.Record) { } } } +*/ diff --git a/pkg/internal/infraolly/getters/proc_status.go b/pkg/internal/infraolly/getters/proc_status.go index 4527bcf5e..e9dd348b5 100644 --- a/pkg/internal/infraolly/getters/proc_status.go +++ b/pkg/internal/infraolly/getters/proc_status.go @@ -8,7 +8,8 @@ import ( "github.com/grafana/beyla/pkg/internal/infraolly/process" ) -func procStatusOTELGettersUser(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { +//nolint:unused +func ProcStatusOTELGettersUser(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { if name == attr.ProcCPUState { return func(_ *process.Status) attribute.KeyValue { return attribute.Key(attr.ProcCPUState).String("user") @@ -16,7 +17,9 @@ func procStatusOTELGettersUser(name attr.Name) (attributes.Getter[*process.Statu } return procStatusOTELGetters(name) } -func procStatusOTELGettersSystem(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { + +//nolint:unused +func ProcStatusOTELGettersSystem(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { if name == attr.ProcCPUState { return func(_ *process.Status) attribute.KeyValue { return attribute.Key(attr.ProcCPUState).String("system") @@ -24,7 +27,9 @@ func procStatusOTELGettersSystem(name attr.Name) (attributes.Getter[*process.Sta } return procStatusOTELGetters(name) } -func procStatusOTELGettersWait(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { + +//nolint:unused +func ProcStatusOTELGettersWait(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { if name == attr.ProcCPUState { return func(_ *process.Status) attribute.KeyValue { return attribute.Key(attr.ProcCPUState).String("wait") diff --git a/pkg/internal/infraolly/infra_pipe.go b/pkg/internal/infraolly/infra_pipe.go index 88a7a3e58..af5423d69 100644 --- a/pkg/internal/infraolly/infra_pipe.go +++ b/pkg/internal/infraolly/infra_pipe.go @@ -51,7 +51,8 @@ func SubPipelineProvider(ctx context.Context, cfg *beyla.Config) pipe.FinalProvi nb := pipe.NewBuilder(&subPipeline{}, pipe.ChannelBufferLen(cfg.ChannelBufferLen)) pipe.AddStartProvider(nb, collector, process.NewCollectorProvider(ctx, &connector, &cfg.Processes)) pipe.AddFinal(nb, otelExport, func(in <-chan []*process.Status) { - for range in { + for i := range in { + fmt.Printf("otel %#v\n", i) } }) pipe.AddFinal(nb, promExport, func(in <-chan []*process.Status) { diff --git a/pkg/internal/infraolly/process/harvest_test.go b/pkg/internal/infraolly/process/harvest_test.go index 0a989c4bf..d6ed4712d 100644 --- a/pkg/internal/infraolly/process/harvest_test.go +++ b/pkg/internal/infraolly/process/harvest_test.go @@ -30,7 +30,7 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { for _, c := range cases { t.Run(fmt.Sprint("mode ", c.mode), func(t *testing.T) { cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(&Config{ProcFSRoot: "/proc", RunMode: c.mode}, cache) + h := newHarvester(&Config{RunMode: c.mode}, cache) // If not privileged, it is expected to not report neither FDs nor IO counters status, err := h.Do(int32(os.Getpid())) @@ -49,7 +49,7 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { func TestLinuxHarvester_Do(t *testing.T) { // Given a process harvester cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(&Config{ProcFSRoot: "/proc"}, cache) + h := newHarvester(&Config{}, cache) // When retrieving for a given process status (e.g. the current testing executable) status, err := h.Do(int32(os.Getpid())) @@ -84,7 +84,7 @@ func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { // Given a process harvester configured to showw the full command line cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(&Config{ProcFSRoot: "/proc", FullCommandLine: true}, cache) + h := newHarvester(&Config{}, cache) test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process status (e.g. the current testing executable) @@ -108,7 +108,7 @@ func TestLinuxHarvester_Do_StripCommandLine(t *testing.T) { // Given a process harvester cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(&Config{ProcFSRoot: "/proc", FullCommandLine: true}, cache) + h := newHarvester(&Config{}, cache) test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process status (e.g. the current testing executable) @@ -129,7 +129,7 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentCmd(t *testing.T) { // That has cached an old process sharing the PID with a new process cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) cache.Add(currentPid, &cacheEntry{process: &linuxProcess{cmdLine: "something old"}}) - h := newHarvester(&Config{ProcFSRoot: "/proc"}, cache) + h := newHarvester(&Config{}, cache) // When the process is harvested status, err := h.Do(currentPid) @@ -147,7 +147,7 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentPid(t *testing.T) { // That has cached an old process sharing the PID with a new process cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) cache.Add(currentPid, &cacheEntry{process: &linuxProcess{stats: procStats{ppid: -1}}}) - h := newHarvester(&Config{ProcFSRoot: "/proc"}, cache) + h := newHarvester(&Config{}, cache) // When the process is harvested status, err := h.Do(currentPid) diff --git a/pkg/internal/infraolly/process/snapshot.go b/pkg/internal/infraolly/process/snapshot.go index efd467609..7ce595c9c 100644 --- a/pkg/internal/infraolly/process/snapshot.go +++ b/pkg/internal/infraolly/process/snapshot.go @@ -358,7 +358,6 @@ func (pw *linuxProcess) CmdLine() (string, error) { if pw.cmdLine != "" { return pw.cmdLine, nil } - pw.process.Cmdline() cmdPath := path.Join(pw.procFSRoot, strconv.Itoa(int(pw.pid)), "cmdline") procCmdline, err := os.ReadFile(cmdPath) diff --git a/pkg/internal/infraolly/process/snapshot_test.go b/pkg/internal/infraolly/process/snapshot_test.go index 626979961..379952763 100644 --- a/pkg/internal/infraolly/process/snapshot_test.go +++ b/pkg/internal/infraolly/process/snapshot_test.go @@ -1,6 +1,8 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +//go:build linux + package process import ( @@ -42,7 +44,7 @@ func TestLinuxProcess_CmdLine(t *testing.T) { for _, tc := range testCases { require.NoError(t, os.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) lp := linuxProcess{pid: 12345} - actual, err := lp.CmdLine(true) + actual, err := lp.CmdLine() assert.NoError(t, err) assert.Equal(t, tc.expected, actual) } @@ -74,7 +76,7 @@ func TestLinuxProcess_CmdLine_NoArgs(t *testing.T) { for _, tc := range testCases { require.NoError(t, os.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) lp := linuxProcess{pid: 12345} - actual, err := lp.CmdLine(false) + actual, err := lp.CmdLine() assert.NoError(t, err) assert.Equal(t, tc.expected, actual) } @@ -107,11 +109,11 @@ func TestLinuxProcess_CmdLine_NotStandard(t *testing.T) { lp := linuxProcess{pid: 12345} // Testing both the cases with and without command line stripping - actual, err := lp.CmdLine(true) + actual, err := lp.CmdLine() assert.NoError(t, err) assert.Equal(t, tc.expected, actual) - actual, err = lp.CmdLine(false) + actual, err = lp.CmdLine() assert.NoError(t, err) assert.Equal(t, tc.expected, actual) } @@ -119,14 +121,14 @@ func TestLinuxProcess_CmdLine_NotStandard(t *testing.T) { func TestLinuxProcess_CmdLine_ProcessNotExist(t *testing.T) { lp := linuxProcess{pid: 999999999} - actual, err := lp.CmdLine(true) + actual, err := lp.CmdLine() assert.NoError(t, err) assert.Equal(t, "", actual) } func TestLinuxProcess_CmdLine_ProcessNotExist_NoStrip(t *testing.T) { lp := linuxProcess{pid: 999999999} - actual, err := lp.CmdLine(false) + actual, err := lp.CmdLine() assert.NoError(t, err) assert.Equal(t, "", actual) } diff --git a/pkg/internal/netolly/export/otel/expirer.go b/pkg/internal/netolly/export/otel/expirer.go index 5c0b976b7..47790c454 100644 --- a/pkg/internal/netolly/export/otel/expirer.go +++ b/pkg/internal/netolly/export/otel/expirer.go @@ -2,6 +2,7 @@ package otel import ( "context" + "fmt" "log/slog" "sync/atomic" "time" @@ -19,16 +20,25 @@ func plog() *slog.Logger { return slog.With("component", "otel.Expirer") } -type attribGetter interface { +type loader[T any] interface { + Load() T Attributes() attribute.Set + SetAttributes(attribute.Set) +} + +type observer[T any] interface { + Observe(T, ...metric.ObserveOption) } // Expirer drops metrics from labels that haven't been updated during a given timeout -// TODO: generify and move to a common section for using it also in AppO11y, supporting more OTEL metrics -type Expirer[RT attribGetter] struct { - attrs []attributes.Field[RT, string] - counters *expire.ExpiryMap[*Counter] - gauges *expire.ExpiryMap[*Gauge] +// Record: type of the record that holds the metric data request.Span, ebpf.Record, process.Status... +// Metric: type of the metric kind: Counter, Gauge... +// VT: type of the metric value: int, float... +type Expirer[Record any, OT observer[VT], Metric loader[VT], VT any] struct { + instancer func(set attribute.Set) Metric + attrs []attributes.Field[Record, string] + entries *expire.ExpiryMap[Metric] + log *slog.Logger } type Counter struct { @@ -36,6 +46,21 @@ type Counter struct { val atomic.Int64 } +func NewCounter(attributes attribute.Set) *Counter { + return &Counter{attributes: attributes} +} +func (g *Counter) Load() int64 { + return g.val.Load() +} + +func (g *Counter) Attributes() attribute.Set { + return g.attributes +} + +func (g *Counter) SetAttributes(a attribute.Set) { + g.attributes = a +} + type Gauge struct { attributes attribute.Set val atomic.Value @@ -47,69 +72,55 @@ func NewGauge(attributes attribute.Set, initVal float64) *Gauge { return &Gauge{attributes: attributes, val: val} } -func (g *Gauge) Load() float64 { +func (g Gauge) Load() float64 { return g.val.Load().(float64) } -func (g *Gauge) Set(val float64) { +func (g Gauge) Set(val float64) { g.val.Store(val) } // NewExpirer creates a metric that wraps a Counter. Its labeled instances are dropped // if they haven't been updated during the last timeout period -func NewExpirer[RT attribGetter](attrs []attributes.Field[RT, string], clock expire.Clock, expireTime time.Duration) *Expirer[RT] { - return &Expirer[RT]{ - attrs: attrs, - counters: expire.NewExpiryMap[*Counter](clock, expireTime), - gauges: expire.NewExpiryMap[*Gauge](clock, expireTime), +func NewExpirer[Record any, OT observer[VT], Metric loader[VT], VT any]( + instancer func(set attribute.Set) Metric, + attrs []attributes.Field[Record, string], + clock expire.Clock, + expireTime time.Duration, +) *Expirer[Record, OT, Metric, VT] { + exp := Expirer[Record, OT, Metric, VT]{ + instancer: instancer, + attrs: attrs, + entries: expire.NewExpiryMap[Metric](clock, expireTime), } + exp.log = plog().With("type", fmt.Sprintf("%T", exp)) + return &exp } -// CounterForRecord returns the Counter for the given eBPF record. If that record +// ForRecord returns the Counter for the given eBPF record. If that record // s accessed for the first time, a new Counter is created. // If not, a cached copy is returned and the "last access" cache time is updated. -func (ex *Expirer[RT]) CounterForRecord(m RT) *Counter { - recordAttrs, attrValues := ex.recordAttributes(m) - return ex.counters.GetOrCreate(attrValues, func() *Counter { - plog().With("labelValues", attrValues).Debug("storing new metric label set") - return &Counter{ - attributes: recordAttrs, - } +func (ex *Expirer[Record, OT, Metric, VT]) ForRecord(r Record) Metric { + recordAttrs, attrValues := ex.recordAttributes(r) + return ex.entries.GetOrCreate(attrValues, func() Metric { + ex.log.With("labelValues", attrValues).Debug("storing new metric label set") + return ex.instancer(recordAttrs) }) } -func (ex *Expirer[RT]) GaugeForRecord(m RT) *Gauge { - recordAttrs, attrValues := ex.recordAttributes(m) - return ex. -} - - - -type observer[VT any] interface { - Observe(value VT, options ...metric.ObserveOption) -} +func (ex *Expirer[Record, OT, Metric, VT]) Collect(_ context.Context, observer OT) error { + ex.log.Debug("invoking metrics collection") + old := ex.entries.DeleteExpired() + ex.log.With("labelValues", old).Debug("deleting old OTEL metric") -func (ex *Expirer[RT]) Collect(_ context.Context, observer observer[]) error { - log := plog() - log.Debug("invoking metrics collection") - if old := ex.counters.DeleteExpired(); len(old) > 0 { - log.With("labelValues", old).Debug("deleting old OTEL counter") - } - if old := ex.gauges.DeleteExpired(); len(old) > 0 { - log.With("labelValues", old).Debug("deleting old OTEL gauges") - } - - for _, v := range ex.counters.All() { - observer.Observe(v.val.Load(), metric.WithAttributeSet(v.attributes)) - } - for _, v := range ex.gauges.All() { - observer.Observe(v.val.Load(), metric.WithAttributeSet(v.attributes)) + for _, v := range ex.entries.All() { + observer.Observe(v.Load(), metric.WithAttributeSet(v.Attributes())) } return nil } -func (ex *Expirer[RT]) recordAttributes(m RT) (attribute.Set, []string) { +func (ex *Expirer[Record, OT, Metric, VT]) recordAttributes(m Record) (attribute.Set, []string) { keyVals := make([]attribute.KeyValue, 0, len(ex.attrs)) vals := make([]string, 0, len(ex.attrs)) diff --git a/pkg/internal/netolly/export/otel/metrics.go b/pkg/internal/netolly/export/otel/metrics.go index f755fa2bf..acb850286 100644 --- a/pkg/internal/netolly/export/otel/metrics.go +++ b/pkg/internal/netolly/export/otel/metrics.go @@ -60,7 +60,7 @@ func newMeterProvider(res *resource.Resource, exporter *metric.Exporter, interva } type metricsExporter struct { - metrics *Expirer + metrics *Expirer[*ebpf.Record, metric2.Int64Observer, *Counter, int64] clock *expire.CachedClock } @@ -93,7 +93,7 @@ func MetricsExporterProvider(ctxInfo *global.ContextInfo, cfg *MetricsConfig) (p attrProv.For(attributes.BeylaNetworkFlow)) clock := expire.NewCachedClock(timeNow) - expirer := NewExpirer(attrs, clock.Time, cfg.Metrics.TTL) + expirer := NewExpirer[*ebpf.Record, metric2.Int64Observer](NewCounter, attrs, clock.Time, cfg.Metrics.TTL) ebpfEvents := provider.Meter("network_ebpf_events") _, err = ebpfEvents.Int64ObservableCounter( @@ -117,7 +117,7 @@ func (me *metricsExporter) Do(in <-chan []*ebpf.Record) { for i := range in { me.clock.Update() for _, v := range i { - me.metrics.CounterForRecord(v).val.Add(int64(v.Metrics.Bytes)) + me.metrics.ForRecord(v).val.Add(int64(v.Metrics.Bytes)) } } } diff --git a/pkg/internal/netolly/export/otel/metrics_test.go b/pkg/internal/netolly/export/otel/metrics_test.go index 3643b56bd..727050de6 100644 --- a/pkg/internal/netolly/export/otel/metrics_test.go +++ b/pkg/internal/netolly/export/otel/metrics_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" "github.com/grafana/beyla/pkg/internal/export/attributes" attr "github.com/grafana/beyla/pkg/internal/export/attributes/names" @@ -35,7 +36,7 @@ func TestMetricAttributes(t *testing.T) { in.Id.SrcIp.In6U.U6Addr8 = [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 12, 34, 56, 78} in.Id.DstIp.In6U.U6Addr8 = [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 33, 22, 11, 1} - me := &metricsExporter{metrics: &Expirer{attrs: attributes.OpenTelemetryGetters( + me := &metricsExporter{metrics: &Expirer[*ebpf.Record, metric.Int64Observer, *Counter, int64]{attrs: attributes.OpenTelemetryGetters( ebpf.RecordGetters, []attr.Name{ attr.SrcAddress, attr.DstAddres, attr.SrcPort, attr.DstPort, attr.SrcName, attr.DstName, attr.K8sSrcName, attr.K8sSrcNamespace, attr.K8sDstName, attr.K8sDstNamespace, @@ -84,7 +85,7 @@ func TestMetricAttributes_Filter(t *testing.T) { in.Id.SrcIp.In6U.U6Addr8 = [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 12, 34, 56, 78} in.Id.DstIp.In6U.U6Addr8 = [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 33, 22, 11, 1} - me := &Expirer{attrs: attributes.OpenTelemetryGetters(ebpf.RecordGetters, []attr.Name{ + me := &Expirer[*ebpf.Record, metric.Int64Observer, *Counter, int64]{attrs: attributes.OpenTelemetryGetters(ebpf.RecordGetters, []attr.Name{ "src.address", "k8s.src.name", "k8s.dst.name", diff --git a/test/integration/docker-compose-python.yml b/test/integration/docker-compose-python.yml index 8adb000a6..6e97994c8 100644 --- a/test/integration/docker-compose-python.yml +++ b/test/integration/docker-compose-python.yml @@ -23,8 +23,8 @@ services: - ../../testoutput/run:/var/run/beyla image: hatest-autoinstrumenter privileged: true # in some environments (not GH Pull Requests) you can set it to false and then cap_add: [ SYS_ADMIN ] - network_mode: "host" - pid: "host" + network_mode: "service:testserver" + pid: "service:testserver" environment: BEYLA_CONFIG_PATH: "/configs/instrumenter-config-java.yml" GOCOVERDIR: "/coverage" @@ -41,13 +41,9 @@ services: BEYLA_METRICS_REPORT_PEER: "true" BEYLA_HOSTNAME: "beyla" BEYLA_BPF_HTTP_REQUEST_TIMEOUT: "5s" - BEYLA_PROMETHEUS_FEATURES: "application,application_process,application_span,application_service_graph" - BEYLA_PROMETHEUS_PORT: "6060" depends_on: testserver: condition: service_started - ports: - - "6060:6060" # OpenTelemetry Collector otelcol: diff --git a/test/integration/suites_test.go b/test/integration/suites_test.go index e05b9d148..91f321967 100644 --- a/test/integration/suites_test.go +++ b/test/integration/suites_test.go @@ -398,11 +398,11 @@ func TestSuite_Python(t *testing.T) { compose.Env = append(compose.Env, `BEYLA_OPEN_PORT=8380`, `BEYLA_EXECUTABLE_NAME=`, `TEST_SERVICE_PORTS=8381:8380`) require.NoError(t, err) require.NoError(t, compose.Up()) - //t.Run("Python RED metrics", testREDMetricsPythonHTTP) - //t.Run("Python RED metrics with timeouts", testREDMetricsTimeoutPythonHTTP) - //t.Run("BPF pinning folder mounted", testBPFPinningMounted) - ////require.NoError(t, compose.Close()) - ////t.Run("BPF pinning folder unmounted", testBPFPinningUnmounted) + t.Run("Python RED metrics", testREDMetricsPythonHTTP) + t.Run("Python RED metrics with timeouts", testREDMetricsTimeoutPythonHTTP) + t.Run("BPF pinning folder mounted", testBPFPinningMounted) + require.NoError(t, compose.Close()) + t.Run("BPF pinning folder unmounted", testBPFPinningUnmounted) } func TestSuite_PythonTLS(t *testing.T) { From e1788687e556ab9de9eddb494a4055bf6c6e8d65 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Fri, 31 May 2024 15:50:31 +0200 Subject: [PATCH 13/27] fix some unit tests --- pkg/beyla/config_test.go | 5 +++++ pkg/internal/infraolly/process/harvest.go | 10 ++++++---- .../infraolly/process/harvest_test.go | 2 ++ pkg/internal/netolly/export/otel/expirer.go | 4 ++++ pkg/internal/netolly/export/otel/metrics.go | 2 +- .../netolly/export/otel/metrics_test.go | 19 ++++++++++--------- 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/pkg/beyla/config_test.go b/pkg/beyla/config_test.go index 37011e95b..3d678af2c 100644 --- a/pkg/beyla/config_test.go +++ b/pkg/beyla/config_test.go @@ -16,6 +16,7 @@ import ( "github.com/grafana/beyla/pkg/internal/export/otel" "github.com/grafana/beyla/pkg/internal/export/prom" "github.com/grafana/beyla/pkg/internal/imetrics" + "github.com/grafana/beyla/pkg/internal/infraolly/process" "github.com/grafana/beyla/pkg/internal/netolly/transform/cidr" "github.com/grafana/beyla/pkg/internal/traces" "github.com/grafana/beyla/pkg/transform" @@ -165,6 +166,10 @@ network: CacheLen: 1024, CacheTTL: 5 * time.Minute, }, + Processes: process.Config{ + RunMode: process.RunModeRoot, + Rate: 5 * time.Second, + }, }, cfg) } diff --git a/pkg/internal/infraolly/process/harvest.go b/pkg/internal/infraolly/process/harvest.go index cc106da0f..b457274f8 100644 --- a/pkg/internal/infraolly/process/harvest.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -195,9 +195,11 @@ func (ps *Harvester) populateIOCounters(status *Status, source *linuxProcess) er if err != nil { return err } - status.IOReadCount = ioCounters.ReadCount - status.IOWriteCount = ioCounters.WriteCount - status.IOReadBytes = ioCounters.ReadBytes - status.IOWriteBytes = ioCounters.WriteBytes + if ioCounters != nil { + status.IOReadCount = ioCounters.ReadCount + status.IOWriteCount = ioCounters.WriteCount + status.IOReadBytes = ioCounters.ReadBytes + status.IOWriteBytes = ioCounters.WriteBytes + } return nil } diff --git a/pkg/internal/infraolly/process/harvest_test.go b/pkg/internal/infraolly/process/harvest_test.go index d6ed4712d..b2f072f8f 100644 --- a/pkg/internal/infraolly/process/harvest_test.go +++ b/pkg/internal/infraolly/process/harvest_test.go @@ -1,6 +1,8 @@ // Copyright 2020 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +//go:build linux + package process import ( diff --git a/pkg/internal/netolly/export/otel/expirer.go b/pkg/internal/netolly/export/otel/expirer.go index 47790c454..88d90a499 100644 --- a/pkg/internal/netolly/export/otel/expirer.go +++ b/pkg/internal/netolly/export/otel/expirer.go @@ -53,6 +53,10 @@ func (g *Counter) Load() int64 { return g.val.Load() } +func (g *Counter) Add(v int64) { + g.val.Add(v) +} + func (g *Counter) Attributes() attribute.Set { return g.attributes } diff --git a/pkg/internal/netolly/export/otel/metrics.go b/pkg/internal/netolly/export/otel/metrics.go index acb850286..13ccd902c 100644 --- a/pkg/internal/netolly/export/otel/metrics.go +++ b/pkg/internal/netolly/export/otel/metrics.go @@ -117,7 +117,7 @@ func (me *metricsExporter) Do(in <-chan []*ebpf.Record) { for i := range in { me.clock.Update() for _, v := range i { - me.metrics.ForRecord(v).val.Add(int64(v.Metrics.Bytes)) + me.metrics.ForRecord(v).Add(int64(v.Metrics.Bytes)) } } } diff --git a/pkg/internal/netolly/export/otel/metrics_test.go b/pkg/internal/netolly/export/otel/metrics_test.go index 727050de6..da794a2e5 100644 --- a/pkg/internal/netolly/export/otel/metrics_test.go +++ b/pkg/internal/netolly/export/otel/metrics_test.go @@ -36,12 +36,12 @@ func TestMetricAttributes(t *testing.T) { in.Id.SrcIp.In6U.U6Addr8 = [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 12, 34, 56, 78} in.Id.DstIp.In6U.U6Addr8 = [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 33, 22, 11, 1} - me := &metricsExporter{metrics: &Expirer[*ebpf.Record, metric.Int64Observer, *Counter, int64]{attrs: attributes.OpenTelemetryGetters( - ebpf.RecordGetters, []attr.Name{ - attr.SrcAddress, attr.DstAddres, attr.SrcPort, attr.DstPort, attr.SrcName, attr.DstName, - attr.K8sSrcName, attr.K8sSrcNamespace, attr.K8sDstName, attr.K8sDstNamespace, - })}} - reportedAttributes, _ := me.metrics.recordAttributes(in) + me := NewExpirer[*ebpf.Record, metric.Int64Observer, *Counter, int64](NewCounter, attributes.OpenTelemetryGetters(ebpf.RecordGetters, []attr.Name{ + attr.SrcAddress, attr.DstAddres, attr.SrcPort, attr.DstPort, attr.SrcName, attr.DstName, + attr.K8sSrcName, attr.K8sSrcNamespace, attr.K8sDstName, attr.K8sDstNamespace, + }), timeNow, timeout) + record := me.ForRecord(in) + reportedAttributes := record.Attributes() for _, mustContain := range []attribute.KeyValue{ attribute.String("src.address", "12.34.56.78"), attribute.String("dst.address", "33.22.11.1"), @@ -85,12 +85,13 @@ func TestMetricAttributes_Filter(t *testing.T) { in.Id.SrcIp.In6U.U6Addr8 = [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 12, 34, 56, 78} in.Id.DstIp.In6U.U6Addr8 = [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 33, 22, 11, 1} - me := &Expirer[*ebpf.Record, metric.Int64Observer, *Counter, int64]{attrs: attributes.OpenTelemetryGetters(ebpf.RecordGetters, []attr.Name{ + me := NewExpirer[*ebpf.Record, metric.Int64Observer, *Counter, int64](NewCounter, attributes.OpenTelemetryGetters(ebpf.RecordGetters, []attr.Name{ "src.address", "k8s.src.name", "k8s.dst.name", - })} - reportedAttributes, _ := me.recordAttributes(in) + }), timeNow, timeout) + record := me.ForRecord(in) + reportedAttributes := record.Attributes() for _, mustContain := range []attribute.KeyValue{ attribute.String("src.address", "12.34.56.78"), attribute.String("k8s.src.name", "srcname"), From b8ffb4fa517953fe813a9f69043ad0521ff40bb5 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Mon, 3 Jun 2024 12:08:43 +0200 Subject: [PATCH 14/27] procolly metrics exporter merges app+net --- pkg/internal/export/otel/metrics.go | 6 +- pkg/internal/export/otel/traces.go | 4 +- pkg/internal/infraolly/export/otel/metrics.go | 147 +++++++++++++----- pkg/internal/infraolly/getters/proc_status.go | 63 -------- .../infraolly/process/status_getters.go | 32 ++++ pkg/internal/netolly/export/otel/expirer.go | 46 +++--- 6 files changed, 166 insertions(+), 132 deletions(-) delete mode 100644 pkg/internal/infraolly/getters/proc_status.go create mode 100644 pkg/internal/infraolly/process/status_getters.go diff --git a/pkg/internal/export/otel/metrics.go b/pkg/internal/export/otel/metrics.go index 7b32b2839..a72d0aeb6 100644 --- a/pkg/internal/export/otel/metrics.go +++ b/pkg/internal/export/otel/metrics.go @@ -446,7 +446,7 @@ func (mr *MetricsReporter) newMetricSet(service svc.ID) (*Metrics, error) { // time units for HTTP and GRPC durations are in seconds, according to the OTEL specification: // https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/metrics/semantic_conventions // TODO: set ExplicitBucketBoundaries here and in prometheus from the previous specification - meter := m.provider.Meter(reporterName) + meter := m.provider.Meter(ReporterName) var err error if mr.cfg.AppMetricsEnabled() { err = mr.setupOtelMeters(&m, meter) @@ -561,7 +561,7 @@ func otelHistogramConfig(metricName string, buckets []float64, useExponentialHis return metric.NewView( metric.Instrument{ Name: metricName, - Scope: instrumentation.Scope{Name: reporterName}, + Scope: instrumentation.Scope{Name: ReporterName}, }, metric.Stream{ Name: metricName, @@ -574,7 +574,7 @@ func otelHistogramConfig(metricName string, buckets []float64, useExponentialHis return metric.NewView( metric.Instrument{ Name: metricName, - Scope: instrumentation.Scope{Name: reporterName}, + Scope: instrumentation.Scope{Name: ReporterName}, }, metric.Stream{ Name: metricName, diff --git a/pkg/internal/export/otel/traces.go b/pkg/internal/export/otel/traces.go index 9a85561ae..1cd9da014 100644 --- a/pkg/internal/export/otel/traces.go +++ b/pkg/internal/export/otel/traces.go @@ -45,7 +45,7 @@ func tlog() *slog.Logger { return slog.With("component", "otel.TracesReporter") } -const reporterName = "github.com/grafana/beyla" +const ReporterName = "github.com/grafana/beyla" type TracesConfig struct { CommonEndpoint string `yaml:"-" env:"OTEL_EXPORTER_OTLP_ENDPOINT"` @@ -324,7 +324,7 @@ func GenerateTraces(span *request.Span, userAttrs map[attr.Name]struct{}) ptrace rs := traces.ResourceSpans().AppendEmpty() ss := rs.ScopeSpans().AppendEmpty() resourceAttrs := attrsToMap(ResourceAttrs(span.ServiceID).Attributes()) - resourceAttrs.PutStr(string(semconv.OTelLibraryNameKey), reporterName) + resourceAttrs.PutStr(string(semconv.OTelLibraryNameKey), ReporterName) resourceAttrs.CopyTo(rs.Resource().Attributes()) traceID := pcommon.TraceID(span.TraceID) diff --git a/pkg/internal/infraolly/export/otel/metrics.go b/pkg/internal/infraolly/export/otel/metrics.go index bc449833f..f084cf654 100644 --- a/pkg/internal/infraolly/export/otel/metrics.go +++ b/pkg/internal/infraolly/export/otel/metrics.go @@ -1,6 +1,5 @@ package otel -/* import ( "context" "fmt" @@ -8,20 +7,19 @@ import ( "slices" "time" - "github.com/google/uuid" "github.com/mariomac/pipes/pipe" "go.opentelemetry.io/otel/attribute" metric2 "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.19.0" "github.com/grafana/beyla/pkg/internal/export/attributes" "github.com/grafana/beyla/pkg/internal/export/expire" "github.com/grafana/beyla/pkg/internal/export/otel" - "github.com/grafana/beyla/pkg/internal/netolly/ebpf" + "github.com/grafana/beyla/pkg/internal/infraolly/process" otel2 "github.com/grafana/beyla/pkg/internal/netolly/export/otel" "github.com/grafana/beyla/pkg/internal/pipe/global" + "github.com/grafana/beyla/pkg/internal/request" "github.com/grafana/beyla/pkg/internal/svc" ) @@ -49,65 +47,128 @@ func newMeterProvider(res *resource.Resource, exporter *metric.Exporter, interva } type metricsExporter struct { - metrics *otel2.Expirer - clock *expire.CachedClock + ctx context.Context + cfg *MetricsConfig + clock *expire.CachedClock + + attributes *attributes.AttrSelector + exporter metric.Exporter + reporters otel.ReporterPool[*Metrics] + + attrCPUTime []attributes.Field[*process.Status, attribute.KeyValue] +} + +type Metrics struct { + ctx context.Context + service svc.ID + provider *metric.MeterProvider + + cpuTime *otel2.Expirer[*process.Status, metric2.Float64Observer, *otel2.Gauge, float64] } -func ProcessMetricsExporterProvider(ctxInfo *global.ContextInfo, cfg *MetricsConfig) (pipe.FinalFunc[[]*ebpf.Record], error) { +func ProcessMetricsExporterProvider( + ctx context.Context, + ctxInfo *global.ContextInfo, + cfg *MetricsConfig, +) (pipe.FinalFunc[[]*process.Status], error) { if !cfg.Enabled() { // This node is not going to be instantiated. Let the pipes library just ignore it. - return pipe.IgnoreFinal[[]*ebpf.Record](), nil + return pipe.IgnoreFinal[[]*process.Status](), nil } + otel.SetupInternalOTELSDKLogger(cfg.Metrics.SDKLogLevel) + log := mlog() - log.Debug("instantiating network metrics exporter provider") - exporter, err := otel.InstantiateMetricsExporter(context.Background(), cfg.Metrics, log) + log.Debug("instantiating process metrics exporter provider") + + attrProv, err := attributes.NewAttrSelector(ctxInfo.MetricAttributeGroups, cfg.AttributeSelectors) if err != nil { - log.Error("", "error", err) - return nil, err + return nil, fmt.Errorf("process OTEL exporter attributes enable: %w", err) } - provider, err := newMeterProvider(otel.ResourceAttrs(), &exporter, cfg.Metrics.Interval) + mr := &metricsExporter{ + ctx: ctx, + cfg: cfg, + attributes: attrProv, + } + mr.attrCPUTime = attributes.OpenTelemetryGetters( + process.OTELGetters, mr.attributes.For(attributes.ProcessCPUUtilization)) + + mr.reporters = otel.NewReporterPool[*Metrics](cfg.Metrics.ReportersCacheLen, + func(id svc.UID, v *Metrics) { + llog := log.With("service", id) + llog.Debug("evicting metrics reporter from cache") + go func() { + if err := v.provider.ForceFlush(ctx); err != nil { + llog.Warn("error flushing evicted metrics provider", "error", err) + } + }() + }, mr.newMetricSet) + + mr.exporter, err = otel.InstantiateMetricsExporter(context.Background(), cfg.Metrics, log) if err != nil { - log.Error("", "error", err) + log.Error("instantiating metrics exporter", "error", err) return nil, err } - attrProv, err := attributes.NewAttrSelector(ctxInfo.MetricAttributeGroups, cfg.AttributeSelectors) - if err != nil { - return nil, fmt.Errorf("network OTEL exporter attributes enable: %w", err) + return mr.Do, nil +} + +func (mr *metricsExporter) newMetricSet(service svc.ID) (*Metrics, error) { + log := mlog().With("service", service) + log.Debug("creating new Metrics exporter") + resources := otel.ResourceAttrs(service) + opts := []metric.Option{ + metric.WithResource(resources), + metric.WithReader(metric.NewPeriodicReader(mr.exporter, + metric.WithInterval(mr.cfg.Metrics.Interval))), } - attrs := attributes.OpenTelemetryGetters( - ebpf.RecordGetters, - attrProv.For(attributes.BeylaNetworkFlow)) - - clock := expire.NewCachedClock(timeNow) - expirer := otel2.NewExpirer(attrs, clock.Time, cfg.Metrics.TTL) - ebpfEvents := provider.Meter("network_ebpf_events") - - _, err = ebpfEvents.Int64ObservableCounter( - attributes.BeylaNetworkFlow.OTEL, - metric2.WithDescription("total bytes_sent value of network flows observed by probe since its launch"), - metric2.WithUnit("{bytes}"), - metric2.WithInt64Callback(expirer.Collect), - ) - if err != nil { - log.Error("creating observable counter", "error", err) - return nil, err + + m := Metrics{ + ctx: mr.ctx, + service: service, + provider: metric.NewMeterProvider(opts...), } - log.Debug("restricting attributes not in this list", "attributes", cfg.AttributeSelectors) - return (&metricsExporter{ - metrics: expirer, - clock: clock, - }).Do, nil + + // TODO: reporterName must go somewhere + //meter := m.provider.Meter(otel.ReporterName) + m.cpuTime = otel2.NewExpirer[*process.Status, metric2.Float64Observer]( + otel2.NewGauge, + mr.attrCPUTime, + timeNow, + mr.cfg.Metrics.TTL, + ) + return &m, nil } -func (me *metricsExporter) Do(in <-chan []*ebpf.Record) { +func (me *metricsExporter) Do(in <-chan []*process.Status) { + var lastSvcUID svc.UID + var reporter *Metrics for i := range in { me.clock.Update() - for _, v := range i { - me.metrics.CounterForRecord(v).val.Add(int64(v.Metrics.Bytes)) + for _, s := range i { + // optimization: do not query the resources' cache if the + // previously processed span belongs to the same service name + // as the current. + // This will save querying OTEL resource reporters when there is + // only a single instrumented process. + // In multi-process tracing, this is likely to happen as most + // tracers group traces belonging to the same service in the same slice. + if s.ServiceID.UID != lastSvcUID || reporter == nil { + lm, err := me.reporters.For(s.ServiceID) + if err != nil { + mlog().Error("unexpected error creating OTEL resource. Ignoring metric", + err, "service", s.ServiceID) + continue + } + lastSvcUID = s.ServiceID.UID + reporter = lm + } + // TODO: support user/system/other + // TODO: precalculate For UUID + lm.cpuTime.ForRecord(s).Set(s.CPUSystemPercent) + lm.cpuTime.ForRecord(s).Set(s.CPUUserPercent) + lm.cpuTime.ForRecord(s).Set(s.CPUPercent) } } } -*/ diff --git a/pkg/internal/infraolly/getters/proc_status.go b/pkg/internal/infraolly/getters/proc_status.go deleted file mode 100644 index e9dd348b5..000000000 --- a/pkg/internal/infraolly/getters/proc_status.go +++ /dev/null @@ -1,63 +0,0 @@ -package getters - -import ( - "go.opentelemetry.io/otel/attribute" - - "github.com/grafana/beyla/pkg/internal/export/attributes" - attr "github.com/grafana/beyla/pkg/internal/export/attributes/names" - "github.com/grafana/beyla/pkg/internal/infraolly/process" -) - -//nolint:unused -func ProcStatusOTELGettersUser(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { - if name == attr.ProcCPUState { - return func(_ *process.Status) attribute.KeyValue { - return attribute.Key(attr.ProcCPUState).String("user") - }, true - } - return procStatusOTELGetters(name) -} - -//nolint:unused -func ProcStatusOTELGettersSystem(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { - if name == attr.ProcCPUState { - return func(_ *process.Status) attribute.KeyValue { - return attribute.Key(attr.ProcCPUState).String("system") - }, true - } - return procStatusOTELGetters(name) -} - -//nolint:unused -func ProcStatusOTELGettersWait(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { - if name == attr.ProcCPUState { - return func(_ *process.Status) attribute.KeyValue { - return attribute.Key(attr.ProcCPUState).String("wait") - }, true - } - return procStatusOTELGetters(name) -} - -// nolint:cyclop -func procStatusOTELGetters(name attr.Name) (attributes.Getter[*process.Status, attribute.KeyValue], bool) { - var g attributes.Getter[*process.Status, attribute.KeyValue] - switch name { - case attr.ProcCommand: - g = func(s *process.Status) attribute.KeyValue { return attribute.Key(attr.ProcCommand).String(s.Command) } - case attr.ProcCommandLine: - g = func(s *process.Status) attribute.KeyValue { - return attribute.Key(attr.ProcCommand).String(s.CommandLine) - } - case attr.ProcOwner: - g = func(s *process.Status) attribute.KeyValue { return attribute.Key(attr.ProcOwner).String(s.User) } - case attr.ProcParentPid: - g = func(s *process.Status) attribute.KeyValue { - return attribute.Key(attr.ProcParentPid).Int(int(s.ParentProcessID)) - } - case attr.ProcPid: - g = func(s *process.Status) attribute.KeyValue { - return attribute.Key(attr.ProcParentPid).Int(int(s.ProcessID)) - } - } - return g, g != nil -} diff --git a/pkg/internal/infraolly/process/status_getters.go b/pkg/internal/infraolly/process/status_getters.go new file mode 100644 index 000000000..d71d12bb2 --- /dev/null +++ b/pkg/internal/infraolly/process/status_getters.go @@ -0,0 +1,32 @@ +package process + +import ( + "go.opentelemetry.io/otel/attribute" + + "github.com/grafana/beyla/pkg/internal/export/attributes" + attr "github.com/grafana/beyla/pkg/internal/export/attributes/names" +) + +// nolint:cyclop +func OTELGetters(name attr.Name) (attributes.Getter[*Status, attribute.KeyValue], bool) { + var g attributes.Getter[*Status, attribute.KeyValue] + switch name { + case attr.ProcCommand: + g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.ProcCommand).String(s.Command) } + case attr.ProcCommandLine: + g = func(s *Status) attribute.KeyValue { + return attribute.Key(attr.ProcCommand).String(s.CommandLine) + } + case attr.ProcOwner: + g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.ProcOwner).String(s.User) } + case attr.ProcParentPid: + g = func(s *Status) attribute.KeyValue { + return attribute.Key(attr.ProcParentPid).Int(int(s.ParentProcessID)) + } + case attr.ProcPid: + g = func(s *Status) attribute.KeyValue { + return attribute.Key(attr.ProcParentPid).Int(int(s.ProcessID)) + } + } + return g, g != nil +} diff --git a/pkg/internal/netolly/export/otel/expirer.go b/pkg/internal/netolly/export/otel/expirer.go index 88d90a499..01ca73b8e 100644 --- a/pkg/internal/netolly/export/otel/expirer.go +++ b/pkg/internal/netolly/export/otel/expirer.go @@ -36,18 +36,30 @@ type observer[T any] interface { // VT: type of the metric value: int, float... type Expirer[Record any, OT observer[VT], Metric loader[VT], VT any] struct { instancer func(set attribute.Set) Metric - attrs []attributes.Field[Record, string] + attrs []attributes.Field[Record, attribute.KeyValue] entries *expire.ExpiryMap[Metric] log *slog.Logger } -type Counter struct { +type metricAttributes struct { attributes attribute.Set - val atomic.Int64 +} + +func (g *metricAttributes) Attributes() attribute.Set { + return g.attributes +} + +func (g *metricAttributes) SetAttributes(a attribute.Set) { + g.attributes = a +} + +type Counter struct { + metricAttributes + val atomic.Int64 } func NewCounter(attributes attribute.Set) *Counter { - return &Counter{attributes: attributes} + return &Counter{metricAttributes: metricAttributes{attributes: attributes}} } func (g *Counter) Load() int64 { return g.val.Load() @@ -57,23 +69,15 @@ func (g *Counter) Add(v int64) { g.val.Add(v) } -func (g *Counter) Attributes() attribute.Set { - return g.attributes -} - -func (g *Counter) SetAttributes(a attribute.Set) { - g.attributes = a -} - type Gauge struct { - attributes attribute.Set - val atomic.Value + metricAttributes + val atomic.Value } -func NewGauge(attributes attribute.Set, initVal float64) *Gauge { +func NewGauge(attributes attribute.Set) *Gauge { val := atomic.Value{} - val.Store(initVal) - return &Gauge{attributes: attributes, val: val} + val.Store(float64(0)) + return &Gauge{metricAttributes: metricAttributes{attributes: attributes}, val: val} } func (g Gauge) Load() float64 { @@ -88,7 +92,7 @@ func (g Gauge) Set(val float64) { // if they haven't been updated during the last timeout period func NewExpirer[Record any, OT observer[VT], Metric loader[VT], VT any]( instancer func(set attribute.Set) Metric, - attrs []attributes.Field[Record, string], + attrs []attributes.Field[Record, attribute.KeyValue], clock expire.Clock, expireTime time.Duration, ) *Expirer[Record, OT, Metric, VT] { @@ -129,9 +133,9 @@ func (ex *Expirer[Record, OT, Metric, VT]) recordAttributes(m Record) (attribute vals := make([]string, 0, len(ex.attrs)) for _, attr := range ex.attrs { - val := attr.Get(m) - keyVals = append(keyVals, attribute.String(attr.ExposedName, val)) - vals = append(vals, val) + kv := attr.Get(m) + keyVals = append(keyVals, kv) + vals = append(vals, kv.Value.Emit()) } return attribute.NewSet(keyVals...), vals From 0d102504691c2d9786dabed9de1fcc3f9c7b42d5 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Mon, 3 Jun 2024 15:13:39 +0200 Subject: [PATCH 15/27] a process exporter per service --- pkg/internal/filter/attribute_test.go | 4 +- pkg/internal/infraolly/export/otel/metrics.go | 38 ++++++---------- pkg/internal/infraolly/process/collect.go | 13 +++--- pkg/internal/infraolly/process/harvest.go | 6 ++- .../infraolly/process/harvest_test.go | 12 +++--- pkg/internal/infraolly/process/status.go | 11 ++++- pkg/internal/netolly/agent/pipeline.go | 2 +- pkg/internal/netolly/ebpf/record_getters.go | 43 +++++++++++++------ .../netolly/export/otel/metrics_test.go | 4 +- pkg/internal/netolly/export/prom/prom.go | 2 +- 10 files changed, 74 insertions(+), 61 deletions(-) diff --git a/pkg/internal/filter/attribute_test.go b/pkg/internal/filter/attribute_test.go index 7dc853e2a..a2a09a799 100644 --- a/pkg/internal/filter/attribute_test.go +++ b/pkg/internal/filter/attribute_test.go @@ -19,7 +19,7 @@ func TestAttributeFilter(t *testing.T) { filterFunc, err := ByAttribute[*ebpf.Record](AttributeFamilyConfig{ "beyla.ip": MatchDefinition{Match: "148.*"}, "k8s.src.namespace": MatchDefinition{NotMatch: "debug"}, - }, ebpf.RecordGetters)() + }, ebpf.RecordStringGetters)() require.NoError(t, err) in := make(chan []*ebpf.Record, 10) @@ -93,7 +93,7 @@ func TestAttributeFilter_VerificationError(t *testing.T) { } for _, tc := range testCases { t.Run(fmt.Sprintf("%v", tc), func(t *testing.T) { - _, err := ByAttribute[*ebpf.Record](tc, ebpf.RecordGetters)() + _, err := ByAttribute[*ebpf.Record](tc, ebpf.RecordStringGetters)() assert.Error(t, err) }) } diff --git a/pkg/internal/infraolly/export/otel/metrics.go b/pkg/internal/infraolly/export/otel/metrics.go index f084cf654..5bbd05303 100644 --- a/pkg/internal/infraolly/export/otel/metrics.go +++ b/pkg/internal/infraolly/export/otel/metrics.go @@ -11,7 +11,6 @@ import ( "go.opentelemetry.io/otel/attribute" metric2 "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/sdk/metric" - "go.opentelemetry.io/otel/sdk/resource" "github.com/grafana/beyla/pkg/internal/export/attributes" "github.com/grafana/beyla/pkg/internal/export/expire" @@ -19,7 +18,6 @@ import ( "github.com/grafana/beyla/pkg/internal/infraolly/process" otel2 "github.com/grafana/beyla/pkg/internal/netolly/export/otel" "github.com/grafana/beyla/pkg/internal/pipe/global" - "github.com/grafana/beyla/pkg/internal/request" "github.com/grafana/beyla/pkg/internal/svc" ) @@ -38,14 +36,6 @@ func mlog() *slog.Logger { return slog.With("component", "otel.ProcessMetricsExporter") } -func newMeterProvider(res *resource.Resource, exporter *metric.Exporter, interval time.Duration) (*metric.MeterProvider, error) { - meterProvider := metric.NewMeterProvider( - metric.WithResource(res), - metric.WithReader(metric.NewPeriodicReader(*exporter, metric.WithInterval(interval))), - ) - return meterProvider, nil -} - type metricsExporter struct { ctx context.Context cfg *MetricsConfig @@ -114,29 +104,29 @@ func ProcessMetricsExporterProvider( return mr.Do, nil } -func (mr *metricsExporter) newMetricSet(service svc.ID) (*Metrics, error) { +func (me *metricsExporter) newMetricSet(service svc.ID) (*Metrics, error) { log := mlog().With("service", service) log.Debug("creating new Metrics exporter") resources := otel.ResourceAttrs(service) opts := []metric.Option{ metric.WithResource(resources), - metric.WithReader(metric.NewPeriodicReader(mr.exporter, - metric.WithInterval(mr.cfg.Metrics.Interval))), + metric.WithReader(metric.NewPeriodicReader(me.exporter, + metric.WithInterval(me.cfg.Metrics.Interval))), } m := Metrics{ - ctx: mr.ctx, + ctx: me.ctx, service: service, provider: metric.NewMeterProvider(opts...), } // TODO: reporterName must go somewhere - //meter := m.provider.Meter(otel.ReporterName) + // meter := m.provider.Meter(otel.ReporterName) m.cpuTime = otel2.NewExpirer[*process.Status, metric2.Float64Observer]( otel2.NewGauge, - mr.attrCPUTime, + me.attrCPUTime, timeNow, - mr.cfg.Metrics.TTL, + me.cfg.Metrics.TTL, ) return &m, nil } @@ -154,21 +144,19 @@ func (me *metricsExporter) Do(in <-chan []*process.Status) { // only a single instrumented process. // In multi-process tracing, this is likely to happen as most // tracers group traces belonging to the same service in the same slice. - if s.ServiceID.UID != lastSvcUID || reporter == nil { - lm, err := me.reporters.For(s.ServiceID) + if s.Service.UID != lastSvcUID || reporter == nil { + // TODO: precalculate For UUID + lm, err := me.reporters.For(*s.Service) if err != nil { mlog().Error("unexpected error creating OTEL resource. Ignoring metric", - err, "service", s.ServiceID) + err, "service", s.Service) continue } - lastSvcUID = s.ServiceID.UID + lastSvcUID = s.Service.UID reporter = lm } // TODO: support user/system/other - // TODO: precalculate For UUID - lm.cpuTime.ForRecord(s).Set(s.CPUSystemPercent) - lm.cpuTime.ForRecord(s).Set(s.CPUUserPercent) - lm.cpuTime.ForRecord(s).Set(s.CPUPercent) + reporter.cpuTime.ForRecord(s).Set(s.CPUSystemPercent) } } } diff --git a/pkg/internal/infraolly/process/collect.go b/pkg/internal/infraolly/process/collect.go index b23ae4996..8c0c166ab 100644 --- a/pkg/internal/infraolly/process/collect.go +++ b/pkg/internal/infraolly/process/collect.go @@ -13,6 +13,7 @@ import ( "github.com/mariomac/pipes/pipe" "github.com/grafana/beyla/pkg/internal/request" + "github.com/grafana/beyla/pkg/internal/svc" ) // Collector returns runtime information about the currently running processes @@ -47,7 +48,7 @@ func NewCollectorProvider(ctx context.Context, input *<-chan []request.Span, cfg func (ps *Collector) Run(out chan<- []*Status) { // TODO: set app metadata as key for later decoration? (e.g. K8s metadata, svc.ID) - pids := map[int32]struct{}{} + pids := map[int32]*svc.ID{} collectTicker := time.NewTicker(ps.cfg.Rate) defer collectTicker.Stop() newPids := *ps.newPids @@ -59,11 +60,11 @@ func (ps *Collector) Run(out chan<- []*Status) { // updating PIDs map with spans information if ps.userPids { for i := range spans { - pids[int32(spans[i].Pid.UserPID)] = struct{}{} + pids[int32(spans[i].Pid.UserPID)] = &spans[i].ServiceID } } else { for i := range spans { - pids[int32(spans[i].Pid.HostPID)] = struct{}{} + pids[int32(spans[i].Pid.HostPID)] = &spans[i].ServiceID } } case <-collectTicker.C: @@ -79,12 +80,12 @@ func (ps *Collector) Run(out chan<- []*Status) { // Collect returns the status for all the running processes, decorated with Docker runtime information, if applies. // It also returns the PIDs that have to be removed from the map, as they do not exist anymore -func (ps *Collector) Collect(pids map[int32]struct{}) ([]*Status, []int32) { +func (ps *Collector) Collect(pids map[int32]*svc.ID) ([]*Status, []int32) { results := make([]*Status, 0, len(pids)) var removed []int32 - for pid := range pids { - status, err := ps.harvest.Do(pid) + for pid, svcID := range pids { + status, err := ps.harvest.Do(pid, svcID) if err != nil { ps.log.Debug("skipping process", "pid", pid, "error", err) ps.harvest.cache.Remove(pid) diff --git a/pkg/internal/infraolly/process/harvest.go b/pkg/internal/infraolly/process/harvest.go index b457274f8..cd667f7d4 100644 --- a/pkg/internal/infraolly/process/harvest.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -17,6 +17,8 @@ import ( "github.com/hashicorp/golang-lru/v2/simplelru" "github.com/shirou/gopsutil/v3/process" + + "github.com/grafana/beyla/pkg/internal/svc" ) func hlog() *slog.Logger { @@ -86,7 +88,7 @@ func (*Harvester) Pids() ([]int32, error) { // Do returns a status of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the // time since this process was statusd for the last time. If the process has been statusd for the first time, this value // will be ignored -func (ps *Harvester) Do(pid int32) (*Status, error) { +func (ps *Harvester) Do(pid int32, svcID *svc.ID) (*Status, error) { ps.log.Debug("harvesting pid", "pid", pid) // Reuses process information that does not vary cached, hasCachedEntry := ps.cache.Get(pid) @@ -107,7 +109,7 @@ func (ps *Harvester) Do(pid int32) (*Status, error) { } // Creates a fresh process status and populates it with the metrics data - status := NewStatus(pid) + status := NewStatus(pid, svcID) if err := ps.populateStaticData(status, cached.process); err != nil { return nil, fmt.Errorf("can't populate static attributes: %w", err) diff --git a/pkg/internal/infraolly/process/harvest_test.go b/pkg/internal/infraolly/process/harvest_test.go index b2f072f8f..92d30f5d3 100644 --- a/pkg/internal/infraolly/process/harvest_test.go +++ b/pkg/internal/infraolly/process/harvest_test.go @@ -35,7 +35,7 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { h := newHarvester(&Config{RunMode: c.mode}, cache) // If not privileged, it is expected to not report neither FDs nor IO counters - status, err := h.Do(int32(os.Getpid())) + status, err := h.Do(int32(os.Getpid()), nil) require.NoError(t, err) if c.privileged { assert.NotZero(t, status.FdCount) @@ -54,7 +54,7 @@ func TestLinuxHarvester_Do(t *testing.T) { h := newHarvester(&Config{}, cache) // When retrieving for a given process status (e.g. the current testing executable) - status, err := h.Do(int32(os.Getpid())) + status, err := h.Do(int32(os.Getpid()), nil) // It returns the corresponding process status with valid data require.NoError(t, err) @@ -90,7 +90,7 @@ func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process status (e.g. the current testing executable) - status, err := h.Do(int32(cmd.Process.Pid)) + status, err := h.Do(int32(cmd.Process.Pid), nil) // It returns the corresponding Command line without stripping arguments require.NoError(t, err) @@ -114,7 +114,7 @@ func TestLinuxHarvester_Do_StripCommandLine(t *testing.T) { test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process status (e.g. the current testing executable) - status, err := h.Do(int32(cmd.Process.Pid)) + status, err := h.Do(int32(cmd.Process.Pid), nil) // It returns the corresponding Command line without stripping arguments require.NoError(t, err) @@ -134,7 +134,7 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentCmd(t *testing.T) { h := newHarvester(&Config{}, cache) // When the process is harvested - status, err := h.Do(currentPid) + status, err := h.Do(currentPid, nil) require.NoError(t, err) // The status is updated @@ -152,7 +152,7 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentPid(t *testing.T) { h := newHarvester(&Config{}, cache) // When the process is harvested - status, err := h.Do(currentPid) + status, err := h.Do(currentPid, nil) require.NoError(t, err) // The status is updated diff --git a/pkg/internal/infraolly/process/status.go b/pkg/internal/infraolly/process/status.go index 93861623b..a256c96af 100644 --- a/pkg/internal/infraolly/process/status.go +++ b/pkg/internal/infraolly/process/status.go @@ -1,6 +1,10 @@ package process -import "log/slog" +import ( + "log/slog" + + "github.com/grafana/beyla/pkg/internal/svc" +) func pslog() *slog.Logger { return slog.With("component", "process.Collector") @@ -24,10 +28,13 @@ type Status struct { IOWriteCount uint64 IOReadBytes uint64 IOWriteBytes uint64 + + Service *svc.ID } -func NewStatus(pid int32) *Status { +func NewStatus(pid int32, svcID *svc.ID) *Status { return &Status{ ProcessID: pid, + Service: svcID, } } diff --git a/pkg/internal/netolly/agent/pipeline.go b/pkg/internal/netolly/agent/pipeline.go index 011957811..f2c022e6e 100644 --- a/pkg/internal/netolly/agent/pipeline.go +++ b/pkg/internal/netolly/agent/pipeline.go @@ -126,7 +126,7 @@ func (f *Flows) pipelineBuilder(ctx context.Context) (*pipe.Builder[*FlowsPipeli pipe.AddMiddleProvider(pb, rdns, func() (pipe.MiddleFunc[[]*ebpf.Record, []*ebpf.Record], error) { return flow.ReverseDNSProvider(&f.cfg.NetworkFlows.ReverseDNS) }) - pipe.AddMiddleProvider(pb, fltr, filter.ByAttribute(f.cfg.Filters.Network, ebpf.RecordGetters)) + pipe.AddMiddleProvider(pb, fltr, filter.ByAttribute(f.cfg.Filters.Network, ebpf.RecordStringGetters)) // Terminal nodes export the flow record information out of the pipeline: OTEL, Prom and printer. // Not all the nodes are mandatory here. Is the responsibility of each Provider function to decide diff --git a/pkg/internal/netolly/ebpf/record_getters.go b/pkg/internal/netolly/ebpf/record_getters.go index daaf27122..4533363f7 100644 --- a/pkg/internal/netolly/ebpf/record_getters.go +++ b/pkg/internal/netolly/ebpf/record_getters.go @@ -1,7 +1,7 @@ package ebpf import ( - "strconv" + "go.opentelemetry.io/otel/attribute" "github.com/grafana/beyla/pkg/internal/export/attributes" attr "github.com/grafana/beyla/pkg/internal/export/attributes/names" @@ -10,35 +10,50 @@ import ( // RecordGetters returns the attributes.Getter function that returns the string value of a given // attribute name. -func RecordGetters(name attr.Name) (attributes.Getter[*Record, string], bool) { - var getter attributes.Getter[*Record, string] +func RecordGetters(name attr.Name) (attributes.Getter[*Record, attribute.KeyValue], bool) { + var getter attributes.Getter[*Record, attribute.KeyValue] switch name { case attr.BeylaIP: - getter = func(r *Record) string { return r.Attrs.BeylaIP } + getter = func(r *Record) attribute.KeyValue { return attribute.String(string(attr.BeylaIP), r.Attrs.BeylaIP) } case attr.Transport: - getter = func(r *Record) string { return transport.Protocol(r.Id.TransportProtocol).String() } + getter = func(r *Record) attribute.KeyValue { + return attribute.String(string(attr.Transport), transport.Protocol(r.Id.TransportProtocol).String()) + } case attr.SrcAddress: - getter = func(r *Record) string { return r.Id.SrcIP().IP().String() } + getter = func(r *Record) attribute.KeyValue { + return attribute.String(string(attr.SrcAddress), r.Id.SrcIP().IP().String()) + } case attr.DstAddres: - getter = func(r *Record) string { return r.Id.DstIP().IP().String() } + getter = func(r *Record) attribute.KeyValue { + return attribute.String(string(attr.DstAddres), r.Id.DstIP().IP().String()) + } case attr.SrcPort: - getter = func(r *Record) string { return strconv.FormatUint(uint64(r.Id.SrcPort), 10) } + getter = func(r *Record) attribute.KeyValue { return attribute.Int(string(attr.SrcPort), int(r.Id.SrcPort)) } case attr.DstPort: - getter = func(r *Record) string { return strconv.FormatUint(uint64(r.Id.DstPort), 10) } + getter = func(r *Record) attribute.KeyValue { return attribute.Int(string(attr.DstPort), int(r.Id.DstPort)) } case attr.SrcName: - getter = func(r *Record) string { return r.Attrs.SrcName } + getter = func(r *Record) attribute.KeyValue { return attribute.String(string(attr.SrcName), r.Attrs.SrcName) } case attr.DstName: - getter = func(r *Record) string { return r.Attrs.DstName } + getter = func(r *Record) attribute.KeyValue { return attribute.String(string(attr.DstName), r.Attrs.DstName) } case attr.Direction: - getter = func(r *Record) string { return directionStr(r.Id.Direction) } + getter = func(r *Record) attribute.KeyValue { + return attribute.String(string(attr.Direction), directionStr(r.Id.Direction)) + } case attr.Iface: - getter = func(r *Record) string { return r.Attrs.Interface } + getter = func(r *Record) attribute.KeyValue { return attribute.String(string(attr.Iface), r.Attrs.Interface) } default: - getter = func(r *Record) string { return r.Attrs.Metadata[name] } + getter = func(r *Record) attribute.KeyValue { return attribute.String(string(name), r.Attrs.Metadata[name]) } } return getter, getter != nil } +func RecordStringGetters(name attr.Name) (attributes.Getter[*Record, string], bool) { + if g, ok := RecordGetters(name); ok { + return func(r *Record) string { return g(r).Value.Emit() }, true + } + return nil, false +} + func directionStr(direction uint8) string { switch direction { case DirectionIngress: diff --git a/pkg/internal/netolly/export/otel/metrics_test.go b/pkg/internal/netolly/export/otel/metrics_test.go index da794a2e5..32189e71e 100644 --- a/pkg/internal/netolly/export/otel/metrics_test.go +++ b/pkg/internal/netolly/export/otel/metrics_test.go @@ -47,8 +47,8 @@ func TestMetricAttributes(t *testing.T) { attribute.String("dst.address", "33.22.11.1"), attribute.String("src.name", "srcname"), attribute.String("dst.name", "dstname"), - attribute.String("src.port", "12345"), - attribute.String("dst.port", "3210"), + attribute.Int("src.port", 12345), + attribute.Int("dst.port", 3210), attribute.String("k8s.src.name", "srcname"), attribute.String("k8s.src.namespace", "srcnamespace"), diff --git a/pkg/internal/netolly/export/prom/prom.go b/pkg/internal/netolly/export/prom/prom.go index 1f287eeed..647fa2378 100644 --- a/pkg/internal/netolly/export/prom/prom.go +++ b/pkg/internal/netolly/export/prom/prom.go @@ -82,7 +82,7 @@ func newReporter( } attrs := attributes.PrometheusGetters( - ebpf.RecordGetters, + ebpf.RecordStringGetters, provider.For(attributes.BeylaNetworkFlow)) labelNames := make([]string, 0, len(attrs)) From 27b29dba580331e2750371d2906fe04a94268f4f Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Mon, 3 Jun 2024 16:43:02 +0200 Subject: [PATCH 16/27] process_cpu_utilization_ratio now reported for OTEL --- pkg/internal/infraolly/export/otel/metrics.go | 37 +++++++++++++++---- pkg/internal/infraolly/infra_pipe.go | 19 ++++++---- pkg/internal/netolly/export/otel/expirer.go | 7 ++-- pkg/internal/pipe/instrumenter.go | 2 +- test/integration/docker-compose-python.yml | 3 +- test/integration/suites_test.go | 6 +-- 6 files changed, 51 insertions(+), 23 deletions(-) diff --git a/pkg/internal/infraolly/export/otel/metrics.go b/pkg/internal/infraolly/export/otel/metrics.go index 5bbd05303..2cc0da650 100644 --- a/pkg/internal/infraolly/export/otel/metrics.go +++ b/pkg/internal/infraolly/export/otel/metrics.go @@ -60,11 +60,21 @@ func ProcessMetricsExporterProvider( ctx context.Context, ctxInfo *global.ContextInfo, cfg *MetricsConfig, -) (pipe.FinalFunc[[]*process.Status], error) { - if !cfg.Enabled() { - // This node is not going to be instantiated. Let the pipes library just ignore it. - return pipe.IgnoreFinal[[]*process.Status](), nil +) pipe.FinalProvider[[]*process.Status] { + return func() (pipe.FinalFunc[[]*process.Status], error) { + if !cfg.Enabled() { + // This node is not going to be instantiated. Let the pipes library just ignore it. + return pipe.IgnoreFinal[[]*process.Status](), nil + } + return newProcessMetricsExporter(ctx, ctxInfo, cfg) } +} + +func newProcessMetricsExporter( + ctx context.Context, + ctxInfo *global.ContextInfo, + cfg *MetricsConfig, +) (pipe.FinalFunc[[]*process.Status], error) { otel.SetupInternalOTELSDKLogger(cfg.Metrics.SDKLogLevel) log := mlog() @@ -76,6 +86,7 @@ func ProcessMetricsExporterProvider( } mr := &metricsExporter{ + clock: expire.NewCachedClock(timeNow), ctx: ctx, cfg: cfg, attributes: attrProv, @@ -95,7 +106,7 @@ func ProcessMetricsExporterProvider( }() }, mr.newMetricSet) - mr.exporter, err = otel.InstantiateMetricsExporter(context.Background(), cfg.Metrics, log) + mr.exporter, err = otel.InstantiateMetricsExporter(ctx, cfg.Metrics, log) if err != nil { log.Error("instantiating metrics exporter", "error", err) return nil, err @@ -120,14 +131,23 @@ func (me *metricsExporter) newMetricSet(service svc.ID) (*Metrics, error) { provider: metric.NewMeterProvider(opts...), } - // TODO: reporterName must go somewhere - // meter := m.provider.Meter(otel.ReporterName) + meter := m.provider.Meter(otel.ReporterName) m.cpuTime = otel2.NewExpirer[*process.Status, metric2.Float64Observer]( otel2.NewGauge, me.attrCPUTime, timeNow, - me.cfg.Metrics.TTL, + 5*time.Minute, //me.cfg.Metrics.TTL, TODO restore ) + if _, err := meter.Float64ObservableGauge( + attributes.ProcessCPUUtilization.OTEL, + metric2.WithDescription("TODO"), + metric2.WithUnit("1"), + metric2.WithFloat64Callback(m.cpuTime.Collect), + ); err != nil { + log.Error("creating observable gauge for "+attributes.ProcessCPUUtilization.OTEL, "error", err) + return nil, err + } + return &m, nil } @@ -155,6 +175,7 @@ func (me *metricsExporter) Do(in <-chan []*process.Status) { lastSvcUID = s.Service.UID reporter = lm } + mlog().Debug("reporting data for record", "record", s) // TODO: support user/system/other reporter.cpuTime.ForRecord(s).Set(s.CPUSystemPercent) } diff --git a/pkg/internal/infraolly/infra_pipe.go b/pkg/internal/infraolly/infra_pipe.go index af5423d69..2902a1b55 100644 --- a/pkg/internal/infraolly/infra_pipe.go +++ b/pkg/internal/infraolly/infra_pipe.go @@ -9,7 +9,9 @@ import ( "github.com/grafana/beyla/pkg/beyla" "github.com/grafana/beyla/pkg/internal/export/otel" + otel2 "github.com/grafana/beyla/pkg/internal/infraolly/export/otel" "github.com/grafana/beyla/pkg/internal/infraolly/process" + "github.com/grafana/beyla/pkg/internal/pipe/global" "github.com/grafana/beyla/pkg/internal/request" ) @@ -41,7 +43,7 @@ func isSubPipeEnabled(cfg *beyla.Config) bool { // SubPipelineProvider returns a Final node that actually has a pipeline inside. // It is manually connected through a channel -func SubPipelineProvider(ctx context.Context, cfg *beyla.Config) pipe.FinalProvider[[]request.Span] { +func SubPipelineProvider(ctx context.Context, ctxInfo *global.ContextInfo, cfg *beyla.Config) pipe.FinalProvider[[]request.Span] { return func() (pipe.FinalFunc[[]request.Span], error) { if !isSubPipeEnabled(cfg) { return pipe.IgnoreFinal[[]request.Span](), nil @@ -50,15 +52,18 @@ func SubPipelineProvider(ctx context.Context, cfg *beyla.Config) pipe.FinalProvi var connector <-chan []request.Span = connectorChan nb := pipe.NewBuilder(&subPipeline{}, pipe.ChannelBufferLen(cfg.ChannelBufferLen)) pipe.AddStartProvider(nb, collector, process.NewCollectorProvider(ctx, &connector, &cfg.Processes)) - pipe.AddFinal(nb, otelExport, func(in <-chan []*process.Status) { - for i := range in { - fmt.Printf("otel %#v\n", i) - } - }) + pipe.AddFinalProvider(nb, otelExport, otel2.ProcessMetricsExporterProvider(ctx, ctxInfo, + &otel2.MetricsConfig{ + Metrics: &cfg.Metrics, + AttributeSelectors: cfg.Attributes.Select, + })) pipe.AddFinal(nb, promExport, func(in <-chan []*process.Status) { for ps := range in { for _, p := range ps { - fmt.Printf("%#v\n", *p) + _ = p + + + //fmt.Printf("%#v\n", *p) } } }) diff --git a/pkg/internal/netolly/export/otel/expirer.go b/pkg/internal/netolly/export/otel/expirer.go index 01ca73b8e..7133756e4 100644 --- a/pkg/internal/netolly/export/otel/expirer.go +++ b/pkg/internal/netolly/export/otel/expirer.go @@ -117,9 +117,10 @@ func (ex *Expirer[Record, OT, Metric, VT]) ForRecord(r Record) Metric { } func (ex *Expirer[Record, OT, Metric, VT]) Collect(_ context.Context, observer OT) error { - ex.log.Debug("invoking metrics collection") - old := ex.entries.DeleteExpired() - ex.log.With("labelValues", old).Debug("deleting old OTEL metric") + //ex.log.Debug("invoking metrics collection") + if old := ex.entries.DeleteExpired(); len(old) > 0 { + ex.log.With("labelValues", old).Debug("deleting old OTEL metric") + } for _, v := range ex.entries.All() { observer.Observe(v.Load(), metric.WithAttributeSet(v.Attributes())) diff --git a/pkg/internal/pipe/instrumenter.go b/pkg/internal/pipe/instrumenter.go index 36cb77d53..4e83a5581 100644 --- a/pkg/internal/pipe/instrumenter.go +++ b/pkg/internal/pipe/instrumenter.go @@ -123,7 +123,7 @@ func newGraphBuilder(ctx context.Context, config *beyla.Config, ctxInfo *global. pipe.AddFinalProvider(gnb, noop, debug.NoopNode(config.Noop)) pipe.AddFinalProvider(gnb, printer, debug.PrinterNode(config.Printer)) - pipe.AddFinalProvider(gnb, process, infraolly.SubPipelineProvider(ctx, config)) + pipe.AddFinalProvider(gnb, process, infraolly.SubPipelineProvider(ctx, ctxInfo, config)) // The returned builder later invokes its "Build" function that, given // the contents of the nodesMap struct, will instantiate diff --git a/test/integration/docker-compose-python.yml b/test/integration/docker-compose-python.yml index 6e97994c8..fd5484dd4 100644 --- a/test/integration/docker-compose-python.yml +++ b/test/integration/docker-compose-python.yml @@ -24,7 +24,7 @@ services: image: hatest-autoinstrumenter privileged: true # in some environments (not GH Pull Requests) you can set it to false and then cap_add: [ SYS_ADMIN ] network_mode: "service:testserver" - pid: "service:testserver" + pid: "host" environment: BEYLA_CONFIG_PATH: "/configs/instrumenter-config-java.yml" GOCOVERDIR: "/coverage" @@ -41,6 +41,7 @@ services: BEYLA_METRICS_REPORT_PEER: "true" BEYLA_HOSTNAME: "beyla" BEYLA_BPF_HTTP_REQUEST_TIMEOUT: "5s" + BEYLA_OTEL_METRICS_FEATURES: "application,application_process" depends_on: testserver: condition: service_started diff --git a/test/integration/suites_test.go b/test/integration/suites_test.go index 91f321967..6f4d47dec 100644 --- a/test/integration/suites_test.go +++ b/test/integration/suites_test.go @@ -400,9 +400,9 @@ func TestSuite_Python(t *testing.T) { require.NoError(t, compose.Up()) t.Run("Python RED metrics", testREDMetricsPythonHTTP) t.Run("Python RED metrics with timeouts", testREDMetricsTimeoutPythonHTTP) - t.Run("BPF pinning folder mounted", testBPFPinningMounted) - require.NoError(t, compose.Close()) - t.Run("BPF pinning folder unmounted", testBPFPinningUnmounted) + //t.Run("BPF pinning folder mounted", testBPFPinningMounted) + //require.NoError(t, compose.Close()) + //t.Run("BPF pinning folder unmounted", testBPFPinningUnmounted) } func TestSuite_PythonTLS(t *testing.T) { From 4d39a07ece1201bb239bd116f744df2bf1b5a369 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Tue, 4 Jun 2024 12:05:46 +0200 Subject: [PATCH 17/27] Basic process sampling working --- pkg/internal/discover/typer.go | 6 +++++- pkg/internal/ebpf/common/pids.go | 2 +- pkg/internal/infraolly/export/otel/metrics.go | 2 +- pkg/internal/infraolly/process/collect.go | 14 +++----------- pkg/internal/infraolly/process/harvest.go | 11 +++-------- pkg/internal/netolly/export/otel/expirer.go | 17 +++++++++-------- .../netolly/export/otel/expirer_test.go | 17 +++++++++++++++++ pkg/internal/svc/svc.go | 2 ++ 8 files changed, 41 insertions(+), 30 deletions(-) diff --git a/pkg/internal/discover/typer.go b/pkg/internal/discover/typer.go index 871e96a38..2aafe18b7 100644 --- a/pkg/internal/discover/typer.go +++ b/pkg/internal/discover/typer.go @@ -69,7 +69,11 @@ func (t *typer) FilterClassify(evs []Event[ProcessMatch]) []Event[Instrumentable ev := &evs[i] switch evs[i].Type { case EventCreated: - svcID := svc.ID{Name: ev.Obj.Criteria.Name, Namespace: ev.Obj.Criteria.Namespace} + svcID := svc.ID{ + Name: ev.Obj.Criteria.Name, + Namespace: ev.Obj.Criteria.Namespace, + ProcPID: ev.Obj.Process.Pid, + } if elfFile, err := exec.FindExecELF(ev.Obj.Process, svcID); err != nil { t.log.Warn("error finding process ELF. Ignoring", "error", err) } else { diff --git a/pkg/internal/ebpf/common/pids.go b/pkg/internal/ebpf/common/pids.go index 2bc5cba8a..5f3df8d89 100644 --- a/pkg/internal/ebpf/common/pids.go +++ b/pkg/internal/ebpf/common/pids.go @@ -215,7 +215,7 @@ func serviceInfo(pid uint32) svc.ID { name := commName(pid) lang := exec.FindProcLanguage(int32(pid), nil) - result := svc.ID{Name: name, SDKLanguage: lang} + result := svc.ID{Name: name, SDKLanguage: lang, ProcPID: int32(pid)} activePids.Add(pid, result) diff --git a/pkg/internal/infraolly/export/otel/metrics.go b/pkg/internal/infraolly/export/otel/metrics.go index 2cc0da650..e1508b7d6 100644 --- a/pkg/internal/infraolly/export/otel/metrics.go +++ b/pkg/internal/infraolly/export/otel/metrics.go @@ -177,7 +177,7 @@ func (me *metricsExporter) Do(in <-chan []*process.Status) { } mlog().Debug("reporting data for record", "record", s) // TODO: support user/system/other - reporter.cpuTime.ForRecord(s).Set(s.CPUSystemPercent) + reporter.cpuTime.ForRecord(s).Set(s.CPUPercent) } } } diff --git a/pkg/internal/infraolly/process/collect.go b/pkg/internal/infraolly/process/collect.go index 8c0c166ab..d15ea82f9 100644 --- a/pkg/internal/infraolly/process/collect.go +++ b/pkg/internal/infraolly/process/collect.go @@ -18,7 +18,6 @@ import ( // Collector returns runtime information about the currently running processes type Collector struct { - userPids bool ctx context.Context cfg *Config harvest *Harvester @@ -41,7 +40,6 @@ func NewCollectorProvider(ctx context.Context, input *<-chan []request.Span, cfg cache: cache, log: pslog(), newPids: input, - userPids: cfg.PidMode == PidModeUser, }).Run, nil } } @@ -58,14 +56,8 @@ func (ps *Collector) Run(out chan<- []*Status) { ps.log.Debug("exiting") case spans := <-newPids: // updating PIDs map with spans information - if ps.userPids { - for i := range spans { - pids[int32(spans[i].Pid.UserPID)] = &spans[i].ServiceID - } - } else { - for i := range spans { - pids[int32(spans[i].Pid.HostPID)] = &spans[i].ServiceID - } + for i := range spans { + pids[spans[i].ServiceID.ProcPID] = &spans[i].ServiceID } case <-collectTicker.C: ps.log.Debug("start process collection") @@ -85,7 +77,7 @@ func (ps *Collector) Collect(pids map[int32]*svc.ID) ([]*Status, []int32) { var removed []int32 for pid, svcID := range pids { - status, err := ps.harvest.Do(pid, svcID) + status, err := ps.harvest.Do(svcID) if err != nil { ps.log.Debug("skipping process", "pid", pid, "error", err) ps.harvest.cache.Remove(pid) diff --git a/pkg/internal/infraolly/process/harvest.go b/pkg/internal/infraolly/process/harvest.go index cd667f7d4..ac9cc6bed 100644 --- a/pkg/internal/infraolly/process/harvest.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -16,7 +16,6 @@ import ( "time" "github.com/hashicorp/golang-lru/v2/simplelru" - "github.com/shirou/gopsutil/v3/process" "github.com/grafana/beyla/pkg/internal/svc" ) @@ -48,7 +47,7 @@ type Config struct { Rate time.Duration - PidMode PidMode + PidMode PidMode `env:"BEYLA_PID_MODE" envDefault:"root"` } func newHarvester(cfg *Config, cache *simplelru.LRU[int32, *cacheEntry]) *Harvester { @@ -80,15 +79,11 @@ type Harvester struct { log *slog.Logger } -// Pids returns a slice of process IDs that are running now -func (*Harvester) Pids() ([]int32, error) { - return process.Pids() -} - // Do returns a status of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the // time since this process was statusd for the last time. If the process has been statusd for the first time, this value // will be ignored -func (ps *Harvester) Do(pid int32, svcID *svc.ID) (*Status, error) { +func (ps *Harvester) Do(svcID *svc.ID) (*Status, error) { + pid := svcID.ProcPID ps.log.Debug("harvesting pid", "pid", pid) // Reuses process information that does not vary cached, hasCachedEntry := ps.cache.Get(pid) diff --git a/pkg/internal/netolly/export/otel/expirer.go b/pkg/internal/netolly/export/otel/expirer.go index 7133756e4..516487467 100644 --- a/pkg/internal/netolly/export/otel/expirer.go +++ b/pkg/internal/netolly/export/otel/expirer.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log/slog" + "math" "sync/atomic" "time" @@ -71,21 +72,21 @@ func (g *Counter) Add(v int64) { type Gauge struct { metricAttributes - val atomic.Value + // Go standard library does not provide atomic packages so we need to + // store the float as bytes and then convert it with the math package + floatBits uint64 } func NewGauge(attributes attribute.Set) *Gauge { - val := atomic.Value{} - val.Store(float64(0)) - return &Gauge{metricAttributes: metricAttributes{attributes: attributes}, val: val} + return &Gauge{metricAttributes: metricAttributes{attributes: attributes}} } -func (g Gauge) Load() float64 { - return g.val.Load().(float64) +func (g *Gauge) Load() float64 { + return math.Float64frombits(atomic.LoadUint64(&g.floatBits)) } -func (g Gauge) Set(val float64) { - g.val.Store(val) +func (g *Gauge) Set(val float64) { + atomic.StoreUint64(&g.floatBits, math.Float64bits(val)) } // NewExpirer creates a metric that wraps a Counter. Its labeled instances are dropped diff --git a/pkg/internal/netolly/export/otel/expirer_test.go b/pkg/internal/netolly/export/otel/expirer_test.go index dee466105..8936adc74 100644 --- a/pkg/internal/netolly/export/otel/expirer_test.go +++ b/pkg/internal/netolly/export/otel/expirer_test.go @@ -9,6 +9,7 @@ import ( "github.com/mariomac/guara/pkg/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/attribute" "github.com/grafana/beyla/pkg/internal/export/attributes" "github.com/grafana/beyla/pkg/internal/export/otel" @@ -105,6 +106,22 @@ func TestMetricsExpiration(t *testing.T) { }) } +func TestGauge(t *testing.T) { + g := NewGauge(attribute.Set{}) + g.Set(123.456) + assert.Equal(t, 123.456, g.Load()) + g.Set(456.123) + assert.Equal(t, 456.123, g.Load()) +} + +func TestCounter(t *testing.T) { + g := NewCounter(attribute.Set{}) + g.Add(123) + assert.EqualValues(t, 123, g.Load()) + g.Add(123) + assert.EqualValues(t, 246, g.Load()) +} + type syncedClock struct { mt sync.Mutex now time.Time diff --git a/pkg/internal/svc/svc.go b/pkg/internal/svc/svc.go index 029a514b3..590ab66b4 100644 --- a/pkg/internal/svc/svc.go +++ b/pkg/internal/svc/svc.go @@ -64,6 +64,8 @@ type ID struct { Instance string Metadata map[attr.Name]string + + ProcPID int32 } func (i *ID) String() string { From 29a4d27863c2508628babbad57915c92e17e814d Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Tue, 4 Jun 2024 12:18:00 +0200 Subject: [PATCH 18/27] fix lint and unit tests --- pkg/internal/infraolly/export/otel/metrics.go | 2 +- pkg/internal/infraolly/infra_pipe.go | 15 ++---------- pkg/internal/infraolly/process/collect.go | 24 +++++++++---------- pkg/internal/infraolly/process/harvest.go | 9 ------- pkg/internal/netolly/export/otel/expirer.go | 1 - test/integration/suites_test.go | 6 ++--- 6 files changed, 18 insertions(+), 39 deletions(-) diff --git a/pkg/internal/infraolly/export/otel/metrics.go b/pkg/internal/infraolly/export/otel/metrics.go index e1508b7d6..9453051f1 100644 --- a/pkg/internal/infraolly/export/otel/metrics.go +++ b/pkg/internal/infraolly/export/otel/metrics.go @@ -136,7 +136,7 @@ func (me *metricsExporter) newMetricSet(service svc.ID) (*Metrics, error) { otel2.NewGauge, me.attrCPUTime, timeNow, - 5*time.Minute, //me.cfg.Metrics.TTL, TODO restore + me.cfg.Metrics.TTL, ) if _, err := meter.Float64ObservableGauge( attributes.ProcessCPUUtilization.OTEL, diff --git a/pkg/internal/infraolly/infra_pipe.go b/pkg/internal/infraolly/infra_pipe.go index 2902a1b55..b401efc56 100644 --- a/pkg/internal/infraolly/infra_pipe.go +++ b/pkg/internal/infraolly/infra_pipe.go @@ -21,15 +21,14 @@ import ( type subPipeline struct { Collector pipe.Start[[]*process.Status] OtelExport pipe.Final[[]*process.Status] - PromExport pipe.Final[[]*process.Status] + // TODO: add prometheus exporter } func collector(sp *subPipeline) *pipe.Start[[]*process.Status] { return &sp.Collector } func otelExport(sp *subPipeline) *pipe.Final[[]*process.Status] { return &sp.OtelExport } -func promExport(sp *subPipeline) *pipe.Final[[]*process.Status] { return &sp.PromExport } func (sp *subPipeline) Connect() { - sp.Collector.SendTo(sp.OtelExport, sp.PromExport) + sp.Collector.SendTo(sp.OtelExport) } // the sub-pipe is enabled only if there is a metrics exporter enabled, @@ -57,16 +56,6 @@ func SubPipelineProvider(ctx context.Context, ctxInfo *global.ContextInfo, cfg * Metrics: &cfg.Metrics, AttributeSelectors: cfg.Attributes.Select, })) - pipe.AddFinal(nb, promExport, func(in <-chan []*process.Status) { - for ps := range in { - for _, p := range ps { - _ = p - - - //fmt.Printf("%#v\n", *p) - } - } - }) runner, err := nb.Build() if err != nil { diff --git a/pkg/internal/infraolly/process/collect.go b/pkg/internal/infraolly/process/collect.go index d15ea82f9..406179fd4 100644 --- a/pkg/internal/infraolly/process/collect.go +++ b/pkg/internal/infraolly/process/collect.go @@ -18,12 +18,12 @@ import ( // Collector returns runtime information about the currently running processes type Collector struct { - ctx context.Context - cfg *Config - harvest *Harvester - cache *simplelru.LRU[int32, *cacheEntry] - log *slog.Logger - newPids *<-chan []request.Span + ctx context.Context + cfg *Config + harvest *Harvester + cache *simplelru.LRU[int32, *cacheEntry] + log *slog.Logger + newPids *<-chan []request.Span } // NewCollectorProvider creates and returns a new process Collector, given an agent context. @@ -34,12 +34,12 @@ func NewCollectorProvider(ctx context.Context, input *<-chan []request.Span, cfg harvest := newHarvester(cfg, cache) return (&Collector{ - ctx: ctx, - cfg: cfg, - harvest: harvest, - cache: cache, - log: pslog(), - newPids: input, + ctx: ctx, + cfg: cfg, + harvest: harvest, + cache: cache, + log: pslog(), + newPids: input, }).Run, nil } } diff --git a/pkg/internal/infraolly/process/harvest.go b/pkg/internal/infraolly/process/harvest.go index ac9cc6bed..0e6babc1b 100644 --- a/pkg/internal/infraolly/process/harvest.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -34,20 +34,11 @@ const ( RunModeUnprivileged = "unprivileged" ) -type PidMode string - -const ( - PidModeHost = "host" - PidModeUser = "user" -) - type Config struct { RunMode RunMode DisableZeroRSSFilter bool Rate time.Duration - - PidMode PidMode `env:"BEYLA_PID_MODE" envDefault:"root"` } func newHarvester(cfg *Config, cache *simplelru.LRU[int32, *cacheEntry]) *Harvester { diff --git a/pkg/internal/netolly/export/otel/expirer.go b/pkg/internal/netolly/export/otel/expirer.go index 516487467..b15332a96 100644 --- a/pkg/internal/netolly/export/otel/expirer.go +++ b/pkg/internal/netolly/export/otel/expirer.go @@ -118,7 +118,6 @@ func (ex *Expirer[Record, OT, Metric, VT]) ForRecord(r Record) Metric { } func (ex *Expirer[Record, OT, Metric, VT]) Collect(_ context.Context, observer OT) error { - //ex.log.Debug("invoking metrics collection") if old := ex.entries.DeleteExpired(); len(old) > 0 { ex.log.With("labelValues", old).Debug("deleting old OTEL metric") } diff --git a/test/integration/suites_test.go b/test/integration/suites_test.go index 6f4d47dec..91f321967 100644 --- a/test/integration/suites_test.go +++ b/test/integration/suites_test.go @@ -400,9 +400,9 @@ func TestSuite_Python(t *testing.T) { require.NoError(t, compose.Up()) t.Run("Python RED metrics", testREDMetricsPythonHTTP) t.Run("Python RED metrics with timeouts", testREDMetricsTimeoutPythonHTTP) - //t.Run("BPF pinning folder mounted", testBPFPinningMounted) - //require.NoError(t, compose.Close()) - //t.Run("BPF pinning folder unmounted", testBPFPinningUnmounted) + t.Run("BPF pinning folder mounted", testBPFPinningMounted) + require.NoError(t, compose.Close()) + t.Run("BPF pinning folder unmounted", testBPFPinningUnmounted) } func TestSuite_PythonTLS(t *testing.T) { From d703a084b3f5ab0244dfdcd76d497f2aadf59689 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Tue, 4 Jun 2024 12:49:47 +0200 Subject: [PATCH 19/27] merging otel exporters into single package --- pkg/internal/export/otel/common.go | 2 +- .../{netolly => }/export/otel/expirer.go | 0 .../{netolly => }/export/otel/expirer_test.go | 14 ++--- pkg/internal/export/otel/metrics.go | 20 +++---- .../metrics.go => export/otel/metrics_net.go} | 25 ++++---- .../otel/metrics_net_test.go} | 35 ++++++------ .../otel/metrics_proc.go} | 57 +++++++++---------- pkg/internal/export/otel/metrics_test.go | 2 - pkg/internal/export/otel/traces.go | 6 +- pkg/internal/infraolly/infra_pipe.go | 7 +-- pkg/internal/netolly/agent/pipeline.go | 4 +- 11 files changed, 82 insertions(+), 90 deletions(-) rename pkg/internal/{netolly => }/export/otel/expirer.go (100%) rename pkg/internal/{netolly => }/export/otel/expirer_test.go (93%) rename pkg/internal/{netolly/export/otel/metrics.go => export/otel/metrics_net.go} (84%) rename pkg/internal/{netolly/export/otel/metrics_test.go => export/otel/metrics_net_test.go} (70%) rename pkg/internal/{infraolly/export/otel/metrics.go => export/otel/metrics_proc.go} (75%) diff --git a/pkg/internal/export/otel/common.go b/pkg/internal/export/otel/common.go index a77221ebf..e0af1057c 100644 --- a/pkg/internal/export/otel/common.go +++ b/pkg/internal/export/otel/common.go @@ -58,7 +58,7 @@ var DefaultBuckets = Buckets{ RequestSizeHistogram: []float64{0, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192}, } -func ResourceAttrs(service svc.ID) *resource.Resource { +func getResourceAttrs(service svc.ID) *resource.Resource { attrs := []attribute.KeyValue{ semconv.ServiceName(service.Name), semconv.ServiceInstanceID(service.Instance), diff --git a/pkg/internal/netolly/export/otel/expirer.go b/pkg/internal/export/otel/expirer.go similarity index 100% rename from pkg/internal/netolly/export/otel/expirer.go rename to pkg/internal/export/otel/expirer.go diff --git a/pkg/internal/netolly/export/otel/expirer_test.go b/pkg/internal/export/otel/expirer_test.go similarity index 93% rename from pkg/internal/netolly/export/otel/expirer_test.go rename to pkg/internal/export/otel/expirer_test.go index 8936adc74..96f6a97c5 100644 --- a/pkg/internal/netolly/export/otel/expirer_test.go +++ b/pkg/internal/export/otel/expirer_test.go @@ -12,15 +12,15 @@ import ( "go.opentelemetry.io/otel/attribute" "github.com/grafana/beyla/pkg/internal/export/attributes" - "github.com/grafana/beyla/pkg/internal/export/otel" "github.com/grafana/beyla/pkg/internal/netolly/ebpf" "github.com/grafana/beyla/pkg/internal/pipe/global" "github.com/grafana/beyla/test/collector" ) -const timeout = 3 * time.Second +const timeout = 10 * time.Second func TestMetricsExpiration(t *testing.T) { + defer restoreEnvAfterExecution()() ctx, cancelCtx := context.WithCancel(context.Background()) defer cancelCtx() @@ -30,13 +30,13 @@ func TestMetricsExpiration(t *testing.T) { now := syncedClock{now: time.Now()} timeNow = now.Now - otelExporter, err := MetricsExporterProvider( - &global.ContextInfo{}, &MetricsConfig{ - Metrics: &otel.MetricsConfig{ + otelExporter, err := NetMetricsExporterProvider( + &global.ContextInfo{}, &NetMetricsConfig{ + Metrics: &MetricsConfig{ Interval: 50 * time.Millisecond, CommonEndpoint: otlp.ServerEndpoint, - MetricsProtocol: otel.ProtocolHTTPProtobuf, - Features: []string{otel.FeatureNetwork}, + MetricsProtocol: ProtocolHTTPProtobuf, + Features: []string{FeatureNetwork}, TTL: 3 * time.Minute, }, AttributeSelectors: attributes.Selection{ attributes.BeylaNetworkFlow.Section: attributes.InclusionLists{ diff --git a/pkg/internal/export/otel/metrics.go b/pkg/internal/export/otel/metrics.go index 193afde2b..db1da0a6e 100644 --- a/pkg/internal/export/otel/metrics.go +++ b/pkg/internal/export/otel/metrics.go @@ -143,12 +143,12 @@ func (m *MetricsConfig) ServiceGraphMetricsEnabled() bool { return slices.Contains(m.Features, FeatureGraph) } -func (m *MetricsConfig) AppMetricsEnabled() bool { +func (m *MetricsConfig) OTelMetricsEnabled() bool { return slices.Contains(m.Features, FeatureApplication) } func (m *MetricsConfig) Enabled() bool { - return m.EndpointEnabled() && (m.AppMetricsEnabled() || m.SpanMetricsEnabled() || m.ServiceGraphMetricsEnabled()) + return m.EndpointEnabled() && (m.OTelMetricsEnabled() || m.SpanMetricsEnabled() || m.ServiceGraphMetricsEnabled()) } // MetricsReporter implements the graph node that receives request.Span @@ -282,7 +282,7 @@ func newMetricsReporter( } func (mr *MetricsReporter) otelMetricOptions(mlog *slog.Logger) []metric.Option { - if !mr.cfg.AppMetricsEnabled() { + if !mr.cfg.OTelMetricsEnabled() { return []metric.Option{} } @@ -327,7 +327,7 @@ func (mr *MetricsReporter) graphMetricOptions(mlog *slog.Logger) []metric.Option } func (mr *MetricsReporter) setupOtelMeters(m *Metrics, meter instrument.Meter) error { - if !mr.cfg.AppMetricsEnabled() { + if !mr.cfg.OTelMetricsEnabled() { return nil } @@ -442,7 +442,7 @@ func (mr *MetricsReporter) setupGraphMeters(m *Metrics, meter instrument.Meter) func (mr *MetricsReporter) newMetricSet(service svc.ID) (*Metrics, error) { mlog := mlog().With("service", service) mlog.Debug("creating new Metrics reporter") - resources := ResourceAttrs(service) + resources := getResourceAttrs(service) opts := []metric.Option{ metric.WithResource(resources), @@ -464,9 +464,9 @@ func (mr *MetricsReporter) newMetricSet(service svc.ID) (*Metrics, error) { // time units for HTTP and GRPC durations are in seconds, according to the OTEL specification: // https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/metrics/semantic_conventions // TODO: set ExplicitBucketBoundaries here and in prometheus from the previous specification - meter := m.provider.Meter(ReporterName) + meter := m.provider.Meter(reporterName) var err error - if mr.cfg.AppMetricsEnabled() { + if mr.cfg.OTelMetricsEnabled() { err = mr.setupOtelMeters(&m, meter) if err != nil { return nil, err @@ -579,7 +579,7 @@ func otelHistogramConfig(metricName string, buckets []float64, useExponentialHis return metric.NewView( metric.Instrument{ Name: metricName, - Scope: instrumentation.Scope{Name: ReporterName}, + Scope: instrumentation.Scope{Name: reporterName}, }, metric.Stream{ Name: metricName, @@ -592,7 +592,7 @@ func otelHistogramConfig(metricName string, buckets []float64, useExponentialHis return metric.NewView( metric.Instrument{ Name: metricName, - Scope: instrumentation.Scope{Name: ReporterName}, + Scope: instrumentation.Scope{Name: reporterName}, }, metric.Stream{ Name: metricName, @@ -670,7 +670,7 @@ func (r *Metrics) record(span *request.Span, mr *MetricsReporter) { t := span.Timings() duration := t.End.Sub(t.RequestStart).Seconds() - if mr.cfg.AppMetricsEnabled() { + if mr.cfg.OTelMetricsEnabled() { switch span.Type { case request.EventTypeHTTP: // TODO: for more accuracy, there must be a way to set the metric time from the actual span end time diff --git a/pkg/internal/netolly/export/otel/metrics.go b/pkg/internal/export/otel/metrics_net.go similarity index 84% rename from pkg/internal/netolly/export/otel/metrics.go rename to pkg/internal/export/otel/metrics_net.go index 13ccd902c..565e1074c 100644 --- a/pkg/internal/netolly/export/otel/metrics.go +++ b/pkg/internal/export/otel/metrics_net.go @@ -17,22 +17,21 @@ import ( "github.com/grafana/beyla/pkg/internal/export/attributes" "github.com/grafana/beyla/pkg/internal/export/expire" - "github.com/grafana/beyla/pkg/internal/export/otel" "github.com/grafana/beyla/pkg/internal/netolly/ebpf" "github.com/grafana/beyla/pkg/internal/pipe/global" ) -type MetricsConfig struct { - Metrics *otel.MetricsConfig +type NetMetricsConfig struct { + Metrics *MetricsConfig AttributeSelectors attributes.Selection } -func (mc MetricsConfig) Enabled() bool { - return mc.Metrics != nil && mc.Metrics.EndpointEnabled() && slices.Contains(mc.Metrics.Features, otel.FeatureNetwork) +func (mc NetMetricsConfig) Enabled() bool { + return mc.Metrics != nil && mc.Metrics.EndpointEnabled() && slices.Contains(mc.Metrics.Features, FeatureNetwork) } -func mlog() *slog.Logger { - return slog.With("component", "flows.MetricsReporter") +func nmlog() *slog.Logger { + return slog.With("component", "otel.NetworkMetricsExporter") } func newResource() *resource.Resource { @@ -59,19 +58,19 @@ func newMeterProvider(res *resource.Resource, exporter *metric.Exporter, interva return meterProvider, nil } -type metricsExporter struct { +type netMetricsExporter struct { metrics *Expirer[*ebpf.Record, metric2.Int64Observer, *Counter, int64] clock *expire.CachedClock } -func MetricsExporterProvider(ctxInfo *global.ContextInfo, cfg *MetricsConfig) (pipe.FinalFunc[[]*ebpf.Record], error) { +func NetMetricsExporterProvider(ctxInfo *global.ContextInfo, cfg *NetMetricsConfig) (pipe.FinalFunc[[]*ebpf.Record], error) { if !cfg.Enabled() { // This node is not going to be instantiated. Let the pipes library just ignore it. return pipe.IgnoreFinal[[]*ebpf.Record](), nil } - log := mlog() + log := nmlog() log.Debug("instantiating network metrics exporter provider") - exporter, err := otel.InstantiateMetricsExporter(context.Background(), cfg.Metrics, log) + exporter, err := InstantiateMetricsExporter(context.Background(), cfg.Metrics, log) if err != nil { log.Error("", "error", err) return nil, err @@ -107,13 +106,13 @@ func MetricsExporterProvider(ctxInfo *global.ContextInfo, cfg *MetricsConfig) (p return nil, err } log.Debug("restricting attributes not in this list", "attributes", cfg.AttributeSelectors) - return (&metricsExporter{ + return (&netMetricsExporter{ metrics: expirer, clock: clock, }).Do, nil } -func (me *metricsExporter) Do(in <-chan []*ebpf.Record) { +func (me *netMetricsExporter) Do(in <-chan []*ebpf.Record) { for i := range in { me.clock.Update() for _, v := range i { diff --git a/pkg/internal/netolly/export/otel/metrics_test.go b/pkg/internal/export/otel/metrics_net_test.go similarity index 70% rename from pkg/internal/netolly/export/otel/metrics_test.go rename to pkg/internal/export/otel/metrics_net_test.go index 32189e71e..b1188a3cf 100644 --- a/pkg/internal/netolly/export/otel/metrics_test.go +++ b/pkg/internal/export/otel/metrics_net_test.go @@ -9,11 +9,11 @@ import ( "github.com/grafana/beyla/pkg/internal/export/attributes" attr "github.com/grafana/beyla/pkg/internal/export/attributes/names" - "github.com/grafana/beyla/pkg/internal/export/otel" "github.com/grafana/beyla/pkg/internal/netolly/ebpf" ) func TestMetricAttributes(t *testing.T) { + defer restoreEnvAfterExecution()() in := &ebpf.Record{ NetFlowRecordT: ebpf.NetFlowRecordT{ Id: ebpf.NetFlowId{ @@ -63,6 +63,7 @@ func TestMetricAttributes(t *testing.T) { } func TestMetricAttributes_Filter(t *testing.T) { + defer restoreEnvAfterExecution()() in := &ebpf.Record{ NetFlowRecordT: ebpf.NetFlowRecordT{ Id: ebpf.NetFlowId{ @@ -112,23 +113,23 @@ func TestMetricAttributes_Filter(t *testing.T) { } } -func TestMetricsConfig_Enabled(t *testing.T) { - assert.True(t, MetricsConfig{Metrics: &otel.MetricsConfig{ - Features: []string{otel.FeatureApplication, otel.FeatureNetwork}, CommonEndpoint: "foo"}}.Enabled()) - assert.True(t, MetricsConfig{Metrics: &otel.MetricsConfig{ - Features: []string{otel.FeatureNetwork, otel.FeatureApplication}, MetricsEndpoint: "foo"}}.Enabled()) - assert.True(t, MetricsConfig{Metrics: &otel.MetricsConfig{ - Features: []string{otel.FeatureNetwork}, Grafana: &otel.GrafanaOTLP{Submit: []string{"traces", "metrics"}, InstanceID: "33221"}}}.Enabled()) +func TestNetMetricsConfig_Enabled(t *testing.T) { + assert.True(t, NetMetricsConfig{Metrics: &MetricsConfig{ + Features: []string{FeatureApplication, FeatureNetwork}, CommonEndpoint: "foo"}}.Enabled()) + assert.True(t, NetMetricsConfig{Metrics: &MetricsConfig{ + Features: []string{FeatureNetwork, FeatureApplication}, MetricsEndpoint: "foo"}}.Enabled()) + assert.True(t, NetMetricsConfig{Metrics: &MetricsConfig{ + Features: []string{FeatureNetwork}, Grafana: &GrafanaOTLP{Submit: []string{"traces", "metrics"}, InstanceID: "33221"}}}.Enabled()) } -func TestMetricsConfig_Disabled(t *testing.T) { - var fa = []string{otel.FeatureApplication} - var fn = []string{otel.FeatureNetwork} - assert.False(t, MetricsConfig{Metrics: &otel.MetricsConfig{Features: fn}}.Enabled()) - assert.False(t, MetricsConfig{Metrics: &otel.MetricsConfig{Features: fn, Grafana: &otel.GrafanaOTLP{Submit: []string{"traces"}, InstanceID: "33221"}}}.Enabled()) - assert.False(t, MetricsConfig{Metrics: &otel.MetricsConfig{Features: fn, Grafana: &otel.GrafanaOTLP{Submit: []string{"metrics"}}}}.Enabled()) +func TestNetMetricsConfig_Disabled(t *testing.T) { + var fa = []string{FeatureApplication} + var fn = []string{FeatureNetwork} + assert.False(t, NetMetricsConfig{Metrics: &MetricsConfig{Features: fn}}.Enabled()) + assert.False(t, NetMetricsConfig{Metrics: &MetricsConfig{Features: fn, Grafana: &GrafanaOTLP{Submit: []string{"traces"}, InstanceID: "33221"}}}.Enabled()) + assert.False(t, NetMetricsConfig{Metrics: &MetricsConfig{Features: fn, Grafana: &GrafanaOTLP{Submit: []string{"metrics"}}}}.Enabled()) // network feature is not enabled - assert.False(t, MetricsConfig{Metrics: &otel.MetricsConfig{CommonEndpoint: "foo"}}.Enabled()) - assert.False(t, MetricsConfig{Metrics: &otel.MetricsConfig{MetricsEndpoint: "foo", Features: fa}}.Enabled()) - assert.False(t, MetricsConfig{Metrics: &otel.MetricsConfig{Grafana: &otel.GrafanaOTLP{Submit: []string{"traces", "metrics"}, InstanceID: "33221"}}}.Enabled()) + assert.False(t, NetMetricsConfig{Metrics: &MetricsConfig{CommonEndpoint: "foo"}}.Enabled()) + assert.False(t, NetMetricsConfig{Metrics: &MetricsConfig{MetricsEndpoint: "foo", Features: fa}}.Enabled()) + assert.False(t, NetMetricsConfig{Metrics: &MetricsConfig{Grafana: &GrafanaOTLP{Submit: []string{"traces", "metrics"}, InstanceID: "33221"}}}.Enabled()) } diff --git a/pkg/internal/infraolly/export/otel/metrics.go b/pkg/internal/export/otel/metrics_proc.go similarity index 75% rename from pkg/internal/infraolly/export/otel/metrics.go rename to pkg/internal/export/otel/metrics_proc.go index 9453051f1..17aa74c64 100644 --- a/pkg/internal/infraolly/export/otel/metrics.go +++ b/pkg/internal/export/otel/metrics_proc.go @@ -5,7 +5,6 @@ import ( "fmt" "log/slog" "slices" - "time" "github.com/mariomac/pipes/pipe" "go.opentelemetry.io/otel/attribute" @@ -14,52 +13,48 @@ import ( "github.com/grafana/beyla/pkg/internal/export/attributes" "github.com/grafana/beyla/pkg/internal/export/expire" - "github.com/grafana/beyla/pkg/internal/export/otel" "github.com/grafana/beyla/pkg/internal/infraolly/process" - otel2 "github.com/grafana/beyla/pkg/internal/netolly/export/otel" "github.com/grafana/beyla/pkg/internal/pipe/global" "github.com/grafana/beyla/pkg/internal/svc" ) -var timeNow = time.Now - -type MetricsConfig struct { - Metrics *otel.MetricsConfig +type ProcMetricsConfig struct { + Metrics *MetricsConfig AttributeSelectors attributes.Selection } -func (mc MetricsConfig) Enabled() bool { - return mc.Metrics != nil && mc.Metrics.EndpointEnabled() && slices.Contains(mc.Metrics.Features, otel.FeatureProcess) +func (mc *ProcMetricsConfig) Enabled() bool { + return mc.Metrics != nil && mc.Metrics.EndpointEnabled() && slices.Contains(mc.Metrics.Features, FeatureProcess) } -func mlog() *slog.Logger { +func pmlog() *slog.Logger { return slog.With("component", "otel.ProcessMetricsExporter") } type metricsExporter struct { ctx context.Context - cfg *MetricsConfig + cfg *ProcMetricsConfig clock *expire.CachedClock attributes *attributes.AttrSelector exporter metric.Exporter - reporters otel.ReporterPool[*Metrics] + reporters ReporterPool[*procMetrics] attrCPUTime []attributes.Field[*process.Status, attribute.KeyValue] } -type Metrics struct { +type procMetrics struct { ctx context.Context service svc.ID provider *metric.MeterProvider - cpuTime *otel2.Expirer[*process.Status, metric2.Float64Observer, *otel2.Gauge, float64] + cpuTime *Expirer[*process.Status, metric2.Float64Observer, *Gauge, float64] } func ProcessMetricsExporterProvider( ctx context.Context, ctxInfo *global.ContextInfo, - cfg *MetricsConfig, + cfg *ProcMetricsConfig, ) pipe.FinalProvider[[]*process.Status] { return func() (pipe.FinalFunc[[]*process.Status], error) { if !cfg.Enabled() { @@ -73,11 +68,11 @@ func ProcessMetricsExporterProvider( func newProcessMetricsExporter( ctx context.Context, ctxInfo *global.ContextInfo, - cfg *MetricsConfig, + cfg *ProcMetricsConfig, ) (pipe.FinalFunc[[]*process.Status], error) { - otel.SetupInternalOTELSDKLogger(cfg.Metrics.SDKLogLevel) + SetupInternalOTELSDKLogger(cfg.Metrics.SDKLogLevel) - log := mlog() + log := pmlog() log.Debug("instantiating process metrics exporter provider") attrProv, err := attributes.NewAttrSelector(ctxInfo.MetricAttributeGroups, cfg.AttributeSelectors) @@ -95,8 +90,8 @@ func newProcessMetricsExporter( mr.attrCPUTime = attributes.OpenTelemetryGetters( process.OTELGetters, mr.attributes.For(attributes.ProcessCPUUtilization)) - mr.reporters = otel.NewReporterPool[*Metrics](cfg.Metrics.ReportersCacheLen, - func(id svc.UID, v *Metrics) { + mr.reporters = NewReporterPool[*procMetrics](cfg.Metrics.ReportersCacheLen, + func(id svc.UID, v *procMetrics) { llog := log.With("service", id) llog.Debug("evicting metrics reporter from cache") go func() { @@ -106,7 +101,7 @@ func newProcessMetricsExporter( }() }, mr.newMetricSet) - mr.exporter, err = otel.InstantiateMetricsExporter(ctx, cfg.Metrics, log) + mr.exporter, err = InstantiateMetricsExporter(ctx, cfg.Metrics, log) if err != nil { log.Error("instantiating metrics exporter", "error", err) return nil, err @@ -115,25 +110,25 @@ func newProcessMetricsExporter( return mr.Do, nil } -func (me *metricsExporter) newMetricSet(service svc.ID) (*Metrics, error) { - log := mlog().With("service", service) +func (me *metricsExporter) newMetricSet(service svc.ID) (*procMetrics, error) { + log := pmlog().With("service", service) log.Debug("creating new Metrics exporter") - resources := otel.ResourceAttrs(service) + resources := getResourceAttrs(service) opts := []metric.Option{ metric.WithResource(resources), metric.WithReader(metric.NewPeriodicReader(me.exporter, metric.WithInterval(me.cfg.Metrics.Interval))), } - m := Metrics{ + m := procMetrics{ ctx: me.ctx, service: service, provider: metric.NewMeterProvider(opts...), } - meter := m.provider.Meter(otel.ReporterName) - m.cpuTime = otel2.NewExpirer[*process.Status, metric2.Float64Observer]( - otel2.NewGauge, + meter := m.provider.Meter(reporterName) + m.cpuTime = NewExpirer[*process.Status, metric2.Float64Observer]( + NewGauge, me.attrCPUTime, timeNow, me.cfg.Metrics.TTL, @@ -153,7 +148,7 @@ func (me *metricsExporter) newMetricSet(service svc.ID) (*Metrics, error) { func (me *metricsExporter) Do(in <-chan []*process.Status) { var lastSvcUID svc.UID - var reporter *Metrics + var reporter *procMetrics for i := range in { me.clock.Update() for _, s := range i { @@ -168,14 +163,14 @@ func (me *metricsExporter) Do(in <-chan []*process.Status) { // TODO: precalculate For UUID lm, err := me.reporters.For(*s.Service) if err != nil { - mlog().Error("unexpected error creating OTEL resource. Ignoring metric", + pmlog().Error("unexpected error creating OTEL resource. Ignoring metric", err, "service", s.Service) continue } lastSvcUID = s.Service.UID reporter = lm } - mlog().Debug("reporting data for record", "record", s) + pmlog().Debug("reporting data for record", "record", s) // TODO: support user/system/other reporter.cpuTime.ForRecord(s).Set(s.CPUPercent) } diff --git a/pkg/internal/export/otel/metrics_test.go b/pkg/internal/export/otel/metrics_test.go index fd1ef5fae..1d54642f8 100644 --- a/pkg/internal/export/otel/metrics_test.go +++ b/pkg/internal/export/otel/metrics_test.go @@ -20,8 +20,6 @@ import ( "github.com/grafana/beyla/pkg/internal/request" ) -const timeout = 10 * time.Second - var fakeMux = sync.Mutex{} func TestHTTPMetricsEndpointOptions(t *testing.T) { diff --git a/pkg/internal/export/otel/traces.go b/pkg/internal/export/otel/traces.go index 88627db4f..099096b09 100644 --- a/pkg/internal/export/otel/traces.go +++ b/pkg/internal/export/otel/traces.go @@ -45,7 +45,7 @@ func tlog() *slog.Logger { return slog.With("component", "otel.TracesReporter") } -const ReporterName = "github.com/grafana/beyla" +const reporterName = "github.com/grafana/beyla" type TracesConfig struct { CommonEndpoint string `yaml:"-" env:"OTEL_EXPORTER_OTLP_ENDPOINT"` @@ -323,8 +323,8 @@ func GenerateTraces(span *request.Span, userAttrs map[attr.Name]struct{}) ptrace traces := ptrace.NewTraces() rs := traces.ResourceSpans().AppendEmpty() ss := rs.ScopeSpans().AppendEmpty() - resourceAttrs := attrsToMap(ResourceAttrs(span.ServiceID).Attributes()) - resourceAttrs.PutStr(string(semconv.OTelLibraryNameKey), ReporterName) + resourceAttrs := attrsToMap(getResourceAttrs(span.ServiceID).Attributes()) + resourceAttrs.PutStr(string(semconv.OTelLibraryNameKey), reporterName) resourceAttrs.CopyTo(rs.Resource().Attributes()) traceID := pcommon.TraceID(span.TraceID) diff --git a/pkg/internal/infraolly/infra_pipe.go b/pkg/internal/infraolly/infra_pipe.go index b401efc56..5984e9b3f 100644 --- a/pkg/internal/infraolly/infra_pipe.go +++ b/pkg/internal/infraolly/infra_pipe.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/beyla/pkg/beyla" "github.com/grafana/beyla/pkg/internal/export/otel" - otel2 "github.com/grafana/beyla/pkg/internal/infraolly/export/otel" "github.com/grafana/beyla/pkg/internal/infraolly/process" "github.com/grafana/beyla/pkg/internal/pipe/global" "github.com/grafana/beyla/pkg/internal/request" @@ -34,7 +33,7 @@ func (sp *subPipeline) Connect() { // the sub-pipe is enabled only if there is a metrics exporter enabled, // and both the "application" and "application_process" features are enabled func isSubPipeEnabled(cfg *beyla.Config) bool { - return (cfg.Metrics.EndpointEnabled() && cfg.Metrics.AppMetricsEnabled() && + return (cfg.Metrics.EndpointEnabled() && cfg.Metrics.OTelMetricsEnabled() && slices.Contains(cfg.Metrics.Features, otel.FeatureProcess)) || (cfg.Prometheus.EndpointEnabled() && cfg.Prometheus.AppMetricsEnabled() && slices.Contains(cfg.Prometheus.Features, otel.FeatureProcess)) @@ -51,8 +50,8 @@ func SubPipelineProvider(ctx context.Context, ctxInfo *global.ContextInfo, cfg * var connector <-chan []request.Span = connectorChan nb := pipe.NewBuilder(&subPipeline{}, pipe.ChannelBufferLen(cfg.ChannelBufferLen)) pipe.AddStartProvider(nb, collector, process.NewCollectorProvider(ctx, &connector, &cfg.Processes)) - pipe.AddFinalProvider(nb, otelExport, otel2.ProcessMetricsExporterProvider(ctx, ctxInfo, - &otel2.MetricsConfig{ + pipe.AddFinalProvider(nb, otelExport, otel.ProcessMetricsExporterProvider(ctx, ctxInfo, + &otel.ProcMetricsConfig{ Metrics: &cfg.Metrics, AttributeSelectors: cfg.Attributes.Select, })) diff --git a/pkg/internal/netolly/agent/pipeline.go b/pkg/internal/netolly/agent/pipeline.go index f2c022e6e..e18b048d4 100644 --- a/pkg/internal/netolly/agent/pipeline.go +++ b/pkg/internal/netolly/agent/pipeline.go @@ -5,10 +5,10 @@ import ( "github.com/mariomac/pipes/pipe" + "github.com/grafana/beyla/pkg/internal/export/otel" "github.com/grafana/beyla/pkg/internal/filter" "github.com/grafana/beyla/pkg/internal/netolly/ebpf" "github.com/grafana/beyla/pkg/internal/netolly/export" - "github.com/grafana/beyla/pkg/internal/netolly/export/otel" "github.com/grafana/beyla/pkg/internal/netolly/export/prom" "github.com/grafana/beyla/pkg/internal/netolly/flow" "github.com/grafana/beyla/pkg/internal/netolly/transform/cidr" @@ -133,7 +133,7 @@ func (f *Flows) pipelineBuilder(ctx context.Context) (*pipe.Builder[*FlowsPipeli // whether each node is going to be instantiated or just ignored. f.cfg.Attributes.Select.Normalize() pipe.AddFinalProvider(pb, otelExport, func() (pipe.FinalFunc[[]*ebpf.Record], error) { - return otel.MetricsExporterProvider(f.ctxInfo, &otel.MetricsConfig{ + return otel.NetMetricsExporterProvider(f.ctxInfo, &otel.NetMetricsConfig{ Metrics: &f.cfg.Metrics, AttributeSelectors: f.cfg.Attributes.Select, }) From 1efa6c2d505826452eddcc0abf9ae8b507c041f3 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Tue, 4 Jun 2024 13:01:00 +0200 Subject: [PATCH 20/27] fix harvest test --- pkg/internal/infraolly/process/harvest_test.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pkg/internal/infraolly/process/harvest_test.go b/pkg/internal/infraolly/process/harvest_test.go index 92d30f5d3..c6e9da48d 100644 --- a/pkg/internal/infraolly/process/harvest_test.go +++ b/pkg/internal/infraolly/process/harvest_test.go @@ -18,6 +18,8 @@ import ( "github.com/mariomac/guara/pkg/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/grafana/beyla/pkg/internal/svc" ) func TestLinuxHarvester_IsPrivileged(t *testing.T) { @@ -35,7 +37,7 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { h := newHarvester(&Config{RunMode: c.mode}, cache) // If not privileged, it is expected to not report neither FDs nor IO counters - status, err := h.Do(int32(os.Getpid()), nil) + status, err := h.Do(&svc.ID{ProcPID: int32(os.Getpid())}) require.NoError(t, err) if c.privileged { assert.NotZero(t, status.FdCount) @@ -54,7 +56,7 @@ func TestLinuxHarvester_Do(t *testing.T) { h := newHarvester(&Config{}, cache) // When retrieving for a given process status (e.g. the current testing executable) - status, err := h.Do(int32(os.Getpid()), nil) + status, err := h.Do(&svc.ID{ProcPID: int32(os.Getpid())}) // It returns the corresponding process status with valid data require.NoError(t, err) @@ -90,7 +92,7 @@ func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process status (e.g. the current testing executable) - status, err := h.Do(int32(cmd.Process.Pid), nil) + status, err := h.Do(&svc.ID{ProcPID: int32(cmd.Process.Pid)}) // It returns the corresponding Command line without stripping arguments require.NoError(t, err) @@ -114,7 +116,7 @@ func TestLinuxHarvester_Do_StripCommandLine(t *testing.T) { test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process status (e.g. the current testing executable) - status, err := h.Do(int32(cmd.Process.Pid), nil) + status, err := h.Do(&svc.ID{ProcPID: int32(cmd.Process.Pid)}) // It returns the corresponding Command line without stripping arguments require.NoError(t, err) @@ -134,7 +136,7 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentCmd(t *testing.T) { h := newHarvester(&Config{}, cache) // When the process is harvested - status, err := h.Do(currentPid, nil) + status, err := h.Do(&svc.ID{ProcPID: currentPid}) require.NoError(t, err) // The status is updated @@ -152,7 +154,7 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentPid(t *testing.T) { h := newHarvester(&Config{}, cache) // When the process is harvested - status, err := h.Do(currentPid, nil) + status, err := h.Do(&svc.ID{ProcPID: currentPid}) require.NoError(t, err) // The status is updated From f9cc569664f3f28e40e4ead7a834656f78f74306 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Tue, 4 Jun 2024 17:47:36 +0200 Subject: [PATCH 21/27] Reorganized code --- devdocs/pipeline-map.md | 6 - pkg/beyla/config.go | 8 +- pkg/beyla/config_test.go | 6 +- pkg/internal/export/otel/common.go | 33 ++++- pkg/internal/export/otel/expirer.go | 130 ++++++++++-------- pkg/internal/export/otel/metrics.go | 30 ++-- pkg/internal/export/otel/metrics_net.go | 1 + pkg/internal/export/otel/metrics_proc.go | 67 ++++----- pkg/internal/export/otel/traces.go | 2 +- pkg/internal/export/prom/prom.go | 16 +-- pkg/internal/infraolly/process/cache.go | 20 --- pkg/internal/infraolly/process/collect.go | 26 +++- pkg/internal/infraolly/process/harvest.go | 64 +++------ .../infraolly/process/harvest_test.go | 41 +++--- .../{infra_pipe.go => process/pipeline.go} | 25 ++-- pkg/internal/infraolly/process/snapshot.go | 24 ++-- pkg/internal/infraolly/process/status.go | 29 ++++ .../infraolly/process/status_getters.go | 32 ----- pkg/internal/pipe/instrumenter.go | 8 +- test/integration/docker-compose-python.yml | 3 +- 20 files changed, 267 insertions(+), 304 deletions(-) delete mode 100644 pkg/internal/infraolly/process/cache.go rename pkg/internal/infraolly/{infra_pipe.go => process/pipeline.go} (67%) delete mode 100644 pkg/internal/infraolly/process/status_getters.go diff --git a/devdocs/pipeline-map.md b/devdocs/pipeline-map.md index 9793bacfc..d39043666 100644 --- a/devdocs/pipeline-map.md +++ b/devdocs/pipeline-map.md @@ -22,8 +22,6 @@ flowchart TD TA(TraceAttacher) -.-> EBPF1(ebpf.Tracer) TA -.-> |creates one per executable| EBPF2(ebpf.Tracer) TA -.-> EBPF3(ebpf.Tracer) - ET --> PN - PN(ProcessNotifier) end subgraph Decoration and forwarding pipeline EBPF1 -.-> TR @@ -43,10 +41,6 @@ flowchart TD KDB(KubeDatabase):::optional <-.- | Aggregated & indexed Pod info | KD IF("Informer
(Kube API)"):::optional -.-> |Pods & ReplicaSets status| KDB IF -.-> |new Kube objects| KWE - subgraph Process pipeline - PN -.-> |PIDs map| PS - PS(ProcessSampler) - end ``` ## Network metrics pipeline diff --git a/pkg/beyla/config.go b/pkg/beyla/config.go index 3036aec11..31b3b99cd 100644 --- a/pkg/beyla/config.go +++ b/pkg/beyla/config.go @@ -100,9 +100,9 @@ var DefaultConfig = Config{ }, Routes: &transform.RoutesConfig{}, NetworkFlows: defaultNetworkConfig, - Processes: process.Config{ - RunMode: process.RunModeRoot, - Rate: 5 * time.Second, + Processes: process.CollectConfig{ + RunMode: process.RunModePrivileged, + Interval: 5 * time.Second, }, } @@ -156,7 +156,7 @@ type Config struct { // Processes metrics for application. They will be only enabled if there is a metrics exporter enabled, // and both the "application" and "application_process" features are enabled - Processes process.Config `yaml:"processes"` + Processes process.CollectConfig `yaml:"processes"` // Grafana Agent specific configuration TracesReceiver TracesReceiverConfig `yaml:"-"` diff --git a/pkg/beyla/config_test.go b/pkg/beyla/config_test.go index 3d678af2c..c38b51a6c 100644 --- a/pkg/beyla/config_test.go +++ b/pkg/beyla/config_test.go @@ -166,9 +166,9 @@ network: CacheLen: 1024, CacheTTL: 5 * time.Minute, }, - Processes: process.Config{ - RunMode: process.RunModeRoot, - Rate: 5 * time.Second, + Processes: process.CollectConfig{ + RunMode: process.RunModePrivileged, + Interval: 5 * time.Second, }, }, cfg) } diff --git a/pkg/internal/export/otel/common.go b/pkg/internal/export/otel/common.go index e0af1057c..e94707e76 100644 --- a/pkg/internal/export/otel/common.go +++ b/pkg/internal/export/otel/common.go @@ -58,7 +58,7 @@ var DefaultBuckets = Buckets{ RequestSizeHistogram: []float64{0, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192}, } -func getResourceAttrs(service svc.ID) *resource.Resource { +func getResourceAttrs(service *svc.ID) *resource.Resource { attrs := []attribute.KeyValue{ semconv.ServiceName(service.Name), semconv.ServiceInstanceID(service.Instance), @@ -87,7 +87,10 @@ func getResourceAttrs(service svc.ID) *resource.Resource { type ReporterPool[T any] struct { pool *simplelru.LRU[svc.UID, T] - itemConstructor func(svc.ID) (T, error) + itemConstructor func(*svc.ID) (T, error) + + lastReporter T + lastService *svc.ID } // NewReporterPool creates a ReporterPool instance given a cache length, @@ -97,7 +100,7 @@ type ReporterPool[T any] struct { func NewReporterPool[T any]( cacheLen int, callback simplelru.EvictCallback[svc.UID, T], - itemConstructor func(id svc.ID) (T, error), + itemConstructor func(id *svc.ID) (T, error), ) ReporterPool[T] { pool, _ := simplelru.NewLRU[svc.UID, T](cacheLen, callback) return ReporterPool[T]{pool: pool, itemConstructor: itemConstructor} @@ -105,14 +108,34 @@ func NewReporterPool[T any]( // For retrieves the associated item for the given service name, or // creates a new one if it does not exist -func (rp *ReporterPool[T]) For(service svc.ID) (T, error) { +func (rp *ReporterPool[T]) For(service *svc.ID) (T, error) { + // optimization: do not query the resources' cache if the + // previously processed span belongs to the same service name + // as the current. + // This will save querying OTEL resource reporters when there is + // only a single instrumented process. + // In multi-process tracing, this is likely to happen as most + // tracers group traces belonging to the same service in the same slice. + if rp.lastService == nil || service.UID != rp.lastService.UID { + lm, err := rp.get(service) + if err != nil { + var t T + return t, err + } + rp.lastService = service + rp.lastReporter = lm + } + return rp.lastReporter, nil +} + +func (rp *ReporterPool[T]) get(service *svc.ID) (T, error) { if m, ok := rp.pool.Get(service.UID); ok { return m, nil } m, err := rp.itemConstructor(service) if err != nil { var t T - return t, fmt.Errorf("creating resource for service %q: %w", &service, err) + return t, fmt.Errorf("creating resource for service %q: %w", service, err) } rp.pool.Add(service.UID, m) return m, nil diff --git a/pkg/internal/export/otel/expirer.go b/pkg/internal/export/otel/expirer.go index b15332a96..b41fbc8bb 100644 --- a/pkg/internal/export/otel/expirer.go +++ b/pkg/internal/export/otel/expirer.go @@ -21,93 +21,58 @@ func plog() *slog.Logger { return slog.With("component", "otel.Expirer") } -type loader[T any] interface { +// dataPoint implements a metric value of a given type, +// for a set of attributes +// Example of implementers: Gauge and Counter +type dataPoint[T any] interface { + // Load the current value for a given set of attributes Load() T + // Attributes return the attributes of the current dataPoint Attributes() attribute.Set - SetAttributes(attribute.Set) } +// observer records measurements for a given metric type type observer[T any] interface { Observe(T, ...metric.ObserveOption) } -// Expirer drops metrics from labels that haven't been updated during a given timeout +// Expirer drops metrics from labels that haven't been updated during a given timeout. +// It has multiple generic types to allow it working with different dataPoints (Gauge, Counter...) +// and different types of data (int, float...). // Record: type of the record that holds the metric data request.Span, ebpf.Record, process.Status... -// Metric: type of the metric kind: Counter, Gauge... -// VT: type of the metric value: int, float... -type Expirer[Record any, OT observer[VT], Metric loader[VT], VT any] struct { +// Metric: type of the dataPoint kind: Counter, Gauge... +// VT: type of the value inside the datapoint: int, float64... +type Expirer[Record any, OT observer[VT], Metric dataPoint[VT], VT any] struct { instancer func(set attribute.Set) Metric attrs []attributes.Field[Record, attribute.KeyValue] entries *expire.ExpiryMap[Metric] log *slog.Logger } -type metricAttributes struct { - attributes attribute.Set -} - -func (g *metricAttributes) Attributes() attribute.Set { - return g.attributes -} - -func (g *metricAttributes) SetAttributes(a attribute.Set) { - g.attributes = a -} - -type Counter struct { - metricAttributes - val atomic.Int64 -} - -func NewCounter(attributes attribute.Set) *Counter { - return &Counter{metricAttributes: metricAttributes{attributes: attributes}} -} -func (g *Counter) Load() int64 { - return g.val.Load() -} - -func (g *Counter) Add(v int64) { - g.val.Add(v) -} - -type Gauge struct { - metricAttributes - // Go standard library does not provide atomic packages so we need to - // store the float as bytes and then convert it with the math package - floatBits uint64 -} - -func NewGauge(attributes attribute.Set) *Gauge { - return &Gauge{metricAttributes: metricAttributes{attributes: attributes}} -} - -func (g *Gauge) Load() float64 { - return math.Float64frombits(atomic.LoadUint64(&g.floatBits)) -} - -func (g *Gauge) Set(val float64) { - atomic.StoreUint64(&g.floatBits, math.Float64bits(val)) -} - -// NewExpirer creates a metric that wraps a Counter. Its labeled instances are dropped -// if they haven't been updated during the last timeout period -func NewExpirer[Record any, OT observer[VT], Metric loader[VT], VT any]( +// NewExpirer creates an expirer that wraps data points of a given type. Its labeled instances are dropped +// if they haven't been updated during the last timeout period. +// Arguments: +// - instancer: the constructor of each datapoint object (e.g. NewCounter, NewGauge...) +// - attrs: attributes for that given data point +// - clock: function that provides the current time +// - ttl: time to live of the datapoints whose attribute sets haven't been updated +func NewExpirer[Record any, OT observer[VT], Metric dataPoint[VT], VT any]( instancer func(set attribute.Set) Metric, attrs []attributes.Field[Record, attribute.KeyValue], clock expire.Clock, - expireTime time.Duration, + ttl time.Duration, ) *Expirer[Record, OT, Metric, VT] { exp := Expirer[Record, OT, Metric, VT]{ instancer: instancer, attrs: attrs, - entries: expire.NewExpiryMap[Metric](clock, expireTime), + entries: expire.NewExpiryMap[Metric](clock, ttl), } exp.log = plog().With("type", fmt.Sprintf("%T", exp)) return &exp } -// ForRecord returns the Counter for the given eBPF record. If that record -// s accessed for the first time, a new Counter is created. +// ForRecord returns the data point for the given eBPF record. If that record +// s accessed for the first time, a new data point is created. // If not, a cached copy is returned and the "last access" cache time is updated. func (ex *Expirer[Record, OT, Metric, VT]) ForRecord(r Record) Metric { recordAttrs, attrValues := ex.recordAttributes(r) @@ -141,3 +106,48 @@ func (ex *Expirer[Record, OT, Metric, VT]) recordAttributes(m Record) (attribute return attribute.NewSet(keyVals...), vals } + +type metricAttributes struct { + attributes attribute.Set +} + +func (g *metricAttributes) Attributes() attribute.Set { + return g.attributes +} + +// Counter data point type +type Counter struct { + metricAttributes + val atomic.Int64 +} + +func NewCounter(attributes attribute.Set) *Counter { + return &Counter{metricAttributes: metricAttributes{attributes: attributes}} +} +func (g *Counter) Load() int64 { + return g.val.Load() +} + +func (g *Counter) Add(v int64) { + g.val.Add(v) +} + +// Gauge data point type +type Gauge struct { + metricAttributes + // Go standard library does not provide atomic packages so we need to + // store the float as bytes and then convert it with the math package + floatBits uint64 +} + +func NewGauge(attributes attribute.Set) *Gauge { + return &Gauge{metricAttributes: metricAttributes{attributes: attributes}} +} + +func (g *Gauge) Load() float64 { + return math.Float64frombits(atomic.LoadUint64(&g.floatBits)) +} + +func (g *Gauge) Set(val float64) { + atomic.StoreUint64(&g.floatBits, math.Float64bits(val)) +} diff --git a/pkg/internal/export/otel/metrics.go b/pkg/internal/export/otel/metrics.go index db1da0a6e..9d2d9ad45 100644 --- a/pkg/internal/export/otel/metrics.go +++ b/pkg/internal/export/otel/metrics.go @@ -176,7 +176,7 @@ type MetricsReporter struct { // There is a Metrics instance for each service/process instrumented by Beyla. type Metrics struct { ctx context.Context - service svc.ID + service *svc.ID provider *metric.MeterProvider httpDuration instrument.Float64Histogram @@ -439,7 +439,7 @@ func (mr *MetricsReporter) setupGraphMeters(m *Metrics, meter instrument.Meter) return nil } -func (mr *MetricsReporter) newMetricSet(service svc.ID) (*Metrics, error) { +func (mr *MetricsReporter) newMetricSet(service *svc.ID) (*Metrics, error) { mlog := mlog().With("service", service) mlog.Debug("creating new Metrics reporter") resources := getResourceAttrs(service) @@ -603,7 +603,7 @@ func otelHistogramConfig(metricName string, buckets []float64, useExponentialHis } -func (mr *MetricsReporter) metricResourceAttributes(service svc.ID) attribute.Set { +func (mr *MetricsReporter) metricResourceAttributes(service *svc.ID) attribute.Set { attrs := []attribute.KeyValue{ request.ServiceMetric(service.Name), semconv.ServiceInstanceID(service.Instance), @@ -726,8 +726,6 @@ func (r *Metrics) record(span *request.Span, mr *MetricsReporter) { } func (mr *MetricsReporter) reportMetrics(input <-chan []request.Span) { - var lastSvcUID svc.UID - var reporter *Metrics for spans := range input { for i := range spans { s := &spans[i] @@ -736,23 +734,11 @@ func (mr *MetricsReporter) reportMetrics(input <-chan []request.Span) { if s.IgnoreSpan == request.IgnoreMetrics { continue } - - // optimization: do not query the resources' cache if the - // previously processed span belongs to the same service name - // as the current. - // This will save querying OTEL resource reporters when there is - // only a single instrumented process. - // In multi-process tracing, this is likely to happen as most - // tracers group traces belonging to the same service in the same slice. - if s.ServiceID.UID != lastSvcUID || reporter == nil { - lm, err := mr.reporters.For(s.ServiceID) - if err != nil { - mlog().Error("unexpected error creating OTEL resource. Ignoring metric", - err, "service", s.ServiceID) - continue - } - lastSvcUID = s.ServiceID.UID - reporter = lm + reporter, err := mr.reporters.For(&s.ServiceID) + if err != nil { + mlog().Error("unexpected error creating OTEL resource. Ignoring metric", + err, "service", s.ServiceID) + continue } reporter.record(s, mr) } diff --git a/pkg/internal/export/otel/metrics_net.go b/pkg/internal/export/otel/metrics_net.go index 565e1074c..ed3577f45 100644 --- a/pkg/internal/export/otel/metrics_net.go +++ b/pkg/internal/export/otel/metrics_net.go @@ -21,6 +21,7 @@ import ( "github.com/grafana/beyla/pkg/internal/pipe/global" ) +// NetMetricsConfig extends MetricsConfig for Network Metrics type NetMetricsConfig struct { Metrics *MetricsConfig AttributeSelectors attributes.Selection diff --git a/pkg/internal/export/otel/metrics_proc.go b/pkg/internal/export/otel/metrics_proc.go index 17aa74c64..6c3c8a363 100644 --- a/pkg/internal/export/otel/metrics_proc.go +++ b/pkg/internal/export/otel/metrics_proc.go @@ -18,40 +18,41 @@ import ( "github.com/grafana/beyla/pkg/internal/svc" ) +// ProcMetricsConfig extends MetricsConfig for process metrics type ProcMetricsConfig struct { Metrics *MetricsConfig AttributeSelectors attributes.Selection } func (mc *ProcMetricsConfig) Enabled() bool { - return mc.Metrics != nil && mc.Metrics.EndpointEnabled() && slices.Contains(mc.Metrics.Features, FeatureProcess) + return mc.Metrics != nil && mc.Metrics.EndpointEnabled() && mc.Metrics.OTelMetricsEnabled() && + slices.Contains(mc.Metrics.Features, FeatureProcess) } func pmlog() *slog.Logger { - return slog.With("component", "otel.ProcessMetricsExporter") + return slog.With("component", "otel.ProcMetricsExporter") } -type metricsExporter struct { +type procMetricsExporter struct { ctx context.Context cfg *ProcMetricsConfig clock *expire.CachedClock - attributes *attributes.AttrSelector - exporter metric.Exporter - reporters ReporterPool[*procMetrics] + exporter metric.Exporter + reporters ReporterPool[*procMetrics] attrCPUTime []attributes.Field[*process.Status, attribute.KeyValue] } type procMetrics struct { ctx context.Context - service svc.ID + service *svc.ID provider *metric.MeterProvider cpuTime *Expirer[*process.Status, metric2.Float64Observer, *Gauge, float64] } -func ProcessMetricsExporterProvider( +func ProcMetricsExporterProvider( ctx context.Context, ctxInfo *global.ContextInfo, cfg *ProcMetricsConfig, @@ -61,11 +62,11 @@ func ProcessMetricsExporterProvider( // This node is not going to be instantiated. Let the pipes library just ignore it. return pipe.IgnoreFinal[[]*process.Status](), nil } - return newProcessMetricsExporter(ctx, ctxInfo, cfg) + return newProcMetricsExporter(ctx, ctxInfo, cfg) } } -func newProcessMetricsExporter( +func newProcMetricsExporter( ctx context.Context, ctxInfo *global.ContextInfo, cfg *ProcMetricsConfig, @@ -75,21 +76,20 @@ func newProcessMetricsExporter( log := pmlog() log.Debug("instantiating process metrics exporter provider") + // only user-provided attributes (or default set) will decorate the metrics attrProv, err := attributes.NewAttrSelector(ctxInfo.MetricAttributeGroups, cfg.AttributeSelectors) if err != nil { - return nil, fmt.Errorf("process OTEL exporter attributes enable: %w", err) + return nil, fmt.Errorf("process OTEL exporter attributes: %w", err) } - mr := &metricsExporter{ - clock: expire.NewCachedClock(timeNow), - ctx: ctx, - cfg: cfg, - attributes: attrProv, + mr := &procMetricsExporter{ + ctx: ctx, + cfg: cfg, + clock: expire.NewCachedClock(timeNow), + attrCPUTime: attributes.OpenTelemetryGetters( + process.OTELGetters, attrProv.For(attributes.ProcessCPUUtilization)), } - mr.attrCPUTime = attributes.OpenTelemetryGetters( - process.OTELGetters, mr.attributes.For(attributes.ProcessCPUUtilization)) - mr.reporters = NewReporterPool[*procMetrics](cfg.Metrics.ReportersCacheLen, func(id svc.UID, v *procMetrics) { llog := log.With("service", id) @@ -110,7 +110,7 @@ func newProcessMetricsExporter( return mr.Do, nil } -func (me *metricsExporter) newMetricSet(service svc.ID) (*procMetrics, error) { +func (me *procMetricsExporter) newMetricSet(service *svc.ID) (*procMetrics, error) { log := pmlog().With("service", service) log.Debug("creating new Metrics exporter") resources := getResourceAttrs(service) @@ -146,29 +146,16 @@ func (me *metricsExporter) newMetricSet(service svc.ID) (*procMetrics, error) { return &m, nil } -func (me *metricsExporter) Do(in <-chan []*process.Status) { - var lastSvcUID svc.UID - var reporter *procMetrics +// Do reads all the process status data points and create the metrics accordingly +func (me *procMetricsExporter) Do(in <-chan []*process.Status) { for i := range in { me.clock.Update() for _, s := range i { - // optimization: do not query the resources' cache if the - // previously processed span belongs to the same service name - // as the current. - // This will save querying OTEL resource reporters when there is - // only a single instrumented process. - // In multi-process tracing, this is likely to happen as most - // tracers group traces belonging to the same service in the same slice. - if s.Service.UID != lastSvcUID || reporter == nil { - // TODO: precalculate For UUID - lm, err := me.reporters.For(*s.Service) - if err != nil { - pmlog().Error("unexpected error creating OTEL resource. Ignoring metric", - err, "service", s.Service) - continue - } - lastSvcUID = s.Service.UID - reporter = lm + reporter, err := me.reporters.For(s.Service) + if err != nil { + pmlog().Error("unexpected error creating OTEL resource. Ignoring metric", + err, "service", s.Service) + continue } pmlog().Debug("reporting data for record", "record", s) // TODO: support user/system/other diff --git a/pkg/internal/export/otel/traces.go b/pkg/internal/export/otel/traces.go index 099096b09..fbf07be22 100644 --- a/pkg/internal/export/otel/traces.go +++ b/pkg/internal/export/otel/traces.go @@ -323,7 +323,7 @@ func GenerateTraces(span *request.Span, userAttrs map[attr.Name]struct{}) ptrace traces := ptrace.NewTraces() rs := traces.ResourceSpans().AppendEmpty() ss := rs.ScopeSpans().AppendEmpty() - resourceAttrs := attrsToMap(getResourceAttrs(span.ServiceID).Attributes()) + resourceAttrs := attrsToMap(getResourceAttrs(&span.ServiceID).Attributes()) resourceAttrs.PutStr(string(semconv.OTelLibraryNameKey), reporterName) resourceAttrs.CopyTo(rs.Resource().Attributes()) diff --git a/pkg/internal/export/prom/prom.go b/pkg/internal/export/prom/prom.go index 59633106b..24eb6e14c 100644 --- a/pkg/internal/export/prom/prom.go +++ b/pkg/internal/export/prom/prom.go @@ -112,25 +112,25 @@ type PrometheusConfig struct { Registry *prometheus.Registry `yaml:"-"` } -func (p *PrometheusConfig) SpanMetricsEnabled() bool { +func (p PrometheusConfig) SpanMetricsEnabled() bool { return slices.Contains(p.Features, otel.FeatureSpan) } -func (p *PrometheusConfig) AppMetricsEnabled() bool { +func (p PrometheusConfig) OTelMetricsEnabled() bool { return slices.Contains(p.Features, otel.FeatureApplication) } -func (p *PrometheusConfig) ServiceGraphMetricsEnabled() bool { +func (p PrometheusConfig) ServiceGraphMetricsEnabled() bool { return slices.Contains(p.Features, otel.FeatureGraph) } -func (p *PrometheusConfig) EndpointEnabled() bool { +func (p PrometheusConfig) EndpointEnabled() bool { return p.Port != 0 || p.Registry != nil } // nolint:gocritic -func (p *PrometheusConfig) Enabled() bool { - return p.EndpointEnabled() && (p.AppMetricsEnabled() || p.SpanMetricsEnabled() || p.ServiceGraphMetricsEnabled()) +func (p PrometheusConfig) Enabled() bool { + return p.EndpointEnabled() && (p.OTelMetricsEnabled() || p.SpanMetricsEnabled() || p.ServiceGraphMetricsEnabled()) } type metricsReporter struct { @@ -394,7 +394,7 @@ func newReporter( registeredMetrics = append(registeredMetrics, mr.beylaInfo) } - if cfg.AppMetricsEnabled() { + if cfg.OTelMetricsEnabled() { registeredMetrics = append(registeredMetrics, mr.httpClientRequestSize, mr.httpClientDuration, @@ -455,7 +455,7 @@ func (r *metricsReporter) observe(span *request.Span) { t := span.Timings() r.beylaInfo.WithLabelValues(span.ServiceID.SDKLanguage.String()).Set(1.0) duration := t.End.Sub(t.RequestStart).Seconds() - if r.cfg.AppMetricsEnabled() { + if r.cfg.OTelMetricsEnabled() { switch span.Type { case request.EventTypeHTTP: r.httpDuration.WithLabelValues( diff --git a/pkg/internal/infraolly/process/cache.go b/pkg/internal/infraolly/process/cache.go deleted file mode 100644 index 7805f88d0..000000000 --- a/pkg/internal/infraolly/process/cache.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2020 New Relic Corporation. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package process - -import ( - "github.com/hashicorp/golang-lru/v2/simplelru" -) - -type cacheEntry struct { - process *linuxProcess - last *Status // The last event we generated for this process, so we can re-use metadata that doesn't change -} - -// removeUntilLen removes the oldest entries until the cache reaches the given length. -func removeUntilLen(c *simplelru.LRU[int32, *cacheEntry], newLength int) { - for c.Len() > newLength { - c.RemoveOldest() - } -} diff --git a/pkg/internal/infraolly/process/collect.go b/pkg/internal/infraolly/process/collect.go index 406179fd4..1ed8d623a 100644 --- a/pkg/internal/infraolly/process/collect.go +++ b/pkg/internal/infraolly/process/collect.go @@ -16,21 +16,29 @@ import ( "github.com/grafana/beyla/pkg/internal/svc" ) +type CollectConfig struct { + // RunMode defaults to "privileged". A non-privileged harvester will omit some information like open FDs. + RunMode RunMode + + // Interval between harvests + Interval time.Duration +} + // Collector returns runtime information about the currently running processes type Collector struct { ctx context.Context - cfg *Config + cfg *CollectConfig harvest *Harvester - cache *simplelru.LRU[int32, *cacheEntry] + cache *simplelru.LRU[int32, *linuxProcess] log *slog.Logger newPids *<-chan []request.Span } // NewCollectorProvider creates and returns a new process Collector, given an agent context. -func NewCollectorProvider(ctx context.Context, input *<-chan []request.Span, cfg *Config) pipe.StartProvider[[]*Status] { +func NewCollectorProvider(ctx context.Context, input *<-chan []request.Span, cfg *CollectConfig) pipe.StartProvider[[]*Status] { return func() (pipe.StartFunc[[]*Status], error) { // we purge entries explicitly so size is unbounded - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) + cache, _ := simplelru.NewLRU[int32, *linuxProcess](math.MaxInt, nil) harvest := newHarvester(cfg, cache) return (&Collector{ @@ -47,7 +55,7 @@ func NewCollectorProvider(ctx context.Context, input *<-chan []request.Span, cfg func (ps *Collector) Run(out chan<- []*Status) { // TODO: set app metadata as key for later decoration? (e.g. K8s metadata, svc.ID) pids := map[int32]*svc.ID{} - collectTicker := time.NewTicker(ps.cfg.Rate) + collectTicker := time.NewTicker(ps.cfg.Interval) defer collectTicker.Stop() newPids := *ps.newPids for { @@ -77,7 +85,7 @@ func (ps *Collector) Collect(pids map[int32]*svc.ID) ([]*Status, []int32) { var removed []int32 for pid, svcID := range pids { - status, err := ps.harvest.Do(svcID) + status, err := ps.harvest.Harvest(svcID) if err != nil { ps.log.Debug("skipping process", "pid", pid, "error", err) ps.harvest.cache.Remove(pid) @@ -88,7 +96,11 @@ func (ps *Collector) Collect(pids map[int32]*svc.ID) ([]*Status, []int32) { results = append(results, status) } - removeUntilLen(ps.cache, len(results)) + // remove processes from cache that haven't been collected in this iteration + // (this means they already disappeared so there is no need for caching) + for ps.cache.Len() > len(results) { + ps.cache.RemoveOldest() + } return results, removed } diff --git a/pkg/internal/infraolly/process/harvest.go b/pkg/internal/infraolly/process/harvest.go index 0e6babc1b..96a244454 100644 --- a/pkg/internal/infraolly/process/harvest.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -13,7 +13,6 @@ import ( "fmt" "log/slog" "os" - "time" "github.com/hashicorp/golang-lru/v2/simplelru" @@ -24,28 +23,23 @@ func hlog() *slog.Logger { return slog.With("component", "process.Harvester") } -var errProcessWithoutRSS = fmt.Errorf("process with zero rss") - type RunMode string const ( - RunModeRoot = "root" RunModePrivileged = "privileged" RunModeUnprivileged = "unprivileged" ) -type Config struct { - RunMode RunMode - DisableZeroRSSFilter bool - - Rate time.Duration +// Harvester fetches processes' information from Linux +type Harvester struct { + // allows overriding the /proc filesystem location via HOST_PROC env var + procFSRoot string + privileged bool + cache *simplelru.LRU[int32, *linuxProcess] + log *slog.Logger } -func newHarvester(cfg *Config, cache *simplelru.LRU[int32, *cacheEntry]) *Harvester { - // If not config, assuming root mode as default - privileged := cfg.RunMode == RunModeRoot || cfg.RunMode == RunModePrivileged - disableZeroRSSFilter := cfg.DisableZeroRSSFilter - +func newHarvester(cfg *CollectConfig, cache *simplelru.LRU[int32, *linuxProcess]) *Harvester { // we need to use the same method to override HOST_PROC that is used by gopsutil library hostProc, ok := os.LookupEnv("HOST_PROC") if !ok { @@ -53,51 +47,33 @@ func newHarvester(cfg *Config, cache *simplelru.LRU[int32, *cacheEntry]) *Harves } return &Harvester{ - procFSRoot: hostProc, - privileged: privileged, - disableZeroRSSFilter: disableZeroRSSFilter, - cache: cache, - log: hlog(), + procFSRoot: hostProc, + privileged: cfg.RunMode == RunModePrivileged, + cache: cache, + log: hlog(), } } -// Harvester is a Harvester implementation that uses various linux sources and manages process caches -type Harvester struct { - procFSRoot string - privileged bool - disableZeroRSSFilter bool - cache *simplelru.LRU[int32, *cacheEntry] - log *slog.Logger -} - -// Do returns a status of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the +// Harvest returns a status of a process whose PID is passed as argument. The 'elapsedSeconds' argument represents the // time since this process was statusd for the last time. If the process has been statusd for the first time, this value // will be ignored -func (ps *Harvester) Do(svcID *svc.ID) (*Status, error) { +func (ps *Harvester) Harvest(svcID *svc.ID) (*Status, error) { pid := svcID.ProcPID ps.log.Debug("harvesting pid", "pid", pid) // Reuses process information that does not vary cached, hasCachedEntry := ps.cache.Get(pid) - // If cached is nil, the linux process will be created from fresh data - if !hasCachedEntry { - cached = &cacheEntry{} - } var err error - cached.process, err = getLinuxProcess(ps.procFSRoot, pid, cached.process, ps.privileged) + // If cached is nil, the linux process will be created from fresh data + cached, err = getLinuxProcess(cached, ps.procFSRoot, pid, ps.privileged) if err != nil { return nil, fmt.Errorf("can't create process: %w", err) } - // We don't need to report processes which are not using memory. This filters out certain kernel processes. - if !ps.disableZeroRSSFilter && cached.process.VMRSS() == 0 { - return nil, errProcessWithoutRSS - } - // Creates a fresh process status and populates it with the metrics data status := NewStatus(pid, svcID) - if err := ps.populateStaticData(status, cached.process); err != nil { + if err := ps.populateStaticData(status, cached); err != nil { return nil, fmt.Errorf("can't populate static attributes: %w", err) } @@ -106,16 +82,14 @@ func (ps *Harvester) Do(svcID *svc.ID) (*Status, error) { ps.cache.Add(pid, cached) } - if err := ps.populateGauges(status, cached.process); err != nil { + if err := ps.populateGauges(status, cached); err != nil { return nil, fmt.Errorf("can't fetch gauge data: %w", err) } - if err := ps.populateIOCounters(status, cached.process); err != nil { + if err := ps.populateIOCounters(status, cached); err != nil { return nil, fmt.Errorf("can't fetch deltas: %w", err) } - cached.last = status - return status, nil } diff --git a/pkg/internal/infraolly/process/harvest_test.go b/pkg/internal/infraolly/process/harvest_test.go index c6e9da48d..433902494 100644 --- a/pkg/internal/infraolly/process/harvest_test.go +++ b/pkg/internal/infraolly/process/harvest_test.go @@ -27,17 +27,16 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { mode RunMode privileged bool }{ - {mode: RunModeRoot, privileged: true}, {mode: RunModePrivileged, privileged: true}, {mode: RunModeUnprivileged, privileged: false}, } for _, c := range cases { t.Run(fmt.Sprint("mode ", c.mode), func(t *testing.T) { - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(&Config{RunMode: c.mode}, cache) + cache, _ := simplelru.NewLRU[int32, *linuxProcess](math.MaxInt, nil) + h := newHarvester(&CollectConfig{RunMode: c.mode}, cache) // If not privileged, it is expected to not report neither FDs nor IO counters - status, err := h.Do(&svc.ID{ProcPID: int32(os.Getpid())}) + status, err := h.Harvest(&svc.ID{ProcPID: int32(os.Getpid())}) require.NoError(t, err) if c.privileged { assert.NotZero(t, status.FdCount) @@ -52,11 +51,11 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { func TestLinuxHarvester_Do(t *testing.T) { // Given a process harvester - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(&Config{}, cache) + cache, _ := simplelru.NewLRU[int32, *linuxProcess](math.MaxInt, nil) + h := newHarvester(&CollectConfig{}, cache) // When retrieving for a given process status (e.g. the current testing executable) - status, err := h.Do(&svc.ID{ProcPID: int32(os.Getpid())}) + status, err := h.Harvest(&svc.ID{ProcPID: int32(os.Getpid())}) // It returns the corresponding process status with valid data require.NoError(t, err) @@ -87,12 +86,12 @@ func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { }() // Given a process harvester configured to showw the full command line - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(&Config{}, cache) + cache, _ := simplelru.NewLRU[int32, *linuxProcess](math.MaxInt, nil) + h := newHarvester(&CollectConfig{}, cache) test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process status (e.g. the current testing executable) - status, err := h.Do(&svc.ID{ProcPID: int32(cmd.Process.Pid)}) + status, err := h.Harvest(&svc.ID{ProcPID: int32(cmd.Process.Pid)}) // It returns the corresponding Command line without stripping arguments require.NoError(t, err) @@ -111,12 +110,12 @@ func TestLinuxHarvester_Do_StripCommandLine(t *testing.T) { }() // Given a process harvester - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - h := newHarvester(&Config{}, cache) + cache, _ := simplelru.NewLRU[int32, *linuxProcess](math.MaxInt, nil) + h := newHarvester(&CollectConfig{}, cache) test.Eventually(t, 5*time.Second, func(t require.TestingT) { // When retrieving for a given process status (e.g. the current testing executable) - status, err := h.Do(&svc.ID{ProcPID: int32(cmd.Process.Pid)}) + status, err := h.Harvest(&svc.ID{ProcPID: int32(cmd.Process.Pid)}) // It returns the corresponding Command line without stripping arguments require.NoError(t, err) @@ -131,12 +130,12 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentCmd(t *testing.T) { // Given a process harvester // That has cached an old process sharing the PID with a new process - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - cache.Add(currentPid, &cacheEntry{process: &linuxProcess{cmdLine: "something old"}}) - h := newHarvester(&Config{}, cache) + cache, _ := simplelru.NewLRU[int32, *linuxProcess](math.MaxInt, nil) + cache.Add(currentPid, &linuxProcess{cmdLine: "something old"}) + h := newHarvester(&CollectConfig{}, cache) // When the process is harvested - status, err := h.Do(&svc.ID{ProcPID: currentPid}) + status, err := h.Harvest(&svc.ID{ProcPID: currentPid}) require.NoError(t, err) // The status is updated @@ -149,12 +148,12 @@ func TestLinuxHarvester_Do_InvalidateCache_DifferentPid(t *testing.T) { // Given a process harvester // That has cached an old process sharing the PID with a new process - cache, _ := simplelru.NewLRU[int32, *cacheEntry](math.MaxInt, nil) - cache.Add(currentPid, &cacheEntry{process: &linuxProcess{stats: procStats{ppid: -1}}}) - h := newHarvester(&Config{}, cache) + cache, _ := simplelru.NewLRU[int32, *linuxProcess](math.MaxInt, nil) + cache.Add(currentPid, &linuxProcess{stats: procStats{ppid: -1}}) + h := newHarvester(&CollectConfig{}, cache) // When the process is harvested - status, err := h.Do(&svc.ID{ProcPID: currentPid}) + status, err := h.Harvest(&svc.ID{ProcPID: currentPid}) require.NoError(t, err) // The status is updated diff --git a/pkg/internal/infraolly/infra_pipe.go b/pkg/internal/infraolly/process/pipeline.go similarity index 67% rename from pkg/internal/infraolly/infra_pipe.go rename to pkg/internal/infraolly/process/pipeline.go index 5984e9b3f..cc091dcc5 100644 --- a/pkg/internal/infraolly/infra_pipe.go +++ b/pkg/internal/infraolly/process/pipeline.go @@ -1,4 +1,4 @@ -package infraolly +package process import ( "context" @@ -9,24 +9,23 @@ import ( "github.com/grafana/beyla/pkg/beyla" "github.com/grafana/beyla/pkg/internal/export/otel" - "github.com/grafana/beyla/pkg/internal/infraolly/process" "github.com/grafana/beyla/pkg/internal/pipe/global" "github.com/grafana/beyla/pkg/internal/request" ) -// SubPipeline is actually a part of the Application Observability pipeline. +// processSubPipeline is actually a part of the Application Observability pipeline. // Its management is moved here because it's only activated if the process // metrics are activated. -type subPipeline struct { - Collector pipe.Start[[]*process.Status] - OtelExport pipe.Final[[]*process.Status] +type processSubPipeline struct { + Collector pipe.Start[[]*Status] + OtelExport pipe.Final[[]*Status] // TODO: add prometheus exporter } -func collector(sp *subPipeline) *pipe.Start[[]*process.Status] { return &sp.Collector } -func otelExport(sp *subPipeline) *pipe.Final[[]*process.Status] { return &sp.OtelExport } +func collector(sp *processSubPipeline) *pipe.Start[[]*Status] { return &sp.Collector } +func otelExport(sp *processSubPipeline) *pipe.Final[[]*Status] { return &sp.OtelExport } -func (sp *subPipeline) Connect() { +func (sp *processSubPipeline) Connect() { sp.Collector.SendTo(sp.OtelExport) } @@ -35,7 +34,7 @@ func (sp *subPipeline) Connect() { func isSubPipeEnabled(cfg *beyla.Config) bool { return (cfg.Metrics.EndpointEnabled() && cfg.Metrics.OTelMetricsEnabled() && slices.Contains(cfg.Metrics.Features, otel.FeatureProcess)) || - (cfg.Prometheus.EndpointEnabled() && cfg.Prometheus.AppMetricsEnabled() && + (cfg.Prometheus.EndpointEnabled() && cfg.Prometheus.OTelMetricsEnabled() && slices.Contains(cfg.Prometheus.Features, otel.FeatureProcess)) } @@ -48,9 +47,9 @@ func SubPipelineProvider(ctx context.Context, ctxInfo *global.ContextInfo, cfg * } connectorChan := make(chan []request.Span, cfg.ChannelBufferLen) var connector <-chan []request.Span = connectorChan - nb := pipe.NewBuilder(&subPipeline{}, pipe.ChannelBufferLen(cfg.ChannelBufferLen)) - pipe.AddStartProvider(nb, collector, process.NewCollectorProvider(ctx, &connector, &cfg.Processes)) - pipe.AddFinalProvider(nb, otelExport, otel.ProcessMetricsExporterProvider(ctx, ctxInfo, + nb := pipe.NewBuilder(&processSubPipeline{}, pipe.ChannelBufferLen(cfg.ChannelBufferLen)) + pipe.AddStartProvider(nb, collector, NewCollectorProvider(ctx, &connector, &cfg.Processes)) + pipe.AddFinalProvider(nb, otelExport, otel.ProcMetricsExporterProvider(ctx, ctxInfo, &otel.ProcMetricsConfig{ Metrics: &cfg.Metrics, AttributeSelectors: cfg.Attributes.Select, diff --git a/pkg/internal/infraolly/process/snapshot.go b/pkg/internal/infraolly/process/snapshot.go index 7ce595c9c..f2fa9b5b9 100644 --- a/pkg/internal/infraolly/process/snapshot.go +++ b/pkg/internal/infraolly/process/snapshot.go @@ -76,7 +76,7 @@ func init() { // getLinuxProcess returns a linux process snapshot, trying to reuse the data from a previous snapshot of the same // process. -func getLinuxProcess(procFSRoot string, pid int32, previous *linuxProcess, privileged bool) (*linuxProcess, error) { +func getLinuxProcess(cachedCopy *linuxProcess, procFSRoot string, pid int32, privileged bool) (*linuxProcess, error) { var gops *process.Process var err error @@ -86,11 +86,11 @@ func getLinuxProcess(procFSRoot string, pid int32, previous *linuxProcess, privi } // Reusing information from the last snapshot for the same process - // If the name or the PPID changed from the previous, we'll consider this sample is just + // If the name or the PPID changed from the cachedCopy, we'll consider this sample is just // a new process that shares the PID with an old one. // if a process with the same Command but different CommandLine or User name // occupies the same PID, the cache won't refresh the CommandLine and Username. - if previous == nil || procStats.command != previous.Command() || procStats.ppid != previous.Ppid() { + if cachedCopy == nil || procStats.command != cachedCopy.Command() || procStats.ppid != cachedCopy.Ppid() { gops, err = process.NewProcess(pid) if err != nil { return nil, err @@ -104,10 +104,10 @@ func getLinuxProcess(procFSRoot string, pid int32, previous *linuxProcess, privi }, nil } - // Otherwise, instead of creating a new process snapshot, we just reuse the previous one, with updated data - previous.stats = procStats + // Otherwise, instead of creating a new process snapshot, we just reuse the cachedCopy one, with updated data + cachedCopy.stats = procStats - return previous, nil + return cachedCopy, nil } func (pw *linuxProcess) Pid() int32 { @@ -256,41 +256,41 @@ func parseProcStat(content string) (procStats, error) { // Parent PID ppid, err := strconv.ParseInt(fields[statPPID], 10, 32) if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) + return stats, errors.Wrapf(err, "for stats: %s", content) } stats.ppid = int32(ppid) // User time utime, err := strconv.ParseInt(fields[statUtime], 10, 64) if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) + return stats, errors.Wrapf(err, "for stats: %s", content) } stats.cpu.User = float64(utime) / float64(clockTicks) // System time stime, err := strconv.ParseInt(fields[statStime], 10, 64) if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) + return stats, errors.Wrapf(err, "for stats: %s", content) } stats.cpu.System = float64(stime) / float64(clockTicks) // Number of threads nthreads, err := strconv.ParseInt(fields[statNumThreads], 10, 32) if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) + return stats, errors.Wrapf(err, "for stats: %s", content) } stats.numThreads = int32(nthreads) // VM Memory size stats.vmSize, err = strconv.ParseInt(fields[statVsize], 10, 64) if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) + return stats, errors.Wrapf(err, "for stats: %s", content) } // VM RSS size stats.vmRSS, err = strconv.ParseInt(fields[statRss], 10, 64) if err != nil { - return stats, errors.Wrapf(err, "for stats: %s", string(content)) + return stats, errors.Wrapf(err, "for stats: %s", content) } stats.vmRSS *= pageSize diff --git a/pkg/internal/infraolly/process/status.go b/pkg/internal/infraolly/process/status.go index a256c96af..5daf6a089 100644 --- a/pkg/internal/infraolly/process/status.go +++ b/pkg/internal/infraolly/process/status.go @@ -3,6 +3,10 @@ package process import ( "log/slog" + "go.opentelemetry.io/otel/attribute" + + "github.com/grafana/beyla/pkg/internal/export/attributes" + attr "github.com/grafana/beyla/pkg/internal/export/attributes/names" "github.com/grafana/beyla/pkg/internal/svc" ) @@ -10,6 +14,7 @@ func pslog() *slog.Logger { return slog.With("component", "process.Collector") } +// Status of a process after being harvested type Status struct { ProcessID int32 Command string @@ -38,3 +43,27 @@ func NewStatus(pid int32, svcID *svc.ID) *Status { Service: svcID, } } + +// nolint:cyclop +func OTELGetters(name attr.Name) (attributes.Getter[*Status, attribute.KeyValue], bool) { + var g attributes.Getter[*Status, attribute.KeyValue] + switch name { + case attr.ProcCommand: + g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.ProcCommand).String(s.Command) } + case attr.ProcCommandLine: + g = func(s *Status) attribute.KeyValue { + return attribute.Key(attr.ProcCommand).String(s.CommandLine) + } + case attr.ProcOwner: + g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.ProcOwner).String(s.User) } + case attr.ProcParentPid: + g = func(s *Status) attribute.KeyValue { + return attribute.Key(attr.ProcParentPid).Int(int(s.ParentProcessID)) + } + case attr.ProcPid: + g = func(s *Status) attribute.KeyValue { + return attribute.Key(attr.ProcParentPid).Int(int(s.ProcessID)) + } + } + return g, g != nil +} diff --git a/pkg/internal/infraolly/process/status_getters.go b/pkg/internal/infraolly/process/status_getters.go deleted file mode 100644 index d71d12bb2..000000000 --- a/pkg/internal/infraolly/process/status_getters.go +++ /dev/null @@ -1,32 +0,0 @@ -package process - -import ( - "go.opentelemetry.io/otel/attribute" - - "github.com/grafana/beyla/pkg/internal/export/attributes" - attr "github.com/grafana/beyla/pkg/internal/export/attributes/names" -) - -// nolint:cyclop -func OTELGetters(name attr.Name) (attributes.Getter[*Status, attribute.KeyValue], bool) { - var g attributes.Getter[*Status, attribute.KeyValue] - switch name { - case attr.ProcCommand: - g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.ProcCommand).String(s.Command) } - case attr.ProcCommandLine: - g = func(s *Status) attribute.KeyValue { - return attribute.Key(attr.ProcCommand).String(s.CommandLine) - } - case attr.ProcOwner: - g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.ProcOwner).String(s.User) } - case attr.ProcParentPid: - g = func(s *Status) attribute.KeyValue { - return attribute.Key(attr.ProcParentPid).Int(int(s.ParentProcessID)) - } - case attr.ProcPid: - g = func(s *Status) attribute.KeyValue { - return attribute.Key(attr.ProcParentPid).Int(int(s.ProcessID)) - } - } - return g, g != nil -} diff --git a/pkg/internal/pipe/instrumenter.go b/pkg/internal/pipe/instrumenter.go index 4e83a5581..565ca456c 100644 --- a/pkg/internal/pipe/instrumenter.go +++ b/pkg/internal/pipe/instrumenter.go @@ -14,7 +14,7 @@ import ( "github.com/grafana/beyla/pkg/internal/export/prom" "github.com/grafana/beyla/pkg/internal/filter" "github.com/grafana/beyla/pkg/internal/imetrics" - "github.com/grafana/beyla/pkg/internal/infraolly" + "github.com/grafana/beyla/pkg/internal/infraolly/process" "github.com/grafana/beyla/pkg/internal/pipe/global" "github.com/grafana/beyla/pkg/internal/request" "github.com/grafana/beyla/pkg/internal/traces" @@ -69,7 +69,7 @@ func otelTraces(n *nodesMap) *pipe.Final[[]request.Span] { re func printer(n *nodesMap) *pipe.Final[[]request.Span] { return &n.Printer } func prometheus(n *nodesMap) *pipe.Final[[]request.Span] { return &n.Prometheus } func noop(n *nodesMap) *pipe.Final[[]request.Span] { return &n.Noop } -func process(n *nodesMap) *pipe.Final[[]request.Span] { return &n.ProcessReport } +func processReport(n *nodesMap) *pipe.Final[[]request.Span] { return &n.ProcessReport } // builder with injectable instantiators for unit testing type graphFunctions struct { @@ -123,7 +123,9 @@ func newGraphBuilder(ctx context.Context, config *beyla.Config, ctxInfo *global. pipe.AddFinalProvider(gnb, noop, debug.NoopNode(config.Noop)) pipe.AddFinalProvider(gnb, printer, debug.PrinterNode(config.Printer)) - pipe.AddFinalProvider(gnb, process, infraolly.SubPipelineProvider(ctx, ctxInfo, config)) + // process subpipeline will start another pipeline only to collect and export data + // about the processes of an instrumented application + pipe.AddFinalProvider(gnb, processReport, process.SubPipelineProvider(ctx, ctxInfo, config)) // The returned builder later invokes its "Build" function that, given // the contents of the nodesMap struct, will instantiate diff --git a/test/integration/docker-compose-python.yml b/test/integration/docker-compose-python.yml index fd5484dd4..6e97994c8 100644 --- a/test/integration/docker-compose-python.yml +++ b/test/integration/docker-compose-python.yml @@ -24,7 +24,7 @@ services: image: hatest-autoinstrumenter privileged: true # in some environments (not GH Pull Requests) you can set it to false and then cap_add: [ SYS_ADMIN ] network_mode: "service:testserver" - pid: "host" + pid: "service:testserver" environment: BEYLA_CONFIG_PATH: "/configs/instrumenter-config-java.yml" GOCOVERDIR: "/coverage" @@ -41,7 +41,6 @@ services: BEYLA_METRICS_REPORT_PEER: "true" BEYLA_HOSTNAME: "beyla" BEYLA_BPF_HTTP_REQUEST_TIMEOUT: "5s" - BEYLA_OTEL_METRICS_FEATURES: "application,application_process" depends_on: testserver: condition: service_started From 9702fb6360590ab04fa0d63330123512a7cfff6c Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Wed, 5 Jun 2024 10:29:17 +0200 Subject: [PATCH 22/27] Fixed compilation and unit tests --- pkg/internal/infraolly/process/collect.go | 6 ++++-- pkg/internal/pipe/instrumenter.go | 3 +-- .../process/pipeline.go => pipe/proc_pipeline.go} | 15 +++++++++------ 3 files changed, 14 insertions(+), 10 deletions(-) rename pkg/internal/{infraolly/process/pipeline.go => pipe/proc_pipeline.go} (79%) diff --git a/pkg/internal/infraolly/process/collect.go b/pkg/internal/infraolly/process/collect.go index 1ed8d623a..afd2c2974 100644 --- a/pkg/internal/infraolly/process/collect.go +++ b/pkg/internal/infraolly/process/collect.go @@ -24,14 +24,16 @@ type CollectConfig struct { Interval time.Duration } -// Collector returns runtime information about the currently running processes +// Collector returns runtime information about the currently running processes. +// The collector receives each application trace from the newPids internal channel, +// to know which PIDs are active. type Collector struct { + newPids *<-chan []request.Span ctx context.Context cfg *CollectConfig harvest *Harvester cache *simplelru.LRU[int32, *linuxProcess] log *slog.Logger - newPids *<-chan []request.Span } // NewCollectorProvider creates and returns a new process Collector, given an agent context. diff --git a/pkg/internal/pipe/instrumenter.go b/pkg/internal/pipe/instrumenter.go index 565ca456c..e58023e5f 100644 --- a/pkg/internal/pipe/instrumenter.go +++ b/pkg/internal/pipe/instrumenter.go @@ -14,7 +14,6 @@ import ( "github.com/grafana/beyla/pkg/internal/export/prom" "github.com/grafana/beyla/pkg/internal/filter" "github.com/grafana/beyla/pkg/internal/imetrics" - "github.com/grafana/beyla/pkg/internal/infraolly/process" "github.com/grafana/beyla/pkg/internal/pipe/global" "github.com/grafana/beyla/pkg/internal/request" "github.com/grafana/beyla/pkg/internal/traces" @@ -125,7 +124,7 @@ func newGraphBuilder(ctx context.Context, config *beyla.Config, ctxInfo *global. // process subpipeline will start another pipeline only to collect and export data // about the processes of an instrumented application - pipe.AddFinalProvider(gnb, processReport, process.SubPipelineProvider(ctx, ctxInfo, config)) + pipe.AddFinalProvider(gnb, processReport, SubPipelineProvider(ctx, ctxInfo, config)) // The returned builder later invokes its "Build" function that, given // the contents of the nodesMap struct, will instantiate diff --git a/pkg/internal/infraolly/process/pipeline.go b/pkg/internal/pipe/proc_pipeline.go similarity index 79% rename from pkg/internal/infraolly/process/pipeline.go rename to pkg/internal/pipe/proc_pipeline.go index cc091dcc5..a1788c5fa 100644 --- a/pkg/internal/infraolly/process/pipeline.go +++ b/pkg/internal/pipe/proc_pipeline.go @@ -1,4 +1,4 @@ -package process +package pipe import ( "context" @@ -9,6 +9,7 @@ import ( "github.com/grafana/beyla/pkg/beyla" "github.com/grafana/beyla/pkg/internal/export/otel" + "github.com/grafana/beyla/pkg/internal/infraolly/process" "github.com/grafana/beyla/pkg/internal/pipe/global" "github.com/grafana/beyla/pkg/internal/request" ) @@ -17,13 +18,13 @@ import ( // Its management is moved here because it's only activated if the process // metrics are activated. type processSubPipeline struct { - Collector pipe.Start[[]*Status] - OtelExport pipe.Final[[]*Status] + Collector pipe.Start[[]*process.Status] + OtelExport pipe.Final[[]*process.Status] // TODO: add prometheus exporter } -func collector(sp *processSubPipeline) *pipe.Start[[]*Status] { return &sp.Collector } -func otelExport(sp *processSubPipeline) *pipe.Final[[]*Status] { return &sp.OtelExport } +func procCollect(sp *processSubPipeline) *pipe.Start[[]*process.Status] { return &sp.Collector } +func otelExport(sp *processSubPipeline) *pipe.Final[[]*process.Status] { return &sp.OtelExport } func (sp *processSubPipeline) Connect() { sp.Collector.SendTo(sp.OtelExport) @@ -48,7 +49,7 @@ func SubPipelineProvider(ctx context.Context, ctxInfo *global.ContextInfo, cfg * connectorChan := make(chan []request.Span, cfg.ChannelBufferLen) var connector <-chan []request.Span = connectorChan nb := pipe.NewBuilder(&processSubPipeline{}, pipe.ChannelBufferLen(cfg.ChannelBufferLen)) - pipe.AddStartProvider(nb, collector, NewCollectorProvider(ctx, &connector, &cfg.Processes)) + pipe.AddStartProvider(nb, procCollect, process.NewCollectorProvider(ctx, &connector, &cfg.Processes)) pipe.AddFinalProvider(nb, otelExport, otel.ProcMetricsExporterProvider(ctx, ctxInfo, &otel.ProcMetricsConfig{ Metrics: &cfg.Metrics, @@ -60,6 +61,8 @@ func SubPipelineProvider(ctx context.Context, ctxInfo *global.ContextInfo, cfg * return nil, fmt.Errorf("creating process subpipeline: %w", err) } return func(in <-chan []request.Span) { + // connect the input channel of this final node to the input of the + // process collector connector = in runner.Start() <-ctx.Done() From 11a1134fed6fdf108583717615b19cd26a25ecf8 Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Wed, 5 Jun 2024 13:03:35 +0200 Subject: [PATCH 23/27] Fixed and improved process harvesting --- pkg/internal/export/attributes/attr_defs.go | 7 +- pkg/internal/infraolly/process/harvest.go | 16 ++-- .../infraolly/process/harvest_test.go | 30 +------ pkg/internal/infraolly/process/snapshot.go | 77 +++++++++++----- .../infraolly/process/snapshot_test.go | 90 ++++++------------- pkg/internal/infraolly/process/status.go | 22 ++++- 6 files changed, 109 insertions(+), 133 deletions(-) diff --git a/pkg/internal/export/attributes/attr_defs.go b/pkg/internal/export/attributes/attr_defs.go index fe9700beb..631a7cd1b 100644 --- a/pkg/internal/export/attributes/attr_defs.go +++ b/pkg/internal/export/attributes/attr_defs.go @@ -158,10 +158,9 @@ func getDefinitions(groups AttrGroups) map[Section]AttrReportGroup { attr.ProcParentPid: true, attr.ProcPid: true, attr.ProcCommandLine: false, - // TODO: not yet supported attributes - // attr.ProcCommandArgs: true, - // attr.ProcExecName: true, - // attr.ProcExecPath: true, + attr.ProcCommandArgs: false, + attr.ProcExecName: false, + attr.ProcExecPath: false, }, } diff --git a/pkg/internal/infraolly/process/harvest.go b/pkg/internal/infraolly/process/harvest.go index 96a244454..525e8cdb2 100644 --- a/pkg/internal/infraolly/process/harvest.go +++ b/pkg/internal/infraolly/process/harvest.go @@ -95,20 +95,20 @@ func (ps *Harvester) Harvest(svcID *svc.ID) (*Status, error) { // populateStaticData populates the status with the process data won't vary during the process life cycle func (ps *Harvester) populateStaticData(status *Status, process *linuxProcess) error { - var err error - status.CommandLine, err = process.CmdLine() - if err != nil { - return fmt.Errorf("acquiring command line: %w", err) - } + process.FetchCommandInfo() + status.Command = process.Command() + status.CommandArgs = process.commandArgs + status.CommandLine = process.commandLine + status.ExecPath = process.execPath + status.ExecName = process.execName status.ProcessID = process.Pid() - status.User, err = process.Username() - if err != nil { + var err error + if status.User, err = process.Username(); err != nil { ps.log.Debug("can't get username for process", "pid", status.ProcessID, "error", err) } - status.Command = process.Command() status.ParentProcessID = process.Ppid() return nil diff --git a/pkg/internal/infraolly/process/harvest_test.go b/pkg/internal/infraolly/process/harvest_test.go index 433902494..ea13047b6 100644 --- a/pkg/internal/infraolly/process/harvest_test.go +++ b/pkg/internal/infraolly/process/harvest_test.go @@ -69,13 +69,8 @@ func TestLinuxHarvester_Do(t *testing.T) { "process status must be R (running), S (interruptible sleep) or D (uninterruptible sleep)") assert.NotZero(t, status.MemoryVMSBytes) assert.NotZero(t, status.MemoryRSSBytes) - assert.NotZero(t, status.CPUPercent) - assert.NotZero(t, status.CPUUserPercent) - assert.NotZero(t, status.CPUSystemPercent) assert.NotZero(t, status.ParentProcessID) assert.NotZero(t, status.ThreadCount) - assert.NotZero(t, status.FdCount) - assert.NotZero(t, status.ThreadCount) } func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { @@ -102,36 +97,13 @@ func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { }) } -func TestLinuxHarvester_Do_StripCommandLine(t *testing.T) { - cmd := exec.Command("/bin/sleep", "1m") - require.NoError(t, cmd.Start()) - defer func() { - _ = cmd.Process.Kill() - }() - - // Given a process harvester - cache, _ := simplelru.NewLRU[int32, *linuxProcess](math.MaxInt, nil) - h := newHarvester(&CollectConfig{}, cache) - - test.Eventually(t, 5*time.Second, func(t require.TestingT) { - // When retrieving for a given process status (e.g. the current testing executable) - status, err := h.Harvest(&svc.ID{ProcPID: int32(cmd.Process.Pid)}) - - // It returns the corresponding Command line without stripping arguments - require.NoError(t, err) - require.NotNil(t, status) - - assert.True(t, strings.HasSuffix(status.CommandLine, "sleep"), "%q should not have arguments", status.CommandLine) - }) -} - func TestLinuxHarvester_Do_InvalidateCache_DifferentCmd(t *testing.T) { currentPid := int32(os.Getpid()) // Given a process harvester // That has cached an old process sharing the PID with a new process cache, _ := simplelru.NewLRU[int32, *linuxProcess](math.MaxInt, nil) - cache.Add(currentPid, &linuxProcess{cmdLine: "something old"}) + cache.Add(currentPid, &linuxProcess{stats: procStats{command: "something old"}}) h := newHarvester(&CollectConfig{}, cache) // When the process is harvested diff --git a/pkg/internal/infraolly/process/snapshot.go b/pkg/internal/infraolly/process/snapshot.go index f2fa9b5b9..702ec460b 100644 --- a/pkg/internal/infraolly/process/snapshot.go +++ b/pkg/internal/infraolly/process/snapshot.go @@ -4,6 +4,7 @@ package process import ( + "bytes" "fmt" "os" "path" @@ -43,9 +44,13 @@ type linuxProcess struct { procFSRoot string // data that will be reused between harvests of the same process. - pid int32 - user string - cmdLine string + pid int32 + user string + commandInfoFetched bool + commandArgs []string + commandLine string + execPath string + execName string } // needed to calculate RSS. @@ -351,13 +356,14 @@ func (pw *linuxProcess) Command() string { } ////////////////////////// -// Data to be derived from /proc//cmdline: command line, and command line without arguments +// Data to be derived from /proc//cmdline: command line and arguments ////////////////////////// -func (pw *linuxProcess) CmdLine() (string, error) { - if pw.cmdLine != "" { - return pw.cmdLine, nil +func (pw *linuxProcess) FetchCommandInfo() { + if pw.commandInfoFetched { + return } + pw.commandInfoFetched = true cmdPath := path.Join(pw.procFSRoot, strconv.Itoa(int(pw.pid)), "cmdline") procCmdline, err := os.ReadFile(cmdPath) @@ -366,7 +372,7 @@ func (pw *linuxProcess) CmdLine() (string, error) { } if len(procCmdline) == 0 { - return "", nil // zombie process + return // zombie process } // Ignoring dash on session commands @@ -374,25 +380,48 @@ func (pw *linuxProcess) CmdLine() (string, error) { procCmdline = procCmdline[1:] } - cmdLineBytes := make([]byte, 0, len(procCmdline)) - for i := 0; i < len(procCmdline); i++ { - if procCmdline[i] == 0 { - // ignoring the trailing zero that ends /proc//cmdline, but adding the last character if the file - // does not end in zero - if i < len(procCmdline)-1 { - cmdLineBytes = append(cmdLineBytes, ' ') - } else { - break - } - } else { - cmdLineBytes = append(cmdLineBytes, procCmdline[i]) + fullCommandLine := strings.Builder{} + // get command + procCmdline = sanitizeCommandLine(procCmdline) + + // get command args + procCmdline, pw.execPath, _ = getNextArg(procCmdline) + pw.execName = path.Base(pw.execPath) + + fullCommandLine.WriteString(pw.execPath) + for { + var arg string + var ok bool + procCmdline, arg, ok = getNextArg(procCmdline) + if !ok { + break } + fullCommandLine.WriteByte(' ') + fullCommandLine.WriteString(arg) + pw.commandArgs = append(pw.commandArgs, arg) } - pw.cmdLine = sanitizeCommandLine(string(cmdLineBytes)) - return pw.cmdLine, nil + pw.commandLine = fullCommandLine.String() +} + +// getNextArg consumes the next found argument from a /proc/*/cmdline string +// (where arguments are separated by the zero byte) +func getNextArg(procCmdline []byte) ([]byte, string, bool) { + if len(procCmdline) == 0 { + return nil, "", false + } + var arg []byte + for len(procCmdline) > 0 && procCmdline[0] != 0 { + arg = append(arg, procCmdline[0]) + procCmdline = procCmdline[1:] + } + // ignore the zero when it's an argument separator + if len(procCmdline) > 0 { + procCmdline = procCmdline[1:] + } + return procCmdline, string(arg), true } // sanitizeCommandLine cleans the command line to remove wrappers like quotation marks. -func sanitizeCommandLine(cmd string) string { - return strings.Trim(cmd, " \t\n\v\f\r\"'`") +func sanitizeCommandLine(cmd []byte) []byte { + return bytes.Trim(cmd, " \t\n\v\f\r\"'`") } diff --git a/pkg/internal/infraolly/process/snapshot_test.go b/pkg/internal/infraolly/process/snapshot_test.go index 379952763..b7b3f8c48 100644 --- a/pkg/internal/infraolly/process/snapshot_test.go +++ b/pkg/internal/infraolly/process/snapshot_test.go @@ -28,57 +28,29 @@ func TestLinuxProcess_CmdLine(t *testing.T) { _ = os.Setenv("HOST_PROC", tmpDir) testCases := []struct { - rawProcCmdline []byte - expected string + rawProcCmdline []byte + expectedExec string + expectedExecPath string + expectedArgs []string + expectedCmdLine string }{ - {[]byte{0}, ""}, - {[]byte{'b', 'a', 's', 'h', 0}, "bash"}, - {[]byte{'/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0}, "/bin/bash"}, - {[]byte{'/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0, 'a', 'r', 'g', 0}, "/bin/bash arg"}, - {[]byte{'-', '/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0, 'a', 'r', 'g', 0}, "/bin/bash arg"}, + {[]byte{0}, "", "", nil, ""}, + {[]byte{'b', 'a', 's', 'h', 0}, "bash", "bash", nil, "bash"}, + {[]byte{'/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0}, "bash", "/bin/bash", nil, "/bin/bash"}, + {[]byte{'/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0, 'a', 'r', 'g', 0}, "bash", "/bin/bash", []string{"arg"}, "/bin/bash arg"}, + {[]byte{'-', '/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0, 'a', 'r', 'g', 0}, "bash", "/bin/bash", []string{"arg"}, "/bin/bash arg"}, { []byte{'/', 'a', ' ', 'f', 'o', 'l', 'd', 'e', 'r', '/', 'c', 'm', 'd', 0, '-', 'a', 'g', 0, 'x', 'x', 0}, - "/a folder/cmd -ag xx", + "cmd", "/a folder/cmd", []string{"-ag", "xx"}, "/a folder/cmd -ag xx", }, } for _, tc := range testCases { require.NoError(t, os.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) - lp := linuxProcess{pid: 12345} - actual, err := lp.CmdLine() - assert.NoError(t, err) - assert.Equal(t, tc.expected, actual) - } -} - -func TestLinuxProcess_CmdLine_NoArgs(t *testing.T) { - hostProc := os.Getenv("HOST_PROC") - defer os.Setenv("HOST_PROC", hostProc) - tmpDir, err := os.MkdirTemp("", "proc") - require.NoError(t, err) - processDir := path.Join(tmpDir, "12345") - require.NoError(t, os.MkdirAll(processDir, 0o755)) - _ = os.Setenv("HOST_PROC", tmpDir) - - testCases := []struct { - rawProcCmdline []byte - expected string - }{ - {[]byte{0}, ""}, - {[]byte{'b', 'a', 's', 'h', 0}, "bash"}, - {[]byte{'-', 'b', 'a', 's', 'h', 0}, "bash"}, - {[]byte{'/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0}, "/bin/bash"}, - {[]byte{'/', 'b', 'i', 'n', '/', 'b', 'a', 's', 'h', 0, 'a', 'r', 'g', 0}, "/bin/bash"}, - { - []byte{'/', 'a', ' ', 'f', 'o', 'l', 'd', 'e', 'r', '/', 'c', 'm', 'd', 0, '-', 'a', 'g', 0, 'x', 'x', 0}, - "/a folder/cmd", - }, - } - for _, tc := range testCases { - require.NoError(t, os.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) - lp := linuxProcess{pid: 12345} - actual, err := lp.CmdLine() - assert.NoError(t, err) - assert.Equal(t, tc.expected, actual) + lp := linuxProcess{pid: 12345, procFSRoot: tmpDir} + lp.FetchCommandInfo() + assert.Equal(t, tc.expectedExecPath, lp.execPath) + assert.Equal(t, tc.expectedArgs, lp.commandArgs) + assert.Equal(t, tc.expectedCmdLine, lp.commandLine) } } @@ -106,31 +78,19 @@ func TestLinuxProcess_CmdLine_NotStandard(t *testing.T) { } for _, tc := range testCases { require.NoError(t, os.WriteFile(path.Join(processDir, "cmdline"), tc.rawProcCmdline, 0o600)) - lp := linuxProcess{pid: 12345} - - // Testing both the cases with and without command line stripping - actual, err := lp.CmdLine() - assert.NoError(t, err) - assert.Equal(t, tc.expected, actual) + lp := linuxProcess{pid: 12345, procFSRoot: tmpDir} - actual, err = lp.CmdLine() - assert.NoError(t, err) - assert.Equal(t, tc.expected, actual) + lp.FetchCommandInfo() + assert.Equal(t, tc.expected, lp.commandLine) } } func TestLinuxProcess_CmdLine_ProcessNotExist(t *testing.T) { lp := linuxProcess{pid: 999999999} - actual, err := lp.CmdLine() - assert.NoError(t, err) - assert.Equal(t, "", actual) -} - -func TestLinuxProcess_CmdLine_ProcessNotExist_NoStrip(t *testing.T) { - lp := linuxProcess{pid: 999999999} - actual, err := lp.CmdLine() - assert.NoError(t, err) - assert.Equal(t, "", actual) + lp.FetchCommandInfo() + assert.Empty(t, lp.execPath) + assert.Empty(t, lp.commandArgs) + assert.Empty(t, lp.commandLine) } func TestParseProcStatMultipleWordsProcess(t *testing.T) { @@ -156,10 +116,10 @@ func TestParseProcStatMultipleWordsProcess(t *testing.T) { } func TestParseProcStatSingleWordProcess(t *testing.T) { - content := `1232 (newrelic-infra) S 1 1232 1232 0 -1 1077960960 4799 282681 88 142 24 15 193 94 20 0 12 0 1071 464912384 4490 18446744073709551615 1 1 0 0 0 0 0 0 2143420159 0 0 0 17 0 0 0 14 0 0 0 0 0 0 0 0 0 0` + content := `1232 (foo-bar) S 1 1232 1232 0 -1 1077960960 4799 282681 88 142 24 15 193 94 20 0 12 0 1071 464912384 4490 18446744073709551615 1 1 0 0 0 0 0 0 2143420159 0 0 0 17 0 0 0 14 0 0 0 0 0 0 0 0 0 0` expected := procStats{ - command: "newrelic-infra", + command: "foo-bar", ppid: 1, numThreads: 12, state: "S", diff --git a/pkg/internal/infraolly/process/status.go b/pkg/internal/infraolly/process/status.go index 5daf6a089..4509ca031 100644 --- a/pkg/internal/infraolly/process/status.go +++ b/pkg/internal/infraolly/process/status.go @@ -16,15 +16,19 @@ func pslog() *slog.Logger { // Status of a process after being harvested type Status struct { - ProcessID int32 - Command string + ProcessID int32 + Command string + CommandArgs []string + CommandLine string + ExecName string + ExecPath string + User string MemoryRSSBytes int64 MemoryVMSBytes int64 CPUPercent float64 CPUUserPercent float64 CPUSystemPercent float64 - CommandLine string Status string ParentProcessID int32 ThreadCount int32 @@ -54,6 +58,18 @@ func OTELGetters(name attr.Name) (attributes.Getter[*Status, attribute.KeyValue] g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.ProcCommand).String(s.CommandLine) } + case attr.ProcExecName: + g = func(status *Status) attribute.KeyValue { + return attribute.Key(attr.ProcExecName).String(status.ExecName) + } + case attr.ProcExecPath: + g = func(status *Status) attribute.KeyValue { + return attribute.Key(attr.ProcExecPath).String(status.ExecPath) + } + case attr.ProcCommandArgs: + g = func(status *Status) attribute.KeyValue { + return attribute.Key(attr.ProcCommand).StringSlice(status.CommandArgs) + } case attr.ProcOwner: g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.ProcOwner).String(s.User) } case attr.ProcParentPid: From 8738980fea97888a56e0c2c51f8fcf6045572abc Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Wed, 5 Jun 2024 15:25:37 +0200 Subject: [PATCH 24/27] Process integration tests --- pkg/internal/export/attributes/metric.go | 2 +- pkg/internal/export/attributes/names/attrs.go | 2 -- pkg/internal/export/otel/metrics_proc.go | 14 +++++---- pkg/internal/infraolly/process/collect.go | 3 +- pkg/internal/infraolly/process/status.go | 6 ++-- .../configs/instrumenter-config-java.yml | 4 +++ test/integration/docker-compose-python.yml | 2 ++ test/integration/process_test.go | 29 +++++++++++++++++++ test/integration/suites_test.go | 6 ++++ 9 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 test/integration/process_test.go diff --git a/pkg/internal/export/attributes/metric.go b/pkg/internal/export/attributes/metric.go index ea3a61db6..3b2d269cd 100644 --- a/pkg/internal/export/attributes/metric.go +++ b/pkg/internal/export/attributes/metric.go @@ -84,7 +84,7 @@ var ( // as long as the metric name is recorgnisable. func normalizeMetric(name Section) Section { nameStr := strings.ReplaceAll(string(name), "_", ".") - for _, suffix := range []string{".bucket", ".sum", ".count", ".total"} { + for _, suffix := range []string{".ratio", ".bucket", ".sum", ".count", ".total"} { if strings.HasSuffix(nameStr, suffix) { nameStr = nameStr[:len(nameStr)-len(suffix)] break diff --git a/pkg/internal/export/attributes/names/attrs.go b/pkg/internal/export/attributes/names/attrs.go index a432b8e3d..f95bb3cae 100644 --- a/pkg/internal/export/attributes/names/attrs.go +++ b/pkg/internal/export/attributes/names/attrs.go @@ -107,8 +107,6 @@ const ( ProcOwner = Name(semconv.ProcessOwnerKey) ProcParentPid = Name(semconv.ProcessParentPIDKey) ProcPid = Name(semconv.ProcessPIDKey) - - // TODO: not yet supported attributes ProcCommandArgs = Name(semconv.ProcessCommandArgsKey) ProcExecName = Name(semconv.ProcessExecutableNameKey) ProcExecPath = Name(semconv.ProcessExecutablePathKey) diff --git a/pkg/internal/export/otel/metrics_proc.go b/pkg/internal/export/otel/metrics_proc.go index 6c3c8a363..462e28999 100644 --- a/pkg/internal/export/otel/metrics_proc.go +++ b/pkg/internal/export/otel/metrics_proc.go @@ -41,6 +41,8 @@ type procMetricsExporter struct { exporter metric.Exporter reporters ReporterPool[*procMetrics] + log *slog.Logger + attrCPUTime []attributes.Field[*process.Status, attribute.KeyValue] } @@ -88,6 +90,7 @@ func newProcMetricsExporter( clock: expire.NewCachedClock(timeNow), attrCPUTime: attributes.OpenTelemetryGetters( process.OTELGetters, attrProv.For(attributes.ProcessCPUUtilization)), + log: log, } mr.reporters = NewReporterPool[*procMetrics](cfg.Metrics.ReportersCacheLen, @@ -111,7 +114,7 @@ func newProcMetricsExporter( } func (me *procMetricsExporter) newMetricSet(service *svc.ID) (*procMetrics, error) { - log := pmlog().With("service", service) + log := me.log.With("service", service) log.Debug("creating new Metrics exporter") resources := getResourceAttrs(service) opts := []metric.Option{ @@ -153,13 +156,14 @@ func (me *procMetricsExporter) Do(in <-chan []*process.Status) { for _, s := range i { reporter, err := me.reporters.For(s.Service) if err != nil { - pmlog().Error("unexpected error creating OTEL resource. Ignoring metric", + me.log.Error("unexpected error creating OTEL resource. Ignoring metric", err, "service", s.Service) continue } - pmlog().Debug("reporting data for record", "record", s) - // TODO: support user/system/other - reporter.cpuTime.ForRecord(s).Set(s.CPUPercent) + me.log.Debug("reporting data for record", "record", s) + // TODO: support process.cpu.state=user/system/total + // TODO: add more process metrics https://opentelemetry.io/docs/specs/semconv/system/process-metrics/ + reporter.cpuTime.ForRecord(s).Set(s.CPUPercent / 100) } } } diff --git a/pkg/internal/infraolly/process/collect.go b/pkg/internal/infraolly/process/collect.go index afd2c2974..0da6e0736 100644 --- a/pkg/internal/infraolly/process/collect.go +++ b/pkg/internal/infraolly/process/collect.go @@ -18,10 +18,11 @@ import ( type CollectConfig struct { // RunMode defaults to "privileged". A non-privileged harvester will omit some information like open FDs. + // TODO: move to an upper layer RunMode RunMode // Interval between harvests - Interval time.Duration + Interval time.Duration `yaml:"interval" env:"BEYLA_PROCESSES_INTERVAL"` } // Collector returns runtime information about the currently running processes. diff --git a/pkg/internal/infraolly/process/status.go b/pkg/internal/infraolly/process/status.go index 4509ca031..4310422e6 100644 --- a/pkg/internal/infraolly/process/status.go +++ b/pkg/internal/infraolly/process/status.go @@ -56,7 +56,7 @@ func OTELGetters(name attr.Name) (attributes.Getter[*Status, attribute.KeyValue] g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.ProcCommand).String(s.Command) } case attr.ProcCommandLine: g = func(s *Status) attribute.KeyValue { - return attribute.Key(attr.ProcCommand).String(s.CommandLine) + return attribute.Key(attr.ProcCommandLine).String(s.CommandLine) } case attr.ProcExecName: g = func(status *Status) attribute.KeyValue { @@ -68,7 +68,7 @@ func OTELGetters(name attr.Name) (attributes.Getter[*Status, attribute.KeyValue] } case attr.ProcCommandArgs: g = func(status *Status) attribute.KeyValue { - return attribute.Key(attr.ProcCommand).StringSlice(status.CommandArgs) + return attribute.Key(attr.ProcCommandArgs).StringSlice(status.CommandArgs) } case attr.ProcOwner: g = func(s *Status) attribute.KeyValue { return attribute.Key(attr.ProcOwner).String(s.User) } @@ -78,7 +78,7 @@ func OTELGetters(name attr.Name) (attributes.Getter[*Status, attribute.KeyValue] } case attr.ProcPid: g = func(s *Status) attribute.KeyValue { - return attribute.Key(attr.ProcParentPid).Int(int(s.ProcessID)) + return attribute.Key(attr.ProcPid).Int(int(s.ProcessID)) } } return g, g != nil diff --git a/test/integration/configs/instrumenter-config-java.yml b/test/integration/configs/instrumenter-config-java.yml index 0d6b7f87e..dfc7a4157 100644 --- a/test/integration/configs/instrumenter-config-java.yml +++ b/test/integration/configs/instrumenter-config-java.yml @@ -4,3 +4,7 @@ routes: unmatched: path otel_metrics_export: endpoint: http://otelcol:4318 +attributes: + select: + process_cpu_utilization: + include: ["*"] \ No newline at end of file diff --git a/test/integration/docker-compose-python.yml b/test/integration/docker-compose-python.yml index 6e97994c8..84f6ea723 100644 --- a/test/integration/docker-compose-python.yml +++ b/test/integration/docker-compose-python.yml @@ -41,6 +41,8 @@ services: BEYLA_METRICS_REPORT_PEER: "true" BEYLA_HOSTNAME: "beyla" BEYLA_BPF_HTTP_REQUEST_TIMEOUT: "5s" + BEYLA_PROCESSES_INTERVAL: "100ms" + BEYLA_OTEL_METRICS_FEATURES: "application,application_process" depends_on: testserver: condition: service_started diff --git a/test/integration/process_test.go b/test/integration/process_test.go new file mode 100644 index 000000000..774f2fcf0 --- /dev/null +++ b/test/integration/process_test.go @@ -0,0 +1,29 @@ +package integration + +import ( + "testing" + + "github.com/mariomac/guara/pkg/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/grafana/beyla/test/integration/components/prom" +) + +func testProcesses(attribMatcher map[string]string) func(t *testing.T) { + return func(t *testing.T) { + pq := prom.Client{HostPort: prometheusHostPort} + var results []prom.Result + test.Eventually(t, testTimeout, func(t require.TestingT) { + var err error + results, err = pq.Query(`process_cpu_utilization_ratio`) + require.NoError(t, err) + assert.NotEmpty(t, results) + for _, result := range results { + for k, v := range attribMatcher { + assert.Equalf(t, v, result.Metric[k], "attribute %v expected to be %v", k, v) + } + } + }) + } +} diff --git a/test/integration/suites_test.go b/test/integration/suites_test.go index 91f321967..ee57743e5 100644 --- a/test/integration/suites_test.go +++ b/test/integration/suites_test.go @@ -400,6 +400,12 @@ func TestSuite_Python(t *testing.T) { require.NoError(t, compose.Up()) t.Run("Python RED metrics", testREDMetricsPythonHTTP) t.Run("Python RED metrics with timeouts", testREDMetricsTimeoutPythonHTTP) + t.Run("Checking process metrics", testProcesses(map[string]string{ + "process_executable_name": "python", + "process_executable_path": "/usr/local/bin/python", + "process_command": "gunicorn", + "process_command_line": "/usr/local/bin/python /usr/local/bin/gunicorn -w 4 -b 0.0.0.0:8380 main:app --timeout 90", + })) t.Run("BPF pinning folder mounted", testBPFPinningMounted) require.NoError(t, compose.Close()) t.Run("BPF pinning folder unmounted", testBPFPinningUnmounted) From 4c7cd6f38f7f63835c44fdba16f10be1f3165f3d Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Wed, 5 Jun 2024 15:47:22 +0200 Subject: [PATCH 25/27] Fix unit test --- pkg/internal/infraolly/process/harvest_test.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pkg/internal/infraolly/process/harvest_test.go b/pkg/internal/infraolly/process/harvest_test.go index ea13047b6..091c8d351 100644 --- a/pkg/internal/infraolly/process/harvest_test.go +++ b/pkg/internal/infraolly/process/harvest_test.go @@ -10,7 +10,6 @@ import ( "math" "os" "os/exec" - "strings" "testing" "time" @@ -49,7 +48,7 @@ func TestLinuxHarvester_IsPrivileged(t *testing.T) { } } -func TestLinuxHarvester_Do(t *testing.T) { +func TestLinuxHarvester_Harvest(t *testing.T) { // Given a process harvester cache, _ := simplelru.NewLRU[int32, *linuxProcess](math.MaxInt, nil) h := newHarvester(&CollectConfig{}, cache) @@ -73,7 +72,7 @@ func TestLinuxHarvester_Do(t *testing.T) { assert.NotZero(t, status.ThreadCount) } -func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { +func TestLinuxHarvester_Harvest_FullCommandLine(t *testing.T) { cmd := exec.Command("/bin/sleep", "1m") require.NoError(t, cmd.Start()) defer func() { @@ -92,8 +91,11 @@ func TestLinuxHarvester_Do_FullCommandLine(t *testing.T) { require.NoError(t, err) require.NotNil(t, status) - assert.False(t, strings.HasSuffix(status.CommandLine, "sleep"), "%q should have arguments", status.CommandLine) - assert.Contains(t, status.CommandLine, "sleep") + assert.Equal(t, "sleep", status.ExecName) + assert.Equal(t, "/bin/sleep", status.ExecPath) + assert.Equal(t, "/bin/sleep 1m", status.CommandLine) + assert.Equal(t, "sleep", status.Command) + assert.Equal(t, []string{"1m"}, status.CommandArgs) }) } From 7720949acdcb6dfcfdefd83397738f028e6423ec Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Wed, 5 Jun 2024 15:59:40 +0200 Subject: [PATCH 26/27] fix integration test --- test/integration/process_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/process_test.go b/test/integration/process_test.go index 774f2fcf0..4f978d07b 100644 --- a/test/integration/process_test.go +++ b/test/integration/process_test.go @@ -1,3 +1,5 @@ +//go:build integration + package integration import ( From 2833a13a69eb276e20f66a3dff0200105b054a4c Mon Sep 17 00:00:00 2001 From: Mario Macias Date: Wed, 5 Jun 2024 16:56:55 +0200 Subject: [PATCH 27/27] Added extra comment --- pkg/internal/svc/svc.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/internal/svc/svc.go b/pkg/internal/svc/svc.go index 590ab66b4..1b97c2e0e 100644 --- a/pkg/internal/svc/svc.go +++ b/pkg/internal/svc/svc.go @@ -65,6 +65,9 @@ type ID struct { Metadata map[attr.Name]string + // ProcPID is the PID of the instrumented process as seen by Beyla's /proc filesystem. + // It is stored here at process discovery time, because it might differ form the + // UserPID and HostPID fields of the request.PidInfo struct. ProcPID int32 }