Thursday, July 14, 2016

ETW event listener

using Diagnostics.Tracing;
using Diagnostics.Tracing.Parsers;
using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Threading;
using System.Threading.Tasks;

// This program is a simple demo, it has both a EventSource and a EventGenerator which generate events for 10 seconds
// as well as a 'listener; that listens for these events (potentially in a differnet process, but we don't do that here)
// and does some rudimentary processing (calculates the difference between two particular events.  
namespace SimpleMonitor
{
    using Microsoft.Cloud.InstrumentationFramework;

    // This code belongs in the process generating the events.  They are samples
    [EventSource(Name = "Microsoft-Demos-SimpleMonitor")]     // This is the name of my eventSource outside my program.
    class MyEventSource : EventSource
    {
        // Notice that the bodies of the events follow a pattern:  WriteEvent(ID, <args>) where ID is a unique ID
        // Starting at 1 and giving each different event a unque one, and passing all the payload arguments on to be sent out.
        public void MyFirstEvent(string MyName, int MyId) { WriteEvent(1, MyName, MyId); }
        public void MySecondEvent(int MyId) { WriteEvent(2, MyId); }
        public void Stop() { WriteEvent(3); }

        // Typically you only create one EventSource and use it throughout your program.  Thus a static field makes sense.
        public static MyEventSource Log = new MyEventSource();
    }

