[KMTESTS]

- use a shared memory buffer for storing test results to provide seamless communication between all test parts
- Now user mode code can easily add messages to the buffer

svn path=/branches/GSoC_2011/KMTestSuite/; revision=52216
This commit is contained in:
Thomas Faber 2011-06-13 17:50:07 +00:00
parent 1de7bf0885
commit a6b4eba335
9 changed files with 219 additions and 135 deletions

View file

@ -23,6 +23,7 @@ add_library(kmtest_drv SHARED ${KMTEST_DRV_SOURCE})
set_module_type(kmtest_drv kernelmodedriver)
target_link_libraries(kmtest_drv ${PSEH_LIB})
add_importlibs(kmtest_drv ntoskrnl hal)
set_property(TARGET kmtest_drv PROPERTY COMPILE_DEFINITIONS KMT_KERNEL_MODE)
add_cd_file(TARGET kmtest_drv DESTINATION reactos/system32/drivers FOR all)
@ -39,5 +40,6 @@ list(APPEND KMTEST_SOURCE
add_executable(kmtest ${KMTEST_SOURCE})
set_module_type(kmtest win32cui)
add_importlibs(kmtest advapi32 msvcrt kernel32)
set_property(TARGET kmtest PROPERTY COMPILE_DEFINITIONS KMT_USER_MODE)
add_cd_file(TARGET kmtest DESTINATION reactos/bin FOR all)

View file

@ -14,6 +14,9 @@
#define IOCTL_KMTEST_RUN_TEST \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
#define IOCTL_KMTEST_SET_RESULTBUFFER \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
#define KMTEST_DEVICE_NAME L"Kmtest"
#define KMTEST_DEVICE_PATH (L"\\\\.\\Global\\GLOBALROOT\\Device\\" KMTEST_DEVICE_NAME)

View file

@ -8,8 +8,6 @@
#ifndef _KMTEST_TEST_H_
#define _KMTEST_TEST_H_
#include <kmt_log.h>
typedef void KMT_TESTFUNC(void);
typedef struct
@ -22,4 +20,60 @@ typedef const KMT_TEST CKMT_TEST, *PCKMT_TEST;
extern const KMT_TEST TestList[];
typedef struct {
volatile LONG Successes;
volatile LONG Failures;
volatile LONG LogBufferLength;
LONG LogBufferMaxLength;
CHAR LogBuffer[ANYSIZE_ARRAY];
} KMT_RESULTBUFFER, *PKMT_RESULTBUFFER;
extern PKMT_RESULTBUFFER ResultBuffer;
#if defined KMT_DEFINE_TEST_FUNCTIONS
PKMT_RESULTBUFFER ResultBuffer = NULL;
#if defined KMT_USER_MODE
static PKMT_RESULTBUFFER KmtAllocateResultBuffer(SIZE_T LogBufferMaxLength)
{
PKMT_RESULTBUFFER Buffer = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(KMT_RESULTBUFFER, LogBuffer[LogBufferMaxLength]));
Buffer->Successes = 0;
Buffer->Failures = 0;
Buffer->LogBufferLength = 0;
Buffer->LogBufferMaxLength = LogBufferMaxLength;
return Buffer;
}
static VOID KmtFreeResultBuffer(PKMT_RESULTBUFFER Buffer)
{
HeapFree(GetProcessHeap(), 0, Buffer);
}
#endif /* defined KMT_USER_MODE */
#define KmtMemCpy memcpy
static VOID KmtAddToLogBuffer(PKMT_RESULTBUFFER Buffer, PCSTR String, SIZE_T Length)
{
LONG OldLength;
LONG NewLength;
do
{
OldLength = Buffer->LogBufferLength;
NewLength = OldLength + Length;
if (NewLength > Buffer->LogBufferMaxLength)
{
/* TODO: indicate failure somehow */
__debugbreak();
return;
}
} while (InterlockedCompareExchange(&Buffer->LogBufferLength, NewLength, OldLength) != OldLength);
KmtMemCpy(&Buffer->LogBuffer[OldLength], String, Length);
}
#endif /* defined KMT_DEFINE_TEST_FUNCTIONS */
#endif /* !defined _KMTEST_TEST_H_ */

View file

@ -1,6 +1,7 @@
<module name="kmtest" type="win32cui" installbase="system32" installname="kmtest.exe">
<include base="kmtest">include</include>
<library>advapi32</library>
<define name="KMT_USER_MODE" />
<directory name="kmtest">
<file>kmtest.c</file>
<file>service.c</file>

View file

