Click here to Skip to main content
15,887,683 members
Articles / Desktop Programming / WPF

Reflection Studio - Part 1 - Introduction: Architecture and Design

Rate me:
Please Sign up or sign in to vote.
4.83/5 (23 votes)
22 Sep 2010GPL36 min read 60K   6.9K   111  
Reflection Studio is a "developer" application for assembly, database, performance, and code generation, written in C# under WPF 4.0.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Threading;
using ReflectionStudio.Core.Properties;
using ReflectionStudio.Core.Events;
using ReflectionStudio.Core.FileManagement;

namespace ReflectionStudio.Core.Project
{
	public class SnapshotService
	{
		#region ----------------SINGLETON----------------

		public static readonly SnapshotService Instance = new SnapshotService();

		/// <summary>
		/// Private constructor for singleton pattern
		/// </summary>
		private SnapshotService()
		{
		}
		#endregion

		#region ----------------PROPERTIES----------------

		private SnapshotWatcher _Watcher = new SnapshotWatcher();

		private Dictionary<string, List<MethodMapInfo>> _Maps = new Dictionary<string, List<MethodMapInfo>>();

		private ObservableCollection<SnapshotEntity> _Snapshots = new ObservableCollection<SnapshotEntity>();
		public ObservableCollection<SnapshotEntity> Snapshots
		{
			get { return _Snapshots; }
			set { _Snapshots = value; }
		}

		#endregion

		public void OnProjectChange(object sender, ProjectEventArgs e)
		{
			Tracer.Verbose("SnapshotService.OnProjectChange START", e.ProjectType.ToString());

			try
			{
				switch (e.ProjectType)
				{
					//case ProjectEventType.Opening:
					case ProjectEventType.Opened:
						_Watcher.Start( ProjectService.Instance.Current.SubFolderSnapshot );
						ScanForSnapshotFile( ProjectService.Instance.Current.SubFolderSnapshot );
						Tracer.Verbose("SnapshotService.OnProjectChange", "OPEN event processed");
						break;

					case ProjectEventType.Closing:
					case ProjectEventType.Closed:
						_Watcher.Stop();
						Snapshots.Clear();
						Tracer.Verbose("SnapshotService.OnProjectChange", "CLOSE event processed");
						break;
					default:
						break;
				}
			}
			catch (Exception error)
			{
				Tracer.Error("SnapshotService:OnProjectChange", error);
			}
			finally
			{
				Tracer.Verbose("SnapshotService.OnProjectChange END", e.ProjectType.ToString());
			}
		}

		public void Delete(object snapEntity)
		{
			Tracer.Verbose("InjectionProfiler.Delete START", ((SnapshotEntity)snapEntity).FullFileName);

			try
			{
				new SnapshotDAC().Delete(((SnapshotEntity)snapEntity).FullFileName);
			}
			catch (Exception error)
			{
				Tracer.Error("InjectionProfiler.Delete", error);
			}
			finally
			{
				Tracer.Verbose("InjectionProfiler.Delete END", ((SnapshotEntity)snapEntity).FullFileName);
			}
		}

		public string GetMethodMapFile( string guid )
		{
			return System.IO.Path.Combine(ProjectService.Instance.Current.SubFolderSnapshot,
				string.Format("MethodMap_{0}.xml", guid));
		}

		public void ScanForSnapshotFile(string folderPath)
		{
			Tracer.Verbose("InjectionProfiler.ScanForSnapshotFile START", folderPath);

			try
			{
				DirectoryInfo inf = new DirectoryInfo(folderPath);
				foreach (FileInfo fi in inf.GetFiles( ProjectConstants.SnapshotExtensionFilter, SearchOption.TopDirectoryOnly))
				{
					Snapshots.Add(new SnapshotEntity()
										{
											DirectoryName = fi.DirectoryName,
											FileName = fi.Name,
											Length = fi.Length,
											LastWriteTime = fi.LastWriteTime
										});
				}
			}
			catch (Exception error)
			{
				Tracer.Error("InjectionProfiler.ScanForSnapshotFile", error);
			}
			finally
			{
				Tracer.Verbose("InjectionProfiler.ScanForSnapshotFile END", folderPath);
			}
		}

