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
4 changes: 2 additions & 2 deletions src/coreclr/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3842,11 +3842,11 @@ HRESULT ClrDataAccess::GetClrWatsonBucketsWorker(Thread * pThread, GenericModeBl
if (oThrowable != NULL)
{
// Does the throwable have buckets?
if (((EXCEPTIONREF)oThrowable)->AreWatsonBucketsPresent())
U1ARRAYREF refWatsonBucketArray = ((EXCEPTIONREF)oThrowable)->GetWatsonBucketReference();
if (refWatsonBucketArray != NULL)
{
// Get the watson buckets from the throwable for non-preallocated
// exceptions
U1ARRAYREF refWatsonBucketArray = ((EXCEPTIONREF)oThrowable)->GetWatsonBucketReference();
pBuckets = dac_cast<PTR_VOID>(refWatsonBucketArray->GetDataPtr());
}
else
Expand Down
36 changes: 21 additions & 15 deletions src/coreclr/vm/excep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9096,6 +9096,7 @@ void SetupWatsonBucketsForUEF(BOOL fUseLastThrownObject)
struct
{
OBJECTREF oThrowable;
U1ARRAYREF oBuckets;
} gc;
ZeroMemory(&gc, sizeof(gc));
GCPROTECT_BEGIN(gc);
Expand Down Expand Up @@ -9197,9 +9198,10 @@ void SetupWatsonBucketsForUEF(BOOL fUseLastThrownObject)
SetupWatsonBucketsForNonPreallocatedExceptions(gc.oThrowable);
}

if (((EXCEPTIONREF)gc.oThrowable)->AreWatsonBucketsPresent())
gc.oBuckets = ((EXCEPTIONREF)gc.oThrowable)->GetWatsonBucketReference();
if (gc.oBuckets != NULL)
{
pUEWatsonBucketTracker->CopyBucketsFromThrowable(gc.oThrowable);
pUEWatsonBucketTracker->CopyBuckets(gc.oBuckets);
}

if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
Expand Down Expand Up @@ -9519,6 +9521,7 @@ BOOL SetupWatsonBucketsForFailFast(EXCEPTIONREF refException)
{
OBJECTREF refException;
OBJECTREF oInnerMostExceptionThrowable;
U1ARRAYREF oBuckets;
} gc;
ZeroMemory(&gc, sizeof(gc));
GCPROTECT_BEGIN(gc);
Expand Down Expand Up @@ -9669,10 +9672,11 @@ BOOL SetupWatsonBucketsForFailFast(EXCEPTIONREF refException)
}

// If it has the buckets, copy them over to the current Watson bucket tracker
if (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent())
gc.oBuckets = ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->GetWatsonBucketReference();
if (gc.oBuckets != NULL)
{
pUEWatsonBucketTracker->ClearWatsonBucketDetails();
pUEWatsonBucketTracker->CopyBucketsFromThrowable(gc.oInnerMostExceptionThrowable);
pUEWatsonBucketTracker->CopyBuckets(gc.oBuckets);
if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
{
LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Got watson buckets from regular innermost exception.\n"));
Expand Down Expand Up @@ -9711,11 +9715,12 @@ BOOL SetupWatsonBucketsForFailFast(EXCEPTIONREF refException)
SetupWatsonBucketsForNonPreallocatedExceptions(gc.refException);
}

if (((EXCEPTIONREF)gc.refException)->AreWatsonBucketsPresent())
gc.oBuckets = ((EXCEPTIONREF)gc.refException)->GetWatsonBucketReference();
if (gc.oBuckets != NULL)
{
// Copy the buckets to the current watson bucket tracker
pUEWatsonBucketTracker->ClearWatsonBucketDetails();
pUEWatsonBucketTracker->CopyBucketsFromThrowable(gc.refException);
pUEWatsonBucketTracker->CopyBuckets(gc.oBuckets);
if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
{
LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Watson buckets copied from the exception object.\n"));
Expand Down Expand Up @@ -9950,6 +9955,9 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)
EX_TRY
{
CopyWatsonBucketsToThrowable(pUEWatsonBucketTracker->RetrieveWatsonBuckets());

// Technically this assert can fail, as another thread could clear the buckets after
// CopyWatsonBucketsToThrowable but before the assert runs, but it is very unlikely.
_ASSERTE(((EXCEPTIONREF)gc.oCurrentThrowable)->AreWatsonBucketsPresent());
}
EX_CATCH
Expand Down Expand Up @@ -10686,16 +10694,15 @@ void EHWatsonBucketTracker::Init()

// This method copies the bucketing details from the specified throwable
// to the current Watson Bucket tracker.
void EHWatsonBucketTracker::CopyBucketsFromThrowable(OBJECTREF oThrowable)
void EHWatsonBucketTracker::CopyBuckets(U1ARRAYREF oBuckets)
{
#ifndef DACCESS_COMPILE
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(oThrowable != NULL);
PRECONDITION(((EXCEPTIONREF)oThrowable)->AreWatsonBucketsPresent());
PRECONDITION(oBuckets != NULL);
PRECONDITION(IsWatsonEnabled());
}
CONTRACTL_END;
Expand All @@ -10704,16 +10711,16 @@ void EHWatsonBucketTracker::CopyBucketsFromThrowable(OBJECTREF oThrowable)

struct
{
OBJECTREF oFrom;
U1ARRAYREF oFromBuckets;
} _gc;

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

_gc.oFrom = oThrowable;
_gc.oFromBuckets = oBuckets;

LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Copying bucketing details from throwable (%p) to tracker (%p)\n",
OBJECTREFToObject(_gc.oFrom), this));
LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Copying bucketing details from bucket (%p) to tracker (%p)\n",
OBJECTREFToObject(_gc.oFromBuckets), this));

