/** * @file llsys.cpp * @brief Impelementation of the basic system query functions. * * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. * $License$ */ #include "linden_common.h" #include "llsys.h" #include #ifdef LL_STANDALONE # include #else # include "zlib/zlib.h" #endif #include "llprocessor.h" #if LL_WINDOWS # define WIN32_LEAN_AND_MEAN # include # include #elif LL_DARWIN # include # include #elif LL_LINUX # include # include # include const char MEMINFO_FILE[] = "/proc/meminfo"; const char CPUINFO_FILE[] = "/proc/cpuinfo"; #endif static const S32 CPUINFO_BUFFER_SIZE = 16383; LLCPUInfo gSysCPU; LLOSInfo::LLOSInfo() : mMajorVer(0), mMinorVer(0), mBuild(0), mOSString("") { #if LL_WINDOWS OSVERSIONINFOEX osvi; BOOL bOsVersionInfoEx; // Try calling GetVersionEx using the OSVERSIONINFOEX structure. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if(!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi))) { // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if(!GetVersionEx( (OSVERSIONINFO *) &osvi)) return; } mMajorVer = osvi.dwMajorVersion; mMinorVer = osvi.dwMinorVersion; mBuild = osvi.dwBuildNumber; switch(osvi.dwPlatformId) { case VER_PLATFORM_WIN32_NT: { // Test for the product. if(osvi.dwMajorVersion <= 4) { mOSString = "Microsoft Windows NT "; } else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) { mOSString = "Microsoft Windows 2000 "; } else if(osvi.dwMajorVersion ==5 && osvi.dwMinorVersion == 1) { mOSString = "Microsoft Windows XP "; } else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) { if(osvi.wProductType == VER_NT_WORKSTATION) mOSString = "Microsoft Windows XP x64 Edition "; else mOSString = "Microsoft Windows Server 2003 "; } else if(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) { if(osvi.wProductType == VER_NT_WORKSTATION) mOSString = "Microsoft Windows Vista "; else mOSString = "Microsoft Windows Vista Server "; } else // Use the registry on early versions of Windows NT. { HKEY hKey; WCHAR szProductType[80]; DWORD dwBufLen; RegOpenKeyEx( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions", 0, KEY_QUERY_VALUE, &hKey ); RegQueryValueEx( hKey, L"ProductType", NULL, NULL, (LPBYTE) szProductType, &dwBufLen); RegCloseKey( hKey ); if ( lstrcmpi( L"WINNT", szProductType) == 0 ) { mOSString += "Professional "; } else if ( lstrcmpi( L"LANMANNT", szProductType) == 0 ) { mOSString += "Server "; } else if ( lstrcmpi( L"SERVERNT", szProductType) == 0 ) { mOSString += "Advanced Server "; } } std::string csdversion = utf16str_to_utf8str(osvi.szCSDVersion); // Display version, service pack (if any), and build number. char tmp[MAX_STRING]; /* Flawfinder: ignore */ if(osvi.dwMajorVersion <= 4) { snprintf( /* Flawfinder: ignore */ tmp, sizeof(tmp), "version %d.%d %s (Build %d)", osvi.dwMajorVersion, osvi.dwMinorVersion, csdversion.c_str(), (osvi.dwBuildNumber & 0xffff)); } else { snprintf( /* Flawfinder: ignore */ tmp, sizeof(tmp), "%s (Build %d)", csdversion.c_str(), (osvi.dwBuildNumber & 0xffff)); } mOSString += tmp; } break; case VER_PLATFORM_WIN32_WINDOWS: // Test for the Windows 95 product family. if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) { mOSString = "Microsoft Windows 95 "; if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' ) { mOSString += "OSR2 "; } } if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) { mOSString = "Microsoft Windows 98 "; if ( osvi.szCSDVersion[1] == 'A' ) { mOSString += "SE "; } } if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) { mOSString = "Microsoft Windows Millennium Edition "; } break; } #else struct utsname un; if(uname(&un) != -1) { mOSString.append(un.sysname); mOSString.append(" "); mOSString.append(un.release); mOSString.append(" "); mOSString.append(un.version); mOSString.append(" "); mOSString.append(un.machine); } else { mOSString.append("Unable to collect OS info"); } #endif } #ifndef LL_WINDOWS // static S32 LLOSInfo::getMaxOpenFiles() { const S32 OPEN_MAX_GUESS = 256; #ifdef OPEN_MAX static S32 open_max = OPEN_MAX; #else static S32 open_max = 0; #endif if (0 == open_max) { // First time through. errno = 0; if ( (open_max = sysconf(_SC_OPEN_MAX)) < 0) { if (0 == errno) { // Indeterminate. open_max = OPEN_MAX_GUESS; } else { llerrs << "LLOSInfo::getMaxOpenFiles: sysconf error for _SC_OPEN_MAX" << llendl; } } } return open_max; } #endif void LLOSInfo::stream(std::ostream& s) const { s << mOSString; } const std::string& LLOSInfo::getOSString() const { return mOSString; } const S32 STATUS_SIZE = 8192; //static U32 LLOSInfo::getProcessVirtualSizeKB() { U32 virtual_size = 0; #if LL_WINDOWS #endif #if LL_LINUX FILE* status_filep = LLFile::fopen("/proc/self/status", "rb"); S32 numRead = 0; char buff[STATUS_SIZE]; /* Flawfinder: ignore */ size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep); buff[nbytes] = '\0'; // All these guys return numbers in KB char *memp = strstr(buff, "VmSize:"); if (memp) { numRead += sscanf(memp, "%*s %u", &virtual_size); } fclose(status_filep); #elif LL_SOLARIS char proc_ps[LL_MAX_PATH]; sprintf(proc_ps, "/proc/%d/psinfo", (int)getpid()); int proc_fd = -1; if((proc_fd = open(proc_ps, O_RDONLY)) == -1){ llwarns << "unable to open " << proc_ps << llendl; return 0; } psinfo_t proc_psinfo; if(read(proc_fd, &proc_psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)){ llwarns << "Unable to read " << proc_ps << llendl; close(proc_fd); return 0; } close(proc_fd); virtual_size = proc_psinfo.pr_size; #endif return virtual_size; } //static U32 LLOSInfo::getProcessResidentSizeKB() { U32 resident_size = 0; #if LL_WINDOWS #endif #if LL_LINUX FILE* status_filep = LLFile::fopen("/proc/self/status", "rb"); if (status_filep != NULL) { S32 numRead = 0; char buff[STATUS_SIZE]; /* Flawfinder: ignore */ size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep); buff[nbytes] = '\0'; // All these guys return numbers in KB char *memp = strstr(buff, "VmRSS:"); if (memp) { numRead += sscanf(memp, "%*s %u", &resident_size); } fclose(status_filep); } #elif LL_SOLARIS char proc_ps[LL_MAX_PATH]; sprintf(proc_ps, "/proc/%d/psinfo", (int)getpid()); int proc_fd = -1; if((proc_fd = open(proc_ps, O_RDONLY)) == -1){ llwarns << "unable to open " << proc_ps << llendl; return 0; } psinfo_t proc_psinfo; if(read(proc_fd, &proc_psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)){ llwarns << "Unable to read " << proc_ps << llendl; close(proc_fd); return 0; } close(proc_fd); resident_size = proc_psinfo.pr_rssize; #endif return resident_size; } LLCPUInfo::LLCPUInfo() { std::ostringstream out; CProcessor proc; const ProcessorInfo* info = proc.GetCPUInfo(); // proc.WriteInfoTextFile("procInfo.txt"); mHasSSE = info->_Ext.SSE_StreamingSIMD_Extensions; mHasSSE2 = info->_Ext.SSE2_StreamingSIMD2_Extensions; mHasAltivec = info->_Ext.Altivec_Extensions; mCPUMhz = (S32)(proc.GetCPUFrequency(50)/1000000.0); mFamily.assign( info->strFamily ); mCPUString = "Unknown"; #if LL_WINDOWS || LL_DARWIN || LL_SOLARIS out << proc.strCPUName; if (200 < mCPUMhz && mCPUMhz < 10000) // *NOTE: cpu speed is often way wrong, do a sanity check { out << " (" << mCPUMhz << " MHz)"; } mCPUString = out.str(); #elif LL_LINUX std::map< LLString, LLString > cpuinfo; FILE* cpuinfo_fp = LLFile::fopen(CPUINFO_FILE, "rb"); if(cpuinfo_fp) { char line[MAX_STRING]; memset(line, 0, MAX_STRING); while(fgets(line, MAX_STRING, cpuinfo_fp)) { // /proc/cpuinfo on Linux looks like: // name\t*: value\n char* tabspot = strchr( line, '\t' ); if (tabspot == NULL) continue; char* colspot = strchr( tabspot, ':' ); if (colspot == NULL) continue; char* spacespot = strchr( colspot, ' ' ); if (spacespot == NULL) continue; char* nlspot = strchr( line, '\n' ); if (nlspot == NULL) nlspot = line + strlen( line ); // Fallback to terminating NUL std::string linename( line, tabspot ); LLString llinename(linename); LLString::toLower(llinename); std::string lineval( spacespot + 1, nlspot ); cpuinfo[ llinename ] = lineval; } fclose(cpuinfo_fp); } # if LL_X86 LLString flags = " " + cpuinfo["flags"] + " "; LLString::toLower(flags); mHasSSE = ( flags.find( " sse " ) != std::string::npos ); mHasSSE2 = ( flags.find( " sse2 " ) != std::string::npos ); F64 mhz; if (LLString::convertToF64(cpuinfo["cpu mhz"], mhz) && 200.0 < mhz && mhz < 10000.0) { mCPUMhz = (S32)llrint(mhz); } if (!cpuinfo["model name"].empty()) mCPUString = cpuinfo["model name"]; # endif // LL_X86 #endif // LL_LINUX } bool LLCPUInfo::hasAltivec() const { return mHasAltivec; } bool LLCPUInfo::hasSSE() const { return mHasSSE; } bool LLCPUInfo::hasSSE2() const { return mHasSSE2; } S32 LLCPUInfo::getMhz() const { return mCPUMhz; } std::string LLCPUInfo::getCPUString() const { return mCPUString; } void LLCPUInfo::stream(std::ostream& s) const { #if LL_WINDOWS || LL_DARWIN || LL_SOLARIS // gather machine information. char proc_buf[CPUINFO_BUFFER_SIZE]; /* Flawfinder: ignore */ CProcessor proc; if(proc.CPUInfoToText(proc_buf, CPUINFO_BUFFER_SIZE)) { s << proc_buf; } else { s << "Unable to collect processor information" << std::endl; } #else // *NOTE: This works on linux. What will it do on other systems? FILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, "rb"); if(cpuinfo) { char line[MAX_STRING]; memset(line, 0, MAX_STRING); while(fgets(line, MAX_STRING, cpuinfo)) { line[strlen(line)-1] = ' '; s << line; } fclose(cpuinfo); s << std::endl; } else { s << "Unable to collect processor information" << std::endl; } #endif // These are interesting as they reflect our internal view of the // CPU's attributes regardless of platform s << "->mHasSSE: " << (U32)mHasSSE << std::endl; s << "->mHasSSE2: " << (U32)mHasSSE2 << std::endl; s << "->mHasAltivec: " << (U32)mHasAltivec << std::endl; s << "->mCPUMhz: " << mCPUMhz << std::endl; s << "->mCPUString: " << mCPUString << std::endl; } LLMemoryInfo::LLMemoryInfo() { } U32 LLMemoryInfo::getPhysicalMemory() const { #if LL_WINDOWS MEMORYSTATUS state; state.dwLength = sizeof(state); GlobalMemoryStatus(&state); return (U32)state.dwTotalPhys; #elif LL_DARWIN // This might work on Linux as well. Someone check... unsigned int phys = 0; int mib[2] = { CTL_HW, HW_PHYSMEM }; size_t len = sizeof(phys); sysctl(mib, 2, &phys, &len, NULL, 0); return phys; #elif LL_LINUX return getpagesize() * get_phys_pages(); #elif LL_SOLARIS return getpagesize() * sysconf(_SC_PHYS_PAGES); #else return 0; #endif } void LLMemoryInfo::stream(std::ostream& s) const { #if LL_WINDOWS MEMORYSTATUS state; state.dwLength = sizeof(state); GlobalMemoryStatus(&state); s << "Percent Memory use: " << (U32)state.dwMemoryLoad << '%' << std::endl; s << "Total Physical Kb: " << (U32)state.dwTotalPhys/1024 << std::endl; s << "Avail Physical Kb: " << (U32)state.dwAvailPhys/1024 << std::endl; s << "Total page Kb: " << (U32)state.dwTotalPageFile/1024 << std::endl; s << "Avail page Kb: " << (U32)state.dwAvailPageFile/1024 << std::endl; s << "Total Virtual Kb: " << (U32)state.dwTotalVirtual/1024 << std::endl; s << "Avail Virtual Kb: " << (U32)state.dwAvailVirtual/1024 << std::endl; #elif LL_DARWIN U64 phys = 0; size_t len = sizeof(phys); if(sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0) { s << "Total Physical Kb: " << phys/1024 << std::endl; } else { s << "Unable to collect memory information"; } #elif LL_SOLARIS U64 phys = 0; phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024); s << "Total Physical Kb: " << phys << std::endl; #else // *NOTE: This works on linux. What will it do on other systems? FILE* meminfo = LLFile::fopen(MEMINFO_FILE,"rb"); if(meminfo) { char line[MAX_STRING]; /* Flawfinder: ignore */ memset(line, 0, MAX_STRING); while(fgets(line, MAX_STRING, meminfo)) { line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/ s << line; } fclose(meminfo); } else { s << "Unable to collect memory information"; } #endif } std::ostream& operator<<(std::ostream& s, const LLOSInfo& info) { info.stream(s); return s; } std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info) { info.stream(s); return s; } std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info) { info.stream(s); return s; } BOOL gunzip_file(const char *srcfile, const char *dstfile) { char tmpfile[LL_MAX_PATH]; /* Flawfinder: ignore */ const S32 UNCOMPRESS_BUFFER_SIZE = 32768; BOOL retval = FALSE; gzFile src = NULL; U8 buffer[UNCOMPRESS_BUFFER_SIZE]; FILE *dst = NULL; S32 bytes = 0; (void *) strcpy(tmpfile, dstfile); /* Flawfinder: ignore */ (void *) strncat(tmpfile, ".t", sizeof(tmpfile) - strlen(tmpfile) -1); /* Flawfinder: ignore */ src = gzopen(srcfile, "rb"); if (! src) goto err; dst = LLFile::fopen(tmpfile, "wb"); /* Flawfinder: ignore */ if (! dst) goto err; do { bytes = gzread(src, buffer, UNCOMPRESS_BUFFER_SIZE); size_t nwrit = fwrite(buffer, sizeof(U8), bytes, dst); if (nwrit < (size_t) bytes) { llerrs << "Short write on " << tmpfile << llendl; } } while(gzeof(src) == 0); fclose(dst); dst = NULL; if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */ retval = TRUE; err: if (src != NULL) gzclose(src); if (dst != NULL) fclose(dst); return retval; } BOOL gzip_file(const char *srcfile, const char *dstfile) { const S32 COMPRESS_BUFFER_SIZE = 32768; char tmpfile[LL_MAX_PATH]; /* Flawfinder: ignore */ BOOL retval = FALSE; U8 buffer[COMPRESS_BUFFER_SIZE]; gzFile dst = NULL; FILE *src = NULL; S32 bytes = 0; (void *) strcpy(tmpfile, dstfile); /* Flawfinder: ignore */ (void *) strncat(tmpfile, ".t", sizeof(tmpfile) - strlen(tmpfile) -1); /* Flawfinder: ignore */ dst = gzopen(tmpfile, "wb"); /* Flawfinder: ignore */ if (! dst) goto err; src = LLFile::fopen(srcfile, "rb"); /* Flawfinder: ignore */ if (! src) goto err; do { bytes = (S32)fread(buffer, sizeof(U8), COMPRESS_BUFFER_SIZE,src); gzwrite(dst, buffer, bytes); } while(feof(src) == 0); gzclose(dst); dst = NULL; #if LL_WINDOWS // Rename in windows needs the dstfile to not exist. LLFile::remove(dstfile); #endif if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */ retval = TRUE; err: if (src != NULL) fclose(src); if (dst != NULL) gzclose(dst); return retval; }