/** * @file llmemory.cpp * @brief Very special memory allocation/deallocation stuff here * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" #include "llmemory.h" #if MEM_TRACK_MEM #include "llthread.h" #endif #if defined(LL_WINDOWS) # include # include #elif defined(LL_DARWIN) # include # include # include #elif LL_LINUX || LL_SOLARIS # include #endif //---------------------------------------------------------------------------- //static char* LLMemory::reserveMem = 0; //static void LLMemory::initClass() { if (!reserveMem) { reserveMem = new char[16*1024]; // reserve 16K for out of memory error handling } } //static void LLMemory::cleanupClass() { delete [] reserveMem; reserveMem = NULL; } //static void LLMemory::freeReserve() { delete [] reserveMem; reserveMem = NULL; } //---------------------------------------------------------------------------- #if defined(LL_WINDOWS) U64 LLMemory::getCurrentRSS() { HANDLE self = GetCurrentProcess(); PROCESS_MEMORY_COUNTERS counters; if (!GetProcessMemoryInfo(self, &counters, sizeof(counters))) { llwarns << "GetProcessMemoryInfo failed" << llendl; return 0; } return counters.WorkingSetSize; } //static U32 LLMemory::getWorkingSetSize() { PROCESS_MEMORY_COUNTERS pmc ; U32 ret = 0 ; if (GetProcessMemoryInfo( GetCurrentProcess(), &pmc, sizeof(pmc)) ) { ret = pmc.WorkingSetSize ; } return ret ; } #elif defined(LL_DARWIN) /* The API used here is not capable of dealing with 64-bit memory sizes, but is available before 10.4. Once we start requiring 10.4, we can use the updated API, which looks like this: task_basic_info_64_data_t basicInfo; mach_msg_type_number_t basicInfoCount = TASK_BASIC_INFO_64_COUNT; if (task_info(mach_task_self(), TASK_BASIC_INFO_64, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS) Of course, this doesn't gain us anything unless we start building the viewer as a 64-bit executable, since that's the only way for our memory allocation to exceed 2^32. */ // if (sysctl(ctl, 2, &page_size, &size, NULL, 0) == -1) // { // llwarns << "Couldn't get page size" << llendl; // return 0; // } else { // return page_size; // } // } U64 LLMemory::getCurrentRSS() { U64 residentSize = 0; task_basic_info_data_t basicInfo; mach_msg_type_number_t basicInfoCount = TASK_BASIC_INFO_COUNT; if (task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS) { residentSize = basicInfo.resident_size; // If we ever wanted it, the process virtual size is also available as: // virtualSize = basicInfo.virtual_size; // llinfos << "resident size is " << residentSize << llendl; } else { llwarns << "task_info failed" << llendl; } return residentSize; } U32 LLMemory::getWorkingSetSize() { return 0 ; } #elif defined(LL_LINUX) U64 LLMemory::getCurrentRSS() { static const char statPath[] = "/proc/self/stat"; LLFILE *fp = LLFile::fopen(statPath, "r"); U64 rss = 0; if (fp == NULL) { llwarns << "couldn't open " << statPath << llendl; goto bail; } // Eee-yew! See Documentation/filesystems/proc.txt in your // nearest friendly kernel tree for details. { int ret = fscanf(fp, "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*d %*d " "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %Lu", &rss); if (ret != 1) { llwarns << "couldn't parse contents of " << statPath << llendl; rss = 0; } } fclose(fp); bail: return rss; } U32 LLMemory::getWorkingSetSize() { return 0 ; } #elif LL_SOLARIS #include #include #include #define _STRUCTURED_PROC 1 #include U64 LLMemory::getCurrentRSS() { char path [LL_MAX_PATH]; /* Flawfinder: ignore */ sprintf(path, "/proc/%d/psinfo", (int)getpid()); int proc_fd = -1; if((proc_fd = open(path, O_RDONLY)) == -1){ llwarns << "LLmemory::getCurrentRSS() unable to open " << path << ". Returning 0 RSS!" << llendl; return 0; } psinfo_t proc_psinfo; if(read(proc_fd, &proc_psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)){ llwarns << "LLmemory::getCurrentRSS() Unable to read from " << path << ". Returning 0 RSS!" << llendl; close(proc_fd); return 0; } close(proc_fd); return((U64)proc_psinfo.pr_rssize * 1024); } U32 LLMemory::getWorkingSetSize() { return 0 ; } #else U64 LLMemory::getCurrentRSS() { return 0; } U32 LLMemory::getWorkingSetSize() { return 0 ; } #endif //-------------------------------------------------------------------------------------------------- #if MEM_TRACK_MEM #include "llframetimer.h" //static LLMemTracker* LLMemTracker::sInstance = NULL ; LLMemTracker::LLMemTracker() { mLastAllocatedMem = LLMemory::getWorkingSetSize() ; mCapacity = 128 ; mCurIndex = 0 ; mCounter = 0 ; mDrawnIndex = 0 ; mPaused = FALSE ; mMutexp = new LLMutex(NULL) ; mStringBuffer = new char*[128] ; mStringBuffer[0] = new char[mCapacity * 128] ; for(S32 i = 1 ; i < mCapacity ; i++) { mStringBuffer[i] = mStringBuffer[i-1] + 128 ; } } LLMemTracker::~LLMemTracker() { delete[] mStringBuffer[0] ; delete[] mStringBuffer; delete mMutexp ; } //static LLMemTracker* LLMemTracker::getInstance() { if(!sInstance) { sInstance = new LLMemTracker() ; } return sInstance ; } //static void LLMemTracker::release() { if(sInstance) { delete sInstance ; sInstance = NULL ; } } //static void LLMemTracker::track(const char* function, const int line) { static const S32 MIN_ALLOCATION = 0 ; //1KB if(mPaused) { return ; } U32 allocated_mem = LLMemory::getWorkingSetSize() ; LLMutexLock lock(mMutexp) ; S32 delta_mem = allocated_mem - mLastAllocatedMem ; mLastAllocatedMem = allocated_mem ; if(delta_mem <= 0) { return ; //occupied memory does not grow } if(delta_mem < MIN_ALLOCATION) { return ; } char* buffer = mStringBuffer[mCurIndex++] ; F32 time = (F32)LLFrameTimer::getElapsedSeconds() ; S32 hours = (S32)(time / (60*60)); S32 mins = (S32)((time - hours*(60*60)) / 60); S32 secs = (S32)((time - hours*(60*60) - mins*60)); strcpy(buffer, function) ; sprintf(buffer + strlen(function), " line: %d DeltaMem: %d (bytes) Time: %d:%02d:%02d", line, delta_mem, hours,mins,secs) ; if(mCounter < mCapacity) { mCounter++ ; } if(mCurIndex >= mCapacity) { mCurIndex = 0 ; } } //static void LLMemTracker::preDraw(BOOL pause) { mMutexp->lock() ; mPaused = pause ; mDrawnIndex = mCurIndex - 1; mNumOfDrawn = 0 ; } //static void LLMemTracker::postDraw() { mMutexp->unlock() ; } //static const char* LLMemTracker::getNextLine() { if(mNumOfDrawn >= mCounter) { return NULL ; } mNumOfDrawn++; if(mDrawnIndex < 0) { mDrawnIndex = mCapacity - 1 ; } return mStringBuffer[mDrawnIndex--] ; } #endif //MEM_TRACK_MEM //--------------------------------------------------------------------------------------------------