Initial /api/get_frame_stats
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user