From 176ffa54b44f2ef73f23e3252dd439f52fab3265 Mon Sep 17 00:00:00 2001
From: Richard Linden <none@none>
Date: Fri, 19 Oct 2012 19:35:01 -0700
Subject: SH-3405 WIP convert existing stats to lltrace system finished most of
 conversion of llviewerassetstats ported some param block fixes from
 viewer-chui converted viewer asset stats to param block format

---
 indra/llcommon/llinitparam.cpp                  |   5 -
 indra/llcommon/llinitparam.h                    |  43 +-
 indra/llcommon/llsdparam.cpp                    |  45 +-
 indra/llcommon/lltracerecording.h               |   2 +-
 indra/newview/llsimplestat.h                    |  58 +--
 indra/newview/lltexturefetch.cpp                |  48 +-
 indra/newview/llviewerassetstats.cpp            | 627 ++++++++++++++----------
 indra/newview/llviewerassetstats.h              |  90 +++-
 indra/newview/llviewerstats.cpp                 |   4 +-
 indra/newview/llviewerstats.h                   |   3 +-
 indra/newview/tests/llviewerassetstats_test.cpp | 510 ++-----------------
 11 files changed, 623 insertions(+), 812 deletions(-)

diff --git a/indra/llcommon/llinitparam.cpp b/indra/llcommon/llinitparam.cpp
index db72aa19b9..54e98e66f3 100644
--- a/indra/llcommon/llinitparam.cpp
+++ b/indra/llcommon/llinitparam.cpp
@@ -196,12 +196,7 @@ namespace LLInitParam
 			if (serialize_func)
 			{
 				const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
-				// each param descriptor remembers its serial number
-				// so we can inspect the same param under different names
-				// and see that it has the same number
-				name_stack.push_back(std::make_pair("", true));
 				serialize_func(*param, parser, name_stack, diff_param);
-				name_stack.pop_back();
 			}
 		}
 
diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h
index 9a6d1eff5c..b7607e91b9 100644
--- a/indra/llcommon/llinitparam.h
+++ b/indra/llcommon/llinitparam.h
@@ -1156,10 +1156,18 @@ namespace LLInitParam
 
 		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name)
 		{ 
+			Parser::name_stack_range_t new_name_stack_range(name_stack_range);
 			self_t& typed_param = static_cast<self_t&>(param);
 			value_t value;
+
+			// pop first element if empty string
+			if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty())
+			{
+				++new_name_stack_range.first;
+			}
+
 			// no further names in stack, attempt to parse value now
-			if (name_stack_range.first == name_stack_range.second)
+			if (new_name_stack_range.first == new_name_stack_range.second)
 			{
 				// attempt to read value directly
 				if (parser.readValue(value))
@@ -1192,14 +1200,14 @@ namespace LLInitParam
 		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
 		{
 			const self_t& typed_param = static_cast<const self_t&>(param);
-			if (!typed_param.isProvided() || name_stack.empty()) return;
+			if (!typed_param.isProvided()) return;
 
 			for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end();
 				it != end_it;
 				++it)
 			{
 				std::string key = it->getValueName();
-				name_stack.back().second = true;
+				name_stack.push_back(std::make_pair(std::string(), true));
 
 				if(key.empty())
 				// not parsed via name values, write out value directly
@@ -1221,6 +1229,8 @@ namespace LLInitParam
 						break;
 					}
 				}
+
+				name_stack.pop_back();
 			}
 		}
 
@@ -1351,10 +1361,19 @@ namespace LLInitParam
 
 		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) 
 		{ 
+			Parser::name_stack_range_t new_name_stack_range(name_stack_range);
 			self_t& typed_param = static_cast<self_t&>(param);
 			bool new_value = false;
+			bool new_array_value = false;
+
+			// pop first element if empty string
+			if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty())
+			{
+				new_array_value = new_name_stack_range.first->second;
+				++new_name_stack_range.first;
+			}
 
-			if (new_name || typed_param.mValues.empty())
+			if (new_name || new_array_value || typed_param.mValues.empty())
 			{
 				new_value = true;
 				typed_param.mValues.push_back(value_t());
@@ -1363,9 +1382,13 @@ namespace LLInitParam
 			param_value_t& value = typed_param.mValues.back();
 
 			// attempt to parse block...
-			if(value.deserializeBlock(parser, name_stack_range, new_name))
+			if(value.deserializeBlock(parser, new_name_stack_range, new_name))
 			{
 				typed_param.setProvided();
+				if (new_array_value)
+				{
+					name_stack_range.first->second = false;
+				}
 				return true;
 			}
 			else if(name_value_lookup_t::valueNamesExist())
@@ -1379,6 +1402,10 @@ namespace LLInitParam
 					{
 						typed_param.mValues.back().setValueName(name);
 						typed_param.setProvided();
+						if (new_array_value)
+						{
+							name_stack_range.first->second = false;
+						}
 						return true;
 					}
 
@@ -1396,13 +1423,13 @@ namespace LLInitParam
 		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
 		{
 			const self_t& typed_param = static_cast<const self_t&>(param);
-			if (!typed_param.isProvided() || name_stack.empty()) return;
+			if (!typed_param.isProvided()) return;
 
 			for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end();
 				it != end_it;
 				++it)
 			{
-				name_stack.back().second = true;
+				name_stack.push_back(std::make_pair(std::string(), true));
 
 				std::string key = it->getValueName();
 				if (!key.empty())
@@ -1415,6 +1442,8 @@ namespace LLInitParam
 				{
 					it->serializeBlock(parser, name_stack, NULL);
 				}
+
+				name_stack.pop_back();
 			}
 		}
 
diff --git a/indra/llcommon/llsdparam.cpp b/indra/llcommon/llsdparam.cpp
index 0e29873bb0..4b8a8dba5c 100644
--- a/indra/llcommon/llsdparam.cpp
+++ b/indra/llcommon/llsdparam.cpp
@@ -223,10 +223,14 @@ LLSD& LLParamSDParserUtilities::getSDWriteNode(LLSD& input, LLInitParam::Parser:
 	{
 		bool new_traversal = it->second;
 
-		LLSD* child_sd = it->first.empty() ? sd_to_write : &(*sd_to_write)[it->first];
-
-		if (child_sd->isArray())
+		LLSD* child_sd;
+		if (it->first.empty())
+		{
+			child_sd = sd_to_write;
+			if (child_sd->isUndefined())
 		{
+				*child_sd = LLSD::emptyArray();
+			}
 			if (new_traversal)
 			{
 				// write to new element at end
@@ -240,22 +244,7 @@ LLSD& LLParamSDParserUtilities::getSDWriteNode(LLSD& input, LLInitParam::Parser:
 		}
 		else
 		{
-			if (new_traversal 
-				&& child_sd->isDefined() 
-				&& !child_sd->isArray())
-			{
-				// copy child contents into first element of an array
-				LLSD new_array = LLSD::emptyArray();
-				new_array.append(*child_sd);
-				// assign array to slot that previously held the single value
-				*child_sd = new_array;
-				// return next element in that array
-				sd_to_write = &((*child_sd)[1]);
-			}
-			else
-			{
-				sd_to_write = child_sd;
-			}
+			sd_to_write = &(*sd_to_write)[it->first];
 		}
 		it->second = false;
 	}
@@ -283,8 +272,9 @@ void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd, LLI
 			it != sd.endArray();
 			++it)
 		{
-			stack.back().second = true;
+			stack.push_back(make_pair(std::string(), true));
 			readSDValues(cb, *it, stack);
+			stack.pop_back();
 		}
 	}
 	else if (sd.isUndefined())
@@ -315,6 +305,12 @@ namespace LLInitParam
 	// block param interface
 	bool ParamValue<LLSD, TypeValues<LLSD>, false>::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name)
 	{
+		if (name_stack.first == name_stack.second
+			&& p.readValue<LLSD>(mValue))
+		{
+			return true;
+		}
+
 		LLSD& sd = LLParamSDParserUtilities::getSDWriteNode(mValue, name_stack);
 
 		LLSD::String string;
@@ -335,8 +331,11 @@ namespace LLInitParam
 
 	void ParamValue<LLSD, TypeValues<LLSD>, false>::serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block) const
 	{
-		// read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc)
-		Parser::name_stack_t stack;
-		LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, stack);
+		// attempt to write LLSD out directly
+		if (!p.writeValue<LLSD>(mValue, name_stack))
+		{
+			// otherwise read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc)
+			LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, name_stack);
+		}
 	}
 }
diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h
index 25f4f5c721..d3f001ab6a 100644
--- a/indra/llcommon/lltracerecording.h
+++ b/indra/llcommon/lltracerecording.h
@@ -231,7 +231,7 @@ namespace LLTrace
 			return stat.getAccumulator(mMeasurements).getSampleCount();
 		}
 
-		F64 getDuration() const { return mElapsedSeconds; }
+		LLUnit::Seconds<F64> getDuration() const { return mElapsedSeconds; }
 
 		// implementation for LLVCRControlsMixin
 		/*virtual*/ void handleStart();
diff --git a/indra/newview/llsimplestat.h b/indra/newview/llsimplestat.h
index 9d7780c4f9..80ce99b774 100644
--- a/indra/newview/llsimplestat.h
+++ b/indra/newview/llsimplestat.h
@@ -99,43 +99,43 @@ public:
 	 * values back to zero.
 	 */
 	void reset()
-		{
-			mCount = 0;
-			mMin = Value(0);
-			mMax = Value(0);
-			mTotal = Value(0);
-		}
+	{
+		mCount = 0;
+		mMin = Value(0);
+		mMax = Value(0);
+		mTotal = Value(0);
+	}
 
 	void record(Value v)
+	{
+		if (mCount)
+		{
+			mMin = llmin(mMin, v);
+			mMax = llmax(mMax, v);
+		}
+		else
 		{
-			if (mCount)
-			{
-				mMin = llmin(mMin, v);
-				mMax = llmax(mMax, v);
-			}
-			else
-			{
-				mMin = v;
-				mMax = v;
-			}
-			mTotal += v;
-			++mCount;
+			mMin = v;
+			mMax = v;
 		}
+		mTotal += v;
+		++mCount;
+	}
 
 	void merge(const LLSimpleStatMMM<VALUE_T> & src)
+	{
+		if (! mCount)
+		{
+			*this = src;
+		}
+		else if (src.mCount)
 		{
-			if (! mCount)
-			{
-				*this = src;
-			}
-			else if (src.mCount)
-			{
-				mMin = llmin(mMin, src.mMin);
-				mMax = llmax(mMax, src.mMax);
-				mCount += src.mCount;
-				mTotal += src.mTotal;
-			}
+			mMin = llmin(mMin, src.mMin);
+			mMax = llmax(mMax, src.mMax);
+			mCount += src.mCount;
+			mTotal += src.mTotal;
 		}
+	}
 	
 	inline U32 getCount() const		{ return mCount; }
 	inline Value getMin() const		{ return mMin; }
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 903fd6acee..ac83fe0ca8 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -53,6 +53,7 @@
 #include "llviewerstatsrecorder.h"
 #include "llviewerassetstats.h"
 #include "llworld.h"
+#include "llsdparam.h"
 #include "llsdutil.h"
 #include "llstartup.h"
 #include "llviewerstats.h"
@@ -2867,8 +2868,8 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
 
 	}; // class lcl_responder
 	
-	if (! gViewerAssetStatsThread1)
-		return true;
+	//if (! gViewerAssetStatsThread1)
+	//	return true;
 
 	static volatile bool reporting_started(false);
 	static volatile S32 report_sequence(0);
@@ -2878,31 +2879,42 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
 	// but leave it in 'this'.  Destructor will rid us of it.
 	LLViewerAssetStats & main_stats = *mMainStats;
 
-	// Merge existing stats into those from main, convert to LLSD
-	main_stats.merge(*gViewerAssetStatsThread1);
-	LLSD merged_llsd = main_stats.asLLSD(true);
-
-	// Add some additional meta fields to the content
-	merged_llsd["session_id"] = mSessionID;
-	merged_llsd["agent_id"] = mAgentID;
-	merged_llsd["message"] = "ViewerAssetMetrics";					// Identifies the type of metrics
-	merged_llsd["sequence"] = report_sequence;						// Sequence number
-	merged_llsd["initial"] = ! reporting_started;					// Initial data from viewer
-	merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak;		// Break in data prior to this report
-		
+	LLViewerAssetStats::AssetStats stats;
+	main_stats.getStats(stats);
+	//LLSD merged_llsd = main_stats.asLLSD();
+
+	stats.session_id = mSessionID;
+	stats.agent_id = mAgentID;
+	stats.message = "ViewerAssetMetrics";
+	stats.sequence = static_cast<bool>(report_sequence);
+	stats.initial = static_cast<bool>(!reporting_started);
+	stats.break_ = static_cast<bool>(LLTextureFetch::svMetricsDataBreak);
+	//// Add some additional meta fields to the content
+	//merged_llsd["session_id"] = mSessionID;
+	//merged_llsd["agent_id"] = mAgentID;
+	//merged_llsd["message"] = "ViewerAssetMetrics";					// Identifies the type of metrics
+	//merged_llsd["sequence"] = report_sequence;						// Sequence number
+	//merged_llsd["initial"] = ! reporting_started;					// Initial data from viewer
+	//merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak;		// Break in data prior to this report
+	
+	LLSD sd;
+	LLParamSDParser parser;
+	parser.writeSD(sd, stats);
+
 	// Update sequence number
 	if (S32_MAX == ++report_sequence)
 		report_sequence = 0;
 
 	// Limit the size of the stats report if necessary.
