Back to index

lightning-sunbird  0.9+nobinonly
nsStackFrameUnix.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is nsStackFrameWin.h code, released
00017  * December 20, 2000.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 2003
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsStackFrameUnix.h"
00041 #include <stdlib.h>
00042 #include <string.h>
00043 #include <math.h>
00044 #include "nscore.h"
00045 
00046 // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
00047 // if __USE_GNU is defined.  I suppose its some kind of standards
00048 // adherence thing.
00049 //
00050 #if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
00051 #define __USE_GNU
00052 #endif
00053 
00054 #ifdef HAVE_LIBDL
00055 #include <dlfcn.h>
00056 #endif
00057 
00058 
00059 
00060 // This thing is exported by libstdc++
00061 // Yes, this is a gcc only hack
00062 #if defined(MOZ_DEMANGLE_SYMBOLS)
00063 #include <cxxabi.h>
00064 #include <stdlib.h> // for free()
00065 #endif // MOZ_DEMANGLE_SYMBOLS
00066 
00067 void DemangleSymbol(const char * aSymbol, 
00068                     char * aBuffer,
00069                     int aBufLen)
00070 {
00071     aBuffer[0] = '\0';
00072 
00073 #if defined(MOZ_DEMANGLE_SYMBOLS)
00074     /* See demangle.h in the gcc source for the voodoo */
00075     char * demangled = abi::__cxa_demangle(aSymbol,0,0,0);
00076     
00077     if (demangled)
00078     {
00079         strncpy(aBuffer,demangled,aBufLen);
00080         free(demangled);
00081     }
00082 #endif // MOZ_DEMANGLE_SYMBOLS
00083 }
00084 
00085 
00086 #if defined(linux) && defined(__GNUC__) && (defined(__i386) || defined(PPC) || defined(__x86_64__)) // i386 or PPC Linux stackwalking code
00087 
00088 
00089 void DumpStackToFile(FILE* aStream)
00090 {
00091   // Stack walking code courtesy Kipp's "leaky".
00092 
00093   // Get the frame pointer
00094   void **bp;
00095 #if defined(__i386) 
00096   __asm__( "movl %%ebp, %0" : "=g"(bp));
00097 #elif defined(__x86_64__)
00098   __asm__( "movq %%rbp, %0" : "=g"(bp));
00099 #else
00100   // It would be nice if this worked uniformly, but at least on i386 and
00101   // x86_64, it stopped working with gcc 4.1, because it points to the
00102   // end of the saved registers instead of the start.
00103   bp = (void**) __builtin_frame_address(0);
00104 #endif
00105 
00106   int skip = 2;
00107   for ( ; (void**)*bp > bp; bp = (void**)*bp) {
00108     void *pc = *(bp+1);
00109     if (--skip <= 0) {
00110       Dl_info info;
00111       int ok = dladdr(pc, &info);
00112       if (!ok) {
00113         fprintf(aStream, "UNKNOWN %p\n", pc);
00114         continue;
00115       }
00116 
00117       PRUint32 foff = (char*)pc - (char*)info.dli_fbase;
00118 
00119       const char * symbol = info.dli_sname;
00120       int len;
00121       if (!symbol || !(len = strlen(symbol))) {
00122         fprintf(aStream, "UNKNOWN [%s +0x%08X]\n",
00123                 info.dli_fname, foff);
00124         continue;
00125       }
00126 
00127       char demangled[4096] = "\0";
00128 
00129       DemangleSymbol(symbol, demangled, sizeof(demangled));
00130 
00131       if (strlen(demangled)) {
00132         symbol = demangled;
00133         len = strlen(symbol);
00134       }
00135 
00136       PRUint32 off = (char*)pc - (char*)info.dli_saddr;
00137       fprintf(aStream, "%s+0x%08X [%s +0x%08X]\n",
00138               symbol, off, info.dli_fname, foff);
00139     }
00140   }
00141 }
00142 
00143 #elif defined(__sun) && (defined(__sparc) || defined(sparc) || defined(__i386) || defined(i386))
00144 
00145 /*
00146  * Stack walking code for Solaris courtesy of Bart Smaalder's "memtrak".
00147  */
00148 
00149 #include <synch.h>
00150 #include <ucontext.h>
00151 #include <sys/frame.h>
00152 #include <sys/regset.h>
00153 #include <sys/stack.h>
00154 
00155 static int    load_address ( void * pc, void * arg, FILE * aStream );
00156 static int    write_address_file ( void * pc );
00157 static struct bucket * newbucket ( void * pc );
00158 static struct frame * cs_getmyframeptr ( void );
00159 static void   cs_walk_stack ( void * (*read_func)(char * address),
00160                               struct frame * fp,
00161                               int (*operate_func)(void *, void *),
00162                               void * usrarg, FILE * aStream );
00163 static void   cs_operate ( void (*operate_func)(void *, void *),
00164                            void * usrarg, FILE * aStream );
00165 
00166 #ifndef STACK_BIAS
00167 #define STACK_BIAS 0
00168 #endif /*STACK_BIAS*/
00169 
00170 #define LOGSIZE 4096
00171 
00172 /* type of demangling function */
00173 typedef int demf_t(const char *, char *, size_t);
00174 
00175 static demf_t *demf;
00176 
00177 static int initialized = 0;
00178 
00179 #if defined(sparc) || defined(__sparc)
00180 #define FRAME_PTR_REGISTER REG_SP
00181 #endif
00182 
00183 #if defined(i386) || defined(__i386)
00184 #define FRAME_PTR_REGISTER EBP
00185 #endif
00186 
00187 struct bucket {
00188     void * pc;
00189     int index;
00190     struct bucket * next;
00191 };
00192 
00193 struct mybuf {
00194     char * buffer;
00195     int chars_left;
00196 };
00197 
00198 
00199 static void myinit();
00200 
00201 #pragma init (myinit)
00202 
00203 static void
00204 myinit()
00205 {
00206 
00207     if (! initialized) {
00208 #ifndef __GNUC__
00209         void *handle;
00210         const char *libdem = "libdemangle.so.1";
00211 
00212         /* load libdemangle if we can and need to (only try this once) */
00213         if ((handle = dlopen(libdem, RTLD_LAZY)) != NULL) {
00214             demf = (demf_t *)dlsym(handle,
00215                            "cplus_demangle"); /*lint !e611 */
00216                 /*
00217                  * lint override above is to prevent lint from
00218                  * complaining about "suspicious cast".
00219                  */
00220         }
00221 #endif /*__GNUC__*/
00222     }    
00223     initialized = 1;
00224 }
00225 
00226 
00227 static int
00228 write_address_file(void * pc, FILE* aStream)
00229 {
00230     static struct bucket table[2048];
00231     static mutex_t lock;
00232     struct bucket * ptr;
00233 
00234     unsigned int val = NS_PTR_TO_INT32(pc);
00235 
00236     ptr = table + ((val >> 2)&2047);
00237 
00238     mutex_lock(&lock);
00239     while (ptr->next) {
00240         if (ptr->next->pc == pc)
00241             break;
00242         ptr = ptr->next;
00243     }
00244 
00245     if (ptr->next) {
00246         mutex_unlock(&lock);
00247         return (ptr->next->index);
00248     } else {
00249         char buffer[4096], dembuff[4096];
00250         Dl_info info;
00251         const char *func = "??", *lib = "??";
00252 
00253         ptr->next = newbucket(pc);
00254         mutex_unlock(&lock);
00255  
00256         if (dladdr(pc, & info)) {
00257             if (info.dli_fname)
00258                 lib =  info.dli_fname;
00259             if (info.dli_sname)
00260                 func = info.dli_sname;
00261         }
00262  
00263 #ifdef __GNUC__
00264         DemangleSymbol(func, dembuff, sizeof(dembuff));
00265 #else
00266         if (!demf || demf(func, dembuff, sizeof (dembuff)))
00267             dembuff[0] = 0;
00268 #endif /*__GNUC__*/
00269         if (strlen(dembuff)) {
00270             func = dembuff;
00271         }
00272         fprintf(aStream, "%u %s:%s+0x%x\n",
00273                 ptr->next->index,
00274                 lib,
00275                 func,
00276                 (char *)pc - (char*)info.dli_saddr);
00277  
00278         return (ptr->next->index);
00279     }
00280 }
00281 
00282 
00283 static int
00284 load_address(void * pc, void * arg, FILE * aStream)
00285 {
00286     struct mybuf * buf = (struct mybuf *) arg;
00287 
00288     char name[80];
00289     int len;
00290 
00291     sprintf(name, " %u", write_address_file(pc, aStream));
00292 
00293     len = strlen(name);
00294 
00295     if (len >= buf->chars_left)
00296         return (1);
00297 
00298     strcat(buf->buffer, name);
00299 
00300     buf->chars_left -= len;
00301 
00302     return (0);
00303 }
00304 
00305 
00306 static struct bucket *
00307 newbucket(void * pc)
00308 {
00309     struct bucket * ptr = (struct bucket *) malloc(sizeof (*ptr));
00310     static int index; /* protected by lock in caller */
00311                      
00312     ptr->index = index++;
00313     ptr->next = NULL;
00314     ptr->pc = pc;    
00315     return (ptr);    
00316 }
00317 
00318 
00319 static struct frame *
00320 csgetframeptr()
00321 {
00322     ucontext_t u;
00323     struct frame *fp;
00324 
00325     (void) getcontext(&u);
00326 
00327     fp = (struct frame *)
00328         ((char *)u.uc_mcontext.gregs[FRAME_PTR_REGISTER] +
00329         STACK_BIAS);
00330 
00331     /* make sure to return parents frame pointer.... */
00332 
00333     return ((struct frame *)((ulong_t)fp->fr_savfp + STACK_BIAS));
00334 }
00335 
00336 
00337 static void
00338 cswalkstack(struct frame *fp, int (*operate_func)(void *, void *, FILE *),
00339     void *usrarg, FILE * aStream)
00340 {
00341 
00342     while (fp != 0 && fp->fr_savpc != 0) {
00343 
00344         if (operate_func((void *)fp->fr_savpc, usrarg, aStream) != 0)
00345             break;
00346         /*
00347          * watch out - libthread stacks look funny at the top
00348          * so they may not have their STACK_BIAS set
00349          */
00350 
00351         fp = (struct frame *)((ulong_t)fp->fr_savfp +
00352             (fp->fr_savfp?(ulong_t)STACK_BIAS:0));
00353     }
00354 }
00355 
00356 
00357 static void
00358 cs_operate(int (*operate_func)(void *, void *, FILE *), void * usrarg, FILE *aStream)
00359 {
00360     cswalkstack(csgetframeptr(), operate_func, usrarg, aStream);
00361 }
00362 
00363 void DumpStackToFile(FILE* aStream)
00364 {
00365     char buffer[LOGSIZE];
00366     struct mybuf mybuf;
00367 
00368     if (!initialized)
00369         myinit();
00370 
00371     mybuf.chars_left = LOGSIZE - strlen(buffer)-1;
00372     mybuf.buffer = buffer;
00373     cs_operate(load_address, &mybuf, aStream);
00374 }
00375 #endif