/*++ Copyright (c) 1998 Microsoft Corporation Module Name: ntsdexts.c Abstract: This function contains the default ntsd debugger extensions Author: Revision History: --*/ // // Lists threads sorted by CPU time consumed, in order to // track runaway threads // typedef struct _INTERESTING_THREAD_INFO { ULONG_PTR ThreadId ; ULONG_PTR Flags ; LARGE_INTEGER UserTime ; LARGE_INTEGER KernelTime ; LARGE_INTEGER ElapsedTime ; } INTERESTING_THREAD_INFO, * PINTERESTING_THREAD_INFO ; #define ITI_USER_DONE 0x00000001 #define ITI_KERNEL_DONE 0x00000002 #define ITI_ELAPSED_DONE 0x00000004 DECLARE_API( runaway ) { PROCESS_BASIC_INFORMATION ProcessInfo ; PSYSTEM_PROCESS_INFORMATION SystemInfo ; PSYSTEM_PROCESS_INFORMATION Walk ; PSYSTEM_THREAD_INFORMATION ThreadInfo ; PINTERESTING_THREAD_INFO Threads ; NTSTATUS Status ; ULONG Flags = 1 ; ULONG i, j, Found ; LARGE_INTEGER Now ; LARGE_INTEGER Compare ; TIME_FIELDS Time ; INIT_API(); if (sscanf( args, "%x", &Flags ) == 0) { goto Exit; } Status = NtQueryInformationProcess( g_hCurrentProcess, ProcessBasicInformation, &ProcessInfo, sizeof( ProcessInfo ), NULL ); if ( !NT_SUCCESS( Status ) ) { dprintf( "could not get process information, %d\n", RtlNtStatusToDosError( Status ) ); goto Exit; } SystemInfo = RtlAllocateHeap( RtlProcessHeap(), 0, 1024 * sizeof( SYSTEM_PROCESS_INFORMATION ) ); if ( !SystemInfo ) { dprintf( "not enough memory\n" ); goto Exit; } Status = NtQuerySystemInformation( SystemProcessInformation, SystemInfo, 1024 * sizeof( SYSTEM_PROCESS_INFORMATION ), NULL ); if ( !NT_SUCCESS( Status ) ) { dprintf( "unable to get system information\n" ); RtlFreeHeap( RtlProcessHeap(), 0, SystemInfo ); goto Exit; } // // First, find the process: // Walk = SystemInfo ; while ( HandleToUlong( Walk->UniqueProcessId ) != ProcessInfo.UniqueProcessId ) { if ( Walk->NextEntryOffset == 0 ) { Walk = NULL ; break; } Walk = (PSYSTEM_PROCESS_INFORMATION) ((PUCHAR) Walk + Walk->NextEntryOffset ); } if ( !Walk ) { dprintf( "unable to find process\n" ); RtlFreeHeap( RtlProcessHeap(), 0, SystemInfo ); goto Exit; } // // Now, walk the threads // ThreadInfo = (PSYSTEM_THREAD_INFORMATION) (Walk + 1); Threads = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof( INTERESTING_THREAD_INFO ) * Walk->NumberOfThreads ); if ( !Threads ) { dprintf( "not enough memory\n" ); RtlFreeHeap( RtlProcessHeap(), 0, SystemInfo ); goto Exit; } GetSystemTimeAsFileTime( (LPFILETIME) &Now ); for ( i = 0 ; i < Walk->NumberOfThreads ; i++ ) { Threads[ i ].Flags = 0 ; Threads[ i ].ThreadId = HandleToUlong( ThreadInfo[ i ].ClientId.UniqueThread ); Threads[ i ].ElapsedTime.QuadPart = Now.QuadPart - ThreadInfo[ i ].CreateTime.QuadPart ; Threads[ i ].KernelTime = ThreadInfo[ i ].KernelTime ; Threads[ i ].UserTime = ThreadInfo[ i ].UserTime ; } // // Scan through the list of threads (in an ugly, bubble-ish sort // of way), and display the threads in order of time, once per time // field, by way of the flags: // if ( Flags & ITI_USER_DONE ) { j = Walk->NumberOfThreads ; Found = 0 ; dprintf( " User Mode Time\n" ); dprintf( " Thread Time\n" ); while ( j-- ) { Compare.QuadPart = 0 ; for ( i = 0 ; i < Walk->NumberOfThreads ; i++ ) { if ( ( ( Threads[ i ].Flags & ITI_USER_DONE ) == 0 ) && ( Threads[ i ].UserTime.QuadPart >= Compare.QuadPart ) ) { Compare.QuadPart = Threads[ i ].UserTime.QuadPart ; Found = i ; } } Threads[ Found ].Flags |= ITI_USER_DONE ; RtlTimeToElapsedTimeFields( &Compare, &Time ); dprintf( " %-3x %3ld:%02ld:%02ld.%04ld\n", Threads[ Found ].ThreadId, Time.Hour, Time.Minute, Time.Second, Time.Milliseconds ); } } if ( Flags & ITI_KERNEL_DONE ) { j = Walk->NumberOfThreads ; Found = 0 ; dprintf( " Kernel Mode Time\n" ); dprintf( " Thread Time\n" ); while ( j-- ) { Compare.QuadPart = 0 ; for ( i = 0 ; i < Walk->NumberOfThreads ; i++ ) { if ( ( ( Threads[ i ].Flags & ITI_KERNEL_DONE ) == 0 ) && ( Threads[ i ].KernelTime.QuadPart >= Compare.QuadPart ) ) { Compare.QuadPart = Threads[ i ].KernelTime.QuadPart ; Found = i ; } } Threads[ Found ].Flags |= ITI_KERNEL_DONE ; RtlTimeToElapsedTimeFields( &Compare, &Time ); dprintf( " %-3x %3ld:%02ld:%02ld.%04ld\n", Threads[ Found ].ThreadId, Time.Hour, Time.Minute, Time.Second, Time.Milliseconds ); } } if ( Flags & ITI_ELAPSED_DONE ) { j = Walk->NumberOfThreads ; Found = 0 ; dprintf( " Elapsed Time\n" ); dprintf( " Thread Time\n" ); while ( j-- ) { Compare.QuadPart = 0 ; for ( i = 0 ; i < Walk->NumberOfThreads ; i++ ) { if ( ( ( Threads[ i ].Flags & ITI_ELAPSED_DONE ) == 0 ) && ( Threads[ i ].ElapsedTime.QuadPart >= Compare.QuadPart ) ) { Compare.QuadPart = Threads[ i ].ElapsedTime.QuadPart ; Found = i ; } } Threads[ Found ].Flags |= ITI_ELAPSED_DONE ; RtlTimeToElapsedTimeFields( &Compare, &Time ); dprintf( " %-3x %3ld:%02ld:%02ld.%04ld\n", Threads[ Found ].ThreadId, Time.Hour, Time.Minute, Time.Second, Time.Milliseconds ); } } if ( SystemInfo ) { RtlFreeHeap( RtlProcessHeap(), 0, SystemInfo ); } Exit: EXIT_API(); }