/** * @file llmemory.cpp * @brief Very special memory allocation/deallocation stuff here * * $LicenseInfo:firstyear=2002&license=viewergpl$ * * Copyright (c) 2002-2009, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "linden_common.h" #if defined(LL_WINDOWS) # include # include #elif defined(LL_DARWIN) # include # include # include #elif defined(LL_LINUX) # include #endif #include "llmemory.h" #include "llmemtype.h" //---------------------------------------------------------------------------- //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; } //---------------------------------------------------------------------------- //static #if MEM_TRACK_TYPE S32 LLMemType::sCurDepth = 0; S32 LLMemType::sCurType = LLMemType::MTYPE_INIT; S32 LLMemType::sType[LLMemType::MTYPE_MAX_DEPTH]; S32 LLMemType::sMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 }; S32 LLMemType::sMaxMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 }; S32 LLMemType::sNewCount[LLMemType::MTYPE_NUM_TYPES] = { 0 }; S32 LLMemType::sOverheadMem = 0; const char* LLMemType::sTypeDesc[LLMemType::MTYPE_NUM_TYPES] = { "INIT", "STARTUP", "MAIN", "IMAGEBASE", "IMAGERAW", "IMAGEFORMATTED", "APPFMTIMAGE", "APPRAWIMAGE", "APPAUXRAWIMAGE", "DRAWABLE", "OBJECT", "PIPELINE", "AVATAR", "PARTICLES", "REGIONS", "INVENTORY", "ANIMATION", "NETWORK", "PHYSICS", "INTERESTLIST", "SCRIPT", "SCRIPT_RUN", "SCRIPT_BYTECODE", "IO_PUMP", "IO_TCP", "IO_BUFFER", "IO_HTTP_SERVER" "IO_SD_SERVER", "IO_SD_CLIENT", "IO_URL_REQUEST", "TEMP1", "TEMP2", "TEMP3", "TEMP4", "TEMP5", "TEMP6", "TEMP7", "TEMP8", "TEMP9" }; #endif S32 LLMemType::sTotalMem = 0; S32 LLMemType::sMaxTotalMem = 0; //static void LLMemType::printMem() { S32 misc_mem = sTotalMem; #if MEM_TRACK_TYPE for (S32 i=0; i>20,sMaxMemCount[i]>>20, sNewCount[i]) << llendl; } misc_mem -= sMemCount[i]; } #endif llinfos << llformat("MEM: % 20s %03d MB","MISC",misc_mem>>20) << llendl; llinfos << llformat("MEM: % 20s %03d MB (Max=%d MB)","TOTAL",sTotalMem>>20,sMaxTotalMem>>20) << llendl; } #if MEM_TRACK_MEM void* ll_allocate (size_t size) { if (size == 0) { llwarns << "Null allocation" << llendl; } size = (size+3)&~3; S32 alloc_size = size + 4; #if MEM_TRACK_TYPE alloc_size += 4; #endif char* p = (char*)malloc(alloc_size); if (p == NULL) { LLMemory::freeReserve(); llerrs << "Out of memory Error" << llendl; } LLMemType::sTotalMem += size; LLMemType::sMaxTotalMem = llmax(LLMemType::sTotalMem, LLMemType::sMaxTotalMem); LLMemType::sOverheadMem += 4; *(size_t*)p = size; p += 4; #if MEM_TRACK_TYPE if (LLMemType::sCurType < 0 || LLMemType::sCurType >= LLMemType::MTYPE_NUM_TYPES) { llerrs << "Memory Type Error: new" << llendl; } LLMemType::sOverheadMem += 4; *(S32*)p = LLMemType::sCurType; p += 4; LLMemType::sMemCount[LLMemType::sCurType] += size; if (LLMemType::sMemCount[LLMemType::sCurType] > LLMemType::sMaxMemCount[LLMemType::sCurType]) { LLMemType::sMaxMemCount[LLMemType::sCurType] = LLMemType::sMemCount[LLMemType::sCurType]; } LLMemType::sNewCount[LLMemType::sCurType]++; #endif return (void*)p; } void ll_release (void *pin) { if (!pin) { return; } char* p = (char*)pin; #if MEM_TRACK_TYPE p -= 4; S32 type = *(S32*)p; if (type < 0 || type >= LLMemType::MTYPE_NUM_TYPES) { llerrs << "Memory Type Error: delete" << llendl; } #endif p -= 4; S32 size = *(size_t*)p; LLMemType::sOverheadMem -= 4; #if MEM_TRACK_TYPE LLMemType::sMemCount[type] -= size; LLMemType::sOverheadMem -= 4; LLMemType::sNewCount[type]--; #endif LLMemType::sTotalMem -= size; free(p); } #else 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); } #endif #if MEM_TRACK_MEM void* operator new (size_t size) { return ll_allocate(size); } void* operator new[] (size_t size) { return ll_allocate(size); } void operator delete (void *p) { ll_release(p); } void operator delete[] (void *p) { ll_release(p); } #endif //---------------------------------------------------------------------------- LLRefCount::LLRefCount() : mRef(0) { } LLRefCount::~LLRefCount() { if (mRef != 0) { llerrs << "deleting non-zero reference" << llendl; } } //---------------------------------------------------------------------------- #if defined(LL_WINDOWS) U64 getCurrentRSS() { HANDLE self = GetCurrentProcess(); PROCESS_MEMORY_COUNTERS counters; if (!GetProcessMemoryInfo(self, &counters, sizeof(counters))) { llwarns << "GetProcessMemoryInfo failed" << llendl; return 0; } return counters.WorkingSetSize; } #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 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; } #elif defined(LL_LINUX) U64 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; } #else U64 getCurrentRSS() { return 0; } #endif