Skip to content
Snippets Groups Projects
llappviewer.cpp 172 KiB
Newer Older
		.width(gSavedSettings.getU32("WindowWidth"))
		.height(gSavedSettings.getU32("WindowHeight"))
		.min_width(gSavedSettings.getU32("MinWindowWidth"))
		.min_height(gSavedSettings.getU32("MinWindowHeight"))
		.fullscreen(gSavedSettings.getBOOL("FullScreen"))
		.ignore_pixel_depth(ignorePixelDepth)
		.first_run(mIsFirstRun);

	gViewerWindow = new LLViewerWindow(window_params);
	LL_INFOS("AppInit") << "gViewerwindow created." << LL_ENDL;

	// Need to load feature table before cheking to start watchdog.
	bool use_watchdog = false;
	int watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled");
		use_watchdog = !LLFeatureManager::getInstance()->isFeatureAvailable("WatchdogDisabled");
	}
	else
	{
		// The user has explicitly set this setting; always use that value.
		use_watchdog = bool(watchdog_enabled_setting);
	}

	LL_INFOS("AppInit") << "watchdog setting is done." << LL_ENDL;
	LLNotificationsUI::LLNotificationManager::getInstance();
	if (gSavedSettings.getBOOL("WindowMaximized"))
		gViewerWindow->getWindow()->maximize();
		LLFeatureManager::getInstance()->setGraphicsLevel(*mForceGraphicsLevel, false);
		gSavedSettings.setU32("RenderQualityPerformance", *mForceGraphicsLevel);
	// Set this flag in case we crash while initializing GL
	gSavedSettings.setBOOL("RenderInitError", TRUE);
	gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE );
	LL_INFOS("AppInit") << "gPipeline Initialized" << LL_ENDL;

	stop_glerror();
	gViewerWindow->initGLDefaults();
	gSavedSettings.setBOOL("RenderInitError", FALSE);
	gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE );
	//If we have a startup crash, it's usually near GL initialization, so simulate that.
	if(gCrashOnStartup)
	{
		LLAppViewer::instance()->forceErrorLLError();
	}

	//
	// Determine if the window should start maximized on initial run based
	// on graphics capability
	//
	if (gSavedSettings.getBOOL("FirstLoginThisInstall") && meetsRequirementsForMaximizedStart())
	{
		LL_INFOS("AppInit") << "This client met the requirements for a maximized initial screen." << LL_ENDL;
		gSavedSettings.setBOOL("WindowMaximized", TRUE);
	}

	if (gSavedSettings.getBOOL("WindowMaximized"))
	{
		gViewerWindow->getWindow()->maximize();

	// Show watch cursor
	gViewerWindow->setCursor(UI_CURSOR_WAIT);

	// Finish view initialization
	gViewerWindow->initBase();

	// show viewer window
	//gViewerWindow->getWindow()->show();
	LL_INFOS("AppInit") << "Window initialization done." << LL_ENDL;
Aura Linden's avatar
Aura Linden committed
void LLAppViewer::writeDebugInfo(bool isStatic)
Aura Linden's avatar
Aura Linden committed
    //Try to do the minimum when writing data during a crash.
    std::string* debug_filename;
    debug_filename = ( isStatic
        ? getStaticDebugFile()
        : getDynamicDebugFile() );
    LL_INFOS() << "Writing debug file " << *debug_filename << LL_ENDL;
    llofstream out_file(debug_filename->c_str());
Aura Linden's avatar
Aura Linden committed
    isStatic ?  LLSDSerialize::toPrettyXML(gDebugInfo, out_file)
             :  LLSDSerialize::toPrettyXML(gDebugInfo["Dynamic"], out_file);
LLSD LLAppViewer::getViewerInfo() const
{
	// The point of having one method build an LLSD info block and the other
	// construct the user-visible About string is to ensure that the same info
	// is available to a getInfo() caller as to the user opening
	// LLFloaterAbout.
	LLSD info;
	auto& versionInfo(LLVersionInfo::instance());
	info["VIEWER_VERSION"] = LLSDArray(versionInfo.getMajor())(versionInfo.getMinor())(versionInfo.getPatch())(versionInfo.getBuild());
	info["VIEWER_VERSION_STR"] = versionInfo.getVersion();
	info["CHANNEL"] = versionInfo.getChannel();
    info["ADDRESS_SIZE"] = ADDRESS_SIZE;
    std::string build_config = versionInfo.getBuildConfig();
    if (build_config != "Release")
    {
        info["BUILD_CONFIG"] = build_config;
    }

	// return a URL to the release notes for this viewer, such as:
	// https://releasenotes.secondlife.com/viewer/2.1.0.123456.html
	std::string url = versionInfo.getReleaseNotes(); // VVM supplied
    if (url.empty())
    {
        url = LLTrans::getString("RELEASE_NOTES_BASE_URL");
        if (!LLStringUtil::endsWith(url, "/"))
            url += "/";
        url += LLURI::escape(versionInfo.getVersion()) + ".html";
    }
	info["VIEWER_RELEASE_NOTES_URL"] = url;

	// Position
	LLViewerRegion* region = gAgent.getRegion();
	if (region)
	{
// [RLVa:KB] - Checked: 2014-02-24 (RLVa-1.4.10)
		if (RlvActions::canShowLocation())
		{
// [/RLVa:KB]
			LLVector3d pos = gAgent.getPositionGlobal();
			info["POSITION"] = ll_sd_from_vector3d(pos);
			info["POSITION_LOCAL"] = ll_sd_from_vector3(gAgent.getPosAgentFromGlobal(pos));
			info["REGION"] = gAgent.getRegion()->getName();

			boost::regex regex("\\.(secondlife|lindenlab)\\..*");
			info["HOSTNAME"] = boost::regex_replace(gAgent.getRegion()->getHost().getHostName(), regex, "");
			info["SERVER_VERSION"] = gLastVersionChannel;
			LLSLURL slurl;
			LLAgentUI::buildSLURL(slurl);
			info["SLURL"] = slurl.getSLURLString();
// [RLVa:KB] - Checked: 2014-02-24 (RLVa-1.4.10)
		}
		else
		{
			info["REGION"] = RlvStrings::getString(RlvStringKeys::Hidden::Region);
		info["SERVER_VERSION"] = gLastVersionChannel;
Richard Linden's avatar
Richard Linden committed
	info["MEMORY_MB"] = LLSD::Integer(gSysMemory.getPhysicalMemoryKB().valueInUnits<LLUnits::Megabytes>());
	// Moved hack adjustment to Windows memory size into llsys.cpp
	info["OS_VERSION"] = LLOSInfo::instance().getOSString();
	info["GRAPHICS_CARD_VENDOR"] = ll_safe_string((const char*)(glGetString(GL_VENDOR)));
	info["GRAPHICS_CARD"] = ll_safe_string((const char*)(glGetString(GL_RENDERER)));
	std::string drvinfo = gDXHardware.getDriverVersionWMI();
	if (!drvinfo.empty())
		LL_WARNS("DriverVersion")<< "Cannot get driver version from getDriverVersionWMI" << LL_ENDL;
		LLSD driver_info = gDXHardware.getDisplayInfo();
		if (driver_info.has("DriverVersion"))
		{
			info["GRAPHICS_DRIVER_VERSION"] = driver_info["DriverVersion"];
		}
// [RLVa:KB] - Checked: 2010-04-18 (RLVa-1.2.0)
	info["RLV_VERSION"] = (rlv_handler_t::isEnabled()) ? RlvStrings::getVersionAbout() : "(disabled)";
// [/RLVa:KB]
	info["OPENGL_VERSION"] = ll_safe_string((const char*)(glGetString(GL_VERSION)));
    // Settings

    LLRect window_rect = gViewerWindow->getWindowRectRaw();
    info["WINDOW_WIDTH"] = window_rect.getWidth();
    info["WINDOW_HEIGHT"] = window_rect.getHeight();
    info["FONT_SIZE_ADJUSTMENT"] = gSavedSettings.getF32("FontScreenDPI");
    info["UI_SCALE"] = gSavedSettings.getF32("UIScaleFactor");
    info["DRAW_DISTANCE"] = gSavedSettings.getF32("RenderFarClip");
    info["NET_BANDWITH"] = gSavedSettings.getF32("ThrottleBandwidthKBPS");
    info["LOD_FACTOR"] = gSavedSettings.getF32("RenderVolumeLODFactor");
    info["RENDER_QUALITY"] = (F32)gSavedSettings.getU32("RenderQualityPerformance");
    info["GPU_SHADERS"] = gSavedSettings.getBOOL("RenderDeferred") ? "Enabled" : "Disabled";
    info["TEXTURE_MEMORY"] = gSavedSettings.getS32("TextureMemory");

    LLSD substitution;
    substitution["datetime"] = (S32)(gVFS ? gVFS->creationTime() : 0);
    info["VFS_TIME"] = LLTrans::getString("AboutTime", substitution);

	info["J2C_VERSION"] = LLImageJ2C::getEngineInfo();
	bool want_fullname = true;
	info["AUDIO_DRIVER_VERSION"] = gAudiop ? LLSD(gAudiop->getDriverName(want_fullname)) : "Undefined";
	if(LLVoiceClient::getInstance()->voiceEnabled())
	{
		LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion();
		std::ostringstream version_string;
		version_string << version.serverType << " " << version.serverVersion << std::endl;
		info["VOICE_VERSION"] = version_string.str();
	}
	{
		info["VOICE_VERSION"] = LLTrans::getString("NotConnected");
	}

	std::ostringstream cef_ver_codec;
	cef_ver_codec << "Dullahan: ";
	cef_ver_codec << DULLAHAN_VERSION_MAJOR;
	cef_ver_codec << ".";
	cef_ver_codec << DULLAHAN_VERSION_MINOR;
	cef_ver_codec << ".";
	cef_ver_codec << DULLAHAN_VERSION_POINT;
	cef_ver_codec << ".";
	cef_ver_codec << DULLAHAN_VERSION_BUILD;
	cef_ver_codec << CHROME_VERSION_MAJOR;
	cef_ver_codec << ".";
	cef_ver_codec << CHROME_VERSION_MINOR;
	cef_ver_codec << ".";
	cef_ver_codec << CHROME_VERSION_BUILD;
	cef_ver_codec << ".";
	cef_ver_codec << CHROME_VERSION_PATCH;
	info["LIBCEF_VERSION"] = cef_ver_codec.str();
#if defined(LIBVLC_VERSION)
	std::ostringstream vlc_ver_codec;
	vlc_ver_codec << LIBVLC_VERSION_MAJOR;
	vlc_ver_codec << ".";
	vlc_ver_codec << LIBVLC_VERSION_MINOR;
	vlc_ver_codec << ".";
	vlc_ver_codec << LIBVLC_VERSION_REVISION;
	info["LIBVLC_VERSION"] = vlc_ver_codec.str();
callum_linden's avatar
callum_linden committed
	info["LIBVLC_VERSION"] = "Undefined";
	S32 packets_in = LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_IN);
	if (packets_in > 0)
		info["PACKETS_LOST"] = LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_LOST);
		info["PACKETS_IN"] = packets_in;
		info["PACKETS_PCT"] = 100.f*info["PACKETS_LOST"].asReal() / info["PACKETS_IN"].asReal();
	}

	if (mServerReleaseNotesURL.empty())
	{
		if (gAgent.getRegion())
		{
			info["SERVER_RELEASE_NOTES_URL"] = LLTrans::getString("RetrievingData");
		}
		else
		{
			info["SERVER_RELEASE_NOTES_URL"] = LLTrans::getString("NotConnected");
		}
	}
	else if (LLStringUtil::startsWith(mServerReleaseNotesURL, "http")) // it's an URL
	{
		info["SERVER_RELEASE_NOTES_URL"] = "[" + LLWeb::escapeURL(mServerReleaseNotesURL) + " " + LLTrans::getString("ReleaseNotes") + "]";
	}
	else
	{
		info["SERVER_RELEASE_NOTES_URL"] = mServerReleaseNotesURL;
	}

	return info;
}

