PageRenderTime 79ms CodeModel.GetById 7ms app.highlight 66ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llviewerassetstats.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 612 lines | 381 code | 93 blank | 138 comment | 39 complexity | 85a587662f5bc0fc2d76f0b71b5a05b1 MD5 | raw file
  1/** 
  2 * @file llviewerassetstats.cpp
  3 * @brief 
  4 *
  5 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h"
 28
 29#include "llviewerassetstats.h"
 30#include "llregionhandle.h"
 31
 32#include "stdtypes.h"
 33
 34/*
 35 * Classes and utility functions for per-thread and per-region
 36 * asset and experiential metrics to be aggregated grid-wide.
 37 *
 38 * The basic metrics grouping is LLViewerAssetStats::PerRegionStats.
 39 * This provides various counters and simple statistics for asset
 40 * fetches binned into a few categories.  One of these is maintained
 41 * for each region encountered and the 'current' region is available
 42 * as a simple reference.  Each thread (presently two) interested
 43 * in participating in these stats gets an instance of the
 44 * LLViewerAssetStats class so that threads are completely
 45 * independent.
 46 *
 47 * The idea of a current region is used for simplicity and speed
 48 * of categorization.  Each metrics event could have taken a
 49 * region uuid argument resulting in a suitable lookup.  Arguments
 50 * against this design include:
 51 *
 52 *  -  Region uuid not trivially available to caller.
 53 *  -  Cost (cpu, disruption in real work flow) too high.
 54 *  -  Additional precision not really meaningful.
 55 *
 56 * By itself, the LLViewerAssetStats class is thread- and
 57 * viewer-agnostic and can be used anywhere without assumptions
 58 * of global pointers and other context.  For the viewer,
 59 * a set of free functions are provided in the namespace
 60 * LLViewerAssetStatsFF which *do* implement viewer-native
 61 * policies about per-thread globals and will do correct
 62 * defensive tests of same.
 63 *
 64 * References
 65 *
 66 * Project:
 67 *   <TBD>
 68 *
 69 * Test Plan:
 70 *   <TBD>
 71 *
 72 * Jiras:
 73 *   <TBD>
 74 *
 75 * Unit Tests:
 76 *   indra/newview/tests/llviewerassetstats_test.cpp
 77 *
 78 */
 79
 80
 81// ------------------------------------------------------
 82// Global data definitions
 83// ------------------------------------------------------
 84LLViewerAssetStats * gViewerAssetStatsMain(0);
 85LLViewerAssetStats * gViewerAssetStatsThread1(0);
 86
 87
 88// ------------------------------------------------------
 89// Local declarations
 90// ------------------------------------------------------
 91namespace
 92{
 93
 94static LLViewerAssetStats::EViewerAssetCategories
 95asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp);
 96
 97}
 98
 99// ------------------------------------------------------
