Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web / AspNetEventSource.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="AspNetEventSource.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 namespace System.Web {
8     using System;
9     using System.Diagnostics.CodeAnalysis;
10     using System.Diagnostics.Tracing;
11     using System.Reflection;
12     using System.Runtime.CompilerServices;
13     using System.Runtime.ConstrainedExecution;
14     using System.Runtime.InteropServices;
15     using System.Web.Hosting;
16     using System.Web.Util;
17
18     // Name and Guid are part of the public contract (for identification by ETW listeners) so cannot
19     // be changed. We're statically specifying a GUID using the same logic as EventSource.GetGuid,
20     // as otherwise EventSource invokes crypto to generate the GUID and this results in an
21     // unacceptable performance degradation (DevDiv #652801).
22     [EventSource(Name = "Microsoft-Windows-ASPNET", Guid = "ee799f41-cfa5-550b-bf2c-344747c1c668")]
23     internal sealed class AspNetEventSource : EventSource {
24
25         // singleton
26         public static readonly AspNetEventSource Instance = new AspNetEventSource();
27
28         private unsafe delegate void WriteEventWithRelatedActivityIdCoreDelegate(int eventId, Guid* childActivityID, int eventDataCount, EventData* data);
29         private readonly WriteEventWithRelatedActivityIdCoreDelegate _writeEventWithRelatedActivityIdCoreDel;
30
31         private AspNetEventSource() {
32             // We need to light up when running on .NET 4.5.1 since we can't compile directly
33             // against the protected methods we might need to consume. Only ever try creating
34             // this delegate if we're in full trust, otherwise exceptions could happen at
35             // inopportune times (such as during invocation).
36
37             if (AppDomain.CurrentDomain.IsHomogenous && AppDomain.CurrentDomain.IsFullyTrusted) {
38                 MethodInfo writeEventWithRelatedActivityIdCoreMethod = typeof(EventSource).GetMethod(
39                     "WriteEventWithRelatedActivityIdCore", BindingFlags.Instance | BindingFlags.NonPublic, null,
40                     new Type[] { typeof(int), typeof(Guid*), typeof(int), typeof(EventData*) }, null);
41
42                 if (writeEventWithRelatedActivityIdCoreMethod != null) {
43                     _writeEventWithRelatedActivityIdCoreDel = (WriteEventWithRelatedActivityIdCoreDelegate)Delegate.CreateDelegate(
44                         typeof(WriteEventWithRelatedActivityIdCoreDelegate), this, writeEventWithRelatedActivityIdCoreMethod, throwOnBindFailure: false);
45                 }
46             }
47         }
48
49         [NonEvent] // use the private member signature for deducing ETW parameters
50         [MethodImpl(MethodImplOptions.AggressiveInlining)]
51         public void RequestEnteredAspNetPipeline(IIS7WorkerRequest wr, Guid childActivityId) {
52             if (!IsEnabled()) {
53                 return;
54             }
55
56             Guid parentActivityId = wr.RequestTraceIdentifier;
57             RequestEnteredAspNetPipelineImpl(parentActivityId, childActivityId);
58         }
59
60         [NonEvent] // use the private member signature for deducing ETW parameters
61         private unsafe void RequestEnteredAspNetPipelineImpl(Guid iisActivityId, Guid aspNetActivityId) {
62             if (ActivityIdHelper.Instance == null || _writeEventWithRelatedActivityIdCoreDel == null || iisActivityId == Guid.Empty) {
63                 return;
64             }
65
66             // IIS doesn't always set the current thread's activity ID before invoking user code. Instead,
67             // its tracing APIs (IHttpTraceContext::RaiseTraceEvent) set the ID, write to ETW, then reset
68             // the ID. If we want to write a transfer event but the current thread's activity ID is
69             // incorrect, then we need to mimic this behavior. We don't use a try / finally since
70             // exceptions here are fatal to the process.
71
72             Guid originalThreadActivityId = ActivityIdHelper.Instance.CurrentThreadActivityId;
73             bool needToSetThreadActivityId = (originalThreadActivityId != iisActivityId);
74
75             // Step 1: Set the ID (if necessary)
76             if (needToSetThreadActivityId) {
77                 ActivityIdHelper.Instance.SetCurrentThreadActivityId(iisActivityId, out originalThreadActivityId);
78             }
79
80             // Step 2: Write to ETW, providing the recipient activity ID.
81             _writeEventWithRelatedActivityIdCoreDel((int)Events.RequestEnteredAspNetPipeline, &aspNetActivityId, 0, null);
82
83             // Step 3: Reset the ID (if necessary)
84             if (needToSetThreadActivityId) {
85                 Guid unused;
86                 ActivityIdHelper.Instance.SetCurrentThreadActivityId(originalThreadActivityId, out unused);
87             }
88         }
89
90         // Transfer event signals that control has transitioned from IIS -> ASP.NET.
91         // Overload used only for deducing ETW parameters; use the public entry point instead.
92         //
93         // !! WARNING !!
94         // The logic in RequestEnteredAspNetPipelineImpl must be kept in sync with these parameters, otherwise
95         // type safety violations could occur.
96         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "ETW looks at this method using reflection.")]
97         [Event((int)Events.RequestEnteredAspNetPipeline, Level = EventLevel.Informational, Task = (EventTask)Tasks.Request, Opcode = EventOpcode.Send, Version = 1)]
98         private void RequestEnteredAspNetPipeline() {
99             throw new NotImplementedException();
100         }
101
102         [NonEvent] // use the private member signature for deducing ETW parameters
103         [MethodImpl(MethodImplOptions.AggressiveInlining)]
104         public unsafe void RequestStarted(IIS7WorkerRequest wr) {
105             if (!IsEnabled()) {
106                 return;
107             }
108
109             RequestStartedImpl(wr);
110         }
111
112         [NonEvent] // use the private member signature for deducing ETW parameters
113         private unsafe void RequestStartedImpl(IIS7WorkerRequest wr) {
114             string httpVerb = wr.GetHttpVerbName();
115             HTTP_COOKED_URL* pCookedUrl = wr.GetCookedUrl();
116             Guid iisEtwActivityId = wr.RequestTraceIdentifier;
117             Guid requestCorrelationId = wr.GetRequestCorrelationId();
118
119             fixed (char* pHttpVerb = httpVerb) {
120                 // !! WARNING !!
121                 // This logic must be kept in sync with the ETW-deduced parameters in RequestStarted,
122                 // otherwise type safety violations could occur.
123                 const int EVENTDATA_COUNT = 3;
124                 EventData* pEventData = stackalloc EventData[EVENTDATA_COUNT];
125
126                 FillInEventData(&pEventData[0], httpVerb, pHttpVerb);
127
128                 // We have knowledge that pFullUrl is null-terminated so we can optimize away
129                 // the copy we'd otherwise have to perform. Still need to adjust the length
130                 // to account for the null terminator, though.
131                 Debug.Assert(pCookedUrl->pFullUrl != null);
132                 pEventData[1].DataPointer = (IntPtr)pCookedUrl->pFullUrl;
133                 pEventData[1].Size = checked(pCookedUrl->FullUrlLength + sizeof(char));
134
135                 FillInEventData(&pEventData[2], &requestCorrelationId);
136                 WriteEventCore((int)Events.RequestStarted, EVENTDATA_COUNT, pEventData);
137             }
138         }
139
140         // Event signals that ASP.NET has started processing a request.
141         // Overload used only for deducing ETW parameters; use the public entry point instead.
142         //
143         // Visual Studio Online #222067 - This event is hardcoded to opt-out of EventSource activityID tracking. 
144         // This would normally be done by setting ActivityOptions = EventActivityOptions.Disable in the 
145         // Event attribute, but this causes a dependency between System.Web and mscorlib that breaks servicing. 
146         // 
147         // !! WARNING !!
148         // The logic in RequestStartedImpl must be kept in sync with these parameters, otherwise
149         // type safety violations could occur.
150         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "ETW looks at this method using reflection.")]
151         [Event((int)Events.RequestStarted, Level = EventLevel.Informational, Task = (EventTask)Tasks.Request, Opcode = EventOpcode.Start, Version = 1)]
152         private unsafe void RequestStarted(string HttpVerb, string FullUrl, Guid RequestCorrelationId) {
153             throw new NotImplementedException();
154         }
155
156         // Event signals that ASP.NET has completed processing a request.
157         //
158         // Visual Studio Online #222067 - This event is hardcoded to opt-out of EventSource activityID tracking. 
159         // This would normally be done by setting ActivityOptions = EventActivityOptions.Disable in the 
160         // Event attribute, but this causes a dependency between System.Web and mscorlib that breaks servicing. 
161         [Event((int)Events.RequestCompleted, Level = EventLevel.Informational, Task = (EventTask)Tasks.Request, Opcode = EventOpcode.Stop, Version = 1)]
162         [MethodImpl(MethodImplOptions.AggressiveInlining)]
163         public void RequestCompleted() {
164             if (!IsEnabled()) {
165                 return;
166             }
167
168             WriteEvent((int)Events.RequestCompleted);
169         }
170
171         /*
172          * Helpers to populate the EventData structure
173          */
174
175         // prerequisite: str must be pinned and provided as pStr; may be null.
176         // we'll convert null strings to empty strings if necessary.
177         [MethodImpl(MethodImplOptions.AggressiveInlining)]
178         private unsafe static void FillInEventData(EventData* pEventData, string str, char* pStr) {
179 #if DBG
180             fixed (char* pStr2 = str) { Debug.Assert(pStr == pStr2); }
181 #endif
182
183             if (pStr != null) {
184                 pEventData->DataPointer = (IntPtr)pStr;
185                 pEventData->Size = checked((str.Length + 1) * sizeof(char)); // size is specified in bytes, including null wide char
186             }
187             else {
188                 pEventData->DataPointer = NullHelper.Instance.PtrToNullChar; // empty string
189                 pEventData->Size = sizeof(char);
190             }
191         }
192
193         [MethodImpl(MethodImplOptions.AggressiveInlining)]
194         private unsafe static void FillInEventData(EventData* pEventData, Guid* pGuid) {
195             Debug.Assert(pGuid != null);
196             pEventData->DataPointer = (IntPtr)pGuid;
197             pEventData->Size = sizeof(Guid);
198         }
199
200         // Each ETW event should have its own entry here.
201         private enum Events {
202             RequestEnteredAspNetPipeline = 1,
203             RequestStarted,
204             RequestCompleted
205         }
206
207         // Tasks are used for correlating events; we're free to define our own.
208         // For example, Tasks.Request with Opcode = Start matches Tasks.Request with Opcode = Stop,
209         // and Tasks.Application with Opcode = Start matches Tasks.Application with Opcode = Stop.
210         //
211         // EventSource requires that this be a public static class with public const fields,
212         // otherwise manifest generation could fail at runtime.
213         public static class Tasks {
214             public const EventTask Request = (EventTask)1;
215         }
216
217         private sealed class NullHelper : CriticalFinalizerObject {
218             public static readonly NullHelper Instance = new NullHelper();
219
220             [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Justification = @"Containing type is a CriticalFinalizerObject.")]
221             public readonly IntPtr PtrToNullChar;
222
223             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
224             private unsafe NullHelper() {
225                 // allocate a single null character
226                 PtrToNullChar = Marshal.AllocHGlobal(sizeof(char));
227                 *((char*)PtrToNullChar) = '\0';
228             }
229
230             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
231             ~NullHelper() {
232                 if (PtrToNullChar != IntPtr.Zero) {
233                     Marshal.FreeHGlobal(PtrToNullChar);
234                 }
235             }
236         }
237     }
238 }