From 6d4e4af0a5677afa41afdabf68b78334c0804f04 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 24 Sep 2015 22:02:22 +0200 Subject: [PATCH 1/2] ability to see Stats in JavaScript + example script which outputs the stats to the console/logviewer --- examples/example/misc/statsExample.js | 67 +++++++++++++++++++++++++++ interface/src/Application.cpp | 1 + interface/src/ui/Stats.cpp | 24 +++++----- interface/src/ui/Stats.h | 6 ++- 4 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 examples/example/misc/statsExample.js diff --git a/examples/example/misc/statsExample.js b/examples/example/misc/statsExample.js new file mode 100644 index 0000000000..3c677af96a --- /dev/null +++ b/examples/example/misc/statsExample.js @@ -0,0 +1,67 @@ +// +// statsExample.js +// examples/example/misc +// +// Created by Thijs Wenker on 24 Sept 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Prints the stats to the console. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// The stats to be displayed +var stats = [ + 'serverCount', + 'framerate', // a.k.a. FPS + 'simrate', + 'avatarSimrate', + 'avatarCount', + 'packetInCount', + 'packetOutCount', + 'mbpsIn', + 'mbpsOut', + 'audioPing', + 'avatarPing', + 'entitiesPing', + 'assetPing', + 'velocity', + 'yaw', + 'avatarMixerKbps', + 'avatarMixerPps', + 'audioMixerKbps', + 'audioMixerPps', + 'downloads', + 'downloadsPending', + 'triangles', + 'quads', + 'materialSwitches', + 'meshOpaque', + 'meshTranslucent', + 'opaqueConsidered', + 'opaqueOutOfView', + 'opaqueTooSmall', + 'translucentConsidered', + 'translucentOutOfView', + 'translucentTooSmall', + 'sendingMode', + 'packetStats', + 'lodStatus', + 'timingStats', + 'serverElements', + 'serverInternal', + 'serverLeaves', + 'localElements', + 'localInternal', + 'localLeaves' +]; + +// Force update the stats, in case the stats panel is invisible +Stats.forceUpdateStats(); + +// Loop through the stats and display them +for (var i in stats) { + var stat = stats[i]; + print(stat + " = " + Stats[stat]); +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c359275d8a..14dfaa3783 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4063,6 +4063,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1); scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance()); + scriptEngine->registerGlobalObject("Stats", Stats::getInstance()); scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 4dd552210c..ebce4f2b96 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -89,14 +89,14 @@ bool Stats::includeTimingRecord(const QString& name) { } -void Stats::updateStats() { - if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { - if (isVisible()) { - setVisible(false); - } - return; - } else { - if (!isVisible()) { +void Stats::updateStats(bool force) { + if (!force) { + if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { + if (isVisible()) { + setVisible(false); + } + return; + } else if (!isVisible()) { setVisible(true); } } @@ -161,7 +161,7 @@ void Stats::updateStats() { STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z)); STAT_UPDATE_FLOAT(velocity, glm::length(myAvatar->getVelocity()), 0.1f); STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f); - if (_expanded) { + if (_expanded || force) { SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer); if (avatarMixer) { STAT_UPDATE(avatarMixerKbps, roundf( @@ -175,7 +175,7 @@ void Stats::updateStats() { STAT_UPDATE(avatarMixerPps, -1); } SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); - if (audioMixerNode) { + if (audioMixerNode || force) { STAT_UPDATE(audioMixerKbps, roundf( bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) + bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); @@ -230,7 +230,7 @@ void Stats::updateStats() { totalLeaves += stats.getTotalLeaves(); } } - if (_expanded) { + if (_expanded || force) { if (serverCount == 0) { sendingModeStream << "---"; } @@ -272,7 +272,7 @@ void Stats::updateStats() { STAT_UPDATE(serverElements, (int)totalNodes); STAT_UPDATE(localElements, (int)OctreeElement::getNodeCount()); - if (_expanded) { + if (_expanded || force) { STAT_UPDATE(serverInternal, (int)totalInternal); STAT_UPDATE(serverLeaves, (int)totalLeaves); // Local Voxels diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index e5c273926b..2e4c5e4dca 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -81,7 +81,7 @@ public: const QString& monospaceFont() { return _monospaceFont; } - void updateStats(); + void updateStats(bool force = false); bool isExpanded() { return _expanded; } bool isTimingExpanded() { return _timingExpanded; } @@ -93,6 +93,9 @@ public: } } +public slots: + void forceUpdateStats() { updateStats(true); } + signals: void expandedChanged(); void timingExpandedChanged(); @@ -149,3 +152,4 @@ private: }; #endif // hifi_Stats_h + From 1051416ed7b0927a877edb65300c94d433a9cc1f Mon Sep 17 00:00:00 2001 From: James Pollack Date: Fri, 25 Sep 2015 10:33:16 -0700 Subject: [PATCH 2/2] Change stat collection to batch stats together to make fewer network calls --- examples/example/misc/collectHifiStats.js | 93 +++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 examples/example/misc/collectHifiStats.js diff --git a/examples/example/misc/collectHifiStats.js b/examples/example/misc/collectHifiStats.js new file mode 100644 index 0000000000..3902622217 --- /dev/null +++ b/examples/example/misc/collectHifiStats.js @@ -0,0 +1,93 @@ +// +// collectHifiStats.js +// +// Created by Thijs Wenker on 24 Sept 2015 +// Additions by James B. Pollack @imgntn on 25 Sept 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Collects stats for analysis to a REST endpoint. Defaults to batching stats, but can be customized. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// The url where the data will be posted. +var ENDPOINT_URL = ""; + +var BATCH_STATS = true; +var BATCH_SIZE = 5; + +var batch = []; + +if (BATCH_STATS) { + + var RECORD_EVERY = 1000; // 1 seconds + var batchCount = 0; + + Script.setInterval(function() { + + if (batchCount === BATCH_SIZE) { + sendBatchToEndpoint(batch); + batchCount = 0; + } + Stats.forceUpdateStats(); + batch.push(getBatchStats()); + batchCount++; + }, RECORD_EVERY); + + +} else { + // Send the data every: + var SEND_EVERY = 30000; // 30 seconds + + Script.setInterval(function() { + Stats.forceUpdateStats(); + var req = new XMLHttpRequest(); + req.open("POST", ENDPOINT_URL, false); + req.send(getStats()); + }, SEND_EVERY); +} + +function getStats() { + return JSON.stringify({ + username: GlobalServices.username, + location: Window.location.hostname, + framerate: Stats.framerate, + simrate: Stats.simrate, + ping: { + audio: Stats.audioPing, + avatar: Stats.avatarPing, + entities: Stats.entitiesPing, + asset: Stats.assetPing + }, + position: Camera.position, + yaw: Stats.yaw, + rotation: Camera.orientation.x + ',' + Camera.orientation.y + ',' + Camera.orientation.z + ',' + Camera.orientation.w + }) +} + +function getBatchStats() { + // print('GET BATCH STATS'); + return { + username: GlobalServices.username, + location: Window.location.hostname, + framerate: Stats.framerate, + simrate: Stats.simrate, + ping: { + audio: Stats.audioPing, + avatar: Stats.avatarPing, + entities: Stats.entitiesPing, + asset: Stats.assetPing + }, + position: Camera.position, + yaw: Stats.yaw, + rotation: Camera.orientation.x + ',' + Camera.orientation.y + ',' + Camera.orientation.z + ',' + Camera.orientation.w + } +} + +function sendBatchToEndpoint(batch) { + // print('SEND BATCH TO ENDPOINT'); + var req = new XMLHttpRequest(); + req.open("POST", ENDPOINT_URL, false); + req.send(JSON.stringify(batch)); +} \ No newline at end of file