std::string LLAppViewer::getViewerInfoString(bool default_string) const
{
	std::ostringstream support;

	LLSD info(getViewerInfo());

	// Render the LLSD from getInfo() as a format_map_t
	LLStringUtil::format_map_t args;

	// allow the "Release Notes" URL label to be localized
	args["ReleaseNotes"] = LLTrans::getString("ReleaseNotes", default_string);

	for (LLSD::map_const_iterator ii(info.beginMap()), iend(info.endMap());
		ii != iend; ++ii)
	{
		if (! ii->second.isArray())
		{
			// Scalar value
			if (ii->second.isUndefined())
			{
				args[ii->first] = LLTrans::getString("none_text", default_string);
			}
			else
			{
				// don't forget to render value asString()
				args[ii->first] = ii->second.asString();
			}
		}
		else
		{
			// array value: build KEY_0, KEY_1 etc. entries
			for (LLSD::Integer n(0), size(ii->second.size()); n < size; ++n)
			{
				args[STRINGIZE(ii->first << '_' << n)] = ii->second[n].asString();
			}
		}
	}

	// Now build the various pieces
	support << LLTrans::getString("AboutHeader", args, default_string);
	if (info.has("BUILD_CONFIG"))
	{
		support << "\n" << LLTrans::getString("BuildConfig", args, default_string);
// [RLVa:KB] - Checked: 2014-02-24 (RLVa-1.4.10)
		support << "\n\n" << LLTrans::getString( (RlvActions::canShowLocation()) ? "AboutPosition" : "AboutPositionRLVShowLoc", args, default_string);
// [/RLVa:KB]
//		support << "\n\n" << LLTrans::getString("AboutPosition", args);
	support << "\n\n" << LLTrans::getString("AboutSystem", args, default_string);
	support << "\n";
	if (info.has("GRAPHICS_DRIVER_VERSION"))
	{
		support << "\n" << LLTrans::getString("AboutDriver", args, default_string);
	support << "\n" << LLTrans::getString("AboutOGL", args, default_string);
	support << "\n\n" << LLTrans::getString("AboutSettings", args, default_string);
Rye Mutt's avatar
Rye Mutt committed

	support << "\n\n" << LLTrans::getString("AboutLibs", args, default_string);
		support << "\n" << LLTrans::getString("AboutCompiler", args, default_string);
		support << '\n' << LLTrans::getString("AboutTraffic", args, default_string);

	// SLT timestamp
	LLSD substitution;
	substitution["datetime"] = (S32)time(NULL);//(S32)time_corrected();
	support << "\n" << LLTrans::getString("AboutTime", substitution, default_string);
void LLAppViewer::cleanupSavedSettings()
{
	gSavedSettings.setBOOL("MouseSun", FALSE);

	gSavedSettings.setBOOL("UseEnergy", TRUE);				// force toggle to turn off, since sends message to simulator

	gSavedSettings.setBOOL("DebugWindowProc", gDebugWindowProc);
	gSavedSettings.setBOOL("ShowObjectUpdates", gShowObjectUpdates);
		gSavedSettings.setBOOL("ShowDebugConsole", gDebugView->mDebugConsolep->getVisible());
	// save window position if not maximized
		BOOL maximized = gViewerWindow->getWindow()->getMaximized();
			if (gViewerWindow->getWindow()->getPosition(&window_pos))
			{
				gSavedSettings.setS32("WindowX", window_pos.mX);
				gSavedSettings.setS32("WindowY", window_pos.mY);
			}
	gSavedSettings.setF32("MapScale", LLWorldMapView::sMapScale );
		gSavedSettings.setF32("RenderFarClip", gAgentCamera.mDrawDistance);
void LLAppViewer::removeCacheFiles(const std::string& file_mask)
	gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), file_mask);
Aura Linden's avatar
Aura Linden committed
    if (! gDebugInfo.has("Dynamic") )
        gDebugInfo["Dynamic"] = LLSD::emptyMap();
Aura Linden's avatar
Aura Linden committed
#if LL_WINDOWS
	gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"Alchemy.log");
Aura Linden's avatar
Aura Linden committed
#else
    //Not ideal but sufficient for good reporting.
    gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"Alchemy.old");  //LLError::logFileName();
