]> sjero.net Git - iperf/blob - src/Reporter.c
Fix CPU Usage Bug
[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 extern Condition ReportDoneCond;
115 int reporter_process_report ( ReportHeader *report );
116 void process_report ( ReportHeader *report );
117 int reporter_handle_packet( ReportHeader *report );
118 int reporter_condprintstats( ReporterData *stats, MultiHeader *multireport, int force );
119 int reporter_print( ReporterData *stats, int type, int end );
120 void PrintMSS( ReporterData *stats );
121
122 MultiHeader* InitMulti( thread_Settings *agent, int inID ) {
123     MultiHeader *multihdr = NULL;
124     if ( agent->mThreads > 1 || agent->mThreadMode == kMode_Server ) {
125         if ( isMultipleReport( agent ) ) {
126             multihdr = malloc(sizeof(MultiHeader) +  sizeof(ReporterData) +
127                               NUM_MULTI_SLOTS * sizeof(Transfer_Info));
128         } else {
129             multihdr = malloc(sizeof(MultiHeader));
130         }
131         if ( multihdr != NULL ) {
132             memset( multihdr, 0, sizeof(MultiHeader) );
133             Condition_Initialize( &multihdr->barrier );
134             multihdr->groupID = inID;
135             multihdr->threads = agent->mThreads;
136             if ( isMultipleReport( agent ) ) {
137                 int i;
138                 ReporterData *data = NULL;
139                 multihdr->report = (ReporterData*)(multihdr + 1);
140                 memset(multihdr->report, 0, sizeof(ReporterData));
141                 multihdr->data = (Transfer_Info*)(multihdr->report + 1);
142                 data = multihdr->report;
143                 for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) {
144                     multihdr->data[i].startTime = -1;
145                     multihdr->data[i].transferID = inID;
146                     multihdr->data[i].groupID = -2;
147                 }
148                 data->type = TRANSFER_REPORT;
149                 if ( agent->mInterval != 0.0 ) {
150                     struct timeval *interval = &data->intervalTime;
151                     interval->tv_sec = (long) agent->mInterval;
152                     interval->tv_usec = (long) ((agent->mInterval - interval->tv_sec) 
153                                                 * rMillion);
154                 }
155                 data->mHost = agent->mHost;
156                 data->mLocalhost = agent->mLocalhost;
157                 data->mBufLen = agent->mBufLen;
158                 data->mMSS = agent->mMSS;
159                 data->mTCPWin = agent->mTCPWin;
160                 data->flags = agent->flags;
161                 data->mThreadMode = agent->mThreadMode;
162                 data->mode = agent->mReportMode;
163                 data->info.mFormat = agent->mFormat;
164                 data->info.mTTL = agent->mTTL;
165                 if ( isUDP( agent ) ) {
166                     multihdr->report->info.mUDP = (char)agent->mThreadMode;
167                 }
168                 if ( isConnectionReport( agent ) ) {
169                     data->type |= CONNECTION_REPORT;
170                     data->connection.peer = agent->peer;
171                     data->connection.size_peer = agent->size_peer;
172                     SockAddr_setPortAny( &data->connection.peer );
173                     data->connection.local = agent->local;
174                     data->connection.size_local = agent->size_local;
175                     SockAddr_setPortAny( &data->connection.local );
176                 }
177             }
178         } else {
179             FAIL(1, "Out of Memory!!\n", agent);
180         }
181     }
182     return multihdr;
183 }
184
185 /*
186  * BarrierClient allows for multiple stream clients to be syncronized
187  */
188 void BarrierClient( ReportHeader *agent ) {
189     Condition_Lock(agent->multireport->barrier);
190     agent->multireport->threads--;
191     if ( agent->multireport->threads == 0 ) {
192         // last one set time and wake up everyone
193         gettimeofday( &(agent->multireport->startTime), NULL );
194         Condition_Broadcast( &agent->multireport->barrier );
195     } else {
196         Condition_Wait( &agent->multireport->barrier );
197     }
198     agent->multireport->threads++;
199     Condition_Unlock( agent->multireport->barrier );
200     agent->report.startTime = agent->multireport->startTime;
201     agent->report.nextTime = agent->report.startTime;
202     TimeAdd( agent->report.nextTime, agent->report.intervalTime );
203 }
204
205 /*
206  * InitReport is called by a transfer agent (client or
207  * server) to setup the needed structures to communicate
208  * traffic.
209  */
210 ReportHeader* InitReport( thread_Settings *agent ) {
211     ReportHeader *reporthdr = NULL;
212     ReporterData *data = NULL;
213     if ( isDataReport( agent ) ) {
214         /*
215          * Create in one big chunk
216          */
217         reporthdr = malloc( sizeof(ReportHeader) +
218                             NUM_REPORT_STRUCTS * sizeof(ReportStruct) );
219         if ( reporthdr != NULL ) {
220             // Only need to make sure the headers are clean
221             memset( reporthdr, 0, sizeof(ReportHeader));
222             reporthdr->data = (ReportStruct*)(reporthdr+1);
223             reporthdr->multireport = agent->multihdr;
224             data = &reporthdr->report;
225             reporthdr->reporterindex = NUM_REPORT_STRUCTS - 1;
226             data->info.transferID = agent->mSock;
227             data->info.groupID = (agent->multihdr != NULL ? agent->multihdr->groupID 
228                                                           : -1);
229             data->type = TRANSFER_REPORT;
230             if ( agent->mInterval != 0.0 ) {
231                 struct timeval *interval = &data->intervalTime;
232                 interval->tv_sec = (long) agent->mInterval;
233                 interval->tv_usec = (long) ((agent->mInterval - interval->tv_sec) 
234                                             * rMillion);
235             }
236             data->mHost = agent->mHost;
237             data->mLocalhost = agent->mLocalhost;
238             data->mBufLen = agent->mBufLen;
239             data->mMSS = agent->mMSS;
240             data->mTCPWin = agent->mTCPWin;
241             data->flags = agent->flags;
242             data->mThreadMode = agent->mThreadMode;
243             data->mode = agent->mReportMode;
244             data->info.mFormat = agent->mFormat;
245             data->info.mTTL = agent->mTTL;
246             if ( isUDP( agent ) ) {
247                 reporthdr->report.info.mUDP = (char)agent->mThreadMode;
248             }
249         } else {
250             FAIL(1, "Out of Memory!!\n", agent);
251         }
252     }
253     if ( isConnectionReport( agent ) ) {
254         if ( reporthdr == NULL ) {
255             /*
256              * Create in one big chunk
257              */
258             reporthdr = malloc( sizeof(ReportHeader) );
259             if ( reporthdr != NULL ) {
260                 // Only need to make sure the headers are clean
261                 memset( reporthdr, 0, sizeof(ReportHeader));
262                 data = &reporthdr->report;
263                 data->info.transferID = agent->mSock;
264                 data->info.groupID = -1;
265             } else {
266                 FAIL(1, "Out of Memory!!\n", agent);
267             }
268         }
269         if ( reporthdr != NULL ) {
270             data->type |= CONNECTION_REPORT;
271             data->connection.peer = agent->peer;
272             data->connection.size_peer = agent->size_peer;
273             data->connection.local = agent->local;
274             data->connection.size_local = agent->size_local;
275         } else {
276             FAIL(1, "Out of Memory!!\n", agent);
277         }
278     }
279     if ( isConnectionReport( agent ) || isDataReport( agent ) ) {
280
281 #ifdef HAVE_THREAD
282         /*
283          * Update the ReportRoot to include this report.
284          */
285         if ( reporthdr->report.mThreadMode == kMode_Client &&
286              reporthdr->multireport != NULL ) {
287             // syncronize watches on my mark......
288             BarrierClient( reporthdr );
289         } else {
290             if ( reporthdr->multireport != NULL && isMultipleReport( agent )) {
291                 reporthdr->multireport->threads++;
292                 if ( reporthdr->multireport->report->startTime.tv_sec == 0 ) {
293                     gettimeofday( &(reporthdr->multireport->report->startTime), NULL );
294                 }
295                 reporthdr->report.startTime = reporthdr->multireport->report->startTime;
296             } else {
297                 // set start time
298                 gettimeofday( &(reporthdr->report.startTime), NULL );
299             }
300             reporthdr->report.nextTime = reporthdr->report.startTime;
301             TimeAdd( reporthdr->report.nextTime, reporthdr->report.intervalTime );
302         }
303         Condition_Lock( ReportCond );
304         reporthdr->next = ReportRoot;
305         ReportRoot = reporthdr;
306         Condition_Signal( &ReportCond );
307         Condition_Unlock( ReportCond );
308 #else
309         // set start time
310         gettimeofday( &(reporthdr->report.startTime), NULL );
311         /*
312          * Process the report in this thread
313          */
314         reporthdr->next = NULL;
315         process_report ( reporthdr );
316 #endif 
317     }
318     if ( !isDataReport( agent ) ) {
319         reporthdr = NULL;
320     }
321     return reporthdr;
322 }
323
324 /*
325  * ReportPacket is called by a transfer agent to record
326  * the arrival or departure of a "packet" (for TCP it 
327  * will actually represent many packets). This needs to
328  * be as simple and fast as possible as it gets called for
329  * every "packet".
330  */
331 void ReportPacket( ReportHeader* agent, ReportStruct *packet ) {
332     if ( agent != NULL ) {
333         int index = agent->reporterindex;
334         /*
335          * First find the appropriate place to put the information
336          */
337         if ( agent->agentindex == NUM_REPORT_STRUCTS ) {
338             // Just need to make sure that reporter is not on the first
339             // item
340             while ( index == 0 ) {
341                 Condition_Signal( &ReportCond );
342                 Condition_Wait( &ReportDoneCond );
343                 index = agent->reporterindex;
344             }
345             agent->agentindex = 0;
346         }
347         // Need to make sure that reporter is not about to be "lapped"
348         while ( index - 1 == agent->agentindex ) {
349             Condition_Signal( &ReportCond );
350             Condition_Wait( &ReportDoneCond );
351             index = agent->reporterindex;
352         }
353         
354         // Put the information there
355         memcpy( agent->data + agent->agentindex, packet, sizeof(ReportStruct) );
356         
357         // Updating agentindex MUST be the last thing done
358         agent->agentindex++;
359 #ifndef HAVE_THREAD
360         /*
361          * Process the report in this thread
362          */
363         process_report ( agent );
364 #endif 
365     }
366 }
367
368 /*
369  * CloseReport is called by a transfer agent to finalize
370  * the report and signal transfer is over.
371  */
372 void CloseReport( ReportHeader *agent, ReportStruct *packet ) {
373     if ( agent != NULL) {
374
375         /*
376          * Using PacketID of -1 ends reporting
377          */
378         packet->packetID = -1;
379         packet->packetLen = 0;
380         ReportPacket( agent, packet );
381         packet->packetID = agent->report.cntDatagrams;
382     }
383 }
384
385 /*
386  * EndReport signifies the agent no longer is interested
387  * in the report. Calls to GetReport will no longer be
388  * filled
389  */
390 void EndReport( ReportHeader *agent ) {
391     if ( agent != NULL ) {
392         int index = agent->reporterindex;
393         while ( index != -1 ) {
394             thread_rest();
395             index = agent->reporterindex;
396         }
397         agent->agentindex = -1;
398 #ifndef HAVE_THREAD
399         /*
400          * Process the report in this thread
401          */
402         process_report ( agent );
403 #endif
404     }
405 }
406
407 /*
408  * GetReport is called by the agent after a CloseReport
409  * but before an EndReport to get the stats generated
410  * by the reporter thread.
411  */
412 Transfer_Info *GetReport( ReportHeader *agent ) {
413     int index = agent->reporterindex;
414     while ( index != -1 ) {
415         thread_rest();
416         index = agent->reporterindex;
417     }
418     return &agent->report.info;
419 }
420
421 /*
422  * ReportSettings will generate a summary report for
423  * settings being used with Listeners or Clients
424  */
425 void ReportSettings( thread_Settings *agent ) {
426     if ( isSettingsReport( agent ) ) {
427         /*
428          * Create in one big chunk
429          */
430         ReportHeader *reporthdr = malloc( sizeof(ReportHeader) );
431     
432         if ( reporthdr != NULL ) {
433             ReporterData *data = &reporthdr->report;
434             data->info.transferID = agent->mSock;
435             data->info.groupID = -1;
436             reporthdr->agentindex = -1;
437             reporthdr->reporterindex = -1;
438         
439             data->mHost = agent->mHost;
440             data->mLocalhost = agent->mLocalhost;
441             data->mode = agent->mReportMode;
442             data->type = SETTINGS_REPORT;
443             data->mBufLen = agent->mBufLen;
444             data->mMSS = agent->mMSS;
445             data->mTCPWin = agent->mTCPWin;
446             data->flags = agent->flags;
447             data->mThreadMode = agent->mThreadMode;
448             data->mPort = agent->mPort;
449             data->info.mFormat = agent->mFormat;
450             data->info.mTTL = agent->mTTL;
451             data->connection.peer = agent->peer;
452             data->connection.size_peer = agent->size_peer;
453             data->connection.local = agent->local;
454             data->connection.size_local = agent->size_local;
455     
456     #ifdef HAVE_THREAD
457             /*
458              * Update the ReportRoot to include this report.
459              */
460             Condition_Lock( ReportCond );
461             reporthdr->next = ReportRoot;
462             ReportRoot = reporthdr;
463             Condition_Signal( &ReportCond );
464             Condition_Unlock( ReportCond );
465     #else
466             /*
467              * Process the report in this thread
468              */
469             reporthdr->next = NULL;
470             process_report ( reporthdr );
471     #endif 
472         } else {
473             FAIL(1, "Out of Memory!!\n", agent);
474         }
475     }
476 }
477
478 /*
479  * ReportServerUDP will generate a report of the UDP
480  * statistics as reported by the server on the client
481  * side.
482  */
483 void ReportServerUDP( thread_Settings *agent, server_hdr *server ) {
484     if ( (ntohl(server->flags) & HEADER_VERSION1) != 0 &&
485          isServerReport( agent ) ) {
486         /*
487          * Create in one big chunk
488          */
489         ReportHeader *reporthdr = malloc( sizeof(ReportHeader) );
490         Transfer_Info *stats = &reporthdr->report.info;
491
492         if ( reporthdr != NULL ) {
493             stats->transferID = agent->mSock;
494             stats->groupID = (agent->multihdr != NULL ? agent->multihdr->groupID 
495                                                       : -1);
496             reporthdr->agentindex = -1;
497             reporthdr->reporterindex = -1;
498
499             reporthdr->report.type = SERVER_RELAY_REPORT;
500             reporthdr->report.mode = agent->mReportMode;
501             stats->mFormat = agent->mFormat;
502             stats->jitter = ntohl( server->jitter1 );
503             stats->jitter += ntohl( server->jitter2 ) / (double)rMillion;
504             stats->TotalLen = (((max_size_t) ntohl( server->total_len1 )) << 32) +
505                                   ntohl( server->total_len2 ); 
506             stats->startTime = 0;
507             stats->endTime = ntohl( server->stop_sec );
508             stats->endTime += ntohl( server->stop_usec ) / (double)rMillion;
509             stats->cntError = ntohl( server->error_cnt );
510             stats->cntOutofOrder = ntohl( server->outorder_cnt );
511             stats->cntDatagrams = ntohl( server->datagrams );
512             stats->mUDP = (char)kMode_Server;
513             reporthdr->report.connection.peer = agent->local;
514             reporthdr->report.connection.size_peer = agent->size_local;
515             reporthdr->report.connection.local = agent->peer;
516             reporthdr->report.connection.size_local = agent->size_peer;
517             
518 #ifdef HAVE_THREAD
519             /*
520              * Update the ReportRoot to include this report.
521              */
522             Condition_Lock( ReportCond );
523             reporthdr->next = ReportRoot;
524             ReportRoot = reporthdr;
525             Condition_Signal( &ReportCond );
526             Condition_Unlock( ReportCond );
527 #else
528             /*
529              * Process the report in this thread
530              */
531             reporthdr->next = NULL;
532             process_report ( reporthdr );
533 #endif 
534         } else {
535             FAIL(1, "Out of Memory!!\n", agent);
536         }
537     }
538 }
539
540 /*
541  * This function is called only when the reporter thread
542  * This function is the loop that the reporter thread processes
543  */
544 void reporter_spawn( thread_Settings *thread ) {
545     do {
546         // This section allows for safe exiting with Ctrl-C
547         Condition_Lock ( ReportCond );
548         if ( ReportRoot == NULL ) {
549             // Allow main thread to exit if Ctrl-C is received
550             thread_setignore();
551             Condition_Wait ( &ReportCond );
552             // Stop main thread from exiting until done with all reports
553             thread_unsetignore();
554         }
555         Condition_Unlock ( ReportCond );
556
557 again:
558         if ( ReportRoot != NULL ) {
559             ReportHeader *temp = ReportRoot;
560             //Condition_Unlock ( ReportCond );
561             if ( reporter_process_report ( temp ) ) {
562                 // This section allows for more reports to be added while
563                 // the reporter is processing reports without needing to
564                 // stop the reporter or immediately notify it
565                 Condition_Lock ( ReportCond );
566                 if ( temp == ReportRoot ) {
567                     // no new reports
568                     ReportRoot = temp->next;
569                 } else {
570                     // new reports added
571                     ReportHeader *itr = ReportRoot;
572                     while ( itr->next != temp ) {
573                         itr = itr->next;
574                     }
575                     itr->next = temp->next;
576                 }
577                 // finished with report so free it
578                 free( temp );
579                 Condition_Unlock ( ReportCond );
580                 Condition_Signal( &ReportDoneCond );
581                 if (ReportRoot)
582                     goto again;
583             }
584             Condition_Signal( &ReportDoneCond );
585             usleep(10000);
586         } else {
587             //Condition_Unlock ( ReportCond );
588         }
589     } while ( 1 );
590 }
591
592 /*
593  * Used for single threaded reporting
594  */
595 void process_report ( ReportHeader *report ) {
596     if ( report != NULL ) {
597         if ( reporter_process_report( report ) ) {
598             free( report );
599         }
600     }
601 }
602
603 /*
604  * Process reports starting with "reporthdr"
605  */
606 int reporter_process_report ( ReportHeader *reporthdr ) {
607     int need_free = 0;
608
609     // Recursively process reports
610     if ( reporthdr->next != NULL ) {
611         if ( reporter_process_report( reporthdr->next ) ) {
612             // If we are done with this report then free it
613             ReportHeader *temp = reporthdr->next;
614             reporthdr->next = reporthdr->next->next;
615             free( temp );
616         }
617     }
618
619     if ( (reporthdr->report.type & SETTINGS_REPORT) != 0 ) {
620         reporthdr->report.type &= ~SETTINGS_REPORT;
621         return reporter_print( &reporthdr->report, SETTINGS_REPORT, 1 );
622     } else if ( (reporthdr->report.type & CONNECTION_REPORT) != 0 ) {
623         reporthdr->report.type &= ~CONNECTION_REPORT;
624         reporter_print( &reporthdr->report, CONNECTION_REPORT,
625                                (reporthdr->report.type == 0 ? 1 : 0) );
626         if ( reporthdr->multireport != NULL && isMultipleReport( (&reporthdr->report) )) {
627             if ( (reporthdr->multireport->report->type & CONNECTION_REPORT) != 0 ) {
628                 reporthdr->multireport->report->type &= ~CONNECTION_REPORT;
629                 reporter_print( reporthdr->multireport->report, CONNECTION_REPORT,
630                                 (reporthdr->report.type == 0 ? 1 : 0) );
631             }
632         }
633     } else if ( (reporthdr->report.type & SERVER_RELAY_REPORT) != 0 ) {
634         reporthdr->report.type &= ~SERVER_RELAY_REPORT;
635         return reporter_print( &reporthdr->report, SERVER_RELAY_REPORT, 1 );
636     }
637     if ( (reporthdr->report.type & TRANSFER_REPORT) != 0 ) {
638         // If there are more packets to process then handle them
639         if ( reporthdr->reporterindex >= 0 ) {
640             // Need to make sure we do not pass the "agent"
641             while ( reporthdr->reporterindex != reporthdr->agentindex - 1 ) {
642                 if ( reporthdr->reporterindex == NUM_REPORT_STRUCTS - 1 ) {
643                     if ( reporthdr->agentindex == 0 ) {
644                         break;
645                     } else {
646                         reporthdr->reporterindex = 0;
647                     }
648                 } else {
649                     reporthdr->reporterindex++;
650                 }
651                 if ( reporter_handle_packet( reporthdr ) ) {
652                     // No more packets to process
653                     reporthdr->reporterindex = -1;
654                     break;
655                 }
656             }
657         }
658         // If the agent is done with the report then free it
659         if ( reporthdr->agentindex == -1 ) {
660             need_free = 1;
661         }
662     }
663     return need_free;
664 }
665
666 /*
667  * Updates connection stats
668  */
669 int reporter_handle_packet( ReportHeader *reporthdr ) {
670     ReportStruct *packet = &reporthdr->data[reporthdr->reporterindex];
671     ReporterData *data = &reporthdr->report;
672     Transfer_Info *stats = &reporthdr->report.info;
673     int finished = 0;
674
675     data->cntDatagrams++;
676     // If this is the last packet set the endTime
677     if ( packet->packetID < 0 ) {
678         data->packetTime = packet->packetTime;
679         finished = 1;
680         if ( reporthdr->report.mThreadMode != kMode_Client ) {
681             data->TotalLen += packet->packetLen;
682         }
683     } else {
684         // update recieved amount and time
685         data->packetTime = packet->packetTime;
686         reporter_condprintstats( &reporthdr->report, reporthdr->multireport, finished );
687         data->TotalLen += packet->packetLen;
688         if ( packet->packetID != 0 ) {
689             // UDP packet
690             double transit;
691             double deltaTransit;
692             
693             // from RFC 1889, Real Time Protocol (RTP) 
694             // J = J + ( | D(i-1,i) | - J ) / 16 
695             transit = TimeDifference( packet->packetTime, packet->sentTime );
696             if ( data->lastTransit != 0.0 ) {
697                 deltaTransit = transit - data->lastTransit;
698                 if ( deltaTransit < 0.0 ) {
699                     deltaTransit = -deltaTransit;
700                 }
701                 stats->jitter += (deltaTransit - stats->jitter) / (16.0);
702             }
703             data->lastTransit = transit;
704     
705             // packet loss occured if the datagram numbers aren't sequential 
706             if ( packet->packetID != data->PacketID + 1 ) {
707                 if ( packet->packetID < data->PacketID + 1 ) {
708                     data->cntOutofOrder++;
709                 } else {
710                     data->cntError += packet->packetID - data->PacketID - 1;
711                 }
712             }
713             // never decrease datagramID (e.g. if we get an out-of-order packet) 
714             if ( packet->packetID > data->PacketID ) {
715                 data->PacketID = packet->packetID;
716             }
717         }
718     }
719
720     // Print a report if appropriate
721     return reporter_condprintstats( &reporthdr->report, reporthdr->multireport, finished );
722 }
723
724 /*
725  * Handles summing of threads
726  */
727 void reporter_handle_multiple_reports( MultiHeader *reporthdr, Transfer_Info *stats, int force ) {
728     if ( reporthdr != NULL ) {
729         if ( reporthdr->threads > 1 ) {
730             int i;
731             Transfer_Info *current = NULL;
732             // Search for start Time
733             for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) {
734                 current = &reporthdr->data[i];
735                 if ( current->startTime == stats->startTime ) {
736                     break;
737                 }
738             }
739             if ( current->startTime != stats->startTime ) {
740                 // Find first available
741                 for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) {
742                     current = &reporthdr->data[i];
743                     if ( current->startTime < 0 ) {
744                         break;
745                     }
746                 }
747                 current->cntDatagrams = stats->cntDatagrams;
748                 current->cntError = stats->cntError;
749                 current->cntOutofOrder = stats->cntOutofOrder;
750                 current->TotalLen = stats->TotalLen;
751                 current->mFormat = stats->mFormat;
752                 current->endTime = stats->endTime;
753                 current->jitter = stats->jitter;
754                 current->startTime = stats->startTime;
755                 current->free = 1;
756             } else {
757                 current->cntDatagrams += stats->cntDatagrams;
758                 current->cntError += stats->cntError;
759                 current->cntOutofOrder += stats->cntOutofOrder;
760                 current->TotalLen += stats->TotalLen;
761                 current->mFormat = stats->mFormat;
762                 if ( current->endTime < stats->endTime ) {
763                     current->endTime = stats->endTime;
764                 }
765                 if ( current->jitter < stats->jitter ) {
766                     current->jitter = stats->jitter;
767                 }
768                 current->free++;
769                 if ( current->free == reporthdr->threads ) {
770                     void *reserved = reporthdr->report->info.reserved_delay;
771                     current->free = force;
772                     memcpy( &reporthdr->report->info, current, sizeof(Transfer_Info) );
773                     current->startTime = -1;
774                     reporthdr->report->info.reserved_delay = reserved;
775                     reporter_print( reporthdr->report, MULTIPLE_REPORT, force );
776                 }
777             }
778         }
779     }
780 }
781
782 /*
783  * Prints reports conditionally
784  */
785 int reporter_condprintstats( ReporterData *stats, MultiHeader *multireport, int force ) {
786     if ( force != 0 ) {
787         stats->info.cntOutofOrder = stats->cntOutofOrder;
788         // assume most of the time out-of-order packets are not
789         // duplicate packets, so conditionally subtract them from the lost packets.
790         stats->info.cntError = stats->cntError;
791         if ( stats->info.cntError > stats->info.cntOutofOrder ) {
792             stats->info.cntError -= stats->info.cntOutofOrder;
793         }
794         stats->info.cntDatagrams = (isUDP(stats) ? stats->PacketID : stats->cntDatagrams);
795         stats->info.TotalLen = stats->TotalLen;
796         stats->info.startTime = 0;
797         stats->info.endTime = TimeDifference( stats->packetTime, stats->startTime );
798         stats->info.free = 1;
799         reporter_print( stats, TRANSFER_REPORT, force );
800         if ( isMultipleReport(stats) ) {
801             reporter_handle_multiple_reports( multireport, &stats->info, force );
802         }
803     } else while ((stats->intervalTime.tv_sec != 0 || 
804                    stats->intervalTime.tv_usec != 0) && 
805                   TimeDifference( stats->nextTime, 
806                                   stats->packetTime ) < 0 ) {
807         stats->info.cntOutofOrder = stats->cntOutofOrder - stats->lastOutofOrder;
808         stats->lastOutofOrder = stats->cntOutofOrder;
809         // assume most of the time out-of-order packets are not
810         // duplicate packets, so conditionally subtract them from the lost packets.
811         stats->info.cntError = stats->cntError - stats->lastError;
812         if ( stats->info.cntError > stats->info.cntOutofOrder ) {
813             stats->info.cntError -= stats->info.cntOutofOrder;
814         }
815         stats->lastError = stats->cntError;
816         stats->info.cntDatagrams = (isUDP( stats ) ? stats->PacketID - stats->lastDatagrams :
817                                                      stats->cntDatagrams - stats->lastDatagrams);
818         stats->lastDatagrams = (isUDP( stats ) ? stats->PacketID : stats->cntDatagrams);
819         stats->info.TotalLen = stats->TotalLen - stats->lastTotal;
820         stats->lastTotal = stats->TotalLen;
821         stats->info.startTime = stats->info.endTime;
822         stats->info.endTime = TimeDifference( stats->nextTime, stats->startTime );
823         TimeAdd( stats->nextTime, stats->intervalTime );
824         stats->info.free = 0;
825         reporter_print( stats, TRANSFER_REPORT, force );
826         if ( isMultipleReport(stats) ) {
827             reporter_handle_multiple_reports( multireport, &stats->info, force );
828         }
829     }
830     return force;
831 }
832
833 /*
834  * This function handles multiple format printing by sending to the
835  * appropriate dispatch function
836  */
837 int reporter_print( ReporterData *stats, int type, int end ) {
838     switch ( type ) {
839         case TRANSFER_REPORT:
840             statistics_reports[stats->mode]( &stats->info );
841             if ( end != 0 && isPrintMSS( stats ) && !isUDP( stats ) ) {
842                 PrintMSS( stats );
843             }
844             break;
845         case SERVER_RELAY_REPORT:
846             serverstatistics_reports[stats->mode]( &stats->connection, &stats->info );
847             break;
848         case SETTINGS_REPORT:
849             settings_reports[stats->mode]( stats );
850             break;
851         case CONNECTION_REPORT:
852             stats->info.reserved_delay = connection_reports[stats->mode]( 
853                                                &stats->connection,
854                                                stats->info.transferID );
855             break;
856         case MULTIPLE_REPORT:
857             multiple_reports[stats->mode]( &stats->info );
858             break;
859         default:
860             fprintf( stderr, "Printing type not implemented! No Output\n" );
861     }
862     fflush( stdout );
863     return end;
864 }
865
866 /* -------------------------------------------------------------------
867  * Report the MSS and MTU, given the MSS (or a guess thereof)
868  * ------------------------------------------------------------------- */
869
870 // compare the MSS against the (MTU - 40) to (MTU - 80) bytes.
871 // 40 byte IP header and somewhat arbitrarily, 40 more bytes of IP options.
872
873 #define checkMSS_MTU( inMSS, inMTU ) (inMTU-40) >= inMSS  &&  inMSS >= (inMTU-80)
874
875 void PrintMSS( ReporterData *stats ) {
876     int inMSS = getsock_tcp_mss( stats->info.transferID );
877
878     if ( inMSS <= 0 ) {
879         printf( report_mss_unsupported, stats->info.transferID );
880     } else {
881         char* net;
882         int mtu = 0;
883
884         if ( checkMSS_MTU( inMSS, 1500 ) ) {
885             net = "ethernet";
886             mtu = 1500;
887         } else if ( checkMSS_MTU( inMSS, 4352 ) ) {
888             net = "FDDI";
889             mtu = 4352;
890         } else if ( checkMSS_MTU( inMSS, 9180 ) ) {
891             net = "ATM";
892             mtu = 9180;
893         } else if ( checkMSS_MTU( inMSS, 65280 ) ) {
894             net = "HIPPI";
895             mtu = 65280;
896         } else if ( checkMSS_MTU( inMSS, 576 ) ) {
897             net = "minimum";
898             mtu = 576;
899             printf( warn_no_pathmtu );
900         } else {
901             mtu = inMSS + 40;
902             net = "unknown interface";
903         }
904
905         printf( report_mss,
906                 stats->info.transferID, inMSS, mtu, net );
907     }
908 }
909 // end ReportMSS
910
911 #ifdef __cplusplus
912 } /* end extern "C" */
913 #endif