Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.Queue;
Expand Down Expand Up @@ -62,17 +63,23 @@
* @since 2.20.1
*/
final class PpidChecker {
private static final BigDecimal THOUSAND = new BigDecimal(1000);
private static final long MINUTES_TO_MILLIS = 60L * 1000L;
// 25 chars https://superuser.com/questions/937380/get-creation-time-of-file-in-milliseconds/937401#937401
private static final int WMIC_CREATION_DATE_VALUE_LENGTH = 25;
private static final int WMIC_CREATION_DATE_TIMESTAMP_LENGTH = 18;
private static final SimpleDateFormat WMIC_CREATION_DATE_FORMAT =
IS_OS_WINDOWS ? createWindowsCreationDateFormat() : null;
private static final Pattern POWERSHELL_CPU_PATTERN = Pattern.compile("^\\d+(\\.\\d+)?$");
private static final String WMIC_CREATION_DATE = "CreationDate";
private static final String POWERSHELL_CMD_HEADER = "CPU";
private static final String WINDOWS_SYSTEM_ROOT_ENV = "SystemRoot";
private static final String RELATIVE_PATH_TO_WMIC = "System32\\Wbem";
private static final String SYSTEM_PATH_TO_WMIC =
"%" + WINDOWS_SYSTEM_ROOT_ENV + "%\\" + RELATIVE_PATH_TO_WMIC + "\\";
private static final String RELATIVE_PATH_TO_WMIC32 = "System32\\Wbem";
private static final String RELATIVE_PATH_TO_WMIC64 = "SysWOW64\\Wbem";
private static final String SYSTEM_PATH_TO_WMIC32 =
"%" + WINDOWS_SYSTEM_ROOT_ENV + "%\\" + RELATIVE_PATH_TO_WMIC32 + "\\";
private static final String SYSTEM_PATH_TO_WMIC64 =
"%" + WINDOWS_SYSTEM_ROOT_ENV + "%\\" + RELATIVE_PATH_TO_WMIC64 + "\\";
private static final String PS_ETIME_HEADER = "ELAPSED";
private static final String PS_PID_HEADER = "PID";

Expand Down Expand Up @@ -159,7 +166,7 @@ ProcessInfo unix() {
ProcessInfoConsumer reader = new ProcessInfoConsumer(charset) {
@Override
@Nonnull
ProcessInfo consumeLine(String line, ProcessInfo previousOutputLine) {
protected ProcessInfo consumeLine(String line, ProcessInfo previousOutputLine) {
if (previousOutputLine.isInvalid()) {
if (hasHeader) {
Matcher matcher = UNIX_CMD_OUT_PATTERN.matcher(line);
Expand Down Expand Up @@ -187,36 +194,61 @@ ProcessInfo consumeLine(String line, ProcessInfo previousOutputLine) {
}

ProcessInfo windows() {
ProcessInfoConsumer reader = new ProcessInfoConsumer("US-ASCII") {
class WindowsProcessInfoConsumer extends ProcessInfoConsumer {
private boolean isPowershell;

WindowsProcessInfoConsumer() {
super("US-ASCII");
}

@Override
@Nonnull
ProcessInfo consumeLine(String line, ProcessInfo previousProcessInfo) throws Exception {
protected ProcessInfo consumeLine(String line, ProcessInfo previousProcessInfo) throws Exception {
if (previousProcessInfo.isInvalid() && !line.isEmpty()) {
if (hasHeader) {
// now the line is CreationDate, e.g. 20180406142327.741074+120
if (line.length() != WMIC_CREATION_DATE_VALUE_LENGTH) {
throw new IllegalStateException("WMIC CreationDate should have 25 characters " + line);
if (isPowershell) {
if (POWERSHELL_CPU_PATTERN.matcher(line).matches()) {
long etime = new BigDecimal(line).multiply(THOUSAND).longValue();
return windowsProcessInfo(ppid, etime);
}
} else {
// now the line is CreationDate, e.g. 20180406142327.741074+120
if (line.length() != WMIC_CREATION_DATE_VALUE_LENGTH) {
throw new IllegalStateException("WMIC CreationDate should have 25 characters " + line);
}
String startTimestamp = line.substring(0, WMIC_CREATION_DATE_TIMESTAMP_LENGTH);
int indexOfTimeZone = WMIC_CREATION_DATE_VALUE_LENGTH - 4;
long startTimestampMillisUTC = WMIC_CREATION_DATE_FORMAT.parse(startTimestamp).getTime()
- parseInt(line.substring(indexOfTimeZone)) * MINUTES_TO_MILLIS;
return windowsProcessInfo(ppid, startTimestampMillisUTC);
}
String startTimestamp = line.substring(0, WMIC_CREATION_DATE_TIMESTAMP_LENGTH);
int indexOfTimeZone = WMIC_CREATION_DATE_VALUE_LENGTH - 4;
long startTimestampMillisUTC =
WMIC_CREATION_DATE_FORMAT.parse(startTimestamp).getTime()
- parseInt(line.substring(indexOfTimeZone)) * MINUTES_TO_MILLIS;
return windowsProcessInfo(ppid, startTimestampMillisUTC);
} else {
hasHeader = WMIC_CREATION_DATE.equals(line);
hasHeader = WMIC_CREATION_DATE.equals(line) || POWERSHELL_CMD_HEADER.equals(line);
}
}
return previousProcessInfo;
}
};
String wmicPath = hasWmicStandardSystemPath() ? SYSTEM_PATH_TO_WMIC : "";
return reader.execute(
"CMD",
"/A",
"/X",
"/C",
wmicPath + "wmic process where (ProcessId=" + ppid + ") get " + WMIC_CREATION_DATE);

ProcessInfo checkPpid() {
if (hasWmicStandardSystemPath64()) {
return execute("CMD", "/A", "/X", "/C",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can execute wmic directly, no need to use the cmd cruft.

SYSTEM_PATH_TO_WMIC64
+ "wmic process where (ProcessId=" + ppid + ") get " + WMIC_CREATION_DATE);
} else if (hasWmicStandardSystemPath32()) {
return execute("CMD", "/A", "/X", "/C",
SYSTEM_PATH_TO_WMIC32
+ "wmic process where (ProcessId=" + ppid + ") get " + WMIC_CREATION_DATE);
} else {
isPowershell = true;
return execute("CMD", "/A", "/X", "/C",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here.

"powershell", " \"Get-Process -Id " + ppid + " | Select-Object -Property CPU" + "\"");
}
}
}

WindowsProcessInfoConsumer reader = new WindowsProcessInfoConsumer();
return reader.checkPpid();

}

void destroyActiveCommands() {
Expand Down Expand Up @@ -254,9 +286,14 @@ private static boolean canExecuteStandardUnixPs() {
}
}

private static boolean hasWmicStandardSystemPath() {
private static boolean hasWmicStandardSystemPath32() {
String systemRoot = System.getenv(WINDOWS_SYSTEM_ROOT_ENV);
return isNotBlank(systemRoot) && new File(systemRoot, RELATIVE_PATH_TO_WMIC32 + "\\wmic.exe").isFile();
}

private static boolean hasWmicStandardSystemPath64() {
String systemRoot = System.getenv(WINDOWS_SYSTEM_ROOT_ENV);
return isNotBlank(systemRoot) && new File(systemRoot, RELATIVE_PATH_TO_WMIC + "\\wmic.exe").isFile();
return isNotBlank(systemRoot) && new File(systemRoot, RELATIVE_PATH_TO_WMIC64 + "\\wmic.exe").isFile();
}

static long fromDays(Matcher matcher) {
Expand Down Expand Up @@ -337,7 +374,8 @@ abstract class ProcessInfoConsumer {
this.charset = charset;
}

abstract @Nonnull ProcessInfo consumeLine(String line, ProcessInfo previousProcessInfo) throws Exception;
protected abstract @Nonnull ProcessInfo consumeLine(String line, ProcessInfo previousProcessInfo)
throws Exception;

ProcessInfo execute(String... command) {
ProcessBuilder processBuilder = new ProcessBuilder(command);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
final class ProcessInfo {
static final ProcessInfo INVALID_PROCESS_INFO = new ProcessInfo(null, 0);
static final ProcessInfo ERR_PROCESS_INFO = new ProcessInfo(null, 0);
static final ProcessInfo ERR_CMD_NOT_FOUND = new ProcessInfo(null, 0);

/**
* On Unix we do not get PID due to the command is interested only to etime of PPID:
Expand Down Expand Up @@ -65,7 +66,7 @@ boolean isInvalid() {
}

boolean isError() {
return this == ERR_PROCESS_INFO;
return this == ERR_PROCESS_INFO || this == ERR_CMD_NOT_FOUND;
}

String getPID() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ public void shouldHaveSystemPathToWmicOnWindows() throws Exception {
assumeThat(System.getenv("SystemRoot"), is(not("")));
assumeTrue(new File(System.getenv("SystemRoot"), "System32\\Wbem").isDirectory());
assumeTrue(new File(System.getenv("SystemRoot"), "System32\\Wbem\\wmic.exe").isFile());
assertThat((Boolean) invokeMethod(PpidChecker.class, "hasWmicStandardSystemPath"))
assertThat((Boolean) invokeMethod(PpidChecker.class, "hasWmicStandardSystemPath32"))
.isTrue();
assertThat(new File(System.getenv("SystemRoot"), "System32\\Wbem\\wmic.exe"))
.isFile();
Expand Down
Loading