00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <sys/time.h>
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, cnt);
00097 cnt++;
00098 (void) this->tbl.Put(key, cnt);
00099 }
00100
00101 void PrintHisto(ostream &out) const throw ()
00102 {
00103
00104 IntIntIter it(&(this->tbl));
00105 IntKey key; int val, i = 0;
00106 int max_key = 0;
00107 while (it.Next( key, val))
00108 {
00109 if(key.Val() > max_key)
00110 {
00111 max_key = key.Val();
00112 }
00113 }
00114
00115
00116
00117
00118 for (i = 0; i <= max_key; i++)
00119 {
00120 key = i;
00121 val = 0;
00122 (void) this->tbl.Get(key, val);
00123 out << i << "\t" << val << endl;
00124 }
00125 }
00126
00127 Histogram &operator+=(const Histogram &other)
00128 {
00129
00130 assert(other.histo_map == this->histo_map);
00131
00132
00133 IntIntIter it(&(other.tbl));
00134 IntKey key; int val;
00135 while (it.Next( key, val))
00136 {
00137 int my_count = 0;
00138 (void) this->tbl.Get(key, my_count);
00139 my_count += val;
00140 (void) this->tbl.Put(key, my_count);
00141 }
00142
00143 return *this;
00144 }
00145 };
00146
00147
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
00166 delta.read(input);
00167
00168
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
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
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
00295
00296
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
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
00440 proc = getc(input);
00441 if(proc == EOF)
00442 {
00443 throw FS::EndOfFile();
00444 }
00445
00446
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
00473 else if(next_byte == 2)
00474 {
00475 Time_Point point(input);
00476
00477 time_points.addhi(point);
00478 }
00479
00480 else if(next_byte == 3)
00481 {
00482 elapsed_time.read(input);
00483 break;
00484 }
00485
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
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
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
00536
00537 bool operator==(const Call_Data &other) const
00538 {
00539
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
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
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
00569 return true;
00570 }
00571
00572
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
00609
00610
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
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( Source_Interval &interval,
00654 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
00799 this->time += call.elapsed_time;
00800 this->count++;
00801
00802
00803
00804
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
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
00832 if(call.longid != 0)
00833 {
00834
00835
00836 Source_Interval lid_interval;
00837 Delta_Time lid_time;
00838 call.longid_interval( lid_interval,
00839 lid_time);
00840
00841
00842 LongId_Intervals &lid_interval_table =
00843 (VolatileRootLongId.isAncestorOf(*(call.longid)))
00844 ? this->slow_volatile_longids
00845 : this->slow_stable_longids;
00846
00847
00848 LongId_Point_List *lid_points = 0;
00849 bool inTbl = lid_interval_table.Get(lid_interval, lid_points);
00850 if(!inTbl)
00851 {
00852
00853 lid_points = NEW(LongId_Point_List);
00854 (void) lid_interval_table.Put(lid_interval, lid_points);
00855 }
00856
00857
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
00870 if(lid_points->size() > keep_count)
00871 {
00872 lid_points->pop_front();
00873 }
00874 }
00875
00876
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
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 my_point_list))
00898 {
00899
00900
00901 my_point_list->merge(*their_point_list);
00902 }
00903 else
00904 {
00905
00906
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 my_point_list))
00918 {
00919
00920
00921 my_point_list->merge(*their_point_list);
00922 }
00923 else
00924 {
00925
00926
00927 this->slow_volatile_longids.Put(longid_interval,
00928 their_point_list);
00929 }
00930 }
00931 }
00932 }
00933 };
00934
00935
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
00952 out << indent << longid << endl;
00953
00954
00955 if((longid.value.byte[0] != 0) ||
00956 ((longid.value.byte[0] == 0) && (longid.value.byte[1] == 1)))
00957 {
00958 try
00959 {
00960
00961
00962
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
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
00993
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
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
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
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
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
01079 tot[proc][arg-optind].add_call(next_call);
01080
01081 file_count++;
01082 }
01083 }
01084 catch(FS::EndOfFile)
01085 {
01086
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
01141 Text report_fname(output_prefix);
01142 report_fname += ".report";
01143 ofstream report(report_fname.cchars());
01144
01145
01146
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
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
01178
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
01197 all_time_histo += totals[proc].time_histo;
01198
01199
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
01233
01234 Source_Interval max_interval, longid_interval;
01235 Interval_Times *cur_interval_times =
01236 (*it).compute_interval_times( max_interval,
01237 longid_interval);
01238
01239
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
01246 aggregate_interval_times += *cur_interval_times;
01247
01248
01249
01250
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
01261
01262
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
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
01278 (void) interval_max_times.Put(interval, interval_time);
01279 }
01280 }
01281
01282
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
01293 interval_histo->AddVal((unsigned int)
01294 floor(interval_time.msecs()));
01295 }
01296
01297
01298 #ifdef LONGIDS_FROM_SLOWEST_CALLS
01299
01300 if((*it).longid != 0)
01301 {
01302
01303 Delta_Time longid_time;
01304 bool inTbl = cur_interval_times->Get(longid_interval, longid_time);
01305
01306
01307 if(inTbl && (longid_time.msecs() > 1))
01308 {
01309
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
01324 total_time += (*it).elapsed_time;
01325 }
01326
01327 report << "\tslowest intervals in slowest 1%:" << endl;
01328
01329
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
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
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
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
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
01380
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
01408
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
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
01451
01452
01453
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
01471 report << "\t\t " << (*it).time.msecs() << " ms" << endl;
01472 display_LongId(*((*it).longid), "\t\t ", report);
01473 reported++;
01474 }
01475 }
01476
01477
01478
01479
01480
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
01487
01488
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
01498 Histogram *interval_histo;
01499 inTbl = interval_histos.Get(intervals[i], interval_histo);
01500 assert(inTbl);
01501 assert(interval_histo != 0);
01502
01503
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
01512 ofstream interval_histo_data(interval_histo_data_fname.cchars());
01513 interval_histo->PrintHisto(interval_histo_data);
01514
01515
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
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
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
01562
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 }