-	merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd);
+	
+	sd["truncated"] = truncate_viewer_metrics(10, sd);
 
 	if (! mCapsURL.empty())
 	{
 		LLCurlRequest::headers_t headers;
 		fetcher->getCurlRequest().post(mCapsURL,
 									   headers,
-									   merged_llsd,
+									   sd,
 									   new lcl_responder(fetcher,
 														 report_sequence,
                                                          report_sequence,
@@ -2917,10 +2929,10 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
 	// In QA mode, Metrics submode, log the result for ease of testing
 	if (fetcher->isQAMode())
 	{
-		LL_INFOS("Textures") << ll_pretty_print_sd(merged_llsd) << LL_ENDL;
+		LL_INFOS("Textures") << ll_pretty_print_sd(sd) << LL_ENDL;
 	}
 
-	gViewerAssetStatsThread1->reset();
+	//gViewerAssetStatsThread1->reset();
 
 	return true;
 }
diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp
index cc15d6433f..0b820f866e 100755
--- a/indra/newview/llviewerassetstats.cpp
+++ b/indra/newview/llviewerassetstats.cpp
@@ -31,6 +31,8 @@
 
 #include "stdtypes.h"
 #include "llvoavatar.h"
+#include "llsdparam.h"
+#include "llsdutil.h"
 
 /*
  * Classes and utility functions for per-thread and per-region
@@ -78,6 +80,152 @@
  *
  */
 
+namespace LLViewerAssetStatsFF
+{
+	static EViewerAssetCategories asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp)
+	{
+		// For statistical purposes, we divide GETs into several
+		// populations of asset fetches:
+		//  - textures which are de-prioritized in the asset system
+		//  - wearables (clothing, bodyparts) which directly affect
+		//    user experiences when they log in
+		//  - sounds
+		//  - gestures
+		//  - everything else.
+		//
+		llassert_always(50 == LLViewerAssetType::AT_COUNT);
+
+		// Multiple asset definitions are floating around so this requires some
+		// maintenance and attention.
+		static const EViewerAssetCategories asset_to_bin_map[LLViewerAssetType::AT_COUNT] =
+		{
+			EVACTextureTempHTTPGet,			// (0) AT_TEXTURE
+			EVACSoundUDPGet,				// AT_SOUND
+			EVACOtherGet,					// AT_CALLINGCARD
+			EVACOtherGet,					// AT_LANDMARK
+			EVACOtherGet,					// AT_SCRIPT
+			EVACWearableUDPGet,				// AT_CLOTHING
+			EVACOtherGet,					// AT_OBJECT
+			EVACOtherGet,					// AT_NOTECARD
+			EVACOtherGet,					// AT_CATEGORY
+			EVACOtherGet,					// AT_ROOT_CATEGORY
+			EVACOtherGet,					// (10) AT_LSL_TEXT
+			EVACOtherGet,					// AT_LSL_BYTECODE
+			EVACOtherGet,					// AT_TEXTURE_TGA
+			EVACWearableUDPGet,				// AT_BODYPART
+			EVACOtherGet,					// AT_TRASH
+			EVACOtherGet,					// AT_SNAPSHOT_CATEGORY
+			EVACOtherGet,					// AT_LOST_AND_FOUND
+			EVACSoundUDPGet,				// AT_SOUND_WAV
+			EVACOtherGet,					// AT_IMAGE_TGA
+			EVACOtherGet,					// AT_IMAGE_JPEG
+			EVACGestureUDPGet,				// (20) AT_ANIMATION
+			EVACGestureUDPGet,				// AT_GESTURE
+			EVACOtherGet,					// AT_SIMSTATE
+			EVACOtherGet,					// AT_FAVORITE
+			EVACOtherGet,					// AT_LINK
+			EVACOtherGet,					// AT_LINK_FOLDER
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// (30)
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// (40)
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					//
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// 
+			EVACOtherGet,					// AT_MESH
+			// (50)
+		};
+
+		if (at < 0 || at >= LLViewerAssetType::AT_COUNT)
+		{
+			return EVACOtherGet;
+		}
+		EViewerAssetCategories ret(asset_to_bin_map[at]);
+		if (EVACTextureTempHTTPGet == ret)
+		{
+			// Indexed with [is_temp][with_http]
+			static const EViewerAssetCategories texture_bin_map[2][2] =
+			{
+				{
+					EVACTextureNonTempUDPGet,
+						EVACTextureNonTempHTTPGet,
+				},
+				{
+					EVACTextureTempUDPGet,
+						EVACTextureTempHTTPGet,
+					}
+			};
+
+			ret = texture_bin_map[is_temp][with_http];
+		}
+		return ret;
+	}
+	static LLTrace::Count<> sEnqueued[EVACCount] = {LLTrace::Count<>("enqueuedassetrequeststemptexturehttp", 
+		"Number of temporary texture asset http requests enqueued"),
+		LLTrace::Count<>("enqueuedassetrequeststemptextureudp", 
+		"Number of temporary texture asset udp requests enqueued"),
+		LLTrace::Count<>("enqueuedassetrequestsnontemptexturehttp", 
+		"Number of texture asset http requests enqueued"),
+		LLTrace::Count<>("enqueuedassetrequestsnontemptextureudp", 
+		"Number of texture asset udp requests enqueued"),
+		LLTrace::Count<>("enqueuedassetrequestswearableudp", 
+		"Number of wearable asset requests enqueued"),
+		LLTrace::Count<>("enqueuedassetrequestssoundudp", 
+		"Number of sound asset requests enqueued"),
+		LLTrace::Count<>("enqueuedassetrequestsgestureudp", 
+		"Number of gesture asset requests enqueued"),
+		LLTrace::Count<>("enqueuedassetrequestsother", 
+		"Number of other asset requests enqueued")};
+
+	static LLTrace::Count<> sDequeued[EVACCount] = {LLTrace::Count<>("dequeuedassetrequeststemptexturehttp", 
+		"Number of temporary texture asset http requests dequeued"),
+		LLTrace::Count<>("dequeuedassetrequeststemptextureudp", 
+		"Number of temporary texture asset udp requests dequeued"),
+		LLTrace::Count<>("dequeuedassetrequestsnontemptexturehttp", 
+		"Number of texture asset http requests dequeued"),
+		LLTrace::Count<>("dequeuedassetrequestsnontemptextureudp", 
+		"Number of texture asset udp requests dequeued"),
+		LLTrace::Count<>("dequeuedassetrequestswearableudp", 
+		"Number of wearable asset requests dequeued"),
+		LLTrace::Count<>("dequeuedassetrequestssoundudp", 
+		"Number of sound asset requests dequeued"),
+		LLTrace::Count<>("dequeuedassetrequestsgestureudp", 
+		"Number of gesture asset requests dequeued"),
+		LLTrace::Count<>("dequeuedassetrequestsother", 
+		"Number of other asset requests dequeued")};
+	static LLTrace::Measurement<LLTrace::Seconds> sResponse[EVACCount] = {LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimestemptexturehttp", 
+		"Time spent responding to temporary texture asset http requests"),
+		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimestemptextureudp", 
+		"Time spent responding to temporary texture asset udp requests"),
+		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimesnontemptexturehttp", 
+		"Time spent responding to texture asset http requests"),
+		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimesnontemptextureudp", 
+		"Time spent responding to texture asset udp requests"),
+		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimeswearableudp", 
+		"Time spent responding to wearable asset requests"),
+		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimessoundudp", 
+		"Time spent responding to sound asset requests"),
+		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimesgestureudp", 
+		"Time spent responding to gesture asset requests"),
+		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimesother", 
+		"Time spent responding to other asset requests")};
+}
 
 // ------------------------------------------------------
 // Global data definitions
@@ -88,7 +236,8 @@ LLViewerAssetStats * gViewerAssetStats(0);
 // LLViewerAssetStats class definition
 // ------------------------------------------------------
 LLViewerAssetStats::LLViewerAssetStats()
-	: mRegionHandle(U64(0))
+:	mRegionHandle(U64(0)),
+	mCurRecording(NULL)
 {
 	reset();
 }
@@ -110,7 +259,10 @@ void LLViewerAssetStats::reset()
 	mRegionRecordings.clear();
 
 	// initialize new recording for current region
-	mCurRecording = &mRegionRecordings[mRegionHandle];
+	if (mRegionHandle)
+	{
+		mCurRecording = &mRegionRecordings[mRegionHandle];
+	}
 }
 
 void LLViewerAssetStats::setRegion(region_handle_t region_handle)
@@ -121,9 +273,15 @@ void LLViewerAssetStats::setRegion(region_handle_t region_handle)
 		return;
 	}
 
-	mCurRecording->stop();
-	mCurRecording = &mRegionRecordings[region_handle];
-	mCurRecording->start();
+	if (mCurRecording)
+	{
+		mCurRecording->pause();
+	}
+	if (region_handle)
+	{
+		mCurRecording = &mRegionRecordings[region_handle];
+		mCurRecording->start();
+	}
 
 	mRegionHandle = region_handle;
 }
@@ -136,116 +294,169 @@ void LLViewerAssetStats::recordAvatarStats()
 	mPhaseStats["cloud-or-gray"] = LLViewerStats::PhaseMap::getPhaseStats("cloud-or-gray");
 }
 
