2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 // Copyright (c) 2005-2008 Dévai Tamás ( gonosztopi@amule.org )
8 // Any parts of this program derived from the xMule, lMule or eMule project,
9 // or contributed by third-party developers are copyrighted by their
10 // respective authors.
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "Statistics.h" // Interface declarations
29 #include <protocol/ed2k/ClientSoftware.h>
31 #include <ec/cpp/ECTag.h> // Needed for CECTag
35 #include <common/Format.h> // Needed for CFormat
37 #include "DataToText.h" // Needed for GetSoftName()
38 #include "Preferences.h" // Needed for thePrefs
39 #include "ListenSocket.h" // (tree, GetAverageConnections)
40 #include "ServerList.h" // Needed for CServerList (tree)
41 #include <cmath> // Needed for std::floor
42 #include "updownclient.h" // Needed for CUpDownClient
44 #include "GetTickCount.h" // Needed for GetTickCount64()
45 #include "Preferences.h"
46 #include <ec/cpp/RemoteConnect.h> // Needed for CRemoteConnect
48 #include "amule.h" // Needed for theApp
56 #define round(x) floor(x+0.5)
62 /*----- CPreciseRateCounter -----*/
64 void CPreciseRateCounter::CalculateRate(uint64_t now
)
66 wxMutexLocker
lock(m_mutex
);
69 m_byte_history
.push_back(m_tmp_sum
);
70 m_tick_history
.push_back(now
);
73 uint64_t timespan
= now
- m_tick_history
.front();
75 // Checking maximal timespan, but make sure not to remove
76 // the extra node in m_tick_history.
77 while (timespan
> m_timespan
&& m_byte_history
.size() > 0) {
78 m_total
-= m_byte_history
.front();
79 m_byte_history
.pop_front();
80 m_tick_history
.pop_front();
81 timespan
= now
- m_tick_history
.front();
85 if (m_count_average
) {
86 if (m_byte_history
.size() > 0) {
87 m_rate
= m_total
/ (double)m_byte_history
.size();
91 m_rate
= m_total
/ (timespan
/ 1000.0);
95 if (m_rate
> m_max_rate
) {
101 /*----- CStatTreeItemRateCounter -----*/
104 wxString
CStatTreeItemRateCounter::GetDisplayString() const
106 return CFormat(wxGetTranslation(m_label
)) % CastItoSpeed(m_show_maxrate
? (uint32
)m_max_rate
: (uint32
)m_rate
);
110 void CStatTreeItemRateCounter::AddECValues(CECTag
* tag
) const
112 CECTag
value(EC_TAG_STAT_NODE_VALUE
, m_show_maxrate
? (uint32
)m_max_rate
: (uint32
)m_rate
);
113 value
.AddTag(CECTag(EC_TAG_STAT_VALUE_TYPE
, (uint8
)EC_VALUE_SPEED
));
118 /*----- CStatTreeItemPeakConnections -----*/
121 wxString
CStatTreeItemPeakConnections::GetDisplayString() const
123 return CFormat(wxGetTranslation(m_label
)) % theStats::GetPeakConnections();
127 void CStatTreeItemPeakConnections::AddECValues(CECTag
* tag
) const
129 tag
->AddTag(CECTag(EC_TAG_STAT_NODE_VALUE
, (uint64
)theStats::GetPeakConnections()));
133 /*----- CStatistics -----*/
138 CPreciseRateCounter
* CStatistics::s_upOverheadRate
;
139 CPreciseRateCounter
* CStatistics::s_downOverheadRate
;
140 CStatTreeItemRateCounter
* CStatistics::s_uploadrate
;
141 CStatTreeItemRateCounter
* CStatistics::s_downloadrate
;
143 #else /* CLIENT_GUI */
145 uint64
CStatistics::s_start_time
;
146 uint64
CStatistics::s_statData
[sdTotalItems
];
148 #endif /* !CLIENT_GUI / CLIENT_GUI */
151 CStatTreeItemBase
* CStatistics::s_statTree
;
155 CStatTreeItemTimer
* CStatistics::s_uptime
;
158 CStatTreeItemUlDlCounter
* CStatistics::s_sessionUpload
;
159 CStatTreeItemPacketTotals
* CStatistics::s_totalUpOverhead
;
160 CStatTreeItemPackets
* CStatistics::s_fileReqUpOverhead
;
161 CStatTreeItemPackets
* CStatistics::s_sourceXchgUpOverhead
;
162 CStatTreeItemPackets
* CStatistics::s_serverUpOverhead
;
163 CStatTreeItemPackets
* CStatistics::s_kadUpOverhead
;
164 CStatTreeItemCounter
* CStatistics::s_cryptUpOverhead
;
165 CStatTreeItemNativeCounter
* CStatistics::s_activeUploads
;
166 CStatTreeItemNativeCounter
* CStatistics::s_waitingUploads
;
167 CStatTreeItemCounter
* CStatistics::s_totalSuccUploads
;
168 CStatTreeItemCounter
* CStatistics::s_totalFailedUploads
;
169 CStatTreeItemCounter
* CStatistics::s_totalUploadTime
;
172 CStatTreeItemUlDlCounter
* CStatistics::s_sessionDownload
;
173 CStatTreeItemPacketTotals
* CStatistics::s_totalDownOverhead
;
174 CStatTreeItemPackets
* CStatistics::s_fileReqDownOverhead
;
175 CStatTreeItemPackets
* CStatistics::s_sourceXchgDownOverhead
;
176 CStatTreeItemPackets
* CStatistics::s_serverDownOverhead
;
177 CStatTreeItemPackets
* CStatistics::s_kadDownOverhead
;
178 CStatTreeItemCounter
* CStatistics::s_cryptDownOverhead
;
179 CStatTreeItemNativeCounter
* CStatistics::s_foundSources
;
180 CStatTreeItemNativeCounter
* CStatistics::s_activeDownloads
;
183 CStatTreeItemReconnects
* CStatistics::s_reconnects
;
184 CStatTreeItemTimer
* CStatistics::s_sinceFirstTransfer
;
185 CStatTreeItemTimer
* CStatistics::s_sinceConnected
;
186 CStatTreeItemCounterMax
* CStatistics::s_activeConnections
;
187 CStatTreeItemMaxConnLimitReached
* CStatistics::s_limitReached
;
188 CStatTreeItemSimple
* CStatistics::s_avgConnections
;
191 CStatTreeItemHiddenCounter
* CStatistics::s_clients
;
192 CStatTreeItemCounter
* CStatistics::s_unknown
;
193 //CStatTreeItem CStatistics::s_lowID;
194 //CStatTreeItem CStatistics::s_secIdentOnOff;
196 CStatTreeItemNativeCounter
* CStatistics::s_hasSocket
;
198 CStatTreeItemNativeCounter
* CStatistics::s_filtered
;
199 CStatTreeItemNativeCounter
* CStatistics::s_banned
;
202 CStatTreeItemSimple
* CStatistics::s_workingServers
;
203 CStatTreeItemSimple
* CStatistics::s_failedServers
;
204 CStatTreeItemNativeCounter
* CStatistics::s_totalServers
;
205 CStatTreeItemNativeCounter
* CStatistics::s_deletedServers
;
206 CStatTreeItemNativeCounter
* CStatistics::s_filteredServers
;
207 CStatTreeItemSimple
* CStatistics::s_usersOnWorking
;
208 CStatTreeItemSimple
* CStatistics::s_filesOnWorking
;
209 CStatTreeItemSimple
* CStatistics::s_totalUsers
;
210 CStatTreeItemSimple
* CStatistics::s_totalFiles
;
211 CStatTreeItemSimple
* CStatistics::s_serverOccupation
;
214 CStatTreeItemCounter
* CStatistics::s_numberOfShared
;
215 CStatTreeItemCounter
* CStatistics::s_sizeOfShare
;
218 uint64
CStatistics::s_kadNodesTotal
;
219 uint16
CStatistics::s_kadNodesCur
;
222 CStatistics::CStatistics()
223 : m_graphRunningAvgDown(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true),
224 m_graphRunningAvgUp(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true),
225 m_graphRunningAvgKad(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true)
227 uint64 start_time
= GetTickCount64();
231 average_minutes
= thePrefs::GetStatsAverageMinutes();
233 HR hr
= {0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0};
235 nHistRanges
= 7; // =ceil(log(max_update_delay)/log(2))
236 nPointsPerRange
= GetPointsPerRange();
237 bitsHistClockMask
= (1 << (nHistRanges
-1)) - 1;
238 aposRecycle
= new listPOS
[nHistRanges
];
239 listPOS
*ppos
= aposRecycle
+nHistRanges
-1;
240 for (int i
=nHistRanges
; i
>0; --i
) { // permanently allocated history list
241 listHR
.push_back(hr
);
242 *ppos
-- = --listHR
.end();
243 for (int j
=nPointsPerRange
; j
>1; --j
)
244 listHR
.push_back(hr
);
247 // Init rate counters outside the tree
249 s_upOverheadRate
= new CPreciseRateCounter(5000);
250 s_downOverheadRate
= new CPreciseRateCounter(5000);
255 s_uptime
->SetStartTime(start_time
);
259 CStatistics::~CStatistics()
261 // clearing listHR frees the memory occupied by the nodes
263 delete [] aposRecycle
;
267 // delete items not in the tree
268 delete s_totalUploadTime
;
270 // delete rate counters outside the tree
271 delete s_upOverheadRate
;
272 delete s_downOverheadRate
;
276 void CStatistics::CalculateRates()
278 uint64_t now
= GetTickCount64();
279 s_downOverheadRate
->CalculateRate(now
);
280 s_upOverheadRate
->CalculateRate(now
);
281 s_downloadrate
->CalculateRate(now
);
282 s_uploadrate
->CalculateRate(now
);
286 /* ------------------------------- GRAPHS ---------------------------- */
291 The basic idea here is that we want to keep as much history as we can without paying
292 a high price in terms of memory space. Because we keep the history for display purposes,
293 we can take advantage of the fact that when the period shown in the graphs is long
294 then each pixel represents a long period. So as the most recent history we keep one
295 window full of points at a resolution of 1 second, the next window full at 2 seconds,
296 the next at 4 seconds and so on, up to the maximum desired. This way there is always
297 at least one sample point per pixel for any update delay set by the user, and the
298 memory required grows with the *log* of the total time period covered.
299 The history is kept in a doubly-linked list, with the most recent snapshot at the tail.
300 The number of nodes in the list is fixed, and there are no calls to RemoveHead() and
301 AddTail() which would add overhead and contribute to memory fragmentation. Instead,
302 every second when a new point gets recorded, one of the existing nodes is recycled;
303 it is disjoined from its present place, put at the tail of the list, and then gets
304 filled with new data. [Emilio Sandoz]
305 This unfortunately does not work with stl classes, as none of them supports moving
306 a node to another place, so we have to erase and re-add nodes.
309 void CStatistics::RecordHistory()
310 { // First we query and compute some values, then we store them in the history list
312 // A few comments about the use of double and float in computations:
313 // Even on a hi-res screen our graphs will have 10 bits of resolution at most,
314 // so the 24 bits resolution of a float on 32 bit Intel processors is more than
315 // enough for all displayed values. Rate computations however, and especially
316 // running average computations, use differences (delta bytes/ delta time), and
317 // for long uptimes the difference between two timestamps can lose too much
318 // accuracy because the large mantissa causes less significant bits to be dropped
319 // (same for the difference between two cumulative byte counts). [We don't store
320 // these values as integers because they will be used in floating point calculations,
321 // and we want to perform the conversion only once). Therefore timestamps and
322 // Kbyte counts are stored in the history as doubles, while computed values use
323 // float (to save space and execution time).
326 Store values; first determine the node to be recycled (using the bits in iClock)
328 oldest records ----------------- listHR ------------------ youngest records
330 O-[Range 2^n sec]-O- ... -O-[Range 4 sec]-O-[Range 2 sec]-O-[Range 1 sec]-O
331 | | | | > every 2 secs -^
332 | | ... | >--------------- every 4 secs -^
333 | | >------------------------ recycle every 8 secs -^
335 | >-the node at this position is recycled every 2^n secs -^
336 >-------------------(ditto for the oldest node at the head of the list) --^
338 aposRecycle[nHistRanges-1] ... aposRecycle[0] Tail
342 int iClockPrev
= iClock
++;
343 int bits
= (iClockPrev
^iClock
) & iClock
; // identify the highest changed bit
344 if (bits
<= bitsHistClockMask
) {
346 while ((bits
/= 2) != 0) // count to the highest bit that was just toggled to 1
348 // recycle one node and jump over the next to move it to the next higher range
349 listHR
.push_back(**ppos
);
350 *ppos
= ++listHR
.erase(*ppos
);
352 ppos
= aposRecycle
+nHistRanges
-1;
353 // recycle the node at the head; there is no higher range to move nodes into
354 listHR
.push_back(**ppos
);
355 *ppos
= listHR
.erase(*ppos
);
358 // now save the latest data point in this node
359 listPOS phr
= --listHR
.end();
360 phr
->kBytesSent
= GetSessionSentBytes() / 1024.0;
361 phr
->kBytesReceived
= GetSessionReceivedBytes() / 1024.0;
362 phr
->kBpsUpCur
= GetUploadRate() / 1024.0;
363 phr
->kBpsDownCur
= GetDownloadRate() / 1024.0;
364 phr
->cntUploads
= GetActiveUploadsCount();
365 phr
->cntConnections
= GetActiveConnections();
366 phr
->cntDownloads
= GetDownloadingSources();
367 phr
->sTimestamp
= GetUptimeMillis() / 1000.0;
369 s_kadNodesTotal
+= s_kadNodesCur
;
370 phr
->kadNodesTotal
= s_kadNodesTotal
;
371 phr
->kadNodesCur
= s_kadNodesCur
;
375 unsigned CStatistics::GetHistory( // Assemble arrays of sample points for a graph
376 unsigned cntPoints
, // number of sample points to assemble
377 double sStep
, // time difference between sample points
378 double sFinal
, // latest allowed timestamp
379 const std::vector
<float *> &ppf
,// an array of pointers to arrays of floats for the result
380 StatsGraphType which_graph
) // the graph which will receive the points
382 if (sStep
==0.0 || cntPoints
==0) {
389 unsigned cntFilled
= 0;
390 listRPOS pos
= listHR
.rbegin();
392 // start of list should be an integer multiple of the sampling period for samples
393 // to be consistent when the graphs are resized horizontally
398 sTarget
= sStep
==1.0 ?
400 std::floor(pos
->sTimestamp
/sStep
) * sStep
;
403 HR
**ahr
= NULL
, **pphr
= NULL
;
404 bool bRateGraph
= (which_graph
!= GRAPH_CONN
); // rate graph or connections graph?
406 ahr
= new HR
* [cntPoints
];
410 while (pos
!= listHR
.rend()) {
411 if (pos
->sTimestamp
> sTarget
) {
415 if (bRateGraph
) { // assemble an array of pointers for ComputeAverages
417 } else { // or build the arrays if possible
418 *pf1
++ = (float)pos
->cntUploads
;
419 *pf2
++ = (float)pos
->cntConnections
;
420 *pf3
++ = (float)pos
->cntDownloads
;
422 if (++cntFilled
== cntPoints
) { // enough points
425 if (pos
->sTimestamp
== 0.0) { // reached beginning of uptime
428 if ((sTarget
-= sStep
) <= 0.0) { // don't overshoot the beginning
432 *pf1
++ = *pf2
++ = *pf3
++ = 0.0;
441 ComputeAverages(pphr
, pos
, cntFilled
, sStep
, ppf
, which_graph
);
450 unsigned CStatistics::GetHistoryForWeb( // Assemble arrays of sample points for the webserver
451 unsigned cntPoints
, // maximum number of sample points to assemble
452 double sStep
, // time difference between sample points
453 double *sStart
, // earliest allowed timestamp
454 uint32
**graphData
) // a pointer to a pointer that will point to the graph data array
459 if (sStep
==0.0 || cntPoints
==0)
461 unsigned cntFilled
= 0;
462 listRPOS pos
= listHR
.rbegin();
463 double LastTimeStamp
= pos
->sTimestamp
;
464 double sTarget
= LastTimeStamp
;
466 HR
**pphr
= new HR
*[cntPoints
];
468 while (pos
!= listHR
.rend()) {
469 if (pos
->sTimestamp
> sTarget
) {
470 ++pos
; // find next history record
473 pphr
[cntFilled
] = &(*pos
);
474 if (++cntFilled
== cntPoints
) // enough points
476 if (pos
->sTimestamp
<= *sStart
) // reached beginning of requested time
478 if ((sTarget
-= sStep
) <= 0.0) { // don't overshoot the beginning
479 pphr
[cntFilled
++] = NULL
;
485 *graphData
= new uint32
[4 * cntFilled
];
487 for (unsigned int i
= 0; i
< cntFilled
; i
++) {
488 HR
*phr
= pphr
[cntFilled
- i
- 1];
490 (*graphData
)[4 * i
] = ENDIAN_HTONL((uint32
)(phr
->kBpsDownCur
* 1024.0));
491 (*graphData
)[4 * i
+ 1] = ENDIAN_HTONL((uint32
)(phr
->kBpsUpCur
* 1024.0));
492 (*graphData
)[4 * i
+ 2] = ENDIAN_HTONL((uint32
)phr
->cntConnections
);
493 (*graphData
)[4 * i
+ 3] = ENDIAN_HTONL((uint32
)phr
->kadNodesCur
);
495 (*graphData
)[4 * i
] = (*graphData
)[4 * i
+ 1] = 0;
496 (*graphData
)[4 * i
+ 2] = (*graphData
)[4 * i
+ 3] = 0;
506 *sStart
= LastTimeStamp
;
512 void CStatistics::ComputeAverages(
513 HR
**pphr
, // pointer to (end of) array of assembled history records
514 listRPOS pos
, // position in history list from which to backtrack
515 unsigned cntFilled
, // number of points in the sample data
516 double sStep
, // time difference between two samples
517 const std::vector
<float *> &ppf
,// an array of pointers to arrays of floats with sample data
518 StatsGraphType which_graph
) // the graph which will receive the points
520 double sTarget
, kValueRun
;
521 uint64 avgTime
= average_minutes
* 60;
522 unsigned nBtPoints
= (unsigned)(avgTime
/ sStep
);
524 CPreciseRateCounter
* runningAvg
= NULL
;
525 switch (which_graph
) {
526 case GRAPH_DOWN
: runningAvg
= &m_graphRunningAvgDown
; break;
527 case GRAPH_UP
: runningAvg
= &m_graphRunningAvgUp
; break;
528 case GRAPH_KAD
: runningAvg
= &m_graphRunningAvgKad
; break;
530 wxCHECK_RET(false, wxT("ComputeAverages called with unsupported graph type."));
533 runningAvg
->m_timespan
= avgTime
* 1000;
534 runningAvg
->m_tick_history
.clear();
535 runningAvg
->m_byte_history
.clear();
536 runningAvg
->m_total
= 0;
537 runningAvg
->m_tmp_sum
= 0;
539 if (pos
== listHR
.rend()) {
542 sTarget
= std::max(0.0, pos
->sTimestamp
- sStep
);
545 while (nBtPoints
--) {
546 while (pos
!= listHR
.rend() && pos
->sTimestamp
> sTarget
) ++pos
; // find next history record
547 if (pos
!= listHR
.rend()) {
548 runningAvg
->m_tick_history
.push_front((uint64
)(pos
->sTimestamp
* 1000.0));
551 switch (which_graph
) {
553 value
= (uint32
)(pos
->kBpsDownCur
* 1024.0);
556 value
= (uint32
)(pos
->kBpsUpCur
* 1024.0);
559 value
= (uint32
)(pos
->kadNodesCur
* 1024.0);
562 wxCHECK_RET(false, wxT("ComputeAverages called with unsupported graph type."));
565 runningAvg
->m_byte_history
.push_front(value
);
566 runningAvg
->m_total
+= value
;
570 if ((sTarget
-= sStep
) < 0.0) {
575 // now compute averages in returned arrays, starting with the earliest values
576 float *pf1
= ppf
[0] + cntFilled
- 1; // holds session avg
577 float *pf2
= ppf
[1] + cntFilled
- 1; // holds running avg
578 float *pf3
= ppf
[2] + cntFilled
- 1; // holds current rate
580 for (int cnt
=cntFilled
; cnt
>0; cnt
--, pf1
--, pf2
--, pf3
--) {
582 if (which_graph
== GRAPH_DOWN
) {
583 kValueRun
= phr
->kBytesReceived
;
584 *pf3
= phr
->kBpsDownCur
;
585 } else if (which_graph
== GRAPH_UP
) {
586 kValueRun
= phr
->kBytesSent
;
587 *pf3
= phr
->kBpsUpCur
;
589 kValueRun
= phr
->kadNodesTotal
;
590 *pf3
= phr
->kadNodesCur
;
593 *pf1
= kValueRun
/ phr
->sTimestamp
;
594 (*runningAvg
) += (uint32
)(*pf3
* 1024.0);
595 runningAvg
->CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
596 *pf2
= (float)(runningAvg
->GetRate() / 1024.0);
601 GraphUpdateInfo
CStatistics::GetPointsForUpdate()
603 GraphUpdateInfo update
;
604 listPOS phr
= --listHR
.end();
605 update
.timestamp
= (double) phr
->sTimestamp
;
607 m_graphRunningAvgDown
+= (uint32
)(phr
->kBpsDownCur
* 1024.0);
608 m_graphRunningAvgUp
+= (uint32
)(phr
->kBpsUpCur
* 1024.0);
609 // Note: kadNodesCur is multiplied by 1024 since the value is done
610 // in other places, so we simply follow suit here to avoid trouble.
611 m_graphRunningAvgKad
+= (uint32
)(phr
->kadNodesCur
* 1024.0);
612 m_graphRunningAvgDown
.CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
613 m_graphRunningAvgUp
.CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
614 m_graphRunningAvgKad
.CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
616 update
.downloads
[0] = phr
->kBytesReceived
/ phr
->sTimestamp
;
617 update
.downloads
[1] = m_graphRunningAvgDown
.GetRate() / 1024.0;
618 update
.downloads
[2] = phr
->kBpsDownCur
;
620 update
.uploads
[0] = phr
->kBytesSent
/ phr
->sTimestamp
;
621 update
.uploads
[1] = m_graphRunningAvgUp
.GetRate() / 1024.0;
622 update
.uploads
[2] = phr
->kBpsUpCur
;
624 update
.connections
[0] = (float)phr
->cntUploads
;
625 update
.connections
[1] = (float)phr
->cntConnections
;
626 update
.connections
[2] = (float)phr
->cntDownloads
;
628 update
.kadnodes
[0] = phr
->kadNodesTotal
/ phr
->sTimestamp
;
629 update
.kadnodes
[1] = m_graphRunningAvgKad
.GetRate() / 1024.0;
630 update
.kadnodes
[2] = phr
->kadNodesCur
;
636 /* ------------------------------- TREE ---------------------------- */
638 void CStatistics::InitStatsTree()
640 s_statTree
= new CStatTreeItemBase(wxTRANSLATE("Statistics"));
642 CStatTreeItemBase
* tmpRoot1
;
643 CStatTreeItemBase
* tmpRoot2
;
645 s_uptime
= (CStatTreeItemTimer
*)s_statTree
->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Uptime: %s")));
647 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Transfer"), stSortChildren
));
649 tmpRoot2
= tmpRoot1
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Uploads")), 2);
650 s_sessionUpload
= (CStatTreeItemUlDlCounter
*)tmpRoot2
->AddChild(new CStatTreeItemUlDlCounter(wxTRANSLATE("Uploaded Data (Session (Total)): %s"), thePrefs::GetTotalUploaded
, stSortChildren
| stSortByValue
));
651 // Children will be added on-the-fly
652 s_totalUpOverhead
= (CStatTreeItemPacketTotals
*)tmpRoot2
->AddChild(new CStatTreeItemPacketTotals(wxTRANSLATE("Total Overhead (Packets): %s")));
653 s_fileReqUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("File Request Overhead (Packets): %s")));
654 s_totalUpOverhead
->AddPacketCounter(s_fileReqUpOverhead
);
655 s_sourceXchgUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Source Exchange Overhead (Packets): %s")));
656 s_totalUpOverhead
->AddPacketCounter(s_sourceXchgUpOverhead
);
657 s_serverUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Server Overhead (Packets): %s")));
658 s_totalUpOverhead
->AddPacketCounter(s_serverUpOverhead
);
659 s_kadUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Kad Overhead (Packets): %s")));
660 s_totalUpOverhead
->AddPacketCounter(s_kadUpOverhead
);
661 s_cryptUpOverhead
= (CStatTreeItemCounter
*)tmpRoot2
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Crypt overhead (UDP): %s")));
662 s_cryptUpOverhead
->SetDisplayMode(dmBytes
);
663 s_activeUploads
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Active Uploads: %s")));
664 s_waitingUploads
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Waiting Uploads: %s")));
665 s_totalSuccUploads
= (CStatTreeItemCounter
*)tmpRoot2
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total successful upload sessions: %s")));
666 s_totalFailedUploads
= (CStatTreeItemCounter
*)tmpRoot2
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total failed upload sessions: %s")));
667 s_totalUploadTime
= new CStatTreeItemCounter(wxEmptyString
);
668 tmpRoot2
->AddChild(new CStatTreeItemAverage(wxTRANSLATE("Average upload time: %s"), s_totalUploadTime
, s_totalSuccUploads
, dmTime
));
670 tmpRoot2
= tmpRoot1
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Downloads")), 1);
671 s_sessionDownload
= (CStatTreeItemUlDlCounter
*)tmpRoot2
->AddChild(new CStatTreeItemUlDlCounter(wxTRANSLATE("Downloaded Data (Session (Total)): %s"), thePrefs::GetTotalDownloaded
, stSortChildren
| stSortByValue
));
672 // Children will be added on-the-fly
673 s_totalDownOverhead
= (CStatTreeItemPacketTotals
*)tmpRoot2
->AddChild(new CStatTreeItemPacketTotals(wxTRANSLATE("Total Overhead (Packets): %s")));
674 s_fileReqDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("File Request Overhead (Packets): %s")));
675 s_totalDownOverhead
->AddPacketCounter(s_fileReqDownOverhead
);
676 s_sourceXchgDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Source Exchange Overhead (Packets): %s")));
677 s_totalDownOverhead
->AddPacketCounter(s_sourceXchgDownOverhead
);
678 s_serverDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Server Overhead (Packets): %s")));
679 s_totalDownOverhead
->AddPacketCounter(s_serverDownOverhead
);
680 s_kadDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Kad Overhead (Packets): %s")));
681 s_totalDownOverhead
->AddPacketCounter(s_kadDownOverhead
);
682 s_cryptDownOverhead
= (CStatTreeItemCounter
*)tmpRoot2
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Crypt overhead (UDP): %s")));
683 s_cryptDownOverhead
->SetDisplayMode(dmBytes
);
684 s_foundSources
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Found Sources: %s"), stSortChildren
| stSortByValue
));
685 s_activeDownloads
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Active Downloads (chunks): %s")));
687 tmpRoot1
->AddChild(new CStatTreeItemRatio(wxTRANSLATE("Session UL:DL Ratio (Total): %s"), s_sessionUpload
, s_sessionDownload
, thePrefs::GetTotalUploaded
, thePrefs::GetTotalDownloaded
), 3);
689 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Connection")));
690 tmpRoot1
->AddChild(new CStatTreeItemAverageSpeed(wxTRANSLATE("Average download rate (Session): %s"), s_sessionDownload
, s_uptime
));
691 tmpRoot1
->AddChild(new CStatTreeItemAverageSpeed(wxTRANSLATE("Average upload rate (Session): %s"), s_sessionUpload
, s_uptime
));
692 s_downloadrate
= (CStatTreeItemRateCounter
*)tmpRoot1
->AddChild(new CStatTreeItemRateCounter(wxTRANSLATE("Max download rate (Session): %s"), true, 30000));
693 s_uploadrate
= (CStatTreeItemRateCounter
*)tmpRoot1
->AddChild(new CStatTreeItemRateCounter(wxTRANSLATE("Max upload rate (Session): %s"), true, 30000));
694 s_reconnects
= (CStatTreeItemReconnects
*)tmpRoot1
->AddChild(new CStatTreeItemReconnects(wxTRANSLATE("Reconnects: %i")));
695 s_sinceFirstTransfer
= (CStatTreeItemTimer
*)tmpRoot1
->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Time Since First Transfer: %s"), stHideIfZero
));
696 s_sinceConnected
= (CStatTreeItemTimer
*)tmpRoot1
->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Connected To Server Since: %s")));
697 s_activeConnections
= (CStatTreeItemCounterMax
*)tmpRoot1
->AddChild(new CStatTreeItemCounterMax(wxTRANSLATE("Active Connections (estimate): %i")));
698 s_limitReached
= (CStatTreeItemMaxConnLimitReached
*)tmpRoot1
->AddChild(new CStatTreeItemMaxConnLimitReached(wxTRANSLATE("Max Connection Limit Reached: %s")));
699 s_avgConnections
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Average Connections (estimate): %g")));
700 s_avgConnections
->SetValue(0.0);
701 tmpRoot1
->AddChild(new CStatTreeItemPeakConnections(wxTRANSLATE("Peak Connections (estimate): %i")));
703 s_clients
= (CStatTreeItemHiddenCounter
*)s_statTree
->AddChild(new CStatTreeItemHiddenCounter(wxTRANSLATE("Clients"), stSortChildren
| stSortByValue
));
704 s_unknown
= (CStatTreeItemCounter
*)s_clients
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Unknown: %s")), 6);
705 //s_lowID = (CStatTreeItem*)s_clients->AddChild(new CStatTreeItem(wxTRANSLATE("LowID: %u (%.2f%% Total %.2f%% Known)")), 5);
706 //s_secIdentOnOff = (CStatTreeItem*)s_clients->AddChild(new CStatTreeItem(wxTRANSLATE("SecIdent On/Off: %u (%.2f%%) : %u (%.2f%%)")), 4);
708 s_hasSocket
= (CStatTreeItemNativeCounter
*)s_clients
->AddChild(new CStatTreeItemNativeCounter(wxT("HasSocket: %s")), 3);
710 s_filtered
= (CStatTreeItemNativeCounter
*)s_clients
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Filtered: %s")), 2);
711 s_banned
= (CStatTreeItemNativeCounter
*)s_clients
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Banned: %s")), 1);
712 s_clients
->AddChild(new CStatTreeItemTotalClients(wxTRANSLATE("Total: %i Known: %i"), s_clients
, s_unknown
), 0x80000000);
714 // TODO: Use counters?
715 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Servers")));
716 s_workingServers
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Working Servers: %i")));
717 s_failedServers
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Failed Servers: %i")));
718 s_totalServers
= (CStatTreeItemNativeCounter
*)tmpRoot1
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Total: %s")));
719 s_deletedServers
= (CStatTreeItemNativeCounter
*)tmpRoot1
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Deleted Servers: %s")));
720 s_filteredServers
= (CStatTreeItemNativeCounter
*)tmpRoot1
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Filtered Servers: %s")));
721 s_usersOnWorking
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Users on Working Servers: %llu")));
722 s_filesOnWorking
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Files on Working Servers: %llu")));
723 s_totalUsers
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Total Users: %llu")));
724 s_totalFiles
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Total Files: %llu")));
725 s_serverOccupation
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Server Occupation: %.2f%%")));
726 s_serverOccupation
->SetValue(0.0);
728 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Shared Files")));
729 s_numberOfShared
= (CStatTreeItemCounter
*)tmpRoot1
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Number of Shared Files: %s")));
730 s_sizeOfShare
= (CStatTreeItemCounter
*)tmpRoot1
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total size of Shared Files: %s")));
731 s_sizeOfShare
->SetDisplayMode(dmBytes
);
732 tmpRoot1
->AddChild(new CStatTreeItemAverage(wxTRANSLATE("Average file size: %s"), s_sizeOfShare
, s_numberOfShared
, dmBytes
));
736 void CStatistics::UpdateStatsTree()
738 // get sort orders right
739 s_sessionUpload
->ReSortChildren();
740 s_sessionDownload
->ReSortChildren();
741 s_clients
->ReSortChildren();
742 s_foundSources
->ReSortChildren();
743 // TODO: sort OS_Info subtrees.
745 s_avgConnections
->SetValue(theApp
->listensocket
->GetAverageConnections());
748 // TODO: make these realtime, too
755 theApp
->serverlist
->GetStatus(servfail
, servuser
, servfile
, servtuser
, servtfile
, servocc
);
756 s_workingServers
->SetValue((uint64
)((*s_totalServers
)-servfail
));
757 s_failedServers
->SetValue((uint64
)servfail
);
758 s_usersOnWorking
->SetValue((uint64
)servuser
);
759 s_filesOnWorking
->SetValue((uint64
)servfile
);
760 s_totalUsers
->SetValue((uint64
)servtuser
);
761 s_totalFiles
->SetValue((uint64
)servtfile
);
762 s_serverOccupation
->SetValue(servocc
);
766 void CStatistics::AddSourceOrigin(unsigned origin
)
768 CStatTreeItemNativeCounter
* counter
= (CStatTreeItemNativeCounter
*)s_foundSources
->GetChildById(0x0100 + origin
);
772 counter
= new CStatTreeItemNativeCounter(OriginToText(origin
) + wxT(": %s"), stHideIfZero
| stShowPercent
);
774 s_foundSources
->AddChild(counter
, 0x0100 + origin
);
778 void CStatistics::RemoveSourceOrigin(unsigned origin
)
780 CStatTreeItemNativeCounter
* counter
= (CStatTreeItemNativeCounter
*)s_foundSources
->GetChildById(0x0100 + origin
);
785 uint32
GetSoftID(uint8 SoftType
)
787 // prevent appearing multiple tree entries with the same name
788 // this should be kept in sync with GetSoftName().
791 return 0x0100 + SO_EMULE
;
792 case SO_NEW_SHAREAZA
:
793 case SO_NEW2_SHAREAZA
:
794 return 0x0100 + SO_SHAREAZA
;
795 case SO_NEW2_MLDONKEY
:
796 return 0x0100 + SO_NEW_MLDONKEY
;
798 return 0x0100 + SoftType
;
802 void CStatistics::AddDownloadFromSoft(uint8 SoftType
, uint32 bytes
)
804 AddReceivedBytes(bytes
);
806 uint32 id
= GetSoftID(SoftType
);
808 if (s_sessionDownload
->HasChildWithId(id
)) {
809 (*((CStatTreeItemCounter
*)s_sessionDownload
->GetChildById(id
))) += bytes
;
811 CStatTreeItemCounter
* tmp
= new CStatTreeItemCounter(GetSoftName(SoftType
) + wxT(": %s"));
812 tmp
->SetDisplayMode(dmBytes
);
814 s_sessionDownload
->AddChild(tmp
, id
);
818 void CStatistics::AddUploadToSoft(uint8 SoftType
, uint32 bytes
)
820 uint32 id
= GetSoftID(SoftType
);
822 if (s_sessionUpload
->HasChildWithId(id
)) {
823 (*((CStatTreeItemCounter
*)s_sessionUpload
->GetChildById(id
))) += bytes
;
825 CStatTreeItemCounter
* tmp
= new CStatTreeItemCounter(GetSoftName(SoftType
) + wxT(": %s"));
826 tmp
->SetDisplayMode(dmBytes
);
828 s_sessionUpload
->AddChild(tmp
, id
);
832 inline bool SupportsOSInfo(unsigned clientSoft
)
834 return (clientSoft
== SO_AMULE
) || (clientSoft
== SO_HYDRANODE
) || (clientSoft
== SO_NEW2_MLDONKEY
);
837 // Do some random black magic to strings to get a relatively unique number for them.
838 uint32
GetIdFromString(const wxString
& str
)
841 for (unsigned i
= 0; i
< str
.Length(); ++i
) {
842 unsigned old_id
= id
;
843 id
+= (uint32
)str
.GetChar(i
);
848 return (((id
>> 1) + id
) | 0x00000100) & 0x7fffffff;
851 void CStatistics::AddKnownClient(CUpDownClient
*pClient
)
855 uint32 clientSoft
= pClient
->GetClientSoft();
856 uint32 id
= GetSoftID(clientSoft
);
858 CStatTreeItemCounter
*client
;
860 if (s_clients
->HasChildWithId(id
)) {
861 client
= (CStatTreeItemCounter
*)s_clients
->GetChildById(id
);
864 uint32 flags
= stSortChildren
| stShowPercent
| stHideIfZero
;
865 if (!SupportsOSInfo(clientSoft
)) {
866 flags
|= stCapChildren
;
868 client
= new CStatTreeItemCounter(GetSoftName(clientSoft
) + wxT(": %s"), flags
);
870 s_clients
->AddChild(client
, id
);
871 if (SupportsOSInfo(clientSoft
)) {
872 client
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Version"), stSortChildren
| stCapChildren
), 2);
873 client
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Operating System"), stSortChildren
| stSortByValue
), 1);
877 CStatTreeItemBase
*versionRoot
= SupportsOSInfo(clientSoft
) ? client
->GetChildById(2) : client
;
878 uint32 clientVersion
= pClient
->GetVersion();
880 if (versionRoot
->HasChildWithId(clientVersion
)) {
881 CStatTreeItemCounter
*version
= (CStatTreeItemCounter
*)versionRoot
->GetChildById(clientVersion
);
884 const wxString
& versionStr
= pClient
->GetVersionString();
885 CStatTreeItemCounter
*version
= new CStatTreeItemCounter((versionStr
.IsEmpty() ? wxString(wxTRANSLATE("Unknown")) : versionStr
) + wxT(": %s"), stShowPercent
| stHideIfZero
);
887 versionRoot
->AddChild(version
, clientVersion
, SupportsOSInfo(clientSoft
));
890 if (SupportsOSInfo(clientSoft
)) {
891 const wxString
& OSInfo
= pClient
->GetClientOSInfo();
892 uint32 OS_ID
= OSInfo
.IsEmpty() ? 0 : GetIdFromString(OSInfo
);
893 CStatTreeItemBase
* OSRoot
= client
->GetChildById(1);
894 CStatTreeItemCounter
* OSNode
= (CStatTreeItemCounter
*)OSRoot
->GetChildById(OS_ID
);
898 OSNode
= new CStatTreeItemCounter((OS_ID
? OSInfo
: wxString(wxTRANSLATE("Not Received"))) + wxT(": %s"), stShowPercent
| stHideIfZero
);
900 OSRoot
->AddChild(OSNode
, OS_ID
, true);
905 void CStatistics::RemoveKnownClient(uint32 clientSoft
, uint32 clientVersion
, const wxString
& OSInfo
)
909 uint32 id
= GetSoftID(clientSoft
);
911 CStatTreeItemCounter
*client
= (CStatTreeItemCounter
*)s_clients
->GetChildById(id
);
915 CStatTreeItemBase
*versionRoot
= SupportsOSInfo(clientSoft
) ? client
->GetChildById(2) : client
;
917 CStatTreeItemCounter
*version
= (CStatTreeItemCounter
*)versionRoot
->GetChildById(clientVersion
);
921 if (SupportsOSInfo(clientSoft
)) {
922 uint32 OS_ID
= OSInfo
.IsEmpty() ? 0 : GetIdFromString(OSInfo
);
923 CStatTreeItemCounter
* OSNode
= (CStatTreeItemCounter
*)client
->GetChildById(1)->GetChildById(OS_ID
);
929 #else /* CLIENT_GUI */
931 CStatistics::CStatistics(CRemoteConnect
&conn
)
935 s_start_time
= GetTickCount64();
938 s_statTree
= new CStatTreeItemBase(_("Statistics"), 0);
940 // Clear stat data container
941 for (int i
= 0; i
< sdTotalItems
; ++i
) {
947 CStatistics::~CStatistics()
953 void CStatistics::UpdateStats(const CECPacket
* stats
)
955 s_statData
[sdUpload
] = stats
->GetTagByNameSafe(EC_TAG_STATS_UL_SPEED
)->GetInt();
956 s_statData
[sdUpOverhead
] = stats
->GetTagByNameSafe(EC_TAG_STATS_UP_OVERHEAD
)->GetInt();
957 s_statData
[sdDownload
] = stats
->GetTagByNameSafe(EC_TAG_STATS_DL_SPEED
)->GetInt();
958 s_statData
[sdDownOverhead
] = stats
->GetTagByNameSafe(EC_TAG_STATS_DOWN_OVERHEAD
)->GetInt();
959 s_statData
[sdWaitingClients
] = stats
->GetTagByNameSafe(EC_TAG_STATS_UL_QUEUE_LEN
)->GetInt();
960 s_statData
[sdBannedClients
] = stats
->GetTagByNameSafe(EC_TAG_STATS_BANNED_COUNT
)->GetInt();
961 s_statData
[sdED2KUsers
] = stats
->GetTagByNameSafe(EC_TAG_STATS_ED2K_USERS
)->GetInt();
962 s_statData
[sdKadUsers
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_USERS
)->GetInt();
963 s_statData
[sdED2KFiles
] = stats
->GetTagByNameSafe(EC_TAG_STATS_ED2K_FILES
)->GetInt();
964 s_statData
[sdKadFiles
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_FILES
)->GetInt();
965 s_statData
[sdKadFirewalledUDP
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_FIREWALLED_UDP
)->GetInt();
966 s_statData
[sdKadIndexedSources
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_INDEXED_SOURCES
)->GetInt();
967 s_statData
[sdKadIndexedKeywords
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_INDEXED_KEYWORDS
)->GetInt();
968 s_statData
[sdKadIndexedNotes
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_INDEXED_NOTES
)->GetInt();
969 s_statData
[sdKadIndexedLoad
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_INDEXED_LOAD
)->GetInt();
970 s_statData
[sdKadIPAdress
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_IP_ADRESS
)->GetInt();
971 s_statData
[sdBuddyStatus
] = stats
->GetTagByNameSafe(EC_TAG_STATS_BUDDY_STATUS
)->GetInt();
972 s_statData
[sdBuddyIP
] = stats
->GetTagByNameSafe(EC_TAG_STATS_BUDDY_IP
)->GetInt();
973 s_statData
[sdBuddyPort
] = stats
->GetTagByNameSafe(EC_TAG_STATS_BUDDY_PORT
)->GetInt();
974 s_statData
[sdKadInLanMode
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_IN_LAN_MODE
)->GetInt();
976 const CECTag
* LoggerTag
= stats
->GetTagByName(EC_TAG_STATS_LOGGER_MESSAGE
);
978 for (CECTag::const_iterator it
= LoggerTag
->begin(); it
!= LoggerTag
->end(); it
++) {
979 theApp
->AddRemoteLogLine(it
->GetStringData());
985 void CStatistics::UpdateStatsTree()
990 void CStatistics::RebuildStatTreeRemote(const CECTag
* tag
)
993 s_statTree
= new CStatTreeItemBase(tag
);
997 uint64
CStatistics::GetUptimeMillis()
999 return GetTickCount64() - s_start_time
;
1003 uint64
CStatistics::GetUptimeSeconds()
1005 return (GetTickCount64() - s_start_time
) / 1000;
1008 #endif /* !CLIENT_GUI */
1010 // File_checked_for_headers