Skip to content

Commit

Permalink
Added c.s.j.win32.Psapi.QueryWorkingSetEx
Browse files Browse the repository at this point in the history
  • Loading branch information
Crain-32 committed Aug 10, 2022
1 parent 999c60a commit 4ce1914
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Next Release (5.13.0)

Features
--------
* [#1454](https://github.com/java-native-access/jna/pull/1454): Add `c.s.j.p.win32.Psapi.QueryWorkingSetEx` and associated Types - [@crain-32](https://github.com/Crain-32).

Bug Fixes
---------
Expand Down
27 changes: 27 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/Psapi.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.Structure.ByReference;
import com.sun.jna.Structure.FieldOrder;
import com.sun.jna.platform.win32.BaseTSD.SIZE_T;
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinNT.HANDLE;
Expand Down Expand Up @@ -292,6 +294,18 @@ public interface Psapi extends StdCallLibrary {
*/
boolean EnumProcesses(int[] lpidProcess, int cb, IntByReference lpcbNeeded);

/**
* Retrieves extended information about the pages at specific
* virtual addresses in the address space of the specified process.
*
* @param hProcess A Handle to the Process
* @param pv A pointer to an array of PSAPI_WORKING_SET_EX_INFORMATION structures
* @param cb The size of the pv buffer, in bytes.
* @return If the function succeeds, the return value is nonzero.
* @see <a href="https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-queryworkingsetex">MSDN</a>
*/
boolean QueryWorkingSetEx(HANDLE hProcess, Pointer pv, int cb);

@FieldOrder({"lpBaseOfDll", "SizeOfImage", "EntryPoint"})
class MODULEINFO extends Structure {
public Pointer EntryPoint;
Expand Down Expand Up @@ -320,4 +334,17 @@ class PERFORMANCE_INFORMATION extends Structure {
public DWORD ProcessCount;
public DWORD ThreadCount;
}

@FieldOrder({"Flags", "Data"})
class PSAPI_WORKING_SET_EX_BLOCK extends Structure implements ByReference {
public ULONG_PTR Flags;
public ULONG_PTR[] Data = new ULONG_PTR[Native.POINTER_SIZE == 8 ? 1 : 2];
}


@FieldOrder({"VirtualAddress", "VirtualAttributes"})
class PSAPI_WORKING_SET_EX_INFORMATION extends Structure {
public Pointer VirtualAddress;
public PSAPI_WORKING_SET_EX_BLOCK VirtualAttributes;
}
}
76 changes: 76 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/PsapiUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.Psapi.PSAPI_WORKING_SET_EX_BLOCK;
import com.sun.jna.ptr.IntByReference;

/**
Expand Down Expand Up @@ -87,4 +88,79 @@ public static String GetProcessImageFileName(HANDLE hProcess) {
}
}
}

@Override
public void read() {
super.read();
innerValue = this.Data[0].longValue();
}

/**
* If this bit is 1, the other values in the PSAPI_WORKING_SET_EX_BLOCK are valid.
*/
public static boolean isPsapiWorkingSetExBlockValid(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 1, 0) == 1;
}

/**
* The number of processes that share the page addressed by the PSAPI_WORKING_SET_EX_BLOCK. The maximum value of this member is 7.
*/
public static int getPsapiWorkingSetExBlockShareCount(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 3, 1);
}

/**
* The memory protection attributes of the page addressed by the PSAPI_WORKING_SET_EX_BLOCK. For a list of values see below.
* @see <a href="https://docs.microsoft.com/en-us/windows/desktop/Memory/memory-protection-constants">Memory Protection Constants</a>.
*/
public static int getPsapiWorkingSetExBlockWin32Protection(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(),11, 3 + 1);
}

/**
* If this bit is 1, the page addressed by the PSAPI_WORKING_SET_EX_BLOCK can be shared.
*/
public static boolean isPsapiWorkingSetExBlockShared(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 1, 11 + 3 + 1) == 1;
}

/**
* The NUMA node of the page addressed by the PSAPI_WORKING_SET_EX_BLOCK. The maximum value of this member is 63.
*/
public static int getPsapiWorkingSetExBlockNode(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 6, 1 + 11 + 3 + 1);
}

/**
* If this bit is 1, the virtual page addressed by the PSAPI_WORKING_SET_EX_BLOCK is locked in physical memory.
*/
public static boolean isPsapiWorkingSetExBlockLocked(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 1, 6 + 1 + 11 + 3 + 1) == 1;
}

/**
* If this bit is 1, the page addressed by the PSAPI_WORKING_SET_EX_BLOCK is a large page.
*/
public static boolean isPsapiWorkingSetExBlockLargePage(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(),1, 1 + 6 + 1 + 11 + 3 + 1) == 1;
}

/**
* If this bit is 1, the page addressed by the PSAPI_WORKING_SET_EX_BLOCK is has been reported as bad.
*/
public static boolean isPsapiWorkingSetExBlockBad(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 1,1 + 1 + 1 + 6 + 1 + 11 + 3 + 1) == 1;
}

