llappviewerlinux.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 
00034 #include "llappviewerlinux.h"
00035 
00036 #include "llcommandlineparser.h"
00037 
00038 #include "llmemtype.h"
00039 #include "llviewernetwork.h"
00040 #include "llviewercontrol.h"
00041 #include "llmd5.h"
00042 #include "llfindlocale.h"
00043 
00044 #include <exception>
00045 
00046 #if LL_LINUX
00047 # include <dlfcn.h>             // RTLD_LAZY
00048 # include <execinfo.h>            // backtrace - glibc only
00049 # ifndef LL_ELFBIN
00050 #  define LL_ELFBIN 1
00051 # endif // LL_ELFBIN
00052 # if LL_ELFBIN
00053 #  include <cxxabi.h>         // for symbol demangling
00054 #  include "ELFIO.h"          // for better backtraces
00055 # endif // LL_ELFBIN
00056 #elif LL_SOLARIS
00057 # include <sys/types.h>
00058 # include <unistd.h>
00059 # include <fcntl.h>
00060 # include <ucontext.h>
00061 #endif
00062 
00063 namespace
00064 {
00065         int gArgC = 0;
00066         char **gArgV = NULL;
00067         void (*gOldTerminateHandler)() = NULL;
00068 }
00069 
00070 static void exceptionTerminateHandler()
00071 {
00072         // reinstall default terminate() handler in case we re-terminate.
00073         if (gOldTerminateHandler) std::set_terminate(gOldTerminateHandler);
00074         // treat this like a regular viewer crash, with nice stacktrace etc.
00075         LLAppViewer::handleSyncViewerCrash();
00076         LLAppViewer::handleViewerCrash();
00077         // we've probably been killed-off before now, but...
00078         gOldTerminateHandler(); // call old terminate() handler
00079 }
00080 
00081 int main( int argc, char **argv ) 
00082 {
00083         LLMemType mt1(LLMemType::MTYPE_STARTUP);
00084 
00085 #if LL_SOLARIS && defined(__sparc)
00086         asm ("ta\t6");           // NOTE:  Make sure memory alignment is enforced on SPARC
00087 #endif
00088 
00089         gArgC = argc;
00090         gArgV = argv;
00091 
00092         LLAppViewer* viewer_app_ptr = new LLAppViewerLinux();
00093 
00094         // install unexpected exception handler
00095         gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler);
00096         // install crash handlers
00097         viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash);
00098         viewer_app_ptr->setSyncErrorHandler(LLAppViewer::handleSyncViewerCrash);
00099 
00100         bool ok = viewer_app_ptr->init();
00101         if(!ok)
00102         {
00103                 llwarns << "Application init failed." << llendl;
00104                 return -1;
00105         }
00106 
00107                 // Run the application main loop
00108         if(!LLApp::isQuitting()) 
00109         {
00110                 viewer_app_ptr->mainLoop();
00111         }
00112 
00113         if (!LLApp::isError())
00114         {
00115                 //
00116                 // We don't want to do cleanup here if the error handler got called -
00117                 // the assumption is that the error handler is responsible for doing
00118                 // app cleanup if there was a problem.
00119                 //
00120                 viewer_app_ptr->cleanup();
00121         }
00122         delete viewer_app_ptr;
00123         viewer_app_ptr = NULL;
00124         return 0;
00125 }
00126 
00127 #ifdef LL_SOLARIS
00128 static inline BOOL do_basic_glibc_backtrace()
00129 {
00130         BOOL success = FALSE;
00131 
00132         std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
00133         llinfos << "Opening stack trace file " << strace_filename << llendl;
00134         LLFILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w");
00135         if (!StraceFile)
00136         {
00137                 llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
00138                 StraceFile = stderr;
00139         }
00140 
00141         printstack(fileno(StraceFile));
00142 
00143         if (StraceFile != stderr)
00144                 fclose(StraceFile);
00145 
00146         return success;
00147 }
00148 #else
00149 #define MAX_STACK_TRACE_DEPTH 40
00150 // This uses glibc's basic built-in stack-trace functions for a not very
00151 // amazing backtrace.
00152 static inline BOOL do_basic_glibc_backtrace()
00153 {
00154         void *array[MAX_STACK_TRACE_DEPTH];
00155         size_t size;
00156         char **strings;
00157         size_t i;
00158         BOOL success = FALSE;
00159 
00160         size = backtrace(array, MAX_STACK_TRACE_DEPTH);
00161         strings = backtrace_symbols(array, size);
00162 
00163         std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
00164         llinfos << "Opening stack trace file " << strace_filename << llendl;
00165         LLFILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w");               // Flawfinder: ignore
00166         if (!StraceFile)
00167         {
00168                 llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
00169                 StraceFile = stderr;
00170         }
00171 
00172         if (size)
00173         {
00174                 for (i = 0; i < size; i++)
00175                         fputs((std::string(strings[i])+"\n").c_str(),
00176                               StraceFile);
00177 
00178                 success = TRUE;
00179         }
00180         
00181         if (StraceFile != stderr)
00182                 fclose(StraceFile);
00183 
00184         free (strings);
00185         return success;
00186 }
00187 
00188 #if LL_ELFBIN
00189 // This uses glibc's basic built-in stack-trace functions together with
00190 // ELFIO's ability to parse the .symtab ELF section for better symbol
00191 // extraction without exporting symbols (which'd cause subtle, fatal bugs).
00192 static inline BOOL do_elfio_glibc_backtrace()
00193 {
00194         void *array[MAX_STACK_TRACE_DEPTH];
00195         size_t btsize;
00196         char **strings;
00197         BOOL success = FALSE;
00198 
00199         std::string appfilename = gDirUtilp->getExecutablePathAndName();
00200 
00201         std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
00202         llinfos << "Opening stack trace file " << strace_filename << llendl;
00203         LLFILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w");               // Flawfinder: ignore
00204         if (!StraceFile)
00205         {
00206                 llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
00207                 StraceFile = stderr;
00208         }
00209 
00210         // get backtrace address list and basic symbol info
00211         btsize = backtrace(array, MAX_STACK_TRACE_DEPTH);
00212         strings = backtrace_symbols(array, btsize);
00213 
00214         // create ELF reader for our app binary
00215         IELFI* pReader;
00216         const IELFISection* pSec = NULL;
00217         IELFISymbolTable* pSymTbl = 0;
00218         if (ERR_ELFIO_NO_ERROR != ELFIO::GetInstance()->CreateELFI(&pReader) ||
00219             ERR_ELFIO_NO_ERROR != pReader->Load(appfilename.c_str()) ||
00220             // find symbol table, create reader-object
00221             NULL == (pSec = pReader->GetSection( ".symtab" )) ||
00222             ERR_ELFIO_NO_ERROR != pReader->CreateSectionReader(IELFI::ELFI_SYMBOL, pSec, (void**)&pSymTbl) )
00223         {
00224                 // Failed to open our binary and read its symbol table somehow
00225                 llinfos << "Could not initialize ELF symbol reading - doing basic backtrace." << llendl;
00226                 if (StraceFile != stderr)
00227                         fclose(StraceFile);
00228                 // note that we may be leaking some of the above ELFIO
00229                 // objects now, but it's expected that we'll be dead soon
00230                 // and we want to tread delicately until we get *some* kind
00231                 // of useful backtrace.
00232                 return do_basic_glibc_backtrace();
00233         }
00234 
00235         // iterate over trace and symtab, looking for plausible symbols
00236         std::string   name;
00237         Elf32_Addr    value;
00238         Elf32_Word    ssize;
00239         unsigned char bind;
00240         unsigned char type;
00241         Elf32_Half    section;
00242         int nSymNo = pSymTbl->GetSymbolNum();
00243         size_t btpos;
00244         for (btpos = 0; btpos < btsize; ++btpos)
00245         {
00246                 fprintf(StraceFile, "%d:\t", btpos);
00247                 int symidx;
00248                 for (symidx = 0; symidx < nSymNo; ++symidx)
00249                 {
00250                         if (ERR_ELFIO_NO_ERROR ==
00251                             pSymTbl->GetSymbol(symidx, name, value, ssize,
00252                                                bind, type, section))
00253                         {
00254                                 // check if trace address within symbol range
00255                                 if (uintptr_t(array[btpos]) >= value &&
00256                                     uintptr_t(array[btpos]) < value+ssize)
00257                                 {
00258                                         char *demangled_str = NULL;
00259                                         int demangle_result = 1;
00260                                         demangled_str =
00261                                                 abi::__cxa_demangle
00262                                                 (name.c_str(), NULL, NULL,
00263                                                  &demangle_result);
00264                                         if (0 == demangle_result &&
00265                                             NULL != demangled_str) {
00266                                                 fprintf(StraceFile,
00267                                                         "ELF(%s", demangled_str);
00268                                                 free(demangled_str);
00269                                         }
00270                                         else // failed demangle; print it raw
00271                                         {
00272                                                 fprintf(StraceFile,
00273                                                         "ELF(%s", name.c_str());
00274                                         }
00275                                         // print offset from symbol start
00276                                         fprintf(StraceFile,
00277                                                 "+0x%lx) [%p]\n",
00278                                                 uintptr_t(array[btpos]) -
00279                                                 value,
00280                                                 array[btpos]);
00281                                         goto got_sym; // early escape
00282                                 }
00283                         }
00284                 }
00285                 // Fallback:
00286                 // Didn't find a suitable symbol in the binary - it's probably
00287                 // a symbol in a DSO; use glibc's idea of what it should be.
00288                 fprintf(StraceFile, "%s\n", strings[btpos]);
00289         got_sym:;
00290         }
00291         
00292         if (StraceFile != stderr)
00293                 fclose(StraceFile);
00294 
00295         pSymTbl->Release();
00296         pSec->Release();
00297         pReader->Release();
00298 
00299         free(strings);
00300 
00301         llinfos << "Finished generating stack trace." << llendl;
00302 
00303         success = TRUE;
00304         return success;
00305 }
00306 #endif // LL_ELFBIN
00307 
00308 #endif // LL_SOLARIS
00309 
00310 
00311 LLAppViewerLinux::LLAppViewerLinux()
00312 {
00313 }
00314 
00315 LLAppViewerLinux::~LLAppViewerLinux()
00316 {
00317 }
00318 
00319 bool LLAppViewerLinux::init()
00320 {
00321         return LLAppViewer::init();
00322 }
00323 
00324 void LLAppViewerLinux::handleSyncCrashTrace()
00325 {
00326         // This backtrace writes into stack_trace.log
00327 #  if LL_ELFBIN
00328         do_elfio_glibc_backtrace(); // more useful backtrace
00329 #  else
00330         do_basic_glibc_backtrace(); // only slightly useful backtrace
00331 #  endif // LL_ELFBIN
00332 }
00333 
00334 void LLAppViewerLinux::handleCrashReporting()
00335 {
00336         // Always generate the report, have the logger do the asking, and
00337         // don't wait for the logger before exiting (-> total cleanup).
00338         if (CRASH_BEHAVIOR_NEVER_SEND != LLAppViewer::instance()->getCrashBehavior())
00339         {       
00340                 // launch the actual crash logger
00341                 char* ask_dialog = "-dialog";
00342                 if (CRASH_BEHAVIOR_ASK != LLAppViewer::instance()->getCrashBehavior())
00343                         ask_dialog = ""; // omit '-dialog' option
00344                 std::string cmd =gDirUtilp->getAppRODataDir();
00345                 cmd += gDirUtilp->getDirDelimiter();
00346                 cmd += "linux-crash-logger.bin";
00347                 char* const cmdargv[] =
00348                         {(char*)cmd.c_str(),
00349                          ask_dialog,
00350                          (char*)"-user",
00351                          (char*)gGridName.c_str(),
00352                          (char*)"-name",
00353                          (char*)LLAppViewer::instance()->getSecondLifeTitle().c_str(),
00354                          NULL};
00355                 fflush(NULL);
00356                 pid_t pid = fork();
00357                 if (pid == 0)
00358                 { // child
00359                         execv(cmd.c_str(), cmdargv);            /* Flawfinder: ignore */
00360                         llwarns << "execv failure when trying to start " << cmd << llendl;
00361                         _exit(1); // avoid atexit()
00362                 } 
00363                 else
00364                 {
00365                         if (pid > 0)
00366                         {
00367                                 // DO NOT wait for child proc to die; we want
00368                                 // the logger to outlive us while we quit to
00369                                 // free up the screen/keyboard/etc.
00372                         } 
00373                         else
00374                         {
00375                                 llwarns << "fork failure." << llendl;
00376                         }
00377                 }
00378         }
00379         // Sometimes signals don't seem to quit the viewer.  Also, we may
00380         // have been called explicitly instead of from a signal handler.
00381         // Make sure we exit so as to not totally confuse the user.
00382         _exit(1); // avoid atexit(), else we may re-crash in dtors.
00383 }
00384 
00385 bool LLAppViewerLinux::beingDebugged()
00386 {
00387         static enum {unknown, no, yes} debugged = unknown;
00388         
00389         if (debugged == unknown)
00390         {
00391                 pid_t ppid = getppid();
00392                 char *name;
00393                 int ret;
00394 
00395                 ret = asprintf(&name, "/proc/%d/exe", ppid);
00396                 if (ret != -1)
00397                 {
00398                         char buf[1024];
00399                         ssize_t n;
00400                         
00401                         n = readlink(name, buf, sizeof(buf) - 1);
00402                         if (n != -1)
00403                         {
00404                                 char *base = strrchr(buf, '/');
00405                                 buf[n + 1] = '\0';
00406                                 if (base == NULL)
00407                                 {
00408                                         base = buf;
00409                                 } else {
00410                                         base += 1;
00411                                 }
00412                                 
00413                                 if (strcmp(base, "gdb") == 0)
00414                                 {
00415                                         debugged = yes;
00416                                 }
00417                         }
00418                         free(name);
00419                 }
00420         }
00421 
00422         return debugged == yes;
00423 }
00424 
00425 bool LLAppViewerLinux::initLogging()
00426 {
00427         // Remove the last stack trace, if any
00428         std::string old_stack_file =
00429                 gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
00430         LLFile::remove(old_stack_file.c_str());
00431 
00432         return LLAppViewer::initLogging();
00433 }
00434 
00435 bool LLAppViewerLinux::initParseCommandLine(LLCommandLineParser& clp)
00436 {
00437         if (!clp.parseCommandLine(gArgC, gArgV))
00438         {
00439                 return false;
00440         }
00441 
00442         // Find the system language.
00443         FL_Locale *locale = NULL;
00444         FL_Success success = FL_FindLocale(&locale, FL_MESSAGES);
00445         if (success != 0)
00446         {
00447                 if (success >= 2 && locale->lang) // confident!
00448                 {
00449                         LLControlVariable* c = gSavedSettings.getControl("SystemLanguage");
00450                         if(c)
00451                         {
00452                                 c->setValue(std::string(locale->lang), false);
00453                         }
00454                 }
00455                 FL_FreeLocale(&locale);
00456         }
00457 
00458         return true;
00459 }
00460 
00461 std::string LLAppViewerLinux::generateSerialNumber()
00462 {
00463         char serial_md5[MD5HEX_STR_SIZE];
00464         serial_md5[0] = 0;
00465 
00466         // TODO
00467 
00468         return serial_md5;
00469 }

Generated on Fri May 16 08:33:15 2008 for SecondLife by  doxygen 1.5.5