queue-monitor.cc

Go to the documentation of this file.
00001 /* -*-  Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
00002 /*
00003  * Copyright (c) 1997 Regents of the University of California.
00004  * All rights reserved.
00005  * 
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, 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. All advertising materials mentioning features or use of this software
00015  *    must display the following acknowledgement:
00016  *      This product includes software developed by the MASH Research
00017  *      Group at the University of California Berkeley.
00018  * 4. Neither the name of the University nor of the Research Group may be
00019  *    used to endorse or promote products derived from this software without
00020  *    specific prior written permission.
00021  * 
00022  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00023  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00024  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00025  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00026  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00027  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00028  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00029  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00031  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00032  * SUCH DAMAGE.
00033  */
00034 
00035 #ifndef lint
00036 static const char rcsid[] =
00037     "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tools/queue-monitor.cc,v 1.29 2004/10/28 01:21:41 sfloyd Exp $";
00038 #endif
00039 
00040 #include "queue-monitor.h"
00041 #include "trace.h"
00042 #include <math.h>
00043 
00044 int QueueMonitor::command(int argc, const char*const* argv)
00045 {
00046     Tcl& tcl = Tcl::instance();
00047 
00048     if (argc == 2) {
00049         if (strcmp(argv[1], "get-bytes-integrator") == 0) {
00050             if (bytesInt_)
00051                 tcl.resultf("%s", bytesInt_->name());
00052             else
00053                 tcl.resultf("");
00054             return (TCL_OK);
00055         }
00056         if (strcmp(argv[1], "get-pkts-integrator") == 0) {
00057             if (pktsInt_)
00058                 tcl.resultf("%s", pktsInt_->name());
00059             else
00060                 tcl.resultf("");
00061             return (TCL_OK);
00062         }
00063         if (strcmp(argv[1], "get-delay-samples") == 0) {
00064             if (delaySamp_)
00065                 tcl.resultf("%s", delaySamp_->name());
00066             else
00067                 tcl.resultf("");
00068             return (TCL_OK);
00069         }
00070         if (strcmp(argv[1], "printRTTs") == 0) {
00071             if (keepRTTstats_ && channel1_) {
00072                 printRTTs();
00073             } 
00074             return (TCL_OK);
00075         }
00076         if (strcmp(argv[1], "printSeqnos") == 0) {
00077             if (keepSeqnoStats_ && channel1_) {
00078                 printSeqnos();
00079             } 
00080             return (TCL_OK);
00081         }
00082     }
00083 
00084     if (argc == 3) {
00085         if (strcmp(argv[1], "set-bytes-integrator") == 0) {
00086             bytesInt_ = (Integrator *)
00087                 TclObject::lookup(argv[2]);
00088             if (bytesInt_ == NULL)
00089                 return (TCL_ERROR);
00090             return (TCL_OK);
00091         }
00092         if (strcmp(argv[1], "set-pkts-integrator") == 0) {
00093             pktsInt_ = (Integrator *)
00094                 TclObject::lookup(argv[2]);
00095             if (pktsInt_ == NULL)
00096                 return (TCL_ERROR);
00097             return (TCL_OK);
00098         }
00099         if (strcmp(argv[1], "set-delay-samples") == 0) {
00100             delaySamp_ = (Samples*)
00101                 TclObject::lookup(argv[2]);
00102             if (delaySamp_ == NULL)
00103                 return (TCL_ERROR);
00104             return (TCL_OK);
00105         }
00106         if (strcmp(argv[1], "trace") == 0) {
00107             // for printStats
00108             int mode;
00109             const char* id = argv[2];
00110             channel_ = Tcl_GetChannel(tcl.interp(), (char*)id, &mode);
00111                         if (channel_ == 0) {
00112                 tcl.resultf("trace: can't attach %s for writing", id);
00113                 return (TCL_ERROR);
00114             }
00115             return (TCL_OK);
00116         }
00117         if (strcmp(argv[1], "traceDist") == 0) {
00118             // for printRTTs and printSeqnos distributions
00119             int mode;
00120             const char* id = argv[2];
00121             channel1_ = Tcl_GetChannel(tcl.interp(), (char*)id, &mode);
00122                         if (channel1_ == 0) {
00123                 tcl.resultf("trace: can't attach %s for writing", id);
00124                 return (TCL_ERROR);
00125             }
00126             return (TCL_OK);
00127         }
00128     }
00129     if (argc == 4) {
00130         if (strcmp(argv[1], "set-src-dst") == 0) {
00131             srcId_ = atoi(argv[2]);
00132             dstId_ = atoi(argv[3]);
00133             return (TCL_OK);
00134         }
00135     }
00136     return TclObject::command(argc, argv);  // else control reaches end of
00137                         // non-void function, see? :-)
00138 }
00139 
00140 static class QueueMonitorClass : public TclClass {
00141  public:
00142     QueueMonitorClass() : TclClass("QueueMonitor") {}
00143     TclObject* create(int, const char*const*) {
00144         return (new QueueMonitor());
00145     }
00146 } queue_monitor_class;
00147 
00148 
00149 void
00150 QueueMonitor::printRTTs() {
00151     int i, n, topBin, MsPerBin;
00152     char wrk[500];
00153 
00154     topBin = maxRTT_ * binsPerSec_;
00155     MsPerBin = int(1000/binsPerSec_);
00156     double now = Scheduler::instance().clock();
00157     sprintf(wrk, "Distribution of RTTs, %d ms bins, time %4.2f\n", MsPerBin, now);
00158     n = strlen(wrk); wrk[n] = 0;
00159     (void)Tcl_Write(channel1_, wrk, n);
00160     for (i = 0; i < topBin; i++) {
00161         if (RTTbins_[i] > 0) {
00162             sprintf(wrk, "%d to %d ms: frac %5.3f num %d time %4.2f\n", 
00163               i*MsPerBin, (i+1)*MsPerBin, 
00164               (double)RTTbins_[i]/numRTTs_,
00165               RTTbins_[i], now); 
00166             n = strlen(wrk); wrk[n] = 0; 
00167             (void)Tcl_Write(channel1_, wrk, n);
00168         }
00169     }
00170     i = topBin - 1;
00171     if (RTTbins_[i] > 0) {
00172         sprintf(wrk, "The last bin might also contain RTTs >= %d ms.\n",
00173         (i+1)*MsPerBin);
00174         n = strlen(wrk); wrk[n] = 0;
00175         (void)Tcl_Write(channel1_, wrk, n);
00176     }
00177 }
00178 
00179 void
00180 QueueMonitor::printSeqnos() {
00181     int i, n, topBin; 
00182     char wrk[500];
00183 
00184     topBin = int(maxSeqno_ / SeqnoBinSize_);
00185     double now = Scheduler::instance().clock();
00186     sprintf(wrk, "Distribution of Seqnos, %d seqnos per bin, time %4.2f\n", 
00187        SeqnoBinSize_, now);
00188     n = strlen(wrk); wrk[n] = 0;
00189     (void)Tcl_Write(channel1_, wrk, n);
00190     for (i = 0; i < topBin; i++) {
00191         if (SeqnoBins_[i] > 0) {
00192             sprintf(wrk, "%d to %d seqnos: frac %5.3f num %d time %4.2f\n", 
00193               i*SeqnoBinSize_, (i+1)*SeqnoBinSize_ - 1, 
00194               (double)SeqnoBins_[i]/numSeqnos_,
00195               SeqnoBins_[i], now); 
00196             n = strlen(wrk); wrk[n] = 0;
00197             (void)Tcl_Write(channel1_, wrk, n);
00198         }
00199     }
00200     i = topBin - 1;
00201     if (SeqnoBins_[i] > 0) {
00202         sprintf(wrk, "The last bin might also contain Seqnos >= %d. \n",
00203         (i+1)*SeqnoBinSize_);
00204         n = strlen(wrk); wrk[n] = 0;
00205         (void)Tcl_Write(channel1_, wrk, n);
00206     }
00207 }
00208 
00209 void
00210 QueueMonitor::printStats() {
00211     char wrk[500];
00212     int n;
00213     double now = Scheduler::instance().clock();
00214     sprintf(wrk, "q -t "TIME_FORMAT" -s %d -d %d -l %d -p %d", now, srcId_, dstId_, size_, pkts_);
00215     n = strlen(wrk);
00216     wrk[n] = '\n';
00217     wrk[n+1] = 0;
00218     (void)Tcl_Write(channel_, wrk, n+1);
00219     wrk[n] = 0;
00220 }   
00221 
00222 // packet arrival to a queue
00223 void QueueMonitor::in(Packet* p)
00224 {
00225     hdr_cmn* hdr = hdr_cmn::access(p);
00226     double now = Scheduler::instance().clock();
00227     int pktsz = hdr->size();
00228     hdr_flags* pf = hdr_flags::access(p);
00229 
00230     last_pkt_ = Scheduler::instance().clock();
00231     if (parrivals_ == 0) {
00232         first_pkt_ = last_pkt_;
00233     }
00234 
00235     if (pf->qs()) {
00236         qs_pkts_++;
00237         qs_bytes_ += pktsz;
00238     }
00239 
00240         //if enabled estimate rate now
00241     if (estimate_rate_) {
00242         estimateRate(p);
00243     }
00244     else {
00245         prevTime_ = now;
00246     }
00247 
00248     barrivals_ += pktsz;
00249     parrivals_++;
00250     size_ += pktsz;
00251     pkts_++;
00252     if (bytesInt_)
00253         bytesInt_->newPoint(now, double(size_));
00254     if (pktsInt_)
00255         pktsInt_->newPoint(now, double(pkts_));
00256     if (delaySamp_)
00257         hdr->timestamp() = now;
00258     if (channel_)
00259         printStats();
00260 
00261 }
00262 
00263 void QueueMonitor::out(Packet* p)
00264 {
00265     hdr_cmn* hdr = hdr_cmn::access(p);
00266     hdr_flags* pf = hdr_flags::access(p);
00267     double now = Scheduler::instance().clock();
00268     int pktsz = hdr->size();
00269 
00270     if (pf->ce() && pf->ect()) 
00271         pmarks_++;
00272     size_ -= pktsz;
00273     pkts_--;
00274     bdepartures_ += pktsz;
00275     pdepartures_++;
00276     if (bytesInt_)
00277         bytesInt_->newPoint(now, double(size_));
00278     if (pktsInt_)
00279         pktsInt_->newPoint(now, double(pkts_));
00280     if (delaySamp_)
00281         delaySamp_->newPoint(now - hdr->timestamp());
00282 
00283         if (keepRTTstats_) {
00284         keepRTTstats(p);
00285     }
00286         if (keepSeqnoStats_) {
00287         keepSeqnoStats(p);
00288     }
00289     if (channel_)
00290         printStats();
00291 }
00292 
00293 void QueueMonitor::drop(Packet* p)
00294 {
00295     hdr_cmn* hdr = hdr_cmn::access(p);
00296     double now = Scheduler::instance().clock();
00297     int pktsz = hdr->size();
00298     hdr_flags* pf = hdr_flags::access(p);
00299 
00300     size_ -= pktsz;
00301     pkts_--;
00302     bdrops_ += pktsz;
00303     pdrops_++;
00304 
00305     if (pf->qs())
00306         qs_drops_++;
00307 
00308     if (bytesInt_)
00309         bytesInt_->newPoint(now, double(size_));
00310     if (pktsInt_)
00311         pktsInt_->newPoint(now, double(pkts_));
00312     if (channel_)
00313         printStats();
00314 }
00315 
00316 // The procedure to estimate the rate of the incoming traffic
00317 void QueueMonitor::estimateRate(Packet *pkt) {
00318     
00319     hdr_cmn* hdr  = hdr_cmn::access(pkt);
00320     int pktSize   = hdr->size() << 3; /* length of the packet in bits */
00321 
00322     double now = Scheduler::instance().clock();
00323     double timeGap = ( now - prevTime_);
00324 
00325     if (timeGap == 0) {
00326         temp_size_ += pktSize;
00327         return;
00328     }
00329     else {
00330         pktSize+= temp_size_;
00331         temp_size_ = 0;
00332     }
00333     
00334     prevTime_ = now;
00335     
00336     estRate_ = (1 - exp(-timeGap/k_))*((double)pktSize)/timeGap + exp(-timeGap/k_)*estRate_;
00337 }
00338 
00339 //The procedure to keep RTT statistics.
00340 void QueueMonitor::keepRTTstats(Packet *pkt) {
00341         int i, j, topBin, rttInMs, MsPerBin;
00342     hdr_cmn* hdr  = hdr_cmn::access(pkt);
00343     packet_t t = hdr->ptype();
00344     if (t == PT_TCP || t == PT_HTTP || t == PT_FTP || t == PT_TELNET) {
00345         hdr_tcp *tcph = hdr_tcp::access(pkt);
00346         rttInMs = tcph->last_rtt(); 
00347         if (rttInMs < 0) rttInMs = 0;
00348         topBin = maxRTT_ * binsPerSec_;
00349         if (numRTTs_ == 0) {
00350             RTTbins_ = (int *)malloc(sizeof(int)*topBin);
00351             for (i = 0; i < topBin; i++) {
00352                 RTTbins_[i] = 0;
00353             }
00354         }
00355         MsPerBin = int(1000/binsPerSec_);
00356         j = (int)(rttInMs/MsPerBin);
00357         if (j < 0) j = 0;
00358         if (j >= topBin) j = topBin - 1;
00359         ++ RTTbins_[j];
00360         ++ numRTTs_;
00361     }
00362 }
00363 
00364 //The procedure to keep Seqno (sequence number) statistics.
00365 void QueueMonitor::keepSeqnoStats(Packet *pkt) {
00366         int i, j, topBin, seqno; 
00367     hdr_cmn* hdr  = hdr_cmn::access(pkt);
00368     packet_t t = hdr->ptype();
00369     if (t == PT_TCP || t == PT_HTTP || t == PT_FTP || t == PT_TELNET) {
00370         hdr_tcp *tcph = hdr_tcp::access(pkt);
00371         seqno = tcph->seqno(); 
00372         if (seqno < 0) seqno = 0;
00373         topBin = int(maxSeqno_ / SeqnoBinSize_);
00374         if (numSeqnos_ == 0) {
00375             SeqnoBins_ = (int *)malloc(sizeof(int)*topBin);
00376             for (i = 0; i < topBin; i++) {
00377                 SeqnoBins_[i] = 0;
00378             }
00379         }
00380         j = (int)(seqno/SeqnoBinSize_);
00381         if (j < 0) j = 0;
00382         if (j >= topBin) j = topBin - 1;
00383         ++ SeqnoBins_[j];
00384         ++ numSeqnos_;
00385     }
00386 }
00387 
00388 /* ##############
00389  * Tcl Stuff
00390  * ##############
00391  */
00392 
00393 static class SnoopQueueInClass : public TclClass {
00394 public:
00395     SnoopQueueInClass() : TclClass("SnoopQueue/In") {}
00396     TclObject* create(int, const char*const*) {
00397         return (new SnoopQueueIn());
00398     }
00399 } snoopq_in_class;
00400 
00401 static class SnoopQueueOutClass : public TclClass {
00402 public:
00403     SnoopQueueOutClass() : TclClass("SnoopQueue/Out") {}
00404     TclObject* create(int, const char*const*) {
00405         return (new SnoopQueueOut());
00406     }
00407 } snoopq_out_class;
00408 
00409 static class SnoopQueueDropClass : public TclClass {
00410 public:
00411     SnoopQueueDropClass() : TclClass("SnoopQueue/Drop") {}
00412     TclObject* create(int, const char*const*) {
00413         return (new SnoopQueueDrop());
00414     }
00415 } snoopq_drop_class;
00416 
00417 static class SnoopQueueEDropClass : public TclClass {
00418 public:
00419     SnoopQueueEDropClass() : TclClass("SnoopQueue/EDrop") {}
00420     TclObject* create(int, const char*const*) {
00421         return (new SnoopQueueEDrop);
00422     }
00423 } snoopq_edrop_class;
00424 
00425 /* Added by Yun Wang, for use of In/Out tagger */
00426 static class SnoopQueueTaggerClass : public TclClass {
00427 public:
00428         SnoopQueueTaggerClass() : TclClass("SnoopQueue/Tagger") {}
00429         TclObject* create(int, const char*const*) {
00430                 return (new SnoopQueueTagger);
00431         }
00432 } snoopq_tagger_class;
00433 
00434 static class QueueMonitorEDClass : public TclClass {
00435 public: 
00436     QueueMonitorEDClass() : TclClass("QueueMonitor/ED") {}
00437     TclObject* create(int, const char*const*) { 
00438         return (new EDQueueMonitor);
00439     }
00440 } queue_monitor_ed_class;
00441 
00442 
00443 /* ############################################################
00444  * a 'QueueMonitorCompat', which is used by the compat
00445  * code to produce the link statistics used available in ns-1
00446  *
00447  * in ns-1, the counters are the number of departures
00448  * ############################################################
00449  */
00450 
00451 #include "ip.h"
00452 QueueMonitorCompat::QueueMonitorCompat()
00453 {
00454     memset(pkts_, 0, sizeof(pkts_));
00455     memset(bytes_, 0, sizeof(bytes_));
00456     memset(drops_, 0, sizeof(drops_));
00457     memset(flowstats_, 0, sizeof(flowstats_));
00458 }
00459 
00460 
00461 /*
00462  * create an entry in the flowstats_ array.
00463  */
00464 
00465 void
00466 QueueMonitorCompat::flowstats(int flowid)
00467 {
00468     Tcl& tcl = Tcl::instance();
00469 
00470     /*
00471      * here is the deal.  we are in C code.  we'd like to do
00472      *     flowstats_[flowid] = new Samples;
00473      * but, we want to create an object that can be
00474      * referenced via tcl.  (in particular, we want ->name_
00475      * to be valid.)
00476      *
00477      * so, how do we do this?
00478      *
00479      * well, the answer is, call tcl to create it.  then,
00480      * do a lookup on the result from tcl!
00481      */
00482 
00483     tcl.evalf("new Samples");
00484     flowstats_[flowid] = (Samples*)TclObject::lookup(tcl.result());
00485     if (flowstats_[flowid] == 0) {
00486         abort();
00487         /*NOTREACHED*/
00488     }
00489 }
00490 
00491 
00492 void QueueMonitorCompat::out(Packet* pkt)
00493 {
00494     hdr_cmn* hdr = hdr_cmn::access(pkt);
00495     hdr_ip* iph = hdr_ip::access(pkt);
00496     double now = Scheduler::instance().clock();
00497     int fid = iph->flowid();
00498 
00499     if (fid >= MAXFLOW) {
00500         abort();
00501         /*NOTREACHED*/
00502     }
00503     // printf("QueueMonitorCompat::out(), fid=%d\n", fid);
00504     bytes_[fid] += hdr_cmn::access(pkt)->size();
00505     pkts_[fid]++;
00506     if (flowstats_[fid] == 0) {
00507         flowstats(fid);
00508     }
00509     flowstats_[fid]->newPoint(now - hdr->timestamp());
00510     QueueMonitor::out(pkt);
00511 }
00512 
00513 void QueueMonitorCompat::in(Packet* pkt)
00514 {
00515     hdr_cmn* hdr = hdr_cmn::access(pkt);
00516     double now = Scheduler::instance().clock();
00517     // QueueMonitor::in() *may* do this, but we always need it...
00518     hdr->timestamp() = now;
00519     QueueMonitor::in(pkt);
00520 }
00521 
00522 void QueueMonitorCompat::drop(Packet* pkt)
00523 {
00524 
00525     hdr_ip* iph = hdr_ip::access(pkt);
00526     int fid = iph->flowid();
00527     if (fid >= MAXFLOW) {
00528         abort();
00529         /*NOTREACHED*/
00530     }
00531     ++drops_[fid];
00532     QueueMonitor::drop(pkt);
00533 }
00534 
00535 int QueueMonitorCompat::command(int argc, const char*const* argv)
00536 {
00537     Tcl& tcl = Tcl::instance();
00538     int fid;
00539     if (argc == 3) {
00540         fid = atoi(argv[2]);
00541         if (strcmp(argv[1], "bytes") == 0) {
00542             if (fid >= MAXFLOW) {
00543                 abort();
00544                 /*NOTREACHED*/
00545             }
00546             tcl.resultf("%d", bytes_[fid]);
00547             return TCL_OK;
00548         } else if (strcmp(argv[1], "pkts") == 0) {
00549             if (fid >= MAXFLOW) {
00550                 abort();
00551                 /*NOTREACHED*/
00552             }
00553             tcl.resultf("%d", pkts_[fid]);
00554             return TCL_OK;
00555         } else if (strcmp(argv[1], "drops") == 0) {
00556             if (fid >= MAXFLOW) {
00557                 abort();
00558                 /*NOTREACHED*/
00559             }
00560             tcl.resultf("%d", drops_[fid]);
00561             return TCL_OK;
00562         } else if (strcmp(argv[1], "get-class-delay-samples") == 0) {
00563             if (fid >= MAXFLOW) {
00564                 abort();
00565                 /*NOTREACHED*/
00566             }
00567             if (flowstats_[fid] == 0) {
00568                 /*
00569                  * instantiate one if user actually
00570                  * cares enough to ask for it!
00571                  *
00572                  * (otherwise, need to return "",
00573                  * and then special-case caller to
00574                  * handle this null return.)
00575                  */
00576                 flowstats(fid);
00577             }
00578             tcl.resultf("%s", flowstats_[fid]->name());
00579             return TCL_OK;
00580         }
00581     }
00582     return (QueueMonitor::command(argc, argv));
00583 }
00584 
00585 static class QueueMonitorCompatClass : public TclClass {
00586  public: 
00587     QueueMonitorCompatClass() : TclClass("QueueMonitor/Compat") {}
00588     TclObject* create(int, const char*const*) { 
00589         return (new QueueMonitorCompat);
00590     }
00591 } queue_monitor_compat_class;

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