Initial /api/get_frame_stats

This commit is contained in:
Lauri Kasanen 2021-07-27 15:33:48 +03:00
parent 32e8d40472
commit fb9dd56703
21 changed files with 655 additions and 12 deletions

View File

@ -37,6 +37,14 @@ namespace network {
// from main thread
void mainUpdateScreen(rfb::PixelBuffer *pb);
void mainUpdateBottleneckStats(const char userid[], const char stats[]);
void mainUpdateServerFrameStats(uint8_t changedPerc, uint32_t all,
uint32_t jpeg, uint32_t webp, uint32_t analysis,
uint32_t jpegarea, uint32_t webparea,
uint16_t njpeg, uint16_t nwebp,
uint16_t w, uint16_t h);
void mainUpdateClientFrameStats(const char userid[], uint32_t render, uint32_t all,
uint32_t ping);
void mainUpdateUserInfo(const uint8_t ownerConn, const uint8_t numUsers);
// from network threads
uint8_t *netGetScreenshot(uint16_t w, uint16_t h,
@ -46,13 +54,24 @@ namespace network {
uint8_t netRemoveUser(const char name[]);
uint8_t netGiveControlTo(const char name[]);
void netGetBottleneckStats(char *buf, uint32_t len);
void netGetFrameStats(char *buf, uint32_t len);
uint8_t netServerFrameStatsReady();
enum USER_ACTION {
//USER_ADD, - handled locally for interactivity
USER_REMOVE,
USER_GIVE_CONTROL,
WANT_FRAME_STATS_SERVERONLY,
WANT_FRAME_STATS_ALL,
WANT_FRAME_STATS_OWNER,
WANT_FRAME_STATS_SPECIFIC,
};
uint8_t netRequestFrameStats(USER_ACTION what, const char *client);
uint8_t netOwnerConnected();
uint8_t netNumActiveUsers();
uint8_t netGetClientFrameStatsNum();
struct action_data {
enum USER_ACTION action;
kasmpasswd_entry_t data;
@ -75,6 +94,34 @@ namespace network {
std::map<std::string, std::string> bottleneckStats;
pthread_mutex_t statMutex;
struct clientFrameStats_t {
uint32_t render;
uint32_t all;
uint32_t ping;
};
struct serverFrameStats_t {
uint32_t all;
uint32_t jpeg;
uint32_t webp;
uint32_t analysis;
uint32_t jpegarea;
uint32_t webparea;
uint16_t njpeg;
uint16_t nwebp;
uint16_t w;
uint16_t h;
uint8_t changedPerc;
uint8_t inprogress;
};
std::map<std::string, clientFrameStats_t> clientFrameStats;
serverFrameStats_t serverFrameStats;
pthread_mutex_t frameStatMutex;
uint8_t ownerConnected;
uint8_t activeUsers;
pthread_mutex_t userInfoMutex;
};
}

View File

@ -56,11 +56,16 @@ static const struct TightJPEGConfiguration conf[10] = {
GetAPIMessager::GetAPIMessager(const char *passwdfile_): passwdfile(passwdfile_),
screenW(0), screenH(0), screenHash(0),
cachedW(0), cachedH(0), cachedQ(0) {
cachedW(0), cachedH(0), cachedQ(0),
ownerConnected(0), activeUsers(0) {
pthread_mutex_init(&screenMutex, NULL);
pthread_mutex_init(&userMutex, NULL);
pthread_mutex_init(&statMutex, NULL);
pthread_mutex_init(&frameStatMutex, NULL);
pthread_mutex_init(&userInfoMutex, NULL);
serverFrameStats.inprogress = 0;
}
// from main thread
@ -105,6 +110,56 @@ void GetAPIMessager::mainUpdateBottleneckStats(const char userid[], const char s
pthread_mutex_unlock(&statMutex);
}
void GetAPIMessager::mainUpdateServerFrameStats(uint8_t changedPerc,
uint32_t all, uint32_t jpeg, uint32_t webp, uint32_t analysis,
uint32_t jpegarea, uint32_t webparea,
uint16_t njpeg, uint16_t nwebp,
uint16_t w, uint16_t h) {
if (pthread_mutex_lock(&frameStatMutex))
return;
serverFrameStats.changedPerc = changedPerc;
serverFrameStats.all = all;
serverFrameStats.jpeg = jpeg;
serverFrameStats.webp = webp;
serverFrameStats.analysis = analysis;
serverFrameStats.jpegarea = jpegarea;
serverFrameStats.webparea = webparea;
serverFrameStats.njpeg = njpeg;
serverFrameStats.nwebp = nwebp;
serverFrameStats.w = w;
serverFrameStats.h = h;
pthread_mutex_unlock(&frameStatMutex);
}
void GetAPIMessager::mainUpdateClientFrameStats(const char userid[], uint32_t render,
uint32_t all, uint32_t ping) {
if (pthread_mutex_lock(&frameStatMutex))
return;
clientFrameStats_t s;
s.render = render;
s.all = all;
s.ping = ping;
clientFrameStats[userid] = s;
pthread_mutex_unlock(&frameStatMutex);
}
void GetAPIMessager::mainUpdateUserInfo(const uint8_t ownerConn, const uint8_t numUsers) {
if (pthread_mutex_lock(&userInfoMutex))
return;
ownerConnected = ownerConn;
activeUsers = numUsers;
pthread_mutex_unlock(&userInfoMutex);
}
// from network threads
uint8_t *GetAPIMessager::netGetScreenshot(uint16_t w, uint16_t h,
const uint8_t q, const bool dedup,
@ -363,3 +418,197 @@ void GetAPIMessager::netGetBottleneckStats(char *buf, uint32_t len) {
out:
pthread_mutex_unlock(&statMutex);
}
void GetAPIMessager::netGetFrameStats(char *buf, uint32_t len) {
/*
{
"frame" : {
"resx": 1024,
"resy": 1280,
"changed": 75,
"server_time": 23
},
"server_side" : [
{ "process_name": "Analysis", "time": 20 },
{ "process_name": "TightWEBPEncoder", "time": 20, "count": 64, "area": 12 },
{ "process_name": "TightJPEGEncoder", "time": 20, "count": 64, "area": 12 }
],
"client_side" : [
"123.1.2.1:1211" : {
"client_time": 20,
"ping": 20,
"processes" : [
{ "process_name": "scanRenderQ", "time": 20 }
]
}
}
}
*/
std::map<std::string, clientFrameStats_t>::const_iterator it;
unsigned i = 0;
FILE *f;
if (pthread_mutex_lock(&frameStatMutex)) {
buf[0] = 0;
return;
}
const unsigned num = clientFrameStats.size();
// Conservative estimate
if (len < 1024) {
buf[0] = 0;
goto out;
}
f = fmemopen(buf, len, "w");
fprintf(f, "{\n");
fprintf(f, "\t\"frame\" : {\n"
"\t\t\"resx\": %u,\n"
"\t\t\"resy\": %u,\n"
"\t\t\"changed\": %u,\n"
"\t\t\"server_time\": %u\n"
"\t},\n",
serverFrameStats.w,
serverFrameStats.h,
serverFrameStats.changedPerc,
serverFrameStats.all);
fprintf(f, "\t\"server_side\" : [\n"
"\t\t{ \"process_name\": \"Analysis\", \"time\": %u },\n"
"\t\t{ \"process_name\": \"TightJPEGEncoder\", \"time\": %u, \"count\": %u, \"area\": %u },\n"
"\t\t{ \"process_name\": \"TightWEBPEncoder\", \"time\": %u, \"count\": %u, \"area\": %u }\n"
"\t],\n",
serverFrameStats.analysis,
serverFrameStats.jpeg,
serverFrameStats.njpeg,
serverFrameStats.jpegarea,
serverFrameStats.webp,
serverFrameStats.nwebp,
serverFrameStats.webparea);
fprintf(f, "\t\"client_side\" : [\n");
for (it = clientFrameStats.begin(); it != clientFrameStats.end(); it++, i++) {
const char *id = it->first.c_str();
const clientFrameStats_t &s = it->second;
fprintf(f, "\t\t\"%s\" : {\n"
"\t\t\t\"client_time\": %u,\n"
"\t\t\t\"ping\": %u,\n"
"\t\t\t\"processes\" : [\n"
"\t\t\t\t{ \"process_name\": \"scanRenderQ\", \"time\": %u }\n"
"\t\t\t]\n"
"\t\t}",
id,
s.all,
s.ping,
s.render);
if (i == num - 1)
fprintf(f, "\n");
else
fprintf(f, ",\n");
}
fprintf(f, "\t]\n}\n");
fclose(f);
serverFrameStats.inprogress = 0;
out:
pthread_mutex_unlock(&frameStatMutex);
}
uint8_t GetAPIMessager::netRequestFrameStats(USER_ACTION what, const char *client) {
// Return 1 for success
action_data act;
act.action = what;
if (client) {
strncpy(act.data.password, client, PASSWORD_LEN);
act.data.password[PASSWORD_LEN - 1] = '\0';
}
// In progress already?
bool fail = false;
if (pthread_mutex_lock(&frameStatMutex))
return 0;
if (serverFrameStats.inprogress) {
fail = true;
vlog.error("Frame stats request already in progress, refusing another");
} else {
clientFrameStats.clear();
memset(&serverFrameStats, 0, sizeof(serverFrameStats_t));
serverFrameStats.inprogress = 1;
}
pthread_mutex_unlock(&frameStatMutex);
if (fail)
return 0;
// Send it in
if (pthread_mutex_lock(&userMutex))
return 0;
actionQueue.push_back(act);
pthread_mutex_unlock(&userMutex);
return 1;
}
uint8_t GetAPIMessager::netOwnerConnected() {
uint8_t ret;
if (pthread_mutex_lock(&userInfoMutex))
return 0;
ret = ownerConnected;
pthread_mutex_unlock(&userInfoMutex);
return ret;
}
uint8_t GetAPIMessager::netNumActiveUsers() {
uint8_t ret;
if (pthread_mutex_lock(&userInfoMutex))
return 0;
ret = activeUsers;
pthread_mutex_unlock(&userInfoMutex);
return ret;
}
uint8_t GetAPIMessager::netGetClientFrameStatsNum() {
uint8_t ret;
if (pthread_mutex_lock(&frameStatMutex))
return 0;
ret = clientFrameStats.size();
pthread_mutex_unlock(&frameStatMutex);
return ret;
}
uint8_t GetAPIMessager::netServerFrameStatsReady() {
uint8_t ret;
if (pthread_mutex_lock(&frameStatMutex))
return 0;
ret = serverFrameStats.w != 0;
pthread_mutex_unlock(&frameStatMutex);
return ret;
}

View File

@ -465,6 +465,60 @@ static void bottleneckStatsCb(void *messager, char *buf, uint32_t len)
msgr->netGetBottleneckStats(buf, len);
}
static void frameStatsCb(void *messager, char *buf, uint32_t len)
{
GetAPIMessager *msgr = (GetAPIMessager *) messager;
msgr->netGetFrameStats(buf, len);
}
static uint8_t requestFrameStatsNoneCb(void *messager)
{
GetAPIMessager *msgr = (GetAPIMessager *) messager;
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_SERVERONLY, NULL);
}
static uint8_t requestFrameStatsOwnerCb(void *messager)
{
GetAPIMessager *msgr = (GetAPIMessager *) messager;
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_OWNER, NULL);
}
static uint8_t requestFrameStatsAllCb(void *messager)
{
GetAPIMessager *msgr = (GetAPIMessager *) messager;
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_ALL, NULL);
}
static uint8_t requestFrameStatsOneCb(void *messager, const char *client)
{
GetAPIMessager *msgr = (GetAPIMessager *) messager;
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_SPECIFIC, client);
}
static uint8_t ownerConnectedCb(void *messager)
{
GetAPIMessager *msgr = (GetAPIMessager *) messager;
return msgr->netOwnerConnected();
}
static uint8_t numActiveUsersCb(void *messager)
{
GetAPIMessager *msgr = (GetAPIMessager *) messager;
return msgr->netNumActiveUsers();
}
static uint8_t getClientFrameStatsNumCb(void *messager)
{
GetAPIMessager *msgr = (GetAPIMessager *) messager;
return msgr->netGetClientFrameStatsNum();
}
static uint8_t serverFrameStatsReadyCb(void *messager)
{
GetAPIMessager *msgr = (GetAPIMessager *) messager;
return msgr->netServerFrameStatsReady();
}
WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
socklen_t listenaddrlen,
@ -556,6 +610,17 @@ WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
settings.removeCb = removeCb;
settings.givecontrolCb = givecontrolCb;
settings.bottleneckStatsCb = bottleneckStatsCb;
settings.frameStatsCb = frameStatsCb;
settings.requestFrameStatsNoneCb = requestFrameStatsNoneCb;
settings.requestFrameStatsOwnerCb = requestFrameStatsOwnerCb;
settings.requestFrameStatsAllCb = requestFrameStatsAllCb;
settings.requestFrameStatsOneCb = requestFrameStatsOneCb;
settings.ownerConnectedCb = ownerConnectedCb;
settings.numActiveUsersCb = numActiveUsersCb;
settings.getClientFrameStatsNumCb = getClientFrameStatsNumCb;
settings.serverFrameStatsReadyCb = serverFrameStatsReadyCb;
pthread_t tid;
pthread_create(&tid, NULL, start_server, NULL);

View File

@ -1089,6 +1089,74 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
wserr("Sent bottleneck stats to API caller\n");
ret = 1;
} else entry("/api/get_frame_stats") {
char statbuf[4096], decname[1024];
unsigned waitfor;
param = parse_get(args, "client", &len);
if (len) {
memcpy(buf, param, len);
buf[len] = '\0';
percent_decode(buf, decname);
} else {
wserr("client param required\n");
goto nope;
}
if (!decname[0])
goto nope;
if (!strcmp(decname, "none")) {
waitfor = 0;
if (!settings.requestFrameStatsNoneCb(settings.messager))
goto nope;
} else if (!strcmp(decname, "auto")) {
waitfor = settings.ownerConnectedCb(settings.messager);
if (!waitfor) {
if (!settings.requestFrameStatsNoneCb(settings.messager))
goto nope;
} else {
if (!settings.requestFrameStatsOwnerCb(settings.messager))
goto nope;
}
} else if (!strcmp(decname, "all")) {
waitfor = settings.numActiveUsersCb(settings.messager);
if (!settings.requestFrameStatsAllCb(settings.messager))
goto nope;
} else {
waitfor = 1;
if (!settings.requestFrameStatsOneCb(settings.messager, decname))
goto nope;
}
while (1) {
usleep(10 * 1000);
if (settings.serverFrameStatsReadyCb(settings.messager))
break;
}
if (waitfor) {
unsigned waits;
for (waits = 0; waits < 20; waits++) { // wait up to 2s
if (settings.getClientFrameStatsNumCb(settings.messager) >= waitfor)
break;
usleep(100 * 1000);
}
}
settings.frameStatsCb(settings.messager, statbuf, 4096);
sprintf(buf, "HTTP/1.1 200 OK\r\n"
"Server: KasmVNC/4.0\r\n"
"Connection: close\r\n"
"Content-type: text/plain\r\n"
"Content-length: %lu\r\n"
"\r\n", strlen(statbuf));
ws_send(ws_ctx, buf, strlen(buf));
ws_send(ws_ctx, statbuf, strlen(statbuf));
wserr("Sent frame stats to API caller\n");
ret = 1;
}
#undef entry

