http_active.c

Go to the documentation of this file.
00001 /*
00002 http_active
00003 
00004 Copyright July 5, 2001, The University of North Carolina at Chapel Hill
00005 
00006 Redistribution and use in source and binary forms, with or without 
00007 modification, are permitted provided that the following conditions are met:
00008 
00009   1. Redistributions of source code must retain the above copyright notice, 
00010      this list of conditions and the following disclaimer.
00011   2. Redistributions in binary form must reproduce the above copyright 
00012      notice, this list of conditions and the following disclaimer in the 
00013      documentation and/or other materials provided with the distribution.
00014   3. The name of the author may not be used to endorse or promote products 
00015      derived from this software without specific prior written permission.
00016 
00017 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
00018 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
00019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
00020 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
00021 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
00022 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
00023 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
00024 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
00025 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
00026 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00027 
00028 Contact person:
00029 
00030     Frank D. Smith, University of North Carolina at Chapel Hill
00031         email: smithfd@cs.unc.edu
00032     phone: 919-962-1884
00033     fax:   919-962-1799
00034 */
00035 
00036 /* Program to create an activity trace (summary form) of web browsing clients
00037    with respect to three types of activity: client sending request data, 
00038    server sending response data, client is idle (no request or response).
00039    Identification of idle periods is used to infer user "think" times 
00040    between requests for new top-level pages.    A client is defined by
00041     a single IP address. 
00042   
00043    "Idle" is defined as a period of time greater than a threshold value
00044    ("idle_limit" with a default of 2 seconds) during which a client has no
00045    requests outstanding.  A request is outstanding from the
00046    start time of a request until the end time (normal or terminated) of
00047    the corresponding response.
00048 
00049    The input to this program is the SORTed output from http_connect.  
00050    The sort to be applied is produced with the following shell script:
00051 
00052       sort -s -o $1.sort +1 -2 +0 -1 -T /tmp $1
00053 
00054    This sorts all the records for a given client IP address in timestamp 
00055    order.  
00056 
00057    The output is also time ordered with respect to a single client (IP
00058    address) and consists only of client request entries (in the same format
00059    as the input) and ordered by start time, server responses (in the same 
00060    format as the input) and ordered by end time, and client idle periods 
00061    giving the elapsed idle time and ordered by the end of the idle period.  
00062    The output file has extension ".activity" added by the program.
00063 
00064    To get usage information, invoke the program with the -h switch.
00065 
00066 */
00067 
00068 #include <stdlib.h>
00069 #include <stdio.h>
00070 #include <math.h>
00071 #include <sys/time.h>
00072 
00073 #define min(a,b) ((a) <= (b) ? (a) : (b))
00074 #define max(a,b) ((a) >= (b) ? (a) : (b))
00075 
00076 void Usage(char *s)
00077 {
00078   fprintf (stderr,"\nUsage: %s\n", s);
00079   fprintf (stderr,"    [-w file_name] (name for output file)\n");
00080   fprintf (stderr,"    [-r file_name] (name for input file)\n");
00081   fprintf (stderr,"    [-I idle_limit] (min inactivity interval)\n");
00082   fprintf (stderr,"\n");
00083   exit(-1);
00084 }
00085 
00086   FILE *dumpFP, *outFP;
00087 
00088   struct timeval time_stamp = {0,0};
00089 
00090   int req;
00091   int rsp;
00092 
00093   char ts[20]; 
00094   char sh[25]; 
00095   char sp[10];
00096   char gt[3]; 
00097   char dh[25]; 
00098   char dp[10];
00099   char fl[5]; 
00100 
00101   char current_src[25] = "";
00102 
00103   enum client_states 
00104              {PENDING_ACTIVE, ACTIVE, PENDING_IDLE, IDLE};
00105   enum client_states client_state = PENDING_ACTIVE;
00106 
00107   enum event_types {SYN, ACT_REQ, ACT_RSP, END, REQ, RSP};
00108   enum event_types event_type;
00109 
00110 
00111   char idle_begin[20];
00112   char earliest_end[20];
00113   char last_client_ts[20];
00114 
00115 /* For each client (IP address) maintain a table of HTTP connections
00116    that are "active" with the following information about each connection:
00117      id (host/port 4-tuple identifying the connection 
00118      activity (1 if a request has been sent and the response is not
00119                yet complete; 0 otherwise)
00120      state (like activity)
00121    A connection is "active" (in the table) from the time a connection
00122    start (SYN, ACT-REQ or ACT-RSP) is seen in the input until a 
00123    connection end (FIN, RST, TRM) is also seen in the input
00124 */
00125      
00126 int active_connections = 0;
00127 #define MAX_CONNECTIONS 1000
00128   struct connect
00129     {
00130      char id[50];
00131      int  activity;
00132      enum event_types state;
00133     }connections[MAX_CONNECTIONS];
00134 
00135   char new_line[500];
00136 
00137 void error_line(char *s);
00138 void error_state(char *s);
00139 
00140 void log_REQ(void);
00141 void log_RSP(void);
00142 void log_IDLE(char *s);
00143 
00144 void set_connection(char *sp, char *dh, char *dp, enum event_types type);
00145 void ClearConnections(void);
00146 int ConnectionsActive(void);
00147 int FindConnection(char *sp, char *dh, char *dp);
00148 int AddConnection(char *sp, char *dh, char *dp);
00149 int RemoveConnection(char *sp, char *dh, char *dp);
00150 
00151 long elapsed_ms(char *end, char *start);
00152 
00153 void main (int argc, char* argv[])
00154 {
00155   int i;
00156 
00157   char input_name[256] = "";
00158   char output_name[256] = "";
00159   long idle_limit = 2000;  /* default threshold for idleness in millisec. */ 
00160   long elapsed;
00161 
00162   char parse_line[500];
00163   char discard[50];
00164 
00165   char *cursor;
00166   char *vp;
00167 
00168   /* Parse the command line */
00169   i = 1;
00170   while (i < argc) {
00171     if (strcmp (argv[i], "-r") == 0) {
00172       if (++i >= argc) Usage (argv[0]);
00173       strcpy (input_name, argv[i]);
00174     }
00175     else if (strcmp (argv[i], "-w") == 0) {
00176       if (++i >= argc) Usage (argv[0]);
00177       strcpy (output_name, argv[i]);
00178     }
00179     else if (strcmp (argv[i], "-I") == 0) {
00180       if (++i >= argc) Usage (argv[0]);
00181       idle_limit = (long)atoi(argv[i]);
00182     }
00183     else 
00184       Usage (argv[0]);
00185     i++;
00186   }
00187 
00188   /* Open files */
00189   if (strcmp(output_name, "") == 0)
00190      outFP = stdout;
00191   else 
00192      {
00193       strcat(output_name, ".activity");
00194       if ((outFP = fopen (output_name, "w")) == NULL) {
00195           fprintf (stderr, "error opening %s\n", output_name);
00196           exit (-1);
00197           }
00198      }
00199 
00200   if (strcmp(input_name, "") == 0)
00201      dumpFP = stdin;
00202   else 
00203      {
00204       if ((dumpFP = fopen (input_name, "r")) == NULL) {
00205           fprintf (stderr, "error opening %s\n", input_name);
00206           exit (-1);
00207          }
00208      }
00209 
00210   /* Read each record in the input file.  Look for a change in the 
00211      source IP address (which indicates a new client).  If a new
00212      client, log the end of an idle period (if any) for the old
00213      client and initialize the connection table for the new client.
00214      If a record for the current client has been read, classify the
00215      type of event it represent and process it to update the client
00216      and connection state.
00217   */
00218 
00219   while (!feof (dumpFP)) {
00220     /* Get and parse line of data */
00221     if (fgets (new_line, sizeof(new_line), dumpFP) == NULL)
00222        break;
00223     /* get first line pieces */
00224 
00225     sscanf (new_line, "%s %s %s %s %s %s %s",
00226                       &ts, &sh, &sp, &gt, &dh, &dp, &fl);
00227 
00228     /* if an ERR line, just show it */
00229     if (strcmp(fl, "ERR:") == 0)
00230        {
00231         error_line(new_line);
00232         continue;
00233        }
00234 
00235     /* now get variable part starting with the ":" considering that */
00236     /* interpretation of the remaining fields depends on the flag value */ 
00237     /* This is necessary to find the ending timestamp for FIN, RST, and 
00238        TRM events.
00239     */
00240 
00241     strcpy(parse_line, new_line);
00242     cursor = parse_line;
00243     vp = (char *)strsep(&cursor, ":" );
00244     if ((cursor == (char *)NULL) ||
00245         (vp == (char *)NULL))
00246         {
00247          error_line(new_line);
00248          continue;
00249         }
00250 
00251     /* Classify the event type by looking at the flag field from input
00252        records */
00253 
00254     if ((strcmp(fl, "REQ") == 0) || 
00255         (strcmp(fl, "REQ-") == 0))
00256        event_type = REQ;
00257     else
00258       {
00259        if ((strcmp(fl, "RSP") == 0) ||
00260            (strcmp(fl, "RSP-") == 0))
00261           event_type = RSP;
00262        else
00263      {
00264           if ((strcmp(fl, "FIN") == 0) ||
00265               (strcmp(fl, "TRM") == 0) ||
00266               (strcmp(fl, "RST") == 0))
00267          {
00268            /* need the ending timestamp from these record types */
00269               sscanf(cursor, "%s %s", &discard, &earliest_end);
00270               event_type = END;
00271              }
00272            else 
00273              {
00274               if (strcmp(fl, "SYN") == 0)
00275                  event_type = SYN;
00276               else
00277         {
00278                  if (strcmp(fl, "ACT-REQ") == 0)
00279                     event_type = ACT_REQ;
00280                  else
00281                      if (strcmp(fl, "ACT-RSP") == 0)
00282                         event_type = ACT_RSP;
00283                 }
00284              }
00285      }
00286       }
00287 
00288    /* now use data from new trace record to update status */  
00289    /* first check to see if this is the same client host */
00290    if (strcmp(current_src, sh) != 0)
00291        {
00292         if (client_state == IDLE)
00293             log_IDLE(last_client_ts);  
00294         ClearConnections();
00295         client_state = PENDING_ACTIVE;
00296         strcpy(current_src, sh);
00297        }
00298 
00299 
00300    /* update the connection status for this client's connection */
00301 
00302    set_connection(sp, dh, dp, event_type);
00303 
00304    
00305    /* The main processing for idle periods is done by maintaining a state
00306       variable (client_status) for the client and looking for specific input 
00307       record types at different values of the state variable.  The 
00308       values of client_state and their implications are:
00309       PENDING_ACTIVE   - A new client is started and remains PENDING_ACTIVE
00310                          until an activity indication such as ACT-REQ,
00311                          ACT-RSP, or REQ is seen in which case it enters
00312                          the ACTIVE state.  If there is an initial response,
00313                          PENDING_IDLE is entered.
00314       ACTIVE           - At least one request is outstanding and the state
00315                          can only change if there is a response completion
00316                          or connection termination.
00317       PENDING_IDLE     - There are no requests outstanding but the idle
00318                          period threshold has not elapsed since it entered
00319                          the PENDING_IDLE state.
00320       IDLE             - No outstanding requests for a period greater than
00321                          the idle threshold.  The IDLE (and PENDING_IDLE)
00322                          states are exited on activity indication such as 
00323                          ACT-REQ, ACT-RSP, or REQ
00324    */
00325 
00326    switch (client_state)
00327       {
00328        case PENDING_ACTIVE:
00329             switch (event_type)
00330            {
00331             case SYN:
00332                     break;
00333             case ACT_REQ:
00334             case ACT_RSP:
00335                     client_state = ACTIVE;
00336                     break;
00337             case REQ:
00338                     client_state = ACTIVE;
00339                     log_REQ();
00340                     break;
00341             case RSP:
00342                     client_state = PENDING_IDLE;
00343                     strcpy(idle_begin, ts);
00344                     log_RSP();
00345                     break;
00346             case END:
00347                     break;
00348                }
00349             break;
00350 
00351        case ACTIVE:
00352             switch (event_type)
00353            {
00354             case SYN:
00355             case ACT_REQ:
00356             case ACT_RSP:
00357                     break;
00358             case REQ:
00359                     log_REQ();
00360                     break;
00361             case RSP:
00362                     log_RSP(); 
00363                     if (ConnectionsActive() == 0) /* Any active connections?*/
00364                {
00365                         client_state = PENDING_IDLE;
00366                         strcpy(idle_begin, ts);
00367                        }
00368                     break;
00369             case END:
00370                     if (ConnectionsActive() == 0) /* Any active connections?*/
00371                {
00372                         client_state = PENDING_IDLE;
00373                         strcpy(idle_begin, earliest_end);
00374                        }
00375                     break;
00376                }
00377             break;
00378 
00379        case PENDING_IDLE:
00380         /* must start checking time, if > n seconds elapse since
00381                entering PENDING_IDLE state, enter IDLE state */
00382             elapsed = elapsed_ms(ts, idle_begin);
00383             if (elapsed < idle_limit)
00384            {
00385                 switch (event_type)
00386            {
00387             case SYN:
00388             case END:
00389                         break;
00390                 case ACT_REQ:
00391                 case ACT_RSP:
00392                         client_state = ACTIVE;
00393                         break;
00394             case REQ:
00395                         client_state = ACTIVE;
00396                         log_REQ();
00397                         break;
00398             case RSP:
00399                         log_RSP();
00400                         break;
00401                    }
00402                 break;  /* ends case PENDING_IDLE */
00403                }
00404             else          /* it has crossed the idle threshold */ 
00405                 client_state = IDLE;
00406        /* NOTE: drop through to IDLE to handle the current event */
00407 
00408        case IDLE:
00409             switch (event_type)
00410            {
00411         case SYN:
00412         case END:
00413                     break;
00414             case ACT_REQ:
00415             case ACT_RSP:
00416                     client_state = ACTIVE;
00417                     log_IDLE(ts);
00418                     break;
00419         case REQ:
00420                     client_state = ACTIVE;
00421                     log_IDLE(ts);
00422                     log_REQ();
00423                     break;
00424         case RSP:
00425                     log_RSP();
00426                     break;
00427                 break;  /* ends case PENDING_IDLE */
00428                }
00429              break;
00430 
00431        default:
00432              break;
00433       } /* end switch */
00434 
00435     strcpy(last_client_ts, ts);
00436 
00437    } /* end while (!feof ....) */ 
00438   close (dumpFP);
00439   close (outFP);
00440 }
00441 
00442 /* updates the status of connections for each interesting event */
00443 
00444 void set_connection(char *sp, char *dh, char *dp, enum event_types type)
00445 {
00446 
00447   int cx;
00448 
00449   /* A connection is identified by the host/port 3-tuple (the source IP
00450      address is not needed because only one client is handled at a time).
00451      The connection's status depends on the type of the event that caused
00452      the update.  The following event type are defined with their effect
00453      on the connection's status:
00454            SYN, ACT-REQ, - The connection has begun (is an "active"
00455            ACT-RSP         connection.  Add it to the table as idle
00456                            (activity == 0) if a SYN or with request/
00457                            response activity (activity == 1) for ACT-REQ
00458                            or ACT-RSP.
00459            REQ           - Find the connection in the table and mark it with
00460                            an outstanding request (activity == 1).
00461            RSP           - Find the connection in the table and mark it with
00462                            a completed request (activity == 0).
00463            END           - The connection has ended (is no longer an "active"
00464                            connection).  Remove it from the table.
00465   */
00466 
00467   switch (type)
00468      {   
00469       case SYN:
00470       case ACT_REQ:
00471       case ACT_RSP:
00472      {
00473           cx = AddConnection(sp, dh, dp);
00474           if (cx < 0)  /* already there */
00475          {
00476               error_state("Add for existing connection");
00477               return;
00478              }            
00479           if (cx > MAX_CONNECTIONS)  /* table overflow */
00480          {
00481               error_state("Active connections exceeds maximum");
00482               exit (-1);
00483              } 
00484           connections[cx].state = type;
00485           if (type == SYN)
00486              connections[cx].activity = 0;
00487           else
00488              connections[cx].activity = 1;
00489           break;
00490      }    
00491 
00492       case REQ:
00493      {
00494           cx = FindConnection(sp, dh, dp);
00495           if (cx < 0)  /* not there */
00496          {
00497               error_state("REQ for non-existent connection");
00498               return;
00499              }
00500           if ((connections[cx].state == RSP) ||
00501               (connections[cx].state == ACT_REQ) ||
00502               (connections[cx].state == SYN))
00503          {
00504               connections[cx].activity = 1; 
00505               connections[cx].state = REQ;
00506              }       
00507           else
00508               error_state("REQ in invalid connection state");
00509 
00510           break;
00511          }
00512 
00513       case RSP:
00514      {
00515           cx = FindConnection(sp, dh, dp);
00516           if (cx < 0)  /* not there */
00517          {
00518               error_state("RSP for non-existent connection");
00519               return;
00520              }
00521           if ((connections[cx].state == REQ) ||
00522               (connections[cx].state == ACT_RSP) ||
00523               (connections[cx].state == SYN))
00524          {
00525               connections[cx].activity = 0; 
00526               connections[cx].state = RSP;
00527              }      
00528           else
00529               error_state("RSP in invalid connection state");
00530 
00531           break;
00532          }
00533 
00534       case END:
00535      {
00536           cx = FindConnection(sp, dh, dp);
00537           if (cx < 0)  /* not there */
00538          {
00539               error_state("End for non-existent connection");
00540               return;
00541              }
00542           connections[cx].activity = 0; 
00543           connections[cx].state = END;
00544           cx = RemoveConnection(sp, dh, dp);
00545           break;
00546      }
00547       default:
00548       break;
00549 
00550      }
00551 }
00552 
00553 /* A set of functions to maintain the table of "active" connections for the
00554    current client (IP address). All of these use simple linear scans of the
00555    table because we expect the number of concurrently active connections 
00556    from a client to be small (< 100) */
00557 
00558 /* Clears the active connections from the connection table and 
00559    resets the count of active connections to zero */
00560 
00561 void ClearConnections(void)
00562 {
00563   int i;
00564 
00565   for (i = 0; i < active_connections; i++)
00566       {
00567        strcpy(connections[i].id, "");
00568        connections[i].activity = 0;
00569        connections[i].state = END;
00570       }
00571   active_connections = 0;
00572 }
00573 
00574 /* Count the number of active connections that have an outstanding
00575    request (activity == 1) */
00576 
00577 int ConnectionsActive(void)
00578 {
00579   int count = 0; 
00580   int i;
00581 
00582   for (i = 0; i < active_connections; i++)
00583       count = count + connections[i].activity;
00584 
00585   return (count);
00586 }
00587 
00588 /* Find a connection in the table by its identifying host/port 3-tuple
00589    and return its index (or -1 if not found).  Note that the source IP
00590    address is not necessary since the table is used for one client
00591    at a time.
00592 */
00593 
00594 int FindConnection(char *sp, char *dh, char *dp)
00595 {
00596   char connection[50];
00597   int i;
00598 
00599   strcpy(connection, sp);
00600   strcat(connection, dh);
00601   strcat(connection, dp);
00602 
00603   /* find the connection in the table */
00604   for (i = 0; i < active_connections; i++)
00605       {
00606        if (strcmp(connections[i].id, connection) == 0)
00607            break;
00608       }
00609   if (i == active_connections) /* not there */
00610      return(-1);
00611   else
00612      return (i);
00613 }
00614 
00615 /* Add a new connection to the table identified by its host/port
00616    3-tuple (source IP is not needed because the table is for one
00617    client (IP address) only).  Return the index of the added 
00618    connection or -1 if it is already in the table.  Increase the
00619    count of active connections by 1 if added.  Initialize the
00620    state of the added connection to the reset state.
00621 */  
00622 
00623 int AddConnection(char *sp, char *dh, char *dp)
00624 {
00625   char connection[50];
00626   int i;
00627 
00628   strcpy(connection, sp);
00629   strcat(connection, dh);
00630   strcat(connection, dp);
00631 
00632   /* check to see if connection already in the table; if not there, add it */
00633   for (i = 0; i < active_connections; i++)
00634       {
00635        if (strcmp(connections[i].id, connection) == 0)
00636            break;
00637       }
00638   if (i < active_connections)
00639      return(-1);   /* already there */
00640   else
00641      {
00642       active_connections += 1;
00643       if (active_connections > MAX_CONNECTIONS)
00644          return (active_connections);    /* table overflow */
00645       strcpy(connections[i].id, connection);
00646       connections[i].activity = 0;
00647       connections[i].state = END;
00648       return (i);
00649      }
00650 }
00651 
00652 
00653 /* Remove a connection from the table and compact the table by shifting
00654    all connections above the "hole" down by one index value.  Return 0
00655    if all is OK or -1 if the connection was not there.  Decrease the 
00656    count of active connections by one if one was removed.  Reset the 
00657    vacated table entry to the reset state
00658 */
00659 
00660 int RemoveConnection(char *sp, char *dh, char *dp)
00661 {
00662   char connection[50];
00663   int i, j;
00664 
00665   strcpy(connection, sp);
00666   strcat(connection, dh);
00667   strcat(connection, dp);
00668 
00669   /* find the connection in the table; if not there, error */
00670   for (i = 0; i < active_connections; i++)
00671       {
00672        if (strcmp(connections[i].id, connection) == 0)
00673            break;
00674       }
00675   if (i == active_connections)
00676       return (-1);
00677 
00678   /* move all active connections above this down by one with overwriting */ 
00679   for (j = i + 1; j < active_connections; j++)
00680       {
00681        strcpy(connections[j-1].id, connections[j].id);
00682        connections[j-1].activity = connections[j].activity;
00683        connections[j-1].state = connections[j].state;
00684       }
00685 
00686   /* clearing the top vacated slot is not strictly necessary but it may
00687      help with debugging */
00688   strcpy(connections[active_connections - 1].id, "");
00689   connections[active_connections - 1].activity = 0;
00690   connections[active_connections - 1].state = END;
00691 
00692   active_connections -= 1;
00693   return (0);
00694 }
00695 
00696 /* Copy the input line to the output file */
00697 void log_REQ(void)
00698 {
00699   fprintf(outFP, "%s", new_line);
00700 }
00701 
00702 /* Copy the input line to the output file */
00703 void log_RSP(void)
00704 {
00705   fprintf(outFP, "%s", new_line);
00706 }
00707 
00708 
00709 /* create a line in the output for the idle period */
00710 void log_IDLE(char *ts)
00711 {
00712   int elapsed;
00713 
00714   elapsed = (int) elapsed_ms(ts, idle_begin);
00715   fprintf(outFP, "%s %-15s %5s > %-15s %5s IDLE%12d  %s\n", 
00716                   ts, current_src, "*", "*", "*", elapsed, idle_begin);
00717                             
00718 }
00719 
00720 void error_line(char * s)
00721 {
00722   fprintf(outFP, "%s", s);
00723 }
00724 
00725 void error_state(char * s)
00726 {
00727   fprintf(outFP, "%s %-15s %5s > %-15s %5s ERROR %s\n", 
00728                   ts, current_src, "*", "*", "*", s);
00729 
00730 }
00731 
00732 /*--------------------------------------------------------------*/ 
00733 /* subtract two timevals (t1 - t0) with result in tdiff         */
00734 /* tdiff, t1 and t0 are all pointers to struct timeval          */
00735 /*--------------------------------------------------------------*/ 
00736 static void
00737 tvsub(tdiff, t1, t0)
00738 struct timeval *tdiff, *t1, *t0;
00739 {
00740 
00741         tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
00742         tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
00743         if (tdiff->tv_usec < 0)
00744            {
00745             tdiff->tv_sec--;
00746             tdiff->tv_usec += 1000000;
00747            }
00748 }
00749 
00750 /*--------------------------------------------------------------*/ 
00751 /* compute the elapsed time in milliseconds to end_time         */
00752 /* from some past time given by start_time (both formatted timevals) */
00753 /*--------------------------------------------------------------*/ 
00754 long elapsed_ms(char *end, char *start)
00755 {
00756  struct timeval delta, end_time, start_time;
00757  long elapsed_time;
00758 
00759  char end_tmp[20];
00760  char start_tmp[20];
00761 
00762  char *cursor;
00763  char *cp;
00764 
00765  strcpy(end_tmp, end);
00766  cursor = end_tmp;
00767  cp = (char *)strsep(&cursor, "." );
00768  end_time.tv_sec = atoi(end_tmp);
00769  end_time.tv_usec = atoi(cursor);
00770 
00771  strcpy(start_tmp, start);
00772  cursor = start_tmp;
00773  cp = (char *)strsep(&cursor, "." );
00774  start_time.tv_sec = atoi(start_tmp);
00775  start_time.tv_usec = atoi(cursor);
00776 
00777  tvsub(&delta, &end_time, &start_time);
00778  /* express as milliseconds */
00779  elapsed_time = (delta.tv_sec * 1000) + (delta.tv_usec/1000);
00780  return (elapsed_time);
00781 }
00782 
00783 
00784 

Generated on Tue Mar 6 16:47:46 2007 for ns2 Network Simulator 2.29 by  doxygen 1.4.6