487 lines
13 KiB
C
487 lines
13 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1992 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
time.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
WinDbg Extension Api
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Ramon J San Andres (ramonsa) 8-Nov-1993
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
User Mode.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
VOID
|
|||
|
FileTimeToString(
|
|||
|
IN LARGE_INTEGER Time,
|
|||
|
IN BOOLEAN TimeZone,
|
|||
|
OUT PCHAR Buffer
|
|||
|
);
|
|||
|
|
|||
|
ULONG64
|
|||
|
DumpKTimer(
|
|||
|
IN ULONG64 pTimer,
|
|||
|
IN ULONGLONG InterruptTimeOffset,
|
|||
|
IN OPTIONAL ULONG64 Blink
|
|||
|
)
|
|||
|
{
|
|||
|
ULONG64 Displacement;
|
|||
|
CHAR Buff[256];
|
|||
|
ULONG Result;
|
|||
|
ULONG64 NextThread;
|
|||
|
LARGE_INTEGER SystemTime;
|
|||
|
ULONG Period, Off;
|
|||
|
LARGE_INTEGER Due;
|
|||
|
ULONG64 Dpc, DeferredRoutine, WaitList_Flink, Timer_Flink, Timer_Blink;
|
|||
|
|
|||
|
if ( GetFieldValue(pTimer, "nt!_KTIMER", "DueTime.QuadPart", Due.QuadPart) ) {
|
|||
|
dprintf("Unable to get contents of Timer @ %p\n", pTimer );
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
SystemTime.QuadPart = Due.QuadPart + InterruptTimeOffset;
|
|||
|
if (SystemTime.QuadPart < 0) {
|
|||
|
strcpy(Buff, " NEVER ");
|
|||
|
} else {
|
|||
|
FileTimeToString(SystemTime, FALSE, Buff);
|
|||
|
}
|
|||
|
|
|||
|
GetFieldValue(pTimer, "nt!_KTIMER", "Period", Period);
|
|||
|
GetFieldValue(pTimer, "nt!_KTIMER", "Dpc", Dpc);
|
|||
|
GetFieldValue(pTimer, "nt!_KTIMER", "Header.WaitListHead.Flink", WaitList_Flink);
|
|||
|
GetFieldValue(pTimer, "nt!_KTIMER", "TimerListEntry.Flink", Timer_Flink);
|
|||
|
GetFieldValue(pTimer, "nt!_KTIMER", "TimerListEntry.Blink", Timer_Blink);
|
|||
|
|
|||
|
dprintf("%c %08lx %08lx [%s] ",
|
|||
|
(Period != 0) ? 'P' : ' ',
|
|||
|
Due.LowPart,
|
|||
|
Due.HighPart,
|
|||
|
Buff);
|
|||
|
|
|||
|
if (Dpc != 0) {
|
|||
|
if (GetFieldValue(Dpc, "nt!_KDPC", "DeferredRoutine", DeferredRoutine)) {
|
|||
|
dprintf("Unable to get contents of DPC @ %p\n", Dpc);
|
|||
|
return(0);
|
|||
|
}
|
|||
|
// dprintf("p(%p)", DeferredRoutine);
|
|||
|
GetSymbol(DeferredRoutine,
|
|||
|
Buff,
|
|||
|
&Displacement);
|
|||
|
dprintf("%s",Buff);
|
|||
|
if (Displacement != 0) {
|
|||
|
dprintf("+%1p ", Displacement);
|
|||
|
} else {
|
|||
|
dprintf(" ");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// List all the threads
|
|||
|
//
|
|||
|
NextThread = WaitList_Flink;
|
|||
|
GetFieldOffset("nt!_KTIMER", "Header.WaitListHead", &Off);
|
|||
|
while (WaitList_Flink && (NextThread != pTimer+Off)) {
|
|||
|
ULONG64 Flink;
|
|||
|
ULONG64 Thread=0;
|
|||
|
|
|||
|
if (GetFieldValue(NextThread, "nt!_KWAIT_BLOCK", "Thread", Thread)) {
|
|||
|
dprintf("Unable to get contents of waitblock @ %p\n", NextThread);
|
|||
|
} else {
|
|||
|
dprintf("thread %p ",Thread);
|
|||
|
}
|
|||
|
|
|||
|
if (GetFieldValue(NextThread,
|
|||
|
"nt!_LIST_ENTRY",
|
|||
|
"Flink",
|
|||
|
Flink)) {
|
|||
|
dprintf("Unable to read next WaitListEntry @ %p\n",NextThread);
|
|||
|
break;
|
|||
|
}
|
|||
|
NextThread = Flink;
|
|||
|
}
|
|||
|
|
|||
|
dprintf("\n");
|
|||
|
|
|||
|
if (Blink &&
|
|||
|
(Timer_Blink != Blink)) {
|
|||
|
dprintf(" Timer at %p has wrong Blink! (Blink %08p, should be %08p)\n",
|
|||
|
pTimer,
|
|||
|
Timer_Blink,
|
|||
|
Blink);
|
|||
|
}
|
|||
|
|
|||
|
if (Timer_Flink == 0) {
|
|||
|
dprintf(" Timer at %p has been zeroed! (Flink %08p, Blink %08p)\n",
|
|||
|
pTimer,
|
|||
|
Timer_Flink,
|
|||
|
Timer_Blink);
|
|||
|
}
|
|||
|
|
|||
|
return(Timer_Flink);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DECLARE_API( timer )
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Dumps all timers in the system.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
args -
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG CurrentList;
|
|||
|
ULONG Index;
|
|||
|
LARGE_INTEGER InterruptTime;
|
|||
|
LARGE_INTEGER SystemTime;
|
|||
|
ULONG MaximumList;
|
|||
|
ULONG MaximumSearchCount=0;
|
|||
|
ULONG MaximumTimerCount;
|
|||
|
ULONG64 NextEntry;
|
|||
|
ULONG64 LastEntry;
|
|||
|
ULONG64 p;
|
|||
|
ULONG64 NextTimer;
|
|||
|
ULONG64 KeTickCount;
|
|||
|
ULONG64 KiMaximumSearchCount;
|
|||
|
ULONG Result;
|
|||
|
ULONG64 TickCount=0;
|
|||
|
ULONG64 TimerTable;
|
|||
|
ULONG TotalTimers;
|
|||
|
ULONG KtimerOffset;
|
|||
|
ULONG TimerListOffset;
|
|||
|
ULONG WakeTimerListOffset;
|
|||
|
ULONG64 WakeTimerList;
|
|||
|
ULONG64 pETimer, Temp;
|
|||
|
ULONG64 SharedUserData;
|
|||
|
CHAR Buffer[256];
|
|||
|
ULONGLONG InterruptTimeOffset;
|
|||
|
UCHAR TypName[]="_KUSER_SHARED_DATA";
|
|||
|
CHAR SystemTime1[12]={0}, InterruptTime1[12]={0};
|
|||
|
FIELD_INFO offField = {"TimerListEntry", NULL, 0, DBG_DUMP_FIELD_RETURN_ADDRESS, 0, NULL};
|
|||
|
SYM_DUMP_PARAM TypeSym ={
|
|||
|
sizeof (SYM_DUMP_PARAM), "nt!_KTIMER", DBG_DUMP_NO_PRINT, 0,
|
|||
|
NULL, NULL, NULL, 1, &offField
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
SharedUserData = MM_SHARED_USER_DATA_VA;
|
|||
|
//
|
|||
|
// Get the system time and print the header banner.
|
|||
|
//
|
|||
|
if (!GetFieldValue(SharedUserData, TypName, "SystemTime", SystemTime1) )
|
|||
|
{
|
|||
|
// For x86
|
|||
|
GetFieldValue(SharedUserData, TypName, "InterruptTime.High1Time", InterruptTime.HighPart);
|
|||
|
GetFieldValue(SharedUserData, TypName, "InterruptTime.LowPart", InterruptTime.LowPart);
|
|||
|
|
|||
|
GetFieldValue(SharedUserData, TypName, "SystemTime.High1Time", SystemTime.HighPart);
|
|||
|
GetFieldValue(SharedUserData, TypName, "SystemTime.LowPart", SystemTime.LowPart);
|
|||
|
}
|
|||
|
else if (!GetFieldValue(SharedUserData, TypName, "InterruptTime", InterruptTime1) ) {
|
|||
|
// For Alphas
|
|||
|
InterruptTime.QuadPart = *((PULONG64) &InterruptTime1[0]);
|
|||
|
SystemTime.QuadPart = *((PULONG64) &SystemTime1[0]);
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
dprintf("%08p: Unable to get shared data\n",SharedUserData);
|
|||
|
return E_INVALIDARG;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
#ifdef TARGET_ALPHA
|
|||
|
InterruptTime.QuadPart = SharedData.InterruptTime;
|
|||
|
SystemTime.QuadPart = SharedData.SystemTime;
|
|||
|
#else
|
|||
|
InterruptTime.HighPart = SharedData.InterruptTime.High1Time;
|
|||
|
InterruptTime.LowPart = SharedData.InterruptTime.LowPart;
|
|||
|
SystemTime.HighPart = SharedData.SystemTime.High1Time;
|
|||
|
SystemTime.LowPart = SharedData.SystemTime.LowPart;
|
|||
|
#endif
|
|||
|
*/
|
|||
|
|
|||
|
InterruptTimeOffset = SystemTime.QuadPart - InterruptTime.QuadPart;
|
|||
|
FileTimeToString(SystemTime, TRUE, Buffer);
|
|||
|
|
|||
|
dprintf("Dump system timers\n\n");
|
|||
|
dprintf("Interrupt time: %08lx %08lx [%s]\n\n",
|
|||
|
InterruptTime.LowPart,
|
|||
|
InterruptTime.HighPart,
|
|||
|
Buffer);
|
|||
|
|
|||
|
//
|
|||
|
// Get the address of the timer table list head array and scan each
|
|||
|
// list for timers.
|
|||
|
//
|
|||
|
|
|||
|
dprintf("List Timer Interrupt Low/High Fire Time DPC/thread\n");
|
|||
|
MaximumList = 0;
|
|||
|
|
|||
|
TimerTable = GetExpression( "nt!KiTimerTableListHead" );
|
|||
|
if ( !TimerTable ) {
|
|||
|
dprintf("Unable to get value of KiTimerTableListHead\n");
|
|||
|
return E_INVALIDARG;
|
|||
|
}
|
|||
|
|
|||
|
TotalTimers = 0;
|
|||
|
// Get The TimerListOffset in KTIMER offset
|
|||
|
if (Ioctl(IG_DUMP_SYMBOL_INFO, &TypeSym, TypeSym.size)) {
|
|||
|
return E_INVALIDARG ;
|
|||
|
}
|
|||
|
TimerListOffset = (ULONG) offField.address;
|
|||
|
|
|||
|
for (Index = 0; Index < TIMER_TABLE_SIZE; Index += 1) {
|
|||
|
|
|||
|
//
|
|||
|
// Read the forward link in the next timer table list head.
|
|||
|
//
|
|||
|
|
|||
|
if ( GetFieldValue(TimerTable, "nt!_LIST_ENTRY", "Flink", NextEntry)) {
|
|||
|
dprintf("Unable to get contents of next entry @ %p\n", TimerTable );
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Scan the current timer list and display the timer values.
|
|||
|
//
|
|||
|
|
|||
|
LastEntry = TimerTable;
|
|||
|
CurrentList = 0;
|
|||
|
while (NextEntry != TimerTable) {
|
|||
|
CurrentList += 1;
|
|||
|
NextTimer = NextEntry - TimerListOffset; // CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
|
|||
|
TotalTimers += 1;
|
|||
|
|
|||
|
if (CurrentList == 1) {
|
|||
|
dprintf("%3ld %08p ", Index, NextTimer);
|
|||
|
} else {
|
|||
|
dprintf(" %08p ", NextTimer);
|
|||
|
}
|
|||
|
|
|||
|
p = LastEntry;
|
|||
|
LastEntry = NextEntry;
|
|||
|
NextEntry = DumpKTimer(NextTimer, InterruptTimeOffset, p);
|
|||
|
if (NextEntry==0) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (CheckControlC()) {
|
|||
|
return E_INVALIDARG;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
TimerTable += GetTypeSize("nt!_LIST_ENTRY");
|
|||
|
if (CurrentList > MaximumList) {
|
|||
|
MaximumList = CurrentList;
|
|||
|
}
|
|||
|
if (CheckControlC()) {
|
|||
|
return E_INVALIDARG;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
dprintf("\n\nTotal Timers: %d, Maximum List: %d\n",
|
|||
|
TotalTimers,
|
|||
|
MaximumList);
|
|||
|
|
|||
|
//
|
|||
|
// Get the current tick count and convert to the hand value.
|
|||
|
//
|
|||
|
|
|||
|
KeTickCount = GetExpression( "nt!KeTickCount" );
|
|||
|
if ( KeTickCount && !GetFieldValue(KeTickCount, "ULONG", NULL, TickCount)) {
|
|||
|
dprintf("Current Hand: %d", (ULONG) TickCount & (TIMER_TABLE_SIZE - 1));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the maximum search count if the target system is a checked
|
|||
|
// build and display the count.
|
|||
|
//
|
|||
|
|
|||
|
KiMaximumSearchCount = GetExpression( "nt!KiMaximumSearchCount" );
|
|||
|
if ( KiMaximumSearchCount &&
|
|||
|
!GetFieldValue(KiMaximumSearchCount, "ULONG", NULL, Temp)) {
|
|||
|
MaximumSearchCount = (ULONG) Temp;
|
|||
|
dprintf(", Maximum Search: %d", MaximumSearchCount);
|
|||
|
}
|
|||
|
|
|||
|
dprintf("\n\n");
|
|||
|
|
|||
|
//
|
|||
|
// Dump the list of wakeable timers
|
|||
|
//
|
|||
|
dprintf("Wakeable timers:\n");
|
|||
|
WakeTimerList = GetExpression("nt!ExpWakeTimerList");
|
|||
|
if (!WakeTimerList) {
|
|||
|
dprintf("Unable to get value of ExpWakeTimerList\n");
|
|||
|
return E_INVALIDARG;
|
|||
|
}
|
|||
|
|
|||
|
// Get The WakeTimerLis tOffset in ETIMER
|
|||
|
TypeSym.sName = "_ETIMER";
|
|||
|
offField.fName = "WakeTimerListEntry";
|
|||
|
if (Ioctl(IG_DUMP_SYMBOL_INFO, &TypeSym, TypeSym.size)) {
|
|||
|
return E_INVALIDARG;
|
|||
|
}
|
|||
|
TimerListOffset = (ULONG) offField.address;
|
|||
|
|
|||
|
offField.fName = "KeTimer";
|
|||
|
Ioctl(IG_DUMP_SYMBOL_INFO, &TypeSym, TypeSym.size);
|
|||
|
KtimerOffset = (ULONG) offField.address;
|
|||
|
|
|||
|
//
|
|||
|
// Read the forward link in the wake timer list
|
|||
|
//
|
|||
|
if (!ReadPointer(WakeTimerList,
|
|||
|
&NextEntry)) {
|
|||
|
dprintf("Unable to get contents of next entry @ %p\n",WakeTimerList);
|
|||
|
return E_INVALIDARG;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Scan the timer list and display the timer values.
|
|||
|
//
|
|||
|
while (NextEntry != WakeTimerList) {
|
|||
|
pETimer = NextEntry - TimerListOffset;
|
|||
|
|
|||
|
dprintf("%08lx\t", pETimer + KtimerOffset);
|
|||
|
DumpKTimer(pETimer + KtimerOffset, InterruptTimeOffset, 0);
|
|||
|
|
|||
|
|
|||
|
GetFieldValue(pETimer, "_ETIMER", "WakeTimerListEntry.Flink", NextEntry);
|
|||
|
// NextEntry = ETimer.WakeTimerListEntry.Flink;
|
|||
|
|
|||
|
if (CheckControlC()) {
|
|||
|
return E_INVALIDARG;
|
|||
|
}
|
|||
|
}
|
|||
|
dprintf("\n");
|
|||
|
|
|||
|
return S_OK;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FileTimeToString(
|
|||
|
IN LARGE_INTEGER Time,
|
|||
|
IN BOOLEAN TimeZone,
|
|||
|
OUT PCHAR Buffer
|
|||
|
)
|
|||
|
{
|
|||
|
TIME_FIELDS TimeFields;
|
|||
|
TIME_ZONE_INFORMATION TimeZoneInfo;
|
|||
|
PWCHAR pszTz;
|
|||
|
ULONGLONG TzBias;
|
|||
|
DWORD Result;
|
|||
|
|
|||
|
//
|
|||
|
// Get the local (to the debugger) timezone bias
|
|||
|
//
|
|||
|
Result = GetTimeZoneInformation(&TimeZoneInfo);
|
|||
|
if (Result == 0xffffffff) {
|
|||
|
pszTz = L"UTC";
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Bias is in minutes, convert to 100ns units
|
|||
|
//
|
|||
|
TzBias = (ULONGLONG)TimeZoneInfo.Bias * 60 * 10000000;
|
|||
|
switch (Result) {
|
|||
|
case TIME_ZONE_ID_UNKNOWN:
|
|||
|
pszTz = L"unknown";
|
|||
|
break;
|
|||
|
case TIME_ZONE_ID_STANDARD:
|
|||
|
pszTz = TimeZoneInfo.StandardName;
|
|||
|
break;
|
|||
|
case TIME_ZONE_ID_DAYLIGHT:
|
|||
|
pszTz = TimeZoneInfo.DaylightName;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
Time.QuadPart -= TzBias;
|
|||
|
}
|
|||
|
|
|||
|
RtlTimeToTimeFields(&Time, &TimeFields);
|
|||
|
if (TimeZone) {
|
|||
|
sprintf(Buffer, "%2d/%2d/%d %02d:%02d:%02d.%03d (%ws)",
|
|||
|
TimeFields.Month,
|
|||
|
TimeFields.Day,
|
|||
|
TimeFields.Year,
|
|||
|
TimeFields.Hour,
|
|||
|
TimeFields.Minute,
|
|||
|
TimeFields.Second,
|
|||
|
TimeFields.Milliseconds,
|
|||
|
pszTz);
|
|||
|
} else {
|
|||
|
sprintf(Buffer, "%2d/%2d/%d %02d:%02d:%02d.%03d",
|
|||
|
TimeFields.Month,
|
|||
|
TimeFields.Day,
|
|||
|
TimeFields.Year,
|
|||
|
TimeFields.Hour,
|
|||
|
TimeFields.Minute,
|
|||
|
TimeFields.Second,
|
|||
|
TimeFields.Milliseconds);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DECLARE_API( filetime )
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Reformats a 64-bit NT time (FILETIME) as something a human
|
|||
|
being can understand
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
args - 64-bit filetime to reformat
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LARGE_INTEGER Time;
|
|||
|
CHAR Buffer[256];
|
|||
|
|
|||
|
Time.QuadPart = GetExpression(args);
|
|||
|
|
|||
|
if (Time.QuadPart == 0) {
|
|||
|
dprintf("!filetime <64-bit FILETIME>\n");
|
|||
|
} else {
|
|||
|
FileTimeToString(Time,TRUE, Buffer);
|
|||
|
dprintf("%s\n",Buffer);
|
|||
|
}
|
|||
|
return S_OK;
|
|||
|
}
|