Changeset 20778

Show
Ignore:
Timestamp:
09/06/07 16:44:48 (1 year ago)
Author:
patrick
Message:

MFT r20773, r20774: Implemented stack trace printing on Mac OS X.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • juggler/branches/2.2/modules/vapor/ChangeLog

    r20709 r20778  
    11DATE       AUTHOR   CHANGE 
    22---------- -------- ----------------------------------------------------------- 
     32007-09-06 patrick  Implemented stack trace printing on Mac OS X. 
     4 
    35[2.0.0 released - 8.14.2007]=================================================== 
    462007-07-24 aronb    vpr::Library::findSymbol() throws vpr::LibraryException if 
  • juggler/branches/2.2/modules/vapor/vpr/SystemBase.cpp

    r20460 r20778  
    3636#include <vpr/vprConfig.h> 
    3737 
     38#include <cstdlib> 
     39#include <sstream> 
     40#include <string> 
     41 
    3842#if defined(HAVE_BACKTRACE) 
    3943#  include <sys/types.h> 
    4044#  include <unistd.h> 
    4145#  include <execinfo.h> 
    42 #  include <sstream> 
     46#elif defined(VPR_OS_Darwin) 
     47#  include <iomanip> 
     48#  include <vector> 
     49#  include <boost/algorithm/string/predicate.hpp> 
     50 
     51extern "C" 
     52
     53 
     54#  include <mach-o/dyld.h> 
     55#  include <mach-o/loader.h> 
     56#  include <mach-o/nlist.h> 
     57#  include <mach-o/stab.h> 
     58 
     59
     60 
    4361#elif defined(VPR_OS_Windows) 
    4462#  if _MSC_VER >= 1400 
     
    4664#  endif 
    4765 
    48 #  include <stdlib.h> 
    4966#  include <dbghelp.h> 
    5067#  include <iomanip> 
    51 #  include <sstream> 
    5268#  include <boost/format.hpp> 
    5369#endif 
    54  
    55 #include <string> 
    5670 
    5771#if (! defined(__INTEL_COMPILER) && defined(__GNUC__) && \ 
     
    8094namespace 
    8195{ 
     96 
    8297#if defined(VPR_OS_Windows) && defined(VPR_DEBUG) 
    8398#if _MSC_VER >= 1400 
     
    192207#endif   /* ifdef VPR_OS_Windows */ 
    193208 
    194 std::string demangleTraceString(char* traceLine) 
     209std::string demangleTraceString(const std::string& traceLine) 
    195210{ 
    196211#ifdef USE_CXA_DEMANGLE 
     
    198213   // and replace it with a demangled version of the name. 
    199214   // Example: build.linux/stuff/classfile(_ZN4vpr11Someing33methodEv+0xd3) [0x80cfa29] 
     215   // For Mac OS X, things are a little different. A symbol may be of the 
     216   // form _ZN3vpr9ExceptionC2ERKSsS2_:F(0,1), or it may be a simple function 
     217   // name, probably with a leading underscore. 
    200218 
    201219   std::string trace_line(traceLine); 
     
    203221 
    204222   std::string::size_type start(std::string::npos), end(std::string::npos); 
     223 
     224#if defined(VPR_OS_Darwin) 
     225   end = trace_line.find(":F("); 
     226 
     227   // If trace_line does not contain ":F(", then we set the start to be either 
     228   // 0 or 1 depending on whether trace_line starts with "_", and we set end 
     229   // to be the end of the string. 
     230   if ( std::string::npos == end ) 
     231   { 
     232      start = boost::algorithm::starts_with(trace_line, "_") ? 1 : 0; 
     233      end   = trace_line.size(); 
     234   } 
     235   else 
     236   { 
     237      start = 0; 
     238   } 
     239#else 
    205240   start = trace_line.find("(_"); 
    206241   if(std::string::npos != start) 
     
    208243      end = trace_line.find_first_of("+)",start); 
    209244   } 
     245#endif 
    210246 
    211247   if(std::string::npos != end) 
    212248   { 
    213       mangled_name.assign(trace_line, start+1, end-start-1); 
     249      std::string::size_type assign_start, assign_end; 
     250#if defined(VPR_OS_Darwin) 
     251      assign_start = start; 
     252      assign_end   = end; 
     253#else 
     254      assign_start = start + 1; 
     255      assign_end   = end - start - 1; 
     256#endif 
     257      mangled_name.assign(trace_line, assign_start, assign_end); 
     258 
    214259      int status; 
    215260      char* demangled_buf = abi::__cxa_demangle(mangled_name.c_str(), NULL, 
     
    217262      if(0==status) 
    218263      { 
    219          demangled_name = std::string(demangled_buf); 
    220          free(demangled_buf); 
    221  
    222          trace_line.replace(start+1, (end-start-1), demangled_name); 
     264#if defined(VPR_OS_Darwin) 
     265         trace_line = demangled_buf; 
     266#else 
     267         trace_line.replace(start + 1, end - start - 1, demangled_buf); 
     268#endif 
     269         std::free(demangled_buf); 
    223270      } 
    224271      else if(-1==status) 
     
    230277      { 
    231278//         std::cerr << "vpr::SystemBase::demangleTraceString: mangled_name " 
    232 //                   << "is not a valid name under the C++ ABI mangling ' 
     279//                   << "is not a valid name under the C++ ABI mangling " 
    233280//                   << "rules.\n"; 
    234281      } 
     
    248295#endif 
    249296} 
     297 
     298#if defined(VPR_OS_Darwin) 
     299struct CrawlFrame 
     300{ 
     301   unsigned int pc; 
     302   size_t frame; 
     303   std::string name; 
     304   unsigned int offset; 
     305}; 
     306 
     307const mach_header* findOwnerOfPC(const unsigned int pc) 
     308{ 
     309   const unsigned int count = _dyld_image_count(); 
     310   for ( unsigned int index = 0; index < count; ++index ) 
     311   { 
     312      const mach_header* header = _dyld_get_image_header(index); 
     313      const unsigned int offset = _dyld_get_image_vmaddr_slide(index); 
     314      const load_command* cmd = 
     315         reinterpret_cast<const load_command*>( 
     316            reinterpret_cast<const char*>(header) + sizeof(mach_header) 
     317         ); 
     318 
     319      for ( unsigned int cmdex = 0; 
     320            cmdex < header->ncmds; 
     321            ++cmdex, cmd = reinterpret_cast<const load_command*>(reinterpret_cast<const char*>(cmd) + cmd->cmdsize)) 
     322      { 
     323         switch (cmd->cmd) 
     324         { 
     325            case LC_SEGMENT: 
     326               { 
     327                  const segment_command* seg = 
     328                     reinterpret_cast<const segment_command*>(cmd); 
     329                  if ( pc >= (seg->vmaddr + offset) && 
     330                       pc < (seg->vmaddr + offset + seg->vmsize) ) 
     331                  { 
     332                     return header; 
     333                  } 
     334               } 
     335               break; 
     336         } 
     337      } 
     338   } 
     339 
     340   return NULL; 
     341} 
     342 
     343std::string getFunctionName(const unsigned int pc, unsigned int* offset) 
     344{ 
     345   const mach_header* header = findOwnerOfPC(pc); 
     346   if ( header != NULL ) 
     347   { 
     348      const segment_command* seg_linkedit(NULL); 
     349      const segment_command* seg_text(NULL); 
     350      const symtab_command* symtab(NULL); 
     351 
     352      const load_command* cmd = 
     353         reinterpret_cast<const load_command*>( 
     354            reinterpret_cast<const char*>(header) + sizeof(mach_header) 
     355         ); 
     356 
     357      for ( unsigned int index = 0; 
     358            index < header->ncmds; 
     359            ++index, cmd = reinterpret_cast<const load_command*>(reinterpret_cast<const char*>(cmd) + cmd->cmdsize) ) 
     360      { 
     361         switch (cmd->cmd) 
     362         { 
     363            case LC_SEGMENT: 
     364               if ( ! strncmp(reinterpret_cast<const segment_command*>(cmd)->segname, SEG_TEXT, 16) ) 
     365               { 
     366                  seg_text = reinterpret_cast<const segment_command*>(cmd); 
     367               } 
     368               else if ( ! strncmp(reinterpret_cast<const segment_command*>(cmd)->segname, SEG_LINKEDIT, 16) ) 
     369               { 
     370                  seg_linkedit = reinterpret_cast<const segment_command*>(cmd); 
     371               } 
     372               break; 
     373             
     374            case LC_SYMTAB: 
     375               symtab = reinterpret_cast<const symtab_command*>(cmd); 
     376               break; 
     377         } 
     378      } 
     379       
     380      if ( seg_text == NULL || seg_linkedit == NULL || symtab == NULL ) 
     381      { 
     382         *offset = 0; 
     383         return NULL; 
     384      } 
     385 
     386      size_t vm_slide = reinterpret_cast<size_t>(header) - seg_text->vmaddr; 
     387      const size_t file_slide = (seg_linkedit->vmaddr - seg_text->vmaddr) - 
     388                                   seg_linkedit->fileoff; 
     389      struct nlist* symbase = 
     390         reinterpret_cast<struct nlist*>( 
     391            reinterpret_cast<size_t>(header) + symtab->symoff + file_slide 
     392         ); 
     393      char* strings = 
     394         reinterpret_cast<char*>( 
     395            reinterpret_cast<size_t>(header) + symtab->stroff + file_slide 
     396         ); 
     397       
     398      // Look for a global symbol. 
     399      unsigned int index; 
     400      struct nlist* sym(NULL); 
     401      for ( index = 0, sym = symbase; index < symtab->nsyms; ++index, ++sym ) 
     402      { 
     403         if ( sym->n_type != N_FUN ) 
     404         { 
     405            continue; 
     406         } 
     407 
     408         char* name = sym->n_un.n_strx ? (strings + sym->n_un.n_strx) : NULL; 
     409         const unsigned int base = sym->n_value + vm_slide; 
     410 
     411         for ( index += 1, sym += 1; index < symtab->nsyms; ++index, ++sym ) 
     412         { 
     413            if ( sym->n_type == N_FUN ) 
     414            { 
     415               break; 
     416            } 
     417         } 
     418 
     419         if ( pc >= base  &&  pc <= (base + sym->n_value) && name != NULL && 
     420              std::strlen(name) > 0 ) 
     421         { 
     422            *offset = pc - base; 
     423            return std::string(name); 
     424         } 
     425      } 
     426 
     427      // Look for a reasonably close private symbol. 
     428      char* name; 
     429      unsigned int base; 
     430      for ( name = NULL, base = 0xFFFFFFFF, index = 0, sym = symbase; 
     431            index < symtab->nsyms; 
     432            ++index, ++sym ) 
     433      { 
     434         if ( (sym->n_type & 0x0E) != 0x0E ) 
     435         { 
     436            continue; 
     437         } 
     438 
     439         if ( (sym->n_value + vm_slide) > pc ) 
     440         { 
     441            continue; 
     442         } 
     443 
     444         if ( base != 0xFFFFFFFF && 
     445              (pc - (sym->n_value + vm_slide)) >= (pc - base) ) 
     446         { 
     447            continue; 
     448         } 
     449 
     450         name = sym->n_un.n_strx ? (strings + sym->n_un.n_strx) : NULL; 
     451         base = sym->n_value + vm_slide; 
     452      } 
     453 
     454      *offset = pc - base; 
     455      return (name != NULL) ? std::string(name) : std::string(); 
     456   } 
     457    
     458   *offset = 0; 
     459   return std::string(); 
     460} 
     461#endif  /* VPR_OS_Darwin */ 
    250462 
    251463} 
     
    263475   char** strings; 
    264476 
    265    pid_t cur_pid = getpid(); 
    266477   size = backtrace(trace_syms, 100); 
    267478   strings = backtrace_symbols(trace_syms, size); 
    268479 
    269480   std::ostringstream trace_stream; 
    270    trace_stream << "Stack trace: thread: " << cur_pid << std::endl; 
     481   trace_stream << "Stack trace for process: " << getpid() << std::endl; 
    271482 
    272483   for (size_t i = 0; i < size; ++i) 
     
    277488 
    278489   free(strings); 
     490 
     491   ret_stack = trace_stream.str(); 
     492#elif defined(VPR_OS_Darwin) 
     493#if defined(__ppc__) || defined(__ppc64__) 
     494   struct StackFrame 
     495   { 
     496      unsigned int savedSP; 
     497      unsigned int savedCR; 
     498      unsigned int savedLR; 
     499      unsigned int reserved[2]; 
     500      unsigned int savedRTOC; 
     501   }; 
     502#elif defined(__i386__) 
     503   struct StackFrame 
     504   { 
     505      unsigned int savedSP; 
     506      unsigned int savedLR; 
     507   }; 
     508#else 
     509#error Unknown platform 
     510#endif 
     511 
     512   std::ostringstream trace_stream; 
     513   trace_stream << "Stack trace for process: " << getpid() << std::endl; 
     514 
     515   std::vector<CrawlFrame> stack; 
     516   StackFrame* frame(NULL); 
     517 
     518#if defined(__ppc__) || defined(__ppc64__) 
     519   frame = *(StackFrame**) __builtin_frame_address(0); 
     520#elif defined(__i386__) 
     521   frame = (StackFrame*) __builtin_frame_address(0); 
     522#endif 
     523 
     524   for ( ; frame != NULL; frame = (StackFrame*) frame->savedSP) 
     525   { 
     526      if ( (frame->savedLR & ~3) == 0 || (~(frame->savedLR) & ~3) == 0 ) 
     527      { 
     528         break; 
     529      } 
     530 
     531      CrawlFrame cur_frame; 
     532      cur_frame.pc    = frame->savedLR; 
     533      cur_frame.frame = reinterpret_cast<size_t>(frame); 
     534 
     535      const std::string func_name = getFunctionName(frame->savedLR, 
     536                                                    &cur_frame.offset); 
     537 
     538      if ( ! func_name.empty() ) 
     539      { 
     540         cur_frame.name = demangleTraceString(func_name); 
     541      } 
     542 
     543      if ( cur_frame.pc != 0 ) 
     544      { 
     545         cur_frame.pc -= 4; 
     546      } 
     547 
     548      stack.push_back(cur_frame); 
     549   } 
     550 
     551   typedef std::vector<CrawlFrame>::reverse_iterator iter_type; 
     552   for ( iter_type i = stack.rbegin(); i != stack.rend(); ++i ) 
     553   { 
     554      std::string func_name("(unknown)"); 
     555 
     556      if ( ! (*i).name.empty() ) 
     557      { 
     558         typedef std::string::size_type size_type; 
     559         const size_type name_len((*i).name.size()); 
     560         for ( size_type j = 0; j < name_len; ++j ) 
     561         { 
     562            bool objc(false); 
     563            if ( j == 1  &&  (*i).name[j] == '[' ) 
     564            { 
     565               objc = true; 
     566            } 
     567 
     568            if ( objc && (*i).name[j] == ']' ) 
     569            { 
     570               objc = false; 
     571            } 
     572 
     573            if ( ! objc && (*i).name[j] == ':' ) 
     574            { 
     575               break; 
     576            } 
     577         } 
     578 
     579         std::ostringstream func_stream; 
     580         func_stream << (*i).name << "+" << std::hex << (*i).offset; 
     581         func_name = func_stream.str(); 
     582      } 
     583 
     584      const char fill_char(trace_stream.fill()); 
     585      const std::ios::fmtflags fmt_flags(trace_stream.flags()); 
     586      trace_stream.fill('0'); 
     587      trace_stream.setf(std::ios::uppercase); 
     588      trace_stream.setf(std::ios::hex | std::ios::right, 
     589                        std::ios::adjustfield | std::ios::basefield); 
     590      trace_stream << "  " << std::setw(sizeof(void*) * 2) << (*i).frame 
     591                   << "  " << std::setw(sizeof(void*) * 2) << (*i).pc; 
     592      trace_stream.flags(fmt_flags); 
     593      trace_stream.fill(fill_char); 
     594      trace_stream << "  " << func_name << std::endl; 
     595   } 
    279596 
    280597   ret_stack = trace_stream.str();