A timer is a dispatcher object that becomes signalled when its timer expires. A file object becomes signalled when an overlapped I/O operation has completed. The file must have been opened in
DebugPrint System Thread Function
Listing 14.1 shows that the DebugPrint system thread for drivers under test primarily consists of a loop that waits for the ExitNow flag to become true or for trace events to arrive.
At the top of this main loop, the thread function calls
The call to
The two formatted print functions,
Listing 14.2 shows how
The event data consists of the following three items:
• the current system time in GMT,
• the driver name (specified in DebugPrintInit), and
• the message.
It would reduce the event structure size if the LARGE_INTEGER output from KeQuerySystemTime were stored directly. However, there is no equivalent of the RtlTimeToTimeFields routine in Win32, so the event structure holds the time as time fields.
DebugPrintMsg now works out the size of the event data (i.e., the three previous data items, including the strings' terminating NULLs). It then determines the size of the DEBUGPRINT_EVENT structure that envelops the event data. It allocates some nonpaged memory for this structure and fills it in. It then uses
Listing 14.2 DebugPrint test driver DebugPrintMsg function
void DebugPrintMsg(char* Msg) {
if (!DebugPrintStarted) return;
// Get current time
LARGE_INTEGER Now, NowLocal;
KeQuerySystemTime(&Now);
TIME_FIELDS NowTF;
RtlTimeToTimeFields(&Now, &NowTF);
// Get size of Msg and complete event
USHORT MsgLen = ANSIstrlen(Msg)+1;
ULONG EventDataLen = sizeof(TIME_FIELDS) + DriverNameLen + MsgLen;
ULONG len = sizeof(LIST_ENTRY)+sizeof(ULONG)+EventDataLen;
// Allocate event buffer
PDEBUGPRINT_EVENT pEvent = (PDEBUGPRINT_EVENT)ExAllocatePool(NonPagedPool.len);
if (pEvent!=NULL) {
PUCHAR buffer = (PUCHAR)pEvent->EventData;
// Copy event info to buffer
RtlCopyMemory(buffer, &NowTF, sizeof(TIME_FIELDS));
buffer += sizeof(TIME_FIELDS);
RtlCopyMemory( buffer, DriverName, DriverNameLen);
buffer += DriverNameLen;
RtlCopyMemory(buffer, Msg, MsgLen);
// Insert event into event list for processing by system thread
pEvent->len = EventDataLen;
ExInterlockedInsertTailList(&EventList, &pEvent->ListEntry, &EventListLock);
}
}
Doubly-Linked Lists
A doubly-linked list is a slightly complicated beast to use safely. First, you need to declare a LIST_ENTRY structure in nonpaged memory for the list head. Drivers that need one list per device declare the list head in the device extension. However, the DebugPrint test driver code declares just its EventList variable as a global, as it is available to all devices.
LIST_ENTRY EventList;
Next, define a structure that you want to put in your doubly-linked list. Include a LIST_ ENTRY field in this structure to provide the links in both directions of the list. The DebugPrint structure is called DEBUGPRINT_EVENT.
typedef struct _DEBUGPRINT_EVENT {
LIST_ENTRY ListEntry;
ULONG Len;
UCHAR EventData[1];
} DEBUGPRINT_EVENT, *PDEBUGPRINT_EVENT;
Initialize a doubly-linked list using
All well and good. However, it is important that attempts to access the list are carried out safely so that the links are not corrupted in a multiprocessor environment. The kernel provides
KeInitializeSpinLock(&EventListLock);
InitializeListHead(&EventList);
Listing 14.2 shows how to use one of the interlocked linked list routines, ExInterlockedInsertTailList. It is passed pointers to the list head, the LIST_ENTRY field in your structure, and the spin lock.
Listing 14.3 shows an extract from the DebugPrint test driver system thread function. This is the code that is run every second to see if any events have been produced by calls to DebugPrintMsg.
Listing 14.3 DebugPrint test driver system thread event processing
// Loop until all available events have been removed
while(true) {
PLIST_ENTRY pListEntry = ExInterlockedRemoveHeadList(&EventList, &EventListLock);
if (pListEntry==NULL) break;
// Get event as DEBUGPRINT_EVENT
PDEBUGPRINT_EVENT pEvent = CONTAINING_RECORD(pListEntry, DEBUGPRINT_EVENT, ListEntry);
// Get length of event data
ULONG EventDataLen = pEvent->Len;
// Send event to DebugPrint
NTSTATUS status = ZwWriteFile(DebugPrintDeviceHandle, NULL, NULL, NULL,
&IoStatus, pEvent->EventData, EventDataLen, &ByteOffset, NULL);