Aura Linden's avatar
Aura Linden committed
#endif
	gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::instance().getChannel();
	gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::instance().getMajor();
	gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::instance().getMinor();
	gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::instance().getPatch();
	gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::instance().getBuild();
	gDebugInfo["ClientInfo"]["AddressSize"] = LLVersionInfo::instance().getAddressSize();
	gDebugInfo["CAFilename"] = gDirUtilp->getCAFile();

	gDebugInfo["CPUInfo"]["CPUString"] = gSysCPU.getCPUString();
	gDebugInfo["CPUInfo"]["CPUFamily"] = gSysCPU.getFamily();
	gDebugInfo["CPUInfo"]["CPUMhz"] = (S32)gSysCPU.getMHz();
	gDebugInfo["CPUInfo"]["CPUAltivec"] = gSysCPU.hasAltivec();
	gDebugInfo["CPUInfo"]["CPUSSE"] = gSysCPU.hasSSE();
	gDebugInfo["CPUInfo"]["CPUSSE2"] = gSysCPU.hasSSE2();
	gDebugInfo["RAMInfo"]["Physical"] = (LLSD::Integer)(gSysMemory.getPhysicalMemoryKB().value());
	gDebugInfo["RAMInfo"]["Allocated"] = (LLSD::Integer)(gMemoryAllocated.valueInUnits<LLUnits::Kilobytes>());
	gDebugInfo["OSInfo"] = LLOSInfo::instance().getOSStringSimple();
	// The user is not logged on yet, but record the current grid choice login url
	// which may have been the intended grid.
	gDebugInfo["GridName"] = LLGridManager::getInstance()->getGridId();
