Add --stats option to print runtime and memory usage statistics.
authorIan Lance Taylor <iant@google.com>
Fri, 12 Oct 2007 05:51:25 +0000 (05:51 +0000)
committerIan Lance Taylor <iant@google.com>
Fri, 12 Oct 2007 05:51:25 +0000 (05:51 +0000)
gold/config.in
gold/configure
gold/configure.ac
gold/fileread.cc
gold/fileread.h
gold/layout.cc
gold/layout.h
gold/main.cc
gold/options.cc
gold/options.h

index 46d67e5746fbfe8ace32e1712e8ccdd3981c61b2..9d1eba2e4b99a5b620cce6d7a14d5850b90e4fae 100644 (file)
@@ -13,6 +13,9 @@
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
+/* Define to 1 if you have the `mallinfo' function. */
+#undef HAVE_MALLINFO
+
 /* Whether the C++ compiler can call a template member with no arguments */
 #undef HAVE_MEMBER_TEMPLATE_SPECIFICATIONS
 
index 305fe72e699fd83af54f813212127824c51fdf47..d12704856999184bc61a92f7be535bfe850845ab 100755 (executable)
@@ -5443,6 +5443,108 @@ fi
 done
 
 
+for ac_func in mallinfo
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_cxx_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
 cat >conftest.$ac_ext <<_ACEOF
 
 class c { public: template<int i> void fn(); };
index 0e0bc7ef0d79af22029b9e36a60d6d6dc99accc0..eb4416a945610c8e311db2737ffeb46611d5003b 100644 (file)
@@ -171,6 +171,7 @@ AC_LANG_PUSH(C++)
 
 AC_CHECK_HEADERS(tr1/unordered_set tr1/unordered_map)
 AC_CHECK_HEADERS(ext/hash_map ext/hash_set)
+AC_CHECK_FUNCS(mallinfo)
 
 dnl Test whether the compiler can specify a member templates to call.
 AC_COMPILE_IFELSE([
index 88ac12611a2a2034e719aa2b99ca6f4c49baa0e5..97773d23a7d2f157fa0d9cc2070bed015aff3644 100644 (file)
@@ -48,6 +48,8 @@ File_read::View::~View()
       if (::munmap(const_cast<unsigned char*>(this->data_), this->size_) != 0)
         fprintf(stderr, _("%s: munmap failed: %s\n"),
                 program_name, strerror(errno));
+
+      File_read::current_mapped_bytes -= this->size_;
     }
 }
 
@@ -72,6 +74,11 @@ File_read::View::is_locked()
 
 // Class File_read.
 
+// The File_read static variables.
+unsigned long long File_read::total_mapped_bytes;
+unsigned long long File_read::current_mapped_bytes;
+unsigned long long File_read::maximum_mapped_bytes;
+
 // The File_read class is designed to support file descriptor caching,
 // but this is not currently implemented.
 
@@ -146,7 +153,15 @@ File_read::unlock()
   gold_assert(this->lock_count_ > 0);
   --this->lock_count_;
   if (this->lock_count_ == 0)
-    this->clear_views(false);
+    {
+      File_read::total_mapped_bytes += this->mapped_bytes_;
+      File_read::current_mapped_bytes += this->mapped_bytes_;
+      this->mapped_bytes_ = 0;
+      if (File_read::current_mapped_bytes > File_read::maximum_mapped_bytes)
+       File_read::maximum_mapped_bytes = File_read::current_mapped_bytes;
+
+      this->clear_views(false);
+    }
 }
 
 bool
@@ -289,6 +304,8 @@ File_read::find_or_make_view(off_t start, off_t size, bool cache)
           gold_exit(false);
         }
 
+      this->mapped_bytes_ += psize;
+
       const unsigned char* pbytes = static_cast<const unsigned char*>(p);
       v = new File_read::View(poff, psize, pbytes, cache, true);
     }
@@ -355,6 +372,17 @@ File_read::clear_views(bool destroying)
     }
 }
 
+// Print statistical information to stderr.  This is used for --stats.
+
+void
+File_read::print_stats()
+{
+  fprintf(stderr, _("%s: total bytes mapped for read: %llu\n"),
+         program_name, File_read::total_mapped_bytes);
+  fprintf(stderr, _("%s: maximum bytes mapped for read at one time: %llu\n"),
+         program_name, File_read::maximum_mapped_bytes);
+}
+
 // Class File_view.
 
 File_view::~File_view()
index 349a2b5217303a02305492286d9cb1c67cda4b70..cf4b3ab98aedcfa041513b7d26be52b8535b12c3 100644 (file)
@@ -46,7 +46,7 @@ class File_read
  public:
   File_read()
     : name_(), descriptor_(-1), size_(0), lock_count_(0), views_(),
-      saved_views_(), contents_(NULL)
+      saved_views_(), contents_(NULL), mapped_bytes_(0)
   { }
 
   ~File_read();
@@ -109,11 +109,27 @@ class File_read
   File_view*
   get_lasting_view(off_t start, off_t size, bool cache);
 
+  // Dump statistical information to stderr.
+  static void
+  print_stats();
+
  private:
   // This class may not be copied.
   File_read(const File_read&);
   File_read& operator=(const File_read&);
 
+  // Total bytes mapped into memory during the link.  This variable is
+  // only accessed from the main thread, when unlocking the object.
+  static unsigned long long total_mapped_bytes;
+
+  // Current number of bytes mapped into memory during the link.  This
+  // variable is only accessed from the main thread.
+  static unsigned long long current_mapped_bytes;
+
+  // High water mark of bytes mapped into memory during the link.
+  // This variable is only accessed from the main thread.
+  static unsigned long long maximum_mapped_bytes;
+
   // A view into the file.
   class View
   {
@@ -167,6 +183,7 @@ class File_read
     bool mapped_;
   };
 
