phoenix-firestorm/indra/llcommon/llsys.cpp

634 lines
15 KiB
C++

/**
* @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 <iostream>
#ifdef LL_STANDALONE
# include <zlib.h>
#else
# include "zlib/zlib.h"
#endif
#include "llprocessor.h"
#if LL_WINDOWS
# define WIN32_LEAN_AND_MEAN
# include <winsock2.h>
# include <windows.h>
#elif LL_DARWIN
# include <sys/sysctl.h>
# include <sys/utsname.h>
#elif LL_LINUX
# include <sys/utsname.h>
# include <unistd.h>
# include <sys/sysinfo.h>
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;
}