1 /**************************************************************************/
2 /*                                                                        */
3 /* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/.         */
4 /*                                                                        */
5 /* NXCOMP, NX protocol compression and NX extensions to this software     */
6 /* are copyright of NoMachine. Redistribution and use of the present      */
7 /* software is allowed according to terms specified in the file LICENSE   */
8 /* which comes in the source distribution.                                */
9 /*                                                                        */
10 /* Check http://www.nomachine.com/licensing.html for applicability.       */
11 /*                                                                        */
12 /* NX and NoMachine are trademarks of Medialogic S.p.A.                   */
13 /*                                                                        */
14 /* All rights reserved.                                                   */
15 /*                                                                        */
16 /**************************************************************************/
17 
18 #include <stdio.h>
19 
20 #include "Statistics.h"
21 
22 #include "Control.h"
23 
24 #include "Proxy.h"
25 
26 #include "ClientStore.h"
27 #include "ServerStore.h"
28 
29 //
30 // Length of temporary buffer
31 // used to format output.
32 //
33 
34 #define FORMAT_LENGTH          1024
35 
36 //
37 // Log level.
38 //
39 
40 #define PANIC
41 #define WARNING
42 #undef  TEST
43 #undef  DEBUG
44 
45 //
46 // Note that when presenting statistics we invert the
47 // correct semantics of X client and server entities.
48 // This is questionable, but matches the user's pers-
49 // pective of running remote X applications on its
50 // local client.
51 //
52 
Statistics(Proxy * proxy)53 Statistics::Statistics(Proxy *proxy) : proxy_(proxy)
54 {
55   transportPartial_.idleTime_             = 0;
56   transportPartial_.readTime_             = 0;
57   transportPartial_.writeTime_            = 0;
58   transportPartial_.proxyBytesIn_         = 0;
59   transportPartial_.proxyBytesOut_        = 0;
60   transportPartial_.proxyFramesIn_        = 0;
61   transportPartial_.proxyFramesOut_       = 0;
62   transportPartial_.proxyWritesOut_       = 0;
63   transportPartial_.compressedBytesIn_    = 0;
64   transportPartial_.compressedBytesOut_   = 0;
65   transportPartial_.decompressedBytesIn_  = 0;
66   transportPartial_.decompressedBytesOut_ = 0;
67   transportPartial_.framingBitsOut_       = 0;
68 
69   transportTotal_.idleTime_             = 0;
70   transportTotal_.readTime_             = 0;
71   transportTotal_.writeTime_            = 0;
72   transportTotal_.proxyBytesIn_         = 0;
73   transportTotal_.proxyBytesOut_        = 0;
74   transportTotal_.proxyFramesIn_        = 0;
75   transportTotal_.proxyFramesOut_       = 0;
76   transportTotal_.proxyWritesOut_       = 0;
77   transportTotal_.compressedBytesIn_    = 0;
78   transportTotal_.compressedBytesOut_   = 0;
79   transportTotal_.decompressedBytesIn_  = 0;
80   transportTotal_.decompressedBytesOut_ = 0;
81   transportTotal_.framingBitsOut_       = 0;
82 
83   for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
84   {
85     protocolPartial_.requestCached_[i]  = 0;
86     protocolPartial_.requestReplied_[i] = 0;
87     protocolPartial_.requestCount_[i]   = 0;
88     protocolPartial_.requestBitsIn_[i]  = 0;
89     protocolPartial_.requestBitsOut_[i] = 0;
90 
91     protocolPartial_.renderRequestCached_[i]  = 0;
92     protocolPartial_.renderRequestCount_[i]   = 0;
93     protocolPartial_.renderRequestBitsIn_[i]  = 0;
94     protocolPartial_.renderRequestBitsOut_[i] = 0;
95 
96     protocolPartial_.replyCached_[i]  = 0;
97     protocolPartial_.replyCount_[i]   = 0;
98     protocolPartial_.replyBitsIn_[i]  = 0;
99     protocolPartial_.replyBitsOut_[i] = 0;
100 
101     protocolPartial_.eventCached_[i]  = 0;
102     protocolPartial_.eventCount_[i]   = 0;
103     protocolPartial_.eventBitsIn_[i]  = 0;
104     protocolPartial_.eventBitsOut_[i] = 0;
105 
106     protocolTotal_.requestCached_[i]  = 0;
107     protocolTotal_.requestReplied_[i] = 0;
108     protocolTotal_.requestCount_[i]   = 0;
109     protocolTotal_.requestBitsIn_[i]  = 0;
110     protocolTotal_.requestBitsOut_[i] = 0;
111 
112     protocolTotal_.renderRequestCached_[i]  = 0;
113     protocolTotal_.renderRequestCount_[i]   = 0;
114     protocolTotal_.renderRequestBitsIn_[i]  = 0;
115     protocolTotal_.renderRequestBitsOut_[i] = 0;
116 
117     protocolTotal_.replyCached_[i]  = 0;
118     protocolTotal_.replyCount_[i]   = 0;
119     protocolTotal_.replyBitsIn_[i]  = 0;
120     protocolTotal_.replyBitsOut_[i] = 0;
121 
122     protocolTotal_.eventCached_[i]  = 0;
123     protocolTotal_.eventCount_[i]   = 0;
124     protocolTotal_.eventBitsIn_[i]  = 0;
125     protocolTotal_.eventBitsOut_[i] = 0;
126   }
127 
128   protocolPartial_.cupsCount_    = 0;
129   protocolPartial_.cupsBitsIn_   = 0;
130   protocolPartial_.cupsBitsOut_  = 0;
131 
132   protocolPartial_.smbCount_     = 0;
133   protocolPartial_.smbBitsIn_    = 0;
134   protocolPartial_.smbBitsOut_   = 0;
135 
136   protocolPartial_.mediaCount_   = 0;
137   protocolPartial_.mediaBitsIn_  = 0;
138   protocolPartial_.mediaBitsOut_ = 0;
139 
140   protocolPartial_.httpCount_    = 0;
141   protocolPartial_.httpBitsIn_   = 0;
142   protocolPartial_.httpBitsOut_  = 0;
143 
144   protocolPartial_.fontCount_    = 0;
145   protocolPartial_.fontBitsIn_   = 0;
146   protocolPartial_.fontBitsOut_  = 0;
147 
148   protocolPartial_.slaveCount_   = 0;
149   protocolPartial_.slaveBitsIn_  = 0;
150   protocolPartial_.slaveBitsOut_ = 0;
151 
152   protocolTotal_.cupsCount_    = 0;
153   protocolTotal_.cupsBitsIn_   = 0;
154   protocolTotal_.cupsBitsOut_  = 0;
155 
156   protocolTotal_.smbCount_     = 0;
157   protocolTotal_.smbBitsIn_    = 0;
158   protocolTotal_.smbBitsOut_   = 0;
159 
160   protocolTotal_.mediaCount_   = 0;
161   protocolTotal_.mediaBitsIn_  = 0;
162   protocolTotal_.mediaBitsOut_ = 0;
163 
164   protocolTotal_.httpCount_    = 0;
165   protocolTotal_.httpBitsIn_   = 0;
166   protocolTotal_.httpBitsOut_  = 0;
167 
168   protocolTotal_.fontCount_    = 0;
169   protocolTotal_.fontBitsIn_   = 0;
170   protocolTotal_.fontBitsOut_  = 0;
171 
172   protocolTotal_.slaveCount_   = 0;
173   protocolTotal_.slaveBitsIn_  = 0;
174   protocolTotal_.slaveBitsOut_ = 0;
175 
176   packedPartial_.packedBytesIn_  = 0;
177   packedPartial_.packedBytesOut_ = 0;
178 
179   packedTotal_.packedBytesIn_    = 0;
180   packedTotal_.packedBytesOut_   = 0;
181 
182   splitPartial_.splitCount_           = 0;
183   splitPartial_.splitAborted_         = 0;
184   splitPartial_.splitAbortedBytesOut_ = 0;
185 
186   splitTotal_.splitCount_             = 0;
187   splitTotal_.splitAborted_           = 0;
188   splitTotal_.splitAbortedBytesOut_   = 0;
189 
190   overallPartial_.overallBytesIn_  = 0;
191   overallPartial_.overallBytesOut_ = 0;
192 
193   overallTotal_.overallBytesIn_    = 0;
194   overallTotal_.overallBytesOut_   = 0;
195 
196   proxyData_.protocolCount_ = 0;
197   proxyData_.controlCount_  = 0;
198   proxyData_.splitCount_    = 0;
199   proxyData_.dataCount_     = 0;
200 
201   proxyData_.streamRatio_ = 1;
202 
203   startShortFrameTs_ = getTimestamp();
204   startLongFrameTs_  = getTimestamp();
205   startFrameTs_      = getTimestamp();
206 
207   bytesInShortFrame_ = 0;
208   bytesInLongFrame_  = 0;
209 
210   bitrateInShortFrame_ = 0;
211   bitrateInLongFrame_  = 0;
212 
213   topBitrate_ = 0;
214 
215   congestionInFrame_ = 0;
216 }
217 
~Statistics()218 Statistics::~Statistics()
219 {
220 }
221 
resetPartialStats()222 int Statistics::resetPartialStats()
223 {
224   transportPartial_.idleTime_             = 0;
225   transportPartial_.readTime_             = 0;
226   transportPartial_.writeTime_            = 0;
227   transportPartial_.proxyBytesIn_         = 0;
228   transportPartial_.proxyBytesOut_        = 0;
229   transportPartial_.proxyFramesIn_        = 0;
230   transportPartial_.proxyFramesOut_       = 0;
231   transportPartial_.proxyWritesOut_       = 0;
232   transportPartial_.compressedBytesIn_    = 0;
233   transportPartial_.compressedBytesOut_   = 0;
234   transportPartial_.decompressedBytesIn_  = 0;
235   transportPartial_.decompressedBytesOut_ = 0;
236   transportPartial_.framingBitsOut_       = 0;
237 
238   for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
239   {
240     protocolPartial_.requestCached_[i]  = 0;
241     protocolPartial_.requestReplied_[i] = 0;
242     protocolPartial_.requestCount_[i]   = 0;
243     protocolPartial_.requestBitsIn_[i]  = 0;
244     protocolPartial_.requestBitsOut_[i] = 0;
245 
246     protocolPartial_.renderRequestCached_[i]  = 0;
247     protocolPartial_.renderRequestCount_[i]   = 0;
248     protocolPartial_.renderRequestBitsIn_[i]  = 0;
249     protocolPartial_.renderRequestBitsOut_[i] = 0;
250 
251     protocolPartial_.replyCached_[i]  = 0;
252     protocolPartial_.replyCount_[i]   = 0;
253     protocolPartial_.replyBitsIn_[i]  = 0;
254     protocolPartial_.replyBitsOut_[i] = 0;
255 
256     protocolPartial_.eventCached_[i]  = 0;
257     protocolPartial_.eventCount_[i]   = 0;
258     protocolPartial_.eventBitsIn_[i]  = 0;
259     protocolPartial_.eventBitsOut_[i] = 0;
260   }
261 
262   protocolPartial_.cupsCount_    = 0;
263   protocolPartial_.cupsBitsIn_   = 0;
264   protocolPartial_.cupsBitsOut_  = 0;
265 
266   protocolPartial_.smbCount_     = 0;
267   protocolPartial_.smbBitsIn_    = 0;
268   protocolPartial_.smbBitsOut_   = 0;
269 
270   protocolPartial_.mediaCount_   = 0;
271   protocolPartial_.mediaBitsIn_  = 0;
272   protocolPartial_.mediaBitsOut_ = 0;
273 
274   protocolPartial_.httpCount_    = 0;
275   protocolPartial_.httpBitsIn_   = 0;
276   protocolPartial_.httpBitsOut_  = 0;
277 
278   protocolPartial_.fontCount_    = 0;
279   protocolPartial_.fontBitsIn_   = 0;
280   protocolPartial_.fontBitsOut_  = 0;
281 
282   protocolPartial_.slaveCount_   = 0;
283   protocolPartial_.slaveBitsIn_  = 0;
284   protocolPartial_.slaveBitsOut_ = 0;
285 
286   packedPartial_.packedBytesIn_  = 0;
287   packedPartial_.packedBytesOut_ = 0;
288 
289   splitPartial_.splitCount_           = 0;
290   splitPartial_.splitAborted_         = 0;
291   splitPartial_.splitAbortedBytesOut_ = 0;
292 
293   overallPartial_.overallBytesIn_  = 0;
294   overallPartial_.overallBytesOut_ = 0;
295 
296   return 1;
297 }
298 
addCompressedBytes(unsigned int bytesIn,unsigned int bytesOut)299 void Statistics::addCompressedBytes(unsigned int bytesIn, unsigned int bytesOut)
300 {
301   transportPartial_.compressedBytesIn_ += bytesIn;
302   transportTotal_.compressedBytesIn_ += bytesIn;
303 
304   transportPartial_.compressedBytesOut_ += bytesOut;
305   transportTotal_.compressedBytesOut_ += bytesOut;
306 
307   double ratio = bytesIn / bytesOut;
308 
309   if (ratio < 1)
310   {
311     ratio = 1;
312   }
313 
314   #if defined(TEST) || defined(TOKEN)
315   *logofs << "Statistics: TOKEN! Old ratio was "
316           << proxyData_.streamRatio_ << " current is "
317           << (double) ratio << " new ratio is " << (double)
318              ((proxyData_.streamRatio_ * 2) + ratio) / 3 << ".\n"
319           << logofs_flush;
320   #endif
321 
322   proxyData_.streamRatio_ = ((proxyData_.streamRatio_ * 2) + ratio) / 3;
323 
324   #if defined(TEST) || defined(TOKEN)
325   *logofs << "Statistics: TOKEN! Updated compressed bytes "
326           << "with " << bytesIn << " in " << bytesOut << " out "
327           << "and ratio " << (double) proxyData_.streamRatio_
328           << ".\n" << logofs_flush;
329   #endif
330 }
331 
332 //
333 // Recalculate the current bitrate. The bytes written
334 // are accounted at the time the transport actually
335 // writes the data to the network, not at the time it
336 // receives the data from the upper layers. The reason
337 // is that data can be compressed by the stream com-
338 // pressor, so we can become aware of the new bitrate
339 // only afer having flushed the ZLIB stream. This also
340 // means that, to have a reliable estimate, we need to
341 // flush the link often.
342 //
343 
updateBitrate(int bytes)344 void Statistics::updateBitrate(int bytes)
345 {
346   T_timestamp thisFrameTs = getNewTimestamp();
347 
348   int diffFramesInMs = diffTimestamp(startFrameTs_, thisFrameTs);
349 
350   #ifdef DEBUG
351   *logofs << "Statistics: Difference since previous timestamp is "
352           << diffFramesInMs << " Ms.\n" << logofs_flush;
353   #endif
354 
355   if (diffFramesInMs > 0)
356   {
357     #ifdef DEBUG
358     *logofs << "Statistics: Removing " << diffFramesInMs
359             << " Ms in short and long time frame.\n"
360             << logofs_flush;
361     #endif
362 
363     int shortBytesToRemove = (int) (((double) bytesInShortFrame_ * (double) diffFramesInMs) /
364                                        (double) control -> ShortBitrateTimeFrame);
365 
366     int longBytesToRemove = (int) (((double) bytesInLongFrame_ * (double) diffFramesInMs) /
367                                       (double) control -> LongBitrateTimeFrame);
368 
369     #ifdef DEBUG
370     *logofs << "Statistics: Removing " << shortBytesToRemove
371             << " bytes from " << bytesInShortFrame_
372             << " in the short frame.\n" << logofs_flush;
373     #endif
374 
375     bytesInShortFrame_ -= shortBytesToRemove;
376 
377     if (bytesInShortFrame_ < 0)
378     {
379       #ifdef TEST
380       *logofs << "Statistics: Bytes in short frame are "
381               << bytesInShortFrame_ << ". Set to 0.\n"
382               << logofs_flush;
383       #endif
384 
385       bytesInShortFrame_ = 0;
386     }
387 
388     #ifdef DEBUG
389     *logofs << "Statistics: Removing " << longBytesToRemove
390             << " bytes from " << bytesInLongFrame_
391             << " in the long frame.\n" << logofs_flush;
392     #endif
393 
394     bytesInLongFrame_ -= longBytesToRemove;
395 
396     if (bytesInLongFrame_ < 0)
397     {
398       #ifdef TEST
399       *logofs << "Statistics: Bytes in long frame are "
400               << bytesInLongFrame_ << ". Set to 0.\n"
401               << logofs_flush;
402       #endif
403 
404       bytesInLongFrame_ = 0;
405     }
406 
407     int diffStartInMs;
408 
409     diffStartInMs = diffTimestamp(thisFrameTs, startShortFrameTs_);
410 
411     if (diffStartInMs > control -> ShortBitrateTimeFrame)
412     {
413       addMsTimestamp(startShortFrameTs_, diffStartInMs);
414     }
415 
416     diffStartInMs = diffTimestamp(thisFrameTs, startLongFrameTs_);
417 
418     if (diffStartInMs > control -> LongBitrateTimeFrame)
419     {
420       addMsTimestamp(startLongFrameTs_, diffStartInMs);
421     }
422 
423     startFrameTs_ = thisFrameTs;
424   }
425 
426   #ifdef DEBUG
427   *logofs << "Statistics: Adding " << bytes << " bytes to "
428           << bytesInShortFrame_ << " in the short frame.\n"
429           << logofs_flush;
430   #endif
431 
432   bytesInShortFrame_ = bytesInShortFrame_ + bytes;
433 
434   #ifdef DEBUG
435   *logofs << "Statistics: Adding " << bytes << " bytes to "
436           << bytesInLongFrame_ << " in the long frame.\n"
437           << logofs_flush;
438   #endif
439 
440   bytesInLongFrame_ = bytesInLongFrame_ + bytes;
441 
442   bitrateInShortFrame_ = (int) ((double) bytesInShortFrame_ /
443                                    ((double) control -> ShortBitrateTimeFrame / 1000));
444 
445   bitrateInLongFrame_ = (int) ((double) bytesInLongFrame_ /
446                                    ((double) control -> LongBitrateTimeFrame / 1000));
447 
448   if (bitrateInShortFrame_ > topBitrate_)
449   {
450     topBitrate_ = bitrateInShortFrame_;
451   }
452 
453   #ifdef TEST
454   *logofs << "Statistics: Current bitrate is short " << bitrateInShortFrame_
455           << " long " << bitrateInLongFrame_ << " top " << topBitrate_
456           << ".\n" << logofs_flush;
457   #endif
458 }
459 
updateCongestion(int remaining,int limit)460 void Statistics::updateCongestion(int remaining, int limit)
461 {
462   #ifdef TEST
463   *logofs << "Statistics: Updating the congestion "
464           << "counters at " << strMsTimestamp()
465           << ".\n" << logofs_flush;
466   #endif
467 
468   double current = remaining;
469 
470   if (current < 0)
471   {
472     current = 0;
473   }
474 
475   current = 9 * (limit - current) / limit;
476 
477   #ifdef TEST
478   *logofs << "Statistics: Current congestion is "
479           << current << " with " << limit << " tokens "
480           << "and " << remaining << " remaining.\n"
481           << logofs_flush;
482   #endif
483 
484   //
485   // If the current congestion counter is greater
486   // than the previous, take the current value,
487   // otherwise ramp down the value by calculating
488   // the average of the last 8 updates.
489   //
490 
491   #ifdef TEST
492   *logofs << "Statistics: Old congestion was "
493           << congestionInFrame_;
494   #endif
495 
496   if (current >= congestionInFrame_)
497   {
498     congestionInFrame_ = current;
499   }
500   else
501   {
502     congestionInFrame_ = ((congestionInFrame_ * 7) + current) / 8;
503   }
504 
505   #ifdef TEST
506   *logofs << " new congestion is "
507           << ((congestionInFrame_ * 7) + current) / 8
508           << ".\n" << logofs_flush;
509   #endif
510 
511   //
512   // Call the function with 0 bytes flushed
513   // so the agent can update its congestion
514   // counter.
515   //
516 
517   FlushCallback(0);
518 }
519 
getClientCacheStats(int type,char * & buffer)520 int Statistics::getClientCacheStats(int type, char *&buffer)
521 {
522   if (type != PARTIAL_STATS && type != TOTAL_STATS)
523   {
524     #ifdef PANIC
525     *logofs << "Statistics: PANIC! Cannot produce statistics "
526             << "with qualifier '" << type << "'.\n"
527             << logofs_flush;
528     #endif
529 
530     return -1;
531   }
532 
533   //
534   // Print message cache data according
535   // to local and remote view.
536   //
537 
538   MessageStore *currentStore = NULL;
539   MessageStore *anyStore     = NULL;
540 
541   char format[FORMAT_LENGTH];
542 
543   strcat(buffer, "\nNX Cache Statistics\n");
544   strcat(buffer, "-------------------\n\n");
545 
546   for (int m = proxy_client; m <= proxy_server; m++)
547   {
548     if (m == proxy_client)
549     {
550       strcat(buffer, "Request\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
551       strcat(buffer, "-------\t------\t--------------\t\t--------------\t\t-----------\n");
552     }
553     else
554     {
555       strcat(buffer, "\nReply\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
556       strcat(buffer, "-----\t------\t--------------\t\t--------------\t\t-----------\n");
557     }
558 
559     for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
560     {
561       if (m == proxy_client)
562       {
563         currentStore = proxy_ -> getClientStore() -> getRequestStore(i);
564       }
565       else
566       {
567         currentStore = proxy_ -> getServerStore() -> getReplyStore(i);
568       }
569 
570       if (currentStore != NULL &&
571               (currentStore -> getLocalStorageSize() ||
572                    currentStore -> getRemoteStorageSize()))
573       {
574         anyStore = currentStore;
575 
576         sprintf(format, "#%d\t%d\t", i, currentStore -> getSize());
577 
578         strcat(buffer, format);
579 
580         sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getLocalStorageSize(),
581                     ((double) currentStore -> getLocalStorageSize()) / 1024);
582 
583         strcat(buffer, format);
584 
585         sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getRemoteStorageSize(),
586                     ((double) currentStore -> getRemoteStorageSize()) / 1024);
587 
588         strcat(buffer, format);
589 
590         sprintf(format, "%d/%.0f KB\n", currentStore -> cacheSlots,
591                     ((double) control -> getUpperStorageSize() / 100 *
592                           currentStore -> cacheThreshold) / 1024);
593 
594         strcat(buffer, format);
595       }
596     }
597 
598     if (anyStore == NULL)
599     {
600       strcat(buffer, "N/A\n");
601     }
602   }
603 
604   if (anyStore != NULL)
605   {
606     sprintf(format, "\ncache: %d bytes (%d KB) available at server.\n",
607                 control -> ClientTotalStorageSize,
608                     control -> ClientTotalStorageSize / 1024);
609 
610     strcat(buffer, format);
611 
612     sprintf(format, "       %d bytes (%d KB) available at client.\n\n",
613                 control -> ServerTotalStorageSize,
614                     control -> ServerTotalStorageSize / 1024);
615 
616     strcat(buffer, format);
617 
618     sprintf(format, "       %d bytes (%d KB) allocated at server.\n",
619                 anyStore -> getLocalTotalStorageSize(),
620                     anyStore -> getLocalTotalStorageSize() / 1024);
621 
622     strcat(buffer, format);
623 
624     sprintf(format, "       %d bytes (%d KB) allocated at client.\n\n\n",
625                 anyStore -> getRemoteTotalStorageSize(),
626                     anyStore -> getRemoteTotalStorageSize() / 1024);
627 
628     strcat(buffer, format);
629   }
630   else
631   {
632     strcat(buffer, "\ncache: N/A\n\n");
633   }
634 
635   return 1;
636 }
637 
getClientProtocolStats(int type,char * & buffer)638 int Statistics::getClientProtocolStats(int type, char *&buffer)
639 {
640   if (type != PARTIAL_STATS && type != TOTAL_STATS)
641   {
642     #ifdef PANIC
643     *logofs << "Statistics: PANIC! Cannot produce statistics "
644             << "with qualifier '" << type << "'.\n"
645             << logofs_flush;
646     #endif
647 
648     return -1;
649   }
650 
651   struct T_transportData *transportData;
652   struct T_protocolData  *protocolData;
653   struct T_overallData   *overallData;
654 
655   if (type == PARTIAL_STATS)
656   {
657     transportData = &transportPartial_;
658     protocolData = &protocolPartial_;
659     overallData = &overallPartial_;
660   }
661   else
662   {
663     transportData = &transportTotal_;
664     protocolData = &protocolTotal_;
665     overallData = &overallTotal_;
666   }
667 
668   char format[FORMAT_LENGTH];
669 
670   double countRequestIn        = 0;
671   double countCachedRequestIn  = 0;
672   double countRepliedRequestIn = 0;
673 
674   double countRequestBitsIn  = 0;
675   double countRequestBitsOut = 0;
676 
677   double countAnyIn   = 0;
678   double countBitsIn  = 0;
679   double countBitsOut = 0;
680 
681   //
682   // Print request data.
683   //
684 
685   strcat(buffer, "NX Server Side Protocol Statistics\n");
686   strcat(buffer, "----------------------------------\n\n");
687 
688   //
689   // Print render data.
690   //
691 
692   strcat(buffer, "Render  Total\tCached\tBits In\t\tBits Out\tBits/Request\t\tRatio\n");
693   strcat(buffer, "------- -----\t------\t-------\t\t--------\t------------\t\t-----\n");
694 
695   for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
696   {
697     if (protocolData -> renderRequestCount_[i])
698     {
699       sprintf(format, "#%d ", i);
700 
701       while (strlen(format) < 8)
702       {
703         strcat(format, " ");
704       }
705 
706       strcat(buffer, format);
707 
708       if (protocolData -> renderRequestCached_[i] > 0)
709       {
710         sprintf(format, "%.0f\t%.0f", protocolData -> renderRequestCount_[i],
711                     protocolData -> renderRequestCached_[i]);
712       }
713       else
714       {
715         sprintf(format, "%.0f\t", protocolData -> renderRequestCount_[i]);
716       }
717 
718       strcat(buffer, format);
719 
720       sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1     \t",
721                   protocolData -> renderRequestBitsIn_[i], protocolData -> renderRequestBitsIn_[i] / 8192,
722                       protocolData -> renderRequestBitsOut_[i], protocolData -> renderRequestBitsOut_[i] / 8192,
723                           protocolData -> renderRequestBitsIn_[i] / protocolData -> renderRequestCount_[i],
724                               protocolData -> renderRequestBitsOut_[i] / protocolData -> renderRequestCount_[i]);
725 
726       strcat(buffer, format);
727 
728       if (protocolData -> renderRequestBitsOut_[i] > 0)
729       {
730         sprintf(format, "%5.3f:1\n", protocolData -> renderRequestBitsIn_[i] /
731                                          protocolData -> renderRequestBitsOut_[i]);
732 
733         strcat(buffer, format);
734       }
735       else
736       {
737         strcat(buffer, "1:1\n");
738       }
739     }
740 
741     countRequestIn        += protocolData -> renderRequestCount_[i];
742     countCachedRequestIn  += protocolData -> renderRequestCached_[i];
743 
744     countRequestBitsIn  += protocolData -> renderRequestBitsIn_[i];
745     countRequestBitsOut += protocolData -> renderRequestBitsOut_[i];
746 
747     countAnyIn   += protocolData -> renderRequestCount_[i];
748     countBitsIn  += protocolData -> renderRequestBitsIn_[i];
749     countBitsOut += protocolData -> renderRequestBitsOut_[i];
750   }
751 
752   if (countRequestIn > 0)
753   {
754     if (countCachedRequestIn > 0)
755     {
756       sprintf(format, "\ntotal:  %.0f\t%.0f", countRequestIn, countCachedRequestIn);
757     }
758     else
759     {
760       sprintf(format, "\ntotal:  %.0f\t", countRequestIn);
761     }
762 
763     strcat(buffer, format);
764 
765     sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1     \t",
766                 countRequestBitsIn, countRequestBitsIn / 8192, countRequestBitsOut,
767                     countRequestBitsOut / 8192, countRequestBitsIn / countRequestIn,
768                         countRequestBitsOut / countRequestIn);
769 
770     strcat(buffer, format);
771 
772     if (countRequestBitsOut > 0)
773     {
774       sprintf(format, "%5.3f:1\n", countRequestBitsIn / countRequestBitsOut);
775     }
776     else
777     {
778       sprintf(format, "1:1\n");
779     }
780 
781     strcat(buffer, format);
782   }
783   else
784   {
785     strcat(buffer, "N/A\n\n");
786   }
787 
788   countRequestIn        = 0;
789   countCachedRequestIn  = 0;
790 
791   countRequestBitsIn  = 0;
792   countRequestBitsOut = 0;
793 
794   countAnyIn   = 0;
795   countBitsIn  = 0;
796   countBitsOut = 0;
797 
798   //
799   // Print other requests' data.
800   //
801 
802   strcat(buffer, "\nRequest Total\tCached\tBits In\t\tBits Out\tBits/Request\t\tRatio\n");
803   strcat(buffer, "------- -----\t------\t-------\t\t--------\t------------\t\t-----\n");
804 
805   for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
806   {
807     if (protocolData -> requestCount_[i])
808     {
809       sprintf(format, "#%d ", i);
810 
811       while (strlen(format) < 5)
812       {
813         strcat(format, " ");
814       }
815 
816       //
817       // Mark NX agent-related requests, those
818       // having a reply and finally those that
819       // have been probably tainted by client
820       // side.
821       //
822 
823       if (i >= X_NXFirstOpcode && i <= X_NXLastOpcode)
824       {
825         strcat(format, "A");
826       }
827 
828       if (i != X_NXInternalGenericReply && protocolData -> requestReplied_[i] > 0)
829       {
830         strcat(format, "R");
831       }
832 
833       if (i == X_NoOperation && control -> TaintReplies)
834       {
835         strcat(format, "T");
836       }
837 
838       while (strlen(format) < 8)
839       {
840         strcat(format, " ");
841       }
842 
843       strcat(buffer, format);
844 
845       if (protocolData -> requestCached_[i] > 0)
846       {
847         sprintf(format, "%.0f\t%.0f", protocolData -> requestCount_[i],
848                     protocolData -> requestCached_[i]);
849       }
850       else
851       {
852         sprintf(format, "%.0f\t", protocolData -> requestCount_[i]);
853       }
854 
855       strcat(buffer, format);
856 
857       sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1     \t",
858                   protocolData -> requestBitsIn_[i], protocolData -> requestBitsIn_[i] / 8192,
859                       protocolData -> requestBitsOut_[i], protocolData -> requestBitsOut_[i] / 8192,
860                           protocolData -> requestBitsIn_[i] / protocolData -> requestCount_[i],
861                               protocolData -> requestBitsOut_[i] / protocolData -> requestCount_[i]);
862 
863       strcat(buffer, format);
864 
865       if (protocolData -> requestBitsOut_[i] > 0)
866       {
867         sprintf(format, "%5.3f:1\n", protocolData -> requestBitsIn_[i] /
868                                          protocolData -> requestBitsOut_[i]);
869 
870         strcat(buffer, format);
871       }
872       else
873       {
874         strcat(buffer, "1:1\n");
875       }
876     }
877 
878     countRequestIn        += protocolData -> requestCount_[i];
879     countCachedRequestIn  += protocolData -> requestCached_[i];
880     countRepliedRequestIn += protocolData -> requestReplied_[i];
881 
882     countRequestBitsIn  += protocolData -> requestBitsIn_[i];
883     countRequestBitsOut += protocolData -> requestBitsOut_[i];
884 
885     countAnyIn   += protocolData -> requestCount_[i];
886     countBitsIn  += protocolData -> requestBitsIn_[i];
887     countBitsOut += protocolData -> requestBitsOut_[i];
888   }
889 
890   if (countRequestIn > 0)
891   {
892     if (countCachedRequestIn > 0)
893     {
894       sprintf(format, "\ntotal:  %.0f\t%.0f", countRequestIn, countCachedRequestIn);
895     }
896     else
897     {
898       sprintf(format, "\ntotal:  %.0f\t", countRequestIn);
899     }
900 
901     strcat(buffer, format);
902 
903     sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1     \t",
904                 countRequestBitsIn, countRequestBitsIn / 8192, countRequestBitsOut,
905                     countRequestBitsOut / 8192, countRequestBitsIn / countRequestIn,
906                         countRequestBitsOut / countRequestIn);
907 
908     strcat(buffer, format);
909 
910     if (countRequestBitsOut > 0)
911     {
912       sprintf(format, "%5.3f:1\n", countRequestBitsIn / countRequestBitsOut);
913     }
914     else
915     {
916       sprintf(format, "1:1\n");
917     }
918 
919     strcat(buffer, format);
920   }
921   else
922   {
923     strcat(buffer, "N/A\n\n");
924   }
925 
926   //
927   // Print transport data.
928   //
929 
930   getTimeStats(type, buffer);
931 
932   countAnyIn   += protocolData -> cupsCount_;
933   countBitsIn  += protocolData -> cupsBitsIn_;
934   countBitsOut += protocolData -> cupsBitsOut_;
935 
936   countAnyIn   += protocolData -> smbCount_;
937   countBitsIn  += protocolData -> smbBitsIn_;
938   countBitsOut += protocolData -> smbBitsOut_;
939 
940   countAnyIn   += protocolData -> mediaCount_;
941   countBitsIn  += protocolData -> mediaBitsIn_;
942   countBitsOut += protocolData -> mediaBitsOut_;
943 
944   countAnyIn   += protocolData -> httpCount_;
945   countBitsIn  += protocolData -> httpBitsIn_;
946   countBitsOut += protocolData -> httpBitsOut_;
947 
948   countAnyIn   += protocolData -> fontCount_;
949   countBitsIn  += protocolData -> fontBitsIn_;
950   countBitsOut += protocolData -> fontBitsOut_;
951 
952   countAnyIn   += protocolData -> slaveCount_;
953   countBitsIn  += protocolData -> slaveBitsIn_;
954   countBitsOut += protocolData -> slaveBitsOut_;
955 
956   //
957   // Save the overall amount of bytes
958   // coming from X clients.
959   //
960 
961   overallData -> overallBytesIn_ = countBitsIn / 8;
962 
963   //
964   // Print performance data.
965   //
966 
967   if (transportData -> readTime_ > 0)
968   {
969     sprintf(format, "      %.0f messages (%.0f KB) encoded per second.\n\n",
970                 countAnyIn / (transportData -> readTime_ / 1000),
971                     (countBitsIn + transportData -> framingBitsOut_) / 8192 /
972                          (transportData -> readTime_ / 1000));
973   }
974   else
975   {
976     sprintf(format, "      %.0f messages (%.0f KB) encoded per second.\n\n",
977                 countAnyIn, (countBitsIn + transportData ->
978                     framingBitsOut_) / 8192);
979   }
980 
981   strcat(buffer, format);
982 
983   strcat(buffer, "link: ");
984 
985   //
986   // ZLIB compression stats.
987   //
988 
989   getStreamStats(type, buffer);
990 
991   //
992   // Save the overall amount of bytes
993   // sent on NX proxy link.
994   //
995 
996   if (transportData -> compressedBytesOut_ > 0)
997   {
998     overallData -> overallBytesOut_ = transportData -> compressedBytesOut_;
999   }
1000   else
1001   {
1002     overallData -> overallBytesOut_ = countBitsOut / 8;
1003   }
1004 
1005   //
1006   // Print info on multiplexing overhead.
1007   //
1008 
1009   getFramingStats(type, buffer);
1010 
1011   //
1012   // Print stats about additional channels.
1013   //
1014 
1015   getServicesStats(type, buffer);
1016 
1017   //
1018   // Compression summary.
1019   //
1020 
1021   double ratio = 1;
1022 
1023   if (transportData -> compressedBytesOut_ / 1024 > 0)
1024   {
1025     ratio = ((countBitsIn + transportData -> framingBitsOut_) / 8192) /
1026                   (transportData -> compressedBytesOut_ / 1024);
1027 
1028   }
1029   else if (countBitsOut > 0)
1030   {
1031     ratio = (countBitsIn + transportData -> framingBitsOut_) /
1032                  countBitsOut;
1033   }
1034 
1035   sprintf(format, "      Protocol compression ratio is %5.3f:1.\n\n",
1036               ratio);
1037 
1038   strcat(buffer, format);
1039 
1040   getBitrateStats(type, buffer);
1041 
1042   getSplitStats(type, buffer);
1043 
1044   sprintf(format, "      %.0f total handled replies (%.0f unmatched).\n\n\n",
1045               countRepliedRequestIn, protocolData -> requestReplied_[X_NXInternalGenericReply]);
1046 
1047   strcat(buffer, format);
1048 
1049   return 1;
1050 }
1051 
getClientOverallStats(int type,char * & buffer)1052 int Statistics::getClientOverallStats(int type, char *&buffer)
1053 {
1054   if (type != PARTIAL_STATS && type != TOTAL_STATS)
1055   {
1056     #ifdef PANIC
1057     *logofs << "Statistics: PANIC! Cannot produce statistics "
1058             << "with qualifier '" << type << "'.\n"
1059             << logofs_flush;
1060     #endif
1061 
1062     return -1;
1063   }
1064 
1065   struct T_overallData *overallData;
1066   struct T_packedData  *packedData;
1067 
1068   if (type == PARTIAL_STATS)
1069   {
1070     overallData = &overallPartial_;
1071     packedData  = &packedPartial_;
1072   }
1073   else
1074   {
1075     overallData = &overallTotal_;
1076     packedData = &packedTotal_;
1077   }
1078 
1079   char format[FORMAT_LENGTH];
1080 
1081   //
1082   // Print header including link type,
1083   // followed by info on packed images.
1084   //
1085 
1086   strcat(buffer, "NX Compression Summary\n");
1087   strcat(buffer, "----------------------\n\n");
1088 
1089   char label[FORMAT_LENGTH];
1090 
1091   switch (control -> LinkMode)
1092   {
1093     case LINK_TYPE_NONE:
1094     {
1095       strcpy(label, "NONE");
1096 
1097       break;
1098     }
1099     case LINK_TYPE_MODEM:
1100     {
1101       strcpy(label, "MODEM");
1102 
1103       break;
1104     }
1105     case LINK_TYPE_ISDN:
1106     {
1107       strcpy(label, "ISDN");
1108 
1109       break;
1110     }
1111     case LINK_TYPE_ADSL:
1112     {
1113       strcpy(label, "ADSL");
1114 
1115       break;
1116     }
1117     case LINK_TYPE_WAN:
1118     {
1119       strcpy(label, "WAN");
1120 
1121       break;
1122     }
1123     case LINK_TYPE_LAN:
1124     {
1125       strcpy(label, "LAN");
1126 
1127       break;
1128     }
1129     default:
1130     {
1131       strcpy(label, "Unknown");
1132 
1133       break;
1134     }
1135   }
1136 
1137   sprintf(format, "link:    %s", label);
1138 
1139   if (control -> LocalDeltaCompression == 1)
1140   {
1141     strcat(format, " with protocol compression enabled.");
1142   }
1143   else
1144   {
1145     strcat(format, " with protocol compression disabled.");
1146   }
1147 
1148   strcat(format, "\n\n");
1149 
1150   strcat(buffer, format);
1151 
1152   if (packedData -> packedBytesIn_ > 0)
1153   {
1154     sprintf(format, "images:  %.0f bytes (%.0f KB) packed to %.0f (%.0f KB).\n\n",
1155                 packedData -> packedBytesOut_, packedData -> packedBytesOut_ / 1024,
1156                     packedData -> packedBytesIn_, packedData -> packedBytesIn_ / 1024);
1157 
1158     strcat(buffer, format);
1159 
1160     sprintf(format, "         Images compression ratio is %5.3f:1.\n\n",
1161                 packedData -> packedBytesOut_ / packedData -> packedBytesIn_);
1162 
1163     strcat(buffer, format);
1164   }
1165 
1166   double overallIn = overallData -> overallBytesIn_ - packedData -> packedBytesIn_ +
1167                          packedData -> packedBytesOut_;
1168 
1169   double overallOut = overallData -> overallBytesOut_;
1170 
1171   sprintf(format, "overall: %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
1172               overallIn, overallIn / 1024, overallOut, overallOut / 1024);
1173 
1174   strcat(buffer, format);
1175 
1176   if (overallData -> overallBytesOut_ > 0)
1177   {
1178     sprintf(format, "         Overall NX server compression ratio is %5.3f:1.\n\n\n",
1179                 overallIn / overallOut);
1180   }
1181   else
1182   {
1183     sprintf(format, "         Overall NX server compression ratio is 1:1.\n\n\n");
1184   }
1185 
1186   strcat(buffer, format);
1187 
1188   return 1;
1189 }
1190 
getServerCacheStats(int type,char * & buffer)1191 int Statistics::getServerCacheStats(int type, char *&buffer)
1192 {
1193   if (type != PARTIAL_STATS && type != TOTAL_STATS)
1194   {
1195     #ifdef PANIC
1196     *logofs << "Statistics: PANIC! Cannot produce statistics "
1197             << "with qualifier '" << type << "'.\n"
1198             << logofs_flush;
1199     #endif
1200 
1201     return -1;
1202   }
1203 
1204   //
1205   // Print message cache data according
1206   // to local and remote view.
1207   //
1208 
1209   MessageStore *currentStore = NULL;
1210   MessageStore *anyStore     = NULL;
1211 
1212   char format[FORMAT_LENGTH];
1213 
1214   strcat(buffer, "\nNX Cache Statistics\n");
1215   strcat(buffer, "-------------------\n\n");
1216 
1217   for (int m = proxy_client; m <= proxy_server; m++)
1218   {
1219     if (m == proxy_client)
1220     {
1221       strcat(buffer, "Request\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
1222       strcat(buffer, "-------\t------\t--------------\t\t--------------\t\t-----------\n");
1223     }
1224     else
1225     {
1226       strcat(buffer, "\nReply\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
1227       strcat(buffer, "-----\t------\t--------------\t\t--------------\t\t-----------\n");
1228     }
1229 
1230     for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
1231     {
1232       if (m == proxy_client)
1233       {
1234         currentStore = proxy_ -> getClientStore() -> getRequestStore(i);
1235       }
1236       else
1237       {
1238         currentStore = proxy_ -> getServerStore() -> getReplyStore(i);
1239       }
1240 
1241       if (currentStore != NULL &&
1242               (currentStore -> getLocalStorageSize() ||
1243                    currentStore -> getRemoteStorageSize()))
1244       {
1245         anyStore = currentStore;
1246 
1247         sprintf(format, "#%d\t%d\t", i, currentStore -> getSize());
1248 
1249         strcat(buffer, format);
1250 
1251         sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getRemoteStorageSize(),
1252                     ((double) currentStore -> getRemoteStorageSize()) / 1024);
1253 
1254         strcat(buffer, format);
1255 
1256         sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getLocalStorageSize(),
1257                     ((double) currentStore -> getLocalStorageSize()) / 1024);
1258 
1259         strcat(buffer, format);
1260 
1261         sprintf(format, "%d/%.0f KB\n", currentStore -> cacheSlots,
1262                     ((double) control -> getUpperStorageSize() / 100 *
1263                           currentStore -> cacheThreshold) / 1024);
1264 
1265         strcat(buffer, format);
1266       }
1267     }
1268 
1269     if (anyStore == NULL)
1270     {
1271       strcat(buffer, "N/A\n");
1272     }
1273   }
1274 
1275   if (anyStore != NULL)
1276   {
1277     sprintf(format, "\ncache: %d bytes (%d KB) available at server.\n",
1278                 control -> ClientTotalStorageSize,
1279                     control -> ClientTotalStorageSize / 1024);
1280 
1281     strcat(buffer, format);
1282 
1283     sprintf(format, "       %d bytes (%d KB) available at client.\n\n",
1284                 control -> ServerTotalStorageSize,
1285                     control -> ServerTotalStorageSize / 1024);
1286 
1287     strcat(buffer, format);
1288 
1289     sprintf(format, "       %d bytes (%d KB) allocated at server.\n",
1290                 anyStore -> getRemoteTotalStorageSize(),
1291                     anyStore -> getRemoteTotalStorageSize() / 1024);
1292 
1293     strcat(buffer, format);
1294 
1295     sprintf(format, "       %d bytes (%d KB) allocated at client.\n\n\n",
1296                 anyStore -> getLocalTotalStorageSize(),
1297                     anyStore -> getLocalTotalStorageSize() / 1024);
1298 
1299     strcat(buffer, format);
1300   }
1301   else
1302   {
1303     strcat(buffer, "\ncache: N/A\n\n");
1304   }
1305 
1306   return 1;
1307 }
1308 
getServerProtocolStats(int type,char * & buffer)1309 int Statistics::getServerProtocolStats(int type, char *&buffer)
1310 {
1311   if (type != PARTIAL_STATS && type != TOTAL_STATS)
1312   {
1313     #ifdef PANIC
1314     *logofs << "Statistics: PANIC! Cannot produce statistics "
1315             << "with qualifier '" << type << "'.\n"
1316             << logofs_flush;
1317     #endif
1318 
1319     return -1;
1320   }
1321 
1322   struct T_transportData *transportData;
1323   struct T_protocolData  *protocolData;
1324   struct T_overallData   *overallData;
1325 
1326   if (type == PARTIAL_STATS)
1327   {
1328     transportData = &transportPartial_;
1329     protocolData = &protocolPartial_;
1330     overallData = &overallPartial_;
1331   }
1332   else
1333   {
1334     transportData = &transportTotal_;
1335     protocolData = &protocolTotal_;
1336     overallData = &overallTotal_;
1337   }
1338 
1339   char format[FORMAT_LENGTH];
1340 
1341   double countReplyBitsIn  = 0;
1342   double countReplyBitsOut = 0;
1343 
1344   double countReplyIn        = 0;
1345   double countCachedReplyIn  = 0;
1346 
1347   double countEventBitsIn  = 0;
1348   double countEventBitsOut = 0;
1349 
1350   double countEventIn        = 0;
1351   double countCachedEventIn  = 0;
1352 
1353   double countAnyIn   = 0;
1354   double countBitsIn  = 0;
1355   double countBitsOut = 0;
1356 
1357   //
1358   // Print reply data.
1359   //
1360 
1361   strcat(buffer, "NX Client Side Protocol Statistics\n");
1362   strcat(buffer, "----------------------------------\n\n");
1363 
1364   strcat(buffer, "Reply   Total\tCached\tBits In\t\tBits Out\tBits/Reply\t\tRatio\n");
1365   strcat(buffer, "------- -----\t------\t-------\t\t--------\t----------\t\t-----\n");
1366 
1367   for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
1368   {
1369     if (protocolData -> replyCount_[i])
1370     {
1371       sprintf(format, "#%d ", i);
1372 
1373       while (strlen(format) < 5)
1374       {
1375         strcat(format, " ");
1376       }
1377 
1378       //
1379       // Mark replies originated
1380       // by NX agent requests.
1381       //
1382 
1383       if (i >= X_NXFirstOpcode && i <= X_NXLastOpcode)
1384       {
1385         strcat(format, "A");
1386       }
1387 
1388       //
1389       // Mark replies that we didn't
1390       // match against a request.
1391       //
1392 
1393       if (i == 1)
1394       {
1395         strcat(format, "U");
1396       }
1397 
1398       while (strlen(format) < 8)
1399       {
1400         strcat(format, " ");
1401       }
1402 
1403       strcat(buffer, format);
1404 
1405       if (protocolData -> replyCached_[i] > 0)
1406       {
1407         sprintf(format, "%.0f\t%.0f", protocolData -> replyCount_[i],
1408                     protocolData -> replyCached_[i]);
1409       }
1410       else
1411       {
1412         sprintf(format, "%.0f\t", protocolData -> replyCount_[i]);
1413       }
1414 
1415       strcat(buffer, format);
1416 
1417       sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1     \t",
1418                   protocolData -> replyBitsIn_[i], protocolData -> replyBitsIn_[i] / 8192,
1419                       protocolData -> replyBitsOut_[i], protocolData -> replyBitsOut_[i] / 8192,
1420                           protocolData -> replyBitsIn_[i] / protocolData -> replyCount_[i],
1421                               protocolData -> replyBitsOut_[i] / protocolData -> replyCount_[i]);
1422 
1423       strcat(buffer, format);
1424 
1425       if (protocolData -> replyBitsOut_[i] > 0)
1426       {
1427         sprintf(format, "%5.3f:1\n", protocolData -> replyBitsIn_[i] /
1428                                          protocolData -> replyBitsOut_[i]);
1429       }
1430       else
1431       {
1432         sprintf(format, "1:1\n");
1433       }
1434 
1435       strcat(buffer, format);
1436     }
1437 
1438     countReplyIn       += protocolData -> replyCount_[i];
1439     countCachedReplyIn += protocolData -> replyCached_[i];
1440 
1441     countReplyBitsIn  += protocolData -> replyBitsIn_[i];
1442     countReplyBitsOut += protocolData -> replyBitsOut_[i];
1443 
1444     countAnyIn   += protocolData -> replyCount_[i];
1445     countBitsIn  += protocolData -> replyBitsIn_[i];
1446     countBitsOut += protocolData -> replyBitsOut_[i];
1447   }
1448 
1449   if (countReplyIn > 0)
1450   {
1451     if (countCachedReplyIn > 0)
1452     {
1453       sprintf(format, "\ntotal:  %.0f\t%.0f", countReplyIn, countCachedReplyIn);
1454     }
1455     else
1456     {
1457       sprintf(format, "\ntotal:  %.0f\t", countReplyIn);
1458     }
1459 
1460     strcat(buffer, format);
1461 
1462     sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1     \t",
1463                 countReplyBitsIn, countReplyBitsIn / 8192, countReplyBitsOut,
1464                     countReplyBitsOut / 8192, countReplyBitsIn / countReplyIn,
1465                         countReplyBitsOut / countReplyIn);
1466 
1467     strcat(buffer, format);
1468 
1469     if (countReplyBitsOut > 0)
1470     {
1471       sprintf(format, "%5.3f:1\n", countReplyBitsIn / countReplyBitsOut);
1472     }
1473     else
1474     {
1475       sprintf(format, "1:1\n");
1476     }
1477 
1478     strcat(buffer, format);
1479   }
1480   else
1481   {
1482     strcat(buffer, "N/A\n");
1483   }
1484 
1485   strcat(buffer, "\n");
1486 
1487   //
1488   // Print event and error data.
1489   //
1490 
1491   strcat(buffer, "Event   Total\tCached\tBits In\t\tBits Out\tBits/Event\t\tRatio\n");
1492   strcat(buffer, "------- -----\t------\t-------\t\t--------\t----------\t\t-----\n");
1493 
1494   for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
1495   {
1496     if (protocolData -> eventCount_[i])
1497     {
1498       sprintf(format, "#%d ", i);
1499 
1500       while (strlen(format) < 8)
1501       {
1502         strcat(format, " ");
1503       }
1504 
1505       strcat(buffer, format);
1506 
1507       if (protocolData -> eventCached_[i] > 0)
1508       {
1509         sprintf(format, "%.0f\t%.0f", protocolData -> eventCount_[i],
1510                     protocolData -> eventCached_[i]);
1511       }
1512       else
1513       {
1514         sprintf(format, "%.0f\t", protocolData -> eventCount_[i]);
1515       }
1516 
1517       strcat(buffer, format);
1518 
1519       sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1     \t",
1520                   protocolData -> eventBitsIn_[i], protocolData -> eventBitsIn_[i] / 8192,
1521                       protocolData -> eventBitsOut_[i], protocolData -> eventBitsOut_[i] / 8192,
1522                           protocolData -> eventBitsIn_[i] / protocolData -> eventCount_[i],
1523                               protocolData -> eventBitsOut_[i] / protocolData -> eventCount_[i]);
1524 
1525       strcat(buffer, format);
1526 
1527       if (protocolData -> eventBitsOut_[i] > 0)
1528       {
1529         sprintf(format, "%5.3f:1\n", protocolData -> eventBitsIn_[i] /
1530                                          protocolData -> eventBitsOut_[i]);
1531       }
1532       else
1533       {
1534         sprintf(format, "1:1\n");
1535       }
1536 
1537       strcat(buffer, format);
1538     }
1539 
1540     countEventIn       += protocolData -> eventCount_[i];
1541     countCachedEventIn += protocolData -> eventCached_[i];
1542 
1543     countEventBitsIn  += protocolData -> eventBitsIn_[i];
1544     countEventBitsOut += protocolData -> eventBitsOut_[i];
1545 
1546     countAnyIn   += protocolData -> eventCount_[i];
1547     countBitsIn  += protocolData -> eventBitsIn_[i];
1548     countBitsOut += protocolData -> eventBitsOut_[i];
1549   }
1550 
1551   if (countEventIn > 0)
1552   {
1553     if (countCachedEventIn > 0)
1554     {
1555       sprintf(format, "\ntotal:  %.0f\t%.0f", countEventIn, countCachedEventIn);
1556     }
1557     else
1558     {
1559       sprintf(format, "\ntotal:  %.0f\t", countEventIn);
1560     }
1561 
1562     strcat(buffer, format);
1563 
1564     sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1     \t",
1565                 countEventBitsIn, countEventBitsIn / 8192, countEventBitsOut,
1566                     countEventBitsOut / 8192, countEventBitsIn / countEventIn,
1567                         countEventBitsOut / countEventIn);
1568 
1569     strcat(buffer, format);
1570 
1571     if (countEventBitsOut > 0)
1572     {
1573       sprintf(format, "%5.3f:1\n", countEventBitsIn / countEventBitsOut);
1574     }
1575     else
1576     {
1577       sprintf(format, "1:1\n");
1578     }
1579 
1580     strcat(buffer, format);
1581   }
1582   else
1583   {
1584     strcat(buffer, "N/A\n\n");
1585   }
1586 
1587   //
1588   // Print transport data.
1589   //
1590 
1591   getTimeStats(type, buffer);
1592 
1593   countAnyIn   += protocolData -> cupsCount_;
1594   countBitsIn  += protocolData -> cupsBitsIn_;
1595   countBitsOut += protocolData -> cupsBitsOut_;
1596 
1597   countAnyIn   += protocolData -> smbCount_;
1598   countBitsIn  += protocolData -> smbBitsIn_;
1599   countBitsOut += protocolData -> smbBitsOut_;
1600 
1601   countAnyIn   += protocolData -> mediaCount_;
1602   countBitsIn  += protocolData -> mediaBitsIn_;
1603   countBitsOut += protocolData -> mediaBitsOut_;
1604 
1605   countAnyIn   += protocolData -> httpCount_;
1606   countBitsIn  += protocolData -> httpBitsIn_;
1607   countBitsOut += protocolData -> httpBitsOut_;
1608 
1609   countAnyIn   += protocolData -> fontCount_;
1610   countBitsIn  += protocolData -> fontBitsIn_;
1611   countBitsOut += protocolData -> fontBitsOut_;
1612 
1613   countAnyIn   += protocolData -> slaveCount_;
1614   countBitsIn  += protocolData -> slaveBitsIn_;
1615   countBitsOut += protocolData -> slaveBitsOut_;
1616 
1617   //
1618   // Save the overall amount of bytes
1619   // coming from X clients.
1620   //
1621 
1622   overallData -> overallBytesIn_ = countBitsIn / 8;
1623 
1624   //
1625   // Print performance data.
1626   //
1627 
1628   if (transportData -> readTime_ > 0)
1629   {
1630     sprintf(format, "      %.0f messages (%.0f KB) encoded per second.\n\n",
1631                 countAnyIn / (transportData -> readTime_ / 1000),
1632                     (countBitsIn + transportData -> framingBitsOut_) / 8192 /
1633                          (transportData -> readTime_ / 1000));
1634   }
1635   else
1636   {
1637     sprintf(format, "      %.0f messages (%.0f KB) encoded per second.\n\n",
1638                 countAnyIn, (countBitsIn + transportData ->
1639                     framingBitsOut_) / 8192);
1640   }
1641 
1642   strcat(buffer, format);
1643 
1644   strcat(buffer, "link: ");
1645 
1646   //
1647   // ZLIB compression stats.
1648   //
1649 
1650   getStreamStats(type, buffer);
1651 
1652   //
1653   // Save the overall amount of bytes
1654   // sent on NX proxy link.
1655   //
1656 
1657   if (transportData -> compressedBytesOut_ > 0)
1658   {
1659     overallData -> overallBytesOut_ = transportData -> compressedBytesOut_;
1660   }
1661   else
1662   {
1663     overallData -> overallBytesOut_ = countBitsOut / 8;
1664   }
1665 
1666   //
1667   // Print info on multiplexing overhead.
1668   //
1669 
1670   getFramingStats(type, buffer);
1671 
1672   //
1673   // Print stats about additional channels.
1674   //
1675 
1676   getServicesStats(type, buffer);
1677 
1678   //
1679   // Compression summary.
1680   //
1681 
1682   double ratio = 1;
1683 
1684   if (transportData -> compressedBytesOut_ / 1024 > 0)
1685   {
1686     ratio = ((countBitsIn + transportData -> framingBitsOut_) / 8192) /
1687                   (transportData -> compressedBytesOut_ / 1024);
1688 
1689   }
1690   else if (countBitsOut > 0)
1691   {
1692     ratio = (countBitsIn + transportData -> framingBitsOut_) /
1693                  countBitsOut;
1694   }
1695 
1696   sprintf(format, "      Protocol compression ratio is %5.3f:1.\n\n",
1697               ratio);
1698 
1699   strcat(buffer, format);
1700 
1701   getBitrateStats(type, buffer);
1702 
1703   //
1704   // These are not included in output.
1705   //
1706   // getSplitStats(type, buffer);
1707   //
1708 
1709   strcat(buffer, "\n");
1710 
1711   //
1712   // These statistics are not included in output.
1713   // You can check it anyway to get the effective
1714   // amount of bytes produced by unpack procedure.
1715   //
1716   // getClientOverallStats(type, buffer);
1717   //
1718 
1719   return 1;
1720 }
1721 
getServerOverallStats(int type,char * & buffer)1722 int Statistics::getServerOverallStats(int type, char *&buffer)
1723 {
1724   return 1;
1725 }
1726 
getTimeStats(int type,char * & buffer)1727 int Statistics::getTimeStats(int type, char *&buffer)
1728 {
1729   struct T_transportData *transportData;
1730 
1731   if (type == PARTIAL_STATS)
1732   {
1733     transportData = &transportPartial_;
1734   }
1735   else
1736   {
1737     transportData = &transportTotal_;
1738   }
1739 
1740   char format[FORMAT_LENGTH];
1741 
1742   sprintf(format, "\ntime: %.0f Ms idle, %.0f Ms (%.0f Ms in read, %.0f Ms in write) running.\n\n",
1743               transportData -> idleTime_, transportData -> readTime_,
1744                   transportData -> readTime_ - transportData -> writeTime_,
1745                       transportData -> writeTime_);
1746 
1747   strcat(buffer, format);
1748 
1749   return 1;
1750 }
1751 
getStreamStats(int type,char * & buffer)1752 int Statistics::getStreamStats(int type, char *&buffer)
1753 {
1754   struct T_transportData *transportData;
1755 
1756   if (type == PARTIAL_STATS)
1757   {
1758     transportData = &transportPartial_;
1759   }
1760   else
1761   {
1762     transportData = &transportTotal_;
1763   }
1764 
1765   char format[FORMAT_LENGTH];
1766 
1767   if (transportData -> compressedBytesOut_ > 0)
1768   {
1769     sprintf(format, "%.0f bytes (%.0f KB) compressed to %.0f (%.0f KB).\n",
1770                 transportData -> compressedBytesIn_, transportData -> compressedBytesIn_ / 1024,
1771                     transportData -> compressedBytesOut_, transportData -> compressedBytesOut_ / 1024);
1772 
1773     strcat(buffer, format);
1774 
1775     sprintf(format, "      %5.3f:1 stream compression ratio.\n\n",
1776                 transportData -> compressedBytesIn_ / transportData -> compressedBytesOut_);
1777 
1778     strcat(buffer, format);
1779   }
1780 
1781   if (transportData -> decompressedBytesOut_ > 0)
1782   {
1783     if (transportData -> compressedBytesOut_ > 0)
1784     {
1785       strcat(buffer, "      ");
1786     }
1787 
1788     sprintf(format, "%.0f bytes (%.0f KB) decompressed to %.0f (%.0f KB).\n",
1789                 transportData -> decompressedBytesIn_, transportData -> decompressedBytesIn_ / 1024,
1790                     transportData -> decompressedBytesOut_, transportData -> decompressedBytesOut_ / 1024);
1791 
1792     strcat(buffer, format);
1793 
1794     sprintf(format, "      %5.3f:1 stream compression ratio.\n\n",
1795                 transportData -> decompressedBytesOut_ / transportData -> decompressedBytesIn_);
1796 
1797     strcat(buffer, format);
1798   }
1799 
1800   if (transportData -> compressedBytesOut_ > 0 ||
1801           transportData -> decompressedBytesOut_ > 0)
1802   {
1803     strcat(buffer, "      ");
1804   }
1805 
1806   return 1;
1807 }
1808 
getServicesStats(int type,char * & buffer)1809 int Statistics::getServicesStats(int type, char *&buffer)
1810 {
1811   struct T_protocolData *protocolData;
1812 
1813   if (type == PARTIAL_STATS)
1814   {
1815     protocolData = &protocolPartial_;
1816   }
1817   else
1818   {
1819     protocolData = &protocolTotal_;
1820   }
1821 
1822   char format[FORMAT_LENGTH];
1823 
1824   if (protocolData -> cupsBitsOut_ > 0)
1825   {
1826     sprintf(format, "      %.0f CUPS messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
1827                 protocolData -> cupsCount_ , protocolData -> cupsBitsIn_ / 8,
1828                     protocolData -> cupsBitsIn_ / 8192, protocolData -> cupsBitsOut_ / 8,
1829                         protocolData -> cupsBitsOut_ / 8192);
1830 
1831     strcat(buffer, format);
1832   }
1833 
1834   if (protocolData -> smbBitsOut_ > 0)
1835   {
1836     sprintf(format, "      %.0f SMB messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
1837                 protocolData -> smbCount_ , protocolData -> smbBitsIn_ / 8,
1838                     protocolData -> smbBitsIn_ / 8192, protocolData -> smbBitsOut_ / 8,
1839                         protocolData -> smbBitsOut_ / 8192);
1840 
1841     strcat(buffer, format);
1842   }
1843 
1844   if (protocolData -> mediaBitsOut_ > 0)
1845   {
1846     sprintf(format, "      %.0f multimedia messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
1847                 protocolData -> mediaCount_ , protocolData -> mediaBitsIn_ / 8,
1848                     protocolData -> mediaBitsIn_ / 8192, protocolData -> mediaBitsOut_ / 8,
1849                         protocolData -> mediaBitsOut_ / 8192);
1850 
1851     strcat(buffer, format);
1852   }
1853 
1854   if (protocolData -> httpBitsOut_ > 0)
1855   {
1856     sprintf(format, "      %.0f HTTP messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
1857                 protocolData -> httpCount_ , protocolData -> httpBitsIn_ / 8,
1858                     protocolData -> httpBitsIn_ / 8192, protocolData -> httpBitsOut_ / 8,
1859                         protocolData -> httpBitsOut_ / 8192);
1860 
1861     strcat(buffer, format);
1862   }
1863 
1864   if (protocolData -> fontBitsOut_ > 0)
1865   {
1866     sprintf(format, "      %.0f font server messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
1867                 protocolData -> fontCount_ , protocolData -> fontBitsIn_ / 8,
1868                     protocolData -> fontBitsIn_ / 8192, protocolData -> fontBitsOut_ / 8,
1869                         protocolData -> fontBitsOut_ / 8192);
1870 
1871     strcat(buffer, format);
1872   }
1873 
1874   if (protocolData -> slaveBitsOut_ > 0)
1875   {
1876     sprintf(format, "      %.0f slave messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
1877                 protocolData -> slaveCount_ , protocolData -> slaveBitsIn_ / 8,
1878                     protocolData -> slaveBitsIn_ / 8192, protocolData -> slaveBitsOut_ / 8,
1879                         protocolData -> slaveBitsOut_ / 8192);
1880 
1881     strcat(buffer, format);
1882   }
1883 
1884   return 1;
1885 }
1886 
getFramingStats(int type,char * & buffer)1887 int Statistics::getFramingStats(int type, char *&buffer)
1888 {
1889   struct T_transportData *transportData;
1890 
1891   if (type == PARTIAL_STATS)
1892   {
1893     transportData = &transportPartial_;
1894   }
1895   else
1896   {
1897     transportData = &transportTotal_;
1898   }
1899 
1900   char format[FORMAT_LENGTH];
1901 
1902   //
1903   // Print info on multiplexing overhead.
1904   //
1905 
1906   sprintf(format, "%.0f frames in, %.0f frames out, %.0f writes out.\n\n",
1907               transportData -> proxyFramesIn_, transportData -> proxyFramesOut_,
1908                   transportData -> proxyWritesOut_);
1909 
1910   strcat(buffer, format);
1911 
1912   sprintf(format, "      %.0f bytes (%.0f KB) used for framing and multiplexing.\n\n",
1913               transportData -> framingBitsOut_ / 8, transportData -> framingBitsOut_ / 8192);
1914 
1915   strcat(buffer, format);
1916 
1917   return 1;
1918 }
1919 
getBitrateStats(int type,char * & buffer)1920 int Statistics::getBitrateStats(int type, char *&buffer)
1921 {
1922   struct T_transportData *transportData;
1923   struct T_overallData   *overallData;
1924 
1925   if (type == PARTIAL_STATS)
1926   {
1927     transportData = &transportPartial_;
1928     overallData = &overallPartial_;
1929   }
1930   else
1931   {
1932     transportData = &transportTotal_;
1933     overallData = &overallTotal_;
1934   }
1935 
1936   double total = 0;
1937 
1938   if (transportData -> idleTime_ + transportData -> readTime_ > 0)
1939   {
1940     total = overallData -> overallBytesOut_ /
1941                 ((transportData -> idleTime_ + transportData -> readTime_) / 1000);
1942   }
1943 
1944   char format[FORMAT_LENGTH];
1945 
1946   sprintf(format, "      %.0f B/s average, %d B/s %ds, %d B/s %ds, %d B/s maximum.\n\n",
1947               total, getBitrateInShortFrame(), control -> ShortBitrateTimeFrame / 1000,
1948                   getBitrateInLongFrame(), control -> LongBitrateTimeFrame / 1000,
1949                       getTopBitrate());
1950 
1951   strcat(buffer, format);
1952 
1953   resetTopBitrate();
1954 
1955   return 1;
1956 }
1957 
getSplitStats(int type,char * & buffer)1958 int Statistics::getSplitStats(int type, char *&buffer)
1959 {
1960   //
1961   // Don't print these statistics if persistent
1962   // cache of images is disabled.
1963   //
1964 
1965   if (control -> ImageCacheEnableLoad == 0 &&
1966           control -> ImageCacheEnableSave == 0)
1967   {
1968     return 0;
1969   }
1970 
1971   struct T_splitData *splitData;
1972 
1973   if (type == PARTIAL_STATS)
1974   {
1975     splitData = &splitPartial_;
1976   }
1977   else
1978   {
1979     splitData = &splitTotal_;
1980   }
1981 
1982   char format[FORMAT_LENGTH];
1983 
1984   //
1985   // Print info on split messages restored from disk.
1986   //
1987 
1988   sprintf(format, "      %.0f images streamed, %.0f restored, %.0f bytes (%.0f KB) cached.\n\n",
1989               splitData -> splitCount_, splitData -> splitAborted_, splitData -> splitAbortedBytesOut_,
1990                   splitData -> splitAbortedBytesOut_ / 1024);
1991 
1992   strcat(buffer, format);
1993 
1994   return 1;
1995 }
1996