// No setuid programs under Windows. bool os::have_special_privileges() { returnfalse;
}
// This method is a periodic task to check for misbehaving JNI applications // under CheckJNI, we can add any periodic checks here. // For Windows at the moment does nothing void os::run_periodic_checks(outputStream* st) { return;
}
// previous UnhandledExceptionFilter, if there is one static LPTOP_LEVEL_EXCEPTION_FILTER prev_uef_handler = NULL;
LONG WINAPI Handle_FLT_Exception(struct _EXCEPTION_POINTERS* exceptionInfo);
if (alt_home_dir != NULL) {
strncpy(home_dir, alt_home_dir, MAX_PATH + 1);
home_dir[MAX_PATH] = '\0';
} else {
os::jvm_path(home_dir, sizeof(home_dir)); // Found the full path to jvm.dll. // Now cut the path to <java_home>/jre if we can.
*(strrchr(home_dir, '\\')) = '\0'; // get rid of \jvm.dll
pslash = strrchr(home_dir, '\\'); if (pslash != NULL) {
*pslash = '\0'; // get rid of \{client|server}
pslash = strrchr(home_dir, '\\'); if (pslash != NULL) {
*pslash = '\0'; // get rid of \bin
}
}
}
if (!set_boot_path('\\', ';')) {
vm_exit_during_initialization("Failed setting boot class path.", NULL);
}
}
// library_path #define EXT_DIR "\\lib\\ext" #define BIN_DIR "\\bin" #define PACKAGE_DIR "\\Sun\\Java"
{ // Win32 library search order (See the documentation for LoadLibrary): // // 1. The directory from which application is loaded. // 2. The system wide Java Extensions directory (Java only) // 3. System directory (GetSystemDirectory) // 4. Windows directory (GetWindowsDirectory) // 5. The PATH environment variable // 6. The current directory
#ifndef _WIN64 // set our UnhandledExceptionFilter and save any previous one
prev_uef_handler = SetUnhandledExceptionFilter(Handle_FLT_Exception); #endif
// Done return;
}
void os::breakpoint() {
DebugBreak();
}
// Invoked from the BREAKPOINT Macro extern"C"void breakpoint() {
os::breakpoint();
}
// RtlCaptureStackBackTrace Windows API may not exist prior to Windows XP. // So far, this method is only used by Native Memory Tracking, which is // only supported on Windows XP or later. // int os::get_native_stack(address* stack, int frames, int toSkip) { int captured = RtlCaptureStackBackTrace(toSkip + 1, frames, (PVOID*)stack, NULL); for (int index = captured; index < frames; index ++) {
stack[index] = NULL;
} return captured;
}
// os::current_stack_base() // // Returns the base of the stack, which is the stack's // starting address. This function must be called // while running on the stack of the thread being queried.
// Add up the sizes of all the regions with the same // AllocationBase. while (1) {
VirtualQuery(stack_bottom+stack_size, &minfo, sizeof(minfo)); if (stack_bottom == (address)minfo.AllocationBase) {
stack_size += minfo.RegionSize;
} else { break;
}
} return stack_bottom + stack_size;
}
if (committed_start == NULL) {
assert(committed_size == 0, "Sanity"); returnfalse;
} else {
assert(committed_start >= start_addr && committed_start < top, "Out of range"); // current region may go beyond the limit, trim to the limit
committed_size = MIN2(committed_size, size_t(top - committed_start)); returntrue;
}
}
#ifdef USE_VECTORED_EXCEPTION_HANDLING // Any exception is caught by the Vectored Exception Handler, so VM can // generate error dump when an exception occurred in non-Java thread // (e.g. VM thread).
thread->call_run(); #else // Install a win32 structured exception handler around every thread created // by VM, so VM can generate error dump when an exception occurred in non- // Java thread (e.g. VM thread).
__try {
thread->call_run();
} __except(topLevelExceptionFilter(
(_EXCEPTION_POINTERS*)_exception_info())) { // Nothing to do.
} #endif
// Note: at this point the thread object may already have deleted itself. // Do not dereference it from here on out.
// Thread must not return from exit_process_or_thread(), but if it does, // let it proceed to exit normally return (unsigned)os::win32::exit_process_or_thread(os::win32::EPT_THREAD, res);
}
static OSThread* create_os_thread(Thread* thread, HANDLE thread_handle, int thread_id) { // Allocate the OSThread object
OSThread* osthread = new OSThread(); if (osthread == NULL) return NULL;
// Initialize the JDK library's interrupt event. // This should really be done when OSThread is constructed, // but there is no way for a constructor to report failure to // allocate the event.
HANDLE interrupt_event = CreateEvent(NULL, true, false, NULL); if (interrupt_event == NULL) { delete osthread; return NULL;
}
osthread->set_interrupt_event(interrupt_event);
// Store info on the Win32 thread into the OSThread
osthread->set_thread_handle(thread_handle);
osthread->set_thread_id(thread_id);
if (UseNUMA) { int lgrp_id = os::numa_get_group_id(); if (lgrp_id != -1) {
thread->set_lgrp_id(lgrp_id);
}
}
// Initial thread state is INITIALIZED, not SUSPENDED
osthread->set_state(INITIALIZED);
// Helper function to trace _beginthreadex attributes, // similar to os::Posix::describe_pthread_attr() staticchar* describe_beginthreadex_attributes(char* buf, size_t buflen,
size_t stacksize, unsigned initflag) {
stringStream ss(buf, buflen); if (stacksize == 0) {
ss.print("stacksize: default, ");
} else {
ss.print("stacksize: " SIZE_FORMAT "k, ", stacksize / K);
}
ss.print("flags: "); #define PRINT_FLAG(f) if (initflag & f) ss.print( #f" "); #define ALL(X) \
X(CREATE_SUSPENDED) \
X(STACK_SIZE_PARAM_IS_A_RESERVATION)
ALL(PRINT_FLAG) #undef ALL #undef PRINT_FLAG return buf;
}
// Allocate and initialize a new OSThread bool os::create_thread(Thread* thread, ThreadType thr_type,
size_t stack_size) { unsigned thread_id;
// Allocate the OSThread object
OSThread* osthread = new OSThread(); if (osthread == NULL) { returnfalse;
}
// Initial state is ALLOCATED but not INITIALIZED
osthread->set_state(ALLOCATED);
// Initialize the JDK library's interrupt event. // This should really be done when OSThread is constructed, // but there is no way for a constructor to report failure to // allocate the event.
HANDLE interrupt_event = CreateEvent(NULL, true, false, NULL); if (interrupt_event == NULL) { delete osthread; returnfalse;
}
osthread->set_interrupt_event(interrupt_event); // We don't call set_interrupted(false) as it will trip the assert in there // as we are not operating on the current thread. We don't need to call it // because the initial state is already correct.
thread->set_osthread(osthread);
if (stack_size == 0) { switch (thr_type) { case os::java_thread: // Java threads use ThreadStackSize which default value can be changed with the flag -Xss if (JavaThread::stack_size_at_create() > 0) {
stack_size = JavaThread::stack_size_at_create();
} break; case os::compiler_thread: if (CompilerThreadStackSize > 0) {
stack_size = (size_t)(CompilerThreadStackSize * K); break;
} // else fall through: // use VMThreadStackSize if CompilerThreadStackSize is not defined case os::vm_thread: case os::gc_thread: case os::asynclog_thread: case os::watcher_thread: if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K); break;
}
}
// Create the Win32 thread // // Contrary to what MSDN document says, "stack_size" in _beginthreadex() // does not specify stack size. Instead, it specifies the size of // initially committed space. The stack size is determined by // PE header in the executable. If the committed "stack_size" is larger // than default value in the PE header, the stack is rounded up to the // nearest multiple of 1MB. For example if the launcher has default // stack size of 320k, specifying any size less than 320k does not // affect the actual stack size at all, it only affects the initial // commitment. On the other hand, specifying 'stack_size' larger than // default value may cause significant increase in memory usage, because // not only the stack space will be rounded up to MB, but also the // entire space is committed upfront. // // Finally Windows XP added a new flag 'STACK_SIZE_PARAM_IS_A_RESERVATION' // for CreateThread() that can treat 'stack_size' as stack size. However we // are not supposed to call CreateThread() directly according to MSDN // document because JVM uses C runtime library. The good news is that the // flag appears to work with _beginthredex() as well.
ResourceMark rm; char buf[64]; if (thread_handle != NULL) {
log_info(os, thread)("Thread \"%s\" started (tid: %u, attributes: %s)",
thread->name(), thread_id,
describe_beginthreadex_attributes(buf, sizeof(buf), stack_size, initflag));
} else {
log_warning(os, thread)("Failed to start thread \"%s\" - _beginthreadex failed (%s) for attributes: %s.",
thread->name(), os::errno_name(errno), describe_beginthreadex_attributes(buf, sizeof(buf), stack_size, initflag)); // Log some OS information which might explain why creating the thread failed.
log_info(os, thread)("Number of threads approx. running in the VM: %d", Threads::number_of_threads());
LogStream st(Log(os, thread)::info());
os::print_memory_info(&st);
}
if (thread_handle == NULL) { // Need to clean up stuff we've allocated so far
thread->set_osthread(NULL); delete osthread; returnfalse;
}
// Store info on the Win32 thread into the OSThread
osthread->set_thread_handle(thread_handle);
osthread->set_thread_id(thread_id);
// Thread state now is INITIALIZED, not SUSPENDED
osthread->set_state(INITIALIZED);
// The thread is returned suspended (in state INITIALIZED), and is started higher up in the call chain returntrue;
}
// Free Win32 resources related to the OSThread void os::free_thread(OSThread* osthread) {
assert(osthread != NULL, "osthread not set");
// We are told to free resources of the argument thread, // but we can only really operate on the current thread.
assert(Thread::current()->osthread() == osthread, "os::free_thread but not current thread");
julong os::win32::available_memory() { // Use GlobalMemoryStatusEx() because GlobalMemoryStatus() may return incorrect // value if total memory is larger than 4GB
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
GlobalMemoryStatusEx(&ms);
bool os::has_allocatable_memory_limit(size_t* limit) {
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
GlobalMemoryStatusEx(&ms); #ifdef _LP64
*limit = (size_t)ms.ullAvailVirtual; returntrue; #else // Limit to 1400m because of the 2gb address space wall
*limit = MIN2((size_t)1400*M, (size_t)ms.ullAvailVirtual); returntrue; #endif
}
int os::active_processor_count() { // User has overridden the number of active processors if (ActiveProcessorCount > 0) {
log_trace(os)("active_processor_count: " "active processor count set by user : %d",
ActiveProcessorCount); return ActiveProcessorCount;
}
DWORD_PTR lpProcessAffinityMask = 0;
DWORD_PTR lpSystemAffinityMask = 0; int proc_count = processor_count(); if (proc_count <= sizeof(UINT_PTR) * BitsPerByte &&
GetProcessAffinityMask(GetCurrentProcess(), &lpProcessAffinityMask, &lpSystemAffinityMask)) { // Nof active processors is number of bits in process affinity mask int bitcount = 0; while (lpProcessAffinityMask != 0) {
lpProcessAffinityMask = lpProcessAffinityMask & (lpProcessAffinityMask-1);
bitcount++;
} return bitcount;
} else { return proc_count;
}
}
if (_SetThreadDescription != NULL) { // SetThreadDescription takes a PCWSTR but we have conversion routines that produce // LPWSTR. The only difference is that PCWSTR is a pointer to const WCHAR.
LPWSTR unicode_name;
errno_t err = convert_to_unicode(name, &unicode_name); if (err == ERROR_SUCCESS) {
HANDLE current = GetCurrentThread();
HRESULT hr = _SetThreadDescription(current, unicode_name); if (FAILED(hr)) {
log_debug(os, thread)("set_native_thread_name: SetThreadDescription failed - falling back to debugger method");
FREE_C_HEAP_ARRAY(WCHAR, unicode_name);
} else {
log_trace(os, thread)("set_native_thread_name: SetThreadDescription succeeded - new name: %s", name);
#ifdef ASSERT // For verification purposes in a debug build we read the thread name back and check it.
PWSTR thread_name;
HRESULT hr2 = _GetThreadDescription(current, &thread_name); if (FAILED(hr2)) {
log_debug(os, thread)("set_native_thread_name: GetThreadDescription failed!");
} else { int res = CompareStringW(LOCALE_USER_DEFAULT, 0, // no special comparison rules
unicode_name,
-1, // null-terminated
thread_name,
-1// null-terminated
);
assert(res == CSTR_EQUAL, "Name strings were not the same - set: %ls, but read: %ls", unicode_name, thread_name);
LocalFree(thread_name);
} #endif
FREE_C_HEAP_ARRAY(WCHAR, unicode_name); return;
}
} else {
log_debug(os, thread)("set_native_thread_name: convert_to_unicode failed - falling back to debugger method");
}
}
// See: http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx // // Note that unfortunately this only works if the process // is already attached to a debugger; debugger must observe // the exception below to show the correct name.
// If there is no debugger attached skip raising the exception if (!IsDebuggerPresent()) {
log_debug(os, thread)("set_native_thread_name: no debugger present so unable to set thread name"); return;
}
const DWORD MS_VC_EXCEPTION = 0x406D1388; struct {
DWORD dwType; // must be 0x1000
LPCSTR szName; // pointer to name (in user addr space)
DWORD dwThreadID; // thread ID (-1=caller thread)
DWORD dwFlags; // reserved for future use, must be zero
} info;
// Windows format: // The FILETIME structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601. // Java format: // Java standards require the number of milliseconds since 1/1/1970
// Constant offset - calculated using offset() static jlong _offset = 116444736000000000; // Fake time counter for reproducible results when debugging static jlong fake_time = 0;
#ifdef ASSERT // Just to be safe, recalculate the offset in debug mode static jlong _calculated_offset = 0; staticint _has_calculated_offset = 0;
jlong windows_to_java_time(FILETIME wt) {
jlong a = jlong_from(wt.dwHighDateTime, wt.dwLowDateTime); return (a - offset()) / 10000;
}
// Returns time ticks in (10th of micro seconds)
jlong windows_to_time_ticks(FILETIME wt) {
jlong a = jlong_from(wt.dwHighDateTime, wt.dwLowDateTime); return (a - offset());
}
void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) {
jlong freq = performance_frequency; if (freq < NANOSECS_PER_SEC) { // the performance counter is 64 bits and we will // be multiplying it -- so no wrap in 64 bits
info_ptr->max_value = ALL_64_BITS;
} elseif (freq > NANOSECS_PER_SEC) { // use the max value the counter can reach to // determine the max value which could be returned
julong max_counter = (julong)ALL_64_BITS;
info_ptr->max_value = (jlong)(max_counter / (freq / NANOSECS_PER_SEC));
} else { // the performance counter is 64 bits and we will // be using it directly -- so no wrap in 64 bits
info_ptr->max_value = ALL_64_BITS;
}
// using a counter, so no skipping
info_ptr->may_skip_backward = false;
info_ptr->may_skip_forward = false;
info_ptr->kind = JVMTI_TIMER_ELAPSED; // elapsed not CPU time
}
// Check for abort hook
abort_hook_t abort_hook = Arguments::abort_hook(); if (abort_hook != NULL) {
abort_hook();
}
}
static HANDLE dumpFile = NULL;
// Check if dump file can be created. void os::check_dump_limit(char* buffer, size_t buffsz) { bool status = true; if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && !CreateCoredumpOnCrash) {
jio_snprintf(buffer, buffsz, "CreateCoredumpOnCrash is disabled from command line");
status = false;
}
#ifndef ASSERT if (!os::win32::is_windows_server() && FLAG_IS_DEFAULT(CreateCoredumpOnCrash)) {
jio_snprintf(buffer, buffsz, "Minidumps are not enabled by default on client versions of Windows");
status = false;
} #endif
if (status) { constchar* cwd = get_current_directory(NULL, 0); int pid = current_process_id(); if (cwd != NULL) {
jio_snprintf(buffer, buffsz, "%s\\hs_err_pid%u.mdmp", cwd, pid);
} else {
jio_snprintf(buffer, buffsz, ".\\hs_err_pid%u.mdmp", pid);
}
// Older versions of dbghelp.dll (the one shipped with Win2003 for example) may not support all // the dump types we really want. If first call fails, lets fall back to just use MiniDumpWithFullMemory then. if (!WindowsDbgHelp::miniDumpWriteDump(hProcess, processId, dumpFile, dumpType, pmei, NULL, NULL) &&
!WindowsDbgHelp::miniDumpWriteDump(hProcess, processId, dumpFile, (MINIDUMP_TYPE)MiniDumpWithFullMemory, pmei, NULL, NULL)) {
jio_fprintf(stderr, "Call to MiniDumpWriteDump() failed (Error 0x%x)\n", GetLastError());
}
CloseHandle(dumpFile);
win32::exit_process_or_thread(win32::EPT_PROCESS, 1);
}
// Die immediately, no exit hook, no abort hook, no cleanup. void os::die() {
win32::exit_process_or_thread(win32::EPT_PROCESS_DIE, -1);
}
// Directory routines copied from src/win32/native/java/io/dirent_md.c // * dirent_md.c 1.15 00/02/02 // // The declarations for DIR and struct dirent are in jvm_win32.h.
// Caller must have already run dirname through JVM_NativePath, which removes // duplicate slashes and converts all instances of '/' into '\\'.
// Win32 accepts "\" in its POSIX stat(), but refuses to treat it // as a directory in FindFirstFile(). We detect this case here and // prepend the current drive name. // if (dirname[1] == '\0' && dirname[0] == '\\') {
alt_dirname[0] = _getdrive() + 'A' - 1;
alt_dirname[1] = ':';
alt_dirname[2] = '\\';
alt_dirname[3] = '\0';
dirname = alt_dirname;
}
// This must be hard coded because it's the system's temporary // directory not the java application's temp directory, ala java.io.tmpdir. constchar* os::get_temp_directory() { staticchar path_buf[MAX_PATH]; if (GetTempPath(MAX_PATH, path_buf) > 0) { return path_buf;
} else {
path_buf[0] = '\0'; return path_buf;
}
}
// Needs to be in os specific directory because windows requires another // header file <direct.h> constchar* os::get_current_directory(char *buf, size_t buflen) { int n = static_cast<int>(buflen); if (buflen > INT_MAX) n = INT_MAX; return _getcwd(buf, n);
}
//----------------------------------------------------------- // Helper functions for fatal error handler #ifdef _WIN64 // Helper routine which returns true if address in // within the NTDLL address space. // staticbool _addr_in_ntdll(address addr) {
HMODULE hmod;
MODULEINFO minfo;
hmod = GetModuleHandle("NTDLL.DLL"); if (hmod == NULL) returnfalse; if (!GetModuleInformation(GetCurrentProcess(), hmod,
&minfo, sizeof(MODULEINFO))) { returnfalse;
}
if (base_addr <= pmod->addr &&
top_address > pmod->addr) { // if a buffer is provided, copy path name to the buffer if (pmod->full_path) {
jio_snprintf(pmod->full_path, pmod->buflen, "%s", mod_fname);
}
pmod->base_addr = base_addr; return1;
} return0;
}
bool os::dll_address_to_library_name(address addr, char* buf, int buflen, int* offset) { // buf is not optional, but offset is optional
assert(buf != NULL, "sanity check");
// NOTE: the reason we don't use SymGetModuleInfo() is it doesn't always // return the full path to the DLL file, sometimes it returns path // to the corresponding PDB file (debug info); sometimes it only // returns partial path, which makes life painful.
buf[0] = '\0'; if (offset) *offset = -1; returnfalse;
}
bool os::dll_address_to_function_name(address addr, char *buf, int buflen, int *offset, bool demangle) { // buf is not optional, but offset is optional
assert(buf != NULL, "sanity check");
// save the start and end address of jvm.dll into param[0] and param[1] staticint _locate_jvm_dll(constchar* mod_fname, address base_addr,
address top_address, void * param) { if (!param) return -1;
// Loads .dll/.so and // in case of error it checks if .dll/.so was built for the // same architecture as Hotspot is running on void * os::dll_load(constchar *name, char *ebuf, int ebuflen) {
log_info(os)("attempting shared library load of %s", name);
void * result = LoadLibrary(name); if (result != NULL) {
Events::log_dll_message(NULL, "Loaded shared library %s", name); // Recalculate pdb search path if a DLL was loaded successfully.
SymbolEngine::recalc_search_path();
log_info(os)("shared library load of %s was successful", name); return result;
}
DWORD errcode = GetLastError(); // Read system error message into ebuf // It may or may not be overwritten below (in the for loop and just above)
lasterror(ebuf, (size_t) ebuflen);
ebuf[ebuflen - 1] = '\0';
Events::log_dll_message(NULL, "Loading shared library %s failed, error code %lu", name, errcode);
log_info(os)("shared library load of %s failed, error code %lu", name, errcode);
// Parsing dll below // If we can read dll-info and find that dll was built // for an architecture other than Hotspot is running in // - then print to buffer "DLL was built for a different architecture" // else call os::lasterror to obtain system error message int fd = ::open(name, O_RDONLY | O_BINARY, 0); if (fd < 0) { return NULL;
}
uint32_t signature_offset;
uint16_t lib_arch = 0; bool failed_to_get_lib_arch =
( // Go to position 3c in the dll
(os::seek_to_file_offset(fd, IMAGE_FILE_PTR_TO_SIGNATURE) < 0)
|| // Read location of signature
(sizeof(signature_offset) !=
(::read(fd, (void*)&signature_offset, sizeof(signature_offset))))
|| // Go to COFF File Header in dll // that is located after "signature" (4 bytes long)
(os::seek_to_file_offset(fd,
signature_offset + IMAGE_FILE_SIGNATURE_LENGTH) < 0)
|| // Read field that contains code of architecture // that dll was built for
(sizeof(lib_arch) != (::read(fd, (void*)&lib_arch, sizeof(lib_arch))))
);
staticconst arch_t arch_array[] = {
{IMAGE_FILE_MACHINE_I386, (char*)"IA 32"},
{IMAGE_FILE_MACHINE_AMD64, (char*)"AMD 64"},
{IMAGE_FILE_MACHINE_ARM64, (char*)"ARM 64"}
}; #if (defined _M_ARM64) staticconst uint16_t running_arch = IMAGE_FILE_MACHINE_ARM64; #elif (defined _M_AMD64) staticconst uint16_t running_arch = IMAGE_FILE_MACHINE_AMD64; #elif (defined _M_IX86) staticconst uint16_t running_arch = IMAGE_FILE_MACHINE_I386; #else #error Method os::dll_load requires that one of following \
is defined :_M_AMD64 or _M_IX86 or _M_ARM64 #endif
// Obtain a string for printf operation // lib_arch_str shall contain string what platform this .dll was built for // running_arch_str shall string contain what platform Hotspot was built for char *running_arch_str = NULL, *lib_arch_str = NULL; for (unsignedint i = 0; i < ARRAY_SIZE(arch_array); i++) { if (lib_arch == arch_array[i].arch_code) {
lib_arch_str = arch_array[i].arch_name;
} if (running_arch == arch_array[i].arch_code) {
running_arch_str = arch_array[i].arch_name;
}
}
assert(running_arch_str, "Didn't find running architecture code in arch_array");
// If the architecture is right // but some other error took place - report os::lasterror(...) msg if (lib_arch == running_arch) { return NULL;
}
if (lib_arch_str != NULL) {
::_snprintf(ebuf, ebuflen - 1, "Can't load %s-bit .dll on a %s-bit platform",
lib_arch_str, running_arch_str);
} else { // don't know what architecture this dll was build for
::_snprintf(ebuf, ebuflen - 1, "Can't load this .dll (machine code=0x%x) on a %s-bit platform",
lib_arch, running_arch_str);
}
// number of modules that are currently loaded int num_modules = size_needed / sizeof(HMODULE);
for (int i = 0; i < MIN2(num_modules, MAX_NUM_MODULES); i++) { // Get Full pathname: if (!GetModuleFileNameEx(hProcess, modules[i], filename, sizeof(filename))) {
filename[0] = '\0';
}
void os::get_summary_os_info(char* buf, size_t buflen) {
stringStream sst(buf, buflen);
os::win32::print_windows_version(&sst); // chop off newline character char* nl = strchr(buf, '\n'); if (nl != NULL) *nl = '\0';
}
int os::vsnprintf(char* buf, size_t len, constchar* fmt, va_list args) { // Starting with Visual Studio 2015, vsnprint is C99 compliant.
ALLOW_C_FUNCTION(::vsnprintf, int result = ::vsnprintf(buf, len, fmt, args);) // If an encoding error occurred (result < 0) then it's not clear // whether the buffer is NUL terminated, so ensure it is. if ((result < 0) && (len > 0)) {
buf[len - 1] = '\0';
} return result;
}
staticinline time_t get_mtime(constchar* filename) { struct stat st; int ret = os::stat(filename, &st);
assert(ret == 0, "failed to stat() file '%s': %s", filename, os::strerror(errno)); return st.st_mtime;
}
// Get the full path to \Windows\System32\kernel32.dll and use that for // determining what version of Windows we're running on.
len = MAX_PATH - (UINT)strlen("\\kernel32.dll") - 1;
ret = GetSystemDirectory(kernel32_path, len); if (ret == 0 || ret > len) {
st->print_cr("Call to GetSystemDirectory failed"); return;
}
strncat(kernel32_path, "\\kernel32.dll", MAX_PATH - ret);
DWORD version_size = GetFileVersionInfoSize(kernel32_path, NULL); if (version_size == 0) {
st->print_cr("Call to GetFileVersionInfoSize failed"); return;
}
LPTSTR version_info = (LPTSTR)os::malloc(version_size, mtInternal); if (version_info == NULL) {
st->print_cr("Failed to allocate version_info"); return;
}
if (!GetFileVersionInfo(kernel32_path, NULL, version_size, version_info)) {
os::free(version_info);
st->print_cr("Call to GetFileVersionInfo failed"); return;
}
if (!VerQueryValue(version_info, TEXT("\\"), (LPVOID*)&file_info, &len)) {
os::free(version_info);
st->print_cr("Call to VerQueryValue failed"); return;
}
int major_version = HIWORD(file_info->dwProductVersionMS); int minor_version = LOWORD(file_info->dwProductVersionMS); int build_number = HIWORD(file_info->dwProductVersionLS); int build_minor = LOWORD(file_info->dwProductVersionLS); int os_vers = major_version * 1000 + minor_version;
os::free(version_info);
case10000: if (is_workstation) { if (build_number >= 22000) {
st->print("11");
} else {
st->print("10");
}
} else { // distinguish Windows Server by build number // - 2016 GA 10/2016 build: 14393 // - 2019 GA 11/2018 build: 17763 // - 2022 GA 08/2021 build: 20348 if (build_number > 20347) {
st->print("Server 2022");
} elseif (build_number > 17762) {
st->print("Server 2019");
} else {
st->print("Server 2016");
}
} break;
default: // Unrecognized windows, print out its major and minor versions
st->print("%d.%d", major_version, minor_version); break;
}
// Retrieve SYSTEM_INFO from GetNativeSystemInfo call so that we could // find out whether we are running on 64 bit processor or not
SYSTEM_INFO si;
ZeroMemory(&si, sizeof(SYSTEM_INFO));
GetNativeSystemInfo(&si); if ((si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) ||
(si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64)) {
st->print(" , 64 bit");
}
// Use GlobalMemoryStatusEx() because GlobalMemoryStatus() may return incorrect // value if total memory is larger than 4GB
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms); int r1 = GlobalMemoryStatusEx(&ms);
// on 32bit Total/AvailVirtual are interesting (show us how close we get to 2-4 GB per process borders) #ifdefined(_M_IX86)
st->print(", user-mode portion of virtual address-space " INT64_FORMAT "M ",
(int64_t) ms.ullTotalVirtual >> 20);
st->print("(" INT64_FORMAT "M free)", (int64_t) ms.ullAvailVirtual >> 20); #endif
} else {
st->print(", GlobalMemoryStatusEx did not succeed so we miss some memory values.");
}
// extended memory statistics for a process
PROCESS_MEMORY_COUNTERS_EX pmex;
ZeroMemory(&pmex, sizeof(PROCESS_MEMORY_COUNTERS_EX));
pmex.cb = sizeof(pmex); int r2 = GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*) &pmex, sizeof(pmex));
if (r2 != 0) {
st->print("\ncurrent process WorkingSet (physical memory assigned to process): " INT64_FORMAT "M, ",
(int64_t) pmex.WorkingSetSize >> 20);
st->print("peak: " INT64_FORMAT "M\n", (int64_t) pmex.PeakWorkingSetSize >> 20);
st->print("current process commit charge (\"private bytes\"): " INT64_FORMAT "M, ",
(int64_t) pmex.PrivateUsage >> 20);
st->print("peak: " INT64_FORMAT "M", (int64_t) pmex.PeakPagefileUsage >> 20);
} else {
st->print("\nGetProcessMemoryInfo did not succeed so we miss some memory values.");
}
st->cr();
}
bool os::signal_sent_by_kill(constvoid* siginfo) { // TODO: Is this possible? returnfalse;
}
// Find the full path to the current module, jvm.dll void os::jvm_path(char *buf, jint buflen) { // Error checking. if (buflen < MAX_PATH) {
assert(false, "must use a large-enough buffer");
buf[0] = '\0'; return;
} // Lazy resolve the path to current module. if (saved_jvm_path[0] != 0) {
strcpy(buf, saved_jvm_path); return;
}
buf[0] = '\0'; if (Arguments::sun_java_launcher_is_altjvm()) { // Support for the java launcher's '-XXaltjvm=<path>' option. Check // for a JAVA_HOME environment variable and fix up the path so it // looks like jvm.dll is installed there (append a fake suffix // hotspot/jvm.dll). char* java_home_var = ::getenv("JAVA_HOME"); if (java_home_var != NULL && java_home_var[0] != 0 &&
strlen(java_home_var) < (size_t)buflen) {
strncpy(buf, java_home_var, buflen);
// determine if this is a legacy image or modules image // modules image doesn't have "jre" subdirectory
size_t len = strlen(buf); char* jrebin_p = buf + len;
jio_snprintf(jrebin_p, buflen-len, "\\jre\\bin\\"); if (0 != _access(buf, 0)) {
jio_snprintf(jrebin_p, buflen-len, "\\bin\\");
}
len = strlen(buf);
jio_snprintf(buf + len, buflen-len, "hotspot\\jvm.dll");
}
}
if ((errval = GetLastError()) != 0) { // DOS error
size_t n = (size_t)FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errval, 0,
buf,
(DWORD)len,
NULL); if (n > 3) { // Drop final '.', CR, LF if (buf[n - 1] == '\n') n--; if (buf[n - 1] == '\r') n--; if (buf[n - 1] == '.') n--;
buf[n] = '\0';
} return n;
}
if (errno != 0) { // C runtime error that has no corresponding DOS error code constchar* s = os::strerror(errno);
size_t n = strlen(s); if (n >= len) n = len - 1;
strncpy(buf, s, n);
buf[n] = '\0'; return n;
}
return0;
}
int os::get_last_error() {
DWORD error = GetLastError(); if (error == 0) {
error = errno;
} return (int)error;
}
// sun.misc.Signal // NOTE that this is a workaround for an apparent kernel bug where if // a signal handler for SIGBREAK is installed then that signal handler // takes priority over the console control handler for CTRL_CLOSE_EVENT. // See bug 4416763. static signal_handler_t sigbreakHandler = NULL;
staticvoid UserHandler(int sig) {
os::signal_notify(sig); // We need to reinstate the signal handler each time...
os::win32::install_signal_handler(sig, UserHandler);
}
// Used mainly by JVM_RegisterSignal to install a signal handler, // but also to install the VM's BREAK_HANDLER. However, due to // the way Windows signals work we also have to reinstall each // handler at the end of its own execution. // The allowed set of signals is restricted by the caller. // The incoming handler is one of: // - psuedo-handler: SIG_IGN or SIG_DFL // - VM defined signal handling function of type signal_handler_t // - unknown signal handling function which we expect* is also // of type signal_handler_t // // * win32 defines a two-arg signal handling function for use solely with // SIGFPE. As we don't allow that to be set via the Java API we know we // only have the single arg version. // Returns the currently installed handler. void* os::win32::install_signal_handler(int sig, signal_handler_t handler) { if ((sig == SIGBREAK) && (!ReduceSignalUsage)) { void* oldHandler = CAST_FROM_FN_PTR(void*, sigbreakHandler);
sigbreakHandler = handler; return oldHandler;
} else { return ::signal(sig, handler);
}
}
// The Win32 C runtime library maps all console control events other than ^C // into SIGBREAK, which makes it impossible to distinguish ^BREAK from close, // logoff, and shutdown events. We therefore install our own console handler // that raises SIGTERM for the latter cases. // staticBOOL WINAPI consoleHandler(DWORD event) { switch (event) { case CTRL_C_EVENT: if (VMError::is_error_reported()) { // Ctrl-C is pressed during error reporting, likely because the error // handler fails to abort. Let VM die immediately.
os::die();
}
::raise(SIGINT); returnTRUE; break; case CTRL_BREAK_EVENT: if (sigbreakHandler != NULL) {
(*sigbreakHandler)(SIGBREAK);
} returnTRUE; break; case CTRL_LOGOFF_EVENT: { // Don't terminate JVM if it is running in a non-interactive session, // such as a service process.
USEROBJECTFLAGS flags;
HANDLE handle = GetProcessWindowStation(); if (handle != NULL &&
GetUserObjectInformation(handle, UOI_FLAGS, &flags, sizeof(USEROBJECTFLAGS), NULL)) { // If it is a non-interactive session, let next handler to deal // with it. if ((flags.dwFlags & WSF_VISIBLE) == 0) { returnFALSE;
}
}
} case CTRL_CLOSE_EVENT: case CTRL_SHUTDOWN_EVENT:
::raise(SIGTERM); returnTRUE; break; default: break;
} returnFALSE;
}
// The following code is moved from os.cpp for making this // code platform specific, which it is by its very nature.
// Return maximum OS signal used + 1 for internal use only // Used as exit signal for signal_thread int os::sigexitnum_pd() { return NSIG;
}
// a counter for each possible signal value, including signal_thread exit signal staticvolatile jint pending_signals[NSIG+1] = { 0 }; static Semaphore* sig_sem = NULL;
staticvoid jdk_misc_signal_init() { // Initialize signal structures
memset((void*)pending_signals, 0, sizeof(pending_signals));
// Initialize signal semaphore
sig_sem = new Semaphore();
// Programs embedding the VM do not want it to attempt to receive // events like CTRL_LOGOFF_EVENT, which are used to implement the // shutdown hooks mechanism introduced in 1.3. For example, when // the VM is run as part of a Windows NT service (i.e., a servlet // engine in a web server), the correct behavior is for any console // control handler to return FALSE, not TRUE, because the OS's // "final" handler for such events allows the process to continue if // it is a service (while terminating it if it is not a service). // To make this behavior uniform and the mechanism simpler, we // completely disable the VM's usage of these console events if -Xrs // (=ReduceSignalUsage) is specified. This means, for example, that // the CTRL-BREAK thread dump mechanism is also disabled in this // case. See bugs 4323062, 4345157, and related bugs.
// Add a CTRL-C handler
SetConsoleCtrlHandler(consoleHandler, TRUE);
// Initialize sigbreakHandler. // The actual work for handling CTRL-BREAK is performed by the Signal // Dispatcher thread, which is created and started at a much later point, // see os::initialize_jdk_signal_support(). Any CTRL-BREAK received // before the Signal Dispatcher thread is started is queued up via the // pending_signals[SIGBREAK] counter, and will be processed by the // Signal Dispatcher thread in a delayed fashion.
os::win32::install_signal_handler(SIGBREAK, UserHandler);
}
void os::signal_notify(int sig) { if (sig_sem != NULL) {
Atomic::inc(&pending_signals[sig]);
sig_sem->signal();
} else { // Signal thread is not created with ReduceSignalUsage and jdk_misc_signal_init // initialization isn't called.
assert(ReduceSignalUsage, "signal semaphore should be created");
}
}
staticint check_pending_signals() { while (true) { for (int i = 0; i < NSIG + 1; i++) {
jint n = pending_signals[i]; if (n > 0 && n == Atomic::cmpxchg(&pending_signals[i], n, n - 1)) { return i;
}
}
sig_sem->wait_with_safepoint_check(JavaThread::current());
}
ShouldNotReachHere(); return0; // Satisfy compiler
}
int os::signal_wait() { return check_pending_signals();
}
// Save pc in thread if (thread != nullptr && thread->is_Java_thread()) {
JavaThread::cast(thread)->set_saved_exception_pc((address)(DWORD_PTR)exceptionInfo->ContextRecord->PC_NAME);
}
// Set pc to handler
exceptionInfo->ContextRecord->PC_NAME = (DWORD64)handler;
// Continue the execution return EXCEPTION_CONTINUE_EXECUTION;
}
// Used for PostMortemDump extern"C"void safepoints(); extern"C"void find(int x); extern"C"void events();
// According to Windows API documentation, an illegal instruction sequence should generate // the 0xC000001C exception code. However, real world experience shows that occasionnaly // the execution of an illegal instruction can generate the exception code 0xC000001E. This // seems to be an undocumented feature of Win NT 4.0 (and probably other Windows systems).
// From "Execution Protection in the Windows Operating System" draft 0.35 // Once a system header becomes available, the "real" define should be // included or copied here. #define EXCEPTION_INFO_EXEC_VIOLATION 0x08
// Windows Vista/2008 heap corruption check #define EXCEPTION_HEAP_CORRUPTION 0xC0000374
// All Visual C++ exceptions thrown from code generated by the Microsoft Visual // C++ compiler contain this error code. Because this is a compiler-generated // error, the code is not listed in the Win32 API header files. // The code is actually a cryptic mnemonic device, with the initial "E" // standing for "exception" and the final 3 bytes (0x6D7363) representing the // ASCII values of "msc".
constchar* os::exception_name(int exception_code, char *buf, size_t size) {
uint code = static_cast<uint>(exception_code); for (uint i = 0; i < ARRAY_SIZE(exceptlabels); ++i) { if (exceptlabels[i].number == code) {
jio_snprintf(buf, size, "%s", exceptlabels[i].name); return buf;
}
}
return NULL;
}
//----------------------------------------------------------------------------- LONG Handle_IDiv_Exception(struct _EXCEPTION_POINTERS* exceptionInfo) { // handle exception caused by idiv; should only happen for -MinInt/-1 // (division by zero is handled explicitly) #ifdefined(_M_ARM64)
PCONTEXT ctx = exceptionInfo->ContextRecord;
address pc = (address)ctx->Sp;
assert(pc[0] == 0x83, "not an sdiv opcode"); //Fixme did i get the right opcode?
assert(ctx->X4 == min_jint, "unexpected idiv exception"); // set correct result values and continue after idiv instruction
ctx->Pc = (uint64_t)pc + 4; // idiv reg, reg, reg is 4 bytes
ctx->X4 = (uint64_t)min_jint; // result
ctx->X5 = (uint64_t)0; // remainder // Continue the execution #elifdefined(_M_AMD64)
PCONTEXT ctx = exceptionInfo->ContextRecord;
address pc = (address)ctx->Rip;
assert(pc[0] >= Assembler::REX && pc[0] <= Assembler::REX_WRXB && pc[1] == 0xF7 || pc[0] == 0xF7, "not an idiv opcode");
assert(pc[0] >= Assembler::REX && pc[0] <= Assembler::REX_WRXB && (pc[2] & ~0x7) == 0xF8 || (pc[1] & ~0x7) == 0xF8, "cannot handle non-register operands"); if (pc[0] == 0xF7) { // set correct result values and continue after idiv instruction
ctx->Rip = (DWORD64)pc + 2; // idiv reg, reg is 2 bytes
} else {
ctx->Rip = (DWORD64)pc + 3; // REX idiv reg, reg is 3 bytes
} // Do not set ctx->Rax as it already contains the correct value (either 32 or 64 bit, depending on the operation) // this is the case because the exception only happens for -MinValue/-1 and -MinValue is always in rax because of the // idiv opcode (0xF7).
ctx->Rdx = (DWORD)0; // remainder // Continue the execution #else
PCONTEXT ctx = exceptionInfo->ContextRecord;
address pc = (address)ctx->Eip;
assert(pc[0] == 0xF7, "not an idiv opcode");
assert((pc[1] & ~0x7) == 0xF8, "cannot handle non-register operands");
assert(ctx->Eax == min_jint, "unexpected idiv exception"); // set correct result values and continue after idiv instruction
ctx->Eip = (DWORD)pc + 2; // idiv reg, reg is 2 bytes
ctx->Eax = (DWORD)min_jint; // result
ctx->Edx = (DWORD)0; // remainder // Continue the execution #endif return EXCEPTION_CONTINUE_EXECUTION;
}
#ifdefined(_M_AMD64) || defined(_M_IX86) //----------------------------------------------------------------------------- LONG WINAPI Handle_FLT_Exception(struct _EXCEPTION_POINTERS* exceptionInfo) {
PCONTEXT ctx = exceptionInfo->ContextRecord; #ifndef _WIN64 // handle exception caused by native method modifying control word
DWORD exception_code = exceptionInfo->ExceptionRecord->ExceptionCode;
switch (exception_code) { case EXCEPTION_FLT_DENORMAL_OPERAND: case EXCEPTION_FLT_DIVIDE_BY_ZERO: case EXCEPTION_FLT_INEXACT_RESULT: case EXCEPTION_FLT_INVALID_OPERATION: case EXCEPTION_FLT_OVERFLOW: case EXCEPTION_FLT_STACK_CHECK: case EXCEPTION_FLT_UNDERFLOW:
jint fp_control_word = (* (jint*) StubRoutines::x86::addr_fpu_cntrl_wrd_std()); if (fp_control_word != ctx->FloatSave.ControlWord) { // Restore FPCW and mask out FLT exceptions
ctx->FloatSave.ControlWord = fp_control_word | 0xffffffc0; // Mask out pending FLT exceptions
ctx->FloatSave.StatusWord &= 0xffffff00; return EXCEPTION_CONTINUE_EXECUTION;
}
}
if (prev_uef_handler != NULL) { // We didn't handle this exception so pass it to the previous // UnhandledExceptionFilter. return (prev_uef_handler)(exceptionInfo);
} #else// !_WIN64 // On Windows, the mxcsr control bits are non-volatile across calls // See also CR 6192333 //
jint MxCsr = INITIAL_MXCSR; // we can't use StubRoutines::x86::addr_mxcsr_std() // because in Win64 mxcsr is not saved there if (MxCsr != ctx->MxCsr) {
ctx->MxCsr = MxCsr; return EXCEPTION_CONTINUE_EXECUTION;
} #endif// !_WIN64
// If UseOSErrorReporting, this will return here and save the error file // somewhere where we can find it in the minidump.
}
//-----------------------------------------------------------------------------
JNIEXPORT LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { if (InterceptOSException) return EXCEPTION_CONTINUE_SEARCH;
PEXCEPTION_RECORD exception_record = exceptionInfo->ExceptionRecord;
DWORD exception_code = exception_record->ExceptionCode; #ifdefined(_M_ARM64)
address pc = (address) exceptionInfo->ContextRecord->Pc; #elifdefined(_M_AMD64)
address pc = (address) exceptionInfo->ContextRecord->Rip; #else
address pc = (address) exceptionInfo->ContextRecord->Eip; #endif
Thread* t = Thread::current_or_null_safe();
#ifndef _WIN64 // Execution protection violation - win32 running on AMD64 only // Handled first to avoid misdiagnosis as a "normal" access violation; // This is safe to do because we have a new/unique ExceptionInformation // code for this condition. if (exception_code == EXCEPTION_ACCESS_VIOLATION) { int exception_subcode = (int) exception_record->ExceptionInformation[0];
address addr = (address) exception_record->ExceptionInformation[1];
if (exception_subcode == EXCEPTION_INFO_EXEC_VIOLATION) { int page_size = os::vm_page_size();
// Make sure the pc and the faulting address are sane. // // If an instruction spans a page boundary, and the page containing // the beginning of the instruction is executable but the following // page is not, the pc and the faulting address might be slightly // different - we still want to unguard the 2nd page in this case. // // 15 bytes seems to be a (very) safe value for max instruction size. bool pc_is_near_addr =
(pointer_delta((void*) addr, (void*) pc, sizeof(char)) < 15); bool instr_spans_page_boundary =
(align_down((intptr_t) pc ^ (intptr_t) addr,
(intptr_t) page_size) > 0);
// In conservative mode, don't unguard unless the address is in the VM if (UnguardOnExecutionViolation > 0 && addr != last_addr &&
(UnguardOnExecutionViolation > 1 || os::address_is_in_vm(addr))) {
// Set memory to RWX and retry
address page_start = align_down(addr, page_size); bool res = os::protect_memory((char*) page_start, page_size,
os::MEM_PROT_RWX);
// Set last_addr so if we fault again at the same address, we don't // end up in an endless loop. // // There are two potential complications here. Two threads trapping // at the same address at the same time could cause one of the // threads to think it already unguarded, and abort the VM. Likely // very rare. // // The other race involves two threads alternately trapping at // different addresses and failing to unguard the page, resulting in // an endless loop. This condition is probably even more unlikely // than the first. // // Although both cases could be avoided by using locks or thread // local last_addr, these solutions are unnecessary complication: // this handler is a best-effort safety net, not a complete solution. // It is disabled by default and should only be used as a workaround // in case we missed any no-execute-unsafe VM code.
last_addr = addr;
return EXCEPTION_CONTINUE_EXECUTION;
}
}
// Last unguard failed or not unguarding
tty->print_raw_cr("Execution protection violation"); #if !defined(USE_VECTORED_EXCEPTION_HANDLING)
report_error(t, exception_code, addr, exception_record,
exceptionInfo->ContextRecord); #endif return EXCEPTION_CONTINUE_SEARCH;
}
} #endif// _WIN64
#ifdefined(_M_AMD64) || defined(_M_IX86) if ((exception_code == EXCEPTION_ACCESS_VIOLATION) &&
VM_Version::is_cpuinfo_segv_addr(pc)) { // Verify that OS save/restore AVX registers. return Handle_Exception(exceptionInfo, VM_Version::cpuinfo_cont_addr());
} #endif
// Handle potential stack overflows up front. if (exception_code == EXCEPTION_STACK_OVERFLOW) {
StackOverflow* overflow_state = thread->stack_overflow_state(); if (overflow_state->stack_guards_enabled()) { if (in_java) {
frame fr; if (os::win32::get_frame_at_stack_banging_point(thread, exceptionInfo, pc, &fr)) {
assert(fr.is_java_frame(), "Must be a Java frame");
SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
}
} // Yellow zone violation. The o/s has unprotected the first yellow // zone page for us. Note: must call disable_stack_yellow_zone to // update the enabled status, even if the zone contains only one page.
assert(!in_vm, "Undersized StackShadowPages");
overflow_state->disable_stack_yellow_reserved_zone(); // If not in java code, return and hope for the best. return in_java
? Handle_Exception(exceptionInfo, SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW))
: EXCEPTION_CONTINUE_EXECUTION;
} else { // Fatal red zone violation.
overflow_state->disable_stack_red_zone();
tty->print_raw_cr("An unrecoverable stack overflow has occurred."); #if !defined(USE_VECTORED_EXCEPTION_HANDLING)
report_error(t, exception_code, pc, exception_record,
exceptionInfo->ContextRecord); #endif return EXCEPTION_CONTINUE_SEARCH;
}
} elseif (exception_code == EXCEPTION_ACCESS_VIOLATION) { if (in_java) { // Either stack overflow or null pointer exception.
address addr = (address) exception_record->ExceptionInformation[1];
address stack_end = thread->stack_end(); if (addr < stack_end && addr >= stack_end - os::vm_page_size()) { // Stack overflow.
assert(!os::uses_stack_guard_pages(), "should be caught by red zone code above."); return Handle_Exception(exceptionInfo,
SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW));
} // Check for safepoint polling and implicit null // We only expect null pointers in the stubs (vtable) // the rest are checked explicitly now.
CodeBlob* cb = CodeCache::find_blob(pc); if (cb != NULL) { if (SafepointMechanism::is_poll_address(addr)) {
address stub = SharedRuntime::get_poll_stub(pc); return Handle_Exception(exceptionInfo, stub);
}
} #ifdef _WIN64 // If it's a legal stack address map the entire region in if (thread->is_in_usable_stack(addr)) {
addr = (address)((uintptr_t)addr &
(~((uintptr_t)os::vm_page_size() - (uintptr_t)1)));
os::commit_memory((char *)addr, thread->stack_base() - addr,
!ExecMem); return EXCEPTION_CONTINUE_EXECUTION;
} #endif // Null pointer exception. if (MacroAssembler::uses_implicit_null_check((void*)addr)) {
address stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); if (stub != NULL) return Handle_Exception(exceptionInfo, stub);
}
report_error(t, exception_code, pc, exception_record,
exceptionInfo->ContextRecord); return EXCEPTION_CONTINUE_SEARCH;
}
#ifdef _WIN64 // Special care for fast JNI field accessors. // jni_fast_Get<Primitive>Field can trap at certain pc's if a GC kicks // in and the heap gets shrunk before the field access.
address slowcase_pc = JNI_FastGetField::find_slowcase_pc(pc); if (slowcase_pc != (address)-1) { return Handle_Exception(exceptionInfo, slowcase_pc);
} #endif
#ifdefined(USE_VECTORED_EXCEPTION_HANDLING) LONG WINAPI topLevelVectoredExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
PEXCEPTION_RECORD exceptionRecord = exceptionInfo->ExceptionRecord; #ifdefined(_M_ARM64)
address pc = (address) exceptionInfo->ContextRecord->Pc; #elifdefined(_M_AMD64)
address pc = (address) exceptionInfo->ContextRecord->Rip; #else
address pc = (address) exceptionInfo->ContextRecord->Eip; #endif
// Fast path for code part of the code cache if (CodeCache::low_bound() <= pc && pc < CodeCache::high_bound()) { return topLevelExceptionFilter(exceptionInfo);
}
// If the exception occurred in the codeCache, pass control // to our normal exception handler.
CodeBlob* cb = CodeCache::find_blob(pc); if (cb != NULL) { return topLevelExceptionFilter(exceptionInfo);
}
return EXCEPTION_CONTINUE_SEARCH;
} #endif
#ifdefined(USE_VECTORED_EXCEPTION_HANDLING) LONG WINAPI topLevelUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { if (InterceptOSException) gotoexit;
DWORD exception_code = exceptionInfo->ExceptionRecord->ExceptionCode; #ifdefined(_M_ARM64)
address pc = (address)exceptionInfo->ContextRecord->Pc; #elifdefined(_M_AMD64)
address pc = (address) exceptionInfo->ContextRecord->Rip; #else
address pc = (address) exceptionInfo->ContextRecord->Eip; #endif
Thread* t = Thread::current_or_null_safe();
#ifndef _WIN64 // Special care for fast JNI accessors. // jni_fast_Get<Primitive>Field can trap at certain pc's if a GC kicks in and // the heap gets shrunk before the field access. // Need to install our own structured exception handler since native code may // install its own. LONG WINAPI fastJNIAccessorExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
DWORD exception_code = exceptionInfo->ExceptionRecord->ExceptionCode; if (exception_code == EXCEPTION_ACCESS_VIOLATION) {
address pc = (address) exceptionInfo->ContextRecord->Eip;
address addr = JNI_FastGetField::find_slowcase_pc(pc); if (addr != (address)-1) { return Handle_Exception(exceptionInfo, addr);
}
} return EXCEPTION_CONTINUE_SEARCH;
}
address os::win32::fast_jni_accessor_wrapper(BasicType type) { switch (type) { case T_BOOLEAN: return (address)jni_fast_GetBooleanField_wrapper; case T_BYTE: return (address)jni_fast_GetByteField_wrapper; case T_CHAR: return (address)jni_fast_GetCharField_wrapper; case T_SHORT: return (address)jni_fast_GetShortField_wrapper; case T_INT: return (address)jni_fast_GetIntField_wrapper; case T_LONG: return (address)jni_fast_GetLongField_wrapper; case T_FLOAT: return (address)jni_fast_GetFloatField_wrapper; case T_DOUBLE: return (address)jni_fast_GetDoubleField_wrapper; default: ShouldNotReachHere();
} return (address)-1;
} #endif
// Virtual Memory
// Windows large page support is available on Windows 2003. In order to use // large page memory, the administrator must first assign additional privilege // to the user: // + select Control Panel -> Administrative Tools -> Local Security Policy // + select Local Policies -> User Rights Assignment // + double click "Lock pages in memory", add users and/or groups // + reboot // Note the above steps are needed for administrator as well, as administrators // by default do not have the privilege to lock pages in memory. // // Note about Windows 2003: although the API supports committing large page // memory on a page-by-page basis and VirtualAlloc() returns success under this // scenario, I found through experiment it only uses large page if the entire // memory region is reserved and committed in a single VirtualAlloc() call. // This makes Windows large page support more or less like Solaris ISM, in // that the entire heap must be committed upfront. This probably will change // in the future, if so the code below needs to be revisited.
public:
NUMANodeListHolder() {
_numa_used_node_count = 0;
_numa_used_node_list = NULL; // do rest of initialization in build routine (after function pointers are set up)
}
~NUMANodeListHolder() {
free_node_list();
}
bool build() {
DWORD_PTR proc_aff_mask;
DWORD_PTR sys_aff_mask; if (!GetProcessAffinityMask(GetCurrentProcess(), &proc_aff_mask, &sys_aff_mask)) returnfalse;
ULONG highest_node_number; if (!GetNumaHighestNodeNumber(&highest_node_number)) returnfalse;
free_node_list();
_numa_used_node_list = NEW_C_HEAP_ARRAY(int, highest_node_number + 1, mtInternal); for (unsignedint i = 0; i <= highest_node_number; i++) {
ULONGLONG proc_mask_numa_node; if (!GetNumaNodeProcessorMask(i, &proc_mask_numa_node)) returnfalse; if ((proc_aff_mask & proc_mask_numa_node)!=0) {
_numa_used_node_list[_numa_used_node_count++] = i;
}
} return (_numa_used_node_count > 1);
}
int get_count() { return _numa_used_node_count; } int get_node_list_entry(int n) { // for indexes out of range, returns -1 return (n < _numa_used_node_count ? _numa_used_node_list[n] : -1);
}
// print a warning if UseNUMAInterleaving flag is specified on command line bool warn_on_failure = !FLAG_IS_DEFAULT(UseNUMAInterleaving);
#define WARN(msg) if (warn_on_failure) { warning(msg); }
// NUMAInterleaveGranularity cannot be less than vm_allocation_granularity (or _large_page_size if using large pages)
size_t min_interleave_granularity = UseLargePages ? _large_page_size : os::vm_allocation_granularity();
NUMAInterleaveGranularity = align_up(NUMAInterleaveGranularity, min_interleave_granularity);
if (!numa_node_list_holder.build()) {
WARN("Process does not cover multiple NUMA nodes.");
WARN("...Ignoring UseNUMAInterleaving flag."); returnfalse;
}
if (log_is_enabled(Debug, os, cpu)) {
Log(os, cpu) log;
log.debug("NUMA UsedNodeCount=%d, namely ", numa_node_list_holder.get_count()); for (int i = 0; i < numa_node_list_holder.get_count(); i++) {
log.debug(" %d ", numa_node_list_holder.get_node_list_entry(i));
}
}
#undef WARN
returntrue;
}
// this routine is used whenever we need to reserve a contiguous VA range // but we need to make separate VirtualAlloc calls for each piece of the range // Reasons for doing this: // * UseLargePagesIndividualAllocation was set (normally only needed on WS2003 but possible to be set otherwise) // * UseNUMAInterleaving requires a separate node for each piece staticchar* allocate_pages_individually(size_t bytes, char* addr, DWORD flags,
DWORD prot, bool should_inject_error = false) { char * p_buf; // note: at setup time we guaranteed that NUMAInterleaveGranularity was aligned up to a page size
size_t page_size = UseLargePages ? _large_page_size : os::vm_allocation_granularity();
size_t chunk_size = UseNUMAInterleaving ? NUMAInterleaveGranularity : page_size;
// first reserve enough address space in advance since we want to be // able to break a single contiguous virtual address range into multiple // large page commits but WS2003 does not allow reserving large page space // so we just use 4K pages for reserve, this gives us a legal contiguous // address space. then we will deallocate that reservation, and re alloc // using large pages const size_t size_of_reserve = bytes + chunk_size; if (bytes > size_of_reserve) { // Overflowed. return NULL;
}
p_buf = (char *) virtualAlloc(addr,
size_of_reserve, // size of Reserve
MEM_RESERVE,
PAGE_READWRITE); // If reservation failed, return NULL if (p_buf == NULL) return NULL;
MemTracker::record_virtual_memory_reserve((address)p_buf, size_of_reserve, CALLER_PC);
os::release_memory(p_buf, bytes + chunk_size);
// we still need to round up to a page boundary (in case we are using large pages) // but not to a chunk boundary (in case InterleavingGranularity doesn't align with page size) // instead we handle this in the bytes_to_rq computation below
p_buf = align_up(p_buf, page_size);
// now go through and allocate one chunk at a time until all bytes are // allocated
size_t bytes_remaining = bytes; // An overflow of align_up() would have been caught above // in the calculation of size_of_reserve. char * next_alloc_addr = p_buf;
HANDLE hProc = GetCurrentProcess();
#ifdef ASSERT // Variable for the failure injection int ran_num = os::random();
size_t fail_after = ran_num % bytes; #endif
int count=0; while (bytes_remaining) { // select bytes_to_rq to get to the next chunk_size boundary
if (inject_error_now) {
p_new = NULL;
} else { if (!UseNUMAInterleaving) {
p_new = (char *) virtualAlloc(next_alloc_addr,
bytes_to_rq,
flags,
prot);
} else { // get the next node to use from the used_node_list
assert(numa_node_list_holder.get_count() > 0, "Multiple NUMA nodes expected");
DWORD node = numa_node_list_holder.get_node_list_entry(count % numa_node_list_holder.get_count());
p_new = (char *)virtualAllocExNuma(hProc, next_alloc_addr, bytes_to_rq, flags, prot, node);
}
}
if (p_new == NULL) { // Free any allocated pages if (next_alloc_addr > p_buf) { // Some memory was committed so release it.
size_t bytes_to_release = bytes - bytes_remaining; // NMT has yet to record any individual blocks, so it // need to create a dummy 'reserve' record to match // the release.
MemTracker::record_virtual_memory_reserve((address)p_buf,
bytes_to_release, CALLER_PC);
os::release_memory(p_buf, bytes_to_release);
} #ifdef ASSERT if (should_inject_error) {
log_develop_debug(pagesize)("Reserving pages individually failed.");
} #endif return NULL;
}
bytes_remaining -= bytes_to_rq;
next_alloc_addr += bytes_to_rq;
count++;
} // Although the memory is allocated individually, it is returned as one. // NMT records it as one block. if ((flags & MEM_COMMIT) != 0) {
MemTracker::record_virtual_memory_reserve_and_commit((address)p_buf, bytes, CALLER_PC);
} else {
MemTracker::record_virtual_memory_reserve((address)p_buf, bytes, CALLER_PC);
}
// made it this far, success return p_buf;
}
static size_t large_page_init_decide_size() { // print a warning if any large page related flag is specified on command line bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages) ||
!FLAG_IS_DEFAULT(LargePageSizeInBytes);
#define WARN(msg) if (warn_on_failure) { warning(msg); }
if (!request_lock_memory_privilege()) {
WARN("JVM cannot use large page memory because it does not have enough privilege to lock pages in memory."); return0;
}
size_t size = GetLargePageMinimum(); if (size == 0) {
WARN("Large page is not supported by the processor."); return0;
}
#ifdefined(IA32) || defined(AMD64) if (size > 4*M || LargePageSizeInBytes > 4*M) {
WARN("JVM cannot use large pages bigger than 4mb."); return0;
} #endif
size_t fullname_len = strlen(dir) + strlen(name_template); char *fullname = (char*)os::malloc(fullname_len + 1, mtInternal); if (fullname == NULL) {
vm_exit_during_initialization(err_msg("Malloc failed during creation of backing file for heap (%s)", os::strerror(errno))); return -1;
} int n = snprintf(fullname, fullname_len + 1, "%s%s", dir, name_template);
assert((size_t)n == fullname_len, "Unexpected number of characters in string");
os::native_path(fullname);
char *path = _mktemp(fullname); if (path == NULL) {
warning("_mktemp could not create file name from template %s (%s)", fullname, os::strerror(errno));
os::free(fullname); return -1;
}
os::free(fullname); if (fd < 0) {
warning("Problem opening file for heap (%s)", os::strerror(errno)); return -1;
} return fd;
}
// If 'base' is not NULL, function will return NULL if it cannot get 'base' char* os::map_memory_to_file(char* base, size_t size, int fd) {
assert(fd != -1, "File descriptor is not valid");
HANDLE fh = (HANDLE)_get_osfhandle(fd); #ifdef _LP64
HANDLE fileMapping = CreateFileMapping(fh, NULL, PAGE_READWRITE,
(DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), NULL); #else
HANDLE fileMapping = CreateFileMapping(fh, NULL, PAGE_READWRITE, 0, (DWORD)size, NULL); #endif if (fileMapping == NULL) { if (GetLastError() == ERROR_DISK_FULL) {
vm_exit_during_initialization(err_msg("Could not allocate sufficient disk space for Java heap"));
} else {
vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
}
// Multiple threads can race in this code but it's not possible to unmap small sections of // virtual space to get requested alignment, like posix-like os's. // Windows prevents multiple thread from remapping over each other so this loop is thread-safe. staticchar* map_or_reserve_memory_aligned(size_t size, size_t alignment, int file_desc) {
assert((alignment & (os::vm_allocation_granularity() - 1)) == 0, "Alignment must be a multiple of allocation granularity (page size)");
assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned");
size_t extra_size = size + alignment;
assert(extra_size >= size, "overflow, size is too large to allow alignment");
// Attempt to map, into the just vacated space, the slightly smaller aligned area. // Which may fail, hence the loop.
aligned_base = file_desc != -1 ? os::attempt_map_memory_to_file_at(aligned_base, size, file_desc) :
os::attempt_reserve_memory_at(aligned_base, size);
}
assert(aligned_base != NULL, "Did not manage to re-map after %d attempts?", max_attempts);
return aligned_base;
}
char* os::reserve_memory_aligned(size_t size, size_t alignment, bool exec) { // exec can be ignored return map_or_reserve_memory_aligned(size, alignment, -1/* file_desc */);
}
// Reserve memory at an arbitrary address, only if that area is // available (and not reserved for something else). char* os::pd_attempt_reserve_memory_at(char* addr, size_t bytes, bool exec) {
assert((size_t)addr % os::vm_allocation_granularity() == 0, "reserve alignment");
assert(bytes % os::vm_page_size() == 0, "reserve page size"); char* res; // note that if UseLargePages is on, all the areas that require interleaving // will go thru reserve_memory_special rather than thru here. bool use_individual = (UseNUMAInterleaving && !UseLargePages); if (!use_individual) {
res = (char*)virtualAlloc(addr, bytes, MEM_RESERVE, PAGE_READWRITE);
} else {
elapsedTimer reserveTimer; if (Verbose && PrintMiscellaneous) reserveTimer.start(); // in numa interleaving, we have to allocate pages individually // (well really chunks of NUMAInterleaveGranularity size)
res = allocate_pages_individually(bytes, addr, MEM_RESERVE, PAGE_READWRITE); if (res == NULL) {
warning("NUMA page allocation failed");
} if (Verbose && PrintMiscellaneous) {
reserveTimer.stop();
tty->print_cr("reserve_memory of %Ix bytes took " JLONG_FORMAT " ms (" JLONG_FORMAT " ticks)", bytes,
reserveTimer.milliseconds(), reserveTimer.ticks());
}
}
assert(res == NULL || addr == NULL || addr == res, "Unexpected address from reserve.");
return res;
}
char* os::pd_attempt_map_memory_to_file_at(char* requested_addr, size_t bytes, int file_desc) {
assert(file_desc >= 0, "file_desc is not valid"); return map_memory_to_file(requested_addr, bytes, file_desc);
}
bool os::can_commit_large_page_memory() { // Windows only uses large page memory when the entire region is reserved // and committed in a single VirtualAlloc() call. This may change in the // future, but with Windows 2003 it's not possible to commit on demand. returnfalse;
}
char * p_buf = allocate_pages_individually(size, req_addr, flags, prot, LargePagesIndividualAllocationInjectError); if (p_buf == NULL) { // give an appropriate warning message if (UseNUMAInterleaving) {
warning("NUMA large page allocation failed, UseLargePages flag ignored");
} if (UseLargePagesIndividualAllocation) {
warning("Individually allocated large pages failed, " "use -XX:-UseLargePagesIndividualAllocation to turn off");
} return NULL;
} return p_buf;
}
staticchar* reserve_large_pages_single_range(size_t size, char* req_addr, bool exec) {
log_debug(pagesize)("Reserving large pages in a single large chunk.");
staticchar* reserve_large_pages(size_t size, char* req_addr, bool exec) { // with large pages, there are two cases where we need to use Individual Allocation // 1) the UseLargePagesIndividualAllocation flag is set (set by default on WS2003) // 2) NUMA Interleaving is enabled, in which case we use a different node for each page if (UseLargePagesIndividualAllocation || UseNUMAInterleaving) { return reserve_large_pages_individually(size, req_addr, exec);
} return reserve_large_pages_single_range(size, req_addr, exec);
}
staticchar* find_aligned_address(size_t size, size_t alignment) { // Temporary reserve memory large enough to ensure we can get the requested // alignment and still fit the reservation. char* addr = (char*) virtualAlloc(NULL, size + alignment, MEM_RESERVE, PAGE_NOACCESS); // Align the address to the requested alignment. char* aligned_addr = align_up(addr, alignment); // Free the temporary reservation.
virtualFree(addr, 0, MEM_RELEASE);
return aligned_addr;
}
staticchar* reserve_large_pages_aligned(size_t size, size_t alignment, bool exec) {
log_debug(pagesize)("Reserving large pages at an aligned address, alignment=" SIZE_FORMAT "%s",
byte_size_in_exact_unit(alignment), exact_unit_for_byte_size(alignment));
// Will try to find a suitable address at most 20 times. The reason we need to try // multiple times is that between finding the aligned address and trying to commit // the large pages another thread might have reserved an overlapping region. constint attempts_limit = 20; for (int attempts = 0; attempts < attempts_limit; attempts++) { // Find aligned address. char* aligned_address = find_aligned_address(size, alignment);
// Try to do the large page reservation using the aligned address.
aligned_address = reserve_large_pages(size, aligned_address, exec); if (aligned_address != NULL) { // Reservation at the aligned address succeeded.
guarantee(is_aligned(aligned_address, alignment), "Must be aligned"); return aligned_address;
}
}
log_debug(pagesize)("Failed reserving large pages at aligned address"); return NULL;
}
char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_size, char* addr, bool exec) {
assert(UseLargePages, "only for large pages");
assert(page_size == os::large_page_size(), "Currently only support one large page size on Windows");
assert(is_aligned(addr, alignment), "Must be");
assert(is_aligned(addr, page_size), "Must be");
if (!is_aligned(bytes, page_size)) { // Fallback to small pages, Windows does not support mixed mappings. return NULL;
}
// The requested alignment can be larger than the page size, for example with G1 // the alignment is bound to the heap region size. So this reservation needs to // ensure that the requested alignment is met. When there is a requested address // this solves it self, since it must be properly aligned already. if (addr == NULL && alignment > page_size) { return reserve_large_pages_aligned(bytes, alignment, exec);
}
// No additional requirements, just reserve the large pages. return reserve_large_pages(bytes, addr, exec);
}
bool os::pd_commit_memory(char* addr, size_t bytes, bool exec) { if (bytes == 0) { // Don't bother the OS with noops. returntrue;
}
assert((size_t) addr % os::vm_page_size() == 0, "commit on page boundaries");
assert(bytes % os::vm_page_size() == 0, "commit in page-sized chunks"); // Don't attempt to print anything if the OS call fails. We're // probably low on resources, so the print itself may cause crashes.
// unless we have NUMAInterleaving enabled, the range of a commit // is always within a reserve covered by a single VirtualAlloc // in that case we can just do a single commit for the requested size if (!UseNUMAInterleaving) { if (virtualAlloc(addr, bytes, MEM_COMMIT, PAGE_READWRITE) == NULL) {
NOT_PRODUCT(warn_fail_commit_memory(addr, bytes, exec);) returnfalse;
} if (exec) {
DWORD oldprot; // Windows doc says to use VirtualProtect to get execute permissions if (!VirtualProtect(addr, bytes, PAGE_EXECUTE_READWRITE, &oldprot)) {
NOT_PRODUCT(warn_fail_commit_memory(addr, bytes, exec);) returnfalse;
}
} returntrue;
} else {
// when NUMAInterleaving is enabled, the commit might cover a range that // came from multiple VirtualAlloc reserves (using allocate_pages_individually). // VirtualQuery can help us determine that. The RegionSize that VirtualQuery // returns represents the number of bytes that can be committed in one step.
size_t bytes_remaining = bytes; char * next_alloc_addr = addr; while (bytes_remaining > 0) {
MEMORY_BASIC_INFORMATION alloc_info;
VirtualQuery(next_alloc_addr, &alloc_info, sizeof(alloc_info));
size_t bytes_to_rq = MIN2(bytes_remaining, (size_t)alloc_info.RegionSize); if (virtualAlloc(next_alloc_addr, bytes_to_rq, MEM_COMMIT,
PAGE_READWRITE) == NULL) {
NOT_PRODUCT(warn_fail_commit_memory(next_alloc_addr, bytes_to_rq,
exec);) returnfalse;
} if (exec) {
DWORD oldprot; if (!VirtualProtect(next_alloc_addr, bytes_to_rq,
PAGE_EXECUTE_READWRITE, &oldprot)) {
NOT_PRODUCT(warn_fail_commit_memory(next_alloc_addr, bytes_to_rq,
exec);) returnfalse;
}
}
bytes_remaining -= bytes_to_rq;
next_alloc_addr += bytes_to_rq;
}
} // if we made it this far, return true returntrue;
}
bool os::pd_commit_memory(char* addr, size_t size, size_t alignment_hint, bool exec) { // alignment_hint is ignored on this OS return pd_commit_memory(addr, size, exec);
}
void os::pd_commit_memory_or_exit(char* addr, size_t size,
size_t alignment_hint, bool exec, constchar* mesg) { // alignment_hint is ignored on this OS
pd_commit_memory_or_exit(addr, size, exec, mesg);
}
bool os::pd_uncommit_memory(char* addr, size_t bytes, bool exec) { if (bytes == 0) { // Don't bother the OS with noops. returntrue;
}
assert((size_t) addr % os::vm_page_size() == 0, "uncommit on page boundaries");
assert(bytes % os::vm_page_size() == 0, "uncommit in page-sized chunks"); return (virtualFree(addr, bytes, MEM_DECOMMIT) == TRUE);
}
bool os::pd_release_memory(char* addr, size_t bytes) { // Given a range we are to release, we require a mapping to start at the beginning of that range; // if NUMA or LP we allow the range to contain multiple mappings, which have to cover the range // completely; otherwise the range must match an OS mapping exactly.
address start = (address)addr;
address end = start + bytes;
os::win32::mapping_info_t mi; constbool multiple_mappings_allowed = UseLargePagesIndividualAllocation || UseNUMAInterleaving;
address p = start; bool first_mapping = true;
do { // Find mapping and check it constchar* err = NULL; if (!os::win32::find_mapping(p, &mi)) {
err = "no mapping found";
} else { if (first_mapping) { if (mi.base != start) {
err = "base address mismatch";
} if (multiple_mappings_allowed ? (mi.size > bytes) : (mi.size != bytes)) {
err = "size mismatch";
}
} else {
assert(p == mi.base && mi.size > 0, "Sanity"); if (mi.base + mi.size > end) {
err = "mapping overlaps end";
} if (mi.size == 0) {
err = "zero length mapping?"; // Should never happen; just to prevent endlessly looping in release.
}
}
} // Handle mapping error. We assert in debug, unconditionally print a warning in release. if (err != NULL) {
log_warning(os)("bad release: [" PTR_FORMAT "-" PTR_FORMAT "): %s", p2i(start), p2i(end), err); #ifdef ASSERT
os::print_memory_mappings((char*)start, bytes, tty);
assert(false, "bad release: [" PTR_FORMAT "-" PTR_FORMAT "): %s", p2i(start), p2i(end), err); #endif returnfalse;
} // Free this range if (virtualFree(p, 0, MEM_RELEASE) == FALSE) { returnfalse;
}
first_mapping = false;
p = mi.base + mi.size;
} while (p < end);
// Use VirtualQuery() to get the chunk size. while (bytes_remaining) {
MEMORY_BASIC_INFORMATION alloc_info; if (VirtualQuery(next_protect_addr, &alloc_info, sizeof(alloc_info)) == 0) { returnfalse;
}
size_t bytes_to_protect = MIN2(bytes_remaining, (size_t)alloc_info.RegionSize); // We used different API at allocate_pages_individually() based on UseNUMAInterleaving, // but we don't distinguish here as both cases are protected by same API.
ret = VirtualProtect(next_protect_addr, bytes_to_protect, p, old_status) != 0;
warning("Failed protecting pages individually for chunk #%u", count); if (!ret) { returnfalse;
}
// Set protections specified bool os::protect_memory(char* addr, size_t bytes, ProtType prot, bool is_committed) { unsignedint p = 0; switch (prot) { case MEM_PROT_NONE: p = PAGE_NOACCESS; break; case MEM_PROT_READ: p = PAGE_READONLY; break; case MEM_PROT_RW: p = PAGE_READWRITE; break; case MEM_PROT_RWX: p = PAGE_EXECUTE_READWRITE; break; default:
ShouldNotReachHere();
}
DWORD old_status;
// Strange enough, but on Win32 one can change protection only for committed // memory, not a big deal anyway, as bytes less or equal than 64K if (!is_committed) {
commit_memory_or_exit(addr, bytes, prot == MEM_PROT_RWX, "cannot commit protection page");
} // One cannot use os::guard_memory() here, as on Win32 guard page // have different (one-shot) semantics, from MSDN on PAGE_GUARD: // // Pages in the region become guard pages. Any attempt to access a guard page // causes the system to raise a STATUS_GUARD_PAGE exception and turn off // the guard page status. Guard pages thus act as a one-time access alarm. bool ret; if (UseNUMAInterleaving) { // If UseNUMAInterleaving is enabled, the pages may have been allocated a chunk at a time, // so we must protect the chunks individually.
ret = protect_pages_individually(addr, bytes, p, &old_status);
} else {
ret = VirtualProtect(addr, bytes, p, &old_status) != 0;
} #ifdef ASSERT if (!ret) { int err = os::get_last_error(); char buf[256];
size_t buf_len = os::lasterror(buf, sizeof(buf));
warning("INFO: os::protect_memory(" PTR_FORMAT ", " SIZE_FORMAT ") failed; error='%s' (DOS error/errno=%d)", addr, bytes,
buf_len != 0 ? buf : "<no_error_string>", err);
} #endif return ret;
}
char* os::non_memory_address_word() { // Must never look like an address returned by reserve_memory, // even in its subfields (as defined by the CPU immediate fields, // if the CPU splits constants across multiple instructions). #ifdef _M_ARM64 // AArch64 has a maximum addressable space of 48-bits return (char*)((1ull << 48) - 1); #else return (char*)-1; #endif
}
void os::pd_start_thread(Thread* thread) {
DWORD ret = ResumeThread(thread->osthread()->thread_handle()); // Returns previous suspend state: // 0: Thread was not suspended // 1: Thread is running now // >1: Thread is still suspended.
assert(ret != SYS_THREAD_ERROR, "StartThread failed"); // should propagate back
}
// Short sleep, direct OS call. // // ms = 0, means allow others (if any) to run. // void os::naked_short_sleep(jlong ms) {
assert(ms < 1000, "Un-interruptable sleep, short time use only");
Sleep(ms);
}
// Windows does not provide sleep functionality with nanosecond resolution, so we // try to approximate this with spinning combined with yielding if another thread // is ready to run on the current processor. void os::naked_short_nanosleep(jlong ns) {
assert(ns > -1 && ns < NANOUNITS, "Un-interruptable sleep, short time use only");
int64_t start = os::javaTimeNanos(); do { if (SwitchToThread() == 0) { // Nothing else is ready to run on this cpu, spin a little
SpinPause();
}
} while (os::javaTimeNanos() - start < ns);
}
// Sleep forever; naked call to OS-specific sleep; use with CAUTION void os::infinite_sleep() { while (true) { // sleep forever ...
Sleep(100000); // ... 100 seconds at a time
}
}
typedefBOOL (WINAPI * STTSignature)(void);
void os::naked_yield() { // Consider passing back the return value from SwitchToThread().
SwitchToThread();
}
// Win32 only gives you access to seven real priorities at a time, // so we compress Java's ten down to seven. It would be better // if we dynamically adjusted relative priorities.
int os::current_process_id() { return (_initial_pid ? _initial_pid : _getpid());
}
int os::win32::_processor_type = 0; // Processor level is not available on non-NT systems, use vm_version instead int os::win32::_processor_level = 0;
julong os::win32::_physical_memory = 0;
bool os::win32::_is_windows_server = false;
// 6573254 // Currently, the bug is observed across all the supported Windows releases, // including the latest one (as of this writing - Windows Server 2012 R2) bool os::win32::_has_exit_bug = true;
// also returns dwAvailPhys (free physical memory bytes), dwTotalVirtual, dwAvailVirtual, // dwMemoryLoad (% of memory in use)
GlobalMemoryStatusEx(&ms);
_physical_memory = ms.ullTotalPhys;
if (FLAG_IS_DEFAULT(MaxRAM)) { // Adjust MaxRAM according to the maximum virtual address space available.
FLAG_SET_DEFAULT(MaxRAM, MIN2(MaxRAM, (uint64_t) ms.ullTotalVirtual));
}
_is_windows_server = IsWindowsServer();
initialize_performance_counter();
}
HINSTANCE os::win32::load_Windows_dll(constchar* name, char *ebuf, int ebuflen) { char path[MAX_PATH];
DWORD size;
DWORD pathLen = (DWORD)sizeof(path);
HINSTANCE result = NULL;
// only allow library name without path component
assert(strchr(name, '\\') == NULL, "path not allowed");
assert(strchr(name, ':') == NULL, "path not allowed"); if (strchr(name, '\\') != NULL || strchr(name, ':') != NULL) {
jio_snprintf(ebuf, ebuflen, "Invalid parameter while calling os::win32::load_windows_dll(): cannot take path: %s", name); return NULL;
}
int os::win32::exit_process_or_thread(Ept what, int exit_code) { // Basic approach: // - Each exiting thread registers its intent to exit and then does so. // - A thread trying to terminate the process must wait for all // threads currently exiting to complete their exit.
if (os::win32::has_exit_bug()) { // The array holds handles of the threads that have started exiting by calling // _endthreadex(). // Should be large enough to avoid blocking the exiting thread due to lack of // a free slot. static HANDLE handles[MAXIMUM_THREADS_TO_KEEP]; staticint handle_count = 0;
// We only attempt to register threads until a process exiting // thread manages to set the process_exiting flag. Any threads // that come through here after the process_exiting flag is set // are unregistered and will be caught in the SuspendThread() // infinite loop below. bool registered = false;
// The first thread that reached this point, initializes the critical section. if (!InitOnceExecuteOnce(&init_once_crit_sect, init_crit_sect_call, &crit_sect, NULL)) {
warning("crit_sect initialization failed in %s: %d\n", __FILE__, __LINE__);
} elseif (Atomic::load_acquire(&process_exiting) == 0) { if (what != EPT_THREAD) { // Atomically set process_exiting before the critical section // to increase the visibility between racing threads.
Atomic::cmpxchg(&process_exiting, (DWORD)0, GetCurrentThreadId());
}
EnterCriticalSection(&crit_sect);
if (what == EPT_THREAD && Atomic::load_acquire(&process_exiting) == 0) { // Remove from the array those handles of the threads that have completed exiting. for (i = 0, j = 0; i < handle_count; ++i) {
res = WaitForSingleObject(handles[i], 0/* don't wait */); if (res == WAIT_TIMEOUT) {
handles[j++] = handles[i];
} else { if (res == WAIT_FAILED) {
warning("WaitForSingleObject failed (%u) in %s: %d\n",
GetLastError(), __FILE__, __LINE__);
} // Don't keep the handle, if we failed waiting for it.
CloseHandle(handles[i]);
}
}
// If there's no free slot in the array of the kept handles, we'll have to // wait until at least one thread completes exiting. if ((handle_count = j) == MAXIMUM_THREADS_TO_KEEP) { // Raise the priority of the oldest exiting thread to increase its chances // to complete sooner.
SetThreadPriority(handles[0], THREAD_PRIORITY_ABOVE_NORMAL);
res = WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, handles, FALSE, EXIT_TIMEOUT); if (res >= WAIT_OBJECT_0 && res < (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS)) {
i = (res - WAIT_OBJECT_0);
handle_count = MAXIMUM_THREADS_TO_KEEP - 1; for (; i < handle_count; ++i) {
handles[i] = handles[i + 1];
}
} else {
warning("WaitForMultipleObjects %s (%u) in %s: %d\n",
(res == WAIT_FAILED ? "failed" : "timed out"),
GetLastError(), __FILE__, __LINE__); // Don't keep handles, if we failed waiting for them. for (i = 0; i < MAXIMUM_THREADS_TO_KEEP; ++i) {
CloseHandle(handles[i]);
}
handle_count = 0;
}
}
// Store a duplicate of the current thread handle in the array of handles.
hproc = GetCurrentProcess();
hthr = GetCurrentThread(); if (!DuplicateHandle(hproc, hthr, hproc, &handles[handle_count], 0, FALSE, DUPLICATE_SAME_ACCESS)) {
warning("DuplicateHandle failed (%u) in %s: %d\n",
GetLastError(), __FILE__, __LINE__);
// We can't register this thread (no more handles) so this thread // may be racing with a thread that is calling exit(). If the thread // that is calling exit() has managed to set the process_exiting // flag, then this thread will be caught in the SuspendThread() // infinite loop below which closes that race. A small timing // window remains before the process_exiting flag is set, but it // is only exposed when we are out of handles.
} else {
++handle_count;
registered = true;
// The current exiting thread has stored its handle in the array, and now // should leave the critical section before calling _endthreadex().
}
} elseif (what != EPT_THREAD && handle_count > 0) {
jlong start_time, finish_time, timeout_left; // Before ending the process, make sure all the threads that had called // _endthreadex() completed.
// Set the priority level of the current thread to the same value as // the priority level of exiting threads. // This is to ensure it will be given a fair chance to execute if // the timeout expires.
hthr = GetCurrentThread();
SetThreadPriority(hthr, THREAD_PRIORITY_ABOVE_NORMAL);
start_time = os::javaTimeNanos();
finish_time = start_time + ((jlong)EXIT_TIMEOUT * 1000000L); for (i = 0; ; ) { int portion_count = handle_count - i; if (portion_count > MAXIMUM_WAIT_OBJECTS) {
portion_count = MAXIMUM_WAIT_OBJECTS;
} for (j = 0; j < portion_count; ++j) {
SetThreadPriority(handles[i + j], THREAD_PRIORITY_ABOVE_NORMAL);
}
timeout_left = (finish_time - start_time) / 1000000L; if (timeout_left < 0) {
timeout_left = 0;
}
res = WaitForMultipleObjects(portion_count, handles + i, TRUE, timeout_left); if (res == WAIT_FAILED || res == WAIT_TIMEOUT) {
warning("WaitForMultipleObjects %s (%u) in %s: %d\n",
(res == WAIT_FAILED ? "failed" : "timed out"),
GetLastError(), __FILE__, __LINE__); // Reset portion_count so we close the remaining // handles due to this error.
portion_count = handle_count - i;
} for (j = 0; j < portion_count; ++j) {
CloseHandle(handles[i + j]);
} if ((i += portion_count) >= handle_count) { break;
}
start_time = os::javaTimeNanos();
}
handle_count = 0;
}
LeaveCriticalSection(&crit_sect);
}
if (!registered &&
Atomic::load_acquire(&process_exiting) != 0 &&
process_exiting != GetCurrentThreadId()) { // Some other thread is about to call exit(), so we don't let // the current unregistered thread proceed to exit() or _endthreadex() while (true) {
SuspendThread(GetCurrentThread()); // Avoid busy-wait loop, if SuspendThread() failed.
Sleep(EXIT_TIMEOUT);
}
}
}
// We are here if either // - there's no 'race at exit' bug on this OS release; // - initialization of the critical section failed (unlikely); // - the current thread has registered itself and left the critical section; // - the process-exiting thread has raised the flag and left the critical section. if (what == EPT_THREAD) {
_endthreadex((unsigned)exit_code);
} elseif (what == EPT_PROCESS) {
ALLOW_C_FUNCTION(::exit, ::exit(exit_code);)
} else { // EPT_PROCESS_DIE
ALLOW_C_FUNCTION(::_exit, ::_exit(exit_code);)
}
// This may be overridden later when argument processing is done.
FLAG_SET_ERGO(UseLargePagesIndividualAllocation, false);
// Initialize main_process and main_thread
main_process = GetCurrentProcess(); // Remember main_process is a pseudo handle if (!DuplicateHandle(main_process, GetCurrentThread(), main_process,
&main_thread, THREAD_ALL_ACCESS, false, 0)) {
fatal("DuplicateHandle failed\n");
}
main_thread_id = (int) GetCurrentThreadId();
// initialize fast thread access - only used for 32-bit
win32::initialize_thread_ptr_offset();
}
// To install functions for atexit processing extern"C" { staticvoid perfMemory_exit_helper() {
perfMemory_exit();
}
}
static jint initSock();
// Minimum usable stack sizes required to get to user code. Space for // HotSpot guard pages is added later.
size_t os::_compiler_thread_min_stack_allowed = 48 * K;
size_t os::_java_thread_min_stack_allowed = 40 * K; #ifdef _LP64
size_t os::_vm_internal_thread_min_stack_allowed = 64 * K; #else
size_t os::_vm_internal_thread_min_stack_allowed = (48 DEBUG_ONLY(+ 4)) * K; #endif// _LP64
// If stack_commit_size is 0, windows will reserve the default size, // but only commit a small portion of it. This stack size is the size of this // current thread but is larger than we need for Java threads. // If -Xss is given to the launcher, it will pick 64K as default stack size and pass that.
size_t os::_os_min_stack_allowed = 64 * K;
// this is called _after_ the global arguments have been parsed
jint os::init_2(void) {
// This could be set any time but all platforms // have to set it the same so we have to mirror Solaris.
DEBUG_ONLY(os::set_mutex_init_done();)
// Check and sets minimum stack sizes against command line options if (set_minimum_stack_sizes() == JNI_ERR) { return JNI_ERR;
}
// at exit methods are called in the reverse order of their registration. // there is no limit to the number of functions registered. atexit does // not set errno.
if (PerfAllowAtExitRegistration) { // only register atexit functions if PerfAllowAtExitRegistration is set. // atexit functions can be delayed until process exit time, which // can be problematic for embedded VM situations. Embedded VMs should // call DestroyJavaVM() to assure that VM resources are released.
// note: perfMemory_exit_helper atexit function may be removed in // the future if the appropriate cleanup code can be added to the // VM_Exit VMOperation's doit method. if (atexit(perfMemory_exit_helper) != 0) {
warning("os::init_2 atexit(perfMemory_exit_helper) failed");
}
}
#ifndef _WIN64 // Print something if NX is enabled (win32 on AMD64)
NOT_PRODUCT(if (PrintMiscellaneous && Verbose) nx_check_protection()); #endif
// initialize thread priority policy
prio_init();
UseNUMA = false; // We don't fully support this yet
if (UseNUMAInterleaving || (UseNUMA && FLAG_IS_DEFAULT(UseNUMAInterleaving))) { if (!numa_interleaving_init()) {
FLAG_SET_ERGO(UseNUMAInterleaving, false);
} elseif (!UseNUMAInterleaving) { // When NUMA requested, not-NUMA-aware allocations default to interleaving.
FLAG_SET_ERGO(UseNUMAInterleaving, true);
}
}
if (initSock() != JNI_OK) { return JNI_ERR;
}
SymbolEngine::recalc_search_path();
// Initialize data for jdk.internal.misc.Signal, and install CTRL-C and // CTRL-BREAK handlers. if (!ReduceSignalUsage) {
jdk_misc_signal_init();
}
// Lookup SetThreadDescription - the docs state we must use runtime-linking of // kernelbase.dll, so that is what we do.
HINSTANCE _kernelbase = LoadLibrary(TEXT("kernelbase.dll")); if (_kernelbase != NULL) {
_SetThreadDescription = reinterpret_cast<SetThreadDescriptionFnPtr>(
GetProcAddress(_kernelbase, "SetThreadDescription")); #ifdef ASSERT
_GetThreadDescription = reinterpret_cast<GetThreadDescriptionFnPtr>(
GetProcAddress(_kernelbase, "GetThreadDescription")); #endif
}
log_info(os, thread)("The SetThreadDescription API is%s available.", _SetThreadDescription == NULL ? " not" : "");
return JNI_OK;
}
// combine the high and low DWORD into a ULONGLONG static ULONGLONG make_double_word(DWORD high_word, DWORD low_word) {
ULONGLONG value = high_word;
value <<= sizeof(high_word) * 8;
value |= low_word; return value;
}
int result = MultiByteToWideChar(CP_ACP,
MB_ERR_INVALID_CHARS,
char_path, -1,
*unicode_path, unicode_path_len);
assert(result == unicode_path_len, "length already checked above");
return ERROR_SUCCESS;
}
static errno_t get_full_path(LPCWSTR unicode_path, LPWSTR* full_path) { // Get required buffer size to convert to full path. The return // value INCLUDES the terminating null character.
DWORD full_path_len = GetFullPathNameW(unicode_path, 0, NULL, NULL); if (full_path_len == 0) { return EINVAL;
}
// When the buffer has sufficient size, the return value EXCLUDES the // terminating null character
DWORD result = GetFullPathNameW(unicode_path, full_path_len, *full_path, NULL);
assert(result <= full_path_len, "length already checked above");
// Returns the given path as an absolute wide path in unc format. The returned path is NULL // on error (with err being set accordingly) and should be freed via os::free() otherwise. // additional_space is the size of space, in wchar_t, the function will additionally add to // the allocation of return buffer (such that the size of the returned buffer is at least // wcslen(buf) + 1 + additional_space). staticwchar_t* wide_abs_unc_path(charconst* path, errno_t & err, int additional_space = 0) { if ((path == NULL) || (path[0] == '\0')) {
err = ENOENT; return NULL;
}
// Need to allocate at least room for 3 characters, since os::native_path transforms C: to C:.
size_t buf_len = 1 + MAX2((size_t)3, strlen(path)); char* buf = NEW_C_HEAP_ARRAY(char, buf_len, mtInternal);
strncpy(buf, path, buf_len);
os::native_path(buf);
// if we could open both paths... if (handle1 != INVALID_HANDLE_VALUE && handle2 != INVALID_HANDLE_VALUE) {
BY_HANDLE_FILE_INFORMATION fileInfo1;
BY_HANDLE_FILE_INFORMATION fileInfo2; if (::GetFileInformationByHandle(handle1, &fileInfo1) &&
::GetFileInformationByHandle(handle2, &fileInfo2)) { // the paths are the same if they refer to the same file (fileindex) on the same volume (volume serial number) if (fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber &&
fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh &&
fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow) {
result = true;
}
}
}
//free the handles if (handle1 != INVALID_HANDLE_VALUE) {
::CloseHandle(handle1);
}
if (handle2 != INVALID_HANDLE_VALUE) {
::CloseHandle(handle2);
}
// current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool) // are used by JVM M&M and JVMTI to get user+sys or user CPU time // of a thread. // // current_thread_cpu_time() and thread_cpu_time(Thread*) returns // the fast estimate available on the platform.
// current_thread_cpu_time() is not optimized for Windows yet
jlong os::current_thread_cpu_time() { // return user + sys since the cost is the same return os::thread_cpu_time(Thread::current(), true/* user+sys */);
}
jlong os::thread_cpu_time(Thread* thread) { // consistent with what current_thread_cpu_time() returns. return os::thread_cpu_time(thread, true/* user+sys */);
}
jlong os::thread_cpu_time(Thread* thread, bool user_sys_cpu_time) { // This code is copy from classic VM -> hpi::sysThreadCPUTime // If this function changes, os::is_thread_cpu_time_supported() should too
FILETIME CreationTime;
FILETIME ExitTime;
FILETIME KernelTime;
FILETIME UserTime;
void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) {
info_ptr->max_value = ALL_64_BITS; // the max value -- all 64 bits
info_ptr->may_skip_backward = false; // GetThreadTimes returns absolute time
info_ptr->may_skip_forward = false; // GetThreadTimes returns absolute time
info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned
}
void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) {
info_ptr->max_value = ALL_64_BITS; // the max value -- all 64 bits
info_ptr->may_skip_backward = false; // GetThreadTimes returns absolute time
info_ptr->may_skip_forward = false; // GetThreadTimes returns absolute time
info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned
}
// Windows doesn't provide a loadavg primitive so this is stubbed out for now. // It does have primitives (PDH API) to get CPU usage and run queue length. // "\\Processor(_Total)\\% Processor Time", "\\System\\Processor Queue Length" // If we wanted to implement loadavg on Windows, we have a few options: // // a) Query CPU usage and run queue length and "fake" an answer by // returning the CPU usage if it's under 100%, and the run queue // length otherwise. It turns out that querying is pretty slow // on Windows, on the order of 200 microseconds on a fast machine. // Note that on the Windows the CPU usage value is the % usage // since the last time the API was called (and the first call // returns 100%), so we'd have to deal with that as well. // // b) Sample the "fake" answer using a sampling thread and store // the answer in a global variable. The call to loadavg would // just return the value of the global, avoiding the slow query. // // c) Sample a better answer using exponential decay to smooth the // value. This is basically the algorithm used by UNIX kernels. // // Note that sampling thread starvation could affect both (b) and (c). int os::loadavg(double loadavg[], int nelem) { return -1;
}
// DontYieldALot=false by default: dutifully perform all yields as requested by JVM_Yield() bool os::dont_yield() { return DontYieldALot;
}
int os::open(constchar *path, int oflag, int mode) {
errno_t err; wchar_t* wide_path = wide_abs_unc_path(path, err);
result = ReadFile(h, (LPVOID)buf, nBytes, &nread, &ov);
return result ? nread : 0;
}
// This method is a slightly reworked copy of JDK's sysNativePath // from src/windows/hpi/src/path_md.c
// Convert a pathname to native format. On win32, this involves forcing all // separators to be '\\' rather than '/' (both are legal inputs, but Win95 // sometimes rejects '/') and removing redundant separators. The input path is // assumed to have been converted into the character encoding used by the local // system. Because this might be a double-byte encoding, care is taken to // treat double-byte lead characters correctly. // // This procedure modifies the given path in place, as the result is never // longer than the original. There is no error return; this operation always // succeeds. char * os::native_path(char *path) { char *src = path, *dst = path, *end = path; char *colon = NULL; // If a drive specifier is found, this will // point to the colon following the drive letter
// Assumption: '/', '\\', ':', and drive letters are never lead bytes
assert(((!::IsDBCSLeadByte('/')) && (!::IsDBCSLeadByte('\\'))
&& (!::IsDBCSLeadByte(':'))), "Illegal lead byte");
// Check for leading separators #define isfilesep(c) ((c) == '/' || (c) == '\\') while (isfilesep(*src)) {
src++;
}
if (::isalpha(*src) && !::IsDBCSLeadByte(*src) && src[1] == ':') { // Remove leading separators if followed by drive specifier. This // hack is necessary to support file URLs containing drive // specifiers (e.g., "file://c:/path"). As a side effect, // "/c:/path" can be used as an alternative to "c:/path".
*dst++ = *src++;
colon = dst;
*dst++ = ':';
src++;
} else {
src = path; if (isfilesep(src[0]) && isfilesep(src[1])) { // UNC pathname: Retain first separator; leave src pointed at // second separator so that further separators will be collapsed // into the second separator. The result will be a pathname // beginning with "\\\\" followed (most likely) by a host name.
src = dst = path + 1;
path[0] = '\\'; // Force first separator to '\\'
}
}
end = dst;
// Remove redundant separators from remainder of path, forcing all // separators to be '\\' rather than '/'. Also, single byte space // characters are removed from the end of the path because those // are not legal ending characters on this operating system. // while (*src != '\0') { if (isfilesep(*src)) {
*dst++ = '\\'; src++; while (isfilesep(*src)) src++; if (*src == '\0') { // Check for trailing separator
end = dst; if (colon == dst - 2) break; // "z:\\" if (dst == path + 1) break; // "\\" if (dst == path + 2 && isfilesep(path[0])) { // "\\\\" is not collapsed to "\\" because "\\\\" marks the // beginning of a UNC pathname. Even though it is not, by // itself, a valid UNC pathname, we leave it as is in order // to be consistent with the path canonicalizer as well // as the win32 APIs, which treat this case as an invalid // UNC pathname rather than as an alias for the root // directory of the current drive. break;
}
end = --dst; // Path does not denote a root directory, so // remove trailing separator break;
}
end = dst;
} else { if (::IsDBCSLeadByte(*src)) { // Copy a double-byte character
*dst++ = *src++; if (*src) *dst++ = *src++;
end = dst;
} else { // Copy a single-byte character char c = *src++;
*dst++ = c; // Space is not a legal ending character if (c != ' ') end = dst;
}
}
}
*end = '\0';
// For "z:", add "." to work around a bug in the C runtime library if (colon == dst - 1) {
path[2] = '.';
path[3] = '\0';
}
return path;
}
// This code is a copy of JDK's sysSetLength // from src/windows/hpi/src/sys_api_md.c
int os::ftruncate(int fd, jlong length) {
HANDLE h = (HANDLE)::_get_osfhandle(fd); long high = (long)(length >> 32);
DWORD ret;
if (h == (HANDLE)(-1)) { return -1;
}
ret = ::SetFilePointer(h, (long)(length), &high, FILE_BEGIN); if ((ret == 0xFFFFFFFF) && (::GetLastError() != NO_ERROR)) { return -1;
}
if (::SetEndOfFile(h) == FALSE) { return -1;
}
return0;
}
int os::get_fileno(FILE* fp) { return _fileno(fp);
}
if (allow_exec) { // CreateFileMapping/MapViewOfFileEx can't map executable memory // unless it comes from a PE image (which the shared archive is not.) // Even VirtualProtect refuses to give execute access to mapped memory // that was not previously executable. // // Instead, stick the executable region in anonymous memory. Yuck. // Penalty is that ~4 pages will not be shareable - in the future // we might consider DLLizing the shared archive with a proper PE // header so that mapping executable + sharing is possible.
base = (char*) virtualAlloc(addr, bytes, MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE); if (base == NULL) {
CloseHandle(hFile); return NULL;
}
// Record virtual memory allocation
MemTracker::record_virtual_memory_reserve_and_commit((address)addr, bytes, CALLER_PC);
DWORD bytes_read;
OVERLAPPED overlapped;
overlapped.Offset = (DWORD)file_offset;
overlapped.OffsetHigh = 0;
overlapped.hEvent = NULL; // ReadFile guarantees that if the return value is true, the requested // number of bytes were read before returning. bool res = ReadFile(hFile, base, (DWORD)bytes, &bytes_read, &overlapped) != 0; if (!res) {
log_info(os)("ReadFile() failed: GetLastError->%ld.", GetLastError());
release_memory(base, bytes);
CloseHandle(hFile); return NULL;
}
} else {
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0,
NULL /* file_name */); if (hMap == NULL) {
log_info(os)("CreateFileMapping() failed: GetLastError->%ld.", GetLastError());
CloseHandle(hFile); return NULL;
}
if (!res) {
log_info(os)("VirtualProtect() failed: GetLastError->%ld.", GetLastError()); // Don't consider this a hard error, on IA32 even if the // VirtualProtect fails, we should still be able to execute
CloseHandle(hFile); return base;
}
}
// Remap a block of memory. char* os::pd_remap_memory(int fd, constchar* file_name, size_t file_offset, char *addr, size_t bytes, bool read_only, bool allow_exec) { // This OS does not allow existing memory maps to be remapped so we // would have to unmap the memory before we remap it.
// Because there is a small window between unmapping memory and mapping // it in again with different protections, CDS archives are mapped RW // on windows, so this function isn't called.
ShouldNotReachHere(); return NULL;
}
// Unmap a block of memory. // Returns true=success, otherwise false.
// Executable memory was not mapped using CreateFileMapping/MapViewOfFileEx. // Instead, executable region was allocated using VirtualAlloc(). See // pd_map_memory() above. // // The following flags should match the 'exec_access' flags used for // VirtualProtect() in pd_map_memory(). if (mem_info.Protect == PAGE_EXECUTE_READ ||
mem_info.Protect == PAGE_EXECUTE_READWRITE) { return pd_release_memory(addr, bytes);
}
BOOL result = unmapViewOfFile(addr); if (result == 0) { returnfalse;
} returntrue;
}
class HighResolutionInterval : public CHeapObj<mtThread> { // The default timer resolution seems to be 10 milliseconds. // (Where is this written down?) // If someone wants to sleep for only a fraction of the default, // then we set the timer resolution down to 1 millisecond for // the duration of their interval. // We carefully set the resolution back, since otherwise we // seem to incur an overhead (3%?) that we don't need. // CONSIDER: if ms is small, say 3, then we should run with a high resolution time. // Buf if ms is large, say 500, or 503, we should avoid the call to timeBeginPeriod(). // Alternatively, we could compute the relative error (503/500 = .6%) and only use // timeBeginPeriod() if the relative error exceeded some threshold. // timeBeginPeriod() has been linked to problems with clock drift on win32 systems and // to decreased efficiency related to increased timer "tick" rates. We want to minimize // (a) calls to timeBeginPeriod() and timeEndPeriod() and (b) time spent with high // resolution timers running. private:
jlong resolution; public:
HighResolutionInterval(jlong ms) {
resolution = ms % 10L; if (resolution != 0) {
MMRESULT result = timeBeginPeriod(1L);
}
}
~HighResolutionInterval() { if (resolution != 0) {
MMRESULT result = timeEndPeriod(1L);
}
resolution = 0L;
}
};
// An Event wraps a win32 "CreateEvent" kernel handle. // // We have a number of choices regarding "CreateEvent" win32 handle leakage: // // 1: When a thread dies return the Event to the EventFreeList, clear the ParkHandle // field, and call CloseHandle() on the win32 event handle. Unpark() would // need to be modified to tolerate finding a NULL (invalid) win32 event handle. // In addition, an unpark() operation might fetch the handle field, but the // event could recycle between the fetch and the SetEvent() operation. // SetEvent() would either fail because the handle was invalid, or inadvertently work, // as the win32 handle value had been recycled. In an ideal world calling SetEvent() // on an stale but recycled handle would be harmless, but in practice this might // confuse other non-Sun code, so it's not a viable approach. // // 2: Once a win32 event handle is associated with an Event, it remains associated // with the Event. The event handle is never closed. This could be construed // as handle leakage, but only up to the maximum # of threads that have been extant // at any one time. This shouldn't be an issue, as windows platforms typically // permit a process to have hundreds of thousands of open handles. // // 3: Same as (1), but periodically, at stop-the-world time, rundown the EventFreeList // and release unused handles. // // 4: Add a CRITICAL_SECTION to the Event to protect LD+SetEvent from LD;ST(null);CloseHandle. // It's not clear, however, that we wouldn't be trading one type of leak for another. // // 5. Use an RCU-like mechanism (Read-Copy Update). // Or perhaps something similar to Maged Michael's "Hazard pointers". // // We use (2). // // TODO-FIXME: // 1. Reconcile Doug's JSR166 j.u.c park-unpark with the objectmonitor implementation. // 2. Consider wrapping the WaitForSingleObject(Ex) calls in SEH try/finally blocks // to recover from (or at least detect) the dreaded Windows 841176 bug. // 3. Collapse the JSR166 parker event, and the objectmonitor ParkEvent // into a single win32 CreateEvent() handle. // // Assumption: // Only one parker can exist on an event, which is why we allocate // them per-thread. Multiple unparkers can coexist. // // _Event transitions in park() // -1 => -1 : illegal // 1 => 0 : pass - return immediately // 0 => -1 : block; then set _Event to 0 before returning // // _Event transitions in unpark() // 0 => 1 : just return // 1 => 1 : just return // -1 => either 0 or 1; must signal target thread // That is, we can safely transition _Event from -1 to either // 0 or 1. // // _Event serves as a restricted-range semaphore. // -1 : thread is blocked, i.e. there is a waiter // 0 : neutral: thread is running or ready, // could have been signaled after a wait started // 1 : signaled - thread is running or ready // // Another possible encoding of _Event would be with // explicit "PARKED" == 01b and "SIGNALED" == 10b bits. //
int PlatformEvent::park(jlong Millis) { // Transitions for _Event: // -1 => -1 : illegal // 1 => 0 : pass - return immediately // 0 => -1 : block; then set _Event to 0 before returning
// CONSIDER: defer assigning a CreateEvent() handle to the Event until // the initial park() operation. // Consider: use atomic decrement instead of CAS-loop
int v; for (;;) {
v = _Event; if (Atomic::cmpxchg(&_Event, v, v-1) == v) break;
}
guarantee((v == 0) || (v == 1), "invariant"); if (v != 0) return OS_OK;
// Do this the hard way by blocking ... // TODO: consider a brief spin here, gated on the success of recent // spin attempts by this thread. // // We decompose long timeouts into series of shorter timed waits. // Evidently large timo values passed in WaitForSingleObject() are problematic on some // versions of Windows. See EventWait() for details. This may be superstition. Or not. // We trust the WAIT_TIMEOUT indication and don't track the elapsed wait time // with os::javaTimeNanos(). Furthermore, we assume that spurious returns from // ::WaitForSingleObject() caused by latent ::setEvent() operations will tend // to happen early in the wait interval. Specifically, after a spurious wakeup (rv == // WAIT_OBJECT_0 but _Event is still < 0) we don't bother to recompute Millis to compensate // for the already waited time. This policy does not admit any new outcomes. // In the future, however, we might want to track the accumulated wait time and // adjust Millis accordingly if we encounter a spurious wakeup.
constint MAXTIMEOUT = 0x10000000;
DWORD rv = WAIT_TIMEOUT; while (_Event < 0 && Millis > 0) {
DWORD prd = Millis; // set prd = MAX (Millis, MAXTIMEOUT) if (Millis > MAXTIMEOUT) {
prd = MAXTIMEOUT;
}
HighResolutionInterval *phri = NULL; if (!ForceTimeHighResolution) {
phri = new HighResolutionInterval(prd);
}
rv = ::WaitForSingleObject(_ParkHandle, prd);
assert(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT, "WaitForSingleObject failed"); if (rv == WAIT_TIMEOUT) {
Millis -= prd;
} delete phri; // if it is NULL, harmless
}
v = _Event;
_Event = 0; // see comment at end of PlatformEvent::park() below:
OrderAccess::fence(); // If we encounter a nearly simultaneous timeout expiry and unpark() // we return OS_OK indicating we awoke via unpark(). // Implementor's license -- returning OS_TIMEOUT would be equally valid, however. return (v >= 0) ? OS_OK : OS_TIMEOUT;
}
void PlatformEvent::park() { // Transitions for _Event: // -1 => -1 : illegal // 1 => 0 : pass - return immediately // 0 => -1 : block; then set _Event to 0 before returning
guarantee(_ParkHandle != NULL, "Invariant"); // Invariant: Only the thread associated with the Event/PlatformEvent // may call park(). // Consider: use atomic decrement instead of CAS-loop int v; for (;;) {
v = _Event; if (Atomic::cmpxchg(&_Event, v, v-1) == v) break;
}
guarantee((v == 0) || (v == 1), "invariant"); if (v != 0) return;
// Do this the hard way by blocking ... // TODO: consider a brief spin here, gated on the success of recent // spin attempts by this thread. while (_Event < 0) {
DWORD rv = ::WaitForSingleObject(_ParkHandle, INFINITE);
assert(rv == WAIT_OBJECT_0, "WaitForSingleObject failed");
}
// Usually we'll find _Event == 0 at this point, but as // an optional optimization we clear it, just in case can // multiple unpark() operations drove _Event up to 1.
_Event = 0;
OrderAccess::fence();
guarantee(_Event >= 0, "invariant");
}
// Transitions for _Event: // 0 => 1 : just return // 1 => 1 : just return // -1 => either 0 or 1; must signal target thread // That is, we can safely transition _Event from -1 to either // 0 or 1. // See also: "Semaphores in Plan 9" by Mullender & Cox // // Note: Forcing a transition from "-1" to "1" on an unpark() means // that it will take two back-to-back park() calls for the owning // thread to block. This has the benefit of forcing a spurious return // from the first park() call after an unpark() call which will help // shake out uses of park() and unpark() without condition variables.
// The Windows implementation of Park is very straightforward: Basic // operations on Win32 Events turn out to have the right semantics to // use them directly.
void Parker::park(bool isAbsolute, jlong time) {
guarantee(_ParkHandle != NULL, "invariant"); // First, demultiplex/decode time arguments if (time < 0) { // don't wait return;
} elseif (time == 0 && !isAbsolute) {
time = INFINITE;
} elseif (isAbsolute) {
time -= os::javaTimeMillis(); // convert to relative time if (time <= 0) { // already elapsed return;
}
} else { // relative
time /= 1000000; // Must coarsen from nanos to millis if (time == 0) { // Wait for the minimal time unit if zero
time = 1;
}
}
JavaThread* thread = JavaThread::current();
// Don't wait if interrupted or already triggered if (thread->is_interrupted(false) ||
WaitForSingleObject(_ParkHandle, 0) == WAIT_OBJECT_0) {
ResetEvent(_ParkHandle); return;
} else {
ThreadBlockInVM tbivm(thread);
OSThreadWaitState osts(thread->osthread(), false/* not Object.wait() */);
// Must already be locked int PlatformMonitor::wait(jlong millis) {
assert(millis >= 0, "negative timeout"); int ret = OS_TIMEOUT; int status = SleepConditionVariableCS(&_cond, &_mutex,
millis == 0 ? INFINITE : millis); if (status != 0) {
ret = OS_OK;
} #ifndef PRODUCT else {
DWORD err = GetLastError();
assert(err == ERROR_TIMEOUT, "SleepConditionVariableCS: %ld:", err);
} #endif return ret;
}
// Run the specified command in a separate process. Return its exit value, // or -1 on failure (e.g. can't create a new process). int os::fork_and_exec(constchar* cmd) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD exit_code;
// get context capable handle for thread
get_thread_handle_for_extended_context(&h, _thread->osthread()->thread_id());
// sanity if (h == NULL || h == INVALID_HANDLE_VALUE) { return;
}
// suspend the thread if (do_suspend(&h)) {
ctxt.ContextFlags = sampling_context_flags; // get thread context
GetThreadContext(h, &ctxt);
SuspendedThreadTaskContext context(_thread, &ctxt); // pass context to Thread Sampling impl
do_task(context); // resume thread
do_resume(&h);
}
// close handle
CloseHandle(h);
}
bool os::start_debugging(char *buf, int buflen) { int len = (int)strlen(buf); char *p = &buf[len];
jio_snprintf(p, buflen-len, "\n\n" "Do you want to debug the problem?\n\n" "To debug, attach Visual Studio to process %d; then switch to thread 0x%x\n" "Select 'Yes' to launch Visual Studio automatically (PATH must include msdev)\n" "Otherwise, select 'No' to abort...",
os::current_process_id(), os::current_thread_id());
if (yes) { // os::breakpoint() calls DebugBreak(), which causes a breakpoint // exception. If VM is running inside a debugger, the debugger will // catch the exception. Otherwise, the breakpoint exception will reach // the default windows exception handler, which can spawn a debugger and // automatically attach to the dying VM.
os::breakpoint();
yes = false;
} return yes;
}
// Builds a platform dependent Agent_OnLoad_<lib_name> function name // which is used to find statically linked in agents. // Additionally for windows, takes into account __stdcall names. // Parameters: // sym_name: Symbol in library we are looking for // lib_name: Name of library to look in, NULL for shared libs. // is_absolute_path == true if lib_name is absolute path to agent // such as "C:/a/b/L.dll" // == false if only the base name of the library is passed in // such as "L" char* os::build_agent_function_name(constchar *sym_name, constchar *lib_name, bool is_absolute_path) { char *agent_entry_name;
size_t len;
size_t name_len;
size_t prefix_len = strlen(JNI_LIB_PREFIX);
size_t suffix_len = strlen(JNI_LIB_SUFFIX); constchar *start;
if (lib_name != NULL) {
len = name_len = strlen(lib_name); if (is_absolute_path) { // Need to strip path, prefix and suffix if ((start = strrchr(lib_name, *os::file_separator())) != NULL) {
lib_name = ++start;
} else { // Need to check for drive prefix if ((start = strchr(lib_name, ':')) != NULL) {
lib_name = ++start;
}
} if (len <= (prefix_len + suffix_len)) { return NULL;
}
lib_name += prefix_len;
name_len = strlen(lib_name) - suffix_len;
}
}
len = (lib_name != NULL ? name_len : 0) + strlen(sym_name) + 2;
agent_entry_name = NEW_C_HEAP_ARRAY_RETURN_NULL(char, len, mtThread); if (agent_entry_name == NULL) { return NULL;
} if (lib_name != NULL) { constchar *p = strrchr(sym_name, '@'); if (p != NULL && p != sym_name) { // sym_name == _Agent_OnLoad@XX
strncpy(agent_entry_name, sym_name, (p - sym_name));
agent_entry_name[(p-sym_name)] = '\0'; // agent_entry_name == _Agent_OnLoad
strcat(agent_entry_name, "_");
strncat(agent_entry_name, lib_name, name_len);
strcat(agent_entry_name, p); // agent_entry_name == _Agent_OnLoad_lib_name@XX
} else {
strcpy(agent_entry_name, sym_name);
strcat(agent_entry_name, "_");
strncat(agent_entry_name, lib_name, name_len);
}
} else {
strcpy(agent_entry_name, sym_name);
} return agent_entry_name;
}
*/ int os::get_signal_number(constchar* name) { staticconststruct { constchar* name; int number;
} siglabels [] = // derived from version 6.0 VC98/include/signal.h
{"ABRT", SIGABRT, // abnormal termination triggered by abort cl "FPE", SIGFPE, // floating point exception "SEGV", SIGSEGV, // segment violation "INT", SIGINT, // interrupt "TERM", SIGTERM, // software term signal from kill "BREAK", SIGBREAK, // Ctrl-Break sequence "ILL", SIGILL}; // illegal instruction for (unsigned i = 0; i < ARRAY_SIZE(siglabels); ++i) { if (strcmp(name, siglabels[i].name) == 0) { return siglabels[i].number;
}
} return -1;
}
// Fast current thread access
int os::win32::_thread_ptr_offset = 0;
staticvoid call_wrapper_dummy() {}
// We need to call the os_exception_wrapper once so that it sets // up the offset from FS of the thread pointer. void os::win32::initialize_thread_ptr_offset() {
os::os_exception_wrapper((java_call_t)call_wrapper_dummy,
NULL, methodHandle(), NULL, NULL);
}
// Given a pointer pointing into an allocation (an area allocated with VirtualAlloc), // return information about that allocation. bool os::win32::find_mapping(address addr, mapping_info_t* mi) { // Query at addr to find allocation base; then, starting at allocation base, // query all regions, until we either find the next allocation or a free area.
ZeroMemory(mi, sizeof(mapping_info_t));
MEMORY_BASIC_INFORMATION minfo;
address allocation_base = NULL;
address allocation_end = NULL; bool rc = false; if (checkedVirtualQuery(addr, &minfo)) { if (minfo.State != MEM_FREE) {
allocation_base = (address)minfo.AllocationBase;
allocation_end = allocation_base; // Iterate through all regions in this allocation to find its end. While we are here, also count things. for (;;) { bool rc = checkedVirtualQuery(allocation_end, &minfo); if (rc == false || // VirtualQuery error, end of allocation?
minfo.State == MEM_FREE || // end of allocation, free memory follows
(address)minfo.AllocationBase != allocation_base) // end of allocation, a new one starts
{ break;
} const size_t region_size = minfo.RegionSize;
mi->regions ++; if (minfo.State == MEM_COMMIT) {
mi->committed_size += minfo.RegionSize;
}
allocation_end += region_size;
} if (allocation_base != NULL && allocation_end > allocation_base) {
mi->base = allocation_base;
mi->size = allocation_end - allocation_base;
rc = true;
}
}
} #ifdef ASSERT if (rc) {
assert(mi->size > 0 && mi->size >= mi->committed_size, "Sanity");
assert(addr >= mi->base && addr < mi->base + mi->size, "Sanity");
assert(mi->regions > 0, "Sanity");
} #endif return rc;
}
// Helper for print_one_mapping: print n words, both as hex and ascii. // Use Safefetch for all values. staticvoid print_snippet(constvoid* p, outputStream* st) { staticconstint num_words = LP64_ONLY(3) NOT_LP64(6); staticconstint num_bytes = num_words * sizeof(int);
intptr_t v[num_words]; constint errval = 0xDE210244; for (int i = 0; i < num_words; i++) {
v[i] = SafeFetchN((intptr_t*)p + i, errval); if (v[i] == errval &&
SafeFetchN((intptr_t*)p + i, ~errval) == ~errval) { return;
}
}
st->put('['); for (int i = 0; i < num_words; i++) {
st->print(INTPTR_FORMAT " ", v[i]);
} constchar* b = (char*)v;
st->put('\"'); for (int i = 0; i < num_bytes; i++) {
st->put(::isgraph(b[i]) ? b[i] : '.');
}
st->put('\"');
st->put(']');
}
// Helper function for print_memory_mappings: // Given a MEMORY_BASIC_INFORMATION, containing information about a non-free region: // print out all regions in that allocation. If any of those regions // fall outside the given range [start, end), indicate that in the output. // Return the pointer to the end of the allocation. static address print_one_mapping(MEMORY_BASIC_INFORMATION* minfo, address start, address end, outputStream* st) { // Print it like this: // // Base: <xxxxx>: [xxxx - xxxx], state=MEM_xxx, prot=x, type=MEM_xxx (region 1) // [xxxx - xxxx], state=MEM_xxx, prot=x, type=MEM_xxx (region 2)
assert(minfo->State != MEM_FREE, "Not inside an allocation.");
address allocation_base = (address)minfo->AllocationBase; #define IS_IN(p) (p >= start && p < end) bool first_line = true; bool is_dll = false; for(;;) { if (first_line) {
st->print("Base " PTR_FORMAT ": ", p2i(allocation_base));
} else {
st->print_raw(NOT_LP64 (" ")
LP64_ONLY(" "));
}
address region_start = (address)minfo->BaseAddress;
address region_end = region_start + minfo->RegionSize;
assert(region_end > region_start, "Sanity"); if (region_end <= start) {
st->print("<outside range> ");
} elseif (region_start >= end) {
st->print("<outside range> ");
} elseif (!IS_IN(region_start) || !IS_IN(region_end - 1)) {
st->print("<partly outside range> ");
}
st->print("[" PTR_FORMAT "-" PTR_FORMAT "), state=", p2i(region_start), p2i(region_end)); switch (minfo->State) { case MEM_COMMIT: st->print_raw("MEM_COMMIT "); break; case MEM_FREE: st->print_raw("MEM_FREE "); break; case MEM_RESERVE: st->print_raw("MEM_RESERVE"); break; default: st->print("%x?", (unsigned)minfo->State);
}
st->print(", prot=%3x, type=", (unsigned)minfo->Protect); switch (minfo->Type) { case MEM_IMAGE: st->print_raw("MEM_IMAGE "); break; case MEM_MAPPED: st->print_raw("MEM_MAPPED "); break; case MEM_PRIVATE: st->print_raw("MEM_PRIVATE"); break; default: st->print("%x?", (unsigned)minfo->State);
} // At the start of every allocation, print some more information about this mapping. // Notes: // - this could be beefed up a lot, similar to os::print_location // - for now we just query the allocation start point. This may be confusing for cases where // the kernel merges multiple mappings. if (first_line) { char buf[MAX_PATH]; if (os::dll_address_to_library_name(allocation_base, buf, sizeof(buf), nullptr)) {
st->print(", %s", buf);
is_dll = true;
}
} // If memory is accessible, and we do not know anything else about it, print a snippet if (!is_dll &&
minfo->State == MEM_COMMIT &&
!(minfo->Protect & PAGE_NOACCESS || minfo->Protect & PAGE_GUARD)) {
st->print_raw(", ");
print_snippet(region_start, st);
}
st->cr(); // Next region... bool rc = checkedVirtualQuery(region_end, minfo); if (rc == false || // VirtualQuery error, end of allocation?
(minfo->State == MEM_FREE) || // end of allocation, free memory follows
((address)minfo->AllocationBase != allocation_base) || // end of allocation, a new one starts
(region_end > end)) // end of range to print.
{ return region_end;
}
first_line = false;
} #undef IS_IN
ShouldNotReachHere(); return NULL;
}
void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {
MEMORY_BASIC_INFORMATION minfo;
address start = (address)addr;
address end = start + bytes;
address p = start; if (p == nullptr) { // Lets skip the zero pages.
p += os::vm_allocation_granularity();
}
address p2 = p; // guard against wraparounds int fuse = 0;
while (p < end && p >= p2) {
p2 = p; // Probe for the next mapping. if (checkedVirtualQuery(p, &minfo)) { if (minfo.State != MEM_FREE) { // Found one. Print it out.
address p2 = print_one_mapping(&minfo, start, end, st);
assert(p2 > p, "Sanity");
p = p2;
} else { // Note: for free regions, most of MEMORY_BASIC_INFORMATION is undefined. // Only region dimensions are not: use those to jump to the end of // the free range.
address region_start = (address)minfo.BaseAddress;
address region_end = region_start + minfo.RegionSize;
assert(p >= region_start && p < region_end, "Sanity");
p = region_end;
}
} else { // MSDN doc on VirtualQuery is unclear about what it means if it returns an error. // In particular, whether querying an address outside any mappings would report // a MEM_FREE region or just return an error. From experiments, it seems to return // a MEM_FREE region for unmapped areas in valid address space and an error if we // are outside valid address space. // Here, we advance the probe pointer by alloc granularity. But if the range to print // is large, this may take a long time. Therefore lets stop right away if the address // is outside of what we know are valid addresses on Windows. Also, add a loop fuse. staticconst address end_virt = (address)(LP64_ONLY(0x7ffffffffffULL) NOT_LP64(3*G)); if (p >= end_virt) { break;
} else { // Advance probe pointer, but with a fuse to break long loops. if (fuse++ == 100000) { break;
}
p += os::vm_allocation_granularity();
}
}
}
}
void os::print_user_info(outputStream* st) { // not implemented yet
}
void os::print_active_locale(outputStream* st) { // not implemented yet
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.144Bemerkung:
(vorverarbeitet am 2026-06-10)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.