// Watson bucket is a "GenericModeBlock" type. Set up an empty GenericModeBlock
// to hold the bucket parameters.
Expand All @@ -10728,8 +10735,7 @@ void EHWatsonBucketTracker::CopyBucketsFromThrowable(OBJECTREF oThrowable)
else
{
// Get the raw array data pointer
U1ARRAYREF refWatsonBucketArray = ((EXCEPTIONREF)_gc.oFrom)->GetWatsonBucketReference();
PTR_VOID pRawWatsonBucketArray = dac_cast<PTR_VOID>(refWatsonBucketArray->GetDataPtr());
PTR_VOID pRawWatsonBucketArray = dac_cast<PTR_VOID>(_gc.oFromBuckets->GetDataPtr());

// Copy over the details to our new allocation
memcpyNoGCRefs(pgmb, pRawWatsonBucketArray, sizeof(GenericModeBlock));
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/exstatecommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ class EHWatsonBucketTracker
EHWatsonBucketTracker();
void Init();
void CopyEHWatsonBucketTracker(const EHWatsonBucketTracker& srcTracker);
void CopyBucketsFromThrowable(OBJECTREF oThrowable);
void CopyBuckets(U1ARRAYREF oBuckets);
void SaveIpForWatsonBucket(UINT_PTR ip);
UINT_PTR RetrieveWatsonBucketIp();
PTR_VOID RetrieveWatsonBuckets();
Expand Down
35 changes: 35 additions & 0 deletions src/coreclr/vm/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,41 @@ OBJECTREF::OBJECTREF(const OBJECTREF & objref)
}


//-------------------------------------------------------------
// VolatileLoadWithoutBarrier constructor
//-------------------------------------------------------------
OBJECTREF::OBJECTREF(const OBJECTREF *pObjref, tagVolatileLoadWithoutBarrier tag)
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_COOPERATIVE;
STATIC_CONTRACT_FORBID_FAULT;

Object* objrefAsObj = VolatileLoadWithoutBarrier(&pObjref->m_asObj);
VALIDATEOBJECT(objrefAsObj);

// !!! If this assert is fired, there are two possibilities:
// !!! 1. You are doing a type cast, e.g. *(OBJECTREF*)pObj
// !!! Instead, you should use ObjectToOBJECTREF(*(Object**)pObj),
// !!! or ObjectToSTRINGREF(*(StringObject**)pObj)
// !!! 2. There is a real GC hole here.
// !!! Either way you need to fix the code.
_ASSERTE(Thread::IsObjRefValid(pObjref));
if ((objrefAsObj != 0) &&
((IGCHeap*)GCHeapUtilities::GetGCHeap())->IsHeapPointer( (BYTE*)this ))
{
_ASSERTE(!"Write Barrier violation. Must use SetObjectReference() to assign OBJECTREF's into the GC heap!");
}
m_asObj = objrefAsObj;

if (m_asObj != 0) {
ENABLESTRESSHEAP();
}

Thread::ObjectRefNew(this);
}


//-------------------------------------------------------------
// To allow NULL to be used as an OBJECTREF.
//-------------------------------------------------------------
Expand Down
12 changes: 6 additions & 6 deletions src/coreclr/vm/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -2418,7 +2418,7 @@ class ExceptionObject : public Object
OBJECTREF GetInnerException()
{
LIMITED_METHOD_DAC_CONTRACT;
return _innerException;
return VolatileLoadWithoutBarrierOBJECTREF(&_innerException);
}

// Returns the innermost exception object - equivalent of the
Expand All @@ -2431,7 +2431,7 @@ class ExceptionObject : public Object
OBJECTREF oInnerMostException = NULL;
OBJECTREF oCurrent = NULL;