		public bool LoadSnapshot(SnapshotEntity sf)
		{
			if (sf.Loaded)
			{
				Tracer.Verbose("InjectionProfiler.LoadSnapshot allready loaded", sf.FileName);
				return true;
			}

			EventDispatcher.Instance.RaiseStatus(Resources.CORE_LOADING, StatusEventType.StartProgress);

			try
			{
				if (File.Exists(sf.BinaryFullFileName))
					//everything is pre-calculated in binary format, just load it
					LoadBinary(sf);
				else
				{
					LoadXml(sf);

					TransformToAgregated(sf);

					//save as binary format all the agregated data
					SaveBinary(sf);

					sf.Loaded = true;
				}
			}
			catch (Exception error)
			{
				Tracer.Error("SnapshotService.LoadSnapshot", error);
				EventDispatcher.Instance.RaiseStatus(Resources.CORE_LOADING_ERROR, StatusEventType.StopProgress);
			}
			finally
			{
				Tracer.Verbose("InjectionProfiler.LoadSnapshot finished", sf.FileName);
				EventDispatcher.Instance.RaiseStatus(Resources.CORE_SNAPSHOT_LOADED, StatusEventType.StopProgress);
			}

			return (sf != null);
		}

		private void SaveBinary(SnapshotEntity sf)
		{
			new SnapshotDAC().SaveBinarySnapshot(sf.BinaryFullFileName, sf.CallstackAg);
		}

		private void LoadBinary(SnapshotEntity sf)
		{
			sf.CallstackAg = new SnapshotDAC().LoadBinarySnapshot(sf.BinaryFullFileName);
		}

		internal bool TransformToAgregated(SnapshotEntity sf)
		{
			Tracer.Verbose("SnapshotService.TransformToAgregated started", sf.FileName);

			try
			{
				List<CallStackInfoAgregated> result = new List<CallStackInfoAgregated>();

				foreach (CallStackInfoExtended item in sf.CallstackEx)
				{
					CallStackInfoAgregated agre = result.Find(p => p.MethodHandle == item.MethodHandle);
					if (agre != null)
					{
						agre.CallCount++;
						agre.InternalTick += item.InternalTick;
						agre.AverageTick = agre.InternalTick / agre.CallCount;
					}
					else
					{
						CallStackInfoAgregated ex = new CallStackInfoAgregated(item);
						ex.CallCount = 1;
						ex.InternalTick = item.InternalTick;
						ex.AverageTick = ex.InternalTick;
						result.Add(ex);
					}
				}

				sf.CallstackAg = result;
				sf.CurrentAgreg = result;

				return true;
			}
			catch (Exception error)
			{
				Tracer.Error("SnapshotService.TransformToAgregated", error);
				EventDispatcher.Instance.RaiseStatus(Resources.CORE_LOADING_ERROR, StatusEventType.StopProgress);

				return false;
			}
			finally
			{
				Tracer.Verbose("SnapshotService.TransformToAgregated finished", sf.FileName);
				EventDispatcher.Instance.RaiseStatus(Resources.CORE_SNAPSHOT_LOADED, StatusEventType.StopProgress);
			}
		}

		internal bool LoadXml(SnapshotEntity sf)
		{
			Tracer.Verbose("SnapshotService.LoadXml started", sf.FileName);

			try
			{
				SnapshotInfo si = new SnapshotDAC().LoadSnapshot(sf.FullFileName);

				sf.Project = si.Project;
				sf.Version = si.Version;
				sf.MethodMapFile = si.Map;

				//the maps for methods name
				if( !LoadMap(sf) ) return false;

				//calculate the data : resolve methods name, calculate times, agregate
				if( !CalculateXml(si, sf) ) return false;

				sf.Loaded = true;

				return true;
			}
			catch (Exception error)
			{
				Tracer.Error("SnapshotService.LoadXml", error);
				return false;
			}
			finally
			{
				Tracer.Verbose("SnapshotService.LoadXml finished", sf.FileName);
			}
		}

		internal bool LoadMap(SnapshotEntity sf)
		{
			Tracer.Verbose("SnapshotService.LoadMap started", sf.FileName);

			try
			{
				if (!_Maps.ContainsKey(sf.MethodMapFile))
				{
					Tracer.Info( "SnapshotService.LoadMap", Resources.CORE_SNAPSHOT_LOAD_MAP);

					List<MethodMapInfo> mapinfo = (List<MethodMapInfo>)SerializeHelper.Deserialize(GetMethodMapFile(sf.MethodMapFile),
																										typeof(List<MethodMapInfo>));
					if (mapinfo != null)
						this._Maps.Add(sf.MethodMapFile, mapinfo);
				}
				return true;
			}
			catch (Exception error)
			{
				Tracer.Error("SnapshotService.LoadMap", error);
				return false;
			}
			finally
			{
				Tracer.Verbose("SnapshotService.LoadMap finished", sf.FileName);
			}
		}

