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 <math.h>
00025
00026
00027 #include <Basics.H>
00028 #include <SRPC.H>
00029
00030 #include <ReposStatsSRPC.H>
00031 #include <VDirSurrogate.H>
00032
00033 using std::cout;
00034 using std::cerr;
00035 using std::endl;
00036
00037
00038 Text program_name;
00039
00040
00041 Text time_format;
00042
00043
00044 Text repos_host, repos_port;
00045
00046 ReposStats::StatsResult *operator-(const ReposStats::StatsResult &lhs,
00047 const ReposStats::StatsResult &rhs)
00048 throw()
00049 {
00050 ReposStats::StatsResult *result = NEW(ReposStats::StatsResult);
00051
00052 ReposStats::StatKey k;
00053 ReposStats::Stat *lhs_v, *rhs_v;
00054 ReposStats::StatsResultIter it(&lhs);
00055 while(it.Next(k,lhs_v))
00056 {
00057 if(rhs.Get(k, rhs_v))
00058 {
00059 switch(k.kind)
00060 {
00061 case ReposStats::fdCache:
00062 {
00063 ReposStats::FdCacheStats
00064 *lhs_fd_stats = (ReposStats::FdCacheStats *) lhs_v,
00065 *rhs_fd_stats = (ReposStats::FdCacheStats *) rhs_v;
00066 result->Put(k,
00067 NEW_CONSTR(ReposStats::FdCacheStats,
00068 (*lhs_fd_stats - *rhs_fd_stats)));
00069 }
00070 break;
00071 case ReposStats::dupeTotal:
00072 {
00073 ReposStats::DupeStats
00074 *lhs_dupe_stats = (ReposStats::DupeStats *) lhs_v,
00075 *rhs_dupe_stats = (ReposStats::DupeStats *) rhs_v;
00076 result->Put(k,
00077 NEW_CONSTR(ReposStats::DupeStats,
00078 (*lhs_dupe_stats - *rhs_dupe_stats)));
00079 }
00080 break;
00081 case ReposStats::srpcTotal:
00082 {
00083 ReposStats::TimedCalls
00084 *lhs_srpc_stats = (ReposStats::TimedCalls *) lhs_v,
00085 *rhs_srpc_stats = (ReposStats::TimedCalls *) rhs_v;
00086 result->Put(k,
00087 NEW_CONSTR(ReposStats::TimedCalls,
00088 (*lhs_srpc_stats - *rhs_srpc_stats)));
00089 }
00090 break;
00091 case ReposStats::nfsTotal:
00092 {
00093 ReposStats::TimedCalls
00094 *lhs_nfs_stats = (ReposStats::TimedCalls *) lhs_v,
00095 *rhs_nfs_stats = (ReposStats::TimedCalls *) rhs_v;
00096 result->Put(k,
00097 NEW_CONSTR(ReposStats::TimedCalls,
00098 (*lhs_nfs_stats - *rhs_nfs_stats)));
00099 }
00100 break;
00101 case ReposStats::memUsage:
00102 {
00103
00104 ReposStats::MemStats
00105 *lhs_mem_stats = (ReposStats::MemStats *) lhs_v;
00106 result->Put(k, NEW_CONSTR(ReposStats::MemStats,
00107 (*lhs_mem_stats)));
00108 }
00109 break;
00110 }
00111 }
00112 }
00113
00114 return result;
00115 }
00116
00117 bool operator<(const ReposStats::StatsResult &lhs,
00118 const ReposStats::StatsResult &rhs)
00119 throw()
00120 {
00121 ReposStats::StatKey k;
00122 ReposStats::Stat *lhs_v, *rhs_v;
00123 ReposStats::StatsResultIter it(&lhs);
00124 while(it.Next(k,lhs_v))
00125 {
00126 if(rhs.Get(k, rhs_v))
00127 {
00128 switch(k.kind)
00129 {
00130 case ReposStats::fdCache:
00131 {
00132 ReposStats::FdCacheStats
00133 *lhs_fd_stats = (ReposStats::FdCacheStats *) lhs_v,
00134 *rhs_fd_stats = (ReposStats::FdCacheStats *) rhs_v;
00135 if((lhs_fd_stats->hits < rhs_fd_stats->hits) ||
00136 (lhs_fd_stats->open_misses < rhs_fd_stats->open_misses) ||
00137 (lhs_fd_stats->try_misses < rhs_fd_stats->try_misses) ||
00138 (lhs_fd_stats->evictions < rhs_fd_stats->evictions) ||
00139 (lhs_fd_stats->expirations < rhs_fd_stats->expirations))
00140 return true;
00141 }
00142 break;
00143 case ReposStats::dupeTotal:
00144 {
00145 ReposStats::DupeStats
00146 *lhs_dupe_stats = (ReposStats::DupeStats *) lhs_v,
00147 *rhs_dupe_stats = (ReposStats::DupeStats *) rhs_v;
00148 if((lhs_dupe_stats->non < rhs_dupe_stats->non) ||
00149 (lhs_dupe_stats->inProcess < rhs_dupe_stats->inProcess) ||
00150 (lhs_dupe_stats->completed < rhs_dupe_stats->completed))
00151 return true;
00152 }
00153 break;
00154 case ReposStats::srpcTotal:
00155 {
00156 ReposStats::TimedCalls
00157 *lhs_srpc_stats = (ReposStats::TimedCalls *) lhs_v,
00158 *rhs_srpc_stats = (ReposStats::TimedCalls *) rhs_v;
00159 if((lhs_srpc_stats->call_count < rhs_srpc_stats->call_count) ||
00160 (lhs_srpc_stats->elapsed_secs <
00161 rhs_srpc_stats->elapsed_secs) ||
00162 ((lhs_srpc_stats->elapsed_secs ==
00163 rhs_srpc_stats->elapsed_secs) &&
00164 (lhs_srpc_stats->elapsed_usecs <
00165 rhs_srpc_stats->elapsed_usecs)))
00166 return true;
00167 }
00168 break;
00169 case ReposStats::nfsTotal:
00170 {
00171 ReposStats::TimedCalls
00172 *lhs_nfs_stats = (ReposStats::TimedCalls *) lhs_v,
00173 *rhs_nfs_stats = (ReposStats::TimedCalls *) rhs_v;
00174 if((lhs_nfs_stats->call_count < rhs_nfs_stats->call_count) ||
00175 (lhs_nfs_stats->elapsed_secs <
00176 rhs_nfs_stats->elapsed_secs) ||
00177 ((lhs_nfs_stats->elapsed_secs ==
00178 rhs_nfs_stats->elapsed_secs) &&
00179 (lhs_nfs_stats->elapsed_usecs <
00180 rhs_nfs_stats->elapsed_usecs)))
00181 return true;
00182 }
00183 break;
00184 }
00185 }
00186 }
00187 return false;
00188 }
00189
00190 void freeStatsResult(ReposStats::StatsResult *trash) throw()
00191 {
00192
00193 ReposStats::StatKey key;
00194 ReposStats::Stat *stat;
00195 ReposStats::StatsResultIter it(trash);
00196 while(it.Next(key, stat))
00197 {
00198 delete stat;
00199 }
00200
00201 delete trash;
00202 }
00203
00204 static void Error(char *msg, char *arg = (char *)NULL) throw ()
00205 {
00206 cerr << "Error: " << msg;
00207 if (arg != (char *)NULL) cerr << ": `" << arg << "'";
00208 cerr << endl;
00209 cerr << "SYNTAX: vreposmonitor [ -update time ] "
00210 << "[ -ts time ] [ -n num ] [ -rows num ]" << endl;
00211 exit(1);
00212 }
00213
00214
00215 ReposStats::StatsRequest statsRequest;
00216
00217 static void PrintHeader() throw ()
00218 {
00219 Text header_lines[3];
00220
00221
00222 for(int i = 0; i < statsRequest.size(); i++)
00223 {
00224 switch(statsRequest.get(i))
00225 {
00226 case ReposStats::nfsTotal:
00227 header_lines[0] += " NFS ";
00228 header_lines[1] += "NUM AVG ";
00229 header_lines[2] += "---- -----";
00230 break;
00231 case ReposStats::srpcTotal:
00232 header_lines[0] += " SRPC ";
00233 header_lines[1] += "NUM AVG ";
00234 header_lines[2] += "---- -----";
00235 break;
00236 case ReposStats::dupeTotal:
00237 header_lines[0] += " DUPE ";
00238 header_lines[1] += "NEW INPR DONE";
00239 header_lines[2] += "---- ---- ----";
00240 break;
00241 case ReposStats::fdCache:
00242 header_lines[0] += " FDCACHE ";
00243 header_lines[1] += "HELD HITS OMIS TMIS EVIC EXPR";
00244 header_lines[2] += "---- ---- ---- ---- ---- ----";
00245 break;
00246 case ReposStats::memUsage:
00247 header_lines[0] += " MEMORY ";
00248 header_lines[1] += "SIZE RES ";
00249 header_lines[2] += "---- -----";
00250 break;
00251 }
00252 header_lines[0] += "|";
00253 header_lines[1] += "|";
00254 header_lines[2] += " ";
00255 }
00256
00257 cout << endl
00258 << header_lines[0] << endl
00259 << header_lines[1] << endl
00260 << header_lines[2] << endl;
00261 }
00262
00263 static void PrintVal(Basics::uint64 val) throw ()
00264 {
00265 const Basics::uint64 OneK = 1024;
00266 const Basics::uint64 OneM = OneK * OneK;
00267 const Basics::uint64 OneG = OneM * OneK;
00268 char buff[10];
00269 if (val < OneK) {
00270 sprintf(buff, "%4d", val);
00271 cout << buff;
00272 } else {
00273 char suffix = 'K';
00274 if (val >= OneG)
00275 {
00276 val /= OneM;
00277 suffix = 'G';
00278 }
00279 else if (val >= OneM)
00280 {
00281 val /= OneK;
00282 suffix = 'M';
00283 }
00284
00285 float ratio = ((float)val) / ((float)OneK);
00286 if (ratio < 9.95) {
00287 sprintf(buff, "%3.1f", ratio);
00288 } else {
00289 val = (val + (OneK / 2)) / OneK;
00290 sprintf(buff, "%3d", val);
00291 }
00292 cout << buff << suffix;
00293 }
00294 cout << ' ';
00295 }
00296
00297 static void PrintTime(Basics::uint64 secs, Basics::uint32 usecs,
00298 Basics::uint64 divisor)
00299 {
00300
00301 if(divisor == 0)
00302 {
00303 cout << "N/A ";
00304 return;
00305 }
00306
00307
00308 double time = secs;
00309 time *= USECS_PER_SEC;
00310 time += usecs;
00311 time /= divisor;
00312
00313 const char *units;
00314
00315 if(time < 100)
00316 {
00317
00318 units = "us";
00319 }
00320
00321 else if(time < 100000)
00322 {
00323
00324 time /= 1000;
00325 units = "ms";
00326 }
00327
00328 else if(time < (6*USECS_PER_SEC))
00329 {
00330
00331 time /= USECS_PER_SEC;
00332 units = "s ";
00333 }
00334 else
00335 {
00336
00337 time /= USECS_PER_SEC;
00338 time /= 60;
00339 units = "m ";
00340 }
00341
00342 char buff[12];
00343 if (time < 9.95) {
00344 sprintf(buff, "%3.1f", time);
00345 } else {
00346 unsigned int time_int = (unsigned int) rint(time);
00347 sprintf(buff, "%3d", time_int);
00348 }
00349
00350 cout << buff << units << ' ';
00351 }
00352
00353 static void filterStatsRequest(ReposStats::StatsResult *received)
00354 {
00355
00356
00357 ReposStats::StatsRequest statsReceived;
00358
00359 for(int i = 0; i < statsRequest.size(); i++)
00360 {
00361 ReposStats::StatKind kind = statsRequest.get(i);
00362 ReposStats::Stat *stats = 0;
00363
00364
00365 if(received->Get(kind, stats))
00366 {
00367 statsReceived.addhi(kind);
00368 }
00369 }
00370
00371
00372
00373 statsRequest = statsReceived;
00374 }
00375
00376
00377 Text TextStartTime(time_t when) throw()
00378 {
00379 char timebuf[256];
00380 strftime(timebuf, sizeof(timebuf), time_format.cchars(), localtime(&when));
00381
00382 return timebuf;
00383 }
00384
00385
00386 Text TextUptime(Basics::uint32 uptime) throw()
00387 {
00388 Basics::uint32 seconds, minutes, hours, days;
00389
00390 seconds = uptime % 60;
00391 uptime /= 60;
00392
00393 minutes = uptime % 60;
00394 uptime /= 60;
00395
00396 hours = uptime % 24;
00397 uptime /= 24;
00398
00399 days = uptime;
00400
00401 Text result;
00402 char buf[20];
00403 if(days > 0)
00404 {
00405 sprintf(buf, "%d", days);
00406 result += buf;
00407 result += " days";
00408 }
00409 if(hours > 0)
00410 {
00411 if(!result.Empty())
00412 result += " ";
00413 sprintf(buf, "%d", hours);
00414 result += buf;
00415 result += " hours";
00416 }
00417 if(minutes > 0)
00418 {
00419 if(!result.Empty())
00420 result += " ";
00421 sprintf(buf, "%d", minutes);
00422 result += buf;
00423 result += " minutes";
00424 }
00425 if(seconds > 0)
00426 {
00427 if(!result.Empty())
00428 result += " ";
00429 sprintf(buf, "%d", seconds);
00430 result += buf;
00431 result += " seconds";
00432 }
00433 return result;
00434 }
00435
00436 static void NewReposStats(const int ts,
00437 ReposStats::StatsResult* &oldStats,
00438 int &rowsPrinted,
00439 int rows) throw (SRPC::failure)
00440 {
00441
00442 static time_t nextStamp = -1;
00443
00444
00445 ReposStats::StatsResult *newStats = NEW(ReposStats::StatsResult);
00446 ReposStats::getStats(*newStats, statsRequest,
00447 0,
00448 repos_host, repos_port);
00449
00450
00451
00452
00453
00454 if((oldStats == 0) && (rowsPrinted == 0))
00455 {
00456 filterStatsRequest(newStats);
00457 }
00458
00459
00460 if ((oldStats == 0) ||
00461 (*newStats < *oldStats))
00462 {
00463 ReposStats::ServerInfo server_info;
00464 ReposStats::getServerInfo(server_info,
00465 0,
00466 repos_host, repos_port);
00467 cout << "Repository: " << repos_host << ":" << repos_port << endl
00468 << "Server Version: " << server_info.version << endl
00469 << "Started: " << TextStartTime(server_info.start_time)
00470 << endl
00471 << "Uptime: " << TextUptime(server_info.uptime) << endl
00472 << endl;
00473
00474 rowsPrinted = 0;
00475 if(oldStats != 0)
00476 {
00477 freeStatsResult(oldStats);
00478 oldStats = 0;
00479 }
00480 }
00481
00482
00483 if (rowsPrinted == 0 || (rows > 0 && rowsPrinted % rows == 0))
00484 PrintHeader();
00485
00486
00487 if (ts >= 0)
00488 {
00489 time_t now = time((time_t *)NULL);
00490 assert(now >= 0);
00491 if (now >= nextStamp)
00492 {
00493
00494 if (nextStamp >= 0)
00495 {
00496 char buffer[64];
00497 (void) ctime_r(&now, buffer);
00498 buffer[strlen(buffer)-1] = '\0';
00499 cout << "------------------------ " << buffer
00500 << " ------------------------" << endl;
00501 }
00502 nextStamp = now + ts;
00503 }
00504 }
00505
00506
00507
00508 ReposStats::StatsResult *printedStats =
00509 ((oldStats == 0)
00510 ? newStats
00511 : *newStats - *oldStats);
00512
00513 for(int i = 0; i < statsRequest.size(); i++)
00514 {
00515 ReposStats::StatKind kind = statsRequest.get(i);
00516 ReposStats::Stat *stats = 0;
00517 if(printedStats->Get(kind, stats))
00518 {
00519 switch(kind)
00520 {
00521 case ReposStats::nfsTotal:
00522 {
00523 ReposStats::TimedCalls *nfsStats =
00524 (ReposStats::TimedCalls *) stats;
00525 PrintVal(nfsStats->call_count);
00526 PrintTime(nfsStats->elapsed_secs,
00527 nfsStats->elapsed_usecs,
00528 nfsStats->call_count);
00529 }
00530 break;
00531 case ReposStats::srpcTotal:
00532 {
00533 ReposStats::TimedCalls *srpcStats =
00534 (ReposStats::TimedCalls *) stats;
00535 PrintVal(srpcStats->call_count);
00536 PrintTime(srpcStats->elapsed_secs,
00537 srpcStats->elapsed_usecs,
00538 srpcStats->call_count);
00539 }
00540 break;
00541 case ReposStats::dupeTotal:
00542 {
00543 ReposStats::DupeStats *dupeStats =
00544 (ReposStats::DupeStats *) stats;
00545 PrintVal(dupeStats->non);
00546 PrintVal(dupeStats->inProcess);
00547 PrintVal(dupeStats->completed);
00548 }
00549 break;
00550 case ReposStats::fdCache:
00551 {
00552 ReposStats::FdCacheStats *fdCacheStats =
00553 (ReposStats::FdCacheStats *) stats;
00554 PrintVal(fdCacheStats->n_in_cache);
00555 PrintVal(fdCacheStats->hits);
00556 PrintVal(fdCacheStats->open_misses);
00557 PrintVal(fdCacheStats->try_misses);
00558 PrintVal(fdCacheStats->evictions);
00559 PrintVal(fdCacheStats->expirations);
00560 }
00561 break;
00562 case ReposStats::memUsage:
00563 {
00564 ReposStats::MemStats *memStats =
00565 (ReposStats::MemStats *) stats;
00566 PrintVal(memStats->total);
00567 PrintVal(memStats->resident);
00568 }
00569 break;
00570 }
00571 }
00572 }
00573
00574 cout << endl;
00575 rowsPrinted++;
00576
00577
00578
00579 if(printedStats != newStats)
00580 {
00581 freeStatsResult(printedStats);
00582 }
00583
00584
00585 if(oldStats != 0)
00586 {
00587 freeStatsResult(oldStats);
00588 }
00589
00590
00591 oldStats = newStats;
00592 }
00593
00594 static void Monitor(int update, int ts, int num, int rows, bool check) throw ()
00595 {
00596
00597 ReposStats::StatsResult *oldStats = 0;
00598
00599
00600 int rowsPrinted = 0;
00601 while (num < 0 || rowsPrinted < num) {
00602 try {
00603 NewReposStats(ts, oldStats, rowsPrinted, rows);
00604 }
00605 catch (SRPC::failure) {
00606
00607 if(check) exit(1);
00608 cout << "Error contacting repository server; retrying..." << endl;
00609 if(oldStats != 0)
00610 {
00611 freeStatsResult(oldStats);
00612 oldStats = 0;
00613 }
00614 }
00615
00616
00617 if(check) exit(0);
00618
00619
00620 Basics::thread::pause(update);
00621 }
00622 }
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633 static int ParseTime(char *flag, char *tm) throw ()
00634 {
00635 char errBuff[80];
00636 int len = strlen(tm);
00637 if (len == 0) {
00638 sprintf(errBuff, "argument to `%s' is empty", flag);
00639 Error(errBuff, tm);
00640 }
00641 char lastChar = tm[len - 1];
00642 int multiplier = 1;
00643 switch (lastChar) {
00644 case 'd':
00645 multiplier *= 24;
00646
00647 case 'h':
00648 multiplier *= 60;
00649
00650 case 'm':
00651 multiplier *= 60;
00652
00653 case 's':
00654 tm[len - 1] = '\0';
00655 break;
00656 default:
00657 if (!isdigit(lastChar)) {
00658 sprintf(errBuff, "illegal unit specifier `%c' in `%s' argument",
00659 lastChar, flag);
00660 Error(errBuff, tm);
00661 }
00662 }
00663 int res;
00664 if (sscanf(tm, "%d", &res) != 1) {
00665 sprintf(errBuff, "argument to `%s' not an integer", flag);
00666 Error(errBuff, tm);
00667 }
00668 return multiplier * res;
00669 }
00670
00671 int main(int argc, char *argv[])
00672 {
00673 int update = 10;
00674 int ts = -1;
00675 int num = -1;
00676 int rows = -1;
00677 int arg = 1;
00678 bool check = false;
00679
00680 program_name = argv[0];
00681
00682 try
00683 {
00684 repos_host = VDirSurrogate::defaultHost();
00685 repos_port = VDirSurrogate::defaultPort();
00686
00687 time_format = VestaConfig::get_Text("UserInterface", "TimeFormat");
00688
00689
00690 while (arg < argc && *argv[arg] == '-') {
00691 if (strcmp(argv[arg], "-update") == 0) {
00692 if (++arg < argc) {
00693 update = ParseTime("-update", argv[arg++]);
00694 } else {
00695 Error("no argument supplied for `-update'");
00696 }
00697 } else if (strcmp(argv[arg], "-ts") == 0) {
00698 if (++arg < argc) {
00699 ts = ParseTime("-ts", argv[arg++]);
00700 } else {
00701 Error("no argument supplied for `-ts'");
00702 }
00703 } else if (strcmp(argv[arg], "-n") == 0) {
00704 if (++arg < argc) {
00705 if (sscanf(argv[arg], "%d", &num) != 1) {
00706 Error("argument to `-n' not an integer", argv[arg]);
00707 }
00708 arg++;
00709 } else {
00710 Error("no argument supplied for `-n'");
00711 }
00712 } else if (strcmp(argv[arg], "-rows") == 0) {
00713 if (++arg < argc) {
00714 if (sscanf(argv[arg], "%d", &rows) != 1) {
00715 Error("argument to `-rows' not an integer", argv[arg]);
00716 }
00717 arg++;
00718 } else {
00719 Error("no argument supplied for `-rows'");
00720 }
00721 } else if (strcmp(argv[arg], "-check") == 0) {
00722 check = true;
00723 arg++;
00724 } else if (strcmp(argv[arg], "-R") == 0) {
00725 if (++arg < argc) {
00726 Text repos = argv[arg];
00727 arg++;
00728
00729 int colon = repos.FindCharR(':');
00730 if (colon == -1) {
00731 repos_host = repos;
00732 } else {
00733 repos_host = repos.Sub(0, colon);
00734 repos_port = repos.Sub(colon+1);
00735 }
00736 } else {
00737 Error("no argument supplied for `-R'");
00738 }
00739 } else {
00740 Error("unrecognized option", argv[arg]);
00741 }
00742 }
00743 if (arg < argc) Error("too many command-line arguments");
00744
00745
00746 statsRequest.addhi(ReposStats::memUsage);
00747 statsRequest.addhi(ReposStats::nfsTotal);
00748 statsRequest.addhi(ReposStats::srpcTotal);
00749 statsRequest.addhi(ReposStats::dupeTotal);
00750 statsRequest.addhi(ReposStats::fdCache);
00751
00752 Monitor(update, ts, num, rows, check);
00753 }
00754 catch (VestaConfig::failure f)
00755 {
00756 cerr << program_name << ": " << f.msg << endl;
00757 exit(2);
00758 }
00759 catch (SRPC::failure f)
00760 {
00761 cerr << program_name
00762 << ": SRPC failure; " << f.msg << " (" << f.r << ")" << endl;
00763 exit(2);
00764 }
00765 return(0);
00766 }