From 0ec4d813ebfd80f82bb2b84e993f7a192e3fddb5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 23 Jun 2011 14:50:28 -0400 Subject: Log enriched memory info for Mac too. Add Mac logic to LLMemoryInfo::stream(): run vm_stat and log its output. Add comments with Mac and Linux suggestions to LLMemoryInfo::getAvailableMemoryKB(), responding to comment: //do not know how to collect available memory info for other systems. --- indra/llcommon/llsys.cpp | 106 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 99 insertions(+), 7 deletions(-) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index e8616a9be6..4190c91fd8 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -1,6 +1,6 @@ /** * @file llsys.cpp - * @brief Impelementation of the basic system query functions. + * @brief Implementation of the basic system query functions. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code @@ -697,6 +697,76 @@ void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_v avail_physical_mem_kb = (U32)(state.ullAvailPhys/1024) ; avail_virtual_mem_kb = (U32)(state.ullAvailVirtual/1024) ; +#elif LL_DARWIN + // Run vm_stat and filter output, scaling for page size: + // $ vm_stat + // Mach Virtual Memory Statistics: (page size of 4096 bytes) + // Pages free: 462078. + // Pages active: 142010. + // Pages inactive: 220007. + // Pages wired down: 159552. + // "Translation faults": 220825184. + // Pages copy-on-write: 2104153. + // Pages zero filled: 167034876. + // Pages reactivated: 65153. + // Pageins: 2097212. + // Pageouts: 41759. + // Object cache: 841598 hits of 7629869 lookups (11% hit rate) + avail_physical_mem_kb = -1 ; + avail_virtual_mem_kb = -1 ; + +#elif LL_LINUX + // Read selected lines from MEMINFO_FILE: + // $ cat /proc/meminfo + // MemTotal: 4108424 kB + // MemFree: 1244064 kB + // Buffers: 85164 kB + // Cached: 1990264 kB + // SwapCached: 0 kB + // Active: 1176648 kB + // Inactive: 1427532 kB + // Active(anon): 529152 kB + // Inactive(anon): 15924 kB + // Active(file): 647496 kB + // Inactive(file): 1411608 kB + // Unevictable: 16 kB + // Mlocked: 16 kB + // HighTotal: 3266316 kB + // HighFree: 721308 kB + // LowTotal: 842108 kB + // LowFree: 522756 kB + // SwapTotal: 6384632 kB + // SwapFree: 6384632 kB + // Dirty: 28 kB + // Writeback: 0 kB + // AnonPages: 528820 kB + // Mapped: 89472 kB + // Shmem: 16324 kB + // Slab: 159624 kB + // SReclaimable: 145168 kB + // SUnreclaim: 14456 kB + // KernelStack: 2560 kB + // PageTables: 5560 kB + // NFS_Unstable: 0 kB + // Bounce: 0 kB + // WritebackTmp: 0 kB + // CommitLimit: 8438844 kB + // Committed_AS: 1271596 kB + // VmallocTotal: 122880 kB + // VmallocUsed: 65252 kB + // VmallocChunk: 52356 kB + // HardwareCorrupted: 0 kB + // HugePages_Total: 0 + // HugePages_Free: 0 + // HugePages_Rsvd: 0 + // HugePages_Surp: 0 + // Hugepagesize: 2048 kB + // DirectMap4k: 434168 kB + // DirectMap2M: 477184 kB + // (could also run 'free', but easier to read a file than run a program) + avail_physical_mem_kb = -1 ; + avail_virtual_mem_kb = -1 ; + #else //do not know how to collect available memory info for other systems. //leave it blank here for now. @@ -720,6 +790,7 @@ void LLMemoryInfo::stream(std::ostream& s) const s << "Avail page KB: " << (U32)(state.ullAvailPageFile/1024) << std::endl; s << "Total Virtual KB: " << (U32)(state.ullTotalVirtual/1024) << std::endl; s << "Avail Virtual KB: " << (U32)(state.ullAvailVirtual/1024) << std::endl; + #elif LL_DARWIN uint64_t phys = 0; @@ -731,22 +802,39 @@ void LLMemoryInfo::stream(std::ostream& s) const } else { - s << "Unable to collect memory information"; + s << "Unable to collect hw.memsize memory information" << std::endl; + } + + FILE* pout = popen("vm_stat 2>&1", "r"); + if (! pout) + { + s << "Unable to collect vm_stat memory information" << std::endl; } + else + { + // Here 'pout' is vm_stat's stdout. Copy it to output stream. + char line[100]; + while (fgets(line, sizeof(line), pout)) + { + s << line; + } + fclose(pout); + } + #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? + +#elif LL_LINUX LLFILE* 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)) + memset(line, 0, sizeof(line)); + while(fgets(line, sizeof(line), meminfo)) { line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/ s << line; @@ -755,8 +843,12 @@ void LLMemoryInfo::stream(std::ostream& s) const } else { - s << "Unable to collect memory information"; + s << "Unable to collect memory information" << std::endl; } + +#else + s << "Unknown system; unable to collect memory information" << std::endl; + #endif } -- cgit v1.2.3 From 2619f3db1f781489b9efab0c5ed26c526b013158 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 24 Jun 2011 10:46:38 -0400 Subject: CHOP-753: add timestamp and marker to memory stats log lines --- indra/llcommon/llsys.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 4190c91fd8..015a24cf23 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -36,6 +36,7 @@ #endif #include "llprocessor.h" +#include "llerrorcontrol.h" #if LL_WINDOWS # define WIN32_LEAN_AND_MEAN @@ -778,18 +779,24 @@ void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_v void LLMemoryInfo::stream(std::ostream& s) const { + // We want these memory stats to be easy to grep from the log, along with + // the timestamp. So preface each line with the timestamp and a + // distinctive marker. Without that, we'd have to search the log for the + // introducer line, then read subsequent lines, etc... + std::string pfx(LLError::utcTime() + " "); + #if LL_WINDOWS MEMORYSTATUSEX state; state.dwLength = sizeof(state); GlobalMemoryStatusEx(&state); - s << "Percent Memory use: " << (U32)state.dwMemoryLoad << '%' << std::endl; - s << "Total Physical KB: " << (U32)(state.ullTotalPhys/1024) << std::endl; - s << "Avail Physical KB: " << (U32)(state.ullAvailPhys/1024) << std::endl; - s << "Total page KB: " << (U32)(state.ullTotalPageFile/1024) << std::endl; - s << "Avail page KB: " << (U32)(state.ullAvailPageFile/1024) << std::endl; - s << "Total Virtual KB: " << (U32)(state.ullTotalVirtual/1024) << std::endl; - s << "Avail Virtual KB: " << (U32)(state.ullAvailVirtual/1024) << std::endl; + s << pfx << "Percent Memory use: " << (U32)state.dwMemoryLoad << '%' << std::endl; + s << pfx << "Total Physical KB: " << (U32)(state.ullTotalPhys/1024) << std::endl; + s << pfx << "Avail Physical KB: " << (U32)(state.ullAvailPhys/1024) << std::endl; + s << pfx << "Total page KB: " << (U32)(state.ullTotalPageFile/1024) << std::endl; + s << pfx << "Avail page KB: " << (U32)(state.ullAvailPageFile/1024) << std::endl; + s << pfx << "Total Virtual KB: " << (U32)(state.ullTotalVirtual/1024) << std::endl; + s << pfx << "Avail Virtual KB: " << (U32)(state.ullAvailVirtual/1024) << std::endl; #elif LL_DARWIN uint64_t phys = 0; @@ -798,7 +805,7 @@ void LLMemoryInfo::stream(std::ostream& s) const if(sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0) { - s << "Total Physical KB: " << phys/1024 << std::endl; + s << pfx << "Total Physical KB: " << phys/1024 << std::endl; } else { @@ -816,7 +823,7 @@ void LLMemoryInfo::stream(std::ostream& s) const char line[100]; while (fgets(line, sizeof(line), pout)) { - s << line; + s << pfx << line; } fclose(pout); } @@ -826,7 +833,7 @@ void LLMemoryInfo::stream(std::ostream& s) const phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024); - s << "Total Physical KB: " << phys << std::endl; + s << pfx << "Total Physical KB: " << phys << std::endl; #elif LL_LINUX LLFILE* meminfo = LLFile::fopen(MEMINFO_FILE,"rb"); @@ -837,7 +844,7 @@ void LLMemoryInfo::stream(std::ostream& s) const while(fgets(line, sizeof(line), meminfo)) { line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/ - s << line; + s << pfx << line; } fclose(meminfo); } -- cgit v1.2.3 From abb8a7018d0fc7a52aa35f4be1cec372719e4eda Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 28 Jun 2011 08:21:49 -0400 Subject: CHOP-753: Log LLMemoryInfo whenever framerate hits a new low. Introduce FrameWatcher, a static object that hooks into the LLEventPump named "mainloop" to get a call every frame. Track framerate over a defined sample time (20 seconds atm); track minimum and log LLMemoryInfo every time we hit a new minimum. --- indra/llcommon/llsys.cpp | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 015a24cf23..92fdf4f327 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -37,6 +37,9 @@ #include "llprocessor.h" #include "llerrorcontrol.h" +#include "llevents.h" +#include "lltimer.h" +#include #if LL_WINDOWS # define WIN32_LEAN_AND_MEAN @@ -71,6 +74,10 @@ extern int errno; static const S32 CPUINFO_BUFFER_SIZE = 16383; LLCPUInfo gSysCPU; +// Don't log memory info any more often than this. It also serves as our +// framerate sample size. +static const F32 MEM_INFO_THROTTLE = 20; + #if LL_WINDOWS #ifndef DLLVERSIONINFO typedef struct _DllVersionInfo @@ -877,6 +884,105 @@ std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info) return s; } +class FrameWatcher +{ +public: + FrameWatcher(): + // Hooking onto the "mainloop" event pump gets us one call per frame. + mConnection(LLEventPumps::instance() + .obtain("mainloop") + .listen("FrameWatcher", boost::bind(&FrameWatcher::tick, this, _1))), + // Initializing mSampleStart to an invalid timestamp alerts us to skip + // trying to compute framerate on the first call. + mSampleStart(-1), + // Initializing mSampleEnd to 0 ensures that we treat the first call + // as the completion of a sample window. + mSampleEnd(0), + mFrames(0), + // Initializing to F32_MAX means that the first real frame will become + // the slowest ever, which sounds like a good idea. + mSlowest(F32_MAX), + mDesc("startup") + {} + + bool tick(const LLSD&) + { + F32 timestamp(mTimer.getElapsedTimeF32()); + + // Count this frame in the interval just completed. + ++mFrames; + + // Have we finished a sample window yet? + if (timestamp < mSampleEnd) + { + // no, just keep waiting + return false; + } + + // Set up for next sample window. Capture values for previous frame in + // local variables and reset data members. + U32 frames(mFrames); + F32 sampleStart(mSampleStart); + // No frames yet in next window + mFrames = 0; + // which starts right now + mSampleStart = timestamp; + // and ends MEM_INFO_THROTTLE seconds in the future + mSampleEnd = mSampleStart + MEM_INFO_THROTTLE; + + // On the very first call, that's all we can do, no framerate + // computation is possible. + if (sampleStart < 0) + { + return false; + } + + // How long did this actually take? As framerate slows, the duration + // of the frame we just finished could push us WELL beyond our desired + // sample window size. + F32 elapsed(timestamp - sampleStart); + F32 framerate(frames/elapsed); + + // We're especially interested in memory as framerate drops. Only log + // when framerate is lower than ever before. (Should always be true + // for the end of the very first sample window.) + if (framerate >= mSlowest) + { + return false; + } + // Congratulations, we've hit a new low. :-P + mSlowest = framerate; + + LL_INFOS("FrameWatcher") << mDesc << " framerate " + << std::fixed << std::setprecision(1) << framerate << '\n' + << LLMemoryInfo() << LL_ENDL; + mDesc = "new lowest"; + + return false; + } + +private: + // Storing the connection in an LLTempBoundListener ensures it will be + // disconnected when we're destroyed. + LLTempBoundListener mConnection; + // Track elapsed time + LLTimer mTimer; + // Some of what you see here is in fact redundant with functionality you + // can get from LLTimer. Unfortunately the LLTimer API is missing the + // feature we need: has at least the stated interval elapsed, and if so, + // exactly how long has passed? So we have to do it by hand, sigh. + // Time at start, end of sample window + F32 mSampleStart, mSampleEnd; + // Frames this sample window + U32 mFrames; + // Slowest framerate EVAR + F32 mSlowest; + // Description of next notable framerate + std::string mDesc; +}; + +static FrameWatcher sFrameWatcher; + BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile) { std::string tmpfile; -- cgit v1.2.3 From 57c230b73ea171d310ad3c132624a8fdd6751b0e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 28 Jun 2011 08:58:10 -0400 Subject: CHOP-753: suppress VS fatal warning 4355 --- indra/llcommon/llsys.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 92fdf4f327..156c78b1e8 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -24,6 +24,10 @@ * $/LicenseInfo$ */ +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + #include "linden_common.h" #include "llsys.h" -- cgit v1.2.3 From 26be53aede499182252bb797e798611169ea0553 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 28 Jun 2011 16:01:16 -0400 Subject: CHOP-753: Introduce a sliding window of framerate samples. The trouble with remembering the slowest-ever framerate is that framerate drops dramatically on login, then typically bounces back to something reasonable during the session. So the session-normal framerate has to drop pretty dramatically before it falls below the original login framerate. To address this, only remember the last ~10 minutes of framerates, and log memory stats every time a new framerate is slower than the previous 10 minutes. --- indra/llcommon/llsys.cpp | 70 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 156c78b1e8..ccd6f261b7 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -44,6 +44,7 @@ #include "llevents.h" #include "lltimer.h" #include +#include #if LL_WINDOWS # define WIN32_LEAN_AND_MEAN @@ -81,6 +82,11 @@ LLCPUInfo gSysCPU; // Don't log memory info any more often than this. It also serves as our // framerate sample size. static const F32 MEM_INFO_THROTTLE = 20; +// Sliding window of samples. We intentionally limit the length of time we +// remember "the slowest" framerate because framerate is very slow at login. +// If we only triggered FrameWatcher logging when the session framerate +// dropped below the login framerate, we'd have very little additional data. +static const F32 MEM_INFO_WINDOW = 10*60; #if LL_WINDOWS #ifndef DLLVERSIONINFO @@ -903,10 +909,13 @@ public: // as the completion of a sample window. mSampleEnd(0), mFrames(0), + // Both MEM_INFO_WINDOW and MEM_INFO_THROTTLE are in seconds. We need + // the number of integer MEM_INFO_THROTTLE sample slots that will fit + // in MEM_INFO_WINDOW. Round up. + mSamples(int((MEM_INFO_WINDOW / MEM_INFO_THROTTLE) + 0.7)), // Initializing to F32_MAX means that the first real frame will become // the slowest ever, which sounds like a good idea. - mSlowest(F32_MAX), - mDesc("startup") + mSlowest(F32_MAX) {} bool tick(const LLSD&) @@ -947,20 +956,54 @@ public: F32 elapsed(timestamp - sampleStart); F32 framerate(frames/elapsed); + // Remember previous slowest framerate because we're just about to + // update it. + F32 slowest(mSlowest); + // Remember previous number of samples. + boost::circular_buffer::size_type prevSize(mSamples.size()); + + // Capture new framerate in our samples buffer. Once the buffer is + // full (after MEM_INFO_WINDOW seconds), this will displace the oldest + // sample. ("So they all rolled over, and one fell out...") + mSamples.push_back(framerate); + + // Calculate the new minimum framerate. I know of no way to update a + // rolling minimum without ever rescanning the buffer. But since there + // are only a few tens of items in this buffer, rescanning it is + // probably cheaper (and certainly easier to reason about) than + // attempting to optimize away some of the scans. + mSlowest = framerate; // pick an arbitrary entry to start + for (boost::circular_buffer::const_iterator si(mSamples.begin()), send(mSamples.end()); + si != send; ++si) + { + if (*si < mSlowest) + { + mSlowest = *si; + } + } + // We're especially interested in memory as framerate drops. Only log - // when framerate is lower than ever before. (Should always be true - // for the end of the very first sample window.) - if (framerate >= mSlowest) + // when framerate drops below the slowest framerate we remember. + // (Should always be true for the end of the very first sample + // window.) + if (framerate >= slowest) { return false; } // Congratulations, we've hit a new low. :-P - mSlowest = framerate; - LL_INFOS("FrameWatcher") << mDesc << " framerate " - << std::fixed << std::setprecision(1) << framerate << '\n' - << LLMemoryInfo() << LL_ENDL; - mDesc = "new lowest"; + LL_INFOS("FrameWatcher") << ' '; + if (! prevSize) + { + LL_CONT << "initial framerate "; + } + else + { + LL_CONT << "slowest framerate for last " << int(prevSize * MEM_INFO_THROTTLE) + << " seconds "; + } + LL_CONT << std::fixed << std::setprecision(1) << framerate << '\n' + << LLMemoryInfo() << LL_ENDL; return false; } @@ -979,12 +1022,13 @@ private: F32 mSampleStart, mSampleEnd; // Frames this sample window U32 mFrames; - // Slowest framerate EVAR + // Sliding window of framerate samples + boost::circular_buffer mSamples; + // Slowest framerate in mSamples F32 mSlowest; - // Description of next notable framerate - std::string mDesc; }; +// Need an instance of FrameWatcher before it does any good static FrameWatcher sFrameWatcher; BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile) -- cgit v1.2.3 From 1262f710b548100e4522866341febba3d7cf535d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 28 Jun 2011 23:09:00 -0400 Subject: CHOP-753: Report Linux memory stats 1/line, like other platforms. Previous code deliberately flowed the different lines from MEMINFO_FILE together on a single line, which seems pointless to me, since we want to be able to grep the viewer log to recognize individual stats. Also replace classic-C LLFILE* machinery used to read MEMINFO_FILE with std::ifstream and std::getline(). --- indra/llcommon/llsys.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index ccd6f261b7..f0f98f5bf6 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -853,17 +853,14 @@ void LLMemoryInfo::stream(std::ostream& s) const s << pfx << "Total Physical KB: " << phys << std::endl; #elif LL_LINUX - LLFILE* meminfo = LLFile::fopen(MEMINFO_FILE,"rb"); - if(meminfo) + std::ifstream meminfo(MEMINFO_FILE); + if (meminfo.is_open()) { - char line[MAX_STRING]; /* Flawfinder: ignore */ - memset(line, 0, sizeof(line)); - while(fgets(line, sizeof(line), meminfo)) + std::string line; + while (std::getline(meminfo, line)) { - line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/ - s << pfx << line; + s << pfx << line << '\n'; } - fclose(meminfo); } else { -- cgit v1.2.3 From 7f8ec9f142f34057fc10436f94ce420183cf75b8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 29 Jun 2011 20:35:44 -0400 Subject: CHOP-753: Introduce LLSD access to LLMemoryInfo ** BROKEN ** This is known not to compile on Mac yet; checking in to concurrently work on Linux-specific code. --- indra/llcommon/llsys.cpp | 211 +++++++++++++++++++++++++++++++++++++++++++++++ indra/llcommon/llsys.h | 28 +++++++ 2 files changed, 239 insertions(+) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index f0f98f5bf6..40914dca3f 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -43,8 +43,15 @@ #include "llerrorcontrol.h" #include "llevents.h" #include "lltimer.h" +#include "llsdserialize.h" +#include "llsdutil.h" #include #include +#include +#include +#include + +using namespace llsd; #if LL_WINDOWS # define WIN32_LEAN_AND_MEAN @@ -633,6 +640,7 @@ void LLCPUInfo::stream(std::ostream& s) const LLMemoryInfo::LLMemoryInfo() { + refresh(); } #if LL_WINDOWS @@ -873,6 +881,209 @@ void LLMemoryInfo::stream(std::ostream& s) const #endif } +LLSD LLMemoryInfo::getStatsMap() const +{ + LLSD map; + + BOOST_FOREACH(LLSD pair, inArray(mData)) + { + // Have to be clear that we want the key asString() to specify map + // indexing rather than array subscripting. + map[pair[0].asString()] = pair[1]; + } + + return map; +} + +LLSD LLMemoryInfo::getStatsArray() const +{ + return mData; +} + +LLMemoryInfo& LLMemoryInfo::refresh() +{ + // This implementation is derived from stream() code (as of 2011-06-29). + // Hopefully we'll reimplement stream() to use mData before long... + mData = LLSD::emptyArray(); + +#if LL_WINDOWS + MEMORYSTATUSEX state; + state.dwLength = sizeof(state); + GlobalMemoryStatusEx(&state); + + mData.append(LLSDArray("Percent Memory use")(LLSD::Integer(state.dwMemoryLoad))); + mData.append(LLSDArray("Total Physical KB") (LLSD::Integer(state.ullTotalPhys/1024))); + mData.append(LLSDArray("Avail Physical KB") (LLSD::Integer(state.ullAvailPhys/1024))); + mData.append(LLSDArray("Total page KB") (LLSD::Integer(state.ullTotalPageFile/1024))); + mData.append(LLSDArray("Avail page KB") (LLSD::Integer(state.ullAvailPageFile/1024))); + mData.append(LLSDArray("Total Virtual KB") (LLSD::Integer(state.ullTotalVirtual/1024))); + mData.append(LLSDArray("Avail Virtual KB") (LLSD::Integer(state.ullAvailVirtual/1024))); + +#elif LL_DARWIN + uint64_t phys = 0; + + size_t len = sizeof(phys); + + if (sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0) + { + mData.append(LLSDArray("Total Physical KB")(LLSD::Integer(phys/1024))); + } + else + { + LL_WARNS("LLMemoryInfo") << "Unable to collect hw.memsize memory information" << LL_ENDL; + } + + FILE* pout = popen("vm_stat 2>&1", "r"); + if (! pout) + { + LL_WARNS("LLMemoryInfo") << "Unable to collect vm_stat memory information" << LL_ENDL; + } + else + { + // Mach Virtual Memory Statistics: (page size of 4096 bytes) + // Pages free: 462078. + // Pages active: 142010. + // Pages inactive: 220007. + // Pages wired down: 159552. + // "Translation faults": 220825184. + // Pages copy-on-write: 2104153. + // Pages zero filled: 167034876. + // Pages reactivated: 65153. + // Pageins: 2097212. + // Pageouts: 41759. + // Object cache: 841598 hits of 7629869 lookups (11% hit rate) + + boost::regex pagesize_rx("\\(page size of ([0-9]+) bytes\\)"); + boost::regex stat_rx("(.+): +([0-9]+)\\."); + boost::regex pages_rx("Pages "); + boost::cmatch matched; + LLSD::Integer pagesizekb(4096/1024); + + // Here 'pout' is vm_stat's stdout. Search it for relevant data. + char line[100]; + line[sizeof(line)-1] = '\0'; + while (fgets(line, sizeof(line)-1, pout)) + { + size_t linelen(strlen(line)); + // Truncate any trailing newline + if (line[linelen - 1] == '\n') + { + line[--linelen] = '\0'; + } + if (boost::regex_search(&line[0], line+linelen, matched, pagesize_rx)) + { + // "Mach Virtual Memory Statistics: (page size of 4096 bytes)" + std::string pagesize_str(matched[1].first, matched[1].second); + // Reasonable to assume that pagesize will always be a + // multiple of 1Kb? + pagesizekb = boost::lexical_cast(pagesize_str)/1024; + } + else if (boost::regex_match(&line[0], line+linelen, matched, stat_rx)) + { + // e.g. "Pages free: 462078." + // Strip double-quotes off certain statistic names + if (matched[1].first[0] == '"' && matched[1].second[-1] == '"') + { + ++matched[1].first; + --matched[1].second; + } + LLSD::String key(matched[1].first, matched[1].second); + LLSD::String value_str(matched[2].first, matched[2].second); + LLSD::Integer value(boost::lexical_cast(value_str)); + // Store this statistic. + mData.append(LLSDArray(key)(value)); + // Is this in units of pages? If so, convert to Kb. + // boost::regex_match() doc sez: "If you want to match a + // prefix of the character string then use regex_search with + // the flag match_continuous set." + boost::smatch smatched; + if (boost::regex_search(key, smatched, pages_rx, boost::match_continuous)) + { + // Synthesize a new key with kB in place of Pages + LLSD::String kbkey("kB "); + kbkey.append(smatched[0].second, key.end()); + mData.append(LLSDArray(kbkey)(value * pagesizekb)); + } + } + else + { + LL_WARNS("LLMemoryInfo") << "unrecognized vm_stat line: " << line << LL_ENDL; + } + } + fclose(pout); + } + +#elif LL_SOLARIS + U64 phys = 0; + + phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024); + + mData.append(LLSDArray("Total Physical KB")(phys)); + +#elif LL_LINUX + std::ifstream meminfo(MEMINFO_FILE); + if (meminfo.is_open()) + { + // MemTotal: 4108424 kB + // MemFree: 1244064 kB + // Buffers: 85164 kB + // Cached: 1990264 kB + // SwapCached: 0 kB + // Active: 1176648 kB + // Inactive: 1427532 kB + // ... + // VmallocTotal: 122880 kB + // VmallocUsed: 65252 kB + // VmallocChunk: 52356 kB + // HardwareCorrupted: 0 kB + // HugePages_Total: 0 + // HugePages_Free: 0 + // HugePages_Rsvd: 0 + // HugePages_Surp: 0 + // Hugepagesize: 2048 kB + // DirectMap4k: 434168 kB + // DirectMap2M: 477184 kB + + boost::regex stat_rx("(.+): +([0-9]+)( kB)?"); + boost::smatch matched; + + std::string line; + while (std::getline(meminfo, line)) + { + if (boost::regex_match(line, line+linelen, matched, stat_rx)) + { + // e.g. "MemTotal: 4108424 kB" + LLSD::String key(matched[1].first, matched[1].second); + LLSD::String value_str(matched[2].first, matched[2].second); + LLSD::Integer value(boost::lexical_cast(value_str)); + // Store this statistic. + mData.append(LLSDArray(key)(value)); + } + else + { + LL_WARNS("LLMemoryInfo") << "unrecognized " << MEMINFO_FILE << " line: " + << line << LL_ENDL; + } + } + } + else + { + s << "Unable to collect memory information" << std::endl; + } + +#else + s << "Unknown system; unable to collect memory information" << std::endl; + +#endif + + // should become LL_DEBUGS when we're happy + LL_INFOS("LLMemoryInfo") << "Populated mData:\n"; + LLSDSerialize::toPrettyXML(mData, LL_CONT); + LL_ENDL; + + return *this; +} + std::ostream& operator<<(std::ostream& s, const LLOSInfo& info) { info.stream(s); diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 41a4f25000..5b44757e08 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -36,6 +36,7 @@ // llinfos << info << llendl; // +#include "llsd.h" #include #include @@ -117,6 +118,33 @@ public: //get the available memory infomation in KiloBytes. static void getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb); + + // Retrieve a map of memory statistics. The keys of the map are platform- + // dependent. The values are in kilobytes. + LLSD getStatsMap() const; + + // Retrieve memory statistics: an array of pair arrays [name, value]. This + // is the same data as presented in getStatsMap(), but it preserves the + // order in which we retrieved it from the OS in case that's useful. The + // set of statistics names is platform-dependent. The values are in + // kilobytes to try to avoid integer overflow. + LLSD getStatsArray() const; + + // Re-fetch memory data (as reported by stream() and getStats*()) from the + // system. Normally this is fetched at construction time. Return (*this) + // to permit usage of the form: + // @code + // LLMemoryInfo info; + // ... + // info.refresh().getStatsArray(); + // @endcode + LLMemoryInfo& refresh(); + +private: + // Internally, we store memory stats as for getStatsArray(). It's + // straightforward to convert that to getStatsMap() form, less so to + // reconstruct the original order when converting the other way. + LLSD mData; }; -- cgit v1.2.3 From 21ff3d0d1ce10446cbb1cf9bc08d2c0ebe9705fe Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 29 Jun 2011 20:42:30 -0400 Subject: CHOP-753: fix minor compilation errors on Linux --- indra/llcommon/llsys.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 40914dca3f..2897a533d9 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -1050,7 +1050,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() std::string line; while (std::getline(meminfo, line)) { - if (boost::regex_match(line, line+linelen, matched, stat_rx)) + if (boost::regex_match(line, matched, stat_rx)) { // e.g. "MemTotal: 4108424 kB" LLSD::String key(matched[1].first, matched[1].second); @@ -1068,11 +1068,11 @@ LLMemoryInfo& LLMemoryInfo::refresh() } else { - s << "Unable to collect memory information" << std::endl; + LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL; } #else - s << "Unknown system; unable to collect memory information" << std::endl; + LL_WARNS("LLMemoryInfo") << "Unknown system; unable to collect memory information" << LL_ENDL; #endif -- cgit v1.2.3 From b75eedb0dce0c6b11e248c4f244e0020a5b97a42 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 30 Jun 2011 10:12:45 -0400 Subject: CHOP-753: Fix errors in LLMemoryInfo Mac-specific code. Handle conversion errors (boost::bad_lexical_cast). Glean additional LLSD statistics from vm_stat output. --- indra/llcommon/llsys.cpp | 93 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 2897a533d9..e4404f31c7 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -50,6 +50,7 @@ #include #include #include +#include using namespace llsd; @@ -955,7 +956,8 @@ LLMemoryInfo& LLMemoryInfo::refresh() boost::regex pagesize_rx("\\(page size of ([0-9]+) bytes\\)"); boost::regex stat_rx("(.+): +([0-9]+)\\."); - boost::regex pages_rx("Pages "); + boost::regex cache_rx("Object cache: ([0-9]+) hits of ([0-9]+) lookups " + "\\(([0-9]+)% hit rate\\)"); boost::cmatch matched; LLSD::Integer pagesizekb(4096/1024); @@ -970,41 +972,81 @@ LLMemoryInfo& LLMemoryInfo::refresh() { line[--linelen] = '\0'; } - if (boost::regex_search(&line[0], line+linelen, matched, pagesize_rx)) + if (boost::regex_search(line, matched, pagesize_rx)) { // "Mach Virtual Memory Statistics: (page size of 4096 bytes)" std::string pagesize_str(matched[1].first, matched[1].second); - // Reasonable to assume that pagesize will always be a - // multiple of 1Kb? - pagesizekb = boost::lexical_cast(pagesize_str)/1024; + try + { + // Reasonable to assume that pagesize will always be a + // multiple of 1Kb? + pagesizekb = boost::lexical_cast(pagesize_str)/1024; + } + catch (const boost::bad_lexical_cast&) + { + LL_WARNS("LLMemoryInfo") << "couldn't parse '" << pagesize_str + << "' in vm_stat line: " << line << LL_ENDL; + continue; + } + mData.append(LLSDArray("page size")(pagesizekb)); } - else if (boost::regex_match(&line[0], line+linelen, matched, stat_rx)) + else if (boost::regex_match(line, matched, stat_rx)) { // e.g. "Pages free: 462078." // Strip double-quotes off certain statistic names - if (matched[1].first[0] == '"' && matched[1].second[-1] == '"') + const char *key_begin(matched[1].first), *key_end(matched[1].second); + if (key_begin[0] == '"' && key_end[-1] == '"') { - ++matched[1].first; - --matched[1].second; + ++key_begin; + --key_end; } - LLSD::String key(matched[1].first, matched[1].second); + LLSD::String key(key_begin, key_end); LLSD::String value_str(matched[2].first, matched[2].second); - LLSD::Integer value(boost::lexical_cast(value_str)); + LLSD::Integer value(0); + try + { + value = boost::lexical_cast(value_str); + } + catch (const boost::bad_lexical_cast&) + { + LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str + << "' in vm_stat line: " << line << LL_ENDL; + continue; + } // Store this statistic. mData.append(LLSDArray(key)(value)); // Is this in units of pages? If so, convert to Kb. - // boost::regex_match() doc sez: "If you want to match a - // prefix of the character string then use regex_search with - // the flag match_continuous set." - boost::smatch smatched; - if (boost::regex_search(key, smatched, pages_rx, boost::match_continuous)) + static const LLSD::String pages("Pages "); + if (key.substr(0, pages.length()) == pages) { - // Synthesize a new key with kB in place of Pages - LLSD::String kbkey("kB "); - kbkey.append(smatched[0].second, key.end()); + // Synthesize a new key with kb in place of Pages + LLSD::String kbkey("kb "); + kbkey.append(key.substr(pages.length())); mData.append(LLSDArray(kbkey)(value * pagesizekb)); } } + else if (boost::regex_match(line, matched, cache_rx)) + { + // e.g. "Object cache: 841598 hits of 7629869 lookups (11% hit rate)" + static const char* cache_keys[] = { "cache hits", "cache lookups", "cache hit%" }; + std::vector cache_values; + for (size_t i = 0; i < (sizeof(cache_keys)/sizeof(cache_keys[0])); ++i) + { + LLSD::String value_str(matched[i+1].first, matched[i+1].second); + LLSD::Integer value(0); + try + { + value = boost::lexical_cast(value_str); + } + catch (boost::bad_lexical_cast&) + { + LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str + << "' in vm_stat line: " << line << LL_ENDL; + continue; + } + mData.append(LLSDArray(cache_keys[i])(value)); + } + } else { LL_WARNS("LLMemoryInfo") << "unrecognized vm_stat line: " << line << LL_ENDL; @@ -1055,7 +1097,18 @@ LLMemoryInfo& LLMemoryInfo::refresh() // e.g. "MemTotal: 4108424 kB" LLSD::String key(matched[1].first, matched[1].second); LLSD::String value_str(matched[2].first, matched[2].second); - LLSD::Integer value(boost::lexical_cast(value_str)); + LLSD::Integer value(0); + try + { + value = boost::lexical_cast(value_str); + } + catch (const boost::bad_lexical_cast&) + { + LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str + << "' in " << MEMINFO_FILE << " line: " + << line << LL_ENDL; + continue; + } // Store this statistic. mData.append(LLSDArray(key)(value)); } -- cgit v1.2.3 From 01607fe418b19e7439020047c270c0e7c86725e7 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 30 Jun 2011 11:50:54 -0400 Subject: CHOP-753: Reduce redundancy in LLMemoryInfo. Recast stream() to display data from LLSD array rather than reinvoking OS operations used to capture it. Make refresh() cache LLSD data in map form as well as array; fetch items from that in a few places to avoid going back to OS. --- indra/llcommon/llsys.cpp | 159 ++++++++++++++++------------------------------- indra/llcommon/llsys.h | 10 +-- 2 files changed, 58 insertions(+), 111 deletions(-) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index e4404f31c7..8222702c50 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -665,11 +665,7 @@ static U32 LLMemoryAdjustKBResult(U32 inKB) U32 LLMemoryInfo::getPhysicalMemoryKB() const { #if LL_WINDOWS - MEMORYSTATUSEX state; - state.dwLength = sizeof(state); - GlobalMemoryStatusEx(&state); - - return LLMemoryAdjustKBResult((U32)(state.ullTotalPhys >> 10)); + return LLMemoryAdjustKBResult(mStatsMap["Total Physical KB"]); #elif LL_DARWIN // This might work on Linux as well. Someone check... @@ -717,15 +713,11 @@ U32 LLMemoryInfo::getPhysicalMemoryClamped() const void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb) { #if LL_WINDOWS - MEMORYSTATUSEX state; - state.dwLength = sizeof(state); - GlobalMemoryStatusEx(&state); - - avail_physical_mem_kb = (U32)(state.ullAvailPhys/1024) ; - avail_virtual_mem_kb = (U32)(state.ullAvailVirtual/1024) ; + avail_physical_mem_kb = mStatsMap["Avail Physical KB"]; + avail_virtual_mem_kb = mStatsMap["Avail Virtual KB"]; #elif LL_DARWIN - // Run vm_stat and filter output, scaling for page size: + // mStatsMap is derived from vm_stat, look for (e.g.) "kb free": // $ vm_stat // Mach Virtual Memory Statistics: (page size of 4096 bytes) // Pages free: 462078. @@ -743,7 +735,7 @@ void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_v avail_virtual_mem_kb = -1 ; #elif LL_LINUX - // Read selected lines from MEMINFO_FILE: + // mStatsMap is derived from MEMINFO_FILE: // $ cat /proc/meminfo // MemTotal: 4108424 kB // MemFree: 1244064 kB @@ -811,114 +803,58 @@ void LLMemoryInfo::stream(std::ostream& s) const // introducer line, then read subsequent lines, etc... std::string pfx(LLError::utcTime() + " "); -#if LL_WINDOWS - MEMORYSTATUSEX state; - state.dwLength = sizeof(state); - GlobalMemoryStatusEx(&state); - - s << pfx << "Percent Memory use: " << (U32)state.dwMemoryLoad << '%' << std::endl; - s << pfx << "Total Physical KB: " << (U32)(state.ullTotalPhys/1024) << std::endl; - s << pfx << "Avail Physical KB: " << (U32)(state.ullAvailPhys/1024) << std::endl; - s << pfx << "Total page KB: " << (U32)(state.ullTotalPageFile/1024) << std::endl; - s << pfx << "Avail page KB: " << (U32)(state.ullAvailPageFile/1024) << std::endl; - s << pfx << "Total Virtual KB: " << (U32)(state.ullTotalVirtual/1024) << std::endl; - s << pfx << "Avail Virtual KB: " << (U32)(state.ullAvailVirtual/1024) << std::endl; - -#elif LL_DARWIN - uint64_t phys = 0; - - size_t len = sizeof(phys); - - if(sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0) - { - s << pfx << "Total Physical KB: " << phys/1024 << std::endl; - } - else - { - s << "Unable to collect hw.memsize memory information" << std::endl; - } + // Most of the reason we even store mStatsArray is to preserve the + // original order in which we obtained these stats from the OS. So use + // mStatsArray in this method rather than mStatsMap, which should present + // the same information but in arbitrary order. - FILE* pout = popen("vm_stat 2>&1", "r"); - if (! pout) - { - s << "Unable to collect vm_stat memory information" << std::endl; - } - else + // Max key length + size_t key_width(0); + BOOST_FOREACH(LLSD pair, inArray(mStatsArray)) { - // Here 'pout' is vm_stat's stdout. Copy it to output stream. - char line[100]; - while (fgets(line, sizeof(line), pout)) + size_t len(pair[0].asString().length()); + if (len > key_width) { - s << pfx << line; + key_width = len; } - fclose(pout); } -#elif LL_SOLARIS - U64 phys = 0; - - phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024); - - s << pfx << "Total Physical KB: " << phys << std::endl; - -#elif LL_LINUX - std::ifstream meminfo(MEMINFO_FILE); - if (meminfo.is_open()) - { - std::string line; - while (std::getline(meminfo, line)) - { - s << pfx << line << '\n'; - } - } - else + // Now stream stats + BOOST_FOREACH(LLSD pair, inArray(mStatsArray)) { - s << "Unable to collect memory information" << std::endl; + s << pfx << std::setw(key_width+1) << (pair[0].asString() + ':') + << ' ' + << std::setw(12) << pair[1].asInteger() << std::endl; } - -#else - s << "Unknown system; unable to collect memory information" << std::endl; - -#endif } LLSD LLMemoryInfo::getStatsMap() const { - LLSD map; - - BOOST_FOREACH(LLSD pair, inArray(mData)) - { - // Have to be clear that we want the key asString() to specify map - // indexing rather than array subscripting. - map[pair[0].asString()] = pair[1]; - } - - return map; + return mStatsMap; } LLSD LLMemoryInfo::getStatsArray() const { - return mData; + return mStatsArray; } LLMemoryInfo& LLMemoryInfo::refresh() { - // This implementation is derived from stream() code (as of 2011-06-29). - // Hopefully we'll reimplement stream() to use mData before long... - mData = LLSD::emptyArray(); + // This implementation is derived from stream() code (as of 2011-06-29). + mStatsArray = LLSD::emptyArray(); #if LL_WINDOWS MEMORYSTATUSEX state; state.dwLength = sizeof(state); GlobalMemoryStatusEx(&state); - mData.append(LLSDArray("Percent Memory use")(LLSD::Integer(state.dwMemoryLoad))); - mData.append(LLSDArray("Total Physical KB") (LLSD::Integer(state.ullTotalPhys/1024))); - mData.append(LLSDArray("Avail Physical KB") (LLSD::Integer(state.ullAvailPhys/1024))); - mData.append(LLSDArray("Total page KB") (LLSD::Integer(state.ullTotalPageFile/1024))); - mData.append(LLSDArray("Avail page KB") (LLSD::Integer(state.ullAvailPageFile/1024))); - mData.append(LLSDArray("Total Virtual KB") (LLSD::Integer(state.ullTotalVirtual/1024))); - mData.append(LLSDArray("Avail Virtual KB") (LLSD::Integer(state.ullAvailVirtual/1024))); + mStatsArray.append(LLSDArray("Percent Memory use")(LLSD::Integer(state.dwMemoryLoad))); + mStatsArray.append(LLSDArray("Total Physical KB") (LLSD::Integer(state.ullTotalPhys/1024))); + mStatsArray.append(LLSDArray("Avail Physical KB") (LLSD::Integer(state.ullAvailPhys/1024))); + mStatsArray.append(LLSDArray("Total page KB") (LLSD::Integer(state.ullTotalPageFile/1024))); + mStatsArray.append(LLSDArray("Avail page KB") (LLSD::Integer(state.ullAvailPageFile/1024))); + mStatsArray.append(LLSDArray("Total Virtual KB") (LLSD::Integer(state.ullTotalVirtual/1024))); + mStatsArray.append(LLSDArray("Avail Virtual KB") (LLSD::Integer(state.ullAvailVirtual/1024))); #elif LL_DARWIN uint64_t phys = 0; @@ -927,7 +863,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() if (sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0) { - mData.append(LLSDArray("Total Physical KB")(LLSD::Integer(phys/1024))); + mStatsArray.append(LLSDArray("Total Physical KB")(LLSD::Integer(phys/1024))); } else { @@ -972,6 +908,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() { line[--linelen] = '\0'; } + LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL; if (boost::regex_search(line, matched, pagesize_rx)) { // "Mach Virtual Memory Statistics: (page size of 4096 bytes)" @@ -988,7 +925,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() << "' in vm_stat line: " << line << LL_ENDL; continue; } - mData.append(LLSDArray("page size")(pagesizekb)); + mStatsArray.append(LLSDArray("page size")(pagesizekb)); } else if (boost::regex_match(line, matched, stat_rx)) { @@ -1014,7 +951,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() continue; } // Store this statistic. - mData.append(LLSDArray(key)(value)); + mStatsArray.append(LLSDArray(key)(value)); // Is this in units of pages? If so, convert to Kb. static const LLSD::String pages("Pages "); if (key.substr(0, pages.length()) == pages) @@ -1022,7 +959,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() // Synthesize a new key with kb in place of Pages LLSD::String kbkey("kb "); kbkey.append(key.substr(pages.length())); - mData.append(LLSDArray(kbkey)(value * pagesizekb)); + mStatsArray.append(LLSDArray(kbkey)(value * pagesizekb)); } } else if (boost::regex_match(line, matched, cache_rx)) @@ -1044,7 +981,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() << "' in vm_stat line: " << line << LL_ENDL; continue; } - mData.append(LLSDArray(cache_keys[i])(value)); + mStatsArray.append(LLSDArray(cache_keys[i])(value)); } } else @@ -1060,7 +997,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024); - mData.append(LLSDArray("Total Physical KB")(phys)); + mStatsArray.append(LLSDArray("Total Physical KB")(phys)); #elif LL_LINUX std::ifstream meminfo(MEMINFO_FILE); @@ -1092,6 +1029,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() std::string line; while (std::getline(meminfo, line)) { + LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL; if (boost::regex_match(line, matched, stat_rx)) { // e.g. "MemTotal: 4108424 kB" @@ -1110,7 +1048,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() continue; } // Store this statistic. - mData.append(LLSDArray(key)(value)); + mStatsArray.append(LLSDArray(key)(value)); } else { @@ -1129,12 +1067,19 @@ LLMemoryInfo& LLMemoryInfo::refresh() #endif - // should become LL_DEBUGS when we're happy - LL_INFOS("LLMemoryInfo") << "Populated mData:\n"; - LLSDSerialize::toPrettyXML(mData, LL_CONT); - LL_ENDL; + // Recast same data as mStatsMap for easy access + BOOST_FOREACH(LLSD pair, inArray(mStatsArray)) + { + // Specify asString() to disambiguate map indexing from array + // subscripting. + mStatsMap[pair[0].asString()] = pair[1]; + } + + LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n"; + LLSDSerialize::toPrettyXML(mStatsMap, LL_CONT); + LL_ENDL; - return *this; + return *this; } std::ostream& operator<<(std::ostream& s, const LLOSInfo& info) diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 5b44757e08..8565bfa0b9 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -141,10 +141,12 @@ public: LLMemoryInfo& refresh(); private: - // Internally, we store memory stats as for getStatsArray(). It's - // straightforward to convert that to getStatsMap() form, less so to - // reconstruct the original order when converting the other way. - LLSD mData; + // Memory stats for getStatsArray(). It's straightforward to convert that + // to getStatsMap() form, less so to reconstruct the original order when + // converting the other way. + LLSD mStatsArray; + // Memory stats for getStatsMap(). + LLSD mStatsMap; }; -- cgit v1.2.3 From abf50e8c7d3cf0bab46286f4b300c7d3be976775 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 30 Jun 2011 13:57:11 -0400 Subject: CHOP-753: Fix compile errors in LLMemoryInfo Windows-specific code. --- indra/llcommon/llsys.cpp | 72 ++++++++++++++++++++++++++++++------------------ indra/llcommon/llsys.h | 4 +++ 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 8222702c50..d02a807000 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -665,7 +665,7 @@ static U32 LLMemoryAdjustKBResult(U32 inKB) U32 LLMemoryInfo::getPhysicalMemoryKB() const { #if LL_WINDOWS - return LLMemoryAdjustKBResult(mStatsMap["Total Physical KB"]); + return LLMemoryAdjustKBResult(mStatsMap["Total Physical KB"].asInteger()); #elif LL_DARWIN // This might work on Linux as well. Someone check... @@ -712,9 +712,13 @@ U32 LLMemoryInfo::getPhysicalMemoryClamped() const //static void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb) { + // Sigh, this shouldn't be a static method, then we wouldn't have to + // reload this data separately from refresh() + LLSD statsMap(loadStatsMap(loadStatsArray())); + #if LL_WINDOWS - avail_physical_mem_kb = mStatsMap["Avail Physical KB"]; - avail_virtual_mem_kb = mStatsMap["Avail Virtual KB"]; + avail_physical_mem_kb = statsMap["Avail Physical KB"].asInteger(); + avail_virtual_mem_kb = statsMap["Avail Virtual KB"].asInteger(); #elif LL_DARWIN // mStatsMap is derived from vm_stat, look for (e.g.) "kb free": @@ -839,22 +843,35 @@ LLSD LLMemoryInfo::getStatsArray() const } LLMemoryInfo& LLMemoryInfo::refresh() +{ + mStatsArray = loadStatsArray(); + // Recast same data as mStatsMap for easy access + mStatsMap = loadStatsMap(mStatsArray); + + LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n"; + LLSDSerialize::toPrettyXML(mStatsMap, LL_CONT); + LL_ENDL; + + return *this; +} + +LLSD LLMemoryInfo::loadStatsArray() { // This implementation is derived from stream() code (as of 2011-06-29). - mStatsArray = LLSD::emptyArray(); + LLSD statsArray(LLSD::emptyArray()); #if LL_WINDOWS MEMORYSTATUSEX state; state.dwLength = sizeof(state); GlobalMemoryStatusEx(&state); - mStatsArray.append(LLSDArray("Percent Memory use")(LLSD::Integer(state.dwMemoryLoad))); - mStatsArray.append(LLSDArray("Total Physical KB") (LLSD::Integer(state.ullTotalPhys/1024))); - mStatsArray.append(LLSDArray("Avail Physical KB") (LLSD::Integer(state.ullAvailPhys/1024))); - mStatsArray.append(LLSDArray("Total page KB") (LLSD::Integer(state.ullTotalPageFile/1024))); - mStatsArray.append(LLSDArray("Avail page KB") (LLSD::Integer(state.ullAvailPageFile/1024))); - mStatsArray.append(LLSDArray("Total Virtual KB") (LLSD::Integer(state.ullTotalVirtual/1024))); - mStatsArray.append(LLSDArray("Avail Virtual KB") (LLSD::Integer(state.ullAvailVirtual/1024))); + statsArray.append(LLSDArray("Percent Memory use")(LLSD::Integer(state.dwMemoryLoad))); + statsArray.append(LLSDArray("Total Physical KB") (LLSD::Integer(state.ullTotalPhys/1024))); + statsArray.append(LLSDArray("Avail Physical KB") (LLSD::Integer(state.ullAvailPhys/1024))); + statsArray.append(LLSDArray("Total page KB") (LLSD::Integer(state.ullTotalPageFile/1024))); + statsArray.append(LLSDArray("Avail page KB") (LLSD::Integer(state.ullAvailPageFile/1024))); + statsArray.append(LLSDArray("Total Virtual KB") (LLSD::Integer(state.ullTotalVirtual/1024))); + statsArray.append(LLSDArray("Avail Virtual KB") (LLSD::Integer(state.ullAvailVirtual/1024))); #elif LL_DARWIN uint64_t phys = 0; @@ -863,7 +880,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() if (sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0) { - mStatsArray.append(LLSDArray("Total Physical KB")(LLSD::Integer(phys/1024))); + statsArray.append(LLSDArray("Total Physical KB")(LLSD::Integer(phys/1024))); } else { @@ -925,7 +942,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() << "' in vm_stat line: " << line << LL_ENDL; continue; } - mStatsArray.append(LLSDArray("page size")(pagesizekb)); + statsArray.append(LLSDArray("page size")(pagesizekb)); } else if (boost::regex_match(line, matched, stat_rx)) { @@ -951,7 +968,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() continue; } // Store this statistic. - mStatsArray.append(LLSDArray(key)(value)); + statsArray.append(LLSDArray(key)(value)); // Is this in units of pages? If so, convert to Kb. static const LLSD::String pages("Pages "); if (key.substr(0, pages.length()) == pages) @@ -959,7 +976,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() // Synthesize a new key with kb in place of Pages LLSD::String kbkey("kb "); kbkey.append(key.substr(pages.length())); - mStatsArray.append(LLSDArray(kbkey)(value * pagesizekb)); + statsArray.append(LLSDArray(kbkey)(value * pagesizekb)); } } else if (boost::regex_match(line, matched, cache_rx)) @@ -981,7 +998,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() << "' in vm_stat line: " << line << LL_ENDL; continue; } - mStatsArray.append(LLSDArray(cache_keys[i])(value)); + statsArray.append(LLSDArray(cache_keys[i])(value)); } } else @@ -997,7 +1014,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024); - mStatsArray.append(LLSDArray("Total Physical KB")(phys)); + statsArray.append(LLSDArray("Total Physical KB")(phys)); #elif LL_LINUX std::ifstream meminfo(MEMINFO_FILE); @@ -1048,7 +1065,7 @@ LLMemoryInfo& LLMemoryInfo::refresh() continue; } // Store this statistic. - mStatsArray.append(LLSDArray(key)(value)); + statsArray.append(LLSDArray(key)(value)); } else { @@ -1067,19 +1084,20 @@ LLMemoryInfo& LLMemoryInfo::refresh() #endif - // Recast same data as mStatsMap for easy access - BOOST_FOREACH(LLSD pair, inArray(mStatsArray)) + return statsArray; +} + +LLSD LLMemoryInfo::loadStatsMap(const LLSD& statsArray) +{ + LLSD statsMap; + + BOOST_FOREACH(LLSD pair, inArray(statsArray)) { // Specify asString() to disambiguate map indexing from array // subscripting. - mStatsMap[pair[0].asString()] = pair[1]; + statsMap[pair[0].asString()] = pair[1]; } - - LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n"; - LLSDSerialize::toPrettyXML(mStatsMap, LL_CONT); - LL_ENDL; - - return *this; + return statsMap; } std::ostream& operator<<(std::ostream& s, const LLOSInfo& info) diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 8565bfa0b9..7fcb050ed0 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -141,6 +141,10 @@ public: LLMemoryInfo& refresh(); private: + // These methods are used to set mStatsArray and mStatsMap. + static LLSD loadStatsArray(); + static LLSD loadStatsMap(const LLSD&); + // Memory stats for getStatsArray(). It's straightforward to convert that // to getStatsMap() form, less so to reconstruct the original order when // converting the other way. -- cgit v1.2.3 From bfbd7c6df5d00b40a5a36fe7d99527084f19e76d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 5 Jul 2011 20:20:26 -0400 Subject: CHOP-753: On Windows, add GetPerformanceInfo to LLMemoryInfo stats. So far we've only been querying GlobalMemoryStatusEx(), but GetPerformanceInfo() delivers a bunch more memory-related stats that may be pertinent. Try capturing those too. May not yet compile on Windows... --- indra/llcommon/llsys.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index d02a807000..e0ce74234d 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -58,6 +58,7 @@ using namespace llsd; # define WIN32_LEAN_AND_MEAN # include # include +# include #elif LL_DARWIN # include # include @@ -873,6 +874,25 @@ LLSD LLMemoryInfo::loadStatsArray() statsArray.append(LLSDArray("Total Virtual KB") (LLSD::Integer(state.ullTotalVirtual/1024))); statsArray.append(LLSDArray("Avail Virtual KB") (LLSD::Integer(state.ullAvailVirtual/1024))); + PERFORMANCE_INFORMATION perf; + perf.cb = sizeof(perf); + GetPerformanceInfo(&perf, sizeof(perf)); + + SIZE_T pagekb(perf.PageSize/1024); + statsArray.append(LLSDArray("CommitTotal KB") (LLSD::Integer(perf.CommitTotal * pagekb))); + statsArray.append(LLSDArray("CommitLimit KB") (LLSD::Integer(perf.CommitLimit * pagekb))); + statsArray.append(LLSDArray("CommitPeak KB") (LLSD::Integer(perf.CommitPeak * pagekb))); + statsArray.append(LLSDArray("PhysicalTotal KB") (LLSD::Integer(perf.PhysicalTotal * pagekb))); + statsArray.append(LLSDArray("PhysicalAvail KB") (LLSD::Integer(perf.PhysicalAvailable * pagekb))); + statsArray.append(LLSDArray("SystemCache KB") (LLSD::Integer(perf.SystemCache * pagekb))); + statsArray.append(LLSDArray("KernelTotal KB") (LLSD::Integer(perf.KernelTotal * pagekb))); + statsArray.append(LLSDArray("KernelPaged KB") (LLSD::Integer(perf.KernelPaged * pagekb))); + statsArray.append(LLSDArray("KernelNonpaged KB")(LLSD::Integer(perf.KernelNonpaged * pagekb))); + statsArray.append(LLSDArray("PageSize KB") (LLSD::Integer(pagekb))); + statsArray.append(LLSDArray("HandleCount") (LLSD::Integer(perf.HandleCount))); + statsArray.append(LLSDArray("ProcessCount") (LLSD::Integer(perf.ProcessCount))); + statsArray.append(LLSDArray("ThreadCount") (LLSD::Integer(perf.ThreadCount))); + #elif LL_DARWIN uint64_t phys = 0; -- cgit v1.2.3