-struct AssetRequestType : public LLInitParam::Block<AssetRequestType>
+void LLViewerAssetStats::getStats(AssetStats& stats, bool compact_output)
 {
-	Optional<S32>	enqueued,
-					dequeued,
-					resp_count;
-	Optional<F64>	resp_min,
-					resp_max,
-					resp_mean;
+	using namespace LLViewerAssetStatsFF;
+
+	if (mCurRecording)
+	{
+		mCurRecording->update();
+	}
 	
-	AssetRequestType()
-	:	enqueued("enqueued"),
-		dequeued("dequeued"),
-		resp_count("resp_count"),
-		resp_min("resp_min"),
-		resp_max("resp_max"),
-		resp_mean("resp_mean")
-	{}
-};
-
-struct FPSStats : public LLInitParam::Block<FPSStats>
-{
-	Optional<S32>	count;
-	Optional<F64>	min,
-					max,
-					mean;
-	FPSStats()
-	:	count("count"),
-		min("min"),
-		max("max"),
-		mean("mean")
-	{}
-};
-
-struct RegionStats : public LLInitParam::Block<RegionStats>
-{
-	Optional<AssetRequestType>	get_texture_temp_http,
-								get_texture_temp_udp,
-								get_texture_non_temp_http,
-								get_texture_non_temp_udp,
-								get_wearable_udp,
-								get_sound_udp,
-								get_gesture_udp,
-								get_other;
-	Optional<FPSStats>			fps;
-	Mandatory<S32>				grid_x,
-								grid_y;
-	Mandatory<F64>				duration;
-
-	RegionStats()
-	:	get_texture_temp_http("get_texture_temp_http"),
-		get_texture_temp_udp("get_texture_temp_udp"),
-		get_texture_non_temp_http("get_texture_non_temp_http"),
-		get_texture_non_temp_udp("get_texture_non_temp_udp"),
-		get_wearable_udp("get_wearable_udp"),
-		get_sound_udp("get_sound_udp"),
-		get_gesture_udp("get_gesture_udp"),
-		get_other("get_other"),
-		fps("fps"),
-		grid_x("grid_x"),
-		grid_y("grid_y"),
-		duration("duration")
-	{}
-};
-
-struct AvatarRezState : public LLInitParam::Block<AvatarRezState>
-{
-	Mandatory<S32>	cloud,
-					gray,
-					textured;
-	AvatarRezState()
-	:	cloud("cloud"),
-		gray("gray"),
-		textured("textured")
-	{}
-};
-
-struct AvatarPhaseStats : public LLInitParam::Block<AvatarPhaseStats>
-{
-	Mandatory<LLSD>	cloud,
-					cloud_or_gray;
+	if (mRegionRecordings.empty())
+	{
+		stats.regions.add().empty.setProvided();
+	}
+	else
+	{
+		for (PerRegionRecordingContainer::iterator it = mRegionRecordings.begin(), end_it = mRegionRecordings.end();
+			it != end_it;
+			++it)
+		{
+			RegionStats& r = stats.regions.add();
+			LLTrace::Recording& rec = it->second;
+			if (!compact_output
+				|| rec.getSum(sEnqueued[EVACTextureTempHTTPGet]) 
+				|| rec.getSum(sDequeued[EVACTextureTempHTTPGet])
+				|| rec.getSum(sResponse[EVACTextureTempHTTPGet]).value())
+			{
+				r.get_texture_temp_http	.enqueued(rec.getSum(sEnqueued[EVACTextureTempHTTPGet]))
+										.dequeued(rec.getSum(sDequeued[EVACTextureTempHTTPGet]))
+										.resp_count(rec.getSum(sResponse[EVACTextureTempHTTPGet]).value())
+										.resp_min(rec.getMin(sResponse[EVACTextureTempHTTPGet]).value())
+										.resp_max(rec.getMax(sResponse[EVACTextureTempHTTPGet]).value())
+										.resp_mean(rec.getMean(sResponse[EVACTextureTempHTTPGet]).value());
+			}
+			if (!compact_output
+				|| rec.getSum(sEnqueued[EVACTextureTempUDPGet]) 
+				|| rec.getSum(sDequeued[EVACTextureTempUDPGet])
+				|| rec.getSum(sResponse[EVACTextureTempUDPGet]).value())
+			{
+				r.get_texture_temp_udp	.enqueued(rec.getSum(sEnqueued[EVACTextureTempUDPGet]))
+										.dequeued(rec.getSum(sDequeued[EVACTextureTempUDPGet]))
+										.resp_count(rec.getSum(sResponse[EVACTextureTempUDPGet]).value())
+										.resp_min(rec.getMin(sResponse[EVACTextureTempUDPGet]).value())
+										.resp_max(rec.getMax(sResponse[EVACTextureTempUDPGet]).value())
+										.resp_mean(rec.getMean(sResponse[EVACTextureTempUDPGet]).value());
+			}
+			if (!compact_output
+				|| rec.getSum(sEnqueued[EVACTextureNonTempHTTPGet]) 
+				|| rec.getSum(sDequeued[EVACTextureNonTempHTTPGet])
+				|| rec.getSum(sResponse[EVACTextureNonTempHTTPGet]).value())
+			{
+				r.get_texture_non_temp_http	.enqueued(rec.getSum(sEnqueued[EVACTextureNonTempHTTPGet]))
+											.dequeued(rec.getSum(sDequeued[EVACTextureNonTempHTTPGet]))
+											.resp_count(rec.getSum(sResponse[EVACTextureNonTempHTTPGet]).value())
+											.resp_min(rec.getMin(sResponse[EVACTextureNonTempHTTPGet]).value())
+											.resp_max(rec.getMax(sResponse[EVACTextureNonTempHTTPGet]).value())
+											.resp_mean(rec.getMean(sResponse[EVACTextureNonTempHTTPGet]).value());
+			}
+
+			if (!compact_output
+				|| rec.getSum(sEnqueued[EVACTextureNonTempUDPGet]) 
+				|| rec.getSum(sDequeued[EVACTextureNonTempUDPGet])
+				|| rec.getSum(sResponse[EVACTextureNonTempUDPGet]).value())
+			{
+				r.get_texture_non_temp_udp	.enqueued(rec.getSum(sEnqueued[EVACTextureNonTempUDPGet]))
+											.dequeued(rec.getSum(sDequeued[EVACTextureNonTempUDPGet]))
+											.resp_count(rec.getSum(sResponse[EVACTextureNonTempUDPGet]).value())
+											.resp_min(rec.getMin(sResponse[EVACTextureNonTempUDPGet]).value())
+											.resp_max(rec.getMax(sResponse[EVACTextureNonTempUDPGet]).value())
+											.resp_mean(rec.getMean(sResponse[EVACTextureNonTempUDPGet]).value());
+			}
+
+			if (!compact_output
+				|| rec.getSum(sEnqueued[EVACWearableUDPGet]) 
+				|| rec.getSum(sDequeued[EVACWearableUDPGet])
+				|| rec.getSum(sResponse[EVACWearableUDPGet]).value())
+			{
+				r.get_wearable_udp	.enqueued(rec.getSum(sEnqueued[EVACWearableUDPGet]))
+									.dequeued(rec.getSum(sDequeued[EVACWearableUDPGet]))
+									.resp_count(rec.getSum(sResponse[EVACWearableUDPGet]).value())
+									.resp_min(rec.getMin(sResponse[EVACWearableUDPGet]).value())
+									.resp_max(rec.getMax(sResponse[EVACWearableUDPGet]).value())
+									.resp_mean(rec.getMean(sResponse[EVACWearableUDPGet]).value());
+			}
+
+			if (!compact_output
+				|| rec.getSum(sEnqueued[EVACSoundUDPGet]) 
+				|| rec.getSum(sDequeued[EVACSoundUDPGet])
+				|| rec.getSum(sResponse[EVACSoundUDPGet]).value())
+			{
+				r.get_sound_udp	.enqueued(rec.getSum(sEnqueued[EVACSoundUDPGet]))
+								.dequeued(rec.getSum(sDequeued[EVACSoundUDPGet]))
+								.resp_count(rec.getSum(sResponse[EVACSoundUDPGet]).value())
+								.resp_min(rec.getMin(sResponse[EVACSoundUDPGet]).value())
+								.resp_max(rec.getMax(sResponse[EVACSoundUDPGet]).value())
+								.resp_mean(rec.getMean(sResponse[EVACSoundUDPGet]).value());
+			}
+
+			if (!compact_output
+				|| rec.getSum(sEnqueued[EVACGestureUDPGet]) 
+				|| rec.getSum(sDequeued[EVACGestureUDPGet])
+				|| rec.getSum(sResponse[EVACGestureUDPGet]).value())
+			{
+				r.get_gesture_udp	.enqueued(rec.getSum(sEnqueued[EVACGestureUDPGet]))
+									.dequeued(rec.getSum(sDequeued[EVACGestureUDPGet]))
+									.resp_count(rec.getSum(sResponse[EVACGestureUDPGet]).value())
+									.resp_min(rec.getMin(sResponse[EVACGestureUDPGet]).value())
+									.resp_max(rec.getMax(sResponse[EVACGestureUDPGet]).value())
+									.resp_mean(rec.getMean(sResponse[EVACGestureUDPGet]).value());
+			}
+
+			if (!compact_output
+				|| rec.getSum(sEnqueued[EVACOtherGet]) 
+				|| rec.getSum(sDequeued[EVACOtherGet])
+				|| rec.getSum(sResponse[EVACOtherGet]).value())
+			{
+				r.get_other	.enqueued(rec.getSum(sEnqueued[EVACOtherGet]))
+							.dequeued(rec.getSum(sDequeued[EVACOtherGet]))
+							.resp_count(rec.getSum(sResponse[EVACOtherGet]).value())
+							.resp_min(rec.getMin(sResponse[EVACOtherGet]).value())
+							.resp_max(rec.getMax(sResponse[EVACOtherGet]).value())
+							.resp_mean(rec.getMean(sResponse[EVACOtherGet]).value());
+			}
+
+			S32 fps = rec.getSum(LLStatViewer::FPS_SAMPLE);
+			if (!compact_output || fps != 0)
+			{
+				r.fps.count(fps);
+				r.fps.min(rec.getMin(LLStatViewer::FPS_SAMPLE));
+				r.fps.max(rec.getMax(LLStatViewer::FPS_SAMPLE));
+				r.fps.mean(rec.getMean(LLStatViewer::FPS_SAMPLE));
+			}
+			U32 grid_x(0), grid_y(0);
+			grid_from_region_handle(it->first, &grid_x, &grid_y);
+			r.grid_x(grid_x);
+			r.grid_y(grid_y);
+			r.duration(LLUnit::Microseconds<F64>(rec.getDuration()).value());
+		}
+	}
 
-	AvatarPhaseStats()
-	:	cloud("cloud"),
-		cloud_or_gray("cloud-or-gray")
-	{}
-};
+	stats.duration(mCurRecording ? LLUnit::Microseconds<F64>(mCurRecording->getDuration()).value() : 0.0);
+	//stats.avatar.setProvided(true);
 
