// processinfo_win32.cpp /* Copyright 2009 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kControl #include "mongo/pch.h" #include #include #include "mongo/util/processinfo.h" #include "mongo/util/log.h" using namespace std; namespace mongo { // dynamically link to psapi.dll (in case this version of Windows // does not support what we need) struct PsApiInit { bool supported; typedef BOOL (WINAPI *pQueryWorkingSetEx)(HANDLE hProcess, PVOID pv, DWORD cb); pQueryWorkingSetEx QueryWSEx; PsApiInit() { HINSTANCE psapiLib = LoadLibrary( TEXT("psapi.dll") ); if (psapiLib) { QueryWSEx = reinterpret_cast ( GetProcAddress( psapiLib, "QueryWorkingSetEx" ) ); if (QueryWSEx) { supported = true; return; } } supported = false; } }; static PsApiInit* psapiGlobal = NULL; int _wconvertmtos( SIZE_T s ) { return (int)( s / ( 1024 * 1024 ) ); } ProcessInfo::ProcessInfo( ProcessId pid ) { } ProcessInfo::~ProcessInfo() { } bool ProcessInfo::supported() { return true; } int ProcessInfo::getVirtualMemorySize() { MEMORYSTATUSEX mse; mse.dwLength = sizeof(mse); verify( GlobalMemoryStatusEx( &mse ) ); DWORDLONG x = (mse.ullTotalVirtual - mse.ullAvailVirtual) / (1024 * 1024) ; verify( x <= 0x7fffffff ); return (int) x; } int ProcessInfo::getResidentSize() { PROCESS_MEMORY_COUNTERS pmc; verify( GetProcessMemoryInfo( GetCurrentProcess() , &pmc, sizeof(pmc) ) ); return _wconvertmtos( pmc.WorkingSetSize ); } void ProcessInfo::getExtraInfo(BSONObjBuilder& info) { MEMORYSTATUSEX mse; mse.dwLength = sizeof(mse); PROCESS_MEMORY_COUNTERS pmc; if( GetProcessMemoryInfo( GetCurrentProcess() , &pmc, sizeof(pmc) ) ) { info.append("page_faults", static_cast(pmc.PageFaultCount)); info.append("usagePageFileMB", static_cast(pmc.PagefileUsage / 1024 / 1024)); } if( GlobalMemoryStatusEx( &mse ) ) { info.append("totalPageFileMB", static_cast(mse.ullTotalPageFile / 1024 / 1024)); info.append("availPageFileMB", static_cast(mse.ullAvailPageFile / 1024 / 1024)); info.append("ramMB", static_cast(mse.ullTotalPhys / 1024 / 1024)); } #ifndef _WIN64 BOOL wow64Process; BOOL retWow64 = IsWow64Process(GetCurrentProcess(), &wow64Process); info.append("wow64Process", static_cast(retWow64 && wow64Process)); #endif } void ProcessInfo::SystemInfo::collectSystemInfo() { BSONObjBuilder bExtra; stringstream verstr; OSVERSIONINFOEX osvi; // os version MEMORYSTATUSEX mse; // memory stats SYSTEM_INFO ntsysinfo; //system stats // get basic processor properties GetNativeSystemInfo( &ntsysinfo ); addrSize = (ntsysinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 64 : 32); numCores = ntsysinfo.dwNumberOfProcessors; pageSize = static_cast(ntsysinfo.dwPageSize); bExtra.append("pageSize", static_cast(pageSize)); // get memory info mse.dwLength = sizeof( mse ); if ( GlobalMemoryStatusEx( &mse ) ) { memSize = mse.ullTotalPhys; } // get OS version info ZeroMemory( &osvi, sizeof( osvi ) ); osvi.dwOSVersionInfoSize = sizeof( osvi ); if ( GetVersionEx( (OSVERSIONINFO*)&osvi ) ) { verstr << osvi.dwMajorVersion << "." << osvi.dwMinorVersion; if ( osvi.wServicePackMajor ) verstr << " SP" << osvi.wServicePackMajor; verstr << " (build " << osvi.dwBuildNumber << ")"; osName = "Microsoft "; switch ( osvi.dwMajorVersion ) { case 6: switch ( osvi.dwMinorVersion ) { case 3: if ( osvi.wProductType == VER_NT_WORKSTATION ) osName += "Windows 8.1"; else osName += "Windows Server 2012 R2"; break; case 2: if ( osvi.wProductType == VER_NT_WORKSTATION ) osName += "Windows 8"; else osName += "Windows Server 2012"; break; case 1: if ( osvi.wProductType == VER_NT_WORKSTATION ) osName += "Windows 7"; else osName += "Windows Server 2008 R2"; // Windows 6.1 is either Windows 7 or Windows 2008 R2. There is no SP2 for // either of these two operating systems, but the check will hold if one // were released. This code assumes that SP2 will include fix for // http://support.microsoft.com/kb/2731284. // if ((osvi.wServicePackMajor >= 0) && (osvi.wServicePackMajor < 2)) { fileZeroNeeded = true; } break; case 0: if ( osvi.wProductType == VER_NT_WORKSTATION ) osName += "Windows Vista"; else osName += "Windows Server 2008"; break; default: osName += "Windows NT version "; osName += verstr.str(); break; } break; case 5: switch ( osvi.dwMinorVersion ) { case 2: osName += "Windows Server 2003"; break; case 1: osName += "Windows XP"; break; case 0: if ( osvi.wProductType == VER_NT_WORKSTATION ) osName += "Windows 2000 Professional"; else osName += "Windows 2000 Server"; break; default: osName += "Windows NT version "; osName += verstr.str(); break; } break; } } else { // unable to get any version data osName += "Windows NT"; } if ( ntsysinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) { cpuArch = "x86_64"; } else if ( ntsysinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL ) { cpuArch = "x86"; } else if ( ntsysinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 ) { cpuArch = "ia64"; } else { cpuArch = "unknown"; } osType = "Windows"; osVersion = verstr.str(); hasNuma = checkNumaEnabled(); _extraStats = bExtra.obj(); if (psapiGlobal == NULL) { psapiGlobal = new PsApiInit(); } } bool ProcessInfo::checkNumaEnabled() { typedef BOOL(WINAPI *LPFN_GLPI)( PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); DWORD returnLength = 0; DWORD numaNodeCount = 0; scoped_array buffer; LPFN_GLPI glpi(reinterpret_cast(GetProcAddress( GetModuleHandleW(L"kernel32"), "GetLogicalProcessorInformation"))); if (glpi == NULL) { return false; } DWORD returnCode = 0; do { returnCode = glpi(buffer.get(), &returnLength); if (returnCode == FALSE) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { buffer.reset(reinterpret_cast( new BYTE[returnLength])); } else { DWORD gle = GetLastError(); warning() << "GetLogicalProcessorInformation failed with " << errnoWithDescription(gle); return false; } } } while (returnCode == FALSE); PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = buffer.get(); unsigned int byteOffset = 0; while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) { if (ptr->Relationship == RelationNumaNode) { // Non-NUMA systems report a single record of this type. numaNodeCount++; } byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ptr++; } // For non-NUMA machines, the count is 1 return numaNodeCount > 1; } bool ProcessInfo::blockCheckSupported() { return psapiGlobal->supported; } bool ProcessInfo::blockInMemory(const void* start) { #if 0 // code for printing out page fault addresses and pc's -- // this could be useful for targetting heavy pagefault locations in the code static BOOL bstat = InitializeProcessForWsWatch( GetCurrentProcess() ); PSAPI_WS_WATCH_INFORMATION_EX wiex[30]; DWORD bufsize = sizeof(wiex); bstat = GetWsChangesEx( GetCurrentProcess(), &wiex[0], &bufsize ); if (bstat) { for (int i=0; i<30; i++) { if (wiex[i].BasicInfo.FaultingPc == 0) break; cout << "faulting pc = " << wiex[i].BasicInfo.FaultingPc << " address = " << wiex[i].BasicInfo.FaultingVa << " thread id = " << wiex[i].FaultingThreadId << endl; } } #endif PSAPI_WORKING_SET_EX_INFORMATION wsinfo; wsinfo.VirtualAddress = const_cast(start); BOOL result = psapiGlobal->QueryWSEx( GetCurrentProcess(), &wsinfo, sizeof(wsinfo) ); if ( result ) if ( wsinfo.VirtualAttributes.Valid ) return true; return false; } bool ProcessInfo::pagesInMemory(const void* start, size_t numPages, vector* out) { out->resize(numPages); scoped_array wsinfo( new PSAPI_WORKING_SET_EX_INFORMATION[numPages]); const void* startOfFirstPage = alignToStartOfPage(start); for (size_t i = 0; i < numPages; i++) { wsinfo[i].VirtualAddress = reinterpret_cast( reinterpret_cast(startOfFirstPage) + i * getPageSize()); } BOOL result = psapiGlobal->QueryWSEx(GetCurrentProcess(), wsinfo.get(), sizeof(PSAPI_WORKING_SET_EX_INFORMATION) * numPages); if (!result) return false; for (size_t i = 0; i < numPages; ++i) { (*out)[i] = wsinfo[i].VirtualAttributes.Valid ? 1 : 0; } return true; } }