Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
oshi.software.os.unix.freebsd.FreeBsdOSProcess Maven / Gradle / Ivy
/*
* Copyright 2020-2023 The OSHI Project Contributors
* SPDX-License-Identifier: MIT
*/
package oshi.software.os.unix.freebsd;
import static oshi.software.os.OSProcess.State.INVALID;
import static oshi.software.os.OSProcess.State.OTHER;
import static oshi.software.os.OSProcess.State.RUNNING;
import static oshi.software.os.OSProcess.State.SLEEPING;
import static oshi.software.os.OSProcess.State.STOPPED;
import static oshi.software.os.OSProcess.State.WAITING;
import static oshi.software.os.OSProcess.State.ZOMBIE;
import static oshi.software.os.OSThread.ThreadFiltering.VALID_THREAD;
import static oshi.util.Memoizer.memoize;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.sun.jna.platform.unix.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.platform.unix.LibCAPI.size_t;
import oshi.annotation.concurrent.ThreadSafe;
import oshi.jna.ByRef.CloseableSizeTByReference;
import oshi.jna.platform.unix.FreeBsdLibc;
import oshi.software.common.AbstractOSProcess;
import oshi.software.os.OSThread;
import oshi.software.os.unix.freebsd.FreeBsdOperatingSystem.PsKeywords;
import oshi.util.ExecutingCommand;
import oshi.util.FileUtil;
import oshi.util.ParseUtil;
import oshi.util.platform.unix.freebsd.BsdSysctlUtil;
import oshi.util.platform.unix.freebsd.ProcstatUtil;
/**
* OSProcess implementation
*/
@ThreadSafe
public class FreeBsdOSProcess extends AbstractOSProcess {
private static final Logger LOG = LoggerFactory.getLogger(FreeBsdOSProcess.class);
private static final int ARGMAX = BsdSysctlUtil.sysctl("kern.argmax", 0);
private final FreeBsdOperatingSystem os;
/*
* Package-private for use by FreeBsdOSThread
*/
enum PsThreadColumns {
TDNAME, LWP, STATE, ETIMES, SYSTIME, TIME, TDADDR, NIVCSW, NVCSW, MAJFLT, MINFLT, PRI;
}
static final String PS_THREAD_COLUMNS = Arrays.stream(PsThreadColumns.values()).map(Enum::name)
.map(name -> name.toLowerCase(Locale.ROOT)).collect(Collectors.joining(","));
private Supplier bitness = memoize(this::queryBitness);
private Supplier commandLine = memoize(this::queryCommandLine);
private Supplier> arguments = memoize(this::queryArguments);
private Supplier> environmentVariables = memoize(this::queryEnvironmentVariables);
private String name;
private String path = "";
private String user;
private String userID;
private String group;
private String groupID;
private State state = INVALID;
private int parentProcessID;
private int threadCount;
private int priority;
private long virtualSize;
private long residentSetSize;
private long kernelTime;
private long userTime;
private long startTime;
private long upTime;
private long bytesRead;
private long bytesWritten;
private long minorFaults;
private long majorFaults;
private long contextSwitches;
private String commandLineBackup;
public FreeBsdOSProcess(int pid, Map psMap, FreeBsdOperatingSystem os) {
super(pid);
this.os = os;
updateAttributes(psMap);
}
@Override
public String getName() {
return this.name;
}
@Override
public String getPath() {
return this.path;
}
@Override
public String getCommandLine() {
return this.commandLine.get();
}
private String queryCommandLine() {
String cl = String.join(" ", getArguments());
return cl.isEmpty() ? this.commandLineBackup : cl;
}
@Override
public List getArguments() {
return arguments.get();
}
private List queryArguments() {
if (ARGMAX > 0) {
// Get arguments via sysctl(3)
int[] mib = new int[4];
mib[0] = 1; // CTL_KERN
mib[1] = 14; // KERN_PROC
mib[2] = 7; // KERN_PROC_ARGS
mib[3] = getProcessID();
// Allocate memory for arguments
try (Memory m = new Memory(ARGMAX);
CloseableSizeTByReference size = new CloseableSizeTByReference(ARGMAX)) {
// Fetch arguments
if (FreeBsdLibc.INSTANCE.sysctl(mib, mib.length, m, size, null, size_t.ZERO) == 0) {
return Collections.unmodifiableList(
ParseUtil.parseByteArrayToStrings(m.getByteArray(0, size.getValue().intValue())));
} else {
LOG.warn(
"Failed sysctl call for process arguments (kern.proc.args), process {} may not exist. Error code: {}",
getProcessID(), Native.getLastError());
}
}
}
return Collections.emptyList();
}
@Override
public Map getEnvironmentVariables() {
return environmentVariables.get();
}
private Map queryEnvironmentVariables() {
if (ARGMAX > 0) {
// Get environment variables via sysctl(3)
int[] mib = new int[4];
mib[0] = 1; // CTL_KERN
mib[1] = 14; // KERN_PROC
mib[2] = 35; // KERN_PROC_ENV
mib[3] = getProcessID();
// Allocate memory for environment variables
try (Memory m = new Memory(ARGMAX);
CloseableSizeTByReference size = new CloseableSizeTByReference(ARGMAX)) {
// Fetch environment variables
if (FreeBsdLibc.INSTANCE.sysctl(mib, mib.length, m, size, null, size_t.ZERO) == 0) {
return Collections.unmodifiableMap(
ParseUtil.parseByteArrayToStringMap(m.getByteArray(0, size.getValue().intValue())));
} else {
LOG.warn(
"Failed sysctl call for process environment variables (kern.proc.env), process {} may not exist. Error code: {}",
getProcessID(), Native.getLastError());
}
}
}
return Collections.emptyMap();
}
@Override
public String getCurrentWorkingDirectory() {
return ProcstatUtil.getCwd(getProcessID());
}
@Override
public String getUser() {
return this.user;
}
@Override
public String getUserID() {
return this.userID;
}
@Override
public String getGroup() {
return this.group;
}
@Override
public String getGroupID() {
return this.groupID;
}
@Override
public State getState() {
return this.state;
}
@Override
public int getParentProcessID() {
return this.parentProcessID;
}
@Override
public int getThreadCount() {
return this.threadCount;
}
@Override
public int getPriority() {
return this.priority;
}
@Override
public long getVirtualSize() {
return this.virtualSize;
}
@Override
public long getResidentSetSize() {
return this.residentSetSize;
}
@Override
public long getKernelTime() {
return this.kernelTime;
}
@Override
public long getUserTime() {
return this.userTime;
}
@Override
public long getUpTime() {
return this.upTime;
}
@Override
public long getStartTime() {
return this.startTime;
}
@Override
public long getBytesRead() {
return this.bytesRead;
}
@Override
public long getBytesWritten() {
return this.bytesWritten;
}
@Override
public long getOpenFiles() {
return ProcstatUtil.getOpenFiles(getProcessID());
}
@Override
public long getSoftOpenFileLimit() {
if (getProcessID() == this.os.getProcessId()) {
final Resource.Rlimit rlimit = new Resource.Rlimit();
FreeBsdLibc.INSTANCE.getrlimit(FreeBsdLibc.RLIMIT_NOFILE, rlimit);
return rlimit.rlim_cur;
} else {
return getProcessOpenFileLimit(getProcessID(), 1);
}
}
@Override
public long getHardOpenFileLimit() {
if (getProcessID() == this.os.getProcessId()) {
final Resource.Rlimit rlimit = new Resource.Rlimit();
FreeBsdLibc.INSTANCE.getrlimit(FreeBsdLibc.RLIMIT_NOFILE, rlimit);
return rlimit.rlim_max;
} else {
return getProcessOpenFileLimit(getProcessID(), 2);
}
}
@Override
public int getBitness() {
return this.bitness.get();
}
@Override
public long getAffinityMask() {
long bitMask = 0L;
// Would prefer to use native cpuset_getaffinity call but variable sizing is
// kernel-dependent and requires C macros, so we use commandline instead.
String cpuset = ExecutingCommand.getFirstAnswer("cpuset -gp " + getProcessID());
// Sample output:
// pid 8 mask: 0, 1
// cpuset: getaffinity: No such process
String[] split = cpuset.split(":");
if (split.length > 1) {
String[] bits = split[1].split(",");
for (String bit : bits) {
int bitToSet = ParseUtil.parseIntOrDefault(bit.trim(), -1);
if (bitToSet >= 0) {
bitMask |= 1L << bitToSet;
}
}
}
return bitMask;
}
private int queryBitness() {
// Get process abi vector
int[] mib = new int[4];
mib[0] = 1; // CTL_KERN
mib[1] = 14; // KERN_PROC
mib[2] = 9; // KERN_PROC_SV_NAME
mib[3] = getProcessID();
// Allocate memory for arguments
try (Memory abi = new Memory(32); CloseableSizeTByReference size = new CloseableSizeTByReference(32)) {
// Fetch abi vector
if (0 == FreeBsdLibc.INSTANCE.sysctl(mib, mib.length, abi, size, null, size_t.ZERO)) {
String elf = abi.getString(0);
if (elf.contains("ELF32")) {
return 32;
} else if (elf.contains("ELF64")) {
return 64;
}
}
}
return 0;
}
@Override
public List getThreadDetails() {
String psCommand = "ps -awwxo " + PS_THREAD_COLUMNS + " -H";
if (getProcessID() >= 0) {
psCommand += " -p " + getProcessID();
}
Predicate> hasColumnsPri = threadMap -> threadMap.containsKey(PsThreadColumns.PRI);
return ExecutingCommand.runNative(psCommand).stream().skip(1).parallel()
.map(thread -> ParseUtil.stringToEnumMap(PsThreadColumns.class, thread.trim(), ' '))
.filter(hasColumnsPri).map(threadMap -> new FreeBsdOSThread(getProcessID(), threadMap))
.filter(VALID_THREAD).collect(Collectors.toList());
}
@Override
public long getMinorFaults() {
return this.minorFaults;
}
@Override
public long getMajorFaults() {
return this.majorFaults;
}
@Override
public long getContextSwitches() {
return this.contextSwitches;
}
@Override
public boolean updateAttributes() {
String psCommand = "ps -awwxo " + FreeBsdOperatingSystem.PS_COMMAND_ARGS + " -p " + getProcessID();
List procList = ExecutingCommand.runNative(psCommand);
if (procList.size() > 1) {
// skip header row
Map psMap = ParseUtil.stringToEnumMap(PsKeywords.class, procList.get(1).trim(), ' ');
// Check if last (thus all) value populated
if (psMap.containsKey(PsKeywords.ARGS)) {
return updateAttributes(psMap);
}
}
this.state = INVALID;
return false;
}
private boolean updateAttributes(Map psMap) {
long now = System.currentTimeMillis();
switch (psMap.get(PsKeywords.STATE).charAt(0)) {
case 'R':
this.state = RUNNING;
break;
case 'I':
case 'S':
this.state = SLEEPING;
break;
case 'D':
case 'L':
case 'U':
this.state = WAITING;
break;
case 'Z':
this.state = ZOMBIE;
break;
case 'T':
this.state = STOPPED;
break;
default:
this.state = OTHER;
break;
}
this.parentProcessID = ParseUtil.parseIntOrDefault(psMap.get(PsKeywords.PPID), 0);
this.user = psMap.get(PsKeywords.USER);
this.userID = psMap.get(PsKeywords.UID);
this.group = psMap.get(PsKeywords.GROUP);
this.groupID = psMap.get(PsKeywords.GID);
this.threadCount = ParseUtil.parseIntOrDefault(psMap.get(PsKeywords.NLWP), 0);
this.priority = ParseUtil.parseIntOrDefault(psMap.get(PsKeywords.PRI), 0);
// These are in KB, multiply
this.virtualSize = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.VSZ), 0) * 1024;
this.residentSetSize = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.RSS), 0) * 1024;
// Avoid divide by zero for processes up less than a second
long elapsedTime = ParseUtil.parseDHMSOrDefault(psMap.get(PsKeywords.ETIMES), 0L);
this.upTime = elapsedTime < 1L ? 1L : elapsedTime;
this.startTime = now - this.upTime;
this.kernelTime = ParseUtil.parseDHMSOrDefault(psMap.get(PsKeywords.SYSTIME), 0L);
this.userTime = ParseUtil.parseDHMSOrDefault(psMap.get(PsKeywords.TIME), 0L) - this.kernelTime;
this.path = psMap.get(PsKeywords.COMM);
this.name = this.path.substring(this.path.lastIndexOf('/') + 1);
this.minorFaults = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.MAJFLT), 0L);
this.majorFaults = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.MINFLT), 0L);
long nonVoluntaryContextSwitches = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.NVCSW), 0L);
long voluntaryContextSwitches = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.NIVCSW), 0L);
this.contextSwitches = voluntaryContextSwitches + nonVoluntaryContextSwitches;
this.commandLineBackup = psMap.get(PsKeywords.ARGS);
return true;
}
private long getProcessOpenFileLimit(long processId, int index) {
final String limitsPath = String.format(Locale.ROOT, "/proc/%d/limits", processId);
if (!Files.exists(Paths.get(limitsPath))) {
return -1; // not supported
}
final List lines = FileUtil.readFile(limitsPath);
final Optional maxOpenFilesLine = lines.stream().filter(line -> line.startsWith("Max open files"))
.findFirst();
if (!maxOpenFilesLine.isPresent()) {
return -1;
}
// Split all non-Digits away -> ["", "{soft-limit}, "{hard-limit}"]
final String[] split = maxOpenFilesLine.get().split("\\D+");
return ParseUtil.parseLongOrDefault(split[index], -1);
}
}