PageRenderTime 144ms CodeModel.GetById 41ms app.highlight 91ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llcommon/llsys.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1394 lines | 979 code | 161 blank | 254 comment | 158 complexity | cc37422b410d5d0cd67000da5835038f MD5 | raw file
   1/** 
   2 * @file llsys.cpp
   3 * @brief Implementation of the basic system query functions.
   4 *
   5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#if LL_WINDOWS
  28#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
  29#endif
  30
  31#include "linden_common.h"
  32
  33#include "llsys.h"
  34
  35#include <iostream>
  36#ifdef LL_STANDALONE
  37# include <zlib.h>
  38#else
  39# include "zlib/zlib.h"
  40#endif
  41
  42#include "llprocessor.h"
  43#include "llerrorcontrol.h"
  44#include "llevents.h"
  45#include "lltimer.h"
  46#include "llsdserialize.h"
  47#include "llsdutil.h"
  48#include <boost/bind.hpp>
  49#include <boost/circular_buffer.hpp>
  50#include <boost/regex.hpp>
  51#include <boost/foreach.hpp>
  52#include <boost/lexical_cast.hpp>
  53#include <boost/range.hpp>
  54#include <boost/utility/enable_if.hpp>
  55#include <boost/type_traits/is_integral.hpp>
  56#include <boost/type_traits/is_float.hpp>
  57
  58using namespace llsd;
  59
  60#if LL_WINDOWS
  61#	define WIN32_LEAN_AND_MEAN
  62#	include <winsock2.h>
  63#	include <windows.h>
  64#   include <psapi.h>               // GetPerformanceInfo() et al.
  65#elif LL_DARWIN
  66#	include <errno.h>
  67#	include <sys/sysctl.h>
  68#	include <sys/utsname.h>
  69#	include <stdint.h>
  70#	include <Carbon/Carbon.h>
  71#   include <stdexcept>
  72#	include <mach/host_info.h>
  73#	include <mach/mach_host.h>
  74#	include <mach/task.h>
  75#	include <mach/task_info.h>
  76#elif LL_LINUX
  77#	include <errno.h>
  78#	include <sys/utsname.h>
  79#	include <unistd.h>
  80#	include <sys/sysinfo.h>
  81#   include <stdexcept>
  82const char MEMINFO_FILE[] = "/proc/meminfo";
  83#elif LL_SOLARIS
  84#	include <stdio.h>
  85#	include <unistd.h>
  86#	include <sys/utsname.h>
  87#	define _STRUCTURED_PROC 1
  88#	include <sys/procfs.h>
  89#	include <sys/types.h>
  90#	include <sys/stat.h>
  91#	include <fcntl.h>
  92#	include <errno.h>
  93extern int errno;
  94#endif
  95
  96
  97static const S32 CPUINFO_BUFFER_SIZE = 16383;
  98LLCPUInfo gSysCPU;
  99
 100// Don't log memory info any more often than this. It also serves as our
 101// framerate sample size.
 102static const F32 MEM_INFO_THROTTLE = 20;
 103// Sliding window of samples. We intentionally limit the length of time we
 104// remember "the slowest" framerate because framerate is very slow at login.
 105// If we only triggered FrameWatcher logging when the session framerate
 106// dropped below the login framerate, we'd have very little additional data.
 107static const F32 MEM_INFO_WINDOW = 10*60;
 108
 109#if LL_WINDOWS
 110#ifndef DLLVERSIONINFO
 111typedef struct _DllVersionInfo
 112{
 113    DWORD cbSize;
 114    DWORD dwMajorVersion;
 115    DWORD dwMinorVersion;
 116    DWORD dwBuildNumber;
 117    DWORD dwPlatformID;
 118}DLLVERSIONINFO;
 119#endif
 120
 121#ifndef DLLGETVERSIONPROC
 122typedef int (FAR WINAPI *DLLGETVERSIONPROC) (DLLVERSIONINFO *);
 123#endif
 124
 125bool get_shell32_dll_version(DWORD& major, DWORD& minor, DWORD& build_number)
 126{
 127	bool result = false;
 128	const U32 BUFF_SIZE = 32767;
 129	WCHAR tempBuf[BUFF_SIZE];
 130	if(GetSystemDirectory((LPWSTR)&tempBuf, BUFF_SIZE))
 131	{
 132		
 133		std::basic_string<WCHAR> shell32_path(tempBuf);
 134
 135		// Shell32.dll contains the DLLGetVersion function. 
 136		// according to msdn its not part of the API
 137		// so you have to go in and get it.
 138		// http://msdn.microsoft.com/en-us/library/bb776404(VS.85).aspx
 139		shell32_path += TEXT("\\shell32.dll");
 140
 141		HMODULE hDllInst = LoadLibrary(shell32_path.c_str());   //load the DLL
 142		if(hDllInst) 
 143		{  // Could successfully load the DLL
 144			DLLGETVERSIONPROC pDllGetVersion;
 145			/*
 146			You must get this function explicitly because earlier versions of the DLL
 147			don't implement this function. That makes the lack of implementation of the
 148			function a version marker in itself.
 149			*/
 150			pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hDllInst, 
 151																"DllGetVersion");
 152
 153			if(pDllGetVersion) 
 154			{    
 155				// DLL supports version retrieval function
 156				DLLVERSIONINFO    dvi;
 157
 158				ZeroMemory(&dvi, sizeof(dvi));
 159				dvi.cbSize = sizeof(dvi);
 160				HRESULT hr = (*pDllGetVersion)(&dvi);
 161
 162				if(SUCCEEDED(hr)) 
 163				{ // Finally, the version is at our hands
 164					major = dvi.dwMajorVersion;
 165					minor = dvi.dwMinorVersion;
 166					build_number = dvi.dwBuildNumber;
 167					result = true;
 168				} 
 169			} 
 170
 171			FreeLibrary(hDllInst);  // Release DLL
 172		} 
 173	}
 174	return result;
 175}
 176#endif // LL_WINDOWS
 177
 178LLOSInfo::LLOSInfo() :
 179	mMajorVer(0), mMinorVer(0), mBuild(0)
 180{
 181
 182#if LL_WINDOWS
 183	OSVERSIONINFOEX osvi;
 184	BOOL bOsVersionInfoEx;
 185
 186	// Try calling GetVersionEx using the OSVERSIONINFOEX structure.
 187	ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
 188	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
 189	if(!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi)))
 190	{
 191		// If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
 192		osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
 193		if(!GetVersionEx( (OSVERSIONINFO *) &osvi))
 194			return;
 195	}
 196	mMajorVer = osvi.dwMajorVersion;
 197	mMinorVer = osvi.dwMinorVersion;
 198	mBuild = osvi.dwBuildNumber;
 199
 200	DWORD shell32_major, shell32_minor, shell32_build;
 201	bool got_shell32_version = get_shell32_dll_version(shell32_major, 
 202													   shell32_minor, 
 203													   shell32_build);
 204
 205	switch(osvi.dwPlatformId)
 206	{
 207	case VER_PLATFORM_WIN32_NT:
 208		{
 209			// Test for the product.
 210			if(osvi.dwMajorVersion <= 4)
 211			{
 212				mOSStringSimple = "Microsoft Windows NT ";
 213			}
 214			else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
 215			{
 216				mOSStringSimple = "Microsoft Windows 2000 ";
 217			}
 218			else if(osvi.dwMajorVersion ==5 && osvi.dwMinorVersion == 1)
 219			{
 220				mOSStringSimple = "Microsoft Windows XP ";
 221			}
 222			else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
 223			{
 224				 if(osvi.wProductType == VER_NT_WORKSTATION)
 225					mOSStringSimple = "Microsoft Windows XP x64 Edition ";
 226				 else
 227					mOSStringSimple = "Microsoft Windows Server 2003 ";
 228			}
 229			else if(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion <= 2)
 230			{
 231				if(osvi.dwMinorVersion == 0)
 232				{
 233					if(osvi.wProductType == VER_NT_WORKSTATION)
 234						mOSStringSimple = "Microsoft Windows Vista ";
 235					else
 236						mOSStringSimple = "Windows Server 2008 ";
 237				}
 238				else if(osvi.dwMinorVersion == 1)
 239				{
 240					if(osvi.wProductType == VER_NT_WORKSTATION)
 241						mOSStringSimple = "Microsoft Windows 7 ";
 242					else
 243						mOSStringSimple = "Windows Server 2008 R2 ";
 244				}
 245				else if(osvi.dwMinorVersion == 2)
 246				{
 247					if(osvi.wProductType == VER_NT_WORKSTATION)
 248						mOSStringSimple = "Microsoft Windows 8 ";
 249					else
 250						mOSStringSimple = "Windows Server 2012 ";
 251				}
 252
 253				///get native system info if available..
 254				typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); ///function pointer for loading GetNativeSystemInfo
 255				SYSTEM_INFO si; //System Info object file contains architecture info
 256				PGNSI pGNSI; //pointer object
 257				ZeroMemory(&si, sizeof(SYSTEM_INFO)); //zero out the memory in information
 258				pGNSI = (PGNSI) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),  "GetNativeSystemInfo"); //load kernel32 get function
 259				if(NULL != pGNSI) //check if it has failed
 260					pGNSI(&si); //success
 261				else 
 262					GetSystemInfo(&si); //if it fails get regular system info 
 263				//(Warning: If GetSystemInfo it may result in incorrect information in a WOW64 machine, if the kernel fails to load)
 264
 265				//msdn microsoft finds 32 bit and 64 bit flavors this way..
 266				//http://msdn.microsoft.com/en-us/library/ms724429(VS.85).aspx (example code that contains quite a few more flavors
 267				//of windows than this code does (in case it is needed for the future)
 268				if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) //check for 64 bit
 269				{
 270					mOSStringSimple += "64-bit ";
 271				}
 272				else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL )
 273				{
 274					mOSStringSimple += "32-bit ";
 275				}
 276			}
 277			else   // Use the registry on early versions of Windows NT.
 278			{
 279				mOSStringSimple = "Microsoft Windows (unrecognized) ";
 280
 281				HKEY hKey;
 282				WCHAR szProductType[80];
 283				DWORD dwBufLen;
 284				RegOpenKeyEx( HKEY_LOCAL_MACHINE,
 285							L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
 286							0, KEY_QUERY_VALUE, &hKey );
 287				RegQueryValueEx( hKey, L"ProductType", NULL, NULL,
 288								(LPBYTE) szProductType, &dwBufLen);
 289				RegCloseKey( hKey );
 290				if ( lstrcmpi( L"WINNT", szProductType) == 0 )
 291				{
 292					mOSStringSimple += "Professional ";
 293				}
 294				else if ( lstrcmpi( L"LANMANNT", szProductType) == 0 )
 295				{
 296					mOSStringSimple += "Server ";
 297				}
 298				else if ( lstrcmpi( L"SERVERNT", szProductType) == 0 )
 299				{
 300					mOSStringSimple += "Advanced Server ";
 301				}
 302			}
 303
 304			std::string csdversion = utf16str_to_utf8str(osvi.szCSDVersion);
 305			// Display version, service pack (if any), and build number.
 306			std::string tmpstr;
 307			if(osvi.dwMajorVersion <= 4)
 308			{
 309				tmpstr = llformat("version %d.%d %s (Build %d)",
 310								  osvi.dwMajorVersion,
 311								  osvi.dwMinorVersion,
 312								  csdversion.c_str(),
 313								  (osvi.dwBuildNumber & 0xffff));
 314			}
 315			else
 316			{
 317				tmpstr = llformat("%s (Build %d)",
 318								  csdversion.c_str(),
 319								  (osvi.dwBuildNumber & 0xffff));
 320			}
 321
 322			mOSString = mOSStringSimple + tmpstr;
 323		}
 324		break;
 325
 326	case VER_PLATFORM_WIN32_WINDOWS:
 327		// Test for the Windows 95 product family.
 328		if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
 329		{
 330			mOSStringSimple = "Microsoft Windows 95 ";
 331			if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' )
 332			{
 333                mOSStringSimple += "OSR2 ";
 334			}
 335		} 
 336		if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
 337		{
 338			mOSStringSimple = "Microsoft Windows 98 ";
 339			if ( osvi.szCSDVersion[1] == 'A' )
 340			{
 341                mOSStringSimple += "SE ";
 342			}
 343		} 
 344		if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
 345		{
 346			mOSStringSimple = "Microsoft Windows Millennium Edition ";
 347		}
 348		mOSString = mOSStringSimple;
 349		break;
 350	}
 351
 352	std::string compatibility_mode;
 353	if(got_shell32_version)
 354	{
 355		if(osvi.dwMajorVersion != shell32_major || osvi.dwMinorVersion != shell32_minor)
 356		{
 357			compatibility_mode = llformat(" compatibility mode. real ver: %d.%d (Build %d)", 
 358											shell32_major,
 359											shell32_minor,
 360											shell32_build);
 361		}
 362	}
 363	mOSString += compatibility_mode;
 364
 365#elif LL_DARWIN
 366	
 367	// Initialize mOSStringSimple to something like:
 368	// "Mac OS X 10.6.7"
 369	{
 370		const char * DARWIN_PRODUCT_NAME = "Mac OS X";
 371		
 372		SInt32 major_version, minor_version, bugfix_version;
 373		OSErr r1 = Gestalt(gestaltSystemVersionMajor, &major_version);
 374		OSErr r2 = Gestalt(gestaltSystemVersionMinor, &minor_version);
 375		OSErr r3 = Gestalt(gestaltSystemVersionBugFix, &bugfix_version);
 376
 377		if((r1 == noErr) && (r2 == noErr) && (r3 == noErr))
 378		{
 379			mMajorVer = major_version;
 380			mMinorVer = minor_version;
 381			mBuild = bugfix_version;
 382
 383			std::stringstream os_version_string;
 384			os_version_string << DARWIN_PRODUCT_NAME << " " << mMajorVer << "." << mMinorVer << "." << mBuild;
 385			
 386			// Put it in the OS string we are compiling
 387			mOSStringSimple.append(os_version_string.str());
 388		}
 389		else
 390		{
 391			mOSStringSimple.append("Unable to collect OS info");
 392		}
 393	}
 394	
 395	// Initialize mOSString to something like:
 396	// "Mac OS X 10.6.7 Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386"
 397	struct utsname un;
 398	if(uname(&un) != -1)
 399	{		
 400		mOSString = mOSStringSimple;
 401		mOSString.append(" ");
 402		mOSString.append(un.sysname);
 403		mOSString.append(" ");
 404		mOSString.append(un.release);
 405		mOSString.append(" ");
 406		mOSString.append(un.version);
 407		mOSString.append(" ");
 408		mOSString.append(un.machine);
 409	}
 410	else
 411	{
 412		mOSString = mOSStringSimple;
 413	}
 414	
 415#else
 416	
 417	struct utsname un;
 418	if(uname(&un) != -1)
 419	{
 420		mOSStringSimple.append(un.sysname);
 421		mOSStringSimple.append(" ");
 422		mOSStringSimple.append(un.release);
 423
 424		mOSString = mOSStringSimple;
 425		mOSString.append(" ");
 426		mOSString.append(un.version);
 427		mOSString.append(" ");
 428		mOSString.append(un.machine);
 429
 430		// Simplify 'Simple'
 431		std::string ostype = mOSStringSimple.substr(0, mOSStringSimple.find_first_of(" ", 0));
 432		if (ostype == "Linux")
 433		{
 434			// Only care about major and minor Linux versions, truncate at second '.'
 435			std::string::size_type idx1 = mOSStringSimple.find_first_of(".", 0);
 436			std::string::size_type idx2 = (idx1 != std::string::npos) ? mOSStringSimple.find_first_of(".", idx1+1) : std::string::npos;
 437			std::string simple = mOSStringSimple.substr(0, idx2);
 438			if (simple.length() > 0)
 439				mOSStringSimple = simple;
 440		}
 441	}
 442	else
 443	{
 444		mOSStringSimple.append("Unable to collect OS info");
 445		mOSString = mOSStringSimple;
 446	}
 447#endif
 448
 449}
 450
 451#ifndef LL_WINDOWS
 452// static
 453S32 LLOSInfo::getMaxOpenFiles()
 454{
 455	const S32 OPEN_MAX_GUESS = 256;
 456
 457#ifdef	OPEN_MAX
 458	static S32 open_max = OPEN_MAX;
 459#else
 460	static S32 open_max = 0;
 461#endif
 462
 463	if (0 == open_max)
 464	{
 465		// First time through.
 466		errno = 0;
 467		if ( (open_max = sysconf(_SC_OPEN_MAX)) < 0)
 468		{
 469			if (0 == errno)
 470			{
 471				// Indeterminate.
 472				open_max = OPEN_MAX_GUESS;
 473			}
 474			else
 475			{
 476				llerrs << "LLOSInfo::getMaxOpenFiles: sysconf error for _SC_OPEN_MAX" << llendl;
 477			}
 478		}
 479	}
 480	return open_max;
 481}
 482#endif
 483
 484void LLOSInfo::stream(std::ostream& s) const
 485{
 486	s << mOSString;
 487}
 488
 489const std::string& LLOSInfo::getOSString() const
 490{
 491	return mOSString;
 492}
 493
 494const std::string& LLOSInfo::getOSStringSimple() const
 495{
 496	return mOSStringSimple;
 497}
 498
 499const S32 STATUS_SIZE = 8192;
 500
 501//static
 502U32 LLOSInfo::getProcessVirtualSizeKB()
 503{
 504	U32 virtual_size = 0;
 505#if LL_WINDOWS
 506#endif
 507#if LL_LINUX
 508	LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb");
 509	if (status_filep)
 510	{
 511		S32 numRead = 0;		
 512		char buff[STATUS_SIZE];		/* Flawfinder: ignore */
 513
 514		size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep);
 515		buff[nbytes] = '\0';
 516
 517		// All these guys return numbers in KB
 518		char *memp = strstr(buff, "VmSize:");
 519		if (memp)
 520		{
 521			numRead += sscanf(memp, "%*s %u", &virtual_size);
 522		}
 523		fclose(status_filep);
 524	}
 525#elif LL_SOLARIS
 526	char proc_ps[LL_MAX_PATH];
 527	sprintf(proc_ps, "/proc/%d/psinfo", (int)getpid());
 528	int proc_fd = -1;
 529	if((proc_fd = open(proc_ps, O_RDONLY)) == -1){
 530		llwarns << "unable to open " << proc_ps << llendl;
 531		return 0;
 532	}
 533	psinfo_t proc_psinfo;
 534	if(read(proc_fd, &proc_psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)){
 535		llwarns << "Unable to read " << proc_ps << llendl;
 536		close(proc_fd);
 537		return 0;
 538	}
 539
 540	close(proc_fd);
 541
 542	virtual_size = proc_psinfo.pr_size;
 543#endif
 544	return virtual_size;
 545}
 546
 547//static
 548U32 LLOSInfo::getProcessResidentSizeKB()
 549{
 550	U32 resident_size = 0;
 551#if LL_WINDOWS
 552#endif
 553#if LL_LINUX
 554	LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb");
 555	if (status_filep != NULL)
 556	{
 557		S32 numRead = 0;
 558		char buff[STATUS_SIZE];		/* Flawfinder: ignore */
 559
 560		size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep);
 561		buff[nbytes] = '\0';
 562
 563		// All these guys return numbers in KB
 564		char *memp = strstr(buff, "VmRSS:");
 565		if (memp)
 566		{
 567			numRead += sscanf(memp, "%*s %u", &resident_size);
 568		}
 569		fclose(status_filep);
 570	}
 571#elif LL_SOLARIS
 572	char proc_ps[LL_MAX_PATH];
 573	sprintf(proc_ps, "/proc/%d/psinfo", (int)getpid());
 574	int proc_fd = -1;
 575	if((proc_fd = open(proc_ps, O_RDONLY)) == -1){
 576		llwarns << "unable to open " << proc_ps << llendl;
 577		return 0;
 578	}
 579	psinfo_t proc_psinfo;
 580	if(read(proc_fd, &proc_psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)){
 581		llwarns << "Unable to read " << proc_ps << llendl;
 582		close(proc_fd);
 583		return 0;
 584	}
 585
 586	close(proc_fd);
 587
 588	resident_size = proc_psinfo.pr_rssize;
 589#endif
 590	return resident_size;
 591}
 592
 593LLCPUInfo::LLCPUInfo()
 594{
 595	std::ostringstream out;
 596	LLProcessorInfo proc;
 597	// proc.WriteInfoTextFile("procInfo.txt");
 598	mHasSSE = proc.hasSSE();
 599	mHasSSE2 = proc.hasSSE2();
 600	mHasAltivec = proc.hasAltivec();
 601	mCPUMHz = (F64)proc.getCPUFrequency();
 602	mFamily = proc.getCPUFamilyName();
 603	mCPUString = "Unknown";
 604
 605	out << proc.getCPUBrandName();
 606	if (200 < mCPUMHz && mCPUMHz < 10000)           // *NOTE: cpu speed is often way wrong, do a sanity check
 607	{
 608		out << " (" << mCPUMHz << " MHz)";
 609	}
 610	mCPUString = out.str();
 611	LLStringUtil::trim(mCPUString);
 612}
 613
 614bool LLCPUInfo::hasAltivec() const
 615{
 616	return mHasAltivec;
 617}
 618
 619bool LLCPUInfo::hasSSE() const
 620{
 621	return mHasSSE;
 622}
 623
 624bool LLCPUInfo::hasSSE2() const
 625{
 626	return mHasSSE2;
 627}
 628
 629F64 LLCPUInfo::getMHz() const
 630{
 631	return mCPUMHz;
 632}
 633
 634std::string LLCPUInfo::getCPUString() const
 635{
 636	return mCPUString;
 637}
 638
 639void LLCPUInfo::stream(std::ostream& s) const
 640{
 641	// gather machine information.
 642	s << LLProcessorInfo().getCPUFeatureDescription();
 643
 644	// These are interesting as they reflect our internal view of the
 645	// CPU's attributes regardless of platform
 646	s << "->mHasSSE:     " << (U32)mHasSSE << std::endl;
 647	s << "->mHasSSE2:    " << (U32)mHasSSE2 << std::endl;
 648	s << "->mHasAltivec: " << (U32)mHasAltivec << std::endl;
 649	s << "->mCPUMHz:     " << mCPUMHz << std::endl;
 650	s << "->mCPUString:  " << mCPUString << std::endl;
 651}
 652
 653// Helper class for LLMemoryInfo: accumulate stats in the form we store for
 654// LLMemoryInfo::getStatsMap().
 655class Stats
 656{
 657public:
 658	Stats():
 659		mStats(LLSD::emptyMap())
 660	{}
 661
 662	// Store every integer type as LLSD::Integer.
 663	template <class T>
 664	void add(const LLSD::String& name, const T& value,
 665			 typename boost::enable_if<boost::is_integral<T> >::type* = 0)
 666	{
 667		mStats[name] = LLSD::Integer(value);
 668	}
 669
 670	// Store every floating-point type as LLSD::Real.
 671	template <class T>
 672	void add(const LLSD::String& name, const T& value,
 673			 typename boost::enable_if<boost::is_float<T> >::type* = 0)
 674	{
 675		mStats[name] = LLSD::Real(value);
 676	}
 677
 678	// Hope that LLSD::Date values are sufficiently unambiguous.
 679	void add(const LLSD::String& name, const LLSD::Date& value)
 680	{
 681		mStats[name] = value;
 682	}
 683
 684	LLSD get() const { return mStats; }
 685
 686private:
 687	LLSD mStats;
 688};
 689
 690// Wrap boost::regex_match() with a function that doesn't throw.
 691template <typename S, typename M, typename R>
 692static bool regex_match_no_exc(const S& string, M& match, const R& regex)
 693{
 694    try
 695    {
 696        return boost::regex_match(string, match, regex);
 697    }
 698    catch (const std::runtime_error& e)
 699    {
 700        LL_WARNS("LLMemoryInfo") << "error matching with '" << regex.str() << "': "
 701                                 << e.what() << ":\n'" << string << "'" << LL_ENDL;
 702        return false;
 703    }
 704}
 705
 706// Wrap boost::regex_search() with a function that doesn't throw.
 707template <typename S, typename M, typename R>
 708static bool regex_search_no_exc(const S& string, M& match, const R& regex)
 709{
 710    try
 711    {
 712        return boost::regex_search(string, match, regex);
 713    }
 714    catch (const std::runtime_error& e)
 715    {
 716        LL_WARNS("LLMemoryInfo") << "error searching with '" << regex.str() << "': "
 717                                 << e.what() << ":\n'" << string << "'" << LL_ENDL;
 718        return false;
 719    }
 720}
 721
 722LLMemoryInfo::LLMemoryInfo()
 723{
 724	refresh();
 725}
 726
 727#if LL_WINDOWS
 728static U32 LLMemoryAdjustKBResult(U32 inKB)
 729{
 730	// Moved this here from llfloaterabout.cpp
 731
 732	//! \bug
 733	// For some reason, the reported amount of memory is always wrong.
 734	// The original adjustment assumes it's always off by one meg, however
 735	// errors of as much as 2520 KB have been observed in the value
 736	// returned from the GetMemoryStatusEx function.  Here we keep the
 737	// original adjustment from llfoaterabout.cpp until this can be
 738	// fixed somehow.
 739	inKB += 1024;
 740
 741	return inKB;
 742}
 743#endif
 744
 745U32 LLMemoryInfo::getPhysicalMemoryKB() const
 746{
 747#if LL_WINDOWS
 748	return LLMemoryAdjustKBResult(mStatsMap["Total Physical KB"].asInteger());
 749
 750#elif LL_DARWIN
 751	// This might work on Linux as well.  Someone check...
 752	uint64_t phys = 0;
 753	int mib[2] = { CTL_HW, HW_MEMSIZE };
 754
 755	size_t len = sizeof(phys);	
 756	sysctl(mib, 2, &phys, &len, NULL, 0);
 757	
 758	return (U32)(phys >> 10);
 759
 760#elif LL_LINUX
 761	U64 phys = 0;
 762	phys = (U64)(getpagesize()) * (U64)(get_phys_pages());
 763	return (U32)(phys >> 10);
 764
 765#elif LL_SOLARIS
 766	U64 phys = 0;
 767	phys = (U64)(getpagesize()) * (U64)(sysconf(_SC_PHYS_PAGES));
 768	return (U32)(phys >> 10);
 769
 770#else
 771	return 0;
 772
 773#endif
 774}
 775
 776U32 LLMemoryInfo::getPhysicalMemoryClamped() const
 777{
 778	// Return the total physical memory in bytes, but clamp it
 779	// to no more than U32_MAX
 780	
 781	U32 phys_kb = getPhysicalMemoryKB();
 782	if (phys_kb >= 4194304 /* 4GB in KB */)
 783	{
 784		return U32_MAX;
 785	}
 786	else
 787	{
 788		return phys_kb << 10;
 789	}
 790}
 791
 792//static
 793void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb)
 794{
 795#if LL_WINDOWS
 796	// Sigh, this shouldn't be a static method, then we wouldn't have to
 797	// reload this data separately from refresh()
 798	LLSD statsMap(loadStatsMap());
 799
 800	avail_physical_mem_kb = statsMap["Avail Physical KB"].asInteger();
 801	avail_virtual_mem_kb  = statsMap["Avail Virtual KB"].asInteger();
 802
 803#elif LL_DARWIN
 804	// mStatsMap is derived from vm_stat, look for (e.g.) "kb free":
 805	// $ vm_stat
 806	// Mach Virtual Memory Statistics: (page size of 4096 bytes)
 807	// Pages free:                   462078.
 808	// Pages active:                 142010.
 809	// Pages inactive:               220007.
 810	// Pages wired down:             159552.
 811	// "Translation faults":      220825184.
 812	// Pages copy-on-write:         2104153.
 813	// Pages zero filled:         167034876.
 814	// Pages reactivated:             65153.
 815	// Pageins:                     2097212.
 816	// Pageouts:                      41759.
 817	// Object cache: 841598 hits of 7629869 lookups (11% hit rate)
 818	avail_physical_mem_kb = -1 ;
 819	avail_virtual_mem_kb = -1 ;
 820
 821#elif LL_LINUX
 822	// mStatsMap is derived from MEMINFO_FILE:
 823	// $ cat /proc/meminfo
 824	// MemTotal:        4108424 kB
 825	// MemFree:         1244064 kB
 826	// Buffers:           85164 kB
 827	// Cached:          1990264 kB
 828	// SwapCached:            0 kB
 829	// Active:          1176648 kB
 830	// Inactive:        1427532 kB
 831	// Active(anon):     529152 kB
 832	// Inactive(anon):    15924 kB
 833	// Active(file):     647496 kB
 834	// Inactive(file):  1411608 kB
 835	// Unevictable:          16 kB
 836	// Mlocked:              16 kB
 837	// HighTotal:       3266316 kB
 838	// HighFree:         721308 kB
 839	// LowTotal:         842108 kB
 840	// LowFree:          522756 kB
 841	// SwapTotal:       6384632 kB
 842	// SwapFree:        6384632 kB
 843	// Dirty:                28 kB
 844	// Writeback:             0 kB
 845	// AnonPages:        528820 kB
 846	// Mapped:            89472 kB
 847	// Shmem:             16324 kB
 848	// Slab:             159624 kB
 849	// SReclaimable:     145168 kB
 850	// SUnreclaim:        14456 kB
 851	// KernelStack:        2560 kB
 852	// PageTables:         5560 kB
 853	// NFS_Unstable:          0 kB
 854	// Bounce:                0 kB
 855	// WritebackTmp:          0 kB
 856	// CommitLimit:     8438844 kB
 857	// Committed_AS:    1271596 kB
 858	// VmallocTotal:     122880 kB
 859	// VmallocUsed:       65252 kB
 860	// VmallocChunk:      52356 kB
 861	// HardwareCorrupted:     0 kB
 862	// HugePages_Total:       0
 863	// HugePages_Free:        0
 864	// HugePages_Rsvd:        0
 865	// HugePages_Surp:        0
 866	// Hugepagesize:       2048 kB
 867	// DirectMap4k:      434168 kB
 868	// DirectMap2M:      477184 kB
 869	// (could also run 'free', but easier to read a file than run a program)
 870	avail_physical_mem_kb = -1 ;
 871	avail_virtual_mem_kb = -1 ;
 872
 873#else
 874	//do not know how to collect available memory info for other systems.
 875	//leave it blank here for now.
 876
 877	avail_physical_mem_kb = -1 ;
 878	avail_virtual_mem_kb = -1 ;
 879#endif
 880}
 881
 882void LLMemoryInfo::stream(std::ostream& s) const
 883{
 884	// We want these memory stats to be easy to grep from the log, along with
 885	// the timestamp. So preface each line with the timestamp and a
 886	// distinctive marker. Without that, we'd have to search the log for the
 887	// introducer line, then read subsequent lines, etc...
 888	std::string pfx(LLError::utcTime() + " <mem> ");
 889
 890	// Max key length
 891	size_t key_width(0);
 892	BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap))
 893	{
 894		size_t len(pair.first.length());
 895		if (len > key_width)
 896		{
 897			key_width = len;
 898		}
 899	}
 900
 901	// Now stream stats
 902	BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap))
 903	{
 904		s << pfx << std::setw(key_width+1) << (pair.first + ':') << ' ';
 905		LLSD value(pair.second);
 906		if (value.isInteger())
 907			s << std::setw(12) << value.asInteger();
 908		else if (value.isReal())
 909			s << std::fixed << std::setprecision(1) << value.asReal();
 910		else if (value.isDate())
 911			value.asDate().toStream(s);
 912		else
 913			s << value;           // just use default LLSD formatting
 914		s << std::endl;
 915	}
 916}
 917
 918LLSD LLMemoryInfo::getStatsMap() const
 919{
 920	return mStatsMap;
 921}
 922
 923LLMemoryInfo& LLMemoryInfo::refresh()
 924{
 925	mStatsMap = loadStatsMap();
 926
 927	LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n";
 928	LLSDSerialize::toPrettyXML(mStatsMap, LL_CONT);
 929	LL_ENDL;
 930
 931	return *this;
 932}
 933
 934LLSD LLMemoryInfo::loadStatsMap()
 935{
 936	// This implementation is derived from stream() code (as of 2011-06-29).
 937	Stats stats;
 938
 939	// associate timestamp for analysis over time
 940	stats.add("timestamp", LLDate::now());
 941
 942#if LL_WINDOWS
 943	MEMORYSTATUSEX state;
 944	state.dwLength = sizeof(state);
 945	GlobalMemoryStatusEx(&state);
 946
 947	stats.add("Percent Memory use", state.dwMemoryLoad);
 948	stats.add("Total Physical KB",  state.ullTotalPhys/1024);
 949	stats.add("Avail Physical KB",  state.ullAvailPhys/1024);
 950	stats.add("Total page KB",      state.ullTotalPageFile/1024);
 951	stats.add("Avail page KB",      state.ullAvailPageFile/1024);
 952	stats.add("Total Virtual KB",   state.ullTotalVirtual/1024);
 953	stats.add("Avail Virtual KB",   state.ullAvailVirtual/1024);
 954
 955	PERFORMANCE_INFORMATION perf;
 956	perf.cb = sizeof(perf);
 957	GetPerformanceInfo(&perf, sizeof(perf));
 958
 959	SIZE_T pagekb(perf.PageSize/1024);
 960	stats.add("CommitTotal KB",     perf.CommitTotal * pagekb);
 961	stats.add("CommitLimit KB",     perf.CommitLimit * pagekb);
 962	stats.add("CommitPeak KB",      perf.CommitPeak * pagekb);
 963	stats.add("PhysicalTotal KB",   perf.PhysicalTotal * pagekb);
 964	stats.add("PhysicalAvail KB",   perf.PhysicalAvailable * pagekb);
 965	stats.add("SystemCache KB",     perf.SystemCache * pagekb);
 966	stats.add("KernelTotal KB",     perf.KernelTotal * pagekb);
 967	stats.add("KernelPaged KB",     perf.KernelPaged * pagekb);
 968	stats.add("KernelNonpaged KB",  perf.KernelNonpaged * pagekb);
 969	stats.add("PageSize KB",        pagekb);
 970	stats.add("HandleCount",        perf.HandleCount);
 971	stats.add("ProcessCount",       perf.ProcessCount);
 972	stats.add("ThreadCount",        perf.ThreadCount);
 973
 974	PROCESS_MEMORY_COUNTERS_EX pmem;
 975	pmem.cb = sizeof(pmem);
 976	// GetProcessMemoryInfo() is documented to accept either
 977	// PROCESS_MEMORY_COUNTERS* or PROCESS_MEMORY_COUNTERS_EX*, presumably
 978	// using the redundant size info to distinguish. But its prototype
 979	// specifically accepts PROCESS_MEMORY_COUNTERS*, and since this is a
 980	// classic-C API, PROCESS_MEMORY_COUNTERS_EX isn't a subclass. Cast the
 981	// pointer.
 982	GetProcessMemoryInfo(GetCurrentProcess(), PPROCESS_MEMORY_COUNTERS(&pmem), sizeof(pmem));
 983
 984	stats.add("Page Fault Count",              pmem.PageFaultCount);
 985	stats.add("PeakWorkingSetSize KB",         pmem.PeakWorkingSetSize/1024);
 986	stats.add("WorkingSetSize KB",             pmem.WorkingSetSize/1024);
 987	stats.add("QutaPeakPagedPoolUsage KB",     pmem.QuotaPeakPagedPoolUsage/1024);
 988	stats.add("QuotaPagedPoolUsage KB",        pmem.QuotaPagedPoolUsage/1024);
 989	stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/1024);
 990	stats.add("QuotaNonPagedPoolUsage KB",     pmem.QuotaNonPagedPoolUsage/1024);
 991	stats.add("PagefileUsage KB",              pmem.PagefileUsage/1024);
 992	stats.add("PeakPagefileUsage KB",          pmem.PeakPagefileUsage/1024);
 993	stats.add("PrivateUsage KB",               pmem.PrivateUsage/1024);
 994
 995#elif LL_DARWIN
 996
 997	const vm_size_t pagekb(vm_page_size / 1024);
 998	
 999	//
