From 1b265b213b65076ee346d85f62d2d61a72ea3ca6 Mon Sep 17 00:00:00 2001
From: Diva Canto
Date: Wed, 10 Jul 2013 16:09:45 -0700
Subject: Added show client-stats [first last] command to expose what viewers
are requesting.
---
.../Region/ClientStack/Linden/UDP/LLClientView.cs | 18 +++-
.../Region/ClientStack/Linden/UDP/LLUDPClient.cs | 27 +++--
.../Agent/UDP/Linden/LindenUDPInfoModule.cs | 109 ++++++++++++++++++++-
3 files changed, 137 insertions(+), 17 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
index bd61c3f..3d92705 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
@@ -678,12 +678,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
//there is a local handler for this packet type
if (pprocessor.Async)
{
+ ClientInfo cinfo = UDPClient.GetClientInfo();
+ if (!cinfo.AsyncRequests.ContainsKey(packet.Type.ToString()))
+ cinfo.AsyncRequests[packet.Type.ToString()] = 0;
+ cinfo.AsyncRequests[packet.Type.ToString()]++;
+
object obj = new AsyncPacketProcess(this, pprocessor.method, packet);
Util.FireAndForget(ProcessSpecificPacketAsync, obj);
result = true;
}
else
{
+ ClientInfo cinfo = UDPClient.GetClientInfo();
+ if (!cinfo.SyncRequests.ContainsKey(packet.Type.ToString()))
+ cinfo.SyncRequests[packet.Type.ToString()] = 0;
+ cinfo.SyncRequests[packet.Type.ToString()]++;
+
result = pprocessor.method(this, packet);
}
}
@@ -698,6 +708,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
if (found)
{
+ ClientInfo cinfo = UDPClient.GetClientInfo();
+ if (!cinfo.GenericRequests.ContainsKey(packet.Type.ToString()))
+ cinfo.GenericRequests[packet.Type.ToString()] = 0;
+ cinfo.GenericRequests[packet.Type.ToString()]++;
+
result = method(this, packet);
}
}
@@ -12030,7 +12045,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
ClientInfo info = m_udpClient.GetClientInfo();
info.proxyEP = null;
- info.agentcircuit = RequestClientInfo();
+ if (info.agentcircuit == null)
+ info.agentcircuit = RequestClientInfo();
return info;
}
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs
index 621e0fd..7749446 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs
@@ -159,6 +159,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_defaultRTO = 1000; // 1sec is the recommendation in the RFC
private int m_maxRTO = 60000;
+ private ClientInfo m_info = new ClientInfo();
+
///
/// Default constructor
///
@@ -240,20 +242,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// TODO: This data structure is wrong in so many ways. Locking and copying the entire lists
// of pending and needed ACKs for every client every time some method wants information about
// this connection is a recipe for poor performance
- ClientInfo info = new ClientInfo();
- info.pendingAcks = new Dictionary();
- info.needAck = new Dictionary();
-
- info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
- info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
- info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
- info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
- info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
- info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
- info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
- info.totalThrottle = (int)m_throttleCategory.DripRate;
-
- return info;
+
+ m_info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
+ m_info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
+ m_info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
+ m_info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
+ m_info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
+ m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
+ m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
+ m_info.totalThrottle = (int)m_throttleCategory.DripRate;
+
+ return m_info;
}
///
diff --git a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs
index 992f38e..b1aec81 100644
--- a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs
+++ b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs
@@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using System.Text;
using log4net;
@@ -51,7 +52,7 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "LindenUDPInfoModule")]
public class LindenUDPInfoModule : ISharedRegionModule
{
-// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected Dictionary m_scenes = new Dictionary();
@@ -130,6 +131,15 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden
"Go on/off emergency monitoring mode",
"Go on/off emergency monitoring mode",
HandleEmergencyMonitoring);
+
+ scene.AddCommand(
+ "Comms", this, "show client-stats",
+ "show client-stats [first_name last_name]",
+ "Show client request stats",
+ "Without the 'first_name last_name' option, all clients are shown."
+ + " With the 'first_name last_name' option only a specific client is shown.",
+ (mod, cmd) => MainConsole.Instance.Output(HandleClientStatsReport(cmd)));
+
}
public void RemoveRegion(Scene scene)
@@ -587,6 +597,101 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden
(throttleRates.Asset * 8) / 1000);
return report.ToString();
- }
+ }
+
+ ///
+ /// Show client stats data
+ ///
+ ///
+ ///
+ protected string HandleClientStatsReport(string[] showParams)
+ {
+ // NOTE: This writes to m_log on purpose. We want to store this information
+ // in case we need to analyze it later.
+ //
+ if (showParams.Length <= 3)
+ {
+ m_log.InfoFormat("[INFO]: {0,-12} {1,20} {2,6} {3,11} {4, 10}", "Region", "Name", "Root", "Time", "Reqs/min");
+ foreach (Scene scene in m_scenes.Values)
+ {
+ scene.ForEachClient(
+ delegate(IClientAPI client)
+ {
+ if (client is LLClientView)
+ {
+ LLClientView llClient = client as LLClientView;
+ ClientInfo cinfo = llClient.UDPClient.GetClientInfo();
+ int avg_reqs = cinfo.AsyncRequests.Count + cinfo.GenericRequests.Count + cinfo.SyncRequests.Count;
+ avg_reqs = avg_reqs / ((DateTime.Now - cinfo.StartedTime).Minutes + 1);
+
+ m_log.InfoFormat("[INFO]: {0,-12} {1,20} {2,4} {3,9}min {4,10}",
+ scene.RegionInfo.RegionName, llClient.Name,
+ (llClient.SceneAgent.IsChildAgent ? "N" : "Y"), (DateTime.Now - cinfo.StartedTime).Minutes, avg_reqs);
+ }
+ });
+ }
+ return string.Empty;
+ }
+
+ string fname = "", lname = "";
+
+ if (showParams.Length > 2)
+ fname = showParams[2];
+ if (showParams.Length > 3)
+ lname = showParams[3];
+
+ foreach (Scene scene in m_scenes.Values)
+ {
+ scene.ForEachClient(
+ delegate(IClientAPI client)
+ {
+ if (client is LLClientView)
+ {
+ LLClientView llClient = client as LLClientView;
+
+ if (llClient.Name == fname + " " + lname)
+ {
+
+ ClientInfo cinfo = llClient.GetClientInfo();
+ AgentCircuitData aCircuit = scene.AuthenticateHandler.GetAgentCircuitData(llClient.CircuitCode);
+ if (aCircuit == null) // create a dummy one
+ aCircuit = new AgentCircuitData();
+
+ if (!llClient.SceneAgent.IsChildAgent)
+ m_log.InfoFormat("[INFO]: {0} # {1} # {2}", llClient.Name, aCircuit.Viewer, aCircuit.Id0);
+
+ int avg_reqs = cinfo.AsyncRequests.Count + cinfo.GenericRequests.Count + cinfo.SyncRequests.Count;
+ avg_reqs = avg_reqs / ((DateTime.Now - cinfo.StartedTime).Minutes + 1);
+
+ m_log.InfoFormat("[INFO]:");
+ m_log.InfoFormat("[INFO]: {0} # {1} # Time: {2}min # Avg Reqs/min: {3}", scene.RegionInfo.RegionName,
+ (llClient.SceneAgent.IsChildAgent ? "Child" : "Root"), (DateTime.Now - cinfo.StartedTime).Minutes, avg_reqs);
+
+ Dictionary sortedDict = (from entry in cinfo.AsyncRequests orderby entry.Value descending select entry)
+ .ToDictionary(pair => pair.Key, pair => pair.Value);
+
+ m_log.InfoFormat("[INFO]: {0,25}", "TOP ASYNC");
+ foreach (KeyValuePair kvp in sortedDict.Take(12))
+ m_log.InfoFormat("[INFO]: {0,25} {1,-6}", kvp.Key, kvp.Value);
+
+ m_log.InfoFormat("[INFO]:");
+ sortedDict = (from entry in cinfo.SyncRequests orderby entry.Value descending select entry)
+ .ToDictionary(pair => pair.Key, pair => pair.Value);
+ m_log.InfoFormat("[INFO]: {0,25}", "TOP SYNC");
+ foreach (KeyValuePair kvp in sortedDict.Take(12))
+ m_log.InfoFormat("[INFO]: {0,25} {1,-6}", kvp.Key, kvp.Value);
+
+ m_log.InfoFormat("[INFO]:");
+ sortedDict = (from entry in cinfo.GenericRequests orderby entry.Value descending select entry)
+ .ToDictionary(pair => pair.Key, pair => pair.Value);
+ m_log.InfoFormat("[INFO]: {0,25}", "TOP GENERIC");
+ foreach (KeyValuePair kvp in sortedDict.Take(12))
+ m_log.InfoFormat("[INFO]: {0,25} {1,-6}", kvp.Key, kvp.Value);
+ }
+ }
+ });
+ }
+ return string.Empty;
+ }
}
}
\ No newline at end of file
--
cgit v1.1