]> sjero.net Git - iperf/blob - src/Reporter.c
Original 2.0.2 iperf sources
[iperf] / src / Reporter.c
1 /*--------------------------------------------------------------- 
2  * Copyright (c) 1999,2000,2001,2002,2003                              
3  * The Board of Trustees of the University of Illinois            
4  * All Rights Reserved.                                           
5  *--------------------------------------------------------------- 
6  * Permission is hereby granted, free of charge, to any person    
7  * obtaining a copy of this software (Iperf) and associated       
8  * documentation files (the "Software"), to deal in the Software  
9  * without restriction, including without limitation the          
10  * rights to use, copy, modify, merge, publish, distribute,        
11  * sublicense, and/or sell copies of the Software, and to permit     
12  * persons to whom the Software is furnished to do
13  * so, subject to the following conditions: 
14  *
15  *     
16  * Redistributions of source code must retain the above 
17  * copyright notice, this list of conditions and 
18  * the following disclaimers. 
19  *
20  *     
21  * Redistributions in binary form must reproduce the above 
22  * copyright notice, this list of conditions and the following 
23  * disclaimers in the documentation and/or other materials 
24  * provided with the distribution. 
25  * 
26  *     
27  * Neither the names of the University of Illinois, NCSA, 
28  * nor the names of its contributors may be used to endorse 
29  * or promote products derived from this Software without
30  * specific prior written permission. 
31  * 
32  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
33  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
34  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
35  * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT 
36  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
37  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
38  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
39  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
40  * ________________________________________________________________
41  * National Laboratory for Applied Network Research 
42  * National Center for Supercomputing Applications 
43  * University of Illinois at Urbana-Champaign 
44  * http://www.ncsa.uiuc.edu
45  * ________________________________________________________________ 
46  *
47  * Reporter.c
48  * by Kevin Gibbs <kgibbs@nlanr.net>
49  *
50  * ________________________________________________________________ */
51
52 #include "headers.h"
53 #include "Settings.hpp"
54 #include "util.h"
55 #include "Reporter.h"
56 #include "Thread.h"
57 #include "Locale.h"
58 #include "PerfSocket.hpp"
59 #include "SocketAddr.h"
60
61 #ifdef __cplusplus
62 extern "C" {
63 #endif
64
65 /*
66   The following 4 functions are provided for Reporting
67   styles that do not have all the reporting formats. For
68   instance the provided CSV format does not have a settings
69   report so it uses settings_notimpl.
70   */
71 void* connection_notimpl( Connection_Info * nused, int nuse ) { 
72     return NULL; 
73 }
74 void settings_notimpl( ReporterData * nused ) { }
75 void statistics_notimpl( Transfer_Info * nused ) { }
76 void serverstatistics_notimpl( Connection_Info *nused1, Transfer_Info *nused2 ) { }
77
78 // To add a reporting style include its header here.
79 #include "report_default.h"
80 #include "report_CSV.h"
81
82 // The following array of report structs contains the
83 // pointers required for reporting in different reporting
84 // styles. To add a reporting style add a report struct
85 // below.
86 report_connection connection_reports[kReport_MAXIMUM] = {
87     reporter_reportpeer,
88     CSV_peer
89 };
90
91 report_settings settings_reports[kReport_MAXIMUM] = {
92     reporter_reportsettings,
93     settings_notimpl
94 };
95
96 report_statistics statistics_reports[kReport_MAXIMUM] = {
97     reporter_printstats,
98     CSV_stats
99 };
100
101 report_serverstatistics serverstatistics_reports[kReport_MAXIMUM] = {
102     reporter_serverstats,
103     CSV_serverstats
104 };
105
106 report_statistics multiple_reports[kReport_MAXIMUM] = {
107     reporter_multistats,
108     CSV_stats
109 };
110
111 char buffer[64]; // Buffer for printing
112 ReportHeader *ReportRoot = NULL;
113 extern Condition ReportCond;
114 int reporter_process_report ( ReportHeader *report );
115 void process_report ( ReportHeader *report );
116 int reporter_handle_packet( ReportHeader *report );
117 int reporter_condprintstats( ReporterData *stats, MultiHeader *multireport, int force );
118 int reporter_print( ReporterData *stats, int type, int end );
119 void PrintMSS( ReporterData *stats );
120
121 MultiHeader* InitMulti( thread_Settings *agent, int inID ) {
122     MultiHeader *multihdr = NULL;
123     if ( agent->mThreads > 1 || agent->mThreadMode == kMode_Server ) {
124         if ( isMultipleReport( agent ) ) {
125             multihdr = malloc(sizeof(MultiHeader) +  sizeof(ReporterData) +
126                               NUM_MULTI_SLOTS * sizeof(Transfer_Info));
127         } else {
128             multihdr = malloc(sizeof(MultiHeader));
129         }
130         if ( multihdr != NULL ) {
131             memset( multihdr, 0, sizeof(MultiHeader) );
132             Condition_Initialize( &multihdr->barrier );
133             multihdr->groupID = inID;
134             multihdr->threads = agent->mThreads;
135             if ( isMultipleReport( agent ) ) {
136                 int i;
137                 ReporterData *data = NULL;
138                 multihdr->report = (ReporterData*)(multihdr + 1);
139                 memset(multihdr->report, 0, sizeof(ReporterData));
140                 multihdr->data = (Transfer_Info*)(multihdr->report + 1);
141                 data = multihdr->report;
142                 for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) {
143                     multihdr->data[i].startTime = -1;
144                     multihdr->data[i].transferID = inID;
145                     multihdr->data[i].groupID = -2;
146                 }
147                 data->type = TRANSFER_REPORT;
148                 if ( agent->mInterval != 0.0 ) {
149                     struct timeval *interval = &data->intervalTime;
150                     interval->tv_sec = (long) agent->mInterval;
151                     interval->tv_usec = (long) ((agent->mInterval - interval->tv_sec) 
152                                                 * rMillion);
153                 }
154                 data->mHost = agent->mHost;
155                 data->mLocalhost = agent->mLocalhost;
156                 data->mBufLen = agent->mBufLen;
157                 data->mMSS = agent->mMSS;
158                 data->mTCPWin = agent->mTCPWin;
159                 data->flags = agent->flags;
160                 data->mThreadMode = agent->mThreadMode;
161                 data->mode = agent->mReportMode;
162                 data->info.mFormat = agent->mFormat;
163                 data->info.mTTL = agent->mTTL;
164                 if ( isUDP( agent ) ) {
165                     multihdr->report->info.mUDP = (char)agent->mThreadMode;
166                 }
167                 if ( isConnectionReport( agent ) ) {
168                     data->type |= CONNECTION_REPORT;
169                     data->connection.peer = agent->peer;
170                     data->connection.size_peer = agent->size_peer;
171                     SockAddr_setPortAny( &data->connection.peer );
172                     data->connection.local = agent->local;
173                     data->connection.size_local = agent->size_local;
174                     SockAddr_setPortAny( &data->connection.local );
175                 }
176             }
177         } else {
178             FAIL(1, "Out of Memory!!\n", agent);
179         }
180     }
181     return multihdr;
182 }
183
184 /*
185  * BarrierClient allows for multiple stream clients to be syncronized
186  */
187 void BarrierClient( ReportHeader *agent ) {
188     Condition_Lock(agent->multireport->barrier);
189     agent->multireport->threads--;
190     if ( agent->multireport->threads == 0 ) {
191         // last one set time and wake up everyone
192         gettimeofday( &(agent->multireport->startTime), NULL );
193         Condition_Broadcast( &agent->multireport->barrier );
194     } else {
195         Condition_Wait( &agent->multireport->barrier );
196     }
197     agent->multireport->threads++;
198     Condition_Unlock( agent->multireport->barrier );
199     agent->report.startTime = agent->multireport->startTime;
200     agent->report.nextTime = agent->report.startTime;
201     TimeAdd( agent->report.nextTime, agent->report.intervalTime );
202 }
203
204 /*
205  * InitReport is called by a transfer agent (client or
206  * server) to setup the needed structures to communicate
207  * traffic.
208  */
209 ReportHeader* InitReport( thread_Settings *agent ) {
210     ReportHeader *reporthdr = NULL;
211     ReporterData *data = NULL;
212     if ( isDataReport( agent ) ) {
213         /*
214          * Create in one big chunk
215          */
216         reporthdr = malloc( sizeof(ReportHeader) +
217                             NUM_REPORT_STRUCTS * sizeof(ReportStruct) );
218         if ( reporthdr != NULL ) {
219             // Only need to make sure the headers are clean
220             memset( reporthdr, 0, sizeof(ReportHeader));
221             reporthdr->data = (ReportStruct*)(reporthdr+1);
222             reporthdr->multireport = agent->multihdr;
223             data = &reporthdr->report;
224             reporthdr->reporterindex = NUM_REPORT_STRUCTS - 1;
225             data->info.transferID = agent->mSock;
226             data->info.groupID = (agent->multihdr != NULL ? agent->multihdr->groupID 
227                                                           : -1);
228             data->type = TRANSFER_REPORT;
229             if ( agent->mInterval != 0.0 ) {
230                 struct timeval *interval = &data->intervalTime;
231                 interval->tv_sec = (long) agent->mInterval;
232                 interval->tv_usec = (long) ((agent->mInterval - interval->tv_sec) 
233                                             * rMillion);
234             }
235             data->mHost = agent->mHost;
236             data->mLocalhost = agent->mLocalhost;
237             data->mBufLen = agent->mBufLen;
238             data->mMSS = agent->mMSS;
239             data->mTCPWin = agent->mTCPWin;
240             data->flags = agent->flags;
241             data->mThreadMode = agent->mThreadMode;
242             data->mode = agent->mReportMode;
243             data->info.mFormat = agent->mFormat;
244             data->info.mTTL = agent->mTTL;
245             if ( isUDP( agent ) ) {
246                 reporthdr->report.info.mUDP = (char)agent->mThreadMode;
247             }
248         } else {
249             FAIL(1, "Out of Memory!!\n", agent);
250         }
251     }
252     if ( isConnectionReport( agent ) ) {
253         if ( reporthdr == NULL ) {
254             /*
255              * Create in one big chunk
256              */
257             reporthdr = malloc( sizeof(ReportHeader) );
258             if ( reporthdr != NULL ) {
259                 // Only need to make sure the headers are clean
260                 memset( reporthdr, 0, sizeof(ReportHeader));
261                 data = &reporthdr->report;
262                 data->info.transferID = agent->mSock;
263                 data->info.groupID = -1;
264             } else {
265                 FAIL(1, "Out of Memory!!\n", agent);
266             }
267         }
268         if ( reporthdr != NULL ) {
269             data->type |= CONNECTION_REPORT;
270             data->connection.peer = agent->peer;
271             data->connection.size_peer = agent->size_peer;
272             data->connection.local = agent->local;
273             data->connection.size_local = agent->size_local;
274         } else {
275             FAIL(1, "Out of Memory!!\n", agent);
276         }
277     }
278     if ( isConnectionReport( agent ) || isDataReport( agent ) ) {
279
280 #ifdef HAVE_THREAD
281         /*
282          * Update the ReportRoot to include this report.
283          */
284         if ( reporthdr->report.mThreadMode == kMode_Client &&
285              reporthdr->multireport != NULL ) {
286             // syncronize watches on my mark......
287             BarrierClient( reporthdr );
288         } else {
289             if ( reporthdr->multireport != NULL && isMultipleReport( agent )) {
290                 reporthdr->multireport->threads++;
291                 if ( reporthdr->multireport->report->startTime.tv_sec == 0 ) {
292                     gettimeofday( &(reporthdr->multireport->report->startTime), NULL );
293                 }
294                 reporthdr->report.startTime = reporthdr->multireport->report->startTime;
295             } else {
296                 // set start time
297                 gettimeofday( &(reporthdr->report.startTime), NULL );
298             }
299             reporthdr->report.nextTime = reporthdr->report.startTime;
300             TimeAdd( reporthdr->report.nextTime, reporthdr->report.intervalTime );
301         }
302         Condition_Lock( ReportCond );
303         reporthdr->next = ReportRoot;
304         ReportRoot = reporthdr;
305         Condition_Signal( &ReportCond );
306         Condition_Unlock( ReportCond );
307 #else
308         // set start time
309         gettimeofday( &(reporthdr->report.startTime), NULL );
310         /*
311          * Process the report in this thread
312          */
313         reporthdr->next = NULL;
314         process_report ( reporthdr );
315 #endif 
316     }
317     if ( !isDataReport( agent ) ) {
318         reporthdr = NULL;
319     }
320     return reporthdr;
321 }
322
323 /*
324  * ReportPacket is called by a transfer agent to record
325  * the arrival or departure of a "packet" (for TCP it 
326  * will actually represent many packets). This needs to
327  * be as simple and fast as possible as it gets called for
328  * every "packet".
329  */
330 void ReportPacket( ReportHeader* agent, ReportStruct *packet ) {
331     if ( agent != NULL ) {
332         int index = agent->reporterindex;
333         /*
334          * First find the appropriate place to put the information
335          */
336         if ( agent->agentindex == NUM_REPORT_STRUCTS ) {
337             // Just need to make sure that reporter is not on the first
338             // item
339             while ( index == 0 ) {
340                 Condition_Signal( &ReportCond );
341                 thread_rest();
342                 index = agent->reporterindex;
343             }
344             agent->agentindex = 0;
345         }
346         // Need to make sure that reporter is not about to be "lapped"
347         while ( index - 1 == agent->agentindex ) {
348             Condition_Signal( &ReportCond );
349             thread_rest();
350             index = agent->reporterindex;
351         }
352         
353         // Put the information there
354         memcpy( agent->data + agent->agentindex, packet, sizeof(ReportStruct) );
355         
356         // Updating agentindex MUST be the last thing done
357         agent->agentindex++;
358 #ifndef HAVE_THREAD
359         /*
360          * Process the report in this thread
361          */
362         process_report ( agent );
363 #endif 
364     }
365 }
366
367 /*
368  * CloseReport is called by a transfer agent to finalize
369  * the report and signal transfer is over.
370  */
371 void CloseReport( ReportHeader *agent, ReportStruct *packet ) {
372     if ( agent != NULL) {
373
374         /*
375          * Using PacketID of -1 ends reporting
376          */
377         packet->packetID = -1;
378         packet->packetLen = 0;
379         ReportPacket( agent, packet );
380         packet->packetID = agent->report.cntDatagrams;
381     }
382 }
383
384 /*
385  * EndReport signifies the agent no longer is interested
386  * in the report. Calls to GetReport will no longer be
387  * filled
388  */
389 void EndReport( ReportHeader *agent ) {
390     if ( agent != NULL ) {
391         int index = agent->reporterindex;
392         while ( index != -1 ) {
393             thread_rest();
394             index = agent->reporterindex;
395         }
396         agent->agentindex = -1;
397 #ifndef HAVE_THREAD
398         /*
399          * Process the report in this thread
400          */
401         process_report ( agent );
402 #endif
403     }
404 }
405
406 /*
407  * GetReport is called by the agent after a CloseReport
408  * but before an EndReport to get the stats generated
409  * by the reporter thread.
410  */
411 Transfer_Info *GetReport( ReportHeader *agent ) {
412     int index = agent->reporterindex;
413     while ( index != -1 ) {
414         thread_rest();
415         index = agent->reporterindex;
416     }
417     return &agent->report.info;
418 }
419
420 /*
421  * ReportSettings will generate a summary report for
422  * settings being used with Listeners or Clients
423  */
424 void ReportSettings( thread_Settings *agent ) {
425     if ( isSettingsReport( agent ) ) {
426         /*
427          * Create in one big chunk
428          */
429         ReportHeader *reporthdr = malloc( sizeof(ReportHeader) );
430     
431         if ( reporthdr != NULL ) {
432             ReporterData *data = &reporthdr->report;
433             data->info.transferID = agent->mSock;
434             data->info.groupID = -1;
435             reporthdr->agentindex = -1;
436             reporthdr->reporterindex = -1;
437         
438             data->mHost = agent->mHost;
439             data->mLocalhost = agent->mLocalhost;
440             data->mode = agent->mReportMode;
441             data->type = SETTINGS_REPORT;
442             data->mBufLen = agent->mBufLen;
443             data->mMSS = agent->mMSS;
444             data->mTCPWin = agent->mTCPWin;
445             data->flags = agent->flags;
446             data->mThreadMode = agent->mThreadMode;
447             data->mPort = agent->mPort;
448             data->info.mFormat = agent->mFormat;
449             data->info.mTTL = agent->mTTL;
450             data->connection.peer = agent->peer;
451             data->connection.size_peer = agent->size_peer;
452             data->connection.local = agent->local;
453             data->connection.size_local = agent->size_local;
454     
455     #ifdef HAVE_THREAD
456             /*
457              * Update the ReportRoot to include this report.
458              */
459             Condition_Lock( ReportCond );
460             reporthdr->next = ReportRoot;
461             ReportRoot = reporthdr;
462             Condition_Signal( &ReportCond );
463             Condition_Unlock( ReportCond );
464     #else
465             /*
466              * Process the report in this thread
467              */
468             reporthdr->next = NULL;
469             process_report ( reporthdr );
470     #endif 
471         } else {
472             FAIL(1, "Out of Memory!!\n", agent);
473         }
474     }
475 }
476
477 /*
478  * ReportServerUDP will generate a report of the UDP
479  * statistics as reported by the server on the client
480  * side.
481  */
482 void ReportServerUDP( thread_Settings *agent, server_hdr *server ) {
483     if ( (ntohl(server->flags) & HEADER_VERSION1) != 0 &&
484          isServerReport( agent ) ) {
485         /*
486          * Create in one big chunk
487          */
488         ReportHeader *reporthdr = malloc( sizeof(ReportHeader) );
489         Transfer_Info *stats = &reporthdr->report.info;
490
491         if ( reporthdr != NULL ) {
492             stats->transferID = agent->mSock;
493             stats->groupID = (agent->multihdr != NULL ? agent->multihdr->groupID 
494                                                       : -1);
495             reporthdr->agentindex = -1;
496             reporthdr->reporterindex = -1;
497
498             reporthdr->report.type = SERVER_RELAY_REPORT;
499             reporthdr->report.mode = agent->mReportMode;
500             stats->mFormat = agent->mFormat;
501             stats->jitter = ntohl( server->jitter1 );
502             stats->jitter += ntohl( server->jitter2 ) / (double)rMillion;
503             stats->TotalLen = (((max_size_t) ntohl( server->total_len1 )) << 32) +
504                                   ntohl( server->total_len2 ); 
505             stats->startTime = 0;
506             stats->endTime = ntohl( server->stop_sec );
507             stats->endTime += ntohl( server->stop_usec ) / (double)rMillion;
508             stats->cntError = ntohl( server->error_cnt );
509             stats->cntOutofOrder = ntohl( server->outorder_cnt );
510             stats->cntDatagrams = ntohl( server->datagrams );
511             stats->mUDP = (char)kMode_Server;
512             reporthdr->report.connection.peer = agent->local;
513             reporthdr->report.connection.size_peer = agent->size_local;
514             reporthdr->report.connection.local = agent->peer;
515             reporthdr->report.connection.size_local = agent->size_peer;
516             
517 #ifdef HAVE_THREAD
518             /*
519              * Update the ReportRoot to include this report.
520              */
521             Condition_Lock( ReportCond );
522             reporthdr->next = ReportRoot;
523             ReportRoot = reporthdr;
524             Condition_Signal( &ReportCond );
525             Condition_Unlock( ReportCond );
526 #else
527             /*
528              * Process the report in this thread
529              */
530             reporthdr->next = NULL;
531             process_report ( reporthdr );
532 #endif 
533         } else {
534             FAIL(1, "Out of Memory!!\n", agent);
535         }
536     }
537 }
538
539 /*
540  * This function is called only when the reporter thread
541  * This function is the loop that the reporter thread processes
542  */
543 void reporter_spawn( thread_Settings *thread ) {
544     do {
545         // This section allows for safe exiting with Ctrl-C
546         Condition_Lock ( ReportCond );
547         if ( ReportRoot == NULL ) {
548             // Allow main thread to exit if Ctrl-C is received
549             thread_setignore();
550             Condition_Wait ( &ReportCond );
551             // Stop main thread from exiting until done with all reports
552             thread_unsetignore();
553         }
554         Condition_Unlock ( ReportCond );
555
556         if ( ReportRoot != NULL ) {
557             ReportHeader *temp = ReportRoot;
558             //Condition_Unlock ( ReportCond );
559             if ( reporter_process_report ( temp ) ) {
560                 // This section allows for more reports to be added while
561                 // the reporter is processing reports without needing to
562                 // stop the reporter or immediately notify it
563                 Condition_Lock ( ReportCond );
564                 if ( temp == ReportRoot ) {
565                     // no new reports
566                     ReportRoot = temp->next;
567                 } else {
568                     // new reports added
569                     ReportHeader *itr = ReportRoot;
570                     while ( itr->next != temp ) {
571                         itr = itr->next;
572                     }
573                     itr->next = temp->next;
574                 }
575                 // finished with report so free it
576                 free( temp );
577                 Condition_Unlock ( ReportCond );
578             }
579             // yield control of CPU is another thread is waiting
580             thread_rest();
581         } else {
582             //Condition_Unlock ( ReportCond );
583         }
584     } while ( 1 );
585 }
586
587 /*
588  * Used for single threaded reporting
589  */
590 void process_report ( ReportHeader *report ) {
591     if ( report != NULL ) {
592         if ( reporter_process_report( report ) ) {
593             free( report );
594         }
595     }
596 }
597
598 /*
599  * Process reports starting with "reporthdr"
600  */
601 int reporter_process_report ( ReportHeader *reporthdr ) {
602     int need_free = 0;
603
604     // Recursively process reports
605     if ( reporthdr->next != NULL ) {
606         if ( reporter_process_report( reporthdr->next ) ) {
607             // If we are done with this report then free it
608             ReportHeader *temp = reporthdr->next;
609             reporthdr->next = reporthdr->next->next;
610             free( temp );
611         }
612     }
613
614     if ( (reporthdr->report.type & SETTINGS_REPORT) != 0 ) {
615         reporthdr->report.type &= ~SETTINGS_REPORT;
616         return reporter_print( &reporthdr->report, SETTINGS_REPORT, 1 );
617     } else if ( (reporthdr->report.type & CONNECTION_REPORT) != 0 ) {
618         reporthdr->report.type &= ~CONNECTION_REPORT;
619         reporter_print( &reporthdr->report, CONNECTION_REPORT,
620                                (reporthdr->report.type == 0 ? 1 : 0) );
621         if ( reporthdr->multireport != NULL && isMultipleReport( (&reporthdr->report) )) {
622             if ( (reporthdr->multireport->report->type & CONNECTION_REPORT) != 0 ) {
623                 reporthdr->multireport->report->type &= ~CONNECTION_REPORT;
624                 reporter_print( reporthdr->multireport->report, CONNECTION_REPORT,
625                                 (reporthdr->report.type == 0 ? 1 : 0) );
626             }
627         }
628     } else if ( (reporthdr->report.type & SERVER_RELAY_REPORT) != 0 ) {
629         reporthdr->report.type &= ~SERVER_RELAY_REPORT;
630         return reporter_print( &reporthdr->report, SERVER_RELAY_REPORT, 1 );
631     }
632     if ( (reporthdr->report.type & TRANSFER_REPORT) != 0 ) {
633         // If there are more packets to process then handle them
634         if ( reporthdr->reporterindex >= 0 ) {
635             // Need to make sure we do not pass the "agent"
636             while ( reporthdr->reporterindex != reporthdr->agentindex - 1 ) {
637                 if ( reporthdr->reporterindex == NUM_REPORT_STRUCTS - 1 ) {
638                     if ( reporthdr->agentindex == 0 ) {
639                         break;
640                     } else {
641                         reporthdr->reporterindex = 0;
642                     }
643                 } else {
644                     reporthdr->reporterindex++;
645                 }
646                 if ( reporter_handle_packet( reporthdr ) ) {
647                     // No more packets to process
648                     reporthdr->reporterindex = -1;
649                     break;
650                 }
651             }
652         }
653         // If the agent is done with the report then free it
654         if ( reporthdr->agentindex == -1 ) {
655             need_free = 1;
656         }
657     }
658     return need_free;
659 }
660
661 /*
662  * Updates connection stats
663  */
664 int reporter_handle_packet( ReportHeader *reporthdr ) {
665     ReportStruct *packet = &reporthdr->data[reporthdr->reporterindex];
666     ReporterData *data = &reporthdr->report;
667     Transfer_Info *stats = &reporthdr->report.info;
668     int finished = 0;
669
670     data->cntDatagrams++;
671     // If this is the last packet set the endTime
672     if ( packet->packetID < 0 ) {
673         data->packetTime = packet->packetTime;
674         finished = 1;
675         if ( reporthdr->report.mThreadMode != kMode_Client ) {
676             data->TotalLen += packet->packetLen;
677         }
678     } else {
679         // update recieved amount and time
680         data->packetTime = packet->packetTime;
681         reporter_condprintstats( &reporthdr->report, reporthdr->multireport, finished );
682         data->TotalLen += packet->packetLen;
683         if ( packet->packetID != 0 ) {
684             // UDP packet
685             double transit;
686             double deltaTransit;
687             
688             // from RFC 1889, Real Time Protocol (RTP) 
689             // J = J + ( | D(i-1,i) | - J ) / 16 
690             transit = TimeDifference( packet->packetTime, packet->sentTime );
691             if ( data->lastTransit != 0.0 ) {
692                 deltaTransit = transit - data->lastTransit;
693                 if ( deltaTransit < 0.0 ) {
694                     deltaTransit = -deltaTransit;
695                 }
696                 stats->jitter += (deltaTransit - stats->jitter) / (16.0);
697             }
698             data->lastTransit = transit;
699     
700             // packet loss occured if the datagram numbers aren't sequential 
701             if ( packet->packetID != data->PacketID + 1 ) {
702                 if ( packet->packetID < data->PacketID + 1 ) {
703                     data->cntOutofOrder++;
704                 } else {
705                     data->cntError += packet->packetID - data->PacketID - 1;
706                 }
707             }
708             // never decrease datagramID (e.g. if we get an out-of-order packet) 
709             if ( packet->packetID > data->PacketID ) {
710                 data->PacketID = packet->packetID;
711             }
712         }
713     }
714
715     // Print a report if appropriate
716     return reporter_condprintstats( &reporthdr->report, reporthdr->multireport, finished );
717 }
718
719 /*
720  * Handles summing of threads
721  */
722 void reporter_handle_multiple_reports( MultiHeader *reporthdr, Transfer_Info *stats, int force ) {
723     if ( reporthdr != NULL ) {
724         if ( reporthdr->threads > 1 ) {
725             int i;
726             Transfer_Info *current = NULL;
727             // Search for start Time
728             for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) {
729                 current = &reporthdr->data[i];
730                 if ( current->startTime == stats->startTime ) {
731                     break;
732                 }
733             }
734             if ( current->startTime != stats->startTime ) {
735                 // Find first available
736                 for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) {
737                     current = &reporthdr->data[i];
738                     if ( current->startTime < 0 ) {
739                         break;
740                     }
741                 }
742                 current->cntDatagrams = stats->cntDatagrams;
743                 current->cntError = stats->cntError;
744                 current->cntOutofOrder = stats->cntOutofOrder;
745                 current->TotalLen = stats->TotalLen;
746                 current->mFormat = stats->mFormat;
747                 current->endTime = stats->endTime;
748                 current->jitter = stats->jitter;
749                 current->startTime = stats->startTime;
750                 current->free = 1;
751             } else {
752                 current->cntDatagrams += stats->cntDatagrams;
753                 current->cntError += stats->cntError;
754                 current->cntOutofOrder += stats->cntOutofOrder;
755                 current->TotalLen += stats->TotalLen;
756                 current->mFormat = stats->mFormat;
757                 if ( current->endTime < stats->endTime ) {
758                     current->endTime = stats->endTime;
759                 }
760                 if ( current->jitter < stats->jitter ) {
761                     current->jitter = stats->jitter;
762                 }
763                 current->free++;
764                 if ( current->free == reporthdr->threads ) {
765                     void *reserved = reporthdr->report->info.reserved_delay;
766                     current->free = force;
767                     memcpy( &reporthdr->report->info, current, sizeof(Transfer_Info) );
768                     current->startTime = -1;
769                     reporthdr->report->info.reserved_delay = reserved;
770                     reporter_print( reporthdr->report, MULTIPLE_REPORT, force );
771                 }
772             }
773         }
774     }
775 }
776
777 /*
778  * Prints reports conditionally
779  */
780 int reporter_condprintstats( ReporterData *stats, MultiHeader *multireport, int force ) {
781     if ( force != 0 ) {
782         stats->info.cntOutofOrder = stats->cntOutofOrder;
783         // assume most of the time out-of-order packets are not
784         // duplicate packets, so conditionally subtract them from the lost packets.
785         stats->info.cntError = stats->cntError;
786         if ( stats->info.cntError > stats->info.cntOutofOrder ) {
787             stats->info.cntError -= stats->info.cntOutofOrder;
788         }
789         stats->info.cntDatagrams = (isUDP(stats) ? stats->PacketID : stats->cntDatagrams);
790         stats->info.TotalLen = stats->TotalLen;
791         stats->info.startTime = 0;
792         stats->info.endTime = TimeDifference( stats->packetTime, stats->startTime );
793         stats->info.free = 1;
794         reporter_print( stats, TRANSFER_REPORT, force );
795         if ( isMultipleReport(stats) ) {
796             reporter_handle_multiple_reports( multireport, &stats->info, force );
797         }
798     } else while ((stats->intervalTime.tv_sec != 0 || 
799                    stats->intervalTime.tv_usec != 0) && 
800                   TimeDifference( stats->nextTime, 
801                                   stats->packetTime ) < 0 ) {
802         stats->info.cntOutofOrder = stats->cntOutofOrder - stats->lastOutofOrder;
803         stats->lastOutofOrder = stats->cntOutofOrder;
804         // assume most of the time out-of-order packets are not
805         // duplicate packets, so conditionally subtract them from the lost packets.
806         stats->info.cntError = stats->cntError - stats->lastError;
807         if ( stats->info.cntError > stats->info.cntOutofOrder ) {
808             stats->info.cntError -= stats->info.cntOutofOrder;
809         }
810         stats->lastError = stats->cntError;
811         stats->info.cntDatagrams = (isUDP( stats ) ? stats->PacketID - stats->lastDatagrams :
812                                                      stats->cntDatagrams - stats->lastDatagrams);
813         stats->lastDatagrams = (isUDP( stats ) ? stats->PacketID : stats->cntDatagrams);
814         stats->info.TotalLen = stats->TotalLen - stats->lastTotal;
815         stats->lastTotal = stats->TotalLen;
816         stats->info.startTime = stats->info.endTime;
817         stats->info.endTime = TimeDifference( stats->nextTime, stats->startTime );
818         TimeAdd( stats->nextTime, stats->intervalTime );
819         stats->info.free = 0;
820         reporter_print( stats, TRANSFER_REPORT, force );
821         if ( isMultipleReport(stats) ) {
822             reporter_handle_multiple_reports( multireport, &stats->info, force );
823         }
824     }
825     return force;
826 }
827
828 /*
829  * This function handles multiple format printing by sending to the
830  * appropriate dispatch function
831  */
832 int reporter_print( ReporterData *stats, int type, int end ) {
833     switch ( type ) {
834         case TRANSFER_REPORT:
835             statistics_reports[stats->mode]( &stats->info );
836             if ( end != 0 && isPrintMSS( stats ) && !isUDP( stats ) ) {
837                 PrintMSS( stats );
838             }
839             break;
840         case SERVER_RELAY_REPORT:
841             serverstatistics_reports[stats->mode]( &stats->connection, &stats->info );
842             break;
843         case SETTINGS_REPORT:
844             settings_reports[stats->mode]( stats );
845             break;
846         case CONNECTION_REPORT:
847             stats->info.reserved_delay = connection_reports[stats->mode]( 
848                                                &stats->connection,
849                                                stats->info.transferID );
850             break;
851         case MULTIPLE_REPORT:
852             multiple_reports[stats->mode]( &stats->info );
853             break;
854         default:
855             fprintf( stderr, "Printing type not implemented! No Output\n" );
856     }
857     fflush( stdout );
858     return end;
859 }
860
861 /* -------------------------------------------------------------------
862  * Report the MSS and MTU, given the MSS (or a guess thereof)
863  * ------------------------------------------------------------------- */
864
865 // compare the MSS against the (MTU - 40) to (MTU - 80) bytes.
866 // 40 byte IP header and somewhat arbitrarily, 40 more bytes of IP options.
867
868 #define checkMSS_MTU( inMSS, inMTU ) (inMTU-40) >= inMSS  &&  inMSS >= (inMTU-80)
869
870 void PrintMSS( ReporterData *stats ) {
871     int inMSS = getsock_tcp_mss( stats->info.transferID );
872
873     if ( inMSS <= 0 ) {
874         printf( report_mss_unsupported, stats->info.transferID );
875     } else {
876         char* net;
877         int mtu = 0;
878
879         if ( checkMSS_MTU( inMSS, 1500 ) ) {
880             net = "ethernet";
881             mtu = 1500;
882         } else if ( checkMSS_MTU( inMSS, 4352 ) ) {
883             net = "FDDI";
884             mtu = 4352;
885         } else if ( checkMSS_MTU( inMSS, 9180 ) ) {
886             net = "ATM";
887             mtu = 9180;
888         } else if ( checkMSS_MTU( inMSS, 65280 ) ) {
889             net = "HIPPI";
890             mtu = 65280;
891         } else if ( checkMSS_MTU( inMSS, 576 ) ) {
892             net = "minimum";
893             mtu = 576;
894             printf( warn_no_pathmtu );
895         } else {
896             mtu = inMSS + 40;
897             net = "unknown interface";
898         }
899
900         printf( report_mss,
901                 stats->info.transferID, inMSS, mtu, net );
902     }
903 }
904 // end ReportMSS
905
906 #ifdef __cplusplus
907 } /* end extern "C" */
908 #endif