-struct AvatarInfo : public LLInitParam::Block<AvatarInfo>
-{
-	Mandatory<AvatarRezState> nearby;
-	Mandatory<AvatarPhaseStats> phase_stats;
+	for (S32 rez_stat=0; rez_stat < mAvatarRezStates.size(); ++rez_stat)
+	{
+		stats.avatar.nearby	.cloud(mAvatarRezStates[0])
+							.gray(mAvatarRezStates[1])
+							.textured(mAvatarRezStates[2]);
+	}
 
-	AvatarInfo()
-	:	nearby("nearby"),
-		phase_stats("phase_stats")
-	{}
-};
+	stats.avatar.phase_stats	.cloud(mPhaseStats["cloud"].asLLSD())
+								.cloud_or_gray(mPhaseStats["cloud-or-gray"].asLLSD());
+}
 
-struct AssetStats : public LLInitParam::Block<AssetStats>
+LLSD LLViewerAssetStats::asLLSD(bool compact_output)
 {
-	Multiple<RegionStats>	regions;
-	Mandatory<F64>			duration;
-
-	AssetStats()
-	:	regions("regions"),
-		duration("duration")
-	{}
-
-};
-
+	LLParamSDParser parser;
+	LLSD sd;
+	AssetStats stats;
+	getStats(stats, compact_output);
+	parser.writeSD(sd, stats);
+	llinfos << ll_pretty_print_sd(sd) << llendl;
+	return sd;
+}
 //LLSD LLViewerAssetStats::asLLSD(bool compact_output)
 //{
 //	// Top-level tags
@@ -358,150 +569,6 @@ struct AssetStats : public LLInitParam::Block<AssetStats>
 
 namespace LLViewerAssetStatsFF
 {
-	static EViewerAssetCategories asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp)
-	{
-		// For statistical purposes, we divide GETs into several
-		// populations of asset fetches:
-		//  - textures which are de-prioritized in the asset system
-		//  - wearables (clothing, bodyparts) which directly affect
-		//    user experiences when they log in
-		//  - sounds
-		//  - gestures
-		//  - everything else.
-		//
-		llassert_always(50 == LLViewerAssetType::AT_COUNT);
-
-		// Multiple asset definitions are floating around so this requires some
-		// maintenance and attention.
-		static const EViewerAssetCategories asset_to_bin_map[LLViewerAssetType::AT_COUNT] =
-		{
-			EVACTextureTempHTTPGet,			// (0) AT_TEXTURE
-			EVACSoundUDPGet,				// AT_SOUND
-			EVACOtherGet,					// AT_CALLINGCARD
-			EVACOtherGet,					// AT_LANDMARK
-			EVACOtherGet,					// AT_SCRIPT
-			EVACWearableUDPGet,				// AT_CLOTHING
-			EVACOtherGet,					// AT_OBJECT
-			EVACOtherGet,					// AT_NOTECARD
-			EVACOtherGet,					// AT_CATEGORY
-			EVACOtherGet,					// AT_ROOT_CATEGORY
-			EVACOtherGet,					// (10) AT_LSL_TEXT
-			EVACOtherGet,					// AT_LSL_BYTECODE
-			EVACOtherGet,					// AT_TEXTURE_TGA
-			EVACWearableUDPGet,				// AT_BODYPART
-			EVACOtherGet,					// AT_TRASH
-			EVACOtherGet,					// AT_SNAPSHOT_CATEGORY
-			EVACOtherGet,					// AT_LOST_AND_FOUND
-			EVACSoundUDPGet,				// AT_SOUND_WAV
-			EVACOtherGet,					// AT_IMAGE_TGA
-			EVACOtherGet,					// AT_IMAGE_JPEG
-			EVACGestureUDPGet,				// (20) AT_ANIMATION
-			EVACGestureUDPGet,				// AT_GESTURE
-			EVACOtherGet,					// AT_SIMSTATE
-			EVACOtherGet,					// AT_FAVORITE
-			EVACOtherGet,					// AT_LINK
-			EVACOtherGet,					// AT_LINK_FOLDER
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// (30)
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// (40)
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					//
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// 
-			EVACOtherGet,					// AT_MESH
-			// (50)
-		};
-
-		if (at < 0 || at >= LLViewerAssetType::AT_COUNT)
-		{
-			return EVACOtherGet;
-		}
-		EViewerAssetCategories ret(asset_to_bin_map[at]);
-		if (EVACTextureTempHTTPGet == ret)
-		{
-			// Indexed with [is_temp][with_http]
-			static const EViewerAssetCategories texture_bin_map[2][2] =
-			{
-				{
-					EVACTextureNonTempUDPGet,
-					EVACTextureNonTempHTTPGet,
-				},
-				{
-					EVACTextureTempUDPGet,
-					EVACTextureTempHTTPGet,
-				}
-			};
-
-			ret = texture_bin_map[is_temp][with_http];
-		}
-		return ret;
-	}
-static LLTrace::Count<> sEnqueued[EVACCount] = {LLTrace::Count<>("enqueuedassetrequeststemptexturehttp", 
-														"Number of temporary texture asset http requests enqueued"),
-													LLTrace::Count<>("enqueuedassetrequeststemptextureudp", 
-														"Number of temporary texture asset udp requests enqueued"),
-													LLTrace::Count<>("enqueuedassetrequestsnontemptexturehttp", 
-														"Number of texture asset http requests enqueued"),
-													LLTrace::Count<>("enqueuedassetrequestsnontemptextureudp", 
-														"Number of texture asset udp requests enqueued"),
-													LLTrace::Count<>("enqueuedassetrequestswearableudp", 
-														"Number of wearable asset requests enqueued"),
-													LLTrace::Count<>("enqueuedassetrequestssoundudp", 
-														"Number of sound asset requests enqueued"),
-													LLTrace::Count<>("enqueuedassetrequestsgestureudp", 
-														"Number of gesture asset requests enqueued"),
-													LLTrace::Count<>("enqueuedassetrequestsother", 
-														"Number of other asset requests enqueued")};
-
-static LLTrace::Count<> sDequeued[EVACCount] = {LLTrace::Count<>("dequeuedassetrequeststemptexturehttp", 
-													"Number of temporary texture asset http requests dequeued"),
-												LLTrace::Count<>("dequeuedassetrequeststemptextureudp", 
-													"Number of temporary texture asset udp requests dequeued"),
-												LLTrace::Count<>("dequeuedassetrequestsnontemptexturehttp", 
-													"Number of texture asset http requests dequeued"),
-												LLTrace::Count<>("dequeuedassetrequestsnontemptextureudp", 
-													"Number of texture asset udp requests dequeued"),
-												LLTrace::Count<>("dequeuedassetrequestswearableudp", 
-													"Number of wearable asset requests dequeued"),
-												LLTrace::Count<>("dequeuedassetrequestssoundudp", 
-													"Number of sound asset requests dequeued"),
-												LLTrace::Count<>("dequeuedassetrequestsgestureudp", 
-													"Number of gesture asset requests dequeued"),
-												LLTrace::Count<>("dequeuedassetrequestsother", 
-													"Number of other asset requests dequeued")};
-static LLTrace::Measurement<LLTrace::Seconds> sResponse[EVACCount] = {LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimestemptexturehttp", 
-																			"Time spent responding to temporary texture asset http requests"),
-																		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimestemptextureudp", 
-																			"Time spent responding to temporary texture asset udp requests"),
-																		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimesnontemptexturehttp", 
-																			"Time spent responding to texture asset http requests"),
-																		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimesnontemptextureudp", 
-																			"Time spent responding to texture asset udp requests"),
-																		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimeswearableudp", 
-																			"Time spent responding to wearable asset requests"),
-																		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimessoundudp", 
-																			"Time spent responding to sound asset requests"),
-																		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimesgestureudp", 
-																			"Time spent responding to gesture asset requests"),
-																		LLTrace::Measurement<LLTrace::Seconds>("assetresponsetimesother", 
-																			"Time spent responding to other asset requests")};
-
 //
 // Target thread is elaborated in the function name.  This could
 // have been something 'templatey' like specializations iterated
@@ -577,3 +644,59 @@ cleanup()
 } // namespace LLViewerAssetStatsFF
 
 
+
+LLViewerAssetStats::AssetRequestType::AssetRequestType() 
+:	enqueued("enqueued"),
+	dequeued("dequeued"),
+	resp_count("resp_count"),
+	resp_min("resp_min"),
+	resp_max("resp_max"),
+	resp_mean("resp_mean")
+{}
+
+LLViewerAssetStats::FPSStats::FPSStats() 
+:	count("count"),
+	min("min"),
+	max("max"),
+	mean("mean")
+{}
+
+LLViewerAssetStats::RegionStats::RegionStats() 
+:	get_texture_temp_http("get_texture_temp_http"),
+	get_texture_temp_udp("get_texture_temp_udp"),
+	get_texture_non_temp_http("get_texture_non_temp_http"),
+	get_texture_non_temp_udp("get_texture_non_temp_udp"),
+	get_wearable_udp("get_wearable_udp"),
+	get_sound_udp("get_sound_udp"),
+	get_gesture_udp("get_gesture_udp"),
+	get_other("get_other"),
+	fps("fps"),
+	grid_x("grid_x"),
+	grid_y("grid_y"),
+	duration("duration")
+{}
+
+LLViewerAssetStats::AvatarRezState::AvatarRezState() 
+:	cloud("cloud"),
+	gray("gray"),
+	textured("textured")
+{}
+
+LLViewerAssetStats::AvatarInfo::AvatarInfo() 
+:	nearby("nearby"),
+	phase_stats("phase_stats")
+{
+
+}
+
+LLViewerAssetStats::AssetStats::AssetStats() 
+:	regions("regions"),
+	duration("duration"),
+	avatar("avatar"),
+	session_id("session_id"),
+	agent_id("agent_id"),
+	message("message"),
+	sequence("sequence"),
+	initial("initial"),
+	break_("break")
+{}
diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h
index 469609ea1f..2f50c52730 100755
--- a/indra/newview/llviewerassetstats.h
+++ b/indra/newview/llviewerassetstats.h
@@ -91,6 +91,90 @@ public:
 	 */
 	typedef U64 region_handle_t;
 
+	struct AssetRequestType : public LLInitParam::Block<AssetRequestType>
+	{
+		Mandatory<S32>	enqueued,
+						dequeued,
+						resp_count;
+		Mandatory<F64>	resp_min,
+						resp_max,
+						resp_mean;
+	
+		AssetRequestType();
+	};
+
+	struct FPSStats : public LLInitParam::Block<FPSStats>
+	{
+		Mandatory<S32>	count;
+		Mandatory<F64>	min,
+						max,
+						mean;
+		FPSStats();
+	};
+
+	struct RegionStats : public LLInitParam::Block<RegionStats>
+	{
+		Optional<LLInitParam::Flag>				empty;
+		Optional<AssetRequestType>	get_texture_temp_http,
+									get_texture_temp_udp,
+									get_texture_non_temp_http,
+									get_texture_non_temp_udp,
+									get_wearable_udp,
+									get_sound_udp,
+									get_gesture_udp,
+									get_other;
+		Optional<FPSStats>			fps;
+		Optional<S32>				grid_x,
+									grid_y;
+		Optional<F64>				duration;
+
+		RegionStats();
+	};
+
+	struct AvatarRezState : public LLInitParam::Block<AvatarRezState>
+	{
+		Mandatory<S32>	cloud,
+						gray,
+						textured;
+		AvatarRezState();
+	};
+
+	struct AvatarPhaseStats : public LLInitParam::Block<AvatarPhaseStats>
+	{
+		Mandatory<LLSD>	cloud,
+						cloud_or_gray;
+
+		AvatarPhaseStats()
+		:	cloud("cloud"),
+			cloud_or_gray("cloud-or-gray")
+		{}
+	};
+
+	struct AvatarInfo : public LLInitParam::Block<AvatarInfo>
+	{
+		Optional<AvatarRezState> nearby;
+		Optional<AvatarPhaseStats> phase_stats;
+
+		AvatarInfo();
+	};
+
+	struct AssetStats : public LLInitParam::Block<AssetStats>
+	{
+		Multiple<RegionStats>	regions;
+		Mandatory<F64>			duration;
+		Mandatory<AvatarInfo>	avatar;
+
+		Mandatory<LLUUID>		session_id,
+								agent_id;
+
+		Mandatory<std::string>	message;
+		Mandatory<S32>			sequence;
+		Mandatory<bool>			initial,
+								break_;
+
+		AssetStats();
+	};
+
 public:
 	LLViewerAssetStats();
 	LLViewerAssetStats(const LLViewerAssetStats &);
@@ -146,11 +230,7 @@ public:
 	//   }
 	// }
 	//
-	// @param	compact_output		If true, omits from conversion any mmm_block
-	//								or stats_block that would contain all zero data.
-	//								Useful for transmission when the receiver knows
-	//								what is expected and will assume zero for missing
-	//								blocks.
+	void getStats(AssetStats& stats, bool compact_output);
 	LLSD asLLSD(bool compact_output);
 
 protected:
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index 03cc9b12e3..04b0c30b40 100755
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -128,7 +128,8 @@ SimMeasurement<>			SIM_TIME_DILATION("simtimedilation", "", LL_SIM_STAT_TIME_DIL
 							SIM_PHYSICS_PINNED_TASKS("physicspinnedtasks", "", LL_SIM_STAT_PHYSICS_PINNED_TASKS),
 							SIM_PHYSICS_LOD_TASKS("physicslodtasks", "", LL_SIM_STAT_PHYSICS_LOD_TASKS);
 
-LLTrace::Measurement<>		NUM_IMAGES("numimagesstat"),
+LLTrace::Measurement<>		FPS_SAMPLE("fpssample"),
+							NUM_IMAGES("numimagesstat"),
 							NUM_RAW_IMAGES("numrawimagesstat"),
 							NUM_OBJECTS("numobjectsstat"),
 							NUM_ACTIVE_OBJECTS("numactiveobjectsstat"),
@@ -343,6 +344,7 @@ void update_statistics()
 	}
 
 	LLStatViewer::FPS.add(1);
+	LLStatViewer::FPS_SAMPLE.sample(LLTrace::get_frame_recording().getTotalRecording().getPerSec(LLStatViewer::FPS));
 	F32 layer_bits = (F32)(gVLManager.getLandBits() + gVLManager.getWindBits() + gVLManager.getCloudBits());
 	LLStatViewer::LAYERS_KBIT.add<LLTrace::Bits>(layer_bits);
 	LLStatViewer::OBJECT_KBIT.add(gObjectData);
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index 78c4b89f71..34731481f5 100755
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -127,7 +127,8 @@ extern SimMeasurement<>						SIM_TIME_DILATION,
 											SIM_PHYSICS_PINNED_TASKS,
 											SIM_PHYSICS_LOD_TASKS;
 
-extern LLTrace::Measurement<>				NUM_IMAGES,
+extern LLTrace::Measurement<>				FPS_SAMPLE,
+											NUM_IMAGES,
 											NUM_RAW_IMAGES,
 											NUM_OBJECTS,
 											NUM_ACTIVE_OBJECTS,
diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp
index e0126ce8d3..f9d30408ac 100755
--- a/indra/newview/tests/llviewerassetstats_test.cpp
+++ b/indra/newview/tests/llviewerassetstats_test.cpp
@@ -37,6 +37,11 @@
 #include "llregionhandle.h"
 #include "../llvoavatar.h"
 
+namespace LLStatViewer
+{
+	LLTrace::Measurement<>		FPS_SAMPLE("fpssample");
+}
+
 void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts)
 {
 	counts.resize(3);
@@ -241,7 +246,7 @@ namespace tut
 	void tst_viewerassetstats_index_object_t::test<1>()
 	{
 		// Check that helpers aren't bothered by missing global stats
-		ensure("Global gViewerAssetStatsMain should be NULL", (NULL == gViewerAssetStatsMain));
+		ensure("Global gViewerAssetStats should be NULL", (NULL == gViewerAssetStats));
 
 		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_TEXTURE, false, false);
 
@@ -254,11 +259,11 @@ namespace tut
 	template<> template<>
 	void tst_viewerassetstats_index_object_t::test<2>()
 	{
-		ensure("Global gViewerAssetStatsMain should be NULL", (NULL == gViewerAssetStatsMain));
+		ensure("Global gViewerAssetStats should be NULL", (NULL == gViewerAssetStats));
 
 		LLViewerAssetStats * it = new LLViewerAssetStats();
 
-		ensure("Global gViewerAssetStatsMain should still be NULL", (NULL == gViewerAssetStatsMain));
+		ensure("Global gViewerAssetStats should still be NULL", (NULL == gViewerAssetStats));
 
 		LLSD sd_full = it->asLLSD(false);
 
@@ -325,7 +330,7 @@ namespace tut
 	template<> template<>
 	void tst_viewerassetstats_index_object_t::test<4>()
 	{
-		gViewerAssetStatsMain = new LLViewerAssetStats();
+		gViewerAssetStats = new LLViewerAssetStats();
 		LLViewerAssetStatsFF::set_region(region1_handle);
 
 		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_TEXTURE, false, false);
@@ -334,7 +339,7 @@ namespace tut
 		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_BODYPART, false, false);
 		LLViewerAssetStatsFF::record_dequeue(LLViewerAssetType::AT_BODYPART, false, false);
 
-		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
+		LLSD sd = gViewerAssetStats->asLLSD(false);
 		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
 		sd = sd["regions"][0];
@@ -348,11 +353,11 @@ namespace tut
 
 		// Reset and check zeros...
 		// Reset leaves current region in place
-		gViewerAssetStatsMain->reset();
-		sd = gViewerAssetStatsMain->asLLSD(false)["regions"][region1_handle_str];
+		gViewerAssetStats->reset();
+		sd = gViewerAssetStats->asLLSD(false)["regions"][region1_handle_str];
 		
-		delete gViewerAssetStatsMain;
-		gViewerAssetStatsMain = NULL;
+		delete gViewerAssetStats;
+		gViewerAssetStats = 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()));
@@ -362,8 +367,7 @@ namespace tut
 	template<> template<>
 	void tst_viewerassetstats_index_object_t::test<5>()
 	{
-		gViewerAssetStatsThread1 = new LLViewerAssetStats();
-		gViewerAssetStatsMain = new LLViewerAssetStats();
+		gViewerAssetStats = new LLViewerAssetStats();
 		LLViewerAssetStatsFF::set_region(region1_handle);
 
 		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_TEXTURE, false, false);
