Show rect/enc counts, add vnc_perf.html test.

- include/rfb.js: Keep track of the number of rects of each encoding
  type and print them out when we close a connection (if 'info'
  logging level).

- tests/vnc_perf.html: first pass at a noVNC based performance
  benchmark.

- utils/wsproxy.py: Fix the output of the record filename.
This commit is contained in:
Joel Martin 2010-09-25 15:50:43 -05:00
parent 3b20e7a90f
commit a679a97d1b
3 changed files with 241 additions and 10 deletions

View File

@ -18,7 +18,7 @@ var that = {}, // Public API interface
// Pre-declare private functions used before definitions (jslint)
init_vars, updateState, init_msg, normal_msg, recv_message,
framebufferUpdate,
framebufferUpdate, print_stats,
pixelFormat, clientEncodings, fbUpdateRequest,
keyEvent, pointerEvent, clientCutText,
@ -61,6 +61,7 @@ var that = {}, // Public API interface
encHandlers = {},
encNames = {},
encStats = {}, // [rectCnt, rectCntTot]
ws = null, // Web Socket object
canvas = null, // Canvas object
@ -216,6 +217,7 @@ function constructor() {
for (i=0; i < encodings.length; i+=1) {
encHandlers[encodings[i][1]] = encHandlers[encodings[i][0]];
encNames[encodings[i][1]] = encodings[i][0];
encStats[encodings[i][1]] = [0, 0];
}
// Initialize canvas
try {
@ -309,6 +311,32 @@ init_vars = function() {
FBU.imgQ = []; // TIGHT_PNG image queue
mouse_buttonMask = 0;
mouse_arr = [];
// Clear the per connection encoding stats
for (i=0; i < encodings.length; i+=1) {
encStats[encodings[i][1]][0] = 0;
}
};
// Print statistics
print_stats = function() {
var i, encName, s;
Util.Info("Encoding stats for this connection:");
for (i=0; i < encodings.length; i+=1) {
s = encStats[encodings[i][1]];
if ((s[0] + s[1]) > 0) {
Util.Info(" " + encodings[i][0] + ": " +
s[0] + " rects");
}
}
Util.Info("Encoding stats since page load:");
for (i=0; i < encodings.length; i+=1) {
s = encStats[encodings[i][1]];
if ((s[0] + s[1]) > 0) {
Util.Info(" " + encodings[i][0] + ": "
+ s[1] + " rects");
}
}
};
//
@ -440,6 +468,8 @@ updateState = function(state, statusMsg) {
}, conf.disconnectTimeout * 1000);
}
print_stats();
// WebSocket.onclose transitions to 'disconnected'
break;
@ -944,7 +974,7 @@ normal_msg = function() {
};
framebufferUpdate = function() {
var now, hdr, fbu_rt_diff, last_bytes, last_rects, ret = true;
var now, hdr, fbu_rt_diff, ret = true, ctx;
if (FBU.rects === 0) {
//Util.Debug("New FBU: rQ.slice(0,20): " + rQ.slice(0,20));
@ -1011,15 +1041,17 @@ framebufferUpdate = function() {
}
timing.last_fbu = (new Date()).getTime();
last_bytes = rQlen();
last_rects = FBU.rects;
// false ret means need more data
ret = encHandlers[FBU.encoding]();
now = (new Date()).getTime();
timing.cur_fbu += (now - timing.last_fbu);
if (ret) {
encStats[FBU.encoding][0] += 1;
encStats[FBU.encoding][1] += 1;
}
if (FBU.rects === 0) {
if (((FBU.width === fb_width) &&
(FBU.height === fb_height)) ||
@ -1058,7 +1090,7 @@ framebufferUpdate = function() {
//
encHandlers.RAW = function display_raw() {
//Util.Debug(">> display_raw");
//Util.Debug(">> display_raw (" + rQlen() + " bytes)");
var cur_y, cur_height;
@ -1084,6 +1116,7 @@ encHandlers.RAW = function display_raw() {
FBU.rects -= 1;
FBU.bytes = 0;
}
//Util.Debug("<< display_raw (" + rQlen() + " bytes)");
return true;
};

197
tests/vnc_perf.html Normal file
View File

@ -0,0 +1,197 @@
<html>
<head>
<title>VNC Performance Benchmark</title>
<link rel="stylesheet" href="include/plain.css">
</head>
<body>
Passes: <input id='passes' style='width:50' value=3>&nbsp;
<input id='startButton' type='button' value='Start' style='width:100px'
onclick="start();" disabled>&nbsp;
<br><br>
Results:<br>
<textarea id="messages" style="font-size: 9;" cols=80 rows=15></textarea>
<br><br>
<div id="VNC_screen">
<div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">
<table border=0 width=100%><tr>
<td><div id="VNC_status">Loading</div></td>
</tr></table>
</div>
<canvas id="VNC_canvas" width="640px" height="20px">
Canvas not supported.
</canvas>
</div>
</body>
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script src="include/vnc.js"></script>
<script src="include/playback.js"></script>
<script src="data_multi.js"></script>
<script>
var start_time, VNC_frame_data, pass, passes, encIdx,
encOrder = ['raw', 'rre', 'hextile', 'tightpng', 'copyrect'],
encTot = {}, encMin = {}, encMax = {},
passCur, passTot, passMin, passMax;
function msg(str) {
console.log(str);
var cell = $('messages');
cell.innerHTML += str + "\n";
cell.scrollTop = cell.scrollHeight;
}
function dbgmsg(str) {
if (Util.get_logging() === 'debug') {
msg(str);
}
}
updateState = function (rfb, state, oldstate, mesg) {
switch (state) {
case 'failed':
case 'fatal':
msg("noVNC sent '" + state +
"' state during pass " + pass +
", iteration " + iteration +
" frame " + frame_idx);
test_state = 'failed';
break;
case 'loaded':
$('startButton').disabled = false;
break;
}
if (typeof mesg !== 'undefined') {
$('VNC_status').innerHTML = mesg;
}
}
function start() {
$('startButton').value = "Running";
$('startButton').disabled = true;
mode = 'perftest'; // full-speed
passes = $('passes').value;
pass = 1;
encIdx = 0;
// Render each encoding once for each pass
iterations = 1;
// Initialize stats counters
for (i = 0; i < encOrder.length; i++) {
enc = encOrder[i];
encTot[i] = 0;
encMin[i] = 2<<23; // Something sufficiently large
encMax[i] = 0;
}
passCur = 0;
passTot = 0;
passMin = 2<<23;
passMax = 0;
// Fire away
next_encoding();
}
function next_encoding() {
var encName;
if (encIdx >= encOrder.length) {
// Accumulate pass stats
if (passCur < passMin) {
passMin = passCur;
}
if (passCur > passMax) {
passMax = passCur;
}
msg("Pass " + pass + " took " + passCur + " ms");
passCur = 0;
encIdx = 0;
pass += 1;
if (pass > passes) {
// We are finished
rfb.get_canvas().stop(); // Shut-off event interception
$('startButton').disabled = false;
$('startButton').value = "Start";
finish_passes();
return; // We are finished, terminate
}
}
encName = encOrder[encIdx];
dbgmsg("Rendering pass " + pass + " encoding '" + encName + "'");
VNC_frame_data = VNC_frame_data_multi[encName];
iteration = 0;
start_time = (new Date()).getTime();
next_iteration();
}
// Finished rendering current encoding
function finish() {
var total_time, end_time = (new Date()).getTime();
total_time = end_time - start_time;
dbgmsg("Encoding " + encOrder[encIdx] + " took " + total_time + "ms");
passCur += total_time;
passTot += total_time;
// Accumulate stats
encTot[encIdx] += total_time;
if (total_time < encMin[encIdx]) {
encMin[encIdx] = total_time;
}
if (total_time > encMax[encIdx]) {
encMax[encIdx] = total_time;
}
encIdx += 1;
next_encoding();
}
function finish_passes() {
var i, enc, avg, passAvg;
msg("STATS (for " + passes + " passes)");
// Encoding stats
for (i = 0; i < encOrder.length; i++) {
enc = encOrder[i];
avg = (encTot[i] / passes).toFixed(1);
msg(" " + enc + ": " + encTot[i] + " ms, " +
encMin[i] + "/" + avg + "/" + encMax[i] +
" (min/avg/max)");
}
// Print pass stats
passAvg = (passTot / passes).toFixed(1);
msg("\n All passes: " + passTot + " ms, " +
passMin + "/" + passAvg + "/" + passMax +
" (min/avg/max)");
}
window.onload = function() {
var i, enc;
dbgmsg("Frame lengths:");
for (i = 0; i < encOrder.length; i++) {
enc = encOrder[i];
dbgmsg(" " + enc + ": " + VNC_frame_data_multi[enc].length);
}
rfb = RFB({'target': 'VNC_canvas',
'updateState': updateState});
rfb.testMode(send_array);
}
</script>
</html>

View File

@ -101,12 +101,13 @@ def do_proxy(client, target):
cpartial = cpartial + buf
def proxy_handler(client):
global target_host, target_port, options, rec
global target_host, target_port, options, rec, fname
if settings['record']:
handler_msg("opening record file: %s" % settings['record'])
rec = open("%s.%s" % (settings['record'],
settings['handler_id']), 'w+')
fname = "%s.%s" % (settings['record'],
settings['handler_id'])
handler_msg("opening record file: %s" % fname)
rec = open(fname, 'w+')
rec.write("var VNC_frame_data = [\n")
handler_msg("connecting to: %s:%s" % (target_host, target_port))