crashanalysercmd/PerfToolsSharedLibraries/Engine/SymbolLib/Sources/Symbol/Engine/SymbolFileEngine.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.IO;
using System.Text;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using SymbianUtils;
using SymbianUtils.Range;
using SymbianUtils.Tracer;
using SymbianUtils.FileSystem;
using SymbianUtils.FileSystem.Utilities;
using SymbolLib.Generics;
using SymbolLib.CodeSegDef;
using SymbolLib.Sources.Symbol.Symbol;
using SymbolLib.Sources.Symbol.File;
using SymbolLib.Sources.Symbol.Parser;
using SymbolLib.Sources.Symbol.Collection;

namespace SymbolLib.Sources.Symbol.Engine
{
    internal class SymbolFileEngine : GenericSymbolEngine, SymbolCollectionCreator, SymbolEntryCreator
	{
		#region Events
		public event AsyncReaderBase.Observer Observer;
		#endregion

        #region Enumerations
        public enum TActivationType
        {
            EImmediate,
            EOnDemand
        }
        #endregion

		#region Constructors
        public SymbolFileEngine( ITracer aTracer, TActivationType aActivationType, bool aAllowNonRomSymbols )
            : base( aTracer )
		{
            iAllowNonRomSymbols = aAllowNonRomSymbols;
            iActivationType = aActivationType;
		}
		#endregion

		#region API
        public static bool IsSymbolFile( string aFileName )
        {
            string extension = Path.GetExtension( aFileName ).ToLower();
            return ( extension == KSymbolFileExtensionPrimary );
        }

        public SymbolsForBinary ReadFirstCollection( string aFileName )
        {
            bool isValid = false;
            return ReadFirstCollection( aFileName, out isValid );
        }

        public SymbolsForBinary ReadFirstCollection( string aFileName, out bool aIsSymbolFile )
        {
            iCurrentBinary = null;
            iSymbolFileName = aFileName;
            //
            iParser = new SymbolFileParser( this, this, aFileName, this );
            iParser.CollectionCompleted += new SymbolLib.Sources.Symbol.Parser.SymbolFileParser.CollectionCompletedHandler( Parser_CollectionCompletedSingleOnly );
            iParser.SymbolCreated += new SymbolLib.Sources.Symbol.Parser.SymbolFileParser.SymbolCreatedHandler( Parser_SymbolCreated );
            iParser.Read( TSynchronicity.ESynchronous );
            //
            SymbolsForBinary ret = null;
            if ( iAllSymbols.Count > 0 )
            {
                ret = iAllSymbols[ 0 ];
                aIsSymbolFile = true;
            }

            // Did we see any collections whatsoever (all be they data or code?)
            aIsSymbolFile = iParser.ContainedAtLeastOneCollectionFileName;

            return ret;
        }

        public void LoadFromFile( string aSymbolFileName, TSynchronicity aSynchronicity )
        {
			iSymbolFileName = aSymbolFileName;
			//
            iParser = new SymbolFileParser( this, this, aSymbolFileName, this );
            iParser.Tag = this;
			iParser.iObserver += new SymbianUtils.AsyncReaderBase.Observer( ParserEventHandler );
            iParser.CollectionCompleted += new SymbolLib.Sources.Symbol.Parser.SymbolFileParser.CollectionCompletedHandler( Parser_CollectionCompleted );
            iParser.SymbolCreated += new SymbolLib.Sources.Symbol.Parser.SymbolFileParser.SymbolCreatedHandler( Parser_SymbolCreated );
            iParser.Read( aSynchronicity );
        }

