e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
297 lines
9.1 KiB
C#
297 lines
9.1 KiB
C#
//-----------------------------------------------------------------------------
|
|
// 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
|
|
}
|