LindenUDPInfoModule.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Reflection;
  30. using System.Text;
  31. using log4net;
  32. using Mono.Addins;
  33. using Nini.Config;
  34. using OpenMetaverse;
  35. using OpenSim.Framework;
  36. using OpenSim.Framework.Console;
  37. using OpenSim.Framework.Statistics;
  38. using OpenSim.Region.ClientStack.LindenUDP;
  39. using OpenSim.Region.Framework.Interfaces;
  40. using OpenSim.Region.Framework.Scenes;
  41. namespace OpenSim.Region.CoreModules.UDP.Linden
  42. {
  43. /// <summary>
  44. /// A module that just holds commands for inspecting the current state of the Linden UDP stack.
  45. /// </summary>
  46. /// <remarks>
  47. /// All actual client stack functionality remains in OpenSim.Region.ClientStack.LindenUDP
  48. /// </remarks>
  49. [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "LindenUDPInfoModule")]
  50. public class LindenUDPInfoModule : ISharedRegionModule
  51. {
  52. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  53. protected Dictionary<UUID, Scene> m_scenes = new Dictionary<UUID, Scene>();
  54. public string Name { get { return "Linden UDP Module"; } }
  55. public Type ReplaceableInterface { get { return null; } }
  56. public void Initialise(IConfigSource source)
  57. {
  58. // m_log.DebugFormat("[LINDEN UDP INFO MODULE]: INITIALIZED MODULE");
  59. }
  60. public void PostInitialise()
  61. {
  62. // m_log.DebugFormat("[LINDEN UDP INFO MODULE]: POST INITIALIZED MODULE");
  63. }
  64. public void Close()
  65. {
  66. // m_log.DebugFormat("[LINDEN UDP INFO MODULE]: CLOSED MODULE");
  67. }
  68. public void AddRegion(Scene scene)
  69. {
  70. // m_log.DebugFormat("[LINDEN UDP INFO MODULE]: REGION {0} ADDED", scene.RegionInfo.RegionName);
  71. lock (m_scenes)
  72. m_scenes[scene.RegionInfo.RegionID] = scene;
  73. scene.AddCommand(
  74. this, "show queues",
  75. "show queues [full]",
  76. "Show queue data for each client",
  77. "Without the 'full' option, only root agents are shown."
  78. + " With the 'full' option child agents are also shown.",
  79. ShowQueuesReport);
  80. scene.AddCommand(
  81. this, "show throttles",
  82. "show throttles [full]",
  83. "Show throttle settings for each client and for the server overall",
  84. "Without the 'full' option, only root agents are shown."
  85. + " With the 'full' option child agents are also shown.",
  86. ShowThrottlesReport);
  87. }
  88. public void RemoveRegion(Scene scene)
  89. {
  90. // m_log.DebugFormat("[LINDEN UDP INFO MODULE]: REGION {0} REMOVED", scene.RegionInfo.RegionName);
  91. lock (m_scenes)
  92. m_scenes.Remove(scene.RegionInfo.RegionID);
  93. }
  94. public void RegionLoaded(Scene scene)
  95. {
  96. // m_log.DebugFormat("[LINDEN UDP INFO MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName);
  97. }
  98. protected void ShowQueuesReport(string module, string[] cmd)
  99. {
  100. MainConsole.Instance.Output(GetQueuesReport(cmd));
  101. }
  102. protected void ShowThrottlesReport(string module, string[] cmd)
  103. {
  104. MainConsole.Instance.Output(GetThrottlesReport(cmd));
  105. }
  106. protected string GetColumnEntry(string entry, int maxLength, int columnPadding)
  107. {
  108. return string.Format(
  109. "{0,-" + maxLength + "}{1,-" + columnPadding + "}",
  110. entry.Length > maxLength ? entry.Substring(0, maxLength) : entry,
  111. "");
  112. }
  113. /// <summary>
  114. /// Generate UDP Queue data report for each client
  115. /// </summary>
  116. /// <param name="showParams"></param>
  117. /// <returns></returns>
  118. protected string GetQueuesReport(string[] showParams)
  119. {
  120. bool showChildren = false;
  121. if (showParams.Length > 2 && showParams[2] == "full")
  122. showChildren = true;
  123. StringBuilder report = new StringBuilder();
  124. int columnPadding = 2;
  125. int maxNameLength = 18;
  126. int maxRegionNameLength = 14;
  127. int maxTypeLength = 4;
  128. int totalInfoFieldsLength = maxNameLength + columnPadding + maxRegionNameLength + columnPadding + maxTypeLength + columnPadding;
  129. report.Append(GetColumnEntry("User", maxNameLength, columnPadding));
  130. report.Append(GetColumnEntry("Region", maxRegionNameLength, columnPadding));
  131. report.Append(GetColumnEntry("Type", maxTypeLength, columnPadding));
  132. report.AppendFormat(
  133. "{0,7} {1,7} {2,9} {3,8} {4,7} {5,7} {6,7} {7,7} {8,9} {9,7} {10,7}\n",
  134. "Pkts",
  135. "Pkts",
  136. "Bytes",
  137. "Pkts",
  138. "Pkts",
  139. "Pkts",
  140. "Pkts",
  141. "Pkts",
  142. "Pkts",
  143. "Pkts",
  144. "Pkts");
  145. report.AppendFormat("{0,-" + totalInfoFieldsLength + "}", "");
  146. report.AppendFormat(
  147. "{0,7} {1,7} {2,9} {3,8} {4,7} {5,7} {6,7} {7,7} {8,9} {9,7} {10,7}\n",
  148. "Out",
  149. "In",
  150. "Unacked",
  151. "Resend",
  152. "Land",
  153. "Wind",
  154. "Cloud",
  155. "Task",
  156. "Texture",
  157. "Asset",
  158. "State");
  159. lock (m_scenes)
  160. {
  161. foreach (Scene scene in m_scenes.Values)
  162. {
  163. scene.ForEachClient(
  164. delegate(IClientAPI client)
  165. {
  166. if (client is IStatsCollector)
  167. {
  168. bool isChild = scene.PresenceChildStatus(client.AgentId);
  169. if (isChild && !showChildren)
  170. return;
  171. string name = client.Name;
  172. string regionName = scene.RegionInfo.RegionName;
  173. report.Append(GetColumnEntry(name, maxNameLength, columnPadding));
  174. report.Append(GetColumnEntry(regionName, maxRegionNameLength, columnPadding));
  175. report.Append(GetColumnEntry(isChild ? "Cd" : "Rt", maxTypeLength, columnPadding));
  176. IStatsCollector stats = (IStatsCollector)client;
  177. report.AppendLine(stats.Report());
  178. }
  179. });
  180. }
  181. }
  182. return report.ToString();
  183. }
  184. /// <summary>
  185. /// Show throttle data
  186. /// </summary>
  187. /// <param name="showParams"></param>
  188. /// <returns></returns>
  189. protected string GetThrottlesReport(string[] showParams)
  190. {
  191. bool showChildren = false;
  192. if (showParams.Length > 2 && showParams[2] == "full")
  193. showChildren = true;
  194. StringBuilder report = new StringBuilder();
  195. int columnPadding = 2;
  196. int maxNameLength = 18;
  197. int maxRegionNameLength = 14;
  198. int maxTypeLength = 4;
  199. int totalInfoFieldsLength = maxNameLength + columnPadding + maxRegionNameLength + columnPadding + maxTypeLength + columnPadding;
  200. report.Append(GetColumnEntry("User", maxNameLength, columnPadding));
  201. report.Append(GetColumnEntry("Region", maxRegionNameLength, columnPadding));
  202. report.Append(GetColumnEntry("Type", maxTypeLength, columnPadding));
  203. report.AppendFormat(
  204. "{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}\n",
  205. "Total",
  206. "Resend",
  207. "Land",
  208. "Wind",
  209. "Cloud",
  210. "Task",
  211. "Texture",
  212. "Asset");
  213. report.AppendFormat("{0,-" + totalInfoFieldsLength + "}", "");
  214. report.AppendFormat(
  215. "{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}",
  216. "kb/s",
  217. "kb/s",
  218. "kb/s",
  219. "kb/s",
  220. "kb/s",
  221. "kb/s",
  222. "kb/s",
  223. "kb/s");
  224. report.AppendLine();
  225. bool firstClient = true;
  226. lock (m_scenes)
  227. {
  228. foreach (Scene scene in m_scenes.Values)
  229. {
  230. scene.ForEachClient(
  231. delegate(IClientAPI client)
  232. {
  233. if (client is LLClientView)
  234. {
  235. LLClientView llClient = client as LLClientView;
  236. if (firstClient)
  237. {
  238. report.AppendLine(GetServerThrottlesReport(llClient.UDPServer));
  239. firstClient = false;
  240. }
  241. bool isChild = scene.PresenceChildStatus(client.AgentId);
  242. if (isChild && !showChildren)
  243. return;
  244. string name = client.Name;
  245. string regionName = scene.RegionInfo.RegionName;
  246. LLUDPClient llUdpClient = llClient.UDPClient;
  247. ClientInfo ci = llUdpClient.GetClientInfo();
  248. report.Append(GetColumnEntry(name, maxNameLength, columnPadding));
  249. report.Append(GetColumnEntry(regionName, maxRegionNameLength, columnPadding));
  250. report.Append(GetColumnEntry(isChild ? "Cd" : "Rt", maxTypeLength, columnPadding));
  251. report.AppendFormat(
  252. "{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}",
  253. (ci.totalThrottle * 8) / 1000,
  254. (ci.resendThrottle * 8) / 1000,
  255. (ci.landThrottle * 8) / 1000,
  256. (ci.windThrottle * 8) / 1000,
  257. (ci.cloudThrottle * 8) / 1000,
  258. (ci.taskThrottle * 8) / 1000,
  259. (ci.textureThrottle * 8) / 1000,
  260. (ci.assetThrottle * 8) / 1000);
  261. report.AppendLine();
  262. }
  263. });
  264. }
  265. }
  266. return report.ToString();
  267. }
  268. protected string GetServerThrottlesReport(LLUDPServer udpServer)
  269. {
  270. StringBuilder report = new StringBuilder();
  271. int columnPadding = 2;
  272. int maxNameLength = 18;
  273. int maxRegionNameLength = 14;
  274. int maxTypeLength = 4;
  275. string name = "SERVER AGENT LIMITS";
  276. report.Append(GetColumnEntry(name, maxNameLength, columnPadding));
  277. report.Append(GetColumnEntry("-", maxRegionNameLength, columnPadding));
  278. report.Append(GetColumnEntry("-", maxTypeLength, columnPadding));
  279. ThrottleRates throttleRates = udpServer.ThrottleRates;
  280. report.AppendFormat(
  281. "{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}",
  282. "n/a",
  283. (throttleRates.ResendLimit * 8) / 1000,
  284. (throttleRates.LandLimit * 8) / 1000,
  285. (throttleRates.WindLimit * 8) / 1000,
  286. (throttleRates.CloudLimit * 8) / 1000,
  287. (throttleRates.TaskLimit * 8) / 1000,
  288. (throttleRates.TextureLimit * 8) / 1000,
  289. (throttleRates.AssetLimit * 8) / 1000);
  290. return report.ToString();
  291. }
  292. }
  293. }