Added show client-stats [first last] command to expose what viewers are requesting.
parent
38e6da5522
commit
1b265b213b
|
@ -33,12 +33,13 @@ namespace OpenSim.Framework
|
|||
{
|
||||
public class ClientInfo
|
||||
{
|
||||
public AgentCircuitData agentcircuit;
|
||||
public readonly DateTime StartedTime = DateTime.Now;
|
||||
public AgentCircuitData agentcircuit = null;
|
||||
|
||||
public Dictionary<uint, byte[]> needAck;
|
||||
|
||||
public List<byte[]> out_packets;
|
||||
public Dictionary<uint, uint> pendingAcks;
|
||||
public List<byte[]> out_packets = new List<byte[]>();
|
||||
public Dictionary<uint, uint> pendingAcks = new Dictionary<uint,uint>();
|
||||
public EndPoint proxyEP;
|
||||
|
||||
public uint sequence;
|
||||
|
@ -53,5 +54,9 @@ namespace OpenSim.Framework
|
|||
public int assetThrottle;
|
||||
public int textureThrottle;
|
||||
public int totalThrottle;
|
||||
|
||||
public Dictionary<string, int> SyncRequests = new Dictionary<string,int>();
|
||||
public Dictionary<string, int> AsyncRequests = new Dictionary<string,int>();
|
||||
public Dictionary<string, int> GenericRequests = new Dictionary<string,int>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
|
@ -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<uint, uint>();
|
||||
info.needAck = new Dictionary<uint, byte[]>();
|
||||
|
||||
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;
|
||||
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 info;
|
||||
return m_info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -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<UUID, Scene> m_scenes = new Dictionary<UUID, Scene>();
|
||||
|
||||
|
@ -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)
|
||||
|
@ -588,5 +598,100 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden
|
|||
|
||||
return report.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show client stats data
|
||||
/// </summary>
|
||||
/// <param name="showParams"></param>
|
||||
/// <returns></returns>
|
||||
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<string, int> 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<string, int> 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<string, int> 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<string, int> kvp in sortedDict.Take(12))
|
||||
m_log.InfoFormat("[INFO]: {0,25} {1,-6}", kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue