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:
Jan Kotas 2018-06-24 07:18:49 -07:00 коммит произвёл GitHub
Родитель 3b24c53585
Коммит d1cdb1b324
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 605 добавлений и 667 удалений

Просмотреть файл

@ -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;