diff -r 000000000000 -r 818e61de6cd1 crashanalysercmd/Libraries/File Formats/Plugins/DExcPlugin/Transformer/DExcTransformer.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crashanalysercmd/Libraries/File Formats/Plugins/DExcPlugin/Transformer/DExcTransformer.cs Thu Feb 11 15:50:58 2010 +0200 @@ -0,0 +1,361 @@ +/* +* 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.Text; +using System.Text.RegularExpressions; +using System.IO; +using System.Collections.Generic; +using CrashItemLib.PluginAPI; +using CrashItemLib.Crash; +using CrashItemLib.Crash.Source; +using CrashItemLib.Crash.Processes; +using CrashItemLib.Crash.Registers; +using CrashItemLib.Crash.Registers.Special; +using CrashItemLib.Crash.Utils; +using CrashItemLib.Crash.Threads; +using CrashItemLib.Crash.CodeSegs; +using CrashItemLib.Crash.Stacks; +using CrashItemLib.Crash.Summarisable; +using CrashItemLib.Crash.Messages; +using CrashItemLib.Crash.Telephony; +using CrashItemLib.Crash.Memory; +using CrashItemLib.Crash.InfoHW; +using CrashItemLib.Crash.InfoSW; +using CrashItemLib.Crash.Events; +using CrashItemLib.Crash.Header; +using CrashItemLib.Crash.Container; +using SymbianStructuresLib.Arm.Registers; +using SymbianUtils.DataBuffer; +using SymbianUtils.DataBuffer.Primer; +using SymbianUtils.Range; +using DExcPlugin.Extractor; +using EM=DExcPlugin.ExpressionManager.DExcExpressionManager; + +namespace DExcPlugin.Transformer +{ + internal class DExcTransformer + { + #region Constructors + public DExcTransformer( CFFSource aDescriptor, CFFDataProvider aDataProvider, DExcExtractedData aData ) + { + iData = aData; + iDescriptor = aDescriptor; + iDataProvider = aDataProvider; + } + #endregion + + #region API + public CIContainer Transform() + { + try + { + iContainer = iDataProvider.CreateContainer( iDescriptor ); + + SaveInputData(); + + CreateHeader(); + CIProcess process = CreateProcess(); + CreateThread( process ); + } + catch ( Exception e ) + { +#if DEBUG + System.Diagnostics.Debug.WriteLine( "DEXC READER QUEUE EXCEPTION: " + e.Message ); + System.Diagnostics.Debug.WriteLine( "DEXC READER QUEUE STACK: " + e.StackTrace ); +#endif + // + iContainer = iDataProvider.CreateErrorContainer( iDescriptor ); + CIMessageError error = new CIMessageError( iContainer, "Error" ); + error.AddLine( e.Message ); + iContainer.Messages.Add( error ); + } + // + return iContainer; + } + #endregion + + #region Internal methods + private void SaveInputData() + { + string inputData = iData.ToString(); + + // Convert the entire text data into binary + List inputDataAsBytes = new List(); + + // We just want to map the raw unicode character onto a single byte. + // ASCII range is probably not sufficient (guess?) so this is why we + // do not use System.Text.ASCIIEncoding, but rather roll our own. + foreach ( char c in inputData ) + { + byte b = System.Convert.ToByte( c ); + inputDataAsBytes.Add( b ); + } + + CISourceElement source = iContainer.Source; + source.InputDataClear(); + source.InputDataAdd( inputDataAsBytes.ToArray() ); + } + + private void CreateHeader() + { + CIHeader header = iContainer.Header; + header.CrashTime = iDescriptor.MasterFile.LastWriteTime; + header.FileFormatVersion = "D_EXC for Symbian OS EKA2"; + } + + private CIProcess CreateProcess() + { + CIProcess process = new CIProcess( iContainer ); + + ExtractProcess( process ); + ExtractProcessCodeSegs( process ); + + iContainer.AddChild( process ); + return process; + } + + private CIThread CreateThread( CIProcess aProcess ) + { + // Make a new thread + CIThread thread = aProcess.CreateThread(); + + // Extract items + ExtractThread( thread ); + ExtractThreadExitReason( thread ); + ExtractThreadRegisters( thread ); + ExtractThreadStack( thread ); + + iContainer.AddChild( thread ); + return thread; + } + #endregion + + #region Internal constants + #endregion + + #region Helpers - process + private void ExtractProcess( CIProcess aProcess ) + { + // Extract process info from thread full name. + DExcExtractorList threadInfo = iData[ DExcExtractorListType.EListThread ]; + foreach ( string line in threadInfo ) + { + Match m = EM.ThreadName.Match( line ); + if ( m.Success ) + { + CIFullNameUtils parser = new CIFullNameUtils( m.Groups[ 1].Value ); + parser.GetProcessInfo( aProcess ); + + return; + } + } + } + + private void ExtractProcessCodeSegs( CIProcess aProcess ) + { + DExcExtractorList codeSegInfo = iData[ DExcExtractorListType.EListCodeSegments ]; + foreach ( string line in codeSegInfo ) + { + Match m = EM.CodeSegmentsEntry.Match( line ); + if ( m.Success ) + { + GroupCollection groups = m.Groups; + // + uint codeSegBase = uint.Parse( groups[ 1 ].Value, System.Globalization.NumberStyles.HexNumber ); + uint codeSegLimit = uint.Parse( groups[ 2 ].Value, System.Globalization.NumberStyles.HexNumber ); + string codeSegName = groups[ 3 ].Value; + // + aProcess.CreateCodeSeg( codeSegName, codeSegBase, codeSegLimit ); + } + } + } + #endregion + + #region Helpers - thread + private void ExtractThread( CIThread aThread ) + { + // Extract process info from thread full name. + DExcExtractorList threadInfo = iData[ DExcExtractorListType.EListThread ]; + foreach ( string line in threadInfo ) + { + Match m = EM.ThreadName.Match( line ); + if ( m.Success ) + { + CIFullNameUtils parser = new CIFullNameUtils( m.Groups[ 1 ].Value ); + parser.GetThreadInfo( aThread ); + } + else + { + m = EM.ThreadId.Match( line ); + if ( m.Success ) + { + aThread.Id = int.Parse( m.Groups[ 1 ].Value ); + } + } + } + } + + private void ExtractThreadExitReason( CIThread aThread ) + { + aThread.ExitInfo.Type = CrashItemLib.Crash.ExitInfo.CIExitInfo.TExitType.EExitTypeException; + + // Extract process info from thread full name. + DExcExtractorList threadInfo = iData[ DExcExtractorListType.EListThread ]; + foreach ( string line in threadInfo ) + { + Match m = EM.ThreadPanicDetails.Match( line ); + if ( m.Success ) + { + aThread.ExitInfo.Type = CrashItemLib.Crash.ExitInfo.CIExitInfo.TExitType.EExitTypePanic; + aThread.ExitInfo.Category = m.Groups[ 1 ].Value; + aThread.ExitInfo.Reason = int.Parse( m.Groups[ 2 ].Value ); + } + } + } + + private void ExtractThreadRegisters( CIThread aThread ) + { + CIThreadRegisterListCollection threadRegs = aThread.Registers; + CIRegisterList regListUser = threadRegs[ TArmRegisterBank.ETypeUser ]; + CIRegisterList regListEXC = threadRegs[ TArmRegisterBank.ETypeException ]; + CIRegisterList regListCOP = threadRegs[ TArmRegisterBank.ETypeCoProcessor ]; + CIRegisterList regListSVC = threadRegs[ TArmRegisterBank.ETypeSupervisor ]; + + #region User registers + foreach ( string line in iData[ DExcExtractorListType.EListRegistersUser ] ) + { + Match m = EM.RegistersUserSet.Match( line ); + if ( m.Success ) + { + GroupCollection groups = m.Groups; + int firstReg = int.Parse( groups[ 1 ].Value ); + for ( int i = firstReg; i < firstReg + 4; i++ ) + { + Group gp = groups[ 2 + ( i - firstReg ) ]; + uint value = uint.Parse( gp.Value, System.Globalization.NumberStyles.HexNumber ); + TArmRegisterType regType = (TArmRegisterType) i; + regListUser[ regType ].Value = value; + } + } + else + { + m = EM.RegistersUserCPSR.Match( line ); + if ( m.Success ) + { + // Get CPSR value and set it + uint cpsrValue = uint.Parse( m.Groups[ 1 ].Value, System.Globalization.NumberStyles.HexNumber ); + threadRegs.CPSR = cpsrValue; + } + } + } + #endregion + + #region Exception registers + foreach ( string line in iData[ DExcExtractorListType.EListRegistersException ] ) + { + Match m = EM.RegistersExceptionSet1.Match( line ); + if ( m.Success ) + { + GroupCollection groups = m.Groups; + // + regListEXC[ TArmRegisterType.EArmReg_EXCCODE ].Value = uint.Parse( m.Groups[ 1 ].Value, System.Globalization.NumberStyles.HexNumber ); + regListEXC[ TArmRegisterType.EArmReg_EXCPC ].Value = uint.Parse( m.Groups[ 2 ].Value, System.Globalization.NumberStyles.HexNumber ); + // + regListCOP[ TArmRegisterType.EArmReg_FAR ].Value = uint.Parse( m.Groups[ 3 ].Value, System.Globalization.NumberStyles.HexNumber ); + regListCOP[ TArmRegisterType.EArmReg_FSR ].Value = uint.Parse( m.Groups[ 4 ].Value, System.Globalization.NumberStyles.HexNumber ); + + if ( regListEXC.Contains( TArmRegisterType.EArmReg_EXCCODE ) ) + { + CIRegister reg = regListEXC[ TArmRegisterType.EArmReg_EXCCODE ]; + System.Diagnostics.Debug.Assert( reg is CIRegisterExcCode ); + CIRegisterExcCode excReg = (CIRegisterExcCode) reg; + // + excReg.ExpandToFullExceptionRange(); + } + + // It also means it was an exception + aThread.ExitInfo.Type = CrashItemLib.Crash.ExitInfo.CIExitInfo.TExitType.EExitTypeException; + } + else + { + m = EM.RegistersExceptionSet2.Match( line ); + if ( m.Success ) + { + GroupCollection groups = m.Groups; + // + regListSVC[ TArmRegisterType.EArmReg_SP ].Value = uint.Parse( m.Groups[ 1 ].Value, System.Globalization.NumberStyles.HexNumber ); + regListSVC[ TArmRegisterType.EArmReg_LR ].Value = uint.Parse( m.Groups[ 2 ].Value, System.Globalization.NumberStyles.HexNumber ); + regListSVC[ TArmRegisterType.EArmReg_SPSR ].Value = uint.Parse( m.Groups[ 3 ].Value, System.Globalization.NumberStyles.HexNumber ); + } + } + } + #endregion + } + + private void ExtractThreadStack( CIThread aThread ) + { + DExcExtractorListStackData stackDataList = (DExcExtractorListStackData) iData[ DExcExtractorListType.EListStackData ]; + DataBuffer stackData = stackDataList.StackData; + + // Get stack range details + DExcExtractorListThreadInfo threadInfo = (DExcExtractorListThreadInfo) iData[ DExcExtractorListType.EListThread ]; + AddressRange stackRange = threadInfo.StackRange; + + // If we didn't get the stack range, we cannot create a stack entry + if ( !stackRange.IsValid || stackRange.Max == 0 || stackRange.Min == 0 ) + { + CIMessageWarning warning = new CIMessageWarning( aThread.Container, "Stack Address Range Unavailable" ); + warning.AddLine( "The stack address range details are invalid." ); + aThread.AddChild( warning ); + } + else if ( stackData.Count == 0 ) + { + // No stack data + CIMessageWarning warning = new CIMessageWarning( aThread.Container, "Stack Data Unavailable" ); + warning.AddLine( "The crash details contain no stack data." ); + aThread.AddChild( warning ); + } + else + { + // Set base address of data buffer if not already set + if ( stackData.AddressOffset == 0 ) + { + stackData.AddressOffset = stackRange.Min; + } + + // In theory, D_EXC only ever captures user-side crashes (panics/exceptions) therefore + // we should always be able to assume that the stack data goes with a user-side thread. + CIRegisterList userRegs = aThread.Registers[ TArmRegisterBank.ETypeUser ]; + if ( userRegs != null ) + { + CIStack stack = aThread.CreateStack( userRegs, stackData, stackData.AddressOffset, stackRange ); + + // Register it as a specific crash instance + iContainer.AddChild( stack ); + } + } + } + #endregion + + #region Data members + private readonly CFFDataProvider iDataProvider; + private readonly CFFSource iDescriptor; + private readonly DExcExtractedData iData = null; + private CIContainer iContainer = null; + #endregion + } +}