/**
* Returns a Right Shifted and Masked version of value.
*/
public static int getBitFieldValue(final int value, final int maskLength, final int rightShiftAmount) {
long bitMask = 0;

for (int l = 0; l < maskLength; l++) {
bitMask |= 1 << l;
}
return (int) ((value >>> rightShiftAmount) & bitMask);
}
}
43 changes: 43 additions & 0 deletions contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
package com.sun.jna.platform.win32;

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertFalse;

import java.util.LinkedList;
import java.util.List;
Expand All @@ -34,12 +36,15 @@

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Psapi.MODULEINFO;
import com.sun.jna.platform.win32.Psapi.PERFORMANCE_INFORMATION;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinNT.MEMORY_BASIC_INFORMATION;
import com.sun.jna.platform.win32.BaseTSD.SIZE_T;
import com.sun.jna.ptr.IntByReference;

/**
Expand Down Expand Up @@ -273,4 +278,42 @@ public void testEnumProcesses() {
}
assertTrue("List should contain my pid", foundMyPid);
}

@Test
public void testQueryWorkingSetEx() {
Win32Exception we = null;
HANDLE selfHandle = Kernel32.INSTANCE.GetCurrentProcess();
MEMORY_BASIC_INFORMATION mbi = new MEMORY_BASIC_INFORMATION();
try {
SIZE_T bytesRead = Kernel32.INSTANCE.VirtualQueryEx(selfHandle, Pointer.NULL, mbi, new SIZE_T(mbi.size()));
assertNotEquals("Kernel should be able to read this Process' Bytes", bytesRead.intValue(), 0);
Psapi.PSAPI_WORKING_SET_EX_INFORMATION pswsi = new Psapi.PSAPI_WORKING_SET_EX_INFORMATION();
pswsi.VirtualAddress = mbi.baseAddress;
if (!Psapi.INSTANCE.QueryWorkingSetEx(selfHandle, pswsi.VirtualAddress, pswsi.size())) {
throw new Win32Exception(Native.getLastError());
}
assertTrue("Virual Attributes should not be null", pswsi.VirtualAttributes != null);
if (Psapi.INSTANCE.QueryWorkingSetEx(new HANDLE(), pswsi.VirtualAddress, pswsi.size())) {
throw new Win32Exception(Native.getLastError());
}
assertFalse("This line should never be called", true);
} catch (Win32Exception e) {
we = e;
throw we; // re-throw to invoke finally block
} finally {
try {
Kernel32Util.closeHandle(selfHandle);
} catch (Win32Exception e) {
if (we == null) {
we = e;
} else {
we.addSuppressedReflected(e);
}
}
if (we != null) {
throw we;
}
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotEquals;


import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Psapi.MODULEINFO;
import com.sun.jna.platform.win32.Psapi.PERFORMANCE_INFORMATION;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinNT.MEMORY_BASIC_INFORMATION;
import com.sun.jna.platform.win32.BaseTSD.SIZE_T;

import org.junit.Test;

Expand All @@ -50,4 +62,39 @@ public void enumProcesses() {
}
assertTrue("List should contain my pid", foundMyPid);
}

@Test
public void testPsapiWorkingSetExBlockUtils() {
Win32Exception we = null;
HANDLE selfHandle = Kernel32.INSTANCE.GetCurrentProcess();
MEMORY_BASIC_INFORMATION mbi = new MEMORY_BASIC_INFORMATION();
try {
SIZE_T bytesRead = Kernel32.INSTANCE.VirtualQueryEx(selfHandle, Pointer.NULL, mbi, new SIZE_T(mbi.size()));
assertNotEquals("Kernel should be able to read this Process' Bytes", bytesRead.intValue(), 0);
Psapi.PSAPI_WORKING_SET_EX_INFORMATION pswsi = new Psapi.PSAPI_WORKING_SET_EX_INFORMATION();
pswsi.VirtualAddress = mbi.baseAddress;
if (!Psapi.INSTANCE.QueryWorkingSetEx(selfHandle, pswsi.VirtualAddress, pswsi.size())) {
throw new Win32Exception(Native.getLastError());
}
assertTrue("Virual Attributes should not be null", pswsi.VirtualAttributes != null);
PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock = pswsi.VirtualAttributes;
assertTrue("The JVM Page should be valid", PsapiUtil.isPsapiWorkingSetExBlockValid(psapiWorkingSetExBlock));
} catch (Win32Exception e) {
we = e;
throw we; // re-throw to invoke finally block
} finally {
try {
Kernel32Util.closeHandle(selfHandle);
} catch (Win32Exception e) {
if (we == null) {
we = e;
} else {
we.addSuppressedReflected(e);
}
}
if (we != null) {
throw we;
}
}
}
}

0 comments on commit 4ce1914

Please sign in to comment.