File: System\ServiceModel\ThreadTrace.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
namespace System.ServiceModel
{
    using System;
    using System.IO;
    using System.Threading;
    using System.Security;
    using System.Runtime.InteropServices;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    using System.Runtime.Versioning;
 
    // Enable this code to track which thread operations occur on
#if false
    static class ThreadTrace
    {
        static LocalDataStoreSlot slot;
        static List<ThreadLog> logs;
        static string logFileName;
        static bool isEnabled;
        static long frequency;
 
        static ThreadTrace()
        {
            logFileName = Environment.GetEnvironmentVariable("ThreadTrace");
            if (logFileName == null)
                logFileName = "";
            isEnabled = logFileName.Length > 0;
            if (isEnabled)
            {
                slot = Thread.AllocateDataSlot();
                logs = new List<ThreadLog>();
                NativeMethods.QueryPerformanceFrequency(out frequency);
                Console.WriteLine("ThreadTrace: enabled");
                new Thread(ThreadProc).Start();
            }
        }
 
        static bool stopTracing;
 
        public static void StopTracing()
        {
            stopTracing = true;
        }
 
        static void ThreadProc()
        {
            while (!stopTracing)
            {
                Thread.Sleep(20000);
                WriteLogFile();
                Console.WriteLine("ThreadTrace: " + logFileName + " saved.");
            }
        }
 
        static object ThisLock
        {
            get { return logs; }
        }
 
        public static void Trace(string operation)
        {
            if (isEnabled)
            {
                TraceInternal(operation);
            }
        }
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        static void TraceInternal(string operation)
        {
            long time;
            NativeMethods.QueryPerformanceCounter(out time);
            ThreadLog log = (ThreadLog)Thread.GetData(slot);
            if (log == null)
            {
                Thread currentThread = Thread.CurrentThread;
                log = new ThreadLog(currentThread);
                lock (ThisLock)
                {
                    logs.Add(log);
                }
                Thread.SetData(slot, log);
            }
            log.Append(time, operation);
        }
 
        static void WriteLogFile()
        {
            Trace("ThreadTrace.Save");
            TextWriter writer = File.CreateText(logFileName);
            using (writer)
            {
                ThreadLogSnapshot[] logSnapshots = new ThreadLogSnapshot[logs.Count];
                writer.Write("Time");
                for (int i = 0; i < logs.Count; i++)
                {
                    logSnapshots[i] = logs[i].GetSnapshot();
                    writer.Write(", Thread ");
                    writer.Write(i.ToString());
                }
                writer.WriteLine();
                writer.Write("(Ms)");
                foreach (ThreadLog log in logs)
                {
                    if (log.IsThreadPoolThread)
                        writer.Write(", (ThreadPool)");
                    else if (log.IsBackgroundThread)
                        writer.Write(", (Background)");
                    else
                        writer.Write(", (Main)");
                }
                writer.WriteLine();
                int[] indices = new int[logs.Count];
                int count = 0;
                for (int i = 0; i < logs.Count; i++)
                    count += logSnapshots[i].Count;
                for (int j = 0; j < count; j++)
                {
                    int earliestIndex = -1;
                    long earliestTime = long.MaxValue;
                    for (int i = 0; i < logs.Count; i++)
                    {
                        ThreadLogSnapshot logSnapshot = logSnapshots[i];
                        int index = indices[i];
                        if (index >= logSnapshot.Count)
                            continue;
                        long time = logSnapshot[index].time;
                        if (time < earliestTime)
                        {
                            earliestIndex = i;
                            earliestTime = time;
                        }
                    }
                    ThreadLogEntry entry = logSnapshots[earliestIndex][indices[earliestIndex]];
                    double timeInMilliseconds = (entry.time * 1000) / (double)frequency;
                    writer.Write(timeInMilliseconds);
                    for (int i = 0; i < logs.Count; i++)
                    {
                        writer.Write(", ");
                        if (i == earliestIndex)
                        {
                            writer.Write('\"');
                            writer.Write(entry.operation);
                            writer.Write('\"');
                        }
                    }
                    writer.WriteLine();
                    indices[earliestIndex]++;
                }
            }
        }
 
        struct ThreadLogEntry
        {
            public long time;
            public string operation;
 
            public ThreadLogEntry(long time, string operation)
            {
                this.time = time;
                this.operation = operation;
            }
        }
 
        class ThreadLogSnapshot
        {
            ThreadLogEntry[] entries;
 
            public ThreadLogSnapshot(ThreadLogEntry[] entries)
            {
                this.entries = entries;
            }
 
            public int Count
            {
                get
                {
                    return this.entries.Length;
                }
            }
 
            public ThreadLogEntry this[int index]
            {
                get
                {
                    return this.entries[index];
                }
            }
        }
 
        class ThreadLog
        {
            int count;
            ThreadLogEntry[] buffer;
            const int bufferSize = 5000;
            const int maxBuffers = 4096;
            bool isThreadPoolThread;
            bool isBackgroundThread;
            ThreadLogEntry[][] buffers;
            int bufferCount;
 
            public ThreadLog(Thread thread)
            {
                this.isThreadPoolThread = thread.IsThreadPoolThread;
                this.isBackgroundThread = thread.IsBackground;
                this.buffer = new ThreadLogEntry[bufferSize];
                this.buffers = new ThreadLogEntry[maxBuffers][];
            }
 
            object ThisLock
            {
                get { return this; }
            }
 
            public bool IsThreadPoolThread
            {
                get { return this.isThreadPoolThread; }
            }
 
            public bool IsBackgroundThread
            {
                get { return this.isBackgroundThread; }
            }
 
            public void Append(long time, string operation)
            {
                if (this.count == bufferSize)
                {
                    lock (ThisLock)
                    {
                        this.buffers[bufferCount++] = this.buffer;
                        this.buffer = new ThreadLogEntry[bufferSize];
                        this.count = 0;
                    }
                }
 
                this.buffer[this.count++] = new ThreadLogEntry(time, operation);
            }
 
            public ThreadLogSnapshot GetSnapshot()
            {
                int currentBufferCount;
                int currentCount;
                ThreadLogEntry[] currentBuffer;
 
                lock (ThisLock)
                {
                    currentBufferCount = this.bufferCount;
                    currentCount = this.count;
                    currentBuffer = this.buffer;
                }
 
                ThreadLogEntry[] entries = new ThreadLogEntry[currentBufferCount * bufferSize + currentCount];
                int index = 0;
                for (int i = 0; i < currentBufferCount; i++)
                {
                    Array.Copy(buffers[i], 0, entries, index, bufferSize);
                    index += bufferSize;
                }
                Array.Copy(currentBuffer, 0, entries, index, currentCount);
                return new ThreadLogSnapshot(entries);
            }
        }
 
        [SuppressUnmanagedCodeSecurity]
        static class NativeMethods
        {
            [DllImport("kernel32.dll")]
	    [ResourceExposure(ResourceScope.None)]
            public static extern int QueryPerformanceCounter(out long time);
 
            [DllImport("kernel32.dll")]
            [ResourceExposure(ResourceScope.None)]
            public static extern int QueryPerformanceFrequency(out long frequency);
        }
    }
#else
    static class ThreadTrace
    {
        public static void Trace(string operation)
        {
        }
 
        public static void StopTracing()
        {
        }
 
        public static void Save()
        {
        }
    }
#endif
}