--- 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
- {
- // <summary>
- // Include stack data in hash
- // </summary>
- EIncludeStack = 1,
-
- // <summary>
- // 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.
- // </summary>
- EIncludeStackForced = 2,
-
- // <summary>
- // 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).
- // </summary>
- EIncludeOffsetsForAllSymbols = 4,
-
- // <summary>
- // Include the processor mode at the time of the crash within the hash.
- // </summary>
- EIncludeProcessorMode = 8,
-
- // <summary>
- // By default we output an MD5 hash, but you can enable plain text
- // output by using this option.
- // </summary>
- EOutputAsText = 16,
-
- // <summary>
- // A general purpose default configuration
- // </summary>
- 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<CIStackEntry> stackEntries = iStack.ChildrenByType<CIStackEntry>();
-
- // 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
- }
-}