[KMTESTS/EX]

- add a test for hard-error handling
- add test for interlocked functions (part 1)

svn path=/branches/GSoC_2011/KMTestSuite/; revision=52673
This commit is contained in:
Thomas Faber 2011-07-13 20:08:05 +00:00
parent ba142f75c3
commit f7dc696190
5 changed files with 552 additions and 0 deletions

View file

@ -14,6 +14,8 @@ list(APPEND KMTEST_DRV_SOURCE
kmtest_drv/testlist.c
example/Example.c
ntos_ex/ExHardError.c
ntos_ex/ExInterlocked.c
ntos_ex/ExPools.c
ntos_ex/ExResource.c
ntos_ex/ExTimer.c

View file

@ -13,6 +13,8 @@
<file>Example.c</file>
</directory>
<directory name="ntos_ex">
<file>ExHardError.c</file>
<file>ExInterlocked.c</file>
<file>ExPools.c</file>
<file>ExResource.c</file>
<file>ExTimer.c</file>

View file

@ -9,6 +9,9 @@
#include <kmt_test.h>
KMT_TESTFUNC Test_Example;
KMT_TESTFUNC Test_ExHardError;
KMT_TESTFUNC Test_ExHardErrorInteractive;
KMT_TESTFUNC Test_ExInterlocked;
KMT_TESTFUNC Test_ExPools;
KMT_TESTFUNC Test_ExResource;
KMT_TESTFUNC Test_ExTimer;
@ -25,6 +28,9 @@ KMT_TESTFUNC Test_ObCreate;
const KMT_TEST TestList[] =
{
{ "Example", Test_Example },
{ "ExHardError", Test_ExHardError },
{ "-ExHardErrorInteractive", Test_ExHardErrorInteractive },
{ "ExInterlocked", Test_ExInterlocked },
{ "ExPools", Test_ExPools },
{ "ExResource", Test_ExResource },
{ "ExTimer", Test_ExTimer },

View file

@ -0,0 +1,253 @@
/*
* PROJECT: ReactOS kernel-mode tests
* LICENSE: GPLv2+ - See COPYING in the top level directory
* PURPOSE: Kernel-Mode Test Suite Hard error message test
* PROGRAMMER: Thomas Faber <thfabba@gmx.de>
*/
#include <ntddk.h>
#include <ntifs.h>
#include <ndk/exfuncs.h>
#include <pseh/pseh2.h>
#include <kmt_test.h>
/* TODO: don't require user interaction, test Io* routines,
* test NTSTATUS values with special handling */
/* TODO: this belongs in ndk/exfuncs.h?! */
NTSTATUS
NTAPI
ExRaiseHardError(IN NTSTATUS ErrorStatus,
IN ULONG NumberOfParameters,
IN ULONG UnicodeStringParameterMask,
IN PULONG_PTR Parameters,
IN ULONG ValidResponseOptions,
OUT PULONG Response);
/* TODO: this belongs in HARDERROR_RESPONSE_OPTION in ndk/extypes.h */
#define OptionBalloonNotification 7
#define OptionCancelTryAgainContinue 8
static
VOID
SetParameters(
OUT PULONG_PTR Parameters,
IN INT Count,
...)
{
INT i;
va_list Arguments;
va_start(Arguments, Count);
for (i = 0; i < Count; ++i)
Parameters[i] = va_arg(Arguments, ULONG_PTR);
va_end(Arguments);
}
#define NoResponse 27
#define CheckHardError(ErrStatus, UnicodeStringMask, ResponseOption, \
ExpectedStatus, ExpectedResponse, \
NumberOfParameters, ...) do \
{ \
SetParameters(HardErrorParameters, NumberOfParameters, __VA_ARGS__);\
Response = NoResponse; \
_SEH2_TRY { \
Status = ExRaiseHardError(ErrStatus, \
NumberOfParameters, \
UnicodeStringMask, \
HardErrorParameters, \
ResponseOption, \
&Response); \
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
Status = _SEH2_GetExceptionCode(); \
} _SEH2_END; \
ok_eq_hex(Status, ExpectedStatus); \
ok_eq_ulong(Response, (ULONG)ExpectedResponse); \
} while (0)
#define CheckInformationalHardError(ErrStatus, String, Thread, \
ExpectedStatus, ExpectedRet) do \
{ \
Status = STATUS_SUCCESS; \
Ret = !ExpectedRet; \
_SEH2_TRY { \
Ret = IoRaiseInformationalHardError(ErrStatus, \
String, \
Thread); \
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
Status = _SEH2_GetExceptionCode(); \
} _SEH2_END; \
ok_eq_hex(Status, ExpectedStatus); \
ok_eq_bool(Ret, ExpectedRet); \
} while (0)
static
VOID
TestHardError(
BOOLEAN InteractivePart1,
BOOLEAN InteractivePart2,
BOOLEAN InteractivePart3,
BOOLEAN InteractivePart4)
{
NTSTATUS Status;
ULONG Response;
WCHAR StringBuffer1[] = L"Parameter1+Garbage";
CHAR StringBuffer1Ansi[] = "Parameter1+Garbage";
WCHAR StringBuffer2[] = L"Parameter2+Garbage";
UNICODE_STRING String1 = RTL_CONSTANT_STRING(StringBuffer1);
ANSI_STRING String1Ansi = RTL_CONSTANT_STRING(StringBuffer1Ansi);
UNICODE_STRING String2 = RTL_CONSTANT_STRING(StringBuffer2);
ULONG_PTR HardErrorParameters[6];
BOOLEAN Ret;
String1.Length = sizeof L"Parameter1" - sizeof UNICODE_NULL;
String1Ansi.Length = sizeof "Parameter1" - sizeof ANSI_NULL;
String2.Length = sizeof L"Parameter2" - sizeof UNICODE_NULL;
if (InteractivePart1)
{
CheckHardError(0x40000000, 0, OptionOk, STATUS_SUCCESS, ResponseOk, 0, 0); // outputs a box :|
CheckHardError(0x40000001, 0, OptionOk, STATUS_SUCCESS, ResponseOk, 4, 1, 2, 3, 4); // outputs a box :|
CheckHardError(0x40000002, 0, OptionOk, STATUS_SUCCESS, ResponseOk, 5, 1, 2, 3, 4, 5); // outputs a box :|
}
CheckHardError(0x40000003, 0, OptionOk, STATUS_SUCCESS, ResponseNotHandled, 6, 1, 2, 3, 4, 5, 6);
CheckHardError(0x40000004, 0, OptionShutdownSystem, STATUS_PRIVILEGE_NOT_HELD, ResponseNotHandled, 0, 0);
CheckHardError(0x40000005, 0, OptionBalloonNotification, STATUS_SUCCESS, ResponseOk, 0, 0); // outputs a balloon notification
CheckHardError(0x4000000f, 0, OptionBalloonNotification, STATUS_SUCCESS, ResponseOk, 0, 0); // outputs a balloon notification
if (InteractivePart1)
{
CheckHardError(0x40000006, 0, OptionAbortRetryIgnore, STATUS_SUCCESS, ResponseAbort, 0, 0); // outputs a box :|
CheckHardError(0x40000006, 0, OptionAbortRetryIgnore, STATUS_SUCCESS, ResponseRetry, 0, 0); // outputs a box :|
CheckHardError(0x40000006, 0, OptionAbortRetryIgnore, STATUS_SUCCESS, ResponseIgnore, 0, 0); // outputs a box :|
CheckHardError(0x40000008, 0, OptionCancelTryAgainContinue, STATUS_SUCCESS, ResponseCancel, 0, 0); // outputs a box :|
CheckHardError(0x40000008, 0, OptionCancelTryAgainContinue, STATUS_SUCCESS, ResponseTryAgain, 0, 0); // outputs a box :|
CheckHardError(0x40000008, 0, OptionCancelTryAgainContinue, STATUS_SUCCESS, ResponseContinue, 0, 0); // outputs a box :|
CheckHardError(0x40000010, 0, OptionOkCancel, STATUS_SUCCESS, ResponseOk, 0, 0); // outputs a box :|
CheckHardError(0x40000010, 0, OptionOkCancel, STATUS_SUCCESS, ResponseCancel, 0, 0); // outputs a box :|
CheckHardError(0x40000011, 0, OptionRetryCancel, STATUS_SUCCESS, ResponseRetry, 0, 0); // outputs a box :|
CheckHardError(0x40000011, 0, OptionRetryCancel, STATUS_SUCCESS, ResponseCancel, 0, 0); // outputs a box :|
CheckHardError(0x40000012, 0, OptionYesNo, STATUS_SUCCESS, ResponseYes, 0, 0); // outputs a box :|
CheckHardError(0x40000012, 0, OptionYesNo, STATUS_SUCCESS, ResponseNo, 0, 0); // outputs a box :|
CheckHardError(0x40000013, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 0, 0); // outputs a box :|
CheckHardError(0x40000013, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 0, 0); // outputs a box :|
CheckHardError(0x40000013, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 0, 0); // outputs a box :|
}
CheckHardError(0x40000009, 0, 9, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
CheckHardError(0x4000000a, 0, 10, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
CheckHardError(0x4000000b, 0, 11, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
CheckHardError(0x4000000c, 0, 12, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
CheckHardError(0x4000000d, 0, MAXULONG / 2 + 1, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
CheckHardError(0x4000000d, 0, MAXULONG, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
if (InteractivePart2)
{
/* try a message with one parameter */
CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 1, &String1); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, &String1); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 0, &String1); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 0, &String1); // outputs a box :|
/* give too many parameters */
CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 2, &String1, &String2); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 2, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 2, &String1, &String2); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 2, &String1, &String2); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 4, &String1, &String2, 0, 0); // outputs a box :|
/* try with stuff that's not a UNICODE_STRING */
CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, &String1Ansi); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, L"Parameter1"); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, "Parameter1"); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, 1234); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, NULL); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, &String1Ansi); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, L"Parameter1"); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, "Parameter1"); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, 1234); // outputs a box :|
CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, NULL); // outputs a box :|
}
if (InteractivePart3)
{
/* try a message with one parameter */
CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, &String1); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, &String1); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 0, &String1); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 0, &String1); // outputs a box :|
/* give too many parameters */
CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 2, &String1, &String2); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 2, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 2, &String1, &String2); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 2, &String1, &String2); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseOk, 3, &String1, &String2, 0); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseOk, 4, &String1, &String2, 0, 0); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 3, OptionBalloonNotification, STATUS_SUCCESS, ResponseOk, 4, &String1, &String2, 0, 0); // outputs a balloon notification
/* try with stuff that's not a UNICODE_STRING */
CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, &String1Ansi); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, L"Parameter1"); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, "Parameter1"); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, 1234); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, NULL); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, &String1Ansi); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, L"Parameter1"); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, "Parameter1"); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, 1234); // outputs a box :|
CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, NULL); // outputs a box :|
}
if (InteractivePart4)
{
/* try a message with one parameter */
CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 1, &String1); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, &String1); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 0, &String1); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 0, &String1); // outputs a box :|
/* give too many parameters */
CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 2, &String1, &String2); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 2, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 2, &String1, &String2); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 2, &String1, &String2); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 4, &String1, &String2, 0, 0); // outputs a box :|
/* try with stuff that's not a UNICODE_STRING */
CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, &String1Ansi); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, L"Parameter1"); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, "Parameter1"); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, 1234); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, NULL); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, &String1Ansi); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, L"Parameter1"); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, "Parameter1"); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, 1234); // outputs a box :|
CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, NULL); // outputs a box :|
}
CheckInformationalHardError(STATUS_WAIT_0, NULL, NULL, STATUS_SUCCESS, TRUE); // outputs a balloon notification
CheckInformationalHardError(STATUS_DLL_NOT_FOUND, &String1, NULL, STATUS_SUCCESS, TRUE); // outputs a balloon notification
CheckInformationalHardError(STATUS_DLL_NOT_FOUND, NULL, NULL, STATUS_SUCCESS, TRUE); // outputs a balloon notification
CheckInformationalHardError(STATUS_SERVICE_NOTIFICATION, &String1, NULL, STATUS_SUCCESS, FALSE);
ok_bool_true(IoSetThreadHardErrorMode(TRUE), "IoSetThreadHardErrorMode returned");
ok_bool_true(IoSetThreadHardErrorMode(FALSE), "IoSetThreadHardErrorMode returned");
ok_bool_false(IoSetThreadHardErrorMode(FALSE), "IoSetThreadHardErrorMode returned");
CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseReturnToCaller, 0, 0);
CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, NULL);
CheckInformationalHardError(STATUS_WAIT_0, NULL, NULL, STATUS_SUCCESS, FALSE);
CheckInformationalHardError(STATUS_DLL_NOT_FOUND, &String1, NULL, STATUS_SUCCESS, FALSE);
CheckInformationalHardError(STATUS_DLL_NOT_FOUND, NULL, NULL, STATUS_SUCCESS, FALSE);
CheckInformationalHardError(STATUS_SERVICE_NOTIFICATION, &String1, NULL, STATUS_SUCCESS, FALSE);
ok_bool_false(IoSetThreadHardErrorMode(TRUE), "IoSetThreadHardErrorMode returned");
}
START_TEST(ExHardError)
{
TestHardError(FALSE, FALSE, FALSE, FALSE);
}
/* Here's how to do the interactive test:
* - First there will be a few messages random messages. If there's
* multiple options available, the same box will appear multiple times --
* click the buttons in order from left to right
* - After that, you must verify the error parameters. You should always
* see Parameter1 or Parameter2 for strings, and 0x12345678 for numbers.
* if there's a message saying an exception occured during processing,
* click cancel. If there's a bad parameter (Parameter1+, Parameter1+Garbage
* or an empty string for example), click no. Otherwise click yes. */
START_TEST(ExHardErrorInteractive)
{
TestHardError(TRUE, TRUE, TRUE, TRUE);
}

