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

vmeasure.C

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
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 w ith Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 //
00020 // vmeasure2.C
00021 //
00022 
00023 // Measure the size of things in the repository, including:
00024 
00025 // - The source set of a build (i.e. all files and directories you
00026 // would need to replicate to perform a build)
00027 
00028 // - The internal representation of a directory (base chain, size
00029 // taken up by directory entries, etc.)
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 // This data structure holds all the statistics we collect as we visit
00057 // multiple directories and files.
00058 
00059 class RecursiveStats
00060 {
00061 private:
00062   // The files and directories we have visited.
00063   VisitedShortidTable visitedFiles, visitedDirs;
00064 
00065   // Total files encountered.  (If we encounter the same file shortid
00066   // twice, it will be counted twice in this number.)
00067   Basics::uint64 totalFiles;
00068 
00069   // Unique files encountered.  (If we encounter the same file shortid
00070   // twice, it will be counted only once in this number.)
00071   Basics::uint64 uniqueFiles;
00072 
00073   // The total bytes in all unique files encountered.
00074   Basics::uint64 uniqueFileBytes;
00075 
00076   // Number of unique directories encountered.  (If we encounter the
00077   // same directory shortid twice, it will be counted only once in
00078   // this number.)
00079   Basics::uint64 uniqueDirs;
00080 
00081   // The total size (in the repository's memory) of all directories
00082   // encountered.  (Note that this includes shadowed entires along the
00083   // base chain.)
00084   Basics::uint64 uniqueDirBytes;
00085 
00086   // The total size (in the repository's memory) of the used directory
00087   // entries in all directories encountered.  (This excludes shadowed
00088   // entires along the base chain, and is closer to the amount of
00089   // space which would be consumed if replicating to an empty
00090   // repository.)
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 // This enum is used for selecting what we're going to be actually
00158 // printing
00159 
00160 typedef enum {
00161   // Recurse into directories and follow model imports.  Each argument
00162   // is handled separately, so anything included in multiple arguments
00163   // will be represented multiple times.
00164   print_recursive_stats,
00165 
00166   // Recurse into directories and follow model imports.  Each argument
00167   // is counted as the additional space used over the previous
00168   // argumetns.  (In other words, if a repository already contained
00169   // the previous arguments, how much additional space would be needed
00170   // to replicate this argument.)
00171   print_recursive_delta_stats,
00172 
00173   // Recurse into directories and follow model imports.  The total
00174   // space used by all arguments is accumulated and printed.
00175   print_recursive_accum_stats,
00176 
00177   // Print all the raw statistics given by
00178   // VestaSource::measureDirectory
00179   print_raw_all,
00180 
00181   // Print just the base chain length
00182   print_raw_base,
00183 
00184   // Print just the directory used entry count.
00185   print_raw_used_count,
00186 
00187   // Print just the directory total entry count (including shadowed
00188   // entries along the base chain).
00189   print_raw_total_count,
00190 
00191   // Print just the in-memory size of the used directory entries.
00192   print_raw_used_size,
00193 
00194   // Print just the in-memory size of all directory entries (including
00195   // shadowed entries along the base chain).
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   // Unreached
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   // Until we find an object with a "checkout-by" attribute...
00246   while(vs && !vs->getAttrib("checkout-by"))
00247     {
00248       // ...move up to the parent
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   // If we've processed a file with this shortid before, we're done.
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   // If we've processed a file with this directory shortid before,
00331   // we're done.
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   // Canonicalize path
00431   Text cpath;
00432   cpath = ReposUI::canonicalize(path, defpkgpar);
00433 
00434   VestaSource *vs = ReposUI::filenameToVS(cpath, host, port);
00435 
00436   // Did the user ask for raw statistics?
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       // Print the statistic(s) the user asked for.
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     // Read config file
00535     //
00536     defpkgpar = VestaConfig::get_Text("UserInterface",
00537                                       "DefaultPackageParent");
00538 
00539     // Default to treating each argument independently and gathering
00540     // full recursive statistics.
00541     print_what output_selection = print_recursive_stats;
00542 
00543     // Use the repository from the config file by default
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     // Use a nondefault repository if specified
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     // If there are multiple arguments, print the path of each before
00607     // the measurement.
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 }

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