1 /*
2 Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3 All rights reserved.
4
5 This file is part of x11vnc.
6
7 x11vnc is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at
10 your option) any later version.
11
12 x11vnc is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with x11vnc; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20 or see <http://www.gnu.org/licenses/>.
21
22 In addition, as a special exception, Karl J. Runge
23 gives permission to link the code of its release of x11vnc with the
24 OpenSSL project's "OpenSSL" library (or with modified versions of it
25 that use the same license as the "OpenSSL" library), and distribute
26 the linked executables. You must obey the GNU General Public License
27 in all respects for all of the code used other than "OpenSSL". If you
28 modify this file, you may extend this exception to your version of the
29 file, but you are not obligated to do so. If you do not wish to do
30 so, delete this exception statement from your version.
31 */
32
33 /* -- rates.c -- */
34
35 #include "x11vnc.h"
36 #include "xwrappers.h"
37 #include "scan.h"
38
39 int measure_speeds = 1;
40 int speeds_net_rate = 0;
41 int speeds_net_rate_measured = 0;
42 int speeds_net_latency = 0;
43 int speeds_net_latency_measured = 0;
44 int speeds_read_rate = 0;
45 int speeds_read_rate_measured = 0;
46
47
48 int get_cmp_rate(void);
49 int get_raw_rate(void);
50 void initialize_speeds(void);
51 int get_read_rate(void);
52 int link_rate(int *latency, int *netrate);
53 int get_net_rate(void);
54 int get_net_latency(void);
55 void measure_send_rates(int init);
56
57
58 static void measure_display_hook(rfbClientPtr cl);
59 static int get_rate(int which);
60 static int get_latency(void);
61
62
measure_display_hook(rfbClientPtr cl)63 static void measure_display_hook(rfbClientPtr cl) {
64 ClientData *cd = (ClientData *) cl->clientData;
65 if (! cd) {
66 return;
67 }
68 dtime0(&cd->timer);
69 }
70
get_rate(int which)71 static int get_rate(int which) {
72 rfbClientIteratorPtr iter;
73 rfbClientPtr cl;
74 int irate, irate_min = 1; /* 1 KB/sec */
75 int irate_max = 100000; /* 100 MB/sec */
76 int count = 0;
77 double slowest = -1.0, rate;
78 static double save_rate = 1000 * NETRATE0;
79
80 if (!screen) {
81 return 0;
82 }
83
84 iter = rfbGetClientIterator(screen);
85 while( (cl = rfbClientIteratorNext(iter)) ) {
86 ClientData *cd = (ClientData *) cl->clientData;
87
88 if (! cd) {
89 continue;
90 }
91 if (cl->state != RFB_NORMAL) {
92 continue;
93 }
94 if (cd->send_cmp_rate == 0.0 || cd->send_raw_rate == 0.0) {
95 continue;
96 }
97 count++;
98
99 if (which == 0) {
100 rate = cd->send_cmp_rate;
101 } else {
102 rate = cd->send_raw_rate;
103 }
104 if (slowest == -1.0 || rate < slowest) {
105 slowest = rate;
106 }
107
108 }
109 rfbReleaseClientIterator(iter);
110
111 if (! count) {
112 return NETRATE0;
113 }
114
115 if (slowest == -1.0) {
116 slowest = save_rate;
117 } else {
118 save_rate = slowest;
119 }
120
121 irate = (int) (slowest/1000.0);
122 if (irate < irate_min) {
123 irate = irate_min;
124 }
125 if (irate > irate_max) {
126 irate = irate_max;
127 }
128 if (0) fprintf(stderr, "get_rate(%d) %d %.3f/%.3f\n", which, irate, save_rate, slowest);
129
130 return irate;
131 }
132
get_latency(void)133 static int get_latency(void) {
134 rfbClientIteratorPtr iter;
135 rfbClientPtr cl;
136 int ilat, ilat_min = 1; /* 1 ms */
137 int ilat_max = 2000; /* 2 sec */
138 double slowest = -1.0, lat;
139 static double save_lat = ((double) LATENCY0)/1000.0;
140 int count = 0;
141
142 if (!screen) {
143 return 0;
144 }
145
146 iter = rfbGetClientIterator(screen);
147 while( (cl = rfbClientIteratorNext(iter)) ) {
148 ClientData *cd = (ClientData *) cl->clientData;
149
150 if (! cd) {
151 continue;
152 }
153 if (cl->state != RFB_NORMAL) {
154 continue;
155 }
156 if (cd->latency == 0.0) {
157 continue;
158 }
159 count++;
160
161 lat = cd->latency;
162 if (slowest == -1.0 || lat > slowest) {
163 slowest = lat;
164 }
165 }
166 rfbReleaseClientIterator(iter);
167
168 if (! count) {
169 return LATENCY0;
170 }
171
172 if (slowest == -1.0) {
173 slowest = save_lat;
174 } else {
175 save_lat = slowest;
176 }
177
178 ilat = (int) (slowest * 1000.0);
179 if (ilat < ilat_min) {
180 ilat = ilat_min;
181 }
182 if (ilat > ilat_max) {
183 ilat = ilat_max;
184 }
185
186 return ilat;
187 }
188
get_cmp_rate(void)189 int get_cmp_rate(void) {
190 return get_rate(0);
191 }
192
get_raw_rate(void)193 int get_raw_rate(void) {
194 return get_rate(1);
195 }
196
initialize_speeds(void)197 void initialize_speeds(void) {
198 char *s, *s_in, *p;
199 int i;
200
201 speeds_read_rate = 0;
202 speeds_net_rate = 0;
203 speeds_net_latency = 0;
204 if (! speeds_str || *speeds_str == '\0') {
205 s_in = strdup("");
206 } else {
207 s_in = strdup(speeds_str);
208 }
209
210 if (!strcmp(s_in, "modem")) {
211 s = strdup("6,4,200");
212 } else if (!strcmp(s_in, "dsl")) {
213 s = strdup("6,100,50");
214 } else if (!strcmp(s_in, "lan")) {
215 s = strdup("6,5000,1");
216 } else {
217 s = strdup(s_in);
218 }
219
220 p = strtok(s, ",");
221 i = 0;
222 while (p) {
223 double val;
224 if (*p != '\0') {
225 val = atof(p);
226 if (i==0) {
227 speeds_read_rate = (int) 1000000 * val;
228 } else if (i==1) {
229 speeds_net_rate = (int) 1000 * val;
230 } else if (i==2) {
231 speeds_net_latency = (int) val;
232 }
233 }
234 i++;
235 p = strtok(NULL, ",");
236 }
237 free(s);
238 free(s_in);
239
240 if (! speeds_read_rate) {
241 int n = 0;
242 double dt, timer;
243 #ifdef MACOSX
244 if (macosx_console && macosx_read_opengl && fullscreen) {
245 copy_image(fullscreen, 0, 0, 0, 0);
246 usleep(10 * 1000);
247 }
248 #endif
249
250 dtime0(&timer);
251 if (fullscreen) {
252 copy_image(fullscreen, 0, 0, 0, 0);
253 n = fullscreen->bytes_per_line * fullscreen->height;
254 } else if (scanline) {
255 copy_image(scanline, 0, 0, 0, 0);
256 n = scanline->bytes_per_line * scanline->height;
257 }
258 dt = dtime(&timer);
259 if (n && dt > 0.0) {
260 double rate = ((double) n) / dt;
261 speeds_read_rate_measured = (int) (rate/1000000.0);
262 if (speeds_read_rate_measured < 1) {
263 speeds_read_rate_measured = 1;
264 } else {
265 rfbLog("fb read rate: %d MB/sec\n",
266 speeds_read_rate_measured);
267 }
268 }
269 }
270 }
271
get_read_rate(void)272 int get_read_rate(void) {
273 if (speeds_read_rate) {
274 return speeds_read_rate;
275 }
276 if (speeds_read_rate_measured) {
277 return speeds_read_rate_measured;
278 }
279 return 0;
280 }
281
link_rate(int * latency,int * netrate)282 int link_rate(int *latency, int *netrate) {
283 *latency = get_net_latency();
284 *netrate = get_net_rate();
285
286 if (speeds_str) {
287 if (!strcmp(speeds_str, "modem")) {
288 return LR_DIALUP;
289 } else if (!strcmp(speeds_str, "dsl")) {
290 return LR_BROADBAND;
291 } else if (!strcmp(speeds_str, "lan")) {
292 return LR_LAN;
293 }
294 }
295
296 if (*latency == LATENCY0 && *netrate == NETRATE0) {
297 return LR_UNSET;
298 } else if (*latency > 150 || *netrate < 20) {
299 return LR_DIALUP;
300 } else if (*latency > 50 || *netrate < 150) {
301 return LR_BROADBAND;
302 } else if (*latency < 10 && *netrate > 300) {
303 return LR_LAN;
304 } else {
305 return LR_UNKNOWN;
306 }
307 }
308
get_net_rate(void)309 int get_net_rate(void) {
310 int spm = speeds_net_rate_measured;
311 if (speeds_net_rate) {
312 return speeds_net_rate;
313 }
314 if (! spm || spm == NETRATE0) {
315 speeds_net_rate_measured = get_cmp_rate();
316 }
317 if (speeds_net_rate_measured) {
318 return speeds_net_rate_measured;
319 }
320 return 0;
321 }
322
get_net_latency(void)323 int get_net_latency(void) {
324 int spm = speeds_net_latency_measured;
325 if (speeds_net_latency) {
326 return speeds_net_latency;
327 }
328 if (! spm || spm == LATENCY0) {
329 speeds_net_latency_measured = get_latency();
330 }
331 if (speeds_net_latency_measured) {
332 return speeds_net_latency_measured;
333 }
334 return 0;
335 }
336
measure_send_rates(int init)337 void measure_send_rates(int init) {
338 double cmp_rate, raw_rate;
339 static double now, start = 0.0;
340 static rfbDisplayHookPtr orig_display_hook = NULL;
341 double cmp_max = 1.0e+08; /* 100 MB/sec */
342 double cmp_min = 1000.0; /* 9600baud */
343 double lat_max = 5.0; /* 5 sec */
344 double lat_min = .0005; /* 0.5 ms */
345 int min_cmp = 10000, nclients;
346 rfbClientIteratorPtr iter;
347 rfbClientPtr cl0, cl;
348 int msg = 0, clcnt0 = 0, cc;
349 int db = 0, ouch_db = 0, ouch = 0;
350
351 if (! measure_speeds) {
352 return;
353 }
354 if (speeds_net_rate && speeds_net_latency) {
355 return;
356 }
357 if (!client_count) {
358 return;
359 }
360
361 if (! orig_display_hook) {
362 orig_display_hook = screen->displayHook;
363 }
364
365 if (start == 0.0) {
366 dtime(&start);
367 }
368
369 dtime0(&now);
370 if (now < last_client_gone+4.0) {
371 return;
372 }
373 now = now - start;
374
375 nclients = 0;
376
377 if (!screen) {
378 return;
379 }
380
381 cl0 = NULL;
382 iter = rfbGetClientIterator(screen);
383 while( (cl = rfbClientIteratorNext(iter)) ) {
384 ClientData *cd = (ClientData *) cl->clientData;
385
386 if (! cd) {
387 continue;
388 }
389 if (cd->send_cmp_rate > 0.0) {
390 continue;
391 }
392 if (cl->onHold) {
393 continue;
394 }
395 nclients++;
396 if (cl0 == NULL) {
397 cl0 = cl;
398 }
399 }
400 rfbReleaseClientIterator(iter);
401
402 cl = cl0;
403 cc = 0;
404
405 while (cl != NULL && cc++ == 0) {
406 int defer, i, cbs, rbs;
407 char *httpdir;
408 double dt, dt1 = 0.0, dt2, dt3;
409 double tm, spin_max = 15.0, spin_lat_max = 1.5;
410 int got_t2 = 0, got_t3 = 0;
411 ClientData *cd = (ClientData *) cl->clientData;
412
413 #if 0
414 for (i=0; i<MAX_ENCODINGS; i++) {
415 cbs += cl->bytesSent[i];
416 }
417 rbs = cl->rawBytesEquivalent;
418 #else
419 #if LIBVNCSERVER_HAS_STATS
420 cbs = rfbStatGetSentBytes(cl);
421 rbs = rfbStatGetSentBytesIfRaw(cl);
422 #endif
423 #endif
424
425 if (init) {
426
427 if (db) fprintf(stderr, "%d client num rects req: %d mod: %d cbs: %d "
428 "rbs: %d dt1: %.3f t: %.3f\n", init,
429 (int) sraRgnCountRects(cl->requestedRegion),
430 (int) sraRgnCountRects(cl->modifiedRegion), cbs, rbs, dt1, now);
431
432 cd->timer = dnow();
433 cd->cmp_bytes_sent = cbs;
434 cd->raw_bytes_sent = rbs;
435 continue;
436 }
437
438 /* first part of the bulk transfer of initial screen */
439 dt1 = dtime(&cd->timer);
440
441 if (db) fprintf(stderr, "%d client num rects req: %d mod: %d cbs: %d "
442 "rbs: %d dt1: %.3f t: %.3f\n", init,
443 (int) sraRgnCountRects(cl->requestedRegion),
444 (int) sraRgnCountRects(cl->modifiedRegion), cbs, rbs, dt1, now);
445
446 if (dt1 <= 0.0) {
447 continue;
448 }
449
450 cbs = cbs - cd->cmp_bytes_sent;
451 rbs = rbs - cd->raw_bytes_sent;
452
453 if (cbs < min_cmp) {
454 continue;
455 }
456
457 if (ouch_db) fprintf(stderr, "START-OUCH: %d\n", client_count);
458 clcnt0 = client_count;
459 #define OUCH ( ouch || (ouch = (!client_count || client_count != clcnt0 || dnow() < last_client_gone+4.0)) )
460
461 rfbPE(1000);
462 if (OUCH && ouch_db) fprintf(stderr, "***OUCH-A\n");
463 if (OUCH) continue;
464
465 if (use_threads) LOCK(cl->updateMutex);
466
467 if (sraRgnCountRects(cl->modifiedRegion)) {
468 rfbPE(1000);
469 if (OUCH && ouch_db) fprintf(stderr, "***OUCH-B\n");
470 if (use_threads) UNLOCK(cl->updateMutex);
471 if (OUCH) continue;
472 }
473
474 if (use_threads) UNLOCK(cl->updateMutex);
475
476 defer = screen->deferUpdateTime;
477 httpdir = screen->httpDir;
478 screen->deferUpdateTime = 0;
479 screen->httpDir = NULL;
480
481 /* mark a small rectangle: */
482 mark_rect_as_modified(0, 0, 16, 16, 1);
483
484 dtime0(&tm);
485
486 dt2 = 0.0;
487 dt3 = 0.0;
488
489 if (dt1 < 0.25) {
490 /* try to cut it down to avoid long pauses. */
491 spin_max = 5.0;
492 }
493
494 /* when req1 = 1 mod1 == 0, end of 2nd part of bulk transfer */
495 while (1) {
496 int req0, req1, mod0, mod1;
497
498 if (OUCH && ouch_db) fprintf(stderr, "***OUCH-C1\n");
499 if (OUCH) break;
500
501 if (use_threads) LOCK(cl->updateMutex);
502
503 req0 = sraRgnCountRects(cl->requestedRegion);
504 mod0 = sraRgnCountRects(cl->modifiedRegion);
505
506 if (use_threads) UNLOCK(cl->updateMutex);
507
508 if (use_threads) {
509 usleep(1000);
510 } else {
511 if (mod0) {
512 rfbPE(1000);
513 } else {
514 rfbCFD(1000);
515 }
516 }
517 dt = dtime(&tm);
518 dt2 += dt;
519 if (dt2 > spin_max) {
520 break;
521 }
522
523 if (OUCH && ouch_db) fprintf(stderr, "***OUCH-C2\n");
524 if (OUCH) break;
525
526 if (use_threads) LOCK(cl->updateMutex);
527
528 req1 = sraRgnCountRects(cl->requestedRegion);
529 mod1 = sraRgnCountRects(cl->modifiedRegion);
530
531 if (use_threads) UNLOCK(cl->updateMutex);
532
533 if (db) fprintf(stderr, "dt2 calc: num rects req: %d/%d mod: %d/%d "
534 #if LIBVNCSERVER_HAS_STATS
535 "fbu-sent: %d dt: %.4f dt2: %.4f tm: %.4f\n",
536 req0, req1, mod0, mod1,
537 rfbStatGetMessageCountSent(cl, rfbFramebufferUpdate),
538 #else
539 "dt: %.4f dt2: %.4f tm: %.4f\n",
540 req0, req1, mod0, mod1,
541 #endif
542 dt, dt2, tm);
543 if (req1 != 0 && mod1 == 0) {
544 got_t2 = 1;
545 break;
546 }
547 }
548 if (OUCH && ouch_db) fprintf(stderr, "***OUCH-D\n");
549 if (OUCH) goto ouch;
550
551 if (! got_t2) {
552 dt2 = 0.0;
553 } else {
554 int tr, trm = 3;
555 double dts[10];
556
557 /*
558 * Note: since often select(2) cannot sleep
559 * less than 1/HZ (e.g. 10ms), the resolution
560 * of the latency may be messed up by something
561 * of this order. Effect may occur on both ends,
562 * i.e. the viewer may not respond immediately.
563 */
564
565 for (tr = 0; tr < trm; tr++) {
566 usleep(5000);
567
568 /* mark a 2nd small rectangle: */
569 mark_rect_as_modified(0, 0, 16, 16, 1);
570 i = 0;
571 dtime0(&tm);
572 dt3 = 0.0;
573
574 /*
575 * when req1 > 0 and mod1 == 0, we say
576 * that is the "ping" time.
577 */
578 while (1) {
579 int req0, req1, mod0, mod1;
580
581 if (use_threads) LOCK(cl->updateMutex);
582
583 req0 = sraRgnCountRects(cl->requestedRegion);
584 mod0 = sraRgnCountRects(cl->modifiedRegion);
585
586 if (use_threads) UNLOCK(cl->updateMutex);
587
588 if (i == 0) {
589 rfbPE(0);
590 } else {
591 if (use_threads) {
592 usleep(1000);
593 } else {
594 /* try to get it all */
595 rfbCFD(1000*1000);
596 }
597 }
598 if (OUCH && ouch_db) fprintf(stderr, "***OUCH-E\n");
599 if (OUCH) goto ouch;
600 dt = dtime(&tm);
601 i++;
602
603 dt3 += dt;
604 if (dt3 > spin_lat_max) {
605 break;
606 }
607
608 if (use_threads) LOCK(cl->updateMutex);
609
610 req1 = sraRgnCountRects(cl->requestedRegion);
611 mod1 = sraRgnCountRects(cl->modifiedRegion);
612
613 if (use_threads) UNLOCK(cl->updateMutex);
614
615 if (db) fprintf(stderr, "dt3 calc: num rects req: %d/%d mod: %d/%d "
616 #if LIBVNCSERVER_HAS_STATS
617 "fbu-sent: %d dt: %.4f dt3: %.4f tm: %.4f\n",
618 req0, req1, mod0, mod1,
619 rfbStatGetMessageCountSent(cl, rfbFramebufferUpdate),
620 #else
621 "dt: %.4f dt3: %.4f tm: %.4f\n",
622 req0, req1, mod0, mod1,
623 #endif
624 dt, dt3, tm);
625
626 if (req1 != 0 && mod1 == 0) {
627 dts[got_t3++] = dt3;
628 break;
629 }
630 }
631 }
632
633 if (! got_t3) {
634 dt3 = 0.0;
635 } else {
636 if (got_t3 == 1) {
637 dt3 = dts[0];
638 } else if (got_t3 == 2) {
639 dt3 = dts[1];
640 } else {
641 if (dts[2] > 0.0) {
642 double rat = dts[1]/dts[2];
643 if (rat > 0.5 && rat < 2.0) {
644 dt3 = dts[1]+dts[2];
645 dt3 *= 0.5;
646 } else {
647 dt3 = dts[1];
648 }
649 } else {
650 dt3 = dts[1];
651 }
652 }
653 }
654 }
655
656 ouch:
657
658 screen->deferUpdateTime = defer;
659 screen->httpDir = httpdir;
660
661 if (OUCH && ouch_db) fprintf(stderr, "***OUCH-F\n");
662 if (OUCH) break;
663
664 dt = dt1 + dt2;
665
666
667 if (dt3 <= dt2/2.0) {
668 /* guess only 1/2 a ping for reply... */
669 dt = dt - dt3/2.0;
670 }
671
672 cmp_rate = cbs/dt;
673 raw_rate = rbs/dt;
674
675 if (cmp_rate > cmp_max) {
676 cmp_rate = cmp_max;
677 }
678 if (cmp_rate <= cmp_min) {
679 cmp_rate = cmp_min;
680 }
681
682 cd->send_cmp_rate = cmp_rate;
683 cd->send_raw_rate = raw_rate;
684
685 if (dt3 > lat_max) {
686 dt3 = lat_max;
687 }
688 if (dt3 <= lat_min) {
689 dt3 = lat_min;
690 }
691
692 cd->latency = dt3;
693
694 rfbLog("client %d network rate %.1f KB/sec (%.1f eff KB/sec)\n",
695 cd->uid, cmp_rate/1000.0, raw_rate/1000.0);
696 rfbLog("client %d latency: %.1f ms\n", cd->uid, 1000.0*dt3);
697 rfbLog("dt1: %.4f, dt2: %.4f dt3: %.4f bytes: %d\n",
698 dt1, dt2, dt3, cbs);
699 msg = 1;
700 }
701
702 if (msg) {
703 int link, latency, netrate;
704 char *str = "error";
705
706 link = link_rate(&latency, &netrate);
707 if (link == LR_UNSET) {
708 str = "LR_UNSET";
709 } else if (link == LR_UNKNOWN) {
710 str = "LR_UNKNOWN";
711 } else if (link == LR_DIALUP) {
712 str = "LR_DIALUP";
713 } else if (link == LR_BROADBAND) {
714 str = "LR_BROADBAND";
715 } else if (link == LR_LAN) {
716 str = "LR_LAN";
717 }
718 rfbLog("link_rate: %s - %d ms, %d KB/s\n", str, latency,
719 netrate);
720 }
721
722 if (init) {
723 if (nclients) {
724 screen->displayHook = measure_display_hook;
725 }
726 } else {
727 screen->displayHook = orig_display_hook;
728 }
729 }
730
731