diff options
author | Steven Bennetts <steve@lindenlab.com> | 2007-11-07 22:55:27 +0000 |
---|---|---|
committer | Steven Bennetts <steve@lindenlab.com> | 2007-11-07 22:55:27 +0000 |
commit | 050dad0ce35207a4ac1562175e853590ad9b7681 (patch) | |
tree | be5dc291d2313112e5733d8c004edfe67da6fc54 /indra/newview/llappviewerlinux.cpp | |
parent | 6fd04521d720a3a4904069d10e8ed970d870ba7f (diff) |
merge svn+ssh://steve@svn/svn/linden/branches/viewer-cleanup-3 -r 73026:73079
Diffstat (limited to 'indra/newview/llappviewerlinux.cpp')
-rw-r--r-- | indra/newview/llappviewerlinux.cpp | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp new file mode 100644 index 0000000000..1993fd0a76 --- /dev/null +++ b/indra/newview/llappviewerlinux.cpp @@ -0,0 +1,414 @@ +/** + * @file llappviewerlinux.cpp + * @brief The LLAppViewerWin32 class definitions + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llmemtype.h" +#include "llappviewerlinux.h" + +#include "llviewernetwork.h" +#include "llmd5.h" + + #if LL_LINUX + # include <dlfcn.h> // RTLD_LAZY + # include <execinfo.h> // backtrace - glibc only + # ifndef LL_ELFBIN + #define LL_ELFBIN 1 + # endif // LL_ELFBIN + # if LL_ELFBIN + # include <cxxabi.h> // for symbol demangling + # include "ELFIO.h" // for better backtraces + # endif // LL_ELFBIN + #elif LL_SOLARIS + # include <sys/types.h> + # include <unistd.h> + # include <fcntl.h> + # include <ucontext.h> + #endif + +int main( int argc, char **argv ) +{ + LLMemType mt1(LLMemType::MTYPE_STARTUP); + +#if LL_SOLARIS && defined(__sparc) + asm ("ta\t6"); // NOTE: Make sure memory alignment is enforced on SPARC +#endif + + LLAppViewer* viewer_app_ptr = new LLAppViewerLinux(); + + viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash); + + bool ok = viewer_app_ptr->tempStoreCommandOptions(argc, argv); + if(!ok) + { + llwarns << "Unable to parse command line." << llendl; + return -1; + } + + ok = viewer_app_ptr->init(); + if(!ok) + { + llwarns << "Application init failed." << llendl; + return -1; + } + + // Run the application main loop + if(!LLApp::isQuitting()) + { + viewer_app_ptr->mainLoop(); + } + + if (!LLApp::isError()) + { + // + // We don't want to do cleanup here if the error handler got called - + // the assumption is that the error handler is responsible for doing + // app cleanup if there was a problem. + // + viewer_app_ptr->cleanup(); + } + delete viewer_app_ptr; + viewer_app_ptr = NULL; + return 0; +} + +#ifdef LL_SOLARIS +static inline BOOL do_basic_glibc_backtrace() +{ + BOOL success = FALSE; + + std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log"); + llinfos << "Opening stack trace file " << strace_filename << llendl; + FILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w"); + if (!StraceFile) + { + llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl; + StraceFile = stderr; + } + + printstack(fileno(StraceFile)); + + if (StraceFile != stderr) + fclose(StraceFile); + + return success; +} +#else +#define MAX_STACK_TRACE_DEPTH 40 +// This uses glibc's basic built-in stack-trace functions for a not very +// amazing backtrace. +static inline BOOL do_basic_glibc_backtrace() +{ + void *array[MAX_STACK_TRACE_DEPTH]; + size_t size; + char **strings; + size_t i; + BOOL success = FALSE; + + size = backtrace(array, MAX_STACK_TRACE_DEPTH); + strings = backtrace_symbols(array, size); + + std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log"); + llinfos << "Opening stack trace file " << strace_filename << llendl; + FILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w"); // Flawfinder: ignore + if (!StraceFile) + { + llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl; + StraceFile = stderr; + } + + if (size) + { + for (i = 0; i < size; i++) + fputs((std::string(strings[i])+"\n").c_str(), + StraceFile); + + success = TRUE; + } + + if (StraceFile != stderr) + fclose(StraceFile); + + free (strings); + return success; +} + +#if LL_ELFBIN +// This uses glibc's basic built-in stack-trace functions together with +// ELFIO's ability to parse the .symtab ELF section for better symbol +// extraction without exporting symbols (which'd cause subtle, fatal bugs). +static inline BOOL do_elfio_glibc_backtrace() +{ + void *array[MAX_STACK_TRACE_DEPTH]; + size_t btsize; + char **strings; + BOOL success = FALSE; + + std::string appfilename = gDirUtilp->getExecutablePathAndName(); + + std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log"); + llinfos << "Opening stack trace file " << strace_filename << llendl; + FILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w"); // Flawfinder: ignore + if (!StraceFile) + { + llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl; + StraceFile = stderr; + } + + // get backtrace address list and basic symbol info + btsize = backtrace(array, MAX_STACK_TRACE_DEPTH); + strings = backtrace_symbols(array, btsize); + + // create ELF reader for our app binary + IELFI* pReader; + const IELFISection* pSec = NULL; + IELFISymbolTable* pSymTbl = 0; + if (ERR_ELFIO_NO_ERROR != ELFIO::GetInstance()->CreateELFI(&pReader) || + ERR_ELFIO_NO_ERROR != pReader->Load(appfilename.c_str()) || + // find symbol table, create reader-object + NULL == (pSec = pReader->GetSection( ".symtab" )) || + ERR_ELFIO_NO_ERROR != pReader->CreateSectionReader(IELFI::ELFI_SYMBOL, pSec, (void**)&pSymTbl) ) + { + // Failed to open our binary and read its symbol table somehow + llinfos << "Could not initialize ELF symbol reading - doing basic backtrace." << llendl; + if (StraceFile != stderr) + fclose(StraceFile); + // note that we may be leaking some of the above ELFIO + // objects now, but it's expected that we'll be dead soon + // and we want to tread delicately until we get *some* kind + // of useful backtrace. + return do_basic_glibc_backtrace(); + } + + // iterate over trace and symtab, looking for plausible symbols + std::string name; + Elf32_Addr value; + Elf32_Word ssize; + unsigned char bind; + unsigned char type; + Elf32_Half section; + int nSymNo = pSymTbl->GetSymbolNum(); + size_t btpos; + for (btpos = 0; btpos < btsize; ++btpos) + { + fprintf(StraceFile, "%d:\t", btpos); + int symidx; + for (symidx = 0; symidx < nSymNo; ++symidx) + { + if (ERR_ELFIO_NO_ERROR == + pSymTbl->GetSymbol(symidx, name, value, ssize, + bind, type, section)) + { + // check if trace address within symbol range + if (uintptr_t(array[btpos]) >= value && + uintptr_t(array[btpos]) < value+ssize) + { + char *demangled_str = NULL; + int demangle_result = 1; + demangled_str = + abi::__cxa_demangle + (name.c_str(), NULL, NULL, + &demangle_result); + if (0 == demangle_result && + NULL != demangled_str) { + fprintf(StraceFile, + "ELF(%s", demangled_str); + free(demangled_str); + } + else // failed demangle; print it raw + { + fprintf(StraceFile, + "ELF(%s", name.c_str()); + } + // print offset from symbol start + fprintf(StraceFile, + "+0x%lx) [%p]\n", + uintptr_t(array[btpos]) - + value, + array[btpos]); + goto got_sym; // early escape + } + } + } + // Fallback: + // Didn't find a suitable symbol in the binary - it's probably + // a symbol in a DSO; use glibc's idea of what it should be. + fprintf(StraceFile, "%s\n", strings[btpos]); + got_sym:; + } + + if (StraceFile != stderr) + fclose(StraceFile); + + pSymTbl->Release(); + pSec->Release(); + pReader->Release(); + + free(strings); + + llinfos << "Finished generating stack trace." << llendl; + + success = TRUE; + return success; +} +#endif // LL_ELFBIN + +#endif // LL_SOLARIS + + +LLAppViewerLinux::LLAppViewerLinux() +{ +} + +LLAppViewerLinux::~LLAppViewerLinux() +{ +} + +bool LLAppViewerLinux::init() +{ + return LLAppViewer::init(); +} + +void LLAppViewerLinux::handleCrashReporting() +{ + + // Always generate the report, have the logger do the asking, and + // don't wait for the logger before exiting (-> total cleanup). + if (CRASH_BEHAVIOR_NEVER_SEND != LLAppViewer::instance()->getCrashBehavior()) + { + // This backtrace writes into stack_trace.log +# if LL_ELFBIN + do_elfio_glibc_backtrace(); // more useful backtrace +# else + do_basic_glibc_backtrace(); // only slightly useful backtrace +# endif // LL_ELFBIN + // launch the actual crash logger + char* ask_dialog = "-dialog"; + if (CRASH_BEHAVIOR_ASK != LLAppViewer::instance()->getCrashBehavior()) + ask_dialog = ""; // omit '-dialog' option + std::string cmd =gDirUtilp->getAppRODataDir(); + cmd += gDirUtilp->getDirDelimiter(); + cmd += "linux-crash-logger.bin"; + char* const cmdargv[] = + {(char*)cmd.c_str(), + ask_dialog, + (char*)"-user", + (char*)gGridName, + (char*)"-name", + (char*)LLAppViewer::instance()->getSecondLifeTitle().c_str(), + NULL}; + pid_t pid = fork(); + if (pid == 0) + { // child + execv(cmd.c_str(), cmdargv); /* Flawfinder: ignore */ + llwarns << "execv failure when trying to start " << cmd << llendl; + _exit(1); // avoid atexit() + } + else + { + if (pid > 0) + { + // DO NOT wait for child proc to die; we want + // the logger to outlive us while we quit to + // free up the screen/keyboard/etc. + ////int childExitStatus; + ////waitpid(pid, &childExitStatus, 0); + } + else + { + llwarns << "fork failure." << llendl; + } + } + } + // Sometimes signals don't seem to quit the viewer. + // Make sure we exit so as to not totally confuse the user. + exit(1); +} + +bool LLAppViewerLinux::beingDebugged() +{ + static enum {unknown, no, yes} debugged = unknown; + + if (debugged == unknown) + { + pid_t ppid = getppid(); + char *name; + int ret; + + ret = asprintf(&name, "/proc/%d/exe", ppid); + if (ret != -1) + { + char buf[1024]; + ssize_t n; + + n = readlink(name, buf, sizeof(buf) - 1); + if (n != -1) + { + char *base = strrchr(buf, '/'); + buf[n + 1] = '\0'; + if (base == NULL) + { + base = buf; + } else { + base += 1; + } + + if (strcmp(base, "gdb") == 0) + { + debugged = yes; + } + } + free(name); + } + } + + return debugged == yes; +} + +bool LLAppViewerLinux::initLogging() +{ + // Remove the last stack trace, if any + std::string old_stack_file = + gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log"); + LLFile::remove(old_stack_file.c_str()); + + return LLAppViewer::initLogging(); +} + +std::string LLAppViewerLinux::generateSerialNumber() +{ + char serial_md5[MD5HEX_STR_SIZE]; + serial_md5[0] = 0; + + // TODO + + return serial_md5; +} |