summaryrefslogtreecommitdiff
path: root/indra/newview/llappviewer.cpp
diff options
context:
space:
mode:
authorAura Linden <aura@lindenlab.com>2013-12-03 17:06:06 -0800
committerAura Linden <aura@lindenlab.com>2013-12-03 17:06:06 -0800
commit680934812598d2c9116303f3245e7a9d60ff58bf (patch)
treedb084813bcfb23f00bd260c014672f4de784949f /indra/newview/llappviewer.cpp
parent787ccaf297e81291469aaf269f563d862fb150a3 (diff)
Creating a cleaner branch
Diffstat (limited to 'indra/newview/llappviewer.cpp')
-rwxr-xr-xindra/newview/llappviewer.cpp324
1 files changed, 167 insertions, 157 deletions
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 539d186441..eaced99c13 100755
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -689,9 +689,26 @@ LLAppViewer::LLAppViewer() :
llerrs << "Oh no! An instance of LLAppViewer already exists! LLAppViewer is sort of like a singleton." << llendl;
}
- setupErrorHandling();
+ mDumpPath ="";
+
+ // Need to do this initialization before we do anything else, since anything
+ // that touches files should really go through the lldir API
+ gDirUtilp->initAppDirs("SecondLife");
+ //
+ // IMPORTANT! Do NOT put anything that will write
+ // into the log files during normal startup until AFTER
+ // we run the "program crashed last time" error handler below.
+ //
sInstance = this;
+
gLoggedInTime.stop();
+
+ initLoggingAndGetLastDuration();
+
+ processMarkerFiles();
+ //
+ // OK to write stuff to logs now, we've now crash reported if necessary
+ //
LLLoginInstance::instance().setUpdaterService(mUpdater.get());
LLLoginInstance::instance().setPlatformInfo(gPlatform, getOSInfo().getOSVersionString());
@@ -707,7 +724,7 @@ LLAppViewer::~LLAppViewer()
destroyMainloopTimeout();
// If we got to this destructor somehow, the app didn't hang.
- removeMarkerFile();
+ removeMarkerFiles();
}
class LLUITranslationBridge : public LLTranslationBridge
@@ -721,13 +738,11 @@ public:
bool LLAppViewer::init()
{
+ setupErrorHandling();
+
//
// Start of the application
//
- // IMPORTANT! Do NOT put anything that will write
- // into the log files during normal startup until AFTER
- // we run the "program crashed last time" error handler below.
- //
LLFastTimer::reset();
@@ -745,21 +760,15 @@ bool LLAppViewer::init()
//initialize particle index pool
LLVOPartGroup::initClass();
- // Need to do this initialization before we do anything else, since anything
- // that touches files should really go through the lldir API
- gDirUtilp->initAppDirs("SecondLife");
// set skin search path to default, will be overridden later
// this allows simple skinned file lookups to work
gDirUtilp->setSkinFolder("default", "en");
initLoggingAndGetLastDuration();
- processMarkerFiles();
-
//
// OK to write stuff to logs now, we've now crash reported if necessary
//
-
init_default_trans_args();
if (!initConfiguration())
@@ -772,11 +781,13 @@ bool LLAppViewer::init()
LLCoros::instance().setStackSize(gSavedSettings.getS32("CoroutineStackSize"));
LLPrivateMemoryPoolManager::initClass((BOOL)gSavedSettings.getBOOL("MemoryPrivatePoolEnabled"), (U32)gSavedSettings.getU32("MemoryPrivatePoolSize")*1024*1024) ;
-
- // write Google Breakpad minidump files to our log directory
- std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
- logdir += gDirUtilp->getDirDelimiter();
+ // write Google Breakpad minidump files to a per-run dump directory to avoid multiple viewer issues.
+ std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "");
+ mDumpPath = logdir;
setMiniDumpDir(logdir);
+ logdir += gDirUtilp->getDirDelimiter();
+ setDebugFileNames(logdir);
+
// Although initLoggingAndGetLastDuration() is the right place to mess with
// setFatalFunction(), we can't query gSavedSettings until after
@@ -1928,9 +1939,8 @@ bool LLAppViewer::cleanup()
llinfos << "Purging all cache files on exit" << llendflush;
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), "*.*");
}
-
- removeMarkerFile(); // Any crashes from here on we'll just have to ignore
+ removeDumpDir();
writeDebugInfo();
LLLocationHistory::getInstance()->save();
@@ -2086,6 +2096,8 @@ bool LLAppViewer::cleanup()
ll_close_fail_log();
+ removeMarkerFiles();
+
MEM_TRACK_RELEASE
llinfos << "Goodbye!" << llendflush;
@@ -2579,7 +2591,7 @@ bool LLAppViewer::initConfiguration()
}
if (clp.hasOption("logevents")) {
- LLViewerEventRecorder::instance().setEventLoggingOn();
+ LLViewerEventRecorder::instance().setEventLoggingOn();
}
std::string CmdLineChannel(gSavedSettings.getString("CmdLineChannel"));
@@ -2661,8 +2673,8 @@ bool LLAppViewer::initConfiguration()
if(start_slurl.getType() == LLSLURL::LOCATION)
{
LLGridManager::getInstance()->setGridChoice(start_slurl.getGrid());
- }
- }
+ }
+ }
//RN: if we received a URL, hand it off to the existing instance.
// don't call anotherInstanceRunning() when doing URL handoff, as
@@ -2704,38 +2716,6 @@ bool LLAppViewer::initConfiguration()
mYieldTime = gSavedSettings.getS32("YieldTime");
- // Read skin/branding settings if specified.
- //if (! gDirUtilp->getSkinDir().empty() )
- //{
- // std::string skin_def_file = gDirUtilp->findSkinnedFilename("skin.xml");
- // LLXmlTree skin_def_tree;
-
- // if (!skin_def_tree.parseFile(skin_def_file))
- // {
- // llerrs << "Failed to parse skin definition." << llendl;
- // }
-
- //}
-
-#if LL_DARWIN
-
-#if __ppc__
- // If the CPU doesn't have Altivec (i.e. it's not at least a G4), don't go any further.
- // Only test PowerPC - all Intel Macs have SSE.
- if(!gSysCPU.hasAltivec())
- {
- std::ostringstream msg;
- msg << LLTrans::getString("MBRequiresAltiVec");
- OSMessageBox(
- msg.str(),
- LLStringUtil::null,
- OSMB_OK);
- removeMarkerFile();
- return false;
- }
-#endif
-
-#endif // LL_DARWIN
// Display splash screen. Must be after above check for previous
// crash as this dialog is always frontmost.
@@ -2806,10 +2786,6 @@ bool LLAppViewer::initConfiguration()
disable_voice->setValue(LLSD(TRUE), DO_NOT_PERSIST);
}
}
- else
- {
- checkForCrash();
- }
// NextLoginLocation is set from the command line option
std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" );
@@ -3113,19 +3089,6 @@ void LLAppViewer::initUpdater()
updater_pump.listen("notify_update", &notify_update);
}
-void LLAppViewer::checkForCrash(void)
-{
-#if LL_SEND_CRASH_REPORTS
- if (gLastExecEvent == LAST_EXEC_FROZE)
- {
- llinfos << "Last execution froze, sending a crash report." << llendl;
-
- bool report_freeze = true;
- handleCrashReporting(report_freeze);
- }
-#endif // LL_SEND_CRASH_REPORTS
-}
-
//
// This function decides whether the client machine meets the minimum requirements to
// run in a maximized window, per the consensus of davep, boa and nyx on 3/30/2011.
@@ -3252,12 +3215,21 @@ bool LLAppViewer::initWindow()
return true;
}
-void LLAppViewer::writeDebugInfo()
+void LLAppViewer::writeDebugInfo(bool isStatic)
{
- std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
- llinfos << "Opening debug file " << debug_filename << llendl;
- llofstream out_file(debug_filename);
- LLSDSerialize::toPrettyXML(gDebugInfo, out_file);
+ //Try to do the minimum when writing data during a crash.
+ std::string* debug_filename;
+ debug_filename = ( isStatic
+ ? getStaticDebugFile()
+ : getDynamicDebugFile() );
+
+ llinfos << "Opening debug file " << *debug_filename << llendl;
+ llofstream out_file(*debug_filename);
+
+ isStatic ? LLSDSerialize::toPrettyXML(gDebugInfo, out_file)
+ : LLSDSerialize::toPrettyXML(gDebugInfo["Dynamic"], out_file);
+
+
out_file.close();
}
@@ -3309,6 +3281,10 @@ void LLAppViewer::removeCacheFiles(const std::string& file_mask)
void LLAppViewer::writeSystemInfo()
{
+
+ if (! gDebugInfo.has("Dynamic") )
+ gDebugInfo["Dynamic"] = LLSD::emptyMap();
+
gDebugInfo["SLLog"] = LLError::logFileName();
gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel();
@@ -3373,15 +3349,62 @@ void LLAppViewer::writeSystemInfo()
LL_INFOS("SystemInfo") << "OS: " << getOSInfo().getOSStringSimple() << LL_ENDL;
LL_INFOS("SystemInfo") << "OS info: " << getOSInfo() << LL_ENDL;
+ gDebugInfo["SettingsFilename"] = gSavedSettings.getString("ClientSettingsFile");
+ gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName();
+ gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath();
+ gDebugInfo["FirstLogin"] = (LLSD::Boolean) gAgent.isFirstLogin();
+ gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall");
+ gDebugInfo["StartupState"] = LLStartUp::getStartupStateString();
+
writeDebugInfo(); // Save out debug_info.log early, in case of crash.
}
+#ifdef LL_WINDOWS
+//For whatever reason, in Windows when using OOP server for breakpad, the callback to get the
+//name of the dump file is not getting triggered by the breakpad library. Unfortunately they
+//also didn't see fit to provide a simple query request across the pipe to get this name either.
+//Since we are putting our output in a runtime generated directory and we know the header data in
+//the dump format, we can however use the following hack to identify our file.
+//SPATTERS TODO make this a member function.
+void getFileList()
+{
+ std::stringstream filenames;
+
+ typedef std::vector<std::string> vec;
+ std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"");
+ vec file_vec = gDirUtilp->getFilesInDir(pathname);
+ for(vec::const_iterator iter=file_vec.begin(); iter!=file_vec.end(); ++iter)
+ {
+ filenames << *iter << " ";
+ if ( ( iter->length() > 30 ) && (iter->rfind(".log") != (iter->length()-4) ) )
+ {
+ std::string fullname = pathname + *iter;
+ std::ifstream fdat( fullname.c_str(), std::ifstream::binary);
+ if (fdat)
+ {
+ char buf[5];
+ fdat.read(buf,4);
+ fdat.close();
+ if (!strncmp(buf,"MDMP",4))
+ {
+ gDebugInfo["Dynamic"]["MinidumpPath"] = fullname;
+ }
+ }
+ }
+ }
+ filenames << std::endl;
+ gDebugInfo["Dynamic"]["DumpDirContents"] = filenames.str();
+}
+#endif
+
void LLAppViewer::handleViewerCrash()
{
llinfos << "Handle viewer crash entry." << llendl;
llinfos << "Last render pool type: " << LLPipeline::sCurRenderPoolType << llendl ;
+ std::cout << "SPATTERS I am here." << std::endl;
+
LLMemory::logMemoryInfo(true) ;
//print out recorded call stacks if there are any.
@@ -3411,73 +3434,51 @@ void LLAppViewer::handleViewerCrash()
std::string crashHostUrl = gSavedSettings.get<std::string>("CrashHostUrl");
if(crashHostUrl != "")
{
- gDebugInfo["CrashHostUrl"] = crashHostUrl;
+ gDebugInfo["Dynamic"]["CrashHostUrl"] = crashHostUrl;
}
- //We already do this in writeSystemInfo(), but we do it again here to make /sure/ we have a version
- //to check against no matter what
- gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel();
-
- gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor();
- gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor();
- gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch();
- gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild();
-
LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
if ( parcel && parcel->getMusicURL()[0])
{
- gDebugInfo["ParcelMusicURL"] = parcel->getMusicURL();
+ gDebugInfo["Dynamic"]["ParcelMusicURL"] = parcel->getMusicURL();
}
if ( parcel && parcel->getMediaURL()[0])
{
- gDebugInfo["ParcelMediaURL"] = parcel->getMediaURL();
+ gDebugInfo["Dynamic"]["ParcelMediaURL"] = parcel->getMediaURL();
}
- gDebugInfo["SettingsFilename"] = gSavedSettings.getString("ClientSettingsFile");
- gDebugInfo["CAFilename"] = gDirUtilp->getCAFile();
- gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName();
- gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath();
- gDebugInfo["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds());
- gDebugInfo["StartupState"] = LLStartUp::getStartupStateString();
- gDebugInfo["RAMInfo"]["Allocated"] = (LLSD::Integer) LLMemory::getCurrentRSS() >> 10;
- gDebugInfo["FirstLogin"] = (LLSD::Boolean) gAgent.isFirstLogin();
- gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall");
-
- char *minidump_file = pApp->getMiniDumpFilename();
- if(minidump_file && minidump_file[0] != 0)
- {
- gDebugInfo["MinidumpPath"] = minidump_file;
- }
+ gDebugInfo["Dynamic"]["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds());
+ gDebugInfo["Dynamic"]["RAMInfo"]["Allocated"] = (LLSD::Integer) LLMemory::getCurrentRSS() >> 10;
if(gLogoutInProgress)
{
- gDebugInfo["LastExecEvent"] = LAST_EXEC_LOGOUT_CRASH;
+ gDebugInfo["Dynamic"]["LastExecEvent"] = LAST_EXEC_LOGOUT_CRASH;
}
else
{
- gDebugInfo["LastExecEvent"] = gLLErrorActivated ? LAST_EXEC_LLERROR_CRASH : LAST_EXEC_OTHER_CRASH;
+ gDebugInfo["Dynamic"]["LastExecEvent"] = gLLErrorActivated ? LAST_EXEC_LLERROR_CRASH : LAST_EXEC_OTHER_CRASH;
}
if(gAgent.getRegion())
{
- gDebugInfo["CurrentSimHost"] = gAgent.getRegionHost().getHostName();
- gDebugInfo["CurrentRegion"] = gAgent.getRegion()->getName();
+ gDebugInfo["Dynamic"]["CurrentSimHost"] = gAgent.getRegionHost().getHostName();
+ gDebugInfo["Dynamic"]["CurrentRegion"] = gAgent.getRegion()->getName();
const LLVector3& loc = gAgent.getPositionAgent();
- gDebugInfo["CurrentLocationX"] = loc.mV[0];
- gDebugInfo["CurrentLocationY"] = loc.mV[1];
- gDebugInfo["CurrentLocationZ"] = loc.mV[2];
+ gDebugInfo["Dynamic"]["CurrentLocationX"] = loc.mV[0];
+ gDebugInfo["Dynamic"]["CurrentLocationY"] = loc.mV[1];
+ gDebugInfo["Dynamic"]["CurrentLocationZ"] = loc.mV[2];
}
if(LLAppViewer::instance()->mMainloopTimeout)
{
- gDebugInfo["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState();
+ gDebugInfo["Dynamic"]["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState();
}
// The crash is being handled here so set this value to false.
// Otherwise the crash logger will think this crash was a freeze.
- gDebugInfo["CrashNotHandled"] = (LLSD::Boolean)false;
+ gDebugInfo["Dynamic"]["CrashNotHandled"] = (LLSD::Boolean)false;
//Write out the crash status file
//Use marker file style setup, as that's the simplest, especially since
@@ -3505,10 +3506,24 @@ void LLAppViewer::handleViewerCrash()
LL_WARNS("MarkerFile") << "No gDirUtilp with which to create error marker file name" << LL_ENDL;
}
+
+ char *minidump_file = pApp->getMiniDumpFilename();
+
+ std::cout << "SPATTERS minidump name is " << minidump_file << std::endl;
+ if(minidump_file && minidump_file[0] != 0)
+ {
+ gDebugInfo["Dynamic"]["MinidumpPath"] = minidump_file;
+ }
+#ifdef LL_WINDOWS
+ getFileList();
+#endif
+ gDebugInfo["Dynamic"]["SPATTERS1"] = "Where my output?";
+ gDebugInfo["Dynamic"]["CrashType"]="crash";
+
if (gMessageSystem && gDirUtilp)
{
std::string filename;
- filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "stats.log");
+ filename = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "stats.log");
llofstream file(filename, llofstream::binary);
if(file.good())
{
@@ -3524,29 +3539,13 @@ void LLAppViewer::handleViewerCrash()
gMessageSystem->stopLogging();
}
- if (LLWorld::instanceExists()) LLWorld::getInstance()->getInfo(gDebugInfo);
+ if (LLWorld::instanceExists()) LLWorld::getInstance()->getInfo(gDebugInfo["Dynamic"]);
// Close the debug file
- pApp->writeDebugInfo();
+ pApp->writeDebugInfo(false); //false answers the isStatic question with the least overhead.
LLError::logToFile("");
-
- // Remove the marker file, since otherwise we'll spawn a process that'll keep it locked
- if(gDebugInfo["LastExecEvent"].asInteger() == LAST_EXEC_LOGOUT_CRASH)
- {
- pApp->removeMarkerFile(true);
- }
- else
- {
- pApp->removeMarkerFile(false);
- }
-
-#if LL_SEND_CRASH_REPORTS
- // Call to pure virtual, handled by platform specific llappviewer instance.
- pApp->handleCrashReporting();
-#endif
-
- return;
+ pApp->removeMarkerFiles();
}
// static
@@ -3743,33 +3742,30 @@ void LLAppViewer::processMarkerFiles()
}
}
-void LLAppViewer::removeMarkerFile(bool leave_logout_marker)
+void LLAppViewer::removeMarkerFiles()
{
if (!mSecondInstance)
{
- LL_DEBUGS("MarkerFile") << (leave_logout_marker?"leave":"remove") <<" logout" << LL_ENDL;
if (mMarkerFile.getFileHandle())
{
- LL_DEBUGS("MarkerFile") << "removing exec marker '"<<mMarkerFileName<<"'"<< LL_ENDL;
mMarkerFile.close() ;
LLAPRFile::remove( mMarkerFileName );
+ LL_DEBUGS("MarkerFile") << "removed exec marker '"<<mMarkerFileName<<"'"<< LL_ENDL;
}
else
{
LL_WARNS("MarkerFile") << "marker '"<<mMarkerFileName<<"' not open"<< LL_ENDL;
- }
- if (!leave_logout_marker)
+ }
+
+ if (mLogoutMarkerFile.getFileHandle())
{
- if (mLogoutMarkerFile.getFileHandle())
- {
- LL_DEBUGS("MarkerFile") << "removing logout marker '"<<mLogoutMarkerFileName<<"'"<< LL_ENDL;
- mLogoutMarkerFile.close();
- }
- else
- {
- LL_WARNS("MarkerFile") << "logout marker '"<<mLogoutMarkerFileName<<"' not open"<< LL_ENDL;
- }
+ mLogoutMarkerFile.close();
LLAPRFile::remove( mLogoutMarkerFileName );
+ LL_DEBUGS("MarkerFile") << "removed logout marker '"<<mLogoutMarkerFileName<<"'"<< LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("MarkerFile") << "logout marker '"<<mLogoutMarkerFileName<<"' not open"<< LL_ENDL;
}
}
else
@@ -3778,6 +3774,14 @@ void LLAppViewer::removeMarkerFile(bool leave_logout_marker)
}
}
+void LLAppViewer::removeDumpDir()
+{
+ //Call this routine only on clean exit. Crash reporter will clean up
+ //its locking table for us.
+ std::string dump_dir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "");
+ gDirUtilp->deleteDirAndContents(dump_dir);
+}
+
void LLAppViewer::forceQuit()
{
LLApp::setQuitting();
@@ -3795,7 +3799,7 @@ void LLAppViewer::fastQuit(S32 error_code)
// figure out the error code
S32 final_error_code = error_code ? error_code : (S32)isError();
// this isn't a crash
- removeMarkerFile();
+ removeMarkerFiles();
// get outta here
_exit(final_error_code);
}
@@ -4973,20 +4977,26 @@ void LLAppViewer::sendLogoutRequest()
{
//Set internal status variables and marker files before actually starting the logout process
gLogoutInProgress = TRUE;
- mLogoutMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,LOGOUT_MARKER_FILE_NAME);
-
- LLAPRFile outfile ;
- mLogoutMarkerFile.open(mLogoutMarkerFileName, LL_APR_WB);
- if (mLogoutMarkerFile.getFileHandle())
+ if (!mSecondInstance)
{
- LL_INFOS("MarkerFile") << "Created logout marker file '"<< mLogoutMarkerFileName << "' " << LL_ENDL;
- recordMarkerVersion(outfile);
+ mLogoutMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,LOGOUT_MARKER_FILE_NAME);
+
+ mLogoutMarkerFile.open(mLogoutMarkerFileName, LL_APR_WB);
+ if (mLogoutMarkerFile.getFileHandle())
+ {
+ LL_INFOS("MarkerFile") << "Created logout marker file '"<< mLogoutMarkerFileName << "' " << LL_ENDL;
+ recordMarkerVersion(mLogoutMarkerFile);
+ }
+ else
+ {
+ LL_WARNS("MarkerFile") << "Cannot create logout marker file " << mLogoutMarkerFileName << LL_ENDL;
+ }
}
else
{
- LL_WARNS("MarkerFile") << "Cannot create logout marker file " << mLogoutMarkerFileName << LL_ENDL;
- }
-
+ LL_INFOS("MarkerFile") << "Did not logout marker file because this is a second instance" << LL_ENDL;
+ }
+
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_LogoutRequest);
msg->nextBlockFast(_PREHASH_AgentData);
@@ -5464,7 +5474,7 @@ void LLAppViewer::launchUpdater()
LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << " " << LLAppViewer::sUpdaterInfo->mParams.str() << LL_ENDL;
//Explicitly remove the marker file, otherwise we pass the lock onto the child process and things get weird.
- LLAppViewer::instance()->removeMarkerFile(); // In case updater fails
+ LLAppViewer::instance()->removeMarkerFiles(); // In case updater fails
// *NOTE:Mani The updater is spawned as the last thing before the WinMain exit.
// see LLAppViewerWin32.cpp