oCurrent = _innerException;
oCurrent = GetInnerException();
while(oCurrent != NULL)
{
oInnerMostException = oCurrent;
Expand Down Expand Up @@ -2469,7 +2469,7 @@ class ExceptionObject : public Object
STRINGREF GetRemoteStackTraceString()
{
LIMITED_METHOD_DAC_CONTRACT;
return _remoteStackTraceString;
return (STRINGREF)VolatileLoadWithoutBarrierOBJECTREF(&_remoteStackTraceString);
}

void SetHelpURL(STRINGREF helpURL)
Expand Down Expand Up @@ -2512,15 +2512,15 @@ class ExceptionObject : public Object
U1ARRAYREF GetWatsonBucketReference()
{
LIMITED_METHOD_CONTRACT;
return _watsonBuckets;
return (U1ARRAYREF)VolatileLoadWithoutBarrierOBJECTREF(&_watsonBuckets);
}

// This method will return a BOOL to indicate if the
// watson buckets are present or not.
BOOL AreWatsonBucketsPresent()
{
LIMITED_METHOD_CONTRACT;
return (_watsonBuckets != NULL)?TRUE:FALSE;
return (GetWatsonBucketReference() != NULL)?TRUE:FALSE;
}

// This method will save the IP to be used for watson bucketing.
Expand All @@ -2545,7 +2545,7 @@ class ExceptionObject : public Object
{
LIMITED_METHOD_CONTRACT;

return _ipForWatsonBuckets;
return VolatileLoadWithoutBarrier(&_ipForWatsonBuckets);
}

// README:
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/vm/vars.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ class OBJECTREF {
};

public:
enum class tagVolatileLoadWithoutBarrier { tag };

//-------------------------------------------------------------
// Default constructor, for non-initializing declarations:
//
Expand All @@ -169,6 +171,12 @@ class OBJECTREF {
//-------------------------------------------------------------
OBJECTREF(const OBJECTREF & objref);

//-------------------------------------------------------------
// Copy constructor, for passing OBJECTREF's as function arguments
// using a volatile without barrier load
//-------------------------------------------------------------
OBJECTREF(const OBJECTREF * pObjref, tagVolatileLoadWithoutBarrier tag);

//-------------------------------------------------------------
// To allow NULL to be used as an OBJECTREF.
//-------------------------------------------------------------
Expand Down Expand Up @@ -302,6 +310,7 @@ class REF : public OBJECTREF
#define OBJECTREFToObject(objref) ((objref).operator-> ())
#define ObjectToSTRINGREF(obj) (STRINGREF(obj))
#define STRINGREFToObject(objref) (*( (StringObject**) &(objref) ))
#define VolatileLoadWithoutBarrierOBJECTREF(pObj) (OBJECTREF(pObj, OBJECTREF::tagVolatileLoadWithoutBarrier::tag))

// 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)
Expand All @@ -316,6 +325,7 @@ class REF : public OBJECTREF
#define OBJECTREFToObject(objref) ((PTR_Object) (objref))
#define ObjectToSTRINGREF(obj) ((PTR_StringObject) (obj))
#define STRINGREFToObject(objref) ((PTR_StringObject) (objref))
#define VolatileLoadWithoutBarrierOBJECTREF(pObj) VolatileLoadWithoutBarrier(pObj)

#endif // _DEBUG_IMPL

Expand Down
39 changes: 31 additions & 8 deletions src/tests/Regressions/coreclr/GitHub_45929/test45929.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Threading;
Expand Down Expand Up @@ -46,18 +47,40 @@ 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 =>
int increment = 100;
bool done = false;
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine($"{DateTime.Now} : {progress * 100D / MaxCount:000.0}% : {stopwatch.ElapsedMilliseconds}");

Action<int> makeProgress = i =>
{
if (Interlocked.Increment(ref progress) % 10000 == 0)
if (done) return;
long newProgress = Interlocked.Increment(ref progress);
if (newProgress % increment == 0)
{
Console.WriteLine($"{DateTime.Now} : {progress * 100D / MaxCount:000.0}%");
int newIncrement = (increment * 3) / 2;
if (newIncrement > 10000)
newIncrement = 10000;
increment = newIncrement;

Console.WriteLine($"{DateTime.Now} : {newProgress * 100D / MaxCount:000.0}% : {stopwatch.ElapsedMilliseconds}");
if (stopwatch.ElapsedMilliseconds > 150000)
{
Console.WriteLine($"Attempting to finish early");
done = true;
}
}
test.Invoke();
});
};

makeProgress(0);

Parallel.For(
1,
MaxCount,
new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount },
makeProgress);
}

public void Invoke()
Expand Down