1000	// Collect the vm_stat's
1001	//
1002	
1003	{
1004		vm_statistics_data_t vmstat;
1005		mach_msg_type_number_t vmstatCount = HOST_VM_INFO_COUNT;
1006
1007		if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t) &vmstat, &vmstatCount) != KERN_SUCCESS)
1008	{
1009			LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL;
1010		}
1011		else
1012		{
1013			stats.add("Pages free KB",		pagekb * vmstat.free_count);
1014			stats.add("Pages active KB",	pagekb * vmstat.active_count);
1015			stats.add("Pages inactive KB",	pagekb * vmstat.inactive_count);
1016			stats.add("Pages wired KB",		pagekb * vmstat.wire_count);
1017
1018			stats.add("Pages zero fill",		vmstat.zero_fill_count);
1019			stats.add("Page reactivations",		vmstat.reactivations);
1020			stats.add("Page-ins",				vmstat.pageins);
1021			stats.add("Page-outs",				vmstat.pageouts);
1022			
1023			stats.add("Faults",					vmstat.faults);
1024			stats.add("Faults copy-on-write",	vmstat.cow_faults);
1025			
1026			stats.add("Cache lookups",			vmstat.lookups);
1027			stats.add("Cache hits",				vmstat.hits);
1028			
1029			stats.add("Page purgeable count",	vmstat.purgeable_count);
1030			stats.add("Page purges",			vmstat.purges);
1031			
1032			stats.add("Page speculative reads",	vmstat.speculative_count);
1033		}
1034	}
1035
1036	//
1037	// Collect the misc task info
1038	//
1039
1040		{
1041		task_events_info_data_t taskinfo;
1042		unsigned taskinfoSize = sizeof(taskinfo);
1043		
1044		if (task_info(mach_task_self(), TASK_EVENTS_INFO, (task_info_t) &taskinfo, &taskinfoSize) != KERN_SUCCESS)
1045					{
1046			LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL;
1047			}
1048			else
1049			{
1050			stats.add("Task page-ins",					taskinfo.pageins);
1051			stats.add("Task copy-on-write faults",		taskinfo.cow_faults);
1052			stats.add("Task messages sent",				taskinfo.messages_sent);
1053			stats.add("Task messages received",			taskinfo.messages_received);
1054			stats.add("Task mach system call count",	taskinfo.syscalls_mach);
1055			stats.add("Task unix system call count",	taskinfo.syscalls_unix);
1056			stats.add("Task context switch count",		taskinfo.csw);
1057			}
1058	}	
1059	
1060	//
1061	// Collect the basic task info
1062	//
1063
1064		{
1065		task_basic_info_64_data_t taskinfo;
1066		unsigned taskinfoSize = sizeof(taskinfo);
1067		
1068		if (task_info(mach_task_self(), TASK_BASIC_INFO_64, (task_info_t) &taskinfo, &taskinfoSize) != KERN_SUCCESS)
1069			{
1070			LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL;
1071				}
1072				else
1073				{
1074			stats.add("Basic suspend count",					taskinfo.suspend_count);
1075			stats.add("Basic virtual memory KB",				taskinfo.virtual_size / 1024);
1076			stats.add("Basic resident memory KB",				taskinfo.resident_size / 1024);
1077			stats.add("Basic new thread policy",				taskinfo.policy);
1078		}
1079	}
1080
1081#elif LL_SOLARIS
1082	U64 phys = 0;
1083
1084	phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024);
1085
1086	stats.add("Total Physical KB", phys);
1087
1088#elif LL_LINUX
1089	std::ifstream meminfo(MEMINFO_FILE);
1090	if (meminfo.is_open())
1091	{
1092		// MemTotal:		4108424 kB
1093		// MemFree:			1244064 kB
1094		// Buffers:			  85164 kB
1095		// Cached:			1990264 kB
1096		// SwapCached:			  0 kB
1097		// Active:			1176648 kB
1098		// Inactive:		1427532 kB
1099		// ...
1100		// VmallocTotal:	 122880 kB
1101		// VmallocUsed:		  65252 kB
1102		// VmallocChunk:	  52356 kB
1103		// HardwareCorrupted:	  0 kB
1104		// HugePages_Total:		  0
1105		// HugePages_Free:		  0
1106		// HugePages_Rsvd:		  0
1107		// HugePages_Surp:		  0
1108		// Hugepagesize:	   2048 kB
1109		// DirectMap4k:		 434168 kB
1110		// DirectMap2M:		 477184 kB
1111
1112		// Intentionally don't pass the boost::no_except flag. This
1113		// boost::regex object is constructed with a string literal, so it
1114		// should be valid every time. If it becomes invalid, we WANT an
1115		// exception, hopefully even before the dev checks in.
1116		boost::regex stat_rx("(.+): +([0-9]+)( kB)?");
1117		boost::smatch matched;
1118
1119		std::string line;
1120		while (std::getline(meminfo, line))
1121		{
1122			LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL;
1123			if (regex_match_no_exc(line, matched, stat_rx))
1124			{
1125				// e.g. "MemTotal:		4108424 kB"
1126				LLSD::String key(matched[1].first, matched[1].second);
1127				LLSD::String value_str(matched[2].first, matched[2].second);
1128				LLSD::Integer value(0);
1129				try
1130				{
1131					value = boost::lexical_cast<LLSD::Integer>(value_str);
1132				}
1133				catch (const boost::bad_lexical_cast&)
1134				{
1135					LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str
1136											 << "' in " << MEMINFO_FILE << " line: "
1137											 << line << LL_ENDL;
1138					continue;
1139				}
1140				// Store this statistic.
1141				stats.add(key, value);
1142			}
1143			else
1144			{
1145				LL_WARNS("LLMemoryInfo") << "unrecognized " << MEMINFO_FILE << " line: "
1146										 << line << LL_ENDL;
1147			}
1148		}
1149	}
1150	else
1151	{
1152		LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL;
1153	}
1154
1155#else
1156	LL_WARNS("LLMemoryInfo") << "Unknown system; unable to collect memory information" << LL_ENDL;
1157
1158#endif
1159
1160	return stats.get();
1161}
1162
1163std::ostream& operator<<(std::ostream& s, const LLOSInfo& info)
1164{
1165	info.stream(s);
1166	return s;
1167}
1168
1169std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info)
1170{
1171	info.stream(s);
1172	return s;
1173}
1174
1175std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info)
1176{
1177	info.stream(s);
1178	return s;
1179}
1180
1181class FrameWatcher
1182{
1183public:
1184    FrameWatcher():
1185        // Hooking onto the "mainloop" event pump gets us one call per frame.
1186        mConnection(LLEventPumps::instance()
1187                    .obtain("mainloop")
1188                    .listen("FrameWatcher", boost::bind(&FrameWatcher::tick, this, _1))),
1189        // Initializing mSampleStart to an invalid timestamp alerts us to skip
1190        // trying to compute framerate on the first call.
1191        mSampleStart(-1),
1192        // Initializing mSampleEnd to 0 ensures that we treat the first call
1193        // as the completion of a sample window.
1194        mSampleEnd(0),
1195        mFrames(0),
1196        // Both MEM_INFO_WINDOW and MEM_INFO_THROTTLE are in seconds. We need
1197        // the number of integer MEM_INFO_THROTTLE sample slots that will fit
1198        // in MEM_INFO_WINDOW. Round up.
1199        mSamples(int((MEM_INFO_WINDOW / MEM_INFO_THROTTLE) + 0.7)),
1200        // Initializing to F32_MAX means that the first real frame will become
1201        // the slowest ever, which sounds like a good idea.
1202        mSlowest(F32_MAX)
1203    {}
1204
1205    bool tick(const LLSD&)
1206    {
1207        F32 timestamp(mTimer.getElapsedTimeF32());
1208
1209        // Count this frame in the interval just completed.
1210        ++mFrames;
1211
1212        // Have we finished a sample window yet?
1213        if (timestamp < mSampleEnd)
1214        {
1215            // no, just keep waiting
1216            return false;
1217        }
1218
1219        // Set up for next sample window. Capture values for previous frame in
1220        // local variables and reset data members.
1221        U32 frames(mFrames);
1222        F32 sampleStart(mSampleStart);
1223        // No frames yet in next window
1224        mFrames = 0;
1225        // which starts right now
1226        mSampleStart = timestamp;
1227        // and ends MEM_INFO_THROTTLE seconds in the future
1228        mSampleEnd = mSampleStart + MEM_INFO_THROTTLE;
1229
1230        // On the very first call, that's all we can do, no framerate
1231        // computation is possible.
1232        if (sampleStart < 0)
1233        {
1234            return false;
1235        }
1236
1237        // How long did this actually take? As framerate slows, the duration
1238        // of the frame we just finished could push us WELL beyond our desired
1239        // sample window size.
1240        F32 elapsed(timestamp - sampleStart);
1241        F32 framerate(frames/elapsed);
1242
1243        // Remember previous slowest framerate because we're just about to
1244        // update it.
1245        F32 slowest(mSlowest);
1246        // Remember previous number of samples.
1247        boost::circular_buffer<F32>::size_type prevSize(mSamples.size());
1248
1249        // Capture new framerate in our samples buffer. Once the buffer is
1250        // full (after MEM_INFO_WINDOW seconds), this will displace the oldest
1251        // sample. ("So they all rolled over, and one fell out...")
1252        mSamples.push_back(framerate);
1253
1254        // Calculate the new minimum framerate. I know of no way to update a
1255        // rolling minimum without ever rescanning the buffer. But since there
1256        // are only a few tens of items in this buffer, rescanning it is
1257        // probably cheaper (and certainly easier to reason about) than
1258        // attempting to optimize away some of the scans.
1259        mSlowest = framerate;       // pick an arbitrary entry to start
1260        for (boost::circular_buffer<F32>::const_iterator si(mSamples.begin()), send(mSamples.end());
1261             si != send; ++si)
1262        {
1263            if (*si < mSlowest)
1264            {
1265                mSlowest = *si;
1266            }
1267        }
1268
1269        // We're especially interested in memory as framerate drops. Only log
1270        // when framerate drops below the slowest framerate we remember.
1271        // (Should always be true for the end of the very first sample
1272        // window.)
1273        if (framerate >= slowest)
1274        {
1275            return false;
1276        }
1277        // Congratulations, we've hit a new low.  :-P
1278
1279        LL_INFOS("FrameWatcher") << ' ';
1280        if (! prevSize)
1281        {
1282            LL_CONT << "initial framerate ";
1283        }
1284        else
1285        {
1286            LL_CONT << "slowest framerate for last " << int(prevSize * MEM_INFO_THROTTLE)
1287                    << " seconds ";
1288        }
1289        LL_CONT << std::fixed << std::setprecision(1) << framerate << '\n'
1290                << LLMemoryInfo() << LL_ENDL;
1291
1292        return false;
1293    }
1294
1295private:
1296    // Storing the connection in an LLTempBoundListener ensures it will be
1297    // disconnected when we're destroyed.
1298    LLTempBoundListener mConnection;
1299    // Track elapsed time
1300    LLTimer mTimer;
1301    // Some of what you see here is in fact redundant with functionality you
1302    // can get from LLTimer. Unfortunately the LLTimer API is missing the
1303    // feature we need: has at least the stated interval elapsed, and if so,
1304    // exactly how long has passed? So we have to do it by hand, sigh.
1305    // Time at start, end of sample window
1306    F32 mSampleStart, mSampleEnd;
1307    // Frames this sample window
1308    U32 mFrames;
1309    // Sliding window of framerate samples
1310    boost::circular_buffer<F32> mSamples;
1311    // Slowest framerate in mSamples
1312    F32 mSlowest;
1313};
1314
1315// Need an instance of FrameWatcher before it does any good
1316static FrameWatcher sFrameWatcher;
1317
1318BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile)
1319{
1320	std::string tmpfile;
1321	const S32 UNCOMPRESS_BUFFER_SIZE = 32768;
1322	BOOL retval = FALSE;
1323	gzFile src = NULL;
1324	U8 buffer[UNCOMPRESS_BUFFER_SIZE];
1325	LLFILE *dst = NULL;
1326	S32 bytes = 0;
1327	tmpfile = dstfile + ".t";
1328	src = gzopen(srcfile.c_str(), "rb");
1329	if (! src) goto err;
1330	dst = LLFile::fopen(tmpfile, "wb");		/* Flawfinder: ignore */
1331	if (! dst) goto err;
1332	do
1333	{
1334		bytes = gzread(src, buffer, UNCOMPRESS_BUFFER_SIZE);
1335		size_t nwrit = fwrite(buffer, sizeof(U8), bytes, dst);
1336		if (nwrit < (size_t) bytes)
1337		{
1338			llwarns << "Short write on " << tmpfile << ": Wrote " << nwrit << " of " << bytes << " bytes." << llendl;
1339			goto err;
1340		}
1341	} while(gzeof(src) == 0);
1342	fclose(dst); 
1343	dst = NULL;	
1344	if (LLFile::rename(tmpfile, dstfile) == -1) goto err;		/* Flawfinder: ignore */
1345	retval = TRUE;
1346err:
1347	if (src != NULL) gzclose(src);
1348	if (dst != NULL) fclose(dst);
1349	return retval;
1350}
1351
1352BOOL gzip_file(const std::string& srcfile, const std::string& dstfile)
1353{
1354	const S32 COMPRESS_BUFFER_SIZE = 32768;
1355	std::string tmpfile;
1356	BOOL retval = FALSE;
1357	U8 buffer[COMPRESS_BUFFER_SIZE];
1358	gzFile dst = NULL;
1359	LLFILE *src = NULL;
1360	S32 bytes = 0;
1361	tmpfile = dstfile + ".t";
1362	dst = gzopen(tmpfile.c_str(), "wb");		/* Flawfinder: ignore */
1363	if (! dst) goto err;
1364	src = LLFile::fopen(srcfile, "rb");		/* Flawfinder: ignore */
1365	if (! src) goto err;
1366
1367	while ((bytes = (S32)fread(buffer, sizeof(U8), COMPRESS_BUFFER_SIZE, src)) > 0)
1368	{
1369		if (gzwrite(dst, buffer, bytes) <= 0)
1370		{
1371			llwarns << "gzwrite failed: " << gzerror(dst, NULL) << llendl;
1372			goto err;
1373		}
1374	}
1375
1376	if (ferror(src))
1377	{
1378		llwarns << "Error reading " << srcfile << llendl;
1379		goto err;
1380	}
1381
1382	gzclose(dst);
1383	dst = NULL;
1384#if LL_WINDOWS
1385	// Rename in windows needs the dstfile to not exist.
1386	LLFile::remove(dstfile);
1387#endif
1388	if (LLFile::rename(tmpfile, dstfile) == -1) goto err;		/* Flawfinder: ignore */
1389	retval = TRUE;
1390 err:
1391	if (src != NULL) fclose(src);
1392	if (dst != NULL) gzclose(dst);
1393	return retval;
1394}