@ -16,6 +16,10 @@
#include "kmtest.h"
#include <winioctl.h>
#include <kmt_public.h>
#define KMT_DEFINE_TEST_FUNCTIONS
#include <kmt_test.h>
#define LOGBUFFER_SIZE 65000
static void OutputError(FILE *fp, DWORD error);
static DWORD RunTest(char *testName);
@ -40,9 +44,14 @@ static DWORD RunTest(char *testName)
{
DWORD error = ERROR_SUCCESS;
HANDLE hDevice = INVALID_HANDLE_VALUE;
DWORD bytesRead;
char buffer[1024];
BOOL ret;
DWORD bytesRead, bytesWritten;
ResultBuffer = KmtAllocateResultBuffer(LOGBUFFER_SIZE);
if (!ResultBuffer)
{
error = GetLastError();
goto cleanup;
}
hDevice = CreateFile(KMTEST_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, 0, NULL);
@ -53,23 +62,19 @@ static DWORD RunTest(char *testName)
goto cleanup;
}
if (!DeviceIoControl(hDevice, IOCTL_KMTEST_SET_RESULTBUFFER, ResultBuffer, FIELD_OFFSET(KMT_RESULTBUFFER, LogBuffer[LOGBUFFER_SIZE]), NULL, 0, &bytesRead, NULL))
{
error = GetLastError();
goto cleanup;
}
if (!DeviceIoControl(hDevice, IOCTL_KMTEST_RUN_TEST, testName, strlen(testName), NULL, 0, &bytesRead, NULL))
{
error = GetLastError();
goto cleanup;
}
while ((ret = ReadFile(hDevice, buffer, sizeof buffer - 1, &bytesRead, NULL)) != 0)
{
if (!bytesRead)
break;
assert(bytesRead < sizeof buffer);
buffer[bytesRead] = '\0';
fputs(buffer, stdout);
}
if (!ret)
if (!WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), ResultBuffer->LogBuffer, ResultBuffer->LogBufferLength, &bytesWritten, NULL))
{
error = GetLastError();
goto cleanup;
@ -79,6 +84,9 @@ cleanup:
if (hDevice != INVALID_HANDLE_VALUE)
CloseHandle(hDevice);
if (ResultBuffer)
KmtFreeResultBuffer(ResultBuffer);
return error;
}
@ -89,7 +97,7 @@ static DWORD ListTests(PSTR *testList)
DWORD bytesRead;
PSTR buffer = NULL;
DWORD bufferSize;
if (!testList)
{
error = ERROR_INVALID_PARAMETER;
@ -104,7 +112,7 @@ static DWORD ListTests(PSTR *testList)
error = GetLastError();
goto cleanup;
}
bufferSize = 1024;
buffer = HeapAlloc(GetProcessHeap(), 0, bufferSize);
if (!buffer)
@ -125,10 +133,10 @@ cleanup:
HeapFree(GetProcessHeap(), 0, buffer);
buffer = NULL;
}
if (hDevice != INVALID_HANDLE_VALUE)
CloseHandle(hDevice);
if (testList)
*testList = buffer;
@ -162,7 +170,7 @@ int __cdecl main(int argc, char **argv)
printf(" %s\n", testName);
testName += len + 1;
}
/* TODO: user-mode test parts */
if (error)
@ -171,7 +179,7 @@ int __cdecl main(int argc, char **argv)
else
{
char *testName = argv[1];
if (argc > 2)
fputs("Excess arguments ignored\n", stderr);

View file

@ -4,6 +4,7 @@
<library>ntdll</library>
<library>hal</library>
<library>pseh</library>
<define name="KMT_KERNEL_MODE" />
<directory name="kmtest_drv">
<file>kmtest_drv.c</file>
<file>log.c</file>

View file

@ -8,6 +8,7 @@
#include <ntddk.h>
#include <ntstrsafe.h>
#include <limits.h>
#include <pseh/pseh2.h>
//#define NDEBUG
#include <debug.h>
@ -19,9 +20,16 @@
/* Prototypes */
DRIVER_INITIALIZE DriverEntry;
static DRIVER_UNLOAD DriverUnload;
static DRIVER_DISPATCH DriverCreateClose;
static DRIVER_DISPATCH DriverCreate;
static DRIVER_DISPATCH DriverClose;
static DRIVER_DISPATCH DriverIoControl;
static DRIVER_DISPATCH DriverRead;
/* Device Extension layout */
typedef struct
{
PKMT_RESULTBUFFER ResultBuffer;
PMDL Mdl;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
/* Globals */
static PDEVICE_OBJECT MainDeviceObject;
@ -43,6 +51,8 @@ NTSTATUS NTAPI DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING Re
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING DeviceName;
PDEVICE_EXTENSION DeviceExtension;
PAGED_CODE();
UNREFERENCED_PARAMETER(RegistryPath);
@ -55,7 +65,8 @@ NTSTATUS NTAPI DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING Re
goto cleanup;
RtlInitUnicodeString(&DeviceName, L"\\Device\\Kmtest");
Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN,
Status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &DeviceName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN | FILE_READ_ONLY_DEVICE,
TRUE, &MainDeviceObject);
@ -64,13 +75,14 @@ NTSTATUS NTAPI DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING Re
DPRINT("DriverEntry. Created DeviceObject %p\n",
MainDeviceObject);
MainDeviceObject->Flags |= DO_DIRECT_IO;
DeviceExtension = MainDeviceObject->DeviceExtension;
DeviceExtension->ResultBuffer = NULL;
DeviceExtension->Mdl = NULL;
DriverObject->DriverUnload = DriverUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverCreateClose;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIoControl;
DriverObject->MajorFunction[IRP_MJ_READ] = DriverRead;
cleanup:
if (MainDeviceObject && !NT_SUCCESS(Status))
@ -100,15 +112,21 @@ static VOID NTAPI DriverUnload(IN PDRIVER_OBJECT DriverObject)
DPRINT("DriverUnload\n");
if (MainDeviceObject)
{
PDEVICE_EXTENSION DeviceExtension = MainDeviceObject->DeviceExtension;
ASSERT(!DeviceExtension->Mdl);
ASSERT(!DeviceExtension->ResultBuffer);
ASSERT(!ResultBuffer);
IoDeleteDevice(MainDeviceObject);
}
LogFree();
}
/**
* @name DriverCreateClose
* @name DriverCreate
*
* Driver Dispatch function for CreateFile/CloseHandle.
* Driver Dispatch function for CreateFile
*
* @param DeviceObject
* Device Object
@ -117,19 +135,66 @@ static VOID NTAPI DriverUnload(IN PDRIVER_OBJECT DriverObject)
*
* @return Status
*/
static NTSTATUS NTAPI DriverCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
static NTSTATUS NTAPI DriverCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS Status = STATUS_SUCCESS;
PIO_STACK_LOCATION IoStackLocation;
PDEVICE_EXTENSION DeviceExtension;
PAGED_CODE();
IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
DPRINT("DriverCreateClose. Function=%s, DeviceObject=%p\n",
IoStackLocation->MajorFunction == IRP_MJ_CREATE ? "Create" : "Close",
DPRINT("DriverCreate. DeviceObject=%p\n",
DeviceObject);
DeviceExtension = DeviceObject->DeviceExtension;
ASSERT(!DeviceExtension->Mdl);
ASSERT(!DeviceExtension->ResultBuffer);
ASSERT(!ResultBuffer);
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
/**
* @name DriverClose
*
* Driver Dispatch function for CloseHandle.
*
* @param DeviceObject
* Device Object
* @param Irp
* I/O request packet
*
* @return Status
*/
static NTSTATUS NTAPI DriverClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS Status = STATUS_SUCCESS;
PIO_STACK_LOCATION IoStackLocation;
PDEVICE_EXTENSION DeviceExtension;
PAGED_CODE();
IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
DPRINT("DriverClose. DeviceObject=%p\n",
DeviceObject);
DeviceExtension = DeviceObject->DeviceExtension;
if (DeviceExtension->Mdl)
{
MmUnlockPages(DeviceExtension->Mdl);
IoFreeMdl(DeviceExtension->Mdl);
DeviceExtension->Mdl = NULL;
ResultBuffer = DeviceExtension->ResultBuffer = NULL;
}
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
@ -223,6 +288,48 @@ static NTSTATUS NTAPI DriverIoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Ir
break;
}
case IOCTL_KMTEST_SET_RESULTBUFFER:
{
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
DPRINT("DriverIoControl. IOCTL_KMTEST_SET_RESULTBUFFER, inlen=%lu, outlen=%lu\n",
IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
if (DeviceExtension->Mdl)
{
MmUnlockPages(DeviceExtension->Mdl);
IoFreeMdl(DeviceExtension->Mdl);
}
DeviceExtension->Mdl = IoAllocateMdl(IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer,
IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
FALSE, FALSE, NULL);
if (!DeviceExtension->Mdl)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
_SEH2_TRY
{
MmProbeAndLockPages(DeviceExtension->Mdl, KernelMode, IoModifyAccess);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
IoFreeMdl(DeviceExtension->Mdl);
DeviceExtension->Mdl = NULL;
break;
} _SEH2_END;
ResultBuffer = DeviceExtension->ResultBuffer = MmGetSystemAddressForMdlSafe(DeviceExtension->Mdl, NormalPagePriority);
DPRINT("DriverIoControl. ResultBuffer: %ld %ld %ld %ld\n",
ResultBuffer->Successes, ResultBuffer->Failures,
ResultBuffer->LogBufferLength, ResultBuffer->LogBufferMaxLength);
break;
}
default:
DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n",
IoStackLocation->Parameters.DeviceIoControl.IoControlCode);
@ -237,46 +344,3 @@ static NTSTATUS NTAPI DriverIoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Ir
return Status;
}
/**
* @name DriverRead
*
* Driver Dispatch function for ReadFile.
*
* @param DeviceObject
* Device Object
* @param Irp
* I/O request packet
*
* @return Status
*/
static NTSTATUS NTAPI DriverRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS Status = STATUS_SUCCESS;
PIO_STACK_LOCATION IoStackLocation;
PVOID ReadBuffer;
SIZE_T Length;
PAGED_CODE();
IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
DPRINT("DriverRead. Offset=%I64u, Length=%lu, DeviceObject=%p\n",
IoStackLocation->Parameters.Read.ByteOffset.QuadPart,
IoStackLocation->Parameters.Read.Length,
DeviceObject);
ReadBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
Length = LogRead(ReadBuffer, IoStackLocation->Parameters.Read.Length);
DPRINT("DriverRead. Length of data read: %lu\n",
Length);
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = Length;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}

