diff options
Diffstat (limited to 'ACE/apps/soreduce')
-rw-r--r-- | ACE/apps/soreduce/Library.cpp | 347 | ||||
-rw-r--r-- | ACE/apps/soreduce/Library.h | 128 | ||||
-rw-r--r-- | ACE/apps/soreduce/Makefile.am | 50 | ||||
-rw-r--r-- | ACE/apps/soreduce/Obj_Module.cpp | 200 | ||||
-rw-r--r-- | ACE/apps/soreduce/Obj_Module.h | 70 | ||||
-rw-r--r-- | ACE/apps/soreduce/README | 163 | ||||
-rw-r--r-- | ACE/apps/soreduce/SO_Group.cpp | 166 | ||||
-rw-r--r-- | ACE/apps/soreduce/SO_Group.h | 48 | ||||
-rw-r--r-- | ACE/apps/soreduce/Sig_List.cpp | 184 | ||||
-rw-r--r-- | ACE/apps/soreduce/Sig_List.h | 49 | ||||
-rw-r--r-- | ACE/apps/soreduce/Signature.cpp | 45 | ||||
-rw-r--r-- | ACE/apps/soreduce/Signature.h | 50 | ||||
-rw-r--r-- | ACE/apps/soreduce/soreduce.cpp | 51 | ||||
-rw-r--r-- | ACE/apps/soreduce/soreduce.mpc | 15 |
14 files changed, 1566 insertions, 0 deletions
diff --git a/ACE/apps/soreduce/Library.cpp b/ACE/apps/soreduce/Library.cpp new file mode 100644 index 00000000000..dcf0ea81301 --- /dev/null +++ b/ACE/apps/soreduce/Library.cpp @@ -0,0 +1,347 @@ +// -*- C++ -*- +// $Id$ + +// File: Library.cpp + +// Author: Phil Mesnier + +// This file contains the implementation of the classes responsible for +// generating specialized mpc files for individual libraries, as well as +// outputting usage metrics for the various object modules contained in the +// library. + +#include "ace/OS_NS_dirent.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_sys_stat.h" +#include "ace/OS_NS_ctype.h" +#include "ace/Log_Msg.h" + +#include "Library.h" + +ACE_RCSID(src, Library, "$Id$") + +MPC_Generator::MPC_Generator (const ACE_CString& libname) + : mpcfile_(), + libname_(libname), + mpcfilename_() +{ + mpcfilename_ = libname_ + "_subset.mpc"; +} + +MPC_Generator::~MPC_Generator () +{ +} + +void +MPC_Generator::write_file (const ACE_CString& file) +{ + mpcfile_ << " " << file << ".cpp" << endl; +} + +void +MPC_Generator::write_prolog (const ACE_CString& path) +{ + ACE_CString fname (path + "/" + mpcfilename_); + ACE_DEBUG ((LM_DEBUG, "writing file %s\n",fname.c_str())); + mpcfile_.open(fname.c_str()); + if (!mpcfile_) + ACE_DEBUG ((LM_DEBUG,"mpc file open failed\n")); + + mpcfile_ + << "// Generated mpc file for producing a subset of the " + << libname_ << " library " << endl << endl + << "project(" << libname_ << "_subset)"; + + this->write_baseprojects (); + + mpcfile_ + << " {" << endl + << " sharedname = " << libname_ << "_subset" << endl + << " pch_header = " << endl + << " pch_source = " << endl; + + this->write_projectinfo (); + + mpcfile_ << endl + << " Source_Files {" << endl; +} + +void +MPC_Generator::write_epilog () +{ + mpcfile_ << " }" << endl + << "}" << endl; + mpcfile_.close(); +} + +void +MPC_Generator::write_baseprojects() +{ + mpcfile_ << ": acedefaults, aceversion, core"; +} + +void +MPC_Generator::write_projectinfo() +{ + mpcfile_ << " libout = $(ACE_ROOT)/lib" << endl + << " dynamicflags = ACE_BUILD_DLL ACE_OS_BUILD_DLL" << endl; +} + +//----------------------------------------------------------------------------- +MPC_ACE_Dep_Lib::MPC_ACE_Dep_Lib (const ACE_CString& libname) + : MPC_Generator(libname) +{} + +void +MPC_ACE_Dep_Lib::write_baseprojects() +{ + mpcfile_ << ": acedefaults, aceversion"; +} + +void +MPC_ACE_Dep_Lib::write_projectinfo() +{ + mpcfile_ << " libout = $(ACE_ROOT)/lib" << endl + << " libs += ACE_subset" << endl + << " after += ACE_subset" << endl; +} + +//----------------------------------------------------------------------------- +MPC_TAO_Lib::MPC_TAO_Lib (const ACE_CString& libname) + : MPC_ACE_Dep_Lib(libname) +{} + +void +MPC_TAO_Lib::write_baseprojects() +{ + MPC_ACE_Dep_Lib::write_baseprojects (); + mpcfile_ << ", taoversion, core, tao_output, taodefaults"; +} + +void +MPC_TAO_Lib::write_projectinfo() +{ + MPC_ACE_Dep_Lib::write_projectinfo(); + mpcfile_ << " dynamicflags = TAO_BUILD_DLL" << endl; +} + +//----------------------------------------------------------------------------- +MPC_TAO_Dep_Lib::MPC_TAO_Dep_Lib (const ACE_CString& libname) + : MPC_TAO_Lib(libname) +{} + +void +MPC_TAO_Dep_Lib::write_baseprojects() +{ + MPC_TAO_Lib::write_baseprojects (); + mpcfile_ << ", taoidldefaults"; +} + +void +MPC_TAO_Dep_Lib::write_projectinfo() +{ + // Try our best to generate the dynamicflags + ACE_CString dflags; + for(size_t i = 0; i < this->libname_.length (); ++i) { + dflags += static_cast<char>(ACE_OS::ace_toupper (this->libname_[i])); + } + dflags += "_BUILD_DLL"; + + MPC_ACE_Dep_Lib::write_projectinfo(); + mpcfile_ << " dynamicflags = " << dflags.c_str () << endl + << " libs += TAO_subset" << endl + << " after += TAO_subset" << endl + << " includes += $(TAO_ROOT)/orbsvcs" << endl + << " idlflags += -I$(TAO_ROOT)/orbsvcs" << endl; +} + +//----------------------------------------------------------------------------- + +Library::Library (const char *name) + : name_(name), + path_(), + num_modules_(0), + num_exports_(0), + num_extrefs_(0), + modules_(0), + exported_(0), + mpcfile_(0) +{ + if (name_ == "ACE") + mpcfile_ = new MPC_Generator(name_); + else if (name_.find ("ACE_") == 0) + mpcfile_ = new MPC_ACE_Dep_Lib (name_); + else if (name_ == "TAO") + mpcfile_ = new MPC_TAO_Lib (name_); + else + mpcfile_ = new MPC_TAO_Dep_Lib (name_); +} + +Library::~Library () +{ + delete mpcfile_; + int i; + for (i = 0; i < num_modules_; delete modules_[i++]); + delete [] modules_; +} + +void +Library::set_path (const char *p) +{ + char abspath[1000]; + memset (abspath,0,1000); + ssize_t abspathlen = ACE_OS::readlink(p,abspath,999); + ACE_CString path (p); + if (abspathlen > 0) { + abspath[abspathlen] = 0; + path = abspath; + } + + ACE_CString::size_type pathsep = path.rfind('/'); + + if (pathsep == ACE_CString::npos) { + path_ = "."; + } else { + path_ = path.substr(0,pathsep); + } +} + +const ACE_CString & +Library::name () const +{ + return name_; +} + +int +Library::has_modules () const +{ + return num_modules_ > 0; +} + +extern "C" { + +static int +selector (const dirent *d) +{ + return ACE_OS::strstr (d->d_name, ACE_TEXT (".o")) != 0; +} + +static int +comparator (const dirent **d1, const dirent **d2) +{ + return ACE_OS::strcmp ((*d1)->d_name, (*d2)->d_name); +} + +} /* extern "C" */ + +void +Library::load_modules () +{ + ACE_CString subdir = path_ + "/.shobj"; + + struct dirent **dent; + num_modules_ = ACE_OS::scandir(ACE_TEXT_CHAR_TO_TCHAR (subdir.c_str()), + &dent,selector,comparator); + + if (num_modules_ > 0) { + modules_ = new Obj_Module * [num_modules_]; + for (int i = 0; i < num_modules_; i++) { + ACE_CString ent_name (ACE_TEXT_ALWAYS_CHAR (dent[i]->d_name)); + modules_[i] = new Obj_Module(ent_name); + modules_[i]->add_source (ACE_CString(subdir + "/" + ent_name).c_str()); + ACE_OS::free(dent[i]); + }; + } + + if (num_modules_ > -1) + ACE_OS::free(dent); +} + +void +Library::resolve (Sig_List &undefs) +{ + if (num_modules_ < 1) + return; + + for (const Signature *uname = undefs.first(); + undefs.hasmore(); + uname = undefs.next()) { + if (exported_.index_of(uname) != -1) { + undefs.remove_current(); + } + else + for (int i = 0; i < num_modules_; i++) + if (modules_[i]->extref() == 0 && + modules_[i]->exports().index_of(uname) != -1) + { + undefs.remove_current(); + exported_.add (modules_[i]->exports()); + for (const Signature *needed = modules_[i]->imports().first(); + modules_[i]->imports().hasmore(); + needed = modules_[i]->imports().next()) + if (exported_.index_of(needed) == -1) + undefs.add (needed->name()); + modules_[i]->add_extref(); + num_extrefs_++; + break; + } + } +} + +void +Library::write_export_list (int show_ref_counts) +{ + if (num_modules_ < 1) + return; + + ACE_CString excludedfilename = path_ + "/excluded_modules"; + ACE_CString rcpath = path_ + "/usage_metrics"; + + ofstream exclusions (excludedfilename.c_str()); + if (!exclusions) { + ACE_ERROR ((LM_ERROR, "%p\n", "open exclusions list")); + } + + if (show_ref_counts) { + ACE_DEBUG ((LM_DEBUG, "Making directory %s\n",rcpath.c_str())); + if (ACE_OS::mkdir(ACE_TEXT_CHAR_TO_TCHAR (rcpath.c_str())) == -1 && + errno != EEXIST) + ACE_ERROR ((LM_ERROR, "%p\n", "mkdir")); + } + + ACE_DEBUG ((LM_DEBUG,"%s: %d out of %d modules required\n", + name_.c_str(), num_extrefs_, num_modules_)); + + mpcfile_->write_prolog(path_); + + for (int i = 0; i < num_modules_ ; i++) + if (modules_[i]->extref()) { + if (show_ref_counts) { + ACE_CString fname = rcpath + "/" + modules_[i]->name(); + ofstream countfile (fname.c_str()); + countfile << "Exported symbols:" << endl; + for (const Signature *sig = modules_[i]->exports().first(); + modules_[i]->exports().hasmore(); + sig = modules_[i]->exports().next()) + { + countfile.width(5); + countfile << sig->used_count() << " " << sig->name() << endl; + } + countfile << "\nImported symbols:" << endl; + for (const Signature *n_sig = modules_[i]->imports().first(); + modules_[i]->imports().hasmore(); + n_sig = modules_[i]->imports().next()) + countfile << n_sig->name() << endl; + } + mpcfile_->write_file(modules_[i]->name().substring(0,modules_[i]->name().length()-2)); + } else { + // const char * modname = modules_[i]->name().c_str(); + exclusions + << modules_[i]->name().substring(0,modules_[i]->name().length()-2) + << endl; + } + + mpcfile_->write_epilog(); +} diff --git a/ACE/apps/soreduce/Library.h b/ACE/apps/soreduce/Library.h new file mode 100644 index 00000000000..af978c61f74 --- /dev/null +++ b/ACE/apps/soreduce/Library.h @@ -0,0 +1,128 @@ +// -*- C++ -*- +#ifndef _LIBRARY_H_ +#define _LIBRARY_H_ + +// -*- C++ -*- +// $Id$ + +// File: Library.h + +// Author: Phil Mesnier + +// A Library is a collection of Obj_Modules that define a single shared +// library. It is used to manipulate the list of unresolved references by +// removing those that are resolved and adding those brought in by new modules +// that are required to resolve references. The Library is responsible +// for outputting a specialized mpc file to build the reduce footprint library. + +#include "Obj_Module.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +// The MPC generator class serves as the base class used to output the +// custom mpc files used to build the subsetted libraries. +// The base class will make libACE_subset.so + +class MPC_Generator +{ +public: + MPC_Generator (const ACE_CString& libname); + virtual ~MPC_Generator(); + + void write_prolog (const ACE_CString& ); + void write_file (const ACE_CString& ); + void write_epilog (); + +protected: + virtual void write_baseprojects(); + virtual void write_projectinfo(); + + ofstream mpcfile_; + ACE_CString libname_; + ACE_CString mpcfilename_; +}; + +// Generate mpc files for libraries dependant on ACE, that are not TAO. +class MPC_ACE_Dep_Lib : public MPC_Generator +{ +public: + MPC_ACE_Dep_Lib (const ACE_CString& libname); + +protected: + virtual void write_baseprojects(); + virtual void write_projectinfo(); +}; + +// Generates mpc files for libTAO_subset.so +class MPC_TAO_Lib : public MPC_ACE_Dep_Lib +{ +public: + MPC_TAO_Lib (const ACE_CString& libname); + +protected: + virtual void write_baseprojects(); + virtual void write_projectinfo(); +}; + +// Generates makefiles for libs dependant on TAO. This has a problem when +// building libraries in the orbsvcs tree. +class MPC_TAO_Dep_Lib : public MPC_TAO_Lib +{ +public: + MPC_TAO_Dep_Lib (const ACE_CString& ); + +protected: + virtual void write_baseprojects(); + virtual void write_projectinfo(); +}; + +//---------------------------------------------------------------------------- + +class Library +{ +public: + + Library (const ACE_TCHAR *name = 0 ); + /// Constructor is responsible for loading all of the modules related to the + /// library + ~Library (); + + // Resolve interates over the supplied list of undefined signatures to locate + // modules that contain definitions. Any symbol defined in a module marked as + // exported is simply removed from the undef list. Any symbol defined in a + // module not yet exported removed from the undef list, the module is marked + // as exported, and its unresolved symbols are added to the undef list. + void resolve (Sig_List &undefs); + + // Outputs a list of files suitable for inclusion in an mpc file to produce + // a subsetted library. If the argument is non-zero, reference countes for + // each module are also listed. + void write_export_list ( int ); + + // set the path to find the .so files + void set_path (const ACE_TCHAR *p ); + + // Load the actual .so files from the path. + void load_modules(); + + // returns the library name + const ACE_CString &name () const; + + // returns non-zero if the module count is > 0. + int has_modules () const; + +private: + ACE_CString name_; + ACE_CString path_; + + int num_modules_; + int num_exports_; + int num_extrefs_; + + Obj_Module **modules_; + Sig_List exported_; + MPC_Generator *mpcfile_; +}; + +#endif /* _LIBRARY_H_ */ diff --git a/ACE/apps/soreduce/Makefile.am b/ACE/apps/soreduce/Makefile.am new file mode 100644 index 00000000000..4f50d294e0d --- /dev/null +++ b/ACE/apps/soreduce/Makefile.am @@ -0,0 +1,50 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + + +## Makefile.soreduce.am + +if !BUILD_ACE_FOR_TAO +if !BUILD_USES_WCHAR +noinst_PROGRAMS = soreduce + +soreduce_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +soreduce_SOURCES = \ + Library.cpp \ + Obj_Module.cpp \ + SO_Group.cpp \ + Sig_List.cpp \ + Signature.cpp \ + soreduce.cpp \ + Library.h \ + Obj_Module.h \ + SO_Group.h \ + Sig_List.h \ + Signature.h + +soreduce_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_USES_WCHAR +endif !BUILD_ACE_FOR_TAO + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/apps/soreduce/Obj_Module.cpp b/ACE/apps/soreduce/Obj_Module.cpp new file mode 100644 index 00000000000..de508c32eff --- /dev/null +++ b/ACE/apps/soreduce/Obj_Module.cpp @@ -0,0 +1,200 @@ +// -*- C++ -*- +// $Id$ + +// File: Obj_Module.cpp + +// Author: Phil Mesnier + +// This file contains the implementation of the classes responsible for +// managing the contents of a single object module (.o file). + +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Process.h" +#include "ace/Pipe.h" +#include "ace/Message_Block.h" +#include "ace/Log_Msg.h" + +#include "Obj_Module.h" + + +ACE_RCSID(src, Obj_Module, "$Id$") + + +//---------------------------------------------------------------------------- + +Obj_Module::Obj_Module (const ACE_CString &name, int cap) + : name_ (name), + imports_(cap), + exports_(cap), + extrefs_(0) +{ +} + +ACE_CString & +Obj_Module::name() +{ + return name_; +} + +Sig_List & +Obj_Module::exports() +{ + return exports_; +} + +Sig_List & +Obj_Module::imports() +{ + return imports_; +} + +int +Obj_Module::extref() +{ + return extrefs_; +} + +void +Obj_Module::add_extref() +{ + extrefs_ ++; +} + +void +Obj_Module::remove_extref() +{ + extrefs_ --; +} + +int +Obj_Module::read_line (ACE_HANDLE src, ACE_Message_Block **buf) +{ + int eoln = 0; + // ACE_Time_Value timeout (1,0); + if (buf == 0 || *buf == 0) { + char dummy; + while (!eoln && ACE_OS::read(src,&dummy,1) == 1) { + eoln = (dummy == '\n'); + } + return eoln; + } + // while (!eoln && ACE::recv(src,buf->wr_ptr(),1,&timeout) == 1) { + while (!eoln && ACE_OS::read(src,(*buf)->wr_ptr(),1) == 1) { + eoln = (*(*buf)->wr_ptr() == '\n'); + (*buf)->wr_ptr(1); + if ((*buf)->space() == 0) { + (*buf)->cont(new ACE_Message_Block(102400)); + *buf = (*buf)->cont(); + } + } + return eoln; +} + +void +Obj_Module::add_source(const char *p, int imports_only) +{ + ACE_Process nmproc; + ACE_Process_Options nm_opts; + ACE_CString path (p); + + ACE_CString::size_type pathsep = path.rfind('/'); + + ACE_CString src_name; + ACE_CString workpath; + + if (pathsep == ACE_CString::npos) { + src_name = path; + workpath = "."; + } else { + src_name = path.substr(pathsep+1); + workpath= path.substr(0,pathsep); + } + + ACE_HANDLE pipe[2]; + ACE_Pipe io(pipe); + + nm_opts.working_directory (workpath.c_str()); + nm_opts.set_handles (ACE_STDIN,pipe[1]); + + // Options for the command line shown here are for the GNU nm 2.9.5 + + int result = nm_opts.command_line ("nm -C %s",src_name.c_str()); + // Prevent compiler warning about "unused variable" if ACE_ASSERT is + // an empty macro. + ACE_UNUSED_ARG (result); + ACE_ASSERT (result == 0); + + nmproc.spawn (nm_opts); + if (ACE_OS::close(pipe[1]) == -1) + ACE_DEBUG ((LM_DEBUG, "%p\n", "close")); + nm_opts.release_handles(); + + int import_lines = 0; + int export_lines = 0; + ACE_Message_Block im_buffer (102400); + ACE_Message_Block ex_buffer (102400); + ACE_Message_Block *im_buf_cur = &im_buffer; + ACE_Message_Block *ex_buf_cur = &ex_buffer; + char dummy; + int eoln = 1; + // ACE_Time_Value timeout (1,0); + int is_import = 1; + int is_export = 1; + + while (eoln == 1) { + for (int i = 0; i < 10; i++) { + if (ACE_OS::read(pipe[0],&dummy,1) != 1) { + eoln = 2; + break; + } + } + if (eoln == 2) + break; + is_import = dummy == 'U'; + is_export = !imports_only && (ACE_OS::strchr("BCDRTVW",dummy) != 0); + + // if (ACE::recv(pipe[0],&dummy,1,&timeout) != 1) + if (ACE_OS::read(pipe[0],&dummy,1) != 1) + break; + + eoln = this->read_line (pipe[0], is_import ? &im_buf_cur : + (is_export ? &ex_buf_cur : 0)); + import_lines += is_import; + export_lines += is_export; + } + // ACE_DEBUG ((LM_DEBUG, "read %d import lines and %d export lines\n", + // import_lines, export_lines)); + + nmproc.wait (); + ACE_OS::close (pipe[0]); + + this->populate_sig_list (imports_,import_lines,&im_buffer); + if (!imports_only) + this->populate_sig_list (exports_,export_lines,&ex_buffer); +} + +void +Obj_Module::populate_sig_list (Sig_List &siglist, + int lines, + ACE_Message_Block *buf) +{ + char *c; + ACE_CString temp; + for (int i = 0; i < lines; i++) { + for (c = buf->rd_ptr(); + c != buf->wr_ptr() && *c != '\n'; c++); + temp += ACE_CString(buf->rd_ptr(),(c - buf->rd_ptr())); + buf->rd_ptr(c+1); + if (*c == '\n') { + // ACE_DEBUG ((LM_DEBUG, "%s\n",temp.c_str())); + siglist.add (temp); + temp.clear(); + } else { + buf = buf->cont(); + if (buf == 0) { + siglist.add (temp); + } + } + } +} diff --git a/ACE/apps/soreduce/Obj_Module.h b/ACE/apps/soreduce/Obj_Module.h new file mode 100644 index 00000000000..1836b75e7ec --- /dev/null +++ b/ACE/apps/soreduce/Obj_Module.h @@ -0,0 +1,70 @@ +// -*- C++ -*- +// $Id$ + +// File: Obj_Module.h + +// Author: Phil Mesnier + +#ifndef _OBJ_MODULE_H_ +#define _OBJ_MODULE_H_ + +// Obj_Module encapsulates the result of applying nm to a single object module +// in a shared library. Currently an object module consists of two types of +// signatures, those that are exported, able to resolve references from others, +// and those that are imported, needing resolution. +// +// Obj_Modules keep track of external references. In the end, any module that +// has one or more external references to it must be included in the resulting +// library. While the means exists to remove external references, perhaps +// through further analysis of undefined signatures and their usage, this is +// not currently done. Once a technique is discovered to allow for easy +// determination that reference is truly unneeded this code may be useful. + +#include "Sig_List.h" + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL +class ACE_Message_Block; +ACE_END_VERSIONED_NAMESPACE_DECL + +class Obj_Module { +public: + Obj_Module ( const ACE_CString &, int = 500); + + // Returns the list of exported signatures, ie. those that are defined in + // this module + Sig_List & exports(); + + // Returns the list of signatures used by this module but not defined within + Sig_List & imports(); + + // Returns the name of the object module. + ACE_CString &name(); + + // Add_source invokes GNU nm on the supplied file and parses the output to + // build the list of imported and exported signatures. When replacing GNU + // nm to use a different tool, this method must be modified. In the future + // this could be a virtual to allow for specialization based on toolset. + void add_source (const char *, int = 0); + + // Get the number of external references to this object module. At the end + // of processing, if the number of external references is 0, the module is + // not included in the final library. + int extref (); + + // add a new external reference to this module. + void add_extref (); + + // remove an exterenal reference. Currently, this function is not used. + void remove_extref(); + +private: + void populate_sig_list (Sig_List &, int , ACE_Message_Block *); + int read_line (ACE_HANDLE src, ACE_Message_Block **buf); + + ACE_CString name_; + Sig_List imports_; + Sig_List exports_; + int extrefs_; +}; + +#endif /* _OBJ_MODULE_H_ */ diff --git a/ACE/apps/soreduce/README b/ACE/apps/soreduce/README new file mode 100644 index 00000000000..5e0264bdab2 --- /dev/null +++ b/ACE/apps/soreduce/README @@ -0,0 +1,163 @@ +Shared Library Reduction Tool +----------------------------- + +The motivation for this tool was the understanding that the ACE & TAO +libraries were to be linked with the VxWorks kernel to allow multiple +applications to be run simultaniously with a minimum of footprint +consumed. Ordinarily a choice is made between static linking +applications, where each application gets only the object modules +needed, and shared object linkage, where multiple applications share +access to full libraries. Frequently a shared library will contain +code and data which is not used by any of the applications in a +particular configuration. The Shared Library Reduction Tool builds +libraries that include only the modules needed to support a specified +set of applications. + +The analysis is performed very late in the application implementation, +allowing the system implementors the freedom to use whatever TAO & ACE +components are needed. Once an application is built and running, its +shared object need may be evaluated. The evaluation is straight +forward. Run the soreduce program, passing the path to all of the +applications that will share the libraries. The soreduce program uses +the following steps to generate its results. + +1. A list of undefined symbols and shared libraries is built by + invoking the ldd and nm commands on each application. For now, + soreduce assumes the GNU variant of these tools. + +2. For each shared library, soreduce tries to invoke nm on each of the + intermediate object files used to build the library. It is + important that target library directory has a current .shobj + subdirectory. + +3. The list of undefined symbols is traversed. For each entry in the list, the + modules loaded from step 2 are examined to look for a matching + symbol. When one is found, the target symbol, and any others + satisfied by the module are removed from the list, and any + undefined symbols in the module are added to the list. This + process is repeated until the entire list of undefined symbols is + traversed without change. + +4. MPC files are generated. Rather than invoking the linker directly, an mpc + file is generated that may be used to build the libs. With these + mpc files, the actual library will be named (orig)_subset. + +Analysis Artifacts +------------------ + +Development of the shared library reduction tool also provided a +secondary benefit. It is able to output usage metrics for the various +modules, which may be useful for directing further hand-crafted +reduction efforts. Using the GNU nm with more verbose output, it is +possible to determine the first function using a given undefined +symbol. While it is not (yet) possible to automate the refactoring of +code based on this analysis, this information can provide a road map +for breaking a single module into two or a few that will result in +fewer incidental dependancies overall. However this speculation has +not been tested. + +Test results +------------ + +Running soreduce providing itself as the sole client. The soreduce +application is built on top of ACE, using just a few of ACE's +features. Here is the output: + +bash$ ./soreduce soreduce +discovering libraries +loading object modules +Libs subject to analysis: + ACE +Starting analysis +pass 0, undef count = 69 +pass 1, undef count = 207 +pass 2, undef count = 278 +pass 3, undef count = 271 +pass 4, undef count = 245 +pass 5, undef count = 235 +Writing results +Making directory /opt/wustl/ACE_wrappers/build/native/ace/usage_metrics +ACE: 61 out of 210 modules required +writing file /opt/wustl/ACE_wrappers/build/native/ace/ACE_subset.mpc +Done. + +The size of libACE.so before rebuilding: + +bash$ size libACE.so + text data bss dec hex filename +2361958 498760 12516 2873234 2bd792 libACE.so + +and after: + +bash$ size libACE_subset.so + text data bss dec hex filename + 987167 207452 7580 1202199 125817 libACE_subset.so + + +2873234 - 1202199 = 1671035 bytes eliminated, a 58.2% reduction + +Here is another example, using the Naming Service, and its simple test +client. Note that the orbsvcs results are bogus, the libraries are +already fairly well factored so that the additional subsetting by +soreduce is not effective. Also, due to the layout of the orbsvcs +library source directory, the tool may not generate valid mpc files. + +bash$ $ACE_ROOT/apps/soreduce/soreduce Naming_Service/Naming_Service tests/Simple_Naming/client +discovering libraries +loading object modules +Libs subject to analysis: + TAO_CosNaming + TAO_Svc_Utils + TAO_IORTable + TAO_PortableServer + TAO + ACE +Starting analysis +pass 0, undef count = 339 +pass 1, undef count = 580 +pass 2, undef count = 438 +pass 3, undef count = 278 +pass 4, undef count = 244 +pass 5, undef count = 246 +pass 6, undef count = 242 +Writing results +Making directory /opt/wustl/ACE_wrappers/build/native/TAO/orbsvcs/orbsvcs/usage_metrics +TAO_CosNaming: 11 out of 256 modules required +writing file /opt/wustl/ACE_wrappers/build/native/TAO/orbsvcs/orbsvcs/TAO_CosNaming_subset.mpc +Making directory /opt/wustl/ACE_wrappers/build/native/TAO/orbsvcs/orbsvcs/usage_metrics +TAO_Svc_Utils: 8 out of 256 modules required +writing file /opt/wustl/ACE_wrappers/build/native/TAO/orbsvcs/orbsvcs/TAO_Svc_Utils_subset.mpc +Making directory /opt/wustl/ACE_wrappers/build/native/TAO/tao/IORTable/usage_metrics +TAO_IORTable: 4 out of 4 modules required +writing file /opt/wustl/ACE_wrappers/build/native/TAO/tao/IORTable/TAO_IORTable_subset.mpc +Making directory /opt/wustl/ACE_wrappers/build/native/TAO/tao/PortableServer/usage_metrics +TAO_PortableServer: 26 out of 29 modules required +writing file /opt/wustl/ACE_wrappers/build/native/TAO/tao/PortableServer/TAO_PortableServer_subset.mpc +Making directory /opt/wustl/ACE_wrappers/build/native/TAO/tao/usage_metrics +TAO: 160 out of 191 modules required +writing file /opt/wustl/ACE_wrappers/build/native/TAO/tao/TAO_subset.mpc +Making directory /opt/wustl/ACE_wrappers/build/native/ace/usage_metrics +ACE: 75 out of 210 modules required +writing file /opt/wustl/ACE_wrappers/build/native/ace/ACE_subset.mpc +Done. + +Size before & after: + text data bss dec hex filename +2361958 498760 12516 2873234 2bd792 libACE.so +3432206 704188 30992 4167386 3f96da libTAO.so +1931145 326632 7528 2265305 2290d9 libTAO_PortableServer.so + 76561 12504 364 89429 15d55 libTAO_IORTable.so + + +Sum: 9395354 + + + text data bss dec hex filename +1340017 275440 8140 1623597 18c62d libACE_subset.so +3131226 643816 27984 3803026 3a0792 libTAO_subset.so +1845515 308812 6896 2161223 20fa47 libTAO_PortableServer_subset.so + 76603 12508 364 89475 15d83 libTAO_IORTable_subset.so + +Sum: 7677321 + +Savings: 1718033 or 18.3% diff --git a/ACE/apps/soreduce/SO_Group.cpp b/ACE/apps/soreduce/SO_Group.cpp new file mode 100644 index 00000000000..f369050a1dd --- /dev/null +++ b/ACE/apps/soreduce/SO_Group.cpp @@ -0,0 +1,166 @@ +// -*- C++ -*- +// $Id$ + +// File: SO_Group.cpp + +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Log_Msg.h" +#include "ace/Process.h" +#include "ace/Pipe.h" + +#include "Library.h" +#include "SO_Group.h" + +ACE_RCSID(src, SO_Group, "$Id$") + + +SO_Group::SO_Group () + : undef_wrapper_ ("nothing"), + undefs_(undef_wrapper_.imports()), + libs_ (0), + max_libs_ (128), + num_libs_(0) +{ + libs_ = new Library*[max_libs_]; +} + +SO_Group::~SO_Group () +{ + for (int i = 0; i < num_libs_; delete libs_[i++]); + delete [] libs_; +} + +void +SO_Group::add_executable (const char * path) +{ + ACE_Process proc; + ACE_Process_Options opts; + + ACE_HANDLE pipe[2]; + ACE_Pipe io(pipe); + + opts.set_handles (ACE_STDIN,pipe[1]); + + int result = opts.command_line ("ldd %s",path); + // Prevent compiler warning about "unused variable" if ACE_ASSERT is + // an empty macro. + ACE_UNUSED_ARG (result); + ACE_ASSERT (result == 0); + + proc.spawn (opts); + if (ACE_OS::close(pipe[1]) == -1) + ACE_DEBUG ((LM_DEBUG, "%p\n", "close")); + opts.release_handles(); + + const int max_line_length = 1000; + char line[max_line_length]; + + while (1) { + ACE_OS::memset (line,0,max_line_length); + int len = 0; + int nread = 0; + int bogus = 0; + // skip initial whitespace + while ((nread = ACE_OS::read(pipe[0],line,1)) == 1 && + (*line == ' ' || *line == '\t')); + + if (nread != 1) + break; + + // read the library name + len = 1; + while ((nread = ACE_OS::read(pipe[0],line + len,1)) == 1 && + (line[len] != ' ')) + if (! bogus && ++len == max_line_length) + { + bogus = 1; + break; + } + if (nread != 1 || bogus) + break; + line[len] = 0; + char * dot = ACE_OS::strchr (line,'.'); + if (dot) + *dot = 0; + char * libname = line + 3; // skip over "lib" + + // check to see if this is a new library + int found = 0; + for (int i = 0; !found && i < num_libs_; i++) + found = (libs_[i]->name() == libname); + + if (!found) { + Library *nlib = new Library(libname); + ACE_OS::memset (line,0,max_line_length); + + // skip over '=> ' + if (ACE_OS::read(pipe[0],line,3) != 3) + break; + + // get library path + len = 0; + while ((nread = ACE_OS::read(pipe[0],line + len,1)) == 1 && + (line[len] != ' ')) + if (! bogus && ++len == max_line_length) + { + bogus = 1; + break; + } + if (nread != 1 || bogus) + break; + line[len] = 0; + nlib->set_path (line); + libs_[num_libs_++] = nlib; + ACE_ASSERT (num_libs_ < max_libs_); // grow max libs? + } + // skip the rest of the line + while ((nread = ACE_OS::read(pipe[0],line,1)) == 1 && *line != '\n'); + if (nread != 1) + break; + } + proc.wait (); + ACE_OS::close (pipe[0]); + + undef_wrapper_.add_source(path,1); + // now do the ldd, iterate over the results to add new libs, etc. +} + +void +SO_Group::analize () +{ + for (int passcount = 0; undefs_.modified(); passcount++) { + ACE_DEBUG ((LM_DEBUG,"pass %d, undef count = %d\n", + passcount,undefs_.size())); + for (int i = 0; i < num_libs_; libs_[i++]->resolve(undefs_)); + } +} + +void +SO_Group::write_results() +{ + for (int i = 0; i < num_libs_; libs_[i++]->write_export_list(1)); +} + +void +SO_Group::load_modules() +{ + for (int i = 0; i < num_libs_; libs_[i++]->load_modules()); +} + +void +SO_Group::list_libs() +{ + ACE_DEBUG ((LM_DEBUG,"Libs subject to analysis:\n")); + for (int i = 0; i < num_libs_; i++) { + if (libs_[i]->has_modules()) + ACE_DEBUG ((LM_DEBUG," %s\n", libs_[i]->name().c_str())); + } +} + + + + + + + diff --git a/ACE/apps/soreduce/SO_Group.h b/ACE/apps/soreduce/SO_Group.h new file mode 100644 index 00000000000..09758c8c7bb --- /dev/null +++ b/ACE/apps/soreduce/SO_Group.h @@ -0,0 +1,48 @@ +// -*- C++ -*- +// $Id$ + +// File: SO_Group.h + +// Author: Phil Mesnier + +#ifndef _SO_GROUP_H_ +#define _SO_GROUP_H_ + +#include "Library.h" + +// A shared object group is a wrapper around all of the information needed to +// analize a collection of applications so that common shared libraries can +// be reduced. + +class SO_Group +{ +public: + SO_Group (); + ~SO_Group (); + + // For each executable named, run ldd to get the dependances list, For each + // library listed, see if there is a path to .shobj and add to the list of + // libraries if found. Finally, add the undefined symbols from the executable + // to the undefs collection. + void add_executable(const char * ); + + // Do the actual business of the program + void analize (); + + // Output the results + void write_results (); + + // load the object modules for the group + void load_modules (); + + void list_libs(); + +private: + Obj_Module undef_wrapper_; + Sig_List &undefs_; + Library **libs_; + int max_libs_; + int num_libs_; +}; + +#endif //_SO_GROUP_H_ diff --git a/ACE/apps/soreduce/Sig_List.cpp b/ACE/apps/soreduce/Sig_List.cpp new file mode 100644 index 00000000000..2f385790aff --- /dev/null +++ b/ACE/apps/soreduce/Sig_List.cpp @@ -0,0 +1,184 @@ +// $Id$ + +// File: Sig_List.cpp + +// Author: Phil Mesnier + +#include "ace/OS_NS_string.h" +#include "Sig_List.h" + +ACE_RCSID(src, Sig_List, "$Id$") + +//----------------------------------------------------------------------------- + +Sig_List::Sig_List (int cap) + : size_(0), + capacity_(cap), + index_(0), + has_nulls_(0), + modified_(0), + array_(0) +{ + array_ = new Signature*[capacity_]; +} + +Sig_List::~Sig_List () +{ + for (int i = 0; i < size_; i++) + if (array_[i]) array_[i]->release(); + delete [] array_; +} + +void +Sig_List::add (const ACE_CString &s) +{ + if (this->index_of (s) != -1) + return; + modified_ = 1; + if (has_nulls_) + for (int i = 0; i < size_; i++) + if (array_[i] == 0) { + array_[i] = new Signature (s); + has_nulls_ --; + return; + } + if (size_ == capacity_) { + int ncap = capacity_ * 2; + Signature ** narray = new Signature *[ncap]; + ACE_OS::memcpy (narray,array_,capacity_ * sizeof(Signature*)); + delete [] array_; + array_ = narray; + capacity_ = ncap; + } + array_[size_++] = new Signature(s); +} + +void +Sig_List::add (const Sig_List &other) +{ + if (capacity_ < size_ + other.capacity_) { + int ncap = size_ + other.capacity_ + 50; + Signature ** narray = new Signature *[ncap]; + ACE_OS::memcpy (narray,array_,capacity_ * sizeof(Signature*)); + delete [] array_; + array_ = narray; + capacity_ = ncap; + } + modified_ = 1; + for (int i = 0; i < other.size_; i++) + if (other.array_[i] != 0 && + this->index_of (other.array_[i]->name()) == -1) + { + if (!has_nulls_) + array_[size_++] = other.array_[i]->dup(); + else + for (int i = 0; i < size_; i++) + if (array_[i] == 0) + { + array_[i] = other.array_[i]->dup(); + has_nulls_ --; + break; + } + } +} + +void +Sig_List::remove (const Signature &s) +{ + for (int i = 0; i < size_; i++) + if (array_[i] && array_[i]->name() == s.name()) { + array_[i]->release(); + array_[i] = 0; + modified_ = 1; + if (i == size_ - 1) + size_ --; + else + has_nulls_ ++; + break; + } +} + +void +Sig_List::remove_current () +{ + array_[index_]->release(); + array_[index_] = 0; + modified_ = 1; + if (index_ == size_ - 1) + size_--; + else + has_nulls_++; +} + +int +Sig_List::index_of (const Signature *s) +{ + for (int i = 0; i < size_; i++) + if (array_[i] && array_[i]->name() == s->name()) { + array_[i]->used(); + return i; + } + return -1; +} + +int +Sig_List::index_of (const ACE_CString &s) +{ + for (int i = 0; i < size_; i++) + if (array_[i] && array_[i]->name() == s) { + return i; + } + return -1; +} + + +const Signature * +Sig_List::first() +{ + for (index_ = 0; index_ < size_; index_++) + if (array_[index_] != 0) + return array_[index_]; + return 0; +} + +const Signature * +Sig_List::next() +{ + for (++index_; index_ < size_; index_++) + if (array_[index_] != 0) + return array_[index_]; + return 0; +} + +int +Sig_List::hasmore () +{ + return index_ < size_; +} + +int +Sig_List::size() +{ + return size_; +} + +int +Sig_List::modified() +{ + int rtn = modified_; + modified_ = 0; + int insert = 0; + if (has_nulls_) { + for (int i = 0; i < size_; i++) + if (array_[i] != 0) { + if (i != insert) { + array_[insert] = array_[i]; + array_[i] = 0; + } + insert++; + } + size_ = insert+1; + has_nulls_ = 0; + } + return rtn; +} diff --git a/ACE/apps/soreduce/Sig_List.h b/ACE/apps/soreduce/Sig_List.h new file mode 100644 index 00000000000..9b4b0dc115f --- /dev/null +++ b/ACE/apps/soreduce/Sig_List.h @@ -0,0 +1,49 @@ +// -*- C++ -*- +// $Id$ + +// File: Sig_List.h + +// Author: Phil Mesnier + + +#ifndef _SIG_LIST_H_ +#define _SIG_LIST_H_ + +// A Sig_List is a specialized container of signatures. The initial use of a +// Sig_List was to manage a variable length of undefined Signatures, so the +// program could know when all possible resolutions were determined. As the +// program grows in complexity, Sig_Lists are used to store other groups as +// well. The methods provide simple list traversal, as well as efficient use +// of space. + +#include "Signature.h" + +class Sig_List { +public: + Sig_List (int cap = 500); + ~Sig_List (); + void add (const ACE_CString &s); + void add (const Sig_List &other); + void remove (const Signature &s); + void remove_current (); + + int index_of (const Signature *s); + int index_of (const ACE_CString &s); + int hasmore(); + const Signature *first(); + const Signature *next(); + + int modified (); + int size(); + +private: + int size_; + int capacity_; + int index_; + int has_nulls_; + int modified_; + Signature ** array_; +}; + + +#endif /* _SIG_LIST_H_ */ diff --git a/ACE/apps/soreduce/Signature.cpp b/ACE/apps/soreduce/Signature.cpp new file mode 100644 index 00000000000..2d4d907e92b --- /dev/null +++ b/ACE/apps/soreduce/Signature.cpp @@ -0,0 +1,45 @@ +#include "Signature.h" + +ACE_RCSID(src, Signature, "$Id$") + +//----------------------------------------------------------------------------- + +Signature::Signature (const ACE_CString &name) + :name_(name), + ref_count_ (1), + used_ (0) +{ +} + +void +Signature::used () +{ + used_++; +} + +int +Signature::used_count() const +{ + return used_; +} + +const ACE_CString & +Signature::name() const +{ + return name_; +} + +Signature * +Signature::dup() +{ + ref_count_++; + return this; +} + +void +Signature::release() +{ + if (--ref_count_ == 0) + delete this; +} + diff --git a/ACE/apps/soreduce/Signature.h b/ACE/apps/soreduce/Signature.h new file mode 100644 index 00000000000..358a756aa7a --- /dev/null +++ b/ACE/apps/soreduce/Signature.h @@ -0,0 +1,50 @@ +// -*- C++ -*- +// $Id$ + +// File: Signature.h + +// Author: Phil Mesnier + + +#ifndef _SIGNATURE_H_ +#define _SIGNATURE_H_ + +// Signature class encapsulates a single line of nm output. This line may be +// either an "undefined" name to be resolved, or text or data which resolves +// the unknowns. Some of the features of the Signature class are currently +// unused, such as owner_, which is anticipation of analysis that may lead to +// further code reduction. The premise being that unresolved symbols that are +// defined within otherwise unused code should not be resolved. However this +// information is not available in the output of nm. Further research is +// required. +// +// Signatures are reference counted to avoid duplication. + +#include <ace/SString.h> + +class Signature { +public: + + enum Kind { + text_, + undef_ + }; + + Signature (const ACE_CString &); + void used (); + int used_count() const; + + const ACE_CString &name() const; + + Signature *dup(); + void release(); + +private: + ACE_CString name_; + int ref_count_; + int used_; + Signature * owner_; + Kind kind_; +}; + +#endif diff --git a/ACE/apps/soreduce/soreduce.cpp b/ACE/apps/soreduce/soreduce.cpp new file mode 100644 index 00000000000..4ac9e99aa16 --- /dev/null +++ b/ACE/apps/soreduce/soreduce.cpp @@ -0,0 +1,51 @@ +// $Id$ + +// File: soreduce.cpp + +// Author: Phil Mesnier + +// theory of operation: +// 1. Build a complete set of applications +// 2. apply "nm" to each of the .o files that make up the libraries to subset +// filter the results into two files for each, one with exported names, the +// other with imported names. +// 3. apply "nm" to all of the elements which use ace & tao. build a list of +// imported names +// 4. Repeat the following steps until no entries remain in the list of +// imports +// 4.1 Take a name from the list of imports, locate the module containing the +// export of that name +// 4.2 Add the exporting module to the list of required modules, add its list +// of exports to the list of resolved exports, add its imported names to +// the list of imports. +// 4.4 Traverse the list of imported names to eliminate any found in the list +// of exported names. +// 4.5 go to step 4.1 +// 5. construct a new makefile for all required modules. +// +// Currently works only with GNU nm + +#include <ace/Log_Msg.h> + +#include "SO_Group.h" + +ACE_RCSID (src, soreduce, "$Id$") + +int +main (int argc, char ** argv) +{ + SO_Group group; + + ACE_DEBUG ((LM_DEBUG,"discovering libraries\n")); + for (int i = 1; i < argc; group.add_executable (argv[i++])); + ACE_DEBUG ((LM_DEBUG,"loading object modules\n")); + group.load_modules(); + group.list_libs(); + ACE_DEBUG ((LM_DEBUG,"Starting analysis\n")); + group.analize(); + ACE_DEBUG ((LM_DEBUG,"Writing results\n")); + group.write_results(); + ACE_DEBUG ((LM_DEBUG,"Done.\n")); + return 0; +} + diff --git a/ACE/apps/soreduce/soreduce.mpc b/ACE/apps/soreduce/soreduce.mpc new file mode 100644 index 00000000000..2fe7cc27101 --- /dev/null +++ b/ACE/apps/soreduce/soreduce.mpc @@ -0,0 +1,15 @@ +// $Id$ + +project(soreduce) : aceexe { + avoids += uses_wchar ace_for_tao + exename = soreduce + Source_Files { + Signature.cpp + Sig_List.cpp + Obj_Module.cpp + Library.cpp + SO_Group.cpp + soreduce.cpp + } +} + |