summaryrefslogtreecommitdiff
path: root/src/pal/src
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/src')
-rw-r--r--src/pal/src/CMakeLists.txt7
-rw-r--r--src/pal/src/arch/amd64/processor.cpp2
-rw-r--r--src/pal/src/arch/i386/asmconstants.h1
-rw-r--r--src/pal/src/arch/i386/context2.S3
-rw-r--r--src/pal/src/arch/i386/exceptionhelper.S2
-rw-r--r--src/pal/src/arch/i386/signalhandlerhelper.cpp1
-rw-r--r--src/pal/src/config.h.in7
-rw-r--r--src/pal/src/configure.cmake15
-rw-r--r--src/pal/src/exception/seh-unwind.cpp1
-rw-r--r--src/pal/src/file/disk.cpp7
-rw-r--r--src/pal/src/file/file.cpp7
-rw-r--r--src/pal/src/include/pal/dbgmsg.h2
-rw-r--r--src/pal/src/include/pal/numa.h39
-rw-r--r--src/pal/src/include/pal/process.h19
-rw-r--r--src/pal/src/init/pal.cpp14
-rw-r--r--src/pal/src/map/map.cpp59
-rw-r--r--src/pal/src/map/virtual.cpp79
-rw-r--r--src/pal/src/misc/sysinfo.cpp55
-rw-r--r--src/pal/src/numa/numa.cpp692
-rw-r--r--src/pal/src/synchmgr/synchmanager.cpp81
-rw-r--r--src/pal/src/synchmgr/synchmanager.hpp3
-rw-r--r--src/pal/src/synchobj/mutex.cpp2
-rw-r--r--src/pal/src/thread/context.cpp3
-rw-r--r--src/pal/src/thread/process.cpp211
24 files changed, 1188 insertions, 124 deletions
diff --git a/src/pal/src/CMakeLists.txt b/src/pal/src/CMakeLists.txt
index 5314cdf86b..145c2c9ed9 100644
--- a/src/pal/src/CMakeLists.txt
+++ b/src/pal/src/CMakeLists.txt
@@ -181,6 +181,7 @@ set(SOURCES
misc/sysinfo.cpp
misc/time.cpp
misc/utils.cpp
+ numa/numa.cpp
objmgr/palobjbase.cpp
objmgr/shmobject.cpp
objmgr/shmobjectmanager.cpp
@@ -372,6 +373,12 @@ if(CMAKE_SYSTEM_NAME STREQUAL NetBSD)
)
endif(CMAKE_SYSTEM_NAME STREQUAL NetBSD)
+if(HAVE_NUMA_H)
+ target_link_libraries(coreclrpal
+ numa
+ )
+endif(HAVE_NUMA_H)
+
add_subdirectory(examples)
if(FEATURE_EVENT_TRACE)
diff --git a/src/pal/src/arch/amd64/processor.cpp b/src/pal/src/arch/amd64/processor.cpp
index 298d685c98..0ab7992286 100644
--- a/src/pal/src/arch/amd64/processor.cpp
+++ b/src/pal/src/arch/amd64/processor.cpp
@@ -65,7 +65,7 @@ extern "C" unsigned int XmmYmmStateSupport()
"end:\n" \
: "=a"(eax) /* output in eax */ \
: /* no inputs */ \
- : "eax", "ebx", "ecx", "edx" /* registers that are clobbered */
+ : "ebx", "ecx", "edx" /* registers that are clobbered */
);
// Check OS has enabled both XMM and YMM state support
return ((eax & 0x06) == 0x06) ? 1 : 0;
diff --git a/src/pal/src/arch/i386/asmconstants.h b/src/pal/src/arch/i386/asmconstants.h
index d947cb8bcd..ff763ef16b 100644
--- a/src/pal/src/arch/i386/asmconstants.h
+++ b/src/pal/src/arch/i386/asmconstants.h
@@ -28,4 +28,3 @@
#define CONTEXT_Xmm5 CONTEXT_Xmm4+16
#define CONTEXT_Xmm6 CONTEXT_Xmm5+16
#define CONTEXT_Xmm7 CONTEXT_Xmm6+16
-#define CONTEXT_ResumeEsp CONTEXT_ExtendedRegisters+512
diff --git a/src/pal/src/arch/i386/context2.S b/src/pal/src/arch/i386/context2.S
index 6c31b074cc..cf7581da49 100644
--- a/src/pal/src/arch/i386/context2.S
+++ b/src/pal/src/arch/i386/context2.S
@@ -42,7 +42,6 @@ LEAF_ENTRY CONTEXT_CaptureContext, _TEXT
mov [eax + CONTEXT_Ebp], ebp
lea ebx, [esp + 12]
mov [eax + CONTEXT_Esp], ebx
- mov [eax + CONTEXT_ResumeEsp], ebx
mov ebx, [esp + 8]
mov [eax + CONTEXT_Eip], ebx
@@ -115,7 +114,7 @@ LOCAL_LABEL(Done_Restore_CONTEXT_FLOATING_POINT):
LOCAL_LABEL(Done_Restore_CONTEXT_EXTENDED_REGISTERS):
// Restore Stack
- mov esp, [eax + CONTEXT_ResumeEsp]
+ mov esp, [eax + CONTEXT_Esp]
// Create a minimal frame
push DWORD PTR [eax + CONTEXT_Eip]
diff --git a/src/pal/src/arch/i386/exceptionhelper.S b/src/pal/src/arch/i386/exceptionhelper.S
index bf44124479..b9ceffcc13 100644
--- a/src/pal/src/arch/i386/exceptionhelper.S
+++ b/src/pal/src/arch/i386/exceptionhelper.S
@@ -23,7 +23,7 @@ LEAF_ENTRY ThrowExceptionFromContextInternal, _TEXT
mov eax, [esp + 8] // ebx: CONTEXT *
mov ebp, [eax + CONTEXT_Ebp]
- mov esp, [eax + CONTEXT_ResumeEsp]
+ mov esp, [eax + CONTEXT_Esp]
mov ebx, [eax + CONTEXT_Ebx]
mov esi, [eax + CONTEXT_Esi]
mov edi, [eax + CONTEXT_Edi]
diff --git a/src/pal/src/arch/i386/signalhandlerhelper.cpp b/src/pal/src/arch/i386/signalhandlerhelper.cpp
index 3369abe093..a7d418a788 100644
--- a/src/pal/src/arch/i386/signalhandlerhelper.cpp
+++ b/src/pal/src/arch/i386/signalhandlerhelper.cpp
@@ -70,7 +70,6 @@ void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context,
// We don't care about the other registers state since the stack unwinding restores
// them for the target frame directly from the signal context.
context2.Esp = (size_t)sp;
- context2.ResumeEsp = (size_t)sp;
context2.Ebp = (size_t)fp;
context2.Eip = (size_t)signal_handler_worker;
diff --git a/src/pal/src/config.h.in b/src/pal/src/config.h.in
index ab5fa0341d..7f37f42222 100644
--- a/src/pal/src/config.h.in
+++ b/src/pal/src/config.h.in
@@ -20,6 +20,9 @@
#cmakedefine01 HAVE_RUNETYPE_H
#cmakedefine01 HAVE_SYS_SYSCTL_H
#cmakedefine01 HAVE_GNU_LIBNAMES_H
+#cmakedefine01 HAVE_PRCTL_H
+#cmakedefine01 HAVE_NUMA_H
+#cmakedefine01 HAVE_PTHREAD_NP_H
#cmakedefine01 HAVE_KQUEUE
#cmakedefine01 HAVE_GETPWUID_R
@@ -32,6 +35,8 @@
#cmakedefine01 HAVE_PTHREAD_GETATTR_NP
#cmakedefine01 HAVE_PTHREAD_GETCPUCLOCKID
#cmakedefine01 HAVE_PTHREAD_SIGQUEUE
+#cmakedefine01 HAVE_PTHREAD_GETAFFINITY_NP
+#cmakedefine01 HAVE_CPUSET_T
#cmakedefine01 HAVE_SIGRETURN
#cmakedefine01 HAVE__THREAD_SYS_SIGRETURN
#cmakedefine01 HAVE_COPYSIGN
@@ -56,6 +61,7 @@
#cmakedefine01 HAS_SYSV_SEMAPHORES
#cmakedefine01 HAS_PTHREAD_MUTEXES
#cmakedefine01 HAVE_TTRACE
+#cmakedefine01 HAVE_SCHED_GETAFFINITY
#cmakedefine HAVE_UNW_GET_SAVE_LOC
#cmakedefine HAVE_UNW_GET_ACCESSORS
#cmakedefine01 HAVE_XSWDEV
@@ -97,6 +103,7 @@
#cmakedefine01 HAVE_CLOCK_MONOTONIC_COARSE
#cmakedefine01 HAVE_MACH_ABSOLUTE_TIME
#cmakedefine01 HAVE_CLOCK_THREAD_CPUTIME
+#cmakedefine01 HAVE_PTHREAD_CONDATTR_SETCLOCK
#cmakedefine01 STATVFS64_PROTOTYPE_BROKEN
#cmakedefine01 HAVE_MMAP_DEV_ZERO
#cmakedefine01 MMAP_ANON_IGNORES_PROTECTION
diff --git a/src/pal/src/configure.cmake b/src/pal/src/configure.cmake
index 4d78f54423..b5b98d5b2d 100644
--- a/src/pal/src/configure.cmake
+++ b/src/pal/src/configure.cmake
@@ -34,6 +34,9 @@ check_include_files(lwp.h HAVE_LWP_H)
check_include_files(libunwind.h HAVE_LIBUNWIND_H)
check_include_files(runetype.h HAVE_RUNETYPE_H)
check_include_files(semaphore.h HAVE_SEMAPHORE_H)
+check_include_files(sys/prctl.h HAVE_PRCTL_H)
+check_include_files(numa.h HAVE_NUMA_H)
+check_include_files(pthread_np.h HAVE_PTHREAD_NP_H)
if(NOT CMAKE_SYSTEM_NAME STREQUAL FreeBSD AND NOT CMAKE_SYSTEM_NAME STREQUAL NetBSD)
set(CMAKE_REQUIRED_FLAGS "-ldl")
@@ -68,6 +71,7 @@ check_library_exists(${PTHREAD_LIBRARY} pthread_attr_get_np "" HAVE_PTHREAD_ATTR
check_library_exists(${PTHREAD_LIBRARY} pthread_getattr_np "" HAVE_PTHREAD_GETATTR_NP)
check_library_exists(${PTHREAD_LIBRARY} pthread_getcpuclockid "" HAVE_PTHREAD_GETCPUCLOCKID)
check_library_exists(${PTHREAD_LIBRARY} pthread_sigqueue "" HAVE_PTHREAD_SIGQUEUE)
+check_library_exists(${PTHREAD_LIBRARY} pthread_getaffinity_np "" HAVE_PTHREAD_GETAFFINITY_NP)
check_function_exists(sigreturn HAVE_SIGRETURN)
check_function_exists(_thread_sys_sigreturn HAVE__THREAD_SYS_SIGRETURN)
@@ -118,6 +122,14 @@ int main(int argc, char **argv) {
}" HAVE_UNW_GET_ACCESSORS)
set(CMAKE_REQUIRED_LIBRARIES)
+check_cxx_source_compiles("
+#include <pthread_np.h>
+int main(int argc, char **argv) {
+ cpuset_t cpuSet;
+
+ return 0;
+}" HAVE_CPUSET_T)
+
check_struct_has_member ("struct stat" st_atimespec "sys/types.h;sys/stat.h" HAVE_STAT_TIMESPEC)
check_struct_has_member ("struct stat" st_atimensec "sys/types.h;sys/stat.h" HAVE_STAT_NSEC)
check_struct_has_member ("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF)
@@ -405,6 +417,9 @@ int main()
exit(ret);
}" HAVE_CLOCK_MONOTONIC)
+
+check_library_exists(pthread pthread_condattr_setclock "" HAVE_PTHREAD_CONDATTR_SETCLOCK)
+
check_cxx_source_runs("
#include <stdlib.h>
#include <time.h>
diff --git a/src/pal/src/exception/seh-unwind.cpp b/src/pal/src/exception/seh-unwind.cpp
index 1f20ee0cad..ba43c2e725 100644
--- a/src/pal/src/exception/seh-unwind.cpp
+++ b/src/pal/src/exception/seh-unwind.cpp
@@ -155,7 +155,6 @@ static void UnwindContextToWinContext(unw_cursor_t *cursor, CONTEXT *winContext)
#elif defined(_X86_)
unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Eip);
unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Esp);
- unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->ResumeEsp);
unw_get_reg(cursor, UNW_X86_EBP, (unw_word_t *) &winContext->Ebp);
unw_get_reg(cursor, UNW_X86_EBX, (unw_word_t *) &winContext->Ebx);
unw_get_reg(cursor, UNW_X86_ESI, (unw_word_t *) &winContext->Esi);
diff --git a/src/pal/src/file/disk.cpp b/src/pal/src/file/disk.cpp
index 08880c9c20..e5d6f831dd 100644
--- a/src/pal/src/file/disk.cpp
+++ b/src/pal/src/file/disk.cpp
@@ -68,6 +68,7 @@ GetDiskFreeSpaceW(
PathCharString dirNameBufferPathString;
size_t length;
char * dirNameBuffer;
+ const char * dirName;
int size;
PERF_ENTRY(GetDiskFreeSpaceW);
@@ -125,7 +126,7 @@ GetDiskFreeSpaceW(
if ( size != 0 )
{
FILEDosToUnixPathA( dirNameBuffer );
- statfsRetVal = statfs( dirNameBuffer, &fsInfoBuffer );
+ dirName = dirNameBuffer;
}
else
{
@@ -136,9 +137,11 @@ GetDiskFreeSpaceW(
}
else
{
- statfsRetVal = statfs( "/", &fsInfoBuffer );
+ dirName = "/";
}
+ statfsRetVal = statfs( dirName, &fsInfoBuffer );
+
if ( statfsRetVal == 0 )
{
*lpBytesPerSector = fsInfoBuffer.f_bsize;
diff --git a/src/pal/src/file/file.cpp b/src/pal/src/file/file.cpp
index d70e62bd52..a4ad20db32 100644
--- a/src/pal/src/file/file.cpp
+++ b/src/pal/src/file/file.cpp
@@ -2999,8 +2999,11 @@ OUT PLARGE_INTEGER lpFileSize)
&dwFileSizeHigh
);
- lpFileSize->u.LowPart = dwFileSizeLow;
- lpFileSize->u.HighPart = dwFileSizeHigh;
+ if (NO_ERROR == palError)
+ {
+ lpFileSize->u.LowPart = dwFileSizeLow;
+ lpFileSize->u.HighPart = dwFileSizeHigh;
+ }
}
else
{
diff --git a/src/pal/src/include/pal/dbgmsg.h b/src/pal/src/include/pal/dbgmsg.h
index 7a49fc0ad6..052c6fa775 100644
--- a/src/pal/src/include/pal/dbgmsg.h
+++ b/src/pal/src/include/pal/dbgmsg.h
@@ -194,7 +194,7 @@ typedef enum
#ifdef FEATURE_PAL_SXS
DCI_SXS,
#endif // FEATURE_PAL_SXS
-
+ DCI_NUMA,
DCI_LAST
} DBG_CHANNEL_ID;
diff --git a/src/pal/src/include/pal/numa.h b/src/pal/src/include/pal/numa.h
new file mode 100644
index 0000000000..4fb2308a7d
--- /dev/null
+++ b/src/pal/src/include/pal/numa.h
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/numa.h
+
+Abstract:
+
+ Header file for the NUMA functions.
+
+
+
+--*/
+
+#ifndef _PAL_NUMA_H_
+#define _PAL_NUMA_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+BOOL
+NUMASupportInitialize();
+
+VOID
+NUMASupportCleanup();
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_CRITSECT_H_ */
diff --git a/src/pal/src/include/pal/process.h b/src/pal/src/include/pal/process.h
index 990aec5b21..63ef5c52ec 100644
--- a/src/pal/src/include/pal/process.h
+++ b/src/pal/src/include/pal/process.h
@@ -121,6 +121,21 @@ Abstract
VOID PROCProcessUnlock(VOID);
/*++
+Function
+ PROCAbortInitialize()
+
+Abstract
+ Initialize the process abort crash dump program file path and
+ name. Doing all of this ahead of time so nothing is allocated
+ or copied in PROCAbort/signal handler.
+
+Return
+ TRUE - succeeds, FALSE - fails
+
+--*/
+BOOL PROCAbortInitialize();
+
+/*++
Function:
PROCAbort()
@@ -130,7 +145,7 @@ Function:
Does not return
--*/
PAL_NORETURN
-void PROCAbort();
+VOID PROCAbort();
/*++
Function:
@@ -141,7 +156,7 @@ Function:
(no return value)
--*/
-void PROCNotifyProcessShutdown();
+VOID PROCNotifyProcessShutdown();
/*++
Function:
diff --git a/src/pal/src/init/pal.cpp b/src/pal/src/init/pal.cpp
index fa94922325..2fdafe4f8c 100644
--- a/src/pal/src/init/pal.cpp
+++ b/src/pal/src/init/pal.cpp
@@ -42,6 +42,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PAL); // some headers have code with asserts, so do th
#include "pal/debug.h"
#include "pal/locale.h"
#include "pal/init.h"
+#include "pal/numa.h"
#include "pal/stackstring.hpp"
#if HAVE_MACH_EXCEPTIONS
@@ -523,6 +524,12 @@ Initialize(
goto CLEANUP15;
}
+ if (FALSE == NUMASupportInitialize())
+ {
+ ERROR("Unable to initialize NUMA support\n");
+ goto CLEANUP15;
+ }
+
TRACE("First-time PAL initialization complete.\n");
init_count++;
@@ -548,6 +555,7 @@ Initialize(
}
goto done;
+ NUMASupportCleanup();
/* No cleanup required for CRTInitStdStreams */
CLEANUP15:
FILECleanupStdHandles();
@@ -650,6 +658,12 @@ PAL_InitializeCoreCLR(const char *szExePath)
return ERROR_DLL_INIT_FAILED;
}
+ if (!PROCAbortInitialize())
+ {
+ printf("PROCAbortInitialize FAILED %d (%s)\n", errno, strerror(errno));
+ return ERROR_GEN_FAILURE;
+ }
+
if (!InitializeFlushProcessWriteBuffers())
{
return ERROR_GEN_FAILURE;
diff --git a/src/pal/src/map/map.cpp b/src/pal/src/map/map.cpp
index f3ec47b846..5fdb6fda38 100644
--- a/src/pal/src/map/map.cpp
+++ b/src/pal/src/map/map.cpp
@@ -44,6 +44,13 @@ using namespace CorUnix;
SET_DEFAULT_DEBUG_CHANNEL(VIRTUAL);
+#include "pal/utils.h"
+
+// This is temporary until #10981 merges.
+// There will be an equivalent but opposite temporary fix in #10981 which
+// will trigger a merge conflict to be sure both of these workarounds are removed
+#define GetVirtualPageSize() VIRTUAL_PAGE_SIZE
+
//
// The mapping critical section guards access to the list
// of currently mapped views. If a thread needs to access
@@ -2012,14 +2019,14 @@ BOOL MAPGetRegionInfo(LPVOID lpAddress,
real_map_sz = pView->NumberOfBytesToMap;
#endif
- MappedSize = ((real_map_sz-1) & ~VIRTUAL_PAGE_MASK) + VIRTUAL_PAGE_SIZE;
+ MappedSize = ALIGN_UP(real_map_sz, GetVirtualPageSize());
if ( real_map_addr <= lpAddress &&
(VOID *)((UINT_PTR)real_map_addr+MappedSize) > lpAddress )
{
if (lpBuffer)
{
- SIZE_T regionSize = MappedSize + (UINT_PTR) real_map_addr -
- ((UINT_PTR) lpAddress & ~VIRTUAL_PAGE_MASK);
+ SIZE_T regionSize = MappedSize + (UINT_PTR) real_map_addr -
+ ALIGN_DOWN((UINT_PTR)lpAddress, GetVirtualPageSize());
lpBuffer->BaseAddress = lpAddress;
lpBuffer->AllocationProtect = 0;
@@ -2241,7 +2248,9 @@ MAPmmapAndRecord(
PAL_ERROR palError = NO_ERROR;
LPVOID pvBaseAddress = NULL;
- pvBaseAddress = mmap(addr, len, prot, flags, fd, offset);
+ off_t adjust = offset & (GetVirtualPageSize() - 1);
+
+ pvBaseAddress = mmap(static_cast<char *>(addr) - adjust, len + adjust, prot, flags, fd, offset - adjust);
if (MAP_FAILED == pvBaseAddress)
{
ERROR_(LOADER)( "mmap failed with code %d: %s.\n", errno, strerror( errno ) );
@@ -2368,14 +2377,6 @@ void * MAPMapPEFile(HANDLE hFile)
goto done;
}
- //this code requires that the file alignment be the same as the page alignment
- if (ntHeader.OptionalHeader.FileAlignment < VIRTUAL_PAGE_SIZE)
- {
- ERROR_(LOADER)( "Optional header file alignment is bad\n" );
- palError = ERROR_INVALID_PARAMETER;
- goto done;
- }
-
//This doesn't read the entire NT header (the optional header technically has a variable length. But I
//don't need more directories.
@@ -2416,7 +2417,7 @@ void * MAPMapPEFile(HANDLE hFile)
{
//if we're forcing relocs, create an anonymous mapping at the preferred base. Only create the
//mapping if we can create it at the specified address.
- pForceRelocBase = mmap( (void*)preferredBase, VIRTUAL_PAGE_SIZE, PROT_NONE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0 );
+ pForceRelocBase = mmap( (void*)preferredBase, GetVirtualPageSize(), PROT_NONE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0 );
if (pForceRelocBase == MAP_FAILED)
{
TRACE_(LOADER)("Attempt to take preferred base of %p to force relocation failed\n", (void*)preferredBase);
@@ -2445,7 +2446,7 @@ void * MAPMapPEFile(HANDLE hFile)
// First try to reserve virtual memory using ExecutableAllcator. This allows all PE images to be
// near each other and close to the coreclr library which also allows the runtime to generate
// more efficient code (by avoiding usage of jump stubs).
- loadedBase = ReserveMemoryFromExecutableAllocator(pThread, virtualSize);
+ loadedBase = ReserveMemoryFromExecutableAllocator(pThread, ALIGN_UP(virtualSize, GetVirtualPageSize()));
if (loadedBase == NULL)
{
// MAC64 requires we pass MAP_SHARED (or MAP_PRIVATE) flags - otherwise, the call is failed.
@@ -2468,7 +2469,7 @@ void * MAPMapPEFile(HANDLE hFile)
if (forceRelocs)
{
_ASSERTE(((SIZE_T)loadedBase) != preferredBase);
- munmap(pForceRelocBase, VIRTUAL_PAGE_SIZE); // now that we've forced relocation, let the original address mapping go
+ munmap(pForceRelocBase, GetVirtualPageSize()); // now that we've forced relocation, let the original address mapping go
}
if (((SIZE_T)loadedBase) != preferredBase)
{
@@ -2484,7 +2485,7 @@ void * MAPMapPEFile(HANDLE hFile)
//separately.
size_t headerSize;
- headerSize = VIRTUAL_PAGE_SIZE; // if there are lots of sections, this could be wrong
+ headerSize = GetVirtualPageSize(); // if there are lots of sections, this could be wrong
//first, map the PE header to the first page in the image. Get pointers to the section headers
palError = MAPmmapAndRecord(pFileObject, loadedBase,
@@ -2519,10 +2520,8 @@ void * MAPMapPEFile(HANDLE hFile)
goto doneReleaseMappingCriticalSection;
}
- void* prevSectionBase;
- prevSectionBase = loadedBase; // the first "section" for our purposes is the header
- size_t prevSectionSizeInMemory;
- prevSectionSizeInMemory = headerSize;
+ void* prevSectionEnd;
+ prevSectionEnd = (char*)loadedBase + headerSize; // the first "section" for our purposes is the header
for (unsigned i = 0; i < numSections; ++i)
{
//for each section, map the section of the file to the correct virtual offset. Gather the
@@ -2532,12 +2531,13 @@ void * MAPMapPEFile(HANDLE hFile)
IMAGE_SECTION_HEADER &currentHeader = firstSection[i];
void* sectionBase = (char*)loadedBase + currentHeader.VirtualAddress;
+ void* sectionBaseAligned = ALIGN_DOWN(sectionBase, GetVirtualPageSize());
// Validate the section header
if ( (sectionBase < loadedBase) // Did computing the section base overflow?
|| ((char*)sectionBase + currentHeader.SizeOfRawData < (char*)sectionBase) // Does the section overflow?
|| ((char*)sectionBase + currentHeader.SizeOfRawData > (char*)loadedBase + virtualSize) // Does the section extend past the end of the image as the header stated?
- || ((char*)prevSectionBase + prevSectionSizeInMemory > sectionBase) // Does this section overlap the previous one?
+ || (prevSectionEnd > sectionBase) // Does this section overlap the previous one?
)
{
ERROR_(LOADER)( "section %d is corrupt\n", i );
@@ -2552,13 +2552,12 @@ void * MAPMapPEFile(HANDLE hFile)
}
// Is there space between the previous section and this one? If so, add a PROT_NONE mapping to cover it.
- if ((char*)prevSectionBase + prevSectionSizeInMemory < sectionBase)
+ if (prevSectionEnd < sectionBaseAligned)
{
- char* gapBase = (char*)prevSectionBase + prevSectionSizeInMemory;
palError = MAPRecordMapping(pFileObject,
loadedBase,
- (void*)gapBase,
- (char*)sectionBase - gapBase,
+ prevSectionEnd,
+ (char*)sectionBaseAligned - (char*)prevSectionEnd,
PROT_NONE);
if (NO_ERROR != palError)
{
@@ -2602,20 +2601,18 @@ void * MAPMapPEFile(HANDLE hFile)
}
#endif // _DEBUG
- prevSectionBase = sectionBase;
- prevSectionSizeInMemory = (currentHeader.SizeOfRawData + VIRTUAL_PAGE_MASK) & ~VIRTUAL_PAGE_MASK; // round up to page boundary
+ prevSectionEnd = ALIGN_UP((char*)sectionBase + currentHeader.SizeOfRawData, GetVirtualPageSize()); // round up to page boundary
}
// Is there space after the last section and before the end of the mapped image? If so, add a PROT_NONE mapping to cover it.
char* imageEnd;
imageEnd = (char*)loadedBase + virtualSize; // actually, points just after the mapped end
- if ((char*)prevSectionBase + prevSectionSizeInMemory < imageEnd)
+ if (prevSectionEnd < imageEnd)
{
- char* gapBase = (char*)prevSectionBase + prevSectionSizeInMemory;
palError = MAPRecordMapping(pFileObject,
loadedBase,
- (void*)gapBase,
- imageEnd - gapBase,
+ prevSectionEnd,
+ (char*)imageEnd - (char*)prevSectionEnd,
PROT_NONE);
if (NO_ERROR != palError)
{
diff --git a/src/pal/src/map/virtual.cpp b/src/pal/src/map/virtual.cpp
index 4a55de9891..7e00843b7a 100644
--- a/src/pal/src/map/virtual.cpp
+++ b/src/pal/src/map/virtual.cpp
@@ -92,6 +92,7 @@ namespace VirtualMemoryLogging
Commit = 0x30,
Decommit = 0x40,
Release = 0x50,
+ Reset = 0x60,
};
// Indicates that the attempted operation has failed
@@ -810,6 +811,58 @@ static BOOL VIRTUALStoreAllocationInfo(
/******
*
+ * VIRTUALResetMemory() - Helper function that resets the memory
+ *
+ *
+ */
+static LPVOID VIRTUALResetMemory(
+ IN CPalThread *pthrCurrent, /* Currently executing thread */
+ IN LPVOID lpAddress, /* Region to reserve or commit */
+ IN SIZE_T dwSize) /* Size of Region */
+{
+ LPVOID pRetVal = NULL;
+ UINT_PTR StartBoundary;
+ SIZE_T MemSize;
+
+ TRACE( "Resetting the memory now..\n");
+
+ StartBoundary = (UINT_PTR)lpAddress & ~VIRTUAL_PAGE_MASK;
+ // Add the sizes, and round down to the nearest page boundary.
+ MemSize = ( ((UINT_PTR)lpAddress + dwSize + VIRTUAL_PAGE_MASK) & ~VIRTUAL_PAGE_MASK ) -
+ StartBoundary;
+
+ int st;
+#if HAVE_MADV_FREE
+ // Try to use MADV_FREE if supported. It tells the kernel that the application doesn't
+ // need the pages in the range. Freeing the pages can be delayed until a memory pressure
+ // occurs.
+ st = madvise((LPVOID)StartBoundary, MemSize, MADV_FREE);
+ if (st != 0)
+#endif
+ {
+ // In case the MADV_FREE is not supported, use MADV_DONTNEED
+ st = madvise((LPVOID)StartBoundary, MemSize, MADV_DONTNEED);
+ }
+
+ if (st == 0)
+ {
+ pRetVal = lpAddress;
+ }
+
+ LogVaOperation(
+ VirtualMemoryLogging::VirtualOperation::Reset,
+ lpAddress,
+ dwSize,
+ 0,
+ 0,
+ pRetVal,
+ pRetVal != NULL);
+
+ return pRetVal;
+}
+
+/******
+ *
* VIRTUALReserveMemory() - Helper function that actually reserves the memory.
*
* NOTE: I call SetLastError in here, because many different error states
@@ -837,8 +890,6 @@ static LPVOID VIRTUALReserveMemory(
MemSize = ( ((UINT_PTR)lpAddress + dwSize + VIRTUAL_PAGE_MASK) & ~VIRTUAL_PAGE_MASK ) -
StartBoundary;
- InternalEnterCriticalSection(pthrCurrent, &virtual_critsec);
-
// If this is a request for special executable (JIT'ed) memory then, first of all,
// try to get memory from the executable memory allocator to satisfy the request.
if (((flAllocationType & MEM_RESERVE_EXECUTABLE) != 0) && (lpAddress == NULL))
@@ -881,7 +932,6 @@ static LPVOID VIRTUALReserveMemory(
pRetVal,
pRetVal != NULL);
- InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec);
return pRetVal;
}
@@ -1211,7 +1261,7 @@ VirtualAlloc(
}
/* Test for un-supported flags. */
- if ( ( flAllocationType & ~( MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN | MEM_RESERVE_EXECUTABLE ) ) != 0 )
+ if ( ( flAllocationType & ~( MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_TOP_DOWN | MEM_RESERVE_EXECUTABLE ) ) != 0 )
{
ASSERT( "flAllocationType can be one, or any combination of MEM_COMMIT, \
MEM_RESERVE, MEM_TOP_DOWN, or MEM_RESERVE_EXECUTABLE.\n" );
@@ -1240,6 +1290,26 @@ VirtualAlloc(
NULL,
TRUE);
+ if ( flAllocationType & MEM_RESET )
+ {
+ if ( flAllocationType != MEM_RESET )
+ {
+ ASSERT( "MEM_RESET cannot be used with any other allocation flags in flAllocationType.\n" );
+ pthrCurrent->SetLastError( ERROR_INVALID_PARAMETER );
+ goto done;
+ }
+
+ InternalEnterCriticalSection(pthrCurrent, &virtual_critsec);
+ pRetVal = VIRTUALResetMemory( pthrCurrent, lpAddress, dwSize );
+ InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec);
+
+ if ( !pRetVal )
+ {
+ /* Error messages are already displayed, just leave. */
+ goto done;
+ }
+ }
+
if ( flAllocationType & MEM_RESERVE )
{
InternalEnterCriticalSection(pthrCurrent, &virtual_critsec);
@@ -1280,7 +1350,6 @@ done:
return pRetVal;
}
-
/*++
Function:
VirtualFree
diff --git a/src/pal/src/misc/sysinfo.cpp b/src/pal/src/misc/sysinfo.cpp
index fff051818f..70fe3e65d2 100644
--- a/src/pal/src/misc/sysinfo.cpp
+++ b/src/pal/src/misc/sysinfo.cpp
@@ -94,6 +94,39 @@ SET_DEFAULT_DEBUG_CHANNEL(MISC);
#endif
#endif // __APPLE__
+/*++
+Function:
+ GetNumberOfProcessors
+
+Return number of processors available for the current process
+--*/
+int GetNumberOfProcessors()
+{
+ int nrcpus = 0;
+
+#if HAVE_SYSCONF
+ nrcpus = sysconf(_SC_NPROCESSORS_ONLN);
+ if (nrcpus < 1)
+ {
+ ASSERT("sysconf failed for _SC_NPROCESSORS_ONLN (%d)\n", errno);
+ }
+#elif HAVE_SYSCTL
+ int rc;
+ size_t sz;
+ int mib[2];
+
+ sz = sizeof(nrcpus);
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ rc = sysctl(mib, 2, &nrcpus, &sz, NULL, 0);
+ if (rc != 0)
+ {
+ ASSERT("sysctl failed for HW_NCPU (%d)\n", errno);
+ }
+#endif // HAVE_SYSCONF
+
+ return nrcpus;
+}
/*++
Function:
@@ -137,27 +170,7 @@ GetSystemInfo(
lpSystemInfo->dwPageSize = pagesize;
lpSystemInfo->dwActiveProcessorMask_PAL_Undefined = 0;
-#if HAVE_SYSCONF
- nrcpus = sysconf(_SC_NPROCESSORS_ONLN);
- if (nrcpus < 1)
- {
- ASSERT("sysconf failed for _SC_NPROCESSORS_ONLN (%d)\n", errno);
- }
-#elif HAVE_SYSCTL
- int rc;
- size_t sz;
- int mib[2];
-
- sz = sizeof(nrcpus);
- mib[0] = CTL_HW;
- mib[1] = HW_NCPU;
- rc = sysctl(mib, 2, &nrcpus, &sz, NULL, 0);
- if (rc != 0)
- {
- ASSERT("sysctl failed for HW_NCPU (%d)\n", errno);
- }
-#endif // HAVE_SYSCONF
-
+ nrcpus = GetNumberOfProcessors();
TRACE("dwNumberOfProcessors=%d\n", nrcpus);
lpSystemInfo->dwNumberOfProcessors = nrcpus;
diff --git a/src/pal/src/numa/numa.cpp b/src/pal/src/numa/numa.cpp
new file mode 100644
index 0000000000..549c10a71f
--- /dev/null
+++ b/src/pal/src/numa/numa.cpp
@@ -0,0 +1,692 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ numa.cpp
+
+Abstract:
+
+ Implementation of NUMA related APIs
+
+--*/
+
+#include "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(NUMA);
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/numa.h"
+#include "pal/corunix.hpp"
+#include "pal/thread.hpp"
+
+#if HAVE_NUMA_H
+#include <numa.h>
+#include <numaif.h>
+#endif
+
+#if HAVE_PTHREAD_NP_H
+#include <pthread_np.h>
+#endif
+
+#include <pthread.h>
+
+using namespace CorUnix;
+
+#if HAVE_CPUSET_T
+typedef cpuset_t cpu_set_t;
+#endif
+
+int GetNumberOfProcessors();
+
+// CPU affinity descriptor
+struct CpuAffinity
+{
+ // NUMA node
+ BYTE Node;
+ // CPU number relative to the group the CPU is in
+ BYTE Number;
+ // CPU group
+ WORD Group;
+};
+
+// Array mapping global CPU index to its affinity
+CpuAffinity *g_cpuToAffinity = NULL;
+
+// Array mapping CPU group and index in the group to the global CPU index
+short *g_groupAndIndexToCpu = NULL;
+// Array mapping CPU group to the corresponding affinity mask of the CPUs in the group
+KAFFINITY *g_groupToCpuMask = NULL;
+// Array mapping CPU group to the number of processors in the group
+BYTE *g_groupToCpuCount = NULL;
+
+// Total number of processors in the system
+int g_cpuCount = 0;
+// Total number of CPU groups
+int g_groupCount = 0;
+// The highest NUMA node available
+int g_highestNumaNode = 0;
+
+static const int MaxCpusPerGroup = 8 * sizeof(KAFFINITY);
+static const WORD NO_GROUP = 0xffff;
+
+/*++
+Function:
+ AllocateLookupArrays
+
+Allocate CPU and group lookup arrays
+--*/
+VOID
+AllocateLookupArrays()
+{
+ g_groupAndIndexToCpu = (short*)malloc(g_groupCount * MaxCpusPerGroup * sizeof(short));
+ g_cpuToAffinity = (CpuAffinity*)malloc(g_cpuCount * sizeof(CpuAffinity));
+ g_groupToCpuMask = (KAFFINITY*)malloc(g_groupCount * sizeof(KAFFINITY));
+ g_groupToCpuCount = (BYTE*)malloc(g_groupCount * sizeof(BYTE));
+
+ memset(g_groupAndIndexToCpu, 0xff, g_groupCount * MaxCpusPerGroup * sizeof(short));
+ memset(g_cpuToAffinity, 0xff, g_cpuCount * sizeof(CpuAffinity));
+ memset(g_groupToCpuMask, 0, g_groupCount * sizeof(KAFFINITY));
+ memset(g_groupToCpuCount, 0, g_groupCount * sizeof(BYTE));
+}
+
+/*++
+Function:
+ FreeLookupArrays
+
+Free CPU and group lookup arrays
+--*/
+VOID
+FreeLookupArrays()
+{
+ free(g_groupAndIndexToCpu);
+ free(g_cpuToAffinity);
+ free(g_groupToCpuMask);
+ free(g_groupToCpuCount);
+
+ g_groupAndIndexToCpu = NULL;
+ g_cpuToAffinity = NULL;
+ g_groupToCpuMask = NULL;
+ g_groupToCpuCount = NULL;
+}
+
+/*++
+Function:
+ GetFullAffinityMask
+
+Get affinity mask for the specified number of processors with all
+the processors enabled.
+--*/
+KAFFINITY GetFullAffinityMask(int cpuCount)
+{
+ return ((KAFFINITY)1 << (cpuCount)) - 1;
+}
+
+/*++
+Function:
+ NUMASupportInitialize
+
+Initialize data structures for getting and setting thread affinities to processors and
+querying NUMA related processor information.
+On systems with no NUMA support, it behaves as if there was a single NUMA node with
+a single group of processors.
+--*/
+BOOL
+NUMASupportInitialize()
+{
+#if HAVE_NUMA_H
+ if (numa_available() != -1)
+ {
+ struct bitmask *mask = numa_allocate_cpumask();
+ int numaNodesCount = numa_max_node() + 1;
+
+ g_cpuCount = numa_num_possible_cpus();
+ g_groupCount = 0;
+
+ for (int i = 0; i < numaNodesCount; i++)
+ {
+ int st = numa_node_to_cpus(i, mask);
+ // The only failure that can happen is that the mask is not large enough
+ // but that cannot happen since the mask was allocated by numa_allocate_cpumask
+ _ASSERTE(st == 0);
+ unsigned int nodeCpuCount = numa_bitmask_weight(mask);
+ unsigned int nodeGroupCount = (nodeCpuCount + MaxCpusPerGroup - 1) / MaxCpusPerGroup;
+ g_groupCount += nodeGroupCount;
+ }
+
+ AllocateLookupArrays();
+
+ WORD currentGroup = 0;
+ int currentGroupCpus = 0;
+
+ for (int i = 0; i < numaNodesCount; i++)
+ {
+ int st = numa_node_to_cpus(i, mask);
+ // The only failure that can happen is that the mask is not large enough
+ // but that cannot happen since the mask was allocated by numa_allocate_cpumask
+ _ASSERTE(st == 0);
+ unsigned int nodeCpuCount = numa_bitmask_weight(mask);
+ unsigned int nodeGroupCount = (nodeCpuCount + MaxCpusPerGroup - 1) / MaxCpusPerGroup;
+ for (int j = 0; j < g_cpuCount; j++)
+ {
+ if (numa_bitmask_isbitset(mask, j))
+ {
+ if (currentGroupCpus == MaxCpusPerGroup)
+ {
+ g_groupToCpuCount[currentGroup] = MaxCpusPerGroup;
+ g_groupToCpuMask[currentGroup] = GetFullAffinityMask(MaxCpusPerGroup);
+ currentGroupCpus = 0;
+ currentGroup++;
+ }
+ g_cpuToAffinity[j].Node = i;
+ g_cpuToAffinity[j].Group = currentGroup;
+ g_cpuToAffinity[j].Number = currentGroupCpus;
+ g_groupAndIndexToCpu[currentGroup * MaxCpusPerGroup + currentGroupCpus] = j;
+ currentGroupCpus++;
+ }
+ }
+
+ if (currentGroupCpus != 0)
+ {
+ g_groupToCpuCount[currentGroup] = currentGroupCpus;
+ g_groupToCpuMask[currentGroup] = GetFullAffinityMask(currentGroupCpus);
+ currentGroupCpus = 0;
+ currentGroup++;
+ }
+ }
+
+ numa_free_cpumask(mask);
+
+ g_highestNumaNode = numa_max_node();
+ }
+ else
+#endif // HAVE_NUMA_H
+ {
+ // No NUMA
+ g_cpuCount = GetNumberOfProcessors();
+ g_groupCount = 1;
+ g_highestNumaNode = 0;
+
+ AllocateLookupArrays();
+ }
+
+ return TRUE;
+}
+
+/*++
+Function:
+ NUMASupportCleanup
+
+Cleanup of the NUMA support data structures
+--*/
+VOID
+NUMASupportCleanup()
+{
+ FreeLookupArrays();
+}
+
+/*++
+Function:
+ GetNumaHighestNodeNumber
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetNumaHighestNodeNumber(
+ OUT PULONG HighestNodeNumber
+)
+{
+ PERF_ENTRY(GetNumaHighestNodeNumber);
+ ENTRY("GetNumaHighestNodeNumber(HighestNodeNumber=%p)\n", HighestNodeNumber);
+ *HighestNodeNumber = (ULONG)g_highestNumaNode;
+
+ BOOL success = TRUE;
+
+ LOGEXIT("GetNumaHighestNodeNumber returns BOOL %d\n", success);
+ PERF_EXIT(GetNumaHighestNodeNumber);
+
+ return success;
+}
+
+/*++
+Function:
+ GetNumaProcessorNodeEx
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetNumaProcessorNodeEx(
+ IN PPROCESSOR_NUMBER Processor,
+ OUT PUSHORT NodeNumber
+)
+{
+ PERF_ENTRY(GetNumaProcessorNodeEx);
+ ENTRY("GetNumaProcessorNodeEx(Processor=%p, NodeNumber=%p)\n", Processor, NodeNumber);
+
+ BOOL success = FALSE;
+
+ if ((Processor->Group < g_groupCount) &&
+ (Processor->Number < MaxCpusPerGroup) &&
+ (Processor->Reserved == 0))
+ {
+ short cpu = g_groupAndIndexToCpu[Processor->Group * MaxCpusPerGroup + Processor->Number];
+ if (cpu != -1)
+ {
+ *NodeNumber = g_cpuToAffinity[cpu].Node;
+ success = TRUE;
+ }
+ }
+
+ if (!success)
+ {
+ *NodeNumber = 0xffff;
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+
+ LOGEXIT("GetNumaProcessorNodeEx returns BOOL %d\n", success);
+ PERF_EXIT(GetNumaProcessorNodeEx);
+
+ return success;
+}
+
+/*++
+Function:
+ GetLogicalProcessorInformationEx
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetLogicalProcessorInformationEx(
+ IN LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType,
+ OUT OPTIONAL PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
+ IN OUT PDWORD ReturnedLength
+)
+{
+ PERF_ENTRY(GetLogicalProcessorInformationEx);
+ ENTRY("GetLogicalProcessorInformationEx(RelationshipType=%d, Buffer=%p, ReturnedLength=%p)\n", RelationshipType, Buffer, ReturnedLength);
+
+ BOOL success = FALSE;
+
+ if (RelationshipType == RelationGroup)
+ {
+ size_t requiredSize = __builtin_offsetof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, Group);
+ requiredSize += __builtin_offsetof(GROUP_RELATIONSHIP, GroupInfo);
+ requiredSize += g_groupCount * sizeof(PROCESSOR_GROUP_INFO);
+
+ if (*ReturnedLength >= requiredSize)
+ {
+ Buffer->Relationship = RelationGroup;
+ Buffer->Size = requiredSize;
+ Buffer->Group.MaximumGroupCount = g_groupCount;
+ Buffer->Group.ActiveGroupCount = g_groupCount;
+ for (int i = 0; i < g_groupCount; i++)
+ {
+ Buffer->Group.GroupInfo[i].MaximumProcessorCount = MaxCpusPerGroup;
+ Buffer->Group.GroupInfo[i].ActiveProcessorCount = g_groupToCpuCount[i];
+ Buffer->Group.GroupInfo[i].ActiveProcessorMask = g_groupToCpuMask[i];
+ }
+
+ success = TRUE;
+ }
+ else
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ }
+
+ *ReturnedLength = requiredSize;
+ }
+ else
+ {
+ // We only support the group relationship
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+
+ LOGEXIT("GetLogicalProcessorInformationEx returns BOOL %d\n", success);
+ PERF_EXIT(GetLogicalProcessorInformationEx);
+
+ return success;
+}
+
+/*++
+Function:
+ GetThreadGroupAffinityInternal
+
+Get the group affinity for the specified pthread
+--*/
+BOOL
+GetThreadGroupAffinityInternal(
+ IN pthread_t thread,
+ OUT PGROUP_AFFINITY GroupAffinity
+)
+{
+ BOOL success = FALSE;
+
+#if HAVE_PTHREAD_GETAFFINITY_NP
+ cpu_set_t cpuSet;
+
+ int st = pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuSet);
+
+ if (st == 0)
+ {
+ WORD group = NO_GROUP;
+ KAFFINITY mask = 0;
+
+ for (int i = 0; i < g_cpuCount; i++)
+ {
+ if (CPU_ISSET(i, &cpuSet))
+ {
+ WORD g = g_cpuToAffinity[i].Group;
+ // Unless the thread affinity was already set by SetThreadGroupAffinity, it is possible that
+ // the current thread has affinity with processors from multiple groups. So we report just the
+ // first group we find.
+ if (group == NO_GROUP || g == group)
+ {
+ group = g;
+ mask |= ((KAFFINITY)1) << g_cpuToAffinity[i].Number;
+ }
+ }
+ }
+
+ GroupAffinity->Group = group;
+ GroupAffinity->Mask = mask;
+ success = TRUE;
+ }
+ else
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ }
+#else // HAVE_PTHREAD_GETAFFINITY_NP
+ // There is no API to manage thread affinity, so let's return a group affinity
+ // with all the CPUs on the system.
+ GroupAffinity->Group = 0;
+ GroupAffinity->Mask = GetFullAffinityMask(g_cpuCount);
+ success = TRUE;
+#endif // HAVE_PTHREAD_GETAFFINITY_NP
+
+ return success;
+}
+
+/*++
+Function:
+ GetThreadGroupAffinity
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetThreadGroupAffinity(
+ IN HANDLE hThread,
+ OUT PGROUP_AFFINITY GroupAffinity
+)
+{
+ PERF_ENTRY(GetThreadGroupAffinity);
+ ENTRY("GetThreadGroupAffinity(hThread=%p, GroupAffinity=%p)\n", hThread, GroupAffinity);
+
+ CPalThread *palThread = InternalGetCurrentThread();
+
+ BOOL success = GetThreadGroupAffinityInternal(palThread->GetPThreadSelf(), GroupAffinity);
+
+ LOGEXIT("GetThreadGroupAffinity returns BOOL %d\n", success);
+ PERF_EXIT(GetThreadGroupAffinity);
+
+ return success;
+}
+
+
+/*++
+Function:
+ SetThreadGroupAffinity
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+SetThreadGroupAffinity(
+ IN HANDLE hThread,
+ IN const GROUP_AFFINITY *GroupAffinity,
+ OUT OPTIONAL PGROUP_AFFINITY PreviousGroupAffinity
+)
+{
+ PERF_ENTRY(SetThreadGroupAffinity);
+ ENTRY("SetThreadGroupAffinity(hThread=%p, GroupAffinity=%p, PreviousGroupAffinity=%p)\n", hThread, GroupAffinity, PreviousGroupAffinity);
+
+ CPalThread *palThread = InternalGetCurrentThread();
+
+ pthread_t thread = palThread->GetPThreadSelf();
+
+ if (PreviousGroupAffinity != NULL)
+ {
+ GetThreadGroupAffinityInternal(thread, PreviousGroupAffinity);
+ }
+
+#if HAVE_PTHREAD_GETAFFINITY_NP
+ int groupStartIndex = GroupAffinity->Group * MaxCpusPerGroup;
+ KAFFINITY mask = 1;
+ cpu_set_t cpuSet;
+ CPU_ZERO(&cpuSet);
+
+ for (int i = 0; i < MaxCpusPerGroup; i++, mask <<= 1)
+ {
+ if (GroupAffinity->Mask & mask)
+ {
+ int cpu = g_groupAndIndexToCpu[groupStartIndex + i];
+ if (cpu != -1)
+ {
+ CPU_SET(cpu, &cpuSet);
+ }
+ }
+ }
+
+ int st = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuSet);
+
+ if (st == -1)
+ {
+ switch (errno)
+ {
+ case EINVAL:
+ // There is no processor in the mask that is allowed to execute the process
+ SetLastError(ERROR_INVALID_PARAMETER);
+ break;
+ case EPERM:
+ SetLastError(ERROR_ACCESS_DENIED);
+ break;
+ default:
+ SetLastError(ERROR_GEN_FAILURE);
+ break;
+ }
+ }
+
+ BOOL success = (st == 0);
+#else // HAVE_PTHREAD_GETAFFINITY_NP
+ // There is no API to manage thread affinity, so let's ignore the request
+ BOOL success = TRUE;
+#endif // HAVE_PTHREAD_GETAFFINITY_NP
+
+ LOGEXIT("SetThreadGroupAffinity returns BOOL %d\n", success);
+ PERF_EXIT(SetThreadGroupAffinity);
+
+ return success;
+}
+
+/*++
+Function:
+ GetCurrentProcessorNumberEx
+
+See MSDN doc.
+--*/
+VOID
+PALAPI
+GetCurrentProcessorNumberEx(
+ OUT PPROCESSOR_NUMBER ProcNumber
+)
+{
+ PERF_ENTRY(GetCurrentProcessorNumberEx);
+ ENTRY("GetCurrentProcessorNumberEx(ProcNumber=%p\n", ProcNumber);
+
+ DWORD cpu = GetCurrentProcessorNumber();
+ _ASSERTE(cpu < g_cpuCount);
+ ProcNumber->Group = g_cpuToAffinity[cpu].Group;
+ ProcNumber->Number = g_cpuToAffinity[cpu].Number;
+
+ LOGEXIT("GetCurrentProcessorNumberEx\n");
+ PERF_EXIT(GetCurrentProcessorNumberEx);
+}
+
+/*++
+Function:
+ GetProcessAffinityMask
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetProcessAffinityMask(
+ IN HANDLE hProcess,
+ OUT PDWORD_PTR lpProcessAffinityMask,
+ OUT PDWORD_PTR lpSystemAffinityMask
+)
+{
+ PERF_ENTRY(GetProcessAffinityMask);
+ ENTRY("GetProcessAffinityMask(hProcess=%p, lpProcessAffinityMask=%p, lpSystemAffinityMask=%p\n", hProcess, lpProcessAffinityMask, lpSystemAffinityMask);
+
+ BOOL success = FALSE;
+
+ if (hProcess == GetCurrentProcess())
+ {
+ DWORD_PTR systemMask = GetFullAffinityMask(g_cpuCount);
+
+#if HAVE_SCHED_GETAFFINITY
+ int pid = getpid();
+ cpu_set_t cpuSet;
+ int st = sched_getaffinity(pid, sizeof(cpu_set_t), &cpuSet);
+ if (st == 0)
+ {
+ WORD group = NO_GROUP;
+ DWORD_PTR processMask = 0;
+
+ for (int i = 0; i < g_cpuCount; i++)
+ {
+ if (CPU_ISSET(i, &cpuSet))
+ {
+ WORD g = g_cpuToAffinity[i].Group;
+ if (group == NO_GROUP || g == group)
+ {
+ group = g;
+ processMask |= ((DWORD_PTR)1) << g_cpuToAffinity[i].Number;
+ }
+ else
+ {
+ // The process has affinity in more than one group, in such case
+ // the function needs to return zero in both masks.
+ processMask = 0;
+ systemMask = 0;
+ group = NO_GROUP;
+ break;
+ }
+ }
+ }
+
+ success = TRUE;
+
+ *lpProcessAffinityMask = processMask;
+ *lpSystemAffinityMask = systemMask;
+ }
+ else
+ {
+ // We should not get any of the errors that the sched_getaffinity can return since none
+ // of them applies for the current thread, so this is an unexpected kind of failure.
+ SetLastError(ERROR_GEN_FAILURE);
+ }
+#else // HAVE_SCHED_GETAFFINITY
+ // There is no API to manage thread affinity, so let's return both affinity masks
+ // with all the CPUs on the system set.
+ *lpSystemAffinityMask = systemMask;
+ *lpProcessAffinityMask = systemMask;
+
+ success = TRUE;
+#endif // HAVE_SCHED_GETAFFINITY
+ }
+ else
+ {
+ // PAL supports getting affinity mask for the current process only
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+
+ LOGEXIT("GetProcessAffinityMask returns BOOL %d\n", success);
+ PERF_EXIT(GetProcessAffinityMask);
+
+ return success;
+}
+
+/*++
+Function:
+ VirtualAllocExNuma
+
+See MSDN doc.
+--*/
+LPVOID
+PALAPI
+VirtualAllocExNuma(
+ IN HANDLE hProcess,
+ IN OPTIONAL LPVOID lpAddress,
+ IN SIZE_T dwSize,
+ IN DWORD flAllocationType,
+ IN DWORD flProtect,
+ IN DWORD nndPreferred
+)
+{
+ PERF_ENTRY(VirtualAllocExNuma);
+ ENTRY("VirtualAllocExNuma(hProcess=%p, lpAddress=%p, dwSize=%u, flAllocationType=%#x, flProtect=%#x, nndPreferred=%d\n",
+ hProcess, lpAddress, dwSize, flAllocationType, flProtect, nndPreferred);
+
+ LPVOID result = NULL;
+
+ if (hProcess == GetCurrentProcess())
+ {
+ if (nndPreferred <= g_highestNumaNode)
+ {
+ result = VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+#if HAVE_NUMA_H
+ if (result != NULL)
+ {
+ int nodeMaskLength = (g_highestNumaNode + 1 + sizeof(unsigned long) - 1) / sizeof(unsigned long);
+ unsigned long *nodeMask = new unsigned long[nodeMaskLength];
+
+ memset(nodeMask, 0, nodeMaskLength);
+
+ int index = nndPreferred / sizeof(unsigned long);
+ int mask = ((unsigned long)1) << (nndPreferred & (sizeof(unsigned long) - 1));
+ nodeMask[index] = mask;
+
+ int st = mbind(result, dwSize, MPOL_PREFERRED, nodeMask, g_highestNumaNode, 0);
+ free(nodeMask);
+ _ASSERTE(st == 0);
+ // If the mbind fails, we still return the allocated memory since the nndPreferred is just a hint
+ }
+#endif // HAVE_NUMA_H
+ }
+ else
+ {
+ // The specified node number is larger than the maximum available one
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+ }
+ else
+ {
+ // PAL supports allocating from the current process virtual space only
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+
+ LOGEXIT("VirtualAllocExNuma returns %p\n", result);
+ PERF_EXIT(VirtualAllocExNuma);
+
+ return result;
+}
diff --git a/src/pal/src/synchmgr/synchmanager.cpp b/src/pal/src/synchmgr/synchmanager.cpp
index 3aec140474..d836a177bb 100644
--- a/src/pal/src/synchmgr/synchmanager.cpp
+++ b/src/pal/src/synchmgr/synchmanager.cpp
@@ -450,7 +450,7 @@ namespace CorUnix
if (dwTimeout != INFINITE)
{
// Calculate absolute timeout
- palErr = GetAbsoluteTimeout(dwTimeout, &tsAbsTmo);
+ palErr = GetAbsoluteTimeout(dwTimeout, &tsAbsTmo, /*fPreferMonotonicClock*/ TRUE);
if (NO_ERROR != palErr)
{
ERROR("Failed to convert timeout to absolute timeout\n");
@@ -1572,7 +1572,7 @@ namespace CorUnix
ptnwdWorkerThreadNativeData =
&pSynchManager->m_pthrWorker->synchronizationInfo.m_tnwdNativeData;
- palErr = GetAbsoluteTimeout(WorkerThreadTerminationTimeout, &tsAbsTmo);
+ palErr = GetAbsoluteTimeout(WorkerThreadTerminationTimeout, &tsAbsTmo, /*fPreferMonotonicClock*/ TRUE);
if (NO_ERROR != palErr)
{
ERROR("Failed to convert timeout to absolute timeout\n");
@@ -4078,6 +4078,9 @@ namespace CorUnix
int iRet;
const int MaxUnavailableResourceRetries = 10;
int iEagains;
+ pthread_condattr_t attrs;
+ pthread_condattr_t *attrsPtr = nullptr;
+
m_shridWaitAwakened = RawSharedObjectAlloc(sizeof(DWORD),
DefaultSharedPool);
if (NULLSharedID == m_shridWaitAwakened)
@@ -4096,6 +4099,36 @@ namespace CorUnix
VolatileStore<DWORD>(pdwWaitState, TWS_ACTIVE);
m_tsThreadState = TS_STARTING;
+#if HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
+ attrsPtr = &attrs;
+ iRet = pthread_condattr_init(&attrs);
+ if (0 != iRet)
+ {
+ ERROR("Failed to initialize thread synchronization condition attribute "
+ "[error=%d (%s)]\n", iRet, strerror(iRet));
+ if (ENOMEM == iRet)
+ {
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+ goto IPrC_exit;
+ }
+
+ // Ensure that the pthread_cond_timedwait will use CLOCK_MONOTONIC
+ iRet = pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
+ if (0 != iRet)
+ {
+ ERROR("Failed set thread synchronization condition timed wait clock "
+ "[error=%d (%s)]\n", iRet, strerror(iRet));
+ palErr = ERROR_INTERNAL_ERROR;
+ pthread_condattr_destroy(&attrs);
+ goto IPrC_exit;
+ }
+#endif // HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
+
iEagains = 0;
Mutex_retry:
iRet = pthread_mutex_init(&m_tnwdNativeData.mutex, NULL);
@@ -4121,7 +4154,9 @@ namespace CorUnix
iEagains = 0;
Cond_retry:
- iRet = pthread_cond_init(&m_tnwdNativeData.cond, NULL);
+
+ iRet = pthread_cond_init(&m_tnwdNativeData.cond, attrsPtr);
+
if (0 != iRet)
{
ERROR("Failed creating thread synchronization condition "
@@ -4146,6 +4181,10 @@ namespace CorUnix
m_tnwdNativeData.fInitialized = true;
IPrC_exit:
+ if (attrsPtr != nullptr)
+ {
+ pthread_condattr_destroy(attrsPtr);
+ }
if (NO_ERROR != palErr)
{
m_tsThreadState = TS_FAILED;
@@ -4515,27 +4554,37 @@ namespace CorUnix
Converts a relative timeout to an absolute one.
--*/
- PAL_ERROR CPalSynchronizationManager::GetAbsoluteTimeout(DWORD dwTimeout, struct timespec * ptsAbsTmo)
+ PAL_ERROR CPalSynchronizationManager::GetAbsoluteTimeout(DWORD dwTimeout, struct timespec * ptsAbsTmo, BOOL fPreferMonotonicClock)
{
PAL_ERROR palErr = NO_ERROR;
int iRet;
-#if HAVE_WORKING_CLOCK_GETTIME
- // Not every platform implements a (working) clock_gettime
- iRet = clock_gettime(CLOCK_REALTIME, ptsAbsTmo);
-#elif HAVE_WORKING_GETTIMEOFDAY
- // Not every platform implements a (working) gettimeofday
- struct timeval tv;
- iRet = gettimeofday(&tv, NULL);
- if (0 == iRet)
+#if HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
+ if (fPreferMonotonicClock)
{
- ptsAbsTmo->tv_sec = tv.tv_sec;
- ptsAbsTmo->tv_nsec = tv.tv_usec * tccMicroSecondsToNanoSeconds;
+ iRet = clock_gettime(CLOCK_MONOTONIC, ptsAbsTmo);
}
+ else
+ {
+#endif
+#if HAVE_WORKING_CLOCK_GETTIME
+ // Not every platform implements a (working) clock_gettime
+ iRet = clock_gettime(CLOCK_REALTIME, ptsAbsTmo);
+#elif HAVE_WORKING_GETTIMEOFDAY
+ // Not every platform implements a (working) gettimeofday
+ struct timeval tv;
+ iRet = gettimeofday(&tv, NULL);
+ if (0 == iRet)
+ {
+ ptsAbsTmo->tv_sec = tv.tv_sec;
+ ptsAbsTmo->tv_nsec = tv.tv_usec * tccMicroSecondsToNanoSeconds;
+ }
#else
- #error "Don't know how to get hi-res current time on this platform"
+ #error "Don't know how to get hi-res current time on this platform"
#endif // HAVE_WORKING_CLOCK_GETTIME, HAVE_WORKING_GETTIMEOFDAY
-
+#if HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
+ }
+#endif
if (0 == iRet)
{
ptsAbsTmo->tv_sec += dwTimeout / tccSecondsToMillieSeconds;
diff --git a/src/pal/src/synchmgr/synchmanager.hpp b/src/pal/src/synchmgr/synchmanager.hpp
index fdef82e936..883d5b8b61 100644
--- a/src/pal/src/synchmgr/synchmanager.hpp
+++ b/src/pal/src/synchmgr/synchmanager.hpp
@@ -1015,7 +1015,8 @@ namespace CorUnix
static PAL_ERROR GetAbsoluteTimeout(
DWORD dwTimeout,
- struct timespec * ptsAbsTmo);
+ struct timespec * ptsAbsTmo,
+ BOOL fPreferMonotonicClock);
};
}
diff --git a/src/pal/src/synchobj/mutex.cpp b/src/pal/src/synchobj/mutex.cpp
index d929eaa472..692f5e2ade 100644
--- a/src/pal/src/synchobj/mutex.cpp
+++ b/src/pal/src/synchobj/mutex.cpp
@@ -859,7 +859,7 @@ MutexTryAcquireLockResult MutexHelpers::TryAcquireLock(pthread_mutex_t *mutex, D
default:
{
struct timespec timeoutTime;
- PAL_ERROR palError = CPalSynchronizationManager::GetAbsoluteTimeout(timeoutMilliseconds, &timeoutTime);
+ PAL_ERROR palError = CPalSynchronizationManager::GetAbsoluteTimeout(timeoutMilliseconds, &timeoutTime, /*fPreferMonotonicClock*/ FALSE);
_ASSERTE(palError == NO_ERROR);
lockResult = pthread_mutex_timedlock(mutex, &timeoutTime);
break;
diff --git a/src/pal/src/thread/context.cpp b/src/pal/src/thread/context.cpp
index 04a6fe5aaf..cc794d5f69 100644
--- a/src/pal/src/thread/context.cpp
+++ b/src/pal/src/thread/context.cpp
@@ -506,8 +506,6 @@ void CONTEXTFromNativeContext(const native_context_t *native, LPCONTEXT lpContex
// although the pc value retrived from native context might not have set the least bit.
// This becomes especially problematic if the context is on the JIT_WRITEBARRIER.
lpContext->Pc |= 0x1;
-#elif defined(_X86_)
- lpContext->ResumeEsp = MCREG_Esp(native->uc_mcontext);
#endif
}
@@ -978,7 +976,6 @@ CONTEXT_GetThreadContextFromThreadState(
lpContext->Esi = pState->esi;
lpContext->Ebp = pState->ebp;
lpContext->Esp = pState->esp;
- lpContext->ResumeEsp = pState->esp;
lpContext->SegSs = pState->ss;
lpContext->EFlags = pState->eflags;
lpContext->Eip = pState->eip;
diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp
index 050665ce7c..2a93d3c57d 100644
--- a/src/pal/src/thread/process.cpp
+++ b/src/pal/src/thread/process.cpp
@@ -49,12 +49,17 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
+#if HAVE_PRCTL_H
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#endif
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <debugmacrosext.h>
#include <semaphore.h>
#include <stdint.h>
+#include <dlfcn.h>
#ifdef __APPLE__
#include <sys/sysctl.h>
@@ -67,6 +72,8 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d
#include <kvm.h>
#endif
+extern char *g_szCoreCLRPath;
+
using namespace CorUnix;
CObjectType CorUnix::otProcess(
@@ -87,18 +94,6 @@ CObjectType CorUnix::otProcess(
CObjectType::NoOwner
);
-static
-DWORD
-PALAPI
-StartupHelperThread(
- LPVOID p);
-
-static
-BOOL
-GetProcessIdDisambiguationKey(
- IN DWORD processId,
- OUT UINT64 *disambiguationKey);
-
//
// Helper memory page used by the FlushProcessWriteBuffers
//
@@ -153,6 +148,9 @@ DWORD gSID = (DWORD) -1;
// Function to call during PAL/process shutdown/abort
Volatile<PSHUTDOWN_CALLBACK> g_shutdownCallback = nullptr;
+// Crash dump generating program arguments. Initialized in PROCAbortInitialize().
+char* g_argvCreateDump[8] = { nullptr };
+
//
// Key used for associating CPalThread's with the underlying pthread
// (through pthread_setspecific)
@@ -172,22 +170,30 @@ enum FILETYPE
FILE_DIR /*Directory*/
};
+static
+DWORD
+PALAPI
+StartupHelperThread(
+ LPVOID p);
+
+static
+BOOL
+GetProcessIdDisambiguationKey(
+ IN DWORD processId,
+ OUT UINT64 *disambiguationKey);
+
PAL_ERROR
PROCGetProcessStatus(
CPalThread *pThread,
HANDLE hProcess,
PROCESS_STATE *pps,
- DWORD *pdwExitCode
- );
+ DWORD *pdwExitCode);
-static BOOL getFileName(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
- PathCharString& lpFileName);
-static char ** buildArgv(LPCWSTR lpCommandLine, PathCharString& lpAppPath,
- UINT *pnArg, BOOL prependLoader);
+static BOOL getFileName(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, PathCharString& lpFileName);
+static char ** buildArgv(LPCWSTR lpCommandLine, PathCharString& lpAppPath, UINT *pnArg, BOOL prependLoader);
static BOOL getPath(PathCharString& lpFileName, PathCharString& lpPathFileName);
static int checkFileType(LPCSTR lpFileName);
-static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode,
- BOOL bTerminateUnconditionally);
+static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUnconditionally);
ProcessModules *GetProcessModulesFromHandle(IN HANDLE hProcess, OUT LPDWORD lpCount);
ProcessModules *CreateProcessModules(IN DWORD dwProcessId, OUT LPDWORD lpCount);
@@ -1382,7 +1388,7 @@ static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUncon
// (1) it doesn't run atexit handlers
// (2) can invoke CrashReporter or produce a coredump,
// which is appropriate for TerminateProcess calls
- abort();
+ PROCAbort();
}
else
{
@@ -2081,9 +2087,12 @@ GetProcessIdDisambiguationKey(DWORD processId, UINT64 *disambiguationKey)
Builds the transport pipe names from the process id.
--*/
-void
+VOID
PALAPI
-PAL_GetTransportPipeName(char *name, DWORD id, const char *suffix)
+PAL_GetTransportPipeName(
+ OUT char *name,
+ IN DWORD id,
+ IN const char *suffix)
{
UINT64 disambiguationKey = 0;
BOOL ret = GetProcessIdDisambiguationKey(id, &disambiguationKey);
@@ -2829,7 +2838,7 @@ Return
None
--*/
-void
+VOID
DestroyProcessModules(IN ProcessModules *listHead)
{
for (ProcessModules *entry = listHead; entry != NULL; )
@@ -2841,7 +2850,7 @@ DestroyProcessModules(IN ProcessModules *listHead)
}
/*++
-Function:
+Function
PROCNotifyProcessShutdown
Calls the abort handler to do any shutdown cleanup. Call be called
@@ -2850,7 +2859,8 @@ Function:
(no return value)
--*/
__attribute__((destructor))
-void PROCNotifyProcessShutdown()
+VOID
+PROCNotifyProcessShutdown()
{
// Call back into the coreclr to clean up the debugger transport pipes
PSHUTDOWN_CALLBACK callback = InterlockedExchangePointer(&g_shutdownCallback, NULL);
@@ -2861,6 +2871,101 @@ void PROCNotifyProcessShutdown()
}
/*++
+Function
+ PROCAbortInitialize()
+
+Abstract
+ Initialize the process abort crash dump program file path and
+ name. Doing all of this ahead of time so nothing is allocated
+ or copied in PROCAbort/signal handler.
+
+Return
+ TRUE - succeeds, FALSE - fails
+
+--*/
+BOOL
+PROCAbortInitialize()
+{
+ char* enabled = getenv("COMPlus_DbgEnableMiniDump");
+ if (enabled != nullptr && _stricmp(enabled, "1") == 0)
+ {
+ if (g_szCoreCLRPath == nullptr)
+ {
+ return FALSE;
+ }
+ const char* DumpGeneratorName = "createdump";
+ int programLen = strlen(g_szCoreCLRPath) + strlen(DumpGeneratorName) + 1;
+ char* program = (char*)InternalMalloc(programLen);
+ if (program == nullptr)
+ {
+ return FALSE;
+ }
+ if (strcpy_s(program, programLen, g_szCoreCLRPath) != SAFECRT_SUCCESS)
+ {
+ return FALSE;
+ }
+ char *last = strrchr(program, '/');
+ if (last != nullptr)
+ {
+ *(last + 1) = '\0';
+ }
+ else
+ {
+ program[0] = '\0';
+ }
+ if (strcat_s(program, programLen, DumpGeneratorName) != SAFECRT_SUCCESS)
+ {
+ return FALSE;
+ }
+ char* pidarg = (char*)InternalMalloc(128);
+ if (pidarg == nullptr)
+ {
+ return FALSE;
+ }
+ if (sprintf_s(pidarg, 128, "%d", gPID) == -1)
+ {
+ return FALSE;
+ }
+ const char** argv = (const char**)g_argvCreateDump;
+ *argv++ = program;
+
+ char* envvar = getenv("COMPlus_DbgMiniDumpName");
+ if (envvar != nullptr)
+ {
+ *argv++ = "--name";
+ *argv++ = envvar;
+ }
+
+ envvar = getenv("COMPlus_DbgMiniDumpType");
+ if (envvar != nullptr)
+ {
+ if (strcmp(envvar, "1") == 0)
+ {
+ *argv++ = "--normal";
+ }
+ else if (strcmp(envvar, "2") == 0)
+ {
+ *argv++ = "--withheap";
+ }
+ else if (strcmp(envvar, "3") == 0)
+ {
+ *argv++ = "--triage";
+ }
+ }
+
+ envvar = getenv("COMPlus_CreateDumpDiagnostics");
+ if (envvar != nullptr && strcmp(envvar, "1") == 0)
+ {
+ *argv++ = "--diag";
+ }
+
+ *argv++ = pidarg;
+ *argv = nullptr;
+ }
+ return TRUE;
+}
+
+/*++
Function:
PROCAbort()
@@ -2870,10 +2975,51 @@ Function:
Does not return
--*/
PAL_NORETURN
-void
+VOID
PROCAbort()
{
+ // Do any shutdown cleanup before aborting or creating a core dump
PROCNotifyProcessShutdown();
+
+#if HAVE_PRCTL_H
+ // If enabled, launch the create minidump utility and wait until it completes
+ if (g_argvCreateDump[0] != nullptr)
+ {
+ // Fork the core dump child process.
+ pid_t childpid = fork();
+
+ // If error, write an error to trace log and abort
+ if (childpid == -1)
+ {
+ ERROR("PROCAbort: fork() FAILED %d (%s)\n", errno, strerror(errno));
+ }
+ else if (childpid == 0)
+ {
+ // Child process
+ if (execve(g_argvCreateDump[0], g_argvCreateDump, palEnvironment) == -1)
+ {
+ ERROR("PROCAbort: execve FAILED %d (%s)\n", errno, strerror(errno));
+ }
+ }
+ else
+ {
+ // Gives the child process permission to use /proc/<pid>/mem and ptrace
+ if (prctl(PR_SET_PTRACER, childpid, 0, 0, 0) == -1)
+ {
+ ERROR("PROCAbort: prctl() FAILED %d (%s)\n", errno, strerror(errno));
+ }
+ // Parent waits until the child process is done
+ int wstatus;
+ int result = waitpid(childpid, &wstatus, 0);
+ if (result != childpid)
+ {
+ ERROR("PROCAbort: waitpid FAILED result %d wstatus %d errno %d (%s)\n",
+ result, wstatus, errno, strerror(errno));
+ }
+ }
+ }
+#endif // HAVE_PRCTL_H
+ // Abort the process after waiting for the core dump to complete
abort();
}
@@ -2886,7 +3032,8 @@ Abstract
Return
TRUE if it succeeded, FALSE otherwise
--*/
-BOOL InitializeFlushProcessWriteBuffers()
+BOOL
+InitializeFlushProcessWriteBuffers()
{
// Verify that the s_helperPage is really aligned to the VIRTUAL_PAGE_SIZE
_ASSERTE((((SIZE_T)s_helperPage) & (VIRTUAL_PAGE_SIZE - 1)) == 0);
@@ -3273,7 +3420,7 @@ Parameter
pThread: Thread object
--*/
-void
+VOID
CorUnix::PROCAddThread(
CPalThread *pCurrentThread,
CPalThread *pTargetThread
@@ -3306,7 +3453,7 @@ Parameter
(no return value)
--*/
-void
+VOID
CorUnix::PROCRemoveThread(
CPalThread *pCurrentThread,
CPalThread *pTargetThread
@@ -3376,7 +3523,7 @@ Return
--*/
INT
CorUnix::PROCGetNumberOfThreads(
- void)
+ VOID)
{
return g_dwThreadCount;
}
@@ -3479,7 +3626,7 @@ Note:
This function is used in ExitThread and TerminateProcess
--*/
-void
+VOID
CorUnix::TerminateCurrentProcessNoExit(BOOL bTerminateUnconditionally)
{
BOOL locked;