        public bool IsLoaded( CodeSegDefinition aDefinition )
        {
            // In ROM, variation might mean that the file name
            // doesn't match the definition name from the actual run-time code
            // segments loaded by a process...
            //
            // If we don't find a file name match, but we do find an address match
            // then we'll treat it as "good enough"
            bool activated = false;
            string searchingFor = aDefinition.ImageFileName.ToLower();
            string searchingForWithoutExtension = FSUtilities.StripAllExtensions( searchingFor );

            // Try to promote a symbol 
            foreach ( SymbolsForBinary file in iActivatedSymbols )
            {
                string entryName = System.IO.Path.GetFileName( file.HostBinaryFileName ).ToLower();
                if ( entryName == searchingFor )
                {
                    activated = true;
                    break;
                }
                else if ( file.AddressRangeStart == aDefinition.AddressStart && file.AddressRangeEnd == aDefinition.AddressEnd )
                {
                    // E.g: ROM.symbol says:         "From    \epoc32\release\armv5\urel\xxx.22.dll"
                    //      runtime code seg says:   "xxx.dll"
                    // We must use the base address instead...

                    activated = true;
                    break;
                }
                else
                {
                    // Try a fuzzy match
                    string entryNameWithoutExtension = FSUtilities.StripAllExtensions( entryName );
                    if ( entryNameWithoutExtension.Contains( searchingForWithoutExtension ) )
                    {
                        // Also make sure the base addresses are the same
                        if ( file.AddressRangeStart == aDefinition.AddressStart )
                        {
                            // Fuzzy match
                            activated = true;
                            break;
                        }
                    }
                }
            }

            return activated;
        }

        public bool Load( CodeSegDefinition aDefinition )
        {
            bool activated = false;
            string searchingFor = aDefinition.EnvironmentFileName.ToLower();
            if ( string.IsNullOrEmpty( searchingFor ) )
            {
                // Try to use on-target name instead, if valid
                searchingFor = aDefinition.ImageFileName.ToLower();
            }

            // Try to promote a symbol 
            foreach( SymbolsForBinary file in iIdleSymbols )
            {
                string entryName = System.IO.Path.GetFileName( file.HostBinaryFileName ).ToLower();
                if  ( entryName == searchingFor )
                {
#if DEBUG
                    System.Diagnostics.Debug.WriteLine( "   LOAD {S}: " + aDefinition.ToString() );
#endif

                    // Fix up the symbols in this collection
                    file.Fixup( aDefinition.AddressStart );
					
                    // Update ranges
                    iRange.UpdateMin( file.AddressRangeStart );
                    iRange.UpdateMax( file.AddressRangeEnd );

                    // Housekeeping
                    iActivatedSymbols.Add( file );
                    iIdleSymbols.Remove( file );
                    iActivatedSymbols.Sort();

                    // Even though the symbols for this binary may not be explicitly referenced
                    // they are definitely required by the callee, therefore we tag them
                    // immediately.
                    file.Tagged = true;

                    // Indicate we loaded the code seg from a symbol file
                    aDefinition.Source = CodeSegDefinition.TSourceType.ESourceWasSymbolFile;

                    activated = true;
                    break;
                }
            }

            return activated;
        }

        public bool Unload( CodeSegDefinition aDefinition )
        {
            bool suspended = false;
            string searchingFor = aDefinition.EnvironmentFileName.ToLower();

            // Try to promote a symbol 
            foreach( SymbolsForBinary file in iActivatedSymbols )
            {
                string entryName = System.IO.Path.GetFileName( file.HostBinaryFileName ).ToLower();
                if  ( entryName == searchingFor )
                {
#if DEBUG
                    System.Diagnostics.Debug.WriteLine( " UNLOAD {S}: " + aDefinition.ToString() );
#endif
                    //
                    iActivatedSymbols.Remove( file );
                    iIdleSymbols.Add( file );
                    //
                    iRange.UpdateMin( file.AddressRangeStart );
                    iRange.UpdateMax( file.AddressRangeEnd );
                    //
                    suspended = true;

                    // NB: We don't untag the file since it was obviously needed at
                    // some point.
                    break;
                }
            }

            return suspended;
        }

		public void UnloadAll()
		{
			iActivatedSymbols.Clear();
			iIdleSymbols.Clear();
			//
			foreach( SymbolsForBinary file in iAllSymbols )
			{
				iIdleSymbols.Add( file );
			}
		}
		#endregion

        #region Properties
		public string SymbolFileName
		{
			get { return iSymbolFileName; }
		}

