/
Exception.CoreCLR.cs
307 lines (260 loc) · 13.4 KB
/
Exception.CoreCLR.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
namespace System
{
public partial class Exception : ISerializable
{
partial void RestoreRemoteStackTrace(SerializationInfo info, StreamingContext context)
{
// Get the WatsonBuckets that were serialized - this is particularly
// done to support exceptions going across AD transitions.
//
// We use the no throw version since we could be deserializing a pre-V4
// exception object that may not have this entry. In such a case, we would
// get null.
_watsonBuckets = (byte[]?)info.GetValueNoThrow("WatsonBuckets", typeof(byte[])); // Do not rename (binary serialization)
// If we are constructing a new exception after a cross-appdomain call...
#pragma warning disable SYSLIB0050 // StreamingContextStates is obsolete
if (context.State == StreamingContextStates.CrossAppDomain)
#pragma warning restore SYSLIB0050
{
// ...this new exception may get thrown. It is logically a re-throw, but
// physically a brand-new exception. Since the stack trace is cleared
// on a new exception, the "_remoteStackTraceString" is provided to
// effectively import a stack trace from a "remote" exception. So,
// move the _stackTraceString into the _remoteStackTraceString. Note
// that if there is an existing _remoteStackTraceString, it will be
// preserved at the head of the new string, so everything works as
// expected.
// Even if this exception is NOT thrown, things will still work as expected
// because the StackTrace property returns the concatenation of the
// _remoteStackTraceString and the _stackTraceString.
_remoteStackTraceString += _stackTraceString;
_stackTraceString = null;
}
}
private IDictionary CreateDataContainer()
{
if (IsImmutableAgileException(this))
return new EmptyReadOnlyDictionaryInternal();
else
return new ListDictionaryInternal();
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool IsImmutableAgileException(Exception e);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IRuntimeMethodInfo GetMethodFromStackTrace(object stackTrace);
private MethodBase? GetExceptionMethodFromStackTrace()
{
Debug.Assert(_stackTrace != null, "_stackTrace shouldn't be null when this method is called");
IRuntimeMethodInfo method = GetMethodFromStackTrace(_stackTrace!);
// Under certain race conditions when exceptions are re-used, this can be null
if (method == null)
return null;
return RuntimeType.GetMethodBase(method);
}
public MethodBase? TargetSite
{
[RequiresUnreferencedCode("Metadata for the method might be incomplete or removed")]
get
{
if (_exceptionMethod != null)
{
return _exceptionMethod;
}
if (_stackTrace == null)
{
return null;
}
_exceptionMethod = GetExceptionMethodFromStackTrace();
return _exceptionMethod;
}
}
// This method will clear the _stackTrace of the exception object upon deserialization
// to ensure that references from another AD/Process dont get accidentally used.
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
_stackTrace = null;
// We wont serialize or deserialize the IP for Watson bucketing since
// we dont know where the deserialized object will be used in.
// Using it across process or an AppDomain could be invalid and result
// in AV in the runtime.
//
// Hence, we set it to zero when deserialization takes place.
_ipForWatsonBuckets = UIntPtr.Zero;
}
// This is used by the runtime when re-throwing a managed exception. It will
// copy the stack trace to _remoteStackTraceString.
internal void InternalPreserveStackTrace()
{
// Make sure that the _source field is initialized if Source is not overridden.
// We want it to contain the original faulting point.
_ = Source;
string? tmpStackTraceString = StackTrace;
if (!string.IsNullOrEmpty(tmpStackTraceString))
{
_remoteStackTraceString = tmpStackTraceString + Environment.NewLineConst;
}
_stackTrace = null;
_stackTraceString = null;
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void PrepareForForeignExceptionRaise();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void GetStackTracesDeepCopy(Exception exception, out byte[]? currentStackTrace, out object[]? dynamicMethodArray);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void SaveStackTracesFromDeepCopy(Exception exception, byte[]? currentStackTrace, object[]? dynamicMethodArray);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern uint GetExceptionCount();
// This is invoked by ExceptionDispatchInfo.Throw to restore the exception stack trace, corresponding to the original throw of the
// exception, just before the exception is "rethrown".
internal void RestoreDispatchState(in DispatchState dispatchState)
{
// Restore only for non-preallocated exceptions
if (!IsImmutableAgileException(this))
{
// When restoring back the fields, we again create a copy and set reference to them
// in the exception object. This will ensure that when this exception is thrown and these
// fields are modified, then EDI's references remain intact.
//
byte[]? stackTraceCopy = (byte[]?)dispatchState.StackTrace?.Clone();
object[]? dynamicMethodsCopy = (object[]?)dispatchState.DynamicMethods?.Clone();
// Watson buckets and remoteStackTraceString fields are captured and restored without any locks. It is possible for them to
// get out of sync without violating overall integrity of the system.
_watsonBuckets = dispatchState.WatsonBuckets;
_ipForWatsonBuckets = dispatchState.IpForWatsonBuckets;
_remoteStackTraceString = dispatchState.RemoteStackTrace;
// The binary stack trace and references to dynamic methods have to be restored under a lock to guarantee integrity of the system.
SaveStackTracesFromDeepCopy(this, stackTraceCopy, dynamicMethodsCopy);
_stackTraceString = null;
// Marks the TES state to indicate we have restored foreign exception
// dispatch information.
PrepareForForeignExceptionRaise();
}
}
private MethodBase? _exceptionMethod; // Needed for serialization.
internal string? _message;
private IDictionary? _data;
private readonly Exception? _innerException;
private string? _helpURL;
private byte[]? _stackTrace;
private byte[]? _watsonBuckets;
private string? _stackTraceString; // Needed for serialization.
private string? _remoteStackTraceString;
#pragma warning disable CA1823, 414 // Fields are not used from managed.
// _dynamicMethods is an array of System.Resolver objects, used to keep
// DynamicMethodDescs alive for the lifetime of the exception. We do this because
// the _stackTrace field holds MethodDescs, and a DynamicMethodDesc can be destroyed
// unless a System.Resolver object roots it.
private readonly object[]? _dynamicMethods;
private string? _source; // Mainly used by VB.
private UIntPtr _ipForWatsonBuckets; // Used to persist the IP for Watson Bucketing
private readonly IntPtr _xptrs; // Internal EE stuff
private readonly int _xcode = _COMPlusExceptionCode; // Internal EE stuff
#pragma warning restore CA1823, 414
// @MANAGED: HResult is used from within the EE! Rename with care - check VM directory
private int _HResult; // HResult
// See src\inc\corexcep.h's EXCEPTION_COMPLUS definition:
private const int _COMPlusExceptionCode = unchecked((int)0xe0434352); // Win32 exception code for COM+ exceptions
private bool HasBeenThrown => _stackTrace != null;
private object? SerializationWatsonBuckets => _watsonBuckets;
// This piece of infrastructure exists to help avoid deadlocks
// between parts of CoreLib that might throw an exception while
// holding a lock that are also used by CoreLib's ResourceManager
// instance. As a special case of code that may throw while holding
// a lock, we also need to fix our asynchronous exceptions to use
// Win32 resources as well (assuming we ever call a managed
// constructor on instances of them). We should grow this set of
// exception messages as we discover problems, then move the resources
// involved to native code.
internal enum ExceptionMessageKind
{
ThreadAbort = 1,
ThreadInterrupted = 2,
OutOfMemory = 3
}
// See comment on ExceptionMessageKind
internal static string GetMessageFromNativeResources(ExceptionMessageKind kind)
{
string? retMesg = null;
GetMessageFromNativeResources(kind, new StringHandleOnStack(ref retMesg));
return retMesg!;
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ExceptionNative_GetMessageFromNativeResources")]
private static partial void GetMessageFromNativeResources(ExceptionMessageKind kind, StringHandleOnStack retMesg);
internal readonly struct DispatchState
{
public readonly byte[]? StackTrace;
public readonly object[]? DynamicMethods;
public readonly string? RemoteStackTrace;
public readonly UIntPtr IpForWatsonBuckets;
public readonly byte[]? WatsonBuckets;
public DispatchState(
byte[]? stackTrace,
object[]? dynamicMethods,
string? remoteStackTrace,
UIntPtr ipForWatsonBuckets,
byte[]? watsonBuckets)
{
StackTrace = stackTrace;
DynamicMethods = dynamicMethods;
RemoteStackTrace = remoteStackTrace;
IpForWatsonBuckets = ipForWatsonBuckets;
WatsonBuckets = watsonBuckets;
}
}
internal DispatchState CaptureDispatchState()
{
GetStackTracesDeepCopy(this, out byte[]? stackTrace, out object[]? dynamicMethods);
return new DispatchState(stackTrace, dynamicMethods,
_remoteStackTraceString, _ipForWatsonBuckets, _watsonBuckets);
}
// Returns true if setting the _remoteStackTraceString field is legal, false if not (immutable exception).
// A false return value means the caller should early-exit the operation.
// Can also throw InvalidOperationException if a stack trace is already set or if object has been thrown.
private bool CanSetRemoteStackTrace()
{
// If this is a preallocated singleton exception, silently skip the operation,
// regardless of the value of throwIfHasExistingStack.
if (IsImmutableAgileException(this))
{
return false;
}
// Check to see if the exception already has a stack set in it.
if (_stackTrace != null || _stackTraceString != null || _remoteStackTraceString != null)
{
ThrowHelper.ThrowInvalidOperationException();
}
return true;
}
// used by vm
internal string? GetHelpContext(out uint helpContext)
{
helpContext = 0;
string? helpFile = HelpLink;
int poundPos, digitEnd;
if (helpFile is null || (poundPos = helpFile.LastIndexOf('#')) == -1)
{
return helpFile;
}
for (digitEnd = poundPos + 1; digitEnd < helpFile.Length; digitEnd++)
{
if (char.IsWhiteSpace(helpFile[digitEnd]))
break;
}
if (uint.TryParse(helpFile.AsSpan(poundPos + 1, digitEnd - poundPos - 1), out helpContext))
{
helpFile = helpFile.Substring(0, poundPos);
}
return helpFile;
}
}
}