Tofu Linden's avatar
Tofu Linden committed
	// *FIX:Mani - move this down in llappviewerwin32
#ifdef LL_WINDOWS
	DWORD thread_id = GetCurrentThreadId();
	gDebugInfo["MainloopThreadID"] = (S32)thread_id;
#endif
	// "CrashNotHandled" is set here, while things are running well,
	// in case of a freeze. If there is a freeze, the crash logger will be launched
	// and can read this value from the debug_info.log.
	// If the crash is handled by LLAppViewer::handleViewerCrash, ie not a freeze,
	// then the value of "CrashNotHandled" will be set to true.
	gDebugInfo["CrashNotHandled"] = (LLSD::Boolean)true;

	// Insert crash host url (url to post crash log to) if configured. This insures
	// that the crash report will go to the proper location in the case of a
	// prior freeze.
	std::string crashHostUrl = gSavedSettings.get<std::string>("CrashHostUrl");
	if(crashHostUrl != "")
	{
		gDebugInfo["CrashHostUrl"] = crashHostUrl;
	}
	LL_INFOS("SystemInfo") << "Application: " << LLTrans::getString("APP_NAME") << LL_ENDL;
	LL_INFOS("SystemInfo") << "Version: " << LLVersionInfo::instance().getChannelAndVersion() << LL_ENDL;

	// Dump the local time and time zone
	time_t now;
	time(&now);
	char tbuffer[256];		/* Flawfinder: ignore */
	strftime(tbuffer, 256, "%Y-%m-%dT%H:%M:%S %Z", localtime(&now));
	LL_INFOS("SystemInfo") << "Local time: " << tbuffer << LL_ENDL;
	LL_INFOS("SystemInfo") << "CPU info:\n" << gSysCPU << LL_ENDL;
	LL_INFOS("SystemInfo") << "Memory info:\n" << gSysMemory << LL_ENDL;
	LL_INFOS("SystemInfo") << "OS: " << LLOSInfo::instance().getOSStringSimple() << LL_ENDL;
	LL_INFOS("SystemInfo") << "OS info: " << LLOSInfo::instance() << LL_ENDL;
