1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Resources; 7 using System.Runtime.InteropServices; 8 using System.Security; 9 10 #if ES_BUILD_STANDALONE 11 using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; 12 namespace Microsoft.Diagnostics.Tracing 13 #else 14 namespace System.Diagnostics.Tracing 15 #endif 16 { 17 /// <summary> 18 /// TraceLogging: This is the implementation of the DataCollector 19 /// functionality. To enable safe access to the DataCollector from 20 /// untrusted code, there is one thread-local instance of this structure 21 /// per thread. The instance must be Enabled before any data is written to 22 /// it. The instance must be Finished before the data is passed to 23 /// EventWrite. The instance must be Disabled before the arrays referenced 24 /// by the pointers are freed or unpinned. 25 /// </summary> 26 internal unsafe struct DataCollector 27 { 28 [ThreadStatic] 29 internal static DataCollector ThreadInstance; 30 31 private byte* scratchEnd; 32 private EventSource.EventData* datasEnd; 33 private GCHandle* pinsEnd; 34 private EventSource.EventData* datasStart; 35 private byte* scratch; 36 private EventSource.EventData* datas; 37 private GCHandle* pins; 38 private byte[] buffer; 39 private int bufferPos; 40 private int bufferNesting; // We may merge many fields int a single blob. If we are doing this we increment this. 41 private bool writingScalars; 42 EnableMicrosoft.Diagnostics.Tracing.DataCollector43 internal void Enable( 44 byte* scratch, 45 int scratchSize, 46 EventSource.EventData* datas, 47 int dataCount, 48 GCHandle* pins, 49 int pinCount) 50 { 51 this.datasStart = datas; 52 this.scratchEnd = scratch + scratchSize; 53 this.datasEnd = datas + dataCount; 54 this.pinsEnd = pins + pinCount; 55 this.scratch = scratch; 56 this.datas = datas; 57 this.pins = pins; 58 this.writingScalars = false; 59 } 60 DisableMicrosoft.Diagnostics.Tracing.DataCollector61 internal void Disable() 62 { 63 this = new DataCollector(); 64 } 65 66 /// <summary> 67 /// Completes the list of scalars. Finish must be called before the data 68 /// descriptor array is passed to EventWrite. 69 /// </summary> 70 /// <returns> 71 /// A pointer to the next unused data descriptor, or datasEnd if they were 72 /// all used. (Descriptors may be unused if a string or array was null.) 73 /// </returns> FinishMicrosoft.Diagnostics.Tracing.DataCollector74 internal EventSource.EventData* Finish() 75 { 76 this.ScalarsEnd(); 77 return this.datas; 78 } 79 AddScalarMicrosoft.Diagnostics.Tracing.DataCollector80 internal void AddScalar(void* value, int size) 81 { 82 var pb = (byte*)value; 83 if (this.bufferNesting == 0) 84 { 85 var scratchOld = this.scratch; 86 var scratchNew = scratchOld + size; 87 if (this.scratchEnd < scratchNew) 88 { 89 throw new IndexOutOfRangeException(SR.EventSource_AddScalarOutOfRange); 90 } 91 92 this.ScalarsBegin(); 93 this.scratch = scratchNew; 94 95 for (int i = 0; i != size; i++) 96 { 97 scratchOld[i] = pb[i]; 98 } 99 } 100 else 101 { 102 var oldPos = this.bufferPos; 103 this.bufferPos = checked(this.bufferPos + size); 104 this.EnsureBuffer(); 105 for (int i = 0; i != size; i++, oldPos++) 106 { 107 this.buffer[oldPos] = pb[i]; 108 } 109 } 110 } 111 AddBinaryMicrosoft.Diagnostics.Tracing.DataCollector112 internal void AddBinary(string value, int size) 113 { 114 if (size > ushort.MaxValue) 115 { 116 size = ushort.MaxValue - 1; 117 } 118 119 if (this.bufferNesting != 0) 120 { 121 this.EnsureBuffer(size + 2); 122 } 123 124 this.AddScalar(&size, 2); 125 126 if (size != 0) 127 { 128 if (this.bufferNesting == 0) 129 { 130 this.ScalarsEnd(); 131 this.PinArray(value, size); 132 } 133 else 134 { 135 var oldPos = this.bufferPos; 136 this.bufferPos = checked(this.bufferPos + size); 137 this.EnsureBuffer(); 138 fixed (void* p = value) 139 { 140 Marshal.Copy((IntPtr)p, buffer, oldPos, size); 141 } 142 } 143 } 144 } 145 AddBinaryMicrosoft.Diagnostics.Tracing.DataCollector146 internal void AddBinary(Array value, int size) 147 { 148 this.AddArray(value, size, 1); 149 } 150 AddArrayMicrosoft.Diagnostics.Tracing.DataCollector151 internal void AddArray(Array value, int length, int itemSize) 152 { 153 if (length > ushort.MaxValue) 154 { 155 length = ushort.MaxValue; 156 } 157 158 var size = length * itemSize; 159 if (this.bufferNesting != 0) 160 { 161 this.EnsureBuffer(size + 2); 162 } 163 164 this.AddScalar(&length, 2); 165 166 if (length != 0) 167 { 168 if (this.bufferNesting == 0) 169 { 170 this.ScalarsEnd(); 171 this.PinArray(value, size); 172 } 173 else 174 { 175 var oldPos = this.bufferPos; 176 this.bufferPos = checked(this.bufferPos + size); 177 this.EnsureBuffer(); 178 Buffer.BlockCopy(value, 0, this.buffer, oldPos, size); 179 } 180 } 181 } 182 183 /// <summary> 184 /// Marks the start of a non-blittable array or enumerable. 185 /// </summary> 186 /// <returns>Bookmark to be passed to EndBufferedArray.</returns> BeginBufferedArrayMicrosoft.Diagnostics.Tracing.DataCollector187 internal int BeginBufferedArray() 188 { 189 this.BeginBuffered(); 190 this.bufferPos += 2; // Reserve space for the array length (filled in by EndEnumerable) 191 return this.bufferPos; 192 } 193 194 /// <summary> 195 /// Marks the end of a non-blittable array or enumerable. 196 /// </summary> 197 /// <param name="bookmark">The value returned by BeginBufferedArray.</param> 198 /// <param name="count">The number of items in the array.</param> EndBufferedArrayMicrosoft.Diagnostics.Tracing.DataCollector199 internal void EndBufferedArray(int bookmark, int count) 200 { 201 this.EnsureBuffer(); 202 this.buffer[bookmark - 2] = unchecked((byte)count); 203 this.buffer[bookmark - 1] = unchecked((byte)(count >> 8)); 204 this.EndBuffered(); 205 } 206 207 /// <summary> 208 /// Marks the start of dynamically-buffered data. 209 /// </summary> BeginBufferedMicrosoft.Diagnostics.Tracing.DataCollector210 internal void BeginBuffered() 211 { 212 this.ScalarsEnd(); 213 this.bufferNesting += 1; 214 } 215 216 /// <summary> 217 /// Marks the end of dynamically-buffered data. 218 /// </summary> EndBufferedMicrosoft.Diagnostics.Tracing.DataCollector219 internal void EndBuffered() 220 { 221 this.bufferNesting -= 1; 222 223 if (this.bufferNesting == 0) 224 { 225 /* 226 TODO (perf): consider coalescing adjacent buffered regions into a 227 single buffer, similar to what we're already doing for adjacent 228 scalars. In addition, if a type contains a buffered region adjacent 229 to a blittable array, and the blittable array is small, it would be 230 more efficient to buffer the array instead of pinning it. 231 */ 232 233 this.EnsureBuffer(); 234 this.PinArray(this.buffer, this.bufferPos); 235 this.buffer = null; 236 this.bufferPos = 0; 237 } 238 } 239 EnsureBufferMicrosoft.Diagnostics.Tracing.DataCollector240 private void EnsureBuffer() 241 { 242 var required = this.bufferPos; 243 if (this.buffer == null || this.buffer.Length < required) 244 { 245 this.GrowBuffer(required); 246 } 247 } 248 EnsureBufferMicrosoft.Diagnostics.Tracing.DataCollector249 private void EnsureBuffer(int additionalSize) 250 { 251 var required = this.bufferPos + additionalSize; 252 if (this.buffer == null || this.buffer.Length < required) 253 { 254 this.GrowBuffer(required); 255 } 256 } 257 GrowBufferMicrosoft.Diagnostics.Tracing.DataCollector258 private void GrowBuffer(int required) 259 { 260 var newSize = this.buffer == null ? 64 : this.buffer.Length; 261 262 do 263 { 264 newSize *= 2; 265 } 266 while (newSize < required); 267 268 Array.Resize(ref this.buffer, newSize); 269 } 270 PinArrayMicrosoft.Diagnostics.Tracing.DataCollector271 private void PinArray(object value, int size) 272 { 273 var pinsTemp = this.pins; 274 if (this.pinsEnd <= pinsTemp) 275 { 276 throw new IndexOutOfRangeException(SR.EventSource_PinArrayOutOfRange); 277 } 278 279 var datasTemp = this.datas; 280 if (this.datasEnd <= datasTemp) 281 { 282 throw new IndexOutOfRangeException(SR.EventSource_DataDescriptorsOutOfRange); 283 } 284 285 this.pins = pinsTemp + 1; 286 this.datas = datasTemp + 1; 287 288 *pinsTemp = GCHandle.Alloc(value, GCHandleType.Pinned); 289 datasTemp->DataPointer = pinsTemp->AddrOfPinnedObject(); 290 datasTemp->m_Size = size; 291 } 292 ScalarsBeginMicrosoft.Diagnostics.Tracing.DataCollector293 private void ScalarsBegin() 294 { 295 if (!this.writingScalars) 296 { 297 var datasTemp = this.datas; 298 if (this.datasEnd <= datasTemp) 299 { 300 throw new IndexOutOfRangeException(SR.EventSource_DataDescriptorsOutOfRange); 301 } 302 303 datasTemp->DataPointer = (IntPtr) this.scratch; 304 this.writingScalars = true; 305 } 306 } 307 ScalarsEndMicrosoft.Diagnostics.Tracing.DataCollector308 private void ScalarsEnd() 309 { 310 if (this.writingScalars) 311 { 312 var datasTemp = this.datas; 313 datasTemp->m_Size = checked((int)(this.scratch - (byte*)datasTemp->m_Ptr)); 314 this.datas = datasTemp + 1; 315 this.writingScalars = false; 316 } 317 } 318 } 319 } 320