opensim-development – Blame information for rev 1

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