Aura Linden's avatar
Aura Linden committed
    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.
Aura Linden's avatar
Aura Linden committed
#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
Aura Linden's avatar
Aura Linden committed
//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.
Aura Linden's avatar
Aura Linden committed
// TODO make this a member function.
Aura Linden's avatar
Aura Linden committed
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(".dmp") == (iter->length()-4) ) )
Aura Linden's avatar
Aura Linden committed
		{
			std::string fullname = pathname + *iter;
			llifstream fdat( fullname.c_str(), std::ifstream::binary);
Aura Linden's avatar
Aura Linden committed
			if (fdat)
			{
				char buf[5];
				fdat.read(buf,4);
				fdat.close();
				if (!strncmp(buf,"MDMP",4))
				{
					gDebugInfo["Dynamic"]["MinidumpPath"] = fullname;
Aura Linden's avatar
Aura Linden committed
				}
			}
		}
	}
	filenames << std::endl;
	gDebugInfo["Dynamic"]["DumpDirContents"] = filenames.str();
}
#endif

	LL_INFOS("CRASHREPORT") << "Handle viewer crash entry." << LL_ENDL;
	LL_INFOS("CRASHREPORT") << "Last render pool type: " << LLPipeline::sCurRenderPoolType << LL_ENDL ;
	//print out recorded call stacks if there are any.