View file

@ -9,14 +9,8 @@
#include <ntstrsafe.h>
#include <kmt_log.h>
#define LOGBUFFER_MAX (1024UL * 1024)
static PCHAR LogBuffer;
static SIZE_T LogOffset;
#define LOG_TAG 'LtmK'
/* TODO: allow concurrent log buffer access */
#define KMT_DEFINE_TEST_FUNCTIONS
#include <kmt_test.h>
/**
* @name LogInit
@ -30,11 +24,6 @@ NTSTATUS LogInit(VOID)
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
LogBuffer = ExAllocatePoolWithTag(NonPagedPool, LOGBUFFER_MAX, LOG_TAG);
if (!LogBuffer)
Status = STATUS_INSUFFICIENT_RESOURCES;
return Status;
}
@ -48,8 +37,6 @@ NTSTATUS LogInit(VOID)
VOID LogFree(VOID)
{
PAGED_CODE();
ExFreePoolWithTag(LogBuffer, LOG_TAG);
}
/**
@ -64,10 +51,10 @@ VOID LogFree(VOID)
*/
VOID LogPrint(IN PCSTR Message)
{
SIZE_T MessageLength = strlen(Message);
ASSERT(LogOffset + MessageLength + 1 < LOGBUFFER_MAX);
RtlCopyMemory(&LogBuffer[LogOffset], Message, MessageLength + 1);
LogOffset += MessageLength;
size_t MessageLength;
ASSERT(NT_SUCCESS(RtlStringCbLengthA(Message, 512, &MessageLength)));
KmtAddToLogBuffer(ResultBuffer, Message, MessageLength);
}
/**
@ -105,47 +92,11 @@ VOID LogPrintF(IN PCSTR Format, ...)
*/
VOID LogVPrintF(IN PCSTR Format, va_list Arguments)
{
CHAR Buffer[1024];
SIZE_T BufferLength;
CHAR Buffer[512];
/* TODO: make this work from any IRQL */
PAGED_CODE();
RtlStringCbVPrintfA(Buffer, sizeof Buffer, Format, Arguments);
BufferLength = strlen(Buffer);
ASSERT(LogOffset + BufferLength + 1 < LOGBUFFER_MAX);
RtlCopyMemory(&LogBuffer[LogOffset], Buffer, BufferLength + 1);
LogOffset += BufferLength;
}
/**
* @name LogRead
*
* Retrieve data from the log buffer.
*
* @param Buffer
* Buffer to copy log data to
* @param BufferSize
* Maximum number of bytes to copy
*
* @return Number of bytes copied
*/
SIZE_T LogRead(OUT PVOID Buffer, IN SIZE_T BufferSize)
{
SIZE_T Size;
PAGED_CODE();
Size = min(BufferSize, LogOffset);
RtlCopyMemory(Buffer, LogBuffer, Size);
if (BufferSize < LogOffset)
{
SIZE_T SizeLeft = LogOffset - BufferSize;
RtlMoveMemory(LogBuffer, &LogBuffer[LogOffset], SizeLeft);
LogOffset = SizeLeft;
}
else
LogOffset = 0;
return Size;
LogPrint(Buffer);
}

View file

@ -5,7 +5,7 @@
* PROGRAMMER: Thomas Faber <thfabba@gmx.de>
*/
#include <stddef.h>
#include <ntddk.h>
#include <kmt_test.h>
KMT_TESTFUNC Test_Example;