Update dependencies and bump Go to v1.24.10

This commit is contained in:
Ciprian Hacman
2025-11-29 11:15:51 +02:00
parent b0d347e262
commit 6b19393e1c
362 changed files with 20314 additions and 29260 deletions
+40 -45
View File
@@ -8,12 +8,14 @@ import (
"errors"
"fmt"
"math/bits"
"path/filepath"
"strconv"
"strings"
"syscall"
"unsafe"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
"github.com/shirou/gopsutil/v4/internal/common"
)
@@ -75,6 +77,8 @@ const (
smbiosEndOfTable = 127 // Minimum length for processor structure
smbiosTypeProcessor = 4 // SMBIOS Type 4: Processor Information
smbiosProcessorMinLength = 0x18 // Minimum length for processor structure
centralProcessorRegistryKey = `HARDWARE\DESCRIPTION\System\CentralProcessor`
)
type relationship uint32
@@ -179,61 +183,27 @@ func getProcessorPowerInformation(ctx context.Context) ([]processorPowerInformat
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: strconv.FormatUint(uint64(l.Family), 10),
VendorID: l.Manufacturer,
ModelName: l.Name,
Cores: int32(l.NumberOfLogicalProcessors), // TO BE REMOVED, set by getSystemLogicalProcessorInformationEx
PhysicalID: procID,
Mhz: float64(l.MaxClockSpeed),
Flags: []string{},
}
ret = append(ret, cpu)
}
processorPackages, err := getSystemLogicalProcessorInformationEx(relationProcessorPackage)
if err != nil {
// return an error whem wmi will be removed
// return ret, fmt.Errorf("failed to get processor package information: %w", err)
return ret, nil
}
if len(processorPackages) != len(ret) {
// this should never happen, but it's kept for safety until wmi is removed
return ret, nil
return ret, fmt.Errorf("failed to get processor package information: %w", err)
}
ppis, powerInformationErr := getProcessorPowerInformation(ctx)
if powerInformationErr != nil {
// return an error whem wmi will be removed
// return ret, fmt.Errorf("failed to get processor power information: %w", err)
return ret, nil
return ret, fmt.Errorf("failed to get processor power information: %w", err)
}
family, processorId, smBIOSErr := getSMBIOSProcessorInfo()
if smBIOSErr != nil {
// return an error whem wmi will be removed
// return ret, smBIOSErr
return ret, nil
return ret, smBIOSErr
}
for i, pkg := range processorPackages {
logicalCount := 0
maxMhz := 0
model := ""
vendorId := ""
// iterate over each set bit in the package affinity mask
for _, ga := range pkg.processor.groupMask {
g := int(ga.group)
forEachSetBit64(uint64(ga.mask), func(bit int) {
@@ -246,12 +216,26 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
maxMhz = m
}
}
registryKeyPath := filepath.Join(centralProcessorRegistryKey, strconv.Itoa(globalLpl))
key, err := registry.OpenKey(registry.LOCAL_MACHINE, registryKeyPath, registry.QUERY_VALUE|registry.READ)
if err == nil {
model = getRegistryStringValueIfUnset(key, "ProcessorNameString", model)
vendorId = getRegistryStringValueIfUnset(key, "VendorIdentifier", vendorId)
_ = key.Close()
}
})
}
ret[i].Mhz = float64(maxMhz)
ret[i].Cores = int32(logicalCount)
ret[i].Family = strconv.FormatUint(uint64(family), 10)
ret[i].PhysicalID = processorId
ret = append(ret, InfoStat{
CPU: int32(i),
Family: strconv.FormatUint(uint64(family), 10),
VendorID: vendorId,
ModelName: model,
Cores: int32(logicalCount),
PhysicalID: processorId,
Mhz: float64(maxMhz),
Flags: []string{},
})
}
return ret, nil
@@ -461,6 +445,17 @@ func getPhysicalCoreCount() (int, error) {
return len(infos), err
}
func getRegistryStringValueIfUnset(key registry.Key, keyName, value string) string {
if value != "" {
return value
}
val, _, err := key.GetStringValue(keyName)
if err == nil {
return strings.TrimSpace(val)
}
return ""
}
func CountsWithContext(_ context.Context, logical bool) (int, error) {
if logical {
// Get logical processor count https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L97
+130 -107
View File
@@ -8,7 +8,7 @@ import (
"context"
"errors"
"fmt"
"reflect"
"strings"
"syscall"
"unicode/utf16"
"unsafe"
@@ -20,9 +20,21 @@ import (
)
const (
volumeNameBufferLength = uint32(windows.MAX_PATH + 1)
volumePathBufferLength = volumeNameBufferLength
maxWarningsInDrive = 5
maxVolumeNameLength = uint32(windows.MAX_PATH + 1) // this should be a GUID (50), but for safety I keep max_path
maxFileSystemNameLength = uint32(windows.MAX_PATH + 1)
maxWarningsInDrive = 5
)
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationa#parameters
const (
rw = "rw"
ro = "ro"
compress = "compress"
)
const (
firstPossibleDriveLetter = 'A'
lastPossibleDriveLetter = 'Z'
)
var (
@@ -96,110 +108,108 @@ func UsageWithContext(_ context.Context, path string) (*UsageStat, error) {
// PartitionsWithContext returns disk partitions.
// It uses procGetLogicalDriveStringsW to get drives with drive letters and procFindFirstVolumeW to get volumes without drive letters.
// Since the api calls don't have a timeout, this method uses context to set deadline by users.
func PartitionsWithContext(ctx context.Context, _ bool) ([]PartitionStat, error) {
func PartitionsWithContext(_ context.Context, _ bool) ([]PartitionStat, error) {
warnings := Warnings{Verbose: true}
var errInitialCall error
retChan := make(chan PartitionStat)
quitChan := make(chan struct{})
defer close(quitChan)
processedPaths := make(map[string]struct{})
partitionStats := []PartitionStat{}
getPartitions := func() {
defer close(retChan)
// Get drives with drive letters (including remote drives, ex: SMB shares)
drives, err := getLogicalDrives()
if err != nil {
return partitionStats, err
}
// Get drives with drive letters (including remote drives, ex: SMB shares)
lpBuffer := make([]byte, 254)
if diskret, _, err := procGetLogicalDriveStringsW.Call(
uintptr(len(lpBuffer)),
uintptr(unsafe.Pointer(&lpBuffer[0]))); diskret == 0 {
errInitialCall = err
return
}
for _, v := range lpBuffer {
if v >= 65 && v <= 90 {
path := string(v) + ":"
if partitionStat, warning := buildPartitionStat(path); warning == nil {
processedPaths[partitionStat.Mountpoint+"\\"] = struct{}{}
select {
case retChan <- partitionStat:
case <-quitChan:
return
}
} else {
warnings.Add(warning)
}
}
partitionStats = processLogicalDrives(drives, processedPaths, partitionStats, warnings)
// Get volumes without drive letters (ex: mounted folders with no drive letter)
partitionStats = processVolumesMountedAsFolders(partitionStats, warnings, processedPaths)
return partitionStats, warnings.Reference()
}
func processVolumesMountedAsFolders(partitionStats []PartitionStat, warnings Warnings, processedPaths map[string]struct{}) []PartitionStat {
volNameBuf := make([]uint16, maxVolumeNameLength)
nextVolHandle, _, err := procFindFirstVolumeW.Call(
uintptr(unsafe.Pointer(&volNameBuf[0])),
uintptr(maxVolumeNameLength))
if windows.Handle(nextVolHandle) == windows.InvalidHandle {
warnings.Add(fmt.Errorf("failed to get first-volume: %w", err))
return partitionStats
}
defer procFindVolumeClose.Call(nextVolHandle)
for {
mounts, err := getVolumePaths(volNameBuf)
if err != nil {
warnings.Add(fmt.Errorf("failed to find paths for volume %s", windows.UTF16ToString(volNameBuf)))
continue
}
// Get volumes without drive letters (ex: mounted folders with no drive letter)
volNameBuf := make([]uint16, volumeNameBufferLength)
nextVolHandle, _, err := procFindFirstVolumeW.Call(
uintptr(unsafe.Pointer(&volNameBuf[0])),
uintptr(volumeNameBufferLength))
if windows.Handle(nextVolHandle) == windows.InvalidHandle {
errInitialCall = fmt.Errorf("failed to get first-volume: %w", err)
return
}
defer procFindVolumeClose.Call(nextVolHandle)
for {
mounts, err := getVolumePaths(volNameBuf)
if err != nil {
warnings.Add(fmt.Errorf("failed to find paths for volume %s", windows.UTF16ToString(volNameBuf)))
for _, mount := range mounts {
if _, ok := processedPaths[mount]; ok {
continue
}
for _, mount := range mounts {
if _, ok := processedPaths[mount]; ok {
continue
}
if partitionStat, warning := buildPartitionStat(mount); warning == nil {
select {
case retChan <- partitionStat:
case <-quitChan:
return
}
} else {
warnings.Add(warning)
}
if partitionStat, warning := buildPartitionStat(mount); warning == nil {
partitionStats = append(partitionStats, partitionStat)
} else {
warnings.Add(warning)
}
}
volNameBuf = make([]uint16, volumeNameBufferLength)
if volRet, _, err := procFindNextVolumeW.Call(
nextVolHandle,
uintptr(unsafe.Pointer(&volNameBuf[0])),
uintptr(volumeNameBufferLength)); err != nil && volRet == 0 {
var errno syscall.Errno
if errors.As(err, &errno) && errno == windows.ERROR_NO_MORE_FILES {
break
}
warnings.Add(fmt.Errorf("failed to find next volume: %w", err))
if len(warnings.List) > maxWarningsInDrive {
break
}
volNameBuf = make([]uint16, maxVolumeNameLength)
if volRet, _, err := procFindNextVolumeW.Call(
nextVolHandle,
uintptr(unsafe.Pointer(&volNameBuf[0])),
uintptr(maxVolumeNameLength)); err != nil && volRet == 0 {
var errno syscall.Errno
if errors.As(err, &errno) && errno == windows.ERROR_NO_MORE_FILES {
break
}
warnings.Add(fmt.Errorf("failed to find next volume: %w", err))
if len(warnings.List) > maxWarningsInDrive {
break
}
}
}
return partitionStats
}
go getPartitions()
var ret []PartitionStat
for {
select {
case p, ok := <-retChan:
if !ok {
if errInitialCall != nil {
return ret, errInitialCall
}
return ret, warnings.Reference()
func processLogicalDrives(drives []string, processedPaths map[string]struct{}, partitionStats []PartitionStat, warnings Warnings) []PartitionStat {
for _, drive := range drives {
if drive != "" && drive[0] >= firstPossibleDriveLetter && drive[0] <= lastPossibleDriveLetter {
v := drive[0]
path := string(v) + ":"
if partitionStat, warning := buildPartitionStat(path); warning == nil {
processedPaths[partitionStat.Mountpoint+"\\"] = struct{}{}
partitionStats = append(partitionStats, partitionStat)
} else {
warnings.Add(warning)
}
if !reflect.DeepEqual(p, PartitionStat{}) {
ret = append(ret, p)
}
case <-ctx.Done():
return ret, ctx.Err()
}
}
return partitionStats
}
// getLogicalDrives retrieves all logical drives using GetLogicalDriveStringsW.
// We first call GetLogicalDriveStringsW with a buffer length of 0 to get the required buffer size.
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlogicaldrivestringsw
func getLogicalDrives() ([]string, error) {
bufferLen, _, err := procGetLogicalDriveStringsW.Call(
uintptr(0),
uintptr(0))
if !errors.Is(err, windows.ERROR_SUCCESS) {
return nil, err // The call failed with an unexpected error
}
lpBuffer := make([]uint16, bufferLen)
// buffer can be longer than MAX_PATH
_, _, err = procGetLogicalDriveStringsW.Call(
uintptr(len(lpBuffer)),
uintptr(unsafe.Pointer(&lpBuffer[0])))
if !errors.Is(err, windows.ERROR_SUCCESS) {
return nil, err // The call failed with an unexpected error
}
drivesString := windows.UTF16ToString(lpBuffer)
drives := strings.Split(drivesString, "\x00")
return drives, nil
}
func buildPartitionStat(path string) (PartitionStat, error) {
@@ -213,16 +223,17 @@ func buildPartitionStat(path string) (PartitionStat, error) {
if driveType == windows.DRIVE_REMOVABLE || driveType == windows.DRIVE_FIXED ||
driveType == windows.DRIVE_REMOTE || driveType == windows.DRIVE_CDROM {
volPath, _ := windows.UTF16PtrFromString(path + "/")
volumeName := make([]byte, 256)
fsName := make([]byte, 256)
var serialNumber, maxComponentLength, fsFlags int64
volumeName := make([]byte, maxVolumeNameLength)
fsName := make([]byte, maxFileSystemNameLength)
var fsFlags int64
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationw
ret, _, err := procGetVolumeInformation.Call(
uintptr(unsafe.Pointer(volPath)),
uintptr(unsafe.Pointer(&volumeName[0])),
uintptr(len(volumeName)),
uintptr(unsafe.Pointer(&serialNumber)),
uintptr(unsafe.Pointer(&maxComponentLength)),
uintptr(0), // serial number
uintptr(0), // max component length
uintptr(unsafe.Pointer(&fsFlags)),
uintptr(unsafe.Pointer(&fsName[0])),
uintptr(len(fsName)),
@@ -235,12 +246,12 @@ func buildPartitionStat(path string) (PartitionStat, error) {
return PartitionStat{}, err
}
opts := []string{"rw"}
opts := []string{rw}
if fsFlags&fileReadOnlyVolume != 0 {
opts = []string{"ro"}
opts = []string{ro}
}
if fsFlags&fileFileCompression != 0 {
opts = append(opts, "compress")
opts = append(opts, compress)
}
return PartitionStat{
@@ -265,7 +276,7 @@ func IOCountersWithContext(_ context.Context, names ...string) (map[string]IOCou
return drivemap, err
}
for _, v := range lpBuffer[:lpBufferLen] {
if v < 'A' || v > 'Z' {
if v < firstPossibleDriveLetter || v > lastPossibleDriveLetter {
continue
}
path := string(rune(v)) + ":"
@@ -276,7 +287,7 @@ func IOCountersWithContext(_ context.Context, names ...string) (map[string]IOCou
}
szDevice := `\\.\` + path
const IOCTL_DISK_PERFORMANCE = 0x70020
h, err := windows.CreateFile(syscall.StringToUTF16Ptr(szDevice), 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE, nil, windows.OPEN_EXISTING, 0, 0)
h, err := windows.CreateFile(windows.StringToUTF16Ptr(szDevice), 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE, nil, windows.OPEN_EXISTING, 0, 0)
if err != nil {
if errors.Is(err, windows.ERROR_FILE_NOT_FOUND) {
continue
@@ -316,16 +327,28 @@ func LabelWithContext(_ context.Context, _ string) (string, error) {
// getVolumePaths returns the path for the given volume name.
func getVolumePaths(volNameBuf []uint16) ([]string, error) {
volPathsBuf := make([]uint16, volumePathBufferLength)
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumepathnamesforvolumenamew
// Consider that NTFS supports paths longer than windows.MAX_PATH
returnLen := uint32(0)
if result, _, err := procGetVolumePathNamesForVolumeNameW.Call(
firstResult, _, volumePathFirstErr := procGetVolumePathNamesForVolumeNameW.Call(
uintptr(unsafe.Pointer(&volNameBuf[0])),
uintptr(0),
uintptr(0),
uintptr(unsafe.Pointer(&returnLen)))
if firstResult == 0 && !errors.Is(volumePathFirstErr, windows.ERROR_MORE_DATA) {
return nil, fmt.Errorf("failed to get volume paths size for volume %s: %w", windows.UTF16ToString(volNameBuf), volumePathFirstErr)
}
volPathsBuf := make([]uint16, returnLen)
ok, _, volumePathNamesErr := procGetVolumePathNamesForVolumeNameW.Call(
uintptr(unsafe.Pointer(&volNameBuf[0])),
uintptr(unsafe.Pointer(&volPathsBuf[0])),
uintptr(volumePathBufferLength),
uintptr(unsafe.Pointer(&returnLen))); err != nil && result == 0 {
return nil, err
uintptr(returnLen),
uintptr(unsafe.Pointer(&returnLen)))
if ok != 0 {
return split0(volPathsBuf, int(returnLen)), nil
}
return split0(volPathsBuf, int(returnLen)), nil
return nil, fmt.Errorf("failed to get volume paths for volume %s: %w", windows.UTF16ToString(volNameBuf), volumePathNamesErr)
}
// split0 iterates through s16 upto `end` and slices `s16` into sub-slices separated by the null character (uint16(0)).
-1
View File
@@ -1,5 +1,4 @@
//go:build aix && ppc64 && cgo
// +build aix,ppc64,cgo
// Guessed at from the following document:
// https://www.ibm.com/docs/sl/ibm-mq/9.2?topic=platforms-standard-data-types-aix-linux-windows
+1 -1
View File
@@ -442,7 +442,7 @@ func HostRootWithContext(ctx context.Context, combineWith ...string) string {
}
// getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running
// sysctl commands (see DoSysctrl).
// sysctl commands.
func getSysctrlEnv(env []string) []string {
foundLC := false
for i, line := range env {
-18
View File
@@ -4,32 +4,14 @@
package common
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"strings"
"unsafe"
"github.com/ebitengine/purego"
"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))
-17
View File
@@ -5,9 +5,6 @@ package common
import (
"fmt"
"os"
"os/exec"
"strings"
"unsafe"
"golang.org/x/sys/unix"
@@ -28,20 +25,6 @@ func SysctlUint(mib string) (uint64, error) {
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))
-15
View File
@@ -7,7 +7,6 @@ import (
"context"
"errors"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
@@ -20,20 +19,6 @@ import (
// 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())
}
-17
View File
@@ -4,28 +4,11 @@
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))
-17
View File
@@ -4,28 +4,11 @@
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))
+7 -2
View File
@@ -1,7 +1,10 @@
// SPDX-License-Identifier: BSD-3-Clause
package common
import "fmt"
import (
"fmt"
"strings"
)
const (
maxWarnings = 100 // An arbitrary limit to avoid excessive memory usage, it has no sense to store hundreds of errors
@@ -33,9 +36,11 @@ func (w *Warnings) Reference() error {
func (w *Warnings) Error() string {
if w.Verbose {
str := ""
var sb strings.Builder
for i, e := range w.List {
str += fmt.Sprintf("\tError %d: %s\n", i, e.Error())
sb.WriteString(fmt.Sprintf("\tError %d: %s\n", i, e.Error()))
}
str += sb.String()
if w.tooManyErrors {
str += fmt.Sprintf("\t%s\n", tooManyErrorsMessage)
}
+6 -7
View File
@@ -50,26 +50,25 @@ func IOCountersByFileWithContext(_ context.Context, pernic bool, filename string
return nil, err
}
parts := make([]string, 2)
statlen := len(lines) - 1
ret := make([]IOCountersStat, 0, statlen)
for _, line := range lines[2:] {
// Split interface name and stats data at the last ":"
separatorPos := strings.LastIndex(line, ":")
if separatorPos == -1 {
continue
}
parts[0] = line[0:separatorPos]
parts[1] = line[separatorPos+1:]
interfacePart := line[0:separatorPos]
statsPart := line[separatorPos+1:]
interfaceName := strings.TrimSpace(parts[0])
interfaceName := strings.TrimSpace(interfacePart)
if interfaceName == "" {
continue
}
fields := strings.Fields(strings.TrimSpace(parts[1]))
fields := strings.Fields(strings.TrimSpace(statsPart))
bytesRecv, err := strconv.ParseUint(fields[0], 10, 64)
if err != nil {
return ret, err
@@ -610,7 +609,7 @@ func getProcInodesAllWithContext(ctx context.Context, root string, maxConn int)
return ret, nil
}
// decodeAddress decode addresse represents addr in proc/net/*
// decodeAddress decode address represents addr in proc/net/*
// ex:
// "0500000A:0016" -> "10.0.0.5", 22
// "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53
+1 -1
View File
@@ -237,7 +237,7 @@ func (p *Process) getKProc() (*unix.KinfoProc, error) {
// call ps command.
// Return value deletes Header line(you must not input wrong arg).
// And splited by Space. Caller have responsibility to manage.
// And split 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, nameOption bool) ([][]string, error) {
var cmd []string