+  friend class View;
   friend class File_view;
 
   // Find a view into the file.
@@ -219,6 +236,10 @@ class File_read
   Saved_views saved_views_;
   // Specified file contents.  Used only for testing purposes.
   const unsigned char* contents_;
+  // Total amount of space mapped into memory.  This is only changed
+  // while the file is locked.  When we unlock the file, we transfer
+  // the total to total_mapped_bytes, and reset this to zero.
+  size_t mapped_bytes_;
 };
 
 // A view of file data that persists even when the file is unlocked.
index f7e136590a4de2b33eb093869a74143e038354d0..5bcdbe1a81c5ed14a58c0c1c078340d0d4e46410 100644 (file)
@@ -67,7 +67,7 @@ Layout::Layout(const General_options& options)
     unattached_section_list_(), special_output_list_(),
     tls_segment_(NULL), symtab_section_(NULL),
     dynsym_section_(NULL), dynamic_section_(NULL), dynamic_data_(NULL),
-    eh_frame_section_(NULL)
+    eh_frame_section_(NULL), output_file_size_(-1)
 {
   // Make space for more than enough segments for a typical file.
   // This is just for efficiency--it's OK if we wind up needing more.
@@ -625,6 +625,8 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
   // Now we know exactly where everything goes in the output file.
   Output_data::layout_complete();
 
+  this->output_file_size_ = off;
+
   return off;
 }
 
index 90cc22bb20cfc500e44695c7a1cd4d3d160b7906..441be9448417986ae4a4db0dde05b1013d068853 100644 (file)
@@ -137,6 +137,11 @@ class Layout
   off_t
   finalize(const Input_objects*, Symbol_table*);
 
+  // Return the size of the output file.
+  off_t
+  output_file_size() const
+  { return this->output_file_size_; }
+
   // Return the TLS segment.  This will return NULL if there isn't
   // one.
   Output_segment*
@@ -368,6 +373,8 @@ class Layout
   Output_data_dynamic* dynamic_data_;
   // The exception frame section.
   Output_section* eh_frame_section_;
+  // The size of the output file.
+  off_t output_file_size_;
 };
 
 // This task handles writing out data which is not part of a section
index ccd958d872bc835139ac7df51de0e6bea0b91326..49b50b2ca4cc0a99137349c233d6b18c994ab01e 100644 (file)
 
 #include "gold.h"
 
+#ifdef HAVE_MALLINFO
+#include <malloc.h>
+#endif
+#include "libiberty.h"
+
 #include "options.h"
 #include "parameters.h"
 #include "dirsearch.h"
@@ -49,6 +54,11 @@ main(int argc, char** argv)
   // Handle the command line options.
   Command_line command_line;
   command_line.process(argc - 1, argv + 1);
+
+  long start_time = 0;
+  if (command_line.options().print_stats())
+    start_time = get_run_time();
+
   initialize_parameters(&command_line.options());
 
   // The work queue.
@@ -75,5 +85,20 @@ main(int argc, char** argv)
   // Run the main task processing loop.
   workqueue.process();
 
+  if (command_line.options().print_stats())
+    {
+      long run_time = get_run_time() - start_time;
+      fprintf(stderr, _("%s: total run time: %ld.%06ld seconds\n"),
+             program_name, run_time / 1000000, run_time % 1000000);
+#ifdef HAVE_MALLINFO
+      struct mallinfo m = mallinfo();
+      fprintf(stderr, _("%s: total space allocated by malloc: %d bytes\n"),
+             program_name, m.arena);
+#endif
+      File_read::print_stats();
+      fprintf(stderr, _("%s: output file size: %lld bytes\n"),
+             program_name, static_cast<long long>(layout.output_file_size()));
+    }
+
   gold_exit(true);
 }
index 89f4ff4418d317424fd003c218322da48b77db55..89eecf66bb8635b01e732535ed2c8808f8bda6b4 100644 (file)
@@ -348,6 +348,8 @@ options::Command_line_options::options[] =
                NULL, ONE_DASH, &General_options::set_shared),
   GENERAL_NOARG('\0', "static", N_("Do not link against shared libraries"),
                NULL, ONE_DASH, &General_options::set_static),
+  GENERAL_NOARG('\0', "stats", N_("Print resource usage statistics"),
+               NULL, TWO_DASHES, &General_options::set_stats),
   GENERAL_ARG('\0', "sysroot", N_("Set target system root directory"),
              N_("--sysroot DIR"), TWO_DASHES, &General_options::set_sysroot),
   POSDEP_NOARG('\0', "as-needed",
@@ -388,6 +390,7 @@ General_options::General_options()
     rpath_link_(),
     is_shared_(false),
     is_static_(false),
+    print_stats_(false),
     sysroot_()
 {
 }
index a11b68cdb00e8fd18fa827d70fa9a63813495475..9848639dff52fd7d88d3ae99f9b49c9a453f4057 100644 (file)
@@ -169,6 +169,11 @@ class General_options
   is_static() const
   { return this->is_static_; }
 
+  // --statis: Print resource usage statistics.
+  bool
+  print_stats() const
+  { return this->print_stats_; }
+
   // --sysroot: The system root of a cross-linker.
   const std::string&
   sysroot() const
@@ -251,6 +256,10 @@ class General_options
   set_static()
   { this->is_static_ = true; }
 
+  void
+  set_stats()
+  { this->print_stats_ = true; }
+
   void
   set_sysroot(const char* arg)
   { this->sysroot_ = arg; }
@@ -275,6 +284,7 @@ class General_options
   Dir_list rpath_link_;
   bool is_shared_;
   bool is_static_;
+  bool print_stats_;
   std::string sysroot_;
 };