From ecc396219d69a1c0a51b32d9b577451a68fa2acf Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Wed, 29 Feb 2012 17:22:24 +0100 Subject: [PATCH 1/4] I can't shake the feeling that timesliced cache clearing is botched. Better safe, byebye. --- indra/newview/lltexturecache.cpp | 183 ++++++++----------------------- indra/newview/lltexturecache.h | 43 +++----- 2 files changed, 61 insertions(+), 165 deletions(-) diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index b239823af..60ced2e4d 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -2,31 +2,25 @@ * @file lltexturecache.cpp * @brief Object which handles local texture caching * - * $LicenseInfo:firstyear=2000&license=viewergpl$ - * - * Copyright (c) 2000-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * 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. * - * 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. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -251,9 +245,9 @@ bool LLTextureCacheLocalFileWorker::doRead() } } #else - if (!mDataSize || mDataSize > local_size/* - mOffset*/) + if (!mDataSize || mDataSize > local_size - mOffset) { - mDataSize = local_size/* - mOffset*/; + mDataSize = local_size - mOffset; } mReadData = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), mDataSize); @@ -333,6 +327,7 @@ bool LLTextureCacheRemoteWorker::doRead() // First state / stage : find out if the file is local if (mState == INIT) { +#if 0 std::string filename = mCache->getLocalFileName(mID); // Is it a JPEG2000 file? { @@ -367,20 +362,24 @@ bool LLTextureCacheRemoteWorker::doRead() } // Determine the next stage: if we found a file, then LOCAL else CACHE mState = (local_size > 0 ? LOCAL : CACHE); + + llassert_always(mState == CACHE) ; +#else + mState = CACHE; +#endif } // Second state / stage : if the file is local, load it and leave if (!done && (mState == LOCAL)) { llassert(local_size != 0); // we're assuming there is a non empty local file here... - if (!mDataSize || mDataSize > local_size/* - mOffset*/) + if (!mDataSize || mDataSize > local_size - mOffset) { - mDataSize = local_size/* - mOffset*/; + mDataSize = local_size - mOffset; } // Allocate read buffer mReadData = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), mDataSize); - S32 bytes_read = LLAPRFile::readEx(local_filename, - mReadData, mOffset, mDataSize); + S32 bytes_read = LLAPRFile::readEx(local_filename, mReadData, mOffset, mDataSize); if (bytes_read != mDataSize) { llwarns << "Error reading file from local cache: " << local_filename @@ -513,19 +512,9 @@ bool LLTextureCacheRemoteWorker::doRead() } else { - if (mImageSize > TEXTURE_CACHE_ENTRY_SIZE) - { - LL_DEBUGS("TextureCache") << "LLTextureCacheWorker: no body for texture: " << mID << LL_ENDL; - FREE_MEM(LLImageBase::getPrivatePool(), mReadData); - mReadData = NULL; - mDataSize = -1; // failed - done = true; - } - else - { // No body, we're done. mDataSize = llmax(TEXTURE_CACHE_ENTRY_SIZE - mOffset, 0); - } + lldebugs << "No body file for: " << filename << llendl; } // Nothing else to do at that point... done = true; @@ -759,7 +748,6 @@ LLTextureCache::~LLTextureCache() { clearDeleteList(); writeUpdatedEntries(); - purgeTextureFilesTimeSliced(true); // VWR-3878 - NB - force-flush all pending file deletes } ////////////////////////////////////////////////////////////////////////////// @@ -1427,21 +1415,22 @@ void LLTextureCache::readHeaderCache() } } } - if (num_entries - empty_entries > sCacheMaxEntries) + if (num_entries > sCacheMaxEntries) { // Special case: cache size was reduced, need to remove entries // Note: After we prune entries, we will call this again and create the LRU U32 entries_to_purge = (num_entries - empty_entries) - sCacheMaxEntries; llinfos << "Texture Cache Entries: " << num_entries << " Max: " << sCacheMaxEntries << " Empty: " << empty_entries << " Purging: " << entries_to_purge << llendl; - // We can exit the following loop with the given condition, since if we'd reach the end of the lru set we'd have: - // purge_list.size() = lru.size() = num_entries - empty_entries = entries_to_purge + sCacheMaxEntries >= entries_to_purge - // So, it's certain that iter will never reach lru.end() first. - std::set::iterator iter = lru.begin(); - while (purge_list.size() < entries_to_purge) + if (entries_to_purge > 0) + { + for (std::set::iterator iter = lru.begin(); iter != lru.end(); ++iter) { purge_list.insert(iter->second); - ++iter; + if (purge_list.size() >= entries_to_purge) + break; } + } + llassert_always(purge_list.size() >= entries_to_purge); } else { @@ -1555,18 +1544,11 @@ void LLTextureCache::purgeAllTextures(bool purge_directories) void LLTextureCache::purgeTextures(bool validate) { - mDoPurge = FALSE; - if (mReadOnly) { return; } - if (!validate && mTexturesSizeTotal <= sCacheMaxTexturesSize) - { - return; - } - if (!mThreaded) { // *FIX:Mani - watchdog off. @@ -1582,15 +1564,12 @@ void LLTextureCache::purgeTextures(bool validate) U32 num_entries = openAndReadEntries(entries); if (!num_entries) { - LLAppViewer::instance()->resumeMainloopTimeout(); return; // nothing to purge } - LL_INFOS("TextureCache") << "TEXTURE CACHE: Purging." << LL_ENDL; - // Use mTexturesSizeMap to collect UUIDs of textures with bodies typedef std::set > time_idx_set_t; - time_idx_set_t time_idx_set; + std::set > time_idx_set; for (size_map_t::iterator iter1 = mTexturesSizeMap.begin(); iter1 != mTexturesSizeMap.end(); ++iter1) { @@ -1606,9 +1585,6 @@ void LLTextureCache::purgeTextures(bool validate) else { llerrs << "mTexturesSizeMap / mHeaderIDMap corrupted." << llendl ; - //clearCorruptedCache(); - //LLAppViewer::instance()->resumeMainloopTimeout(); - //return; } } } @@ -1661,8 +1637,7 @@ void LLTextureCache::purgeTextures(bool validate) { purge_count++; LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL; - mFilesToDelete.insert(std::make_pair(entries[idx].mID, filename)); - removeEntry(idx, entries[idx], filename, false); // remove the entry but not the file + removeEntry(idx, entries[idx], filename) ; cache_size -= entries[idx].mBodySize; } } @@ -1674,80 +1649,11 @@ void LLTextureCache::purgeTextures(bool validate) // *FIX:Mani - watchdog back on. LLAppViewer::instance()->resumeMainloopTimeout(); - mSlicedPurgeTimer.reset(); - LL_INFOS("TextureCache") << "TEXTURE CACHE:" << " PURGED: " << purge_count << " ENTRIES: " << num_entries - << " CACHE SIZE: " << mTexturesSizeTotal / (1024 * 1024) << " MB" + << " CACHE SIZE: " << mTexturesSizeTotal / 1024*1024 << " MB" << llendl; - -} - -void LLTextureCache::purgeTextureFilesTimeSliced(bool force) -{ - const F32 delay_between_passes = 2.0f; // seconds - const F32 max_time_per_pass = 0.1f; // seconds - - if (!force && mSlicedPurgeTimer.getElapsedTimeF32() <= delay_between_passes) - { - return; - } - - if (!mFilesToDelete.empty()) - { - LL_INFOS("TextureCache") << "time sliced purging with " << mFilesToDelete.size() - << " files scheduled for deletion" << LL_ENDL; - - LLMutexLock lock(&mHeaderMutex); - mSlicedPurgeTimer.reset(); - U32 purged = 0; - std::string filename; - - for (LLTextureCache::purge_map_t::iterator iter = mFilesToDelete.begin(); iter != mFilesToDelete.end(); ) - { - LLTextureCache::purge_map_t::iterator curiter = iter++; - // Only remove files for textures that have not been cached again - // since we selected them for removal ! - if (mHeaderIDMap.find(curiter->first) == mHeaderIDMap.end()) - { - filename = curiter->second; - if(LLAPRFile::isExist(filename)) - LLAPRFile::remove(filename); - } - else - { - LL_DEBUGS("TextureCache") << curiter->second - << " selected for removal, but texture cached again since !" - << LL_ENDL; - } - mFilesToDelete.erase(curiter); - purged++; - - if (!force && mSlicedPurgeTimer.getElapsedTimeF32() > max_time_per_pass) - { - break; - } - } - - if (mFilesToDelete.empty()) - { - LL_INFOS("TextureCache") << "time sliced purge finished with " << purged - << " files deleted in " - << mSlicedPurgeTimer.getElapsedTimeF32() - << "s" << LL_ENDL; - } - else - { - LL_INFOS("TextureCache") << "time sliced purge: " << purged - << " files deleted in " - << mSlicedPurgeTimer.getElapsedTimeF32() - << "s (" << mFilesToDelete.size() - << " files left for next pass)" << LL_ENDL; - } - - mSlicedPurgeTimer.reset(); - } } ////////////////////////////////////////////////////////////////////////////// @@ -1888,11 +1794,12 @@ LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 prio } if (mDoPurge) { + // NOTE: This may cause an occasional hiccup, + // but it really needs to be done on the control thread + // (i.e. here) purgeTextures(false); + mDoPurge = FALSE; } - - purgeTextureFilesTimeSliced(); // VWR-3878 - NB - purge textures from cache in a non-hiccup-way - LLMutexLock lock(&mWorkersMutex); LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, data, datasize, 0, @@ -1951,7 +1858,7 @@ void LLTextureCache::removeCachedTexture(const LLUUID& id) } //called after mHeaderMutex is locked. -void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename, bool remove_file) +void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename) { bool file_maybe_exists = true; // Always attempt to remove when idx is invalid. @@ -1969,16 +1876,16 @@ void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename, b } } - mTexturesSizeTotal -= entry.mBodySize; entry.mImageSize = -1; entry.mBodySize = 0; mHeaderIDMap.erase(entry.mID); mTexturesSizeMap.erase(entry.mID); + mTexturesSizeTotal -= entry.mBodySize; mFreeList.insert(idx); } - if (remove_file && file_maybe_exists) + if (file_maybe_exists) { LLAPRFile::remove(filename); } diff --git a/indra/newview/lltexturecache.h b/indra/newview/lltexturecache.h index 54f52df6a..991d8598b 100644 --- a/indra/newview/lltexturecache.h +++ b/indra/newview/lltexturecache.h @@ -2,31 +2,25 @@ * @file lltexturecache.h * @brief Object for managing texture cachees. * - * $LicenseInfo:firstyear=2000&license=viewergpl$ - * - * Copyright (c) 2000-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * 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. * - * 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. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -154,7 +148,6 @@ private: void clearCorruptedCache(); void purgeAllTextures(bool purge_directories); void purgeTextures(bool validate); - void purgeTextureFilesTimeSliced(bool force = false); LLAPRFile* openHeaderEntriesFile(bool readonly, S32 offset); void closeHeaderEntriesFile(); void readEntriesHeader(); @@ -166,7 +159,7 @@ private: void writeEntriesAndClose(const std::vector& entries); void readEntryFromHeaderImmediately(S32& idx, Entry& entry) ; void writeEntryToHeaderImmediately(S32& idx, Entry& entry, bool write_header = false) ; - void removeEntry(S32 idx, Entry& entry, std::string& filename, bool remove_file = true); + void removeEntry(S32 idx, Entry& entry, std::string& filename); void removeCachedTexture(const LLUUID& id) ; S32 getHeaderCacheEntry(const LLUUID& id, Entry& entry); S32 setHeaderCacheEntry(const LLUUID& id, Entry& entry, S32 imagesize, S32 datasize); @@ -192,10 +185,6 @@ private: typedef std::vector, bool> > responder_list_t; responder_list_t mCompletedList; - typedef std::map purge_map_t; - purge_map_t mFilesToDelete; - LLTimer mSlicedPurgeTimer; - BOOL mReadOnly; // HEADERS (Include first mip) From ab26e1008fafa4ac2e12f9d4cf526717d2c0f6aa Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Wed, 29 Feb 2012 19:05:19 +0100 Subject: [PATCH 2/4] RLVa crash fix, thx Kitty! --- indra/newview/rlvcommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp index 5943f4af8..1fe03bb54 100644 --- a/indra/newview/rlvcommon.cpp +++ b/indra/newview/rlvcommon.cpp @@ -467,7 +467,7 @@ bool RlvUtil::sendChatReply(S32 nChannel, const std::string& strUTF8Text) gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); gMessageSystem->nextBlockFast(_PREHASH_ChatData); - gMessageSystem->addStringFast(_PREHASH_Message, strUTF8Text); + gMessageSystem->addStringFast(_PREHASH_Message, utf8str_truncate(strUTF8Text, MAX_MSG_STR_LEN)); gMessageSystem->addU8Fast(_PREHASH_Type, CHAT_TYPE_SHOUT); gMessageSystem->addS32("Channel", nChannel); gAgent.sendReliableMessage(); From 251b0fa5c07487d416400dfea72816d14fb6641a Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Thu, 1 Mar 2012 13:49:47 +0100 Subject: [PATCH 3/4] Memory consumption in statistics floater --- indra/newview/llfloaterstats.cpp | 6 +-- indra/newview/llviewerdisplay.cpp | 2 + indra/newview/llviewerstats.cpp | 12 ++++++ indra/newview/sgmemstat.cpp | 68 +++++++++++++++++++++++++++++-- indra/newview/sgmemstat.h | 2 + 5 files changed, 84 insertions(+), 6 deletions(-) diff --git a/indra/newview/llfloaterstats.cpp b/indra/newview/llfloaterstats.cpp index 84c637f2c..fa71cfb13 100644 --- a/indra/newview/llfloaterstats.cpp +++ b/indra/newview/llfloaterstats.cpp @@ -105,9 +105,9 @@ void LLFloaterStats::buildStats() stat_barp = stat_viewp->addStat("Allocated memory", &(LLViewerStats::getInstance()->mMallocStat), "DebugStatModeMalloc"); stat_barp->setUnitLabel(" MB"); stat_barp->mMinBar = 0.f; - stat_barp->mMaxBar = 4000.f; - stat_barp->mTickSpacing = 100.f; - stat_barp->mLabelSpacing = 200.f; + stat_barp->mMaxBar = 2048.f; + stat_barp->mTickSpacing = 128.f; + stat_barp->mLabelSpacing = 512.f; stat_barp->mPerSec = FALSE; stat_barp->mDisplayMean = FALSE; } diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 949235013..bf7d50e9f 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -83,6 +83,7 @@ #include "llwlparammanager.h" #include "llwaterparammanager.h" #include "llpostprocess.h" +#include "sgmemstat.h" // [RLVa:KB] #include "rlvhandler.h" @@ -229,6 +230,7 @@ void display_stats() U32 memory = (U32)(gMemoryAllocated / (1024*1024)); llinfos << llformat("MEMORY: %d MB", memory) << llendl; llinfos << "THREADS: "<< LLThread::getCount() << llendl; + llinfos << "MALLOC: " << SGMemStat::getPrintableStat() <= mem_stats_freq) + { + LLViewerStats::getInstance()->mMallocStat.addValue(SGMemStat::getMalloc()/1024.f/1024.f); + mem_stats_timer.reset(); + } + } + + #if LL_LCD_COMPILE bool LCDenabled = gLcdScreen->Enabled(); LLViewerStats::getInstance()->setStat(LLViewerStats::ST_LOGITECH_LCD, LCDenabled); diff --git a/indra/newview/sgmemstat.cpp b/indra/newview/sgmemstat.cpp index 27f3cc7cb..0fbe006f6 100644 --- a/indra/newview/sgmemstat.cpp +++ b/indra/newview/sgmemstat.cpp @@ -18,14 +18,76 @@ #include "llviewerprecompiledheaders.h" #include "sgmemstat.h" +#if 0 bool SGMemStat::haveStat() { - return false; + return false; } F32 SGMemStat::getMalloc() { - return 0.f; + return 0.f; } U32 SGMemStat::getNumObjects() { - return 0; + return 0; } + +std::string SGMemStat::getPrintableStat() { + return std::string(); +} + +#else + +extern "C" { + typedef void (*MallocExtension_GetStats_t)(char* buffer, int buffer_length); + typedef int (*MallocExtension_GetNumericProperty_t)(const char* property, size_t* value); + //typedef int (*MallocExtension_SetNumericProperty_t)(const char* property, size_t value); +} + +static MallocExtension_GetNumericProperty_t MallocExtension_GetNumericProperty = 0; +static MallocExtension_GetStats_t MallocExtension_GetStats = 0; + +static void initialize() { + static bool initialized = false; + if (!initialized) { + apr_dso_handle_t* hprog;// = (apr_dso_handle_t*)dlopen(0,0); + LLAPRPool pool; + pool.create(); + apr_dso_load(&hprog, 0, pool()); + apr_dso_sym((apr_dso_handle_sym_t*)&MallocExtension_GetNumericProperty, + hprog, "MallocExtension_GetNumericProperty"); + apr_dso_sym((apr_dso_handle_sym_t*)&MallocExtension_GetStats, + hprog, "MallocExtension_GetStats"); + initialized = true; + } +} + +bool SGMemStat::haveStat() { + initialize(); + return MallocExtension_GetNumericProperty; +} + +F32 SGMemStat::getMalloc() { + if(MallocExtension_GetNumericProperty) { + size_t value; + MallocExtension_GetNumericProperty("generic.current_allocated_bytes", &value); + return value; + } + else return 0.0f; +} + +U32 SGMemStat::getNumObjects() { + return 0; +} + +std::string SGMemStat::getPrintableStat() { + initialize(); + if (MallocExtension_GetStats) { + char buffer[4096]; + buffer[4095] = 0; + MallocExtension_GetStats(buffer, 4095); + return std::string(buffer); + } + else return std::string(); +} + +#endif diff --git a/indra/newview/sgmemstat.h b/indra/newview/sgmemstat.h index 8a89e3b0d..1768f66ef 100644 --- a/indra/newview/sgmemstat.h +++ b/indra/newview/sgmemstat.h @@ -26,6 +26,8 @@ F32 getMalloc(); U32 getNumObjects(); +std::string getPrintableStat(); + } #endif From 5f7f7b7d61f428d4071a81c6cdf36f45a530804c Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Thu, 1 Mar 2012 23:38:48 +0100 Subject: [PATCH 4/4] Unsegfault memory tracker on Windows --- indra/newview/sgmemstat.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/newview/sgmemstat.cpp b/indra/newview/sgmemstat.cpp index 0fbe006f6..d4e22a882 100644 --- a/indra/newview/sgmemstat.cpp +++ b/indra/newview/sgmemstat.cpp @@ -18,7 +18,7 @@ #include "llviewerprecompiledheaders.h" #include "sgmemstat.h" -#if 0 +#if (!LL_WINDOWS && !LL_LINUX) bool SGMemStat::haveStat() { return false; } @@ -49,10 +49,14 @@ static MallocExtension_GetStats_t MallocExtension_GetStats = 0; static void initialize() { static bool initialized = false; if (!initialized) { - apr_dso_handle_t* hprog;// = (apr_dso_handle_t*)dlopen(0,0); + apr_dso_handle_t* hprog = 0; LLAPRPool pool; pool.create(); +#if LL_WINDOWS + apr_dso_load(&hprog, "libtcmalloc_minimal.dll", pool()); +#else apr_dso_load(&hprog, 0, pool()); +#endif apr_dso_sym((apr_dso_handle_sym_t*)&MallocExtension_GetNumericProperty, hprog, "MallocExtension_GetNumericProperty"); apr_dso_sym((apr_dso_handle_sym_t*)&MallocExtension_GetStats,