451 lines
14 KiB
C++
451 lines
14 KiB
C++
/**
|
|
* @file llviewerassetstats.cpp
|
|
* @brief
|
|
*
|
|
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "llviewerassetstats.h"
|
|
#include "llregionhandle.h"
|
|
|
|
#include "stdtypes.h"
|
|
#include "llvoavatar.h"
|
|
#include "llsdparam.h"
|
|
#include "llsdutil.h"
|
|
|
|
/*
|
|
* Classes and utility functions for per-thread and per-region
|
|
* asset and experiential metrics to be aggregated grid-wide.
|
|
*
|
|
* The basic metrics grouping is LLViewerAssetStats::PerRegionStats.
|
|
* This provides various counters and simple statistics for asset
|
|
* fetches binned into a few categories. One of these is maintained
|
|
* for each region encountered and the 'current' region is available
|
|
* as a simple reference. Each thread (presently two) interested
|
|
* in participating in these stats gets an instance of the
|
|
* LLViewerAssetStats class so that threads are completely
|
|
* independent.
|
|
*
|
|
* The idea of a current region is used for simplicity and speed
|
|
* of categorization. Each metrics event could have taken a
|
|
* region uuid argument resulting in a suitable lookup. Arguments
|
|
* against this design include:
|
|
*
|
|
* - Region uuid not trivially available to caller.
|
|
* - Cost (cpu, disruption in real work flow) too high.
|
|
* - Additional precision not really meaningful.
|
|
*
|
|
* By itself, the LLViewerAssetStats class is thread- and
|
|
* viewer-agnostic and can be used anywhere without assumptions
|
|
* of global pointers and other context. For the viewer,
|
|
* a set of free functions are provided in the namespace
|
|
* LLViewerAssetStatsFF which *do* implement viewer-native
|
|
* policies about per-thread globals and will do correct
|
|
* defensive tests of same.
|
|
*
|
|
* References
|
|
*
|
|
* Project:
|
|
* <TBD>
|
|
*
|
|
* Test Plan:
|
|
* <TBD>
|
|
*
|
|
* Jiras:
|
|
* <TBD>
|
|
*
|
|
* Unit Tests:
|
|
* indra/newview/tests/llviewerassetstats_test.cpp
|
|
*
|
|
*/
|
|
|
|
namespace LLTrace
|
|
{
|
|
// This little bit of shimmery is to allow the creation of
|
|
// default-constructed stat and event handles so we can make arrays of
|
|
// the things.
|
|
|
|
// The only sensible way to use this function is to immediately make a
|
|
// copy of the contents, since it always returns the same pointer.
|
|
const char *makeNewAutoName()
|
|
{
|
|
static char name[64];
|
|
static S32 auto_namer_number = 0;
|
|
snprintf(name,64,"auto_name_%d",auto_namer_number);
|
|
auto_namer_number++;
|
|
return name;
|
|
}
|
|
|
|
template <typename T = F64>
|
|
class DCCountStatHandle:
|
|
public CountStatHandle<T>
|
|
{
|
|
public:
|
|
DCCountStatHandle(const char *name = makeNewAutoName(), const char *description=NULL):
|
|
CountStatHandle<T>(name,description)
|
|
{
|
|
}
|
|
};
|
|
|
|
template <typename T = F64>
|
|
class DCEventStatHandle:
|
|
public EventStatHandle<T>
|
|
{
|
|
public:
|
|
DCEventStatHandle(const char *name = makeNewAutoName(), const char *description=NULL):
|
|
EventStatHandle<T>(name,description)
|
|
{
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
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, including animations
|
|
// - everything else.
|
|
//
|
|
|
|
EViewerAssetCategories ret;
|
|
switch (at)
|
|
{
|
|
case LLAssetType::AT_TEXTURE:
|
|
if (is_temp)
|
|
ret = with_http ? EVACTextureTempHTTPGet : EVACTextureTempUDPGet;
|
|
else
|
|
ret = with_http ? EVACTextureNonTempHTTPGet : EVACTextureNonTempUDPGet;
|
|
break;
|
|
case LLAssetType::AT_SOUND:
|
|
case LLAssetType::AT_SOUND_WAV:
|
|
ret = with_http ? EVACSoundHTTPGet : EVACSoundUDPGet;
|
|
break;
|
|
case LLAssetType::AT_CLOTHING:
|
|
case LLAssetType::AT_BODYPART:
|
|
ret = with_http ? EVACWearableHTTPGet : EVACWearableUDPGet;
|
|
break;
|
|
case LLAssetType::AT_ANIMATION:
|
|
case LLAssetType::AT_GESTURE:
|
|
ret = with_http ? EVACGestureHTTPGet : EVACGestureUDPGet;
|
|
break;
|
|
case LLAssetType::AT_LANDMARK:
|
|
ret = with_http ? EVACLandmarkHTTPGet : EVACLandmarkUDPGet;
|
|
break;
|
|
default:
|
|
ret = with_http ? EVACOtherHTTPGet : EVACOtherUDPGet;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static LLTrace::DCCountStatHandle<> sEnqueued[EVACCount];
|
|
static LLTrace::DCCountStatHandle<> sDequeued[EVACCount];
|
|
static LLTrace::DCEventStatHandle<> sBytesFetched[EVACCount];
|
|
static LLTrace::DCEventStatHandle<F64Seconds > sResponse[EVACCount];
|
|
}
|
|
|
|
// ------------------------------------------------------
|
|
// Global data definitions
|
|
// ------------------------------------------------------
|
|
LLViewerAssetStats * gViewerAssetStats(0);
|
|
|
|
// ------------------------------------------------------
|
|
// LLViewerAssetStats class definition
|
|
// ------------------------------------------------------
|
|
LLViewerAssetStats::LLViewerAssetStats()
|
|
: mRegionHandle(U64(0)),
|
|
mCurRecording(NULL)
|
|
{
|
|
start();
|
|
}
|
|
|
|
|
|
LLViewerAssetStats::LLViewerAssetStats(const LLViewerAssetStats & src)
|
|
: mRegionHandle(src.mRegionHandle)
|
|
{
|
|
mRegionRecordings = src.mRegionRecordings;
|
|
|
|
mCurRecording = &mRegionRecordings[mRegionHandle];
|
|
|
|
// assume this is being passed to another thread, so make sure we have unique copies of recording data
|
|
for (PerRegionRecordingContainer::iterator it = mRegionRecordings.begin(), end_it = mRegionRecordings.end();
|
|
it != end_it;
|
|
++it)
|
|
{
|
|
it->second.stop();
|
|
it->second.makeUnique();
|
|
}
|
|
|
|
LLStopWatchControlsMixin<LLViewerAssetStats>::setPlayState(src.getPlayState());
|
|
}
|
|
|
|
void LLViewerAssetStats::handleStart()
|
|
{
|
|
if (mCurRecording)
|
|
{
|
|
mCurRecording->start();
|
|
}
|
|
}
|
|
|
|
void LLViewerAssetStats::handleStop()
|
|
{
|
|
if (mCurRecording)
|
|
{
|
|
mCurRecording->stop();
|
|
}
|
|
}
|
|
|
|
void LLViewerAssetStats::handleReset()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
|
|
void LLViewerAssetStats::reset()
|
|
{
|
|
// Empty the map of all region stats
|
|
mRegionRecordings.clear();
|
|
|
|
// initialize new recording for current region
|
|
if (mRegionHandle)
|
|
{
|
|
mCurRecording = &mRegionRecordings[mRegionHandle];
|
|
mCurRecording->setPlayState(getPlayState());
|
|
}
|
|
}
|
|
|
|
void LLViewerAssetStats::setRegion(region_handle_t region_handle)
|
|
{
|
|
if (region_handle == mRegionHandle)
|
|
{
|
|
// Already active, ignore.
|
|
return;
|
|
}
|
|
|
|
if (mCurRecording)
|
|
{
|
|
mCurRecording->pause();
|
|
}
|
|
if (region_handle)
|
|
{
|
|
mCurRecording = &mRegionRecordings[region_handle];
|
|
mCurRecording->setPlayState(getPlayState());
|
|
}
|
|
|
|
mRegionHandle = region_handle;
|
|
}
|
|
|
|
template <typename T>
|
|
void LLViewerAssetStats::getStat(LLTrace::Recording& rec, T& req, LLViewerAssetStatsFF::EViewerAssetCategories cat, bool compact_output)
|
|
{
|
|
using namespace LLViewerAssetStatsFF;
|
|
|
|
if (!compact_output
|
|
|| rec.getSampleCount(sEnqueued[cat])
|
|
|| rec.getSampleCount(sDequeued[cat])
|
|
|| rec.getSampleCount(sResponse[cat]))
|
|
{
|
|
req .enqueued(rec.getSampleCount(sEnqueued[cat]))
|
|
.dequeued(rec.getSampleCount(sDequeued[cat]))
|
|
.resp_count(rec.getSampleCount(sResponse[cat]))
|
|
.resp_min(rec.getMin(sResponse[cat]).value())
|
|
.resp_max(rec.getMax(sResponse[cat]).value())
|
|
.resp_mean(rec.getMean(sResponse[cat]).value())
|
|
.resp_mean_bytes(rec.getMean(sBytesFetched[cat]));
|
|
}
|
|
}
|
|
|
|
void LLViewerAssetStats::getStats(AssetStats& stats, bool compact_output)
|
|
{
|
|
using namespace LLViewerAssetStatsFF;
|
|
|
|
stats.regions.setProvided();
|
|
|
|
for (PerRegionRecordingContainer::iterator it = mRegionRecordings.begin(), end_it = mRegionRecordings.end();
|
|
it != end_it;
|
|
++it)
|
|
{
|
|
RegionStats& r = stats.regions.add();
|
|
LLTrace::Recording& rec = it->second;
|
|
|
|
getStat(rec, r.get_texture_temp_http, EVACTextureTempHTTPGet, compact_output);
|
|
getStat(rec, r.get_texture_temp_udp, EVACTextureTempUDPGet, compact_output);
|
|
getStat(rec, r.get_texture_non_temp_http, EVACTextureNonTempHTTPGet, compact_output);
|
|
getStat(rec, r.get_texture_non_temp_udp, EVACTextureNonTempUDPGet, compact_output);
|
|
getStat(rec, r.get_wearable_http, EVACWearableHTTPGet, compact_output);
|
|
getStat(rec, r.get_wearable_udp, EVACWearableUDPGet, compact_output);
|
|
getStat(rec, r.get_sound_http, EVACSoundHTTPGet, compact_output);
|
|
getStat(rec, r.get_sound_udp, EVACSoundUDPGet, compact_output);
|
|
getStat(rec, r.get_gesture_http, EVACGestureHTTPGet, compact_output);
|
|
getStat(rec, r.get_gesture_udp, EVACGestureUDPGet, compact_output);
|
|
getStat(rec, r.get_landmark_http, EVACLandmarkHTTPGet, compact_output);
|
|
getStat(rec, r.get_landmark_udp, EVACLandmarkUDPGet, compact_output);
|
|
getStat(rec, r.get_other_http, EVACOtherHTTPGet, compact_output);
|
|
getStat(rec, r.get_other_udp, EVACOtherUDPGet, compact_output);
|
|
|
|
S32 fps = (S32)rec.getLastValue(LLStatViewer::FPS_SAMPLE);
|
|
if (!compact_output || fps != 0)
|
|
{
|
|
r.fps .count(fps)
|
|
.min(rec.getMin(LLStatViewer::FPS_SAMPLE))
|
|
.max(rec.getMax(LLStatViewer::FPS_SAMPLE))
|
|
.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)
|
|
.grid_y(grid_y)
|
|
.duration(F64Seconds(rec.getDuration()).value());
|
|
}
|
|
|
|
stats.duration(mCurRecording ? F64Seconds(mCurRecording->getDuration()).value() : 0.0);
|
|
}
|
|
|
|
LLSD LLViewerAssetStats::asLLSD(bool compact_output)
|
|
{
|
|
LLParamSDParser parser;
|
|
LLSD sd;
|
|
AssetStats stats;
|
|
getStats(stats, compact_output);
|
|
LLInitParam::predicate_rule_t rule = LLInitParam::default_parse_rules();
|
|
if (!compact_output)
|
|
{
|
|
rule.allow(LLInitParam::EMPTY);
|
|
}
|
|
parser.writeSD(sd, stats, rule);
|
|
return sd;
|
|
}
|
|
|
|
// ------------------------------------------------------
|
|
// Global free-function definitions (LLViewerAssetStatsFF namespace)
|
|
// ------------------------------------------------------
|
|
|
|
namespace LLViewerAssetStatsFF
|
|
{
|
|
void set_region(LLViewerAssetStats::region_handle_t region_handle)
|
|
{
|
|
if (! gViewerAssetStats)
|
|
return;
|
|
|
|
gViewerAssetStats->setRegion(region_handle);
|
|
}
|
|
|
|
void record_enqueue(LLViewerAssetType::EType at, bool with_http, bool is_temp)
|
|
{
|
|
const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
|
|
|
|
add(sEnqueued[int(eac)], 1);
|
|
}
|
|
|
|
void record_dequeue(LLViewerAssetType::EType at, bool with_http, bool is_temp)
|
|
{
|
|
const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
|
|
|
|
add(sDequeued[int(eac)], 1);
|
|
}
|
|
|
|
void record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration, F64 bytes)
|
|
{
|
|
const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
|
|
|
|
record(sResponse[int(eac)], F64Seconds(duration));
|
|
record(sBytesFetched[int(eac)], bytes);
|
|
}
|
|
|
|
void init()
|
|
{
|
|
if (! gViewerAssetStats)
|
|
{
|
|
gViewerAssetStats = new LLViewerAssetStats();
|
|
}
|
|
}
|
|
|
|
void
|
|
cleanup()
|
|
{
|
|
delete gViewerAssetStats;
|
|
gViewerAssetStats = 0;
|
|
}
|
|
|
|
|
|
} // 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"),
|
|
resp_mean_bytes("resp_mean_bytes")
|
|
{}
|
|
|
|
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_http("get_wearable_http"),
|
|
get_wearable_udp("get_wearable_udp"),
|
|
get_sound_http("get_sound_http"),
|
|
get_sound_udp("get_sound_udp"),
|
|
get_gesture_http("get_gesture_http"),
|
|
get_gesture_udp("get_gesture_udp"),
|
|
get_landmark_http("get_landmark_http"),
|
|
get_landmark_udp("get_landmark_udp"),
|
|
get_other_http("get_other_http"),
|
|
get_other_udp("get_other_udp"),
|
|
fps("fps"),
|
|
grid_x("grid_x"),
|
|
grid_y("grid_y"),
|
|
duration("duration")
|
|
{}
|
|
|
|
LLViewerAssetStats::AssetStats::AssetStats()
|
|
: regions("regions"),
|
|
duration("duration"),
|
|
session_id("session_id"),
|
|
agent_id("agent_id"),
|
|
message("message"),
|
|
sequence("sequence"),
|
|
initial("initial"),
|
|
break_("break")
|
|
{}
|