summaryrefslogtreecommitdiff
path: root/src/utilcode/util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/utilcode/util.cpp')
-rw-r--r--src/utilcode/util.cpp113
1 files changed, 80 insertions, 33 deletions
diff --git a/src/utilcode/util.cpp b/src/utilcode/util.cpp
index bcfe74ace7..33722e5297 100644
--- a/src/utilcode/util.cpp
+++ b/src/utilcode/util.cpp
@@ -562,6 +562,17 @@ LPVOID ClrVirtualAllocAligned(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocatio
#endif // !FEATURE_PAL
}
+#ifdef _DEBUG
+static DWORD ShouldInjectFaultInRange()
+{
+ static DWORD fInjectFaultInRange = 99;
+
+ if (fInjectFaultInRange == 99)
+ fInjectFaultInRange = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault) & 0x40);
+ return fInjectFaultInRange;
+}
+#endif
+
// Reserves free memory within the range [pMinAddr..pMaxAddr] using
// ClrVirtualQuery to find free memory and ClrVirtualAlloc to reserve it.
//
@@ -576,17 +587,6 @@ LPVOID ClrVirtualAllocAligned(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocatio
// the range.
//
-#ifdef _DEBUG
-static DWORD ShouldInjectFaultInRange()
-{
- static DWORD fInjectFaultInRange = 99;
-
- if (fInjectFaultInRange == 99)
- fInjectFaultInRange = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault) & 0x40);
- return fInjectFaultInRange;
-}
-#endif
-
BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr,
const BYTE *pMaxAddr,
SIZE_T dwSize,
@@ -601,7 +601,11 @@ BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr,
}
CONTRACTL_END;
- BYTE *pResult = NULL;
+ BYTE *pResult = nullptr; // our return value;
+
+ static unsigned countOfCalls = 0; // We log the number of tims we call this method
+ countOfCalls++; // increment the call counter
+
//
// First lets normalize the pMinAddr and pMaxAddr values
//
@@ -630,55 +634,98 @@ BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr,
return NULL;
}
- // We will do one scan: [pMinAddr .. pMaxAddr]
- // Align to 64k. See docs for VirtualAllocEx and lpAddress and 64k alignment for reasons.
- BYTE *tryAddr = (BYTE *)ALIGN_UP((BYTE *)pMinAddr, VIRTUAL_ALLOC_RESERVE_GRANULARITY);
+ // We will do one scan from [pMinAddr .. pMaxAddr]
+ // First align the tryAddr up to next 64k base address.
+ // See docs for VirtualAllocEx and lpAddress and 64k alignment for reasons.
+ //
+ BYTE * tryAddr = (BYTE *)ALIGN_UP((BYTE *)pMinAddr, VIRTUAL_ALLOC_RESERVE_GRANULARITY);
+ bool virtualQueryFailed = false;
+ bool faultInjected = false;
+ unsigned virtualQueryCount = 0;
// Now scan memory and try to find a free block of the size requested.
while ((tryAddr + dwSize) <= (BYTE *) pMaxAddr)
{
MEMORY_BASIC_INFORMATION mbInfo;
-
+
// Use VirtualQuery to find out if this address is MEM_FREE
//
+ virtualQueryCount++;
if (!ClrVirtualQuery((LPCVOID)tryAddr, &mbInfo, sizeof(mbInfo)))
+ {
+ // Exit and return nullptr if the VirtualQuery call fails.
+ virtualQueryFailed = true;
break;
-
+ }
+
// Is there enough memory free from this start location?
- // The PAL version of VirtualQuery sets RegionSize to 0 for free
- // memory regions, in which case we go just ahead and try
- // VirtualAlloc without checking the size, and see if it succeeds.
- if (mbInfo.State == MEM_FREE &&
- (mbInfo.RegionSize >= (SIZE_T) dwSize || mbInfo.RegionSize == 0))
+ // Note that for most versions of UNIX the mbInfo.RegionSize returned will always be 0
+ if ((mbInfo.State == MEM_FREE) &&
+ (mbInfo.RegionSize >= (SIZE_T) dwSize || mbInfo.RegionSize == 0))
{
// Try reserving the memory using VirtualAlloc now
- pResult = (BYTE*) ClrVirtualAlloc(tryAddr, dwSize, MEM_RESERVE, flProtect);
+ pResult = (BYTE*)ClrVirtualAlloc(tryAddr, dwSize, MEM_RESERVE, flProtect);
- if (pResult != NULL)
+ // Normally this will be successful
+ //
+ if (pResult != nullptr)
{
- return pResult;
+ // return pResult
+ break;
}
-#ifdef _DEBUG
- // pResult == NULL
- else if (ShouldInjectFaultInRange())
+
+#ifdef _DEBUG
+ if (ShouldInjectFaultInRange())
{
- return NULL;
+ // return nullptr (failure)
+ faultInjected = true;
+ break;
}
#endif // _DEBUG
- // We could fail in a race. Just move on to next region and continue trying
+ // On UNIX we can also fail if our request size 'dwSize' is larger than 64K and
+ // and our tryAddr is pointing at a small MEM_FREE region (smaller than 'dwSize')
+ // However we can't distinguish between this and the race case.
+
+ // We might fail in a race. So just move on to next region and continue trying
tryAddr = tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY;
}
else
{
// Try another section of memory
tryAddr = max(tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY,
- (BYTE*) mbInfo.BaseAddress + mbInfo.RegionSize);
+ (BYTE*) mbInfo.BaseAddress + mbInfo.RegionSize);
+ }
+ }
+
+ STRESS_LOG7(LF_JIT, LL_INFO100,
+ "ClrVirtualAllocWithinRange request #%u for %08x bytes in [ %p .. %p ], query count was %u - returned %s: %p\n",
+ countOfCalls, (DWORD)dwSize, pMinAddr, pMaxAddr,
+ virtualQueryCount, (pResult != nullptr) ? "success" : "failure", pResult);
+
+ // If we failed this call the process will typically be terminated
+ // so we log any additional reason for failing this call.
+ //
+ if (pResult == nullptr)
+ {
+ if ((tryAddr + dwSize) > (BYTE *)pMaxAddr)
+ {
+ // Our tryAddr reached pMaxAddr
+ STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: Address space exhausted.\n");
+ }
+
+ if (virtualQueryFailed)
+ {
+ STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: VirtualQuery operation failed.\n");
+ }
+
+ if (faultInjected)
+ {
+ STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: fault injected.\n");
}
}
- // Our tryAddr reached pMaxAddr
- return NULL;
+ return pResult;
}
//******************************************************************************