Adam Moss's avatar
Adam Moss committed
	LLError::LLCallStacks::print();
	LLAppViewer* pApp = LLAppViewer::instance();
	if (pApp->beingDebugged())
	{
		// This will drop us into the debugger.
		abort();
	}

	// Returns whether a dialog was shown.
	// Only do the logic in here once
	if (pApp->mReportedCrash)
	{
		return;
	}
	pApp->mReportedCrash = TRUE;
	// Insert crash host url (url to post crash log to) if configured.
	std::string crashHostUrl = gSavedSettings.get<std::string>("CrashHostUrl");
	if(crashHostUrl != "")
	{
Aura Linden's avatar
Aura Linden committed
		gDebugInfo["Dynamic"]["CrashHostUrl"] = crashHostUrl;
	LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
	if ( parcel && parcel->getMusicURL()[0])
	{
Aura Linden's avatar
Aura Linden committed
		gDebugInfo["Dynamic"]["ParcelMusicURL"] = parcel->getMusicURL();
	if ( parcel && parcel->getMediaURL()[0])
	{
Aura Linden's avatar
Aura Linden committed
		gDebugInfo["Dynamic"]["ParcelMediaURL"] = parcel->getMediaURL();
Aura Linden's avatar
Aura Linden committed
	gDebugInfo["Dynamic"]["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds());
	gDebugInfo["Dynamic"]["RAMInfo"]["Allocated"] = LLSD::Integer(LLMemory::getCurrentRSS() / 1024);
Aura Linden's avatar
Aura Linden committed
		gDebugInfo["Dynamic"]["LastExecEvent"] = LAST_EXEC_LOGOUT_CRASH;
Aura Linden's avatar
Aura Linden committed
		gDebugInfo["Dynamic"]["LastExecEvent"] = gLLErrorActivated ? LAST_EXEC_LLERROR_CRASH : LAST_EXEC_OTHER_CRASH;
Aura Linden's avatar
Aura Linden committed
		gDebugInfo["Dynamic"]["CurrentSimHost"] = gAgent.getRegionHost().getHostName();
		gDebugInfo["Dynamic"]["CurrentRegion"] = gAgent.getRegion()->getName();
		const LLVector3& loc = gAgent.getPositionAgent();
Aura Linden's avatar
Aura Linden committed
		gDebugInfo["Dynamic"]["CurrentLocationX"] = loc.mV[0];
		gDebugInfo["Dynamic"]["CurrentLocationY"] = loc.mV[1];
		gDebugInfo["Dynamic"]["CurrentLocationZ"] = loc.mV[2];
	if(LLAppViewer::instance()->mMainloopTimeout)
	{
Aura Linden's avatar
Aura Linden committed
		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.
Aura Linden's avatar
Aura Linden committed
	gDebugInfo["Dynamic"]["CrashNotHandled"] = (LLSD::Boolean)false;
	//Write out the crash status file
	//Use marker file style setup, as that's the simplest, especially since
	//we're already in a crash situation
		std::string crash_marker_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
																			gLLErrorActivated
																			? LLERROR_MARKER_FILE_NAME
																			: ERROR_MARKER_FILE_NAME);
		LLAPRFile crash_marker_file ;
		crash_marker_file.open(crash_marker_file_name, LL_APR_WB);
		if (crash_marker_file.getFileHandle())
			LL_INFOS("MarkerFile") << "Created crash marker file " << crash_marker_file_name << LL_ENDL;
			recordMarkerVersion(crash_marker_file);
			LL_WARNS("MarkerFile") << "Cannot create error marker file " << crash_marker_file_name << LL_ENDL;
		}
	else
	{
		LL_WARNS("MarkerFile") << "No gDirUtilp with which to create error marker file name" << LL_ENDL;
Aura Linden's avatar
Aura Linden committed
	Sleep(200);
Aura Linden's avatar
Aura Linden committed

    gDebugInfo["Dynamic"]["CrashType"]="crash";
	if (gMessageSystem && gDirUtilp)
	{
		std::string filename;
Aura Linden's avatar
Aura Linden committed
		filename = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "stats.log");
        LL_DEBUGS("CRASHREPORT") << "recording stats " << filename << LL_ENDL;
		llofstream file(filename.c_str(), std::ios_base::binary);
		if(file.good())
		{
			gMessageSystem->summarizeLogs(file);
        else
        {
            LL_WARNS("CRASHREPORT") << "problem recording stats" << LL_ENDL;
		gMessageSystem->getCircuitInfo(gDebugInfo["CircuitInfo"]);
Aura Linden's avatar
Aura Linden committed
	if (LLWorld::instanceExists()) LLWorld::getInstance()->getInfo(gDebugInfo["Dynamic"]);
Aura Linden's avatar
Aura Linden committed
	pApp->writeDebugInfo(false);  //false answers the isStatic question with the least overhead.
void LLAppViewer::recordMarkerVersion(LLAPRFile& marker_file)
{
	std::string marker_version(LLVersionInfo::instance().getChannelAndVersion());
	if ( marker_version.length() > MAX_MARKER_LENGTH )
	{
		LL_WARNS_ONCE("MarkerFile") << "Version length ("<< marker_version.length()<< ")"
									<< " greater than maximum (" << MAX_MARKER_LENGTH << ")"
									<< ": marker matching may be incorrect"
									<< LL_ENDL;
	}

	// record the viewer version in the marker file
	marker_file.write(marker_version.data(), marker_version.length());
}

bool LLAppViewer::markerIsSameVersion(const std::string& marker_name) const
{
	bool sameVersion = false;

	std::string my_version(LLVersionInfo::instance().getChannelAndVersion());
	char marker_version[MAX_MARKER_LENGTH];
	S32  marker_version_length;

	LLAPRFile marker_file;
	marker_file.open(marker_name, LL_APR_RB);
	if (marker_file.getFileHandle())
	{
		marker_version_length = marker_file.read(marker_version, sizeof(marker_version));
		std::string marker_string(marker_version, marker_version_length);
		if ( 0 == my_version.compare( 0, my_version.length(), marker_version, 0, marker_version_length ) )
		{
			sameVersion = true;
		}
		LL_DEBUGS("MarkerFile") << "Compare markers for '" << marker_name << "': "
								<< "\n   mine '" << my_version    << "'"
								<< "\n marker '" << marker_string << "'"
								<< "\n " << ( sameVersion ? "same" : "different" ) << " version"
								<< LL_ENDL;
	//We've got 4 things to test for here
	// - Other Process Running (SecondLife.exec_marker present, locked)
	// - Freeze (SecondLife.exec_marker present, not locked)
	// - LLError Crash (SecondLife.llerror_marker present)
	// - Other Crash (SecondLife.error_marker present)
	// These checks should also remove these files for the last 2 cases if they currently exist

	bool marker_is_same_version = true;
	// first, look for the marker created at startup and deleted on a clean exit
	mMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,MARKER_FILE_NAME);
	if (LLAPRFile::isExist(mMarkerFileName, NULL, LL_APR_RB))
		// File exists...
		// first, read it to see if it was created by the same version (we need this later)
		marker_is_same_version = markerIsSameVersion(mMarkerFileName);

		// now test to see if this file is locked by a running process (try to open for write)
		LL_DEBUGS("MarkerFile") << "Checking exec marker file for lock..." << LL_ENDL;
		mMarkerFile.open(mMarkerFileName, LL_APR_WB);
		apr_file_t* fMarker = mMarkerFile.getFileHandle() ;
			LL_INFOS("MarkerFile") << "Exec marker file open failed - assume it is locked." << LL_ENDL;
			mSecondInstance = true; // lock means that instance is running.
		}
		else
			// We were able to open it, now try to lock it ourselves...
			if (apr_file_lock(fMarker, APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE) != APR_SUCCESS)
			{
				LL_WARNS_ONCE("MarkerFile") << "Locking exec marker failed." << LL_ENDL;
				mSecondInstance = true; // lost a race? be conservative
			}
			else
			{
				// No other instances; we've locked this file now, so record our version; delete on quit.
				recordMarkerVersion(mMarkerFile);
				LL_DEBUGS("MarkerFile") << "Exec marker file existed but was not locked; rewritten." << LL_ENDL;
			}
		}

		if (mSecondInstance)
		{
			LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' owned by another instance" << LL_ENDL;
		}
		else if (marker_is_same_version)
		{
			// the file existed, is ours, and matched our version, so we can report on what it says
			LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found; last exec FROZE" << LL_ENDL;
		}
		else
		{
			LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found, but versions did not match" << LL_ENDL;
		}
	}
	else // marker did not exist... last exec (if any) did not freeze
	{
		// Create the marker file for this execution & lock it; it will be deleted on a clean exit
		apr_status_t s;
		s = mMarkerFile.open(mMarkerFileName, LL_APR_WB, TRUE);

		if (s == APR_SUCCESS && mMarkerFile.getFileHandle())
		{
			LL_DEBUGS("MarkerFile") << "Exec marker file '"<< mMarkerFileName << "' created." << LL_ENDL;
			if (APR_SUCCESS == apr_file_lock(mMarkerFile.getFileHandle(), APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE))
			{
				recordMarkerVersion(mMarkerFile);
				LL_DEBUGS("MarkerFile") << "Exec marker file locked." << LL_ENDL;
			}
			else
			{
				LL_WARNS("MarkerFile") << "Exec marker file cannot be locked." << LL_ENDL;
			}
		}
		else
		{
			LL_WARNS("MarkerFile") << "Failed to create exec marker file '"<< mMarkerFileName << "'." << LL_ENDL;
		}
	}

	// now check for cases in which the exec marker may have been cleaned up by crash handlers

	// check for any last exec event report based on whether or not it happened during logout
	// (the logout marker is created when logout begins)
	std::string logout_marker_file =  gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOGOUT_MARKER_FILE_NAME);
	if(LLAPRFile::isExist(logout_marker_file, NULL, LL_APR_RB))
		if (markerIsSameVersion(logout_marker_file))
		{
			gLastExecEvent = LAST_EXEC_LOGOUT_FROZE;
			LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "', changing LastExecEvent to LOGOUT_FROZE" << LL_ENDL;
		}
		else
		{
			LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "' found, but versions did not match" << LL_ENDL;
		}
		LLAPRFile::remove(logout_marker_file);
	// further refine based on whether or not a marker created during an llerr crash is found
	std::string llerror_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LLERROR_MARKER_FILE_NAME);
	if(LLAPRFile::isExist(llerror_marker_file, NULL, LL_APR_RB))
		if (markerIsSameVersion(llerror_marker_file))
		{
			if ( gLastExecEvent == LAST_EXEC_LOGOUT_FROZE )
			{
				gLastExecEvent = LAST_EXEC_LOGOUT_CRASH;
				LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' crashed, setting LastExecEvent to LOGOUT_CRASH" << LL_ENDL;
			}
			else
			{
				gLastExecEvent = LAST_EXEC_LLERROR_CRASH;
				LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' crashed, setting LastExecEvent to LLERROR_CRASH" << LL_ENDL;
			}
			LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' found, but versions did not match" << LL_ENDL;
		LLAPRFile::remove(llerror_marker_file);
	// and last refine based on whether or not a marker created during a non-llerr crash is found
	std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME);
	if(LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB))
		if (markerIsSameVersion(error_marker_file))
			if (gLastExecEvent == LAST_EXEC_LOGOUT_FROZE)
			{
				gLastExecEvent = LAST_EXEC_LOGOUT_CRASH;
				LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' crashed, setting LastExecEvent to LOGOUT_CRASH" << LL_ENDL;
			}
			else
			{
				gLastExecEvent = LAST_EXEC_OTHER_CRASH;
				LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL;
			LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' marker found, but versions did not match" << LL_ENDL;
		LLAPRFile::remove(error_marker_file);
Aura Linden's avatar
Aura Linden committed
void LLAppViewer::removeMarkerFiles()
			mMarkerFile.close() ;
			LLAPRFile::remove( mMarkerFileName );
Aura Linden's avatar
Aura Linden committed
			LL_DEBUGS("MarkerFile") << "removed exec marker '"<<mMarkerFileName<<"'"<< LL_ENDL;
			LL_WARNS("MarkerFile") << "marker '"<<mMarkerFileName<<"' not open"<< LL_ENDL;
		if (mLogoutMarkerFile.getFileHandle())
Aura Linden's avatar
Aura Linden committed
			LL_DEBUGS("MarkerFile") << "removed logout marker '"<<mLogoutMarkerFileName<<"'"<< LL_ENDL;
Aura Linden's avatar
Aura Linden committed
			LL_WARNS("MarkerFile") << "logout marker '"<<mLogoutMarkerFileName<<"' not open"<< LL_ENDL;
	}
	else
	{
		LL_WARNS("MarkerFile") << "leaving markers because this is a second instance" << LL_ENDL;
Aura Linden's avatar
Aura Linden committed
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);
}

