diff options
| author | Monty Brandenberg <monty@lindenlab.com> | 2010-11-10 08:44:53 -0800 | 
|---|---|---|
| committer | Monty Brandenberg <monty@lindenlab.com> | 2010-11-10 08:44:53 -0800 | 
| commit | fd2d4dc1b16430edd367a8b0f4162238bbb7e22c (patch) | |
| tree | de1c5a1399ee7f1bedade0e2bb437f1816d968da | |
| parent | 1ed9d997a6c380f71f2da182c8083321e35b5034 (diff) | |
ESC-110 ESC-111
Cleanup passes on the two threaded collectors with better comments
and more complete unit tests.
| -rw-r--r-- | indra/newview/llviewerassetstats.cpp | 172 | ||||
| -rw-r--r-- | indra/newview/llviewerassetstats.h | 152 | ||||
| -rw-r--r-- | indra/newview/tests/llviewerassetstats_test.cpp | 303 | 
3 files changed, 533 insertions, 94 deletions
diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 0852573bbd..a6c4685bf1 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -47,7 +47,7 @@   *   <TBD>   *   * Unit Tests: - *   <TBD> + *   indra/newview/tests/llviewerassetstats_test.cpp   *   */ @@ -55,7 +55,8 @@  // ------------------------------------------------------  // Global data definitions  // ------------------------------------------------------ -LLViewerAssetStats * gViewerAssetStats = NULL; +LLViewerAssetStats * gViewerAssetStatsMain(0); +LLViewerAssetStats * gViewerAssetStatsThread1(0);  // ------------------------------------------------------ @@ -70,6 +71,21 @@ asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool i  }  // ------------------------------------------------------ +// LLViewerAssetStats::PerRegionStats struct definition +// ------------------------------------------------------ +void +LLViewerAssetStats::PerRegionStats::reset() +{ +	for (int i(0); i < LL_ARRAY_SIZE(mRequests); ++i) +	{ +		mRequests[i].mEnqueued.reset(); +		mRequests[i].mDequeued.reset(); +		mRequests[i].mResponse.reset(); +	} +} + + +// ------------------------------------------------------  // LLViewerAssetStats class definition  // ------------------------------------------------------  LLViewerAssetStats::LLViewerAssetStats() @@ -81,20 +97,55 @@ LLViewerAssetStats::LLViewerAssetStats()  void  LLViewerAssetStats::reset()  { -	for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i) +	// Empty the map of all region stats +	mRegionStats.clear(); + +	// If we have a current stats, reset it, otherwise, as at construction, +	// create a new one. +	if (mCurRegionStats)  	{ -		mRequests[i].mEnqueued.reset(); -		mRequests[i].mDequeued.reset(); -		mRequests[i].mResponse.reset(); +		mCurRegionStats->reset();  	} +	else +	{ +		mCurRegionStats = new PerRegionStats(mRegionID); +	} + +	// And add reference to map +	mRegionStats[mRegionID] = mCurRegionStats;  } + +void +LLViewerAssetStats::setRegionID(const LLUUID & region_id) +{ +	if (region_id == mRegionID) +	{ +		// Already active, ignore. +		return; +	} +	 +	PerRegionContainer::iterator new_stats = mRegionStats.find(region_id); +	if (mRegionStats.end() == new_stats) +	{ +		// Haven't seen this region_id before, create a new block make it current. +		mCurRegionStats = new PerRegionStats(region_id); +		mRegionStats[region_id] = mCurRegionStats; +	} +	else +	{ +		mCurRegionStats = new_stats->second; +	} +	mRegionID = region_id; +} + +  void  LLViewerAssetStats::recordGetEnqueued(LLViewerAssetType::EType at, bool with_http, bool is_temp)  {  	const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); -	++mRequests[int(eac)].mEnqueued; +	++(mCurRegionStats->mRequests[int(eac)].mEnqueued);  }  void @@ -102,7 +153,7 @@ LLViewerAssetStats::recordGetDequeued(LLViewerAssetType::EType at, bool with_htt  {  	const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); -	++mRequests[int(eac)].mDequeued; +	++(mCurRegionStats->mRequests[int(eac)].mDequeued);  }  void @@ -110,7 +161,7 @@ LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, bool with_htt  {  	const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); -	mRequests[int(eac)].mResponse.record(duration); +	mCurRegionStats->mRequests[int(eac)].mResponse.record(duration);  }  const LLSD @@ -139,16 +190,33 @@ LLViewerAssetStats::asLLSD() const  	LLSD ret = LLSD::emptyMap(); -	for (int i = 0; i < EVACCount; ++i) +	for (PerRegionContainer::const_iterator it = mRegionStats.begin(); +		 mRegionStats.end() != it; +		 ++it)  	{ -		LLSD & slot = ret[tags[i]]; -		slot = LLSD::emptyMap(); -		slot[enq_tag] = LLSD(S32(mRequests[i].mEnqueued.getCount())); -		slot[deq_tag] = LLSD(S32(mRequests[i].mDequeued.getCount())); -		slot[rcnt_tag] = LLSD(S32(mRequests[i].mResponse.getCount())); -		slot[rmin_tag] = LLSD(mRequests[i].mResponse.getMin()); -		slot[rmax_tag] = LLSD(mRequests[i].mResponse.getMax()); -		slot[rmean_tag] = LLSD(mRequests[i].mResponse.getMean()); +		if (it->first.isNull()) +		{ +			// Never emit NULL UUID in results. +			continue; +		} + +		const PerRegionStats & stats = *it->second; +		 +		LLSD reg_stat = LLSD::emptyMap(); +		 +		for (int i = 0; i < EVACCount; ++i) +		{ +			LLSD & slot = reg_stat[tags[i]]; +			slot = LLSD::emptyMap(); +			slot[enq_tag] = LLSD(S32(stats.mRequests[i].mEnqueued.getCount())); +			slot[deq_tag] = LLSD(S32(stats.mRequests[i].mDequeued.getCount())); +			slot[rcnt_tag] = LLSD(S32(stats.mRequests[i].mResponse.getCount())); +			slot[rmin_tag] = LLSD(stats.mRequests[i].mResponse.getMin()); +			slot[rmax_tag] = LLSD(stats.mRequests[i].mResponse.getMax()); +			slot[rmean_tag] = LLSD(stats.mRequests[i].mResponse.getMean()); +		} + +		ret[it->first.asString()] = reg_stat;  	}  	return ret; @@ -161,31 +229,81 @@ LLViewerAssetStats::asLLSD() const  namespace LLViewerAssetStatsFF  { +// Target thread is elaborated in the function name.  This could +// have been something 'templatey' like specializations iterated +// over a set of constants but with so few, this is clearer I think. + +void +set_region_main(const LLUUID & region_id) +{ +	if (! gViewerAssetStatsMain) +		return; + +	gViewerAssetStatsMain->setRegionID(region_id); +} + +void +record_enqueue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp) +{ +	if (! gViewerAssetStatsMain) +		return; + +	gViewerAssetStatsMain->recordGetEnqueued(at, with_http, is_temp); +} + +void +record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp) +{ +	if (! gViewerAssetStatsMain) +		return; + +	gViewerAssetStatsMain->recordGetDequeued(at, with_http, is_temp); +} + +void +record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration) +{ +	if (! gViewerAssetStatsMain) +		return; + +	gViewerAssetStatsMain->recordGetServiced(at, with_http, is_temp, duration); +} + + +void +set_region_thread1(const LLUUID & region_id) +{ +	if (! gViewerAssetStatsThread1) +		return; + +	gViewerAssetStatsThread1->setRegionID(region_id); +} +  void -record_enqueue(LLViewerAssetType::EType at, bool with_http, bool is_temp) +record_enqueue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp)  { -	if (! gViewerAssetStats) +	if (! gViewerAssetStatsThread1)  		return; -	gViewerAssetStats->recordGetEnqueued(at, with_http, is_temp); +	gViewerAssetStatsThread1->recordGetEnqueued(at, with_http, is_temp);  }  void -record_dequeue(LLViewerAssetType::EType at, bool with_http, bool is_temp) +record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp)  { -	if (! gViewerAssetStats) +	if (! gViewerAssetStatsThread1)  		return; -	gViewerAssetStats->recordGetDequeued(at, with_http, is_temp); +	gViewerAssetStatsThread1->recordGetDequeued(at, with_http, is_temp);  }  void -record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration) +record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration)  { -	if (! gViewerAssetStats) +	if (! gViewerAssetStatsThread1)  		return; -	gViewerAssetStats->recordGetServiced(at, with_http, is_temp, duration); +	gViewerAssetStatsThread1->recordGetServiced(at, with_http, is_temp, duration);  }  } // namespace LLViewerAssetStatsFF diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h index 9d66a1e89b..b8356a5ff5 100644 --- a/indra/newview/llviewerassetstats.h +++ b/indra/newview/llviewerassetstats.h @@ -36,6 +36,8 @@  #include "linden_common.h" +#include "llpointer.h" +#include "llrefcount.h"  #include "llviewerassettype.h"  #include "llviewerassetstorage.h"  #include "llsimplestat.h" @@ -43,50 +45,42 @@  /**   * @class LLViewerAssetStats - * @brief Records events and performance of asset put/get operations. + * @brief Records performance aspects of asset access operations.   * - * The asset system is a combination of common code and server- - * and viewer-overridden derivations.  The common code is presented - * in here as the 'front-end' and deriviations (really the server) - * are presented as 'back-end'.  The distinction isn't perfect as - * there are legacy asset transfer systems which mostly appear - * as front-end stats. + * This facility is derived from a very similar simulator-based + * one, LLSimAssetStats.  It's function is to count asset access + * operations and characterize response times.  Collected data + * are binned in several dimensions: + * + *  - Asset types collapsed into a few aggregated categories + *  - By simulator UUID + *  - By transport mechanism (HTTP vs MessageSystem) + *  - By persistence (temp vs non-temp) + * + * Statistics collected are fairly basic at this point:   * - * Statistics collected are fairly basic:   *  - Counts of enqueue and dequeue operations - *  - Counts of duplicated request fetches   *  - Min/Max/Mean of asset transfer operations   * - * While the stats collection interfaces appear to be fairly - * orthogonal across methods (GET, PUT) and asset types (texture, - * bodypart, etc.), the actual internal collection granularity - * varies greatly.  GET's operations found in the cache are - * treated as a single group as are duplicate requests.  Non- - * cached items are broken down into three groups:  textures, - * wearables (bodyparts, clothing) and the rest.  PUT operations - * are broken down into two categories:  temporary assets and - * non-temp.  Back-end operations do not distinguish asset types, - * only GET, PUT (temp) and PUT (non-temp). - *  - * No coverage for Estate Assets or Inventory Item Assets which use - * some different interface conventions.  It could be expanded to cover - * them. + * This collector differs from the simulator-based on in a + * number of ways: + * + *  - The front-end/back-end distinction doesn't exist in viewer + *    code + *  - Multiple threads must be safely accomodated in the viewer   *   * Access to results is by conversion to an LLSD with some standardized - * key names.  The intent of this structure is to be emitted as + * key names.  The intent of this structure is that it be emitted as   * standard syslog-based metrics formatting where it can be picked   * up by interested parties.   * - * For convenience, a set of free functions in namespace LLAssetStatsFF - * are provided which operate on various counters in a way that - * is highly-compatible with the simulator code. + * For convenience, a set of free functions in namespace + * LLViewerAssetStatsFF is provided for conditional test-and-call + * operations.   */  class LLViewerAssetStats  {  public: -	LLViewerAssetStats(); -	// Default destructor and assignment operator are correct. -	  	enum EViewerAssetCategories  	{  		EVACTextureTempHTTPGet,			//< Texture GETs @@ -100,45 +94,109 @@ public:  		EVACCount						// Must be last  	}; -	 + +	/** +	 * Collected data for a single region visited by the avatar. +	 */ +	class PerRegionStats : public LLRefCount +	{ +	public: +		PerRegionStats(const LLUUID & region_id) +			: LLRefCount(), +			  mRegionID(region_id) +			{ +				reset(); +			} +		 +		void reset(); + +	public: +		LLUUID mRegionID; +		struct +		{ +			LLSimpleStatCounter		mEnqueued; +			LLSimpleStatCounter		mDequeued; +			LLSimpleStatMMM<>		mResponse; +		} mRequests [EVACCount]; +	}; + +public: +	LLViewerAssetStats(); +	// Default destructor is correct. +	LLViewerAssetStats & operator=(const LLViewerAssetStats &);			// Not defined + +	// Clear all metrics data.  This leaves the currently-active region +	// in place but with zero'd data for all metrics.  All other regions +	// are removed from the collection map.  	void reset(); +	// Set hidden region argument and establish context for subsequent +	// collection calls. +	void setRegionID(const LLUUID & region_id); +  	// Non-Cached GET Requests  	void recordGetEnqueued(LLViewerAssetType::EType at, bool with_http, bool is_temp);  	void recordGetDequeued(LLViewerAssetType::EType at, bool with_http, bool is_temp);  	void recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration); -	// Report Generation +	// Retrieve current metrics for all visited regions.  	const LLSD asLLSD() const;  protected: +	typedef std::map<LLUUID, LLPointer<PerRegionStats> > PerRegionContainer; -	struct  -	{ -		LLSimpleStatCounter		mEnqueued; -		LLSimpleStatCounter		mDequeued; -		LLSimpleStatMMM<>		mResponse; -	} mRequests [EVACCount]; +	// Region of the currently-active region.  Always valid but may +	// be a NULL UUID after construction or when explicitly set.  Unchanged +	// by a reset() call. +	LLUUID mRegionID; + +	// Pointer to metrics collection for currently-active region.  Always +	// valid and unchanged after reset() though contents will be changed. +	// Always points to a collection contained in mRegionStats. +	LLPointer<PerRegionStats> mCurRegionStats; + +	// Metrics data for all regions during one collection cycle +	PerRegionContainer mRegionStats;  };  /** - * Expectation is that the simulator and other asset-handling - * code will create a single instance of the stats class and - * make it available here.  The free functions examine this - * for non-zero and perform their functions conditionally.  The - * instance methods themselves make no assumption about this. + * Global stats collectors one for each independent thread where + * assets and other statistics are gathered.  The globals are + * expected to be created at startup time and then picked up by + * their respective threads afterwards.  A set of free functions + * are provided to access methods behind the globals while both + * minimally disrupting visual flow and supplying a description + * of intent. + * + * Expected thread assignments: + * + *  - Main:  main() program execution thread + *  - Thread1:  TextureFetch worker thread   */ -extern LLViewerAssetStats * gViewerAssetStats; +extern LLViewerAssetStats * gViewerAssetStatsMain; + +extern LLViewerAssetStats * gViewerAssetStatsThread1;  namespace LLViewerAssetStatsFF  { -void record_enqueue(LLViewerAssetType::EType at, bool with_http, bool is_temp); +void set_region_main(const LLUUID & region_id); + +void record_enqueue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp); + +void record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp); + +void record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration); + + +void set_region_thread1(const LLUUID & region_id); + +void record_enqueue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp); -void record_dequeue(LLViewerAssetType::EType at, bool with_http, bool is_temp); +void record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp); -void record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration); +void record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration);  } // namespace LLViewerAssetStatsFF diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp index 50d348c7e3..affe16c177 100644 --- a/indra/newview/tests/llviewerassetstats_test.cpp +++ b/indra/newview/tests/llviewerassetstats_test.cpp @@ -38,6 +38,7 @@  #include "lltut.h"  #include "../llviewerassetstats.h" +#include "lluuid.h"  static const char * all_keys[] =   { @@ -73,6 +74,27 @@ static const char * sub_keys[] =  	"resp_mean"  }; +static const LLUUID region1("4e2d81a3-6263-6ffe-ad5c-8ce04bee07e8"); +static const LLUUID region2("68762cc8-b68b-4e45-854b-e830734f2d4a"); + +static bool +is_empty_map(const LLSD & sd) +{ +	return sd.isMap() && 0 == sd.size(); +} + +static bool +is_single_key_map(const LLSD & sd, const std::string & key) +{ +	return sd.isMap() && 1 == sd.size() && sd.has(key); +} + +static bool +is_double_key_map(const LLSD & sd, const std::string & key1, const std::string & key2) +{ +	return sd.isMap() && 2 == sd.size() && sd.has(key1) && sd.has(key2); +} +  namespace tut  {  	struct tst_viewerassetstats_index @@ -86,29 +108,40 @@ namespace tut  	void tst_viewerassetstats_index_object_t::test<1>()  	{  		// Check that helpers aren't bothered by missing global stats -		ensure("Global gViewerAssetStats should be NULL", (NULL == gViewerAssetStats)); +		ensure("Global gViewerAssetStatsMain should be NULL", (NULL == gViewerAssetStatsMain)); -		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_TEXTURE, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); -		LLViewerAssetStatsFF::record_dequeue(LLViewerAssetType::AT_TEXTURE, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); -		LLViewerAssetStatsFF::record_response(LLViewerAssetType::AT_GESTURE, false, false, 12.3); +		LLViewerAssetStatsFF::record_response_main(LLViewerAssetType::AT_GESTURE, false, false, 12.3);  	}  	// Create a non-global instance and check the structure  	template<> template<>  	void tst_viewerassetstats_index_object_t::test<2>()  	{ -		ensure("Global gViewerAssetStats should be NULL", (NULL == gViewerAssetStats)); +		ensure("Global gViewerAssetStatsMain should be NULL", (NULL == gViewerAssetStatsMain));  		LLViewerAssetStats * it = new LLViewerAssetStats(); -		ensure("Global gViewerAssetStats should still be NULL", (NULL == gViewerAssetStats)); -		 -		LLSD sd = it->asLLSD(); -		 -		delete it; +		ensure("Global gViewerAssetStatsMain should still be NULL", (NULL == gViewerAssetStatsMain)); +		LLSD sd_full = it->asLLSD(); + +		// Default (NULL) region ID doesn't produce LLSD results so should +		// get an empty map back from output +		ensure("Null LLSD initially", is_empty_map(sd_full)); + +		// Once the region is set, we will get a response even with no data collection +		it->setRegionID(region1); +		sd_full = it->asLLSD(); +		ensure("Correct single-key LLSD map", is_single_key_map(sd_full, region1.asString())); + +		LLSD sd = sd_full[region1.asString()]; + +		delete it; +			  		// Check the structure of the LLSD  		for (int i = 0; i < LL_ARRAY_SIZE(all_keys); ++i)  		{ @@ -131,8 +164,11 @@ namespace tut  	void tst_viewerassetstats_index_object_t::test<3>()  	{  		LLViewerAssetStats * it = new LLViewerAssetStats(); +		it->setRegionID(region1);  		LLSD sd = it->asLLSD(); +		ensure("Correct single-key LLSD map", is_single_key_map(sd, region1.asString())); +		sd = sd[region1.asString()];  		delete it; @@ -145,15 +181,57 @@ namespace tut  	template<> template<>  	void tst_viewerassetstats_index_object_t::test<4>()  	{ -		gViewerAssetStats = new LLViewerAssetStats(); +		gViewerAssetStatsMain = new LLViewerAssetStats(); +		LLViewerAssetStatsFF::set_region_main(region1); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); + +		LLSD sd = gViewerAssetStatsMain->asLLSD(); +		ensure("Correct single-key LLSD map", is_single_key_map(sd, region1.asString())); +		sd = sd[region1.asString()]; +		 +		// Check a few points on the tree for content +		ensure("sd[get_texture_non_temp_udp][enqueued] is 1", (1 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger())); +		ensure("sd[get_texture_temp_udp][enqueued] is 0", (0 == sd["get_texture_temp_udp"]["enqueued"].asInteger())); +		ensure("sd[get_texture_non_temp_http][enqueued] is 0", (0 == sd["get_texture_non_temp_http"]["enqueued"].asInteger())); +		ensure("sd[get_texture_temp_http][enqueued] is 0", (0 == sd["get_texture_temp_http"]["enqueued"].asInteger())); +		ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger())); + +		// Reset and check zeros... +		// Reset leaves current region in place +		gViewerAssetStatsMain->reset(); +		sd = gViewerAssetStatsMain->asLLSD()[region1.asString()]; +		 +		delete gViewerAssetStatsMain; +		gViewerAssetStatsMain = NULL; + +		ensure("sd[get_texture_non_temp_udp][enqueued] is reset", (0 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger())); +		ensure("sd[get_gesture_udp][dequeued] is reset", (0 == sd["get_gesture_udp"]["dequeued"].asInteger())); +	} + +	// Create two global instances and verify no interactions +	template<> template<> +	void tst_viewerassetstats_index_object_t::test<5>() +	{ +		gViewerAssetStatsThread1 = new LLViewerAssetStats(); +		gViewerAssetStatsMain = new LLViewerAssetStats(); +		LLViewerAssetStatsFF::set_region_main(region1); -		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_TEXTURE, false, false); -		LLViewerAssetStatsFF::record_dequeue(LLViewerAssetType::AT_TEXTURE, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); -		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_BODYPART, false, false); -		LLViewerAssetStatsFF::record_dequeue(LLViewerAssetType::AT_BODYPART, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); -		LLSD sd = gViewerAssetStats->asLLSD(); +		LLSD sd = gViewerAssetStatsThread1->asLLSD(); +		ensure("Other collector is empty", is_empty_map(sd)); +		sd = gViewerAssetStatsMain->asLLSD(); +		ensure("Correct single-key LLSD map", is_single_key_map(sd, region1.asString())); +		sd = sd[region1.asString()];  		// Check a few points on the tree for content  		ensure("sd[get_texture_non_temp_udp][enqueued] is 1", (1 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger())); @@ -163,11 +241,196 @@ namespace tut  		ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));  		// Reset and check zeros... -		gViewerAssetStats->reset(); -		sd = gViewerAssetStats->asLLSD(); +		// Reset leaves current region in place +		gViewerAssetStatsMain->reset(); +		sd = gViewerAssetStatsMain->asLLSD()[region1.asString()]; +		 +		delete gViewerAssetStatsMain; +		gViewerAssetStatsMain = NULL; +		delete gViewerAssetStatsThread1; +		gViewerAssetStatsThread1 = NULL; + +		ensure("sd[get_texture_non_temp_udp][enqueued] is reset", (0 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger())); +		ensure("sd[get_gesture_udp][dequeued] is reset", (0 == sd["get_gesture_udp"]["dequeued"].asInteger())); +	} + +    // Check multiple region collection +	template<> template<> +	void tst_viewerassetstats_index_object_t::test<6>() +	{ +		gViewerAssetStatsMain = new LLViewerAssetStats(); + +		LLViewerAssetStatsFF::set_region_main(region1); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); + +		LLViewerAssetStatsFF::set_region_main(region2); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + +		LLSD sd = gViewerAssetStatsMain->asLLSD(); + +		ensure("Correct double-key LLSD map", is_double_key_map(sd, region1.asString(), region2.asString())); +		LLSD sd1 = sd[region1.asString()]; +		LLSD sd2 = sd[region2.asString()]; +		 +		// Check a few points on the tree for content +		ensure("sd1[get_texture_non_temp_udp][enqueued] is 1", (1 == sd1["get_texture_non_temp_udp"]["enqueued"].asInteger())); +		ensure("sd1[get_texture_temp_udp][enqueued] is 0", (0 == sd1["get_texture_temp_udp"]["enqueued"].asInteger())); +		ensure("sd1[get_texture_non_temp_http][enqueued] is 0", (0 == sd1["get_texture_non_temp_http"]["enqueued"].asInteger())); +		ensure("sd1[get_texture_temp_http][enqueued] is 0", (0 == sd1["get_texture_temp_http"]["enqueued"].asInteger())); +		ensure("sd1[get_gesture_udp][dequeued] is 0", (0 == sd1["get_gesture_udp"]["dequeued"].asInteger())); + +		// Check a few points on the tree for content +		ensure("sd2[get_gesture_udp][enqueued] is 4", (4 == sd2["get_gesture_udp"]["enqueued"].asInteger())); +		ensure("sd2[get_gesture_udp][dequeued] is 0", (0 == sd2["get_gesture_udp"]["dequeued"].asInteger())); +		ensure("sd2[get_texture_non_temp_udp][enqueued] is 0", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger())); + +		// Reset and check zeros... +		// Reset leaves current region in place +		gViewerAssetStatsMain->reset(); +		sd = gViewerAssetStatsMain->asLLSD(); +		ensure("Correct single-key LLSD map", is_single_key_map(sd, region2.asString())); +		sd2 = sd[region2.asString()]; +		 +		delete gViewerAssetStatsMain; +		gViewerAssetStatsMain = NULL; + +		ensure("sd2[get_texture_non_temp_udp][enqueued] is reset", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger())); +		ensure("sd2[get_gesture_udp][enqueued] is reset", (0 == sd2["get_gesture_udp"]["enqueued"].asInteger())); +	} + +    // Check multiple region collection jumping back-and-forth between regions +	template<> template<> +	void tst_viewerassetstats_index_object_t::test<7>() +	{ +		gViewerAssetStatsMain = new LLViewerAssetStats(); + +		LLViewerAssetStatsFF::set_region_main(region1); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); + +		LLViewerAssetStatsFF::set_region_main(region2); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + +		LLViewerAssetStatsFF::set_region_main(region1); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, true, true); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, true, true); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); + +		LLViewerAssetStatsFF::set_region_main(region2); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + +		LLSD sd = gViewerAssetStatsMain->asLLSD(); + +		ensure("Correct double-key LLSD map", is_double_key_map(sd, region1.asString(), region2.asString())); +		LLSD sd1 = sd[region1.asString()]; +		LLSD sd2 = sd[region2.asString()]; +		 +		// Check a few points on the tree for content +		ensure("sd1[get_texture_non_temp_udp][enqueued] is 1", (1 == sd1["get_texture_non_temp_udp"]["enqueued"].asInteger())); +		ensure("sd1[get_texture_temp_udp][enqueued] is 0", (0 == sd1["get_texture_temp_udp"]["enqueued"].asInteger())); +		ensure("sd1[get_texture_non_temp_http][enqueued] is 0", (0 == sd1["get_texture_non_temp_http"]["enqueued"].asInteger())); +		ensure("sd1[get_texture_temp_http][enqueued] is 1", (1 == sd1["get_texture_temp_http"]["enqueued"].asInteger())); +		ensure("sd1[get_gesture_udp][dequeued] is 0", (0 == sd1["get_gesture_udp"]["dequeued"].asInteger())); + +		// Check a few points on the tree for content +		ensure("sd2[get_gesture_udp][enqueued] is 8", (8 == sd2["get_gesture_udp"]["enqueued"].asInteger())); +		ensure("sd2[get_gesture_udp][dequeued] is 0", (0 == sd2["get_gesture_udp"]["dequeued"].asInteger())); +		ensure("sd2[get_texture_non_temp_udp][enqueued] is 0", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger())); + +		// Reset and check zeros... +		// Reset leaves current region in place +		gViewerAssetStatsMain->reset(); +		sd = gViewerAssetStatsMain->asLLSD(); +		ensure("Correct single-key LLSD map", is_single_key_map(sd, region2.asString())); +		sd2 = sd[region2.asString()]; +		 +		delete gViewerAssetStatsMain; +		gViewerAssetStatsMain = NULL; + +		ensure("sd2[get_texture_non_temp_udp][enqueued] is reset", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger())); +		ensure("sd2[get_gesture_udp][enqueued] is reset", (0 == sd2["get_gesture_udp"]["enqueued"].asInteger())); +	} + +	// Non-texture assets ignore transport and persistence flags +	template<> template<> +	void tst_viewerassetstats_index_object_t::test<8>() +	{ +		gViewerAssetStatsThread1 = new LLViewerAssetStats(); +		gViewerAssetStatsMain = new LLViewerAssetStats(); +		LLViewerAssetStatsFF::set_region_main(region1); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, true); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, true); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, true, false); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, true, false); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, true, true); +		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, true, true); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, false, false); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, false, true); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, true, false); + +		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + +		LLSD sd = gViewerAssetStatsThread1->asLLSD(); +		ensure("Other collector is empty", is_empty_map(sd)); +		sd = gViewerAssetStatsMain->asLLSD(); +		ensure("Correct single-key LLSD map", is_single_key_map(sd, region1.asString())); +		sd = sd[region1.asString()]; +		 +		// Check a few points on the tree for content +		ensure("sd[get_gesture_udp][enqueued] is 0", (0 == sd["get_gesture_udp"]["enqueued"].asInteger())); +		ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger())); + +		ensure("sd[get_wearable_udp][enqueued] is 4", (4 == sd["get_wearable_udp"]["enqueued"].asInteger())); +		ensure("sd[get_wearable_udp][dequeued] is 4", (4 == sd["get_wearable_udp"]["dequeued"].asInteger())); + +		ensure("sd[get_other][enqueued] is 4", (4 == sd["get_other"]["enqueued"].asInteger())); +		ensure("sd[get_other][dequeued] is 0", (0 == sd["get_other"]["dequeued"].asInteger())); + +		// Reset and check zeros... +		// Reset leaves current region in place +		gViewerAssetStatsMain->reset(); +		sd = gViewerAssetStatsMain->asLLSD()[region1.asString()]; -		delete gViewerAssetStats; -		gViewerAssetStats = NULL; +		delete gViewerAssetStatsMain; +		gViewerAssetStatsMain = NULL; +		delete gViewerAssetStatsThread1; +		gViewerAssetStatsThread1 = NULL;  		ensure("sd[get_texture_non_temp_udp][enqueued] is reset", (0 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger()));  		ensure("sd[get_gesture_udp][dequeued] is reset", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));  | 