View file

@ -0,0 +1,289 @@
/*
* PROJECT: ReactOS kernel-mode tests
* LICENSE: GPLv2+ - See COPYING in the top level directory
* PURPOSE: Kernel-Mode Test Suite Interlocked function test
* PROGRAMMER: Thomas Faber <thfabba@gmx.de>
*/
/* missing prototypes >:| */
#ifndef _MSC_VER
typedef long long __int64;
#endif
struct _KSPIN_LOCK;
__declspec(dllimport) long __fastcall InterlockedCompareExchange(volatile long *, long, long);
__declspec(dllimport) __int64 __fastcall ExInterlockedCompareExchange64(volatile __int64 *, __int64 *, __int64 *, void *);
__declspec(dllimport) __int64 __fastcall ExfInterlockedCompareExchange64(volatile __int64 *, __int64 *, __int64 *);
__declspec(dllimport) long __fastcall InterlockedExchange(volatile long *, long);
__declspec(dllimport) unsigned long __stdcall ExInterlockedExchangeUlong(unsigned long *, unsigned long, void *);
__declspec(dllimport) long __fastcall InterlockedExchangeAdd(volatile long *, long);
__declspec(dllimport) unsigned long __stdcall ExInterlockedAddUlong(unsigned long *, unsigned long, void *);
__declspec(dllimport) unsigned long __stdcall Exi386InterlockedExchangeUlong(unsigned long *, unsigned long);
__declspec(dllimport) long __fastcall InterlockedIncrement(long *);
__declspec(dllimport) long __fastcall InterlockedDecrement(long *);
__declspec(dllimport) int __stdcall ExInterlockedIncrementLong(long *, void *);
__declspec(dllimport) int __stdcall ExInterlockedDecrementLong(long *, void *);
__declspec(dllimport) int __stdcall Exi386InterlockedIncrementLong(long *);
__declspec(dllimport) int __stdcall Exi386InterlockedDecrementLong(long *);
#undef NTDDI_VERSION
#define NTDDI_VERSION NTDDI_WS03SP1
#include <ntddk.h>
#include <pseh/pseh2.h>
#include <kmt_test.h>
/* TODO: There are quite some changes needed for other architectures!
ExInterlockedAddLargeInteger, ExInterlockedAddUlong are the only two
functions actually exported by my win7/x64 kernel! */
/* TODO: stress-testing */
static KSPIN_LOCK SpinLock;
static
LARGE_INTEGER
Large(
ULONGLONG Value)
{
LARGE_INTEGER Ret;
Ret.QuadPart = Value;
return Ret;
}
#define CheckInterlockedCmpXchg(Function, Type, Print, Val, Cmp, Xchg, \
ExpectedValue, ExpectedRet, ...) do \
{ \
Type Ret##Type = 0; \
Type Value##Type = Val; \
Status = STATUS_SUCCESS; \
_SEH2_TRY { \
Ret##Type = Function(&Value##Type, Xchg, Cmp, ##__VA_ARGS__); \
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
Status = _SEH2_GetExceptionCode(); \
} _SEH2_END; \
ok_eq_hex(Status, STATUS_SUCCESS); \
ok_eq_print(Ret##Type, ExpectedRet, Print); \
ok_eq_print(Value##Type, ExpectedValue, Print); \
} while (0)
#define CheckInterlockedCmpXchgI(Function, Type, Print, Val, Cmp, Xchg, \
ExpectedValue, ExpectedRet, ...) do \
{ \
Type Ret##Type = 0; \
Type Value##Type = Val; \
Type Compare##Type = Cmp; \
Type Exchange##Type = Xchg; \
Status = STATUS_SUCCESS; \
_SEH2_TRY { \
Ret##Type = Function(&Value##Type, &Exchange##Type, \
&Compare##Type, ##__VA_ARGS__); \
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
Status = _SEH2_GetExceptionCode(); \
} _SEH2_END; \
ok_eq_hex(Status, STATUS_SUCCESS); \
ok_eq_print(Ret##Type, ExpectedRet, Print); \
ok_eq_print(Value##Type, ExpectedValue, Print); \
ok_eq_print(Exchange##Type, Xchg, Print); \
ok_eq_print(Compare##Type, Cmp, Print); \
} while(0)
#define CheckInterlockedOp(Function, Type, Print, Val, Op, \
ExpectedValue, ExpectedRet, ...) do \
{ \
Type Ret##Type = 0; \
Type Value##Type = Val; \
Status = STATUS_SUCCESS; \
_SEH2_TRY { \
Ret##Type = Function(&Value##Type, Op, ##__VA_ARGS__); \
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
Status = _SEH2_GetExceptionCode(); \
} _SEH2_END; \
ok_eq_hex(Status, STATUS_SUCCESS); \
ok_eq_print(Ret##Type, ExpectedRet, Print); \
ok_eq_print(Value##Type, ExpectedValue, Print); \
} while (0)
#define CheckInterlockedOpNoArg(Function, Type, Print, Val, \
ExpectedValue, ExpectedRet, ...) do \
{ \
Type Ret##Type = 0; \
Type Value##Type = Val; \
Status = STATUS_SUCCESS; \
_SEH2_TRY { \
Ret##Type = Function(&Value##Type, ##__VA_ARGS__); \
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
Status = _SEH2_GetExceptionCode(); \
} _SEH2_END; \
ok_eq_hex(Status, STATUS_SUCCESS); \
ok_eq_print(Ret##Type, ExpectedRet, Print); \
ok_eq_print(Value##Type, ExpectedValue, Print); \
} while (0)
#define CheckInterlockedOpLarge(Function, Type, Print, Val, Op, \
ExpectedValue, ExpectedRet, ...) do \
{ \
Type Ret##Type = Large(0); \
Type Value##Type = Val; \
Status = STATUS_SUCCESS; \
_SEH2_TRY { \
Ret##Type = Function(&Value##Type, Op, ##__VA_ARGS__); \
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
Status = _SEH2_GetExceptionCode(); \
} _SEH2_END; \
ok_eq_hex(Status, STATUS_SUCCESS); \
ok_eq_print(Ret##Type.QuadPart, ExpectedRet, Print); \
ok_eq_print(Value##Type.QuadPart, ExpectedValue, Print); \
} while (0)
#define CheckInterlockedOpLargeNoRet(Function, Type, Print, Val, Op, \
ExpectedValue, ...) do \
{ \
Type Value##Type = Val; \
Status = STATUS_SUCCESS; \
_SEH2_TRY { \
Function(&Value##Type, Op, ##__VA_ARGS__); \
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
Status = _SEH2_GetExceptionCode(); \
} _SEH2_END; \
ok_eq_hex(Status, STATUS_SUCCESS); \
ok_eq_print(Value##Type.QuadPart, ExpectedValue, Print); \
} while (0)
/* TODO: missing in wdm.h! */
#define InterlockedCompareExchangeAcquire InterlockedCompareExchange
#define InterlockedCompareExchangeRelease InterlockedCompareExchange
#define InterlockedIncrementAcquire InterlockedIncrement
#define InterlockedIncrementRelease InterlockedIncrement
#define InterlockedDecrementAcquire InterlockedDecrement
#define InterlockedDecrementRelease InterlockedDecrement
static
VOID
TestInterlockedFunctional(VOID)
{
NTSTATUS Status;
PKSPIN_LOCK pSpinLock = &SpinLock;
/* on x86, most of these are supported intrinsicly and don't need a spinlock! */
#if defined _M_IX86 || defined _M_AMD64
pSpinLock = NULL;
#endif
/* CompareExchange */
/* macro version */
CheckInterlockedCmpXchg(InterlockedCompareExchange, LONG, "%ld", 5, 6, 8, 5L, 5L);
CheckInterlockedCmpXchg(InterlockedCompareExchange, LONG, "%ld", 5, 5, 9, 9L, 5L);
/* exported function */
#undef InterlockedCompareExchange
CheckInterlockedCmpXchg(InterlockedCompareExchange, LONG, "%ld", 5, 6, 8, 5L, 5L);
CheckInterlockedCmpXchg(InterlockedCompareExchange, LONG, "%ld", 5, 5, 9, 9L, 5L);
/* these only exist as macros on x86 */
CheckInterlockedCmpXchg(InterlockedCompareExchangeAcquire, LONG, "%ld", 16, 9, 12, 16L, 16L);
CheckInterlockedCmpXchg(InterlockedCompareExchangeAcquire, LONG, "%ld", 16, 16, 4, 4L, 16L);
CheckInterlockedCmpXchg(InterlockedCompareExchangeRelease, LONG, "%ld", 27, 123, 38, 27L, 27L);
CheckInterlockedCmpXchg(InterlockedCompareExchangeRelease, LONG, "%ld", 27, 27, 39, 39L, 27L);
/* only exists as a macro */
CheckInterlockedCmpXchg(InterlockedCompareExchangePointer, PVOID, "%p", (PVOID)117, (PVOID)711, (PVOID)12, (PVOID)117, (PVOID)117);
CheckInterlockedCmpXchg(InterlockedCompareExchangePointer, PVOID, "%p", (PVOID)117, (PVOID)117, (PVOID)228, (PVOID)228, (PVOID)117);
/* macro version */
CheckInterlockedCmpXchgI(ExInterlockedCompareExchange64, LONGLONG, "%I64d", 17, 4LL, 20LL, 17LL, 17LL, pSpinLock);
CheckInterlockedCmpXchgI(ExInterlockedCompareExchange64, LONGLONG, "%I64d", 17, 17LL, 21LL, 21LL, 17LL, pSpinLock);
/* exported function */
CheckInterlockedCmpXchgI((ExInterlockedCompareExchange64), LONGLONG, "%I64d", 17, 4LL, 20LL, 17LL, 17LL, pSpinLock);
CheckInterlockedCmpXchgI((ExInterlockedCompareExchange64), LONGLONG, "%I64d", 17, 17LL, 21LL, 21LL, 17LL, pSpinLock);
/* fastcall version */
CheckInterlockedCmpXchgI(ExfInterlockedCompareExchange64, LONGLONG, "%I64d", 17, 4LL, 20LL, 17LL, 17LL);
CheckInterlockedCmpXchgI(ExfInterlockedCompareExchange64, LONGLONG, "%I64d", 17, 17LL, 21LL, 21LL, 17LL);
/* Exchange */
CheckInterlockedOp(InterlockedExchange, LONG, "%ld", 5, 8, 8L, 5L);
#undef InterlockedExchange
CheckInterlockedOp(InterlockedExchange, LONG, "%ld", 5, 8, 8L, 5L);
CheckInterlockedOp(InterlockedExchangePointer, PVOID, "%p", (PVOID)700, (PVOID)93, (PVOID)93, (PVOID)700);
CheckInterlockedOp(ExInterlockedExchangeUlong, ULONG, "%lu", 212, 121, 121LU, 212LU, pSpinLock);
CheckInterlockedOp((ExInterlockedExchangeUlong), ULONG, "%lu", 212, 121, 121LU, 212LU, pSpinLock);
#ifdef _M_IX86
CheckInterlockedOp(Exi386InterlockedExchangeUlong, ULONG, "%lu", 212, 121, 121LU, 212LU);
CheckInterlockedOp(Exfi386InterlockedExchangeUlong, ULONG, "%lu", 212, 121, 121LU, 212LU);
#endif
/* ExchangeAdd */
/* TODO: ExInterlockedExchangeAddLargeInteger? */
CheckInterlockedOp(InterlockedExchangeAdd, LONG, "%ld", 312, 7, 319L, 312L);
#undef InterlockedExchangeAdd
CheckInterlockedOp(InterlockedExchangeAdd, LONG, "%ld", 312, 7, 319L, 312L);
/* Add */
/* these DO need a valid spinlock even on x86 */
CheckInterlockedOpLarge(ExInterlockedAddLargeInteger, LARGE_INTEGER, "%I64d", Large(23), Large(7), 30LL, 23LL, &SpinLock);
CheckInterlockedOpLargeNoRet(ExInterlockedAddLargeStatistic, LARGE_INTEGER, "%I64d", Large(15), 17LL, 32LL);
CheckInterlockedOp(ExInterlockedAddUlong, ULONG, "%lu", 239, 44, 283LU, 239LU, &SpinLock);
#undef ExInterlockedAddUlong
CheckInterlockedOp(ExInterlockedAddUlong, ULONG, "%lu", 239, 44, 283LU, 239LU, &SpinLock);
/* Increment */
CheckInterlockedOpNoArg(InterlockedIncrement, LONG, "%ld", 2341L, 2342L, 2342L);
CheckInterlockedOpNoArg(InterlockedIncrement, LONG, "%ld", (LONG)MAXLONG, (LONG)MINLONG, (LONG)MINLONG);
#undef InterlockedIncrement
CheckInterlockedOpNoArg(InterlockedIncrement, LONG, "%ld", 2341L, 2342L, 2342L);
CheckInterlockedOpNoArg(InterlockedIncrement, LONG, "%ld", (LONG)MAXLONG, (LONG)MINLONG, (LONG)MINLONG);
CheckInterlockedOpNoArg(InterlockedIncrementAcquire, LONG, "%ld", 2341L, 2342L, 2342L);
CheckInterlockedOpNoArg(InterlockedIncrementRelease, LONG, "%ld", 2341L, 2342L, 2342L);
CheckInterlockedOpNoArg(ExInterlockedIncrementLong, LONG, "%ld", -2L, -1L, (LONG)ResultNegative, pSpinLock);
CheckInterlockedOpNoArg(ExInterlockedIncrementLong, LONG, "%ld", -1L, 0L, (LONG)ResultZero, pSpinLock);
CheckInterlockedOpNoArg(ExInterlockedIncrementLong, LONG, "%ld", 0L, 1L, (LONG)ResultPositive, pSpinLock);
CheckInterlockedOpNoArg(ExInterlockedIncrementLong, LONG, "%ld", (LONG)MAXLONG, (LONG)MINLONG, (LONG)ResultNegative, pSpinLock);
CheckInterlockedOpNoArg((ExInterlockedIncrementLong), LONG, "%ld", -2L, -1L, (LONG)ResultNegative, pSpinLock);
CheckInterlockedOpNoArg((ExInterlockedIncrementLong), LONG, "%ld", -1L, 0L, (LONG)ResultZero, pSpinLock);
CheckInterlockedOpNoArg((ExInterlockedIncrementLong), LONG, "%ld", 0L, 1L, (LONG)ResultPositive, pSpinLock);
CheckInterlockedOpNoArg((ExInterlockedIncrementLong), LONG, "%ld", (LONG)MAXLONG, (LONG)MINLONG, (LONG)ResultNegative, pSpinLock);
#ifdef _M_IX86
CheckInterlockedOpNoArg(Exi386InterlockedIncrementLong, LONG, "%ld", -2L, -1L, (LONG)ResultNegative);
CheckInterlockedOpNoArg(Exi386InterlockedIncrementLong, LONG, "%ld", -1L, 0L, (LONG)ResultZero);
CheckInterlockedOpNoArg(Exi386InterlockedIncrementLong, LONG, "%ld", 0L, 1L, (LONG)ResultPositive);
CheckInterlockedOpNoArg(Exi386InterlockedIncrementLong, LONG, "%ld", (LONG)MAXLONG, (LONG)MINLONG, (LONG)ResultNegative);
#endif
/* Decrement */
CheckInterlockedOpNoArg(InterlockedDecrement, LONG, "%ld", 1745L, 1744L, 1744L);
CheckInterlockedOpNoArg(InterlockedDecrement, LONG, "%ld", (LONG)MINLONG, (LONG)MAXLONG, (LONG)MAXLONG);
#undef InterlockedDecrement
CheckInterlockedOpNoArg(InterlockedDecrement, LONG, "%ld", 1745L, 1744L, 1744L);
CheckInterlockedOpNoArg(InterlockedDecrement, LONG, "%ld", (LONG)MINLONG, (LONG)MAXLONG, (LONG)MAXLONG);
CheckInterlockedOpNoArg(InterlockedDecrementAcquire, LONG, "%ld", 1745L, 1744L, 1744L);
CheckInterlockedOpNoArg(InterlockedDecrementRelease, LONG, "%ld", 1745L, 1744L, 1744L);
CheckInterlockedOpNoArg(ExInterlockedDecrementLong, LONG, "%ld", (LONG)MINLONG, (LONG)MAXLONG, (LONG)ResultPositive, pSpinLock);
CheckInterlockedOpNoArg(ExInterlockedDecrementLong, LONG, "%ld", 0L, -1L, (LONG)ResultNegative, pSpinLock);
CheckInterlockedOpNoArg(ExInterlockedDecrementLong, LONG, "%ld", 1L, 0L, (LONG)ResultZero, pSpinLock);
CheckInterlockedOpNoArg(ExInterlockedDecrementLong, LONG, "%ld", 2L, 1L, (LONG)ResultPositive, pSpinLock);
CheckInterlockedOpNoArg((ExInterlockedDecrementLong), LONG, "%ld", (LONG)MINLONG, (LONG)MAXLONG, (LONG)ResultPositive, pSpinLock);
CheckInterlockedOpNoArg((ExInterlockedDecrementLong), LONG, "%ld", 0L, -1L, (LONG)ResultNegative, pSpinLock);
CheckInterlockedOpNoArg((ExInterlockedDecrementLong), LONG, "%ld", 1L, 0L, (LONG)ResultZero, pSpinLock);
CheckInterlockedOpNoArg((ExInterlockedDecrementLong), LONG, "%ld", 2L, 1L, (LONG)ResultPositive, pSpinLock);
#ifdef _M_IX86
CheckInterlockedOpNoArg(Exi386InterlockedDecrementLong, LONG, "%ld", (LONG)MINLONG, (LONG)MAXLONG, (LONG)ResultPositive);
CheckInterlockedOpNoArg(Exi386InterlockedDecrementLong, LONG, "%ld", 0L, -1L, (LONG)ResultNegative);
CheckInterlockedOpNoArg(Exi386InterlockedDecrementLong, LONG, "%ld", 1L, 0L, (LONG)ResultZero);
CheckInterlockedOpNoArg(Exi386InterlockedDecrementLong, LONG, "%ld", 2L, 1L, (LONG)ResultPositive);
#endif
/* And, Or, Xor */
CheckInterlockedOp(InterlockedAnd, LONG, "0x%lx", 0x1234L, 0x1111L, 0x1010L, 0x1234L);
CheckInterlockedOp(InterlockedOr, LONG, "0x%lx", 0x1234L, 0x1111L, 0x1335L, 0x1234L);
CheckInterlockedOp(InterlockedXor, LONG, "0x%lx", 0x1234L, 0x1111L, 0x0325L, 0x1234L);
#ifdef _WIN64
CheckInterlockedOp(InterlockedXor64, LONGLONG, "0x%I64x", 0x200001234LL, 0x100001111LL, 0x300000325LL, 0x200001234LL);
#endif
}
START_TEST(ExInterlocked)
{
KIRQL Irql;
KeInitializeSpinLock(&SpinLock);
/* functional testing */
TestInterlockedFunctional();
KeRaiseIrql(HIGH_LEVEL, &Irql);
TestInterlockedFunctional();
KeLowerIrql(Irql);
}