crashanalysercmd/Libraries/Engine/CrashItemLib/Crash/Processes/CIProcess.cs
author Matti Laitinen <matti.t.laitinen@nokia.com>
Thu, 11 Feb 2010 15:50:58 +0200
changeset 0 818e61de6cd1
permissions -rw-r--r--
Add initial version of Crash Analyser cmdline under EPL

/*
* 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.IO;
using System.Collections.Generic;
using SymbianUtils;
using SymbianUtils.Range;
using SymbianStructuresLib.Uids;
using SymbianStructuresLib.CodeSegments;
using SymbianStructuresLib.Debug.Common.FileName;
using SymbianStructuresLib.Debug.Symbols;
using SymbianDebugLib.Engine;
using SymbianDebugLib.PluginAPI.Types;
using SymbianDebugLib.PluginAPI.Types.Symbol;
using CrashItemLib.Crash;
using CrashItemLib.Crash.Base;
using CrashItemLib.Crash.Base.DataBinding;
using CrashItemLib.Crash.Threads;
using CrashItemLib.Crash.CodeSegs;
using CrashItemLib.Crash.Container;
using CrashItemLib.Crash.Symbols;
using CrashItemLib.Crash.Summarisable;

namespace CrashItemLib.Crash.Processes
{
    #region Attributes
    [CIDBAttributeColumn( "Name", 0 )]
    [CIDBAttributeColumn( "Value", 1, true )]
    #endregion
    public class CIProcess : CIElement, ICISymbolManager
	{
		#region Constructors
        public CIProcess( CIContainer aContainer )
            : base( aContainer )
		{
            base.AddChild( new CISymbolDictionary( aContainer ) );
		}
		#endregion

        #region API
        public CIThread CreateThread()
        {
            CIThread ret = new CIThread( this );
            base.AddChild( ret );
            return ret;
        }

        public CICodeSeg CreateCodeSeg( string aName, uint aBase, uint aLimit )
        {
            CICodeSeg ret = CreateCodeSeg( aName, aBase, aLimit, true );
            return ret;
        }
        #endregion

        #region Properties
        [CIDBAttributeCell( "UIDs", 5 )]
        public UidType Uids
        {
            get { return iUids; }
        }

        [CIDBAttributeCell( "Generation", 3 )]
        public int Generation
        {
            get { return iGeneration; }
            set { iGeneration = value; }
        }

        [CIDBAttributeCell( "Priority", 4, 0 )]
        public int Priority
        {
            get { return iPriority; }
            set { iPriority = value; }
        }

        [CIDBAttributeCell( "SID", 2, "x8", 0u )]
        public uint SID
        {
            get
            {
                uint ret = iSID;
                //
                if ( ret == 0 && iUids.MostSignificant != 0 )
                {
                    ret = iUids.MostSignificant;
                }
                //
                return ret; 
            }
            set 
            { 
                iSID = value;

                // Keep UID3 in line with SID
                if ( Uids[ 2 ] != value )
                {
                    Uids[ 2 ] = value;
                }
            }
        }

        [CIDBAttributeCell( "Name", 1 )]
        public override string Name
        {
            get { return iName; }
            set 
            { 
                iName = value; 

                // Make sure it ends in .exe
                if ( !iName.ToLower().EndsWith( KProcessExtension ) )
                {
                    iName += KProcessExtension;
                }
            }
        }

        public CICodeSegList CodeSegments
        {
            get
            {
                CIElementList<CICodeSeg> codeSegs = base.ChildrenByType<CICodeSeg>();

                // Sort them
                Comparison<CICodeSeg> comparer = delegate( CICodeSeg aLeft, CICodeSeg aRight )
                {
                    return string.Compare( aLeft.Name, aRight.Name, true );
                };
                codeSegs.Sort( comparer );

                CICodeSegList ret = new CICodeSegList( this, codeSegs );
                return ret; 
            }
        }

        public CIThread[] Threads
        {
            get
            {
                CIElementList<CIThread> list = base.ChildrenByType<CIThread>();
                CIThread[] ret = list.ToArray();
                return ret;
            }
        }

        public CIThread[] ThreadsWhichExitedAbnormally
        {
            get
            {
                // We use an anonymous delegate (predicate in this case) to search through
                // all the direct children of this object that have crashed.
                CIElementList<CIThread> threads = base.ChildrenByType<CIThread>( 
                    delegate(CIThread aThread )
                    {
                        return aThread.IsAbnormalTermination;
                    }
                );
                return threads.ToArray();
            }
        }

        public CIThread PrimaryThread
        {
            get
            {
                CIThread ret = null;
                //
                CIElementList<CIThread> threads = this.ChildrenByType<CIThread>();
                if ( threads.Count == 1 )
                {
                    ret = threads[ 0 ];
                }
                else
                {
                    // Assumption: The main thread (at least in symbian OS) is the thread which has
                    // the closest id to that of the process itself.
                    CIElementId minDelta = int.MaxValue;
                    foreach ( CIThread thread in this.GetEnumeratorThreads() )
                    {
                        CIElementId delta = thread.Id - this.Id;
                        //
                        if ( delta < minDelta )
                        {
                            ret = thread;
                        }
                        if ( delta == 1 )
                        {
                            // Optimisation
                            break;
                        }
                    }
                }
                //
                return ret;
            }
        }

        public CIThread FirstCrashedThread
        {
            get
            {
                CIThread  ret = null;

                // Try the primary thread first
                CIThread primary = PrimaryThread;
                if ( primary != null && primary.IsAbnormalTermination )
                {
                    ret = primary;
                }
                else
                {
                    // The primary summary is the first summary we can locate
                    // that relates to a crash.
                    foreach ( CIThread thread in this.GetEnumeratorThreads() )
                    {
                        bool isCrash = thread.IsAbnormalTermination;
                        if ( isCrash )
                        {
                            // Try to find corresponding summarisable entry
                            ret = thread;
                            break;
                        }
                    }
                }
                //
                return ret;
            }
        }

        public CISymbolDictionary Symbols
        {
            get { return base.ChildByType( typeof( CISymbolDictionary ) ) as CISymbolDictionary; }
        }

        public bool IsAbnormalTermination
        {
            get
            {
                bool ret = false;
                //
                foreach ( CIThread thread in this.GetEnumeratorThreads() )
                {
                    if ( thread.IsAbnormalTermination )
                    {
                        ret = true;
                        break;
                    }
                }
                //
                return ret;
            }
        }
        #endregion

        #region Enumerators
        public IEnumerable<CIThread> GetEnumeratorThreads()
        {
            return Threads;
        }

        public IEnumerable<CICodeSeg> GetEnumeratorCodeSegs()
        {
            return CodeSegments;
        }
        #endregion

        #region Internal constants
        private const string KProcessExtension = ".exe";
        private const string KUnknownProcessName = "UnknownProcess.exe";
        private const int KBaseOperationMultiplier = 5;
        #endregion

        #region Internal methods
        private CICodeSeg CreateCodeSeg( string aName, uint aBase, uint aLimit, bool aExplict )
        {
            CICodeSeg ret = new CICodeSeg( this );
            ret.Name = aName;
            ret.Base = aBase;
            ret.Limit = aLimit;
            ret.IsExplicit = aExplict;

            // Primary store is the child nodes
            base.AddChild( ret );

            base.Trace( "[CIProcess] CreateCodeSeg() - this: {0}, ret: {1}", this, ret );
            return ret;
        }

        private void EnsureCodeSegmentExistsForSymbol( CIElementFinalizationParameters aParams, CISymbol aSymbol )
        {
            if ( !aSymbol.IsNull )
            {
                Symbol symbol = aSymbol;
                SymbolCollection collection = symbol.Collection;
                //
                string binaryInDevice = PlatformFileNameConstants.Device.KPathWildcardSysBin;
                binaryInDevice += Path.GetFileName( collection.FileName.EitherFullNameButDevicePreferred );
                //
                CICodeSegList codeSegs = CodeSegments;
                bool alreadyExists = codeSegs.Contains( binaryInDevice );
                if ( !alreadyExists )
                {
                    // Assume no match found - create implicit/speculative code segment
                    AddressRange newCodeSegRange = collection.SubsumedPrimaryRange;
                    CICodeSeg newCodeSeg = CreateCodeSeg( binaryInDevice, newCodeSegRange.Min, newCodeSegRange.Max, false );

                    base.Trace( "[CIProcess] EnsureCodeSegmentExistsForSymbol() - creating implicitly identified code seg: " + newCodeSeg.ToString() + " for symbol: " + aSymbol.ToString() );

                    // Resolve it
                    newCodeSeg.Resolve( aParams.DebugEngineView );
                }
            }
        }

        private void DiscardImplicitCodeSegments()
        {
            // Go through each child and see if it's a code seg. If it is, and if 
            // it is implicit, throw it away
            int childCount = base.Count;
            for( int i=childCount-1; i>=0; i-- )
            {
                CIElement element = base[ i ];
                if ( element is CICodeSeg )
                {
                    CICodeSeg cs = (CICodeSeg) element;
                    //
                    if ( !cs.IsExplicit )
                    {
                        base.Trace( string.Format( "[CIProcess] DiscardImplicitCodeSegments() - dicarded: {0}", cs ) );
                        base.RemoveChild( cs );
                    }
                }
            }
        }

        private void CreateImplicitCodeSegments( CIElementFinalizationParameters aParams )
        {
            CIElementList<CISymbol> children = base.ChildrenByType<CISymbol>( TChildSearchType.EEntireHierarchy );
            base.Trace( string.Format( "[CIProcess] CreateImplicitCodeSegments() - children count: {1}, {0}", this, children.Count ) );
            //
            foreach ( CISymbol symbol in children )
            {
                EnsureCodeSegmentExistsForSymbol( aParams, symbol );
            }
        }
        #endregion

        #region From CIElement
        internal override void DoFinalize( CIElementFinalizationParameters aParams, Queue<CIElement> aCallBackLast, bool aForceFinalize )
        {
            base.Trace( string.Format( "[CIProcess] DoFinalize() - START - {0}", this ) );

            // The process' children need to use a process-relative debug engine view in order that they
            // can correctly resolve any RAM-loaded code.
            // Therefore, rather than use the so-called "global" debug engine view (which only has
            // XIP visibility) we create a process-specific set of finalization parameters (for use with 
            // the process' children) which contain a process-relative view of the world.
            using ( CIElementFinalizationParameters processRelativeParameters = new CIElementFinalizationParameters( aParams.Engine, this.Name, this.CodeSegments ) )
            {
                // Discard any implicit code segments. These are created automagically when an XIP symbol
                // is found. We'll re-create them anyway in a moment after the symbols have been updated.
                base.Trace( string.Format( "[CIProcess] DoFinalize() - discarding implicit XIP CodeSegs... - {0}", this ) );
                DiscardImplicitCodeSegments();
                base.Trace( string.Format( "[CIProcess] DoFinalize() - discarded implicit XIP CodeSegs - {0}", this ) );

                // Tell our children
                base.DoFinalize( processRelativeParameters, aCallBackLast, aForceFinalize );

                // Finally, re-create implicit XIP codesegments
                base.Trace( string.Format( "[CIProcess] DoFinalize() - creating implicit XIP CodeSegs... - {0}", this ) );
                CreateImplicitCodeSegments( aParams );
                base.Trace( string.Format( "[CIProcess] DoFinalize() - created implicit XIP CodeSegs - {0}", this ) );
            }
            //
            base.Trace( string.Format( "[CIProcess] DoFinalize() - END - {0}", this ) );
        }
        #endregion

        #region From System.Object
        public override string ToString()
        {
            return Name;
        }
        #endregion

        #region From ICISymbolManager
        public CISymbolDictionary SymbolDictionary
        {
            get { return this.Symbols; }
        }
        #endregion

        #region Data members
        private uint iSID = 0;
        private int iPriority = 0;
        private int iGeneration = 1;
        private UidType iUids = new UidType();
        private string iName = KUnknownProcessName;
        #endregion
    }
}