oshi.driver.windows.registry.HkeyPerformanceDataUtil Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2020-2022 The OSHI Project Contributors
* SPDX-License-Identifier: MIT
*/
package oshi.driver.windows.registry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.jna.Memory;
import com.sun.jna.platform.win32.Advapi32;
import com.sun.jna.platform.win32.Advapi32Util;
import com.sun.jna.platform.win32.Win32Exception;
import com.sun.jna.platform.win32.WinBase.FILETIME;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinPerf.PERF_COUNTER_BLOCK;
import com.sun.jna.platform.win32.WinPerf.PERF_COUNTER_DEFINITION;
import com.sun.jna.platform.win32.WinPerf.PERF_DATA_BLOCK;
import com.sun.jna.platform.win32.WinPerf.PERF_INSTANCE_DEFINITION;
import com.sun.jna.platform.win32.WinPerf.PERF_OBJECT_TYPE;
import com.sun.jna.platform.win32.WinReg;
import oshi.annotation.SuppressForbidden;
import oshi.annotation.concurrent.ThreadSafe;
import oshi.jna.ByRef.CloseableIntByReference;
import oshi.util.platform.windows.PerfCounterWildcardQuery.PdhCounterWildcardProperty;
import oshi.util.tuples.Pair;
import oshi.util.tuples.Triplet;
/**
* Utility to read HKEY_PERFORMANCE_DATA information.
*/
@ThreadSafe
public final class HkeyPerformanceDataUtil {
private static final Logger LOG = LoggerFactory.getLogger(HkeyPerformanceDataUtil.class);
/*
* Do a one-time lookup of the HKEY_PERFORMANCE_TEXT counter indices and store in a map for efficient lookups
* on-demand.
*/
private static final String HKEY_PERFORMANCE_TEXT = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009";
private static final String COUNTER = "Counter";
private static final Map COUNTER_INDEX_MAP = mapCounterIndicesFromRegistry();
private static int maxPerfBufferSize = 16384;
private HkeyPerformanceDataUtil() {
}
/**
* Reads and parses a block of performance data from the registry.
*
* @param PDH Counters use an Enum to identify the fields to query in either the counter or WMI backup,
* and use the enum values as keys to retrieve the results.
* @param objectName The counter object for which to fetch data
* @param counterEnum Which counters to return data for
* @return A triplet containing the results. The first element maps the input enum to the counter values where the
* first enum will contain the instance name as a {@link String}, and the remaining values will either be
* {@link Long}, {@link Integer}, or {@code null} depending on whether the specified enum counter was
* present and the size of the counter value. The second element is a timestamp in 100nSec increments
* (Windows 1601 Epoch) while the third element is a timestamp in milliseconds since the 1970 Epoch.
*/
public static & PdhCounterWildcardProperty> Triplet>, Long, Long> readPerfDataFromRegistry(
String objectName, Class counterEnum) {
// Load indices
// e.g., call with "Process" and ProcessPerformanceProperty.class
Pair> indices = getCounterIndices(objectName, counterEnum);
if (indices == null) {
return null;
}
// The above test checks validity of objectName as an index but it could still
// fail to read
try (Memory pPerfData = readPerfDataBuffer(objectName)) {
if (pPerfData == null) {
return null;
}
// Buffer is now successfully populated.
// See format at
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa373105(v=vs.85).aspx
// Start with a data header (PERF_DATA_BLOCK)
// Then iterate one or more objects
// Each object contains
// [ ] Object Type header (PERF_OBJECT_TYPE)
// [ ][ ][ ] Multiple counter definitions (PERF_COUNTER_DEFINITION)
// Then after object(s), multiple:
// [ ] Instance Definition
// [ ] Instance name
// [ ] Counter Block
// [ ][ ][ ] Counter data for each definition above
// Store timestamp
PERF_DATA_BLOCK perfData = new PERF_DATA_BLOCK(pPerfData.share(0));
long perfTime100nSec = perfData.PerfTime100nSec.getValue(); // 1601
long now = FILETIME.filetimeToDate((int) (perfTime100nSec >> 32), (int) (perfTime100nSec & 0xffffffffL))
.getTime(); // 1970
// Iterate object types.
long perfObjectOffset = perfData.HeaderLength;
for (int obj = 0; obj < perfData.NumObjectTypes; obj++) {
PERF_OBJECT_TYPE perfObject = new PERF_OBJECT_TYPE(pPerfData.share(perfObjectOffset));
// Some counters will require multiple objects so we iterate until we find the
// right one. e.g. Process (230) is by itself but Thread (232) has Process
// object first
if (perfObject.ObjectNameTitleIndex == COUNTER_INDEX_MAP.get(objectName).intValue()) {
// We found a matching object.
// Counter definitions start after the object header
long perfCounterOffset = perfObjectOffset + perfObject.HeaderLength;
// Iterate counter definitions and fill maps with counter offsets and sizes
Map counterOffsetMap = new HashMap<>();
Map counterSizeMap = new HashMap<>();
for (int counter = 0; counter < perfObject.NumCounters; counter++) {
PERF_COUNTER_DEFINITION perfCounter = new PERF_COUNTER_DEFINITION(
pPerfData.share(perfCounterOffset));
counterOffsetMap.put(perfCounter.CounterNameTitleIndex, perfCounter.CounterOffset);
counterSizeMap.put(perfCounter.CounterNameTitleIndex, perfCounter.CounterSize);
// Increment for next Counter
perfCounterOffset += perfCounter.ByteLength;
}
// Instances start after all the object definitions. The DefinitionLength
// includes both the header and all the definitions.
long perfInstanceOffset = perfObjectOffset + perfObject.DefinitionLength;
// Iterate instances and fill map
List