398 lines
8.0 KiB
C++
398 lines
8.0 KiB
C++
/**
|
|
* @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 <windows.h>
|
|
# include <psapi.h>
|
|
#elif defined(LL_DARWIN)
|
|
# include <sys/types.h>
|
|
# include <mach/task.h>
|
|
# include <mach/mach_init.h>
|
|
#elif LL_LINUX || LL_SOLARIS
|
|
# include <unistd.h>
|
|
#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;
|
|
}
|
|
|
|
void* ll_allocate (size_t size)
|
|
{
|
|
if (size == 0)
|
|
{
|
|
llwarns << "Null allocation" << llendl;
|
|
}
|
|
void *p = malloc(size);
|
|
if (p == NULL)
|
|
{
|
|
LLMemory::freeReserve();
|
|
llerrs << "Out of memory Error" << llendl;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void ll_release (void *p)
|
|
{
|
|
free(p);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
#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 <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#define _STRUCTURED_PROC 1
|
|
#include <sys/procfs.h>
|
|
|
|
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
|
|
//--------------------------------------------------------------------------------------------------
|
|
|