зеркало из https://github.com/mono/corefx.git
Update performance counter interop to use Span<T> (#30614)
* Move NtProcessInfoHelper to ProcessManager.Win32.cs NtProcessInfoHelper was split between ProcessManager.Win32.cs and ProcessManager.Windows.cs for no good reason * Update performance counter interop to use Span<T> This is both faster and more secure * Delete FEATURE_TRACESWITCH
This commit is contained in:
Родитель
3b24c53585
Коммит
d1cdb1b324
|
@ -10,7 +10,7 @@ internal partial class Interop
|
|||
internal partial class NtDll
|
||||
{
|
||||
[DllImport(Libraries.NtDll, CharSet = CharSet.Unicode)]
|
||||
internal static extern int NtQuerySystemInformation(int query, IntPtr dataPtr, int size, out int returnedSize);
|
||||
internal static unsafe extern int NtQuerySystemInformation(int query, void* dataPtr, int size, out int returnedSize);
|
||||
|
||||
internal const int NtQuerySystemProcessInformation = 5;
|
||||
internal const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
@ -10,80 +11,83 @@ internal partial class Interop
|
|||
internal partial class Advapi32
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal class PERF_COUNTER_BLOCK
|
||||
internal struct PERF_COUNTER_BLOCK
|
||||
{
|
||||
internal int ByteLength = 0;
|
||||
internal int ByteLength;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal class PERF_COUNTER_DEFINITION
|
||||
internal struct PERF_COUNTER_DEFINITION
|
||||
{
|
||||
internal int ByteLength = 0;
|
||||
internal int CounterNameTitleIndex = 0;
|
||||
internal int CounterNameTitlePtr = 0;
|
||||
internal int CounterHelpTitleIndex = 0;
|
||||
internal int CounterHelpTitlePtr = 0;
|
||||
internal int DefaultScale = 0;
|
||||
internal int DetailLevel = 0;
|
||||
internal int CounterType = 0;
|
||||
internal int CounterSize = 0;
|
||||
internal int CounterOffset = 0;
|
||||
internal int ByteLength;
|
||||
internal int CounterNameTitleIndex;
|
||||
internal int CounterNameTitlePtr;
|
||||
internal int CounterHelpTitleIndex;
|
||||
internal int CounterHelpTitlePtr;
|
||||
internal int DefaultScale;
|
||||
internal int DetailLevel;
|
||||
internal int CounterType;
|
||||
internal int CounterSize;
|
||||
internal int CounterOffset;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal class PERF_DATA_BLOCK
|
||||
internal struct PERF_DATA_BLOCK
|
||||
{
|
||||
internal int Signature1 = 0;
|
||||
internal int Signature2 = 0;
|
||||
internal int LittleEndian = 0;
|
||||
internal int Version = 0;
|
||||
internal int Revision = 0;
|
||||
internal int TotalByteLength = 0;
|
||||
internal int HeaderLength = 0;
|
||||
internal int NumObjectTypes = 0;
|
||||
internal int DefaultObject = 0;
|
||||
internal SYSTEMTIME SystemTime = null;
|
||||
internal int pad1 = 0; // Need to pad the struct to get quadword alignment for the 'long' after SystemTime
|
||||
internal long PerfTime = 0;
|
||||
internal long PerfFreq = 0;
|
||||
internal long PerfTime100nSec = 0;
|
||||
internal int SystemNameLength = 0;
|
||||
internal int SystemNameOffset = 0;
|
||||
internal int Signature1;
|
||||
internal int Signature2;
|
||||
internal int LittleEndian;
|
||||
internal int Version;
|
||||
internal int Revision;
|
||||
internal int TotalByteLength;
|
||||
internal int HeaderLength;
|
||||
internal int NumObjectTypes;
|
||||
internal int DefaultObject;
|
||||
internal SYSTEMTIME SystemTime;
|
||||
internal int pad1; // Need to pad the struct to get quadword alignment for the 'long' after SystemTime
|
||||
internal long PerfTime;
|
||||
internal long PerfFreq;
|
||||
internal long PerfTime100nSec;
|
||||
internal int SystemNameLength;
|
||||
internal int SystemNameOffset;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal class PERF_INSTANCE_DEFINITION
|
||||
internal struct PERF_INSTANCE_DEFINITION
|
||||
{
|
||||
internal int ByteLength = 0;
|
||||
internal int ParentObjectTitleIndex = 0;
|
||||
internal int ParentObjectInstance = 0;
|
||||
internal int UniqueID = 0;
|
||||
internal int NameOffset = 0;
|
||||
internal int NameLength = 0;
|
||||
internal int ByteLength;
|
||||
internal int ParentObjectTitleIndex;
|
||||
internal int ParentObjectInstance;
|
||||
internal int UniqueID;
|
||||
internal int NameOffset;
|
||||
internal int NameLength;
|
||||
|
||||
internal static ReadOnlySpan<char> GetName(in PERF_INSTANCE_DEFINITION instance, ReadOnlySpan<byte> data)
|
||||
=> (instance.NameLength == 0) ? default
|
||||
: MemoryMarshal.Cast<byte, char>(data.Slice(instance.NameOffset, instance.NameLength - sizeof(char))); // NameLength includes the null-terminator
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal class PERF_OBJECT_TYPE
|
||||
internal struct PERF_OBJECT_TYPE
|
||||
{
|
||||
internal int TotalByteLength = 0;
|
||||
internal int DefinitionLength = 0;
|
||||
internal int HeaderLength = 0;
|
||||
internal int ObjectNameTitleIndex = 0;
|
||||
internal int ObjectNameTitlePtr = 0;
|
||||
internal int ObjectHelpTitleIndex = 0;
|
||||
internal int ObjectHelpTitlePtr = 0;
|
||||
internal int DetailLevel = 0;
|
||||
internal int NumCounters = 0;
|
||||
internal int DefaultCounter = 0;
|
||||
internal int NumInstances = 0;
|
||||
internal int CodePage = 0;
|
||||
internal long PerfTime = 0;
|
||||
internal long PerfFreq = 0;
|
||||
internal int TotalByteLength;
|
||||
internal int DefinitionLength;
|
||||
internal int HeaderLength;
|
||||
internal int ObjectNameTitleIndex;
|
||||
internal int ObjectNameTitlePtr;
|
||||
internal int ObjectHelpTitleIndex;
|
||||
internal int ObjectHelpTitlePtr;
|
||||
internal int DetailLevel;
|
||||
internal int NumCounters;
|
||||
internal int DefaultCounter;
|
||||
internal int NumInstances;
|
||||
internal int CodePage;
|
||||
internal long PerfTime;
|
||||
internal long PerfFreq;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal class SYSTEMTIME
|
||||
internal struct SYSTEMTIME
|
||||
{
|
||||
internal short wYear;
|
||||
internal short wMonth;
|
||||
|
|
|
@ -12,6 +12,8 @@ using System.ComponentModel;
|
|||
using Microsoft.Win32;
|
||||
using System.IO;
|
||||
|
||||
using static Interop.Advapi32;
|
||||
|
||||
namespace System.Diagnostics
|
||||
{
|
||||
internal class PerformanceCounterLib
|
||||
|
@ -100,7 +102,11 @@ namespace System.Diagnostics
|
|||
}
|
||||
}
|
||||
|
||||
private unsafe Hashtable CategoryTable
|
||||
// TODO: Replace with https://github.com/dotnet/corefx/issues/30613
|
||||
private static ref readonly T AsStruct<T>(ReadOnlySpan<byte> span) where T : struct
|
||||
=> ref MemoryMarshal.Cast<byte, T>(span)[0];
|
||||
|
||||
private Hashtable CategoryTable
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -110,70 +116,62 @@ namespace System.Diagnostics
|
|||
{
|
||||
if (_categoryTable == null)
|
||||
{
|
||||
byte[] perfData = GetPerformanceData("Global");
|
||||
ReadOnlySpan<byte> data = GetPerformanceData("Global");
|
||||
|
||||
ref readonly PERF_DATA_BLOCK dataBlock = ref AsStruct<PERF_DATA_BLOCK>(data);
|
||||
int pos = dataBlock.HeaderLength;
|
||||
|
||||
fixed (byte* perfDataPtr = perfData)
|
||||
int numPerfObjects = dataBlock.NumObjectTypes;
|
||||
|
||||
// on some machines MSMQ claims to have 4 categories, even though it only has 2.
|
||||
// This causes us to walk past the end of our data, potentially crashing or reading
|
||||
// data we shouldn't. We use dataBlock.TotalByteLength to make sure we don't go past the end
|
||||
// of the perf data.
|
||||
Hashtable tempCategoryTable = new Hashtable(numPerfObjects, StringComparer.OrdinalIgnoreCase);
|
||||
for (int index = 0; index < numPerfObjects && pos < dataBlock.TotalByteLength; index++)
|
||||
{
|
||||
IntPtr dataRef = new IntPtr((void*)perfDataPtr);
|
||||
Interop.Advapi32.PERF_DATA_BLOCK dataBlock = new Interop.Advapi32.PERF_DATA_BLOCK();
|
||||
Marshal.PtrToStructure(dataRef, dataBlock);
|
||||
dataRef = (IntPtr)((long)dataRef + dataBlock.HeaderLength);
|
||||
int categoryNumber = dataBlock.NumObjectTypes;
|
||||
ref readonly PERF_OBJECT_TYPE perfObject = ref AsStruct<PERF_OBJECT_TYPE>(data.Slice(pos));
|
||||
|
||||
// on some machines MSMQ claims to have 4 categories, even though it only has 2.
|
||||
// This causes us to walk past the end of our data, potentially crashing or reading
|
||||
// data we shouldn't. We use endPerfData to make sure we don't go past the end
|
||||
// of the perf data.
|
||||
long endPerfData = (long)(new IntPtr((void*)perfDataPtr)) + dataBlock.TotalByteLength;
|
||||
Hashtable tempCategoryTable = new Hashtable(categoryNumber, StringComparer.OrdinalIgnoreCase);
|
||||
for (int index = 0; index < categoryNumber && ((long)dataRef < endPerfData); index++)
|
||||
CategoryEntry newCategoryEntry = new CategoryEntry(in perfObject);
|
||||
int nextPos = pos + perfObject.TotalByteLength;
|
||||
pos += perfObject.HeaderLength;
|
||||
|
||||
int index3 = 0;
|
||||
int previousCounterIndex = -1;
|
||||
//Need to filter out counters that are repeated, some providers might
|
||||
//return several adyacent copies of the same counter.
|
||||
for (int index2 = 0; index2 < newCategoryEntry.CounterIndexes.Length; ++index2)
|
||||
{
|
||||
Interop.Advapi32.PERF_OBJECT_TYPE perfObject = new Interop.Advapi32.PERF_OBJECT_TYPE();
|
||||
|
||||
Marshal.PtrToStructure(dataRef, perfObject);
|
||||
|
||||
CategoryEntry newCategoryEntry = new CategoryEntry(perfObject);
|
||||
IntPtr nextRef = (IntPtr)((long)dataRef + perfObject.TotalByteLength);
|
||||
dataRef = (IntPtr)((long)dataRef + perfObject.HeaderLength);
|
||||
|
||||
int index3 = 0;
|
||||
int previousCounterIndex = -1;
|
||||
//Need to filter out counters that are repeated, some providers might
|
||||
//return several adyacent copies of the same counter.
|
||||
for (int index2 = 0; index2 < newCategoryEntry.CounterIndexes.Length; ++index2)
|
||||
ref readonly PERF_COUNTER_DEFINITION perfCounter = ref AsStruct<PERF_COUNTER_DEFINITION>(data.Slice(pos));
|
||||
if (perfCounter.CounterNameTitleIndex != previousCounterIndex)
|
||||
{
|
||||
Interop.Advapi32.PERF_COUNTER_DEFINITION perfCounter = new Interop.Advapi32.PERF_COUNTER_DEFINITION();
|
||||
Marshal.PtrToStructure(dataRef, perfCounter);
|
||||
if (perfCounter.CounterNameTitleIndex != previousCounterIndex)
|
||||
{
|
||||
newCategoryEntry.CounterIndexes[index3] = perfCounter.CounterNameTitleIndex;
|
||||
newCategoryEntry.HelpIndexes[index3] = perfCounter.CounterHelpTitleIndex;
|
||||
previousCounterIndex = perfCounter.CounterNameTitleIndex;
|
||||
++index3;
|
||||
}
|
||||
dataRef = (IntPtr)((long)dataRef + perfCounter.ByteLength);
|
||||
newCategoryEntry.CounterIndexes[index3] = perfCounter.CounterNameTitleIndex;
|
||||
newCategoryEntry.HelpIndexes[index3] = perfCounter.CounterHelpTitleIndex;
|
||||
previousCounterIndex = perfCounter.CounterNameTitleIndex;
|
||||
++index3;
|
||||
}
|
||||
|
||||
//Lets adjust the entry counter arrays in case there were repeated copies
|
||||
if (index3 < newCategoryEntry.CounterIndexes.Length)
|
||||
{
|
||||
int[] adjustedCounterIndexes = new int[index3];
|
||||
int[] adjustedHelpIndexes = new int[index3];
|
||||
Array.Copy(newCategoryEntry.CounterIndexes, adjustedCounterIndexes, index3);
|
||||
Array.Copy(newCategoryEntry.HelpIndexes, adjustedHelpIndexes, index3);
|
||||
newCategoryEntry.CounterIndexes = adjustedCounterIndexes;
|
||||
newCategoryEntry.HelpIndexes = adjustedHelpIndexes;
|
||||
}
|
||||
|
||||
string categoryName = (string)NameTable[newCategoryEntry.NameIndex];
|
||||
if (categoryName != null)
|
||||
tempCategoryTable[categoryName] = newCategoryEntry;
|
||||
|
||||
dataRef = nextRef;
|
||||
pos += perfCounter.ByteLength;
|
||||
}
|
||||
|
||||
_categoryTable = tempCategoryTable;
|
||||
//Lets adjust the entry counter arrays in case there were repeated copies
|
||||
if (index3 < newCategoryEntry.CounterIndexes.Length)
|
||||
{
|
||||
int[] adjustedCounterIndexes = new int[index3];
|
||||
int[] adjustedHelpIndexes = new int[index3];
|
||||
Array.Copy(newCategoryEntry.CounterIndexes, adjustedCounterIndexes, index3);
|
||||
Array.Copy(newCategoryEntry.HelpIndexes, adjustedHelpIndexes, index3);
|
||||
newCategoryEntry.CounterIndexes = adjustedCounterIndexes;
|
||||
newCategoryEntry.HelpIndexes = adjustedHelpIndexes;
|
||||
}
|
||||
|
||||
string categoryName = (string)NameTable[newCategoryEntry.NameIndex];
|
||||
if (categoryName != null)
|
||||
tempCategoryTable[categoryName] = newCategoryEntry;
|
||||
|
||||
pos = nextPos;
|
||||
}
|
||||
|
||||
_categoryTable = tempCategoryTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1369,7 +1367,7 @@ namespace System.Diagnostics
|
|||
internal int[] CounterIndexes;
|
||||
internal int[] HelpIndexes;
|
||||
|
||||
internal CategoryEntry(Interop.Advapi32.PERF_OBJECT_TYPE perfObject)
|
||||
internal CategoryEntry(in PERF_OBJECT_TYPE perfObject)
|
||||
{
|
||||
NameIndex = perfObject.ObjectNameTitleIndex;
|
||||
HelpIndex = perfObject.ObjectHelpTitleIndex;
|
||||
|
@ -1391,203 +1389,193 @@ namespace System.Diagnostics
|
|||
private CategoryEntry _entry;
|
||||
private PerformanceCounterLib _library;
|
||||
|
||||
internal unsafe CategorySample(byte[] data, CategoryEntry entry, PerformanceCounterLib library)
|
||||
// TODO: Replace with https://github.com/dotnet/corefx/issues/30613
|
||||
private static ref readonly T AsStruct<T>(ReadOnlySpan<byte> span) where T : struct
|
||||
=> ref MemoryMarshal.Cast<byte, T>(span)[0];
|
||||
|
||||
internal CategorySample(ReadOnlySpan<byte> data, CategoryEntry entry, PerformanceCounterLib library)
|
||||
{
|
||||
_entry = entry;
|
||||
_library = library;
|
||||
int categoryIndex = entry.NameIndex;
|
||||
Interop.Advapi32.PERF_DATA_BLOCK dataBlock = new Interop.Advapi32.PERF_DATA_BLOCK();
|
||||
fixed (byte* dataPtr = data)
|
||||
|
||||
ref readonly PERF_DATA_BLOCK dataBlock = ref AsStruct<PERF_DATA_BLOCK>(data);
|
||||
|
||||
_systemFrequency = dataBlock.PerfFreq;
|
||||
_timeStamp = dataBlock.PerfTime;
|
||||
_timeStamp100nSec = dataBlock.PerfTime100nSec;
|
||||
int pos = dataBlock.HeaderLength;
|
||||
int numPerfObjects = dataBlock.NumObjectTypes;
|
||||
if (numPerfObjects == 0)
|
||||
{
|
||||
IntPtr dataRef = new IntPtr((void*)dataPtr);
|
||||
_counterTable = new Hashtable();
|
||||
_instanceNameTable = new Hashtable(StringComparer.OrdinalIgnoreCase);
|
||||
return;
|
||||
}
|
||||
|
||||
Marshal.PtrToStructure(dataRef, dataBlock);
|
||||
_systemFrequency = dataBlock.PerfFreq;
|
||||
_timeStamp = dataBlock.PerfTime;
|
||||
_timeStamp100nSec = dataBlock.PerfTime100nSec;
|
||||
dataRef = (IntPtr)((long)dataRef + dataBlock.HeaderLength);
|
||||
int numPerfObjects = dataBlock.NumObjectTypes;
|
||||
if (numPerfObjects == 0)
|
||||
//Need to find the right category, GetPerformanceData might return
|
||||
//several of them.
|
||||
bool foundCategory = false;
|
||||
for (int index = 0; index < numPerfObjects; index++)
|
||||
{
|
||||
ref readonly PERF_OBJECT_TYPE perfObjectType = ref AsStruct<PERF_OBJECT_TYPE>(data.Slice(pos));
|
||||
|
||||
if (perfObjectType.ObjectNameTitleIndex == categoryIndex)
|
||||
{
|
||||
_counterTable = new Hashtable();
|
||||
_instanceNameTable = new Hashtable(StringComparer.OrdinalIgnoreCase);
|
||||
return;
|
||||
foundCategory = true;
|
||||
break;
|
||||
}
|
||||
|
||||
//Need to find the right category, GetPerformanceData might return
|
||||
//several of them.
|
||||
Interop.Advapi32.PERF_OBJECT_TYPE perfObject = null;
|
||||
bool foundCategory = false;
|
||||
for (int index = 0; index < numPerfObjects; index++)
|
||||
pos += perfObjectType.TotalByteLength;
|
||||
}
|
||||
|
||||
if (!foundCategory)
|
||||
throw new InvalidOperationException(SR.Format(SR.CantReadCategoryIndex, categoryIndex.ToString(CultureInfo.CurrentCulture)));
|
||||
|
||||
ref readonly PERF_OBJECT_TYPE perfObject = ref AsStruct<PERF_OBJECT_TYPE>(data.Slice(pos));
|
||||
|
||||
_counterFrequency = perfObject.PerfFreq;
|
||||
_counterTimeStamp = perfObject.PerfTime;
|
||||
int counterNumber = perfObject.NumCounters;
|
||||
int instanceNumber = perfObject.NumInstances;
|
||||
|
||||
if (instanceNumber == -1)
|
||||
_isMultiInstance = false;
|
||||
else
|
||||
_isMultiInstance = true;
|
||||
|
||||
// Move pointer forward to end of PERF_OBJECT_TYPE
|
||||
pos += perfObject.HeaderLength;
|
||||
|
||||
CounterDefinitionSample[] samples = new CounterDefinitionSample[counterNumber];
|
||||
_counterTable = new Hashtable(counterNumber);
|
||||
for (int index = 0; index < samples.Length; ++index)
|
||||
{
|
||||
ref readonly PERF_COUNTER_DEFINITION perfCounter = ref AsStruct<PERF_COUNTER_DEFINITION>(data.Slice(pos));
|
||||
samples[index] = new CounterDefinitionSample(in perfCounter, this, instanceNumber);
|
||||
pos += perfCounter.ByteLength;
|
||||
|
||||
int currentSampleType = samples[index]._counterType;
|
||||
if (!PerformanceCounterLib.IsBaseCounter(currentSampleType))
|
||||
{
|
||||
perfObject = new Interop.Advapi32.PERF_OBJECT_TYPE();
|
||||
Marshal.PtrToStructure(dataRef, perfObject);
|
||||
|
||||
if (perfObject.ObjectNameTitleIndex == categoryIndex)
|
||||
{
|
||||
foundCategory = true;
|
||||
break;
|
||||
}
|
||||
|
||||
dataRef = (IntPtr)((long)dataRef + perfObject.TotalByteLength);
|
||||
// We'll put only non-base counters in the table.
|
||||
if (currentSampleType != Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_NODATA)
|
||||
_counterTable[samples[index]._nameIndex] = samples[index];
|
||||
}
|
||||
|
||||
if (!foundCategory)
|
||||
throw new InvalidOperationException(SR.Format(SR.CantReadCategoryIndex, categoryIndex.ToString(CultureInfo.CurrentCulture)));
|
||||
|
||||
_counterFrequency = perfObject.PerfFreq;
|
||||
_counterTimeStamp = perfObject.PerfTime;
|
||||
int counterNumber = perfObject.NumCounters;
|
||||
int instanceNumber = perfObject.NumInstances;
|
||||
|
||||
if (instanceNumber == -1)
|
||||
_isMultiInstance = false;
|
||||
else
|
||||
_isMultiInstance = true;
|
||||
{
|
||||
// it's a base counter, try to hook it up to the main counter.
|
||||
Debug.Assert(index > 0, "Index > 0 because base counters should never be at index 0");
|
||||
if (index > 0)
|
||||
samples[index - 1]._baseCounterDefinitionSample = samples[index];
|
||||
}
|
||||
}
|
||||
|
||||
// Move pointer forward to end of PERF_OBJECT_TYPE
|
||||
dataRef = (IntPtr)((long)dataRef + perfObject.HeaderLength);
|
||||
// now set up the InstanceNameTable.
|
||||
if (!_isMultiInstance)
|
||||
{
|
||||
_instanceNameTable = new Hashtable(1, StringComparer.OrdinalIgnoreCase);
|
||||
_instanceNameTable[PerformanceCounterLib.SingleInstanceName] = 0;
|
||||
|
||||
CounterDefinitionSample[] samples = new CounterDefinitionSample[counterNumber];
|
||||
_counterTable = new Hashtable(counterNumber);
|
||||
for (int index = 0; index < samples.Length; ++index)
|
||||
{
|
||||
Interop.Advapi32.PERF_COUNTER_DEFINITION perfCounter = new Interop.Advapi32.PERF_COUNTER_DEFINITION();
|
||||
Marshal.PtrToStructure(dataRef, perfCounter);
|
||||
samples[index] = new CounterDefinitionSample(perfCounter, this, instanceNumber);
|
||||
dataRef = (IntPtr)((long)dataRef + perfCounter.ByteLength);
|
||||
|
||||
int currentSampleType = samples[index]._counterType;
|
||||
if (!PerformanceCounterLib.IsBaseCounter(currentSampleType))
|
||||
{
|
||||
// We'll put only non-base counters in the table.
|
||||
if (currentSampleType != Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_NODATA)
|
||||
_counterTable[samples[index]._nameIndex] = samples[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
// it's a base counter, try to hook it up to the main counter.
|
||||
Debug.Assert(index > 0, "Index > 0 because base counters should never be at index 0");
|
||||
if (index > 0)
|
||||
samples[index - 1]._baseCounterDefinitionSample = samples[index];
|
||||
}
|
||||
samples[index].SetInstanceValue(0, data.Slice(pos));
|
||||
}
|
||||
|
||||
// now set up the InstanceNameTable.
|
||||
if (!_isMultiInstance)
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] parentInstanceNames = null;
|
||||
_instanceNameTable = new Hashtable(instanceNumber, StringComparer.OrdinalIgnoreCase);
|
||||
for (int i = 0; i < instanceNumber; i++)
|
||||
{
|
||||
_instanceNameTable = new Hashtable(1, StringComparer.OrdinalIgnoreCase);
|
||||
_instanceNameTable[PerformanceCounterLib.SingleInstanceName] = 0;
|
||||
ref readonly PERF_INSTANCE_DEFINITION perfInstance = ref AsStruct<PERF_INSTANCE_DEFINITION>(data.Slice(pos));
|
||||
if (perfInstance.ParentObjectTitleIndex > 0 && parentInstanceNames == null)
|
||||
parentInstanceNames = GetInstanceNamesFromIndex(perfInstance.ParentObjectTitleIndex);
|
||||
|
||||
string instanceName = PERF_INSTANCE_DEFINITION.GetName(in perfInstance, data.Slice(pos)).ToString();
|
||||
if (parentInstanceNames != null && perfInstance.ParentObjectInstance >= 0 && perfInstance.ParentObjectInstance < parentInstanceNames.Length - 1)
|
||||
instanceName = parentInstanceNames[perfInstance.ParentObjectInstance] + "/" + instanceName;
|
||||
|
||||
//In some cases instance names are not unique (Process), same as perfmon
|
||||
//generate a unique name.
|
||||
string newInstanceName = instanceName;
|
||||
int newInstanceNumber = 1;
|
||||
while (true)
|
||||
{
|
||||
if (!_instanceNameTable.ContainsKey(newInstanceName))
|
||||
{
|
||||
_instanceNameTable[newInstanceName] = i;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
newInstanceName = instanceName + "#" + newInstanceNumber.ToString(CultureInfo.InvariantCulture);
|
||||
++newInstanceNumber;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pos += perfInstance.ByteLength;
|
||||
|
||||
for (int index = 0; index < samples.Length; ++index)
|
||||
{
|
||||
samples[index].SetInstanceValue(0, dataRef);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] parentInstanceNames = null;
|
||||
_instanceNameTable = new Hashtable(instanceNumber, StringComparer.OrdinalIgnoreCase);
|
||||
for (int i = 0; i < instanceNumber; i++)
|
||||
{
|
||||
Interop.Advapi32.PERF_INSTANCE_DEFINITION perfInstance = new Interop.Advapi32.PERF_INSTANCE_DEFINITION();
|
||||
Marshal.PtrToStructure(dataRef, perfInstance);
|
||||
if (perfInstance.ParentObjectTitleIndex > 0 && parentInstanceNames == null)
|
||||
parentInstanceNames = GetInstanceNamesFromIndex(perfInstance.ParentObjectTitleIndex);
|
||||
samples[index].SetInstanceValue(i, data.Slice(pos));
|
||||
|
||||
string instanceName;
|
||||
if (parentInstanceNames != null && perfInstance.ParentObjectInstance >= 0 && perfInstance.ParentObjectInstance < parentInstanceNames.Length - 1)
|
||||
instanceName = parentInstanceNames[perfInstance.ParentObjectInstance] + "/" + Marshal.PtrToStringUni((IntPtr)((long)dataRef + perfInstance.NameOffset));
|
||||
else
|
||||
instanceName = Marshal.PtrToStringUni((IntPtr)((long)dataRef + perfInstance.NameOffset));
|
||||
|
||||
//In some cases instance names are not unique (Process), same as perfmon
|
||||
//generate a unique name.
|
||||
string newInstanceName = instanceName;
|
||||
int newInstanceNumber = 1;
|
||||
while (true)
|
||||
{
|
||||
if (!_instanceNameTable.ContainsKey(newInstanceName))
|
||||
{
|
||||
_instanceNameTable[newInstanceName] = i;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
newInstanceName = instanceName + "#" + newInstanceNumber.ToString(CultureInfo.InvariantCulture);
|
||||
++newInstanceNumber;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dataRef = (IntPtr)((long)dataRef + perfInstance.ByteLength);
|
||||
for (int index = 0; index < samples.Length; ++index)
|
||||
samples[index].SetInstanceValue(i, dataRef);
|
||||
|
||||
dataRef = (IntPtr)((long)dataRef + Marshal.ReadInt32(dataRef));
|
||||
}
|
||||
pos += AsStruct<PERF_COUNTER_BLOCK>(data.Slice(pos)).ByteLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe string[] GetInstanceNamesFromIndex(int categoryIndex)
|
||||
internal string[] GetInstanceNamesFromIndex(int categoryIndex)
|
||||
{
|
||||
byte[] data = _library.GetPerformanceData(categoryIndex.ToString(CultureInfo.InvariantCulture));
|
||||
fixed (byte* dataPtr = data)
|
||||
ReadOnlySpan<byte> data = _library.GetPerformanceData(categoryIndex.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
ref readonly PERF_DATA_BLOCK dataBlock = ref AsStruct<PERF_DATA_BLOCK>(data);
|
||||
int pos = dataBlock.HeaderLength;
|
||||
int numPerfObjects = dataBlock.NumObjectTypes;
|
||||
|
||||
bool foundCategory = false;
|
||||
for (int index = 0; index < numPerfObjects; index++)
|
||||
{
|
||||
IntPtr dataRef = new IntPtr((void*)dataPtr);
|
||||
ref readonly PERF_OBJECT_TYPE type = ref AsStruct<PERF_OBJECT_TYPE>(data.Slice(pos));
|
||||
|
||||
Interop.Advapi32.PERF_DATA_BLOCK dataBlock = new Interop.Advapi32.PERF_DATA_BLOCK();
|
||||
Marshal.PtrToStructure(dataRef, dataBlock);
|
||||
dataRef = (IntPtr)((long)dataRef + dataBlock.HeaderLength);
|
||||
int numPerfObjects = dataBlock.NumObjectTypes;
|
||||
|
||||
Interop.Advapi32.PERF_OBJECT_TYPE perfObject = null;
|
||||
bool foundCategory = false;
|
||||
for (int index = 0; index < numPerfObjects; index++)
|
||||
if (type.ObjectNameTitleIndex == categoryIndex)
|
||||
{
|
||||
perfObject = new Interop.Advapi32.PERF_OBJECT_TYPE();
|
||||
Marshal.PtrToStructure(dataRef, perfObject);
|
||||
|
||||
if (perfObject.ObjectNameTitleIndex == categoryIndex)
|
||||
{
|
||||
foundCategory = true;
|
||||
break;
|
||||
}
|
||||
|
||||
dataRef = (IntPtr)((long)dataRef + perfObject.TotalByteLength);
|
||||
foundCategory = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundCategory)
|
||||
return Array.Empty<string>();
|
||||
|
||||
int counterNumber = perfObject.NumCounters;
|
||||
int instanceNumber = perfObject.NumInstances;
|
||||
dataRef = (IntPtr)((long)dataRef + perfObject.HeaderLength);
|
||||
|
||||
if (instanceNumber == -1)
|
||||
return Array.Empty<string>();
|
||||
|
||||
CounterDefinitionSample[] samples = new CounterDefinitionSample[counterNumber];
|
||||
for (int index = 0; index < samples.Length; ++index)
|
||||
{
|
||||
Interop.Advapi32.PERF_COUNTER_DEFINITION perfCounter = new Interop.Advapi32.PERF_COUNTER_DEFINITION();
|
||||
Marshal.PtrToStructure(dataRef, perfCounter);
|
||||
dataRef = (IntPtr)((long)dataRef + perfCounter.ByteLength);
|
||||
}
|
||||
|
||||
string[] instanceNames = new string[instanceNumber];
|
||||
for (int i = 0; i < instanceNumber; i++)
|
||||
{
|
||||
Interop.Advapi32.PERF_INSTANCE_DEFINITION perfInstance = new Interop.Advapi32.PERF_INSTANCE_DEFINITION();
|
||||
Marshal.PtrToStructure(dataRef, perfInstance);
|
||||
instanceNames[i] = Marshal.PtrToStringUni((IntPtr)((long)dataRef + perfInstance.NameOffset));
|
||||
dataRef = (IntPtr)((long)dataRef + perfInstance.ByteLength);
|
||||
dataRef = (IntPtr)((long)dataRef + Marshal.ReadInt32(dataRef));
|
||||
}
|
||||
|
||||
return instanceNames;
|
||||
pos += type.TotalByteLength;
|
||||
}
|
||||
|
||||
if (!foundCategory)
|
||||
return Array.Empty<string>();
|
||||
|
||||
ref readonly PERF_OBJECT_TYPE perfObject = ref AsStruct<PERF_OBJECT_TYPE>(data.Slice(pos));
|
||||
|
||||
int counterNumber = perfObject.NumCounters;
|
||||
int instanceNumber = perfObject.NumInstances;
|
||||
pos += perfObject.HeaderLength;
|
||||
|
||||
if (instanceNumber == -1)
|
||||
return Array.Empty<string>();
|
||||
|
||||
CounterDefinitionSample[] samples = new CounterDefinitionSample[counterNumber];
|
||||
for (int index = 0; index < samples.Length; ++index)
|
||||
{
|
||||
pos += AsStruct<PERF_COUNTER_DEFINITION>(data.Slice(pos)).ByteLength;
|
||||
}
|
||||
|
||||
string[] instanceNames = new string[instanceNumber];
|
||||
for (int i = 0; i < instanceNumber; i++)
|
||||
{
|
||||
ref readonly PERF_INSTANCE_DEFINITION perfInstance = ref AsStruct<PERF_INSTANCE_DEFINITION>(data.Slice(pos));
|
||||
instanceNames[i] = PERF_INSTANCE_DEFINITION.GetName(in perfInstance, data.Slice(pos)).ToString();
|
||||
pos += perfInstance.ByteLength;
|
||||
|
||||
pos += AsStruct<PERF_COUNTER_BLOCK>(data.Slice(pos)).ByteLength;
|
||||
}
|
||||
|
||||
return instanceNames;
|
||||
}
|
||||
|
||||
internal CounterDefinitionSample GetCounterDefinitionSample(string counter)
|
||||
|
@ -1657,7 +1645,7 @@ namespace System.Diagnostics
|
|||
private long[] _instanceValues;
|
||||
private CategorySample _categorySample;
|
||||
|
||||
internal CounterDefinitionSample(Interop.Advapi32.PERF_COUNTER_DEFINITION perfCounter, CategorySample categorySample, int instanceNumber)
|
||||
internal CounterDefinitionSample(in PERF_COUNTER_DEFINITION perfCounter, CategorySample categorySample, int instanceNumber)
|
||||
{
|
||||
_nameIndex = perfCounter.CounterNameTitleIndex;
|
||||
_counterType = perfCounter.CounterType;
|
||||
|
@ -1673,15 +1661,15 @@ namespace System.Diagnostics
|
|||
_categorySample = categorySample;
|
||||
}
|
||||
|
||||
private long ReadValue(IntPtr pointer)
|
||||
private long ReadValue(ReadOnlySpan<byte> data)
|
||||
{
|
||||
if (_size == 4)
|
||||
{
|
||||
return (long)(uint)Marshal.ReadInt32((IntPtr)((long)pointer + _offset));
|
||||
return (long)MemoryMarshal.Read<uint>(data.Slice(_offset));
|
||||
}
|
||||
else if (_size == 8)
|
||||
{
|
||||
return (long)Marshal.ReadInt64((IntPtr)((long)pointer + _offset));
|
||||
return MemoryMarshal.Read<long>(data.Slice(_offset));
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
@ -1774,9 +1762,9 @@ namespace System.Diagnostics
|
|||
_categorySample._counterTimeStamp);
|
||||
}
|
||||
|
||||
internal void SetInstanceValue(int index, IntPtr dataRef)
|
||||
internal void SetInstanceValue(int index, ReadOnlySpan<byte> data)
|
||||
{
|
||||
long rawValue = ReadValue(dataRef);
|
||||
long rawValue = ReadValue(data);
|
||||
_instanceValues[index] = rawValue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -735,9 +735,6 @@ namespace System.Diagnostics
|
|||
}
|
||||
finally
|
||||
{
|
||||
#if FEATURE_TRACESWITCH
|
||||
Debug.WriteLineIf(_processTracing.TraceVerbose, "Process - CloseHandle(processToken)");
|
||||
#endif
|
||||
if (hToken != null)
|
||||
{
|
||||
hToken.Dispose();
|
||||
|
@ -753,15 +750,6 @@ namespace System.Diagnostics
|
|||
/// <internalonly/>
|
||||
private SafeProcessHandle GetProcessHandle(int access, bool throwIfExited)
|
||||
{
|
||||
#if FEATURE_TRACESWITCH
|
||||
Debug.WriteLineIf(_processTracing.TraceVerbose, "GetProcessHandle(access = 0x" + access.ToString("X8", CultureInfo.InvariantCulture) + ", throwIfExited = " + throwIfExited + ")");
|
||||
#if DEBUG
|
||||
if (_processTracing.TraceVerbose) {
|
||||
StackFrame calledFrom = new StackTrace(true).GetFrame(0);
|
||||
Debug.WriteLine(" called from " + calledFrom.GetFileName() + ", line " + calledFrom.GetFileLineNumber());
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
if (_haveProcessHandle)
|
||||
{
|
||||
if (throwIfExited)
|
||||
|
|
|
@ -80,14 +80,6 @@ namespace System.Diagnostics
|
|||
internal AsyncStreamReader _error;
|
||||
internal bool _pendingOutputRead;
|
||||
internal bool _pendingErrorRead;
|
||||
#if FEATURE_TRACESWITCH
|
||||
internal static TraceSwitch _processTracing =
|
||||
#if DEBUG
|
||||
new TraceSwitch("processTracing", "Controls debug output from Process component");
|
||||
#else
|
||||
null;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// <devdoc>
|
||||
/// <para>
|
||||
|
@ -853,9 +845,6 @@ namespace System.Diagnostics
|
|||
// This sets _waitHandle to null which causes CompletionCallback to not emit events.
|
||||
StopWatchingForExit();
|
||||
}
|
||||
#if FEATURE_TRACESWITCH
|
||||
Debug.WriteLineIf(_processTracing.TraceVerbose, "Process - CloseHandle(process) in Close()");
|
||||
#endif
|
||||
_processHandle.Dispose();
|
||||
_processHandle = null;
|
||||
_haveProcessHandle = false;
|
||||
|
@ -1067,18 +1056,6 @@ namespace System.Diagnostics
|
|||
ProcessInfo processInfo = processInfos[i];
|
||||
processes[i] = new Process(machineName, isRemoteMachine, processInfo.ProcessId, processInfo);
|
||||
}
|
||||
#if FEATURE_TRACESWITCH
|
||||
Debug.WriteLineIf(_processTracing.TraceVerbose, "Process.GetProcesses(" + machineName + ")");
|
||||
#if DEBUG
|
||||
if (_processTracing.TraceVerbose) {
|
||||
Debug.Indent();
|
||||
for (int i = 0; i < processInfos.Length; i++) {
|
||||
Debug.WriteLine(processes[i].Id + ": " + processes[i].ProcessName);
|
||||
}
|
||||
Debug.Unindent();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
return processes;
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace System.Diagnostics
|
|||
}
|
||||
}
|
||||
|
||||
internal static partial class NtProcessInfoHelper
|
||||
internal static class NtProcessInfoHelper
|
||||
{
|
||||
internal static ProcessInfo[] GetProcessInfos(Predicate<int> processIdFilter = null)
|
||||
{
|
||||
|
|
|
@ -227,9 +227,6 @@ namespace System.Diagnostics
|
|||
}
|
||||
finally
|
||||
{
|
||||
#if FEATURE_TRACESWITCH
|
||||
Debug.WriteLineIf(Process._processTracing.TraceVerbose, "Process - CloseHandle(process)");
|
||||
#endif
|
||||
if (!processHandle.IsInvalid)
|
||||
{
|
||||
processHandle.Dispose();
|
||||
|
@ -238,18 +235,24 @@ namespace System.Diagnostics
|
|||
}
|
||||
}
|
||||
|
||||
internal static partial class NtProcessInfoHelper
|
||||
internal static class NtProcessInfoHelper
|
||||
{
|
||||
// Cache a single buffer for use in GetProcessInfos().
|
||||
private static long[] CachedBuffer;
|
||||
|
||||
// Use a smaller buffer size on debug to ensure we hit the retry path.
|
||||
#if DEBUG
|
||||
private const int DefaultCachedBufferSize = 1024;
|
||||
#else
|
||||
private const int DefaultCachedBufferSize = 128 * 1024;
|
||||
#endif
|
||||
|
||||
internal static ProcessInfo[] GetProcessInfos(Predicate<int> processIdFilter = null)
|
||||
{
|
||||
int requiredSize = 0;
|
||||
int status;
|
||||
|
||||
ProcessInfo[] processInfos;
|
||||
GCHandle bufferHandle = new GCHandle();
|
||||
|
||||
// Start with the default buffer size.
|
||||
int bufferSize = DefaultCachedBufferSize;
|
||||
|
@ -272,17 +275,20 @@ namespace System.Diagnostics
|
|||
bufferSize = buffer.Length * sizeof(long);
|
||||
}
|
||||
|
||||
bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||
|
||||
status = Interop.NtDll.NtQuerySystemInformation(
|
||||
Interop.NtDll.NtQuerySystemProcessInformation,
|
||||
bufferHandle.AddrOfPinnedObject(),
|
||||
bufferSize,
|
||||
out requiredSize);
|
||||
unsafe
|
||||
{
|
||||
fixed (long* bufferPtr = buffer)
|
||||
{
|
||||
status = Interop.NtDll.NtQuerySystemInformation(
|
||||
Interop.NtDll.NtQuerySystemProcessInformation,
|
||||
bufferPtr,
|
||||
bufferSize,
|
||||
out requiredSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (unchecked((uint)status) == Interop.NtDll.STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
if (bufferHandle.IsAllocated) bufferHandle.Free();
|
||||
buffer = null;
|
||||
bufferSize = GetNewBufferSize(bufferSize, requiredSize);
|
||||
}
|
||||
|
@ -294,17 +300,236 @@ namespace System.Diagnostics
|
|||
}
|
||||
|
||||
// Parse the data block to get process information
|
||||
processInfos = GetProcessInfos(bufferHandle.AddrOfPinnedObject(), processIdFilter);
|
||||
processInfos = GetProcessInfos(MemoryMarshal.AsBytes<long>(buffer), processIdFilter);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Cache the final buffer for use on the next call.
|
||||
Interlocked.Exchange(ref CachedBuffer, buffer);
|
||||
|
||||
if (bufferHandle.IsAllocated) bufferHandle.Free();
|
||||
}
|
||||
|
||||
return processInfos;
|
||||
}
|
||||
|
||||
private static int GetNewBufferSize(int existingBufferSize, int requiredSize)
|
||||
{
|
||||
if (requiredSize == 0)
|
||||
{
|
||||
//
|
||||
// On some old OS like win2000, requiredSize will not be set if the buffer
|
||||
// passed to NtQuerySystemInformation is not enough.
|
||||
//
|
||||
int newSize = existingBufferSize * 2;
|
||||
if (newSize < existingBufferSize)
|
||||
{
|
||||
// In reality, we should never overflow.
|
||||
// Adding the code here just in case it happens.
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// allocating a few more kilo bytes just in case there are some new process
|
||||
// kicked in since new call to NtQuerySystemInformation
|
||||
int newSize = requiredSize + 1024 * 10;
|
||||
if (newSize < requiredSize)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with https://github.com/dotnet/corefx/issues/30613
|
||||
private static ref readonly T AsStruct<T>(ReadOnlySpan<byte> span) where T : struct
|
||||
=> ref MemoryMarshal.Cast<byte, T>(span)[0];
|
||||
|
||||
private static unsafe ProcessInfo[] GetProcessInfos(ReadOnlySpan<byte> data, Predicate<int> processIdFilter)
|
||||
{
|
||||
// Use a dictionary to avoid duplicate entries if any
|
||||
// 60 is a reasonable number for processes on a normal machine.
|
||||
Dictionary<int, ProcessInfo> processInfos = new Dictionary<int, ProcessInfo>(60);
|
||||
|
||||
int processInformationOffset = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
ref readonly SystemProcessInformation pi = ref AsStruct<SystemProcessInformation>(data.Slice(processInformationOffset));
|
||||
|
||||
// Process ID shouldn't overflow. OS API GetCurrentProcessID returns DWORD.
|
||||
int processInfoProcessId = pi.UniqueProcessId.ToInt32();
|
||||
if (processIdFilter == null || processIdFilter(processInfoProcessId))
|
||||
{
|
||||
// get information for a process
|
||||
ProcessInfo processInfo = new ProcessInfo();
|
||||
processInfo.ProcessId = processInfoProcessId;
|
||||
processInfo.SessionId = (int)pi.SessionId;
|
||||
processInfo.PoolPagedBytes = (long)pi.QuotaPagedPoolUsage;
|
||||
processInfo.PoolNonPagedBytes = (long)pi.QuotaNonPagedPoolUsage;
|
||||
processInfo.VirtualBytes = (long)pi.VirtualSize;
|
||||
processInfo.VirtualBytesPeak = (long)pi.PeakVirtualSize;
|
||||
processInfo.WorkingSetPeak = (long)pi.PeakWorkingSetSize;
|
||||
processInfo.WorkingSet = (long)pi.WorkingSetSize;
|
||||
processInfo.PageFileBytesPeak = (long)pi.PeakPagefileUsage;
|
||||
processInfo.PageFileBytes = (long)pi.PagefileUsage;
|
||||
processInfo.PrivateBytes = (long)pi.PrivatePageCount;
|
||||
processInfo.BasePriority = pi.BasePriority;
|
||||
processInfo.HandleCount = (int)pi.HandleCount;
|
||||
|
||||
if (pi.ImageName.Buffer == IntPtr.Zero)
|
||||
{
|
||||
if (processInfo.ProcessId == NtProcessManager.SystemProcessID)
|
||||
{
|
||||
processInfo.ProcessName = "System";
|
||||
}
|
||||
else if (processInfo.ProcessId == NtProcessManager.IdleProcessID)
|
||||
{
|
||||
processInfo.ProcessName = "Idle";
|
||||
}
|
||||
else
|
||||
{
|
||||
// for normal process without name, using the process ID.
|
||||
processInfo.ProcessName = processInfo.ProcessId.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string processName = GetProcessShortName(Marshal.PtrToStringUni(pi.ImageName.Buffer, pi.ImageName.Length / sizeof(char)));
|
||||
processInfo.ProcessName = processName;
|
||||
}
|
||||
|
||||
// get the threads for current process
|
||||
processInfos[processInfo.ProcessId] = processInfo;
|
||||
|
||||
int threadInformationOffset = processInformationOffset + sizeof(SystemProcessInformation);
|
||||
for (int i = 0; i < pi.NumberOfThreads; i++)
|
||||
{
|
||||
ref readonly SystemThreadInformation ti = ref AsStruct<SystemThreadInformation>(data.Slice(threadInformationOffset));
|
||||
|
||||
ThreadInfo threadInfo = new ThreadInfo();
|
||||
|
||||
threadInfo._processId = (int)ti.ClientId.UniqueProcess;
|
||||
threadInfo._threadId = (ulong)ti.ClientId.UniqueThread;
|
||||
threadInfo._basePriority = ti.BasePriority;
|
||||
threadInfo._currentPriority = ti.Priority;
|
||||
threadInfo._startAddress = ti.StartAddress;
|
||||
threadInfo._threadState = (ThreadState)ti.ThreadState;
|
||||
threadInfo._threadWaitReason = NtProcessManager.GetThreadWaitReason((int)ti.WaitReason);
|
||||
|
||||
processInfo._threadInfoList.Add(threadInfo);
|
||||
|
||||
threadInformationOffset += sizeof(SystemThreadInformation);
|
||||
}
|
||||
}
|
||||
|
||||
if (pi.NextEntryOffset == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
processInformationOffset += (int)pi.NextEntryOffset;
|
||||
}
|
||||
|
||||
ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
|
||||
processInfos.Values.CopyTo(temp, 0);
|
||||
return temp;
|
||||
}
|
||||
|
||||
// This function generates the short form of process name.
|
||||
//
|
||||
// This is from GetProcessShortName in NT code base.
|
||||
// Check base\screg\winreg\perfdlls\process\perfsprc.c for details.
|
||||
internal static string GetProcessShortName(String name)
|
||||
{
|
||||
if (String.IsNullOrEmpty(name))
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
int slash = -1;
|
||||
int period = -1;
|
||||
|
||||
for (int i = 0; i < name.Length; i++)
|
||||
{
|
||||
if (name[i] == '\\')
|
||||
slash = i;
|
||||
else if (name[i] == '.')
|
||||
period = i;
|
||||
}
|
||||
|
||||
if (period == -1)
|
||||
period = name.Length - 1; // set to end of string
|
||||
else
|
||||
{
|
||||
// if a period was found, then see if the extension is
|
||||
// .EXE, if so drop it, if not, then use end of string
|
||||
// (i.e. include extension in name)
|
||||
ReadOnlySpan<char> extension = name.AsSpan(period);
|
||||
|
||||
if (extension.Equals(".exe", StringComparison.OrdinalIgnoreCase))
|
||||
period--; // point to character before period
|
||||
else
|
||||
period = name.Length - 1; // set to end of string
|
||||
}
|
||||
|
||||
if (slash == -1)
|
||||
slash = 0; // set to start of string
|
||||
else
|
||||
slash++; // point to character next to slash
|
||||
|
||||
// copy characters between period (or end of string) and
|
||||
// slash (or start of string) to make image name
|
||||
return name.Substring(slash, period - slash + 1);
|
||||
}
|
||||
|
||||
// native struct defined in ntexapi.h
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct SystemProcessInformation
|
||||
{
|
||||
internal uint NextEntryOffset;
|
||||
internal uint NumberOfThreads;
|
||||
private fixed byte Reserved1[48];
|
||||
internal Interop.UNICODE_STRING ImageName;
|
||||
internal int BasePriority;
|
||||
internal IntPtr UniqueProcessId;
|
||||
private UIntPtr Reserved2;
|
||||
internal uint HandleCount;
|
||||
internal uint SessionId;
|
||||
private UIntPtr Reserved3;
|
||||
internal UIntPtr PeakVirtualSize; // SIZE_T
|
||||
internal UIntPtr VirtualSize;
|
||||
private uint Reserved4;
|
||||
internal UIntPtr PeakWorkingSetSize; // SIZE_T
|
||||
internal UIntPtr WorkingSetSize; // SIZE_T
|
||||
private UIntPtr Reserved5;
|
||||
internal UIntPtr QuotaPagedPoolUsage; // SIZE_T
|
||||
private UIntPtr Reserved6;
|
||||
internal UIntPtr QuotaNonPagedPoolUsage; // SIZE_T
|
||||
internal UIntPtr PagefileUsage; // SIZE_T
|
||||
internal UIntPtr PeakPagefileUsage; // SIZE_T
|
||||
internal UIntPtr PrivatePageCount; // SIZE_T
|
||||
private fixed long Reserved7[6];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct SystemThreadInformation
|
||||
{
|
||||
private fixed long Reserved1[3];
|
||||
private uint Reserved2;
|
||||
internal IntPtr StartAddress;
|
||||
internal CLIENT_ID ClientId;
|
||||
internal int Priority;
|
||||
internal int BasePriority;
|
||||
private uint Reserved3;
|
||||
internal uint ThreadState;
|
||||
internal uint WaitReason;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct CLIENT_ID
|
||||
{
|
||||
internal IntPtr UniqueProcess;
|
||||
internal IntPtr UniqueThread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ using System.Runtime.InteropServices;
|
|||
using System.Threading;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
using static Interop.Advapi32;
|
||||
|
||||
namespace System.Diagnostics
|
||||
{
|
||||
internal static partial class ProcessManager
|
||||
|
@ -32,7 +34,7 @@ namespace System.Diagnostics
|
|||
// Otherwise enumerate all processes and compare ids
|
||||
if (!IsRemoteMachine(machineName))
|
||||
{
|
||||
using (SafeProcessHandle processHandle = Interop.Kernel32.OpenProcess(Interop.Advapi32.ProcessOptions.PROCESS_QUERY_INFORMATION, false, processId))
|
||||
using (SafeProcessHandle processHandle = Interop.Kernel32.OpenProcess(ProcessOptions.PROCESS_QUERY_INFORMATION, false, processId))
|
||||
{
|
||||
if (!processHandle.IsInvalid)
|
||||
{
|
||||
|
@ -349,7 +351,7 @@ namespace System.Diagnostics
|
|||
// On the next call to GetProcessInfos, we'd have to load them all up again, which is SLOW!
|
||||
}
|
||||
|
||||
static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library)
|
||||
private static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library)
|
||||
{
|
||||
ProcessInfo[] processInfos;
|
||||
|
||||
|
@ -376,117 +378,102 @@ namespace System.Diagnostics
|
|||
return processInfos;
|
||||
}
|
||||
|
||||
static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library, int processIndex, int threadIndex, byte[] data)
|
||||
// TODO: Replace with https://github.com/dotnet/corefx/issues/30613
|
||||
private static ref readonly T AsStruct<T>(ReadOnlySpan<byte> span) where T:struct
|
||||
=> ref MemoryMarshal.Cast<byte, T>(span)[0];
|
||||
|
||||
private static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library, int processIndex, int threadIndex, ReadOnlySpan<byte> data)
|
||||
{
|
||||
#if FEATURE_TRACESWITCH
|
||||
Debug.WriteLineIf(Process._processTracing.TraceVerbose, "GetProcessInfos()");
|
||||
#endif
|
||||
Dictionary<int, ProcessInfo> processInfos = new Dictionary<int, ProcessInfo>();
|
||||
List<ThreadInfo> threadInfos = new List<ThreadInfo>();
|
||||
|
||||
GCHandle dataHandle = new GCHandle();
|
||||
try
|
||||
ref readonly PERF_DATA_BLOCK dataBlock = ref AsStruct<PERF_DATA_BLOCK>(data);
|
||||
|
||||
int typePos = dataBlock.HeaderLength;
|
||||
for (int i = 0; i < dataBlock.NumObjectTypes; i++)
|
||||
{
|
||||
dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
IntPtr dataBlockPtr = dataHandle.AddrOfPinnedObject();
|
||||
Interop.Advapi32.PERF_DATA_BLOCK dataBlock = new Interop.Advapi32.PERF_DATA_BLOCK();
|
||||
Marshal.PtrToStructure(dataBlockPtr, dataBlock);
|
||||
IntPtr typePtr = (IntPtr)((long)dataBlockPtr + dataBlock.HeaderLength);
|
||||
Interop.Advapi32.PERF_INSTANCE_DEFINITION instance = new Interop.Advapi32.PERF_INSTANCE_DEFINITION();
|
||||
Interop.Advapi32.PERF_COUNTER_BLOCK counterBlock = new Interop.Advapi32.PERF_COUNTER_BLOCK();
|
||||
for (int i = 0; i < dataBlock.NumObjectTypes; i++)
|
||||
ref readonly PERF_OBJECT_TYPE type = ref AsStruct<PERF_OBJECT_TYPE>(data.Slice(typePos));
|
||||
|
||||
PERF_COUNTER_DEFINITION[] counters = new PERF_COUNTER_DEFINITION[type.NumCounters];
|
||||
|
||||
int counterPos = typePos + type.HeaderLength;
|
||||
for (int j = 0; j < type.NumCounters; j++)
|
||||
{
|
||||
Interop.Advapi32.PERF_OBJECT_TYPE type = new Interop.Advapi32.PERF_OBJECT_TYPE();
|
||||
Marshal.PtrToStructure(typePtr, type);
|
||||
IntPtr instancePtr = (IntPtr)((long)typePtr + type.DefinitionLength);
|
||||
IntPtr counterPtr = (IntPtr)((long)typePtr + type.HeaderLength);
|
||||
List<Interop.Advapi32.PERF_COUNTER_DEFINITION> counterList = new List<Interop.Advapi32.PERF_COUNTER_DEFINITION>();
|
||||
ref readonly PERF_COUNTER_DEFINITION counter = ref AsStruct<PERF_COUNTER_DEFINITION>(data.Slice(counterPos));
|
||||
|
||||
for (int j = 0; j < type.NumCounters; j++)
|
||||
string counterName = library.GetCounterName(counter.CounterNameTitleIndex);
|
||||
|
||||
counters[j] = counter;
|
||||
if (type.ObjectNameTitleIndex == processIndex)
|
||||
counters[j].CounterNameTitlePtr = (int)GetValueId(counterName);
|
||||
else if (type.ObjectNameTitleIndex == threadIndex)
|
||||
counters[j].CounterNameTitlePtr = (int)GetValueId(counterName);
|
||||
|
||||
counterPos += counter.ByteLength;
|
||||
}
|
||||
|
||||
int instancePos = typePos + type.DefinitionLength;
|
||||
for (int j = 0; j < type.NumInstances; j++)
|
||||
{
|
||||
ref readonly PERF_INSTANCE_DEFINITION instance = ref AsStruct<PERF_INSTANCE_DEFINITION>(data.Slice(instancePos));
|
||||
|
||||
ReadOnlySpan<char> instanceName = PERF_INSTANCE_DEFINITION.GetName(in instance, data.Slice(instancePos));
|
||||
|
||||
if (instanceName.Equals("_Total", StringComparison.Ordinal))
|
||||
{
|
||||
Interop.Advapi32.PERF_COUNTER_DEFINITION counter = new Interop.Advapi32.PERF_COUNTER_DEFINITION();
|
||||
Marshal.PtrToStructure(counterPtr, counter);
|
||||
string counterName = library.GetCounterName(counter.CounterNameTitleIndex);
|
||||
|
||||
if (type.ObjectNameTitleIndex == processIndex)
|
||||
counter.CounterNameTitlePtr = (int)GetValueId(counterName);
|
||||
else if (type.ObjectNameTitleIndex == threadIndex)
|
||||
counter.CounterNameTitlePtr = (int)GetValueId(counterName);
|
||||
counterList.Add(counter);
|
||||
counterPtr = (IntPtr)((long)counterPtr + counter.ByteLength);
|
||||
// continue
|
||||
}
|
||||
|
||||
Interop.Advapi32.PERF_COUNTER_DEFINITION[] counters = counterList.ToArray();
|
||||
|
||||
for (int j = 0; j < type.NumInstances; j++)
|
||||
else if (type.ObjectNameTitleIndex == processIndex)
|
||||
{
|
||||
Marshal.PtrToStructure(instancePtr, instance);
|
||||
IntPtr namePtr = (IntPtr)((long)instancePtr + instance.NameOffset);
|
||||
string instanceName = Marshal.PtrToStringUni(namePtr);
|
||||
if (instanceName.Equals("_Total")) continue;
|
||||
IntPtr counterBlockPtr = (IntPtr)((long)instancePtr + instance.ByteLength);
|
||||
Marshal.PtrToStructure(counterBlockPtr, counterBlock);
|
||||
if (type.ObjectNameTitleIndex == processIndex)
|
||||
ProcessInfo processInfo = GetProcessInfo(in type, data.Slice(instancePos + instance.ByteLength), counters);
|
||||
if (processInfo.ProcessId == 0 && !instanceName.Equals("Idle", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ProcessInfo processInfo = GetProcessInfo(type, (IntPtr)((long)instancePtr + instance.ByteLength), counters);
|
||||
if (processInfo.ProcessId == 0 && !string.Equals(instanceName, "Idle", StringComparison.OrdinalIgnoreCase))
|
||||
// Sometimes we'll get a process structure that is not completely filled in.
|
||||
// We can catch some of these by looking for non-"idle" processes that have id 0
|
||||
// and ignoring those.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (processInfos.ContainsKey(processInfo.ProcessId))
|
||||
{
|
||||
// Sometimes we'll get a process structure that is not completely filled in.
|
||||
// We can catch some of these by looking for non-"idle" processes that have id 0
|
||||
// and ignoring those.
|
||||
#if FEATURE_TRACESWITCH
|
||||
Debug.WriteLineIf(Process._processTracing.TraceVerbose, "GetProcessInfos() - found a non-idle process with id 0; ignoring.");
|
||||
#endif
|
||||
// We've found two entries in the perfcounters that claim to be the
|
||||
// same process. We throw an exception. Is this really going to be
|
||||
// helpful to the user? Should we just ignore?
|
||||
}
|
||||
else
|
||||
{
|
||||
if (processInfos.ContainsKey(processInfo.ProcessId))
|
||||
// the performance counters keep a 15 character prefix of the exe name, and then delete the ".exe",
|
||||
// if it's in the first 15. The problem is that sometimes that will leave us with part of ".exe"
|
||||
// at the end. If instanceName ends in ".", ".e", or ".ex" we remove it.
|
||||
if (instanceName.Length == 15)
|
||||
{
|
||||
// We've found two entries in the perfcounters that claim to be the
|
||||
// same process. We throw an exception. Is this really going to be
|
||||
// helpful to the user? Should we just ignore?
|
||||
#if FEATURE_TRACESWITCH
|
||||
Debug.WriteLineIf(Process._processTracing.TraceVerbose, "GetProcessInfos() - found a duplicate process id");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// the performance counters keep a 15 character prefix of the exe name, and then delete the ".exe",
|
||||
// if it's in the first 15. The problem is that sometimes that will leave us with part of ".exe"
|
||||
// at the end. If instanceName ends in ".", ".e", or ".ex" we remove it.
|
||||
string processName = instanceName;
|
||||
if (processName.Length == 15)
|
||||
{
|
||||
if (instanceName.EndsWith(".", StringComparison.Ordinal)) processName = instanceName.Substring(0, 14);
|
||||
else if (instanceName.EndsWith(".e", StringComparison.Ordinal)) processName = instanceName.Substring(0, 13);
|
||||
else if (instanceName.EndsWith(".ex", StringComparison.Ordinal)) processName = instanceName.Substring(0, 12);
|
||||
}
|
||||
processInfo.ProcessName = processName;
|
||||
processInfos.Add(processInfo.ProcessId, processInfo);
|
||||
if (instanceName.EndsWith(".", StringComparison.Ordinal)) instanceName = instanceName.Slice(0, 14);
|
||||
else if (instanceName.EndsWith(".e", StringComparison.Ordinal)) instanceName = instanceName.Slice(0, 13);
|
||||
else if (instanceName.EndsWith(".ex", StringComparison.Ordinal)) instanceName = instanceName.Slice(0, 12);
|
||||
}
|
||||
processInfo.ProcessName = instanceName.ToString();
|
||||
processInfos.Add(processInfo.ProcessId, processInfo);
|
||||
}
|
||||
}
|
||||
else if (type.ObjectNameTitleIndex == threadIndex)
|
||||
{
|
||||
ThreadInfo threadInfo = GetThreadInfo(type, (IntPtr)((long)instancePtr + instance.ByteLength), counters);
|
||||
if (threadInfo._threadId != 0) threadInfos.Add(threadInfo);
|
||||
}
|
||||
instancePtr = (IntPtr)((long)instancePtr + instance.ByteLength + counterBlock.ByteLength);
|
||||
}
|
||||
else if (type.ObjectNameTitleIndex == threadIndex)
|
||||
{
|
||||
ThreadInfo threadInfo = GetThreadInfo(in type, data.Slice(instancePos + instance.ByteLength), counters);
|
||||
if (threadInfo._threadId != 0) threadInfos.Add(threadInfo);
|
||||
}
|
||||
|
||||
typePtr = (IntPtr)((long)typePtr + type.TotalByteLength);
|
||||
instancePos += instance.ByteLength;
|
||||
|
||||
instancePos += AsStruct<PERF_COUNTER_BLOCK>(data.Slice(instancePos)).ByteLength;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (dataHandle.IsAllocated) dataHandle.Free();
|
||||
|
||||
typePos += type.TotalByteLength;
|
||||
}
|
||||
|
||||
for (int i = 0; i < threadInfos.Count; i++)
|
||||
{
|
||||
ThreadInfo threadInfo = (ThreadInfo)threadInfos[i];
|
||||
ProcessInfo processInfo;
|
||||
if (processInfos.TryGetValue(threadInfo._processId, out processInfo))
|
||||
ThreadInfo threadInfo = threadInfos[i];
|
||||
if (processInfos.TryGetValue(threadInfo._processId, out ProcessInfo processInfo))
|
||||
{
|
||||
processInfo._threadInfoList.Add(threadInfo);
|
||||
}
|
||||
|
@ -497,13 +484,13 @@ namespace System.Diagnostics
|
|||
return temp;
|
||||
}
|
||||
|
||||
static ThreadInfo GetThreadInfo(Interop.Advapi32.PERF_OBJECT_TYPE type, IntPtr instancePtr, Interop.Advapi32.PERF_COUNTER_DEFINITION[] counters)
|
||||
private static ThreadInfo GetThreadInfo(in PERF_OBJECT_TYPE type, ReadOnlySpan<byte> instanceData, PERF_COUNTER_DEFINITION[] counters)
|
||||
{
|
||||
ThreadInfo threadInfo = new ThreadInfo();
|
||||
for (int i = 0; i < counters.Length; i++)
|
||||
{
|
||||
Interop.Advapi32.PERF_COUNTER_DEFINITION counter = counters[i];
|
||||
long value = ReadCounterValue(counter.CounterType, (IntPtr)((long)instancePtr + counter.CounterOffset));
|
||||
PERF_COUNTER_DEFINITION counter = counters[i];
|
||||
long value = ReadCounterValue(counter.CounterType, instanceData.Slice(counter.CounterOffset));
|
||||
switch ((ValueId)counter.CounterNameTitlePtr)
|
||||
{
|
||||
case ValueId.ProcessId:
|
||||
|
@ -561,13 +548,13 @@ namespace System.Diagnostics
|
|||
}
|
||||
}
|
||||
|
||||
static ProcessInfo GetProcessInfo(Interop.Advapi32.PERF_OBJECT_TYPE type, IntPtr instancePtr, Interop.Advapi32.PERF_COUNTER_DEFINITION[] counters)
|
||||
private static ProcessInfo GetProcessInfo(in PERF_OBJECT_TYPE type, ReadOnlySpan<byte> instanceData, PERF_COUNTER_DEFINITION[] counters)
|
||||
{
|
||||
ProcessInfo processInfo = new ProcessInfo();
|
||||
for (int i = 0; i < counters.Length; i++)
|
||||
{
|
||||
Interop.Advapi32.PERF_COUNTER_DEFINITION counter = counters[i];
|
||||
long value = ReadCounterValue(counter.CounterType, (IntPtr)((long)instancePtr + counter.CounterOffset));
|
||||
PERF_COUNTER_DEFINITION counter = counters[i];
|
||||
long value = ReadCounterValue(counter.CounterType, instanceData.Slice(counter.CounterOffset));
|
||||
switch ((ValueId)counter.CounterNameTitlePtr)
|
||||
{
|
||||
case ValueId.ProcessId:
|
||||
|
@ -605,13 +592,13 @@ namespace System.Diagnostics
|
|||
break;
|
||||
case ValueId.HandleCount:
|
||||
processInfo.HandleCount = (int)value;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return processInfo;
|
||||
}
|
||||
|
||||
static ValueId GetValueId(string counterName)
|
||||
private static ValueId GetValueId(string counterName)
|
||||
{
|
||||
if (counterName != null)
|
||||
{
|
||||
|
@ -623,18 +610,18 @@ namespace System.Diagnostics
|
|||
return ValueId.Unknown;
|
||||
}
|
||||
|
||||
static long ReadCounterValue(int counterType, IntPtr dataPtr)
|
||||
private static long ReadCounterValue(int counterType, ReadOnlySpan<byte> data)
|
||||
{
|
||||
if ((counterType & Interop.Advapi32.PerfCounterOptions.NtPerfCounterSizeLarge) != 0)
|
||||
return Marshal.ReadInt64(dataPtr);
|
||||
if ((counterType & PerfCounterOptions.NtPerfCounterSizeLarge) != 0)
|
||||
return MemoryMarshal.Read<long>(data);
|
||||
else
|
||||
return (long)Marshal.ReadInt32(dataPtr);
|
||||
return (long)MemoryMarshal.Read<int>(data);
|
||||
}
|
||||
|
||||
enum ValueId
|
||||
{
|
||||
Unknown = -1,
|
||||
HandleCount,
|
||||
HandleCount,
|
||||
PoolPagedBytes,
|
||||
PoolNonpagedBytes,
|
||||
ElapsedTime,
|
||||
|
@ -656,235 +643,4 @@ namespace System.Diagnostics
|
|||
ThreadWaitReason
|
||||
}
|
||||
}
|
||||
|
||||
internal static partial class NtProcessInfoHelper
|
||||
{
|
||||
private static int GetNewBufferSize(int existingBufferSize, int requiredSize)
|
||||
{
|
||||
if (requiredSize == 0)
|
||||
{
|
||||
//
|
||||
// On some old OS like win2000, requiredSize will not be set if the buffer
|
||||
// passed to NtQuerySystemInformation is not enough.
|
||||
//
|
||||
int newSize = existingBufferSize * 2;
|
||||
if (newSize < existingBufferSize)
|
||||
{
|
||||
// In reality, we should never overflow.
|
||||
// Adding the code here just in case it happens.
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// allocating a few more kilo bytes just in case there are some new process
|
||||
// kicked in since new call to NtQuerySystemInformation
|
||||
int newSize = requiredSize + 1024 * 10;
|
||||
if (newSize < requiredSize)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Use a smaller buffer size on debug to ensure we hit the retry path.
|
||||
#if DEBUG
|
||||
private const int DefaultCachedBufferSize = 1024;
|
||||
#else
|
||||
private const int DefaultCachedBufferSize = 128 * 1024;
|
||||
#endif
|
||||
|
||||
private static unsafe ProcessInfo[] GetProcessInfos(IntPtr dataPtr, Predicate<int> processIdFilter)
|
||||
{
|
||||
// Use a dictionary to avoid duplicate entries if any
|
||||
// 60 is a reasonable number for processes on a normal machine.
|
||||
Dictionary<int, ProcessInfo> processInfos = new Dictionary<int, ProcessInfo>(60);
|
||||
|
||||
long totalOffset = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
IntPtr currentPtr = (IntPtr)((long)dataPtr + totalOffset);
|
||||
ref SystemProcessInformation pi = ref *(SystemProcessInformation *)(currentPtr);
|
||||
|
||||
// Process ID shouldn't overflow. OS API GetCurrentProcessID returns DWORD.
|
||||
var processInfoProcessId = pi.UniqueProcessId.ToInt32();
|
||||
if (processIdFilter == null || processIdFilter(processInfoProcessId))
|
||||
{
|
||||
// get information for a process
|
||||
ProcessInfo processInfo = new ProcessInfo();
|
||||
processInfo.ProcessId = processInfoProcessId;
|
||||
processInfo.SessionId = (int)pi.SessionId;
|
||||
processInfo.PoolPagedBytes = (long)pi.QuotaPagedPoolUsage;
|
||||
processInfo.PoolNonPagedBytes = (long)pi.QuotaNonPagedPoolUsage;
|
||||
processInfo.VirtualBytes = (long)pi.VirtualSize;
|
||||
processInfo.VirtualBytesPeak = (long)pi.PeakVirtualSize;
|
||||
processInfo.WorkingSetPeak = (long)pi.PeakWorkingSetSize;
|
||||
processInfo.WorkingSet = (long)pi.WorkingSetSize;
|
||||
processInfo.PageFileBytesPeak = (long)pi.PeakPagefileUsage;
|
||||
processInfo.PageFileBytes = (long)pi.PagefileUsage;
|
||||
processInfo.PrivateBytes = (long)pi.PrivatePageCount;
|
||||
processInfo.BasePriority = pi.BasePriority;
|
||||
processInfo.HandleCount = (int)pi.HandleCount;
|
||||
|
||||
if (pi.ImageName.Buffer == IntPtr.Zero)
|
||||
{
|
||||
if (processInfo.ProcessId == NtProcessManager.SystemProcessID)
|
||||
{
|
||||
processInfo.ProcessName = "System";
|
||||
}
|
||||
else if (processInfo.ProcessId == NtProcessManager.IdleProcessID)
|
||||
{
|
||||
processInfo.ProcessName = "Idle";
|
||||
}
|
||||
else
|
||||
{
|
||||
// for normal process without name, using the process ID.
|
||||
processInfo.ProcessName = processInfo.ProcessId.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string processName = GetProcessShortName(Marshal.PtrToStringUni(pi.ImageName.Buffer, pi.ImageName.Length / sizeof(char)));
|
||||
processInfo.ProcessName = processName;
|
||||
}
|
||||
|
||||
// get the threads for current process
|
||||
processInfos[processInfo.ProcessId] = processInfo;
|
||||
|
||||
currentPtr = (IntPtr)((long)currentPtr + sizeof(SystemProcessInformation));
|
||||
int i = 0;
|
||||
while (i < pi.NumberOfThreads)
|
||||
{
|
||||
ref SystemThreadInformation ti = ref *(SystemThreadInformation *)(currentPtr);
|
||||
ThreadInfo threadInfo = new ThreadInfo();
|
||||
|
||||
threadInfo._processId = (int)ti.ClientId.UniqueProcess;
|
||||
threadInfo._threadId = (ulong)ti.ClientId.UniqueThread;
|
||||
threadInfo._basePriority = ti.BasePriority;
|
||||
threadInfo._currentPriority = ti.Priority;
|
||||
threadInfo._startAddress = ti.StartAddress;
|
||||
threadInfo._threadState = (ThreadState)ti.ThreadState;
|
||||
threadInfo._threadWaitReason = NtProcessManager.GetThreadWaitReason((int)ti.WaitReason);
|
||||
|
||||
processInfo._threadInfoList.Add(threadInfo);
|
||||
currentPtr = (IntPtr)((long)currentPtr + sizeof(SystemThreadInformation));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (pi.NextEntryOffset == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
totalOffset += pi.NextEntryOffset;
|
||||
}
|
||||
|
||||
ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
|
||||
processInfos.Values.CopyTo(temp, 0);
|
||||
return temp;
|
||||
}
|
||||
|
||||
// This function generates the short form of process name.
|
||||
//
|
||||
// This is from GetProcessShortName in NT code base.
|
||||
// Check base\screg\winreg\perfdlls\process\perfsprc.c for details.
|
||||
internal static string GetProcessShortName(String name)
|
||||
{
|
||||
if (String.IsNullOrEmpty(name))
|
||||
{
|
||||
#if FEATURE_TRACESWITCH
|
||||
Debug.WriteLineIf(Process._processTracing.TraceVerbose, "GetProcessInfos() - unexpected blank ProcessName");
|
||||
#endif
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
int slash = -1;
|
||||
int period = -1;
|
||||
|
||||
for (int i = 0; i < name.Length; i++)
|
||||
{
|
||||
if (name[i] == '\\')
|
||||
slash = i;
|
||||
else if (name[i] == '.')
|
||||
period = i;
|
||||
}
|
||||
|
||||
if (period == -1)
|
||||
period = name.Length - 1; // set to end of string
|
||||
else
|
||||
{
|
||||
// if a period was found, then see if the extension is
|
||||
// .EXE, if so drop it, if not, then use end of string
|
||||
// (i.e. include extension in name)
|
||||
ReadOnlySpan<char> extension = name.AsSpan(period);
|
||||
|
||||
if (extension.Equals(".exe", StringComparison.OrdinalIgnoreCase))
|
||||
period--; // point to character before period
|
||||
else
|
||||
period = name.Length - 1; // set to end of string
|
||||
}
|
||||
|
||||
if (slash == -1)
|
||||
slash = 0; // set to start of string
|
||||
else
|
||||
slash++; // point to character next to slash
|
||||
|
||||
// copy characters between period (or end of string) and
|
||||
// slash (or start of string) to make image name
|
||||
return name.Substring(slash, period - slash + 1);
|
||||
}
|
||||
|
||||
// native struct defined in ntexapi.h
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct SystemProcessInformation
|
||||
{
|
||||
internal uint NextEntryOffset;
|
||||
internal uint NumberOfThreads;
|
||||
private fixed byte Reserved1[48];
|
||||
internal Interop.UNICODE_STRING ImageName;
|
||||
internal int BasePriority;
|
||||
internal IntPtr UniqueProcessId;
|
||||
private UIntPtr Reserved2;
|
||||
internal uint HandleCount;
|
||||
internal uint SessionId;
|
||||
private UIntPtr Reserved3;
|
||||
internal UIntPtr PeakVirtualSize; // SIZE_T
|
||||
internal UIntPtr VirtualSize;
|
||||
private uint Reserved4;
|
||||
internal UIntPtr PeakWorkingSetSize; // SIZE_T
|
||||
internal UIntPtr WorkingSetSize; // SIZE_T
|
||||
private UIntPtr Reserved5;
|
||||
internal UIntPtr QuotaPagedPoolUsage; // SIZE_T
|
||||
private UIntPtr Reserved6;
|
||||
internal UIntPtr QuotaNonPagedPoolUsage; // SIZE_T
|
||||
internal UIntPtr PagefileUsage; // SIZE_T
|
||||
internal UIntPtr PeakPagefileUsage; // SIZE_T
|
||||
internal UIntPtr PrivatePageCount; // SIZE_T
|
||||
private fixed long Reserved7[6];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct SystemThreadInformation
|
||||
{
|
||||
private fixed long Reserved1[3];
|
||||
private uint Reserved2;
|
||||
internal IntPtr StartAddress;
|
||||
internal CLIENT_ID ClientId;
|
||||
internal int Priority;
|
||||
internal int BasePriority;
|
||||
private uint Reserved3;
|
||||
internal uint ThreadState;
|
||||
internal uint WaitReason;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct CLIENT_ID
|
||||
{
|
||||
internal IntPtr UniqueProcess;
|
||||
internal IntPtr UniqueThread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ namespace System.Net.NetworkInformation
|
|||
int icmpHeaderOffset = ipHeaderLength;
|
||||
|
||||
// Skip IP header.
|
||||
IcmpHeader receivedHeader = MemoryMarshal.Cast<byte, IcmpHeader>(receiveBuffer.AsSpan(icmpHeaderOffset))[0];
|
||||
IcmpHeader receivedHeader = MemoryMarshal.Read<IcmpHeader>(receiveBuffer.AsSpan(icmpHeaderOffset));
|
||||
type = receivedHeader.Type;
|
||||
code = receivedHeader.Code;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче