diff options
-rw-r--r-- | OpenSim/Framework/ClientInfo.cs | 11 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | 18 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs | 27 | ||||
-rw-r--r-- | OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs | 109 |
4 files changed, 145 insertions, 20 deletions
diff --git a/OpenSim/Framework/ClientInfo.cs b/OpenSim/Framework/ClientInfo.cs index 62acb70..9021315 100644 --- a/OpenSim/Framework/ClientInfo.cs +++ b/OpenSim/Framework/ClientInfo.cs | |||
@@ -33,12 +33,13 @@ namespace OpenSim.Framework | |||
33 | { | 33 | { |
34 | public class ClientInfo | 34 | public class ClientInfo |
35 | { | 35 | { |
36 | public AgentCircuitData agentcircuit; | 36 | public readonly DateTime StartedTime = DateTime.Now; |
37 | public AgentCircuitData agentcircuit = null; | ||
37 | 38 | ||
38 | public Dictionary<uint, byte[]> needAck; | 39 | public Dictionary<uint, byte[]> needAck; |
39 | 40 | ||
40 | public List<byte[]> out_packets; | 41 | public List<byte[]> out_packets = new List<byte[]>(); |
41 | public Dictionary<uint, uint> pendingAcks; | 42 | public Dictionary<uint, uint> pendingAcks = new Dictionary<uint,uint>(); |
42 | public EndPoint proxyEP; | 43 | public EndPoint proxyEP; |
43 | 44 | ||
44 | public uint sequence; | 45 | public uint sequence; |
@@ -53,5 +54,9 @@ namespace OpenSim.Framework | |||
53 | public int assetThrottle; | 54 | public int assetThrottle; |
54 | public int textureThrottle; | 55 | public int textureThrottle; |
55 | public int totalThrottle; | 56 | public int totalThrottle; |
57 | |||
58 | public Dictionary<string, int> SyncRequests = new Dictionary<string,int>(); | ||
59 | public Dictionary<string, int> AsyncRequests = new Dictionary<string,int>(); | ||
60 | public Dictionary<string, int> GenericRequests = new Dictionary<string,int>(); | ||
56 | } | 61 | } |
57 | } | 62 | } |
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 | |||
678 | //there is a local handler for this packet type | 678 | //there is a local handler for this packet type |
679 | if (pprocessor.Async) | 679 | if (pprocessor.Async) |
680 | { | 680 | { |
681 | ClientInfo cinfo = UDPClient.GetClientInfo(); | ||
682 | if (!cinfo.AsyncRequests.ContainsKey(packet.Type.ToString())) | ||
683 | cinfo.AsyncRequests[packet.Type.ToString()] = 0; | ||
684 | cinfo.AsyncRequests[packet.Type.ToString()]++; | ||
685 | |||
681 | object obj = new AsyncPacketProcess(this, pprocessor.method, packet); | 686 | object obj = new AsyncPacketProcess(this, pprocessor.method, packet); |
682 | Util.FireAndForget(ProcessSpecificPacketAsync, obj); | 687 | Util.FireAndForget(ProcessSpecificPacketAsync, obj); |
683 | result = true; | 688 | result = true; |
684 | } | 689 | } |
685 | else | 690 | else |
686 | { | 691 | { |
692 | ClientInfo cinfo = UDPClient.GetClientInfo(); | ||
693 | if (!cinfo.SyncRequests.ContainsKey(packet.Type.ToString())) | ||
694 | cinfo.SyncRequests[packet.Type.ToString()] = 0; | ||
695 | cinfo.SyncRequests[packet.Type.ToString()]++; | ||
696 | |||
687 | result = pprocessor.method(this, packet); | 697 | result = pprocessor.method(this, packet); |
688 | } | 698 | } |
689 | } | 699 | } |
@@ -698,6 +708,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
698 | } | 708 | } |
699 | if (found) | 709 | if (found) |
700 | { | 710 | { |
711 | ClientInfo cinfo = UDPClient.GetClientInfo(); | ||
712 | if (!cinfo.GenericRequests.ContainsKey(packet.Type.ToString())) | ||
713 | cinfo.GenericRequests[packet.Type.ToString()] = 0; | ||
714 | cinfo.GenericRequests[packet.Type.ToString()]++; | ||
715 | |||
701 | result = method(this, packet); | 716 | result = method(this, packet); |
702 | } | 717 | } |
703 | } | 718 | } |
@@ -12030,7 +12045,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12030 | ClientInfo info = m_udpClient.GetClientInfo(); | 12045 | ClientInfo info = m_udpClient.GetClientInfo(); |
12031 | 12046 | ||
12032 | info.proxyEP = null; | 12047 | info.proxyEP = null; |
12033 | info.agentcircuit = RequestClientInfo(); | 12048 | if (info.agentcircuit == null) |
12049 | info.agentcircuit = RequestClientInfo(); | ||
12034 | 12050 | ||
12035 | return info; | 12051 | return info; |
12036 | } | 12052 | } |
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 | |||
159 | private int m_defaultRTO = 1000; // 1sec is the recommendation in the RFC | 159 | private int m_defaultRTO = 1000; // 1sec is the recommendation in the RFC |
160 | private int m_maxRTO = 60000; | 160 | private int m_maxRTO = 60000; |
161 | 161 | ||
162 | private ClientInfo m_info = new ClientInfo(); | ||
163 | |||
162 | /// <summary> | 164 | /// <summary> |
163 | /// Default constructor | 165 | /// Default constructor |
164 | /// </summary> | 166 | /// </summary> |
@@ -240,20 +242,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
240 | // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists | 242 | // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists |
241 | // of pending and needed ACKs for every client every time some method wants information about | 243 | // of pending and needed ACKs for every client every time some method wants information about |
242 | // this connection is a recipe for poor performance | 244 | // this connection is a recipe for poor performance |
243 | ClientInfo info = new ClientInfo(); | 245 | |
244 | info.pendingAcks = new Dictionary<uint, uint>(); | 246 | m_info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; |
245 | info.needAck = new Dictionary<uint, byte[]>(); | 247 | m_info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; |
246 | 248 | m_info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; | |
247 | info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; | 249 | m_info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; |
248 | info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; | 250 | m_info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; |
249 | info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; | 251 | m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; |
250 | info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; | 252 | m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; |
251 | info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; | 253 | m_info.totalThrottle = (int)m_throttleCategory.DripRate; |
252 | info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; | 254 | |
253 | info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; | 255 | return m_info; |
254 | info.totalThrottle = (int)m_throttleCategory.DripRate; | ||
255 | |||
256 | return info; | ||
257 | } | 256 | } |
258 | 257 | ||
259 | /// <summary> | 258 | /// <summary> |
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 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Linq; | ||
30 | using System.Reflection; | 31 | using System.Reflection; |
31 | using System.Text; | 32 | using System.Text; |
32 | using log4net; | 33 | using log4net; |
@@ -51,7 +52,7 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
51 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "LindenUDPInfoModule")] | 52 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "LindenUDPInfoModule")] |
52 | public class LindenUDPInfoModule : ISharedRegionModule | 53 | public class LindenUDPInfoModule : ISharedRegionModule |
53 | { | 54 | { |
54 | // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 55 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
55 | 56 | ||
56 | protected Dictionary<UUID, Scene> m_scenes = new Dictionary<UUID, Scene>(); | 57 | protected Dictionary<UUID, Scene> m_scenes = new Dictionary<UUID, Scene>(); |
57 | 58 | ||
@@ -130,6 +131,15 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
130 | "Go on/off emergency monitoring mode", | 131 | "Go on/off emergency monitoring mode", |
131 | "Go on/off emergency monitoring mode", | 132 | "Go on/off emergency monitoring mode", |
132 | HandleEmergencyMonitoring); | 133 | HandleEmergencyMonitoring); |
134 | |||
135 | scene.AddCommand( | ||
136 | "Comms", this, "show client-stats", | ||
137 | "show client-stats [first_name last_name]", | ||
138 | "Show client request stats", | ||
139 | "Without the 'first_name last_name' option, all clients are shown." | ||
140 | + " With the 'first_name last_name' option only a specific client is shown.", | ||
141 | (mod, cmd) => MainConsole.Instance.Output(HandleClientStatsReport(cmd))); | ||
142 | |||
133 | } | 143 | } |
134 | 144 | ||
135 | public void RemoveRegion(Scene scene) | 145 | public void RemoveRegion(Scene scene) |
@@ -587,6 +597,101 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
587 | (throttleRates.Asset * 8) / 1000); | 597 | (throttleRates.Asset * 8) / 1000); |
588 | 598 | ||
589 | return report.ToString(); | 599 | return report.ToString(); |
590 | } | 600 | } |
601 | |||
602 | /// <summary> | ||
603 | /// Show client stats data | ||
604 | /// </summary> | ||
605 | /// <param name="showParams"></param> | ||
606 | /// <returns></returns> | ||
607 | protected string HandleClientStatsReport(string[] showParams) | ||
608 | { | ||
609 | // NOTE: This writes to m_log on purpose. We want to store this information | ||
610 | // in case we need to analyze it later. | ||
611 | // | ||
612 | if (showParams.Length <= 3) | ||
613 | { | ||
614 | m_log.InfoFormat("[INFO]: {0,-12} {1,20} {2,6} {3,11} {4, 10}", "Region", "Name", "Root", "Time", "Reqs/min"); | ||
615 | foreach (Scene scene in m_scenes.Values) | ||
616 | { | ||
617 | scene.ForEachClient( | ||
618 | delegate(IClientAPI client) | ||
619 | { | ||
620 | if (client is LLClientView) | ||
621 | { | ||
622 | LLClientView llClient = client as LLClientView; | ||
623 | ClientInfo cinfo = llClient.UDPClient.GetClientInfo(); | ||
624 | int avg_reqs = cinfo.AsyncRequests.Count + cinfo.GenericRequests.Count + cinfo.SyncRequests.Count; | ||
625 | avg_reqs = avg_reqs / ((DateTime.Now - cinfo.StartedTime).Minutes + 1); | ||
626 | |||
627 | m_log.InfoFormat("[INFO]: {0,-12} {1,20} {2,4} {3,9}min {4,10}", | ||
628 | scene.RegionInfo.RegionName, llClient.Name, | ||
629 | (llClient.SceneAgent.IsChildAgent ? "N" : "Y"), (DateTime.Now - cinfo.StartedTime).Minutes, avg_reqs); | ||
630 | } | ||
631 | }); | ||
632 | } | ||
633 | return string.Empty; | ||
634 | } | ||
635 | |||
636 | string fname = "", lname = ""; | ||
637 | |||
638 | if (showParams.Length > 2) | ||
639 | fname = showParams[2]; | ||
640 | if (showParams.Length > 3) | ||
641 | lname = showParams[3]; | ||
642 | |||
643 | foreach (Scene scene in m_scenes.Values) | ||
644 | { | ||
645 | scene.ForEachClient( | ||
646 | delegate(IClientAPI client) | ||
647 | { | ||
648 | if (client is LLClientView) | ||
649 | { | ||
650 | LLClientView llClient = client as LLClientView; | ||
651 | |||
652 | if (llClient.Name == fname + " " + lname) | ||
653 | { | ||
654 | |||
655 | ClientInfo cinfo = llClient.GetClientInfo(); | ||
656 | AgentCircuitData aCircuit = scene.AuthenticateHandler.GetAgentCircuitData(llClient.CircuitCode); | ||
657 | if (aCircuit == null) // create a dummy one | ||
658 | aCircuit = new AgentCircuitData(); | ||
659 | |||
660 | if (!llClient.SceneAgent.IsChildAgent) | ||
661 | m_log.InfoFormat("[INFO]: {0} # {1} # {2}", llClient.Name, aCircuit.Viewer, aCircuit.Id0); | ||
662 | |||
663 | int avg_reqs = cinfo.AsyncRequests.Count + cinfo.GenericRequests.Count + cinfo.SyncRequests.Count; | ||
664 | avg_reqs = avg_reqs / ((DateTime.Now - cinfo.StartedTime).Minutes + 1); | ||
665 | |||
666 | m_log.InfoFormat("[INFO]:"); | ||
667 | m_log.InfoFormat("[INFO]: {0} # {1} # Time: {2}min # Avg Reqs/min: {3}", scene.RegionInfo.RegionName, | ||
668 | (llClient.SceneAgent.IsChildAgent ? "Child" : "Root"), (DateTime.Now - cinfo.StartedTime).Minutes, avg_reqs); | ||
669 | |||
670 | Dictionary<string, int> sortedDict = (from entry in cinfo.AsyncRequests orderby entry.Value descending select entry) | ||
671 | .ToDictionary(pair => pair.Key, pair => pair.Value); | ||
672 | |||
673 | m_log.InfoFormat("[INFO]: {0,25}", "TOP ASYNC"); | ||
674 | foreach (KeyValuePair<string, int> kvp in sortedDict.Take(12)) | ||
675 | m_log.InfoFormat("[INFO]: {0,25} {1,-6}", kvp.Key, kvp.Value); | ||
676 | |||
677 | m_log.InfoFormat("[INFO]:"); | ||
678 | sortedDict = (from entry in cinfo.SyncRequests orderby entry.Value descending select entry) | ||
679 | .ToDictionary(pair => pair.Key, pair => pair.Value); | ||
680 | m_log.InfoFormat("[INFO]: {0,25}", "TOP SYNC"); | ||
681 | foreach (KeyValuePair<string, int> kvp in sortedDict.Take(12)) | ||
682 | m_log.InfoFormat("[INFO]: {0,25} {1,-6}", kvp.Key, kvp.Value); | ||
683 | |||
684 | m_log.InfoFormat("[INFO]:"); | ||
685 | sortedDict = (from entry in cinfo.GenericRequests orderby entry.Value descending select entry) | ||
686 | .ToDictionary(pair => pair.Key, pair => pair.Value); | ||
687 | m_log.InfoFormat("[INFO]: {0,25}", "TOP GENERIC"); | ||
688 | foreach (KeyValuePair<string, int> kvp in sortedDict.Take(12)) | ||
689 | m_log.InfoFormat("[INFO]: {0,25} {1,-6}", kvp.Key, kvp.Value); | ||
690 | } | ||
691 | } | ||
692 | }); | ||
693 | } | ||
694 | return string.Empty; | ||
695 | } | ||
591 | } | 696 | } |
592 | } \ No newline at end of file | 697 | } \ No newline at end of file |