@@ -372,9 +376,7 @@ namespace tut
 		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_BODYPART, false, false);
 		LLViewerAssetStatsFF::record_dequeue(LLViewerAssetType::AT_BODYPART, false, false);
 
-		LLSD sd = gViewerAssetStatsThread1->asLLSD(false);
-		ensure("Other collector is empty", is_no_stats_map(sd));
-		sd = gViewerAssetStatsMain->asLLSD(false);
+		LLSD sd = gViewerAssetStats->asLLSD(false);
 		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
 		sd = sd["regions"][0];
@@ -388,11 +390,11 @@ namespace tut
 
 		// Reset and check zeros...
 		// Reset leaves current region in place
-		gViewerAssetStatsMain->reset();
-		sd = gViewerAssetStatsMain->asLLSD(false)["regions"][0];
+		gViewerAssetStats->reset();
+		sd = gViewerAssetStats->asLLSD(false)["regions"][0];
 		
-		delete gViewerAssetStatsMain;
-		gViewerAssetStatsMain = NULL;
+		delete gViewerAssetStats;
+		gViewerAssetStats = 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()));
@@ -402,7 +404,7 @@ namespace tut
 	template<> template<>
 	void tst_viewerassetstats_index_object_t::test<6>()
 	{
-		gViewerAssetStatsMain = new LLViewerAssetStats();
+		gViewerAssetStats = new LLViewerAssetStats();
 
 		LLViewerAssetStatsFF::set_region(region1_handle);
 
@@ -419,7 +421,7 @@ namespace tut
 		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_GESTURE, false, false);
 		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_GESTURE, false, false);
 
-		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
+		LLSD sd = gViewerAssetStats->asLLSD(false);
 
 		// std::cout << sd << std::endl;
 		
@@ -444,14 +446,14 @@ namespace tut
 
 		// Reset and check zeros...
 		// Reset leaves current region in place
-		gViewerAssetStatsMain->reset();
-		sd = gViewerAssetStatsMain->asLLSD(false);
+		gViewerAssetStats->reset();
+		sd = gViewerAssetStats->asLLSD(false);
 		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions (p2)", is_single_slot_array(sd["regions"], region2_handle));
 		sd2 = sd["regions"][0];
 		
-		delete gViewerAssetStatsMain;
-		gViewerAssetStatsMain = NULL;
+		delete gViewerAssetStats;
+		gViewerAssetStats = 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()));
@@ -461,7 +463,7 @@ namespace tut
 	template<> template<>
 	void tst_viewerassetstats_index_object_t::test<7>()
 	{
-		gViewerAssetStatsMain = new LLViewerAssetStats();
+		gViewerAssetStats = new LLViewerAssetStats();
 
 		LLViewerAssetStatsFF::set_region(region1_handle);
 
@@ -493,7 +495,7 @@ namespace tut
 		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_GESTURE, false, false);
 		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_GESTURE, false, false);
 
-		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
+		LLSD sd = gViewerAssetStats->asLLSD(false);
 
 		ensure("Correct double-key LLSD map root", is_triple_key_map(sd, "duration", "regions", "avatar"));
 		ensure("Correct double-slot LLSD array regions", is_double_slot_array(sd["regions"], region1_handle, region2_handle));
@@ -502,6 +504,8 @@ namespace tut
 		ensure("Region1 is present in results", sd1.isMap());
 		ensure("Region2 is present in results", sd2.isMap());
 		
+		llinfos << ll_pretty_print_sd(sd1) << llendl;
+
 		// 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()));
@@ -516,15 +520,15 @@ namespace tut
 
 		// Reset and check zeros...
 		// Reset leaves current region in place
-		gViewerAssetStatsMain->reset();
-		sd = gViewerAssetStatsMain->asLLSD(false);
+		gViewerAssetStats->reset();
+		sd = gViewerAssetStats->asLLSD(false);
 		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "duration", "regions", "avatar"));
 		ensure("Correct single-slot LLSD array regions (p2)", is_single_slot_array(sd["regions"], region2_handle));
 		sd2 = get_region(sd, region2_handle);
 		ensure("Region2 is present in results", sd2.isMap());
 		
-		delete gViewerAssetStatsMain;
-		gViewerAssetStatsMain = NULL;
+		delete gViewerAssetStats;
+		gViewerAssetStats = NULL;
 
 		ensure_equals("sd2[get_texture_non_temp_udp][enqueued] is reset", sd2["get_texture_non_temp_udp"]["enqueued"].asInteger(), 0);
 		ensure_equals("sd2[get_gesture_udp][enqueued] is reset", sd2["get_gesture_udp"]["enqueued"].asInteger(), 0);
@@ -534,8 +538,7 @@ namespace tut
 	template<> template<>
 	void tst_viewerassetstats_index_object_t::test<8>()
 	{
-		gViewerAssetStatsThread1 = new LLViewerAssetStats();
-		gViewerAssetStatsMain = new LLViewerAssetStats();
+		gViewerAssetStats = new LLViewerAssetStats();
 		LLViewerAssetStatsFF::set_region(region1_handle);
 
 		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_TEXTURE, false, false);
@@ -561,9 +564,7 @@ namespace tut
 
 		LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
 
-		LLSD sd = gViewerAssetStatsThread1->asLLSD(false);
-		ensure("Other collector is empty", is_no_stats_map(sd));
-		sd = gViewerAssetStatsMain->asLLSD(false);
+		LLSD sd = gViewerAssetStats->asLLSD(false);
 		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
 		sd = get_region(sd, region1_handle);
@@ -581,445 +582,14 @@ namespace tut
 
 		// Reset and check zeros...
 		// Reset leaves current region in place
-		gViewerAssetStatsMain->reset();
-		sd = get_region(gViewerAssetStatsMain->asLLSD(false), region1_handle);
+		gViewerAssetStats->reset();
+		sd = get_region(gViewerAssetStats->asLLSD(false), region1_handle);
 		ensure("Region1 is present in results", sd.isMap());
 		
-		delete gViewerAssetStatsMain;
-		gViewerAssetStatsMain = NULL;
-		delete gViewerAssetStatsThread1;
-		gViewerAssetStatsThread1 = NULL;
+		delete gViewerAssetStats;
+		gViewerAssetStats = NULL;
 
 		ensure_equals("sd[get_texture_non_temp_udp][enqueued] is reset", sd["get_texture_non_temp_udp"]["enqueued"].asInteger(), 0);
 		ensure_equals("sd[get_gesture_udp][dequeued] is reset", sd["get_gesture_udp"]["dequeued"].asInteger(), 0);
 	}
