From 6072e63bec47e96ec6300c97029cef806ac59393 Mon Sep 17 00:00:00 2001 From: Burlen Loring <bloring@lbl.gov> Date: Mon, 1 Oct 2012 14:48:39 -0700 Subject: [PATCH] SystemInformation: support for resource limits Add methods to report host memory total, host memory available, process memory available, host memory used, and process memory used. In this context memory is unavailable if there are resource limits in place that would prevent its use. Such resource limits assumed to be applied on a per host basis both to cooperatively operating process groups, such as mpi programms running in parallel, and to individual processes. When reporting host memory available consult an application specified environment variable. When reporting process memory available consult unix resource rlimits and an application specified environment variable. The environmant variables provide a means of communicating resource limits that are being applied in a non-standard way. Change-Id: Ifb3b0fdaab8db0ab87140fa2dcafad3c51e2d874 --- CMakeLists.txt | 65 ++++- SystemInformation.cxx | 563 ++++++++++++++++++++++++++++++-------- SystemInformation.hxx.in | 49 +++- kwsysPlatformTestsCXX.cxx | 66 +++++ testSystemInformation.cxx | 34 ++- 5 files changed, 651 insertions(+), 126 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 314ebd6d..777d76f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -509,19 +509,28 @@ ENDIF(KWSYS_USE_FundamentalType) IF(KWSYS_USE_IOStream) # Determine whether iostreams support long long. + SET(KWSYS_PLATFORM_CXX_TEST_DEFINES + -DKWSYS_IOS_USE_ANSI=${KWSYS_IOS_USE_ANSI} + -DKWSYS_IOS_HAVE_STD=${KWSYS_IOS_HAVE_STD}) IF(KWSYS_CXX_HAS_LONG_LONG) - SET(KWSYS_PLATFORM_CXX_TEST_DEFINES - -DKWSYS_IOS_USE_ANSI=${KWSYS_IOS_USE_ANSI} - -DKWSYS_IOS_HAVE_STD=${KWSYS_IOS_HAVE_STD}) KWSYS_PLATFORM_CXX_TEST(KWSYS_IOS_HAS_ISTREAM_LONG_LONG "Checking if istream supports long long" DIRECT) KWSYS_PLATFORM_CXX_TEST(KWSYS_IOS_HAS_OSTREAM_LONG_LONG "Checking if ostream supports long long" DIRECT) - SET(KWSYS_PLATFORM_CXX_TEST_DEFINES) ELSE() SET(KWSYS_IOS_HAS_ISTREAM_LONG_LONG 0) SET(KWSYS_IOS_HAS_OSTREAM_LONG_LONG 0) ENDIF() + IF(KWSYS_CXX_HAS___INT64) + KWSYS_PLATFORM_CXX_TEST(KWSYS_IOS_HAS_ISTREAM___INT64 + "Checking if istream supports __int64" DIRECT) + KWSYS_PLATFORM_CXX_TEST(KWSYS_IOS_HAS_OSTREAM___INT64 + "Checking if ostream supports __int64" DIRECT) + ELSE() + SET(KWSYS_IOS_HAS_ISTREAM___INT64 0) + SET(KWSYS_IOS_HAS_OSTREAM___INT64 0) + ENDIF() + SET(KWSYS_PLATFORM_CXX_TEST_DEFINES) ENDIF(KWSYS_USE_IOStream) IF(KWSYS_NAMESPACE MATCHES "^kwsys$") @@ -588,6 +597,54 @@ IF(KWSYS_USE_SystemInformation) ENDIF() ENDIF() ENDIF() + IF(KWSYS_LFS_AVAILABLE AND NOT KWSYS_LFS_DISABLE) + SET(KWSYS_PLATFORM_CXX_TEST_DEFINES -DKWSYS_HAS_LFS=1) + ENDIF() + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_RLIMIT64 + "Checking whether CXX compiler has rlimit64" DIRECT) + SET(KWSYS_PLATFORM_CXX_TEST_DEFINES) + IF(KWSYS_CXX_HAS_RLIMIT64) + SET_PROPERTY(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_CXX_HAS_RLIMIT64=1) + ENDIF() + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_ATOL + "Checking whether CXX compiler has atol" DIRECT) + IF(KWSYS_CXX_HAS_ATOL) + SET_PROPERTY(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_CXX_HAS_ATOL=1) + ENDIF() + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_ATOLL + "Checking whether CXX compiler has atoll" DIRECT) + IF(KWSYS_CXX_HAS_ATOLL) + SET_PROPERTY(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_CXX_HAS_ATOLL=1) + ENDIF() + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS__ATOI64 + "Checking whether CXX compiler has _atoi64" DIRECT) + IF(KWSYS_CXX_HAS__ATOI64) + SET_PROPERTY(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_CXX_HAS__ATOI64=1) + ENDIF() + IF(KWSYS_USE___INT64) + SET_PROPERTY(SOURCE SystemInformation.cxx testSystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_USE___INT64=1) + ENDIF() + IF(KWSYS_USE_LONG_LONG) + SET_PROPERTY(SOURCE SystemInformation.cxx testSystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_USE_LONG_LONG=1) + ENDIF() + IF(KWSYS_IOS_HAS_OSTREAM_LONG_LONG) + SET_PROPERTY(SOURCE SystemInformation.cxx testSystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_IOS_HAS_OSTREAM_LONG_LONG=1) + ENDIF() + IF(KWSYS_IOS_HAS_OSTREAM___INT64) + SET_PROPERTY(SOURCE SystemInformation.cxx testSystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_IOS_HAS_OSTREAM___INT64=1) + ENDIF() + IF(KWSYS_BUILD_SHARED) + SET_PROPERTY(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_BUILD_SHARED=1) + ENDIF() ENDIF() #----------------------------------------------------------------------------- diff --git a/SystemInformation.cxx b/SystemInformation.cxx index a9efe7b3..767f820b 100644 --- a/SystemInformation.cxx +++ b/SystemInformation.cxx @@ -9,7 +9,9 @@ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more information. ============================================================================*/ -#ifdef _WIN32 + +#if defined(_WIN32) +# define NOMINMAX // use our min,max # if !defined(_WIN32_WINNT) && !(defined(_MSC_VER) && _MSC_VER < 1300) # define _WIN32_WINNT 0x0501 # endif @@ -54,6 +56,7 @@ #if defined(_WIN32) # include <windows.h> +# include <errno.h> # if defined(KWSYS_SYS_HAS_PSAPI) # include <psapi.h> # endif @@ -64,6 +67,7 @@ typedef int siginfo_t; # include <sys/types.h> # include <sys/time.h> # include <sys/utsname.h> // int uname(struct utsname *buf); +# include <sys/resource.h> // getrlimit # include <unistd.h> # include <signal.h> # include <fcntl.h> @@ -71,15 +75,15 @@ typedef int siginfo_t; #endif #ifdef __APPLE__ -#include <sys/sysctl.h> -#include <mach/vm_statistics.h> -#include <mach/host_info.h> -#include <mach/mach.h> -#include <mach/mach_types.h> -#include <fenv.h> -#include <sys/socket.h> -#include <netdb.h> -#include <netinet/in.h> +# include <sys/sysctl.h> +# include <mach/vm_statistics.h> +# include <mach/host_info.h> +# include <mach/mach.h> +# include <mach/mach_types.h> +# include <fenv.h> +# include <sys/socket.h> +# include <netdb.h> +# include <netinet/in.h> # if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__-0 >= 1050 # include <execinfo.h> # define KWSYS_SYSTEMINFORMATION_HAVE_BACKTRACE @@ -95,6 +99,13 @@ typedef int siginfo_t; # include <execinfo.h> # define KWSYS_SYSTEMINFORMATION_HAVE_BACKTRACE # endif +# if defined(KWSYS_CXX_HAS_RLIMIT64) +typedef struct rlimit64 ResourceLimitType; +# define GetResourceLimit getrlimit64 +# else +typedef struct rlimit ResourceLimitType; +# define GetResourceLimit getrlimit +# endif #elif defined( __hpux ) # include <sys/param.h> # include <sys/pstat.h> @@ -105,7 +116,7 @@ typedef int siginfo_t; #endif #ifdef __HAIKU__ -#include <OS.h> +# include <OS.h> #endif #include <memory.h> @@ -114,8 +125,38 @@ typedef int siginfo_t; #include <string.h> #include <ctype.h> // int isdigit(int c); +#if defined(KWSYS_USE_LONG_LONG) +# if defined(KWSYS_IOS_HAS_OSTREAM_LONG_LONG) +# define iostreamLongLong(x) (x) +# else +# define iostreamLongLong(x) ((long)x) +# endif +#elif defined(KWSYS_USE___INT64) +# if defined(KWSYS_IOS_HAS_OSTREAM___INT64) +# define iostreamLongLong(x) (x) +# else +# define iostreamLongLong(x) ((long)x) +# endif +#else +# error "No Long Long" +#endif + +#if defined(KWSYS_CXX_HAS_ATOLL) +# define atoLongLong atoll +#else +# if defined(KWSYS_CXX_HAS__ATOI64) +# define atoLongLong _atoi64 +# elif defined(KWSYS_CXX_HAS_ATOL) +# define atoLongLong atol +# else +# define atoLongLong atoi +# endif +#endif + namespace KWSYS_NAMESPACE { +template<typename T> +T min(T a, T b){ return a<b ? a : b; } extern "C" { typedef void (*SigAction)(int,siginfo_t*,void*); } @@ -168,8 +209,14 @@ public: LongLong GetProcessId(); // Retrieve memory information in kib - LongLong GetMemoryTotal(); - LongLong GetMemoryUsed(); + LongLong GetHostMemoryTotal(); + LongLong GetHostMemoryAvailable(const char *envVarName); + LongLong GetHostMemoryUsed(); + + LongLong GetProcMemoryAvailable( + const char *hostLimitEnvVarName, + const char *procLimitEnvVarName); + LongLong GetProcMemoryUsed(); // enable/disable stack trace signal handler. static @@ -448,11 +495,7 @@ const char * SystemInformation::GetHostname() kwsys_stl::string SystemInformation::GetFullyQualifiedDomainName() { kwsys_stl::string fqdn; - int ierr=this->Implementation->GetFullyQualifiedDomainName(fqdn); - if (ierr) - { - fqdn="localhost"; - } + this->Implementation->GetFullyQualifiedDomainName(fqdn); return fqdn; } @@ -552,27 +595,54 @@ size_t SystemInformation::GetAvailablePhysicalMemory() return this->Implementation->GetAvailablePhysicalMemory(); } -kwsys_stl::string SystemInformation::GetMemoryDescription() +kwsys_stl::string SystemInformation::GetMemoryDescription( + const char *hostLimitEnvVarName, + const char *procLimitEnvVarName) { kwsys_stl::ostringstream oss; oss - << this->GetTotalPhysicalMemory() - << " MB physical " - << this->GetTotalVirtualMemory() - << " MB virtual"; - + << "Host Total: " + << iostreamLongLong(this->GetHostMemoryTotal()) + << " KiB, Host Available: " + << iostreamLongLong(this->GetHostMemoryAvailable(hostLimitEnvVarName)) + << " KiB, Process Available: " + << iostreamLongLong( + this->GetProcMemoryAvailable(hostLimitEnvVarName,procLimitEnvVarName)) + << " KiB"; return oss.str(); } -// Get total system RAM in units of KiB. -SystemInformation::LongLong SystemInformation::GetMemoryTotal() +// host memory info in units of KiB. +SystemInformation::LongLong SystemInformation::GetHostMemoryTotal() { - return this->Implementation->GetMemoryTotal(); + return this->Implementation->GetHostMemoryTotal(); } -SystemInformation::LongLong SystemInformation::GetMemoryUsed() +SystemInformation::LongLong +SystemInformation::GetHostMemoryAvailable(const char *hostLimitEnvVarName) +{ + return this->Implementation->GetHostMemoryAvailable(hostLimitEnvVarName); +} + +SystemInformation::LongLong SystemInformation::GetHostMemoryUsed() { - return this->Implementation->GetMemoryUsed(); + return this->Implementation->GetHostMemoryUsed(); +} + +// process memory info in units of KiB. +SystemInformation::LongLong +SystemInformation::GetProcMemoryAvailable( + const char *hostLimitEnvVarName, + const char *procLimitEnvVarName) +{ + return this->Implementation->GetProcMemoryAvailable( + hostLimitEnvVarName, + procLimitEnvVarName); +} + +SystemInformation::LongLong SystemInformation::GetProcMemoryUsed() +{ + return this->Implementation->GetProcMemoryUsed(); } SystemInformation::LongLong SystemInformation::GetProcessId() @@ -672,30 +742,48 @@ void SystemInformation::RunMemoryCheck() // Default value = 0xff if HT is not supported -//***************************************************************************** +// ***************************************************************************** int LoadLines( - const char *fileName, + FILE *file, kwsys_stl::vector<kwsys_stl::string> &lines) { // Load each line in the given file into a the vector. int nRead=0; const int bufSize=1024; char buf[bufSize]={'\0'}; - kwsys_stl::ifstream file(fileName); - if (!file.is_open()) + while (!feof(file) && !ferror(file)) + { + errno=0; + if (fgets(buf,bufSize,file) == 0) + { + if (ferror(file) && (errno==EINTR)) + { + clearerr(file); + } + continue; + } + lines.push_back(buf); + ++nRead; + } + if (ferror(file)) { return 0; } - while(file.good()) + return nRead; +} + +// ***************************************************************************** +int LoadLines( + const char *fileName, + kwsys_stl::vector<kwsys_stl::string> &lines) +{ + FILE *file=fopen(fileName,"r"); + if (file==0) { - file.getline(buf,bufSize); - if (file.gcount()>1) - { - lines.push_back(buf); - ++nRead; - } + return 0; } - file.close(); + int nRead=LoadLines(file,lines); + fclose(file); return nRead; } @@ -708,14 +796,14 @@ int NameValue( size_t nLines=lines.size(); for (size_t i=0; i<nLines; ++i) { - kwsys_stl::string tok; - kwsys_stl::istringstream is(lines[i]); - is >> tok; - if (tok==name) + size_t at=lines[i].find(name); + if (at==kwsys_stl::string::npos) { - is >> value; - return 0; + continue; } + kwsys_stl::istringstream is(lines[i].substr(at+name.size())); + is >> value; + return 0; } return -1; } @@ -726,197 +814,276 @@ int GetFieldFromFile( const char *fileName, const char *fieldName, T &value) +{ + const char *fieldNames[2]={fieldName,NULL}; + T values[1]={T(0)}; + int ierr=GetFieldsFromFile(fileName,fieldNames,values); + if (ierr) + { + return ierr; + } + value=values[0]; + return 0; +} + +// **************************************************************************** +template<typename T> +int GetFieldsFromFile( + const char *fileName, + const char **fieldNames, + T *values) { kwsys_stl::vector<kwsys_stl::string> fields; if (!LoadLines(fileName,fields)) { return -1; } - int ierr=NameValue(fields,fieldName,value); + int i=0; + while (fieldNames[i]!=NULL) + { + int ierr=NameValue(fields,fieldNames[i],values[i]); + if (ierr) + { + return -(i+2); + } + i+=1; + } + return 0; +} + +// **************************************************************************** +template<typename T> +int GetFieldFromCommand( + const char *command, + const char *fieldName, + T &value) +{ + const char *names[2]={fieldName,NULL}; + T values[1]={T(0)}; + int ierr=GetFieldsFromCommand(command,names,values); if (ierr) { - return -2; + return ierr; } + value=values[0]; return 0; } -//***************************************************************************** +// **************************************************************************** +template<typename T> +int GetFieldsFromCommand( + const char *command, + const char **fieldNames, + T *values) +{ + FILE *file=popen(command,"r"); + if (file==0) + { + return -1; + } + kwsys_stl::vector<kwsys_stl::string> fields; + int nl=LoadLines(file,fields); + pclose(file); + if (nl==0) + { + return -1; + } + int i=0; + while (fieldNames[i]!=NULL) + { + int ierr=NameValue(fields,fieldNames[i],values[i]); + if (ierr) + { + return -(i+2); + } + i+=1; + } + return 0; +} + +// **************************************************************************** void StacktraceSignalHandler( int sigNo, siginfo_t *sigInfo, void * /*sigContext*/) { #if defined(__linux) || defined(__APPLE__) - kwsys_ios::cerr << "[" << getpid() << "] "; - + kwsys_ios::ostringstream oss; + oss + << "=========================================================" << kwsys_ios::endl + << "Process id " << getpid() << " "; switch (sigNo) { case SIGFPE: - kwsys_ios::cerr << "Caught SIGFPE "; + oss << "Caught SIGFPE "; switch (sigInfo->si_code) { # if defined(FPE_INTDIV) case FPE_INTDIV: - kwsys_ios::cerr << "integer division by zero"; + oss << "integer division by zero"; break; # endif # if defined(FPE_INTOVF) case FPE_INTOVF: - kwsys_ios::cerr << "integer overflow"; + oss << "integer overflow"; break; # endif case FPE_FLTDIV: - kwsys_ios::cerr << "floating point divide by zero"; + oss << "floating point divide by zero"; break; case FPE_FLTOVF: - kwsys_ios::cerr << "floating point overflow"; + oss << "floating point overflow"; break; case FPE_FLTUND: - kwsys_ios::cerr << "floating point underflow"; + oss << "floating point underflow"; break; case FPE_FLTRES: - kwsys_ios::cerr << "floating point inexact result"; + oss << "floating point inexact result"; break; case FPE_FLTINV: - kwsys_ios::cerr << "floating point invalid operation"; + oss << "floating point invalid operation"; break; #if defined(FPE_FLTSUB) case FPE_FLTSUB: - kwsys_ios::cerr << "floating point subscript out of range"; + oss << "floating point subscript out of range"; break; #endif default: - kwsys_ios::cerr << "code " << sigInfo->si_code; + oss << "code " << sigInfo->si_code; break; } break; case SIGSEGV: - kwsys_ios::cerr << "Caught SIGSEGV "; + oss << "Caught SIGSEGV "; switch (sigInfo->si_code) { case SEGV_MAPERR: - kwsys_ios::cerr << "address not mapped to object"; + oss << "address not mapped to object"; break; case SEGV_ACCERR: - kwsys_ios::cerr << "invalid permission for mapped object"; + oss << "invalid permission for mapped object"; break; default: - kwsys_ios::cerr << "code " << sigInfo->si_code; + oss << "code " << sigInfo->si_code; break; } break; case SIGINT: - kwsys_ios::cerr << "Caught SIGTERM"; + oss << "Caught SIGTERM"; break; case SIGTERM: - kwsys_ios::cerr << "Caught SIGTERM"; + oss << "Caught SIGTERM"; break; case SIGBUS: - kwsys_ios::cerr << "Caught SIGBUS type "; + oss << "Caught SIGBUS type "; switch (sigInfo->si_code) { case BUS_ADRALN: - kwsys_ios::cerr << "invalid address alignment"; + oss << "invalid address alignment"; break; # if defined(BUS_ADRERR) case BUS_ADRERR: - kwsys_ios::cerr << "non-exestent physical address"; + oss << "non-exestent physical address"; break; # endif # if defined(BUS_OBJERR) case BUS_OBJERR: - kwsys_ios::cerr << "object specific hardware error"; + oss << "object specific hardware error"; break; # endif default: - kwsys_ios::cerr << "code " << sigInfo->si_code; + oss << "code " << sigInfo->si_code; break; } break; case SIGILL: - kwsys_ios::cerr << "Caught SIGILL "; + oss << "Caught SIGILL "; switch (sigInfo->si_code) { case ILL_ILLOPC: - kwsys_ios::cerr << "illegal opcode"; + oss << "illegal opcode"; break; # if defined(ILL_ILLOPN) case ILL_ILLOPN: - kwsys_ios::cerr << "illegal operand"; + oss << "illegal operand"; break; # endif # if defined(ILL_ILLADR) case ILL_ILLADR: - kwsys_ios::cerr << "illegal addressing mode."; + oss << "illegal addressing mode."; break; # endif case ILL_ILLTRP: - kwsys_ios::cerr << "illegal trap"; + oss << "illegal trap"; case ILL_PRVOPC: - kwsys_ios::cerr << "privileged opcode"; + oss << "privileged opcode"; break; # if defined(ILL_PRVREG) case ILL_PRVREG: - kwsys_ios::cerr << "privileged register"; + oss << "privileged register"; break; # endif # if defined(ILL_COPROC) case ILL_COPROC: - kwsys_ios::cerr << "co-processor error"; + oss << "co-processor error"; break; # endif # if defined(ILL_BADSTK) case ILL_BADSTK: - kwsys_ios::cerr << "internal stack error"; + oss << "internal stack error"; break; # endif default: - kwsys_ios::cerr << "code " << sigInfo->si_code; + oss << "code " << sigInfo->si_code; break; } break; default: - kwsys_ios::cerr << "Caught " << sigNo << " code " << sigInfo->si_code; + oss << "Caught " << sigNo << " code " << sigInfo->si_code; break; } - kwsys_ios::cerr << kwsys_ios::endl; - + oss << kwsys_ios::endl; #if defined(KWSYS_SYSTEMINFORMATION_HAVE_BACKTRACE) - kwsys_ios::cerr << "Stack:" << kwsys_ios::endl; - void *stack[128]; - int n=backtrace(stack,128); - backtrace_symbols_fd(stack,n,2); + oss << "Program Stack:" << kwsys_ios::endl; + void *stackSymbols[128]; + int n=backtrace(stackSymbols,128); + char **stackText=backtrace_symbols(stackSymbols,n); + for (int i=0; i<n; ++i) + { + oss << " " << stackText[i] << kwsys_ios::endl; + } #endif - + oss + << "=========================================================" << kwsys_ios::endl; + kwsys_ios::cerr << oss.str() << kwsys_ios::endl; abort(); - #else // avoid warning C4100 (void)sigNo; @@ -1047,6 +1214,29 @@ const char * SystemInformationImplementation::GetOSName() /** Get the hostname */ const char* SystemInformationImplementation::GetHostname() { + if (this->Hostname.empty()) + { + this->Hostname="localhost"; +#if defined(_WIN32) + WORD wVersionRequested; + WSADATA wsaData; + char name[255]; + wVersionRequested = MAKEWORD(2,0); + if ( WSAStartup( wVersionRequested, &wsaData ) == 0 ) + { + gethostname(name,sizeof(name)); + WSACleanup( ); + } + this->Hostname = name; +#else + struct utsname unameInfo; + int errorFlag = uname(&unameInfo); + if(errorFlag == 0) + { + this->Hostname = unameInfo.nodename; + } +#endif + } return this->Hostname.c_str(); } @@ -1092,7 +1282,12 @@ int SystemInformationImplementation::GetFullyQualifiedDomainName( // we want the fully qualified domain name. Because there are // any number of interfaces on this system we look for the // first of these that contains the name returned by gethostname - // and is longer. failing that we return gethostname. + // and is longer. failing that we return gethostname and indicate + // with a failure code. Return of a failure code is not necessarilly + // an indication of an error. for instance gethostname may return + // the fully qualified domain name, or there may not be one if the + // system lives on a private network such as in the case of a cluster + // node. int ierr=0; char base[NI_MAXHOST]; @@ -1132,8 +1327,8 @@ int SystemInformationImplementation::GetFullyQualifiedDomainName( NI_NAMEREQD); if (ierr) { - // don't report the error now since we may succeed on another - // interface. If all attempts fail then retrun an error code. + // don't report the failure now since we may succeed on another + // interface. If all attempts fail then return the failure code. ierr=-3; continue; } @@ -1153,6 +1348,7 @@ int SystemInformationImplementation::GetFullyQualifiedDomainName( return ierr; #else /* TODO: Implement on more platforms. */ + fqdn=this->GetHostname(); return -1; #endif } @@ -2905,7 +3101,7 @@ int SystemInformationImplementation::RetreiveInformationFromCpuInfoFile() Get total system RAM in units of KiB. */ SystemInformation::LongLong -SystemInformationImplementation::GetMemoryTotal() +SystemInformationImplementation::GetHostMemoryTotal() { #if defined(_WIN32) # if defined(_MSC_VER) && _MSC_VER < 1300 @@ -2920,7 +3116,7 @@ SystemInformationImplementation::GetMemoryTotal() return statex.ullTotalPhys/1024; # endif #elif defined(__linux) - LongLong memTotal=0; + SystemInformation::LongLong memTotal=0; int ierr=GetFieldFromFile("/proc/meminfo","MemTotal:",memTotal); if (ierr) { @@ -2941,16 +3137,157 @@ SystemInformationImplementation::GetMemoryTotal() #endif } +/** +Get total system RAM in units of KiB. This may differ from the +host total if a host-wide resource limit is applied. +*/ +SystemInformation::LongLong +SystemInformationImplementation::GetHostMemoryAvailable(const char *hostLimitEnvVarName) +{ + SystemInformation::LongLong memTotal=this->GetHostMemoryTotal(); + + // the following mechanism is provided for systems that + // apply resource limits across groups of processes. + // this is of use on certain SMP systems (eg. SGI UV) + // where the host has a large amount of ram but a given user's + // access to it is severly restricted. The system will + // apply a limit across a set of processes. Units are in KiB. + if (hostLimitEnvVarName) + { + const char *hostLimitEnvVarValue=getenv(hostLimitEnvVarName); + if (hostLimitEnvVarValue) + { + SystemInformation::LongLong hostLimit=atoLongLong(hostLimitEnvVarValue); + if (hostLimit>0) + { + memTotal=min(hostLimit,memTotal); + } + } + } + + return memTotal; +} + +/** +Get total system RAM in units of KiB. This may differ from the +host total if a per-process resource limit is applied. +*/ +SystemInformation::LongLong +SystemInformationImplementation::GetProcMemoryAvailable( + const char *hostLimitEnvVarName, + const char *procLimitEnvVarName) +{ + SystemInformation::LongLong memAvail + = this->GetHostMemoryAvailable(hostLimitEnvVarName); + + // the following mechanism is provide for systems where rlimits + // are not employed. Units are in KiB. + if (procLimitEnvVarName) + { + const char *procLimitEnvVarValue=getenv(procLimitEnvVarName); + if (procLimitEnvVarValue) + { + SystemInformation::LongLong procLimit=atoLongLong(procLimitEnvVarValue); + if (procLimit>0) + { + memAvail=min(procLimit,memAvail); + } + } + } + +#if defined(__linux) + int ierr; + ResourceLimitType rlim; + ierr=GetResourceLimit(RLIMIT_DATA,&rlim); + if ((ierr==0) && (rlim.rlim_cur != RLIM_INFINITY)) + { + memAvail=min((SystemInformation::LongLong)rlim.rlim_cur/1024,memAvail); + } + + ierr=GetResourceLimit(RLIMIT_AS,&rlim); + if ((ierr==0) && (rlim.rlim_cur != RLIM_INFINITY)) + { + memAvail=min((SystemInformation::LongLong)rlim.rlim_cur/1024,memAvail); + } +#elif defined(__APPLE__) + struct rlimit rlim; + int ierr; + ierr=getrlimit(RLIMIT_DATA,&rlim); + if ((ierr==0) && (rlim.rlim_cur != RLIM_INFINITY)) + { + memAvail=min((SystemInformation::LongLong)rlim.rlim_cur/1024,memAvail); + } + + ierr=getrlimit(RLIMIT_RSS,&rlim); + if ((ierr==0) && (rlim.rlim_cur != RLIM_INFINITY)) + { + memAvail=min((SystemInformation::LongLong)rlim.rlim_cur/1024,memAvail); + } +#endif + + return memAvail; +} + +/** +Get RAM used by all processes in the host, in units of KiB. +*/ +SystemInformation::LongLong +SystemInformationImplementation::GetHostMemoryUsed() +{ +#if defined(_WIN32) +# if defined(_MSC_VER) && _MSC_VER < 1300 + MEMORYSTATUS stat; + stat.dwLength = sizeof(stat); + GlobalMemoryStatus(&stat); + return (stat.dwTotalPhys - stat.dwAvailPhys)/1024; +# else + MEMORYSTATUSEX statex; + statex.dwLength=sizeof(statex); + GlobalMemoryStatusEx(&statex); + return (statex.ullTotalPhys - statex.ullAvailPhys)/1024; +# endif +#elif defined(__linux) + const char *names[3]={"MemTotal:","MemFree:",NULL}; + SystemInformation::LongLong values[2]={SystemInformation::LongLong(0)}; + int ierr=GetFieldsFromFile("/proc/meminfo",names,values); + if (ierr) + { + return ierr; + } + SystemInformation::LongLong &memTotal=values[0]; + SystemInformation::LongLong &memFree=values[1]; + return memTotal - memFree; +#elif defined(__APPLE__) + SystemInformation::LongLong psz=getpagesize(); + if (psz<1) + { + return -1; + } + const char *names[4]={"Pages active:","Pages inactive:","Pages wired down:",NULL}; + SystemInformation::LongLong values[3]={SystemInformation::LongLong(0)}; + int ierr=GetFieldsFromCommand("vm_stat", names, values); + if (ierr) + { + return -1; + } + SystemInformation::LongLong &vmActive=values[0]; + SystemInformation::LongLong &vmInactive=values[1]; + SystemInformation::LongLong &vmWired=values[2]; + return ((vmActive+vmInactive+vmWired)*psz)/1024; +#else + return 0; +#endif +} + /** Get system RAM used by the process associated with the given process id in units of KiB. */ SystemInformation::LongLong -SystemInformationImplementation::GetMemoryUsed() +SystemInformationImplementation::GetProcMemoryUsed() { #if defined(_WIN32) && defined(KWSYS_SYS_HAS_PSAPI) long pid=GetCurrentProcessId(); - HANDLE hProc; hProc=OpenProcess( PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, @@ -2960,7 +3297,6 @@ SystemInformationImplementation::GetMemoryUsed() { return -1; } - PROCESS_MEMORY_COUNTERS pmc; int ok=GetProcessMemoryInfo(hProc,&pmc,sizeof(pmc)); CloseHandle(hProc); @@ -2970,7 +3306,7 @@ SystemInformationImplementation::GetMemoryUsed() } return pmc.WorkingSetSize/1024; #elif defined(__linux) - LongLong memUsed=0; + SystemInformation::LongLong memUsed=0; int ierr=GetFieldFromFile("/proc/self/status","VmRSS:",memUsed); if (ierr) { @@ -2978,23 +3314,34 @@ SystemInformationImplementation::GetMemoryUsed() } return memUsed; #elif defined(__APPLE__) + SystemInformation::LongLong memUsed=0; pid_t pid=getpid(); kwsys_stl::ostringstream oss; oss << "ps -o rss= -p " << pid; - FILE *f=popen(oss.str().c_str(),"r"); - if (f==0) + FILE *file=popen(oss.str().c_str(),"r"); + if (file==0) { return -1; } oss.str(""); - char buf[256]={'\0'}; - while (fgets(buf, 256, f) != 0) + while (!feof(file) && !ferror(file)) { - oss << buf; + char buf[256]={'\0'}; + errno=0; + size_t nRead=fread(buf,1,256,file); + if (ferror(file) && (errno==EINTR)) + { + clearerr(file); + } + if (nRead) oss << buf; + } + int ierr=ferror(file); + pclose(file); + if (ierr) + { + return -2; } - pclose(f); kwsys_stl::istringstream iss(oss.str()); - LongLong memUsed=0; iss >> memUsed; return memUsed; #else diff --git a/SystemInformation.hxx.in b/SystemInformation.hxx.in index cb6c7590..8f4cb4e5 100644 --- a/SystemInformation.hxx.in +++ b/SystemInformation.hxx.in @@ -24,7 +24,6 @@ namespace @KWSYS_NAMESPACE@ { - // forward declare the implementation class class SystemInformationImplementation; @@ -96,15 +95,44 @@ public: size_t GetTotalPhysicalMemory(); size_t GetAvailablePhysicalMemory(); - // returns an informative general description if the ram - // on this system - kwsys_stl::string GetMemoryDescription(); - - // Retrieve physical memory information in kib - LongLong GetMemoryTotal(); - LongLong GetMemoryUsed(); - - // enable/disable stack trace signal handler. + // returns an informative general description if the installed and + // available ram on this system. See the GetHostMmeoryTotal, and + // Get{Host,Proc}MemoryAvailable methods for more information. + kwsys_stl::string GetMemoryDescription( + const char *hostLimitEnvVarName=NULL, + const char *procLimitEnvVarName=NULL); + + // Retrieve amount of physical memory installed on the system in KiB + // units. + LongLong GetHostMemoryTotal(); + + // Get total system RAM in units of KiB available colectivley to all + // processes in a process group. An example of a process group + // are the processes comprising an mpi program which is running in + // parallel. The amount of memory reported may differ from the host + // total if a host wide resource limit is applied. Such reource limits + // are reported to us via an applicaiton specified environment variable. + LongLong GetHostMemoryAvailable(const char *hostLimitEnvVarName=NULL); + + // Get total system RAM in units of KiB available to this process. + // This may differ from the host available if a per-process resource + // limit is applied. per-process memory limits are applied on unix + // system via rlimit api. Resource limits that are not imposed via + // rlimit api may be reported to us via an application specified + // environment variable. + LongLong GetProcMemoryAvailable( + const char *hostLimitEnvVarName=NULL, + const char *procLimitEnvVarName=NULL); + + // Get the system RAM used by all processes on the host, in units of KiB. + LongLong GetHostMemoryUsed(); + + // Get system RAM used by this process id in units of KiB. + LongLong GetProcMemoryUsed(); + + // enable/disable stack trace signal handler. In order to + // produce an informative stack trace the application should + // be dynamically linked and compiled with debug symbols. static void SetStackTraceOnError(int enable); @@ -113,6 +141,7 @@ public: void RunOSCheck(); void RunMemoryCheck(); }; + } // namespace @KWSYS_NAMESPACE@ /* Undefine temporary macros. */ diff --git a/kwsysPlatformTestsCXX.cxx b/kwsysPlatformTestsCXX.cxx index 7b73d06d..ae587034 100644 --- a/kwsysPlatformTestsCXX.cxx +++ b/kwsysPlatformTestsCXX.cxx @@ -358,6 +358,30 @@ int main() } #endif +#ifdef TEST_KWSYS_IOS_HAS_ISTREAM___INT64 +int test_istream(kwsys_ios::istream& is, __int64& x) +{ + return (is >> x)? 1:0; +} +int main() +{ + __int64 x = 0; + return test_istream(kwsys_ios::cin, x); +} +#endif + +#ifdef TEST_KWSYS_IOS_HAS_OSTREAM___INT64 +int test_ostream(kwsys_ios::ostream& os, __int64 x) +{ + return (os << x)? 1:0; +} +int main() +{ + __int64 x = 0; + return test_ostream(kwsys_ios::cout, x); +} +#endif + #ifdef TEST_KWSYS_CHAR_IS_SIGNED /* Return 0 for char signed and 1 for char unsigned. */ int main() @@ -428,6 +452,48 @@ int main() } #endif +#ifdef TEST_KWSYS_CXX_HAS_RLIMIT64 +# if defined(KWSYS_HAS_LFS) +# define _LARGEFILE_SOURCE +# define _LARGEFILE64_SOURCE +# define _LARGE_FILES +# define _FILE_OFFSET_BITS 64 +# endif +# include <sys/resource.h> +int main() +{ + struct rlimit64 rlim; + return getrlimit64(0,&rlim); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_ATOLL +#include <stdlib.h> +int main() +{ + const char *str="1024"; + return static_cast<int>(atoll(str)); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_ATOL +#include <stdlib.h> +int main() +{ + const char *str="1024"; + return static_cast<int>(atol(str)); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS__ATOI64 +#include <stdlib.h> +int main() +{ + const char *str="1024"; + return static_cast<int>(_atoi64(str)); +} +#endif + #ifdef TEST_KWSYS_CXX_TYPE_INFO /* Collect fundamental type information and save it to a CMake script. */ diff --git a/testSystemInformation.cxx b/testSystemInformation.cxx index b3afc9de..41fcf384 100644 --- a/testSystemInformation.cxx +++ b/testSystemInformation.cxx @@ -13,8 +13,6 @@ #include KWSYS_HEADER(SystemInformation.hxx) #include KWSYS_HEADER(ios/iostream) - - // Work-around CMake dependency scanning limitation. This must // duplicate the above list of headers. #if 0 @@ -22,12 +20,31 @@ # include "kwsys_ios_iostream.h.in" #endif -#define printMethod(inof, m) kwsys_ios::cout << #m << ": " \ +#if defined(KWSYS_USE_LONG_LONG) +# if defined(KWSYS_IOS_HAS_OSTREAM_LONG_LONG) +# define iostreamLongLong(x) (x) +# else +# define iostreamLongLong(x) ((long)x) +# endif +#elif defined(KWSYS_USE___INT64) +# if defined(KWSYS_IOS_HAS_OSTREAM___INT64) +# define iostreamLongLong(x) (x) +# else +# define iostreamLongLong(x) ((long)x) +# endif +#else +# error "No Long Long" +#endif + +#define printMethod(info, m) kwsys_ios::cout << #m << ": " \ << info.m() << "\n" -#define printMethod2(inof, m, unit) kwsys_ios::cout << #m << ": " \ +#define printMethod2(info, m, unit) kwsys_ios::cout << #m << ": " \ << info.m() << " " << unit << "\n" +#define printMethod3(info, m, unit) kwsys_ios::cout << #m << ": " \ +<< iostreamLongLong(info.m) << " " << unit << "\n" + int testSystemInformation(int, char*[]) { kwsys::SystemInformation info; @@ -35,7 +52,11 @@ int testSystemInformation(int, char*[]) info.RunOSCheck(); info.RunMemoryCheck(); printMethod(info, GetOSName); + printMethod(info, GetOSIsLinux); + printMethod(info, GetOSIsApple); + printMethod(info, GetOSIsWindows); printMethod(info, GetHostname); + printMethod(info, GetFullyQualifiedDomainName); printMethod(info, GetOSRelease); printMethod(info, GetOSVersion); printMethod(info, GetOSPlatform); @@ -58,6 +79,11 @@ int testSystemInformation(int, char*[]) printMethod2(info, GetAvailableVirtualMemory, "MB"); printMethod2(info, GetTotalPhysicalMemory, "MB"); printMethod2(info, GetAvailablePhysicalMemory, "MB"); + printMethod3(info, GetHostMemoryTotal(), "KiB"); + printMethod3(info, GetHostMemoryAvailable("KWSHL"), "KiB"); + printMethod3(info, GetProcMemoryAvailable("KWSHL","KWSPL"), "KiB"); + printMethod3(info, GetHostMemoryUsed(), "KiB"); + printMethod3(info, GetProcMemoryUsed(), "KiB"); //int GetProcessorCacheXSize(long int); // bool DoesCPUSupportFeature(long int); -- GitLab