diff options
Diffstat (limited to 'src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs')
-rw-r--r-- | src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs new file mode 100644 index 0000000000..d0d687e8d8 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Eventing/TraceLogging/DataCollector.cs @@ -0,0 +1,319 @@ +// Licensed to the .NET Foundation under one or more agreements. +// 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.Runtime.InteropServices; +using System.Security; + +#if ES_BUILD_STANDALONE +using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// TraceLogging: This is the implementation of the DataCollector + /// functionality. To enable safe access to the DataCollector from + /// untrusted code, there is one thread-local instance of this structure + /// per thread. The instance must be Enabled before any data is written to + /// it. The instance must be Finished before the data is passed to + /// EventWrite. The instance must be Disabled before the arrays referenced + /// by the pointers are freed or unpinned. + /// </summary> + [SecurityCritical] + internal unsafe struct DataCollector + { + [ThreadStatic] + internal static DataCollector ThreadInstance; + + private byte* scratchEnd; + private EventSource.EventData* datasEnd; + private GCHandle* pinsEnd; + private EventSource.EventData* datasStart; + private byte* scratch; + private EventSource.EventData* datas; + private GCHandle* pins; + private byte[] buffer; + private int bufferPos; + private int bufferNesting; // We may merge many fields int a single blob. If we are doing this we increment this. + private bool writingScalars; + + internal void Enable( + byte* scratch, + int scratchSize, + EventSource.EventData* datas, + int dataCount, + GCHandle* pins, + int pinCount) + { + this.datasStart = datas; + this.scratchEnd = scratch + scratchSize; + this.datasEnd = datas + dataCount; + this.pinsEnd = pins + pinCount; + this.scratch = scratch; + this.datas = datas; + this.pins = pins; + this.writingScalars = false; + } + + internal void Disable() + { + this = new DataCollector(); + } + + /// <summary> + /// Completes the list of scalars. Finish must be called before the data + /// descriptor array is passed to EventWrite. + /// </summary> + /// <returns> + /// A pointer to the next unused data descriptor, or datasEnd if they were + /// all used. (Descriptors may be unused if a string or array was null.) + /// </returns> + internal EventSource.EventData* Finish() + { + this.ScalarsEnd(); + return this.datas; + } + + internal void AddScalar(void* value, int size) + { + var pb = (byte*)value; + if (this.bufferNesting == 0) + { + var scratchOld = this.scratch; + var scratchNew = scratchOld + size; + if (this.scratchEnd < scratchNew) + { + throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_AddScalarOutOfRange")); + } + + this.ScalarsBegin(); + this.scratch = scratchNew; + + for (int i = 0; i != size; i++) + { + scratchOld[i] = pb[i]; + } + } + else + { + var oldPos = this.bufferPos; + this.bufferPos = checked(this.bufferPos + size); + this.EnsureBuffer(); + for (int i = 0; i != size; i++, oldPos++) + { + this.buffer[oldPos] = pb[i]; + } + } + } + + internal void AddBinary(string value, int size) + { + if (size > ushort.MaxValue) + { + size = ushort.MaxValue - 1; + } + + if (this.bufferNesting != 0) + { + this.EnsureBuffer(size + 2); + } + + this.AddScalar(&size, 2); + + if (size != 0) + { + if (this.bufferNesting == 0) + { + this.ScalarsEnd(); + this.PinArray(value, size); + } + else + { + var oldPos = this.bufferPos; + this.bufferPos = checked(this.bufferPos + size); + this.EnsureBuffer(); + fixed (void* p = value) + { + Marshal.Copy((IntPtr)p, this.buffer, oldPos, size); + } + } + } + } + + internal void AddBinary(Array value, int size) + { + this.AddArray(value, size, 1); + } + + internal void AddArray(Array value, int length, int itemSize) + { + if (length > ushort.MaxValue) + { + length = ushort.MaxValue; + } + + var size = length * itemSize; + if (this.bufferNesting != 0) + { + this.EnsureBuffer(size + 2); + } + + this.AddScalar(&length, 2); + + if (length != 0) + { + if (this.bufferNesting == 0) + { + this.ScalarsEnd(); + this.PinArray(value, size); + } + else + { + var oldPos = this.bufferPos; + this.bufferPos = checked(this.bufferPos + size); + this.EnsureBuffer(); + Buffer.BlockCopy(value, 0, this.buffer, oldPos, size); + } + } + } + + /// <summary> + /// Marks the start of a non-blittable array or enumerable. + /// </summary> + /// <returns>Bookmark to be passed to EndBufferedArray.</returns> + internal int BeginBufferedArray() + { + this.BeginBuffered(); + this.bufferPos += 2; // Reserve space for the array length (filled in by EndEnumerable) + return this.bufferPos; + } + + /// <summary> + /// Marks the end of a non-blittable array or enumerable. + /// </summary> + /// <param name="bookmark">The value returned by BeginBufferedArray.</param> + /// <param name="count">The number of items in the array.</param> + internal void EndBufferedArray(int bookmark, int count) + { + this.EnsureBuffer(); + this.buffer[bookmark - 2] = unchecked((byte)count); + this.buffer[bookmark - 1] = unchecked((byte)(count >> 8)); + this.EndBuffered(); + } + + /// <summary> + /// Marks the start of dynamically-buffered data. + /// </summary> + internal void BeginBuffered() + { + this.ScalarsEnd(); + this.bufferNesting += 1; + } + + /// <summary> + /// Marks the end of dynamically-buffered data. + /// </summary> + internal void EndBuffered() + { + this.bufferNesting -= 1; + + if (this.bufferNesting == 0) + { + /* + TODO (perf): consider coalescing adjacent buffered regions into a + single buffer, similar to what we're already doing for adjacent + scalars. In addition, if a type contains a buffered region adjacent + to a blittable array, and the blittable array is small, it would be + more efficient to buffer the array instead of pinning it. + */ + + this.EnsureBuffer(); + this.PinArray(this.buffer, this.bufferPos); + this.buffer = null; + this.bufferPos = 0; + } + } + + private void EnsureBuffer() + { + var required = this.bufferPos; + if (this.buffer == null || this.buffer.Length < required) + { + this.GrowBuffer(required); + } + } + + private void EnsureBuffer(int additionalSize) + { + var required = this.bufferPos + additionalSize; + if (this.buffer == null || this.buffer.Length < required) + { + this.GrowBuffer(required); + } + } + + private void GrowBuffer(int required) + { + var newSize = this.buffer == null ? 64 : this.buffer.Length; + + do + { + newSize *= 2; + } + while (newSize < required); + + Array.Resize(ref this.buffer, newSize); + } + + private void PinArray(object value, int size) + { + var pinsTemp = this.pins; + if (this.pinsEnd <= pinsTemp) + { + throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_PinArrayOutOfRange")); + } + + var datasTemp = this.datas; + if (this.datasEnd <= datasTemp) + { + throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_DataDescriptorsOutOfRange")); + } + + this.pins = pinsTemp + 1; + this.datas = datasTemp + 1; + + *pinsTemp = GCHandle.Alloc(value, GCHandleType.Pinned); + datasTemp->m_Ptr = (long)(ulong)(UIntPtr)(void*)pinsTemp->AddrOfPinnedObject(); + datasTemp->m_Size = size; + } + + private void ScalarsBegin() + { + if (!this.writingScalars) + { + var datasTemp = this.datas; + if (this.datasEnd <= datasTemp) + { + throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_DataDescriptorsOutOfRange")); + } + + datasTemp->m_Ptr = (long)(ulong)(UIntPtr)this.scratch; + this.writingScalars = true; + } + } + + private void ScalarsEnd() + { + if (this.writingScalars) + { + var datasTemp = this.datas; + datasTemp->m_Size = checked((int)(this.scratch - (byte*)datasTemp->m_Ptr)); + this.datas = datasTemp + 1; + this.writingScalars = false; + } + } + } +} |