Richard Linden's avatar
Richard Linden committed
//TODO: remove
void LLAppViewer::fastQuit(S32 error_code)
	// finish pending transfers
	flushVFSIO();
	// let sim know we're logging out
	sendLogoutRequest();
	// flush network buffers by shutting down messaging system
	end_messaging_system();
	// figure out the error code
	S32 final_error_code = error_code ? error_code : (S32)isError();
Aura Linden's avatar
Aura Linden committed
	removeMarkerFiles();
	LL_INFOS() << "requestQuit" << LL_ENDL;
	if( (LLStartUp::getStartupState() < STATE_STARTED) || !region )
	{
		// If we have a region, make some attempt to send a logout request first.
		// This prevents the halfway-logged-in avatar from hanging around inworld for a couple minutes.
		if(region)
		{
			sendLogoutRequest();
		}
	// Try to send metrics back to the grid
	metricsSend(!gDisconnected);

	// Try to send last batch of avatar rez metrics.
	if (!gDisconnected && isAgentAvatarValid())
	{
		gAgentAvatarp->updateAvatarRezMetrics(true); // force a last packet to be sent.
	}
	if (!gSavedSettings.getBOOL("AlchemyDisableEffectSpiral"))
	{
		LLHUDEffectSpiral* effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
		effectp->setPositionGlobal(gAgent.getPositionGlobal());
		effectp->setColor(LLColor4U(gAgent.getEffectColor()));
		LLHUDManager::getInstance()->sendEffects();
		effectp->markDead();//remove it.
	}

	// Attempt to close all floaters that might be
	// editing things.