        public bool AllowNonRomSymbols
        {
            get { return iAllowNonRomSymbols; }
        }

		public int Progress
		{
			get
			{
				int prog = 0;
				//
				if	( iParser != null )
				{
					prog = iParser.Progress;
				}
				//
				return prog;
			}
		}

        public string[] BinaryFileNames
        {
            get
            {
                List<string> fileNames = new List<string>( AllSymbols.Count );
                //
                foreach ( GenericSymbolCollection collection in AllSymbols )
                {
                    fileNames.Add( collection.HostBinaryFileName );
                }
                //
                return fileNames.ToArray();
            }
        }

		public SymbolsForBinaryCollection AllSymbols
		{
			get { return iAllSymbols; }
		}
		#endregion

        #region From GenericSymbolEngine
        public override void Reset()
		{
            iActivatedSymbols.Clear();
			iIdleSymbols.Clear();
			iAllSymbols.Clear();
            iSymbolFileName = string.Empty;
            iCurrentBinary = null;
            iRange = new AddressRange();
		}

        public override bool IsLoaded( string aFileName )
        {
            return ( aFileName.ToLower() == iSymbolFileName.ToLower() );
        }

        public override bool IsReady
        {
            get
            {
                bool ready = ( SymbolFileName.Length > 0 );
                //
                if	( ready && iParser != null )
                {
                    ready = iParser.IsReady;
                }
                //
                return ready;
            }
        }

        public override GenericSymbolCollection this[ int aIndex ]
        {
            get
            {
                return iActivatedSymbols[ aIndex ];
            }
        }

        public override void SaveTaggedCollections( string aFileName )
        {
            // We override this so that we search through 'iAllSymbols' rather
            // than just the activated symbols (which would be the case if we
            // used the base class version of this method).
            using ( StreamWriter writer = new StreamWriter( aFileName, false ) )
            {
                foreach ( GenericSymbolCollection collection in iAllSymbols )
                {
                    if ( collection.Tagged )
                    {
                        System.Diagnostics.Debug.WriteLine( "STORING: " + collection.HostBinaryFileName );
                        collection.WriteToStream( writer );
                    }
                }
            }
        }

        public override AddressRange Range
        {
            get
            {
                return iRange;
            }
        }

        internal override void UnloadUntagged()
        {

        }
        #endregion

        #region From IGenericSymbolCollectionStatisticsInterface
        public override int NumberOfCollections
        {
            get { return iActivatedSymbols.Count; }
        }
        #endregion

		#region AsyncReaderBase observer
		private void ParserEventHandler( SymbianUtils.AsyncReaderBase.TEvent aEvent, SymbianUtils.AsyncReaderBase aObject )
		{
			if	( Observer != null )
			{
				Observer( aEvent, aObject );
			}

            if  ( aEvent == AsyncReaderBase.TEvent.EReadingComplete )
            {
                iActivatedSymbols.Sort();
                iParser = null;
            }
		}
		#endregion

