480 lines
17 KiB
C
480 lines
17 KiB
C
/*
|
|
* stats.c
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2002 RealVNC Ltd.
|
|
* OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
|
|
* Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
|
|
* All Rights Reserved.
|
|
*
|
|
* This is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This software is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this software; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
* USA.
|
|
*/
|
|
|
|
#include <rfb/rfb.h>
|
|
|
|
char *messageNameServer2Client(uint32_t type, char *buf, int len);
|
|
char *messageNameClient2Server(uint32_t type, char *buf, int len);
|
|
char *encodingName(uint32_t enc, char *buf, int len);
|
|
|
|
rfbStatList *rfbStatLookupEncoding(rfbClientPtr cl, uint32_t type);
|
|
rfbStatList *rfbStatLookupMessage(rfbClientPtr cl, uint32_t type);
|
|
|
|
void rfbStatRecordEncodingSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw);
|
|
void rfbStatRecordEncodingRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw);
|
|
void rfbStatRecordMessageSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw);
|
|
void rfbStatRecordMessageRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw);
|
|
void rfbResetStats(rfbClientPtr cl);
|
|
void rfbPrintStats(rfbClientPtr cl);
|
|
|
|
|
|
|
|
|
|
char *messageNameServer2Client(uint32_t type, char *buf, int len) {
|
|
if (buf==NULL) return "error";
|
|
switch (type) {
|
|
case rfbFramebufferUpdate: snprintf(buf, len, "FramebufferUpdate"); break;
|
|
case rfbSetColourMapEntries: snprintf(buf, len, "SetColourMapEntries"); break;
|
|
case rfbBell: snprintf(buf, len, "Bell"); break;
|
|
case rfbServerCutText: snprintf(buf, len, "ServerCutText"); break;
|
|
case rfbResizeFrameBuffer: snprintf(buf, len, "ResizeFrameBuffer"); break;
|
|
case rfbFileTransfer: snprintf(buf, len, "FileTransfer"); break;
|
|
case rfbTextChat: snprintf(buf, len, "TextChat"); break;
|
|
case rfbPalmVNCReSizeFrameBuffer: snprintf(buf, len, "PalmVNCReSize"); break;
|
|
case rfbXvp: snprintf(buf, len, "XvpServerMessage"); break;
|
|
default:
|
|
snprintf(buf, len, "svr2cli-0x%08X", 0xFF);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
char *messageNameClient2Server(uint32_t type, char *buf, int len) {
|
|
if (buf==NULL) return "error";
|
|
switch (type) {
|
|
case rfbSetPixelFormat: snprintf(buf, len, "SetPixelFormat"); break;
|
|
case rfbFixColourMapEntries: snprintf(buf, len, "FixColourMapEntries"); break;
|
|
case rfbSetEncodings: snprintf(buf, len, "SetEncodings"); break;
|
|
case rfbFramebufferUpdateRequest: snprintf(buf, len, "FramebufferUpdate"); break;
|
|
case rfbKeyEvent: snprintf(buf, len, "KeyEvent"); break;
|
|
case rfbPointerEvent: snprintf(buf, len, "PointerEvent"); break;
|
|
case rfbClientCutText: snprintf(buf, len, "ClientCutText"); break;
|
|
case rfbFileTransfer: snprintf(buf, len, "FileTransfer"); break;
|
|
case rfbSetScale: snprintf(buf, len, "SetScale"); break;
|
|
case rfbSetServerInput: snprintf(buf, len, "SetServerInput"); break;
|
|
case rfbSetSW: snprintf(buf, len, "SetSingleWindow"); break;
|
|
case rfbTextChat: snprintf(buf, len, "TextChat"); break;
|
|
case rfbPalmVNCSetScaleFactor: snprintf(buf, len, "PalmVNCSetScale"); break;
|
|
case rfbXvp: snprintf(buf, len, "XvpClientMessage"); break;
|
|
default:
|
|
snprintf(buf, len, "cli2svr-0x%08X", type);
|
|
|
|
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/* Encoding name must be <=16 characters to fit nicely on the status output in
|
|
* an 80 column terminal window
|
|
*/
|
|
char *encodingName(uint32_t type, char *buf, int len) {
|
|
if (buf==NULL) return "error";
|
|
|
|
switch (type) {
|
|
case rfbEncodingRaw: snprintf(buf, len, "raw"); break;
|
|
case rfbEncodingCopyRect: snprintf(buf, len, "copyRect"); break;
|
|
case rfbEncodingRRE: snprintf(buf, len, "RRE"); break;
|
|
case rfbEncodingCoRRE: snprintf(buf, len, "CoRRE"); break;
|
|
case rfbEncodingHextile: snprintf(buf, len, "hextile"); break;
|
|
case rfbEncodingZlib: snprintf(buf, len, "zlib"); break;
|
|
case rfbEncodingTight: snprintf(buf, len, "tight"); break;
|
|
case rfbEncodingTightPng: snprintf(buf, len, "tightPng"); break;
|
|
case rfbEncodingZlibHex: snprintf(buf, len, "zlibhex"); break;
|
|
case rfbEncodingUltra: snprintf(buf, len, "ultra"); break;
|
|
case rfbEncodingZRLE: snprintf(buf, len, "ZRLE"); break;
|
|
case rfbEncodingZYWRLE: snprintf(buf, len, "ZYWRLE"); break;
|
|
case rfbEncodingCache: snprintf(buf, len, "cache"); break;
|
|
case rfbEncodingCacheEnable: snprintf(buf, len, "cacheEnable"); break;
|
|
case rfbEncodingXOR_Zlib: snprintf(buf, len, "xorZlib"); break;
|
|
case rfbEncodingXORMonoColor_Zlib: snprintf(buf, len, "xorMonoZlib"); break;
|
|
case rfbEncodingXORMultiColor_Zlib: snprintf(buf, len, "xorColorZlib"); break;
|
|
case rfbEncodingSolidColor: snprintf(buf, len, "solidColor"); break;
|
|
case rfbEncodingXOREnable: snprintf(buf, len, "xorEnable"); break;
|
|
case rfbEncodingCacheZip: snprintf(buf, len, "cacheZip"); break;
|
|
case rfbEncodingSolMonoZip: snprintf(buf, len, "monoZip"); break;
|
|
case rfbEncodingUltraZip: snprintf(buf, len, "ultraZip"); break;
|
|
|
|
case rfbEncodingXCursor: snprintf(buf, len, "Xcursor"); break;
|
|
case rfbEncodingRichCursor: snprintf(buf, len, "RichCursor"); break;
|
|
case rfbEncodingPointerPos: snprintf(buf, len, "PointerPos"); break;
|
|
|
|
case rfbEncodingLastRect: snprintf(buf, len, "LastRect"); break;
|
|
case rfbEncodingNewFBSize: snprintf(buf, len, "NewFBSize"); break;
|
|
case rfbEncodingKeyboardLedState: snprintf(buf, len, "LedState"); break;
|
|
case rfbEncodingSupportedMessages: snprintf(buf, len, "SupportedMessage"); break;
|
|
case rfbEncodingSupportedEncodings: snprintf(buf, len, "SupportedEncoding"); break;
|
|
case rfbEncodingServerIdentity: snprintf(buf, len, "ServerIdentify"); break;
|
|
|
|
/* The following lookups do not report in stats */
|
|
case rfbEncodingCompressLevel0: snprintf(buf, len, "CompressLevel0"); break;
|
|
case rfbEncodingCompressLevel1: snprintf(buf, len, "CompressLevel1"); break;
|
|
case rfbEncodingCompressLevel2: snprintf(buf, len, "CompressLevel2"); break;
|
|
case rfbEncodingCompressLevel3: snprintf(buf, len, "CompressLevel3"); break;
|
|
case rfbEncodingCompressLevel4: snprintf(buf, len, "CompressLevel4"); break;
|
|
case rfbEncodingCompressLevel5: snprintf(buf, len, "CompressLevel5"); break;
|
|
case rfbEncodingCompressLevel6: snprintf(buf, len, "CompressLevel6"); break;
|
|
case rfbEncodingCompressLevel7: snprintf(buf, len, "CompressLevel7"); break;
|
|
case rfbEncodingCompressLevel8: snprintf(buf, len, "CompressLevel8"); break;
|
|
case rfbEncodingCompressLevel9: snprintf(buf, len, "CompressLevel9"); break;
|
|
|
|
case rfbEncodingQualityLevel0: snprintf(buf, len, "QualityLevel0"); break;
|
|
case rfbEncodingQualityLevel1: snprintf(buf, len, "QualityLevel1"); break;
|
|
case rfbEncodingQualityLevel2: snprintf(buf, len, "QualityLevel2"); break;
|
|
case rfbEncodingQualityLevel3: snprintf(buf, len, "QualityLevel3"); break;
|
|
case rfbEncodingQualityLevel4: snprintf(buf, len, "QualityLevel4"); break;
|
|
case rfbEncodingQualityLevel5: snprintf(buf, len, "QualityLevel5"); break;
|
|
case rfbEncodingQualityLevel6: snprintf(buf, len, "QualityLevel6"); break;
|
|
case rfbEncodingQualityLevel7: snprintf(buf, len, "QualityLevel7"); break;
|
|
case rfbEncodingQualityLevel8: snprintf(buf, len, "QualityLevel8"); break;
|
|
case rfbEncodingQualityLevel9: snprintf(buf, len, "QualityLevel9"); break;
|
|
|
|
|
|
default:
|
|
snprintf(buf, len, "Enc(0x%08X)", type);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rfbStatList *rfbStatLookupEncoding(rfbClientPtr cl, uint32_t type)
|
|
{
|
|
rfbStatList *ptr;
|
|
if (cl==NULL) return NULL;
|
|
for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
|
|
{
|
|
if (ptr->type==type) return ptr;
|
|
}
|
|
/* Well, we are here... need to *CREATE* an entry */
|
|
ptr = (rfbStatList *)malloc(sizeof(rfbStatList));
|
|
if (ptr!=NULL)
|
|
{
|
|
memset((char *)ptr, 0, sizeof(rfbStatList));
|
|
ptr->type = type;
|
|
/* add to the top of the list */
|
|
ptr->Next = cl->statEncList;
|
|
cl->statEncList = ptr;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
|
|
rfbStatList *rfbStatLookupMessage(rfbClientPtr cl, uint32_t type)
|
|
{
|
|
rfbStatList *ptr;
|
|
if (cl==NULL) return NULL;
|
|
for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
|
|
{
|
|
if (ptr->type==type) return ptr;
|
|
}
|
|
/* Well, we are here... need to *CREATE* an entry */
|
|
ptr = (rfbStatList *)malloc(sizeof(rfbStatList));
|
|
if (ptr!=NULL)
|
|
{
|
|
memset((char *)ptr, 0, sizeof(rfbStatList));
|
|
ptr->type = type;
|
|
/* add to the top of the list */
|
|
ptr->Next = cl->statMsgList;
|
|
cl->statMsgList = ptr;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
void rfbStatRecordEncodingSentAdd(rfbClientPtr cl, uint32_t type, int byteCount) /* Specifically for tight encoding */
|
|
{
|
|
rfbStatList *ptr;
|
|
|
|
ptr = rfbStatLookupEncoding(cl, type);
|
|
if (ptr!=NULL)
|
|
ptr->bytesSent += byteCount;
|
|
}
|
|
|
|
|
|
void rfbStatRecordEncodingSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw)
|
|
{
|
|
rfbStatList *ptr;
|
|
|
|
ptr = rfbStatLookupEncoding(cl, type);
|
|
if (ptr!=NULL)
|
|
{
|
|
ptr->sentCount++;
|
|
ptr->bytesSent += byteCount;
|
|
ptr->bytesSentIfRaw += byteIfRaw;
|
|
}
|
|
}
|
|
|
|
void rfbStatRecordEncodingRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw)
|
|
{
|
|
rfbStatList *ptr;
|
|
|
|
ptr = rfbStatLookupEncoding(cl, type);
|
|
if (ptr!=NULL)
|
|
{
|
|
ptr->rcvdCount++;
|
|
ptr->bytesRcvd += byteCount;
|
|
ptr->bytesRcvdIfRaw += byteIfRaw;
|
|
}
|
|
}
|
|
|
|
void rfbStatRecordMessageSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw)
|
|
{
|
|
rfbStatList *ptr;
|
|
|
|
ptr = rfbStatLookupMessage(cl, type);
|
|
if (ptr!=NULL)
|
|
{
|
|
ptr->sentCount++;
|
|
ptr->bytesSent += byteCount;
|
|
ptr->bytesSentIfRaw += byteIfRaw;
|
|
}
|
|
}
|
|
|
|
void rfbStatRecordMessageRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw)
|
|
{
|
|
rfbStatList *ptr;
|
|
|
|
ptr = rfbStatLookupMessage(cl, type);
|
|
if (ptr!=NULL)
|
|
{
|
|
ptr->rcvdCount++;
|
|
ptr->bytesRcvd += byteCount;
|
|
ptr->bytesRcvdIfRaw += byteIfRaw;
|
|
}
|
|
}
|
|
|
|
|
|
int rfbStatGetSentBytes(rfbClientPtr cl)
|
|
{
|
|
rfbStatList *ptr=NULL;
|
|
int bytes=0;
|
|
if (cl==NULL) return 0;
|
|
for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
|
|
bytes += ptr->bytesSent;
|
|
for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
|
|
bytes += ptr->bytesSent;
|
|
return bytes;
|
|
}
|
|
|
|
int rfbStatGetSentBytesIfRaw(rfbClientPtr cl)
|
|
{
|
|
rfbStatList *ptr=NULL;
|
|
int bytes=0;
|
|
if (cl==NULL) return 0;
|
|
for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
|
|
bytes += ptr->bytesSentIfRaw;
|
|
for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
|
|
bytes += ptr->bytesSentIfRaw;
|
|
return bytes;
|
|
}
|
|
|
|
int rfbStatGetRcvdBytes(rfbClientPtr cl)
|
|
{
|
|
rfbStatList *ptr=NULL;
|
|
int bytes=0;
|
|
if (cl==NULL) return 0;
|
|
for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
|
|
bytes += ptr->bytesRcvd;
|
|
for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
|
|
bytes += ptr->bytesRcvd;
|
|
return bytes;
|
|
}
|
|
|
|
int rfbStatGetRcvdBytesIfRaw(rfbClientPtr cl)
|
|
{
|
|
rfbStatList *ptr=NULL;
|
|
int bytes=0;
|
|
if (cl==NULL) return 0;
|
|
for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
|
|
bytes += ptr->bytesRcvdIfRaw;
|
|
for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
|
|
bytes += ptr->bytesRcvdIfRaw;
|
|
return bytes;
|
|
}
|
|
|
|
int rfbStatGetMessageCountSent(rfbClientPtr cl, uint32_t type)
|
|
{
|
|
rfbStatList *ptr=NULL;
|
|
if (cl==NULL) return 0;
|
|
for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
|
|
if (ptr->type==type) return ptr->sentCount;
|
|
return 0;
|
|
}
|
|
int rfbStatGetMessageCountRcvd(rfbClientPtr cl, uint32_t type)
|
|
{
|
|
rfbStatList *ptr=NULL;
|
|
if (cl==NULL) return 0;
|
|
for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
|
|
if (ptr->type==type) return ptr->rcvdCount;
|
|
return 0;
|
|
}
|
|
|
|
int rfbStatGetEncodingCountSent(rfbClientPtr cl, uint32_t type)
|
|
{
|
|
rfbStatList *ptr=NULL;
|
|
if (cl==NULL) return 0;
|
|
for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
|
|
if (ptr->type==type) return ptr->sentCount;
|
|
return 0;
|
|
}
|
|
int rfbStatGetEncodingCountRcvd(rfbClientPtr cl, uint32_t type)
|
|
{
|
|
rfbStatList *ptr=NULL;
|
|
if (cl==NULL) return 0;
|
|
for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
|
|
if (ptr->type==type) return ptr->rcvdCount;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
void rfbResetStats(rfbClientPtr cl)
|
|
{
|
|
rfbStatList *ptr;
|
|
if (cl==NULL) return;
|
|
while (cl->statEncList!=NULL)
|
|
{
|
|
ptr = cl->statEncList;
|
|
cl->statEncList = ptr->Next;
|
|
free(ptr);
|
|
}
|
|
while (cl->statMsgList!=NULL)
|
|
{
|
|
ptr = cl->statMsgList;
|
|
cl->statMsgList = ptr->Next;
|
|
free(ptr);
|
|
}
|
|
}
|
|
|
|
|
|
void rfbPrintStats(rfbClientPtr cl)
|
|
{
|
|
rfbStatList *ptr=NULL;
|
|
char encBuf[64];
|
|
double savings=0.0;
|
|
int totalRects=0;
|
|
double totalBytes=0.0;
|
|
double totalBytesIfRaw=0.0;
|
|
|
|
char *name=NULL;
|
|
int bytes=0;
|
|
int bytesIfRaw=0;
|
|
int count=0;
|
|
|
|
if (cl==NULL) return;
|
|
|
|
rfbLog("%-21.21s %-6.6s %9.9s/%9.9s (%6.6s)\n", "Statistics", "events", "Transmit","RawEquiv","saved");
|
|
for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
|
|
{
|
|
name = messageNameServer2Client(ptr->type, encBuf, sizeof(encBuf));
|
|
count = ptr->sentCount;
|
|
bytes = ptr->bytesSent;
|
|
bytesIfRaw = ptr->bytesSentIfRaw;
|
|
|
|
savings = 0.0;
|
|
if (bytesIfRaw>0.0)
|
|
savings = 100.0 - (((double)bytes / (double)bytesIfRaw) * 100.0);
|
|
if ((bytes>0) || (count>0) || (bytesIfRaw>0))
|
|
rfbLog(" %-20.20s: %6d | %9d/%9d (%5.1f%%)\n",
|
|
name, count, bytes, bytesIfRaw, savings);
|
|
totalRects += count;
|
|
totalBytes += bytes;
|
|
totalBytesIfRaw += bytesIfRaw;
|
|
}
|
|
|
|
for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
|
|
{
|
|
name = encodingName(ptr->type, encBuf, sizeof(encBuf));
|
|
count = ptr->sentCount;
|
|
bytes = ptr->bytesSent;
|
|
bytesIfRaw = ptr->bytesSentIfRaw;
|
|
savings = 0.0;
|
|
|
|
if (bytesIfRaw>0.0)
|
|
savings = 100.0 - (((double)bytes / (double)bytesIfRaw) * 100.0);
|
|
if ((bytes>0) || (count>0) || (bytesIfRaw>0))
|
|
rfbLog(" %-20.20s: %6d | %9d/%9d (%5.1f%%)\n",
|
|
name, count, bytes, bytesIfRaw, savings);
|
|
totalRects += count;
|
|
totalBytes += bytes;
|
|
totalBytesIfRaw += bytesIfRaw;
|
|
}
|
|
savings=0.0;
|
|
if (totalBytesIfRaw>0.0)
|
|
savings = 100.0 - ((totalBytes/totalBytesIfRaw)*100.0);
|
|
rfbLog(" %-20.20s: %6d | %9.0f/%9.0f (%5.1f%%)\n",
|
|
"TOTALS", totalRects, totalBytes,totalBytesIfRaw, savings);
|
|
|
|
totalRects=0.0;
|
|
totalBytes=0.0;
|
|
totalBytesIfRaw=0.0;
|
|
|
|
rfbLog("%-21.21s %-6.6s %9.9s/%9.9s (%6.6s)\n", "Statistics", "events", "Received","RawEquiv","saved");
|
|
for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
|
|
{
|
|
name = messageNameClient2Server(ptr->type, encBuf, sizeof(encBuf));
|
|
count = ptr->rcvdCount;
|
|
bytes = ptr->bytesRcvd;
|
|
bytesIfRaw = ptr->bytesRcvdIfRaw;
|
|
savings = 0.0;
|
|
|
|
if (bytesIfRaw>0.0)
|
|
savings = 100.0 - (((double)bytes / (double)bytesIfRaw) * 100.0);
|
|
if ((bytes>0) || (count>0) || (bytesIfRaw>0))
|
|
rfbLog(" %-20.20s: %6d | %9d/%9d (%5.1f%%)\n",
|
|
name, count, bytes, bytesIfRaw, savings);
|
|
totalRects += count;
|
|
totalBytes += bytes;
|
|
totalBytesIfRaw += bytesIfRaw;
|
|
}
|
|
for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
|
|
{
|
|
name = encodingName(ptr->type, encBuf, sizeof(encBuf));
|
|
count = ptr->rcvdCount;
|
|
bytes = ptr->bytesRcvd;
|
|
bytesIfRaw = ptr->bytesRcvdIfRaw;
|
|
savings = 0.0;
|
|
|
|
if (bytesIfRaw>0.0)
|
|
savings = 100.0 - (((double)bytes / (double)bytesIfRaw) * 100.0);
|
|
if ((bytes>0) || (count>0) || (bytesIfRaw>0))
|
|
rfbLog(" %-20.20s: %6d | %9d/%9d (%5.1f%%)\n",
|
|
name, count, bytes, bytesIfRaw, savings);
|
|
totalRects += count;
|
|
totalBytes += bytes;
|
|
totalBytesIfRaw += bytesIfRaw;
|
|
}
|
|
savings=0.0;
|
|
if (totalBytesIfRaw>0.0)
|
|
savings = 100.0 - ((totalBytes/totalBytesIfRaw)*100.0);
|
|
rfbLog(" %-20.20s: %6d | %9.0f/%9.0f (%5.1f%%)\n",
|
|
"TOTALS", totalRects, totalBytes,totalBytesIfRaw, savings);
|
|
|
|
}
|
|
|