diff options
Diffstat (limited to 'indra/llcrashlogger/llcrashlogger.cpp')
-rw-r--r--[-rwxr-xr-x] | indra/llcrashlogger/llcrashlogger.cpp | 285 |
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; |