		internal bool CalculateXml(SnapshotInfo si, SnapshotEntity sf)
		{
			Tracer.Verbose("SnapshotService.CalculateXml started", sf.FileName);

			try
			{
				//calculate the data : resolve methods name, calculate times, agregate
				Tracer.Info("SnapshotService.CalculateXml", Resources.CORE_SNAPSHOT_CALCULATE);
				sf.CallstackEx = CalculateSnapshot(si.CallStack);

				Tracer.Info("SnapshotService.CalculateXml", Resources.CORE_SNAPSHOT_RESOLVE_MAP);
				ResolveMethodNames(sf);

				return true;
			}
			catch (Exception error)
			{
				Tracer.Error("SnapshotService.CalculateXml", error);
				return false;
			}
			finally
			{
				Tracer.Verbose("SnapshotService.CalculateXml finished", sf.FileName);
			}
		}

		internal void ResolveMethodNames(SnapshotEntity sf)
		{
			Tracer.Verbose("SnapshotService.ResolveMethodNames START", sf.FileName);

			try
			{
				List<MethodMapInfo> mapinfo;
				_Maps.TryGetValue(sf.MethodMapFile, out mapinfo);

				foreach (CallStackInfoExtended item in sf.CallstackEx)
				{
					MethodMapInfo tmp = mapinfo.Single(p => p.Handle == item.MethodHandle);
					item.Namespace = tmp.Namespace;
					item.MethodName = tmp.Method;

					tmp = mapinfo.Single(p => p.Handle == item.CalledByHandle);
					item.CalledBy = tmp.Method;

					Tracer.Info("SnapshotService.ResolveMethodNames METHOD", "Handle {0}==> Method{1} + Handle {2}==> Method {3}",
						item.MethodHandle, item.MethodName, item.CalledByHandle, item.CalledBy);
				}
			}
			catch (Exception error)
			{
				Tracer.Error("SnapshotService.ResolveMethodNames", error);
			}
			finally
			{
				Tracer.Verbose("SnapshotService.ResolveMethodNames END", sf.FileName);
			}
		}

		internal List<CallStackInfoExtended> CalculateSnapshot(List<CallStackInfo> stack)
		{
			Tracer.Verbose("SnapshotService.CalculateSnapshot", "START calculating the snapshot");

			List<CallStackInfoExtended> result = new List<CallStackInfoExtended>();

			try
			{
				IEnumerable<CallStackInfo> mainCall = stack.Where(p => p.CalledByHandle == 0);
				foreach (CallStackInfo item in mainCall)
				{
					CallStackInfoExtended ex = new CallStackInfoExtended(item);
					result.Add(ex);
					CalculateCallStackInfoIternals(result, stack, ex);
				}
			}
			catch (Exception error)
			{
				Tracer.Error("SnapshotService.CalculateSnapshot", error);
			}
			finally
			{
				Tracer.Verbose("SnapshotService.CalculateSnapshot", "END calculating the snapshot");
			}
			return result;
		}

		internal void CalculateCallStackInfoIternals(List<CallStackInfoExtended> result, List<CallStackInfo> stack, CallStackInfoExtended parent)
		{
			Tracer.Verbose("SnapshotService.CalculateCallStackInfoIternals", "START calculating method times");

			try
			{
				IEnumerable<CallStackInfo> calledBy = stack.Where(p => p.CalledByHandle == parent.MethodHandle);
				if (calledBy != null)
				{
					//when starting the internal time method is equal to total time
					parent.InternalTick = parent.TotalTick;

					foreach (CallStackInfo item in calledBy)
					{
						//we remove the time spent in each called methods
						parent.InternalTick -= item.TotalTick;

						//we create a extended callstak object based on it
						CallStackInfoExtended ex = new CallStackInfoExtended(item);
						result.Add(ex);

						//recursive call through the call stack
						CalculateCallStackInfoIternals(result, stack, ex);
					}
				}
			}
			catch (Exception error)
			{
				Tracer.Error("SnapshotService.CalculateCallStackInfoIternals", error);
			}
			finally
			{
				Tracer.Verbose("SnapshotService.CalculateCallStackInfoIternals", "END calculating method times");
			}
		}

		#region ----------------DISTANT ACCESS----------------


		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Architect
France France
WPF and MVVM fan, I practice C # in all its forms from the beginning of the NET Framework without mentioning C ++ / MFC and other software packages such as databases, ASP, WCF, Web & Windows services, Application, and now Core and UWP.
In my wasted hours, I am guilty of having fathered C.B.R. and its cousins C.B.R. for WinRT and UWP on the Windows store.
But apart from that, I am a great handyman ... the house, a rocket stove to heat the jacuzzi and the last one: a wood oven for pizza, bread, and everything that goes inside

https://guillaumewaser.wordpress.com/
https://fouretcompagnie.wordpress.com/

Comments and Discussions