    // This code belongs in the process generating the events.   It is in this process for simplicity.
    class EventGenerator
    {
        public static void CreateEvents()
        {
            // This just spanws a thread that generates events every second for 10 seconds than issues a Stop event.
            Task.Factory.StartNew(delegate
            {
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("** Generateing the MyFirst and MySecond events.");
                    MyEventSource.Log.MyFirstEvent("Some string", i);
                    Thread.Sleep(10);
                    MyEventSource.Log.MySecondEvent(i);
                    Thread.Sleep(1000);
                }
                //Console.WriteLine("** Generating the Stop Event.");
                //MyEventSource.Log.Stop();
             
                    string monitoringAccountName = "TestAccount";
                    string monitoringNamespace = "TestNamespace";
                    string metricName = "TestMetric";
                    string[] dimensionNames = new[] {"Name"};
                    string[] dimensionValues = new[] {"Vivek"};
                    //ErrorContext errorContext = new ErrorContext();
                    //MeasureMetric measureMetric = MeasureMetric.Create(
                    //    monitoringAccountName,
                    //    monitoringNamespace,
                    //    metricName,
                    //    true,
                    //    dimensionNames);
                    //measureMetric.LogValue(5, ref errorContext, dimensionValues);
             
            });
        }
    }

    /// <summary>
    /// The main program is the 'listener' that listens and processes the events that come from EventGenerator
    /// </summary>
    class Program
    {
        /// <summary>
        /// This is a demo of using TraceEvent to activate a 'real time' provider that is listening to
        /// the MyEventSource above.   Normally this event source would be in a differnet process,  but
        /// it also works if this process generate the evnets and I do that here for simplicity.
        /// </summary>
        static int Main(string[] args)
        {
            try
            {
                // This is the name of the event source.  
                var providerName = "Microsoft-Demos-SimpleMonitor";
                Debug.Assert(providerName == MyEventSource.Log.Name);
                // Given just the name of the eventSource you can get the GUID for the evenSource by calling this API.
                // From a ETW perspective, the GUID is the 'true name' of the EventSource.
                var providerGuid = TraceEventSession.GetEventSourceGuidFromName(providerName);
                Debug.Assert(providerGuid == MyEventSource.Log.Guid);

                var provider2 = "Test-Event";
                var providerGuid2 = TraceEventSession.GetEventSourceGuidFromName(provider2);

                var provider3 = "IfxMetricsEvent";
                var providerGuid3 = TraceEventSession.GetEventSourceGuidFromName(provider3);

                var provider4 = "Microsoft-Cloud-InstrumentationFramework-Metrics";
                var providerGuid4 = TraceEventSession.GetEventSourceGuidFromName(provider4);

                var provider5 = "MetricEtwEvent";
                var providerGuid5 = TraceEventSession.GetEventSourceGuidFromName(provider5);

                // Today you have to be Admin to turn on ETW events (anyone can write ETW events).  
                if (!(TraceEventSession.IsElevated() ?? false))
                {
                    Console.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process.");
                    return -1;
                }

                // As mentioned below, sessions can outlive the process that created them.  Thus you need a way of
                // naming the session so that you can 'reconnect' to it from another process.   This is what the name
                // is for.  It can be anything, but it should be descriptive and unique.   If you expect mulitple versions
                // of your program to run simultaneously, you need to generate unique names (e.g. add a process ID suffix)
                Console.WriteLine("Creating a 'My Session' session");
                var sessionName = "My Session";
                using (var session = new TraceEventSession(sessionName, null)) // the null second parameter means 'real time session'
//                using (var session = new EtwRealtimeConsumer("EtwRealtimeConsumer"))
                {
                    // Note that sessions create a OS object (a session) that lives beyond the lifetime of the process
                    // that created it (like Filles), thus you have to be more careful about always cleaning them up.
                    // An importanty way you can do this is to set the 'StopOnDispose' property which will cause the session to
                    // stop (and thus the OS object will die) when the TraceEventSession dies.   Because we used a 'using'
                    // statement, this means that any exception in the code below will clean up the OS object.  
                    session.StopOnDispose = true;

                    // By default, if you hit Ctrl-C your .NET objects may not be disposed, so force it to.  It is OK if dispose is called twice.
                    Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) { session.Dispose(); };

                    // prepare to read from the session, connect the ETWTraceEventSource to the session
                    using (var source = new ETWTraceEventSource(sessionName, TraceEventSourceType.Session))
                    {
                        // To demonstrate non-trivial event manipuation, we calculate the time delta between 'MyFirstEvent and 'MySecondEvent'
                        // These variables are used in this calculation
                        int lastMyEventID = int.MinValue; // an illegal value to start with.
                        double lastMyEventMSec = 0;

                        // Hook up the parser that knows about EventSources
                        var parser = new DynamicTraceEventParser(source);
                        parser.All += delegate (TraceEvent data)
                        {
                            Console.WriteLine("GOT EVENT: " + data.ToString());

                            if (true /*data.ProviderGuid == providerGuid */) // We don't actually need this since we only turned one one provider.
                            {
                                // Note that this is the inefficient way of parsing events (there are string comparisions on the
                                // event Name and every payload value), however it is fine for events that occur less than 100 times
                                // a second.   For more volumous events, you should consider making a parser for you eventSource
                                // (covered in another demo).  This makes your code fare less 'reflection-like' where you have lots
                                // of strings (e.g. "MyFirstEvent", "MyId" ...) which is better even ignoring the performance benefit.
                                if (data.EventName == "MyFirstEvent")
                                {
                                    // On First Events, simply remember the ID and time of the event
                                    lastMyEventID = (int)data.PayloadByName("MyId");
                                    lastMyEventMSec = data.TimeStampRelativeMSec;
                                }
                                else if (data.EventName == "MySecondEvent")
                                {
                                    // On Second Events, if the ID matches, compute the delta and display it.
                                    if (lastMyEventID == (int)data.PayloadByName("MyId"))
                                        Console.WriteLine("   > Time Delta from first Event = {0:f3} MSec", data.TimeStampRelativeMSec - lastMyEventMSec);
                                }
                                else if (data.EventName == "Stop")
                                {
                                    // Stop processing after we we see the 'Stop' event
                                    source.Close();
                                }
                            }
                        };

                        // Enable my provider, you can call many of these on the same session to get other events.
                        session.EnableProvider(providerGuid);
                        session.EnableProvider(providerGuid2);
                        session.EnableProvider(providerGuid3);
                        session.EnableProvider(providerGuid4);
                        session.EnableProvider(providerGuid5);
                        session.EnableProvider(new Guid("95595d3e-4dd8-49b1-9f90-85870e95b10b"));
                        //session.EnableProvider(new Guid("edc24920-e004-40f6-a8e1-0e6e48f39d84"));
                        session.EnableProvider(Guid.Parse("{edc24920-e004-40f6-a8e1-0e6e48f39d84}"));

                        // Start another thread that Causes MyEventSource to create some events
                        // Normally this code as well as the EventSource itself would be in a different process.
                        EventGenerator.CreateEvents();

                        Console.WriteLine("Staring Listing for events");
                        // go into a loop processing events can calling the callbacks.  Because this is live data (not from a file)
                        // processing never completes by itself, but only because someone called 'source.Close()'.
                        source.Process();
                        Console.WriteLine();
                        Console.WriteLine("Stopping the collection of events.");


                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            //while (true)
            //{
            //    Thread.Sleep(100);
            //}
            return 0;
        }
    }
}

Thursday, June 30, 2016

Sql server most used indexes

SELECT 
 db_name(ius.database_id) AS DatabaseName,
 t.NAME AS TableName,
 i.NAME AS IndexName,
 i.type_desc AS IndexType,
 ius.user_seeks + ius.user_scans + ius.user_lookups AS NbrTimesAccessed
FROM sys.dm_db_index_usage_stats ius
INNER JOIN sys.indexes i ON i.OBJECT_ID = ius.OBJECT_ID AND i.index_id = ius.index_id
INNER JOIN sys.tables t ON t.OBJECT_ID = i.object_id
WHERE database_id = DB_ID('MyDb')
ORDER BY ius.user_seeks + ius.user_scans + ius.user_lookups DESC

Sql server most hit tables/ Table usage in Sql server

SELECT 
 db_name(ius.database_id) AS DatabaseName,
 t.NAME AS TableName,
 SUM(ius.user_seeks + ius.user_scans + ius.user_lookups) AS NbrTimesAccessed
FROM sys.dm_db_index_usage_stats ius
INNER JOIN sys.tables t ON t.OBJECT_ID = ius.object_id
WHERE database_id = DB_ID('MyDb')
GROUP BY database_id, t.name
ORDER BY SUM(ius.user_seeks + ius.user_scans + ius.user_lookups) DESC

Thursday, June 23, 2016

Custom config section with nested collection

<?xml version="1.0"?>
 <configuration>
   <configSections>
     <section name="TunnelSection" type="SSHTunnelWF.TunnelSection,SSHTunnelWF" />
   </configSections>
   <TunnelSection>
     <host SSHServerHostname="tsg.edssdn.net" username="user" SSHport="22" password="pass" privatekey="" privatekeypassphrase="">
       <tunnels>
         <tunnel name="tfs" localport="8081"  remoteport="8080" destinationserver="tfs2010.dev.com"  />
         <tunnel name="sql" localport="14331"  remoteport="1433" destinationserver="sql2008.dev.com"  />
         <tunnel name="crm2011app" localport="81"  remoteport="80" destinationserver="crm2011betaapp.dev.com"  />
       </tunnels>
     </host>
     <host SSHServerHostname="blade16" username="root" SSHport="22"  password="pass" privatekey="" privatekeypassphrase="">
      <tunnels>
        <tunnel name="vnc" localport="5902"  remoteport="5902" destinationserver="blade1.dev.com" />
      </tunnels>
     </host>
   </TunnelSection>
 </configuration>

using System;
using System.Configuration;

namespace SSHTunnelWF
{
    public class TunnelSection : ConfigurationSection
    {
        [ConfigurationProperty("", IsDefaultCollection = true)]  
        public HostCollection Tunnels
        {
            get
            {
                HostCollection hostCollection = (HostCollection)base[""];
                return hostCollection;                
            }
        }
    }

    public class HostCollection : ConfigurationElementCollection
    {
        public HostCollection()
        {
            HostConfigElement details = (HostConfigElement)CreateNewElement();
            if (details.SSHServerHostname != "")
            {
                Add(details);
            }
        }

        public override ConfigurationElementCollectionType CollectionType
        {
            get
            {
                return ConfigurationElementCollectionType.BasicMap;
            }
        }

        protected override ConfigurationElement CreateNewElement()
        {
            return new HostConfigElement();
        }

        protected override Object GetElementKey(ConfigurationElement element)
        {
            return ((HostConfigElement)element).SSHServerHostname;
        }

        public HostConfigElement this[int index]
        {
            get
            {
                return (HostConfigElement)BaseGet(index);
            }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                BaseAdd(index, value);
            }
        }

        new public HostConfigElement this[string name]
        {
            get
            {
                return (HostConfigElement)BaseGet(name);
            }
        }

        public int IndexOf(HostConfigElement details)
        {
            return BaseIndexOf(details);
        }

        public void Add(HostConfigElement details)
        {
            BaseAdd(details);
        }

        protected override void BaseAdd(ConfigurationElement element)
        {
            BaseAdd(element, false);
        }

        public void Remove(HostConfigElement details)
        {
            if (BaseIndexOf(details) >= 0)
                BaseRemove(details.SSHServerHostname);
        }

        public void RemoveAt(int index)
        {
            BaseRemoveAt(index);
        }

        public void Remove(string name)
        {
            BaseRemove(name);
        }

        public void Clear()
        {
            BaseClear();
        }

        protected override string ElementName
        {
            get { return "host"; }
        }
    }

    public class HostConfigElement:ConfigurationElement
    {
        [ConfigurationProperty("SSHServerHostname", IsRequired = true, IsKey = true)]
        [StringValidator(InvalidCharacters = "  ~!@#$%^&*()[]{}/;’\"|\\")]
        public string SSHServerHostname
        {
            get { return (string)this["SSHServerHostname"]; }
            set { this["SSHServerHostname"] = value; }
        }

        [ConfigurationProperty("username", IsRequired = true)]
        [StringValidator(InvalidCharacters = "  ~!@#$%^&*()[]{}/;’\"|\\")]
        public string Username
        {
            get { return (string)this["username"]; }
            set { this["username"] = value; }
        }

        [ConfigurationProperty("SSHport", IsRequired = true, DefaultValue = 22)]
        [IntegerValidator(MinValue = 1, MaxValue = 65536)]
        public int SSHPort
        {
            get { return (int)this["SSHport"]; }
            set { this["SSHport"] = value; }
        }

        [ConfigurationProperty("password", IsRequired = false)]
        public string Password
        {
            get { return (string)this["password"]; }
            set { this["password"] = value; }
        }

        [ConfigurationProperty("privatekey", IsRequired = false)]
        public string Privatekey
        {
            get { return (string)this["privatekey"]; }
            set { this["privatekey"] = value; }
        }

        [ConfigurationProperty("privatekeypassphrase", IsRequired = false)]
        public string Privatekeypassphrase
        {
            get { return (string)this["privatekeypassphrase"]; }
            set { this["privatekeypassphrase"] = value; }
        }

        [ConfigurationProperty("tunnels", IsDefaultCollection = false)]
        public TunnelCollection Tunnels
        {
            get { return (TunnelCollection)base["tunnels"]; }
        }
    }

    public class TunnelCollection : ConfigurationElementCollection
    {
        public new TunnelConfigElement this[string name]
        {
            get
            {
                if (IndexOf(name) < 0) return null;
                return (TunnelConfigElement)BaseGet(name);
            }
        }

        public TunnelConfigElement this[int index]
        {
            get { return (TunnelConfigElement)BaseGet(index); }
        }

        public int IndexOf(string name)
        {
            name = name.ToLower();

            for (int idx = 0; idx < base.Count; idx++)
            {
                if (this[idx].Name.ToLower() == name)
                    return idx;
            }
            return -1;
        }

        public override ConfigurationElementCollectionType CollectionType
        {
            get { return ConfigurationElementCollectionType.BasicMap; }
        }

        protected override ConfigurationElement CreateNewElement()
        {
            return new TunnelConfigElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((TunnelConfigElement)element).Name;
        }

        protected override string ElementName
        {
            get { return "tunnel"; }
        }
    }

    public class TunnelConfigElement : ConfigurationElement
    {        
        public TunnelConfigElement()
        {
        }

        public TunnelConfigElement(string name, int localport, int remoteport, string destinationserver)
        {
            this.DestinationServer = destinationserver;
            this.RemotePort = remoteport;
            this.LocalPort = localport;            
            this.Name = name;
        }

        [ConfigurationProperty("name", IsRequired = true, IsKey = true, DefaultValue = "")]       
        public string Name
        {
            get { return (string)this["name"]; }
            set { this["name"] = value; }
        }        

        [ConfigurationProperty("localport", IsRequired = true, DefaultValue =1)]
        [IntegerValidator(MinValue = 1, MaxValue = 65536)]
        public int LocalPort
        {
            get { return (int)this["localport"]; }
            set { this["localport"] = value; }
        }

        [ConfigurationProperty("remoteport", IsRequired = true, DefaultValue =1)]
        [IntegerValidator(MinValue = 1, MaxValue = 65536)]
        public int RemotePort
        {
            get { return (int)this["remoteport"]; }
            set { this["remoteport"] = value; }
        }

        [ConfigurationProperty("destinationserver", IsRequired = true)]
        [StringValidator(InvalidCharacters = "  ~!@#$%^&*()[]{}/;’\"|\\")]
        public string DestinationServer
        {
            get { return (string)this["destinationserver"]; }
            set { this["destinationserver"] = value; }
        }
    }
}

http://stackoverflow.com/questions/5661544/correct-implementation-of-a-custom-config-section-with-nested-collections