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 ZwCreateFile with the DesiredAccess SYNCHRONIZE flag set. You can also wait for thread completion.

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 KeWaitForSingleObject to wait for the ThreadEvent to become signalled. As stated previously, DebugPrintClose sets the ExitNow flag to true and sets the ThreadEvent into the signalled state. The thread function is released; if it finds ExitNow true, it exits its main loop, tidies up, and terminates.

The call to KeWaitForSingleObject includes a one-second time-out. This is used to let the thread function look for and process trace events in the EventList buffer, as described in the next sections.

Generating Trace Events

The two formatted print functions, DebugPrint and DebugPrint2, eventually call DebugPrintMsg. I will not go into the details of how the formatted prints work. You can work it out for yourself by looking at the code in the Print… and DebugSprintf routines in DebugPrint.c. The only point to note is that the DebugPrint routines can accept a variable number of arguments. I have assumed — successfully so far — that the va_list macros defined in stdarg.h work satisfactorily in kernel mode drivers.

Listing 14.2 shows how DebugPrintMsg builds a trace event and puts it in a DEBUGPRINT_EVENT structure allocated from nonpaged memory. The DEBUGPRINT_EVENT structure is added into the EventList doubly-linked list. DebugPrintMsg is passed a NULL-terminated ANSI message string.

The event data consists of the following three items:

• the current system time in GMT,

• the driver name (specified in DebugPrintInit), and

• the message.

DebugPrintMsg first gets the current system time in GMT using KeQuerySystemTime. It converts the number of 100-nanosecond intervals since January 1, 1601 into a more recognizable TIME_FIELDS structure using RtlTimeToTimeFields. The ExSystemTimeToLocalTime function (which converts from GMT to local time) is only available in W2000, so it is not used here. The time is converted to the local timezone in the DebugPrint Monitor application.

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 ExInterlockedInsertTailList to insert the DEBUGPRINT_EVENT structure at the end of EventList.

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);

 }

}

Linked Lists

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. EventData is a variable length field, as it is not always 1-byte long. The Len field gives its length.

typedef struct _DEBUGPRINT_EVENT {

 LIST_ENTRY ListEntry;

 ULONG Len;

 UCHAR EventData[1];

} DEBUGPRINT_EVENT, *PDEBUGPRINT_EVENT;

Initialize a doubly-linked list using InitializeListHead, passing a pointer to the list head variable. You can now insert DEBUGPRINT_EVENT structures at the head or tail of the list using the InsertHeadList and InsertTailList routines. The corresponding RemoveHeadList and RemoveTailList routines remove entries from the list[33]. Find out if the list is empty first using IsListEmpty.

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 interlocked versions of the add and remove routines that use a spin lock to guard access to the link structure. The DebugPrint test driver code uses a spin lock called EventListLock and initializes it as normal.

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);

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату
×