-
-
-	// LLViewerAssetStats::merge() basic functions work
-	template<> template<>
-	void tst_viewerassetstats_index_object_t::test<9>()
-	{
-		LLViewerAssetStats s1;
-		LLViewerAssetStats s2;
-
-		s1.setRegion(region1_handle);
-		s2.setRegion(region1_handle);
-
-		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 5000000);
-		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 6000000);
-		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 8000000);
-		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 7000000);
-		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 9000000);
-		
-		s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 2000000);
-		s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 3000000);
-		s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 4000000);
-
-		s2.merge(s1);
-
-		LLSD s2_llsd = get_region(s2.asLLSD(false), region1_handle);
-		ensure("Region1 is present in results", s2_llsd.isMap());
-		
-		ensure_equals("count after merge", s2_llsd["get_texture_temp_http"]["resp_count"].asInteger(), 8);
-		ensure_approximately_equals("min after merge", s2_llsd["get_texture_temp_http"]["resp_min"].asReal(), 2.0, 22);
-		ensure_approximately_equals("max after merge", s2_llsd["get_texture_temp_http"]["resp_max"].asReal(), 9.0, 22);
-		ensure_approximately_equals("max after merge", s2_llsd["get_texture_temp_http"]["resp_mean"].asReal(), 5.5, 22);
-	}
-
-	// LLViewerAssetStats::merge() basic functions work without corrupting source data
-	template<> template<>
-	void tst_viewerassetstats_index_object_t::test<10>()
-	{
-		LLViewerAssetStats s1;
-		LLViewerAssetStats s2;
-
-		s1.setRegion(region1_handle);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 23289200);
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 282900);
-
-		
-		s2.setRegion(region2_handle);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 6500000);
-		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 10000);
-
-		{
-			s2.merge(s1);
-			
-			LLSD src = s1.asLLSD(false);
-			LLSD dst = s2.asLLSD(false);
-
-			ensure_equals("merge src has single region", src["regions"].size(), 1);
-			ensure_equals("merge dst has dual regions", dst["regions"].size(), 2);
-			
-			// Remove time stamps, they're a problem
-			src.erase("duration");
-			src["regions"][0].erase("duration");
-			dst.erase("duration");
-			dst["regions"][0].erase("duration");
-			dst["regions"][1].erase("duration");
-
-			LLSD s1_llsd = get_region(src, region1_handle);
-			ensure("Region1 is present in src", s1_llsd.isMap());
-			LLSD s2_llsd = get_region(dst, region1_handle);
-			ensure("Region1 is present in dst", s2_llsd.isMap());
-
-			ensure("result from src is in dst", llsd_equals(s1_llsd, s2_llsd));
-		}
-
-		s1.setRegion(region1_handle);
-		s2.setRegion(region1_handle);
-		s1.reset();
-		s2.reset();
-		
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 23289200);
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 282900);
-
-		
-		s2.setRegion(region1_handle);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 6500000);
-		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 10000);
-
-		{
-			s2.merge(s1);
-			
-			LLSD src = s1.asLLSD(false);
-			LLSD dst = s2.asLLSD(false);
-
-			ensure_equals("merge src has single region (p2)", src["regions"].size(), 1);
-			ensure_equals("merge dst has single region (p2)", dst["regions"].size(), 1);
-
-			// Remove time stamps, they're a problem
-			src.erase("duration");
-			src["regions"][0].erase("duration");
-			dst.erase("duration");
-			dst["regions"][0].erase("duration");
-			
-			LLSD s1_llsd = get_region(src, region1_handle);
-			ensure("Region1 is present in src", s1_llsd.isMap());
-			LLSD s2_llsd = get_region(dst, region1_handle);
-			ensure("Region1 is present in dst", s2_llsd.isMap());
-
-			ensure_equals("src counts okay (enq)", s1_llsd["get_other"]["enqueued"].asInteger(), 4);
-			ensure_equals("src counts okay (deq)", s1_llsd["get_other"]["dequeued"].asInteger(), 4);
-			ensure_equals("src resp counts okay", s1_llsd["get_other"]["resp_count"].asInteger(), 2);
-			ensure_approximately_equals("src respmin okay", s1_llsd["get_other"]["resp_min"].asReal(), 0.2829, 20);
-			ensure_approximately_equals("src respmax okay", s1_llsd["get_other"]["resp_max"].asReal(), 23.2892, 20);
-			
-			ensure_equals("dst counts okay (enq)", s2_llsd["get_other"]["enqueued"].asInteger(), 12);
-			ensure_equals("src counts okay (deq)", s2_llsd["get_other"]["dequeued"].asInteger(), 11);
-			ensure_equals("dst resp counts okay", s2_llsd["get_other"]["resp_count"].asInteger(), 4);
-			ensure_approximately_equals("dst respmin okay", s2_llsd["get_other"]["resp_min"].asReal(), 0.010, 20);
-			ensure_approximately_equals("dst respmax okay", s2_llsd["get_other"]["resp_max"].asReal(), 23.2892, 20);
-		}
-	}
-
-
-    // Maximum merges are interesting when one side contributes nothing
-	template<> template<>
-	void tst_viewerassetstats_index_object_t::test<11>()
-	{
-		LLViewerAssetStats s1;
-		LLViewerAssetStats s2;
-
-		s1.setRegion(region1_handle);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		// Want to test negative numbers here but have to work in U64
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
-
-		s2.setRegion(region1_handle);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		{
-			s2.merge(s1);
-			
-			LLSD src = s1.asLLSD(false);
-			LLSD dst = s2.asLLSD(false);
-
-			ensure_equals("merge src has single region", src["regions"].size(), 1);
-			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
-			
-			// Remove time stamps, they're a problem
-			src.erase("duration");
-			src["regions"][0].erase("duration");
-			dst.erase("duration");
-			dst["regions"][0].erase("duration");
-
-			LLSD s2_llsd = get_region(dst, region1_handle);
-			ensure("Region1 is present in dst", s2_llsd.isMap());
-			
-			ensure_equals("dst counts come from src only", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
-
-			ensure_approximately_equals("dst maximum with count 0 does not contribute to merged maximum",
-										s2_llsd["get_other"]["resp_max"].asReal(), F64(0.0), 20);
-		}
-
-		// Other way around
-		s1.setRegion(region1_handle);
-		s2.setRegion(region1_handle);
-		s1.reset();
-		s2.reset();
-
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		// Want to test negative numbers here but have to work in U64
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
-
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		{
-			s1.merge(s2);
-			
-			LLSD src = s2.asLLSD(false);
-			LLSD dst = s1.asLLSD(false);
-
-			ensure_equals("merge src has single region", src["regions"].size(), 1);
-			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
-			
-			// Remove time stamps, they're a problem
-			src.erase("duration");
-			src["regions"][0].erase("duration");
-			dst.erase("duration");
-			dst["regions"][0].erase("duration");
-
-			LLSD s2_llsd = get_region(dst, region1_handle);
-			ensure("Region1 is present in dst", s2_llsd.isMap());
-
-			ensure_equals("dst counts come from src only (flipped)", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
-
-			ensure_approximately_equals("dst maximum with count 0 does not contribute to merged maximum (flipped)",
-										s2_llsd["get_other"]["resp_max"].asReal(), F64(0.0), 20);
-		}
-	}
-
-    // Minimum merges are interesting when one side contributes nothing
-	template<> template<>
-	void tst_viewerassetstats_index_object_t::test<12>()
-	{
-		LLViewerAssetStats s1;
-		LLViewerAssetStats s2;
-
-		s1.setRegion(region1_handle);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 3800000);
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2700000);
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2900000);
-
-		s2.setRegion(region1_handle);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		{
-			s2.merge(s1);
-			
-			LLSD src = s1.asLLSD(false);
-			LLSD dst = s2.asLLSD(false);
-
-			ensure_equals("merge src has single region", src["regions"].size(), 1);
-			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
-			
-			// Remove time stamps, they're a problem
-			src.erase("duration");
-			src["regions"][0].erase("duration");
-			dst.erase("duration");
-			dst["regions"][0].erase("duration");
-
-			LLSD s2_llsd = get_region(dst, region1_handle);
-			ensure("Region1 is present in dst", s2_llsd.isMap());
-
-			ensure_equals("dst counts come from src only", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
-
-			ensure_approximately_equals("dst minimum with count 0 does not contribute to merged minimum",
-										s2_llsd["get_other"]["resp_min"].asReal(), F64(2.7), 20);
-		}
-
-		// Other way around
-		s1.setRegion(region1_handle);
-		s2.setRegion(region1_handle);
-		s1.reset();
-		s2.reset();
-
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 3800000);
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2700000);
-		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2900000);
-
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
-
-		{
-			s1.merge(s2);
-			
-			LLSD src = s2.asLLSD(false);
-			LLSD dst = s1.asLLSD(false);
-
-			ensure_equals("merge src has single region", src["regions"].size(), 1);
-			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
-			
-			// Remove time stamps, they're a problem
-			src.erase("duration");
-			src["regions"][0].erase("duration");
-			dst.erase("duration");
-			dst["regions"][0].erase("duration");
-
-			LLSD s2_llsd = get_region(dst, region1_handle);
-			ensure("Region1 is present in dst", s2_llsd.isMap());
-
-			ensure_equals("dst counts come from src only (flipped)", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
-
-			ensure_approximately_equals("dst minimum with count 0 does not contribute to merged minimum (flipped)",
-										s2_llsd["get_other"]["resp_min"].asReal(), F64(2.7), 20);
-		}
-	}
-
 }
-- 
cgit v1.2.3