Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 52 additions & 35 deletions src/coreclr/vm/excep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9713,6 +9713,7 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)
{
OBJECTREF oCurrentThrowable;
OBJECTREF oInnerMostExceptionThrowable;
U1ARRAYREF refSourceWatsonBucketArray;
} gc;
ZeroMemory(&gc, sizeof(gc));

Expand Down Expand Up @@ -9907,6 +9908,10 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)
}

phase1:
UINT_PTR ipForWatsonBuckets = 0;
gc.refSourceWatsonBucketArray = NULL;
bool gotWatsonBucketDetails = true;

if (fAreBucketingDetailsPresent)
{
// Since we already have the buckets, simply bail out
Expand Down Expand Up @@ -9940,9 +9945,12 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)
}
else
{
ipForWatsonBuckets = ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->GetIPForWatsonBuckets();
gc.refSourceWatsonBucketArray = ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->GetWatsonBucketReference();

// Do we have either the IP for Watson buckets or the buckets themselves?
fAreBucketingDetailsPresent = (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() ||
((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent());
fAreBucketingDetailsPresent = (gc.refSourceWatsonBucketArray != NULL ||
ipForWatsonBuckets != 0);
}
}

Expand Down Expand Up @@ -10028,16 +10036,12 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)
{
// The inner exception object should be having either the IP for watson bucketing or the buckets themselves.
// We shall copy over, whatever is available, to the current exception object.
_ASSERTE(gc.oInnerMostExceptionThrowable != NULL);
_ASSERTE(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() ||
((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent());

if (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent())
if (gc.refSourceWatsonBucketArray != NULL)
{
EX_TRY
{
// Copy the bucket details from innermost exception to the current exception object.
CopyWatsonBucketsFromThrowableToCurrentThrowable(gc.oInnerMostExceptionThrowable);
CopyWatsonBucketsFromThrowableToCurrentThrowable(gc.refSourceWatsonBucketArray);
}
EX_CATCH
{
Expand All @@ -10046,17 +10050,24 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)

LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Copied watson bucket details from the innermost exception\n"));
}
else
else if (ipForWatsonBuckets != NULL)
{
// Copy the IP to the current exception object
((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->GetIPForWatsonBuckets());
((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(ipForWatsonBuckets);
LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Copied watson bucket IP from the innermost exception\n"));
}
else
{
gotWatsonBucketDetails = false;
}
}

done:
// Set the flag that we have got the bucketing details
pExState->GetFlags()->SetGotWatsonBucketDetails();
if (gotWatsonBucketDetails)
{
pExState->GetFlags()->SetGotWatsonBucketDetails();
}

GCPROTECT_END();

Expand All @@ -10065,7 +10076,7 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)

// This function is a wrapper to copy the watson bucket byte[] from the specified
// throwable to the current throwable.
void CopyWatsonBucketsFromThrowableToCurrentThrowable(OBJECTREF oThrowableFrom)
void CopyWatsonBucketsFromThrowableToCurrentThrowable(U1ARRAYREF oManagedWatsonBuckets)
{
#ifndef DACCESS_COMPILE

Expand All @@ -10074,29 +10085,27 @@ void CopyWatsonBucketsFromThrowableToCurrentThrowable(OBJECTREF oThrowableFrom)
GC_TRIGGERS;
MODE_COOPERATIVE;
THROWS;
PRECONDITION(oThrowableFrom != NULL);
PRECONDITION(!CLRException::IsPreallocatedExceptionObject(oThrowableFrom));
PRECONDITION(((EXCEPTIONREF)oThrowableFrom)->AreWatsonBucketsPresent());
PRECONDITION(oManagedWatsonBuckets != NULL);
PRECONDITION(IsWatsonEnabled());
}
CONTRACTL_END;

struct
{
OBJECTREF oThrowableFrom;
U1ARRAYREF oSourceWatsonBuckets;
} _gc;

ZeroMemory(&_gc, sizeof(_gc));
GCPROTECT_BEGIN(_gc);
_gc.oThrowableFrom = oThrowableFrom;
_gc.oSourceWatsonBuckets = oManagedWatsonBuckets;

// Copy the watson buckets to the current throwable by NOT passing
// the second argument that will default to NULL.
//
// CopyWatsonBucketsBetweenThrowables will pass that NULL to
// CopyWatsonBucketsToThrowables that will make it copy the buckets
// to the current throwable.
CopyWatsonBucketsBetweenThrowables(_gc.oThrowableFrom);
CopyWatsonBucketsBetweenThrowables(_gc.oSourceWatsonBuckets);

GCPROTECT_END();

Expand All @@ -10108,7 +10117,7 @@ void CopyWatsonBucketsFromThrowableToCurrentThrowable(OBJECTREF oThrowableFrom)
//
// If the destination throwable is NULL, it will result in the buckets
// being copied to the current throwable.
void CopyWatsonBucketsBetweenThrowables(OBJECTREF oThrowableFrom, OBJECTREF oThrowableTo /*=NULL*/)
void CopyWatsonBucketsBetweenThrowables(U1ARRAYREF oManagedWatsonBuckets, OBJECTREF oThrowableTo /*=NULL*/)
{
#ifndef DACCESS_COMPILE

Expand All @@ -10117,9 +10126,7 @@ void CopyWatsonBucketsBetweenThrowables(OBJECTREF oThrowableFrom, OBJECTREF oThr
GC_TRIGGERS;
MODE_COOPERATIVE;
THROWS;
PRECONDITION(oThrowableFrom != NULL);
PRECONDITION(!CLRException::IsPreallocatedExceptionObject(oThrowableFrom));
PRECONDITION(((EXCEPTIONREF)oThrowableFrom)->AreWatsonBucketsPresent());
PRECONDITION(oManagedWatsonBuckets != NULL);
PRECONDITION(IsWatsonEnabled());
}
CONTRACTL_END;
Expand All @@ -10128,15 +10135,15 @@ void CopyWatsonBucketsBetweenThrowables(OBJECTREF oThrowableFrom, OBJECTREF oThr

struct
{
OBJECTREF oFrom;
U1ARRAYREF oSourceWatsonBuckets;
OBJECTREF oTo;
OBJECTREF oWatsonBuckets;
} _gc;

ZeroMemory(&_gc, sizeof(_gc));
GCPROTECT_BEGIN(_gc);

_gc.oFrom = oThrowableFrom;
_gc.oSourceWatsonBuckets = oManagedWatsonBuckets;
_gc.oTo = (oThrowableTo == NULL)?GetThread()->GetThrowable():oThrowableTo;
_ASSERTE(_gc.oTo != NULL);

Expand All @@ -10157,8 +10164,7 @@ void CopyWatsonBucketsBetweenThrowables(OBJECTREF oThrowableFrom, OBJECTREF oThr
else
{
// Get the raw array data pointer of the source array
U1ARRAYREF refSourceWatsonBucketArray = ((EXCEPTIONREF)_gc.oFrom)->GetWatsonBucketReference();
PTR_VOID pRawSourceWatsonBucketArray = dac_cast<PTR_VOID>(refSourceWatsonBucketArray->GetDataPtr());
PTR_VOID pRawSourceWatsonBucketArray = dac_cast<PTR_VOID>(_gc.oSourceWatsonBuckets->GetDataPtr());

// Get the raw array data pointer to the destination array
U1ARRAYREF refDestWatsonBucketArray = (U1ARRAYREF)_gc.oWatsonBuckets;
Expand Down Expand Up @@ -10310,6 +10316,7 @@ void SetStateForWatsonBucketing(BOOL fIsRethrownException, OBJECTHANDLE ohOrigin
{
OBJECTREF oCurrentThrowable;
OBJECTREF oInnerMostExceptionThrowable;
U1ARRAYREF refSourceWatsonBucketArray;
} gc;
ZeroMemory(&gc, sizeof(gc));
GCPROTECT_BEGIN(gc);
Expand Down Expand Up @@ -10484,6 +10491,8 @@ void SetStateForWatsonBucketing(BOOL fIsRethrownException, OBJECTHANDLE ohOrigin
// when we get the IP for bucketing.
if (!fCreateBucketsForExceptionBeingThrown)
{
bool gotWatsonBucketDetails = true;

// Preallocated exception objects do not have inner exception objects.
// Thus, if we are here, then the current throwable cannot be
// a preallocated exception object.
Expand Down Expand Up @@ -10533,15 +10542,16 @@ void SetStateForWatsonBucketing(BOOL fIsRethrownException, OBJECTHANDLE ohOrigin
{
// Assert that the inner exception has the Watson buckets
_ASSERTE(gc.oInnerMostExceptionThrowable != NULL);
_ASSERTE(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() ||
((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent());

if (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent())
gc.refSourceWatsonBucketArray = ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->GetWatsonBucketReference();
UINT_PTR ipForWatsonBuckets = ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->GetIPForWatsonBuckets();

if (gc.refSourceWatsonBucketArray != NULL)
{
// Copy the bucket information from the inner exception object to the current throwable
EX_TRY
{
CopyWatsonBucketsFromThrowableToCurrentThrowable(gc.oInnerMostExceptionThrowable);
CopyWatsonBucketsFromThrowableToCurrentThrowable(gc.refSourceWatsonBucketArray);
}
EX_CATCH
{
Expand All @@ -10550,16 +10560,23 @@ void SetStateForWatsonBucketing(BOOL fIsRethrownException, OBJECTHANDLE ohOrigin
}
EX_END_CATCH(SwallowAllExceptions);
}
else
else if (ipForWatsonBuckets != NULL)
{
// Copy the IP for Watson bucketing to the exception object
((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->GetIPForWatsonBuckets());
((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(ipForWatsonBuckets);
}
else
{
gotWatsonBucketDetails = false;
}
}

// Set the flag that we got bucketing details for the exception
pCurExState->GetFlags()->SetGotWatsonBucketDetails();
LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Using innermost exception details for Watson bucketing for thrown exception.\n"));
if (gotWatsonBucketDetails)
{
// Set the flag that we got bucketing details for the exception
pCurExState->GetFlags()->SetGotWatsonBucketDetails();
LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Using innermost exception details for Watson bucketing for thrown exception.\n"));
}
}
done:;
}
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/exstatecommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,8 @@ class EHWatsonBucketTracker

void SetStateForWatsonBucketing(BOOL fIsRethrownException, OBJECTHANDLE ohOriginalException);
BOOL CopyWatsonBucketsToThrowable(PTR_VOID pUnmanagedBuckets, OBJECTREF oTargetThrowable = NULL);
void CopyWatsonBucketsFromThrowableToCurrentThrowable(OBJECTREF oThrowableFrom);
void CopyWatsonBucketsBetweenThrowables(OBJECTREF oThrowableFrom, OBJECTREF oThrowableTo = NULL);
void CopyWatsonBucketsFromThrowableToCurrentThrowable(U1ARRAYREF oManagedWatsonBuckets);
void CopyWatsonBucketsBetweenThrowables(U1ARRAYREF oManagedWatsonBuckets, OBJECTREF oThrowableTo = NULL);
void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp);
BOOL SetupWatsonBucketsForFailFast(EXCEPTIONREF refException);
void SetupWatsonBucketsForUEF(BOOL fUseLastThrownObject);
Expand Down
8 changes: 4 additions & 4 deletions src/coreclr/vm/vars.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,15 +298,15 @@ class REF : public OBJECTREF

};

// the while (0) syntax below is to force a trailing semicolon on users of the macro
#define VALIDATEOBJECTREF(objref) do {if ((objref) != NULL) (objref).Validate();} while (0)
#define VALIDATEOBJECT(obj) do {if ((obj) != NULL) (obj)->Validate();} while (0)

#define ObjectToOBJECTREF(obj) (OBJECTREF(obj))
#define OBJECTREFToObject(objref) ((objref).operator-> ())
#define ObjectToSTRINGREF(obj) (STRINGREF(obj))
#define STRINGREFToObject(objref) (*( (StringObject**) &(objref) ))

// the while (0) syntax below is to force a trailing semicolon on users of the macro
#define VALIDATEOBJECT(obj) do {if ((obj) != NULL) (obj)->Validate();} while (0)
#define VALIDATEOBJECTREF(objref) do { Object* validateObjectRefObj = OBJECTREFToObject(objref); VALIDATEOBJECT(validateObjectRefObj); } while (0)

#else // _DEBUG_IMPL

#define VALIDATEOBJECTREF(objref)
Expand Down
127 changes: 127 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_45929/test45929.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: License header

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, all of my recent regression tests are missing licence header. I'll fix them.

using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;

namespace test45929
{
public class Program
{
static int Main(string[] _)
{
Console.WriteLine("The test started.");
Console.WriteLine("Progress:");

ThreadPool.GetMinThreads(out int _, out int cptMin);
ThreadPool.SetMinThreads(3500, cptMin);

Test.Run();

Console.WriteLine("Finished successfully");

return 100;
}

class Test
{
readonly TestCore methods;
MethodInfo methodInfo;

public Test()
{
methods = new TestCore();
methodInfo = GetMethod("ExceptionDispatchInfoCaptureThrow");
if (methodInfo is null)
{
throw new InvalidOperationException("The methodInfo object is missing or empty.");
}
}

public static void Run()
{
long progress = 0;
var test = new Test();
const int MaxCount = 1000000;
Parallel.For(
0,
MaxCount,
new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount },
i =>
{
if (Interlocked.Increment(ref progress) % 10000 == 0)
{
Console.WriteLine($"{DateTime.Now} : {progress * 100D / MaxCount:000.0}%");
}
test.Invoke();
});
}

public void Invoke()
{
try
{
methodInfo.Invoke(methods, null);
}
catch
{
// Ignore
}
}

static MethodInfo GetMethod(string methodName)
{
foreach (MethodInfo method in typeof(TestCore).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
if (methodName == method.Name)
{
return method;
}
}
return null;
}

class TestCore
{
const int ExpirySeconds = 1000;

// An exception instance that gets refreshed every ExpirySeconds.
(ExceptionDispatchInfo Exception, DateTime CacheExpiryDateTime) exceptionCache;

/// <summary>
/// Captures and throws the a cached instance of an exception.
/// </summary>
public void ExceptionDispatchInfoCaptureThrow()
{
var error = GetCachedError();
error.Throw();
}

ExceptionDispatchInfo GetCachedError()
{
try
{
var cache = exceptionCache;
if (cache.Exception != null)
{
if (exceptionCache.CacheExpiryDateTime > DateTime.UtcNow)
{
return cache.Exception;
}
}
throw new Exception("Test");
}
catch (Exception ex)
{
ExceptionDispatchInfo edi = ExceptionDispatchInfo.Capture(ex);
exceptionCache = (edi, DateTime.UtcNow.AddSeconds(ExpirySeconds));
return edi;
}
}
}
}
}
}
Loading