View File

@ -85,6 +85,17 @@ typedef struct {
uint8_t (*removeCb)(void *messager, const char name[]);
uint8_t (*givecontrolCb)(void *messager, const char name[]);
void (*bottleneckStatsCb)(void *messager, char *buf, uint32_t len);
void (*frameStatsCb)(void *messager, char *buf, uint32_t len);
uint8_t (*requestFrameStatsNoneCb)(void *messager);
uint8_t (*requestFrameStatsOwnerCb)(void *messager);
uint8_t (*requestFrameStatsAllCb)(void *messager);
uint8_t (*requestFrameStatsOneCb)(void *messager, const char *client);
uint8_t (*ownerConnectedCb)(void *messager);
uint8_t (*numActiveUsersCb)(void *messager);
uint8_t (*getClientFrameStatsNumCb)(void *messager);
uint8_t (*serverFrameStatsReadyCb)(void *messager);
} settings_t;
#ifdef __cplusplus

View File

@ -695,6 +695,8 @@ bool ComparingUpdateTracker::compare(bool skipScrollDetection, const Region &ski
std::vector<Rect> rects;
std::vector<Rect>::iterator i;
changedPerc = 100;
if (!enabled)
return false;
@ -749,8 +751,13 @@ bool ComparingUpdateTracker::compare(bool skipScrollDetection, const Region &ski
for (i = rects.begin(); i != rects.end(); i++)
totalPixels += i->area();
newChanged.get_rects(&rects);
for (i = rects.begin(); i != rects.end(); i++)
unsigned newchangedarea = 0;
for (i = rects.begin(); i != rects.end(); i++) {
missedPixels += i->area();
newchangedarea += i->area();
}
changedPerc = newchangedarea * 100 / fb->area();
if (changed.equals(newChanged))
return false;

View File

@ -48,6 +48,8 @@ namespace rfb {
virtual void getUpdateInfo(UpdateInfo* info, const Region& cliprgn);
virtual void clear();
rdr::U8 changedPerc;
private:
void compareRect(const Rect& r, Region* newchanged, const Region &skipCursorArea);
PixelBuffer* fb;

View File

@ -298,6 +298,11 @@ size_t Congestion::getBandwidth()
return congWindow * 1000 / safeBaseRTT;
}
unsigned Congestion::getPingTime() const
{
return safeBaseRTT;
}
void Congestion::debugTrace(const char* filename, int fd)
{
#ifdef CONGESTION_TRACE

View File

@ -51,6 +51,8 @@ namespace rfb {
// per second.
size_t getBandwidth();
unsigned getPingTime() const;
// debugTrace() writes the current congestion window, as well as the
// congestion window of the underlying TCP layer, to the specified
// file

View File

@ -359,6 +359,8 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
changed = changed_;
gettimeofday(&start, NULL);
memset(&jpegstats, 0, sizeof(codecstats_t));
memset(&webpstats, 0, sizeof(codecstats_t));
if (allowLossy && activeEncoders[encoderFullColour] == encoderTightWEBP) {
const unsigned rate = 1024 * 1000 / rfb::Server::frameRate;
@ -1014,6 +1016,7 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
std::vector<uint8_t> isWebp, fromCache;
std::vector<Palette> palettes;
std::vector<std::vector<uint8_t> > compresseds;
std::vector<uint32_t> ms;
uint32_t i;
if (rfb::Server::rectThreads > 0)
@ -1078,6 +1081,7 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
palettes.resize(subrects.size());
compresseds.resize(subrects.size());
scaledrects.resize(subrects.size());
ms.resize(subrects.size());
// In case the current resolution is above the max video res, and video was detected,
// scale to that res, keeping aspect ratio
@ -1134,10 +1138,19 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
for (i = 0; i < subrects.size(); ++i) {
encoderTypes[i] = getEncoderType(subrects[i], pb, &palettes[i], compresseds[i],
&isWebp[i], &fromCache[i],
scaledpb, scaledrects[i]);
scaledpb, scaledrects[i], ms[i]);
checkWebpFallback(start);
}
for (i = 0; i < subrects.size(); ++i) {
if (encoderTypes[i] == encoderFullColour) {
if (isWebp[i])
webpstats.ms += ms[i];
else
jpegstats.ms += ms[i];
}
}
if (start) {
encodingTime = msSince(start);
@ -1178,7 +1191,8 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
Palette *pal, std::vector<uint8_t> &compressed,
uint8_t *isWebp, uint8_t *fromCache,
const PixelBuffer *scaledpb, const Rect& scaledrect) const
const PixelBuffer *scaledpb, const Rect& scaledrect,
uint32_t &ms) const
{
struct RectInfo info;
unsigned int maxColours = 256;
@ -1231,9 +1245,12 @@ uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
*isWebp = 0;
*fromCache = 0;
ms = 0;
if (type == encoderFullColour) {
uint32_t len;
const void *data;
struct timeval start;
gettimeofday(&start, NULL);
if (encCache->enabled &&
(data = encCache->get(activeEncoders[encoderFullColour],
@ -1274,6 +1291,8 @@ uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
compressed,
videoDetected);
}
ms = msSince(&start);
}
delete ppb;
@ -1292,10 +1311,15 @@ void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb,
encoder = startRect(rect, type, compressed.size() == 0, isWebp);
if (compressed.size()) {
if (isWebp)
if (isWebp) {
((TightWEBPEncoder *) encoder)->writeOnly(compressed);
else
webpstats.area += rect.area();
webpstats.rects++;
} else {
((TightJPEGEncoder *) encoder)->writeOnly(compressed);
jpegstats.area += rect.area();
jpegstats.rects++;
}
} else {
if (encoder->flags & EncoderUseNativePF) {
ppb = preparePixelBuffer(rect, pb, false);

View File

@ -72,6 +72,14 @@ namespace rfb {
return encodingTime;
};
struct codecstats_t {
uint32_t ms;
uint32_t area;
uint32_t rects;
};
codecstats_t jpegstats, webpstats;
protected:
void doUpdate(bool allowLossy, const Region& changed,
const Region& copied, const Point& copy_delta,
@ -105,7 +113,8 @@ namespace rfb {
uint8_t getEncoderType(const Rect& rect, const PixelBuffer *pb, Palette *pal,
std::vector<uint8_t> &compressed, uint8_t *isWebp,
uint8_t *fromCache,
const PixelBuffer *scaledpb, const Rect& scaledrect) const;
const PixelBuffer *scaledpb, const Rect& scaledrect,
uint32_t &ms) const;
virtual bool handleTimeout(Timer* t);
bool checkSolidTile(const Rect& r, const rdr::U8* colourValue,

View File

@ -64,6 +64,7 @@ namespace rfb {
const rdr::U8* const* data);
virtual void sendStats(const bool toClient = true) = 0;
virtual void handleFrameStats(rdr::U32 all, rdr::U32 render) = 0;
virtual bool canChangeKasmSettings() const = 0;

View File

@ -80,6 +80,9 @@ void SMsgReader::readMsg()
case msgTypeRequestStats:
readRequestStats();
break;
case msgTypeFrameStats:
readFrameStats();
break;
case msgTypeKeyEvent:
readKeyEvent();
break;
@ -346,6 +349,14 @@ void SMsgReader::readRequestStats()
handler->sendStats();
}
void SMsgReader::readFrameStats()
{
is->skip(3);
rdr::U32 all = is->readU32();
rdr::U32 render = is->readU32();
handler->handleFrameStats(all, render);
}
void SMsgReader::readQEMUMessage()
{
int subType = is->readU8();

View File

@ -57,6 +57,7 @@ namespace rfb {
void readClientCutText();
void readExtendedClipboard(rdr::S32 len);
void readRequestStats();
void readFrameStats();
void readQEMUMessage();
void readQEMUKeyEvent();

View File

@ -208,6 +208,12 @@ void SMsgWriter::writeStats(const char* str, int len)
endMsg();
}
void SMsgWriter::writeRequestFrameStats()
{
startMsg(msgTypeRequestFrameStats);
endMsg();
}
void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
{
if (!cp->supportsFence)

View File

@ -65,6 +65,8 @@ namespace rfb {
void writeStats(const char* str, int len);
void writeRequestFrameStats();
// writeFence() sends a new fence request or response to the client.
void writeFence(rdr::U32 flags, unsigned len, const char data[]);

View File

@ -62,7 +62,7 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
continuousUpdates(false), encodeManager(this, &server_->encCache),
needsPermCheck(false), pointerEventTime(0),
clientHasCursor(false),
accessRights(AccessDefault), startTime(time(0))
accessRights(AccessDefault), startTime(time(0)), frameTracking(false)
{
setStreams(&sock->inStream(), &sock->outStream());
peerEndpoint.buf = sock->getPeerEndpoint();
@ -99,6 +99,9 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
gettimeofday(&lastKeyEvent, NULL);
server->clients.push_front(this);
if (server->apimessager)
server->apimessager->mainUpdateUserInfo(checkOwnerConn(), server->clients.size());
}
@ -129,6 +132,9 @@ VNCSConnectionST::~VNCSConnectionST()
server->clients.remove(this);
delete [] fenceData;
if (server->apimessager)
server->apimessager->mainUpdateUserInfo(checkOwnerConn(), server->clients.size());
}
@ -1230,6 +1236,9 @@ void VNCSConnectionST::writeFramebufferUpdate()
// window.
sock->cork(true);
if (frameTracking)
writer()->writeRequestFrameStats();
// First take care of any updates that cannot contain framebuffer data
// changes.
writeNoDataUpdate();
@ -1507,6 +1516,22 @@ void VNCSConnectionST::sendStats(const bool toClient) {
}
}
void VNCSConnectionST::handleFrameStats(rdr::U32 all, rdr::U32 render)
{
if (server->apimessager) {
const char *at = strchr(peerEndpoint.buf, '@');
if (!at)
at = peerEndpoint.buf;
else
at++;
server->apimessager->mainUpdateClientFrameStats(at, render, all,
congestion.getPingTime());
}
frameTracking = false;
}
// setCursor() is called whenever the cursor has changed shape or pixel format.
// If the client supports local cursor then it will arrange for the cursor to
// be sent to the client.
@ -1616,3 +1641,15 @@ int VNCSConnectionST::getStatus()
return 4;
}
bool VNCSConnectionST::checkOwnerConn() const
{
std::list<VNCSConnectionST*>::const_iterator it;
for (it = server->clients.begin(); it != server->clients.end(); it++) {
bool write, owner;
if ((*it)->getPerms(write, owner) && owner)
return true;
}
return false;
}

View File

@ -165,6 +165,26 @@ namespace rfb {
int getStatus();
virtual void sendStats(const bool toClient = true);
virtual void handleFrameStats(rdr::U32 all, rdr::U32 render);
bool is_owner() const {
bool write, owner;
if (getPerms(write, owner) && owner)
return true;
return false;
}
void setFrameTracking() {
frameTracking = true;
}
EncodeManager::codecstats_t getJpegStats() const {
return encodeManager.jpegstats;
}
EncodeManager::codecstats_t getWebpStats() const {
return encodeManager.webpstats;
}
private:
// SConnection callbacks
@ -220,6 +240,8 @@ namespace rfb {
bool getPerms(bool &write, bool &owner) const;
bool checkOwnerConn() const;
// Congestion control
void writeRTTPing();
bool isCongested();
@ -295,6 +317,8 @@ namespace rfb {
time_t startTime;
std::vector<CopyPassRect> copypassed;
bool frameTracking;
};
}
#endif

View File

@ -128,7 +128,7 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
renderedCursorInvalid(false),
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
lastConnectionTime(0), disableclients(false),
frameTimer(this), apimessager(NULL)
frameTimer(this), apimessager(NULL), trackingFrameStats(0)
{
lastUserInputTime = lastDisconnectTime = time(0);
slog.debug("creating single-threaded server %s", name.buf);
@ -210,6 +210,8 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
if (inotify_add_watch(inotifyfd, kasmpasswdpath, IN_CLOSE_WRITE | IN_DELETE_SELF) < 0)
slog.error("Failed to set watch");
}
trackingClient[0] = 0;
}
VNCServerST::~VNCServerST()
@ -774,7 +776,8 @@ int VNCServerST::msToNextUpdate()
return frameTimer.getRemainingMs();
}
static void checkAPIMessages(network::GetAPIMessager *apimessager)
static void checkAPIMessages(network::GetAPIMessager *apimessager,
rdr::U8 &trackingFrameStats, char trackingClient[])
{
if (pthread_mutex_lock(&apimessager->userMutex))
return;
@ -827,6 +830,20 @@ static void checkAPIMessages(network::GetAPIMessager *apimessager)
slog.error("Tried to give control to nonexistent user %s", act.data.user);
}
break;
case network::GetAPIMessager::WANT_FRAME_STATS_SERVERONLY:
trackingFrameStats = act.action;
break;
case network::GetAPIMessager::WANT_FRAME_STATS_ALL:
trackingFrameStats = act.action;
break;
case network::GetAPIMessager::WANT_FRAME_STATS_OWNER:
trackingFrameStats = act.action;
break;
case network::GetAPIMessager::WANT_FRAME_STATS_SPECIFIC:
trackingFrameStats = act.action;
memcpy(trackingClient, act.data.password, 128);
break;
}
if (set) {
@ -923,6 +940,9 @@ void VNCServerST::writeUpdate()
assert(blockCounter == 0);
assert(desktopStarted);
struct timeval start;
gettimeofday(&start, NULL);
if (DLPRegion.enabled) {
comparer->enable_copyrect(false);
blackOut();
@ -949,6 +969,9 @@ void VNCServerST::writeUpdate()
else
comparer->disable();
struct timeval beforeAnalysis;
gettimeofday(&beforeAnalysis, NULL);
// Skip scroll detection if the client is slow, and didn't get the previous one yet
if (comparer->compare(clients.size() == 1 && (*clients.begin())->has_copypassed(),
cursorReg))
@ -956,6 +979,8 @@ void VNCServerST::writeUpdate()
comparer->clear();
const unsigned analysisMs = msSince(&beforeAnalysis);
encCache.clear();
encCache.enabled = clients.size() > 1;
@ -984,22 +1009,64 @@ void VNCServerST::writeUpdate()
if (apimessager) {
apimessager->mainUpdateScreen(pb);
checkAPIMessages(apimessager);
trackingFrameStats = 0;
checkAPIMessages(apimessager, trackingFrameStats, trackingClient);
}
EncodeManager::codecstats_t jpegstats, webpstats;
memset(&jpegstats, 0, sizeof(EncodeManager::codecstats_t));
memset(&webpstats, 0, sizeof(EncodeManager::codecstats_t));
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
ci_next = ci; ci_next++;
if (permcheck)
(*ci)->recheckPerms();
if (trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_ALL ||
(trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_OWNER &&
(*ci)->is_owner()) ||
(trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_SPECIFIC &&
strstr((*ci)->getPeerEndpoint(), trackingClient))) {
(*ci)->setFrameTracking();
// Only one owner
if (trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_OWNER)
trackingFrameStats = network::GetAPIMessager::WANT_FRAME_STATS_SERVERONLY;
}
(*ci)->add_copied(ui.copied, ui.copy_delta);
(*ci)->add_copypassed(ui.copypassed);
(*ci)->add_changed(ui.changed);
(*ci)->writeFramebufferUpdateOrClose();
if (apimessager)
if (apimessager) {
(*ci)->sendStats(false);
const EncodeManager::codecstats_t subjpeg = (*ci)->getJpegStats();
const EncodeManager::codecstats_t subwebp = (*ci)->getWebpStats();
jpegstats.ms += subjpeg.ms;
jpegstats.area += subjpeg.area;
jpegstats.rects += subjpeg.rects;
webpstats.ms += subwebp.ms;
webpstats.area += subwebp.area;
webpstats.rects += subwebp.rects;
}
}
if (trackingFrameStats) {
const unsigned totalMs = msSince(&start);
if (apimessager)
apimessager->mainUpdateServerFrameStats(comparer->changedPerc, totalMs,
jpegstats.ms, webpstats.ms,
analysisMs,
jpegstats.area, webpstats.area,
jpegstats.rects, webpstats.rects,
pb->getRect().width(),
pb->getRect().height());
}
}

View File

@ -267,6 +267,9 @@ namespace rfb {
network::GetAPIMessager *apimessager;
rdr::U8 trackingFrameStats;
char trackingClient[128];
struct {
bool enabled;
int x1, y1, x2, y2;

View File

@ -30,6 +30,7 @@ namespace rfb {
// kasm
const int msgTypeStats = 178;
const int msgTypeRequestFrameStats = 179;
const int msgTypeServerFence = 248;
@ -47,6 +48,7 @@ namespace rfb {
// kasm
const int msgTypeRequestStats = 178;
const int msgTypeFrameStats = 179;
const int msgTypeClientFence = 248;