diff options
| author | simon-linden <57500096+simon-linden@users.noreply.github.com> | 2023-05-09 09:27:53 -0700 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-09 09:27:53 -0700 | 
| commit | a9c64940cbac27328b6304f9d63cdf4265fae2f7 (patch) | |
| tree | c804fa0666f8a7a4954c61569e47376ce1e450bc /indra/newview | |
| parent | 971e851157da688b480d6ada188daec532e4b682 (diff) | |
| parent | 90631fccef6ab1596655b18fb7dbdc87a4cb80ac (diff) | |
Merge pull request #205 from secondlife/sl-19676-360mode-stats
sl-19676 - Stats on updates and scene loading
Diffstat (limited to 'indra/newview')
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 33 | ||||
| -rw-r--r-- | indra/newview/llappviewer.cpp | 7 | ||||
| -rw-r--r-- | indra/newview/llfloater360capture.cpp | 18 | ||||
| -rw-r--r-- | indra/newview/llmeshrepository.cpp | 3 | ||||
| -rw-r--r-- | indra/newview/lltexturefetch.cpp | 13 | ||||
| -rw-r--r-- | indra/newview/llviewermenu.cpp | 143 | ||||
| -rw-r--r-- | indra/newview/llviewerobjectlist.cpp | 42 | ||||
| -rwxr-xr-x | indra/newview/llviewerregion.cpp | 17 | ||||
| -rw-r--r-- | indra/newview/llviewerregion.h | 12 | ||||
| -rw-r--r-- | indra/newview/llviewerstatsrecorder.cpp | 280 | ||||
| -rw-r--r-- | indra/newview/llviewerstatsrecorder.h | 129 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/menu_viewer.xml | 24 | 
12 files changed, 440 insertions, 281 deletions
| diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 8859080531..9f2d6f746d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -17085,5 +17085,38 @@      <key>Value</key>      <integer>0</integer>    </map> +  <key>StatsReportMaxDuration</key> +  <map> +    <key>Comment</key> +    <string>Maximum seconds for viewer stats file data, prevents huge file</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>F32</string> +    <key>Value</key> +    <real>300</real> +  </map> +	<key>StatsReportFileInterval</key> +	<map> +		<key>Comment</key> +		<string>Interval to save viewer stats file data</string> +		<key>Persist</key> +		<integer>1</integer> +		<key>Type</key> +		<string>F32</string> +		<key>Value</key> +		<real>0.2</real> +	</map> +  <key>StatsReportSkipZeroDataSaves</key> +  <map> +    <key>Comment</key> +    <string>In viewer stats data file, skip saving entry if there is no data</string> +    <key>Persist</key> +    <integer>0</integer> +    <key>Type</key> +    <string>Boolean</string> +    <key>Value</key> +    <integer>0</integer> +  </map>  </map>  </llsd> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 61fa3dc829..23d99da8ba 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1536,7 +1536,12 @@ bool LLAppViewer::doFrame()                      LLFloaterSimpleOutfitSnapshot::update();                      gGLActive = FALSE;                  } -		} + +                if (LLViewerStatsRecorder::instanceExists()) +                { +                    LLViewerStatsRecorder::instance().idle(); +                } +            }  		}  		{ diff --git a/indra/newview/llfloater360capture.cpp b/indra/newview/llfloater360capture.cpp index 23f86e2361..9c25cdbc7d 100644 --- a/indra/newview/llfloater360capture.cpp +++ b/indra/newview/llfloater360capture.cpp @@ -197,21 +197,18 @@ void LLFloater360Capture::changeInterestListMode(bool send_everything)      if (gAgent.requestPostCapability("InterestList", body, [](const LLSD & response)      { -        LL_INFOS("360Capture") << -                               "InterestList capability responded: \n" << +        LL_DEBUGS("360Capture") << "InterestList capability responded: \n" <<                                 ll_pretty_print_sd(response) <<                                 LL_ENDL;      }))      { -        LL_INFOS("360Capture") << -                               "Successfully posted an InterestList capability request with payload: \n" << +        LL_DEBUGS("360Capture") << "Successfully posted an InterestList capability request with payload: \n" <<                                 ll_pretty_print_sd(body) <<                                 LL_ENDL;      }      else      { -        LL_INFOS("360Capture") << -                               "Unable to post an InterestList capability request with payload: \n" << +        LL_WARNS("360Capture") << "Unable to post an InterestList capability request with payload: \n" <<                                 ll_pretty_print_sd(body) <<                                 LL_ENDL;      } @@ -632,11 +629,8 @@ void LLFloater360Capture::capture360Images()      // display time to encode all 6 images.  It tends to be a fairly linear      // time for each so we don't need to worry about displaying the time      // for each - this gives us plenty to use for optimizing -    LL_INFOS("360Capture") << -                           "Time to encode and save 6 images was " << -                           encode_time_total << -                           " seconds" << -                           LL_ENDL; +    LL_INFOS("360Capture") << "Time to encode and save 6 images was " << +                           encode_time_total << " seconds" << LL_ENDL;      // Write the JavaScript file footer (the bottom of the file after the      // declarations of the actual data URLs array). The footer comprises of @@ -668,7 +662,7 @@ void LLFloater360Capture::capture360Images()      // as a change - only the subsequent 5 are      if (camera_changed_times < 5)      { -        LL_INFOS("360Capture") << "Warning: we only captured " << camera_changed_times << " images." << LL_ENDL; +        LL_WARNS("360Capture") << "360 image capture expected 5 or more images, only captured " << camera_changed_times << " images." << LL_ENDL;      }      // now we have the 6 shots saved in a well specified location, diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index f937754368..e2fb218b1b 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -56,6 +56,7 @@  #include "llviewermessage.h"  #include "llviewerobjectlist.h"  #include "llviewerregion.h" +#include "llviewerstatsrecorder.h"  #include "llviewertexturelist.h"  #include "llvolume.h"  #include "llvolumemgr.h" @@ -4089,6 +4090,8 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol  		}  		mLoadingMeshes[detail].erase(obj_iter); + +		LLViewerStatsRecorder::instance().meshLoaded();  	}  } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 6f6ca2be9b..a1beec7c1f 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2183,7 +2183,6 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response,  		LL_DEBUGS(LOG_TXT) << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL;  		if (data_size > 0)  		{ -			LLViewerStatsRecorder::instance().textureFetch(data_size);  			// *TODO: set the formatted image data here directly to avoid the copy  			// Hold on to body for later copy @@ -2249,6 +2248,13 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response,  			mHaveAllData = TRUE;  		}  		mRequestedSize = data_size; + +		if (mHaveAllData) +        { +            LLViewerStatsRecorder::instance().textureFetch(); +        } + +        // *TODO: set the formatted image data here directly to avoid the copy  	}  	else  	{ @@ -2258,11 +2264,6 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response,  	mLoaded = TRUE;  	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -	if (LLViewerStatsRecorder::instanceExists()) -	{ -		// Do not create this instance inside thread -		LLViewerStatsRecorder::instance().log(0.2f); -	}  	return data_size ;  } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 5b82fb5030..d1b240d2c4 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -119,6 +119,7 @@  #include "llviewerobjectlist.h"  #include "llviewerparcelmgr.h"  #include "llviewerstats.h" +#include "llviewerstatsrecorder.h"  #include "llvoavatarself.h"  #include "llvoicevivox.h"  #include "llworldmap.h" @@ -1295,49 +1296,99 @@ class LLAdvancedDumpRegionObjectCache : public view_listener_t  	}  }; -class LLAdvancedInterestListFullUpdate : public view_listener_t +class LLAdvancedToggleInterestList360Mode : public view_listener_t  { -	bool handleEvent(const LLSD& userdata) -	{ -		LLSD request; -		LLSD body; -		static bool using_360 = false; - -		if (using_360) -		{ -			body["mode"] = LLSD::String("default"); -		} -		else -		{ -			body["mode"] = LLSD::String("360"); -		} -		using_360 = !using_360; +public: +    bool handleEvent(const LLSD &userdata) +    { +        LLSD request; +        LLSD body; + +        // First do a GET to report on current mode and update stats +        if (gAgent.requestGetCapability("InterestList", +                                        [](const LLSD &response) { +                                            LL_DEBUGS("360Capture") << "InterestList capability GET responded: \n" +                                                                   << ll_pretty_print_sd(response) << LL_ENDL; +                                        })) +        { +            LL_DEBUGS("360Capture") << "Successful GET InterestList capability request with return body: \n" +                                   << ll_pretty_print_sd(body) << LL_ENDL; +        } +        else +        { +            LL_WARNS("360Capture") << "Unable to GET InterestList capability request with return body: \n" +                                   << ll_pretty_print_sd(body) << LL_ENDL; +        } -        if (gAgent.requestPostCapability("InterestList", body, [](const LLSD& response) +        // Now do a POST to change the mode +        if (sUsing360)          { -            LL_INFOS("360Capture") << -                "InterestList capability responded: \n" << -                ll_pretty_print_sd(response) << -                LL_ENDL; -        })) +            body["mode"] = LLSD::String("default"); +        } +        else          { -            LL_INFOS("360Capture") << -                "Successfully posted an InterestList capability request with payload: \n" << -                ll_pretty_print_sd(body) << -                LL_ENDL; +            body["mode"] = LLSD::String("360"); +        } +        sUsing360 = !sUsing360; +        LL_INFOS("360Capture") << "Setting InterestList capability mode to " << body["mode"].asString() << LL_ENDL; + +        if (gAgent.requestPostCapability("InterestList", body, +                                         [](const LLSD &response) { +                                             LL_DEBUGS("360Capture") << "InterestList capability responded: \n" +                                                                    << ll_pretty_print_sd(response) << LL_ENDL; +                                         })) +        { +            LL_DEBUGS("360Capture") << "Successfully posted an InterestList capability request with payload: \n" +                                   << ll_pretty_print_sd(body) << LL_ENDL;              return true;          }          else          { -            LL_INFOS("360Capture") << -                "Unable to post an InterestList capability request with payload: \n" << -                ll_pretty_print_sd(body) << -                LL_ENDL; +            LL_DEBUGS("360Capture") << "Unable to post an InterestList capability request with payload: \n" +                                   << ll_pretty_print_sd(body) << LL_ENDL;              return false;          } +    }; + +	static bool sUsing360; +}; + +bool LLAdvancedToggleInterestList360Mode::sUsing360 = false; + +class LLAdvancedCheckInterestList360Mode : public view_listener_t +{ +	bool handleEvent(const LLSD& userdata) +	{ +		return LLAdvancedToggleInterestList360Mode::sUsing360;  	}  }; +class LLAdvancedToggleStatsRecorder : public view_listener_t +{ +    bool handleEvent(const LLSD &userdata) +    { +        if (LLViewerStatsRecorder::instance().isEnabled()) +		{	// Turn off both recording and logging +			LLViewerStatsRecorder::instance().enableObjectStatsRecording(false); +		} +		else +		{	// Turn on both recording and logging +			LLViewerStatsRecorder::instance().enableObjectStatsRecording(true, true); +		} +        return true; +    } +}; + +class LLAdvancedCheckStatsRecorder : public view_listener_t +{ +    bool handleEvent(const LLSD &userdata) +    {	// Use the logging state as the indicator of whether the stats recorder is on +        return LLViewerStatsRecorder::instance().isLogging(); +    } +}; + + +  class LLAdvancedBuyCurrencyTest : public view_listener_t  	{  	bool handleEvent(const LLSD& userdata) @@ -4492,33 +4543,6 @@ void handle_duplicate_in_place(void*)  	LLSelectMgr::getInstance()->selectDuplicate(offset, TRUE);  } -/* dead code 30-apr-2008 -void handle_deed_object_to_group(void*) -{ -	LLUUID group_id; -	 -	LLSelectMgr::getInstance()->selectGetGroup(group_id); -	LLSelectMgr::getInstance()->sendOwner(LLUUID::null, group_id, FALSE); -	LLViewerStats::getInstance()->incStat(LLViewerStats::ST_RELEASE_COUNT); -} - -BOOL enable_deed_object_to_group(void*) -{ -	if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) return FALSE; -	LLPermissions perm; -	LLUUID group_id; - -	if (LLSelectMgr::getInstance()->selectGetGroup(group_id) && -		gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && -		LLSelectMgr::getInstance()->selectGetPermissions(perm) && -		perm.deedToGroup(gAgent.getID(), group_id)) -	{ -		return TRUE; -	} -	return FALSE; -} - -*/  /* @@ -9469,7 +9493,10 @@ void initialize_menus()  	// Advanced > World  	view_listener_t::addMenu(new LLAdvancedDumpScriptedCamera(), "Advanced.DumpScriptedCamera");  	view_listener_t::addMenu(new LLAdvancedDumpRegionObjectCache(), "Advanced.DumpRegionObjectCache"); -	view_listener_t::addMenu(new LLAdvancedInterestListFullUpdate(), "Advanced.InterestListFullUpdate"); +    view_listener_t::addMenu(new LLAdvancedToggleInterestList360Mode(), "Advanced.ToggleInterestList360Mode"); +    view_listener_t::addMenu(new LLAdvancedCheckInterestList360Mode(), "Advanced.CheckInterestList360Mode"); +    view_listener_t::addMenu(new LLAdvancedToggleStatsRecorder(), "Advanced.ToggleStatsRecorder"); +    view_listener_t::addMenu(new LLAdvancedCheckStatsRecorder(), "Advanced.CheckStatsRecorder");  	// Advanced > UI  	commit.add("Advanced.WebBrowserTest", boost::bind(&handle_web_browser_test,	_2));	// sigh! this one opens the MEDIA browser diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index efc4ded79e..0c9e929cf3 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -369,7 +369,7 @@ LLViewerObject* LLViewerObjectList::processObjectUpdateFromCache(LLVOCacheEntry*  		if (!objectp)  		{  			LL_INFOS() << "createObject failure for object: " << fullid << LL_ENDL; -			recorder.objectUpdateFailure(entry->getLocalID(), OUT_FULL_CACHED, 0); +			recorder.objectUpdateFailure(0);  			return NULL;  		}  		justCreated = true; @@ -393,7 +393,6 @@ LLViewerObject* LLViewerObjectList::processObjectUpdateFromCache(LLVOCacheEntry*  		objectp->setLastUpdateType(OUT_FULL_COMPRESSED); //newly cached  		objectp->setLastUpdateCached(TRUE);  	} -	recorder.log(0.2f);  	LLVOAvatar::cullAvatarsByPixelArea();  	return objectp; @@ -472,18 +471,15 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  	for (i = 0; i < num_objects; i++)  	{ -		// timer is unused? -		LLTimer update_timer;  		BOOL justCreated = FALSE;  		S32	msg_size = 0;  		bool update_cache = false; //update object cache if it is a full-update or terse update  		if (compressed)  		{ -			S32							uncompressed_length = 2048;  			compressed_dp.reset(); -			uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data); +			S32 uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);  			LL_DEBUGS("ObjectUpdate") << "got binary data from message to compressed_dpbuffer" << LL_ENDL;  			mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compressed_dpbuffer, 0, i, 2048);  			compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length); @@ -505,7 +501,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  						<< " Flags: " << flags  						<< " Region: " << regionp->getName()  						<< " Region id: " << regionp->getRegionID() << LL_ENDL; -					recorder.objectUpdateFailure(local_id, update_type, msg_size); +					recorder.objectUpdateFailure(msg_size);  					continue;  				}  				else if ((flags & FLAGS_TEMPORARY_ON_REZ) == 0) @@ -616,7 +612,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  				if (update_type == OUT_TERSE_IMPROVED)  				{  					// LL_INFOS() << "terse update for an unknown object (compressed):" << fullid << LL_ENDL; -					recorder.objectUpdateFailure(local_id, update_type, msg_size); +					recorder.objectUpdateFailure(msg_size);  					continue;  				}  			} @@ -625,7 +621,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  				if (update_type != OUT_FULL)  				{  					//LL_INFOS() << "terse update for an unknown object:" << fullid << LL_ENDL; -					recorder.objectUpdateFailure(local_id, update_type, msg_size); +					recorder.objectUpdateFailure(msg_size);  					continue;  				} @@ -638,7 +634,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			{  				mNumDeadObjectUpdates++;  				//LL_INFOS() << "update for a dead object:" << fullid << LL_ENDL; -				recorder.objectUpdateFailure(local_id, update_type, msg_size); +				recorder.objectUpdateFailure(msg_size);  				continue;  			}  #endif @@ -651,7 +647,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			if (!objectp)  			{  				LL_INFOS() << "createObject failure for object: " << fullid << LL_ENDL; -				recorder.objectUpdateFailure(local_id, update_type, msg_size); +				recorder.objectUpdateFailure(msg_size);  				continue;  			} @@ -681,11 +677,11 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  				if(!(flags & FLAGS_TEMPORARY_ON_REZ))  				{ -				bCached = true; +					bCached = true;  					LLViewerRegion::eCacheUpdateResult result = objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp, flags); -				recorder.cacheFullUpdate(local_id, update_type, result, objectp, msg_size); +					recorder.cacheFullUpdate(result); +				}  			} -		}  #endif  		}  		else @@ -696,12 +692,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			}  			processUpdateCore(objectp, user_data, i, update_type, NULL, justCreated);  		} -		recorder.objectUpdateEvent(local_id, update_type, objectp, msg_size); +		recorder.objectUpdateEvent(update_type);  		objectp->setLastUpdateType(update_type);  	} -	recorder.log(0.2f); -  	LLVOAvatar::cullAvatarsByPixelArea();  } @@ -748,14 +742,14 @@ void LLViewerObjectList::processCachedObjectUpdate(LLMessageSystem *mesgsys,  		// Lookup data packer and add this id to cache miss lists if necessary.  		U8 cache_miss_type = LLViewerRegion::CACHE_MISS_TYPE_NONE; -		if(!regionp->probeCache(id, crc, flags, cache_miss_type)) -		{ -			// Cache Miss. +        if (regionp->probeCache(id, crc, flags, cache_miss_type)) +		{	// Cache Hit +            recorder.cacheHitEvent(); +		} +        else +		{	// Cache Miss              LL_DEBUGS("ObjectUpdate") << "cache miss for id " << id << " crc " << crc << " miss type " << (S32) cache_miss_type << LL_ENDL; - -			recorder.cacheMissEvent(id, update_type, cache_miss_type, msg_size); - -			continue; // no data packer, skip this object +            recorder.cacheMissEvent(cache_miss_type);  		}  	} diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index e122baadce..54d787366f 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2622,14 +2622,10 @@ LLVOCacheEntry* LLViewerRegion::getCacheEntry(U32 local_id, bool valid)  	return NULL;  	} -void LLViewerRegion::addCacheMiss(U32 id, LLViewerRegion::eCacheMissType miss_type) +void LLViewerRegion::addCacheMiss(U32 id, LLViewerRegion::eCacheMissType cache_miss_type)  {  	mRegionCacheMissCount++; -#if 0 -	mCacheMissList.insert(CacheMissItem(id, miss_type)); -#else -	mCacheMissList.push_back(CacheMissItem(id, miss_type)); -#endif +    mCacheMissList.push_back(CacheMissItem(id, cache_miss_type));  }  //check if a non-cacheable object is already created. @@ -2705,10 +2701,10 @@ bool LLViewerRegion::probeCache(U32 local_id, U32 crc, U32 flags, U8 &cache_miss  		}  	}  	else -	{ +	{	// Total miss, don't have the object in cache  		// LL_INFOS() << "Cache miss for " << local_id << LL_ENDL; -		addCacheMiss(local_id, CACHE_MISS_TYPE_FULL); -        cache_miss_type = CACHE_MISS_TYPE_FULL; +        addCacheMiss(local_id, CACHE_MISS_TYPE_TOTAL); +        cache_miss_type = CACHE_MISS_TYPE_TOTAL;  	}  	return false; @@ -2716,7 +2712,7 @@ bool LLViewerRegion::probeCache(U32 local_id, U32 crc, U32 flags, U8 &cache_miss  void LLViewerRegion::addCacheMissFull(const U32 local_id)  { -	addCacheMiss(local_id, CACHE_MISS_TYPE_FULL); +	addCacheMiss(local_id, CACHE_MISS_TYPE_TOTAL);  }  void LLViewerRegion::requestCacheMisses() @@ -2767,7 +2763,6 @@ void LLViewerRegion::requestCacheMisses()  	mCacheDirty = TRUE ;  	// LL_INFOS() << "KILLDEBUG Sent cache miss full " << full_count << " crc " << crc_count << LL_ENDL;  	LLViewerStatsRecorder::instance().requestCacheMissesEvent(mCacheMissList.size()); -	LLViewerStatsRecorder::instance().log(0.2f);  	mCacheMissList.clear();  } diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 6548e8d372..81371b7f30 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -332,9 +332,9 @@ public:  	typedef enum  	{ -		CACHE_MISS_TYPE_FULL = 0, -		CACHE_MISS_TYPE_CRC, -		CACHE_MISS_TYPE_NONE +		CACHE_MISS_TYPE_TOTAL = 0,	// total cache miss - object not in cache +		CACHE_MISS_TYPE_CRC,		// object in cache, but CRC doesn't match +		CACHE_MISS_TYPE_NONE		// not a miss:  cache hit  	} eCacheMissType;  	typedef enum @@ -551,10 +551,10 @@ private:  	class CacheMissItem  	{  	public: -		CacheMissItem(U32 id, LLViewerRegion::eCacheMissType miss_type) : mID(id), mType(miss_type){} +        CacheMissItem(U32 id, LLViewerRegion::eCacheMissType miss_type) : mID(id), mType(miss_type) {} -		U32                            mID;     //local object id -		LLViewerRegion::eCacheMissType mType;   //cache miss type +		U32                         mID;     //local object id +        LLViewerRegion::eCacheMissType	mType;  // cache miss type  		typedef std::list<CacheMissItem> cache_miss_list_t;  	}; diff --git a/indra/newview/llviewerstatsrecorder.cpp b/indra/newview/llviewerstatsrecorder.cpp index b5ccf4ffa0..f95a960186 100644 --- a/indra/newview/llviewerstatsrecorder.cpp +++ b/indra/newview/llviewerstatsrecorder.cpp @@ -28,24 +28,25 @@  #include "llviewerstatsrecorder.h" +#include "llcontrol.h"  #include "llfile.h"  #include "llviewerregion.h"  #include "llviewerobject.h" +#include "llworld.h" - -// To do - something using region name or global position -#if LL_WINDOWS -	static const std::string STATS_FILE_NAME("C:\\ViewerObjectCacheStats.csv"); -#else -	static const std::string STATS_FILE_NAME("/tmp/viewerstats.csv"); -#endif +extern LLControlGroup  gSavedSettings;  LLViewerStatsRecorder* LLViewerStatsRecorder::sInstance = NULL;  LLViewerStatsRecorder::LLViewerStatsRecorder() : -	mObjectCacheFile(NULL), +	mStatsFile(NULL),  	mTimer(), -	mStartTime(0.0), -	mLastSnapshotTime(0.0) +	mFileOpenTime(0.0), +	mLastSnapshotTime(0.0), +    mEnableStatsRecording(false), +    mEnableStatsLogging(false), +    mInterval(0.2), +    mMaxDuration(300.f), +    mSkipSaveIfZeros(false)  {  	if (NULL != sInstance)  	{ @@ -57,79 +58,84 @@ LLViewerStatsRecorder::LLViewerStatsRecorder() :  LLViewerStatsRecorder::~LLViewerStatsRecorder()  { -	if (mObjectCacheFile != NULL) +	if (mStatsFile)  	{ -		// last chance snapshot -		writeToLog(0.f); -		LLFile::close(mObjectCacheFile); -		mObjectCacheFile = NULL; +        writeToLog(0.f);  // Save last data +        closeStatsFile();  	}  }  void LLViewerStatsRecorder::clearStats()  {  	mObjectCacheHitCount = 0; -	mObjectCacheHitSize = 0;  	mObjectCacheMissFullCount = 0; -	mObjectCacheMissFullSize = 0;  	mObjectCacheMissCrcCount = 0; -	mObjectCacheMissCrcSize = 0;  	mObjectFullUpdates = 0; -	mObjectFullUpdatesSize = 0;  	mObjectTerseUpdates = 0; -	mObjectTerseUpdatesSize = 0;  	mObjectCacheMissRequests = 0;  	mObjectCacheMissResponses = 0; -	mObjectCacheMissResponsesSize = 0;  	mObjectCacheUpdateDupes = 0;  	mObjectCacheUpdateChanges = 0;  	mObjectCacheUpdateAdds = 0;  	mObjectCacheUpdateReplacements = 0;  	mObjectUpdateFailures = 0;  	mObjectUpdateFailuresSize = 0; -	mTextureFetchSize = 0; +	mTextureFetchCount = 0; +    mMeshLoadedCount = 0; +} + + +void LLViewerStatsRecorder::enableObjectStatsRecording(bool enable, bool logging /* false */) +{ +    mEnableStatsRecording = enable; + +	// if logging is stopping, close the file +    if (mStatsFile && !logging) +	{ +        writeToLog(0.f);  // Save last data +        closeStatsFile(); +	} +    mEnableStatsLogging = logging;  } -void LLViewerStatsRecorder::recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type, S32 msg_size) + +void LLViewerStatsRecorder::recordObjectUpdateFailure(S32 msg_size)  {  	mObjectUpdateFailures++;  	mObjectUpdateFailuresSize += msg_size;  } -void LLViewerStatsRecorder::recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type, S32 msg_size) +void LLViewerStatsRecorder::recordCacheMissEvent(U8 cache_miss_type)  { -	if (LLViewerRegion::CACHE_MISS_TYPE_FULL == cache_miss_type) +	if (LLViewerRegion::CACHE_MISS_TYPE_TOTAL == cache_miss_type)  	{  		mObjectCacheMissFullCount++; -		mObjectCacheMissFullSize += msg_size;  	}  	else  	{  		mObjectCacheMissCrcCount++; -		mObjectCacheMissCrcSize += msg_size;  	}  } -void LLViewerStatsRecorder::recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp, S32 msg_size) + +void LLViewerStatsRecorder::recordCacheHitEvent() +{ +    mObjectCacheHitCount++; +} + +void LLViewerStatsRecorder::recordObjectUpdateEvent(const EObjectUpdateType update_type)  { -	switch (update_type) +    switch (update_type)  	{  	case OUT_FULL:  		mObjectFullUpdates++; -		mObjectFullUpdatesSize += msg_size;  		break;  	case OUT_TERSE_IMPROVED:  		mObjectTerseUpdates++; -		mObjectTerseUpdatesSize += msg_size;  		break;  	case OUT_FULL_COMPRESSED:  		mObjectCacheMissResponses++; -		mObjectCacheMissResponsesSize += msg_size; -		break; -	case OUT_FULL_CACHED: -		mObjectCacheHitCount++; -		mObjectCacheHitSize += msg_size;  		break;  	default:  		LL_WARNS() << "Unknown update_type" << LL_ENDL; @@ -137,9 +143,9 @@ void LLViewerStatsRecorder::recordObjectUpdateEvent(U32 local_id, const EObjectU  	};  } -void LLViewerStatsRecorder::recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp, S32 msg_size) +void LLViewerStatsRecorder::recordCacheFullUpdate(LLViewerRegion::eCacheUpdateResult update_result)  { -	switch (update_result) +    switch (update_result)  	{  		case LLViewerRegion::CACHE_UPDATE_DUPE:  			mObjectCacheUpdateDupes++; @@ -154,7 +160,7 @@ void LLViewerStatsRecorder::recordCacheFullUpdate(U32 local_id, const EObjectUpd  			mObjectCacheUpdateReplacements++;  			break;  		default: -			LL_WARNS() << "Unknown update_result type" << LL_ENDL; +            LL_WARNS() << "Unknown update_result type " << (S32) update_result << LL_ENDL;  			break;  	};  } @@ -166,14 +172,30 @@ void LLViewerStatsRecorder::recordRequestCacheMissesEvent(S32 count)  void LLViewerStatsRecorder::writeToLog( F32 interval )  { +    if (!mEnableStatsLogging || !mEnableStatsRecording) +	{ +		return; +	} +  	size_t data_size = 0; -	F64 delta_time = LLTimer::getTotalSeconds() - mLastSnapshotTime; -	S32 total_objects = mObjectCacheHitCount + mObjectCacheMissCrcCount + mObjectCacheMissFullCount + mObjectFullUpdates + mObjectTerseUpdates + mObjectCacheMissRequests + mObjectCacheMissResponses + mObjectCacheUpdateDupes + mObjectCacheUpdateChanges + mObjectCacheUpdateAdds + mObjectCacheUpdateReplacements + mObjectUpdateFailures; +	F64 delta_time = LLFrameTimer::getTotalSeconds() - mLastSnapshotTime; +    if (delta_time < interval) +        return; -	if ( delta_time < interval || total_objects == 0) return; +	if (mSkipSaveIfZeros) +	{ +        S32 total_events = mObjectCacheHitCount + mObjectCacheMissCrcCount + mObjectCacheMissFullCount + mObjectFullUpdates + +                            mObjectTerseUpdates + mObjectCacheMissRequests + mObjectCacheMissResponses + mObjectCacheUpdateDupes + +                            mObjectCacheUpdateChanges + mObjectCacheUpdateAdds + mObjectCacheUpdateReplacements + mObjectUpdateFailures; +		if (total_events == 0) +		{ +            LL_DEBUGS("ILXZeroData") << "ILX: not saving zero data" << LL_ENDL; +            return; +        } +	} -	mLastSnapshotTime = LLTimer::getTotalSeconds(); -	LL_DEBUGS() << "ILX: "  +	mLastSnapshotTime = LLFrameTimer::getTotalSeconds(); +    LL_DEBUGS("ILX") << "ILX: "  		<< mObjectCacheHitCount << " hits, "   		<< mObjectCacheMissFullCount << " full misses, "  		<< mObjectCacheMissCrcCount << " crc misses, " @@ -188,90 +210,136 @@ void LLViewerStatsRecorder::writeToLog( F32 interval )  		<< mObjectUpdateFailures << " update failures"  		<< LL_ENDL; -	if (mObjectCacheFile == NULL) +	if (mStatsFile == NULL)  	{ -		mStartTime = LLTimer::getTotalSeconds(); -		mObjectCacheFile = LLFile::fopen(STATS_FILE_NAME, "wb"); -		if (mObjectCacheFile) -		{	// Write column headers -			std::ostringstream data_msg; -			data_msg << "EventTime(ms)\t" -				<< "Cache Hits\t" -				<< "Cache Full Misses\t" -				<< "Cache Crc Misses\t" -				<< "Full Updates\t" -				<< "Terse Updates\t" -				<< "Cache Miss Requests\t" -				<< "Cache Miss Responses\t" -				<< "Cache Update Dupes\t" -				<< "Cache Update Changes\t" -				<< "Cache Update Adds\t" -				<< "Cache Update Replacements\t" -				<< "Update Failures\t" -				<< "Cache Hits bps\t" -				<< "Cache Full Misses bps\t" -				<< "Cache Crc Misses bps\t" -				<< "Full Updates bps\t" -				<< "Terse Updates bps\t" -				<< "Cache Miss Responses bps\t" -				<< "Texture Fetch bps\t" -				<< "\n"; - -			data_size = data_msg.str().size(); -			if (fwrite(data_msg.str().c_str(), 1, data_size, mObjectCacheFile ) != data_size) +		// Refresh settings +        mInterval        = gSavedSettings.getF32("StatsReportFileInterval"); +        mSkipSaveIfZeros = gSavedSettings.getBOOL("StatsReportSkipZeroDataSaves"); +        mMaxDuration	 = gSavedSettings.getF32("StatsReportMaxDuration"); + +		// Open the data file +        makeStatsFileName(); +        mStatsFile = LLFile::fopen(mStatsFileName, "wb"); + +		if (mStatsFile) +		{ +            LL_INFOS("ILX") << "ILX: Writing update information to " << mStatsFileName << LL_ENDL; + +            mFileOpenTime = LLFrameTimer::getTotalSeconds(); + +            // Write column headers +			std::ostringstream col_headers; +			col_headers << "Time (sec)," +					<< "Regions," +					<< "Active Cached Objects," +					<< "Cache Hits," +					<< "Cache Full Misses," +					<< "Cache Crc Misses," +					<< "Full Updates," +					<< "Terse Updates," +					<< "Cache Miss Requests," +					<< "Cache Miss Responses," +					<< "Cache Update Dupes," +					<< "Cache Update Changes," +					<< "Cache Update Adds," +					<< "Cache Update Replacements," +					<< "Update Failures," +					<< "Texture Count," +                    << "Mesh Load Count," +                    << "\n"; + +			data_size = col_headers.str().size(); +			if (fwrite(col_headers.str().c_str(), 1, data_size, mStatsFile ) != data_size)  			{ -				LL_WARNS() << "failed to write full headers to " << STATS_FILE_NAME << LL_ENDL; +				LL_WARNS() << "failed to write full headers to " << mStatsFileName << LL_ENDL;  			}  		}  		else -		{ -			//LL_WARNS() << "Couldn't open " << STATS_FILE_NAME << " for logging." << LL_ENDL; +		{	// Failed to open file +			LL_WARNS() << "Couldn't open " << mStatsFileName << " for logging, turning off stats recording." << LL_ENDL; +            mEnableStatsLogging = false;  			return;  		}  	} -	std::ostringstream data_msg; - -	data_msg << getTimeSinceStart() -		<< "\t " << mObjectCacheHitCount -		<< "\t" << mObjectCacheMissFullCount -		<< "\t" << mObjectCacheMissCrcCount -		<< "\t" << mObjectFullUpdates -		<< "\t" << mObjectTerseUpdates -		<< "\t" << mObjectCacheMissRequests -		<< "\t" << mObjectCacheMissResponses -		<< "\t" << mObjectCacheUpdateDupes -		<< "\t" << mObjectCacheUpdateChanges -		<< "\t" << mObjectCacheUpdateAdds -		<< "\t" << mObjectCacheUpdateReplacements -		<< "\t" << mObjectUpdateFailures -		<< "\t" << (mObjectCacheHitSize * 8 / delta_time) -		<< "\t" << (mObjectCacheMissFullSize * 8 / delta_time) -		<< "\t" << (mObjectCacheMissCrcSize * 8 / delta_time) -		<< "\t" << (mObjectFullUpdatesSize * 8 / delta_time) -		<< "\t" << (mObjectTerseUpdatesSize * 8 / delta_time) -		<< "\t" << (mObjectCacheMissResponsesSize * 8 / delta_time) -		<< "\t" << (mTextureFetchSize * 8 / delta_time) +	std::ostringstream stats_data; + +	stats_data << getTimeSinceStart() +		<< "," << LLWorld::getInstance()->getRegionList().size() +		<< "," << LLWorld::getInstance()->getNumOfActiveCachedObjects() +		<< "," << mObjectCacheHitCount +		<< "," << mObjectCacheMissFullCount +		<< "," << mObjectCacheMissCrcCount +		<< "," << mObjectFullUpdates +		<< "," << mObjectTerseUpdates +		<< "," << mObjectCacheMissRequests +		<< "," << mObjectCacheMissResponses +		<< "," << mObjectCacheUpdateDupes +		<< "," << mObjectCacheUpdateChanges +		<< "," << mObjectCacheUpdateAdds +		<< "," << mObjectCacheUpdateReplacements +		<< "," << mObjectUpdateFailures +		<< "," << mTextureFetchCount +		<< "," << mMeshLoadedCount  		<< "\n"; -	data_size = data_msg.str().size(); -	if ( data_size != fwrite(data_msg.str().c_str(), 1, data_size, mObjectCacheFile )) +	data_size = stats_data.str().size(); +	if ( data_size != fwrite(stats_data.str().c_str(), 1, data_size, mStatsFile ))  	{ -				LL_WARNS() << "Unable to write complete column data to " << STATS_FILE_NAME << LL_ENDL; -	} +		LL_WARNS() << "Unable to write complete column data to " << mStatsFileName << LL_ENDL; +        closeStatsFile(); +    }  	clearStats(); + +	if (getTimeSinceStart() >= mMaxDuration) +    {  // If file recording has been running for too long, stop it. +        closeStatsFile(); +    } +} + +void LLViewerStatsRecorder::closeStatsFile() +{ +	if (mStatsFile) +	{ +        LL_INFOS("ILX") << "ILX: Stopped writing update information to " << mStatsFileName << " after " << getTimeSinceStart() +                        << " seconds." << LL_ENDL; +        LLFile::close(mStatsFile); +		mStatsFile = NULL; +    } +    mEnableStatsLogging = false; +} + +void LLViewerStatsRecorder::makeStatsFileName() +{ +    // Create filename - tbd: use pid? +#if LL_WINDOWS +    std::string stats_file_name("SLViewerStats-"); +#else +    std::string stats_file_name("slviewerstats-"); +#endif + +    F64         now      = LLFrameTimer::getTotalSeconds(); +    std::string date_str = LLDate(now).asString(); +    std::replace(date_str.begin(), date_str.end(), ':', '-');  // Make it valid for a filename +    stats_file_name.append(date_str); +    stats_file_name.append(".csv"); +    mStatsFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, stats_file_name);  }  F32 LLViewerStatsRecorder::getTimeSinceStart()  { -	return (F32) (LLTimer::getTotalSeconds() - mStartTime); +	return (F32) (LLFrameTimer::getTotalSeconds() - mFileOpenTime);  } -void LLViewerStatsRecorder::recordTextureFetch( S32 msg_size ) +void LLViewerStatsRecorder::recordTextureFetch()  { -	mTextureFetchSize += msg_size; +    mTextureFetchCount += 1;  } +void LLViewerStatsRecorder::recordMeshLoaded() +{ +	mMeshLoadedCount += 1; +} diff --git a/indra/newview/llviewerstatsrecorder.h b/indra/newview/llviewerstatsrecorder.h index c974bea49d..37c08a51cf 100644 --- a/indra/newview/llviewerstatsrecorder.h +++ b/indra/newview/llviewerstatsrecorder.h @@ -31,10 +31,6 @@  // This is a diagnostic class used to record information from the viewer  // for analysis. -// This is normally 0.  Set to 1 to enable viewer stats recording -#define LL_RECORD_VIEWER_STATS	0 - -  #include "llframetimer.h"  #include "llviewerobject.h"  #include "llviewerregion.h" @@ -49,97 +45,130 @@ class LLViewerStatsRecorder : public LLSingleton<LLViewerStatsRecorder>  	~LLViewerStatsRecorder();   public: -	void objectUpdateFailure(U32 local_id, const EObjectUpdateType update_type, S32 msg_size) -	{ -#if LL_RECORD_VIEWER_STATS -		recordObjectUpdateFailure(local_id, update_type, msg_size); -#endif +	// Enable/disable stats recording.  This is broken down into two +	// flags so we can record stats without writing them to the log +	// file.  This is useful to analyzing updates for scene loading. +   void enableObjectStatsRecording(bool enable, bool logging = false); + +	bool isEnabled() const { return mEnableStatsRecording; } +	bool isLogging() const { return mEnableStatsLogging; } + +	void objectUpdateFailure(S32 msg_size) +    { +        if (mEnableStatsRecording) +        { +            recordObjectUpdateFailure(msg_size); +        }  	} -	void cacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type, S32 msg_size) +	void cacheMissEvent(U8 cache_miss_type)  	{ -#if LL_RECORD_VIEWER_STATS -		recordCacheMissEvent(local_id, update_type, cache_miss_type, msg_size); -#endif +        if (mEnableStatsRecording) +        { +            recordCacheMissEvent(cache_miss_type); +        } +    } + +	void cacheHitEvent() +    { +        if (mEnableStatsRecording) +        { +            recordCacheHitEvent(); +        } +    } + +    void objectUpdateEvent(const EObjectUpdateType update_type) +    { +		if (mEnableStatsRecording) +		{ +			recordObjectUpdateEvent(update_type); +		}  	} -	void objectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp, S32 msg_size) -	{ -#if LL_RECORD_VIEWER_STATS -		recordObjectUpdateEvent(local_id, update_type, objectp, msg_size); -#endif +	void cacheFullUpdate(LLViewerRegion::eCacheUpdateResult update_result) +    { +		if (mEnableStatsRecording) +		{ +			recordCacheFullUpdate(update_result); +		}  	} -	void cacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp, S32 msg_size) +	void requestCacheMissesEvent(S32 count)  	{ -#if LL_RECORD_VIEWER_STATS -		recordCacheFullUpdate(local_id, update_type, update_result, objectp, msg_size); -#endif +        if (mEnableStatsRecording) +        { +            recordRequestCacheMissesEvent(count); +        }  	} -	void requestCacheMissesEvent(S32 count) +	void textureFetch()  	{ -#if LL_RECORD_VIEWER_STATS -		recordRequestCacheMissesEvent(count); -#endif +        if (mEnableStatsRecording) +        { +            recordTextureFetch(); +        }  	} -	void textureFetch(S32 msg_size) +	void meshLoaded()  	{ -#if LL_RECORD_VIEWER_STATS -		recordTextureFetch(msg_size); -#endif +		if (mEnableStatsRecording) +		{ +			recordMeshLoaded(); +		}  	} -	void log(F32 interval) +	void idle()  	{ -#if LL_RECORD_VIEWER_STATS -		writeToLog(interval); -#endif +        writeToLog(mInterval);  	}  	F32 getTimeSinceStart();  private: -	void recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type, S32 msg_size); -	void recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type, S32 msg_size); -	void recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp, S32 msg_size); -	void recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp, S32 msg_size); +	void recordObjectUpdateFailure(S32 msg_size); +	void recordCacheMissEvent(U8 cache_miss_type); +	void recordCacheHitEvent(); +	void recordObjectUpdateEvent(const EObjectUpdateType update_type); +	void recordCacheFullUpdate(LLViewerRegion::eCacheUpdateResult update_result);  	void recordRequestCacheMissesEvent(S32 count); -	void recordTextureFetch(S32 msg_size); +	void recordTextureFetch(); +	void recordMeshLoaded();  	void writeToLog(F32 interval); +    void closeStatsFile(); +	void makeStatsFileName();  	static LLViewerStatsRecorder* sInstance; -	LLFILE *	mObjectCacheFile;		// File to write data into +	LLFILE *	mStatsFile;			// File to write data into +    std::string mStatsFileName; +  	LLFrameTimer	mTimer; -	F64			mStartTime; +	F64			mFileOpenTime;  	F64			mLastSnapshotTime; +    F32         mInterval;					// Interval between data log writes +    F32         mMaxDuration;				// Time limit on file + +	bool        mEnableStatsRecording;		// Set to true to enable recording stats data +    bool		mEnableStatsLogging;		// Set true to write stats to log file +	bool		mSkipSaveIfZeros;			// Set true to skip saving stats if all values are zero	  	S32			mObjectCacheHitCount; -	S32			mObjectCacheHitSize;  	S32			mObjectCacheMissFullCount; -	S32			mObjectCacheMissFullSize;  	S32			mObjectCacheMissCrcCount; -	S32			mObjectCacheMissCrcSize;  	S32			mObjectFullUpdates; -	S32			mObjectFullUpdatesSize;  	S32			mObjectTerseUpdates; -	S32			mObjectTerseUpdatesSize;  	S32			mObjectCacheMissRequests;  	S32			mObjectCacheMissResponses; -	S32			mObjectCacheMissResponsesSize;  	S32			mObjectCacheUpdateDupes;  	S32			mObjectCacheUpdateChanges;  	S32			mObjectCacheUpdateAdds;  	S32			mObjectCacheUpdateReplacements;  	S32			mObjectUpdateFailures;  	S32			mObjectUpdateFailuresSize; -	S32			mTextureFetchSize; - +	S32			mTextureFetchCount; +    S32         mMeshLoadedCount;  	void	clearStats();  };  #endif // LLVIEWERSTATSRECORDER_H - diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 7db316f498..07471ed5bb 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3485,14 +3485,24 @@ function="World.EnvPreset"                  <menu_item_call.on_click                   function="Advanced.DumpRegionObjectCache" />              </menu_item_call> -         -<menu_item_call -             label="Interest List: Full Update" -             name="Interest List: Full Update" + +			<menu_item_check +			  label="Interest List 360 Mode" +             name="Interest List: 360 Mode"               shortcut="alt|shift|I"> -                <menu_item_call.on_click -                 function="Advanced.InterestListFullUpdate" /> -            </menu_item_call> +			   <menu_item_check.on_check +				function="Advanced.CheckInterestList360Mode" /> +			   <menu_item_check.on_click +				  function="Advanced.ToggleInterestList360Mode" /> +            </menu_item_check> +			<menu_item_check +             label="Record Stats to File" +             name="Stats Recorder File"> +				<menu_item_check.on_check +                 function="Advanced.CheckStatsRecorder" /> +				<menu_item_check.on_click +                 function="Advanced.ToggleStatsRecorder" /> +			</menu_item_check>          </menu>          <menu           create_jump_keys="true" | 
