page-analyze.cpp

    1  // page-analyze.cpp -- analyze a snapshot file created by page-collect
    2  //    and generate specified reports.
    3  // Copyright, C2009 by EQware Engineering, Inc.
    4  //
    5  //    page-analyze.cpp is part of PageMapTools.
    6  //
    7  //    PageMapTools is free software: you can redistribute it and/or modify
    8  //    it under the terms of version 3 of the GNU General Public License
    9  //    as published by the Free Software Foundation
   10  //
   11  //    PageMapTools is distributed in the hope that it will be useful,
   12  //    but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  //    GNU General Public License for more details.
   15  //
   16  //    You should have received a copy of the GNU General Public License
   17  //    along with PageMapTools.  If not, see http://www.gnu.org/licenses. 
   18  //
   19  
   20  #define _LARGEFILE64_SOURCE
   21  
   22  #include <stddef.h>
   23  #include <stdlib.h>
   24  #include <stdio.h>
   25  #include <assert.h>
   26  #include <errno.h>
   27  #include <string.h>
   28  
   29  #include <map>
   30  #include <string>
   31  #include <vector>
   32  
   33  #define FILENAMELEN             256
   34  #define LINELEN                 256
   35  #define PAGE_SIZE               4096
   36  #define MB                      0x100000
   37  
   38  #define IN_NAME                 "./page-collect.dat"
   39  #define OUT_NAME                "./page-analyze.dat"
   40  
   41  #define FLAGS_IN_MB             0x00000001
   42  #define FLAGS_COMPONENT_RPT     0x00000010
   43  #define FLAGS_PROCESS_RPT       0x00000020
   44  #define FLAGS_PROCVSCOMP_RPT    0x00000040
   45  #define FLAGS_CSV_FMT           0x00010000
   46  
   47  
   48  //class mem_stats_t --
   49  //
   50  class mem_stats_t
   51  {
   52    public:
   53      unsigned long long uss;
   54      double             pss;
   55      unsigned long long rss;
   56      unsigned long long vss;
   57  
   58      mem_stats_t() { uss = rss = vss = 0; pss = 0.0; }
   59  
   60  };  //class mem_stats_t
   61  
   62  
   63  //class proc_mem_t --
   64  //
   65  class proc_mem_t
   66  {
   67    public:
   68      std::string           name;
   69      mem_stats_t           stats;
   70      unsigned              comp_usage[200];
   71  
   72      proc_mem_t() 
   73          { name = ""; 
   74            memset(&stats, 0, sizeof(stats)); 
   75            memset(comp_usage, 0, sizeof(comp_usage));
   76          }
   77      proc_mem_t(std::string p, mem_stats_t &s) 
   78          { name = p; 
   79            stats = s; 
   80            memset(comp_usage, 0, sizeof(comp_usage));
   81          }
   82  };  //class proc_mem_t
   83  
   84  
   85  //ERR() --
   86  //
   87  #define ERR(format, ...) fprintf(stderr, format, ## __VA_ARGS__)
   88  
   89  
   90  //is_switch() -- Returns true if the character c is a switch character, false otherwise.
   91  //
   92  static inline bool is_switch(char c)
   93      { return c == '-';
   94      }
   95  
   96  
   97  //comp_pair_uss_gt() --
   98  //
   99  static bool comp_pair_uss_gt(const std::pair<std::string, const mem_stats_t *> &a, 
  100                               const std::pair<std::string, const mem_stats_t *> &b)
  101  { 
  102      return (a.second->uss > b.second->uss); 
  103  
  104  }   //comp_pair_uss_gt(const std::pair<std::string, const mem_stats_t *> &, 
  105      //                 const std::pair<std::string, const mem_stats_t *> &)
  106  
  107  
  108  //proc_mem_uss_gt() --
  109  //
  110  static bool proc_mem_uss_gt(const proc_mem_t *a, const proc_mem_t *b)
  111  { 
  112      return (a->stats.uss > b->stats.uss);
  113  
  114  }   //proc_mem_uss_gt(const proc_mem_t *, const proc_mem_t *)
  115  
  116  
  117  //make_short_name() --
  118  //
  119  static void make_short_name(char *buf, const char *long_name)
  120  {
  121      const char *b = strrchr(long_name, '/');
  122      if (b == NULL) 
  123      {
  124          b = long_name;
  125          if (*b == '0')
  126          {
  127              b = "ANON";
  128          }
  129          else if (*b == '[')
  130          {
  131              b++;
  132              if (strchr(b, ']') != NULL)
  133              {
  134                  *strchr(b, ']') = '0';
  135              }
  136          }
  137      }
  138      else
  139      {
  140          b++;
  141      }
  142  
  143      strcpy(buf, b);
  144      buf[5] = '0';
  145  
  146  }   //make_short_name(char *, const char *)
  147  
  148  
  149  //print_report_line() --
  150  //
  151  static void print_report_line(FILE *out, unsigned long flags,
  152                                unsigned long long uss, double pss,
  153                                unsigned long long rss, unsigned long long vss,
  154                                const char *info)
  155  {
  156      if (flags & FLAGS_IN_MB)
  157      {
  158          fprintf(out, "%10.2lf %12.2lf %10.2lf %10.2lf : %sn",
  159                  double(uss * PAGE_SIZE) / MB, 
  160                  double(pss * PAGE_SIZE) / MB, 
  161                  double(rss * PAGE_SIZE) / MB, 
  162                  double(vss * PAGE_SIZE) / MB, 
  163                  info);
  164      }
  165      else
  166      {
  167          fprintf(out, "%10llu %12.2lf %10llu %10llu : %sn",
  168                  uss, 
  169                  pss, 
  170                  rss, 
  171                  vss, 
  172                  info);
  173      }
  174  }   //print_report_line(FILE *, unsigned long, unsigned long long, double,
  175      //                  unsigned long long, unsigned long long, const char *)
  176  
  177  
  178  //build_phys_page_usage_map() -- Build an associative array consisting of a count 
  179  //  of references for each mapped physical page.
  180  //
  181  static void build_phys_page_usage_map(FILE *in, unsigned long flags, 
  182                                        std::map<unsigned long long, unsigned> &pa_map)
  183  {
  184      char line[LINELEN];
  185  
  186      assert(in != NULL);
  187  
  188      //For each physical page mapping entry in the input file...
  189      //
  190      while (fgets(line, LINELEN, in) != NULL)
  191      {
  192          if (*line == ':')
  193          {
  194              //...increment the entry in the pa_map tree (create
  195              //  the entry and set to 1, initially).
  196              //
  197              unsigned long long pa = strtoull(line + 9, NULL, 16);
  198              if (pa_map.find(pa) == pa_map.end())
  199              {
  200                  pa_map[pa] = 1;
  201              }
  202              else
  203              {
  204                  pa_map[pa]++;
  205              }
  206          }
  207      }
  208  }   ///build_phys_page_usage_map(FILE *, unsigned long,
  209      //                           std:map<unsigned long long, unsigned> &)
  210  
  211  
  212  //generate_process_report() -- Output a per-process report, giving the memory usage
  213  //  for each process.  Output consists of:
  214  //      USS (Unique Set Size) -- the number of mapped physical pages which are
  215  //           referenced ONLY by the associated process.
  216  //      PSS (Proportional Set Size) -- the number of mapped physical pages which are 
  217  //          referenced by the associated process, where shared pages are counted
  218  //          toward each process in the proportion by which they are shared.
  219  //      RSS (Resident Set Size) -- the number of mapped physical pages referenced by
  220  //          the associated process.
  221  //      VSS (Virtual Set Size) -- the number of pages, mapped or unmapped, which are
  222  //          assigned to the associated process
  223  //
  224  static void generate_process_report(FILE *in, FILE *out, unsigned long flags,
  225                                      std::map<unsigned long long, unsigned> &pa_map,
  226                                      std::map<std::string, proc_mem_t> *proc_map)
  227  {
  228      char line[LINELEN];
  229      char process[LINELEN];
  230  
  231      mem_stats_t ms;
  232      mem_stats_t msc;
  233  
  234      assert(in != NULL);
  235      assert(out != NULL);
  236  
  237      if ((flags & FLAGS_PROCESS_RPT) != 0)
  238      {
  239          fprintf(out, "n");
  240          fprintf(out, "       USS          PSS        RSS    Virtual : Processn");
  241      }
  242  
  243      //For each line in the input file...
  244      //
  245      process[0] = '0';
  246      while (fgets(line, LINELEN, in) != NULL)
  247      {
  248          //If a new process entry is seen...
  249          //
  250          if (*line == '@')
  251          {
  252              unsigned j;
  253  
  254              //Write out the previous process, including all the counters and the process string.
  255              //
  256            last_line:
  257              if (ms.vss != 0)
  258              {
  259                  if ((flags & FLAGS_PROCESS_RPT) != 0)
  260                  {
  261                      print_report_line(out, flags, ms.uss, ms.pss, ms.rss, ms.vss, process);
  262                  }
  263                  msc.uss += ms.uss;
  264                  msc.pss += ms.pss;
  265                  msc.rss += ms.rss;
  266                  msc.vss += ms.vss;
  267  
  268                  if (proc_map != NULL)
  269                  {
  270                      (*proc_map)[process].name = process + 2;
  271                      (*proc_map)[process].name.resize(26);
  272                      (*proc_map)[process].stats = ms;
  273                  }
  274              }
  275  
  276              //Reset to read in the new process.
  277              //
  278              ms.uss = 0;
  279              ms.pss = 0.0;
  280              ms.rss = 0;
  281              ms.vss = 0;
  282  
  283              j = strtoul(line + 8, NULL, 10);
  284              sprintf(process, "%6u%s", j, strchr(line + 8, ' '));
  285              process[strlen(process) - 1] = '0';
  286          }
  287  
  288          //If a maps file entry is seen...
  289          //
  290          else if (*line == '=')
  291          {
  292              ; //do nothing
  293          }
  294  
  295          //If a physical page address entry is seen...
  296          //
  297          else if (*line == ':')
  298          {
  299              //Get the numeric page address value.
  300              //
  301              unsigned long long pa = strtoull(line + 9, NULL, 16);
  302  
  303              //Increment the appropriate counters.
  304              //
  305              ms.vss++;
  306              if (pa != 0) 
  307              {
  308                  ms.rss++;
  309                  ms.pss += 1.0 / pa_map[pa];
  310                  if (pa_map[pa] == 1) 
  311                  {
  312                      ms.uss++;
  313                  }
  314              }
  315          }
  316  
  317          //Should be impossible -- invalid input file.
  318          //
  319          else
  320          {
  321              assert(false);
  322          }
  323      }
  324  
  325      //Write out the previous process, including all the counters and the process string.
  326      //
  327      if (ms.uss != 0)
  328      {
  329          goto last_line;
  330      }
  331  
  332      //Write out a total line.
  333      //
  334      if ((flags & FLAGS_PROCESS_RPT) != 0)
  335      {
  336          print_report_line(out, flags, msc.uss, msc.pss, msc.rss, msc.vss,
  337                            (flags & FLAGS_IN_MB)? " TOTAL (in MBytes)":" TOTAL (in pages)");
  338      }
  339      fprintf(out, "n");
  340  
  341  }   //generate_process_report(FILE *, FILE *, unsigned long,
  342      //                        std::map<std::string, proc_mem_t> &,
  343      //                        std::map<unsigned long long, unsigned> &)
  344  
  345  
  346  //build_component_page_map() --
  347  //
  348  static void build_component_page_map(FILE *in, unsigned long flags,
  349                                       std::map<unsigned long long, unsigned> &pa_map, 
  350                                       std::map<std::string, mem_stats_t> &comp_map)
  351  {
  352      char line[LINELEN];
  353  
  354      mem_stats_t *ms = NULL;
  355  
  356      assert(in != NULL);
  357  
  358      //For each line in the input file...
  359      //
  360      while (fgets(line, LINELEN, in) != NULL)
  361      {
  362          line[strlen(line) - 1] = '0';
  363  
  364          //If a new process entry is seen...
  365          //
  366          if (*line == '@')
  367          {
  368              ;   //do nothing
  369          }
  370  
  371          //If a maps file entry is seen...
  372          //
  373          else if (*line == '=')
  374          {
  375              //Get a pointer to the comp_map entry for this component.
  376              //  Create the entry if needed.
  377              //
  378              char *l = line + 40;                        //MAGIC NUMBER
  379              while (*l != 'n' && !isspace(*l)) l++;
  380              while (*l != 'n' &&  isspace(*l)) l++;
  381              ms = &comp_map[l];
  382          }
  383  
  384          //If a physical page address entry is seen...
  385          //
  386          else if (*line == ':')
  387          {
  388              assert(ms != NULL);
  389  
  390              //Get the numeric page address value.
  391              //
  392              unsigned long long pa = strtoull(line + 9, NULL, 16);
  393  
  394              //Increment the appropriate counters.
  395              //
  396              ms->vss++;
  397              if (pa != 0) 
  398              {
  399                  ms->rss++;
  400                  ms->pss += 1.0 / pa_map[pa];
  401                  if (pa_map[pa] == 1) 
  402                  {
  403                      ms->uss++;
  404                  }
  405              }
  406          }
  407  
  408          //Should be impossible -- invalid input file.
  409          //
  410          else
  411          {
  412              assert(false);
  413          }
  414      }
  415  }   //build_component_page_map(FILE *, unsigned long,
  416      //                         std::map<unsigned long long, unsigned> &,
  417      //                         std::map<std::string, mem_stats_t> &)
  418  
  419  
  420  //generate_component_report() --
  421  //
  422  static void generate_component_report(FILE *out, unsigned long flags,
  423                                        std::map<std::string, mem_stats_t> &comp_map)
  424  {
  425      std::map<std::string, mem_stats_t>::iterator ci;
  426  
  427      assert(out != NULL);
  428  
  429      fprintf(out, "n");
  430      fprintf(out, "       USS          PSS        RSS    Virtual : Componentn");
  431  
  432      for (ci = comp_map.begin(); ci != comp_map.end(); ci++)
  433      {
  434          mem_stats_t *ms = &(*ci).second;
  435  
  436          print_report_line(out, flags, ms->uss, ms->pss, ms->rss, ms->vss, 
  437                                          ((*ci).first).c_str());
  438      }
  439      fprintf(out, "n");
  440  
  441  }   //generate_component_report(FILE *, unsigned long, 
  442      //                          std::map<std::string, mem_stats_t> &)
  443  
  444  
  445  //build_comp_mag_xref() -- Builds two cross-reference tables:
  446  //  1. mag_comp_xref is a vector of component names/stats indexed
  447  //      by decreasing component USS.
  448  //  2. comp_mag_xref is a map of mag_comp_xref indices, themselves indexed
  449  //      by component name.
  450  //
  451  static void build_comp_mag_xref(
  452      const std::map<std::string, mem_stats_t> &comp_map, 
  453      std::vector< std::pair<std::string, const mem_stats_t *> > &mag_comp_xref,
  454      std::map<std::string, unsigned> &comp_mag_xref)
  455  {
  456      //Copy all of the comp_map elements to a list.  
  457      //  Sort the list from largest USS to smallest.
  458      //
  459      for (std::map<std::string, mem_stats_t>::const_iterator it = comp_map.begin(); 
  460            it != comp_map.end(); 
  461            it++)
  462      {
  463          mag_comp_xref.push_back(make_pair(it->first, &it->second));
  464      }
  465      sort(mag_comp_xref.begin(), mag_comp_xref.end(), comp_pair_uss_gt);
  466  
  467      //Add each mag_comp_xref string and index to comp_mag_xref.
  468      //
  469      for (unsigned i = 0; i < mag_comp_xref.size(); i++)
  470      {
  471          comp_mag_xref[mag_comp_xref[i].first] = i;
  472      }
  473  }   //build_comp_mag_xref(
  474      //  const std::map<std::string, mem_stats_t> &,
  475      //  std::vector< std::pair<std::string, const mem_stats_t *> > &,    
  476      //  std::map<std::string, unsigned> comp_mag_xref &)
  477  
  478  
  479  //build_comp_usage_table() --
  480  //
  481  static void build_comp_usage_table(
  482      FILE *in, 
  483      unsigned flags, 
  484      std::map<std::string, proc_mem_t> &proc_map,
  485      const std::map<std::string, unsigned> &comp_mag_xref)
  486  {
  487      char line[LINELEN];
  488      std::string proc = "";
  489      std::string comp = "";
  490  
  491      assert(in != NULL);
  492  
  493      //For each line in the input file...
  494      //
  495      while (fgets(line, LINELEN, in) != NULL)
  496      {
  497          line[strlen(line) - 1] = '0';
  498  
  499          //If a new process entry is seen...
  500          //
  501          if (*line == '@')
  502          {
  503              char buf[LINELEN];
  504              unsigned j = strtoul(line + 8, NULL, 10);
  505              sprintf(buf, "%6u%s", j, strchr(line + 8, ' '));
  506              proc = buf;
  507          }
  508  
  509          //If a maps file entry is seen...
  510          //
  511          else if (*line == '=')
  512          {
  513              //Get a pointer to the comp_map entry for this component.
  514              //  Create the entry if needed.
  515              //
  516              char *l = line + 40;                        //MAGIC NUMBER
  517              while (*l != 'n' && !isspace(*l)) l++;
  518              while (*l != 'n' &&  isspace(*l)) l++;
  519              comp = l;
  520          }
  521  
  522          //If a physical page address entry is seen...
  523          //
  524          else if (*line == ':')
  525          {
  526              //Skip upper characters of page address.
  527              //s
  528              if (strtoull(line + 9, NULL, 16) != 0)
  529              {
  530                  proc_map[proc].comp_usage[comp_mag_xref.find(comp)->second]++;
  531              }
  532          }
  533  
  534          //Should be impossible -- invalid input file.
  535          //
  536          else
  537          {
  538              assert(false);
  539          }
  540      }
  541  }   //build_comp_usage_table(
  542      //  FILE *,                                         
  543      //  unsigned,                                   
  544      //  std::map<std::string, proc_mem_t> &,             
  545      //  const std::map<std::string, unsigned> &)
  546  
  547  
  548  //generate_proc_vs_comp_report() --
  549  //
  550  static void generate_proc_vs_comp_report(
  551      FILE *out, 
  552      unsigned flags, 
  553      const std::vector<proc_mem_t *> &proc_list, 
  554      const std::vector< std::pair<std::string, const mem_stats_t *> > &mag_comp_xref)
  555  {
  556      if ((flags & FLAGS_CSV_FMT) != 0)
  557      {
  558          //Header line #1: Process USS/PSS/RSS labels + Component names.
  559          //
  560          fprintf(out, ""PID  - PROCESS NAME","USS","PSS","RSS"");  
  561          for (unsigned m = 0; m < mag_comp_xref.size(); m++)
  562          {
  563              if (*mag_comp_xref[m].first.c_str() == '0')
  564                  fprintf(out, ","%s"", "[anon]");
  565              else
  566                  fprintf(out, ","%s"", mag_comp_xref[m].first.c_str());
  567          }
  568          fprintf(out, "n");
  569  
  570          //Process lines: One line for each process, in form
  571          //  PID - Name : USS PSS RSS : comp-ref comp-ref ...
  572          //
  573          for (unsigned n = 0; n < proc_list.size(); n++)
  574          {
  575              const proc_mem_t *p = proc_list[n];
  576  
  577              fprintf(out, ""%s",%lld,%lf,%lld", 
  578                  p->name.c_str(), p->stats.uss, p->stats.pss, p->stats.rss);
  579  
  580              for (unsigned m = 0; m < mag_comp_xref.size(); m++)
  581              {
  582                  fprintf(out, ",%u", p->comp_usage[m]);
  583              }
  584              fprintf(out, "n");
  585          }
  586          fprintf(out, "nn");
  587      }
  588      else
  589      {
  590          //Header line #0: Component indices.
  591          //
  592          fprintf(out, "                                                  :");  
  593          for (unsigned m = 0; m < mag_comp_xref.size(); m++)
  594          {
  595              fprintf(out, " %5u", m + 1);
  596          }
  597          fprintf(out, "n");
  598      
  599          //Header line #1: Process USS/PSS/RSS labels + Component short-names.
  600          //
  601          fprintf(out, "PID  - PROCESS NAME        :   USS      PSS   RSS :");  
  602          for (unsigned m = 0; m < mag_comp_xref.size(); m++)
  603          {
  604              char buf[LINELEN];
  605              make_short_name(buf, mag_comp_xref[m].first.c_str());
  606              fprintf(out, " %5s", buf);
  607          }
  608          fprintf(out, "n");
  609      
  610          //Separator line.
  611          //
  612          fprintf(out, "---- - ------------------- - ----- -------- ----- -");
  613          for (unsigned m = 0; m < mag_comp_xref.size(); m++)
  614          {
  615              fprintf(out, " -----");
  616          }
  617          fprintf(out, "n");
  618      
  619          //Process lines: One line for each process, in form
  620          //  PID - Name : USS PSS RSS : comp-ref comp-ref ...
  621          //
  622          for (unsigned n = 0; n < proc_list.size(); n++)
  623          {
  624              const proc_mem_t *p = proc_list[n];
  625      
  626              fprintf(out, "%-26s : %5lld %8.2lf %5lld :", 
  627                  p->name.c_str(), p->stats.uss, p->stats.pss, p->stats.rss);
  628      
  629              for (unsigned m = 0; m < mag_comp_xref.size(); m++)
  630              {
  631                  fprintf(out, " %5u", p->comp_usage[m]);
  632              }
  633              fprintf(out, "n");
  634      
  635              if (((n + 1) % 5) == 0)
  636              {
  637                  //Separator line.
  638                  //
  639                  fprintf(out, "---- - ------------------- - ----- -------- ----- -");
  640                  for (unsigned m = 0; m < mag_comp_xref.size(); m++)
  641                  {
  642                      fprintf(out, " -----");
  643                  }
  644                  fprintf(out, "n");
  645              }
  646          }
  647      
  648          //Separator line.
  649          //
  650          fprintf(out, "---- - ------------------- - ----- -------- ----- -");
  651          for (unsigned m = 0; m < mag_comp_xref.size(); m++)
  652          {
  653              fprintf(out, " -----");
  654          }
  655          fprintf(out, "n");
  656      
  657          //Footer line #1: Component short-names.
  658          //
  659          fprintf(out, "                                                  :");  
  660          for (unsigned m = 0; m < mag_comp_xref.size(); m++)
  661          {
  662              char buf[LINELEN];
  663              make_short_name(buf, mag_comp_xref[m].first.c_str());
  664              fprintf(out, " %5s", buf);
  665          }
  666          fprintf(out, "nn");
  667      
  668          //Legend header line #1:
  669          //
  670          fprintf(out, "    SNAME : LONG NAMEn");
  671          for (unsigned m = 0; m < mag_comp_xref.size(); m++)
  672          {
  673              char buf[LINELEN];
  674              make_short_name(buf, mag_comp_xref[m].first.c_str());
  675              fprintf(out, "%3u %5s : %sn", m + 1, buf, mag_comp_xref[m].first.c_str());
  676          }
  677          fprintf(out, "n");
  678      }
  679  }   //generate_proc_vs_comp_report(
  680      //  FILE *,
  681      //  unsigned,
  682      //  const std::vector<proc_mem_t *> &,
  683      //  const std::vector< std::pair<std::string, const mem_stats_t *> > &)
  684  
  685  
  686  //main() --
  687  //
  688  int main(int argc, char *argv[])
  689  {
  690      int n;
  691      FILE *in;
  692      FILE *out;
  693      std::map<unsigned long long, unsigned> pa_map;
  694      std::map<std::string, mem_stats_t> comp_map;
  695      std::map<std::string, unsigned> comp_mag_xref;
  696      std::vector< std::pair<std::string, const mem_stats_t *> > mag_comp_xref;
  697      std::map<std::string, proc_mem_t> proc_map;
  698      std::vector<proc_mem_t *> proc_list;
  699  
  700      const char *in_name = IN_NAME;
  701      const char *out_name = OUT_NAME;
  702  
  703      unsigned long flags = 0;
  704      int retval = 0;
  705  
  706      //Process command-line arguments.
  707      //
  708      for (n = 1; n < argc; n++)
  709      {
  710          char *arg = argv[n];
  711  
  712          if (is_switch(*arg))
  713          {
  714              arg++;
  715  
  716              if (strncmp(arg, "c", 1) == 0)
  717              {
  718                  flags |= FLAGS_COMPONENT_RPT;
  719              }
  720  
  721              // -i in-file 
  722              //
  723              else if (strncmp(arg, "i", 1) == 0)
  724              {
  725                  if (arg[1] == '0') arg = argv[++n];
  726                  else                arg++;
  727  
  728                  in_name = arg;
  729              }
  730  
  731              // -mcsv
  732              //
  733              else if (strncmp(arg, "mcsv", 4) == 0)
  734              {
  735                  flags |= (FLAGS_PROCVSCOMP_RPT|FLAGS_CSV_FMT);
  736              }
  737  
  738              // -m
  739              //
  740              else if (strncmp(arg, "m", 1) == 0)
  741              {
  742                  flags |= FLAGS_PROCVSCOMP_RPT;
  743              }
  744  
  745              // -Mb 
  746              //
  747              else if (strncmp(arg, "Mb", 2) == 0)
  748              {
  749                  flags |= FLAGS_IN_MB;
  750              }
  751  
  752              // -o out-file 
  753              //
  754              else if (strncmp(arg, "o", 1) == 0)
  755              {
  756                  if (arg[1] == '0') arg = argv[++n];
  757                  else                arg++;
  758  
  759                  out_name = arg;
  760              }
  761  
  762              else if (strncmp(arg, "p", 1) == 0)
  763              {
  764                  flags |= FLAGS_PROCESS_RPT;
  765              }
  766  
  767              // Unknown switch.
  768              //
  769              else goto usage;
  770          }
  771          else
  772          {
  773            usage:
  774              fprintf(stderr, "n"
  775                              "page-analyze -- analyze a snapshot file created by page-collectn"
  776                              "  and generate specified reports.n"
  777                              "n"
  778                              "usage: page-analyze {switches}n"
  779                              "switches:n"
  780                              " -c           -- Generate component report.n"
  781                              " -i in-file   -- Input file name (def=%s)n"
  782                              " -m           -- Generate process/component matrix.n"
  783                              " -mcsv        -- Generate matrix in CSV format.n"
  784                              " -Mb          -- Report in Mbytes (def=pages)n"
  785                              " -o out-file  -- Output file name (def=%s)n"
  786                              " -p           -- Generate process report.n"
  787                              "n",
  788                              IN_NAME, 
  789                              OUT_NAME);
  790              goto done;
  791          }
  792      }
  793  
  794      // Open output file for writing.
  795      //
  796      out = fopen(out_name, "w");
  797      if (out == NULL)
  798      {
  799          ERR("Unable to open file "%s" for writing (errno=%d). (1)n", out_name, errno);
  800          retval = -1;
  801          goto done;
  802      }
  803  
  804      // Open input file for reading.
  805      //
  806      in = fopen(in_name, "r");
  807      if (in == NULL)
  808      {
  809          ERR("Unable to open file "%s" for reading (errno=%d). (2)n", in_name, errno);
  810          retval = -1;
  811          goto done;
  812      }
  813  
  814      //Build a table of physical page addresses -> reference-counts for that page.
  815      //
  816      build_phys_page_usage_map(in, flags, pa_map);
  817  
  818      if ((flags & (FLAGS_PROCESS_RPT|FLAGS_PROCVSCOMP_RPT)) != 0)
  819      {
  820          //Rewind input file.
  821          //
  822          n = fseek(in, 0, SEEK_SET);
  823          if (n != 0)
  824          {
  825              ERR("Unable to rewind file "%s" for reading (errno=%d). (3)n", in_name, errno);
  826              retval = -1;
  827              goto done;
  828          }
  829  
  830          //Generate a report of memory usage for each process.
  831          //
  832          generate_process_report(in, out, flags, pa_map, &proc_map);
  833      }
  834  
  835      if ((flags & (FLAGS_COMPONENT_RPT|FLAGS_PROCVSCOMP_RPT)) != 0)
  836      {
  837          // Rewind input file.
  838          //
  839          n = fseek(in, 0, SEEK_SET);
  840          if (n != 0)
  841          {
  842              ERR("Unable to rewind file "%s" for reading (errno=%d). (4)n", in_name, errno);
  843              retval = -1;
  844              goto done;
  845          }
  846  
  847          //Build a table of component names -> memory status.
  848          //
  849          build_component_page_map(in, flags, pa_map, comp_map);
  850  
  851          if ((flags & FLAGS_COMPONENT_RPT) != 0)
  852          {
  853              //Generate a report of memory usage for each component.
  854              //
  855              generate_component_report(out, flags, comp_map);
  856          }
  857  
  858          if ((flags & FLAGS_PROCVSCOMP_RPT) != 0)
  859          {
  860              //Build component-magnitude-idx <-> component-name look-up tables.
  861              //
  862              build_comp_mag_xref(comp_map, mag_comp_xref, comp_mag_xref);
  863  
  864              // Rewind input file.
  865              //
  866              n = fseek(in, 0, SEEK_SET);
  867              if (n != 0)
  868              {
  869                  ERR("Unable to rewind file "%s" for reading (errno=%d). (4)n", in_name, errno);
  870                  retval = -1;
  871                  goto done;
  872              }
  873  
  874              //Build the component usage table for each process.
  875              //
  876              build_comp_usage_table(in, flags, proc_map, comp_mag_xref);
  877  
  878              //Build a list of pointers to process structures, sort it by decreasing USS...
  879              //
  880              for (std::map<std::string, proc_mem_t>::iterator it = proc_map.begin(); it != proc_map.end(); it++)
  881              {
  882                  proc_list.push_back(&it->second);
  883              }
  884              sort(proc_list.begin(), proc_list.end(), proc_mem_uss_gt);
  885  
  886              //..and use it to generate the process vs component memory usage matrix.
  887              //
  888              generate_proc_vs_comp_report(out, flags, proc_list, mag_comp_xref);
  889          }
  890      }
  891  
  892    done:
  893      if (out != NULL)
  894      {
  895          fclose(out);
  896      }
  897      if (in != NULL)
  898      {
  899          fclose(in);
  900      }
  901      return retval;
  902  
  903  }   //main(int, char *[])
  904  
  905  
  906  
  907