From 4957e2d6555c5b6ac0d41ac9e6096dea711e88f0 Mon Sep 17 00:00:00 2001 From: Phil Mesnier Date: Wed, 20 Feb 2002 22:05:12 +0000 Subject: ChangeLog tag: Wed Feb 20 15:26:43 2002 Phil Mesnier --- apps/soreduce/Library.cpp | 368 +++++++++++++++++++++++++++++++++++++++++++ apps/soreduce/Library.h | 126 +++++++++++++++ apps/soreduce/Makefile | 42 +++++ apps/soreduce/Obj_Module.cpp | 193 +++++++++++++++++++++++ apps/soreduce/Obj_Module.h | 66 ++++++++ apps/soreduce/README | 153 ++++++++++++++++++ apps/soreduce/SO_Group.cpp | 158 +++++++++++++++++++ apps/soreduce/SO_Group.h | 48 ++++++ apps/soreduce/Sig_List.cpp | 184 ++++++++++++++++++++++ apps/soreduce/Sig_List.h | 49 ++++++ apps/soreduce/Signature.cpp | 45 ++++++ apps/soreduce/Signature.h | 50 ++++++ apps/soreduce/soreduce.cpp | 51 ++++++ 13 files changed, 1533 insertions(+) create mode 100644 apps/soreduce/Library.cpp create mode 100644 apps/soreduce/Library.h create mode 100644 apps/soreduce/Makefile create mode 100644 apps/soreduce/Obj_Module.cpp create mode 100644 apps/soreduce/Obj_Module.h create mode 100644 apps/soreduce/README create mode 100644 apps/soreduce/SO_Group.cpp create mode 100644 apps/soreduce/SO_Group.h create mode 100644 apps/soreduce/Sig_List.cpp create mode 100644 apps/soreduce/Sig_List.h create mode 100644 apps/soreduce/Signature.cpp create mode 100644 apps/soreduce/Signature.h create mode 100644 apps/soreduce/soreduce.cpp (limited to 'apps') diff --git a/apps/soreduce/Library.cpp b/apps/soreduce/Library.cpp new file mode 100644 index 00000000000..198d34aac99 --- /dev/null +++ b/apps/soreduce/Library.cpp @@ -0,0 +1,368 @@ +// -*- C++ -*- +// $Id$ + +// File: Library.cpp + +// Author: Phil Mesnier + +// This file contains the implementation of the classes responsible for +// generating specialized makefiles for individual libraries, as well as +// outputting usage metrics for the various object modules contained in the +// library. + + +#include +#include +#include +#include + +#include "Library.h" + +ACE_RCSID(src, Library, "$Id$") + +Makefile_Generator::Makefile_Generator (const ACE_CString& libname) + : makefile_(), + libname_(libname), + makefilename_() +{ + makefilename_ = "Makefile." + libname_ + "_subset"; +} + +Makefile_Generator::~Makefile_Generator () +{ +} + +void +Makefile_Generator::write_file (const ACE_CString& file) +{ + makefile_ << " \\\n\t" << file << flush; +} + +void +Makefile_Generator::write_prolog (const ACE_CString& path) +{ + ACE_CString fname (path + "/" + makefilename_); + ACE_DEBUG ((LM_DEBUG, "writing file %s\n",fname.c_str())); + makefile_.open(fname.c_str()); + if (!makefile_) + ACE_DEBUG ((LM_DEBUG,"makefile open failed\n")); + + makefile_ + << "#--------------------------------------------------------------------" + << endl; + makefile_ + << "# Generated makefile for producing a subset of the " + << libname_ << " library " << endl; + makefile_ + << "#--------------------------------------------------------------------" + << endl; + + makefile_ << "\nMAKEFILE = " << makefilename_ << endl; + makefile_ << "LIB = lib" << libname_ << "_subset.a" << endl; + makefile_ << "SHLIB = lib" << libname_ << "_subset.$(SOEXT)" << endl; + makefile_ << "\nFILES =" << flush; +} + +void +Makefile_Generator::write_epilog () +{ + makefile_ << "\n" << endl; + this->write_libdeps(); + makefile_ + << "#--------------------------------------------------------" << endl; + makefile_ + << "# Include macros and targets" << endl; + makefile_ + << "#--------------------------------------------------------" << endl; + makefile_ + << "include $(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU" << endl; + + this->write_initial_rules(); + + makefile_ + << "\nLSRC = $(addsuffix .cpp,$(FILES))\n" << endl; + + makefile_ + << "include $(ACE_ROOT)/include/makeinclude/macros.GNU" << endl; + makefile_ + << "include $(ACE_ROOT)/include/makeinclude/rules.common.GNU" << endl; + makefile_ + << "include $(ACE_ROOT)/include/makeinclude/rules.nested.GNU" << endl; + makefile_ + << "include $(ACE_ROOT)/include/makeinclude/rules.lib.GNU" << endl; + makefile_ + << "include $(ACE_ROOT)/include/makeinclude/rules.local.GNU" << endl; + + this->write_final_rules(); + + makefile_ << "\n" << endl; + + makefile_ + << "#-----------------------------------------------------------" << endl; + makefile_ + << "# Dependencies" << endl; + makefile_ + << "#-----------------------------------------------------------" << endl; + makefile_ + << "# DO NOT DELETE THIS LINE -- g++dep uses it." << endl; + makefile_ + << "# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY." << endl; + makefile_ + << "# IF YOU PUT ANYTHING HERE IT WILL GO AWAY" << endl; + + makefile_.close(); +} + +void +Makefile_Generator::write_libdeps() +{ + // nothing to do +} + +void +Makefile_Generator::write_initial_rules() +{ + // nothing to do +} + +void +Makefile_Generator::write_final_rules() +{ + // nothing to do +} + +//----------------------------------------------------------------------------- +Make_ACE_Dep_Lib::Make_ACE_Dep_Lib (const ACE_CString& libname) + : Makefile_Generator(libname) +{} + +void +Make_ACE_Dep_Lib::write_libdeps() +{ + makefile_ << "ACE_SHLIBS = -lACE_subset" << endl; +} + +//----------------------------------------------------------------------------- +Make_TAO_Lib::Make_TAO_Lib (const ACE_CString& libname) + : Make_ACE_Dep_Lib(libname) +{} + +void +Make_TAO_Lib::write_libdeps() +{ + makefile_ << "ifndef TAO_ROOT" << endl; + makefile_ << "TAO_ROOT = $(ACE_ROOT)/TAO" << endl; + makefile_ << "endif" << endl; + + makefile_ << "ACE_SHLIBS = -lACE_subset" << endl; +} + +void +Make_TAO_Lib::write_initial_rules() +{ + makefile_ << "include $(TAO_ROOT)/rules.tao.GNU" << endl; +} + +void +Make_TAO_Lib::write_final_rules() +{ + makefile_ << "include $(TAO_ROOT)/taoconfig.mk" << endl; +} + +//----------------------------------------------------------------------------- +Make_TAO_Dep_Lib::Make_TAO_Dep_Lib (const ACE_CString& libname) + : Make_TAO_Lib(libname) +{} + +void +Make_TAO_Dep_Lib::write_libdeps() +{ + makefile_ << "ifndef TAO_ROOT" << endl; + makefile_ << "TAO_ROOT = $(ACE_ROOT)/TAO" << endl; + makefile_ << "endif" << endl; + + makefile_ << "ACE_SHLIBS = -lTAO_subset -lACE_subset" << endl; +} + +//----------------------------------------------------------------------------- + +Library::Library (const ACE_TCHAR *name) + : name_(name), + path_(), + num_modules_(0), + num_exports_(0), + num_extrefs_(0), + modules_(0), + exported_(0), + makefile_(0) +{ + if (name_ == "ACE") + makefile_ = new Makefile_Generator(name_); + else if (name_.find ("ACE_") == 0) + makefile_ = new Make_ACE_Dep_Lib (name_); + else if (name_ == "TAO") + makefile_ = new Make_TAO_Lib (name_); + else + makefile_ = new Make_TAO_Dep_Lib (name_); +} + +Library::~Library () +{ + delete makefile_; + int i; + for (i = 0; i < num_modules_; delete modules_[i++]); + delete [] modules_; +} + +void +Library::set_path (const ACE_TCHAR *p) +{ + char abspath[1000]; + memset (abspath,0,1000); + int abspathlen = readlink(p,abspath,999); + ACE_CString path (p); + if (abspathlen > 0) { + abspath[abspathlen] = 0; + path = abspath; + } + + int 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; +} + +static int +selector (const dirent *d) +{ + return ACE_OS_String::strstr (d->d_name, ".o") != 0; +} + +static int +comparator (const dirent **d1, const dirent **d2) +{ + return ACE_OS_String::strcmp ((*d1)->d_name, (*d2)->d_name); +} + +void +Library::load_modules () +{ + ACE_CString subdir = path_ + "/.shobj"; + + struct dirent **dent; + num_modules_ = ACE_OS_Dirent::scandir(subdir.c_str(), + &dent,selector,comparator); + + if (num_modules_ > 0) { + modules_ = new Obj_Module * [num_modules_]; + for (int i = 0; i < num_modules_; i++) { + modules_[i] = new Obj_Module(dent[i]->d_name); + modules_[i]->add_source (ACE_CString(subdir + "/" + dent[i]->d_name).c_str()); + ACE_OS_Memory::free(dent[i]); + }; + } + + if (num_modules_ > -1) + ACE_OS_Memory::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(rcpath.c_str()) == -1) + 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_)); + + makefile_->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 *sig = modules_[i]->imports().first(); + modules_[i]->imports().hasmore(); + sig = modules_[i]->imports().next()) + countfile << sig->name() << endl; + } + makefile_->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; + } + + makefile_->write_epilog(); +} diff --git a/apps/soreduce/Library.h b/apps/soreduce/Library.h new file mode 100644 index 00000000000..46cb03feac7 --- /dev/null +++ b/apps/soreduce/Library.h @@ -0,0 +1,126 @@ +// -*- 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 makefile build the reduce footprint library. + +#include "Obj_Module.h" + +// The Makefile generator class serves as the base class used to output the +// custom makefiles (or in the future, project files) used to build the +// subsetted libraries. +// The base class will make libACE_subset.so + +class Makefile_Generator +{ +public: + Makefile_Generator (const ACE_CString& ); + virtual ~Makefile_Generator(); + + void write_prolog (const ACE_CString& ); + void write_file (const ACE_CString& ); + void write_epilog (); + +protected: + virtual void write_libdeps(); + virtual void write_initial_rules(); + virtual void write_final_rules(); + + ofstream makefile_; + ACE_CString libname_; + ACE_CString makefilename_; +}; + +// Generate makefiles for libraries dependant on ACE, that are not TAO. +class Make_ACE_Dep_Lib : public Makefile_Generator +{ +public: + Make_ACE_Dep_Lib (const ACE_CString& ); + +protected: + virtual void write_libdeps(); +}; + +// Generates makefiles for libTAO_subset.so +class Make_TAO_Lib : public Make_ACE_Dep_Lib +{ +public: + Make_TAO_Lib (const ACE_CString& ); + +protected: + virtual void write_libdeps(); + virtual void write_initial_rules(); + virtual void write_final_rules(); +}; + +// Generates makefiles for libs dependant on TAO. This has a problem when +// building libraries in the orbsvcs tree. +class Make_TAO_Dep_Lib : public Make_TAO_Lib +{ +public: + Make_TAO_Dep_Lib (const ACE_CString& ); + +protected: + virtual void write_libdeps(); +}; + +//---------------------------------------------------------------------------- + +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 a makefile 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_; + Makefile_Generator *makefile_; +}; + +#endif /* _LIBRARY_H_ */ diff --git a/apps/soreduce/Makefile b/apps/soreduce/Makefile new file mode 100644 index 00000000000..2fe50972d21 --- /dev/null +++ b/apps/soreduce/Makefile @@ -0,0 +1,42 @@ +#---------------------------------------------------------------------------- +# $Id$ +# +# Makefile for soreduce utility +#---------------------------------------------------------------------------- + +BIN = soreduce +INSBIN = $(ACE_ROOT)/bin/soreduce + +FILES = Signature \ + Sig_List \ + Obj_Module \ + Library \ + SO_Group \ + soreduce + +SRC = $(addsuffix .cpp,$(FILES)) +OBJ = $(addsuffix .o,$(FILES)) + +BUILD = $(VBIN) + +INSTALL = $(VBIN:%=$(INSBIN)/%$(EXEEXT)) + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(ACE_ROOT)/include/makeinclude/macros.GNU +include $(ACE_ROOT)/include/makeinclude/rules.common.GNU +include $(ACE_ROOT)/include/makeinclude/rules.nonested.GNU +include $(ACE_ROOT)/include/makeinclude/rules.bin.GNU +include $(ACE_ROOT)/include/makeinclude/rules.local.GNU + +#---------------------------------------------------------------------------- +# Dependencies +#---------------------------------------------------------------------------- + +# DO NOT DELETE THIS LINE -- g++dep uses it. +# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. + +# IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/apps/soreduce/Obj_Module.cpp b/apps/soreduce/Obj_Module.cpp new file mode 100644 index 00000000000..ac6881dd96f --- /dev/null +++ b/apps/soreduce/Obj_Module.cpp @@ -0,0 +1,193 @@ +// -*- 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 +#include +#include + +#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 ACE_TCHAR *p, int imports_only) +{ + ACE_Process nmproc; + ACE_Process_Options nm_opts; + ACE_CString path (p); + + int 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 + + ACE_ASSERT(nm_opts.command_line ("nm -C %s",src_name.c_str()) == 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_String::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 (); + 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/apps/soreduce/Obj_Module.h b/apps/soreduce/Obj_Module.h new file mode 100644 index 00000000000..7d2dd489754 --- /dev/null +++ b/apps/soreduce/Obj_Module.h @@ -0,0 +1,66 @@ +// -*- 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" + +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 ACE_TCHAR *, 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/apps/soreduce/README b/apps/soreduce/README new file mode 100644 index 00000000000..abe3d64f084 --- /dev/null +++ b/apps/soreduce/README @@ -0,0 +1,153 @@ +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. Makefiles are generated. Rather than invoking the linker directly, a make +file is generated that may be used to build the libs. With these makefiles, the +actual library will be named lib(orig)_subset.so. + +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 +mkdir: File exists +ACE: 61 out of 210 modules required +writing file /opt/wustl/ACE_wrappers/build/native/ace/Makefile.ACE_subset +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 does not +generate valid makefiles. + +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/Makefile.TAO_CosNaming_subset +Making directory /opt/wustl/ACE_wrappers/build/native/TAO/orbsvcs/orbsvcs/usage_metrics +mkdir: File exists +TAO_Svc_Utils: 8 out of 256 modules required +writing file /opt/wustl/ACE_wrappers/build/native/TAO/orbsvcs/orbsvcs/Makefile.TAO_Svc_Utils_subset +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/Makefile.TAO_IORTable_subset +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/Makefile.TAO_PortableServer_subset +Making directory /opt/wustl/ACE_wrappers/build/native/TAO/tao/usage_metrics +mkdir: File exists +TAO: 160 out of 191 modules required +writing file /opt/wustl/ACE_wrappers/build/native/TAO/tao/Makefile.TAO_subset +Making directory /opt/wustl/ACE_wrappers/build/native/ace/usage_metrics +mkdir: File exists +ACE: 75 out of 210 modules required +writing file /opt/wustl/ACE_wrappers/build/native/ace/Makefile.ACE_subset +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/apps/soreduce/SO_Group.cpp b/apps/soreduce/SO_Group.cpp new file mode 100644 index 00000000000..b00cdeeb019 --- /dev/null +++ b/apps/soreduce/SO_Group.cpp @@ -0,0 +1,158 @@ + +#include +#include + +#include +#include + +#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_ (30), + 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]); + + ACE_ASSERT(opts.command_line ("ldd %s",path) == 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 (); + 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/apps/soreduce/SO_Group.h b/apps/soreduce/SO_Group.h new file mode 100644 index 00000000000..09758c8c7bb --- /dev/null +++ b/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/apps/soreduce/Sig_List.cpp b/apps/soreduce/Sig_List.cpp new file mode 100644 index 00000000000..4327a14bdc4 --- /dev/null +++ b/apps/soreduce/Sig_List.cpp @@ -0,0 +1,184 @@ + +// $Id$ + +// File: Sig_List.cpp + +// Author: Phil Mesnier + +#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/apps/soreduce/Sig_List.h b/apps/soreduce/Sig_List.h new file mode 100644 index 00000000000..9b4b0dc115f --- /dev/null +++ b/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/apps/soreduce/Signature.cpp b/apps/soreduce/Signature.cpp new file mode 100644 index 00000000000..2d4d907e92b --- /dev/null +++ b/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/apps/soreduce/Signature.h b/apps/soreduce/Signature.h new file mode 100644 index 00000000000..358a756aa7a --- /dev/null +++ b/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 + +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/apps/soreduce/soreduce.cpp b/apps/soreduce/soreduce.cpp new file mode 100644 index 00000000000..4ac9e99aa16 --- /dev/null +++ b/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 + +#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; +} + -- cgit v1.2.1