Home | History | Annotate | Download | only in llvm-size
      1 //===-- llvm-size.cpp - Print the size of each object section -------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This program is a utility that works like traditional Unix "size",
     11 // that is, it prints out the size of each section, and the total size of all
     12 // sections.
     13 //
     14 //===----------------------------------------------------------------------===//
     15 
     16 #include "llvm/ADT/APInt.h"
     17 #include "llvm/Object/Archive.h"
     18 #include "llvm/Object/ObjectFile.h"
     19 #include "llvm/Support/Casting.h"
     20 #include "llvm/Support/CommandLine.h"
     21 #include "llvm/Support/FileSystem.h"
     22 #include "llvm/Support/Format.h"
     23 #include "llvm/Support/ManagedStatic.h"
     24 #include "llvm/Support/MemoryBuffer.h"
     25 #include "llvm/Support/PrettyStackTrace.h"
     26 #include "llvm/Support/Signals.h"
     27 #include "llvm/Support/raw_ostream.h"
     28 #include "llvm/Support/system_error.h"
     29 #include <algorithm>
     30 #include <string>
     31 using namespace llvm;
     32 using namespace object;
     33 
     34 enum OutputFormatTy {berkeley, sysv};
     35 static cl::opt<OutputFormatTy>
     36        OutputFormat("format",
     37          cl::desc("Specify output format"),
     38          cl::values(clEnumVal(sysv, "System V format"),
     39                     clEnumVal(berkeley, "Berkeley format"),
     40                     clEnumValEnd),
     41          cl::init(berkeley));
     42 
     43 static cl::opt<OutputFormatTy>
     44        OutputFormatShort(cl::desc("Specify output format"),
     45          cl::values(clEnumValN(sysv, "A", "System V format"),
     46                     clEnumValN(berkeley, "B", "Berkeley format"),
     47                     clEnumValEnd),
     48          cl::init(berkeley));
     49 
     50 enum RadixTy {octal = 8, decimal = 10, hexadecimal = 16};
     51 static cl::opt<unsigned int>
     52        Radix("-radix",
     53          cl::desc("Print size in radix. Only 8, 10, and 16 are valid"),
     54          cl::init(decimal));
     55 
     56 static cl::opt<RadixTy>
     57        RadixShort(cl::desc("Print size in radix:"),
     58          cl::values(clEnumValN(octal, "o", "Print size in octal"),
     59                     clEnumValN(decimal, "d", "Print size in decimal"),
     60                     clEnumValN(hexadecimal, "x", "Print size in hexadecimal"),
     61                     clEnumValEnd),
     62          cl::init(decimal));
     63 
     64 static cl::list<std::string>
     65        InputFilenames(cl::Positional, cl::desc("<input files>"),
     66                       cl::ZeroOrMore);
     67 
     68 static std::string ToolName;
     69 
     70 ///  @brief If ec is not success, print the error and return true.
     71 static bool error(error_code ec) {
     72   if (!ec) return false;
     73 
     74   outs() << ToolName << ": error reading file: " << ec.message() << ".\n";
     75   outs().flush();
     76   return true;
     77 }
     78 
     79 /// @brief Get the length of the string that represents @p num in Radix
     80 ///        including the leading 0x or 0 for hexadecimal and octal respectively.
     81 static size_t getNumLengthAsString(uint64_t num) {
     82   APInt conv(64, num);
     83   SmallString<32> result;
     84   conv.toString(result, Radix, false, true);
     85   return result.size();
     86 }
     87 
     88 /// @brief Print the size of each section in @p o.
     89 ///
     90 /// The format used is determined by @c OutputFormat and @c Radix.
     91 static void PrintObjectSectionSizes(ObjectFile *o) {
     92   uint64_t total = 0;
     93   std::string fmtbuf;
     94   raw_string_ostream fmt(fmtbuf);
     95 
     96   const char *radix_fmt = 0;
     97   switch (Radix) {
     98   case octal:
     99     radix_fmt = PRIo64;
    100     break;
    101   case decimal:
    102     radix_fmt = PRIu64;
    103     break;
    104   case hexadecimal:
    105     radix_fmt = PRIx64;
    106     break;
    107   }
    108   if (OutputFormat == sysv) {
    109     // Run two passes over all sections. The first gets the lengths needed for
    110     // formatting the output. The second actually does the output.
    111     std::size_t max_name_len = strlen("section");
    112     std::size_t max_size_len = strlen("size");
    113     std::size_t max_addr_len = strlen("addr");
    114     error_code ec;
    115     for (section_iterator i = o->begin_sections(),
    116                           e = o->end_sections(); i != e;
    117                           i.increment(ec)) {
    118       if (error(ec))
    119         return;
    120       uint64_t size = 0;
    121       if (error(i->getSize(size)))
    122         return;
    123       total += size;
    124 
    125       StringRef name;
    126       uint64_t addr = 0;
    127       if (error(i->getName(name))) return;
    128       if (error(i->getAddress(addr))) return;
    129       max_name_len = std::max(max_name_len, name.size());
    130       max_size_len = std::max(max_size_len, getNumLengthAsString(size));
    131       max_addr_len = std::max(max_addr_len, getNumLengthAsString(addr));
    132     }
    133 
    134     // Add extra padding.
    135     max_name_len += 2;
    136     max_size_len += 2;
    137     max_addr_len += 2;
    138 
    139     // Setup header format.
    140     fmt << "%-" << max_name_len << "s "
    141         << "%" << max_size_len << "s "
    142         << "%" << max_addr_len << "s\n";
    143 
    144     // Print header
    145     outs() << format(fmt.str().c_str(),
    146                      static_cast<const char*>("section"),
    147                      static_cast<const char*>("size"),
    148                      static_cast<const char*>("addr"));
    149     fmtbuf.clear();
    150 
    151     // Setup per section format.
    152     fmt << "%-" << max_name_len << "s "
    153         << "%#" << max_size_len << radix_fmt << " "
    154         << "%#" << max_addr_len << radix_fmt << "\n";
    155 
    156     // Print each section.
    157     for (section_iterator i = o->begin_sections(),
    158                           e = o->end_sections(); i != e;
    159                           i.increment(ec)) {
    160       if (error(ec))
    161         return;
    162 
    163       StringRef name;
    164       uint64_t size = 0;
    165       uint64_t addr = 0;
    166       if (error(i->getName(name))) return;
    167       if (error(i->getSize(size))) return;
    168       if (error(i->getAddress(addr))) return;
    169       std::string namestr = name;
    170 
    171       outs() << format(fmt.str().c_str(),
    172                        namestr.c_str(),
    173                        size,
    174                        addr);
    175     }
    176 
    177     // Print total.
    178     fmtbuf.clear();
    179     fmt << "%-" << max_name_len << "s "
    180         << "%#" << max_size_len << radix_fmt << "\n";
    181     outs() << format(fmt.str().c_str(),
    182                      static_cast<const char*>("Total"),
    183                      total);
    184   } else {
    185     // The Berkeley format does not display individual section sizes. It
    186     // displays the cumulative size for each section type.
    187     uint64_t total_text = 0;
    188     uint64_t total_data = 0;
    189     uint64_t total_bss = 0;
    190 
    191     // Make one pass over the section table to calculate sizes.
    192     error_code ec;
    193     for (section_iterator i = o->begin_sections(),
    194                           e = o->end_sections(); i != e;
    195                           i.increment(ec)) {
    196       if (error(ec))
    197         return;
    198 
    199       uint64_t size = 0;
    200       bool isText = false;
    201       bool isData = false;
    202       bool isBSS = false;
    203       if (error(i->getSize(size))) return;
    204       if (error(i->isText(isText))) return;
    205       if (error(i->isData(isData))) return;
    206       if (error(i->isBSS(isBSS))) return;
    207       if (isText)
    208         total_text += size;
    209       else if (isData)
    210         total_data += size;
    211       else if (isBSS)
    212         total_bss += size;
    213     }
    214 
    215     total = total_text + total_data + total_bss;
    216 
    217     // Print result.
    218     fmt << "%#7" << radix_fmt << " "
    219         << "%#7" << radix_fmt << " "
    220         << "%#7" << radix_fmt << " ";
    221     outs() << format(fmt.str().c_str(),
    222                      total_text,
    223                      total_data,
    224                      total_bss);
    225     fmtbuf.clear();
    226     fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << " "
    227         << "%7" PRIx64 " ";
    228     outs() << format(fmt.str().c_str(),
    229                      total,
    230                      total);
    231   }
    232 }
    233 
    234 /// @brief Print the section sizes for @p file. If @p file is an archive, print
    235 ///        the section sizes for each archive member.
    236 static void PrintFileSectionSizes(StringRef file) {
    237   // If file is not stdin, check that it exists.
    238   if (file != "-") {
    239     bool exists;
    240     if (sys::fs::exists(file, exists) || !exists) {
    241       errs() << ToolName << ": '" << file << "': " << "No such file\n";
    242       return;
    243     }
    244   }
    245 
    246   // Attempt to open the binary.
    247   OwningPtr<Binary> binary;
    248   if (error_code ec = createBinary(file, binary)) {
    249     errs() << ToolName << ": " << file << ": " << ec.message() << ".\n";
    250     return;
    251   }
    252 
    253   if (Archive *a = dyn_cast<Archive>(binary.get())) {
    254     // This is an archive. Iterate over each member and display its sizes.
    255     for (object::Archive::child_iterator i = a->begin_children(),
    256                                          e = a->end_children(); i != e; ++i) {
    257       OwningPtr<Binary> child;
    258       if (error_code ec = i->getAsBinary(child)) {
    259         errs() << ToolName << ": " << file << ": " << ec.message() << ".\n";
    260         continue;
    261       }
    262       if (ObjectFile *o = dyn_cast<ObjectFile>(child.get())) {
    263         if (OutputFormat == sysv)
    264           outs() << o->getFileName() << "   (ex " << a->getFileName()
    265                   << "):\n";
    266         PrintObjectSectionSizes(o);
    267         if (OutputFormat == berkeley)
    268           outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n";
    269       }
    270     }
    271   } else if (ObjectFile *o = dyn_cast<ObjectFile>(binary.get())) {
    272     if (OutputFormat == sysv)
    273       outs() << o->getFileName() << "  :\n";
    274     PrintObjectSectionSizes(o);
    275     if (OutputFormat == berkeley)
    276       outs() << o->getFileName() << "\n";
    277   } else {
    278     errs() << ToolName << ": " << file << ": " << "Unrecognized file type.\n";
    279   }
    280   // System V adds an extra newline at the end of each file.
    281   if (OutputFormat == sysv)
    282     outs() << "\n";
    283 }
    284 
    285 int main(int argc, char **argv) {
    286   // Print a stack trace if we signal out.
    287   sys::PrintStackTraceOnErrorSignal();
    288   PrettyStackTraceProgram X(argc, argv);
    289 
    290   llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.
    291   cl::ParseCommandLineOptions(argc, argv, "llvm object size dumper\n");
    292 
    293   ToolName = argv[0];
    294   if (OutputFormatShort.getNumOccurrences())
    295     OutputFormat = OutputFormatShort;
    296   if (RadixShort.getNumOccurrences())
    297     Radix = RadixShort;
    298 
    299   if (InputFilenames.size() == 0)
    300     InputFilenames.push_back("a.out");
    301 
    302   if (OutputFormat == berkeley)
    303     outs() << "   text    data     bss     "
    304            << (Radix == octal ? "oct" : "dec")
    305            << "     hex filename\n";
    306 
    307   std::for_each(InputFilenames.begin(), InputFilenames.end(),
    308                 PrintFileSectionSizes);
    309 
    310   return 0;
    311 }
    312