From b50df60aa180e904aa0733bb60cef91cf9df6ff6 Mon Sep 17 00:00:00 2001 From: callum_linden Date: Thu, 21 Apr 2016 16:13:39 -0700 Subject: DRTVWR-418 remove vestiges of TCMALLOC and GooglePerfTools from the viewer --- indra/llcommon/CMakeLists.txt | 5 ----- indra/llcommon/llallocator.cpp | 43 ------------------------------------------ indra/llcommon/llmemory.h | 8 -------- 3 files changed, 56 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 907dbab8f8..1e75ede4e1 100755 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -10,7 +10,6 @@ include(Boost) include(LLSharedLibs) include(JsonCpp) include(GoogleBreakpad) -include(GooglePerfTools) include(Copy3rdPartyLibs) include(ZLIB) include(URIPARSER) @@ -328,8 +327,4 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}") - # *TODO - reenable these once tcmalloc libs no longer break the build. - #ADD_BUILD_TEST(llallocator llcommon) - #ADD_BUILD_TEST(llallocator_heap_profile llcommon) - #ADD_BUILD_TEST(llmemtype llcommon) endif (LL_TESTS) diff --git a/indra/llcommon/llallocator.cpp b/indra/llcommon/llallocator.cpp index 34fc28d8cc..ac97fb71dd 100755 --- a/indra/llcommon/llallocator.cpp +++ b/indra/llcommon/llallocator.cpp @@ -27,47 +27,6 @@ #include "linden_common.h" #include "llallocator.h" -#if (LL_USE_TCMALLOC && LL_USE_HEAP_PROFILER) - -#include "google/heap-profiler.h" -#include "google/commandlineflags_public.h" - -DECLARE_bool(heap_profile_use_stack_trace); -//DECLARE_double(tcmalloc_release_rate); - -void LLAllocator::setProfilingEnabled(bool should_enable) -{ - // NULL disables dumping to disk - static char const * const PREFIX = NULL; - if(should_enable) - { - HeapProfilerSetUseStackTrace(false); - HeapProfilerStart(PREFIX); - } - else - { - HeapProfilerStop(); - } -} - -// static -bool LLAllocator::isProfiling() -{ - return IsHeapProfilerRunning(); -} - -std::string LLAllocator::getRawProfile() -{ - // *TODO - fix google-perftools to accept an buffer to avoid this - // malloc-copy-free cycle. - char * buffer = GetHeapProfile(); - std::string ret = buffer; - free(buffer); - return ret; -} - -#else // LL_USE_TCMALLOC - // // stub implementations for when tcmalloc is disabled // @@ -87,8 +46,6 @@ std::string LLAllocator::getRawProfile() return std::string(); } -#endif // LL_USE_TCMALLOC - LLAllocatorHeapProfile const & LLAllocator::getProfile() { mProf.mLines.clear(); diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 0fb257aab1..99acc76dac 100755 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -134,7 +134,6 @@ template T* LL_NEXT_ALIGNED_ADDRESS_64(T* address) //------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------ -#if !LL_USE_TCMALLOC inline void* ll_aligned_malloc_16(size_t size) // returned hunk MUST be freed with ll_aligned_free_16(). { #if defined(LL_WINDOWS) @@ -183,13 +182,6 @@ inline void* ll_aligned_realloc_16(void* ptr, size_t size, size_t old_size) // r #endif } -#else // USE_TCMALLOC -// ll_aligned_foo_16 are not needed with tcmalloc -#define ll_aligned_malloc_16 malloc -#define ll_aligned_realloc_16(a,b,c) realloc(a,b) -#define ll_aligned_free_16 free -#endif // USE_TCMALLOC - inline void* ll_aligned_malloc_32(size_t size) // returned hunk MUST be freed with ll_aligned_free_32(). { #if defined(LL_WINDOWS) -- cgit v1.2.3 From 056f0983029000041555ca53c61cbe5e8689cae9 Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 22 Apr 2016 12:58:51 +0200 Subject: Windows x64: Cannot use inline assembly. (transplanted from ee32840fc591f5529a0b544243e7b4146eb8f531) --- indra/llcommon/llfasttimer.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index f56e5596f5..0336f9d0e9 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -91,6 +91,7 @@ public: static U32 getCPUClockCount32() { U32 ret_val; +#if !defined(_M_AMD64) __asm { _emit 0x0f @@ -100,6 +101,11 @@ public: or eax, edx mov dword ptr [ret_val], eax } +#else + unsigned __int64 val = __rdtsc(); + val = val >> 8; + ret_val = static_cast(val); +#endif return ret_val; } @@ -107,6 +113,7 @@ public: static U64 getCPUClockCount64() { U64 ret_val; +#if !defined(_M_AMD64) __asm { _emit 0x0f @@ -116,6 +123,9 @@ public: mov dword ptr [ret_val+4], edx mov dword ptr [ret_val], eax } +#else + ret_val = static_cast( __rdtsc() ); +#endif return ret_val; } -- cgit v1.2.3 From a116f96a2fce19fa7e5dc56316044332c13d97d5 Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 22 Apr 2016 13:02:37 +0200 Subject: Windows: USe the correct datatypes when calling the Windows API. (transplanted from 8b0c42b1a4f0416a17c8ec6078a85c5773f69a25) --- indra/llcommon/llprocessor.cpp | 2 +- indra/llcommon/llthread.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index e3e1d0c391..65b4507e2d 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -398,7 +398,7 @@ static F64 calculate_cpu_frequency(U32 measure_msecs) HANDLE hThread = GetCurrentThread(); unsigned long dwCurPriorityClass = GetPriorityClass(hProcess); int iCurThreadPriority = GetThreadPriority(hThread); - unsigned long dwProcessMask, dwSystemMask, dwNewMask = 1; + DWORD_PTR dwProcessMask, dwSystemMask, dwNewMask = 1; GetProcessAffinityMask(hProcess, &dwProcessMask, &dwSystemMask); SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS); diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index c3f235c6ee..52255bfaeb 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -63,7 +63,7 @@ void set_thread_name( DWORD dwThreadID, const char* threadName) __try { - ::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info ); + ::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info ); } __except(EXCEPTION_CONTINUE_EXECUTION) { -- cgit v1.2.3 From c87d24ac71c662ab37b6b937f92d960c6d8d092f Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 22 Apr 2016 23:59:28 +0200 Subject: Fasttimers: Windows) Always use the __rdtsc() intrinsic rather than inline assembly. Linux/OSX) The rtdsc assembly intruction is clobbering EAX and EDX, the snippet was not protecting EDX accordingly. (transplanted from 6307b134f821390367d4c86a03b9a492ac7ed282) --- indra/llcommon/llfasttimer.h | 44 ++++++++------------------------------------ 1 file changed, 8 insertions(+), 36 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 0336f9d0e9..2024d707da 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -90,43 +90,15 @@ public: #if LL_FASTTIMER_USE_RDTSC static U32 getCPUClockCount32() { - U32 ret_val; -#if !defined(_M_AMD64) - __asm - { - _emit 0x0f - _emit 0x31 - shr eax,8 - shl edx,24 - or eax, edx - mov dword ptr [ret_val], eax - } -#else unsigned __int64 val = __rdtsc(); val = val >> 8; - ret_val = static_cast(val); -#endif - return ret_val; + return static_cast(val); } // return full timer value, *not* shifted by 8 bits static U64 getCPUClockCount64() { - U64 ret_val; -#if !defined(_M_AMD64) - __asm - { - _emit 0x0f - _emit 0x31 - mov eax,eax - mov edx,edx - mov dword ptr [ret_val+4], edx - mov dword ptr [ret_val], eax - } -#else - ret_val = static_cast( __rdtsc() ); -#endif - return ret_val; + return static_cast( __rdtsc() ); } #else @@ -183,16 +155,16 @@ public: // Mac+Linux+Solaris FAST x86 implementation of CPU clock static U32 getCPUClockCount32() { - U64 x; - __asm__ volatile (".byte 0x0f, 0x31": "=A"(x)); - return (U32)(x >> 8); + U32 low(0),high(0); + __asm__ volatile (".byte 0x0f, 0x31": "=a"(low), "=d"(high) ); + return (low>>8) | (high<<24); } static U64 getCPUClockCount64() { - U64 x; - __asm__ volatile (".byte 0x0f, 0x31": "=A"(x)); - return x; + U32 low(0),high(0); + __asm__ volatile (".byte 0x0f, 0x31": "=a"(low), "=d"(high) ); + return (U64)low | ( ((U64)high) << 32); } #endif -- cgit v1.2.3 From a590d1c63ae4c1434da600d60b5c32c9b8a7a1de Mon Sep 17 00:00:00 2001 From: Nicky Date: Sun, 24 Apr 2016 12:51:26 +0200 Subject: Windows z64: Disable warning 4267 via llpreprocessor rather than cmake files (transplanted from 165fa5852652a1da005cf3b2201c192f028efd43) --- indra/llcommon/llpreprocessor.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 2c4bcc91f6..7c277c2bed 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -138,6 +138,12 @@ #pragma warning( 3 : 4266 ) // 'function' : no override available for virtual member function from base 'type'; function is hidden #pragma warning (disable : 4180) // qualifier applied to function type has no meaning; ignored //#pragma warning( disable : 4284 ) // silly MS warning deep inside their include file + +#ifdef _M_AMD64 +// That one is all over the place for x64 builds. +#pragma warning( disable : 4267 ) // 'var' : conversion from 'size_t' to 'type', possible loss of data) +#endif + #pragma warning( disable : 4503 ) // 'decorated name length exceeded, name was truncated'. Does not seem to affect compilation. #pragma warning( disable : 4800 ) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning) #pragma warning( disable : 4996 ) // warning: deprecated -- cgit v1.2.3 From 6f6c8fa5ff6619b123f368d9137c11ed679a5349 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 28 Jun 2016 18:27:39 -0400 Subject: DRTVWR-418: Double coroutine stack size for 64-bit builds on the advice of NickyD. --- indra/llcommon/llcoros.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index d16bf0160b..290abbf45c 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -100,7 +100,11 @@ LLCoros::LLCoros(): // Previously we used // boost::context::guarded_stack_allocator::default_stacksize(); // empirically this is 64KB on Windows and Linux. Try quadrupling. +#if WORD_SIZE == 64 + mStackSize(512*1024) +#else mStackSize(256*1024) +#endif { // Register our cleanup() method for "mainloop" ticks LLEventPumps::instance().obtain("mainloop").listen( -- cgit v1.2.3 From 6c7a97286113b1d3335d585d0d7cbc047baae021 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 15 Nov 2016 15:53:24 -0500 Subject: DRTVWR-418: Fold windows64 into windows platform with new autobuild. autobuild 1.1 now supports expanding $variables within a config file -- support that was explicitly added to address this very problem. So now the windows platform in autobuild.xml uses $AUTOBUILD_ADDRSIZE, $AUTOBUILD_WIN_VSPLATFORM and $AUTOBUILD_WIN_CMAKE_GEN, which should handle most of the deltas between the windows platform and windows64. This permits removing the windows64 platform definition from autobuild.xml. The one remaining delta between the windows64 and windows platform definitions was -DLL_64BIT_BUILD=TRUE. But we can handle that instead by checking ADDRESS_SIZE. Change all existing references to WORD_SIZE to ADDRESS_SIZE instead, and set ADDRESS_SIZE to $AUTOBUILD_ADDRSIZE. Change the one existing LL_64BIT_BUILD reference to test (ADDRESS_SIZE EQUAL 64) instead. --- indra/llcommon/CMakeLists.txt | 4 ++-- indra/llcommon/llcoros.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 493aa5d0f1..b29b2b2ccf 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -245,13 +245,13 @@ list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES}) if(LLCOMMON_LINK_SHARED) add_library (llcommon SHARED ${llcommon_SOURCE_FILES}) - if(NOT WORD_SIZE EQUAL 32) + if(NOT ADDRESS_SIZE EQUAL 32) if(WINDOWS) add_definitions(/FIXED:NO) else(WINDOWS) # not windows therefore gcc LINUX and DARWIN add_definitions(-fPIC) endif(WINDOWS) - endif(NOT WORD_SIZE EQUAL 32) + endif(NOT ADDRESS_SIZE EQUAL 32) if(WINDOWS) # always generate llcommon.pdb, even for "Release" builds set_target_properties(llcommon PROPERTIES LINK_FLAGS "/DEBUG") diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 0d9e19f672..bc72faca5d 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -101,7 +101,7 @@ LLCoros::LLCoros(): // Previously we used // boost::context::guarded_stack_allocator::default_stacksize(); // empirically this is 64KB on Windows and Linux. Try quadrupling. -#if WORD_SIZE == 64 +#if ADDRESS_SIZE == 64 mStackSize(512*1024) #else mStackSize(256*1024) -- cgit v1.2.3 From f5e983962703b5cb39278048b1c35e712b2b2263 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 16 Nov 2016 15:39:00 -0500 Subject: DRTVWR-418: Replace preprocessor tests for Windows-specific _M_AMD64 with tests on ADDRESS_SIZE, which is now set on the compiler command line. --- indra/llcommon/llpreprocessor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 7c277c2bed..3698d9db44 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -139,7 +139,7 @@ #pragma warning (disable : 4180) // qualifier applied to function type has no meaning; ignored //#pragma warning( disable : 4284 ) // silly MS warning deep inside their include file -#ifdef _M_AMD64 +#if ADDRESS_SIZE == 64 // That one is all over the place for x64 builds. #pragma warning( disable : 4267 ) // 'var' : conversion from 'size_t' to 'type', possible loss of data) #endif -- cgit v1.2.3 From 40f7501319087291c8b9881095b4b35f0dcf0554 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 22 Nov 2016 08:35:41 -0500 Subject: DRTVWR-418: Use uintptr_t when casting pointers to ints. LLPrivateMemoryPool and LLPrivateMemoryPoolManager have assumed that it's always valid to cast a pointer to U32. With 64-bit pointers, no longer true. --- indra/llcommon/llmemory.cpp | 24 ++++++++++++------------ indra/llcommon/llmemory.h | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index 3a8eabac09..1e04044269 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -591,7 +591,7 @@ char* LLPrivateMemoryPool::LLMemoryBlock::allocate() void LLPrivateMemoryPool::LLMemoryBlock::freeMem(void* addr) { //bit index - U32 idx = ((U32)addr - (U32)mBuffer - mDummySize) / mSlotSize ; + uintptr_t idx = ((uintptr_t)addr - (uintptr_t)mBuffer - mDummySize) / mSlotSize ; U32* bits = &mUsageBits ; if(idx >= 32) @@ -773,7 +773,7 @@ char* LLPrivateMemoryPool::LLMemoryChunk::allocate(U32 size) void LLPrivateMemoryPool::LLMemoryChunk::freeMem(void* addr) { - U32 blk_idx = getPageIndex((U32)addr) ; + U32 blk_idx = getPageIndex((uintptr_t)addr) ; LLMemoryBlock* blk = (LLMemoryBlock*)(mMetaBuffer + blk_idx * sizeof(LLMemoryBlock)) ; blk = blk->mSelf ; @@ -798,7 +798,7 @@ bool LLPrivateMemoryPool::LLMemoryChunk::empty() bool LLPrivateMemoryPool::LLMemoryChunk::containsAddress(const char* addr) const { - return (U32)mBuffer <= (U32)addr && (U32)mBuffer + mBufferSize > (U32)addr ; + return (uintptr_t)mBuffer <= (uintptr_t)addr && (uintptr_t)mBuffer + mBufferSize > (uintptr_t)addr ; } //debug use @@ -831,13 +831,13 @@ void LLPrivateMemoryPool::LLMemoryChunk::dump() for(U32 i = 1 ; i < blk_list.size(); i++) { total_size += blk_list[i]->getBufferSize() ; - if((U32)blk_list[i]->getBuffer() < (U32)blk_list[i-1]->getBuffer() + blk_list[i-1]->getBufferSize()) + if((uintptr_t)blk_list[i]->getBuffer() < (uintptr_t)blk_list[i-1]->getBuffer() + blk_list[i-1]->getBufferSize()) { LL_ERRS() << "buffer corrupted." << LL_ENDL ; } } - llassert_always(total_size + mMinBlockSize >= mBufferSize - ((U32)mDataBuffer - (U32)mBuffer)) ; + llassert_always(total_size + mMinBlockSize >= mBufferSize - ((uintptr_t)mDataBuffer - (uintptr_t)mBuffer)) ; U32 blk_num = (mBufferSize - (mDataBuffer - mBuffer)) / mMinBlockSize ; for(U32 i = 0 ; i < blk_num ; ) @@ -860,7 +860,7 @@ void LLPrivateMemoryPool::LLMemoryChunk::dump() #endif #if 0 LL_INFOS() << "---------------------------" << LL_ENDL ; - LL_INFOS() << "Chunk buffer: " << (U32)getBuffer() << " size: " << getBufferSize() << LL_ENDL ; + LL_INFOS() << "Chunk buffer: " << (uintptr_t)getBuffer() << " size: " << getBufferSize() << LL_ENDL ; LL_INFOS() << "available blocks ... " << LL_ENDL ; for(S32 i = 0 ; i < mBlockLevels ; i++) @@ -868,7 +868,7 @@ void LLPrivateMemoryPool::LLMemoryChunk::dump() LLMemoryBlock* blk = mAvailBlockList[i] ; while(blk) { - LL_INFOS() << "blk buffer " << (U32)blk->getBuffer() << " size: " << blk->getBufferSize() << LL_ENDL ; + LL_INFOS() << "blk buffer " << (uintptr_t)blk->getBuffer() << " size: " << blk->getBufferSize() << LL_ENDL ; blk = blk->mNext ; } } @@ -879,7 +879,7 @@ void LLPrivateMemoryPool::LLMemoryChunk::dump() LLMemoryBlock* blk = mFreeSpaceList[i] ; while(blk) { - LL_INFOS() << "blk buffer " << (U32)blk->getBuffer() << " size: " << blk->getBufferSize() << LL_ENDL ; + LL_INFOS() << "blk buffer " << (uintptr_t)blk->getBuffer() << " size: " << blk->getBufferSize() << LL_ENDL ; blk = blk->mNext ; } } @@ -1155,9 +1155,9 @@ void LLPrivateMemoryPool::LLMemoryChunk::addToAvailBlockList(LLMemoryBlock* blk) return ; } -U32 LLPrivateMemoryPool::LLMemoryChunk::getPageIndex(U32 addr) +U32 LLPrivateMemoryPool::LLMemoryChunk::getPageIndex(uintptr_t addr) { - return (addr - (U32)mDataBuffer) / mMinBlockSize ; + return (addr - (uintptr_t)mDataBuffer) / mMinBlockSize ; } //for mAvailBlockList @@ -1495,7 +1495,7 @@ void LLPrivateMemoryPool::removeChunk(LLMemoryChunk* chunk) U16 LLPrivateMemoryPool::findHashKey(const char* addr) { - return (((U32)addr) / CHUNK_SIZE) % mHashFactor ; + return (((uintptr_t)addr) / CHUNK_SIZE) % mHashFactor ; } LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::findChunk(const char* addr) @@ -1720,7 +1720,7 @@ LLPrivateMemoryPoolManager::~LLPrivateMemoryPoolManager() S32 k = 0 ; for(mem_allocation_info_t::iterator iter = sMemAllocationTracker.begin() ; iter != sMemAllocationTracker.end() ; ++iter) { - LL_INFOS() << k++ << ", " << (U32)iter->first << " : " << iter->second << LL_ENDL ; + LL_INFOS() << k++ << ", " << (uintptr_t)iter->first << " : " << iter->second << LL_ENDL ; } sMemAllocationTracker.clear() ; } diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 98e08cdc55..5a3c9bd762 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -415,7 +415,7 @@ public: { bool operator()(const LLMemoryBlock* const& lhs, const LLMemoryBlock* const& rhs) { - return (U32)lhs->getBuffer() < (U32)rhs->getBuffer(); + return (uintptr_t)lhs->getBuffer() < (uintptr_t)rhs->getBuffer(); } }; }; @@ -446,7 +446,7 @@ public: void dump() ; private: - U32 getPageIndex(U32 addr) ; + U32 getPageIndex(uintptr_t addr) ; U32 getBlockLevel(U32 size) ; U16 getPageLevel(U32 size) ; LLMemoryBlock* addBlock(U32 blk_idx) ; -- cgit v1.2.3 From 9c55b368566ad1874d845573c4df66dc77766d29 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 22 Nov 2016 08:37:45 -0500 Subject: DRTVWR-418: Update comments to reflect status of P0091R3. Some day llmake() will be unnecessary because compiler deduction of class template arguments from constructor arguments has been approved by ISO. --- indra/llcommon/llmake.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llmake.h b/indra/llcommon/llmake.h index 9a662a0640..08744f90fb 100644 --- a/indra/llcommon/llmake.h +++ b/indra/llcommon/llmake.h @@ -12,12 +12,10 @@ * * also relevant: * - * Template parameter deduction for constructors - * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0091r0.html - * - * https://github.com/viboes/std-make - * - * but obviously we're not there yet. + * Template argument deduction for class templates + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0091r3.html + * was apparently adopted in June 2016? Unclear when compilers will + * portably support this, but there is hope. * * $LicenseInfo:firstyear=2015&license=viewerlgpl$ * Copyright (c) 2015, Linden Research, Inc. -- cgit v1.2.3 From 548f59042f116c04dd3d34d66b0328801eb2286f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 22 Nov 2016 08:40:41 -0500 Subject: DRTVWR-418: libc++ has stat data in . --- indra/llcommon/llfile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 3e25228aeb..315e18e4f2 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -45,7 +45,7 @@ typedef FILE LLFILE; typedef struct _stat llstat; #else typedef struct stat llstat; -#include +#include #endif #ifndef S_ISREG -- cgit v1.2.3 From d4b23ccb8ab79425d7962def3be940d3dcd951ca Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 30 Nov 2016 16:24:32 -0500 Subject: DRTVWR-418: VertexMap::mapped_type -> size_t: we store map.size(). --- indra/llcommon/lldependencies.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lldependencies.h b/indra/llcommon/lldependencies.h index 125bd6a835..de214a8943 100644 --- a/indra/llcommon/lldependencies.h +++ b/indra/llcommon/lldependencies.h @@ -508,7 +508,7 @@ public: // been explicitly added. Rely on std::map rejecting a second attempt // to insert the same key. Use the map's size() as the vertex number // to get a distinct value for each successful insertion. - typedef std::map VertexMap; + typedef std::map VertexMap; VertexMap vmap; // Nest each of these loops because !@#$%? MSVC warns us that its // former broken behavior has finally been fixed -- and our builds -- cgit v1.2.3 From 68d98acb920a49880662db0d20ccdf52edbc0151 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 1 Dec 2016 08:44:52 -0500 Subject: DRTVWR-418: In 64 bits, storing size_t in an int is a no-no. --- indra/llcommon/lldependencies.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lldependencies.h b/indra/llcommon/lldependencies.h index de214a8943..db2bbab8b0 100644 --- a/indra/llcommon/lldependencies.h +++ b/indra/llcommon/lldependencies.h @@ -124,8 +124,8 @@ public: virtual std::string describe(bool full=true) const; protected: - typedef std::vector< std::pair > EdgeList; - typedef std::vector VertexList; + typedef std::vector< std::pair > EdgeList; + typedef std::vector VertexList; VertexList topo_sort(int vertices, const EdgeList& edges) const; /** -- cgit v1.2.3 From 8a461c00f4eb64027e4d81c081d6b7aa6d680d6e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 1 Dec 2016 08:50:10 -0500 Subject: DRTVWR-418: Until we figure out how to say FIXED:NO to linker, don't. The present CMake logic wants to pass FIXED:NO to the linker for 64-bit builds, which on the face of it seems like a Good Thing: it permits code to be relocated in memory, preventing collisions if two libraries happen to want to load into overlapping address ranges. However the way it's being specified is wrong and harmful. Passing /FIXED:NO to the compiler command line engages /FI (Forced Include!) of a nonexistent file XED:NO -- producing lots of baffling fatal compile errors. Thanks Callum for diagnosing this! --- indra/llcommon/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index b29b2b2ccf..0468015bfa 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -247,7 +247,7 @@ if(LLCOMMON_LINK_SHARED) add_library (llcommon SHARED ${llcommon_SOURCE_FILES}) if(NOT ADDRESS_SIZE EQUAL 32) if(WINDOWS) - add_definitions(/FIXED:NO) + ##add_definitions(/FIXED:NO) else(WINDOWS) # not windows therefore gcc LINUX and DARWIN add_definitions(-fPIC) endif(WINDOWS) -- cgit v1.2.3 From bcb4f2900bba09412e2f3f29ae7b343b6b89dfb3 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 20 Dec 2016 11:01:17 -0500 Subject: DRTVWR-418: operator comparison methods should be const. clang has started to reject our non-const comparison operator methods used within standard algorithms. --- indra/llcommon/llstring.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index a40db0f8cc..2fdb8be84f 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -443,7 +443,7 @@ public: struct LLDictionaryLess { public: - bool operator()(const std::string& a, const std::string& b) + bool operator()(const std::string& a, const std::string& b) const { return (LLStringUtil::precedesDict(a, b) ? true : false); } -- cgit v1.2.3 From c2c4a1e8514dc8a0df8e9d2f7b2e743691ef7ce3 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 3 Feb 2017 10:34:01 -0500 Subject: DRTVWR-418: Make operator()() method for comparator functor const. --- indra/llcommon/llheteromap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llheteromap.h b/indra/llcommon/llheteromap.h index 9d6f303d08..7e96172333 100644 --- a/indra/llcommon/llheteromap.h +++ b/indra/llcommon/llheteromap.h @@ -77,7 +77,7 @@ private: // not always equal &typeid(A) in some other part. Use special comparator. struct type_info_ptr_comp { - bool operator()(const std::type_info* lhs, const std::type_info* rhs) + bool operator()(const std::type_info* lhs, const std::type_info* rhs) const { return lhs->before(*rhs); } -- cgit v1.2.3 From ae0b3149badf369eb2b1f10aba830eef8b4af9b4 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 23 Feb 2017 16:49:49 -0500 Subject: DRTVWR-418: Fix a round of compile errors surfaced by -std=c++11. These are mostly things that were in fact erroneous, but accepted by older compilers. This changeset has not yet been built with Visual Studio 2013 or Linux gcc, even with -std=c++11. This changeset has not been built *without* -std=c++11. It should be used in conjunction with a corresponding change to LL_BUILD_DARWIN_BASE_SWITCHES in viewer-build-variables/variables. This is a work in progress. We do not assert that this changeset completes the work needed to turn on -std=c++11, even on the Mac. --- indra/llcommon/lleventdispatcher.h | 4 ++-- indra/llcommon/llpreprocessor.h | 10 +++------- indra/llcommon/tests/llsdserialize_test.cpp | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h index 7acc61de4e..9e1244ef5b 100644 --- a/indra/llcommon/lleventdispatcher.h +++ b/indra/llcommon/lleventdispatcher.h @@ -47,13 +47,13 @@ // namespace) that a global 'nil' macro breaks badly. #if defined(nil) // Capture the value of the macro 'nil', hoping int is an appropriate type. -static const int nil_(nil); +static const auto nil_(nil); // Now forget the macro. #undef nil // Finally, reintroduce 'nil' as a properly-scoped alias for the previously- // defined const 'nil_'. Make it static since otherwise it produces duplicate- // symbol link errors later. -static const int& nil(nil_); +static const auto& nil(nil_); #endif #include diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 3698d9db44..2879038c36 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -192,13 +192,9 @@ # define LL_COMMON_API #endif // LL_COMMON_LINK_SHARED -#if LL_WINDOWS -#define LL_TYPEOF(exp) decltype(exp) -#elif LL_LINUX -#define LL_TYPEOF(exp) typeof(exp) -#elif LL_DARWIN -#define LL_TYPEOF(exp) typeof(exp) -#endif +// With C++11, decltype() is standard. We no longer need a platform-dependent +// macro to get the type of an expression. +#define LL_TYPEOF(expr) decltype(expr) #define LL_TO_STRING_HELPER(x) #x #define LL_TO_STRING(x) LL_TO_STRING_HELPER(x) diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index 81b930e1e2..8836230640 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -1553,7 +1553,7 @@ namespace tut params.executable = PYTHON; params.args.add(scriptfile.getName()); LLProcessPtr py(LLProcess::create(params)); - ensure(STRINGIZE("Couldn't launch " << desc << " script"), py); + ensure(STRINGIZE("Couldn't launch " << desc << " script"), bool(py)); // Implementing timeout would mean messing with alarm() and // catching SIGALRM... later maybe... int status(0); -- cgit v1.2.3 From 1a8c8df6862620de64f621363b025b0ffbef72fa Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 13 Mar 2017 14:09:14 -0400 Subject: DRTVWR-418: Ignore logging that requires resurrecting singletons. The logging subsystem depends on two different LLSingletons for some reason. It turns out to be very difficult to completely avoid executing any logging calls after the LLSingletonBase::deleteAll(), but we really don't want to resurrect those LLSingletons so late in the run for a couple stragglers. Introduce LLSingleton::wasDeleted() query method, and use it in logging subsystem to simply bypass last-millisecond logging requests. --- indra/llcommon/llerror.cpp | 97 ++++++++++++++++++++++++++++---------------- indra/llcommon/llsingleton.h | 8 ++++ 2 files changed, 70 insertions(+), 35 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index e6407ecf22..2ddb3edbdd 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -1067,7 +1067,15 @@ namespace LLError { return false; } - + + // If we hit a logging request very late during shutdown processing, + // when either of the relevant LLSingletons has already been deleted, + // DO NOT resurrect them. + if (Settings::wasDeleted() || Globals::wasDeleted()) + { + return false; + } + SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); s->mShouldLogCallCounter++; @@ -1106,7 +1114,10 @@ namespace LLError std::ostringstream* Log::out() { LogLock lock; - if (lock.ok()) + // If we hit a logging request very late during shutdown processing, + // when either of the relevant LLSingletons has already been deleted, + // DO NOT resurrect them. + if (lock.ok() && ! (Settings::wasDeleted() || Globals::wasDeleted())) { Globals* g = Globals::getInstance(); @@ -1116,41 +1127,49 @@ namespace LLError return &g->messageStream; } } - + return new std::ostringstream; } - + void Log::flush(std::ostringstream* out, char* message) - { - LogLock lock; - if (!lock.ok()) - { - return; - } - - if(strlen(out->str().c_str()) < 128) - { - strcpy(message, out->str().c_str()); - } - else - { - strncpy(message, out->str().c_str(), 127); - message[127] = '\0' ; - } - - Globals* g = Globals::getInstance(); - if (out == &g->messageStream) - { - g->messageStream.clear(); - g->messageStream.str(""); - g->messageStreamInUse = false; - } - else - { - delete out; - } - return ; - } + { + LogLock lock; + if (!lock.ok()) + { + return; + } + + // If we hit a logging request very late during shutdown processing, + // when either of the relevant LLSingletons has already been deleted, + // DO NOT resurrect them. + if (Settings::wasDeleted() || Globals::wasDeleted()) + { + return; + } + + if(strlen(out->str().c_str()) < 128) + { + strcpy(message, out->str().c_str()); + } + else + { + strncpy(message, out->str().c_str(), 127); + message[127] = '\0' ; + } + + Globals* g = Globals::getInstance(); + if (out == &g->messageStream) + { + g->messageStream.clear(); + g->messageStream.str(""); + g->messageStreamInUse = false; + } + else + { + delete out; + } + return ; + } void Log::flush(std::ostringstream* out, const CallSite& site) { @@ -1159,7 +1178,15 @@ namespace LLError { return; } - + + // If we hit a logging request very late during shutdown processing, + // when either of the relevant LLSingletons has already been deleted, + // DO NOT resurrect them. + if (Settings::wasDeleted() || Globals::wasDeleted()) + { + return; + } + Globals* g = Globals::getInstance(); SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 1b915dfd6e..0d4a1f34f8 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -452,6 +452,14 @@ public: return sData.mInitState == INITIALIZED; } + // Has this singleton been deleted? This can be useful during shutdown + // processing to avoid "resurrecting" a singleton we thought we'd already + // cleaned up. + static bool wasDeleted() + { + return sData.mInitState == DELETED; + } + private: struct SingletonData { -- cgit v1.2.3 From e6fc3528fdfd2a251571ef86f321e321865d4592 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 13 Mar 2017 14:22:19 -0400 Subject: DRTVWR-418: #include "llrefcount.h" : LLTombStone uses LLRefCount. Apparently we've been getting away so far without this essential #include only by "leakage" from other #includes in existing consumers. --- indra/llcommon/llhandle.h | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h index feb5f41848..570cd330b8 100644 --- a/indra/llcommon/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -28,6 +28,7 @@ #define LLHANDLE_H #include "llpointer.h" +#include "llrefcount.h" #include "llexception.h" #include #include -- cgit v1.2.3 From c1458713dea2ac8cec100628c0ca5238fcca93ba Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 13 Mar 2017 14:31:38 -0400 Subject: DRTVWR-418: Make LLEventPumps an LLHandleProvider for LLEventPump. LLEventPump's destructor was using LLEventPumps::instance() to unregister the LLEventPump instance from LLEventPumps. Evidently, though, there are lingering LLEventPump instances that persist even after the LLSingletonBase::deleteAll() call destroys the LLEventPumps LLSingleton instance. These were resurrecting LLEventPumps -- pointlessly, since a newly-resurrected LLEventPumps instance can have no knowledge of the LLEventPump instance! Unregistering is unnecessary! What we want is a reference we can bind into each LLEventPump instance that allows us to safely test whether the LLEventPumps instance still exists. LLHandle is exactly that. Make LLEventPumps an LLHandleProvider and bind its LLHandle in each LLEventPump's constructor; then the destructor can unregister only when LLEventPumps still exists. --- indra/llcommon/llevents.cpp | 12 +++++++++--- indra/llcommon/llevents.h | 18 +++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 97270e4931..a3856e4fc4 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -281,7 +281,8 @@ const std::string LLEventPump::ANONYMOUS = std::string(); LLEventPump::LLEventPump(const std::string& name, bool tweak): // Register every new instance with LLEventPumps - mName(LLEventPumps::instance().registerNew(*this, name, tweak)), + mRegistry(LLEventPumps::instance().getHandle()), + mName(mRegistry.get()->registerNew(*this, name, tweak)), mSignal(new LLStandardSignal()), mEnabled(true) {} @@ -292,8 +293,13 @@ LLEventPump::LLEventPump(const std::string& name, bool tweak): LLEventPump::~LLEventPump() { - // Unregister this doomed instance from LLEventPumps - LLEventPumps::instance().unregister(*this); + // Unregister this doomed instance from LLEventPumps -- but only if + // LLEventPumps is still around! + LLEventPumps* registry = mRegistry.get(); + if (registry) + { + registry->unregister(*this); + } } // static data member diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 7cff7dfd45..1d51c660ed 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -62,6 +62,7 @@ #include "lldependencies.h" #include "llstl.h" #include "llexception.h" +#include "llhandle.h" /*==========================================================================*| // override this to allow binding free functions with more parameters @@ -227,7 +228,15 @@ class LLEventPump; * LLEventPumps is a Singleton manager through which one typically accesses * this subsystem. */ -class LL_COMMON_API LLEventPumps: public LLSingleton +// LLEventPumps isa LLHandleProvider only for (hopefully rare) long-lived +// class objects that must refer to this class late in their lifespan, say in +// the destructor. Specifically, the case that matters is a possible reference +// after LLEventPumps::deleteSingleton(). (Lingering LLEventPump instances are +// capable of this.) In that case, instead of calling LLEventPumps::instance() +// again -- resurrecting the deleted LLSingleton -- store an +// LLHandle and test it before use. +class LL_COMMON_API LLEventPumps: public LLSingleton, + public LLHandleProvider { LLSINGLETON(LLEventPumps); public: @@ -590,6 +599,9 @@ private: return this->listen_impl(name, listener, after, before); } + // must precede mName; see LLEventPump::LLEventPump() + LLHandle mRegistry; + std::string mName; protected: @@ -817,14 +829,14 @@ public: mConnection(new LLBoundListener) { } - + /// Copy constructor. Copy shared_ptrs to original instance data. LLListenerWrapperBase(const LLListenerWrapperBase& that): mName(that.mName), mConnection(that.mConnection) { } - virtual ~LLListenerWrapperBase() {} + virtual ~LLListenerWrapperBase() {} /// Ask LLEventPump::listen() for the listener name virtual void accept_name(const std::string& name) const -- cgit v1.2.3 From 64581fb8d001262d2e34b3e3b653e485555d9c9c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 29 Mar 2017 16:07:58 -0400 Subject: DRTVWR-418: Instead of "Unknown", try be informative about platform. When a 'family' code isn't recognized, for instance, report the family code. That should at least clue us in to look up and add an entry for the relevant family code. --- indra/llcommon/llprocessor.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 65b4507e2d..446c312ca9 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -26,9 +26,11 @@ #include "linden_common.h" #include "llprocessor.h" - +#include "llstring.h" +#include "stringize.h" #include "llerror.h" +#include //#include #if LL_WINDOWS @@ -188,7 +190,7 @@ namespace case 0xF: return "Intel Pentium 4"; case 0x10: return "Intel Itanium 2 (IA-64)"; } - return "Unknown"; + return STRINGIZE("Intel "); } std::string amd_CPUFamilyName(int composed_family) @@ -201,26 +203,26 @@ namespace case 0xF: return "AMD K8"; case 0x10: return "AMD K8L"; } - return "Unknown"; + return STRINGIZE("AMD "); } std::string compute_CPUFamilyName(const char* cpu_vendor, int family, int ext_family) { const char* intel_string = "GenuineIntel"; const char* amd_string = "AuthenticAMD"; - if(!strncmp(cpu_vendor, intel_string, strlen(intel_string))) + if (LLStringUtil::startsWith(cpu_vendor, intel_string)) { U32 composed_family = family + ext_family; return intel_CPUFamilyName(composed_family); } - else if(!strncmp(cpu_vendor, amd_string, strlen(amd_string))) + else if (LLStringUtil::startsWith(cpu_vendor, amd_string)) { U32 composed_family = (family == 0xF) ? family + ext_family : family; return amd_CPUFamilyName(composed_family); } - return "Unknown"; + return STRINGIZE("Unrecognized CPU vendor <" << cpu_vendor << ">"); } } // end unnamed namespace @@ -258,8 +260,8 @@ public: return hasExtension("Altivec"); } - std::string getCPUFamilyName() const { return getInfo(eFamilyName, "Unknown").asString(); } - std::string getCPUBrandName() const { return getInfo(eBrandName, "Unknown").asString(); } + std::string getCPUFamilyName() const { return getInfo(eFamilyName, "Unset family").asString(); } + std::string getCPUBrandName() const { return getInfo(eBrandName, "Unset brand").asString(); } // This is virtual to support a different linux format. // *NOTE:Mani - I didn't want to screw up server use of this data... @@ -271,7 +273,7 @@ public: out << "//////////////////////////" << std::endl; out << "Processor Name: " << getCPUBrandName() << std::endl; out << "Frequency: " << getCPUFrequency() << " MHz" << std::endl; - out << "Vendor: " << getInfo(eVendor, "Unknown").asString() << std::endl; + out << "Vendor: " << getInfo(eVendor, "Unset vendor").asString() << std::endl; out << "Family: " << getCPUFamilyName() << " (" << getInfo(eFamily, 0) << ")" << std::endl; out << "Extended family: " << getInfo(eExtendedFamily, 0) << std::endl; out << "Model: " << getInfo(eModel, 0) << std::endl; -- cgit v1.2.3 From e9fe0714ad422c2b9250c8ce13d3b2837dff5430 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 30 Mar 2017 15:39:47 -0400 Subject: DRTVWR-418: Xcode 8.3 complains about LLSafeHandle implementation. The previous LLSafeHandle implementation declares a static data member of the template class but provides no (generic) definition, relying on particular specializations to provide the definition. The data member is a function pointer, which is called in one of the methods to produce a pointer to a "null" T instance: that is, a dummy instance to be dereferenced in case the wrapped T* is null. Xcode 8.3's version of clang is bothered by the call, in a generic method, through this (usually) uninitialized pointer. It happens that the only specializations of LLSafeHandle do both provide definitions. I don't know whether that's formally valid C++03 or not; but I agree with the compiler: I don't like it. Instead of declaring a public static function pointer which each specialization is required to define, add a protected static method to the template class. This protected static method simply returns a pointer to a function-static T instance. This is functionally similar to a static LLPointer set on demand (as in the two specializations), including lazy instantiation. Unlike the previous implementation, this approach prohibits a given specialization from customizing the "null" instance function. Although there exist reasonable ways to support that (e.g. a related traits template), I decided not to complicate the LLSafeHandle implementation to make it more generally useful. I don't really approve of LLSafeHandle, and don't want to see it proliferate. It's not clear that unconditionally dereferencing LLSafeHandle is in any way better than conditionally dereferencing LLPointer. It doesn't even skip the runtime conditional test; it simply obscures it. (There exist hints in the code that at one time it might have immediately replaced any wrapped null pointer value with the pointer to the "null" instance, obviating the test at dereference time, but this is not the current functionality. Perhaps it was only ever wishful thinking.) Remove the corresponding functions and static LLPointers from the two classes that use LLSafeHandle. --- indra/llcommon/llsafehandle.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsafehandle.h b/indra/llcommon/llsafehandle.h index 4226bf04f0..af1c26dd4f 100644 --- a/indra/llcommon/llsafehandle.h +++ b/indra/llcommon/llsafehandle.h @@ -112,10 +112,6 @@ public: return *this; } -public: - typedef Type* (*NullFunc)(); - static const NullFunc sNullFunc; - protected: void ref() { @@ -155,6 +151,12 @@ protected: return ptr == NULL ? sNullFunc() : ptr; } + static Type* sNullFunc() + { + static Type sInstance; + return &sInstance; + } + protected: Type* mPointer; }; -- cgit v1.2.3 From 9dd7c67012e305fa61d20135e6082389e622c88a Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Wed, 19 Apr 2017 16:50:56 -0700 Subject: Pull in improvements to LLProcess termination via a commit from Nat Linden here: https://bitbucket.org/rider_linden/doduo-viewer/commits/4f39500cb46e879dbb732e6547cc66f3ba39959e?at=default --- indra/llcommon/llprocess.cpp | 12 +++-- indra/llcommon/llprocess.h | 29 +++++++++-- indra/llcommon/tests/llprocess_test.cpp | 89 ++++++++++++++++++++++++++++----- 3 files changed, 110 insertions(+), 20 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index 8c321d06b9..5753efdc59 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -517,6 +517,10 @@ LLProcessPtr LLProcess::create(const LLSDOrParams& params) LLProcess::LLProcess(const LLSDOrParams& params): mAutokill(params.autokill), + // Because 'autokill' originally meant both 'autokill' and 'attached', to + // preserve existing semantics, we promise that mAttached defaults to the + // same setting as mAutokill. + mAttached(params.attached.isProvided()? params.attached : params.autokill), mPipes(NSLOTS) { // Hmm, when you construct a ptr_vector with a size, it merely reserves @@ -625,9 +629,9 @@ LLProcess::LLProcess(const LLSDOrParams& params): // std handles and the like, and that's a bit more detachment than we // want. autokill=false just means not to implicitly kill the child when // the parent terminates! -// chkapr(apr_procattr_detach_set(procattr, params.autokill? 0 : 1)); +// chkapr(apr_procattr_detach_set(procattr, mAutokill? 0 : 1)); - if (params.autokill) + if (mAutokill) { #if ! defined(APR_HAS_PROCATTR_AUTOKILL_SET) // Our special preprocessor symbol isn't even defined -- wrong APR @@ -696,7 +700,7 @@ LLProcess::LLProcess(const LLSDOrParams& params): // take steps to terminate the child. This is all suspenders-and-belt: in // theory our destructor should kill an autokill child, but in practice // that doesn't always work (e.g. VWR-21538). - if (params.autokill) + if (mAutokill) { /*==========================================================================*| // NO: There may be an APR bug, not sure -- but at least on Mac, when @@ -799,7 +803,7 @@ LLProcess::~LLProcess() sProcessListener.dropPoll(*this); } - if (mAutokill) + if (mAttached) { kill("destructor"); } diff --git a/indra/llcommon/llprocess.h b/indra/llcommon/llprocess.h index bfac4567a5..e3386ad88e 100644 --- a/indra/llcommon/llprocess.h +++ b/indra/llcommon/llprocess.h @@ -167,6 +167,7 @@ public: args("args"), cwd("cwd"), autokill("autokill", true), + attached("attached", true), files("files"), postend("postend"), desc("desc") @@ -183,9 +184,31 @@ public: Multiple args; /// current working directory, if need it changed Optional cwd; - /// implicitly kill process on destruction of LLProcess object - /// (default true) + /// implicitly kill child process on termination of parent, whether + /// voluntary or crash (default true) Optional autokill; + /// implicitly kill process on destruction of LLProcess object + /// (default same as autokill) + /// + /// Originally, 'autokill' conflated two concepts: kill child process on + /// - destruction of its LLProcess object, and + /// - termination of parent process, voluntary or otherwise. + /// + /// It's useful to tease these apart. Some child processes are sent a + /// "clean up and terminate" message before the associated LLProcess + /// object is destroyed. A child process launched with attached=false + /// has an extra time window from the destruction of its LLProcess + /// until parent-process termination in which to perform its own + /// orderly shutdown, yet autokill=true still guarantees that we won't + /// accumulate orphan instances of such processes indefinitely. With + /// attached=true, if a child process cannot clean up between the + /// shutdown message and LLProcess destruction (presumably very soon + /// thereafter), it's forcibly killed anyway -- which can lead to + /// distressing user-visible crash indications. + /// + /// (The usefulness of attached=true with autokill=false is less + /// clear, but we don't prohibit that combination.) + Optional attached; /** * Up to three FileParam items: for child stdin, stdout, stderr. * Passing two FileParam entries means default treatment for stderr, @@ -540,7 +563,7 @@ private: std::string mDesc; std::string mPostend; apr_proc_t mProcess; - bool mAutokill; + bool mAutokill, mAttached; Status mStatus; // explicitly want this ptr_vector to be able to store NULLs typedef boost::ptr_vector< boost::nullable > PipeVector; diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index 5ba343b183..b27e125d2e 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -788,6 +788,69 @@ namespace tut template<> template<> void object::test<10>() + { + set_test_name("attached=false"); + // almost just like autokill=false, except set autokill=true with + // attached=false. + NamedTempFile from("from", "not started"); + NamedTempFile to("to", ""); + LLProcess::handle phandle(0); + { + PythonProcessLauncher py(get_test_name(), + "from __future__ import with_statement\n" + "import sys, time\n" + "with open(sys.argv[1], 'w') as f:\n" + " f.write('ok')\n" + "# wait for 'go' from test program\n" + "for i in xrange(60):\n" + " time.sleep(1)\n" + " with open(sys.argv[2]) as f:\n" + " go = f.read()\n" + " if go == 'go':\n" + " break\n" + "else:\n" + " with open(sys.argv[1], 'w') as f:\n" + " f.write('never saw go')\n" + " sys.exit(1)\n" + "# okay, saw 'go', write 'ack'\n" + "with open(sys.argv[1], 'w') as f:\n" + " f.write('ack')\n"); + py.mParams.args.add(from.getName()); + py.mParams.args.add(to.getName()); + py.mParams.autokill = true; + py.mParams.attached = false; + py.launch(); + // Capture handle for later + phandle = py.mPy->getProcessHandle(); + // Wait for the script to wake up and do its first write + int i = 0, timeout = 60; + for ( ; i < timeout; ++i) + { + yield(); + if (readfile(from.getName(), "from autokill script") == "ok") + break; + } + // If we broke this loop because of the counter, something's wrong + ensure("script never started", i < timeout); + // Now destroy the LLProcess, which should NOT kill the child! + } + // If the destructor killed the child anyway, give it time to die + yield(2); + // How do we know it's not terminated? By making it respond to + // a specific stimulus in a specific way. + { + std::ofstream outf(to.getName().c_str()); + outf << "go"; + } // flush and close. + // now wait for the script to terminate... one way or another. + waitfor(phandle, "autokill script"); + // If the LLProcess destructor implicitly called kill(), the + // script could not have written 'ack' as we expect. + ensure_equals(get_test_name() + " script output", readfile(from.getName()), "ack"); + } + + template<> template<> + void object::test<11>() { set_test_name("'bogus' test"); CaptureLog recorder; @@ -801,7 +864,7 @@ namespace tut } template<> template<> - void object::test<11>() + void object::test<12>() { set_test_name("'file' test"); // Replace this test with one or more real 'file' tests when we @@ -815,7 +878,7 @@ namespace tut } template<> template<> - void object::test<12>() + void object::test<13>() { set_test_name("'tpipe' test"); // Replace this test with one or more real 'tpipe' tests when we @@ -832,7 +895,7 @@ namespace tut } template<> template<> - void object::test<13>() + void object::test<14>() { set_test_name("'npipe' test"); // Replace this test with one or more real 'npipe' tests when we @@ -850,7 +913,7 @@ namespace tut } template<> template<> - void object::test<14>() + void object::test<15>() { set_test_name("internal pipe name warning"); CaptureLog recorder; @@ -914,7 +977,7 @@ namespace tut } while (0) template<> template<> - void object::test<15>() + void object::test<16>() { set_test_name("get*Pipe() validation"); PythonProcessLauncher py(get_test_name(), @@ -934,7 +997,7 @@ namespace tut } template<> template<> - void object::test<16>() + void object::test<17>() { set_test_name("talk to stdin/stdout"); PythonProcessLauncher py(get_test_name(), @@ -992,7 +1055,7 @@ namespace tut } template<> template<> - void object::test<17>() + void object::test<18>() { set_test_name("listen for ReadPipe events"); PythonProcessLauncher py(get_test_name(), @@ -1052,7 +1115,7 @@ namespace tut } template<> template<> - void object::test<18>() + void object::test<19>() { set_test_name("ReadPipe \"eof\" event"); PythonProcessLauncher py(get_test_name(), @@ -1078,7 +1141,7 @@ namespace tut } template<> template<> - void object::test<19>() + void object::test<20>() { set_test_name("setLimit()"); PythonProcessLauncher py(get_test_name(), @@ -1107,7 +1170,7 @@ namespace tut } template<> template<> - void object::test<20>() + void object::test<21>() { set_test_name("peek() ReadPipe data"); PythonProcessLauncher py(get_test_name(), @@ -1160,7 +1223,7 @@ namespace tut } template<> template<> - void object::test<21>() + void object::test<22>() { set_test_name("bad postend"); std::string pumpname("postend"); @@ -1185,7 +1248,7 @@ namespace tut } template<> template<> - void object::test<22>() + void object::test<23>() { set_test_name("good postend"); PythonProcessLauncher py(get_test_name(), @@ -1241,7 +1304,7 @@ namespace tut }; template<> template<> - void object::test<23>() + void object::test<24>() { set_test_name("all data visible at postend"); PythonProcessLauncher py(get_test_name(), -- cgit v1.2.3 From d415e019a61cfd20bb1e254fbc9279f96047da85 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 24 Apr 2017 16:36:54 -0400 Subject: DRTVWR-418: Remove final shutdown cleanup as a cause of crashes. The recent LLSingleton work added a hook that would run during the C++ runtime's final destruction of static objects. When the LAST LLSingleton in any module was destroyed, its destructor would call LLSingletonBase::deleteAll(). That mechanism was intended to permit an application consuming LLSingletons to skip making an explicit deleteAll() call, knowing that all instantiated LLSingleton instances would eventually be cleaned up anyway. However -- experience proves that kicking off deleteAll() processing during the C++ runtime's final cleanup is too late. Too much has already been destroyed. That call tends to cause more shutdown crashes than it resolves. This commit deletes that whole mechanism. Going forward, if you want to clean up LLSingleton instances, you must explicitly call LLSingletonBase::deleteAll() during the application lifetime. If you don't, LLSingleton instances will simply be leaked -- which might be okay, considering the application is terminating anyway. --- indra/llcommon/llsingleton.cpp | 46 ++---------------------------------------- indra/llcommon/llsingleton.h | 30 ++++++++------------------- 2 files changed, 10 insertions(+), 66 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index 9025e53bb2..18e1b96a5f 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -369,62 +369,20 @@ void LLSingletonBase::deleteAll() } } -/*------------------------ Final cleanup management ------------------------*/ -class LLSingletonBase::MasterRefcount -{ -public: - // store a POD int so it will be statically initialized to 0 - int refcount; -}; -static LLSingletonBase::MasterRefcount sMasterRefcount; - -LLSingletonBase::ref_ptr_t LLSingletonBase::get_master_refcount() -{ - // Calling this method constructs a new ref_ptr_t, which implicitly calls - // intrusive_ptr_add_ref(MasterRefcount*). - return &sMasterRefcount; -} - -void intrusive_ptr_add_ref(LLSingletonBase::MasterRefcount* mrc) -{ - // Count outstanding SingletonLifetimeManager instances. - ++mrc->refcount; -} - -void intrusive_ptr_release(LLSingletonBase::MasterRefcount* mrc) -{ - // Notice when each SingletonLifetimeManager instance is destroyed. - if (! --mrc->refcount) - { - // The last instance was destroyed. Time to kill any remaining - // LLSingletons -- but in dependency order. - LLSingletonBase::deleteAll(); - } -} - /*---------------------------- Logging helpers -----------------------------*/ namespace { bool oktolog() { // See comments in log() below. - return sMasterRefcount.refcount && LLError::is_available(); + return LLError::is_available(); } void log(LLError::ELevel level, const char* p1, const char* p2, const char* p3, const char* p4) { - // Check whether we're in the implicit final LLSingletonBase::deleteAll() - // call. We've carefully arranged for deleteAll() to be called when the - // last SingletonLifetimeManager instance is destroyed -- in other words, - // when the last translation unit containing an LLSingleton instance - // cleans up static data. That could happen after std::cerr is destroyed! // The is_available() test below ensures that we'll stop logging once // LLError has been cleaned up. If we had a similar portable test for - // std::cerr, this would be a good place to use it. As we do not, just - // don't log anything during implicit final deleteAll(). Detect that by - // the master refcount having gone to zero. - if (sMasterRefcount.refcount == 0) - return; + // std::cerr, this would be a good place to use it. // Check LLError::is_available() because some of LLError's infrastructure // is itself an LLSingleton. If that LLSingleton has not yet been diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 0d4a1f34f8..859e271e26 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -36,8 +35,6 @@ class LLSingletonBase: private boost::noncopyable { public: class MasterList; - class MasterRefcount; - typedef boost::intrusive_ptr ref_ptr_t; private: // All existing LLSingleton instances are tracked in this master list. @@ -119,9 +116,6 @@ protected: const char* p3="", const char* p4=""); static std::string demangle(const char* mangled); - // obtain canonical ref_ptr_t - static ref_ptr_t get_master_refcount(); - // Default methods in case subclass doesn't declare them. virtual void initSingleton() {} virtual void cleanupSingleton() {} @@ -175,10 +169,6 @@ public: static void deleteAll(); }; -// support ref_ptr_t -void intrusive_ptr_add_ref(LLSingletonBase::MasterRefcount*); -void intrusive_ptr_release(LLSingletonBase::MasterRefcount*); - // Most of the time, we want LLSingleton_manage_master() to forward its // methods to real LLSingletonBase methods. template @@ -298,8 +288,7 @@ private: // stores pointer to singleton instance struct SingletonLifetimeManager { - SingletonLifetimeManager(): - mMasterRefcount(LLSingletonBase::get_master_refcount()) + SingletonLifetimeManager() { construct(); } @@ -317,17 +306,14 @@ private: // of static-object destruction, mean that we DO NOT WANT this // destructor to delete this LLSingleton. This destructor will run // without regard to any other LLSingleton whose cleanup might - // depend on its existence. What we really want is to count the - // runtime's attempts to cleanup LLSingleton static data -- and on - // the very last one, call LLSingletonBase::deleteAll(). That - // method will properly honor cross-LLSingleton dependencies. This - // is why we store an intrusive_ptr to a MasterRefcount: our - // ref_ptr_t member counts SingletonLifetimeManager instances. - // Once the runtime destroys the last of these, THEN we can delete - // every remaining LLSingleton. + // depend on its existence. If you want to clean up LLSingletons, + // call LLSingletonBase::deleteAll() sometime before static-object + // destruction begins. That method will properly honor cross- + // LLSingleton dependencies. Otherwise we simply leak LLSingleton + // instances at shutdown. Since the whole process is terminating + // anyway, that's not necessarily a bad thing; it depends on what + // resources your LLSingleton instances are managing. } - - LLSingletonBase::ref_ptr_t mMasterRefcount; }; protected: -- cgit v1.2.3 From 29dd5f0123a89f44688477d04a421b90d1efe635 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 27 Apr 2017 18:49:33 -0400 Subject: DRTVWR-418: Use (protected) LLSingleton to store "null instance" of LLSafeHandle's referenced type. Using LLSingleton gives us a well-defined time at which the "null instance" is deleted: LLSingletonBase::deleteAll(). --- indra/llcommon/llsafehandle.h | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsafehandle.h b/indra/llcommon/llsafehandle.h index af1c26dd4f..74d31297a0 100644 --- a/indra/llcommon/llsafehandle.h +++ b/indra/llcommon/llsafehandle.h @@ -27,6 +27,7 @@ #define LLSAFEHANDLE_H #include "llerror.h" // *TODO: consider eliminating this +#include "llsingleton.h" // Expands LLPointer to return a pointer to a special instance of class Type instead of NULL. // This is useful in instances where operations on NULL pointers are semantically safe and/or @@ -146,15 +147,24 @@ protected: } } - static Type* nonNull(Type* ptr) + // Define an LLSingleton whose sole purpose is to hold a "null instance" + // of the subject Type: the canonical instance to dereference if this + // LLSafeHandle actually holds a null pointer. We use LLSingleton + // specifically so that the "null instance" can be cleaned up at a well- + // defined time, specifically LLSingletonBase::deleteAll(). + // Of course, as with any LLSingleton, the "null instance" is only + // instantiated on demand -- in this case, if you actually try to + // dereference an LLSafeHandle containing null. + class NullInstanceHolder: public LLSingleton { - return ptr == NULL ? sNullFunc() : ptr; - } + LLSINGLETON_EMPTY_CTOR(NullInstanceHolder); + public: + Type mNullInstance; + }; - static Type* sNullFunc() + static Type* nonNull(Type* ptr) { - static Type sInstance; - return &sInstance; + return ptr? ptr : &NullInstanceHolder::instance().mNullInstance; } protected: -- cgit v1.2.3 From 52899ed62a241d7875277b0f3412e2be78f7b3af Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 May 2017 10:51:18 -0400 Subject: DRTVWR-418, MAINT-6996: Rationalize LLMemory wrt 64-bit support. There were two distinct LLMemory methods getCurrentRSS() and getWorkingSetSize(). It was pointless to have both: on Windows they were completely redundant; on other platforms getWorkingSetSize() always returned 0. (Amusingly, though the Windows implementations both made exactly the same GetProcessMemoryInfo() call and used exactly the same logic, the code was different in the two -- as though the second was implemented without awareness of the first, even though they were adjacent in the source file.) One of the actual MAINT-6996 problems was due to the fact that getWorkingSetSize() returned U32, where getCurrentRSS() returns U64. In other words, getWorkingSetSize() was both useless *and* wrong. Remove it, and change its one call to getCurrentRSS() instead. The other culprit was that in several places, the 64-bit WorkingSetSize returned by the Windows GetProcessMemoryInfo() call (and by getCurrentRSS()) was explicitly cast to a 32-bit data type. That works only when explicitly or implicitly (using LLUnits type conversion) scaling the value to kilobytes or megabytes. When the size in bytes is desired, use 64-bit types instead. In addition to the symptoms, LLMemory was overdue for a bit of cleanup. There was a 16K block of memory called reserveMem, the comment on which read: "reserve 16K for out of memory error handling." Yet *nothing* was ever done with that block! If it were going to be useful, one would think someone would at some point explicitly free the block. In fact there was a method freeReserve(), apparently for just that purpose -- which was never called. As things stood, reserveMem served only to *prevent* the viewer from ever using that chunk of memory. Remove reserveMem and the unused freeReserve(). The only function of initClass() and cleanupClass() was to allocate and free reserveMem. Remove initClass(), cleanupClass() and the LLCommon calls to them. In a similar vein, there was an LLMemoryInfo::getPhysicalMemoryClamped() method that returned U32Bytes. Its job was simply to return a size in bytes that could fit into a U32 data type, returning U32_MAX if the 64-bit value exceeded 4GB. Eliminate that; change all its calls to getPhysicalMemoryKB() (which getPhysicalMemoryClamped() used internally anyway). We no longer care about any platform that cannot handle 64-bit data types. --- indra/llcommon/llcommon.cpp | 2 - indra/llcommon/llmemory.cpp | 89 ++++++++------------------------------------- indra/llcommon/llmemory.h | 5 --- indra/llcommon/llsys.cpp | 16 -------- indra/llcommon/llsys.h | 5 --- 5 files changed, 15 insertions(+), 102 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp index 439ff4e628..2d665c611b 100644 --- a/indra/llcommon/llcommon.cpp +++ b/indra/llcommon/llcommon.cpp @@ -41,7 +41,6 @@ static LLTrace::ThreadRecorder* sMasterThreadRecorder = NULL; //static void LLCommon::initClass() { - LLMemory::initClass(); if (!sAprInitialized) { ll_init_apr(); @@ -70,5 +69,4 @@ void LLCommon::cleanupClass() ll_cleanup_apr(); sAprInitialized = FALSE; } - SUBSYSTEM_CLEANUP(LLMemory); } diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index 1e04044269..6a7e1362f0 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -44,10 +44,10 @@ #include "llsys.h" #include "llframetimer.h" #include "lltrace.h" +#include "llerror.h" //---------------------------------------------------------------------------- //static -char* LLMemory::reserveMem = 0; U32Kilobytes LLMemory::sAvailPhysicalMemInKB(U32_MAX); U32Kilobytes LLMemory::sMaxPhysicalMemInKB(0); static LLTrace::SampleStatHandle sAllocatedMem("allocated_mem", "active memory in use by application"); @@ -78,29 +78,6 @@ void ll_assert_aligned_func(uintptr_t ptr,U32 alignment) #endif } -//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 void LLMemory::initMaxHeapSizeGB(F32Gigabytes max_heap_size, BOOL prevent_heap_failure) { @@ -111,19 +88,18 @@ void LLMemory::initMaxHeapSizeGB(F32Gigabytes max_heap_size, BOOL prevent_heap_f //static void LLMemory::updateMemoryInfo() { -#if LL_WINDOWS - HANDLE self = GetCurrentProcess(); +#if LL_WINDOWS PROCESS_MEMORY_COUNTERS counters; - - if (!GetProcessMemoryInfo(self, &counters, sizeof(counters))) + + if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters))) { LL_WARNS() << "GetProcessMemoryInfo failed" << LL_ENDL; return ; } - sAllocatedMemInKB = (U32Bytes)(counters.WorkingSetSize) ; + sAllocatedMemInKB = U64Bytes(counters.WorkingSetSize) ; sample(sAllocatedMem, sAllocatedMemInKB); - sAllocatedPageSizeInKB = (U32Bytes)(counters.PagefileUsage) ; + sAllocatedPageSizeInKB = U64Bytes(counters.PagefileUsage) ; sample(sVirtualMem, sAllocatedPageSizeInKB); U32Kilobytes avail_phys, avail_virtual; @@ -140,9 +116,9 @@ void LLMemory::updateMemoryInfo() } #else //not valid for other systems for now. - sAllocatedMemInKB = (U32Bytes)LLMemory::getCurrentRSS(); - sMaxPhysicalMemInKB = (U32Bytes)U32_MAX ; - sAvailPhysicalMemInKB = (U32Bytes)U32_MAX ; + sAllocatedMemInKB = U64Bytes(LLMemory::getCurrentRSS()); + sMaxPhysicalMemInKB = U64Bytes(U32_MAX); + sAvailPhysicalMemInKB = U64Bytes(U32_MAX); #endif return ; @@ -169,7 +145,7 @@ void* LLMemory::tryToAlloc(void* address, U32 size) return address ; #else return (void*)0x01 ; //skip checking -#endif +#endif } //static @@ -183,7 +159,7 @@ void LLMemory::logMemoryInfo(BOOL update) LL_INFOS() << "Current allocated physical memory(KB): " << sAllocatedMemInKB << LL_ENDL ; LL_INFOS() << "Current allocated page size (KB): " << sAllocatedPageSizeInKB << LL_ENDL ; - LL_INFOS() << "Current availabe physical memory(KB): " << sAvailPhysicalMemInKB << LL_ENDL ; + LL_INFOS() << "Current available physical memory(KB): " << sAvailPhysicalMemInKB << LL_ENDL ; LL_INFOS() << "Current max usable memory(KB): " << sMaxPhysicalMemInKB << LL_ENDL ; LL_INFOS() << "--- private pool information -- " << LL_ENDL ; @@ -263,12 +239,12 @@ U32Kilobytes LLMemory::getAllocatedMemKB() #if defined(LL_WINDOWS) +//static U64 LLMemory::getCurrentRSS() { - HANDLE self = GetCurrentProcess(); PROCESS_MEMORY_COUNTERS counters; - - if (!GetProcessMemoryInfo(self, &counters, sizeof(counters))) + + if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters))) { LL_WARNS() << "GetProcessMemoryInfo failed" << LL_ENDL; return 0; @@ -277,20 +253,6 @@ U64 LLMemory::getCurrentRSS() 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) /* @@ -337,11 +299,6 @@ U64 LLMemory::getCurrentRSS() return residentSize; } -U32 LLMemory::getWorkingSetSize() -{ - return 0 ; -} - #elif defined(LL_LINUX) U64 LLMemory::getCurrentRSS() @@ -353,7 +310,7 @@ U64 LLMemory::getCurrentRSS() if (fp == NULL) { LL_WARNS() << "couldn't open " << statPath << LL_ENDL; - goto bail; + return 0; } // Eee-yew! See Documentation/filesystems/proc.txt in your @@ -372,15 +329,9 @@ U64 LLMemory::getCurrentRSS() fclose(fp); -bail: return rss; } -U32 LLMemory::getWorkingSetSize() -{ - return 0 ; -} - #elif LL_SOLARIS #include #include @@ -410,11 +361,6 @@ U64 LLMemory::getCurrentRSS() return((U64)proc_psinfo.pr_rssize * 1024); } -U32 LLMemory::getWorkingSetSize() -{ - return 0 ; -} - #else U64 LLMemory::getCurrentRSS() @@ -422,11 +368,6 @@ U64 LLMemory::getCurrentRSS() return 0; } -U32 LLMemory::getWorkingSetSize() -{ - return 0; -} - #endif //-------------------------------------------------------------------------------------------------- diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 5a3c9bd762..c37967e10e 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -334,13 +334,9 @@ inline void ll_memcpy_nonaliased_aligned_16(char* __restrict dst, const char* __ class LL_COMMON_API LLMemory { public: - static void initClass(); - static void cleanupClass(); - static void freeReserve(); // Return the resident set size of the current process, in bytes. // Return value is zero if not known. static U64 getCurrentRSS(); - static U32 getWorkingSetSize(); static void* tryToAlloc(void* address, U32 size); static void initMaxHeapSizeGB(F32Gigabytes max_heap_size, BOOL prevent_heap_failure); static void updateMemoryInfo() ; @@ -351,7 +347,6 @@ public: static U32Kilobytes getMaxMemKB() ; static U32Kilobytes getAllocatedMemKB() ; private: - static char* reserveMem; static U32Kilobytes sAvailPhysicalMemInKB ; static U32Kilobytes sMaxPhysicalMemInKB ; static U32Kilobytes sAllocatedMemInKB; diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 1a66612e87..265c637b69 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -914,22 +914,6 @@ U32Kilobytes LLMemoryInfo::getPhysicalMemoryKB() const #endif } -U32Bytes LLMemoryInfo::getPhysicalMemoryClamped() const -{ - // Return the total physical memory in bytes, but clamp it - // to no more than U32_MAX - - U32Kilobytes phys_kb = getPhysicalMemoryKB(); - if (phys_kb >= U32Gigabytes(4)) - { - return U32Bytes(U32_MAX); - } - else - { - return phys_kb; - } -} - //static void LLMemoryInfo::getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb) { diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 962367f69f..95ef4cf065 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -113,11 +113,6 @@ public: void stream(std::ostream& s) const; ///< output text info to s U32Kilobytes getPhysicalMemoryKB() const; - - /*! Memory size in bytes, if total memory is >= 4GB then U32_MAX will - ** be returned. - */ - U32Bytes getPhysicalMemoryClamped() const; ///< Memory size in clamped bytes //get the available memory infomation in KiloBytes. static void getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb); -- cgit v1.2.3 From 2bb19aec989d5964b22f64cc01aa7cbe962e1e6b Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 May 2017 11:05:13 -0400 Subject: DRTVWR-418, MAINT-6996: Update Mac LLMemory::getCurrentRSS(). Evidently the Mac implementation of LLMemory::getCurrentRSS() goes back to OS X 10.3, because there was a helpful comment of the form: ------ 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: [new current implementation] 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. ------ Hey, guess what, we're building 64-bit viewers now! Thank you, whoever thoughtfully noted that, both for calling out the issue and sparing us the research. (The comment goes back to Subversion days, so hg blame shows only the merge-to-release changeset.) --- indra/llcommon/llmemory.cpp | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index 6a7e1362f0..9f9c3af892 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -255,19 +255,6 @@ U64 LLMemory::getCurrentRSS() #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) // { // LL_WARNS() << "Couldn't get page size" << LL_ENDL; @@ -280,9 +267,9 @@ U64 LLMemory::getCurrentRSS() 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) + 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) { residentSize = basicInfo.resident_size; -- cgit v1.2.3 From 9002e3cbc7a4f7ef2847ab6652de0f11eaff6ef2 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 3 May 2017 13:20:56 -0400 Subject: DRTVWR-418: Add big deprecation notice to llsafehandle.h. --- indra/llcommon/llsafehandle.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsafehandle.h b/indra/llcommon/llsafehandle.h index 74d31297a0..968a5cfeb1 100644 --- a/indra/llcommon/llsafehandle.h +++ b/indra/llcommon/llsafehandle.h @@ -29,6 +29,29 @@ #include "llerror.h" // *TODO: consider eliminating this #include "llsingleton.h" +/*==========================================================================*| + ____ ___ _ _ ___ _____ _ _ ____ _____ _ +| _ \ / _ \ | \ | |/ _ \_ _| | | | / ___|| ____| | +| | | | | | | | \| | | | || | | | | \___ \| _| | | +| |_| | |_| | | |\ | |_| || | | |_| |___) | |___|_| +|____/ \___/ |_| \_|\___/ |_| \___/|____/|_____(_) + +This handle class is deprecated. Unfortunately it is already in widespread use +to reference the LLObjectSelection and LLParcelSelection classes, but do not +apply LLSafeHandle to other classes, or declare new instances. + +Instead, use LLPointer or other smart pointer types with appropriate checks +for NULL. If you're certain the reference cannot (or must not) be NULL, +consider storing a C++ reference instead -- or use (e.g.) LLCheckedHandle. + +When an LLSafeHandle containing NULL is dereferenced, it resolves to a +canonical "null" T instance. This raises issues about the lifespan of the +"null" instance. In addition to encouraging sloppy coding practices, it +potentially masks bugs when code that performs some mutating operation +inadvertently applies it to the "null" instance. That result might or might +not ever affect subsequent computations. +|*==========================================================================*/ + // Expands LLPointer to return a pointer to a special instance of class Type instead of NULL. // This is useful in instances where operations on NULL pointers are semantically safe and/or // when error checking occurs at a different granularity or in a different part of the code -- cgit v1.2.3 From d6b870c0cae04a9a2576c8849ebe03822eb78e6c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 3 May 2017 22:53:34 -0400 Subject: DRTVWR-418: Add dtor to LLSafeHandle::NullInstanceHolder to suppress fatal warnings in Visual Studio. --- indra/llcommon/llsafehandle.h | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsafehandle.h b/indra/llcommon/llsafehandle.h index 968a5cfeb1..9550e6253e 100644 --- a/indra/llcommon/llsafehandle.h +++ b/indra/llcommon/llsafehandle.h @@ -181,6 +181,7 @@ protected: class NullInstanceHolder: public LLSingleton { LLSINGLETON_EMPTY_CTOR(NullInstanceHolder); + ~NullInstanceHolder() {} public: Type mNullInstance; }; -- cgit v1.2.3 From 49cfd6d9917c7ba35869e33785cbacba7c5e2d98 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 4 May 2017 18:13:56 -0400 Subject: DRTVWR-418, MAINT-6996: On Mac, obtain total mem, not resident mem. The LLMemory method getCurrentRSS() is defined to return the "resident set size," but in fact on Windows it returns the WorkingSetSize -- and that's actually what callers want from it: the total memory consumed by the application for statistics purposes. It's not really clear what users gain by knowing how much of that is resident in real memory, versus the total consumption. So despite the commentation and the method name itself, on Mac make it return the virtual size consumed. --- indra/llcommon/llmemory.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index 9f9c3af892..aac6c26d9a 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -271,12 +271,11 @@ U64 LLMemory::getCurrentRSS() 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) { - residentSize = basicInfo.resident_size; - - // If we ever wanted it, the process virtual size is also available as: - // virtualSize = basicInfo.virtual_size; - -// LL_INFOS() << "resident size is " << residentSize << LL_ENDL; +// residentSize = basicInfo.resident_size; + // Although this method is defined to return the "resident set size," + // in fact what callers want from it is the total virtual memory + // consumed by the application. + residentSize = basicInfo.virtual_size; } else { -- cgit v1.2.3 From 322c4c6bec54b4968d0105cf1bb28bb62c6dfcbc Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 8 May 2017 09:09:22 -0400 Subject: DRTVWR-418: Fix -std=c++11 llinstancetracker_test crash. LLInstanceTracker performs validation in ~LLInstanceTracker(). Normally validation failure logs an error and terminates the program, which is fine. In the test executable, though, we want validation failure to throw an exception instead so we can catch it and continue testing other failure conditions. But since destructors in C++11 are implicitly noexcept(true), that exception never made it out of ~LLInstanceTracker(): it crashed the test program instead. Declaring ~LLInstanceTracker() noexcept(false) solves that, allowing the test program to catch the exception and continue. However, if we unconditionally declare that, then every destructor anywhere in the inheritance hierarchy for any LLInstanceTracker subclass must also be noexcept(false)! That's way too pervasive, especially for functionality we only need (or want) in a specific test executable. Instead, make the CMake macros LL_ADD_PROJECT_UNIT_TESTS() and LL_ADD_INTEGRATION_TEST() -- with which we define all viewer build-time tests -- define two new command-line macros: LL_TEST=testname and LL_TEST_testname. That way, preprocessor logic in a header file can detect whether it's being compiled for production code or for a test executable. (While at it, encapsulate in a new GET_OPT_SOURCE_FILE_PROPERTY() CMake macro an ugly repetitive pattern. The builtin GET_SOURCE_FILE_PROPERTY() sets the target variable to "NOTFOUND" -- rather than an empty string -- if the specified property wasn't set. Every call to GET_SOURCE_FILE_PROPERTY() in LL_ADD_PROJECT_UNIT_TESTS() was followed by a test for NOTFOUND and an assignment to "". Wrap all that in a macro whose 'unset' value is "".) Now llinstancetracker.h can detect when we're building the LLInstanceTracker unit test executable, and *only then* declare ~LLInstanceTracker() as noexcept(false). We #define LLINSTANCETRACKER_DTOR_NOEXCEPT to expand either empty or noexcept(false), also detecting clang in C++11 mode. (It all works fine without noexcept(false) until we turn on C++11 mode.) We also use that macro for the StatBase class in lltrace.h. Turns out some of the infrastructure headers required for tests in general, including the LLInstanceTracker test, use LLInstanceTracker. Fortunately that appears to be the only other class we must annotate this way for the LLInstanceTracker tests. --- indra/llcommon/llinstancetracker.h | 24 +++++++++++++++++++++--- indra/llcommon/lltrace.h | 2 +- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 9783644e66..69c712b656 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -35,6 +35,24 @@ #include #include +// As of 2017-05-06, as far as nat knows, only clang supports __has_feature(). +#if defined(LL_TEST_llinstancetracker) && defined(__clang__) && __has_feature(cxx_noexcept) +// ~LLInstanceTracker() performs llassert_always() validation. That's fine in +// production code, since the llassert_always() is implemented as an LL_ERRS +// message, which will crash-with-message. In our integration test executable, +// though, this llassert_always() throws an exception instead so we can test +// error conditions and continue running the test. However -- as of C++11, +// destructors are implicitly noexcept(true). Unless we mark +// ~LLInstanceTracker() noexcept(false), the test executable crashes even on +// the ATTEMPT to throw. +#define LLINSTANCETRACKER_DTOR_NOEXCEPT noexcept(false) +#else +// If we're building for production, or in fact building *any other* test, or +// we're using a compiler that doesn't support __has_feature(), or we're not +// compiling with a C++ version that supports noexcept -- don't specify it. +#define LLINSTANCETRACKER_DTOR_NOEXCEPT +#endif + /** * Base class manages "class-static" data that must actually have singleton * semantics: one instance per process, rather than one instance per module as @@ -198,11 +216,11 @@ protected: getStatic(); add_(key); } - virtual ~LLInstanceTracker() + virtual ~LLInstanceTracker() LLINSTANCETRACKER_DTOR_NOEXCEPT { // it's unsafe to delete instances of this type while all instances are being iterated over. llassert_always(getStatic().getDepth() == 0); - remove_(); + remove_(); } virtual void setKey(KEY key) { remove_(); add_(key); } virtual const KEY& getKey() const { return mInstanceKey; } @@ -335,7 +353,7 @@ protected: getStatic(); getSet_().insert(static_cast(this)); } - virtual ~LLInstanceTracker() + virtual ~LLInstanceTracker() LLINSTANCETRACKER_DTOR_NOEXCEPT { // it's unsafe to delete instances of this type while all instances are being iterated over. llassert_always(getStatic().getDepth() == 0); diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h index 5f1289dad8..79ff55b739 100644 --- a/indra/llcommon/lltrace.h +++ b/indra/llcommon/lltrace.h @@ -57,7 +57,7 @@ class StatBase { public: StatBase(const char* name, const char* description); - virtual ~StatBase() {}; + virtual ~StatBase() LLINSTANCETRACKER_DTOR_NOEXCEPT {} virtual const char* getUnitLabel() const; const std::string& getName() const { return mName; } -- cgit v1.2.3 From 8502b7dc474094c807e49f2896ab6ff7622f96bb Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 8 May 2017 12:33:33 -0400 Subject: DRTVWR-418: Work around VS2013's lack of __has_feature(). --- indra/llcommon/llinstancetracker.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 69c712b656..910c8dbd99 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -36,7 +36,14 @@ #include // As of 2017-05-06, as far as nat knows, only clang supports __has_feature(). -#if defined(LL_TEST_llinstancetracker) && defined(__clang__) && __has_feature(cxx_noexcept) +// Unfortunately VS2013's preprocessor shortcut logic doesn't prevent it from +// producing (fatal) warnings for defined(__clang__) && __has_feature(...). +// Have to work around that. +#if ! defined(__clang__) +#define __has_feature(x) 0 +#endif // __clang__ + +#if defined(LL_TEST_llinstancetracker) && __has_feature(cxx_noexcept) // ~LLInstanceTracker() performs llassert_always() validation. That's fine in // production code, since the llassert_always() is implemented as an LL_ERRS // message, which will crash-with-message. In our integration test executable, -- cgit v1.2.3 From 9fa131b088aeb17f3af9157184264ffa5c737610 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 10 May 2017 14:19:44 -0400 Subject: DRTVWR-418, MAINT-6996: Update Mac mem queries (per Drake Arconis) Drake points out that the OS X 64-bit-capable memory-query APIs recommended in comments by some long-ago maintainer are by now themselves obsolete. He offered this patch to update us to current macOS memory APIs. --- indra/llcommon/llmemory.cpp | 6 +++--- indra/llcommon/llsys.cpp | 32 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index aac6c26d9a..049e962638 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -267,9 +267,9 @@ U64 LLMemory::getCurrentRSS() U64 LLMemory::getCurrentRSS() { U64 residentSize = 0; - 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) + mach_task_basic_info_data_t basicInfo; + mach_msg_type_number_t basicInfoCount = MACH_TASK_BASIC_INFO_COUNT; + if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS) { // residentSize = basicInfo.resident_size; // Although this method is defined to return the "resident set size," diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 265c637b69..fd1828b1cc 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -1128,10 +1128,10 @@ LLSD LLMemoryInfo::loadStatsMap() // { - vm_statistics_data_t vmstat; - mach_msg_type_number_t vmstatCount = HOST_VM_INFO_COUNT; + vm_statistics64_data_t vmstat; + mach_msg_type_number_t vmstatCount = HOST_VM_INFO64_COUNT; - if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t) &vmstat, &vmstatCount) != KERN_SUCCESS) + if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t) &vmstat, &vmstatCount) != KERN_SUCCESS) { LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL; } @@ -1189,20 +1189,20 @@ LLSD LLMemoryInfo::loadStatsMap() // { - task_basic_info_64_data_t taskinfo; - unsigned taskinfoSize = sizeof(taskinfo); - - if (task_info(mach_task_self(), TASK_BASIC_INFO_64, (task_info_t) &taskinfo, &taskinfoSize) != KERN_SUCCESS) + mach_task_basic_info_data_t taskinfo; + mach_msg_type_number_t task_count = MACH_TASK_BASIC_INFO_COUNT; + if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t) &taskinfo, &task_count) != KERN_SUCCESS) { - LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL; - } - else - { - stats.add("Basic suspend count", taskinfo.suspend_count); - stats.add("Basic virtual memory KB", taskinfo.virtual_size / 1024); - stats.add("Basic resident memory KB", taskinfo.resident_size / 1024); - stats.add("Basic new thread policy", taskinfo.policy); - } + LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL; + } + else + { + stats.add("Basic virtual memory KB", taskinfo.virtual_size / 1024); + stats.add("Basic resident memory KB", taskinfo.resident_size / 1024); + stats.add("Basic max resident memory KB", taskinfo.resident_size_max / 1024); + stats.add("Basic new thread policy", taskinfo.policy); + stats.add("Basic suspend count", taskinfo.suspend_count); + } } #elif LL_SOLARIS -- cgit v1.2.3 From b0d8f3a1ab671668abb98fe1327f0fd73fca0fc7 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Mon, 10 Jul 2017 16:30:28 -0400 Subject: MAINT-4532: properly detect Windows 10 in the 64bit build (only - 32bit runs in Windows 8 compatibility mode) --- indra/llcommon/llsys.cpp | 377 ++++++++++++++--------------------------------- 1 file changed, 112 insertions(+), 265 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index fd1828b1cc..1ef6c538ba 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -61,6 +61,7 @@ using namespace llsd; #if LL_WINDOWS # include "llwin32headerslean.h" # include // GetPerformanceInfo() et al. +# include #elif LL_DARWIN # include # include @@ -110,78 +111,6 @@ static const F32 MEM_INFO_THROTTLE = 20; // dropped below the login framerate, we'd have very little additional data. static const F32 MEM_INFO_WINDOW = 10*60; -#if LL_WINDOWS -// We cannot trust GetVersionEx function on Win8.1 , we should check this value when creating OS string -static const U32 WINNT_WINBLUE = 0x0603; - -#ifndef DLLVERSIONINFO -typedef struct _DllVersionInfo -{ - DWORD cbSize; - DWORD dwMajorVersion; - DWORD dwMinorVersion; - DWORD dwBuildNumber; - DWORD dwPlatformID; -}DLLVERSIONINFO; -#endif - -#ifndef DLLGETVERSIONPROC -typedef int (FAR WINAPI *DLLGETVERSIONPROC) (DLLVERSIONINFO *); -#endif - -bool get_shell32_dll_version(DWORD& major, DWORD& minor, DWORD& build_number) -{ - bool result = false; - const U32 BUFF_SIZE = 32767; - WCHAR tempBuf[BUFF_SIZE]; - if(GetSystemDirectory((LPWSTR)&tempBuf, BUFF_SIZE)) - { - - std::basic_string shell32_path(tempBuf); - - // Shell32.dll contains the DLLGetVersion function. - // according to msdn its not part of the API - // so you have to go in and get it. - // http://msdn.microsoft.com/en-us/library/bb776404(VS.85).aspx - shell32_path += TEXT("\\shell32.dll"); - - HMODULE hDllInst = LoadLibrary(shell32_path.c_str()); //load the DLL - if(hDllInst) - { // Could successfully load the DLL - DLLGETVERSIONPROC pDllGetVersion; - /* - You must get this function explicitly because earlier versions of the DLL - don't implement this function. That makes the lack of implementation of the - function a version marker in itself. - */ - pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hDllInst, - "DllGetVersion"); - - if(pDllGetVersion) - { - // DLL supports version retrieval function - DLLVERSIONINFO dvi; - - ZeroMemory(&dvi, sizeof(dvi)); - dvi.cbSize = sizeof(dvi); - HRESULT hr = (*pDllGetVersion)(&dvi); - - if(SUCCEEDED(hr)) - { // Finally, the version is at our hands - major = dvi.dwMajorVersion; - minor = dvi.dwMinorVersion; - build_number = dvi.dwBuildNumber; - result = true; - } - } - - FreeLibrary(hDllInst); // Release DLL - } - } - return result; -} -#endif // LL_WINDOWS - // Wrap boost::regex_match() with a function that doesn't throw. template static bool regex_match_no_exc(const S& string, M& match, const R& regex) @@ -214,221 +143,139 @@ static bool regex_search_no_exc(const S& string, M& match, const R& regex) } } -#if LL_WINDOWS -// GetVersionEx should not works correct with Windows 8.1 and the later version. We need to check this case -static bool check_for_version(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) -{ - OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 }; - DWORDLONG const dwlConditionMask = VerSetConditionMask( - VerSetConditionMask( - VerSetConditionMask( - 0, VER_MAJORVERSION, VER_GREATER_EQUAL), - VER_MINORVERSION, VER_GREATER_EQUAL), - VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); - - osvi.dwMajorVersion = wMajorVersion; - osvi.dwMinorVersion = wMinorVersion; - osvi.wServicePackMajor = wServicePackMajor; - - return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; -} -#endif - LLOSInfo::LLOSInfo() : mMajorVer(0), mMinorVer(0), mBuild(0), mOSVersionString("") { #if LL_WINDOWS - OSVERSIONINFOEX osvi; - BOOL bOsVersionInfoEx; - BOOL bShouldUseShellVersion = false; - // Try calling GetVersionEx using the OSVERSIONINFOEX structure. - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - if(!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi))) + if (IsWindowsVersionOrGreater(10, 0, 0)) { - // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. - osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); - if(!GetVersionEx( (OSVERSIONINFO *) &osvi)) - return; + mMajorVer = 10; + mMinorVer = 0; + mOSStringSimple = "Microsoft Windows 10 "; } - mMajorVer = osvi.dwMajorVersion; - mMinorVer = osvi.dwMinorVersion; - mBuild = osvi.dwBuildNumber; - - DWORD shell32_major, shell32_minor, shell32_build; - bool got_shell32_version = get_shell32_dll_version(shell32_major, - shell32_minor, - shell32_build); - - switch(osvi.dwPlatformId) + else if (IsWindows8Point1OrGreater()) { - case VER_PLATFORM_WIN32_NT: + mMajorVer = 6; + mMinorVer = 3; + if (IsWindowsServer()) { - // Test for the product. - if(osvi.dwMajorVersion <= 4) - { - mOSStringSimple = "Microsoft Windows NT "; - } - else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) - { - mOSStringSimple = "Microsoft Windows 2000 "; - } - else if(osvi.dwMajorVersion ==5 && osvi.dwMinorVersion == 1) - { - mOSStringSimple = "Microsoft Windows XP "; - } - else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) - { - if(osvi.wProductType == VER_NT_WORKSTATION) - mOSStringSimple = "Microsoft Windows XP x64 Edition "; - else - mOSStringSimple = "Microsoft Windows Server 2003 "; - } - else if(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion <= 2) - { - if(osvi.dwMinorVersion == 0) - { - if(osvi.wProductType == VER_NT_WORKSTATION) - mOSStringSimple = "Microsoft Windows Vista "; - else - mOSStringSimple = "Windows Server 2008 "; - } - else if(osvi.dwMinorVersion == 1) - { - if(osvi.wProductType == VER_NT_WORKSTATION) - mOSStringSimple = "Microsoft Windows 7 "; - else - mOSStringSimple = "Windows Server 2008 R2 "; - } - else if(osvi.dwMinorVersion == 2) - { - if (check_for_version(HIBYTE(WINNT_WINBLUE), LOBYTE(WINNT_WINBLUE), 0)) - { - mOSStringSimple = "Microsoft Windows 8.1 "; - bShouldUseShellVersion = true; // GetVersionEx failed, going to use shell version - } - else - { - if(osvi.wProductType == VER_NT_WORKSTATION) - mOSStringSimple = "Microsoft Windows 8 "; - else - mOSStringSimple = "Windows Server 2012 "; - } - } - - ///get native system info if available.. - typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); ///function pointer for loading GetNativeSystemInfo - SYSTEM_INFO si; //System Info object file contains architecture info - PGNSI pGNSI; //pointer object - ZeroMemory(&si, sizeof(SYSTEM_INFO)); //zero out the memory in information - pGNSI = (PGNSI) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); //load kernel32 get function - if(NULL != pGNSI) //check if it has failed - pGNSI(&si); //success - else - GetSystemInfo(&si); //if it fails get regular system info - //(Warning: If GetSystemInfo it may result in incorrect information in a WOW64 machine, if the kernel fails to load) - - //msdn microsoft finds 32 bit and 64 bit flavors this way.. - //http://msdn.microsoft.com/en-us/library/ms724429(VS.85).aspx (example code that contains quite a few more flavors - //of windows than this code does (in case it is needed for the future) - if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) //check for 64 bit - { - mOSStringSimple += "64-bit "; - } - else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL ) - { - mOSStringSimple += "32-bit "; - } - } - else // Use the registry on early versions of Windows NT. - { - mOSStringSimple = "Microsoft Windows (unrecognized) "; - - HKEY hKey; - WCHAR szProductType[80]; - DWORD dwBufLen; - RegOpenKeyEx( HKEY_LOCAL_MACHINE, - L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions", - 0, KEY_QUERY_VALUE, &hKey ); - RegQueryValueEx( hKey, L"ProductType", NULL, NULL, - (LPBYTE) szProductType, &dwBufLen); - RegCloseKey( hKey ); - if ( lstrcmpi( L"WINNT", szProductType) == 0 ) - { - mOSStringSimple += "Professional "; - } - else if ( lstrcmpi( L"LANMANNT", szProductType) == 0 ) - { - mOSStringSimple += "Server "; - } - else if ( lstrcmpi( L"SERVERNT", szProductType) == 0 ) - { - mOSStringSimple += "Advanced Server "; - } - } - - std::string csdversion = utf16str_to_utf8str(osvi.szCSDVersion); - // Display version, service pack (if any), and build number. - std::string tmpstr; - if(osvi.dwMajorVersion <= 4) - { - tmpstr = llformat("version %d.%d %s (Build %d)", - osvi.dwMajorVersion, - osvi.dwMinorVersion, - csdversion.c_str(), - (osvi.dwBuildNumber & 0xffff)); - } - else - { - tmpstr = !bShouldUseShellVersion ? llformat("%s (Build %d)", csdversion.c_str(), (osvi.dwBuildNumber & 0xffff)): - llformat("%s (Build %d)", csdversion.c_str(), shell32_build); - } - - mOSString = mOSStringSimple + tmpstr; + mOSStringSimple = "Windows Server 2012 R2 "; } - break; - - case VER_PLATFORM_WIN32_WINDOWS: - // Test for the Windows 95 product family. - if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) + else { - mOSStringSimple = "Microsoft Windows 95 "; - if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' ) - { - mOSStringSimple += "OSR2 "; - } - } - if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) + mOSStringSimple = "Microsoft Windows 8.1 "; + } + } + else if (IsWindows8OrGreater()) + { + mMajorVer = 6; + mMinorVer = 2; + if (IsWindowsServer()) { - mOSStringSimple = "Microsoft Windows 98 "; - if ( osvi.szCSDVersion[1] == 'A' ) - { - mOSStringSimple += "SE "; - } - } - if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) + mOSStringSimple = "Windows Server 2012 "; + } + else { - mOSStringSimple = "Microsoft Windows Millennium Edition "; + mOSStringSimple = "Microsoft Windows 8 "; + } + } + else if (IsWindows7SP1OrGreater()) + { + mMajorVer = 6; + mMinorVer = 1; + if (IsWindowsServer()) + { + mOSStringSimple = "Windows Server 2008 R2 SP1 "; + } + else + { + mOSStringSimple = "Microsoft Windows 7 SP1 "; + } + } + else if (IsWindows7OrGreater()) + { + mMajorVer = 6; + mMinorVer = 1; + if (IsWindowsServer()) + { + mOSStringSimple = "Windows Server 2008 R2 "; + } + else + { + mOSStringSimple = "Microsoft Windows 7 "; } - mOSString = mOSStringSimple; - break; } + else if (IsWindowsVistaSP2OrGreater()) + { + mMajorVer = 6; + mMinorVer = 0; + if (IsWindowsServer()) + { + mOSStringSimple = "Windows Server 2008 SP2 "; + } + else + { + mOSStringSimple = "Microsoft Windows Vista SP2 "; + } + } + else + { + mOSStringSimple = "Unsupported Windows version "; + } + + ///get native system info if available.. + typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); ///function pointer for loading GetNativeSystemInfo + SYSTEM_INFO si; //System Info object file contains architecture info + PGNSI pGNSI; //pointer object + ZeroMemory(&si, sizeof(SYSTEM_INFO)); //zero out the memory in information + pGNSI = (PGNSI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); //load kernel32 get function + if (NULL != pGNSI) //check if it has failed + pGNSI(&si); //success + else + GetSystemInfo(&si); //if it fails get regular system info + //(Warning: If GetSystemInfo it may result in incorrect information in a WOW64 machine, if the kernel fails to load) - std::string compatibility_mode; - if(got_shell32_version) + //msdn microsoft finds 32 bit and 64 bit flavors this way.. + //http://msdn.microsoft.com/en-us/library/ms724429(VS.85).aspx (example code that contains quite a few more flavors + //of windows than this code does (in case it is needed for the future) + if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) //check for 64 bit { - if((osvi.dwMajorVersion != shell32_major || osvi.dwMinorVersion != shell32_minor) && !bShouldUseShellVersion) + mOSStringSimple += "64-bit "; + } + else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) + { + mOSStringSimple += "32-bit "; + } + + // Try calling GetVersionEx using the OSVERSIONINFOEX structure. + OSVERSIONINFOEX osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if (GetVersionEx((OSVERSIONINFO *)&osvi)) + { + mBuild = osvi.dwBuildNumber & 0xffff; + } + else + { + // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (GetVersionEx((OSVERSIONINFO *)&osvi)) { - compatibility_mode = llformat(" compatibility mode. real ver: %d.%d (Build %d)", - shell32_major, - shell32_minor, - shell32_build); + mBuild = osvi.dwBuildNumber & 0xffff; } } - mOSString += compatibility_mode; + + mOSString = mOSStringSimple; + if (mBuild > 0) + { + mOSString += llformat("(Build %d)", mBuild); + } + + LLStringUtil::trim(mOSStringSimple); + LLStringUtil::trim(mOSString); #elif LL_DARWIN -- cgit v1.2.3 From 57d5744f2c064ccb7bf8dd3dca2a24f6755297ac Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 28 Jul 2017 14:07:25 -0700 Subject: MAINT-7634: Move StatsAccumulator into llcommon, collect data sent and error codes from core. --- indra/llcommon/CMakeLists.txt | 1 + indra/llcommon/llstatsaccumulator.h | 120 ++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 indra/llcommon/llstatsaccumulator.h (limited to 'indra/llcommon') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 142e56dfca..d9eb13d65a 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -226,6 +226,7 @@ set(llcommon_HEADER_FILES llstring.h llstringtable.h llstaticstringtable.h + llstatsaccumulator.h llsys.h llthread.h llthreadlocalstorage.h diff --git a/indra/llcommon/llstatsaccumulator.h b/indra/llcommon/llstatsaccumulator.h new file mode 100644 index 0000000000..a893cc301d --- /dev/null +++ b/indra/llcommon/llstatsaccumulator.h @@ -0,0 +1,120 @@ +/** +* @file llstatsaccumulator.h +* @brief Class for accumulating statistics. +* +* $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$ +*/ + +#ifndef LL_STATS_ACCUMULATOR_H +#define LL_STATS_ACCUMULATOR_H +#include "llsd.h" + +class LLStatsAccumulator +{ +public: + inline LLStatsAccumulator() + { + reset(); + } + + inline void push(F32 val) + { + if (mCountOfNextUpdatesToIgnore > 0) + { + mCountOfNextUpdatesToIgnore--; + return; + } + + mCount++; + mSum += val; + mSumOfSquares += val * val; + if (mCount == 1 || val > mMaxValue) + { + mMaxValue = val; + } + if (mCount == 1 || val < mMinValue) + { + mMinValue = val; + } + } + + inline F32 getSum() const + { + return mSum; + } + + inline F32 getMean() const + { + return (mCount == 0) ? 0.f : ((F32)mSum) / mCount; + } + + inline F32 getMinValue() const + { + return mMinValue; + } + + inline F32 getMaxValue() const + { + return mMaxValue; + } + + inline F32 getStdDev() const + { + const F32 mean = getMean(); + return (mCount < 2) ? 0.f : sqrt(llmax(0.f, mSumOfSquares / mCount - (mean * mean))); + } + + inline U32 getCount() const + { + return mCount; + } + + inline void reset() + { + mCount = 0; + mSum = mSumOfSquares = 0.f; + mMinValue = 0.0f; + mMaxValue = 0.0f; + mCountOfNextUpdatesToIgnore = 0; + } + + inline LLSD asLLSD() const + { + LLSD data; + data["mean"] = getMean(); + data["std_dev"] = getStdDev(); + data["count"] = (S32)mCount; + data["min"] = getMinValue(); + data["max"] = getMaxValue(); + return data; + } + +private: + S32 mCount; + F32 mSum; + F32 mSumOfSquares; + F32 mMinValue; + F32 mMaxValue; + U32 mCountOfNextUpdatesToIgnore; +}; + +#endif -- cgit v1.2.3 From 1038633526330cf931ba097dbafdd270b5bb56e3 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Tue, 8 Aug 2017 09:04:32 -0700 Subject: MAINT-7634: Logging and instrumentation canges to narrow down viewer crashes. --- indra/llcommon/llthread.cpp | 465 ++++++++++++++++++++++++-------------------- indra/llcommon/llthread.h | 190 +++++++++--------- 2 files changed, 352 insertions(+), 303 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 52255bfaeb..b96b2ce4bc 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -34,6 +34,7 @@ #include "lltimer.h" #include "lltrace.h" #include "lltracethreadrecorder.h" +#include "llexception.h" #if LL_LINUX || LL_SOLARIS #include @@ -46,28 +47,28 @@ const DWORD MS_VC_EXCEPTION=0x406D1388; #pragma pack(push,8) typedef struct tagTHREADNAME_INFO { - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. } THREADNAME_INFO; #pragma pack(pop) void set_thread_name( DWORD dwThreadID, const char* threadName) { - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = threadName; - info.dwThreadID = dwThreadID; - info.dwFlags = 0; - - __try - { - ::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info ); - } - __except(EXCEPTION_CONTINUE_EXECUTION) - { - } + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = threadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + ::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info ); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } } #endif @@ -99,17 +100,17 @@ U32 LLThread::sIDIter = 0; LL_COMMON_API void assert_main_thread() { - static U32 s_thread_id = LLThread::currentID(); - if (LLThread::currentID() != s_thread_id) - { - LL_WARNS() << "Illegal execution from thread id " << (S32) LLThread::currentID() - << " outside main thread " << (S32) s_thread_id << LL_ENDL; - } + static U32 s_thread_id = LLThread::currentID(); + if (LLThread::currentID() != s_thread_id) + { + LL_WARNS() << "Illegal execution from thread id " << (S32) LLThread::currentID() + << " outside main thread " << (S32) s_thread_id << LL_ENDL; + } } void LLThread::registerThreadID() { - sThreadID = ++sIDIter; + sThreadID = ++sIDIter; } // @@ -117,157 +118,203 @@ void LLThread::registerThreadID() // void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap) { - LLThread *threadp = (LLThread *)datap; + LLThread *threadp = (LLThread *)datap; #ifdef LL_WINDOWS - set_thread_name(-1, threadp->mName.c_str()); + set_thread_name(-1, threadp->mName.c_str()); #endif - // for now, hard code all LLThreads to report to single master thread recorder, which is known to be running on main thread - threadp->mRecorder = new LLTrace::ThreadRecorder(*LLTrace::get_master_thread_recorder()); - - sThreadID = threadp->mID; - - // Run the user supplied function - threadp->run(); - - //LL_INFOS() << "LLThread::staticRun() Exiting: " << threadp->mName << LL_ENDL; - - delete threadp->mRecorder; - threadp->mRecorder = NULL; - - // We're done with the run function, this thread is done executing now. - //NB: we are using this flag to sync across threads...we really need memory barriers here - threadp->mStatus = STOPPED; - - return NULL; + // for now, hard code all LLThreads to report to single master thread recorder, which is known to be running on main thread + threadp->mRecorder = new LLTrace::ThreadRecorder(*LLTrace::get_master_thread_recorder()); + + sThreadID = threadp->mID; + + try + { + // Run the user supplied function + do + { + try + { + threadp->run(); + } + catch (const LLContinueError &e) + { + LL_WARNS("THREAD") << "ContinueException on thread '" << threadp->mName << + "' reentering run(). Error what is: '" << e.what() << "'" << LL_ENDL; + //output possible call stacks to log file. + LLError::LLCallStacks::print(); + + LOG_UNHANDLED_EXCEPTION("LLThread"); + continue; + } + break; + + } while (true); + + //LL_INFOS() << "LLThread::staticRun() Exiting: " << threadp->mName << LL_ENDL; + + // We're done with the run function, this thread is done executing now. + //NB: we are using this flag to sync across threads...we really need memory barriers here + threadp->mStatus = STOPPED; + } + catch (std::bad_alloc) + { + threadp->mStatus = CRASHED; + LLMemory::logMemoryInfo(TRUE); + + //output possible call stacks to log file. + LLError::LLCallStacks::print(); + + LL_ERRS("THREAD") << "Bad memory allocation in LLThread::staticRun() named '" << threadp->mName << "'!" << LL_ENDL; + } + catch (...) + { + threadp->mStatus = CRASHED; + CRASH_ON_UNHANDLED_EXCEPTION("LLThread"); + } + + delete threadp->mRecorder; + threadp->mRecorder = NULL; + + return NULL; } LLThread::LLThread(const std::string& name, apr_pool_t *poolp) : - mPaused(FALSE), - mName(name), - mAPRThreadp(NULL), - mStatus(STOPPED), - mRecorder(NULL) + mPaused(FALSE), + mName(name), + mAPRThreadp(NULL), + mStatus(STOPPED), + mRecorder(NULL) { - mID = ++sIDIter; - - // Thread creation probably CAN be paranoid about APR being initialized, if necessary - if (poolp) - { - mIsLocalPool = FALSE; - mAPRPoolp = poolp; - } - else - { - mIsLocalPool = TRUE; - apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread - } - mRunCondition = new LLCondition(mAPRPoolp); - mDataLock = new LLMutex(mAPRPoolp); - mLocalAPRFilePoolp = NULL ; + mID = ++sIDIter; + + // Thread creation probably CAN be paranoid about APR being initialized, if necessary + if (poolp) + { + mIsLocalPool = FALSE; + mAPRPoolp = poolp; + } + else + { + mIsLocalPool = TRUE; + apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread + } + mRunCondition = new LLCondition(mAPRPoolp); + mDataLock = new LLMutex(mAPRPoolp); + mLocalAPRFilePoolp = NULL ; } LLThread::~LLThread() { - shutdown(); - - if(mLocalAPRFilePoolp) - { - delete mLocalAPRFilePoolp ; - mLocalAPRFilePoolp = NULL ; - } + shutdown(); + + if (isCrashed()) + { + LL_WARNS("THREAD") << "Destroying crashed thread named '" << mName << "'" << LL_ENDL; + } + + if(mLocalAPRFilePoolp) + { + delete mLocalAPRFilePoolp ; + mLocalAPRFilePoolp = NULL ; + } } void LLThread::shutdown() { - // Warning! If you somehow call the thread destructor from itself, - // the thread will die in an unclean fashion! - if (mAPRThreadp) - { - if (!isStopped()) - { - // The thread isn't already stopped - // First, set the flag that indicates that we're ready to die - setQuitting(); - - //LL_INFOS() << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << LL_ENDL; - // Now wait a bit for the thread to exit - // It's unclear whether I should even bother doing this - this destructor - // should never get called unless we're already stopped, really... - S32 counter = 0; - const S32 MAX_WAIT = 600; - while (counter < MAX_WAIT) - { - if (isStopped()) - { - break; - } - // Sleep for a tenth of a second - ms_sleep(100); - yield(); - counter++; - } - } - - if (!isStopped()) - { - // This thread just wouldn't stop, even though we gave it time - //LL_WARNS() << "LLThread::~LLThread() exiting thread before clean exit!" << LL_ENDL; - // Put a stake in its heart. - delete mRecorder; - - apr_thread_exit(mAPRThreadp, -1); - return; - } - mAPRThreadp = NULL; - } - - delete mRunCondition; - mRunCondition = NULL; - - delete mDataLock; - mDataLock = NULL; - - if (mIsLocalPool && mAPRPoolp) - { - apr_pool_destroy(mAPRPoolp); - mAPRPoolp = 0; - } - - if (mRecorder) - { - // missed chance to properly shut down recorder (needs to be done in thread context) - // probably due to abnormal thread termination - // so just leak it and remove it from parent - LLTrace::get_master_thread_recorder()->removeChildRecorder(mRecorder); - } + if (isCrashed()) + { + LL_WARNS("THREAD") << "Shutting down crashed thread named '" << mName << "'" << LL_ENDL; + } + + // Warning! If you somehow call the thread destructor from itself, + // the thread will die in an unclean fashion! + if (mAPRThreadp) + { + if (!isStopped()) + { + // The thread isn't already stopped + // First, set the flag that indicates that we're ready to die + setQuitting(); + + //LL_INFOS() << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << LL_ENDL; + // Now wait a bit for the thread to exit + // It's unclear whether I should even bother doing this - this destructor + // should never get called unless we're already stopped, really... + S32 counter = 0; + const S32 MAX_WAIT = 600; + while (counter < MAX_WAIT) + { + if (isStopped()) + { + break; + } + // Sleep for a tenth of a second + ms_sleep(100); + yield(); + counter++; + } + } + + if (!isStopped()) + { + // This thread just wouldn't stop, even though we gave it time + //LL_WARNS() << "LLThread::~LLThread() exiting thread before clean exit!" << LL_ENDL; + // Put a stake in its heart. + delete mRecorder; + + apr_thread_exit(mAPRThreadp, -1); + return; + } + mAPRThreadp = NULL; + } + + delete mRunCondition; + mRunCondition = NULL; + + delete mDataLock; + mDataLock = NULL; + + if (mIsLocalPool && mAPRPoolp) + { + apr_pool_destroy(mAPRPoolp); + mAPRPoolp = 0; + } + + if (mRecorder) + { + // missed chance to properly shut down recorder (needs to be done in thread context) + // probably due to abnormal thread termination + // so just leak it and remove it from parent + LLTrace::get_master_thread_recorder()->removeChildRecorder(mRecorder); + } } void LLThread::start() { - llassert(isStopped()); - - // Set thread state to running - mStatus = RUNNING; - - apr_status_t status = - apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp); - - if(status == APR_SUCCESS) - { - // We won't bother joining - apr_thread_detach(mAPRThreadp); - } - else - { - mStatus = STOPPED; - LL_WARNS() << "failed to start thread " << mName << LL_ENDL; - ll_apr_warn_status(status); - } + llassert(isStopped()); + + // Set thread state to running + mStatus = RUNNING; + + apr_status_t status = + apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp); + + if(status == APR_SUCCESS) + { + // We won't bother joining + apr_thread_detach(mAPRThreadp); + } + else + { + mStatus = STOPPED; + LL_WARNS() << "failed to start thread " << mName << LL_ENDL; + ll_apr_warn_status(status); + } } @@ -278,28 +325,28 @@ void LLThread::start() // The thread will pause when (and if) it calls checkPause() void LLThread::pause() { - if (!mPaused) - { - // this will cause the thread to stop execution as soon as checkPause() is called - mPaused = 1; // Does not need to be atomic since this is only set/unset from the main thread - } + if (!mPaused) + { + // this will cause the thread to stop execution as soon as checkPause() is called + mPaused = 1; // Does not need to be atomic since this is only set/unset from the main thread + } } void LLThread::unpause() { - if (mPaused) - { - mPaused = 0; - } + if (mPaused) + { + mPaused = 0; + } - wake(); // wake up the thread if necessary + wake(); // wake up the thread if necessary } // virtual predicate function -- returns true if the thread should wake up, false if it should sleep. bool LLThread::runCondition(void) { - // by default, always run. Handling of pause/unpause is done regardless of this function's result. - return true; + // by default, always run. Handling of pause/unpause is done regardless of this function's result. + return true; } //============================================================================ @@ -307,65 +354,65 @@ bool LLThread::runCondition(void) // Stop thread execution if requested until unpaused. void LLThread::checkPause() { - mDataLock->lock(); - - // This is in a while loop because the pthread API allows for spurious wakeups. - while(shouldSleep()) - { - mDataLock->unlock(); - mRunCondition->wait(); // unlocks mRunCondition - mDataLock->lock(); - // mRunCondition is locked when the thread wakes up - } - - mDataLock->unlock(); + mDataLock->lock(); + + // This is in a while loop because the pthread API allows for spurious wakeups. + while(shouldSleep()) + { + mDataLock->unlock(); + mRunCondition->wait(); // unlocks mRunCondition + mDataLock->lock(); + // mRunCondition is locked when the thread wakes up + } + + mDataLock->unlock(); } //============================================================================ void LLThread::setQuitting() { - mDataLock->lock(); - if (mStatus == RUNNING) - { - mStatus = QUITTING; - } - mDataLock->unlock(); - wake(); + mDataLock->lock(); + if (mStatus == RUNNING) + { + mStatus = QUITTING; + } + mDataLock->unlock(); + wake(); } // static U32 LLThread::currentID() { - return sThreadID; + return sThreadID; } // static void LLThread::yield() { #if LL_LINUX || LL_SOLARIS - sched_yield(); // annoyingly, apr_thread_yield is a noop on linux... + sched_yield(); // annoyingly, apr_thread_yield is a noop on linux... #else - apr_thread_yield(); + apr_thread_yield(); #endif } void LLThread::wake() { - mDataLock->lock(); - if(!shouldSleep()) - { - mRunCondition->signal(); - } - mDataLock->unlock(); + mDataLock->lock(); + if(!shouldSleep()) + { + mRunCondition->signal(); + } + mDataLock->unlock(); } void LLThread::wakeLocked() { - if(!shouldSleep()) - { - mRunCondition->signal(); - } + if(!shouldSleep()) + { + mRunCondition->signal(); + } } //============================================================================ @@ -378,38 +425,38 @@ LLMutex* LLThreadSafeRefCount::sMutex = 0; //static void LLThreadSafeRefCount::initThreadSafeRefCount() { - if (!sMutex) - { - sMutex = new LLMutex(0); - } + if (!sMutex) + { + sMutex = new LLMutex(0); + } } //static void LLThreadSafeRefCount::cleanupThreadSafeRefCount() { - delete sMutex; - sMutex = NULL; + delete sMutex; + sMutex = NULL; } - + //---------------------------------------------------------------------------- LLThreadSafeRefCount::LLThreadSafeRefCount() : - mRef(0) + mRef(0) { } LLThreadSafeRefCount::LLThreadSafeRefCount(const LLThreadSafeRefCount& src) { - mRef = 0; + mRef = 0; } LLThreadSafeRefCount::~LLThreadSafeRefCount() { - if (mRef != 0) - { - LL_ERRS() << "deleting non-zero reference" << LL_ENDL; - } + if (mRef != 0) + { + LL_ERRS() << "deleting non-zero reference" << LL_ENDL; + } } //============================================================================ diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 6f9ec10fd3..dda7fa8ffb 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -38,118 +38,120 @@ LL_COMMON_API void assert_main_thread(); namespace LLTrace { - class ThreadRecorder; + class ThreadRecorder; } class LL_COMMON_API LLThread { private: - friend class LLMutex; - static U32 sIDIter; + friend class LLMutex; + static U32 sIDIter; public: - typedef enum e_thread_status - { - STOPPED = 0, // The thread is not running. Not started, or has exited its run function - RUNNING = 1, // The thread is currently running - QUITTING= 2 // Someone wants this thread to quit - } EThreadStatus; - - LLThread(const std::string& name, apr_pool_t *poolp = NULL); - virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state. - virtual void shutdown(); // stops the thread - - bool isQuitting() const { return (QUITTING == mStatus); } - bool isStopped() const { return (STOPPED == mStatus); } - - static U32 currentID(); // Return ID of current thread - static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. - + typedef enum e_thread_status + { + STOPPED = 0, // The thread is not running. Not started, or has exited its run function + RUNNING = 1, // The thread is currently running + QUITTING= 2, // Someone wants this thread to quit + CRASHED = -1 // An uncaught exception was thrown by the thread + } EThreadStatus; + + LLThread(const std::string& name, apr_pool_t *poolp = NULL); + virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state. + virtual void shutdown(); // stops the thread + + bool isQuitting() const { return (QUITTING == mStatus); } + bool isStopped() const { return (STOPPED == mStatus) || (CRASHED == mStatus); } + bool isCrashed() const { return (CRASHED == mStatus); } + + static U32 currentID(); // Return ID of current thread + static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. + public: - // PAUSE / RESUME functionality. See source code for important usage notes. - // Called from MAIN THREAD. - void pause(); - void unpause(); - bool isPaused() { return isStopped() || mPaused == TRUE; } - - // Cause the thread to wake up and check its condition - void wake(); - - // Same as above, but to be used when the condition is already locked. - void wakeLocked(); - - // Called from run() (CHILD THREAD). Pause the thread if requested until unpaused. - void checkPause(); - - // this kicks off the apr thread - void start(void); - - apr_pool_t *getAPRPool() { return mAPRPoolp; } - LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; } - - U32 getID() const { return mID; } - - // Called by threads *not* created via LLThread to register some - // internal state used by LLMutex. You must call this once early - // in the running thread to prevent collisions with the main thread. - static void registerThreadID(); - + // PAUSE / RESUME functionality. See source code for important usage notes. + // Called from MAIN THREAD. + void pause(); + void unpause(); + bool isPaused() { return isStopped() || mPaused == TRUE; } + + // Cause the thread to wake up and check its condition + void wake(); + + // Same as above, but to be used when the condition is already locked. + void wakeLocked(); + + // Called from run() (CHILD THREAD). Pause the thread if requested until unpaused. + void checkPause(); + + // this kicks off the apr thread + void start(void); + + apr_pool_t *getAPRPool() { return mAPRPoolp; } + LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; } + + U32 getID() const { return mID; } + + // Called by threads *not* created via LLThread to register some + // internal state used by LLMutex. You must call this once early + // in the running thread to prevent collisions with the main thread. + static void registerThreadID(); + private: - BOOL mPaused; - - // static function passed to APR thread creation routine - static void *APR_THREAD_FUNC staticRun(struct apr_thread_t *apr_threadp, void *datap); + BOOL mPaused; + + // static function passed to APR thread creation routine + static void *APR_THREAD_FUNC staticRun(struct apr_thread_t *apr_threadp, void *datap); protected: - std::string mName; - class LLCondition* mRunCondition; - LLMutex* mDataLock; - - apr_thread_t *mAPRThreadp; - apr_pool_t *mAPRPoolp; - BOOL mIsLocalPool; - EThreadStatus mStatus; - U32 mID; - LLTrace::ThreadRecorder* mRecorder; - - //a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used. - //Note: this pool is used by APRFile ONLY, do NOT use it for any other purposes. - // otherwise it will cause severe memory leaking!!! --bao - LLVolatileAPRPool *mLocalAPRFilePoolp ; - - void setQuitting(); - - // virtual function overridden by subclass -- this will be called when the thread runs - virtual void run(void) = 0; - - // virtual predicate function -- returns true if the thread should wake up, false if it should sleep. - virtual bool runCondition(void); - - // Lock/Unlock Run Condition -- use around modification of any variable used in runCondition() - inline void lockData(); - inline void unlockData(); - - // This is the predicate that decides whether the thread should sleep. - // It should only be called with mDataLock locked, since the virtual runCondition() function may need to access - // data structures that are thread-unsafe. - bool shouldSleep(void) { return (mStatus == RUNNING) && (isPaused() || (!runCondition())); } - - // To avoid spurious signals (and the associated context switches) when the condition may or may not have changed, you can do the following: - // mDataLock->lock(); - // if(!shouldSleep()) - // mRunCondition->signal(); - // mDataLock->unlock(); + std::string mName; + class LLCondition* mRunCondition; + LLMutex* mDataLock; + + apr_thread_t *mAPRThreadp; + apr_pool_t *mAPRPoolp; + BOOL mIsLocalPool; + EThreadStatus mStatus; + U32 mID; + LLTrace::ThreadRecorder* mRecorder; + + //a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used. + //Note: this pool is used by APRFile ONLY, do NOT use it for any other purposes. + // otherwise it will cause severe memory leaking!!! --bao + LLVolatileAPRPool *mLocalAPRFilePoolp ; + + void setQuitting(); + + // virtual function overridden by subclass -- this will be called when the thread runs + virtual void run(void) = 0; + + // virtual predicate function -- returns true if the thread should wake up, false if it should sleep. + virtual bool runCondition(void); + + // Lock/Unlock Run Condition -- use around modification of any variable used in runCondition() + inline void lockData(); + inline void unlockData(); + + // This is the predicate that decides whether the thread should sleep. + // It should only be called with mDataLock locked, since the virtual runCondition() function may need to access + // data structures that are thread-unsafe. + bool shouldSleep(void) { return (mStatus == RUNNING) && (isPaused() || (!runCondition())); } + + // To avoid spurious signals (and the associated context switches) when the condition may or may not have changed, you can do the following: + // mDataLock->lock(); + // if(!shouldSleep()) + // mRunCondition->signal(); + // mDataLock->unlock(); }; void LLThread::lockData() { - mDataLock->lock(); + mDataLock->lock(); } void LLThread::unlockData() { - mDataLock->unlock(); + mDataLock->unlock(); } @@ -160,9 +162,9 @@ void LLThread::unlockData() class LL_COMMON_API LLResponder : public LLThreadSafeRefCount { protected: - virtual ~LLResponder(); + virtual ~LLResponder(); public: - virtual void completed(bool success) = 0; + virtual void completed(bool success) = 0; }; //============================================================================ -- cgit v1.2.3