Catch up with LL regarding viewer stat recording.

This commit is contained in:
Shyotl
2013-02-14 17:45:10 -06:00
parent 2f5ab6f8e8
commit 51cbb8d6bd
15 changed files with 475 additions and 970 deletions

View File

@@ -53,6 +53,7 @@
#include "llviewertexture.h"
#include "llviewerregion.h"
#include "llviewerstats.h"
#include "llviewerstatsrecorder.h"
#include "llworld.h"
#include "llstartup.h"
#include "llbuffer.h"
@@ -61,6 +62,9 @@ class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy HTTPGetResponder_timeout;
extern AIHTTPTimeoutPolicy lcl_responder_timeout;
LLStat LLTextureFetch::sCacheHitRate("texture_cache_hits", 128);
LLStat LLTextureFetch::sCacheReadLatency("texture_cache_read_latency", 128);
//////////////////////////////////////////////////////////////////////////////
class LLTextureFetchWorker : public LLWorkerClass
{
@@ -250,6 +254,8 @@ private:
S32 mDecodedDiscard;
LLFrameTimer mRequestedTimer;
LLFrameTimer mFetchTimer;
LLTimer mCacheReadTimer;
F32 mCacheReadTime;
LLTextureCache::handle_t mCacheReadHandle;
LLTextureCache::handle_t mCacheWriteHandle;
std::vector<U8> mHttpBuffer;
@@ -289,9 +295,6 @@ private:
S32 mLastPacket;
U16 mTotalPackets;
U8 mImageCodec;
#if HTTP_METRICS
LLViewerAssetStats::duration_t mMetricsStartTime;
#endif
};
//////////////////////////////////////////////////////////////////////////////
@@ -384,20 +387,6 @@ public:
}
mFetcher->removeFromHTTPQueue(mID, data_size);
#if HTTP_METRICS
if (worker->mMetricsStartTime)
{
LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
true,
LLImageBase::TYPE_AVATAR_BAKE == worker->mType,
LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime);
worker->mMetricsStartTime = 0;
}
LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
true,
LLImageBase::TYPE_AVATAR_BAKE == worker->mType);
#endif
}
else
{
@@ -514,230 +503,6 @@ static bool sgConnectionThrottle() {
}
#endif
#if HTTP_METRICS
// Cross-thread messaging for asset metrics.
/**
* @brief Base class for cross-thread requests made of the fetcher
*
* I believe the intent of the LLQueuedThread class was to
* have these operations derived from LLQueuedThread::QueuedRequest
* but the texture fetcher has elected to manage the queue
* in its own manner. So these are free-standing objects which are
* managed in simple FIFO order on the mCommands queue of the
* LLTextureFetch object.
*
* What each represents is a simple command sent from an
* outside thread into the TextureFetch thread to be processed
* in order and in a timely fashion (though not an absolute
* higher priority than other operations of the thread).
* Each operation derives a new class from the base customizing
* members, constructors and the doWork() method to effect
* the command.
*
* The flow is one-directional. There are two global instances
* of the LLViewerAssetStats collector, one for the main program's
* thread pointed to by gViewerAssetStatsMain and one for the
* TextureFetch thread pointed to by gViewerAssetStatsThread1.
* Common operations has each thread recording metrics events
* into the respective collector unconcerned with locking and
* the state of any other thread. But when the agent moves into
* a different region or the metrics timer expires and a report
* needs to be sent back to the grid, messaging across threads
* is required to distribute data and perform global actions.
* In pseudo-UML, it looks like:
*
* Main Thread1
* . .
* . .
* +-----+ .
* | AM | .
* +--+--+ .
* +-------+ | .
* | Main | +--+--+ .
* | | | SRE |---. .
* | Stats | +-----+ \ .
* | | | \ (uuid) +-----+
* | Coll. | +--+--+ `-------->| SR |
* +-------+ | MSC | +--+--+
* | ^ +-----+ |
* | | (uuid) / . +-----+ (uuid)
* | `--------' . | MSC |---------.
* | . +-----+ |
* | +-----+ . v
* | | TE | . +-------+
* | +--+--+ . | Thd1 |
* | | . | |
* | +-----+ . | Stats |
* `--------->| RSC | . | |
* +--+--+ . | Coll. |
* | . +-------+
* +--+--+ . |
* | SME |---. . |
* +-----+ \ . |
* . \ (clone) +-----+ |
* . `-------->| SM | |
* . +--+--+ |
* . | |
* . +-----+ |
* . | RSC |<--------'
* . +-----+
* . |
* . +-----+
* . | CP |--> HTTP POST
* . +-----+
* . .
* . .
*
*
* Key:
*
* SRE - Set Region Enqueued. Enqueue a 'Set Region' command in
* the other thread providing the new UUID of the region.
* TFReqSetRegion carries the data.
* SR - Set Region. New region UUID is sent to the thread-local
* collector.
* SME - Send Metrics Enqueued. Enqueue a 'Send Metrics' command
* including an ownership transfer of a cloned LLViewerAssetStats.
* TFReqSendMetrics carries the data.
* SM - Send Metrics. Global metrics reporting operation. Takes
* the cloned stats from the command, merges it with the
* thread's local stats, converts to LLSD and sends it on
* to the grid.
* AM - Agent Moved. Agent has completed some sort of move to a
* new region.
* TE - Timer Expired. Metrics timer has expired (on the order
* of 10 minutes).
* CP - CURL Post
* MSC - Modify Stats Collector. State change in the thread-local
* collector. Typically a region change which affects the
* global pointers used to find the 'current stats'.
* RSC - Read Stats Collector. Extract collector data cloning it
* (i.e. deep copy) when necessary.
*
*/
class LLTextureFetch::TFRequest // : public LLQueuedThread::QueuedRequest
{
public:
// Default ctors and assignment operator are correct.
virtual ~TFRequest()
{}
// Patterned after QueuedRequest's method but expected behavior
// is different. Always expected to complete on the first call
// and work dispatcher will assume the same and delete the
// request after invocation.
virtual bool doWork(LLTextureFetch * fetcher) = 0;
};
namespace
{
/**
* @brief Implements a 'Set Region' cross-thread command.
*
* When an agent moves to a new region, subsequent metrics need
* to be binned into a new or existing stats collection in 1:1
* relationship with the region. We communicate this region
* change across the threads involved in the communication with
* this message.
*
* Corresponds to LLTextureFetch::commandSetRegion()
*/
class TFReqSetRegion : public LLTextureFetch::TFRequest
{
public:
TFReqSetRegion(U64 region_handle)
: LLTextureFetch::TFRequest(),
mRegionHandle(region_handle)
{}
TFReqSetRegion & operator=(const TFReqSetRegion &); // Not defined
virtual ~TFReqSetRegion()
{}
virtual bool doWork(LLTextureFetch * fetcher);
public:
const U64 mRegionHandle;
};
/**
* @brief Implements a 'Send Metrics' cross-thread command.
*
* This is the big operation. The main thread gathers metrics
* for a period of minutes into LLViewerAssetStats and other
* objects then makes a snapshot of the data by cloning the
* collector. This command transfers the clone, along with a few
* additional arguments (UUIDs), handing ownership to the
* TextureFetch thread. It then merges its own data into the
* cloned copy, converts to LLSD and kicks off an HTTP POST of
* the resulting data to the currently active metrics collector.
*
* Corresponds to LLTextureFetch::commandSendMetrics()
*/
class TFReqSendMetrics : public LLTextureFetch::TFRequest
{
public:
/**
* Construct the 'Send Metrics' command to have the TextureFetch
* thread add and log metrics data.
*
* @param caps_url URL of a "ViewerMetrics" Caps target
* to receive the data. Does not have to
* be associated with a particular region.
*
* @param session_id UUID of the agent's session.
*
* @param agent_id UUID of the agent. (Being pure here...)
*
* @param main_stats Pointer to a clone of the main thread's
* LLViewerAssetStats data. Thread1 takes
* ownership of the copy and disposes of it
* when done.
*/
TFReqSendMetrics(const std::string & caps_url,
const LLUUID & session_id,
const LLUUID & agent_id,
LLViewerAssetStats * main_stats)
: LLTextureFetch::TFRequest(),
mCapsURL(caps_url),
mSessionID(session_id),
mAgentID(agent_id),
mMainStats(main_stats)
{}
TFReqSendMetrics & operator=(const TFReqSendMetrics &); // Not defined
virtual ~TFReqSendMetrics();
virtual bool doWork(LLTextureFetch * fetcher);
public:
const std::string mCapsURL;
const LLUUID mSessionID;
const LLUUID mAgentID;
LLViewerAssetStats * mMainStats;
};
/*
* Examines the merged viewer metrics report and if found to be too long,
* will attempt to truncate it in some reasonable fashion.
*
* @param max_regions Limit of regions allowed in report.
*
* @param metrics Full, merged viewer metrics report.
*
* @returns If data was truncated, returns true.
*/
bool truncate_viewer_metrics(int max_regions, LLSD & metrics);
} // end of anonymous namespace
#endif
//////////////////////////////////////////////////////////////////////////////
//static
@@ -757,11 +522,6 @@ const char* LLTextureFetchWorker::sStateDescs[] = {
"DONE",
};
#if HTTP_METRICS
// static
volatile bool LLTextureFetch::svMetricsDataBreak(true); // Start with a data break
#endif
// called from MAIN THREAD
LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
@@ -786,6 +546,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
mRequestedDiscard(-1),
mLoadedDiscard(-1),
mDecodedDiscard(-1),
mCacheReadTime(0.f),
mCacheReadHandle(LLTextureCache::nullHandle()),
mCacheWriteHandle(LLTextureCache::nullHandle()),
mRequestedSize(0),
@@ -810,9 +571,6 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
mLastPacket(-1),
mTotalPackets(0),
mImageCodec(IMG_CODEC_INVALID)
#if HTTP_METRICS
,mMetricsStartTime(0)
#endif
{
mCanUseNET = mUrl.empty() ;
@@ -1064,6 +822,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority,
offset, size, responder);
mCacheReadTimer.reset();
}
else if (mUrl.empty())
{
@@ -1072,8 +831,9 @@ bool LLTextureFetchWorker::doWork(S32 param)
CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
offset, size, responder);
mCacheReadTimer.reset();
}
else if(mCanUseHTTP)
else if(!mUrl.empty() && mCanUseHTTP)
{
if (!(mUrl.compare(0, 7, "http://") == 0))
{
@@ -1101,6 +861,9 @@ bool LLTextureFetchWorker::doWork(S32 param)
}
else
{
//
//This should never happen
//
return false;
}
}
@@ -1124,7 +887,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize()
<< " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight())
<< " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
// fall through
LLTextureFetch::sCacheHitRate.addValue(100.f);
}
else
{
@@ -1139,7 +902,9 @@ bool LLTextureFetchWorker::doWork(S32 param)
LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL;
mState = LOAD_FROM_NETWORK;
}
// fall through
LLTextureFetch::sCacheHitRate.addValue(0.f);
}
}
@@ -1198,15 +963,6 @@ bool LLTextureFetchWorker::doWork(S32 param)
mRequestedDiscard = mDesiredDiscard;
mSentRequest = QUEUED;
mFetcher->addToNetworkQueue(this);
#if HTTP_METRICS
if (! mMetricsStartTime)
{
mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
}
LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
false,
LLImageBase::TYPE_AVATAR_BAKE == mType);
#endif
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
return false;
@@ -1217,12 +973,6 @@ bool LLTextureFetchWorker::doWork(S32 param)
//llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end());
// Make certain this is in the network queue
//mFetcher->addToNetworkQueue(this);
//if (! mMetricsStartTime)
//{
// mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
//}
//LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false,
// LLImageBase::TYPE_AVATAR_BAKE == mType);
//setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
return false;
}
@@ -1247,33 +997,10 @@ bool LLTextureFetchWorker::doWork(S32 param)
setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
mState = DECODE_IMAGE;
mWriteToCacheState = SHOULD_WRITE;
#if HTTP_METRICS
if (mMetricsStartTime)
{
LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
false,
LLImageBase::TYPE_AVATAR_BAKE == mType,
LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime);
mMetricsStartTime = 0;
}
LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
false,
LLImageBase::TYPE_AVATAR_BAKE == mType);
#endif
}
else
{
mFetcher->addToNetworkQueue(this); // failsafe
#if HTTP_METRICS
if (! mMetricsStartTime)
{
mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
}
LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
false,
LLImageBase::TYPE_AVATAR_BAKE == mType);
#endif
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
}
return false;
@@ -1340,15 +1067,6 @@ bool LLTextureFetchWorker::doWork(S32 param)
mState = WAIT_HTTP_REQ;
mFetcher->addToHTTPQueue(mID);
#if HTTP_METRICS
if (! mMetricsStartTime)
{
mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
}
LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
true,
LLImageBase::TYPE_AVATAR_BAKE == mType);
#endif
if(mRequestedOffset>0)
{
@@ -1573,11 +1291,12 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == DECODE_IMAGE)
{
static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled");
if(textures_decode_disabled)
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
if (textures_decode_disabled)
{
// for debug use, don't decode
mState = DONE;
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
return true;
}
@@ -1585,7 +1304,6 @@ bool LLTextureFetchWorker::doWork(S32 param)
{
// We aborted, don't decode
mState = DONE;
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
return true;
}
@@ -1595,7 +1313,6 @@ bool LLTextureFetchWorker::doWork(S32 param)
//abort, don't decode
mState = DONE;
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
return true;
}
if (mLoadedDiscard < 0)
@@ -1604,10 +1321,9 @@ bool LLTextureFetchWorker::doWork(S32 param)
//abort, don't decode
mState = DONE;
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
return true;
}
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
mRawImage = NULL;
mAuxImage = NULL;
llassert_always(mFormattedImage.notNull());
@@ -1899,6 +1615,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL;
if (data_size > 0)
{
LLViewerStatsRecorder::instance().textureFetch(data_size);
// *TODO: set the formatted image data here directly to avoid the copy
llassert(mHttpBuffer.empty());
mHttpBuffer.resize(data_size);
@@ -1932,6 +1649,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
mLoaded = TRUE;
setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
LLViewerStatsRecorder::instance().log(0.2f);
return data_size ;
}
@@ -2017,6 +1735,7 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag
mDecoded = TRUE;
// llinfos << mID << " : DECODE COMPLETE " << llendl;
setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
mCacheReadTime = mCacheReadTimer.getElapsedTimeF32();
}
//////////////////////////////////////////////////////////////////////////////
@@ -2058,13 +1777,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
mTextureBandwidth(0),
mHTTPTextureBits(0),
mTotalHTTPRequests(0)
#if HTTP_METRICS
,mQAMode(qa_mode)
#endif
{
#if HTTP_METRICS
mCurlPOSTRequestCount = 0;
#endif
mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
}
@@ -2072,14 +1785,6 @@ LLTextureFetch::~LLTextureFetch()
{
clearDeleteList() ;
#if HTTP_METRICS
while (! mCommands.empty())
{
TFRequest * req(mCommands.front());
mCommands.erase(mCommands.begin());
delete req;
}
#endif
// ~LLQueuedThread() called here
}
@@ -2341,6 +2046,11 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
discard_level = worker->mDecodedDiscard;
raw = worker->mRawImage;
aux = worker->mAuxImage;
F32 cache_read_time = worker->mCacheReadTime;
if (cache_read_time != 0.f)
{
sCacheReadLatency.addValue(cache_read_time * 1000.f);
}
res = true;
LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL;
worker->unlockWorkMutex();
@@ -2393,10 +2103,6 @@ S32 LLTextureFetch::getPending()
LLMutexLock lock(&mQueueMutex);
res = mRequestQueue.size();
#if HTTP_METRICS
res += mCurlPOSTRequestCount;
res += mCommands.size();
#endif
}
unlockData();
return res;
@@ -2414,22 +2120,7 @@ bool LLTextureFetch::runCondition()
//
// Changes here may need to be reflected in getPending().
#if HTTP_METRICS
bool have_no_commands(false);
{
LLMutexLock lock(&mQueueMutex);
have_no_commands = mCommands.empty();
}
bool have_no_curl_requests(0 == mCurlPOSTRequestCount);
return ! (have_no_commands
&& have_no_curl_requests
&& (mRequestQueue.empty() && mIdleThread)); // From base class
#else
return !(mRequestQueue.empty() && mIdleThread);
#endif
return ! (mRequestQueue.empty() && mIdleThread); // From base class
}
//////////////////////////////////////////////////////////////////////////////
@@ -2437,10 +2128,6 @@ bool LLTextureFetch::runCondition()
// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs)
void LLTextureFetch::commonUpdate()
{
#if HTTP_METRICS
// Run a cross-thread command, if any.
cmdDoWork();
#endif
}
@@ -2799,11 +2486,16 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8
mNetworkQueueMutex.unlock() ;
return false;
}
LLViewerStatsRecorder::instance().textureFetch(data_size);
LLViewerStatsRecorder::instance().log(0.1f);
worker->lockWorkMutex();
// Copy header data into image object
worker->mImageCodec = codec;
worker->mTotalPackets = packets;
// Copy header data into image object
worker->mImageCodec = codec;
worker->mTotalPackets = packets;
worker->mFileSize = (S32)totalbytes;
llassert_always(totalbytes > 0);
llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize);
@@ -2844,8 +2536,12 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1
mNetworkQueueMutex.unlock() ;
return false;
}
LLViewerStatsRecorder::instance().textureFetch(data_size);
LLViewerStatsRecorder::instance().log(0.1f);
worker->lockWorkMutex();
res = worker->insertPacket(packet_num, data, data_size);
@@ -2959,277 +2655,12 @@ void LLTextureFetch::dump()
<< " STATE: " << worker->sStateDescs[worker->mState]
<< llendl;
}
}
//////////////////////////////////////////////////////////////////////////////
// cross-thread command methods
#if HTTP_METRICS
void LLTextureFetch::commandSetRegion(U64 region_handle)
{
TFReqSetRegion * req = new TFReqSetRegion(region_handle);
cmdEnqueue(req);
}
void LLTextureFetch::commandSendMetrics(const std::string & caps_url,
const LLUUID & session_id,
const LLUUID & agent_id,
LLViewerAssetStats * main_stats)
{
TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats);
cmdEnqueue(req);
}
void LLTextureFetch::commandDataBreak()
{
// The pedantically correct way to implement this is to create a command
// request object in the above fashion and enqueue it. However, this is
// simple data of an advisorial not operational nature and this case
// of shared-write access is tolerable.
LLTextureFetch::svMetricsDataBreak = true;
}
void LLTextureFetch::cmdEnqueue(TFRequest * req)
{
lockQueue();
mCommands.push_back(req);
unlockQueue();
unpause();
}
LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue()
{
TFRequest * ret = 0;
lockQueue();
if (! mCommands.empty())
llinfos << "LLTextureFetch ACTIVE_HTTP:" << llendl;
for (queue_t::const_iterator iter(mHTTPTextureQueue.begin());
mHTTPTextureQueue.end() != iter;
++iter)
{
ret = mCommands.front();
mCommands.erase(mCommands.begin());
}
unlockQueue();
return ret;
}
void LLTextureFetch::cmdDoWork()
{
if (mDebugPause)
{
return; // debug: don't do any work
}
TFRequest * req = cmdDequeue();
if (req)
{
// One request per pass should really be enough for this.
req->doWork(this);
delete req;
llinfos << " ID: " << (*iter) << llendl;
}
}
//////////////////////////////////////////////////////////////////////////////
// Private (anonymous) class methods implementing the command scheme.
namespace
{
/**
* Implements the 'Set Region' command.
*
* Thread: Thread1 (TextureFetch)
*/
bool
TFReqSetRegion::doWork(LLTextureFetch *)
{
LLViewerAssetStatsFF::set_region_thread1(mRegionHandle);
return true;
}
TFReqSendMetrics::~TFReqSendMetrics()
{
delete mMainStats;
mMainStats = 0;
}
/**
* Implements the 'Send Metrics' command. Takes over
* ownership of the passed LLViewerAssetStats pointer.
*
* Thread: Thread1 (TextureFetch)
*/
bool
TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
{
/*
* HTTP POST responder. Doesn't do much but tries to
* detect simple breaks in recording the metrics stream.
*
* The 'volatile' modifiers don't indicate signals,
* mmap'd memory or threads, really. They indicate that
* the referenced data is part of a pseudo-closure for
* this responder rather than being required for correct
* operation.
*
* We don't try very hard with the POST request. We give
* it one shot and that's more-or-less it. With a proper
* refactoring of the LLQueuedThread usage, these POSTs
* could be put in a request object and made more reliable.
*/
class lcl_responder : public LLHTTPClient::ResponderWithResult
{
public:
lcl_responder(LLTextureFetch * fetcher,
S32 expected_sequence,
volatile const S32 & live_sequence,
volatile bool & reporting_break,
volatile bool & reporting_started)
: mFetcher(fetcher),
mExpectedSequence(expected_sequence),
mLiveSequence(live_sequence),
mReportingBreak(reporting_break),
mReportingStarted(reporting_started)
{
mFetcher->incrCurlPOSTCount();
}
~lcl_responder()
{
mFetcher->decrCurlPOSTCount();
}
/*virtual*/ void error(U32 status_num, const std::string & reason)
{
if (mLiveSequence == mExpectedSequence)
{
mReportingBreak = true;
}
LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service. Reason: "
<< reason << LL_ENDL;
}
/*virtual*/ void result(const LLSD & content)
{
if (mLiveSequence == mExpectedSequence)
{
mReportingBreak = false;
mReportingStarted = true;
}
}
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return lcl_responder_timeout; }
/*virtual*/ char const* getName(void) const { return "lcl_responder"; }
private:
LLTextureFetch * mFetcher;
S32 mExpectedSequence;
volatile const S32 & mLiveSequence;
volatile bool & mReportingBreak;
volatile bool & mReportingStarted;
}; // class lcl_responder
if (! gViewerAssetStatsThread1)
return true;
static volatile bool reporting_started(false);
static volatile S32 report_sequence(0);
// We've taken over ownership of the stats copy at this
// point. Get a working reference to it for merging here
// 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
// 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);
if (! mCapsURL.empty())
{
LLHTTPClient::post(mCapsURL,
merged_llsd,
new lcl_responder(fetcher,
report_sequence,
report_sequence,
LLTextureFetch::svMetricsDataBreak,
reporting_started));
}
else
{
LLTextureFetch::svMetricsDataBreak = true;
}
// In QA mode, Metrics submode, log the result for ease of testing
if (fetcher->isQAMode())
{
LL_INFOS("Textures") << merged_llsd << LL_ENDL;
}
gViewerAssetStatsThread1->reset();
return true;
}
bool
truncate_viewer_metrics(int max_regions, LLSD & metrics)
{
static const LLSD::String reg_tag("regions");
static const LLSD::String duration_tag("duration");
LLSD & reg_map(metrics[reg_tag]);
if (reg_map.size() <= max_regions)
{
return false;
}
// Build map of region hashes ordered by duration
typedef std::multimap<LLSD::Real, int> reg_ordered_list_t;
reg_ordered_list_t regions_by_duration;
int ind(0);
LLSD::array_const_iterator it_end(reg_map.endArray());
for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind)
{
LLSD::Real duration = (*it)[duration_tag].asReal();
regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind));
}
// Build a replacement regions array with the longest-persistence regions
LLSD new_region(LLSD::emptyArray());
reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend());
reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin());
for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2)
{
new_region.append(reg_map[it2->second]);
}
reg_map = new_region;
return true;
}
} // end of anonymous namespace
#endif