crashanalysercmd/Libraries/File Formats/Plugins/DExcPlugin/Transformer/DExcTransformer.cs
changeset 0 818e61de6cd1
--- /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<byte> inputDataAsBytes = new List<byte>();
+
+            // 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
+	}
+}