        #region Parser observer - for normal parsing
        private bool Parser_CollectionCompleted( SymbolsForBinary aCollection )
        {
            // Check whether the collection contains any item. If it doesn't, ditch it.
            // Remove empty collections or sort completed ones.
            bool takeCollection = false;
            int count = aCollection.Count;
            if ( count > 0 )
            {
                // Check whether the collection contains at least 2 symbols, and if not
                // does the one and only symbol just have a length of zero?
                if ( count == 1 )
                {
                    GenericSymbol symbol = aCollection.FirstSymbol;
                    takeCollection = ( symbol.Size > 0 ) || symbol.IsUnknownSymbol;
                }
                else
                {
                    takeCollection = true;
                }
            }

            // If its okay to take the collection, let's sort it and activate if necessary.
            if ( takeCollection )
            {
#if INSPECT_SYMBOL_DATA
                using ( StreamWriter writer = new StreamWriter( @"C:\Temp\OldSymbols\" + Path.GetFileName( aCollection.HostBinaryFileName ) + ".symbol" ) )
                {
                    aCollection.WriteToStream( writer );
                }
#endif
                // All the symbol collections - whether they are loaded or idle.
                iAllSymbols.Add( aCollection );

                // Then put the collection in the correct container depending on
                // activation type.
                if ( iActivationType == TActivationType.EImmediate )
                {
                    aCollection.Sort();
                    iActivatedSymbols.Add( aCollection );
                    //
                    iRange.UpdateMin( aCollection.AddressRangeStart );
                    iRange.UpdateMax( aCollection.AddressRangeEnd );
                }
                else if ( iActivationType == TActivationType.EOnDemand )
                {
                    ThreadPool.QueueUserWorkItem( new WaitCallback( SortCollection ), aCollection );
                    iIdleSymbols.Add( aCollection );
                }
            }
            else
            {
                //System.Diagnostics.Debug.WriteLine( "Discarded Symbol Collection: " + aCollection.TargetBinary );
                //System.Diagnostics.Debug.WriteLine( " " );
            }

            iCurrentBinary = null;
            return SymbolFileParser.KCollectionCompletedAndContinueParsing;
        }

        private void Parser_SymbolCreated( SymbolSymbol aSymbol )
        {
            // 1) We accept symbols with an address of zero, providing that their size is greater
            //    than zero.
            //
            // 2) We accept symbols with an address greater than zero, irrespective of their size.
			if	( ( aSymbol.Address >= 0 || aSymbol.Size > 0 ) || // 1
                  ( aSymbol.Address == 0 && aSymbol.Size > 0 )    // 2
                )
			{
                iCurrentBinary.Add( this, aSymbol, AllowNonRomSymbols );
			}
            else
            {
                System.Diagnostics.Debug.WriteLine( "  Discarded symbol: " + aSymbol );
            }
        }
        #endregion

        #region Parser observer - for peeking at a symbol file's first collection
        private bool Parser_CollectionCompletedSingleOnly( SymbolLib.Sources.Symbol.File.SymbolsForBinary aCollection )
        {
            // Call our standard function to handle the collection
            Parser_CollectionCompleted( aCollection );

            // Indicate no more parsing required
            bool ret = SymbolFileParser.KCollectionCompletedAndContinueParsing;
            if ( aCollection.Count > 0 && iAllSymbols.Count > 0 )
            {
                ret = SymbolFileParser.KCollectionCompletedAndAbortParsing;
            }
            //
            return ret;
        }
        #endregion

        #region SymbolCollectionCreator interface
        public SymbolsForBinary CreateCollection( string aHostFileName )
        {
            iCurrentBinary = new SymbolsForBinary( aHostFileName );
            iCurrentBinary.SourceFile = SymbolFileName;
            return iCurrentBinary;
        }
        #endregion

        #region SymbolEntryCreator interface
        public SymbolSymbol CreateSymbol()
        {
            return SymbolSymbol.New( iCurrentBinary );
        }
        #endregion

        #region Internal methods
        private void CalculateRange()
        {
            iRange.Reset();
            //
            foreach( SymbolsForBinary file in iActivatedSymbols )
            {
                iRange.UpdateMin( file.AddressRangeStart );
                iRange.UpdateMax( file.AddressRangeEnd );
            }
        }

        private void SortCollection( object aCollection )
        {
            SymbolsForBinary symbols = aCollection as SymbolsForBinary;
            if ( symbols != null )
            {
                symbols.Sort();
            }
        }
        #endregion

        #region Internal constants
        private const string KSymbolFileExtensionPrimary = ".symbol";
        #endregion

        #region Data members
        private readonly TActivationType iActivationType;
        private readonly bool iAllowNonRomSymbols;
		private string iSymbolFileName = string.Empty;
        private SymbolsForBinary iCurrentBinary = null;
        private SymbolFileParser iParser = null;
        private AddressRange iRange = new AddressRange();
		private SymbolsForBinaryCollection iAllSymbols = new SymbolsForBinaryCollection();
		private SymbolsForBinaryCollection iIdleSymbols = new SymbolsForBinaryCollection();
        private SymbolsForBinaryCollection iActivatedSymbols = new SymbolsForBinaryCollection();
        #endregion
    }
}