773 lines
20 KiB
C++
773 lines
20 KiB
C++
/**
|
|
* @file llapp.cpp
|
|
* @brief Implementation of the LLApp class.
|
|
*
|
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "linden_common.h"
|
|
|
|
#include "llapp.h"
|
|
|
|
#include <cstdlib>
|
|
|
|
#ifdef LL_DARWIN
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
|
|
#include "llcommon.h"
|
|
#include "llapr.h"
|
|
#include "llerrorcontrol.h"
|
|
#include "llframetimer.h"
|
|
#include "lllivefile.h"
|
|
#include "llmemory.h"
|
|
#include "llstl.h" // for DeletePointer()
|
|
#include "llstring.h"
|
|
#include "lleventtimer.h"
|
|
#include "stringize.h"
|
|
#include "llcleanup.h"
|
|
#include "llevents.h"
|
|
#include "llsdutil.h"
|
|
|
|
//
|
|
// Signal handling
|
|
#ifndef LL_WINDOWS
|
|
# include <signal.h>
|
|
# include <unistd.h> // for fork()
|
|
void setup_signals();
|
|
void default_unix_signal_handler(int signum, siginfo_t *info, void *);
|
|
|
|
#if LL_LINUX
|
|
#else
|
|
// Called by breakpad exception handler after the minidump has been generated.
|
|
bool unix_post_minidump_callback(const char *dump_dir,
|
|
const char *minidump_id,
|
|
void *context, bool succeeded);
|
|
#endif
|
|
|
|
# if LL_DARWIN
|
|
/* OSX doesn't support SIGRT* */
|
|
S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
|
|
S32 LL_HEARTBEAT_SIGNAL = SIGUSR2;
|
|
# else // linux or (assumed) other similar unixoid
|
|
/* We want reliable delivery of our signals - SIGRT* is it. */
|
|
/* Old LinuxThreads versions eat SIGRTMIN+0 to SIGRTMIN+2, avoid those. */
|
|
/* Note that SIGRTMIN/SIGRTMAX may expand to a glibc function call with a
|
|
nonconstant result so these are not consts and cannot be used in constant-
|
|
expressions. SIGRTMAX may return -1 on rare broken setups. */
|
|
S32 LL_SMACKDOWN_SIGNAL = (SIGRTMAX >= 0) ? (SIGRTMAX-1) : SIGUSR1;
|
|
S32 LL_HEARTBEAT_SIGNAL = (SIGRTMAX >= 0) ? (SIGRTMAX-0) : SIGUSR2;
|
|
# endif // LL_DARWIN
|
|
#endif // !LL_WINDOWS
|
|
|
|
// the static application instance
|
|
LLApp* LLApp::sApplication = NULL;
|
|
|
|
// Allows the generation of core files for post mortem under gdb
|
|
// and disables crashlogger
|
|
bool LLApp::sDisableCrashlogger = false;
|
|
|
|
// static
|
|
// Keeps track of application status
|
|
LLScalarCond<LLApp::EAppStatus> LLApp::sStatus{LLApp::APP_STATUS_STOPPED};
|
|
LLAppErrorHandler LLApp::sErrorHandler = NULL;
|
|
|
|
|
|
LLApp::LLApp()
|
|
{
|
|
// Set our status to running
|
|
setStatus(APP_STATUS_RUNNING);
|
|
|
|
LLCommon::initClass();
|
|
|
|
// initialize the options structure. We need to make this an array
|
|
// because the structured data will not auto-allocate if we
|
|
// reference an invalid location with the [] operator.
|
|
mOptions = LLSD::emptyArray();
|
|
LLSD sd;
|
|
for(int i = 0; i < PRIORITY_COUNT; ++i)
|
|
{
|
|
mOptions.append(sd);
|
|
}
|
|
|
|
// Make sure we clean up APR when we exit
|
|
// Don't need to do this if we're cleaning up APR in the destructor
|
|
//atexit(ll_cleanup_apr);
|
|
|
|
// Set the application to this instance.
|
|
sApplication = this;
|
|
|
|
// initialize the buffer to write the minidump filename to
|
|
// (this is used to avoid allocating memory in the crash handler)
|
|
memset(mMinidumpPath, 0, MAX_MINDUMP_PATH_LENGTH);
|
|
mCrashReportPipeStr = L"\\\\.\\pipe\\LLCrashReporterPipe";
|
|
}
|
|
|
|
|
|
LLApp::~LLApp()
|
|
{
|
|
|
|
// reclaim live file memory
|
|
std::for_each(mLiveFiles.begin(), mLiveFiles.end(), DeletePointer());
|
|
mLiveFiles.clear();
|
|
|
|
setStopped();
|
|
|
|
SUBSYSTEM_CLEANUP_DBG(LLCommon);
|
|
}
|
|
|
|
// static
|
|
LLApp* LLApp::instance()
|
|
{
|
|
return sApplication;
|
|
}
|
|
|
|
|
|
LLSD LLApp::getOption(const std::string& name) const
|
|
{
|
|
LLSD rv;
|
|
LLSD::array_const_iterator iter = mOptions.beginArray();
|
|
LLSD::array_const_iterator end = mOptions.endArray();
|
|
for(; iter != end; ++iter)
|
|
{
|
|
rv = (*iter)[name];
|
|
if(rv.isDefined()) break;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
bool LLApp::parseCommandOptions(int argc, char** argv)
|
|
{
|
|
LLSD commands;
|
|
std::string name;
|
|
std::string value;
|
|
for(int ii = 1; ii < argc; ++ii)
|
|
{
|
|
if(argv[ii][0] != '-')
|
|
{
|
|
LL_INFOS() << "Did not find option identifier while parsing token: "
|
|
<< argv[ii] << LL_ENDL;
|
|
return false;
|
|
}
|
|
int offset = 1;
|
|
if(argv[ii][1] == '-') ++offset;
|
|
name.assign(&argv[ii][offset]);
|
|
if(((ii+1) >= argc) || (argv[ii+1][0] == '-'))
|
|
{
|
|
// we found another option after this one or we have
|
|
// reached the end. simply record that this option was
|
|
// found and continue.
|
|
int flag = name.compare("logfile");
|
|
if (0 == flag)
|
|
{
|
|
commands[name] = "log";
|
|
}
|
|
else
|
|
{
|
|
commands[name] = true;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
++ii;
|
|
value.assign(argv[ii]);
|
|
|
|
#if LL_WINDOWS
|
|
//Windows changed command line parsing. Deal with it.
|
|
size_t slen = value.length() - 1;
|
|
size_t start = 0;
|
|
size_t end = slen;
|
|
if (argv[ii][start]=='"')start++;
|
|
if (argv[ii][end]=='"')end--;
|
|
if (start!=0 || end!=slen)
|
|
{
|
|
value = value.substr (start,end);
|
|
}
|
|
#endif
|
|
|
|
commands[name] = value;
|
|
}
|
|
setOptionData(PRIORITY_COMMAND_LINE, commands);
|
|
return true;
|
|
}
|
|
|
|
bool LLApp::parseCommandOptions(int argc, wchar_t** wargv)
|
|
{
|
|
LLSD commands;
|
|
std::string name;
|
|
std::string value;
|
|
for(int ii = 1; ii < argc; ++ii)
|
|
{
|
|
if(wargv[ii][0] != '-')
|
|
{
|
|
LL_INFOS() << "Did not find option identifier while parsing token: "
|
|
<< (intptr_t)wargv[ii] << LL_ENDL;
|
|
return false;
|
|
}
|
|
int offset = 1;
|
|
if(wargv[ii][1] == '-') ++offset;
|
|
|
|
#if LL_WINDOWS
|
|
name.assign(utf16str_to_utf8str(&wargv[ii][offset]));
|
|
#else
|
|
name.assign(wstring_to_utf8str(&wargv[ii][offset]));
|
|
#endif
|
|
if(((ii+1) >= argc) || (wargv[ii+1][0] == '-'))
|
|
{
|
|
// we found another option after this one or we have
|
|
// reached the end. simply record that this option was
|
|
// found and continue.
|
|
int flag = name.compare("logfile");
|
|
if (0 == flag)
|
|
{
|
|
commands[name] = "log";
|
|
}
|
|
else
|
|
{
|
|
commands[name] = true;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
++ii;
|
|
|
|
#if LL_WINDOWS
|
|
value.assign(utf16str_to_utf8str((wargv[ii])));
|
|
#else
|
|
value.assign(wstring_to_utf8str((wargv[ii])));
|
|
#endif
|
|
|
|
#if LL_WINDOWS
|
|
//Windows changed command line parsing. Deal with it.
|
|
size_t slen = value.length() - 1;
|
|
size_t start = 0;
|
|
size_t end = slen;
|
|
if (wargv[ii][start]=='"')start++;
|
|
if (wargv[ii][end]=='"')end--;
|
|
if (start!=0 || end!=slen)
|
|
{
|
|
value = value.substr (start,end);
|
|
}
|
|
#endif
|
|
|
|
commands[name] = value;
|
|
}
|
|
setOptionData(PRIORITY_COMMAND_LINE, commands);
|
|
return true;
|
|
}
|
|
|
|
void LLApp::manageLiveFile(LLLiveFile* livefile)
|
|
{
|
|
if(!livefile) return;
|
|
livefile->checkAndReload();
|
|
livefile->addToEventTimer();
|
|
mLiveFiles.push_back(livefile);
|
|
}
|
|
|
|
bool LLApp::setOptionData(OptionPriority level, LLSD data)
|
|
{
|
|
if((level < 0)
|
|
|| (level >= PRIORITY_COUNT)
|
|
|| (data.type() != LLSD::TypeMap))
|
|
{
|
|
return false;
|
|
}
|
|
mOptions[level] = data;
|
|
return true;
|
|
}
|
|
|
|
LLSD LLApp::getOptionData(OptionPriority level)
|
|
{
|
|
if((level < 0) || (level >= PRIORITY_COUNT))
|
|
{
|
|
return LLSD();
|
|
}
|
|
return mOptions[level];
|
|
}
|
|
|
|
void LLApp::stepFrame()
|
|
{
|
|
LLFrameTimer::updateFrameTime();
|
|
LLFrameTimer::updateFrameCount();
|
|
LLEventTimer::updateClass();
|
|
mRunner.run();
|
|
}
|
|
|
|
void LLApp::setupErrorHandling(bool second_instance)
|
|
{
|
|
// Error handling is done by starting up an error handling thread, which just sleeps and
|
|
// occasionally checks to see if the app is in an error state, and sees if it needs to be run.
|
|
|
|
#if LL_WINDOWS
|
|
|
|
#else // ! LL_WINDOWS
|
|
|
|
#if ! defined(LL_BUGSPLAT)
|
|
//
|
|
// Start up signal handling.
|
|
//
|
|
// There are two different classes of signals. Synchronous signals are delivered to a specific
|
|
// thread, asynchronous signals can be delivered to any thread (in theory)
|
|
//
|
|
setup_signals();
|
|
#endif // ! LL_BUGSPLAT
|
|
|
|
#endif // ! LL_WINDOWS
|
|
}
|
|
|
|
void LLApp::setErrorHandler(LLAppErrorHandler handler)
|
|
{
|
|
LLApp::sErrorHandler = handler;
|
|
}
|
|
|
|
// static
|
|
void LLApp::runErrorHandler()
|
|
{
|
|
if (LLApp::sErrorHandler)
|
|
{
|
|
LLApp::sErrorHandler();
|
|
}
|
|
|
|
//LL_INFOS() << "App status now STOPPED" << LL_ENDL;
|
|
LLApp::setStopped();
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
static std::map<LLApp::EAppStatus, const char*> statusDesc
|
|
{
|
|
{ LLApp::APP_STATUS_RUNNING, "running" },
|
|
{ LLApp::APP_STATUS_QUITTING, "quitting" },
|
|
{ LLApp::APP_STATUS_STOPPED, "stopped" },
|
|
{ LLApp::APP_STATUS_ERROR, "error" }
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
// static
|
|
void LLApp::setStatus(EAppStatus status)
|
|
{
|
|
auto status_it = statusDesc.find(status);
|
|
std::string status_text = status_it != statusDesc.end() ? std::string(status_it->second) : std::to_string(status);
|
|
LL_INFOS() << "status: " << status_text << LL_ENDL;
|
|
// notify everyone waiting on sStatus any time its value changes
|
|
sStatus.set_all(status);
|
|
|
|
// This can also happen very late in the application lifecycle -- don't
|
|
// resurrect a deleted LLSingleton
|
|
if (! LLEventPumps::wasDeleted())
|
|
{
|
|
// notify interested parties of status change
|
|
LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", status_text));
|
|
}
|
|
}
|
|
|
|
|
|
// static
|
|
void LLApp::setError()
|
|
{
|
|
// set app status to ERROR
|
|
setStatus(APP_STATUS_ERROR);
|
|
}
|
|
|
|
// static
|
|
bool LLApp::sleep(F32Milliseconds duration)
|
|
{
|
|
return ! sStatus.wait_for_unequal(duration, APP_STATUS_RUNNING);
|
|
}
|
|
|
|
void LLApp::setDebugFileNames(const std::string &path)
|
|
{
|
|
mStaticDebugFileName = path + "static_debug_info.log";
|
|
mDynamicDebugFileName = path + "dynamic_debug_info.log";
|
|
}
|
|
|
|
void LLApp::writeMiniDump()
|
|
{
|
|
}
|
|
|
|
// static
|
|
void LLApp::setQuitting()
|
|
{
|
|
if (!isExiting())
|
|
{
|
|
// If we're already exiting, we don't want to reset our state back to quitting.
|
|
LL_INFOS() << "Setting app state to QUITTING" << LL_ENDL;
|
|
setStatus(APP_STATUS_QUITTING);
|
|
}
|
|
}
|
|
|
|
|
|
// static
|
|
void LLApp::setStopped()
|
|
{
|
|
setStatus(APP_STATUS_STOPPED);
|
|
}
|
|
|
|
|
|
// static
|
|
bool LLApp::isStopped()
|
|
{
|
|
return (APP_STATUS_STOPPED == sStatus.get());
|
|
}
|
|
|
|
|
|
// static
|
|
bool LLApp::isRunning()
|
|
{
|
|
return (APP_STATUS_RUNNING == sStatus.get());
|
|
}
|
|
|
|
|
|
// static
|
|
bool LLApp::isError()
|
|
{
|
|
return (APP_STATUS_ERROR == sStatus.get());
|
|
}
|
|
|
|
|
|
// static
|
|
bool LLApp::isQuitting()
|
|
{
|
|
return (APP_STATUS_QUITTING == sStatus.get());
|
|
}
|
|
|
|
// static
|
|
bool LLApp::isExiting()
|
|
{
|
|
return isQuitting() || isError();
|
|
}
|
|
|
|
void LLApp::disableCrashlogger()
|
|
{
|
|
sDisableCrashlogger = true;
|
|
}
|
|
|
|
// static
|
|
bool LLApp::isCrashloggerDisabled()
|
|
{
|
|
return sDisableCrashlogger;
|
|
}
|
|
|
|
// static
|
|
int LLApp::getPid()
|
|
{
|
|
#if LL_WINDOWS
|
|
return GetCurrentProcessId();
|
|
#else
|
|
return getpid();
|
|
#endif
|
|
}
|
|
|
|
// static
|
|
void LLApp::notifyOutOfDiskSpace()
|
|
{
|
|
static const U32Seconds min_interval = U32Seconds(60);
|
|
static U32Seconds min_time_to_send = U32Seconds(0);
|
|
U32Seconds now = LLTimer::getTotalTime();
|
|
if (now < min_time_to_send)
|
|
return;
|
|
|
|
min_time_to_send = now + min_interval;
|
|
|
|
if (LLApp* app = instance())
|
|
{
|
|
app->sendOutOfDiskSpaceNotification();
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "No app instance" << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
// virtual
|
|
void LLApp::sendOutOfDiskSpaceNotification()
|
|
{
|
|
LL_WARNS() << "Should never be called" << LL_ENDL; // Should be overridden
|
|
}
|
|
|
|
#ifndef LL_WINDOWS
|
|
void setup_signals()
|
|
{
|
|
//
|
|
// Set up signal handlers that may result in program termination
|
|
//
|
|
struct sigaction act;
|
|
act.sa_sigaction = default_unix_signal_handler;
|
|
sigemptyset( &act.sa_mask );
|
|
act.sa_flags = SA_SIGINFO;
|
|
|
|
// Synchronous signals
|
|
# ifndef LL_BUGSPLAT
|
|
sigaction(SIGABRT, &act, NULL);
|
|
# endif
|
|
sigaction(SIGALRM, &act, NULL);
|
|
sigaction(SIGBUS, &act, NULL);
|
|
sigaction(SIGFPE, &act, NULL);
|
|
sigaction(SIGHUP, &act, NULL);
|
|
sigaction(SIGILL, &act, NULL);
|
|
sigaction(SIGPIPE, &act, NULL);
|
|
sigaction(SIGSEGV, &act, NULL);
|
|
sigaction(SIGSYS, &act, NULL);
|
|
|
|
sigaction(LL_HEARTBEAT_SIGNAL, &act, NULL);
|
|
sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
|
|
|
|
// Asynchronous signals that are normally ignored
|
|
#ifndef LL_IGNORE_SIGCHLD
|
|
sigaction(SIGCHLD, &act, NULL);
|
|
#endif // LL_IGNORE_SIGCHLD
|
|
sigaction(SIGUSR2, &act, NULL);
|
|
|
|
// Asynchronous signals that result in attempted graceful exit
|
|
sigaction(SIGHUP, &act, NULL);
|
|
sigaction(SIGTERM, &act, NULL);
|
|
sigaction(SIGINT, &act, NULL);
|
|
|
|
// Asynchronous signals that result in core
|
|
sigaction(SIGQUIT, &act, NULL);
|
|
|
|
}
|
|
|
|
void clear_signals()
|
|
{
|
|
struct sigaction act;
|
|
act.sa_handler = SIG_DFL;
|
|
sigemptyset( &act.sa_mask );
|
|
act.sa_flags = SA_SIGINFO;
|
|
|
|
// Synchronous signals
|
|
# ifndef LL_BUGSPLAT
|
|
sigaction(SIGABRT, &act, NULL);
|
|
# endif
|
|
sigaction(SIGALRM, &act, NULL);
|
|
sigaction(SIGBUS, &act, NULL);
|
|
sigaction(SIGFPE, &act, NULL);
|
|
sigaction(SIGHUP, &act, NULL);
|
|
sigaction(SIGILL, &act, NULL);
|
|
sigaction(SIGPIPE, &act, NULL);
|
|
sigaction(SIGSEGV, &act, NULL);
|
|
sigaction(SIGSYS, &act, NULL);
|
|
|
|
sigaction(LL_HEARTBEAT_SIGNAL, &act, NULL);
|
|
sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
|
|
|
|
// Asynchronous signals that are normally ignored
|
|
#ifndef LL_IGNORE_SIGCHLD
|
|
sigaction(SIGCHLD, &act, NULL);
|
|
#endif // LL_IGNORE_SIGCHLD
|
|
|
|
// Asynchronous signals that result in attempted graceful exit
|
|
sigaction(SIGHUP, &act, NULL);
|
|
sigaction(SIGTERM, &act, NULL);
|
|
sigaction(SIGINT, &act, NULL);
|
|
|
|
// Asynchronous signals that result in core
|
|
sigaction(SIGUSR2, &act, NULL);
|
|
sigaction(SIGQUIT, &act, NULL);
|
|
}
|
|
|
|
|
|
|
|
void default_unix_signal_handler(int signum, siginfo_t *info, void *)
|
|
{
|
|
// Unix implementation of synchronous signal handler
|
|
// This runs in the thread that threw the signal.
|
|
// We do the somewhat sketchy operation of blocking in here until the error handler
|
|
// has gracefully stopped the app.
|
|
|
|
// FIXME(brad) - we are using this handler for asynchronous signals as well, so sLogInSignal is currently
|
|
// disabled for safety. we need to find a way to selectively reenable it when it is safe.
|
|
// see issue secondlife/viewer#2566
|
|
|
|
if (LLApp::sLogInSignal)
|
|
{
|
|
LL_INFOS() << "Signal handler - Got signal " << signum << " - " << apr_signal_description_get(signum) << LL_ENDL;
|
|
}
|
|
|
|
|
|
switch (signum)
|
|
{
|
|
case SIGCHLD:
|
|
case SIGHUP:
|
|
if (LLApp::sLogInSignal)
|
|
{
|
|
LL_INFOS() << "Signal handler - Got SIGCHLD or SIGHUP from " << info->si_pid << LL_ENDL;
|
|
}
|
|
|
|
return;
|
|
case SIGABRT:
|
|
// Note that this handler is not set for SIGABRT when using Bugsplat
|
|
// Abort just results in termination of the app, no funky error handling.
|
|
if (LLApp::sLogInSignal)
|
|
{
|
|
LL_WARNS() << "Signal handler - Got SIGABRT, terminating" << LL_ENDL;
|
|
}
|
|
clear_signals();
|
|
raise(signum);
|
|
return;
|
|
case SIGINT:
|
|
case SIGTERM:
|
|
if (LLApp::sLogInSignal)
|
|
{
|
|
LL_WARNS() << "Signal handler - Got SIGINT, or TERM, exiting gracefully" << LL_ENDL;
|
|
}
|
|
// Graceful exit
|
|
// Just set our state to quitting, not error
|
|
if (LLApp::isQuitting() || LLApp::isError())
|
|
{
|
|
// We're already trying to die, just ignore this signal
|
|
if (LLApp::sLogInSignal)
|
|
{
|
|
LL_INFOS() << "Signal handler - Already trying to quit, ignoring signal!" << LL_ENDL;
|
|
}
|
|
return;
|
|
}
|
|
LLApp::setQuitting();
|
|
return;
|
|
case SIGALRM:
|
|
case SIGPIPE:
|
|
case SIGUSR2:
|
|
default:
|
|
if (signum == LL_SMACKDOWN_SIGNAL ||
|
|
signum == SIGBUS ||
|
|
signum == SIGILL ||
|
|
signum == SIGFPE ||
|
|
signum == SIGSEGV ||
|
|
signum == SIGQUIT)
|
|
{
|
|
if (signum == LL_SMACKDOWN_SIGNAL)
|
|
{
|
|
// Smackdown treated just like any other app termination, for now
|
|
if (LLApp::sLogInSignal)
|
|
{
|
|
LL_WARNS() << "Signal handler - Handling smackdown signal!" << LL_ENDL;
|
|
}
|
|
else
|
|
{
|
|
// Don't log anything, even errors - this is because this signal could happen anywhere.
|
|
LLError::setDefaultLevel(LLError::LEVEL_NONE);
|
|
}
|
|
|
|
// Change the signal that we reraise to SIGABRT, so we generate a core dump.
|
|
signum = SIGABRT;
|
|
}
|
|
|
|
if (LLApp::sLogInSignal)
|
|
{
|
|
LL_WARNS() << "Signal handler - Handling fatal signal!" << LL_ENDL;
|
|
}
|
|
|
|
if (LLApp::isError())
|
|
{
|
|
// Received second fatal signal while handling first, just die right now
|
|
// Set the signal handlers back to default before handling the signal - this makes the next signal wipe out the app.
|
|
clear_signals();
|
|
|
|
if (LLApp::sLogInSignal)
|
|
{
|
|
LL_WARNS() << "Signal handler - Got another fatal signal while in the error handler, die now!" << LL_ENDL;
|
|
}
|
|
raise(signum);
|
|
return;
|
|
}
|
|
|
|
if (LLApp::sLogInSignal)
|
|
{
|
|
LL_WARNS() << "Signal handler - Flagging error status and waiting for shutdown" << LL_ENDL;
|
|
}
|
|
|
|
if (LLApp::isCrashloggerDisabled()) // Don't gracefully handle any signal, crash and core for a gdb post mortem
|
|
{
|
|
clear_signals();
|
|
LL_WARNS() << "Fatal signal received, not handling the crash here, passing back to operating system" << LL_ENDL;
|
|
raise(signum);
|
|
return;
|
|
}
|
|
|
|
// Flag status to ERROR
|
|
LLApp::setError();
|
|
|
|
if (LLApp::sLogInSignal)
|
|
{
|
|
LL_WARNS() << "Signal handler - App is stopped, reraising signal" << LL_ENDL;
|
|
}
|
|
clear_signals();
|
|
raise(signum);
|
|
return;
|
|
}
|
|
|
|
if (LLApp::sLogInSignal)
|
|
{
|
|
LL_INFOS() << "Signal handler - Unhandled signal " << signum << ", ignoring!" << LL_ENDL;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool unix_post_minidump_callback(const char *dump_dir,
|
|
const char *minidump_id,
|
|
void *context, bool succeeded)
|
|
{
|
|
// Copy minidump file path into fixed buffer in the app instance to avoid
|
|
// heap allocations in a crash handler.
|
|
|
|
// path format: <dump_dir>/<minidump_id>.dmp
|
|
auto dirPathLength = strlen(dump_dir);
|
|
auto idLength = strlen(minidump_id);
|
|
|
|
// The path must not be truncated, and we need to have room left for a terminating zero.
|
|
llassert((dirPathLength + idLength + 5) < LLApp::MAX_MINDUMP_PATH_LENGTH); // <FS:Beq/> Buffer overrun avoidance (from Aleric Inglewood)
|
|
|
|
char * path = LLApp::instance()->getMiniDumpFilename();
|
|
auto remaining = LLApp::MAX_MINDUMP_PATH_LENGTH - 1; // <FS:Beq/> Buffer overrun avoidance (from Aleric Inglewood
|
|
strncpy(path, dump_dir, remaining);
|
|
remaining -= dirPathLength;
|
|
path += dirPathLength;
|
|
if (remaining > 0 && dirPathLength > 0 && path[-1] != '/')
|
|
{
|
|
*path++ = '/';
|
|
--remaining;
|
|
}
|
|
if (remaining > 0)
|
|
{
|
|
strncpy(path, minidump_id, remaining);
|
|
remaining -= idLength;
|
|
path += idLength;
|
|
strncpy(path, ".dmp", remaining);
|
|
}
|
|
|
|
LL_INFOS("CRASHREPORT") << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << LL_ENDL;
|
|
LLApp::runErrorHandler();
|
|
|
|
#ifndef LL_RELEASE_FOR_DOWNLOAD
|
|
clear_signals();
|
|
return false;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
#endif // !WINDOWS
|
|
|