OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
WebStatsModule.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.IO;
32 using System.Net; // to be used for REST-->Grid shortly
33 using System.Reflection;
34 using System.Text;
35 using System.Threading;
36 using log4net;
37 using Nini.Config;
38 using OpenMetaverse;
39 using OpenMetaverse.StructuredData;
40 using OpenSim.Framework;
41 using OpenSim.Framework.Servers;
42 using OpenSim.Framework.Servers.HttpServer;
43 using OpenSim.Region.Framework.Interfaces;
44 using OpenSim.Region.Framework.Scenes;
45 using Mono.Data.SqliteClient;
46 using Mono.Addins;
47 
49 
52 
53 namespace OpenSim.Region.UserStatistics
54 {
55  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebStatsModule")]
57  {
58  private static readonly ILog m_log =
59  LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
60 
61  private static SqliteConnection dbConn;
62 
66  private Dictionary<UUID, UserSession> m_sessions = new Dictionary<UUID, UserSession>();
67 
68  private List<Scene> m_scenes = new List<Scene>();
69  private Dictionary<string, IStatsController> reports = new Dictionary<string, IStatsController>();
70  private Dictionary<UUID, USimStatsData> m_simstatsCounters = new Dictionary<UUID, USimStatsData>();
71  private const int updateStatsMod = 6;
72  private int updateLogMod = 1;
73  private volatile int updateLogCounter = 0;
74  private volatile int concurrencyCounter = 0;
75  private bool enabled = false;
76  private string m_loglines = String.Empty;
77  private volatile int lastHit = 12000;
78 
79  #region ISharedRegionModule
80 
81  public virtual void Initialise(IConfigSource config)
82  {
83  IConfig cnfg = config.Configs["WebStats"];
84 
85  if (cnfg != null)
86  enabled = cnfg.GetBoolean("enabled", false);
87  }
88 
89  public virtual void PostInitialise()
90  {
91  if (!enabled)
92  return;
93 
94  if (Util.IsWindows())
95  Util.LoadArchSpecificWindowsDll("sqlite3.dll");
96 
97  //IConfig startupConfig = config.Configs["Startup"];
98 
99  dbConn = new SqliteConnection("URI=file:LocalUserStatistics.db,version=3");
100  dbConn.Open();
101  CreateTables(dbConn);
102 
104  Updater_distributor updatedep = new Updater_distributor();
105  ActiveConnectionsAJAX ajConnections = new ActiveConnectionsAJAX();
106  SimStatsAJAX ajSimStats = new SimStatsAJAX();
107  LogLinesAJAX ajLogLines = new LogLinesAJAX();
108  Default_Report defaultReport = new Default_Report();
109  Clients_report clientReport = new Clients_report();
110  Sessions_Report sessionsReport = new Sessions_Report();
111 
112  reports.Add("prototype.js", protodep);
113  reports.Add("updater.js", updatedep);
114  reports.Add("activeconnectionsajax.html", ajConnections);
115  reports.Add("simstatsajax.html", ajSimStats);
116  reports.Add("activelogajax.html", ajLogLines);
117  reports.Add("default.report", defaultReport);
118  reports.Add("clients.report", clientReport);
119  reports.Add("sessions.report", sessionsReport);
120 
121  reports.Add("sim.css", new Prototype_distributor("sim.css"));
122  reports.Add("sim.html", new Prototype_distributor("sim.html"));
123  reports.Add("jquery.js", new Prototype_distributor("jquery.js"));
124 
126  // Add Your own Reports here (Do Not Modify Lines here Devs!)
128 
130  // End Own reports section
132 
133  MainServer.Instance.AddHTTPHandler("/SStats/", HandleStatsRequest);
134  MainServer.Instance.AddHTTPHandler("/CAPS/VS/", HandleUnknownCAPSRequest);
135  }
136 
137  public virtual void AddRegion(Scene scene)
138  {
139  if (!enabled)
140  return;
141 
142  lock (m_scenes)
143  {
144  m_scenes.Add(scene);
145  updateLogMod = m_scenes.Count * 2;
146 
147  m_simstatsCounters.Add(scene.RegionInfo.RegionID, new USimStatsData(scene.RegionInfo.RegionID));
148 
149  scene.EventManager.OnRegisterCaps += OnRegisterCaps;
150  scene.EventManager.OnDeregisterCaps += OnDeRegisterCaps;
151  scene.EventManager.OnClientClosed += OnClientClosed;
152  scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
153  scene.StatsReporter.OnSendStatsResult += ReceiveClassicSimStatsPacket;
154  }
155  }
156 
157  public void RegionLoaded(Scene scene)
158  {
159  }
160 
161  public void RemoveRegion(Scene scene)
162  {
163  if (!enabled)
164  return;
165 
166  lock (m_scenes)
167  {
168  m_scenes.Remove(scene);
169  updateLogMod = m_scenes.Count * 2;
170  m_simstatsCounters.Remove(scene.RegionInfo.RegionID);
171  }
172  }
173 
174  public virtual void Close()
175  {
176  if (!enabled)
177  return;
178 
179  dbConn.Close();
180  dbConn.Dispose();
181  m_sessions.Clear();
182  m_scenes.Clear();
183  reports.Clear();
184  m_simstatsCounters.Clear();
185  }
186 
187  public virtual string Name
188  {
189  get { return "ViewerStatsModule"; }
190  }
191 
192  public Type ReplaceableInterface
193  {
194  get { return null; }
195  }
196 
197  #endregion
198 
199  private void ReceiveClassicSimStatsPacket(SimStats stats)
200  {
201  if (!enabled)
202  return;
203 
204  try
205  {
206  // Ignore the update if there's a report running right now
207  // ignore the update if there hasn't been a hit in 30 seconds.
208  if (concurrencyCounter > 0 || System.Environment.TickCount - lastHit > 30000)
209  return;
210 
211  // We will conduct this under lock so that fields such as updateLogCounter do not potentially get
212  // confused if a scene is removed.
213  // XXX: Possibly the scope of this lock could be reduced though it's not critical.
214  lock (m_scenes)
215  {
216  if (updateLogMod != 0 && updateLogCounter++ % updateLogMod == 0)
217  {
218  m_loglines = readLogLines(10);
219 
220  if (updateLogCounter > 10000)
221  updateLogCounter = 1;
222  }
223 
224  USimStatsData ss = m_simstatsCounters[stats.RegionUUID];
225 
226  if ((++ss.StatsCounter % updateStatsMod) == 0)
227  {
228  ss.ConsumeSimStats(stats);
229  }
230  }
231  }
232  catch (KeyNotFoundException)
233  {
234  }
235  }
236 
237  private Hashtable HandleUnknownCAPSRequest(Hashtable request)
238  {
239  //string regpath = request["uri"].ToString();
240  int response_code = 200;
241  string contenttype = "text/html";
242  UpdateUserStats(ParseViewerStats(request["body"].ToString(), UUID.Zero), dbConn);
243  Hashtable responsedata = new Hashtable();
244 
245  responsedata["int_response_code"] = response_code;
246  responsedata["content_type"] = contenttype;
247  responsedata["keepalive"] = false;
248  responsedata["str_response_string"] = string.Empty;
249  return responsedata;
250  }
251 
252  private Hashtable HandleStatsRequest(Hashtable request)
253  {
254  lastHit = System.Environment.TickCount;
255  Hashtable responsedata = new Hashtable();
256  string regpath = request["uri"].ToString();
257  int response_code = 404;
258  string contenttype = "text/html";
259  bool jsonFormatOutput = false;
260 
261  string strOut = string.Empty;
262 
263  // The request patch should be "/SStats/reportName" where 'reportName'
264  // is one of the names added to the 'reports' hashmap.
265  regpath = regpath.Remove(0, 8);
266  if (regpath.Length == 0) regpath = "default.report";
267  if (reports.ContainsKey(regpath))
268  {
269  IStatsController rep = reports[regpath];
270  Hashtable repParams = new Hashtable();
271 
272  if (request.ContainsKey("json"))
273  jsonFormatOutput = true;
274 
275  if (request.ContainsKey("requestvars"))
276  repParams["RequestVars"] = request["requestvars"];
277  else
278  repParams["RequestVars"] = new Hashtable();
279 
280  if (request.ContainsKey("querystringkeys"))
281  repParams["QueryStringKeys"] = request["querystringkeys"];
282  else
283  repParams["QueryStringKeys"] = new string[0];
284 
285 
286  repParams["DatabaseConnection"] = dbConn;
287  repParams["Scenes"] = m_scenes;
288  repParams["SimStats"] = m_simstatsCounters;
289  repParams["LogLines"] = m_loglines;
290  repParams["Reports"] = reports;
291 
292  concurrencyCounter++;
293 
294  if (jsonFormatOutput)
295  {
296  strOut = rep.RenderJson(rep.ProcessModel(repParams));
297  contenttype = "text/json";
298  }
299  else
300  {
301  strOut = rep.RenderView(rep.ProcessModel(repParams));
302  }
303 
304  if (regpath.EndsWith("js"))
305  {
306  contenttype = "text/javascript";
307  }
308 
309  if (regpath.EndsWith("css"))
310  {
311  contenttype = "text/css";
312  }
313 
314  concurrencyCounter--;
315 
316  response_code = 200;
317  }
318  else
319  {
320  strOut = MainServer.Instance.GetHTTP404("");
321  }
322 
323  responsedata["int_response_code"] = response_code;
324  responsedata["content_type"] = contenttype;
325  responsedata["keepalive"] = false;
326  responsedata["str_response_string"] = strOut;
327 
328  return responsedata;
329  }
330 
331  private void CreateTables(SqliteConnection db)
332  {
333  using (SqliteCommand createcmd = new SqliteCommand(SQL_STATS_TABLE_CREATE, db))
334  {
335  createcmd.ExecuteNonQuery();
336  }
337  }
338 
339  private void OnRegisterCaps(UUID agentID, Caps caps)
340  {
341 // m_log.DebugFormat("[WEB STATS MODULE]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps);
342 
343  string capsPath = "/CAPS/VS/" + UUID.Random();
344  caps.RegisterHandler(
345  "ViewerStats",
346  new RestStreamHandler(
347  "POST",
348  capsPath,
349  (request, path, param, httpRequest, httpResponse)
350  => ViewerStatsReport(request, path, param, agentID, caps),
351  "ViewerStats",
352  agentID.ToString()));
353  }
354 
355  private void OnDeRegisterCaps(UUID agentID, Caps caps)
356  {
357  }
358 
359  protected virtual void AddEventHandlers()
360  {
361  lock (m_scenes)
362  {
363  updateLogMod = m_scenes.Count * 2;
364  foreach (Scene scene in m_scenes)
365  {
366  scene.EventManager.OnRegisterCaps += OnRegisterCaps;
367  scene.EventManager.OnDeregisterCaps += OnDeRegisterCaps;
368  scene.EventManager.OnClientClosed += OnClientClosed;
369  scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
370  }
371  }
372  }
373 
374  private void OnMakeRootAgent(ScenePresence agent)
375  {
376 // m_log.DebugFormat(
377 // "[WEB STATS MODULE]: Looking for session {0} for {1} in {2}",
378 // agent.ControllingClient.SessionId, agent.Name, agent.Scene.Name);
379 
380  lock (m_sessions)
381  {
382  UserSession uid;
383 
384  if (!m_sessions.ContainsKey(agent.UUID))
385  {
386  UserSessionData usd = UserSessionUtil.newUserSessionData();
387  uid = new UserSession();
388  uid.name_f = agent.Firstname;
389  uid.name_l = agent.Lastname;
390  uid.session_data = usd;
391 
392  m_sessions.Add(agent.UUID, uid);
393  }
394  else
395  {
396  uid = m_sessions[agent.UUID];
397  }
398 
399  uid.region_id = agent.Scene.RegionInfo.RegionID;
400  uid.session_id = agent.ControllingClient.SessionId;
401  }
402  }
403 
404  private void OnClientClosed(UUID agentID, Scene scene)
405  {
406  lock (m_sessions)
407  {
408  if (m_sessions.ContainsKey(agentID) && m_sessions[agentID].region_id == scene.RegionInfo.RegionID)
409  {
410  m_sessions.Remove(agentID);
411  }
412  }
413  }
414 
415  private string readLogLines(int amount)
416  {
417  Encoding encoding = Encoding.ASCII;
418  int sizeOfChar = encoding.GetByteCount("\n");
419  byte[] buffer = encoding.GetBytes("\n");
420  string logfile = Util.logFile();
421  FileStream fs = new FileStream(logfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
422  Int64 tokenCount = 0;
423  Int64 endPosition = fs.Length / sizeOfChar;
424 
425  for (Int64 position = sizeOfChar; position < endPosition; position += sizeOfChar)
426  {
427  fs.Seek(-position, SeekOrigin.End);
428  fs.Read(buffer, 0, buffer.Length);
429 
430  if (encoding.GetString(buffer) == "\n")
431  {
432  tokenCount++;
433  if (tokenCount == amount)
434  {
435  byte[] returnBuffer = new byte[fs.Length - fs.Position];
436  fs.Read(returnBuffer, 0, returnBuffer.Length);
437  fs.Close();
438  fs.Dispose();
439  return encoding.GetString(returnBuffer);
440  }
441  }
442  }
443 
444  // handle case where number of tokens in file is less than numberOfTokens
445  fs.Seek(0, SeekOrigin.Begin);
446  buffer = new byte[fs.Length];
447  fs.Read(buffer, 0, buffer.Length);
448  fs.Close();
449  fs.Dispose();
450  return encoding.GetString(buffer);
451  }
452 
462  private string ViewerStatsReport(string request, string path, string param,
463  UUID agentID, Caps caps)
464  {
465 // m_log.DebugFormat("[WEB STATS MODULE]: Received viewer starts report from {0}", agentID);
466 
467  UpdateUserStats(ParseViewerStats(request, agentID), dbConn);
468 
469  return String.Empty;
470  }
471 
472  private UserSession ParseViewerStats(string request, UUID agentID)
473  {
474  UserSession uid = new UserSession();
475  UserSessionData usd;
476  OSD message = OSDParser.DeserializeLLSDXml(request);
477  OSDMap mmap;
478 
479  lock (m_sessions)
480  {
481  if (agentID != UUID.Zero)
482  {
483  if (!m_sessions.ContainsKey(agentID))
484  {
485  m_log.WarnFormat("[WEB STATS MODULE]: no session for stat disclosure for agent {0}", agentID);
486  return new UserSession();
487  }
488 
489  uid = m_sessions[agentID];
490 
491 // m_log.DebugFormat("[WEB STATS MODULE]: Got session {0} for {1}", uid.session_id, agentID);
492  }
493  else
494  {
495  // parse through the beginning to locate the session
496  if (message.Type != OSDType.Map)
497  return new UserSession();
498 
499  mmap = (OSDMap)message;
500  {
501  UUID sessionID = mmap["session_id"].AsUUID();
502 
503  if (sessionID == UUID.Zero)
504  return new UserSession();
505 
506 
507  // search through each session looking for the owner
508  foreach (UUID usersessionid in m_sessions.Keys)
509  {
510  // got it!
511  if (m_sessions[usersessionid].session_id == sessionID)
512  {
513  agentID = usersessionid;
514  uid = m_sessions[usersessionid];
515  break;
516  }
517 
518  }
519 
520  // can't find a session
521  if (agentID == UUID.Zero)
522  {
523  return new UserSession();
524  }
525  }
526  }
527  }
528 
529  usd = uid.session_data;
530 
531  if (message.Type != OSDType.Map)
532  return new UserSession();
533 
534  mmap = (OSDMap)message;
535  {
536  if (mmap["agent"].Type != OSDType.Map)
537  return new UserSession();
538  OSDMap agent_map = (OSDMap)mmap["agent"];
539  usd.agent_id = agentID;
540  usd.name_f = uid.name_f;
541  usd.name_l = uid.name_l;
542  usd.region_id = uid.region_id;
543  usd.a_language = agent_map["language"].AsString();
544  usd.mem_use = (float)agent_map["mem_use"].AsReal();
545  usd.meters_traveled = (float)agent_map["meters_traveled"].AsReal();
546  usd.regions_visited = agent_map["regions_visited"].AsInteger();
547  usd.run_time = (float)agent_map["run_time"].AsReal();
548  usd.start_time = (float)agent_map["start_time"].AsReal();
549  usd.client_version = agent_map["version"].AsString();
550 
551  UserSessionUtil.UpdateMultiItems(ref usd, agent_map["agents_in_view"].AsInteger(),
552  (float)agent_map["ping"].AsReal(),
553  (float)agent_map["sim_fps"].AsReal(),
554  (float)agent_map["fps"].AsReal());
555 
556  if (mmap["downloads"].Type != OSDType.Map)
557  return new UserSession();
558  OSDMap downloads_map = (OSDMap)mmap["downloads"];
559  usd.d_object_kb = (float)downloads_map["object_kbytes"].AsReal();
560  usd.d_texture_kb = (float)downloads_map["texture_kbytes"].AsReal();
561  usd.d_world_kb = (float)downloads_map["workd_kbytes"].AsReal();
562 
563 // m_log.DebugFormat("[WEB STATS MODULE]: mmap[\"session_id\"] = [{0}]", mmap["session_id"].AsUUID());
564 
565  usd.session_id = mmap["session_id"].AsUUID();
566 
567  if (mmap["system"].Type != OSDType.Map)
568  return new UserSession();
569  OSDMap system_map = (OSDMap)mmap["system"];
570 
571  usd.s_cpu = system_map["cpu"].AsString();
572  usd.s_gpu = system_map["gpu"].AsString();
573  usd.s_os = system_map["os"].AsString();
574  usd.s_ram = system_map["ram"].AsInteger();
575 
576  if (mmap["stats"].Type != OSDType.Map)
577  return new UserSession();
578 
579  OSDMap stats_map = (OSDMap)mmap["stats"];
580  {
581 
582  if (stats_map["failures"].Type != OSDType.Map)
583  return new UserSession();
584  OSDMap stats_failures = (OSDMap)stats_map["failures"];
585  usd.f_dropped = stats_failures["dropped"].AsInteger();
586  usd.f_failed_resends = stats_failures["failed_resends"].AsInteger();
587  usd.f_invalid = stats_failures["invalid"].AsInteger();
588  usd.f_resent = stats_failures["resent"].AsInteger();
589  usd.f_send_packet = stats_failures["send_packet"].AsInteger();
590 
591  if (stats_map["net"].Type != OSDType.Map)
592  return new UserSession();
593  OSDMap stats_net = (OSDMap)stats_map["net"];
594  {
595  if (stats_net["in"].Type != OSDType.Map)
596  return new UserSession();
597 
598  OSDMap net_in = (OSDMap)stats_net["in"];
599  usd.n_in_kb = (float)net_in["kbytes"].AsReal();
600  usd.n_in_pk = net_in["packets"].AsInteger();
601 
602  if (stats_net["out"].Type != OSDType.Map)
603  return new UserSession();
604  OSDMap net_out = (OSDMap)stats_net["out"];
605 
606  usd.n_out_kb = (float)net_out["kbytes"].AsReal();
607  usd.n_out_pk = net_out["packets"].AsInteger();
608  }
609  }
610  }
611 
612  uid.session_data = usd;
613  m_sessions[agentID] = uid;
614 
615 // m_log.DebugFormat(
616 // "[WEB STATS MODULE]: Parse data for {0} {1}, session {2}", uid.name_f, uid.name_l, uid.session_id);
617 
618  return uid;
619  }
620 
621  private void UpdateUserStats(UserSession uid, SqliteConnection db)
622  {
623 // m_log.DebugFormat(
624 // "[WEB STATS MODULE]: Updating user stats for {0} {1}, session {2}", uid.name_f, uid.name_l, uid.session_id);
625 
626  if (uid.session_id == UUID.Zero)
627  return;
628 
629  lock (db)
630  {
631  using (SqliteCommand updatecmd = new SqliteCommand(SQL_STATS_TABLE_INSERT, db))
632  {
633  updatecmd.Parameters.Add(new SqliteParameter(":session_id", uid.session_data.session_id.ToString()));
634  updatecmd.Parameters.Add(new SqliteParameter(":agent_id", uid.session_data.agent_id.ToString()));
635  updatecmd.Parameters.Add(new SqliteParameter(":region_id", uid.session_data.region_id.ToString()));
636  updatecmd.Parameters.Add(new SqliteParameter(":last_updated", (int) uid.session_data.last_updated));
637  updatecmd.Parameters.Add(new SqliteParameter(":remote_ip", uid.session_data.remote_ip));
638  updatecmd.Parameters.Add(new SqliteParameter(":name_f", uid.session_data.name_f));
639  updatecmd.Parameters.Add(new SqliteParameter(":name_l", uid.session_data.name_l));
640  updatecmd.Parameters.Add(new SqliteParameter(":avg_agents_in_view", uid.session_data.avg_agents_in_view));
641  updatecmd.Parameters.Add(new SqliteParameter(":min_agents_in_view",
642  (int) uid.session_data.min_agents_in_view));
643  updatecmd.Parameters.Add(new SqliteParameter(":max_agents_in_view",
644  (int) uid.session_data.max_agents_in_view));
645  updatecmd.Parameters.Add(new SqliteParameter(":mode_agents_in_view",
646  (int) uid.session_data.mode_agents_in_view));
647  updatecmd.Parameters.Add(new SqliteParameter(":avg_fps", uid.session_data.avg_fps));
648  updatecmd.Parameters.Add(new SqliteParameter(":min_fps", uid.session_data.min_fps));
649  updatecmd.Parameters.Add(new SqliteParameter(":max_fps", uid.session_data.max_fps));
650  updatecmd.Parameters.Add(new SqliteParameter(":mode_fps", uid.session_data.mode_fps));
651  updatecmd.Parameters.Add(new SqliteParameter(":a_language", uid.session_data.a_language));
652  updatecmd.Parameters.Add(new SqliteParameter(":mem_use", uid.session_data.mem_use));
653  updatecmd.Parameters.Add(new SqliteParameter(":meters_traveled", uid.session_data.meters_traveled));
654  updatecmd.Parameters.Add(new SqliteParameter(":avg_ping", uid.session_data.avg_ping));
655  updatecmd.Parameters.Add(new SqliteParameter(":min_ping", uid.session_data.min_ping));
656  updatecmd.Parameters.Add(new SqliteParameter(":max_ping", uid.session_data.max_ping));
657  updatecmd.Parameters.Add(new SqliteParameter(":mode_ping", uid.session_data.mode_ping));
658  updatecmd.Parameters.Add(new SqliteParameter(":regions_visited", uid.session_data.regions_visited));
659  updatecmd.Parameters.Add(new SqliteParameter(":run_time", uid.session_data.run_time));
660  updatecmd.Parameters.Add(new SqliteParameter(":avg_sim_fps", uid.session_data.avg_sim_fps));
661  updatecmd.Parameters.Add(new SqliteParameter(":min_sim_fps", uid.session_data.min_sim_fps));
662  updatecmd.Parameters.Add(new SqliteParameter(":max_sim_fps", uid.session_data.max_sim_fps));
663  updatecmd.Parameters.Add(new SqliteParameter(":mode_sim_fps", uid.session_data.mode_sim_fps));
664  updatecmd.Parameters.Add(new SqliteParameter(":start_time", uid.session_data.start_time));
665  updatecmd.Parameters.Add(new SqliteParameter(":client_version", uid.session_data.client_version));
666  updatecmd.Parameters.Add(new SqliteParameter(":s_cpu", uid.session_data.s_cpu));
667  updatecmd.Parameters.Add(new SqliteParameter(":s_gpu", uid.session_data.s_gpu));
668  updatecmd.Parameters.Add(new SqliteParameter(":s_os", uid.session_data.s_os));
669  updatecmd.Parameters.Add(new SqliteParameter(":s_ram", uid.session_data.s_ram));
670  updatecmd.Parameters.Add(new SqliteParameter(":d_object_kb", uid.session_data.d_object_kb));
671  updatecmd.Parameters.Add(new SqliteParameter(":d_texture_kb", uid.session_data.d_texture_kb));
672  updatecmd.Parameters.Add(new SqliteParameter(":d_world_kb", uid.session_data.d_world_kb));
673  updatecmd.Parameters.Add(new SqliteParameter(":n_in_kb", uid.session_data.n_in_kb));
674  updatecmd.Parameters.Add(new SqliteParameter(":n_in_pk", uid.session_data.n_in_pk));
675  updatecmd.Parameters.Add(new SqliteParameter(":n_out_kb", uid.session_data.n_out_kb));
676  updatecmd.Parameters.Add(new SqliteParameter(":n_out_pk", uid.session_data.n_out_pk));
677  updatecmd.Parameters.Add(new SqliteParameter(":f_dropped", uid.session_data.f_dropped));
678  updatecmd.Parameters.Add(new SqliteParameter(":f_failed_resends", uid.session_data.f_failed_resends));
679  updatecmd.Parameters.Add(new SqliteParameter(":f_invalid", uid.session_data.f_invalid));
680  updatecmd.Parameters.Add(new SqliteParameter(":f_off_circuit", uid.session_data.f_off_circuit));
681  updatecmd.Parameters.Add(new SqliteParameter(":f_resent", uid.session_data.f_resent));
682  updatecmd.Parameters.Add(new SqliteParameter(":f_send_packet", uid.session_data.f_send_packet));
683 
684 // StringBuilder parameters = new StringBuilder();
685 // SqliteParameterCollection spc = updatecmd.Parameters;
686 // foreach (SqliteParameter sp in spc)
687 // parameters.AppendFormat("{0}={1},", sp.ParameterName, sp.Value);
688 //
689 // m_log.DebugFormat("[WEB STATS MODULE]: Parameters {0}", parameters);
690 
691 // m_log.DebugFormat("[WEB STATS MODULE]: Database stats update for {0}", uid.session_data.agent_id);
692 
693  updatecmd.ExecuteNonQuery();
694  }
695  }
696  }
697 
698  #region SQL
699  private const string SQL_STATS_TABLE_CREATE = @"CREATE TABLE IF NOT EXISTS stats_session_data (
700  session_id VARCHAR(36) NOT NULL PRIMARY KEY,
701  agent_id VARCHAR(36) NOT NULL DEFAULT '',
702  region_id VARCHAR(36) NOT NULL DEFAULT '',
703  last_updated INT NOT NULL DEFAULT '0',
704  remote_ip VARCHAR(16) NOT NULL DEFAULT '',
705  name_f VARCHAR(50) NOT NULL DEFAULT '',
706  name_l VARCHAR(50) NOT NULL DEFAULT '',
707  avg_agents_in_view FLOAT NOT NULL DEFAULT '0',
708  min_agents_in_view INT NOT NULL DEFAULT '0',
709  max_agents_in_view INT NOT NULL DEFAULT '0',
710  mode_agents_in_view INT NOT NULL DEFAULT '0',
711  avg_fps FLOAT NOT NULL DEFAULT '0',
712  min_fps FLOAT NOT NULL DEFAULT '0',
713  max_fps FLOAT NOT NULL DEFAULT '0',
714  mode_fps FLOAT NOT NULL DEFAULT '0',
715  a_language VARCHAR(25) NOT NULL DEFAULT '',
716  mem_use FLOAT NOT NULL DEFAULT '0',
717  meters_traveled FLOAT NOT NULL DEFAULT '0',
718  avg_ping FLOAT NOT NULL DEFAULT '0',
719  min_ping FLOAT NOT NULL DEFAULT '0',
720  max_ping FLOAT NOT NULL DEFAULT '0',
721  mode_ping FLOAT NOT NULL DEFAULT '0',
722  regions_visited INT NOT NULL DEFAULT '0',
723  run_time FLOAT NOT NULL DEFAULT '0',
724  avg_sim_fps FLOAT NOT NULL DEFAULT '0',
725  min_sim_fps FLOAT NOT NULL DEFAULT '0',
726  max_sim_fps FLOAT NOT NULL DEFAULT '0',
727  mode_sim_fps FLOAT NOT NULL DEFAULT '0',
728  start_time FLOAT NOT NULL DEFAULT '0',
729  client_version VARCHAR(255) NOT NULL DEFAULT '',
730  s_cpu VARCHAR(255) NOT NULL DEFAULT '',
731  s_gpu VARCHAR(255) NOT NULL DEFAULT '',
732  s_os VARCHAR(2255) NOT NULL DEFAULT '',
733  s_ram INT NOT NULL DEFAULT '0',
734  d_object_kb FLOAT NOT NULL DEFAULT '0',
735  d_texture_kb FLOAT NOT NULL DEFAULT '0',
736  d_world_kb FLOAT NOT NULL DEFAULT '0',
737  n_in_kb FLOAT NOT NULL DEFAULT '0',
738  n_in_pk INT NOT NULL DEFAULT '0',
739  n_out_kb FLOAT NOT NULL DEFAULT '0',
740  n_out_pk INT NOT NULL DEFAULT '0',
741  f_dropped INT NOT NULL DEFAULT '0',
742  f_failed_resends INT NOT NULL DEFAULT '0',
743  f_invalid INT NOT NULL DEFAULT '0',
744  f_off_circuit INT NOT NULL DEFAULT '0',
745  f_resent INT NOT NULL DEFAULT '0',
746  f_send_packet INT NOT NULL DEFAULT '0'
747  );";
748 
749  private const string SQL_STATS_TABLE_INSERT = @"INSERT OR REPLACE INTO stats_session_data (
750 session_id, agent_id, region_id, last_updated, remote_ip, name_f, name_l, avg_agents_in_view, min_agents_in_view, max_agents_in_view,
751 mode_agents_in_view, avg_fps, min_fps, max_fps, mode_fps, a_language, mem_use, meters_traveled, avg_ping, min_ping, max_ping, mode_ping,
752 regions_visited, run_time, avg_sim_fps, min_sim_fps, max_sim_fps, mode_sim_fps, start_time, client_version, s_cpu, s_gpu, s_os, s_ram,
753 d_object_kb, d_texture_kb, d_world_kb, n_in_kb, n_in_pk, n_out_kb, n_out_pk, f_dropped, f_failed_resends, f_invalid, f_off_circuit,
754 f_resent, f_send_packet
755 )
756 VALUES
757 (
758 :session_id, :agent_id, :region_id, :last_updated, :remote_ip, :name_f, :name_l, :avg_agents_in_view, :min_agents_in_view, :max_agents_in_view,
759 :mode_agents_in_view, :avg_fps, :min_fps, :max_fps, :mode_fps, :a_language, :mem_use, :meters_traveled, :avg_ping, :min_ping, :max_ping, :mode_ping,
760 :regions_visited, :run_time, :avg_sim_fps, :min_sim_fps, :max_sim_fps, :mode_sim_fps, :start_time, :client_version, :s_cpu, :s_gpu, :s_os, :s_ram,
761 :d_object_kb, :d_texture_kb, :d_world_kb, :n_in_kb, :n_in_pk, :n_out_kb, :n_out_pk, :f_dropped, :f_failed_resends, :f_invalid, :f_off_circuit,
762 :f_resent, :f_send_packet
763 )
764 ";
765 
766  #endregion
767 
768  }
769 
770  public static class UserSessionUtil
771  {
772  public static UserSessionData newUserSessionData()
773  {
774  UserSessionData obj = ZeroSession(new UserSessionData());
775  return obj;
776  }
777 
778  public static void UpdateMultiItems(ref UserSessionData s, int agents_in_view, float ping, float sim_fps, float fps)
779  {
780  // don't insert zero values here or it'll skew the statistics.
781  if (agents_in_view == 0 && fps == 0 && sim_fps == 0 && ping == 0)
782  return;
783  s._agents_in_view.Add(agents_in_view);
784  s._fps.Add(fps);
785  s._sim_fps.Add(sim_fps);
786  s._ping.Add(ping);
787 
788  int[] __agents_in_view = s._agents_in_view.ToArray();
789 
790  s.avg_agents_in_view = ArrayAvg_i(__agents_in_view);
791  s.min_agents_in_view = ArrayMin_i(__agents_in_view);
792  s.max_agents_in_view = ArrayMax_i(__agents_in_view);
793  s.mode_agents_in_view = ArrayMode_i(__agents_in_view);
794 
795  float[] __fps = s._fps.ToArray();
796  s.avg_fps = ArrayAvg_f(__fps);
797  s.min_fps = ArrayMin_f(__fps);
798  s.max_fps = ArrayMax_f(__fps);
799  s.mode_fps = ArrayMode_f(__fps);
800 
801  float[] __sim_fps = s._sim_fps.ToArray();
802  s.avg_sim_fps = ArrayAvg_f(__sim_fps);
803  s.min_sim_fps = ArrayMin_f(__sim_fps);
804  s.max_sim_fps = ArrayMax_f(__sim_fps);
805  s.mode_sim_fps = ArrayMode_f(__sim_fps);
806 
807  float[] __ping = s._ping.ToArray();
808  s.avg_ping = ArrayAvg_f(__ping);
809  s.min_ping = ArrayMin_f(__ping);
810  s.max_ping = ArrayMax_f(__ping);
811  s.mode_ping = ArrayMode_f(__ping);
812  }
813 
814  #region Statistics
815 
816  public static int ArrayMin_i(int[] arr)
817  {
818  int cnt = arr.Length;
819  if (cnt == 0)
820  return 0;
821 
822  Array.Sort(arr);
823  return arr[0];
824  }
825 
826  public static int ArrayMax_i(int[] arr)
827  {
828  int cnt = arr.Length;
829  if (cnt == 0)
830  return 0;
831 
832  Array.Sort(arr);
833  return arr[cnt-1];
834  }
835 
836  public static float ArrayMin_f(float[] arr)
837  {
838  int cnt = arr.Length;
839  if (cnt == 0)
840  return 0;
841 
842  Array.Sort(arr);
843  return arr[0];
844  }
845 
846  public static float ArrayMax_f(float[] arr)
847  {
848  int cnt = arr.Length;
849  if (cnt == 0)
850  return 0;
851 
852  Array.Sort(arr);
853  return arr[cnt - 1];
854  }
855 
856  public static float ArrayAvg_i(int[] arr)
857  {
858  int cnt = arr.Length;
859 
860  if (cnt == 0)
861  return 0;
862 
863  float result = arr[0];
864 
865  for (int i = 1; i < cnt; i++)
866  result += arr[i];
867 
868  return result / cnt;
869  }
870 
871  public static float ArrayAvg_f(float[] arr)
872  {
873  int cnt = arr.Length;
874 
875  if (cnt == 0)
876  return 0;
877 
878  float result = arr[0];
879 
880  for (int i = 1; i < cnt; i++)
881  result += arr[i];
882 
883  return result / cnt;
884  }
885 
886  public static float ArrayMode_f(float[] arr)
887  {
888  List<float> mode = new List<float>();
889 
890  float[] srtArr = new float[arr.Length];
891  float[,] freq = new float[arr.Length, 2];
892  Array.Copy(arr, srtArr, arr.Length);
893  Array.Sort(srtArr);
894 
895  float tmp = srtArr[0];
896  int index = 0;
897  int i = 0;
898  while (i < srtArr.Length)
899  {
900  freq[index, 0] = tmp;
901 
902  while (tmp == srtArr[i])
903  {
904  freq[index, 1]++;
905  i++;
906 
907  if (i > srtArr.Length - 1)
908  break;
909  }
910 
911  if (i < srtArr.Length)
912  {
913  tmp = srtArr[i];
914  index++;
915  }
916 
917  }
918 
919  Array.Clear(srtArr, 0, srtArr.Length);
920 
921  for (i = 0; i < srtArr.Length; i++)
922  srtArr[i] = freq[i, 1];
923 
924  Array.Sort(srtArr);
925 
926  if ((srtArr[srtArr.Length - 1]) == 0 || (srtArr[srtArr.Length - 1]) == 1)
927  return 0;
928 
929  float freqtest = (float)freq.Length / freq.Rank;
930 
931  for (i = 0; i < freqtest; i++)
932  {
933  if (freq[i, 1] == srtArr[index])
934  mode.Add(freq[i, 0]);
935 
936  }
937 
938  return mode.ToArray()[0];
939  }
940 
941  public static int ArrayMode_i(int[] arr)
942  {
943  List<int> mode = new List<int>();
944 
945  int[] srtArr = new int[arr.Length];
946  int[,] freq = new int[arr.Length, 2];
947  Array.Copy(arr, srtArr, arr.Length);
948  Array.Sort(srtArr);
949 
950  int tmp = srtArr[0];
951  int index = 0;
952  int i = 0;
953  while (i < srtArr.Length)
954  {
955  freq[index, 0] = tmp;
956 
957  while (tmp == srtArr[i])
958  {
959  freq[index, 1]++;
960  i++;
961 
962  if (i > srtArr.Length - 1)
963  break;
964  }
965 
966  if (i < srtArr.Length)
967  {
968  tmp = srtArr[i];
969  index++;
970  }
971 
972  }
973 
974  Array.Clear(srtArr, 0, srtArr.Length);
975 
976  for (i = 0; i < srtArr.Length; i++)
977  srtArr[i] = freq[i, 1];
978 
979  Array.Sort(srtArr);
980 
981  if ((srtArr[srtArr.Length - 1]) == 0 || (srtArr[srtArr.Length - 1]) == 1)
982  return 0;
983 
984  float freqtest = (float)freq.Length / freq.Rank;
985 
986  for (i = 0; i < freqtest; i++)
987  {
988  if (freq[i, 1] == srtArr[index])
989  mode.Add(freq[i, 0]);
990 
991  }
992 
993  return mode.ToArray()[0];
994  }
995 
996  #endregion
997 
998  private static UserSessionData ZeroSession(UserSessionData s)
999  {
1000  s.session_id = UUID.Zero;
1001  s.agent_id = UUID.Zero;
1002  s.region_id = UUID.Zero;
1003  s.last_updated = Util.UnixTimeSinceEpoch();
1004  s.remote_ip = "";
1005  s.name_f = "";
1006  s.name_l = "";
1007  s.avg_agents_in_view = 0;
1008  s.min_agents_in_view = 0;
1009  s.max_agents_in_view = 0;
1010  s.mode_agents_in_view = 0;
1011  s.avg_fps = 0;
1012  s.min_fps = 0;
1013  s.max_fps = 0;
1014  s.mode_fps = 0;
1015  s.a_language = "";
1016  s.mem_use = 0;
1017  s.meters_traveled = 0;
1018  s.avg_ping = 0;
1019  s.min_ping = 0;
1020  s.max_ping = 0;
1021  s.mode_ping = 0;
1022  s.regions_visited = 0;
1023  s.run_time = 0;
1024  s.avg_sim_fps = 0;
1025  s.min_sim_fps = 0;
1026  s.max_sim_fps = 0;
1027  s.mode_sim_fps = 0;
1028  s.start_time = 0;
1029  s.client_version = "";
1030  s.s_cpu = "";
1031  s.s_gpu = "";
1032  s.s_os = "";
1033  s.s_ram = 0;
1034  s.d_object_kb = 0;
1035  s.d_texture_kb = 0;
1036  s.d_world_kb = 0;
1037  s.n_in_kb = 0;
1038  s.n_in_pk = 0;
1039  s.n_out_kb = 0;
1040  s.n_out_pk = 0;
1041  s.f_dropped = 0;
1042  s.f_failed_resends = 0;
1043  s.f_invalid = 0;
1044  s.f_off_circuit = 0;
1045  s.f_resent = 0;
1046  s.f_send_packet = 0;
1047  s._ping = new List<float>();
1048  s._fps = new List<float>();
1049  s._sim_fps = new List<float>();
1050  s._agents_in_view = new List<int>();
1051  return s;
1052  }
1053  }
1054  #region structs
1055 
1056  public class UserSession
1057  {
1059  public UUID region_id;
1060  public string name_f;
1061  public string name_l;
1063  }
1064 
1065  public struct UserSessionData
1066  {
1068  public UUID agent_id;
1069  public UUID region_id;
1070  public float last_updated;
1071  public string remote_ip;
1072  public string name_f;
1073  public string name_l;
1074  public float avg_agents_in_view;
1075  public float min_agents_in_view;
1076  public float max_agents_in_view;
1077  public float mode_agents_in_view;
1078  public float avg_fps;
1079  public float min_fps;
1080  public float max_fps;
1081  public float mode_fps;
1082  public string a_language;
1083  public float mem_use;
1084  public float meters_traveled;
1085  public float avg_ping;
1086  public float min_ping;
1087  public float max_ping;
1088  public float mode_ping;
1089  public int regions_visited;
1090  public float run_time;
1091  public float avg_sim_fps;
1092  public float min_sim_fps;
1093  public float max_sim_fps;
1094  public float mode_sim_fps;
1095  public float start_time;
1096  public string client_version;
1097  public string s_cpu;
1098  public string s_gpu;
1099  public string s_os;
1100  public int s_ram;
1101  public float d_object_kb;
1102  public float d_texture_kb;
1103  public float d_world_kb;
1104  public float n_in_kb;
1105  public int n_in_pk;
1106  public float n_out_kb;
1107  public int n_out_pk;
1108  public int f_dropped;
1109  public int f_failed_resends;
1110  public int f_invalid;
1111  public int f_off_circuit;
1112  public int f_resent;
1113  public int f_send_packet;
1114  public List<float> _ping;
1115  public List<float> _fps;
1116  public List<float> _sim_fps;
1117  public List<int> _agents_in_view;
1118  }
1119 
1120  #endregion
1121 
1122  public class USimStatsData
1123  {
1124  private UUID m_regionID = UUID.Zero;
1125  private volatile int m_statcounter = 0;
1126  private volatile float m_timeDilation;
1127  private volatile float m_simFps;
1128  private volatile float m_physicsFps;
1129  private volatile float m_agentUpdates;
1130  private volatile float m_rootAgents;
1131  private volatile float m_childAgents;
1132  private volatile float m_totalPrims;
1133  private volatile float m_activePrims;
1134  private volatile float m_totalFrameTime;
1135  private volatile float m_netFrameTime;
1136  private volatile float m_physicsFrameTime;
1137  private volatile float m_otherFrameTime;
1138  private volatile float m_imageFrameTime;
1139  private volatile float m_inPacketsPerSecond;
1140  private volatile float m_outPacketsPerSecond;
1141  private volatile float m_unackedBytes;
1142  private volatile float m_agentFrameTime;
1143  private volatile float m_pendingDownloads;
1144  private volatile float m_pendingUploads;
1145  private volatile float m_activeScripts;
1146  private volatile float m_scriptLinesPerSecond;
1147 
1148  public UUID RegionId { get { return m_regionID; } }
1149  public int StatsCounter { get { return m_statcounter; } set { m_statcounter = value;}}
1150  public float TimeDilation { get { return m_timeDilation; } }
1151  public float SimFps { get { return m_simFps; } }
1152  public float PhysicsFps { get { return m_physicsFps; } }
1153  public float AgentUpdates { get { return m_agentUpdates; } }
1154  public float RootAgents { get { return m_rootAgents; } }
1155  public float ChildAgents { get { return m_childAgents; } }
1156  public float TotalPrims { get { return m_totalPrims; } }
1157  public float ActivePrims { get { return m_activePrims; } }
1158  public float TotalFrameTime { get { return m_totalFrameTime; } }
1159  public float NetFrameTime { get { return m_netFrameTime; } }
1160  public float PhysicsFrameTime { get { return m_physicsFrameTime; } }
1161  public float OtherFrameTime { get { return m_otherFrameTime; } }
1162  public float ImageFrameTime { get { return m_imageFrameTime; } }
1163  public float InPacketsPerSecond { get { return m_inPacketsPerSecond; } }
1164  public float OutPacketsPerSecond { get { return m_outPacketsPerSecond; } }
1165  public float UnackedBytes { get { return m_unackedBytes; } }
1166  public float AgentFrameTime { get { return m_agentFrameTime; } }
1167  public float PendingDownloads { get { return m_pendingDownloads; } }
1168  public float PendingUploads { get { return m_pendingUploads; } }
1169  public float ActiveScripts { get { return m_activeScripts; } }
1170  public float ScriptLinesPerSecond { get { return m_scriptLinesPerSecond; } }
1171 
1172  public USimStatsData(UUID pRegionID)
1173  {
1174  m_regionID = pRegionID;
1175  }
1176 
1177  public void ConsumeSimStats(SimStats stats)
1178  {
1179  m_regionID = stats.RegionUUID;
1180  m_timeDilation = stats.StatsBlock[0].StatValue;
1181  m_simFps = stats.StatsBlock[1].StatValue;
1182  m_physicsFps = stats.StatsBlock[2].StatValue;
1183  m_agentUpdates = stats.StatsBlock[3].StatValue;
1184  m_rootAgents = stats.StatsBlock[4].StatValue;
1185  m_childAgents = stats.StatsBlock[5].StatValue;
1186  m_totalPrims = stats.StatsBlock[6].StatValue;
1187  m_activePrims = stats.StatsBlock[7].StatValue;
1188  m_totalFrameTime = stats.StatsBlock[8].StatValue;
1189  m_netFrameTime = stats.StatsBlock[9].StatValue;
1190  m_physicsFrameTime = stats.StatsBlock[10].StatValue;
1191  m_otherFrameTime = stats.StatsBlock[11].StatValue;
1192  m_imageFrameTime = stats.StatsBlock[12].StatValue;
1193  m_inPacketsPerSecond = stats.StatsBlock[13].StatValue;
1194  m_outPacketsPerSecond = stats.StatsBlock[14].StatValue;
1195  m_unackedBytes = stats.StatsBlock[15].StatValue;
1196  m_agentFrameTime = stats.StatsBlock[16].StatValue;
1197  m_pendingDownloads = stats.StatsBlock[17].StatValue;
1198  m_pendingUploads = stats.StatsBlock[18].StatValue;
1199  m_activeScripts = stats.StatsBlock[19].StatValue;
1200  m_scriptLinesPerSecond = stats.ExtraStatsBlock[0].StatValue;
1201  }
1202  }
1203 }
virtual void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
OpenSim.Framework.Capabilities.Caps Caps
OpenMetaverse.StructuredData.OSDMap OSDMap
virtual void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
OpenSim.Framework.Capabilities.Caps Caps
OpenMetaverse.StructuredData.OSDMap OSDMap
Enapsulate statistics for a simulator/scene.
Definition: SimStats.cs:39
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
OpenMetaverse.StructuredData.OSD OSD
OpenMetaverse.StructuredData.OSD OSD
virtual void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
virtual void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...