Click here to Skip to main content
15,891,704 members
Articles / Desktop Programming / XAML

Diagnostic Explorer

Rate me:
Please Sign up or sign in to vote.
4.93/5 (41 votes)
29 Nov 2010LGPL315 min read 82.5K   1.5K   120  
A .NET library and website which allows developers to expose and view arbitrary diagnostic information about their .NET processes.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Diagnostics;
using System.Timers;
using System.Threading;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Configuration;
using System.Reflection;

namespace DiagnosticExplorer
{
	public static partial class DiagnosticManager
	{
		private static List<RegisteredObject> RegisteredObjects { get; set; }
		private static Dictionary<string, List<PropertyGetter>> typeHash
	= new Dictionary<string, List<PropertyGetter>>();

		static DiagnosticManager()
		{
			RegisteredObjects = new List<RegisteredObject>();
		}

		public static void Register(object o, string bagName, string bagCategory)
		{
			lock (RegisteredObjects)
			{
				RegisteredObject existing = RegisteredObjects.Find(
					ro => ReferenceEquals(ro.Object, o));

				if (existing == null)
				{
					bagName += GetUniqueBagNameExtension(bagName);
					RegisteredObjects.Add(new RegisteredObject(o, bagName, bagCategory));
				}
			}
		}

		public static void Unregister(object obj)
		{
			lock (RegisteredObjects)
			{
				RegisteredObject existing = RegisteredObjects.Find(
					ro => ReferenceEquals(ro.Object, obj));

				if (existing != null)
					RegisteredObjects.Remove(existing);
			}
		}


		public static IEnumerable<PropertyBag> GetRegisteredProperties()
		{
			RegisteredObject[] objects = null;

			lock (RegisteredObjects)
				objects = RegisteredObjects.ToArray();

			foreach (RegisteredObject regObj in objects)
			{
				object obj = regObj.Object;
				if (obj == null)
				{
					lock (RegisteredObjects)
						RegisteredObjects.Remove(regObj);
				}
				else
				{
					yield return ObjectToPropertyBag(obj, regObj.BagName, regObj.BagCategory);
				}
			}
		}

		private static string GetUniqueBagNameExtension(string name)
		{
			if (name == null)
				return null;

			if (!BagNameExists(name))
				return null;

			for (int i = 2; ; i++)
			{
				string extension = string.Format(" {0}", i);
				string newName = string.Format("{0}{1}", name, extension);
				if (!BagNameExists(newName))
					return extension;
			}
		}

		private static bool BagNameExists(string name)
		{
			lock (RegisteredObjects)
			{
				RegisteredObject regObj = RegisteredObjects.Find(
					ro => string.Compare(name, ro.BagName, true) == 0);

				return regObj != null;
			}
		}

		internal static PropertyBag ObjectToPropertyBag(object obj, string bagName, string bagCategory)
		{
			PropertyBag bag = new PropertyBag();
			bag.Name = bagName;
			bag.Category = bagCategory;

			List<PropertyGetter> valueGetters = GetPropertyGetters(obj);

			foreach (PropertyGetter getter in valueGetters)
				bag.Properties.AddRange(getter.GetProperties(obj));

			return bag;
		}

		public const BindingFlags PublicInstancePropertyFlags = BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Instance;
		public const BindingFlags PublicStaticPropertyFlags = BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static;

		internal static List<PropertyGetter> GetPropertyGetters(object obj, string categoryPrepend)
		{
			List<PropertyGetter> getters = GetPropertyGetters(obj);
			List<PropertyGetter> newGetters = new List<PropertyGetter>();

			foreach (PropertyGetter g in getters)
				newGetters.Add(new CategoryPrependPropertyGetter(g, categoryPrepend));

			return newGetters;
		}

		internal static List<PropertyGetter> GetPropertyGetters(object obj)
		{
			if (obj == null) return new List<PropertyGetter>();

			Type type = obj.GetType();
			string typeKey = type.AssemblyQualifiedName;
			if (obj is Type)
			{
				type = (Type)obj;
				typeKey = "Static: " + type.AssemblyQualifiedName;
			}

			List<PropertyGetter> propertyList;
			if (!typeHash.TryGetValue(typeKey, out propertyList))
			{
				propertyList = new List<PropertyGetter>();

				IEnumerable<PropertyInfo> properties = obj is Type ? GetStaticProperties(type) : GetInstanceProperties(type, null);
				foreach (PropertyInfo info in properties)
				{
					Type underlying = GetUnderlyingType(info.PropertyType);

					PropertyAttribute propAttr = GetAttribute<PropertyAttribute>(info);
					CollectionPropertyAttribute colPropAttr = propAttr as CollectionPropertyAttribute;
					ExtendedPropertyAttribute extPropAttr = propAttr as ExtendedPropertyAttribute;

					if (colPropAttr != null)
					{
						propertyList.Add(new CollectionGetter(info, colPropAttr));
					}
					else if (extPropAttr != null)
					{
						propertyList.Add(new ExtendedPropertyGetter(info, extPropAttr));
					}
					else if (info.PropertyType == typeof(RateCounter))
					{
						RatePropertyAttribute rateAttr = propAttr as RatePropertyAttribute;
						propertyList.Add(new RateGetter(info, rateAttr));
					}
					else if (underlying == typeof(DateTime))
					{
						DatePropertyAttribute dateAttr = propAttr as DatePropertyAttribute;
						propertyList.Add(new DateGetter(info, dateAttr));
					}
					else
					{
						propertyList.Add(new PropertyGetter(info));
					}
				}
				typeHash[typeKey] = propertyList;
			}
			return propertyList;
		}

		private static IEnumerable<PropertyInfo> GetInstanceProperties(Type type, DiagnosticClassAttribute inheritedAttr)
		{
			if (type != typeof (object))
			{
				DiagnosticClassAttribute diagAttr = GetAttribute<DiagnosticClassAttribute>(type, false);

				if (inheritedAttr == null || !inheritedAttr.DeclaringTypeOnly || diagAttr != null)
				{
					foreach (PropertyInfo propInfo in type.GetProperties(PublicInstancePropertyFlags | BindingFlags.DeclaredOnly))
						if (ShouldIncludeProperty(diagAttr ?? inheritedAttr, type, propInfo))
							yield return propInfo;
				}

				foreach (PropertyInfo propInfo in GetInstanceProperties(type.BaseType, diagAttr ?? inheritedAttr))
					yield return propInfo;
			}
		}

		private static IEnumerable<PropertyInfo> GetStaticProperties(Type type)
		{
			DiagnosticClassAttribute diagAttr = GetAttribute<DiagnosticClassAttribute>(type, false);

			return type
				.GetProperties(PublicStaticPropertyFlags)
				.Where(propInfo => ShouldIncludeProperty(diagAttr, type, propInfo));
		}

		private static bool ShouldIncludeProperty(DiagnosticClassAttribute diagAttr, Type declaringType, PropertyInfo info)
		{
			if (info.PropertyType == typeof(EventSink)) return false;
			if (info.DeclaringType.IsAssignableFrom(typeof(ApplicationSettingsBase)))
				return GetAttribute<SettingAttribute>(info) != null;

			bool attributedOnly = diagAttr != null && diagAttr.AttributedPropertiesOnly;
			BrowsableAttribute browseAttr = GetAttribute<BrowsableAttribute>(info);
			PropertyAttribute propAttr = GetAttribute<PropertyAttribute>(info);

			if (propAttr != null) 
				return !propAttr.Ignore;
			
			if (browseAttr != null && !browseAttr.Browsable)
				return false;

			if (attributedOnly)
				return browseAttr != null;

			return true;
		}

		public static Type GetUnderlyingType(Type t)
		{
			if (t == null) throw new ArgumentNullException("t");

			if (!t.IsGenericType) return t;
			if (t.GetGenericTypeDefinition() != typeof(Nullable<>)) return t;

			return t.GetGenericArguments()[0];
		}

		private static T GetAttribute<T>(PropertyInfo info) where T : Attribute
		{
			object[] attrs = info.GetCustomAttributes(typeof(T), false);
			if (attrs.Length == 0)
				return null;

			return attrs[0] as T;
		}

		private static T GetAttribute<T>(Type info, bool inherit) where T : Attribute
		{
			object[] attrs = info.GetCustomAttributes(typeof(T), inherit);
			if (attrs.Length == 0)
				return null;

			return attrs[0] as T;
		}

	}
}

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 Lesser General Public License (LGPLv3)


Written By
Software Developer
United Kingdom United Kingdom
I am a software developer originally from Auckland, New Zealand. I have lived and worked in London since 2005.

Comments and Discussions