Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

read_timing.C

Go to the documentation of this file.
00001 // Copyright (C) 2004, Kenneth C. Schalk
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 // read_timing.C - Tool for reading and analyzing the repository's
00020 // fine-grained NFS call timing.
00021 
00022 // Last modified on Fri Apr 22 13:42:05 EDT 2005 by ken@xorian.net
00023 
00024 #include <sys/time.h> /* struct timeval */
00025 #include <Basics.H>
00026 #include <BasicsAllocator.H>
00027 #include <BufStream.H>
00028 #include <Generics.H>
00029 #include <FS.H>
00030 
00031 #include <list>
00032 #include <algorithm>
00033 
00034 #include <stdio.h>
00035 #include <math.h>
00036 #include <getopt.h>
00037 
00038 #include "nfs_prot.h"
00039 
00040 #include <ReposUI.H>
00041 
00042 #include "read_timing_common.H"
00043 
00044 #include <iomanip>
00045 
00046 using std::ostream;
00047 using std::ofstream;
00048 using std::cout;
00049 using std::cerr;
00050 using std::endl;
00051 using std::setw;
00052 using std::setfill;
00053 using std::hex;
00054 using std::dec;
00055 using std::list;
00056 using std::sort;
00057 using std::lower_bound;
00058 using Basics::OBufStream;
00059 
00060 static int IntCompare(const void *v1, const void *v2) throw ()
00061 {
00062     unsigned int *i1 = (unsigned int *)v1, *i2 = (unsigned int *)v2;
00063     return *i1 - *i2;
00064 }
00065 
00066 class Histogram
00067 {
00068 public:
00069   typedef unsigned int (*histo_map_t)(unsigned int);
00070 private:
00071   histo_map_t histo_map;
00072   IntIntTbl tbl;
00073 
00074 public:
00075   Histogram(histo_map_t map_func)
00076     : histo_map(map_func)
00077   {
00078   }
00079   Histogram()
00080     : histo_map(0)
00081   {
00082   }
00083 
00084   void SetHistoMap(histo_map_t map_func)
00085   {
00086     assert(this->histo_map == 0);
00087     this->histo_map = map_func;
00088   }
00089 
00090   void AddVal(unsigned int val)
00091   {
00092     if(this->histo_map)
00093       val = (*(this->histo_map))(val);
00094     IntKey key(val);
00095     int cnt = 0;
00096     (void) this->tbl.Get(key, /*OUT*/ cnt);
00097     cnt++;
00098     (void) this->tbl.Put(key, cnt);
00099   }
00100 
00101   void PrintHisto(ostream &out) const throw ()
00102   {
00103     // iterate over table to find the largest key
00104     IntIntIter it(&(this->tbl));
00105     IntKey key; int val, i = 0;
00106     int max_key = 0;
00107     while (it.Next(/*OUT*/ key, /*OUT*/ val))
00108       {
00109         if(key.Val() > max_key)
00110           {
00111             max_key = key.Val();
00112           }
00113       }
00114 
00115     // Print the histogram in increasing key order.  Note that we may
00116     // print lines for values not in the table.  This makes for data
00117     // more readily usable with gnuplot.
00118     for (i = 0; i <= max_key; i++)
00119       {
00120         key = i;
00121         val = 0;
00122         (void) this->tbl.Get(key, /*OUT*/ val);
00123         out << i << "\t" << val << endl;
00124       }
00125   }
00126 
00127   Histogram &operator+=(const Histogram &other)
00128   {
00129     // We'd better both be using the same histogram mapping function.
00130     assert(other.histo_map == this->histo_map);
00131 
00132     // iterate over table, filling keys array
00133     IntIntIter it(&(other.tbl));
00134     IntKey key; int val;
00135     while (it.Next(/*OUT*/ key, /*OUT*/ val))
00136       {
00137         int my_count = 0;
00138         (void) this->tbl.Get(key, /*OUT*/ my_count);
00139         my_count += val;
00140         (void) this->tbl.Put(key, my_count);
00141       }
00142 
00143     return *this;
00144   }
00145 };
00146 
00147 // For use with Histogram
00148 
00149 static unsigned int Log2(unsigned int x) throw ()
00150 {
00151     int res = 0;
00152     for (unsigned int n = 1U; n < x; n <<= 1) res++;
00153     return res;
00154 }
00155 
00156 struct Time_Point
00157 {
00158   Atom file;
00159   unsigned short line;
00160 
00161   Delta_Time delta;
00162 
00163   void read(FILE *input) throw(FS::EndOfFile)
00164   {
00165     // Read the delta time
00166     delta.read(input);
00167 
00168     // Read the filename
00169     char fname_buf[1024];
00170     unsigned int fname_pos = 0;
00171     int next_char;
00172     while((next_char = getc(input)) != EOF)
00173       {
00174         fname_buf[fname_pos++] = next_char;
00175         if(next_char == 0)
00176           break;
00177       }
00178     if(next_char == EOF)
00179       {
00180         throw FS::EndOfFile();
00181       }
00182     file = fname_buf;
00183 
00184     // Read the line number
00185     size_t n_read = fread((void *) &line, sizeof(line), 1, input);
00186     if(n_read < 1)
00187       {
00188         throw FS::EndOfFile();
00189       }
00190     assert(n_read == 1);
00191   }
00192 
00193   Time_Point(FILE *input) throw(FS::EndOfFile)
00194   {
00195     read(input);
00196   }
00197 
00198   Time_Point()
00199     : line(0), file("<invalid>")
00200   {
00201   }
00202 
00203   Time_Point(const Time_Point &other)
00204     : file(other.file), line(other.line), delta(other.delta)
00205   {
00206   }
00207 
00208   bool operator==(const Time_Point &other) const throw()
00209   {
00210     return ((file == other.file) &&
00211             (line == other.line) &&
00212             (delta == other.delta));
00213   }
00214 
00215   Time_Point &operator=(const Time_Point &other) throw()
00216   {
00217     file = other.file;
00218     line = other.line;
00219     delta = other.delta;
00220     return *this;
00221   }
00222 
00223   bool operator!=(const Time_Point &other) const throw()
00224   {
00225     return !(*this == other);
00226   }
00227 
00228   Text as_text() const throw()
00229   {
00230     OBufStream time_point_stream;
00231     time_point_stream << this->file << ":"
00232                       << this->line;
00233     return Text(time_point_stream.str());
00234   }
00235 };
00236 
00237 typedef Sequence<Time_Point> Time_Point_Seq;
00238 
00239 struct Source_Interval
00240 {
00241   Atom start, end;
00242 
00243   Source_Interval()
00244     : start("<invalid strart>"), end("<invalid end>")
00245   {
00246   }
00247 
00248   Source_Interval(const Source_Interval &other)
00249     : start(other.start), end(other.end)
00250   {
00251   }
00252 
00253   Source_Interval(const Atom &start, const Atom &end)
00254     : start(start), end(end)
00255   {
00256   }
00257 
00258   // Methods required to be a key in a Table
00259 
00260   Word Hash() const throw ()
00261   {
00262     return (start.Hash() ^ end.Hash());
00263   }
00264 
00265   Source_Interval& operator=(const Source_Interval &other) throw ()
00266   {
00267     start = other.start;
00268     end = other.end;
00269     return *this;
00270   }
00271 
00272   bool operator == (const Source_Interval &other) throw ()
00273   {
00274     return ((start == other.start) && (end == other.end));
00275   }
00276 
00277   Text as_fname() const throw()
00278   {
00279     return Text(start.cchars())+"_to_"+end;
00280   }
00281 
00282   Text as_text() const throw()
00283   {
00284     return Text(start.cchars())+" to "+end;
00285   }
00286 };
00287 
00288 ostream &operator<<(ostream &out, const Source_Interval &interval)
00289 {
00290   out << interval.as_text();
00291   return out;
00292 }
00293 
00294 // The represents the time interval in which a LongId was recorded.
00295 // (The intent is that this time interval is the one where it's worth
00296 // knowing the LongId.)
00297 struct LongId_Point
00298 {
00299   LongId *longid;
00300   Delta_Time time;
00301 
00302   LongId_Point(LongId *l, Delta_Time t) : longid(l), time(t) { }
00303   LongId_Point(const LongId_Point &other) : longid(other.longid), time(other.time) { }
00304   LongId_Point() : longid(0) { }
00305 
00306   LongId_Point &operator=(const LongId_Point &other)
00307   {
00308     longid = other.longid;
00309     time = other.time;
00310     return *this;
00311   }
00312 
00313   // Compare based on delta time.
00314   bool operator<(const LongId_Point &other) const
00315   {
00316     return other.time > this->time;
00317   }
00318 };
00319 
00320 bool LongId_Point_Slower(const LongId_Point &a, const LongId_Point &b)
00321 {
00322   return a.time > b.time;
00323 }
00324 
00325 typedef list<LongId_Point,
00326              Basics::Allocator<LongId_Point> > LongId_Point_List;
00327 
00328 typedef Table<Source_Interval, Delta_Time>::Default Interval_Times;
00329 typedef Table<Source_Interval, Delta_Time>::Iterator Interval_Times_Iter;
00330 
00331 typedef Table<Source_Interval, unsigned long>::Default Interval_Counts;
00332 typedef Table<Source_Interval, unsigned long>::Iterator Interval_Counts_Iter;
00333 
00334 typedef Table<Source_Interval, Histogram*>::Default Interval_Histos;
00335 typedef Table<Source_Interval, Histogram*>::Iterator Interval_Histos_Iter;
00336 
00337 typedef Table<Source_Interval, LongId_Point_List*>::Default LongId_Intervals;
00338 typedef Table<Source_Interval, LongId_Point_List*>::Iterator LongId_Intervals_Iter;
00339 
00340 class Interval_Time_Greater
00341 {
00342 private:
00343   Interval_Times &table;
00344 public:
00345   Interval_Time_Greater(Interval_Times &table)
00346     : table(table)
00347   {
00348   }
00349 
00350   bool operator()(const Source_Interval &a, const Source_Interval &b)
00351   {
00352     Delta_Time time_a, time_b;
00353     bool inTbl = table.Get(a, time_a);
00354     assert(inTbl);
00355     inTbl = table.Get(b, time_b);
00356     assert(inTbl);
00357 
00358     return time_a > time_b;
00359   }
00360 };
00361 
00362 class Interval_Count_Greater
00363 {
00364 private:
00365   Interval_Counts &table;
00366 public:
00367   Interval_Count_Greater(Interval_Counts &table)
00368     : table(table)
00369   {
00370   }
00371 
00372   bool operator()(const Source_Interval &a, const Source_Interval &b)
00373   {
00374     unsigned long count_a, count_b;
00375     bool inTbl = table.Get(a, count_a);
00376     assert(inTbl);
00377     inTbl = table.Get(b, count_b);
00378     assert(inTbl);
00379 
00380     return count_a > count_b;
00381   }
00382 };
00383 
00384 Interval_Times &operator+=(Interval_Times &lhs, const Interval_Times &rhs)
00385 {
00386   Interval_Times_Iter rhs_iter(&rhs);
00387   Source_Interval interval;
00388   Delta_Time rhs_time;
00389   while(rhs_iter.Next(interval, rhs_time))
00390     {
00391       Delta_Time lhs_time;
00392       (void) lhs.Get(interval, lhs_time);
00393       lhs_time += rhs_time;
00394       (void) lhs.Put(interval, lhs_time);
00395     }
00396 
00397   return lhs;
00398 }
00399 
00400 template<class K, class V>
00401 K *table_keys(const typename Table<K, V>::Default &table)
00402 {
00403   unsigned int size = table.Size(), i = 0;
00404   K *result = NEW_ARRAY(K, table.Size());
00405 
00406   typename Table<K, V>::Iterator iter(&table);
00407   V unused;
00408   while(iter.Next(result[i], unused))
00409     {
00410       i++;
00411       assert(i <= size);
00412     }
00413   assert(i == size);
00414 
00415   return result;
00416 }
00417 
00418 struct Call_Data
00419 {
00420   struct FileFormatError {};
00421   struct timeval start_time;
00422   Delta_Time elapsed_time;
00423 
00424   int proc;
00425 
00426   Time_Point_Seq time_points;
00427 
00428   bool duplicate, have_dupe;
00429   Delta_Time dupe_time;
00430 
00431   LongId *longid;
00432   unsigned int longid_start_point;
00433 
00434   void read(FILE *input) throw(FS::EndOfFile, FileFormatError)
00435   {
00436     int next_byte = getc(input);
00437     if(next_byte == 1)
00438       {
00439         // NFS procedure of this call
00440         proc = getc(input);
00441         if(proc == EOF)
00442           {
00443             throw FS::EndOfFile();
00444           }
00445 
00446         // Start time
00447         unsigned int start_secs, start_usecs;
00448         size_t n_read =
00449           fread((void *) &start_secs, sizeof(unsigned int), 1, input);
00450         if(n_read < 1)
00451           {
00452             throw FS::EndOfFile();
00453           }
00454         assert(n_read == 1);
00455         n_read =
00456           fread((void *) &start_usecs, sizeof(unsigned int), 1, input);
00457         if(n_read < 1)
00458           {
00459             throw FS::EndOfFile();
00460           }
00461         assert(n_read == 1);
00462 
00463         start_time.tv_sec = start_secs;
00464         start_time.tv_usec = start_usecs;
00465         while(1)
00466           {
00467             next_byte = getc(input);
00468             if(next_byte == EOF)
00469               {
00470                 throw FS::EndOfFile();
00471               }
00472             // Intermediate time point
00473             else if(next_byte == 2)
00474               {
00475                 Time_Point point(input);
00476 
00477                 time_points.addhi(point);
00478               }
00479             // End of call
00480             else if(next_byte == 3)
00481               {
00482                 elapsed_time.read(input);
00483                 break;
00484               }
00485             // Duplicate status
00486             else if((next_byte == 4) || (next_byte == 5))
00487               {
00488                 duplicate = (next_byte == 5);
00489                 dupe_time.read(input);
00490                 have_dupe = true;
00491               }
00492             // LongId
00493             else if(next_byte == 6)
00494               {
00495                 longid = NEW(LongId);
00496                 longid_start_point = time_points.size();
00497                 size_t read_count = fread(&(longid->value.byte[0]),
00498                                           sizeof(longid->value), 1,
00499                                           input);
00500                 if(read_count < 1)
00501                   {
00502                     throw FS::EndOfFile();
00503                   }
00504               }
00505           }
00506       }
00507     else if(next_byte == EOF)
00508       {
00509         throw FS::EndOfFile();
00510       }
00511     else
00512       {
00513         // File format error?
00514         throw FileFormatError();
00515       }
00516   }
00517 
00518   Call_Data(FILE *input) throw(FS::EndOfFile, FileFormatError)
00519     : have_dupe(false), longid(0)
00520   {
00521     read(input);
00522   }
00523 
00524   Call_Data() throw()
00525     : have_dupe(false), proc(NFSPROC_NULL), longid(0)
00526   {
00527   }
00528 
00529   Interval_Times *compute_interval_times(Source_Interval &max_interval,
00530                                          Source_Interval &longid_interval);
00531 
00532   void longid_interval(Source_Interval &interval,
00533                        Delta_Time &time) const;
00534 
00535   // Required to be in a list
00536 
00537   bool operator==(const Call_Data &other) const
00538   {
00539     // Simple comparisons first
00540     if((this->proc != other.proc) ||
00541        (this->start_time.tv_sec != other.start_time.tv_sec) ||
00542        (this->start_time.tv_usec != other.start_time.tv_usec) ||
00543        (this->elapsed_time != other.elapsed_time) ||
00544        (this->time_points.size() != other.time_points.size()))
00545       return false;
00546 
00547     // Duplicate status/time comparison
00548     if(this->have_dupe)
00549       {
00550         if(!other.have_dupe)
00551           return false;
00552 
00553         if((this->duplicate && !other.duplicate) ||
00554            (!this->duplicate && other.duplicate))
00555           return false;
00556 
00557         if(this->dupe_time != other.dupe_time)
00558           return false;
00559       }
00560 
00561     // Time point comparison
00562     for(unsigned int i = 0; i < this->time_points.size(); i++)
00563       {
00564         if(this->time_points.get(i) != other.time_points.get(i))
00565           return false;
00566       }
00567 
00568     // I guess they're equal.
00569     return true;
00570   }
00571 
00572   // Compare based on elapsed time.
00573   bool operator<(const Call_Data &other) const
00574   {
00575     return other.elapsed_time > this->elapsed_time;
00576   }
00577 };
00578 
00579 Interval_Times *Call_Data::compute_interval_times(Source_Interval &max_interval,
00580                                                   Source_Interval &longid_interval)
00581 {
00582   Interval_Times *result = NEW_CONSTR(Interval_Times,
00583                                       (time_points.size(), true));
00584   Delta_Time last_time;
00585   Atom last_point = "start";
00586   Delta_Time max_time;
00587 
00588   if(have_dupe)
00589     {
00590       Atom current_point = "dupe";
00591       Source_Interval interval(last_point, current_point);
00592       (void) result->Put(interval, dupe_time);
00593 
00594       last_time = dupe_time;
00595       last_point = current_point;
00596 
00597       max_time = dupe_time;
00598       max_interval = interval;
00599     }
00600 
00601   for(unsigned int i = 0; i < time_points.size(); i++)
00602     {
00603       Time_Point time_point = time_points.get(i);
00604       Atom current_point(time_point.as_text());
00605 
00606       Source_Interval interval(last_point, current_point);
00607 
00608       // Get the interval from the table first. If we hit this
00609       // interval multiple times, this will accumulate total time
00610       // spent in the interval.
00611       Delta_Time interval_time;
00612       (void) result->Get(interval, interval_time);
00613 
00614       interval_time += (time_point.delta - last_time);
00615 
00616       (void) result->Put(interval, interval_time);
00617 
00618       if(interval_time > max_time)
00619         {
00620           max_time = interval_time;
00621           max_interval = interval;
00622         }
00623 
00624       if((longid != 0) && (i == longid_start_point))
00625         {
00626           longid_interval = interval;
00627         }
00628 
00629       last_time = time_point.delta;
00630       last_point = current_point;
00631     }
00632 
00633   // Time to the end.
00634 
00635   Source_Interval interval(last_point, "end");
00636   Delta_Time interval_time = elapsed_time - last_time;
00637   (void) result->Put(interval, interval_time);
00638 
00639   if(interval_time > max_time)
00640     {
00641       max_time = interval_time;
00642       max_interval = interval;
00643     }
00644 
00645   if((longid != 0) && (time_points.size() == longid_start_point))
00646     {
00647       longid_interval = interval;
00648     }
00649 
00650   return result;
00651 }
00652 
00653 void Call_Data::longid_interval(/*OUT*/ Source_Interval &interval,
00654                                 /*OUT*/ Delta_Time &time) const
00655 {
00656   assert(longid != 0);
00657 
00658   Text start_point, end_point;
00659   Delta_Time start_time, end_time;
00660   if(longid_start_point == 0)
00661     {
00662       if(have_dupe)
00663         {
00664           start_point = "dupe";
00665           start_time = dupe_time;
00666         }
00667       else
00668         {
00669           start_point = "start";
00670         }
00671     }
00672   else
00673     {
00674       assert(longid_start_point <= time_points.size());
00675 
00676       Time_Point time_point = time_points.get(longid_start_point-1);
00677       start_point = time_point.as_text();
00678       start_time = time_point.delta;
00679     }
00680 
00681   if(longid_start_point < time_points.size())
00682     {
00683       Time_Point time_point = time_points.get(longid_start_point);
00684       end_point = time_point.as_text();
00685       end_time = time_point.delta;
00686     }
00687   else
00688     {
00689       end_point = "end";
00690       end_time = elapsed_time;
00691     }
00692 
00693   time = end_time - start_time;
00694   interval = Source_Interval(start_point, end_point);
00695 }
00696 
00697 typedef list<Call_Data,
00698              Basics::Allocator<Call_Data> > Call_List;
00699 
00700 void sorted_slow_calls_insert(Call_List &slow_calls, const Call_Data &new_call)
00701 {
00702 #if 0
00703     Call_List::iterator start = slow_calls.begin();
00704     Call_List::iterator end = slow_calls.end();
00705     Call_List::iterator placement = start;
00706     int mid = slow_calls.size();
00707 
00708     while (start != end) {
00709         placement = start;
00710         mid = mid/2;
00711         for (int ii = 0; ii < mid; ii++) {
00712             ++placement;
00713         }
00714         if (new_call.elapsed_time > (*placement).elapsed_time) {
00715             start = placement;
00716             if (mid == 0 && start != end) {
00717                 ++start;
00718             }
00719         } else {
00720             end = placement;
00721             if (mid == 0 && start != end) {
00722                 --end;
00723             }
00724         }
00725     }
00726     placement = end;    
00727 #else
00728     Call_List::iterator placement = lower_bound(slow_calls.begin(),
00729                                                 slow_calls.end(),
00730                                                 new_call);
00731 #endif
00732     slow_calls.insert(placement, new_call);
00733 }
00734 
00735 static const char *NFS_proc_name(int proc)
00736 {
00737   switch (proc)
00738     {
00739     case NFSPROC_NULL:
00740       return "null";
00741     case NFSPROC_GETATTR:
00742       return "getattr";
00743     case NFSPROC_SETATTR:
00744       return "setattr";
00745     case NFSPROC_ROOT:
00746       return "root";
00747     case NFSPROC_LOOKUP:
00748       return "lookup";
00749     case NFSPROC_READLINK:
00750       return "readlink";
00751     case NFSPROC_READ:
00752       return "read";
00753     case NFSPROC_WRITECACHE:
00754       return "writecache";
00755     case NFSPROC_WRITE:
00756       return "write";
00757     case NFSPROC_CREATE:
00758       return "create";
00759     case NFSPROC_REMOVE:
00760       return "remove";
00761     case NFSPROC_RENAME:
00762       return "rename";
00763     case NFSPROC_LINK:
00764       return "link";
00765     case NFSPROC_SYMLINK:
00766       return "symlink";
00767     case NFSPROC_MKDIR:
00768       return "mkdir";
00769     case NFSPROC_RMDIR:
00770       return "rmdir";
00771     case NFSPROC_READDIR:
00772       return "readdir";
00773     case NFSPROC_STATFS:
00774       return "statfs";
00775     }
00776   return "<INVALID>";
00777 }
00778 
00779 #define NUM_NFS_PROCS 18
00780 
00781 struct Proc_Totals
00782 {
00783   unsigned long count;
00784   Delta_Time time;
00785   Histogram time_histo;
00786 
00787   Call_List slowest_calls;
00788 
00789   LongId_Intervals slow_stable_longids, slow_volatile_longids;
00790 
00791   Proc_Totals()
00792     : count(0), time_histo(Log2)
00793   {
00794   }
00795 
00796   void add_call(const Call_Data &call)
00797   {
00798     // Increment totals
00799     this->time += call.elapsed_time;
00800     this->count++;
00801 
00802     // Figure out how many slow calls we want to keep.  (The real goal
00803     // is to get the slowest 1%, but in case the distributuion is
00804     // un-even, we keep more than 1%.)
00805     unsigned long two_percent_count =
00806       (unsigned long) ceil(((double)2*this->count)/100);
00807     unsigned long one_percent_plus_count = 
00808       (unsigned long) ceil(((double)this->count)/100);
00809     one_percent_plus_count += 20;
00810 
00811     unsigned long keep_count =
00812       ((one_percent_plus_count > two_percent_count)
00813        ? one_percent_plus_count
00814        : two_percent_count);
00815 
00816     // Store this in the slowest calls list if appropriate.
00817     if(this->slowest_calls.size() <
00818        keep_count)
00819       {
00820         sorted_slow_calls_insert(this->slowest_calls,
00821                                  call);
00822       }
00823     else if(call.elapsed_time >
00824             this->slowest_calls.front().elapsed_time)
00825       {
00826         sorted_slow_calls_insert(this->slowest_calls,
00827                                  call);
00828         this->slowest_calls.pop_front();
00829       }
00830 
00831     // If this call recorded a LongId...
00832     if(call.longid != 0)
00833       {
00834         // Get the source interval the LongId was recorded in (the
00835         // "interesting" one) and the time of that interval.
00836         Source_Interval lid_interval;
00837         Delta_Time lid_time;
00838         call.longid_interval(/*OUT*/ lid_interval,
00839                              /*OUT*/ lid_time);
00840 
00841         // Pick the right table of LongIds in slow intervals
00842         LongId_Intervals &lid_interval_table =
00843           (VolatileRootLongId.isAncestorOf(*(call.longid)))
00844           ? this->slow_volatile_longids
00845           : this->slow_stable_longids;
00846 
00847         // Get the list of LongIds recorded for this interval
00848         LongId_Point_List *lid_points = 0;
00849         bool inTbl = lid_interval_table.Get(lid_interval, lid_points);
00850         if(!inTbl)
00851           {
00852             // Create the list
00853             lid_points = NEW(LongId_Point_List);
00854             (void) lid_interval_table.Put(lid_interval, lid_points);
00855           }
00856         
00857         // Insert into the list if it makes sense to do so.
00858         if((lid_points->size() < keep_count) ||
00859            (lid_time > lid_points->front().time))
00860           {
00861             LongId_Point new_point = LongId_Point(call.longid, lid_time);
00862             LongId_Point_List::iterator placement =
00863               lower_bound(lid_points->begin(),
00864                           lid_points->end(),
00865                           new_point);
00866             lid_points->insert(placement, new_point);
00867           }
00868 
00869         // Prune the points
00870         if(lid_points->size() > keep_count)
00871           {
00872             lid_points->pop_front();
00873           }
00874       }
00875 
00876     // Record this call's elapsed time in the histogram.
00877     this->time_histo.AddVal((unsigned int) floor(call.elapsed_time.msecs()));
00878   }
00879 
00880   void merge(struct Proc_Totals &other)
00881   {
00882     this->time += other.time;
00883     this->count += other.count;
00884     this->time_histo += other.time_histo;
00885 
00886     this->slowest_calls.merge(other.slowest_calls);
00887 
00888     // Merge slow LongId data
00889     Source_Interval longid_interval;
00890     LongId_Point_List *their_point_list;
00891     {
00892       LongId_Intervals_Iter lid_it(&(other.slow_stable_longids));
00893       while(lid_it.Next(longid_interval, their_point_list))
00894         {
00895           LongId_Point_List *my_point_list;
00896           if(this->slow_stable_longids.Get(longid_interval,
00897                                            /*OUT*/ my_point_list))
00898             {
00899               // We already have a list for this source interval:
00900               // merge their list into ours.
00901               my_point_list->merge(*their_point_list);
00902             }
00903           else
00904             {
00905               // We don't have a list for this source interval: take
00906               // theirs as ours.
00907               this->slow_stable_longids.Put(longid_interval, their_point_list);
00908             }
00909         }
00910     }
00911     {
00912       LongId_Intervals_Iter lid_it(&(other.slow_volatile_longids));
00913       while(lid_it.Next(longid_interval, their_point_list))
00914         {
00915           LongId_Point_List *my_point_list;
00916           if(this->slow_volatile_longids.Get(longid_interval,
00917                                            /*OUT*/ my_point_list))
00918             {
00919               // We already have a list for this source interval:
00920               // merge their list into ours.
00921               my_point_list->merge(*their_point_list);
00922             }
00923           else
00924             {
00925               // We don't have a list for this source interval: take
00926               // theirs as ours.
00927               this->slow_volatile_longids.Put(longid_interval,
00928                                               their_point_list);
00929             }
00930         }
00931     }
00932   }
00933 };
00934 
00935 // Lifted from VLogHelp.C
00936 ostream&
00937 operator<<(ostream& s, const LongId& longid)
00938 {
00939     int i;
00940     int len = longid.length();
00941     for (i=0; i<len; i++) {
00942         s << setw(2) << setfill('0') << hex
00943           << (int) (longid.value.byte[i] & 0xff);
00944     }
00945     s << setfill(' ') << dec;
00946     return s;
00947 }
00948 
00949 static void display_LongId(const LongId& longid, const char *indent, ostream& out)
00950 {
00951   // First just print the raw LongId
00952   out << indent << longid << endl;
00953 
00954   // If this is in the appendable or mutable roots, try to look it up
00955   if((longid.value.byte[0] != 0) ||
00956      ((longid.value.byte[0] == 0) && (longid.value.byte[1] == 1)))
00957     {
00958       try
00959         {
00960           // The object in question may have been deleted.  If looking
00961           // it up fails, we look up its parent.  We repeat this until
00962           // we find an object that exists.
00963           LongId current = longid;
00964           unsigned int missing = 0;
00965           VestaSource *source = 0;
00966           while((source = current.lookup()) == 0)
00967             {
00968               current = current.getParent();
00969               missing++;
00970             }
00971 
00972           out << indent << ReposUI::vsToFilename(source);
00973           for(unsigned int printed = 0; missing > printed; printed++)
00974             {
00975               out << "/<deleted" << printed+1 << ">";
00976             }
00977           out << endl;
00978         }
00979       catch(...)
00980         {
00981           out <<  indent << "(exception caught while trying to get more information)"
00982               << endl;
00983         }
00984     }
00985   // In the volatile root
00986   else if((longid.value.byte[0] == 0) && (longid.value.byte[1] == 2))
00987     {
00988       out << indent << "<volatile root>";
00989 
00990       if(longid.value.byte[2] != 0)
00991         {
00992           // Climb up until we find the volatile root, counting the
00993           // directory depth and remembering the last index.
00994           LongId current = longid;
00995           unsigned int children = 0;
00996           unsigned int index = 0;
00997           while(!(current == VolatileRootLongId))
00998             {
00999               current = current.getParent(&index);
01000               children++;
01001             }
01002           char first_arc[(sizeof(index) * 2) + 1];
01003           sprintf(first_arc, "%08x", index);
01004           out << "/" << first_arc;
01005           for(unsigned int printed = 1; children > printed; printed++)
01006             {
01007               out << "/<arc" << printed << ">";
01008             }
01009         }
01010 
01011       out << endl;
01012     }
01013   // Directory shortid
01014   else if((longid.value.byte[0] == 0) && (longid.value.byte[1] == 3))
01015     {
01016       LongId current = longid;
01017       unsigned int index = 0;
01018       while(!(current == DirShortIdRootLongId))
01019         {
01020           current = current.getParent(&index);
01021         }
01022       out << indent << "<directory shortid 0x" << hex << index << ">" << endl;
01023     }
01024   // File shortid
01025   else if((longid.value.byte[0] == 0) && (longid.value.byte[1] == 4))
01026     {
01027       LongId current = longid;
01028       unsigned int index = 0;
01029       while(!(current == FileShortIdRootLongId))
01030         {
01031           current = current.getParent(&index);
01032         }
01033       out << indent << "<file shortid 0x" << hex << index << ">" << endl;
01034     }
01035   // NullLongId
01036   else if((longid.value.byte[0] == 0) && (longid.value.byte[1] == 255))
01037     {
01038       out << indent << "<null LongId>" << endl;
01039     }
01040 }
01041 
01042 static const char *program_name;
01043 
01044 static void usage()
01045 {
01046   cerr << "Usage: " << program_name
01047        << " [-o <output prefix>] <timing file(s)>"
01048        << endl;
01049   exit(1);
01050 }
01051 
01052 void parse(int optind, int argc, char * const argv[], Proc_Totals totals[]) {
01053 
01054   Proc_Totals* tot[NUM_NFS_PROCS];
01055   for(int proc = 0; proc < NUM_NFS_PROCS; proc++) { 
01056       tot[proc] = NEW_ARRAY(Proc_Totals, argc-optind);
01057   }
01058 
01059   for(int arg = optind; arg < argc; arg++) {
01060       OBufStream l_cmd;
01061       l_cmd << "gunzip -q -c " << argv[arg];
01062       FILE *stream = popen(l_cmd.str(), "r");
01063       unsigned long file_count = 0;
01064 
01065       try
01066         {
01067           while(1)
01068             {
01069               Call_Data next_call(stream);
01070 
01071               // Skip duplicates
01072               if(next_call.have_dupe && next_call.duplicate)
01073                 continue;
01074 
01075               int proc = next_call.proc;
01076               assert(proc < NUM_NFS_PROCS);
01077 
01078               // Store data about this call
01079               tot[proc][arg-optind].add_call(next_call);
01080 
01081               file_count++;
01082             }
01083         }
01084       catch(FS::EndOfFile)
01085         {
01086           // We don't care
01087         }
01088       catch(Call_Data::FileFormatError)
01089         {
01090           cerr << "File format error reading " << argv[arg] << endl;
01091         }
01092 
01093       cout << argv[arg] << ": " << file_count << " non-duplicate calls"
01094            << endl;
01095 
01096       int gunzip_status = pclose(stream);
01097       cout << "gunzip status: " << gunzip_status << endl;
01098   } 
01099 
01100   for(int proc = 0; proc < NUM_NFS_PROCS; proc++) { 
01101       for(int arg = 0; arg < argc-optind; arg++) {
01102         totals[proc].merge(tot[proc][arg]);
01103 #if 0
01104           totals[proc].time += tot[proc][arg].time;
01105           totals[proc].count += tot[proc][arg].count;
01106           totals[proc].time_histo += tot[proc][arg].time_histo;
01107 
01108           totals[proc].slowest_calls.merge(tot[proc][arg].slowest_calls);
01109 #endif
01110       }
01111       delete [] tot[proc];
01112   }
01113 
01114 }
01115 
01116 int main(int argc, char * const argv[])
01117 {
01118   program_name = argv[0];
01119   const char *output_prefix = "read_timing.out";
01120 
01121   int option;
01122   opterr = 0;
01123   while((option = getopt(argc, argv, "ho:")) != EOF)
01124     {
01125       switch(option)
01126         {
01127         case 'o':
01128           output_prefix = optarg;
01129           break;
01130         case 'h':
01131         case '?':
01132         default:
01133           usage();
01134         }
01135     }
01136 
01137   Proc_Totals totals[NUM_NFS_PROCS];
01138   parse(optind, argc, argv, totals);
01139 
01140   // File used to generate a text report from the data.
01141   Text report_fname(output_prefix);
01142   report_fname += ".report";
01143   ofstream report(report_fname.cchars());
01144 
01145   // Used to accumulate histogram of call timing across all
01146   // procedures.
01147   Histogram all_time_histo(Log2);
01148 
01149   OBufStream all_histo_plot_args;
01150 
01151   for(int proc = 0; proc < NUM_NFS_PROCS; proc++)
01152     {
01153       if(totals[proc].count > 0)
01154         {
01155           const char *proc_name = NFS_proc_name(proc);
01156 
01157           double total_ms = totals[proc].time.msecs();
01158           report << proc_name << ":" << endl
01159                  << "\tcount        = " << totals[proc].count << endl
01160                  << "\ttotal time   = " << total_ms << " ms" << endl
01161                  << "\tslowest time = "
01162                  << totals[proc].slowest_calls.back().elapsed_time.msecs()
01163                  << " ms" << endl
01164                  << "\taverage time = " << (total_ms/totals[proc].count)
01165                  << " ms" << endl;
01166 
01167           // Generate histogram data file for this procedure.
01168           Text histo_base_fname(output_prefix);
01169           histo_base_fname += ".";
01170           histo_base_fname += proc_name;
01171           histo_base_fname += "_histo";
01172           Text histo_data_fname(histo_base_fname + ".dat");
01173           ofstream histo_data(histo_data_fname.cchars());
01174 
01175           totals[proc].time_histo.PrintHisto(histo_data);
01176 
01177           // gnuplot instructions for graphing the histogram of call
01178           // time.
01179           Text histo_gnuplot_fname(histo_base_fname + ".gnuplot");
01180           ofstream histo_gnuplot(histo_gnuplot_fname.cchars());
01181 
01182           histo_gnuplot << "set data style linespoints" << endl
01183                         << "set term png color" << endl
01184                         << "set title \"" << proc_name
01185                         << " turnaround time\"" << endl
01186                         << "set output \"" << histo_base_fname
01187                         << ".png\"" << endl
01188                         << "set xlabel \"ceil(log_2(time in ms))\"" << endl
01189                         << "set ylabel \"# calls\"" << endl
01190                         << "set logscale y" << endl
01191                         << "plot \"" << histo_data_fname << "\"" << endl;
01192 
01193           all_histo_plot_args << ", \"" << histo_data_fname
01194                               << "\" title \"" << proc_name << "\"";
01195 
01196           // Accumulate histogram data across all calls.
01197           all_time_histo += totals[proc].time_histo;
01198 
01199           // Throw out all but the slowest 1%
01200           unsigned long one_percent_count =
01201             (unsigned long) ceil(((double)totals[proc].count)/100);
01202           while(totals[proc].slowest_calls.size() > one_percent_count)
01203             {
01204               totals[proc].slowest_calls.pop_front();
01205             }
01206 
01207           report << "\tslowest 1% (" << one_percent_count << " calls):"
01208                  << totals[proc].slowest_calls.front().elapsed_time.msecs()
01209                  << " ms - "
01210                  << totals[proc].slowest_calls.back().elapsed_time.msecs()
01211                  << " ms" << endl;
01212 
01213           Interval_Counts max_interval_counts;
01214           unsigned int size_hint =
01215             totals[proc].slowest_calls.front().time_points.size();
01216           Interval_Times aggregate_interval_times(size_hint, true);
01217           Delta_Time total_time;
01218 
01219           Interval_Times interval_min_times(size_hint, true);
01220           Interval_Times interval_max_times(size_hint, true);
01221 
01222           Interval_Histos interval_histos(size_hint, true);
01223 
01224 #ifdef LONGIDS_FROM_SLOWEST_CALLS
01225           LongId_Intervals longid_intervals(size_hint, true);
01226 #endif
01227 
01228           for(Call_List::iterator it = totals[proc].slowest_calls.begin();
01229               it != totals[proc].slowest_calls.end();
01230               it++)
01231             {
01232               // Determine the interval times and slowest interval for
01233               // this call.
01234               Source_Interval max_interval, longid_interval;
01235               Interval_Times *cur_interval_times =
01236                 (*it).compute_interval_times(/*OUT*/ max_interval,
01237                                              /*OUT*/ longid_interval);
01238 
01239               // Increment the count for the slowest interval.
01240               unsigned long interval_count = 0;
01241               (void) max_interval_counts.Get(max_interval, interval_count);
01242               interval_count++;
01243               (void) max_interval_counts.Put(max_interval, interval_count);
01244 
01245               // Accumulate interval times.
01246               aggregate_interval_times += *cur_interval_times;
01247 
01248               // Record the minimum and maximum time for each
01249               // interval, and record histogram data for each
01250               // interval.
01251               Interval_Times_Iter cur_times_iter(cur_interval_times);
01252               Source_Interval interval;
01253               Delta_Time interval_time;
01254               while(cur_times_iter.Next(interval, interval_time))
01255                 {
01256                   Delta_Time table_time;
01257                   bool inTbl = interval_min_times.Get(interval, table_time);
01258                   if(!inTbl)
01259                     {
01260                       // First time seeing this interval, record this
01261                       // time as both the min and max for this
01262                       // interval.
01263                       (void) interval_min_times.Put(interval, interval_time);
01264                       (void) interval_max_times.Put(interval, interval_time);
01265                     }
01266                   else
01267                     {
01268                       if(table_time > interval_time)
01269                         {
01270                           // New minimum for this interval
01271                           (void) interval_min_times.Put(interval, interval_time);
01272                         }
01273                       inTbl = interval_max_times.Get(interval, table_time);
01274                       assert(inTbl);
01275                       if(interval_time > table_time)
01276                         {
01277                           // New maximum for this interval
01278                           (void) interval_max_times.Put(interval, interval_time);
01279                         }
01280                     }
01281 
01282                   // Get or create the interval time histogram
01283                   Histogram *interval_histo = 0;
01284                   inTbl = interval_histos.Get(interval, interval_histo);
01285                   if(!inTbl)
01286                     {
01287                       interval_histo = NEW_CONSTR(Histogram, (Log2));
01288                       (void) interval_histos.Put(interval, interval_histo);
01289                     }
01290                   assert(interval_histo != 0);
01291 
01292                   // Add this value to the histogram
01293                   interval_histo->AddVal((unsigned int)
01294                                          floor(interval_time.msecs()));
01295                 }
01296 
01297 
01298 #ifdef LONGIDS_FROM_SLOWEST_CALLS
01299               // If this call has a LongId recorded...
01300               if((*it).longid != 0)
01301                 {
01302                   // Get the time in the interval that contained the recorded LongId.
01303                   Delta_Time longid_time;
01304                   bool inTbl = cur_interval_times->Get(longid_interval, longid_time);
01305 
01306                   // As long as it was there and took a noticable amount of time...
01307                   if(inTbl && (longid_time.msecs() > 1))
01308                     {
01309                       // Get the sequence of Longid/Time pairs.
01310                       LongId_Point_List *longid_times = 0;
01311                       inTbl = longid_intervals.Get(longid_interval, longid_times);
01312                       if(!inTbl)
01313                         {
01314                           longid_times = NEW(LongId_Point_List);
01315                           (void) longid_intervals.Put(longid_interval, longid_times);
01316                         }
01317 
01318                       longid_times->push_back(LongId_Point((*it).longid, longid_time));
01319                     }
01320                 }
01321 #endif
01322 
01323               // Accumulate total time.
01324               total_time += (*it).elapsed_time;
01325             }
01326 
01327           report << "\tslowest intervals in slowest 1%:" << endl;
01328 
01329           // Sort the keys of max_interval_counts by number
01330           
01331           Source_Interval *intervals =
01332             table_keys<Source_Interval, unsigned long>(max_interval_counts);
01333           sort(&intervals[0], &intervals[max_interval_counts.Size()],
01334                Interval_Count_Greater(max_interval_counts));
01335 
01336           // Print max_interval_counts sorted by number
01337 
01338           for(unsigned int i = 0; i < max_interval_counts.Size(); i++)
01339             {
01340               unsigned long count;
01341               bool inTbl = max_interval_counts.Get(intervals[i], count);
01342               assert(inTbl);
01343               report << "\t\t" << intervals[i] << ": " << count
01344                      << " (" << (count * 100/((double) one_percent_count))
01345                      << "% of slowest 1% calls)" << endl;
01346             }
01347 
01348           report << "\ttotal time spent in all intervals in slowest 1%:"
01349                  << endl;
01350 
01351           // Sort the keys of aggregate_interval_times by number
01352 
01353           intervals = table_keys<Source_Interval, Delta_Time>(aggregate_interval_times);
01354           sort(&intervals[0], &intervals[aggregate_interval_times.Size()],
01355                Interval_Time_Greater(aggregate_interval_times));
01356 
01357           // Print aggregate_interval_times sorted by time
01358 
01359           for(unsigned int i = 0; i < aggregate_interval_times.Size(); i++)
01360             {
01361               Delta_Time time;
01362               bool inTbl = aggregate_interval_times.Get(intervals[i], time);
01363               assert(inTbl);
01364 
01365               report << "\t\t" << intervals[i] << ": " << time.msecs()
01366                      << " ms (" << (time.msecs() * 100/total_time.msecs())
01367                      << "% of slowest 1% total time)" << endl;
01368             }
01369 
01370           report << "\trange of time time spent in all intervals in slowest 1%:"
01371                  << endl;
01372 
01373           // Sort the keys of interval_max_times by max time.
01374 
01375           intervals = table_keys<Source_Interval, Delta_Time>(interval_max_times);
01376           sort(&intervals[0], &intervals[interval_max_times.Size()],
01377                Interval_Time_Greater(interval_max_times));
01378 
01379           // Print interval_min_times and interval_max_times sorted by
01380           // max time
01381 
01382           for(unsigned int i = 0; i < interval_max_times.Size(); i++)
01383             {
01384               Delta_Time min_time, max_time;
01385               bool inTbl = interval_min_times.Get(intervals[i], min_time);
01386               assert(inTbl);
01387               inTbl = interval_max_times.Get(intervals[i], max_time);
01388               assert(inTbl);
01389 
01390               report << "\t\t" << intervals[i] << ": " << min_time.msecs()
01391                      << " ms - " << max_time.msecs()
01392                      << " ms" << endl;
01393             }
01394 
01395 #ifdef LONGIDS_FROM_SLOWEST_CALLS
01396           report << "\tLongIds recorded for intervals in slowest 1%:"
01397                  << endl;
01398 
01399           Source_Interval longid_interval;
01400           LongId_Point_List *longid_point_seq;
01401           LongId_Intervals_Iter lid_it(&longid_intervals);
01402           while(lid_it.Next(longid_interval, longid_point_seq))
01403             {
01404               assert(longid_point_seq != 0);
01405               report << "\t\t" << longid_interval<< ":" << endl;
01406 
01407               // Turn the sequence into an array and then sort it by
01408               // elapsed time.
01409               unsigned int lid_point_count = longid_point_seq->size();
01410               LongId_Point *lid_points = NEW_ARRAY(LongId_Point,
01411                                                    lid_point_count);
01412               unsigned int i = 0;
01413               for(LongId_Point_List::iterator lid_list_it =
01414                     longid_point_seq->begin();
01415                   lid_list_it != longid_point_seq->end();
01416                   lid_list_it++, i++)
01417                 {
01418                   lid_points[i] = *lid_list_it;
01419                 }
01420               sort(&lid_points[0], &lid_points[lid_point_count],
01421                    LongId_Point_Slower);
01422 
01423               // Print out the elapsed times and the LongIds.
01424               for(unsigned int i = 0; i < lid_point_count; i++)
01425                 {
01426                   report << "\t\t  " << lid_points[i].time.msecs() << endl;
01427                   display_LongId(*(lid_points[i].longid), "\t\t    ", report);
01428                 }
01429             }
01430 #endif
01431 
01432           report << "\tSlowest StableLock LongId intervals recorded:"
01433                  << endl;
01434           Source_Interval longid_interval;
01435           LongId_Point_List *longid_point_list; 
01436           LongId_Intervals_Iter lid_it(&(totals[proc].slow_stable_longids));
01437           while(lid_it.Next(longid_interval, longid_point_list))
01438             {
01439               assert(longid_point_list != 0);
01440               report << "\t\t" << longid_interval << ":" << endl
01441                      << "\t\t  [" << longid_point_list->size() << " total]"
01442                      << endl
01443                      << "\t\t  [longest: "
01444                      << longid_point_list->back().time.msecs() << " ms]"
01445                      << endl;
01446 
01447               unsigned int
01448                 report_limit = totals[proc].count / 100,
01449                 reported = 0;
01450               // Loop over the LongIds going back until we reach the
01451               // end of the list, or we reach one that's less than 1
01452               // ms, or we report on more than 1% of the calls in this
01453               // category.
01454               LongId_Point_List::iterator it = longid_point_list->end();
01455               while(it != longid_point_list->begin())
01456                 {
01457                   it--;
01458                   if((*it).time.msecs() <= 1)
01459                     {
01460                       report << "\t\t  [stopping listing at sub-ms time]"
01461                              << endl;
01462                       break;
01463                     }
01464                   if(reported >= report_limit)
01465                     {
01466                       report << "\t\t  [stopping at report limit of "
01467                              << report_limit << " LongIds]" << endl;
01468                       break;
01469                     }
01470                   // Report this LongId
01471                   report << "\t\t  " << (*it).time.msecs() << " ms" << endl;
01472                   display_LongId(*((*it).longid), "\t\t    ", report);
01473                   reported++;
01474                 }
01475             }
01476 
01477           // Generate datafiles and gnuplot instructions for
01478           // histograms of the source interval times in the slowest
01479           // 1%.  (We'll re-use the keys from interval_max_times to
01480           // generate these.)
01481           OBufStream interval_plot_args;
01482           bool first_interval_histo = true;
01483           unsigned int i;
01484           for(i = 0; i < interval_max_times.Size(); i++)
01485             {
01486               // Stop when we reach an interval with a maximum time of
01487               // less than 1 ms, as its histogram will be
01488               // uninteresting.
01489               Delta_Time max_time;
01490               bool inTbl = interval_max_times.Get(intervals[i], max_time);
01491               assert(inTbl);
01492               if(max_time.msecs() < 1)
01493                 {
01494                   break;
01495                 }
01496 
01497               // Get the interval histogram
01498               Histogram *interval_histo;
01499               inTbl = interval_histos.Get(intervals[i], interval_histo);
01500               assert(inTbl);
01501               assert(interval_histo != 0);
01502 
01503               // Generate data filename
01504               Text interval_histo_data_fname(output_prefix);
01505               interval_histo_data_fname += ".slow_";
01506               interval_histo_data_fname += proc_name;
01507               interval_histo_data_fname += "_";
01508               interval_histo_data_fname += intervals[i].as_fname();
01509               interval_histo_data_fname += ".dat";
01510 
01511               // Store the data
01512               ofstream interval_histo_data(interval_histo_data_fname.cchars());
01513               interval_histo->PrintHisto(interval_histo_data);
01514 
01515               // Remember this data file for the gnuplot instructions.
01516               if(!first_interval_histo)
01517                 {
01518                   interval_plot_args << ", ";
01519                 }
01520               interval_plot_args
01521                 << "\"" << interval_histo_data_fname
01522                 << "\" title \"" << intervals[i].as_text() << "\"";
01523               first_interval_histo = false;
01524             }
01525 
01526           // If we actually found more than one interesting interval
01527           if(!first_interval_histo && (i > 1))
01528             {
01529               Text interval_histo_base_fname(output_prefix);
01530               interval_histo_base_fname += ".slow_";
01531               interval_histo_base_fname += proc_name;
01532               interval_histo_base_fname += "_histo";
01533               Text interval_histo_gnuplot_fname(interval_histo_base_fname);
01534               interval_histo_gnuplot_fname += ".gnuplot";
01535               ofstream interval_histo_gnuplot(interval_histo_gnuplot_fname.cchars());
01536 
01537               interval_plot_args;
01538               interval_histo_gnuplot
01539                 << "set data style linespoints" << endl
01540                 << "set term png color" << endl
01541                 << "set title \"interval times in slow " << proc_name
01542                 << " calls\"" << endl
01543                 << "set output \"" << interval_histo_base_fname
01544                 << ".png\"" << endl
01545                 << "set xlabel \"ceil(log_2(time in ms))\"" << endl
01546                 << "set ylabel \"# calls\"" << endl
01547                 << "set logscale y" << endl
01548                 << "plot " << interval_plot_args.str() << endl;
01549             }
01550         }
01551     }
01552 
01553   // Generate histogram data for all procedures.
01554   Text histo_base_fname(output_prefix);
01555   histo_base_fname += ".all_procs_histo";
01556   Text histo_data_fname(histo_base_fname + ".dat");
01557   ofstream histo_data(histo_data_fname.cchars());
01558 
01559   all_time_histo.PrintHisto(histo_data);
01560 
01561   // gnuplot instructions for graphing the histogram of call
01562   // time.
01563   Text histo_gnuplot_fname(histo_base_fname + ".gnuplot");
01564   ofstream histo_gnuplot(histo_gnuplot_fname.cchars());
01565 
01566   histo_gnuplot << "set data style linespoints" << endl
01567                 << "set term png color" << endl
01568                 << "set title \"turnaround time for all NFS procedures\""
01569                 << endl
01570                 << "set output \"" << histo_base_fname
01571                 << ".png\"" << endl
01572                 << "set xlabel \"ceil(log_2(time in ms))\"" << endl
01573                 << "set ylabel \"# calls\"" << endl
01574                 << "set logscale y" << endl
01575                 << "plot \"" << histo_data_fname << "\" title \"all\"";
01576 
01577   all_histo_plot_args;
01578 
01579   histo_gnuplot << all_histo_plot_args.str() << endl;
01580 
01581   return 0;
01582 }

Generated on Mon May 8 00:48:46 2006 for Vesta by  doxygen 1.4.2