diff -r 7a31f7298d8f -r 0c91f0baec58 crashanalysercmd/Libraries/File Formats/Plugins/CrashInfoFilePlugin/FileFormat/CCrashInfoHashBuilder.cs --- a/crashanalysercmd/Libraries/File Formats/Plugins/CrashInfoFilePlugin/FileFormat/CCrashInfoHashBuilder.cs Tue Feb 23 17:05:24 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,399 +0,0 @@ -/* -* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). -* All rights reserved. -* This component and the accompanying materials are made available -* under the terms of "Eclipse Public License v1.0" -* which accompanies this distribution, and is available -* at the URL "http://www.eclipse.org/legal/epl-v10.html". -* -* Initial Contributors: -* Nokia Corporation - initial contribution. -* -* Contributors: -* -* Description: -* -*/ -using System; -using System.IO; -using System.Text; -using System.Security.Cryptography; -using System.Collections.Generic; -using CrashItemLib.Crash; -using CrashItemLib.Crash.Base; -using CrashItemLib.Crash.Processes; -using CrashItemLib.Crash.Registers; -using CrashItemLib.Crash.Registers.Special; -using CrashItemLib.Crash.Stacks; -using CrashItemLib.Crash.Threads; -using CrashItemLib.Crash.Symbols; -using CrashItemLib.Crash.Summarisable; -using SymbianStructuresLib.Arm.Registers; - - -namespace CrashInfoFilePlugin.PluginImplementations.FileFormat -{ - public class CCrashInfoHashBuilder - { - #region Enumerations - [Flags] - public enum TConfiguration - { - // - // Include stack data in hash - // - EIncludeStack = 1, - - // - // Force inclusion of stack data in hash, even if hash builder - // believes that it's inclusion is not needed in order to uniquely - // pin-point the crash. - // - EIncludeStackForced = 2, - - // - // By default, offset is only included in program counter symbol. - // This will force it to be incldued for all symbols (i.e. even - // those within stack trace). - // - EIncludeOffsetsForAllSymbols = 4, - - // - // Include the processor mode at the time of the crash within the hash. - // - EIncludeProcessorMode = 8, - - // - // By default we output an MD5 hash, but you can enable plain text - // output by using this option. - // - EOutputAsText = 16, - - // - // A general purpose default configuration - // - EDefault = EIncludeStack | EIncludeStackForced | EIncludeProcessorMode - } - #endregion - - #region Factory - public static CCrashInfoHashBuilder New( TConfiguration aConfig, CISummarisableEntity aEntity ) - { - CCrashInfoHashBuilder ret = CCrashInfoHashBuilder.New( aConfig, aEntity, KDefaultNumberOfStackEntriesToCheckForSymbols ); - return ret; - } - - public static CCrashInfoHashBuilder New( TConfiguration aConfig, CISummarisableEntity aEntity, int aNumberOfStackEntriesToCheck ) - { - // By default we'll return an empty string for the hash if mandatory input - // elements are not available - CCrashInfoHashBuilder ret = null; - // - CIStack stack = aEntity.Stack; - CIThread thread = aEntity.Thread; - CIProcess process = aEntity.Process; - CIRegisterList regs = aEntity.Registers; - // - if ( stack != null && regs != null ) - { - if ( process == null || thread == null ) - { - ret = new CCrashInfoHashBuilder( aConfig, regs, stack, aNumberOfStackEntriesToCheck ); - } - else - { - ret = new CCrashInfoHashBuilder( aConfig, regs, stack, thread, process, aNumberOfStackEntriesToCheck ); - } - } - else - { - throw new ArgumentException( LibResources.KRes_CCrashInfoHashBuilder_BadSummarisable ); - } - // - return ret; - } - #endregion - - #region Constructors - private CCrashInfoHashBuilder( TConfiguration aConfig, CIRegisterList aRegisters, CIStack aStack, int aNumberOfStackEntriesToCheck ) - : this( aConfig, aRegisters, aStack, null, null, aNumberOfStackEntriesToCheck ) - { - } - - private CCrashInfoHashBuilder( TConfiguration aConfig, CIRegisterList aRegisters, CIStack aStack, CIThread aThread, CIProcess aProcess, int aNumberOfStackEntriesToCheck ) - { - iConfig = aConfig; - iRegisters = aRegisters; - iStack = aStack; - iProcess = aProcess; - iThread = aThread; - iNumberOfStackEntriesToCheck = aNumberOfStackEntriesToCheck; - } - #endregion - - #region API - public string GetHash() - { - StringBuilder hash = new StringBuilder(); - // - if ( ( iConfig & TConfiguration.EIncludeProcessorMode ) != 0 ) - { - string processorMode = GetProcessorMode(); - hash.AppendFormat( "MODE: [{0}] ", processorMode ); - } - // - string moduleName = GetAppropriateBinaryModuleName(); - hash.AppendFormat( "MODN: [{0}] ", moduleName ); - // - string programCounter = GetProgramCounter(); - hash.AppendFormat( "PC: [{0}] ", programCounter ); - // - if ( ( iConfig & TConfiguration.EIncludeStack ) != 0 || ( iConfig & TConfiguration.EIncludeStackForced ) != 0 ) - { - string stackSymbols = GetStackSymbols(); - hash.AppendFormat( "STK: [{0}] ", stackSymbols ); - } - - // Final stage is to MD5 hash the text, unless the caller requested - // plain text output. - string ret = hash.ToString(); - if ( ( iConfig & TConfiguration.EOutputAsText ) == 0 ) - { - ret = GetMD5( ret ); - } - // - return ret; - } - #endregion - - #region Internal constants - private const int KDefaultNumberOfStackEntriesToCheckForSymbols = 4; - #endregion - - #region Internal methods - private string GetProcessorMode() - { - string ret = LibResources.KRes_CCrashInfoHashBuilder_NoCPUMode; - // - if ( iRegisters.IsCurrentProcessorMode ) - { - CIRegisterCPSR cpsr = iRegisters[ TArmRegisterType.EArmReg_CPSR ] as CIRegisterCPSR; - if ( cpsr != null ) - { - ret = ArmRegisterBankUtils.BankAsString( cpsr.ProcessorMode ); - } - } - // - return ret; - } - - private string GetAppropriateBinaryModuleName() - { - // We'll use the name of the binary associated with the program - // counter symbol (if present) or then if not, we'll fall back - // to using the process name. - string ret = string.Empty; - // - bool fallBack = false; - if ( iRegisters.Contains( TArmRegisterType.EArmReg_PC ) ) - { - CIRegister pc = iRegisters[ TArmRegisterType.EArmReg_PC ]; - if ( pc.Symbol.IsNull == false ) - { - // Symbol available - use the associated binary name - string binName = pc.Symbol.Symbol.Collection.FileName.EitherFullNameButDevicePreferred; - ret = Path.GetFileName( binName ); - } - else - { - fallBack = true; - } - } - else - { - fallBack = true; - } - - // Do we need to fallback because symbol et al was unavailable? - if ( fallBack ) - { - // No Symbol, then in this case we'll try to fall back - // to the process name. - if ( iProcess != null ) - { - ret = iProcess.Name; - } - else - { - // Must be e.g. IRQ, FIQ, ABT, etc - ret = LibResources.KRes_CCrashInfoHashBuilder_AbortModeStack; - } - } - // - return ret; - } - - private string GetProgramCounter() - { - string ret = string.Empty; - // - if ( iRegisters.Contains( TArmRegisterType.EArmReg_PC ) ) - { - CIRegister pc = iRegisters[ TArmRegisterType.EArmReg_PC ]; - ret = CleanSymbol( pc.Value, pc.Symbol, true ); - } - // - return ret; - } - - private string GetStackSymbols() - { - StringBuilder ret = new StringBuilder(); - // - if ( iStack.IsStackOutputAvailable ) - { - bool isOverflow = iStack.IsOverflow; - bool isForced = ( iConfig & TConfiguration.EIncludeStackForced ) != 0; - bool isNullDereference = ( iStack.Registers.Contains( TArmRegisterType.EArmReg_00 ) && iStack.Registers[ TArmRegisterType.EArmReg_00 ].Value == 0 ); - - // If dealing with a stack overflow, then we don't, by default, include any - // symbols from the stack, as they are likely to be entirely dirty or "ghosts" for - // the most part. - // - // Furthermore, if the 'crash' was caused by dereferencing a NULL this pointer - // (which is somewhat of a guess on our part, but we ascertain this by inspecting - // the value of R0) then we don't include stack either. - // - // However, the above behaviour can be overriden by forcing stack processing. - bool processEntries = ( ( isOverflow == false && isNullDereference == false ) || isForced ); - if ( processEntries ) - { - // Get the entries and work out the number of items we should check for - // associated symbols. - CIElementList stackEntries = iStack.ChildrenByType(); - - // Discard all the entries that are outside of the stack pointer range. - // Once we see a register-based entry (for accurate decoding) or the current - // stack pointer entry, we've reached the start of the interesting stack data. - // - // However, if there has been a stack overflow then don't discard anything - // or else we'll end up with nothing left! - if ( iStack.IsOverflow == false && stackEntries.Count > 0 ) - { - int i = 0; - while ( stackEntries.Count > 0 ) - { - // If we see a register based entry then the stack reconstruction almost certainly - // used the accurate algorithm. - // - // Since we have already included the program counter in the hash text, it doesn't - // make sense to include it twice, so skip that. - // However, link register might be useful. - bool remove = true; - CIStackEntry entry = stackEntries[ i ]; - - if ( entry.IsCurrentStackPointerEntry ) - { - break; - } - else if ( entry.IsRegisterBasedEntry ) - { - // Preserve LR, skip PC - if ( entry.Register.Type == TArmRegisterType.EArmReg_LR ) - { - ++i; - remove = false; - } - } - - if ( remove ) - { - stackEntries.RemoveAt( 0 ); - } - } - } - - // Did the caller also want offsets within the output? By default only the program - // counter receives this treatment, but it can be overridden. - bool includeOffset = ( iConfig & TConfiguration.EIncludeOffsetsForAllSymbols ) != 0; - - // We should now have the stack entries directly relating to the crash call stack. - // Process them in turn, but only look at entries which happen to have associated - // symbols. - - int SymbolsNeeded = iNumberOfStackEntriesToCheck; - foreach (CIStackEntry entry in stackEntries) - { - if ( entry.Symbol != null && entry.Symbol.IsNull == false ) - { - string txt = CleanSymbol( entry.Data, entry.Symbol, includeOffset ); - ret.AppendFormat( "{0}, ", txt ); - SymbolsNeeded--; - } - if (SymbolsNeeded == 0) - { - break; - } - } - - // Remove trailing comma - string t = ret.ToString(); - if ( t.EndsWith( ", " ) ) - { - ret = ret.Remove( ret.Length - 2, 2 ); - } - } - } - // - string final = ret.ToString().TrimEnd(); - return final; - } - - private string GetMD5( string aMakeHash ) - { - MD5 md5 = MD5.Create(); - byte[] inputBytes = Encoding.ASCII.GetBytes( aMakeHash ); - byte[] hash = md5.ComputeHash( inputBytes ); - // - StringBuilder sb = new StringBuilder(); - // - for ( int i = 0; i < hash.Length; i++ ) - { - sb.Append( hash[ i ].ToString( "x2" ) ); - } - // - return sb.ToString(); - } - - private static string CleanSymbol( uint aAddress, CISymbol aSymbol, bool aAddOffset ) - { - string ret = string.Empty; - // - if ( aSymbol.IsNull == false ) - { - if ( aAddOffset ) - { - // Only include the name and offset, not the address - uint offset = aSymbol.Symbol.Offset( aAddress ); - ret = string.Format( "|0x{0:x4}| {1}", offset, aSymbol.Symbol.Name ); - } - else - { - ret = aSymbol.Symbol.Name; - } - } - // - return ret; - } - #endregion - - #region Data members - private readonly TConfiguration iConfig; - private readonly CIRegisterList iRegisters; - private readonly CIStack iStack; - private readonly CIThread iThread; - private readonly CIProcess iProcess; - private readonly int iNumberOfStackEntriesToCheck; - #endregion - } -}