100// LLViewerAssetStats::PerRegionStats struct definition
101// ------------------------------------------------------
102void
103LLViewerAssetStats::PerRegionStats::reset()
104{
105	for (int i(0); i < LL_ARRAY_SIZE(mRequests); ++i)
106	{
107		mRequests[i].mEnqueued.reset();
108		mRequests[i].mDequeued.reset();
109		mRequests[i].mResponse.reset();
110	}
111	mFPS.reset();
112	
113	mTotalTime = 0;
114	mStartTimestamp = LLViewerAssetStatsFF::get_timestamp();
115}
116
117
118void
119LLViewerAssetStats::PerRegionStats::merge(const LLViewerAssetStats::PerRegionStats & src)
120{
121	// mRegionHandle, mTotalTime, mStartTimestamp are left alone.
122	
123	// mFPS
124	if (src.mFPS.getCount() && mFPS.getCount())
125	{
126		mFPS.merge(src.mFPS);
127	}
128
129	// Requests
130	for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i)
131	{
132		mRequests[i].mEnqueued.merge(src.mRequests[i].mEnqueued);
133		mRequests[i].mDequeued.merge(src.mRequests[i].mDequeued);
134		mRequests[i].mResponse.merge(src.mRequests[i].mResponse);
135	}
136}
137
138
139void
140LLViewerAssetStats::PerRegionStats::accumulateTime(duration_t now)
141{
142	mTotalTime += (now - mStartTimestamp);
143	mStartTimestamp = now;
144}
145
146
147// ------------------------------------------------------
148// LLViewerAssetStats class definition
149// ------------------------------------------------------
150LLViewerAssetStats::LLViewerAssetStats()
151	: mRegionHandle(U64(0))
152{
153	reset();
154}
155
156
157LLViewerAssetStats::LLViewerAssetStats(const LLViewerAssetStats & src)
158	: mRegionHandle(src.mRegionHandle),
159	  mResetTimestamp(src.mResetTimestamp)
160{
161	const PerRegionContainer::const_iterator it_end(src.mRegionStats.end());
162	for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it)
163	{
164		mRegionStats[it->first] = new PerRegionStats(*it->second);
165	}
166	mCurRegionStats = mRegionStats[mRegionHandle];
167}
168
169
170void
171LLViewerAssetStats::reset()
172{
173	// Empty the map of all region stats
174	mRegionStats.clear();
175
176	// If we have a current stats, reset it, otherwise, as at construction,
177	// create a new one as we must always have a current stats block.
178	if (mCurRegionStats)
179	{
180		mCurRegionStats->reset();
181	}
182	else
183	{
184		mCurRegionStats = new PerRegionStats(mRegionHandle);
185	}
186
187	// And add reference to map
188	mRegionStats[mRegionHandle] = mCurRegionStats;
189
190	// Start timestamp consistent with per-region collector
191	mResetTimestamp = mCurRegionStats->mStartTimestamp;
192}
193
194
195void
196LLViewerAssetStats::setRegion(region_handle_t region_handle)
197{
198	if (region_handle == mRegionHandle)
199	{
200		// Already active, ignore.
201		return;
202	}
203
204	// Get duration for current set
205	const duration_t now = LLViewerAssetStatsFF::get_timestamp();
206	mCurRegionStats->accumulateTime(now);
207
208	// Prepare new set
209	PerRegionContainer::iterator new_stats = mRegionStats.find(region_handle);
210	if (mRegionStats.end() == new_stats)
211	{
212		// Haven't seen this region_id before, create a new block and make it current.
213		mCurRegionStats = new PerRegionStats(region_handle);
214		mRegionStats[region_handle] = mCurRegionStats;
215	}
216	else
217	{
218		mCurRegionStats = new_stats->second;
219	}
220	mCurRegionStats->mStartTimestamp = now;
221	mRegionHandle = region_handle;
222}
223
224
225void
226LLViewerAssetStats::recordGetEnqueued(LLViewerAssetType::EType at, bool with_http, bool is_temp)
227{
228	const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
229	
230	++(mCurRegionStats->mRequests[int(eac)].mEnqueued);
231}
232	
233void
234LLViewerAssetStats::recordGetDequeued(LLViewerAssetType::EType at, bool with_http, bool is_temp)
235{
236	const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
237
238	++(mCurRegionStats->mRequests[int(eac)].mDequeued);
239}
240
241void
242LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, duration_t duration)
243{
244	const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
245
246	mCurRegionStats->mRequests[int(eac)].mResponse.record(duration);
247}
248
249void
250LLViewerAssetStats::recordFPS(F32 fps)
251{
252	mCurRegionStats->mFPS.record(fps);
253}
254
255LLSD
256LLViewerAssetStats::asLLSD(bool compact_output)
257{
258	// Top-level tags
259	static const LLSD::String tags[EVACCount] = 
260		{
261			LLSD::String("get_texture_temp_http"),
262			LLSD::String("get_texture_temp_udp"),
263			LLSD::String("get_texture_non_temp_http"),
264			LLSD::String("get_texture_non_temp_udp"),
265			LLSD::String("get_wearable_udp"),
266			LLSD::String("get_sound_udp"),
267			LLSD::String("get_gesture_udp"),
268			LLSD::String("get_other")
269		};
270
271	// Stats Group Sub-tags.
272	static const LLSD::String enq_tag("enqueued");
273	static const LLSD::String deq_tag("dequeued");
274	static const LLSD::String rcnt_tag("resp_count");
275	static const LLSD::String rmin_tag("resp_min");
276	static const LLSD::String rmax_tag("resp_max");
277	static const LLSD::String rmean_tag("resp_mean");
278
279	// MMM Group Sub-tags.
280	static const LLSD::String cnt_tag("count");
281	static const LLSD::String min_tag("min");
282	static const LLSD::String max_tag("max");
283	static const LLSD::String mean_tag("mean");
284
285	const duration_t now = LLViewerAssetStatsFF::get_timestamp();
286	mCurRegionStats->accumulateTime(now);
287
288	LLSD regions = LLSD::emptyArray();
289	for (PerRegionContainer::iterator it = mRegionStats.begin();
290		 mRegionStats.end() != it;
291		 ++it)
292	{
293		if (0 == it->first)
294		{
295			// Never emit NULL UUID/handle in results.
296			continue;
297		}
298
299		PerRegionStats & stats = *it->second;
300		
301		LLSD reg_stat = LLSD::emptyMap();
302		
303		for (int i = 0; i < LL_ARRAY_SIZE(tags); ++i)
304		{
305			PerRegionStats::prs_group & group(stats.mRequests[i]);
306			
307			if ((! compact_output) ||
308				group.mEnqueued.getCount() ||
309				group.mDequeued.getCount() ||
310				group.mResponse.getCount())
311			{
312				LLSD & slot = reg_stat[tags[i]];
313				slot = LLSD::emptyMap();
314				slot[enq_tag] = LLSD(S32(stats.mRequests[i].mEnqueued.getCount()));
315				slot[deq_tag] = LLSD(S32(stats.mRequests[i].mDequeued.getCount()));
316				slot[rcnt_tag] = LLSD(S32(stats.mRequests[i].mResponse.getCount()));
317				slot[rmin_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMin() * 1.0e-6));
318				slot[rmax_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMax() * 1.0e-6));
319				slot[rmean_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMean() * 1.0e-6));
320			}
321		}
322
323		if ((! compact_output) || stats.mFPS.getCount())
324		{
325			LLSD & slot = reg_stat["fps"];
326			slot = LLSD::emptyMap();
327			slot[cnt_tag] = LLSD(S32(stats.mFPS.getCount()));
328			slot[min_tag] = LLSD(F64(stats.mFPS.getMin()));
329			slot[max_tag] = LLSD(F64(stats.mFPS.getMax()));
330			slot[mean_tag] = LLSD(F64(stats.mFPS.getMean()));
331		}
332
333		U32 grid_x(0), grid_y(0);
334		grid_from_region_handle(it->first, &grid_x, &grid_y);
335		reg_stat["grid_x"] = LLSD::Integer(grid_x);
336		reg_stat["grid_y"] = LLSD::Integer(grid_y);
337		reg_stat["duration"] = LLSD::Real(stats.mTotalTime * 1.0e-6);		
338		regions.append(reg_stat);
339	}
340
341	LLSD ret = LLSD::emptyMap();
342	ret["regions"] = regions;
343	ret["duration"] = LLSD::Real((now - mResetTimestamp) * 1.0e-6);
344	
345	return ret;
346}
347
348void
349LLViewerAssetStats::merge(const LLViewerAssetStats & src)
350{
351	// mRegionHandle, mCurRegionStats and mResetTimestamp are left untouched.
352	// Just merge the stats bodies
353
354	const PerRegionContainer::const_iterator it_end(src.mRegionStats.end());
355	for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it)
356	{
357		PerRegionContainer::iterator dst(mRegionStats.find(it->first));
358		if (mRegionStats.end() == dst)
359		{
360			// Destination is missing data, just make a private copy
361			mRegionStats[it->first] = new PerRegionStats(*it->second);
362		}
363		else
364		{
365			dst->second->merge(*it->second);
366		}
367	}
368}
369
370
371// ------------------------------------------------------
372// Global free-function definitions (LLViewerAssetStatsFF namespace)
373// ------------------------------------------------------
374
375namespace LLViewerAssetStatsFF
376{
377
378//
379// Target thread is elaborated in the function name.  This could
380// have been something 'templatey' like specializations iterated
381// over a set of constants but with so few, this is clearer I think.
382//
383// As for the threads themselves... rather than do fine-grained
384// locking as we gather statistics, this code creates a collector
385// for each thread, allocated and run independently.  Logging
386// happens at relatively infrequent intervals and at that time
387// the data is sent to a single thread to be aggregated into
388// a single entity with locks, thread safety and other niceties.
389//
390// A particularly fussy implementation would distribute the
391// per-thread pointers across separate cache lines.  But that should
392// be beyond current requirements.
393//
394
395// 'main' thread - initial program thread
396
397void
398set_region_main(LLViewerAssetStats::region_handle_t region_handle)
399{
400	if (! gViewerAssetStatsMain)
401		return;
402
403	gViewerAssetStatsMain->setRegion(region_handle);
404}
405
406void
407record_enqueue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp)
408{
409	if (! gViewerAssetStatsMain)
410		return;
411
412	gViewerAssetStatsMain->recordGetEnqueued(at, with_http, is_temp);
413}
414
415void
416record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp)
417{
418	if (! gViewerAssetStatsMain)
419		return;
420
421	gViewerAssetStatsMain->recordGetDequeued(at, with_http, is_temp);
422}
423
424void
425record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration)
426{
427	if (! gViewerAssetStatsMain)
428		return;
429
430	gViewerAssetStatsMain->recordGetServiced(at, with_http, is_temp, duration);
431}
432
433void
434record_fps_main(F32 fps)
435{
436	if (! gViewerAssetStatsMain)
437		return;
438
439	gViewerAssetStatsMain->recordFPS(fps);
440}
441
442
443// 'thread1' - should be for TextureFetch thread
444
445void
446set_region_thread1(LLViewerAssetStats::region_handle_t region_handle)
447{
448	if (! gViewerAssetStatsThread1)
449		return;
450
451	gViewerAssetStatsThread1->setRegion(region_handle);
452}
453
454void
455record_enqueue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp)
456{
457	if (! gViewerAssetStatsThread1)
458		return;
459
460	gViewerAssetStatsThread1->recordGetEnqueued(at, with_http, is_temp);
461}
462
463void
464record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp)
465{
466	if (! gViewerAssetStatsThread1)
467		return;
468
469	gViewerAssetStatsThread1->recordGetDequeued(at, with_http, is_temp);
470}
471
472void
473record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration)
474{
475	if (! gViewerAssetStatsThread1)
476		return;
477
478	gViewerAssetStatsThread1->recordGetServiced(at, with_http, is_temp, duration);
479}
480
481
482void
483init()
484{
485	if (! gViewerAssetStatsMain)
486	{
487		gViewerAssetStatsMain = new LLViewerAssetStats();
488	}
489	if (! gViewerAssetStatsThread1)
490	{
491		gViewerAssetStatsThread1 = new LLViewerAssetStats();
492	}
493}
494
495void
496cleanup()
497{
498	delete gViewerAssetStatsMain;
499	gViewerAssetStatsMain = 0;
500
501	delete gViewerAssetStatsThread1;
502	gViewerAssetStatsThread1 = 0;
503}
504
505
506} // namespace LLViewerAssetStatsFF
507
508
509// ------------------------------------------------------
510// Local function definitions
511// ------------------------------------------------------
512
513namespace
514{
515
516LLViewerAssetStats::EViewerAssetCategories
517asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp)
518{
519	// For statistical purposes, we divide GETs into several
520	// populations of asset fetches:
521	//  - textures which are de-prioritized in the asset system
522	//  - wearables (clothing, bodyparts) which directly affect
523	//    user experiences when they log in
524	//  - sounds
525	//  - gestures
526	//  - everything else.
527	//
528	llassert_always(50 == LLViewerAssetType::AT_COUNT);
529
530	// Multiple asset definitions are floating around so this requires some
531	// maintenance and attention.
532	static const LLViewerAssetStats::EViewerAssetCategories asset_to_bin_map[LLViewerAssetType::AT_COUNT] =
533		{
534			LLViewerAssetStats::EVACTextureTempHTTPGet,			// (0) AT_TEXTURE
535			LLViewerAssetStats::EVACSoundUDPGet,				// AT_SOUND
536			LLViewerAssetStats::EVACOtherGet,					// AT_CALLINGCARD
537			LLViewerAssetStats::EVACOtherGet,					// AT_LANDMARK
538			LLViewerAssetStats::EVACOtherGet,					// AT_SCRIPT
539			LLViewerAssetStats::EVACWearableUDPGet,				// AT_CLOTHING
540			LLViewerAssetStats::EVACOtherGet,					// AT_OBJECT
541			LLViewerAssetStats::EVACOtherGet,					// AT_NOTECARD
542			LLViewerAssetStats::EVACOtherGet,					// AT_CATEGORY
543			LLViewerAssetStats::EVACOtherGet,					// AT_ROOT_CATEGORY
544			LLViewerAssetStats::EVACOtherGet,					// (10) AT_LSL_TEXT
545			LLViewerAssetStats::EVACOtherGet,					// AT_LSL_BYTECODE
546			LLViewerAssetStats::EVACOtherGet,					// AT_TEXTURE_TGA
547			LLViewerAssetStats::EVACWearableUDPGet,				// AT_BODYPART
548			LLViewerAssetStats::EVACOtherGet,					// AT_TRASH
549			LLViewerAssetStats::EVACOtherGet,					// AT_SNAPSHOT_CATEGORY
550			LLViewerAssetStats::EVACOtherGet,					// AT_LOST_AND_FOUND
551			LLViewerAssetStats::EVACSoundUDPGet,				// AT_SOUND_WAV
552			LLViewerAssetStats::EVACOtherGet,					// AT_IMAGE_TGA
553			LLViewerAssetStats::EVACOtherGet,					// AT_IMAGE_JPEG
554			LLViewerAssetStats::EVACGestureUDPGet,				// (20) AT_ANIMATION
555			LLViewerAssetStats::EVACGestureUDPGet,				// AT_GESTURE
556			LLViewerAssetStats::EVACOtherGet,					// AT_SIMSTATE
557			LLViewerAssetStats::EVACOtherGet,					// AT_FAVORITE
558			LLViewerAssetStats::EVACOtherGet,					// AT_LINK
559			LLViewerAssetStats::EVACOtherGet,					// AT_LINK_FOLDER
560			LLViewerAssetStats::EVACOtherGet,					// 
561			LLViewerAssetStats::EVACOtherGet,					// 
562			LLViewerAssetStats::EVACOtherGet,					// 
563			LLViewerAssetStats::EVACOtherGet,					// 
564			LLViewerAssetStats::EVACOtherGet,					// (30)
565			LLViewerAssetStats::EVACOtherGet,					// 
566			LLViewerAssetStats::EVACOtherGet,					// 
567			LLViewerAssetStats::EVACOtherGet,					// 
568			LLViewerAssetStats::EVACOtherGet,					// 
569			LLViewerAssetStats::EVACOtherGet,					// 
570			LLViewerAssetStats::EVACOtherGet,					// 
571			LLViewerAssetStats::EVACOtherGet,					// 
572			LLViewerAssetStats::EVACOtherGet,					// 
573			LLViewerAssetStats::EVACOtherGet,					// 
574			LLViewerAssetStats::EVACOtherGet,					// (40)
575			LLViewerAssetStats::EVACOtherGet,					// 
576			LLViewerAssetStats::EVACOtherGet,					// 
577			LLViewerAssetStats::EVACOtherGet,					// 
578			LLViewerAssetStats::EVACOtherGet,					// 
579			LLViewerAssetStats::EVACOtherGet,					//
580			LLViewerAssetStats::EVACOtherGet,					// 
581			LLViewerAssetStats::EVACOtherGet,					// 
582			LLViewerAssetStats::EVACOtherGet,					// 
583			LLViewerAssetStats::EVACOtherGet,					// AT_MESH
584																// (50)
585		};
586	
587	if (at < 0 || at >= LLViewerAssetType::AT_COUNT)
588	{
589		return LLViewerAssetStats::EVACOtherGet;
590	}
591	LLViewerAssetStats::EViewerAssetCategories ret(asset_to_bin_map[at]);
592	if (LLViewerAssetStats::EVACTextureTempHTTPGet == ret)
593	{
594		// Indexed with [is_temp][with_http]
595		static const LLViewerAssetStats::EViewerAssetCategories texture_bin_map[2][2] =
596			{
597				{
598					LLViewerAssetStats::EVACTextureNonTempUDPGet,
599					LLViewerAssetStats::EVACTextureNonTempHTTPGet,
600				},
601				{
602					LLViewerAssetStats::EVACTextureTempUDPGet,
603					LLViewerAssetStats::EVACTextureTempHTTPGet,
604				}
605			};
606
607		ret = texture_bin_map[is_temp][with_http];
608	}
609	return ret;
610}
611
612} // anonymous namespace