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