summaryrefslogtreecommitdiff
path: root/indra/llcrashlogger/llcrashlogger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcrashlogger/llcrashlogger.cpp')
-rw-r--r--[-rwxr-xr-x]indra/llcrashlogger/llcrashlogger.cpp285
1 files changed, 185 insertions, 100 deletions
diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp
index c8b5f06b49..68e45f36e4 100755..100644
--- a/indra/llcrashlogger/llcrashlogger.cpp
+++ b/indra/llcrashlogger/llcrashlogger.cpp
@@ -2,30 +2,25 @@
* @file llcrashlogger.cpp
* @brief Crash logger implementation
*
-* $LicenseInfo:firstyear=2003&license=viewergpl$
-*
-* Copyright (c) 2003-2007, Linden Research, Inc.
-*
+* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* 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
+* 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.
*
-* 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
+* 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.
*
-* 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.
+* 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
*
-* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
-* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
-* COMPLETENESS OR PERFORMANCE.
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include <cstdio>
@@ -76,7 +71,7 @@ bool LLCrashLoggerText::mainLoop()
return true;
}
-void LLCrashLoggerText::updateApplication(LLString message)
+void LLCrashLoggerText::updateApplication(const std::string& message)
{
LLCrashLogger::updateApplication(message);
std::cout << message << std::endl;
@@ -85,9 +80,12 @@ void LLCrashLoggerText::updateApplication(LLString message)
LLCrashLogger::LLCrashLogger() :
mCrashBehavior(CRASH_BEHAVIOR_ASK),
mCrashInPreviousExec(false),
- mSentCrashLogs(false)
+ mCrashSettings("CrashSettings"),
+ mSentCrashLogs(false),
+ mCrashHost("")
{
-
+ // Set up generic error handling
+ setupErrorHandling();
}
LLCrashLogger::~LLCrashLogger()
@@ -95,42 +93,86 @@ LLCrashLogger::~LLCrashLogger()
}
-void LLCrashLogger::gatherFiles()
+// TRIM_SIZE must remain larger than LINE_SEARCH_SIZE.
+const int TRIM_SIZE = 128000;
+const int LINE_SEARCH_DIST = 500;
+const std::string SKIP_TEXT = "\n ...Skipping... \n";
+void trimSLLog(std::string& sllog)
{
-
- /*
- //TODO:This function needs to be reimplemented somewhere in here...
- if(!previous_crash && is_crash_log)
+ if(sllog.length() > TRIM_SIZE * 2)
{
- // Make sure the file isn't too old.
- double age = difftime(gLaunchTime, stat_data.st_mtimespec.tv_sec);
-
- // llinfos << "age is " << age << llendl;
-
- if(age > 60.0)
+ std::string::iterator head = sllog.begin() + TRIM_SIZE;
+ std::string::iterator tail = sllog.begin() + sllog.length() - TRIM_SIZE;
+ std::string::iterator new_head = std::find(head, head - LINE_SEARCH_DIST, '\n');
+ if(new_head != head - LINE_SEARCH_DIST)
+ {
+ head = new_head;
+ }
+
+ std::string::iterator new_tail = std::find(tail, tail + LINE_SEARCH_DIST, '\n');
+ if(new_tail != tail + LINE_SEARCH_DIST)
{
- // The file was last modified more than 60 seconds before the crash reporter was launched. Assume it's stale.
- llwarns << "File " << mFilename << " is too old!" << llendl;
- return;
+ tail = new_tail;
}
+
+ sllog.erase(head, tail);
+ sllog.insert(head, SKIP_TEXT.begin(), SKIP_TEXT.end());
+ }
+}
+
+std::string getStartupStateFromLog(std::string& sllog)
+{
+ std::string startup_state = "STATE_FIRST";
+ std::string startup_token = "Startup state changing from ";
+
+ int index = sllog.rfind(startup_token);
+ if (index < 0 || index + startup_token.length() > sllog.length()) {
+ return startup_state;
}
- */
+ // find new line
+ char cur_char = sllog[index + startup_token.length()];
+ std::string::size_type newline_loc = index + startup_token.length();
+ while(cur_char != '\n' && newline_loc < sllog.length())
+ {
+ newline_loc++;
+ cur_char = sllog[newline_loc];
+ }
+
+ // get substring and find location of " to "
+ std::string state_line = sllog.substr(index, newline_loc - index);
+ std::string::size_type state_index = state_line.find(" to ");
+ startup_state = state_line.substr(state_index + 4, state_line.length() - state_index - 4);
+
+ return startup_state;
+}
+
+void LLCrashLogger::gatherFiles()
+{
updateApplication("Gathering logs...");
// Figure out the filename of the debug log
- std::string db_file_name = gDirUtilp->getExpandedFilename(
- LL_PATH_LOGS,
- "debug_info.log");
- llifstream debug_log_file(db_file_name.c_str());
+ std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
+ std::ifstream debug_log_file(db_file_name.c_str());
// Look for it in the debug_info.log file
if (debug_log_file.is_open())
{
LLSDSerialize::fromXML(mDebugLog, debug_log_file);
+
+ mCrashInPreviousExec = mDebugLog["CrashNotHandled"].asBoolean();
+
mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString();
mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString();
- LLCurl::setCAFile(mDebugLog["CAFilename"].asString());
+ if(mDebugLog.has("CAFilename"))
+ {
+ LLCurl::setCAFile(mDebugLog["CAFilename"].asString());
+ }
+ else
+ {
+ LLCurl::setCAFile(gDirUtilp->getCAFile());
+ }
+
llinfos << "Using log file from debug log " << mFileMap["SecondLifeLog"] << llendl;
llinfos << "Using settings file from debug log " << mFileMap["SettingsXml"] << llendl;
}
@@ -142,54 +184,100 @@ void LLCrashLogger::gatherFiles()
mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
}
+ if(mCrashInPreviousExec)
+ {
+ // Restarting after freeze.
+ // Replace the log file ext with .old, since the
+ // instance that launched this process has overwritten
+ // SecondLife.log
+ std::string log_filename = mFileMap["SecondLifeLog"];
+ log_filename.replace(log_filename.size() - 4, 4, ".old");
+ mFileMap["SecondLifeLog"] = log_filename;
+ }
+
gatherPlatformSpecificFiles();
//Use the debug log to reconstruct the URL to send the crash report to
- mCrashHost = "https://";
- mCrashHost += mDebugLog["CurrentSimHost"].asString();
- mCrashHost += ":12043/crash/report";
- // Use login servers as the alternate, since they are already load balanced and have a known name
- // First, check to see if we have a valid grid name. If not, use agni.
- mAltCrashHost = "https://login.";
- if(mDebugLog["GridName"].asString() != "")
+ if(mDebugLog.has("CrashHostUrl"))
{
- mAltCrashHost += mDebugLog["GridName"].asString();
+ // Crash log receiver has been manually configured.
+ mCrashHost = mDebugLog["CrashHostUrl"].asString();
}
- else
+ else if(mDebugLog.has("CurrentSimHost"))
{
- mAltCrashHost += "agni";
+ mCrashHost = "https://";
+ mCrashHost += mDebugLog["CurrentSimHost"].asString();
+ mCrashHost += ":12043/crash/report";
}
- mAltCrashHost += ".lindenlab.com:12043/crash/report";
+ else if(mDebugLog.has("GridName"))
+ {
+ // This is a 'little' hacky, but its the best simple solution.
+ std::string grid_host = mDebugLog["GridName"].asString();
+ LLStringUtil::toLower(grid_host);
+
+ mCrashHost = "https://login.";
+ mCrashHost += grid_host;
+ mCrashHost += ".lindenlab.com:12043/crash/report";
+ }
+
+ // Use login servers as the alternate, since they are already load balanced and have a known name
+ mAltCrashHost = "https://login.agni.lindenlab.com:12043/crash/report";
mCrashInfo["DebugLog"] = mDebugLog;
mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
- mFileMap["StackTrace"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
updateApplication("Encoding files...");
- for(std::map<LLString, LLString>::iterator itr = mFileMap.begin(); itr != mFileMap.end(); ++itr)
+ for(std::map<std::string, std::string>::iterator itr = mFileMap.begin(); itr != mFileMap.end(); ++itr)
{
std::ifstream f((*itr).second.c_str());
if(!f.is_open())
{
- std::cout << "Can't find file " << (*itr).second.c_str() << std::endl;
+ std::cout << "Can't find file " << (*itr).second << std::endl;
continue;
}
std::stringstream s;
s << f.rdbuf();
- mCrashInfo[(*itr).first] = s.str();
+
+ std::string crash_info = s.str();
+ if(itr->first == "SecondLifeLog")
+ {
+ if(!mCrashInfo["DebugLog"].has("StartupState"))
+ {
+ mCrashInfo["DebugLog"]["StartupState"] = getStartupStateFromLog(crash_info);
+ }
+ trimSLLog(crash_info);
+ }
+
+ mCrashInfo[(*itr).first] = LLStringFn::strip_invalid_xml(rawstr_to_utf8(crash_info));
}
+
+ // Add minidump as binary.
+ std::string minidump_path = mDebugLog["MinidumpPath"];
+ if(minidump_path != "")
+ {
+ std::ifstream minidump_stream(minidump_path.c_str(), std::ios_base::in | std::ios_base::binary);
+ if(minidump_stream.is_open())
+ {
+ minidump_stream.seekg(0, std::ios::end);
+ size_t length = minidump_stream.tellg();
+ minidump_stream.seekg(0, std::ios::beg);
+
+ LLSD::Binary data;
+ data.resize(length);
+
+ minidump_stream.read(reinterpret_cast<char *>(&(data[0])),length);
+ minidump_stream.close();
+
+ mCrashInfo["Minidump"] = data;
+ }
+ }
+ mCrashInfo["DebugLog"].erase("MinidumpPath");
}
LLSD LLCrashLogger::constructPostData()
{
LLSD ret;
-
- if(mCrashInPreviousExec)
- {
- mCrashInfo["CrashInPreviousExecution"] = "Y";
- }
-
return mCrashInfo;
}
@@ -218,6 +306,26 @@ bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
return true;
}
+bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout)
+{
+ gBreak = false;
+ std::string status_message;
+ for(int i = 0; i < retries; ++i)
+ {
+ status_message = llformat("%s, try %d...", msg.c_str(), i+1);
+ LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout);
+ while(!gBreak)
+ {
+ updateApplication(status_message);
+ }
+ if(gSent)
+ {
+ return gSent;
+ }
+ }
+ return gSent;
+}
+
bool LLCrashLogger::sendCrashLogs()
{
gatherFiles();
@@ -234,32 +342,26 @@ bool LLCrashLogger::sendCrashLogs()
std::ofstream out_file(report_file.c_str());
LLSDSerialize::toPrettyXML(post_data, out_file);
out_file.close();
- LLHTTPClient::post(mCrashHost, post_data, new LLCrashLoggerResponder(), 5);
- gBreak = false;
- while(!gBreak)
+ bool sent = false;
+
+ //*TODO: Translate
+ if(mCrashHost != "")
{
- updateApplication("Sending logs...");
+ sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3, 5);
}
- if(!gSent)
+ if(!sent)
{
- gBreak = false;
- LLHTTPClient::post(mAltCrashHost, post_data, new LLCrashLoggerResponder(), 5);
-
- while(!gBreak)
- {
- updateApplication("Sending logs to Alternate Server...");
- }
+ sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3, 5);
}
-
- mSentCrashLogs = gSent;
+ mSentCrashLogs = sent;
return true;
}
-void LLCrashLogger::updateApplication(LLString message)
+void LLCrashLogger::updateApplication(const std::string& message)
{
gServicePump->pump();
gServicePump->callback();
@@ -267,6 +369,8 @@ void LLCrashLogger::updateApplication(LLString message)
bool LLCrashLogger::init()
{
+ LLCurl::initClass();
+
// We assume that all the logs we're looking for reside on the current drive
gDirUtilp->initAppDirs("SecondLife");
@@ -279,25 +383,6 @@ bool LLCrashLogger::init()
llinfos << "Loading crash behavior setting" << llendl;
mCrashBehavior = loadCrashBehaviorSetting();
- //Run through command line options
- if(getOption("previous").isDefined())
- {
- llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
- mCrashInPreviousExec = TRUE;
- }
-
- if(getOption("dialog").isDefined())
- {
- llinfos << "Show the user dialog" << llendl;
- mCrashBehavior = CRASH_BEHAVIOR_ASK;
- }
-
- LLSD name = getOption("name");
- if(name.isDefined())
- {
- mProductName = name.asString();
- }
-
// If user doesn't want to send, bail out
if (mCrashBehavior == CRASH_BEHAVIOR_NEVER_SEND)
{
@@ -312,8 +397,8 @@ bool LLCrashLogger::init()
//If we've opened the crash logger, assume we can delete the marker file if it exists
if( gDirUtilp )
{
- LLString marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
- ll_apr_file_remove( marker_file );
+ std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
+ LLAPRFile::remove( marker_file );
}
return true;