00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include <Basics.H>
00032 #include <Text.H>
00033 #include <Units.H>
00034 #include <VestaConfig.H>
00035 #include <VestaSource.H>
00036 #include <VDirSurrogate.H>
00037 #include <ReposUI.H>
00038 #include <ParseImports.H>
00039 #include <RemoteModelSpace.H>
00040 #include <ShortIdKey.H>
00041
00042 #if !defined(__sun__)
00043 extern "C" {
00044 #include <getopt.h>
00045 }
00046 #endif
00047
00048 using std::cout;
00049 using std::cerr;
00050 using std::endl;
00051 using std::ostream;
00052
00053 typedef Table<ShortIdKey,bool>::Default VisitedShortidTable;
00054 typedef Table<ShortIdKey,bool>::Iterator VisitedShortidIter;
00055
00056
00057
00058
00059 class RecursiveStats
00060 {
00061 private:
00062
00063 VisitedShortidTable visitedFiles, visitedDirs;
00064
00065
00066
00067 Basics::uint64 totalFiles;
00068
00069
00070
00071 Basics::uint64 uniqueFiles;
00072
00073
00074 Basics::uint64 uniqueFileBytes;
00075
00076
00077
00078
00079 Basics::uint64 uniqueDirs;
00080
00081
00082
00083
00084 Basics::uint64 uniqueDirBytes;
00085
00086
00087
00088
00089
00090
00091 Basics::uint64 uniqueDirUsedBytes;
00092
00093 public:
00094 RecursiveStats()
00095 : totalFiles(0), uniqueFiles(0), uniqueFileBytes(0),
00096 uniqueDirs(0), uniqueDirBytes(0), uniqueDirUsedBytes(0)
00097 {
00098 }
00099
00100 void report(ostream &out, const char *indent)
00101 {
00102 out << indent << "total files = "
00103 << Basics::FormatUnitVal(totalFiles, true) << endl
00104 << indent << "unique files = "
00105 << Basics::FormatUnitVal(uniqueFiles, true) << endl
00106 << indent << "unique file size = "
00107 << Basics::FormatUnitVal(uniqueFileBytes) << endl
00108 << indent << "total directories = "
00109 << Basics::FormatUnitVal(uniqueDirs, true) << endl
00110 << indent << "directory size = "
00111 << Basics::FormatUnitVal(uniqueDirUsedBytes) << endl;
00112 }
00113
00114 void resetCounters()
00115 {
00116 totalFiles = 0;
00117 uniqueFiles = 0;
00118 uniqueFileBytes = 0;
00119 uniqueDirs = 0;
00120 uniqueDirBytes = 0;
00121 uniqueDirUsedBytes = 0;
00122 }
00123
00124 void resetAll()
00125 {
00126 resetCounters();
00127 ShortIdKey k; bool v;
00128 VisitedShortidIter file_it(&visitedFiles);
00129 while(file_it.Next(k, v))
00130 {
00131 visitedFiles.Delete(k, v, false);
00132 }
00133 VisitedShortidIter dir_it(&visitedDirs);
00134 while(dir_it.Next(k, v))
00135 {
00136 visitedDirs.Delete(k, v, false);
00137 }
00138 }
00139
00140 void visit(const Text &path, const Text &host, const Text &port);
00141 void visit(const Text &path, VestaSource *vs);
00142
00143 void visitFile(VestaSource *parentVS, unsigned int index,
00144 ShortId fileSid);
00145
00146 void visitDir(VestaSource *dirVS);
00147
00148 void visitImports(const Text &modelname, const Text &host, const Text &port);
00149
00150 bool empty()
00151 {
00152 return ((visitedFiles.Size() == 0) &&
00153 (visitedDirs.Size() == 0));
00154 }
00155 };
00156
00157
00158
00159
00160 typedef enum {
00161
00162
00163
00164 print_recursive_stats,
00165
00166
00167
00168
00169
00170
00171 print_recursive_delta_stats,
00172
00173
00174
00175 print_recursive_accum_stats,
00176
00177
00178
00179 print_raw_all,
00180
00181
00182 print_raw_base,
00183
00184
00185 print_raw_used_count,
00186
00187
00188
00189 print_raw_total_count,
00190
00191
00192 print_raw_used_size,
00193
00194
00195
00196 print_raw_total_size
00197 } print_what;
00198
00199 Text program_name;
00200
00201 Text defpkgpar;
00202
00203 void usage()
00204 {
00205 cerr << "Usage: " << program_name << endl
00206 << " [-d] [-t] [-r] [-b] [-c] [-C] [-s] [-S]" << endl
00207 << " [-R repos]" << endl
00208 << " [path...]" << endl << endl;
00209 exit(1);
00210 }
00211
00212 VestaSource *upToVersion(VestaSource *vs)
00213 {
00214 assert(RootLongId.isAncestorOf(vs->longid));
00215 assert((vs->type == VestaSource::immutableDirectory) ||
00216 (vs->type == VestaSource::immutableFile));
00217
00218 while(1)
00219 {
00220 VestaSource *parent = vs->getParent();
00221 if(parent->type == VestaSource::appendableDirectory)
00222 {
00223 delete parent;
00224 return vs;
00225 }
00226 else if(parent->type == VestaSource::immutableDirectory)
00227 {
00228 delete vs;
00229 vs = parent;
00230 }
00231 }
00232
00233
00234 return 0;
00235 }
00236
00237 VestaSource *upToWorkingDir(VestaSource *vs)
00238 {
00239 assert(MutableRootLongId.isAncestorOf(vs->longid));
00240 assert((vs->type == VestaSource::immutableDirectory) ||
00241 (vs->type == VestaSource::mutableDirectory) ||
00242 (vs->type == VestaSource::immutableFile) ||
00243 (vs->type == VestaSource::mutableFile));
00244
00245
00246 while(vs && !vs->getAttrib("checkout-by"))
00247 {
00248
00249 VestaSource *parent = vs->getParent();
00250 delete vs;
00251 vs = parent;
00252 }
00253
00254 return vs;
00255 }
00256
00257 struct visitDirEntriesClosure
00258 {
00259 VestaSource *dirVS;
00260 RecursiveStats *stats;
00261 };
00262
00263 bool visitDirEntriesCallback(void* closure, VestaSource::typeTag type, Arc arc,
00264 unsigned int index, Bit32 pseudoInode,
00265 ShortId fileSid, bool master)
00266 {
00267 visitDirEntriesClosure *cl = (visitDirEntriesClosure *) closure;
00268
00269 switch(type)
00270 {
00271 case VestaSource::immutableFile:
00272 case VestaSource::mutableFile:
00273 cl->stats->visitFile(cl->dirVS, index, fileSid);
00274 break;
00275 case VestaSource::immutableDirectory:
00276 case VestaSource::mutableDirectory:
00277 {
00278 VestaSource *subdirVS;
00279 VestaSource::errorCode err =
00280 cl->dirVS->lookupIndex(index, subdirVS);
00281 if(err == VestaSource::ok)
00282 {
00283 cl->stats->visitDir(subdirVS);
00284 delete subdirVS;
00285 }
00286 else
00287 {
00288 cerr << program_name << ": error looking up directory in "
00289 << ReposUI::vsToFilename(cl->dirVS) << ": "
00290 << ReposUI::errorCodeText(err) << endl;
00291 }
00292 }
00293 break;
00294 }
00295
00296 return true;
00297 }
00298
00299 void RecursiveStats::visitFile(VestaSource *parentVS, unsigned int index,
00300 ShortId fileSid)
00301 {
00302 this->totalFiles++;
00303 bool visited = true;
00304
00305 if(this->visitedFiles.Get(fileSid, visited)) return;
00306
00307 this->visitedFiles.Put(fileSid, visited);
00308
00309 this->uniqueFiles++;
00310
00311 VestaSource *fileVS;
00312 VestaSource::errorCode err =
00313 parentVS->lookupIndex(index, fileVS);
00314 if(err != VestaSource::ok)
00315 {
00316 cerr << program_name << ": error looking up file in "
00317 << ReposUI::vsToFilename(parentVS) << ": "
00318 << ReposUI::errorCodeText(err) << endl;
00319 return;
00320 }
00321
00322 this->uniqueFileBytes += fileVS->size();
00323
00324 delete fileVS;
00325 }
00326
00327 void RecursiveStats::visitDir(VestaSource *dirVS)
00328 {
00329 bool visited = true;
00330
00331
00332 if(this->visitedDirs.Get(dirVS->shortId(), visited)) return;
00333
00334 this->visitedDirs.Put(dirVS->shortId(), visited);
00335
00336 this->uniqueDirs++;
00337
00338 VestaSource::directoryStats dirStats;
00339 VestaSource::errorCode err = dirVS->measureDirectory(dirStats);
00340 if(err != VestaSource::ok)
00341 {
00342 cerr << program_name << ": error measuring directory "
00343 << ReposUI::vsToFilename(dirVS) << ": "
00344 << ReposUI::errorCodeText(err) << endl;
00345 return;
00346 }
00347
00348 this->uniqueDirBytes += dirStats.totalEntrySize;
00349 this->uniqueDirUsedBytes += dirStats.usedEntrySize;
00350
00351 visitDirEntriesClosure cl;
00352 cl.dirVS = dirVS;
00353 cl.stats = this;
00354
00355 err = dirVS->list(0, visitDirEntriesCallback, (void *) &cl);
00356
00357 if(err != VestaSource::ok)
00358 {
00359 cerr << program_name << ": error listing directory "
00360 << ReposUI::vsToFilename(dirVS) << ": "
00361 << ReposUI::errorCodeText(err) << endl;
00362 return;
00363 }
00364 }
00365
00366 void RecursiveStats::visitImports(const Text &modelname,
00367 const Text &host, const Text &port)
00368 {
00369 try
00370 {
00371 VestaSource *root = VDirSurrogate::LongIdLookup(RootLongId, host, port);
00372 {
00373 ParseImports::RemoteModelSpace ms(root, 0);
00374 ImportSeq seq;
00375 ParseImports::P(modelname, seq);
00376 while(seq.size())
00377 {
00378 Import *imp = seq.remlo();
00379 this->visit(imp->path, host, port);
00380 delete imp;
00381 }
00382 }
00383 delete root;
00384 }
00385 catch(...)
00386 {
00387 }
00388 }
00389
00390 void RecursiveStats::visit(const Text &path, VestaSource *vs)
00391 {
00392 if(((vs->type == VestaSource::immutableFile) ||
00393 (vs->type == VestaSource::mutableFile)) &&
00394 (path.Sub(path.Length() - 4) == ".ves"))
00395 {
00396 this->visitImports(path, vs->host(), vs->port());
00397 }
00398
00399 if(RootLongId.isAncestorOf(vs->longid))
00400 {
00401 if((vs->type == VestaSource::immutableDirectory) ||
00402 (vs->type == VestaSource::immutableFile))
00403 {
00404 vs = upToVersion(vs);
00405 }
00406 }
00407 else if(MutableRootLongId.isAncestorOf(vs->longid))
00408 {
00409 vs = upToWorkingDir(vs);
00410 }
00411
00412 if((vs->type == VestaSource::immutableDirectory) ||
00413 (vs->type == VestaSource::mutableDirectory))
00414 {
00415 this->visitDir(vs);
00416 }
00417 }
00418
00419 void RecursiveStats::visit(const Text &path,
00420 const Text &host, const Text &port)
00421 {
00422 VestaSource *vs = ReposUI::filenameToVS(path, host, port);
00423 this->visit(path, vs);
00424 }
00425
00426 void process(const Text &path, const Text &host, const Text &port,
00427 RecursiveStats &stats,
00428 print_what output_selection, bool print_path)
00429 {
00430
00431 Text cpath;
00432 cpath = ReposUI::canonicalize(path, defpkgpar);
00433
00434 VestaSource *vs = ReposUI::filenameToVS(cpath, host, port);
00435
00436
00437 if((output_selection == print_raw_all) ||
00438 (output_selection == print_raw_base) ||
00439 (output_selection == print_raw_used_count) ||
00440 (output_selection == print_raw_total_count) ||
00441 (output_selection == print_raw_used_size) ||
00442 (output_selection == print_raw_total_size))
00443 {
00444 VestaSource::directoryStats dirStats;
00445 VestaSource::errorCode err = vs->measureDirectory(dirStats);
00446 if(err != VestaSource::ok)
00447 {
00448 cerr << program_name << ": " << cpath << ": "
00449 << ReposUI::errorCodeText(err) << endl;
00450 exit(2);
00451 }
00452
00453 if(print_path)
00454 cout << cpath << ":";
00455
00456
00457 if(print_path)
00458 cout << " ";
00459 switch(output_selection)
00460 {
00461 case print_raw_base:
00462 cout << dirStats.baseChainLength << endl;
00463 break;
00464 case print_raw_used_count:
00465 cout << dirStats.usedEntryCount << endl;
00466 break;
00467 case print_raw_total_count:
00468 cout << dirStats.totalEntryCount << endl;
00469 break;
00470 case print_raw_used_size:
00471 cout << dirStats.usedEntrySize << endl;
00472 break;
00473 case print_raw_total_size:
00474 cout << dirStats.totalEntrySize << endl;
00475 break;
00476 default:
00477 if(print_path)
00478 cout << endl << "\t";
00479 cout << "base chain length = " << dirStats.baseChainLength << endl;
00480 if(print_path)
00481 cout << "\t";
00482 cout << "used entry count = " << dirStats.usedEntryCount << endl;
00483 if(print_path)
00484 cout << "\t";
00485 cout << "total entry count = " << dirStats.totalEntryCount << endl;
00486 if(print_path)
00487 cout << "\t";
00488 cout << "used entry size = "
00489 << Basics::FormatUnitVal(dirStats.usedEntrySize, true) << endl;
00490 if(print_path)
00491 cout << "\t";
00492 cout << "total entry size = "
00493 << Basics::FormatUnitVal(dirStats.totalEntrySize, true) << endl;
00494 break;
00495 }
00496 }
00497 else
00498 {
00499 bool first = stats.empty();
00500
00501 stats.visit(cpath, vs);
00502
00503 if(output_selection == print_recursive_stats)
00504 {
00505 if(print_path)
00506 cout << cpath << endl;
00507 stats.report(cout, print_path ? "\t" : "");
00508
00509 stats.resetAll();
00510 }
00511 else if(output_selection == print_recursive_delta_stats)
00512 {
00513 if(print_path)
00514 {
00515 cout << cpath;
00516 if(!first)
00517 cout << " (delta)";
00518 cout << endl;
00519 }
00520 stats.report(cout, print_path ? "\t" : "");
00521
00522 stats.resetCounters();
00523 }
00524 }
00525 }
00526
00527 int
00528 main(int argc, char* argv[])
00529 {
00530 program_name = argv[0];
00531
00532 try {
00533
00534
00535
00536 defpkgpar = VestaConfig::get_Text("UserInterface",
00537 "DefaultPackageParent");
00538
00539
00540
00541 print_what output_selection = print_recursive_stats;
00542
00543
00544 Text repos;
00545 bool default_repos = true;
00546 Text host(VDirSurrogate::defaultHost());
00547 Text port(VDirSurrogate::defaultPort());
00548
00549 opterr = 0;
00550 for (;;) {
00551 int c = getopt(argc, argv, "?bcCsSrtdR:");
00552 if (c == EOF) break;
00553 switch (c) {
00554 case 'b':
00555 output_selection = print_raw_base;
00556 break;
00557 case 'c':
00558 output_selection = print_raw_used_count;
00559 break;
00560 case 'C':
00561 output_selection = print_raw_total_count;
00562 break;
00563 case 's':
00564 output_selection = print_raw_used_size;
00565 break;
00566 case 'S':
00567 output_selection = print_raw_total_size;
00568 break;
00569 case 'r':
00570 output_selection = print_raw_all;
00571 break;
00572 case 'd':
00573 output_selection = print_recursive_delta_stats;
00574 break;
00575 case 't':
00576 output_selection = print_recursive_accum_stats;
00577 break;
00578 case 'R':
00579 repos = optarg;
00580 default_repos = false;
00581 break;
00582 case '?':
00583 default:
00584 usage();
00585 }
00586 }
00587
00588
00589 if (!default_repos)
00590 {
00591 int colon = repos.FindCharR(':');
00592 if (colon == -1)
00593 {
00594 host = repos;
00595 repos = repos + ":" + port;
00596 }
00597 else
00598 {
00599 host = repos.Sub(0, colon);
00600 port = repos.Sub(colon+1);
00601 }
00602 }
00603
00604 RecursiveStats stats;
00605
00606
00607
00608 bool print_path = (argc > (optind + 1));
00609
00610 if((argc - optind) == 0)
00611 {
00612 process(".", host, port, stats,
00613 output_selection, print_path);
00614 }
00615 else
00616 {
00617 for(unsigned int i = optind; i < argc; i++)
00618 {
00619 process(argv[i], host, port,
00620 stats, output_selection, print_path);
00621 }
00622 }
00623 if(output_selection == print_recursive_accum_stats)
00624 {
00625 cout << "Total:" << endl;
00626 stats.report(cout, "\t");
00627 }
00628
00629 } catch (VestaConfig::failure f) {
00630 cerr << program_name << ": " << f.msg << endl;
00631 exit(2);
00632 } catch (SRPC::failure f) {
00633 cerr << program_name
00634 << ": SRPC failure; " << f.msg << " (" << f.r << ")" << endl;
00635 exit(2);
00636 } catch (ReposUI::failure f) {
00637 cerr << program_name << ": " << f.msg << endl;
00638 exit(2);
00639 }
00640
00641 return 0;
00642 }