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