|
|
/*++
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; }
|