ESC-110 ESC-111

Cleanup passes on the two threaded collectors with better comments
and more complete unit tests.
meow-7.2.2
Monty Brandenberg 2010-11-10 08:44:53 -08:00
parent 1ed9d997a6
commit fd2d4dc1b1
3 changed files with 533 additions and 94 deletions

View File

@ -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);
// ------------------------------------------------------
@ -69,6 +70,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
// ------------------------------------------------------
@ -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
record_enqueue(LLViewerAssetType::EType at, bool with_http, bool is_temp)
set_region_main(const LLUUID & region_id)
{
if (! gViewerAssetStats)
if (! gViewerAssetStatsMain)
return;
gViewerAssetStats->recordGetEnqueued(at, with_http, is_temp);
gViewerAssetStatsMain->setRegionID(region_id);
}
void
record_dequeue(LLViewerAssetType::EType at, bool with_http, bool is_temp)
record_enqueue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp)
{
if (! gViewerAssetStats)
if (! gViewerAssetStatsMain)
return;
gViewerAssetStats->recordGetDequeued(at, with_http, is_temp);
gViewerAssetStatsMain->recordGetEnqueued(at, with_http, is_temp);
}
void
record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration)
record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp)
{
if (! gViewerAssetStats)
if (! gViewerAssetStatsMain)
return;
gViewerAssetStats->recordGetServiced(at, with_http, is_temp, duration);
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_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp)
{
if (! gViewerAssetStatsThread1)
return;
gViewerAssetStatsThread1->recordGetEnqueued(at, with_http, is_temp);
}
void
record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp)
{
if (! gViewerAssetStatsThread1)
return;
gViewerAssetStatsThread1->recordGetDequeued(at, with_http, is_temp);
}
void
record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration)
{
if (! gViewerAssetStatsThread1)
return;
gViewerAssetStatsThread1->recordGetServiced(at, with_http, is_temp, duration);
}
} // namespace LLViewerAssetStatsFF

View File

@ -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_dequeue(LLViewerAssetType::EType at, bool with_http, bool is_temp);
void record_enqueue_main(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_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_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp);
void record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration);
} // namespace LLViewerAssetStatsFF

View File

@ -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,18 @@ 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(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 = 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 +202,235 @@ 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 gViewerAssetStats;
gViewerAssetStats = NULL;
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_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 = 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()));
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;
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 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()));