docs: minor update in readme description, several sections
[netsniff-ng.git] / src / rcv_rtp.c
blob336a6e0d11915427c67298a0c6fbe479b9f359c9
1 /*
2 * Mausezahn - A fast versatile traffic generator
3 * Copyright (C) 2008-2010 Herbert Haas
4 *
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License version 2 as published by the
7 * Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12 * details.
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html
20 ///////////////////////////////////////////////////
21 //
22 // Table of contents:
23 //
24 // rcv_rtp_init()
25 // rcv_rtp()
26 // compare4B()
27 // got_rtp_packet()
28 // print_jitterbar()
29 //
31 ///////////////////////////////////////////////////
32 //
33 // Documentation about RTP traffic analysis
34 //
35 // See http://wiki.wireshark.org/RTP_statistics
37 //
39 #include "mz.h"
40 #include "mops.h"
42 // Initialize the rcv_rtp process: Read user parameters and initialize globals
43 int rcv_rtp_init()
45 char argval[MAX_PAYLOAD_SIZE];
46 char dummy[512];
47 int len;
48 u_int32_t port = 30000; // 4-byte variable to catch errors, see below
50 int ssrc_s = 0;
52 // Help text
54 if (getarg(tx.arg_string,"help", NULL)==1) {
55 fprintf(stderr,"\n"
56 MAUSEZAHN_VERSION
57 "\n"
58 "| RTP reception for jitter measurements.\n"
59 "|\n"
60 "| Parameters:\n"
61 "|\n"
62 "| bar ...... Display modes: By default 'bar' is used and shows the RFC 3550 jitter as\n"
63 "| ASCII-based waterfall diagram.\n"
64 "| txt ...... The 'txt' mode prints all measurement values numerically upon each\n"
65 "| measurement interval.\n"
66 // "| curse ...... Shows all values and a diagram within an resizesable ncurses window.\n"
67 "|\n"
68 "| ssrc ....... Listen to the stream with the specified SSRC. You must specify this\n"
69 "| when there are concurrent streams, e. g. one in each direction.\n"
70 "|\n"
71 "| log ....... Write moving average also in a datafile (not only on terminal).\n"
72 "| logg ....... Like log but additionally write detailed real-time statistics in a data file\n"
73 "| path = <path> ....... Path to directory where datafiles can be stored (default: local directory).\n"
74 "| num = <10-%d> ...... number of packets to be received for averaging (default: %d).\n"
75 "| port = <0-65535> ....... Change if RTP packets are sent to a different port than 30000 (default).\n"
76 "|\n"
77 "| Note:\n"
78 "|\n"
79 "| Mausezahn can log actual realtime measurement data in data files (in the specified path or\n"
80 "| current directory) but always prints the moving average on the command line (this can be disabled\n"
81 "| using the 'quiet' option (-q)).\n"
82 "|\n"
83 "| The realtime data file(s) consist of two columns:\n"
84 "|\n"
85 "| 1. relative timestamp in usec\n"
86 "| 2. 'true' jitter in usec\n"
87 "|\n"
88 "| where the 'true' jitter is calculated using the (relative) timestamps inside the received\n"
89 "| packets t(i) and the (relative) timestamps T(i) observed locally when packets are received using\n"
90 "| the formula:\n"
91 "|\n"
92 "| jitter(i) = [T(i) - T(i-1)] - [t(i) - t(i-1)] + jitter(i-1) .\n"
93 "|\n"
94 "| This method has two advantages: (i) we do not need to synchronize the clocks of sender and\n"
95 "| receiver, and (ii) the TX-side jitter (mainly caused by the kernel-scheduler) is subtracted\n"
96 "| so that we primarily measure the jitter caused by the network.\n"
97 "| \n"
98 "| The data files consist of seven columns:\n"
99 "| \n"
100 "| 1. relative timestamp in seconds\n"
101 "| 2. minimum jitter\n"
102 "| 3. average jitter\n"
103 "| 4. minimum jitter\n"
104 "| 5. estimated jitter variance according RFC-3550\n"
105 "| 6. packet drop count (total)\n"
106 "| 7. packet disorder count (total)\n"
107 "| \n"
108 "| All measurement values are done in usec and refer to the current set of samples (see parameter 'num').\n"
109 "| Note that an RFC-conform jitter (smoothed mean deviation) is calculated and collected in column five.\n"
110 "| The drop value refers to the current measurement window, while the total drop and disorder values are\n"
111 "| calculated using some weird estimation functions; the goal was to provide a 'time-less' estimation\n"
112 "| while being able to automatically resynchronize to a re-started RTP measurement stream.\n"
113 "| \n"
114 "| EXAMPLE USAGE:\n"
115 "|\n"
116 "| At the TX-station enter:\n"
117 "|\n"
118 "| # mz eth0 -t rtp -B 10.3.3.42 (optionally change rate via -d option, payload size via pld command)\n"
119 "|\n"
120 "| At the RX-station (10.3.3.42) enter:\n"
121 "|\n"
122 "| # mz eth0 -T rtp \"log, path=/tmp/mz/\"\n"
123 "|\n"
124 "\n", TIME_COUNT_MAX, TIME_COUNT);
125 exit(0);
129 // check argstring for arguments
131 if (getarg(tx.arg_string,"bar", NULL)==1) {
132 rtp_dm = BAR;
135 if (getarg(tx.arg_string,"txt", NULL)==1) {
136 rtp_dm = TEXT;
139 if (getarg(tx.arg_string,"curses", NULL)==1) {
140 rtp_dm = BAR; //NCURSES;
141 fprintf(stderr, " XXX This Mausezahn version does not support ncurses windows.\n");
144 if (getarg(tx.arg_string,"width", argval)==1) {
145 if (rtp_dm != BAR) {
146 fprintf(stderr, " mz/rcv_rtp: The 'width' parameter requires the display mode 'bar'\n");
147 return -1;
149 bwidth = (int) str2int(argval); // [TODO] bwidth is currently not used
150 if (bwidth>RCV_RTP_MAX_BAR_WIDTH) {
151 fprintf(stderr, "The width must not exceed %i\n",
152 RCV_RTP_MAX_BAR_WIDTH);
153 return -1;
155 } else bwidth=80;
157 if (getarg(tx.arg_string,"ssrc", argval)==1) {
158 ssrc_s = str2hex(argval, mz_ssrc, 4);
159 if (ssrc_s<0) {
160 fprintf(stderr, " mz/rtp_rcv: invalid ssrc!\n");
161 return -1;
165 if (getarg(tx.arg_string,"log", NULL)==1) {
166 rtp_log = 1;
169 if (getarg(tx.arg_string,"logg", NULL)==1) {
170 rtp_log = 2;
174 if (getarg(tx.arg_string,"path", argval)==1) {
175 len = strlen(argval);
176 if (len>128) {
177 fprintf(stderr, " mz/Error: path must not exceed 128 characters!\n");
178 exit (-1);
180 if (argval[len-1]!='/') {
181 strncat(argval, "/",1); // ensure that all paths end with "/"
183 strncpy(path, argval, 128);
187 if (getarg(tx.arg_string,"num", argval)==1) {
188 gind_max = (u_int32_t) str2int(argval);
189 if (gind_max > TIME_COUNT_MAX) {
190 gind_max = TIME_COUNT_MAX;
191 fprintf(stderr, " mz/Warning: num range is 10..%d. Will reset to %d.\n",
192 TIME_COUNT_MAX, TIME_COUNT_MAX);
194 else if (gind_max < 10) {
195 gind_max = 10;
196 fprintf(stderr, " mz/Warning: num range is 10..%d. Will reset to 10.\n",
197 TIME_COUNT_MAX);
202 // initialize global filter string
203 strncpy (rtp_filter_str, "udp dst port 30000", 64);
205 if (getarg(tx.arg_string,"port", argval)==1) {
206 port = (u_int32_t) str2int(argval);
207 if (port>65535) {
208 port = 30000;
209 fprintf(stderr, " mz: Too large port number! Reset to default port (30000).\n");
212 sprintf(rtp_filter_str, "udp dst port %u", (unsigned int) port);
216 if (ssrc_s==0) str2hex("ca:fe:fe:ed", mz_ssrc, 4);
218 // open file
220 if (rtp_log) {
221 // get a new filename
222 timestamp_human(filename, "rtp_avg_");
223 strncpy(dummy, path, 128);
224 strncat(dummy, filename, 64);
225 if (verbose) fprintf(stderr, " mz: Will open %s\n", dummy);
227 fp = fopen (dummy, "w+");
229 if (fp == NULL) {
230 perror("fopen");
231 exit (-1);
234 gtotal=0; // counts written data blocks
235 fprintf(fp, "# Average jitter measurements made by Mausezahn " MAUSEZAHN_VERSION_SHORT ".\n");
236 fprintf(fp, "# Timestamp is in seconds, all other values in microseconds.\n");
237 fprintf(fp, "# Column values (from left to right):\n");
238 fprintf(fp, "# 1. Timestamp\n"
239 "# 2. min_jitter\n"
240 "# 3. avg_jitter\n"
241 "# 4. max_jitter\n"
242 "# 5. estimated jitter according RFC-3550\n"
243 "# 6. packet drop count (total)\n"
244 "# 7. packet disorder count (total)\n");
247 ///////////// also detailed log required /////////////
248 if (rtp_log==2) {
249 // get a new filename
250 timestamp_human(filename, "rtp_rt_");
251 strncpy(dummy, path, 128);
252 strncat(dummy, filename, 64);
253 if (verbose) fprintf(stderr, " mz: Will open %s\n", dummy);
255 fp2 = fopen (dummy, "w+");
257 if (fp2 == NULL) {
258 perror("fopen");
259 exit (-1);
262 fprintf(fp2, "# Jitter measurements by Mausezahn " MAUSEZAHN_VERSION_SHORT ".\n");
263 fprintf(fp2, "# Timestamp (usec) , true jitter (nsec)\n");
268 drop=0;
269 dis=0;
270 jitter_rfc=0;
272 return 0;
281 ////////////////////////////////////////////////////////////////////////////////////////////
283 // Defines the pcap handler and the callback function
284 int rcv_rtp()
286 char errbuf[PCAP_ERRBUF_SIZE];
288 pcap_t *p;
290 struct bpf_program filter;
294 p = pcap_open_live (tx.device,
295 MAXBYTES_TO_READ, // max num of bytes to read
296 0, // 1 if promiscuous mode
297 PCAP_READ_TIMEOUT_MSEC, // read timeout in msec
298 errbuf);
300 if (p == NULL)
302 fprintf(stderr," mz/rcv_rtp: %s\n",errbuf);
303 exit(1);
307 if ( pcap_compile(p,
308 &filter, // the compiled version of the filter
309 rtp_filter_str, // text version of filter
310 0, // 1 = optimize
311 0) // netmask
312 == -1)
314 fprintf(stderr," mz/rcv_rtp: Error calling pcap_compile\n");
315 exit(1);
320 if ( pcap_setfilter(p, &filter) == -1)
322 fprintf(stderr," mz/rcv_rtp: Error setting filter\n");
323 pcap_geterr(p);
324 exit(1);
327 again:
330 pcap_loop (p,
331 1, // number of packets to wait
332 got_rtp_packet, // name of callback function
333 NULL); // optional additional arguments for callback function
336 goto again;
339 // TODO: Currently we never reach this point!
340 fprintf(stderr, " mz: receiving of RTP finished.\n");
341 pcap_close(p);
343 return 0;
349 // Compares two 4-byte variables byte by byte
350 // returns 0 if identical, 1 if different
351 inline int compare4B (u_int8_t *ip1, u_int8_t *ip2)
353 if (*ip1 != *ip2) return 1;
354 if (*(ip1+1) != *(ip2+1)) return 1;
355 if (*(ip1+2) != *(ip2+2)) return 1;
356 if (*(ip1+3) != *(ip2+3)) return 1;
358 return 0;
365 // Handler function to do something when RTP messages are received
366 void got_rtp_packet(u_char *args,
367 const struct pcap_pkthdr *header, // statistics about the packet (see 'struct pcap_pkthdr')
368 const u_char *packet) // the bytestring sniffed
370 const struct struct_ethernet *ethernet;
371 const struct struct_ip *ip;
372 const struct struct_udp *udp;
373 const struct struct_rtp *rtp;
375 int size_ethernet = sizeof(struct struct_ethernet);
376 int size_ip = sizeof(struct struct_ip);
377 int size_udp = sizeof(struct struct_udp);
378 // int size_rtp = sizeof(struct struct_rtp);
380 ethernet = (struct struct_ethernet*)(packet);
381 ip = (struct struct_ip*)(packet+size_ethernet);
382 udp = (struct struct_udp*)(packet+size_ethernet+size_ip);
383 rtp = (struct struct_rtp*)(packet+size_ethernet+size_ip+size_udp);
385 struct mz_timestamp
386 deltaTX,
387 deltaRX;
389 u_int32_t
391 jitter_abs,
392 jitter_avg,
393 jitter_max,
394 jitter_min,
395 curtime=0;
397 int32_t ltemp;
399 u_int8_t *x,*y;
401 char dummy[256];
402 char ts_hms[10];
403 unsigned char *dum;
404 static u_int32_t drop_last=0, drop_prev=0;
405 int s1, s2;
407 // check if the RTP packet is really from a Mausezahn instance:
408 if (compare4B((u_int8_t*) &rtp->ssrc, mz_ssrc)==0) {
409 // we got a valid RTP packet from a Mausezahn instance
410 // Get current SQNR and store it in 'sqnr_cur' in host byte order
411 x = (u_int8_t*) &rtp->sqnr;
412 y = (u_int8_t*) &sqnr_cur;
414 *y = *(x+1);
415 y++;
416 *y = *x;
418 /////////////////////////////////////////////////////////////////////
419 // Packet drop and disorder detection:
420 if (sqnr0_flag) {
421 if (sqnr_next==sqnr_cur) { // correct SQNR received
422 sqnr_next++;
423 sqnr_last++;
424 } else if (sqnr_last>sqnr_cur) { // disordered sequence
425 dis++;
426 if (drop) drop--; // don't get below 0
427 else { // drop reached zero: resync (restarted RTP stream?)
428 sqnr_last = sqnr_cur;
429 sqnr_next = (++sqnr_last);
430 dis=0;
432 } else { // packet drop
433 drop += (sqnr_cur-sqnr_next);
434 sqnr_last = sqnr_cur;
435 sqnr_next = (++sqnr_last);
437 } else {
438 // initial synchronization with observed SQNR:
439 sqnr_last = sqnr_cur;
440 sqnr_next = (++sqnr_last);
441 sqnr0_flag++;
444 /////////////////////////////////////////////////////////////////////
447 // Get RX timestamp from pcap header
448 timeRX[gind].sec = header->ts.tv_sec;
449 timeRX[gind].nsec = header->ts.tv_usec *1000;
451 // Get TX timestamp from the packet
452 mops_hton4((u_int32_t*) &rtp->time_sec, (u_int8_t*) &timeTX[gind].sec);
453 mops_hton4((u_int32_t*) &rtp->time_nsec, (u_int8_t*) &timeTX[gind].nsec);
455 // printf("%li %li\n", (long int) timeTX[gind].sec, (long int) timeTX[gind].nsec);
457 gind++;
459 ////////////////////////////////////////////////////////////////
460 if (gind == gind_max) { // array full, now calculate statistics
461 gind=0;
462 gtotal++;
464 jitter_avg = 0;
465 jitter_min = 0xffffffff;
466 jitter_max = 0;
469 ///////////////////////////////////////////////////////
470 // calculate deltas and jitters
471 for (i=2; i<gind_max; i++) { // omit the first 2 data
472 // entries because of
473 // artificial high TX-delta!
475 ///////////////////////////////////////////////
476 // calculate deltaTX and deltaRX
478 s1=timestamp_subtract (&timeTX[i], &timeTX[i-1], &deltaTX);
479 s2=timestamp_subtract (&timeRX[i], &timeRX[i-1], &deltaRX);
480 if (s1) fprintf(stderr, " *** ***\n");
482 // Then calculate the precise jitter by considering
483 // also TX-jitter: (pseudo)jitter = deltaRX - deltaTX,
484 // hence we have positive and negative jitter (delay
485 // deviations) jitter entries are in +/- nanoseconds
486 jitter[i] = (deltaRX.sec*1000000000L + deltaRX.nsec)
487 - (deltaTX.sec*1000000000L + deltaTX.nsec);
488 // Calculate RFC 3550 jitter estimation. According to
489 // that RFC the jitter should be measured in timestamp
490 // units; however currently Mausezahn uses nanoseconds.
491 // (If we want to solve this: G.711 timestamp units are
492 // 125 usec, so jitter/=125 would be sufficient, AFAIK)
493 ltemp = labs(jitter[i]) - jitter_rfc;
494 jitter_rfc += (ltemp>>4);
495 // Add previous pseudojitter to get the true jitter
496 // (See Documentation!)
497 jitter[i] += jitter[i-1];
499 ////////////////////////////////////////////////
504 ////////////////////////////////////////////////
505 // Determine avg, min, and max jitter within this time frame:
506 jitter_abs = labs(jitter[i]);
507 jitter_avg += jitter_abs;
508 if (jitter_abs < jitter_min) jitter_min = jitter_abs;
509 if (jitter_abs > jitter_max) jitter_max = jitter_abs;
511 ////////////////////////////////
513 /// PRINT IN FILE_2: Detailed jitter data ///
514 if (rtp_log==2) {
515 // Calculate relative timestamp for column 1 of the datafile
516 curtime = timeRX[i].sec*1000000+timeRX[i].nsec/1000;
517 if (time0_flag) {
518 curtime = curtime - time0;
519 } else { // this is only done once during the Mausezahn process
520 time0 = curtime;
521 time0_flag=1;
522 curtime = curtime - time0;
524 fprintf(fp2, "%lu, %li\n",
525 (long unsigned int) curtime,
526 (long int) jitter[i]);
527 fflush(fp2); // save everything immediately
528 // (CHECK if fsync() is additionally needed)
530 } // end for (i=2; i<gind_max; i++)
532 ////////////////////////////////////////////////////////
535 jitter_avg = jitter_avg / (gind_max-2); // average true jitter, always positive
537 if (drop>=drop_prev) { // because the total drop count may decrease(!) if disordered packets appear lately
538 drop_last = drop - drop_prev;
539 drop_prev=drop;
540 } else drop_last=0;
542 // PRINT ON CLI: statistics data
543 switch (rtp_dm) {
544 case TEXT:
545 dum = (unsigned char*) &ip->src;
546 fprintf(stdout,
547 "Got %u packets from host %u.%u.%u.%u: %lu lost (%lu absolute lost, %lu out of order)\n"
548 " Jitter_RFC (low pass filtered) = %li usec\n"
549 " Samples jitter (min/avg/max) = %lu/%lu/%lu usec\n",
550 gind_max,
551 *(dum),*(dum+1),*(dum+2),*(dum+3),
552 (long unsigned int) drop_last,
553 (long unsigned int) drop,
554 (long unsigned int) dis,
555 (long int) jitter_rfc/1000,
556 (long unsigned int) jitter_min/1000,
557 (long unsigned int) jitter_avg/1000,
558 (long unsigned int) jitter_max/1000);
559 break;
561 case BAR:
562 print_jitterbar(jitter_rfc/1000, drop_last);
563 break;
565 case NCURSES: // would be nice...?
566 break;
568 default:
569 break;
572 // Determine whether some packets got lost:
580 /// PRINT IN FILE_1: statistics only ///
581 if (rtp_log) {
582 ts_hms[0]=0x00;
583 timestamp_hms (ts_hms);
584 fprintf(fp,
585 "%s, %lu, %lu, %lu, %li, %u, %u\n",
586 ts_hms,
587 (long unsigned int) jitter_min/1000,
588 (long unsigned int) jitter_avg/1000,
589 (long unsigned int) jitter_max/1000,
590 (long int) jitter_rfc/1000,
591 drop,
592 dis);
593 fflush(fp);
598 // Open another file if current file reaches a limit
600 if ((rtp_log==2) && (gtotal>MAX_DATA_BLOCKS)) { // file big enough,
601 gtotal=0;
602 if (fclose(fp2) == EOF) {
603 perror("fclose");
604 exit(1);
607 if (verbose)
608 fprintf(stderr, " mz: %s written.\n",filename);
610 timestamp_human(filename, "rtp_"); // get a new filename
611 strncpy(dummy, path, 128);
612 strncat(dummy, filename, 64);
614 if (verbose) fprintf(stderr, " mz: Will open %s\n", dummy);
616 if ( (fp2 = fopen (dummy, "w+")) == NULL) {
617 if (errno != EAGAIN) {
618 perror("fopen");
619 exit (-1);
622 fprintf(fp2, "# Jitter measurements by Mausezahn "
623 MAUSEZAHN_VERSION_SHORT ".\n");
624 fprintf(fp2, "# Timestamp (usec) , true jitter (nsec)\n");
626 } // statistics end *********************************************************************
633 void print_jitterbar (long int j, u_int32_t d)
635 // Determine actual data window by considering two events:
637 // 1) window move (j exceeds lower or upper limit)
638 // 2) window rescale (window moves happen too often or the variance
639 // of successive data points is too small)
641 // The most critical value is the chosen resolution (window range),
642 // especially the _initial_ resolution.
644 static long int range=0, min=0, max=0, minvar=0, j0=0, dj=0;
645 static int moved=0, varcount=0, barcount=0;
646 char str[128], bar[150],
647 str1[8], str2[8], str3[8], str4[8];
648 int event=0, anz;
649 long int tmp;
651 // Initialize vars (start with an opened window)
652 // Note that 'range' is actually half of the window
653 if (!range) {
654 range=j;
655 if (range<500) range=500;
656 max = j+range;
657 min = 0;
658 minvar=range/40;
659 event++;
660 } else {
661 dj = labs(j-j0); // no initialization: calculate jitter delta
664 // Move window when borders crossed:
665 if ((j<min) || (j>max)) {
666 max = j + range;
667 min = max-2*range;
668 if (min<0) {
669 min=0;
670 range=(max-min)/2;
671 fprintf(stdout, "\nNOTE: +- Rescaled window to %4.2f msec\n", (double) range/500);
673 moved++;
674 event++;
675 fprintf(stdout,"\n");
676 // printf("move event: min=%li max=%li\n", min, max);
677 } else {
678 if (moved) moved--;
679 // printf("normal event: min=%li max=%li\n", min, max);
683 // Increase range when window moved 5 times in a row
684 if (moved>2) {
685 range*=3;
686 if (range>10000000L) range=10000000L;
687 minvar=range/40;
688 if (minvar<1000) minvar=1000;
689 max=j+range;
690 min=j-range;
691 if (min<0) {
692 min=0;
693 range=(max-min)/2;
695 moved=0;
696 event++;
697 // printf("scale up event: min=%li max=%li\n", min, max);
698 fprintf(stdout, "\nNOTE: ++ Rescaled window to %4.2f msec\n", (double) range/500);
702 // Decrease range when jitter deltas are smaller than minvar
703 // 5 times in a row
704 if (dj<minvar)
705 varcount++;
706 else
707 varcount=0;
709 if (varcount>5) {
710 range*=0.75;
711 if (range>j) range=j;
712 if (range<500) {
713 range=500;
715 minvar=range/40;
716 if (minvar<1000) minvar=1000;
717 max=j+range;
718 min=j-range;
719 if (min<0) {
720 min=0;
721 range=(max-min)/2;
723 fprintf(stdout, "\nNOTE: -- Rescaled window to %4.2f msec\n", (double) range/500);
724 varcount=0;
725 event++;
726 // printf("scale down event: min=%li max=%li\n", min, max);
729 j0=j;
731 barcount++;
732 if (barcount==24) {
733 event=1;
734 barcount=0;
737 if (event) {
738 tmp=range*0.667;
739 sprintf(str1,"%4.2f", (double) min/1000);
740 sprintf(str2,"%4.2f", (double) (min+tmp)/1000);
741 sprintf(str3,"%4.2f", (double) (max-tmp)/1000);
742 sprintf(str4,"%4.2f", (double) max/1000);
744 fprintf(stdout,
745 "%-6s %-6s %-6s %-6s\n"
746 "|-------------------------|-------------------------|-------------------------|\n",
747 str1, str2, str3, str4);
748 barcount=0;
751 anz = 80*(j-min)/(2*range);
752 if (anz) {
753 memset((void*) str, '#', anz);
754 memset((void*) str+anz, ' ', 80-anz);
755 str[80]='\0';
757 else {
758 memset((void*) str, ' ', 80);
759 str[0]='#';
760 str[80]='\0';
762 if (d)
763 sprintf(bar, "%s%4.2f msec !%lu dropped!", str, (double) j/1000, (unsigned long int) d);
764 else
765 sprintf(bar, "%s%4.2f msec", str, (double) j/1000);
767 fprintf(stdout,"%s\n", bar);