/*++ Copyright (c) 1998-2001 Microsoft Corporation Module Name: ref.c Abstract: Implements the ref command. Author: Keith Moore (keithmo) 17-Jun-1998 Environment: User Mode. Revision History: --*/ #include "precomp.h" #undef BEGIN_REF_ACTION #undef END_REF_ACTION #undef REF_ACTION #define BEGIN_REF_ACTION() NAMED_REFTRACE_ACTION g_RefTraceActions[] = { #define END_REF_ACTION() }; #define REF_ACTION(x) { REF_ACTION_##x, #x }, #include "..\drv\refaction.h" #define NUM_REFTRACE_ACTIONS \ (sizeof(g_RefTraceActions) / sizeof(g_RefTraceActions[0])) const CHAR* Action2Name( ULONG Action) { if (Action < NUM_REFTRACE_ACTIONS) { ASSERT(g_RefTraceActions[Action].Action == Action); return g_RefTraceActions[Action].Name; } else return "????"; } typedef BOOLEAN (*FN_MATCH_CONTEXT)( IN ULONG_PTR context, IN PREF_TRACE_LOG_ENTRY plogEntry ); BOOLEAN RefMatchContext( IN ULONG_PTR context, IN PREF_TRACE_LOG_ENTRY plogEntry) { return (context == 0 || context == (ULONG_PTR) plogEntry->pContext); } BOOLEAN ThreadMatchContext( IN ULONG_PTR context, IN PREF_TRACE_LOG_ENTRY plogEntry) { return (context == 0 || context == (ULONG_PTR) plogEntry->pThread); } // Do all the real VOID DumpRefTrace( PCSTR args, FN_MATCH_CONTEXT pfnMatchContext, PCSTR cmd) { ULONG_PTR address = 0; ULONG_PTR context = 0; ULONG_PTR flags = 0; ULONG_PTR entryAddress; ULONG result; TRACE_LOG logHeader; REF_TRACE_LOG_ENTRY logEntry; PSTR fileName; LONGLONG index; ULONGLONG index2; ULONGLONG index1000; ULONG_PTR offset1; ULONG_PTR offset2; CHAR filePath[MAX_PATH]; PVOID pPrevFilePath; CHAR symbol1[MAX_SYMBOL_LENGTH]; CHAR symbol2[MAX_SYMBOL_LENGTH]; ULONG Dumped = 0; ULONG NonMatch = 0; ULONG64 address64 = 0; ULONG64 context64 = 0; ULONG64 flags64 = 0; ULONG NumToDump = 0; while (*args == ' ' || *args == '\t') { args++; } if (*args == '-') { args++; switch (*args) { case 'l' : for (index = 0; index < NUM_REFTRACE_ACTIONS; ++index) { dprintf( "%4u: REF_ACTION_%s\n", g_RefTraceActions[index].Action, g_RefTraceActions[index].Name); } return; default : PrintUsage( cmd ); return; } } // // Snag the address and optional context and flags from the command line. // if (! GetExpressionEx(args, &address64, &args)) { PrintUsage( cmd ); return; } if (GetExpressionEx(args, &context64, &args)) GetExpressionEx(args, &flags64, &args); address = (ULONG_PTR) address64; context = (ULONG_PTR) context64; flags = (ULONG_PTR) flags64; // // Read the log header. // if (!ReadMemory( address, &logHeader, sizeof(logHeader), &result )) { dprintf( "%s: cannot read TRACE_LOG @ %p\n", cmd, address ); return; } dprintf( "%s: log @ %p\n" " Signature = %08lx '%c%c%c%c' (%s)\n" " TypeSignature = %08lx '%c%c%c%c'\n" " LogSize = %lu\n" " NextEntry = %I64d\n" " EntrySize = %lu\n" " LogBuffer = %p\n", cmd, address, logHeader.Signature, DECODE_SIGNATURE(logHeader.Signature), logHeader.Signature == TRACE_LOG_SIGNATURE ? "OK" : logHeader.Signature == TRACE_LOG_SIGNATURE_X ? "FREED" : "INVALID", logHeader.TypeSignature, DECODE_SIGNATURE(logHeader.TypeSignature), logHeader.LogSize, logHeader.NextEntry, logHeader.EntrySize, logHeader.pLogBuffer ); if (logHeader.pLogBuffer > ( (PUCHAR)address + sizeof(logHeader) )) { dprintf( " ExtraData @ %p\n", address + sizeof(logHeader) ); } if (logHeader.Signature != TRACE_LOG_SIGNATURE && logHeader.Signature != TRACE_LOG_SIGNATURE_X) { dprintf( "%s: log @ %p has invalid signature %08lx:\n", cmd, address, logHeader.Signature ); return; } if (logHeader.EntrySize != sizeof(logEntry) || logHeader.TypeSignature != REF_TRACELOG_SIGNATURE ) { dprintf( "%s: log @ %p is not a ref count log\n", cmd, address ); return; } if (logHeader.NextEntry == -1) { dprintf( "%s: empty log @ %p\n", cmd, address ); return; } // // Calculate the log size to dump. // NumToDump = logHeader.LogSize; index = max( 0, (logHeader.NextEntry + 1) - NumToDump ); index2 = index % logHeader.LogSize; index1000 = index % 1000; pPrevFilePath = NULL; *filePath = '\0'; entryAddress = (ULONG_PTR) logHeader.pLogBuffer + (ULONG_PTR)( index2 * sizeof(logEntry) ); // // Dump the log. // for ( ; index <= logHeader.NextEntry; index++, index2++, index1000++, entryAddress += sizeof(logEntry) ) { if (CheckControlC()) { break; } if (index2 >= (ULONG)(logHeader.LogSize)) { index2 = 0; entryAddress = (ULONG_PTR) logHeader.pLogBuffer; } if (index1000 >= 1000) index1000 = 0; if (!ReadMemory( entryAddress, &logEntry, sizeof(logEntry), NULL )) { dprintf( "%s: cannot read memory @ %p\n", cmd, entryAddress ); return; } if ((*pfnMatchContext)(context, &logEntry)) { if (logEntry.pFileName != pPrevFilePath) { if (ReadMemory( (ULONG_PTR)logEntry.pFileName, filePath, sizeof(filePath), &result )) { fileName = strrchr( filePath, '\\' ); if (fileName != NULL) { fileName++; } else { fileName = filePath; } pPrevFilePath = logEntry.pFileName; } else { sprintf( filePath, "%p", logEntry.pFileName ); fileName = filePath; } } dprintf( "%s%4I64d: CPU=%lu Ctx=%p Act=%2lu %-30s Ref=%4d Src=%s:%lu\n", (NonMatch > 0) ? "\n" : "", index, (ULONG)logEntry.Processor, logEntry.pContext, (ULONG)logEntry.Action, Action2Name(logEntry.Action), logEntry.NewRefCount, fileName, (ULONG)logEntry.LineNumber ); if (flags & 1) { GetSymbol( logEntry.pCaller, symbol1, &offset1 ); GetSymbol( logEntry.pCallersCaller, symbol2, &offset2 ); dprintf( " Process=%p Thread=%p\n" " Caller1=%p (%s+0x%p)\n" " Caller2=%p (%s+0x%p)\n", logEntry.pProcess, logEntry.pThread, logEntry.pCaller, symbol1, offset1, logEntry.pCallersCaller, symbol2, offset2 ); } ++Dumped; NonMatch = 0; } else { if (index1000 == 0) dprintf("%I64d", index); if ((++NonMatch & 127) == 127) dprintf("."); } } if (context != 0) dprintf("%d entries dumped\n\n", Dumped); } // DumpRefTrace // // Public functions. // DECLARE_API( ref ) /*++ Routine Description: Dumps the reference trace log at the specified address. Arguments: None. Return Value: None. --*/ { SNAPSHOT_EXTENSION_DATA(); DumpRefTrace(args, RefMatchContext, "ref"); } // ref DECLARE_API( tref ) /*++ Routine Description: Dumps the trace log at the specified address. Filtering done by thread instead of context. Arguments: None. Return Value: None. --*/ { SNAPSHOT_EXTENSION_DATA(); DumpRefTrace(args, ThreadMatchContext, "tref"); } // ref