Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions pkg/process/checks/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,11 @@ func fmtProcesses(

// Hide disallow-listed args if the Scrubber is enabled
fp.Cmdline = scrubber.ScrubProcessCommand(fp)
var voluntaryCtxSwitches, involuntaryCtxSwitches uint64
if fp.Stats.CtxSwitches != nil {
voluntaryCtxSwitches = uint64(fp.Stats.CtxSwitches.Voluntary)
involuntaryCtxSwitches = uint64(fp.Stats.CtxSwitches.Involuntary)
}
proc := &model.Process{
Pid: fp.Pid,
NsPid: fp.NsPid,
Expand All @@ -506,8 +511,8 @@ func fmtProcesses(
OpenFdCount: fp.Stats.OpenFdCount,
State: model.ProcessState(model.ProcessState_value[fp.Stats.Status]),
IoStat: formatIO(fp.Stats, lastProcs[fp.Pid].Stats.IOStat, now, lastRun),
VoluntaryCtxSwitches: uint64(fp.Stats.CtxSwitches.Voluntary),
InvoluntaryCtxSwitches: uint64(fp.Stats.CtxSwitches.Involuntary),
VoluntaryCtxSwitches: voluntaryCtxSwitches,
InvoluntaryCtxSwitches: involuntaryCtxSwitches,
ContainerId: ctrByProc[int(fp.Pid)],
ProcessContext: serviceExtractor.GetServiceContext(fp.Pid),
// SERVICE DISCOVERY FIELDS
Expand Down
9 changes: 7 additions & 2 deletions pkg/process/checks/process_rt.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ func fmtProcessStats(
ioStat = formatIO(fp, lastProcs[pid].IOStat, now, lastRun)
}

var voluntaryCtxSwitches, involuntaryCtxSwitches uint64
if fp.CtxSwitches != nil {
voluntaryCtxSwitches = uint64(fp.CtxSwitches.Voluntary)
involuntaryCtxSwitches = uint64(fp.CtxSwitches.Involuntary)
}
stat := &model.ProcessStat{
Pid: pid,
CreateTime: fp.CreateTime,
Expand All @@ -134,8 +139,8 @@ func fmtProcessStats(
OpenFdCount: fp.OpenFdCount,
ProcessState: model.ProcessState(model.ProcessState_value[fp.Status]),
IoStat: ioStat,
VoluntaryCtxSwitches: uint64(fp.CtxSwitches.Voluntary),
InvoluntaryCtxSwitches: uint64(fp.CtxSwitches.Involuntary),
VoluntaryCtxSwitches: voluntaryCtxSwitches,
InvoluntaryCtxSwitches: involuntaryCtxSwitches,
ContainerId: pidToCid[int(pid)],
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/process/checks/system_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build !windows && !darwin
//go:build !windows && !darwin && !aix

package checks

Expand Down
51 changes: 51 additions & 0 deletions pkg/process/checks/system_info_aix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build aix

package checks

import (
model "github.com/DataDog/agent-payload/v5/process"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/host"
"github.com/shirou/gopsutil/v4/mem"
)

// CollectSystemInfo collects a set of system-level information that will not
// change until a restart. On AIX, fields that cannot be retrieved are left empty.
func CollectSystemInfo() (*model.SystemInfo, error) {
hi, err := host.Info()
if err != nil || hi == nil {
hi = &host.InfoStat{}
}

cpuInfo, _ := cpu.Info()

mi, err := mem.VirtualMemory()
if err != nil || mi == nil {
mi = &mem.VirtualMemoryStat{}
}

cpus := make([]*model.CPUInfo, 0, len(cpuInfo))
for _, c := range cpuInfo {
cpus = append(cpus, &model.CPUInfo{
Cores: c.Cores,
})
}

return &model.SystemInfo{
Uuid: hi.HostID,
Os: &model.OSInfo{
Name: hi.OS,
Platform: hi.Platform,
Family: hi.PlatformFamily,
Version: hi.PlatformVersion,
KernelVersion: hi.KernelVersion,
},
Cpus: cpus,
TotalMemory: int64(mi.Total),
}, nil
}
214 changes: 214 additions & 0 deletions pkg/process/procutil/process_aix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build aix

package procutil

// Process collection for AIX via /proc/<pid>/psinfo.
//
// AIX exposes a binary psinfo_t structure at /proc/<pid>/psinfo (world-readable,
// 448 bytes, big-endian). This file reads that structure directly without
// shelling out or requiring elevated privileges.
//
// The struct layout is taken from /usr/include/sys/procfs.h on AIX 7.x ppc64.

import (
"encoding/binary"
"errors"
"fmt"
"os"
"strconv"
"strings"
"time"
)

// prTimestruc64 mirrors AIX timestruc64_t: { tv_sec int64; tv_nsec int32; _pad uint32 }
type prTimestruc64 struct {
Sec int64
Nsec int32
_ uint32
}

// lwpSinfo mirrors the representative LWP entry inside psinfo_t (120 bytes).
type lwpSinfo struct {
LwpID uint64
Addr uint64
Wchan uint64
Flag uint32
Wtype uint8
State int8
Sname byte // process state character: 'O','R','S','Z', etc.
Nice uint8
Pri int32
Policy uint32
Clname [8]byte
Onpro int32
Bindpro int32
Ptid uint32
_ uint32
_ [7]uint64
}

// psinfo mirrors AIX psinfo_t from /usr/include/sys/procfs.h (448 bytes, big-endian ppc64).
type psinfo struct {
Flag uint32
Flag2 uint32
Nlwp uint32 // number of threads
_ uint32
Uid uint64
Euid uint64
Gid uint64
Egid uint64
Pid uint64
Ppid uint64
Pgid uint64
Sid uint64
Ttydev uint64
Addr uint64
Size uint64 // virtual memory size in pages
Rssize uint64 // resident set size in pages
Start prTimestruc64 // process start time
Time prTimestruc64 // combined user+system CPU time
Cid uint16
_ uint16
Argc uint32
Argv uint64
Envp uint64
Fname [16]byte // executable name, null-terminated
Psargs [80]byte // process args, space-separated, null-terminated
_ [8]uint64
Lwp lwpSinfo // representative LWP
}

func readPsinfo(pid int32) (*psinfo, error) {
f, err := os.Open(fmt.Sprintf("/proc/%d/psinfo", pid))
if err != nil {
return nil, err
}
defer f.Close()

var psi psinfo
if err := binary.Read(f, binary.BigEndian, &psi); err != nil {
return nil, err
}
return &psi, nil
}

func nullTermBytes(b []byte) string {
for i, c := range b {
if c == 0 {
return string(b[:i])
}
}
return string(b)
}

func listPIDs() ([]int32, error) {
dir, err := os.Open("/proc")
if err != nil {
return nil, err
}
defer dir.Close()

names, err := dir.Readdirnames(-1)
if err != nil {
return nil, err
}

pids := make([]int32, 0, len(names))
for _, name := range names {
pid, err := strconv.ParseInt(name, 10, 32)
if err != nil {
continue // skip non-numeric entries (e.g. "net", "sys")
}
pids = append(pids, int32(pid))
}
return pids, nil
}

func psinfoToProcess(psi *psinfo, pid int32, pageSize int) *Process {
name := nullTermBytes(psi.Fname[:])
args := nullTermBytes(psi.Psargs[:])

var cmdline []string
if args != "" {
cmdline = strings.Fields(args)
}

cpuSecs := float64(psi.Time.Sec) + float64(psi.Time.Nsec)/1e9

return &Process{
Pid: pid,
Ppid: int32(psi.Ppid),
Name: name,
Cmdline: cmdline,
Uids: []int32{int32(psi.Uid), int32(psi.Euid), int32(psi.Uid), int32(psi.Euid)},
Gids: []int32{int32(psi.Gid), int32(psi.Egid), int32(psi.Gid), int32(psi.Egid)},
Stats: &Stats{
CreateTime: psi.Start.Sec * 1000, // milliseconds since epoch
Status: string([]byte{psi.Lwp.Sname}),
Nice: int32(int8(psi.Lwp.Nice)),
NumThreads: int32(psi.Nlwp),
CPUTime: &CPUTimesStat{
// psinfo only provides combined user+sys time; no per-category split.
User: cpuSecs,
},
MemInfo: &MemoryInfoStat{
RSS: uint64(psi.Rssize) * uint64(pageSize),
VMS: uint64(psi.Size) * uint64(pageSize),
Comment thread
pgimalac marked this conversation as resolved.
Outdated
},
// IOStat, MemInfoEx, CtxSwitches are not available without root on AIX.
},
}
}

// NewProcessProbe returns a Probe for AIX.
func NewProcessProbe(options ...Option) Probe {
p := &probe{}
for _, option := range options {
option(p)
}
return p
}

type probe struct{}

func (p *probe) Close() {}

func (p *probe) ProcessesByPID(_ time.Time, _ bool) (map[int32]*Process, error) {
pids, err := listPIDs()
if err != nil {
return nil, fmt.Errorf("aix ProcessesByPID: could not list pids: %w", err)
}

pageSize := os.Getpagesize()
result := make(map[int32]*Process, len(pids))
for _, pid := range pids {
psi, err := readPsinfo(pid)
if err != nil {
// Process may have exited between listPIDs and now; skip it.
continue
}
result[pid] = psinfoToProcess(psi, pid, pageSize)
}
return result, nil
}

func (p *probe) StatsForPIDs(_ []int32, _ time.Time) (map[int32]*Stats, error) {
procs, err := p.ProcessesByPID(time.Now(), false)
if err != nil {
return nil, err
}
stats := make(map[int32]*Stats, len(procs))
for pid, proc := range procs {
stats[pid] = proc.Stats
}
return stats, nil
}

func (p *probe) StatsWithPermByPID(_ []int32) (map[int32]*StatsWithPerm, error) {
return nil, errors.New("StatsWithPermByPID is not implemented on AIX")
}
2 changes: 1 addition & 1 deletion pkg/process/procutil/process_fallback.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build !linux && !windows && !darwin
//go:build !linux && !windows && !darwin && !aix

package procutil

Expand Down
Loading