/indra/llcommon/llsys.cpp

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