diff options
Diffstat (limited to 'indra/llcrashlogger/llcrashlogger.cpp')
-rwxr-xr-x | indra/llcrashlogger/llcrashlogger.cpp | 300 |
1 files changed, 225 insertions, 75 deletions
diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index aa66ceb4ec..38858a1a91 100755 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -23,12 +23,14 @@ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ + #include <cstdio> #include <cstdlib> #include <sstream> #include <map> #include "llcrashlogger.h" +#include "llcrashlock.h" #include "linden_common.h" #include "llstring.h" #include "indra_constants.h" // CRASH_BEHAVIOR_... @@ -43,7 +45,7 @@ #include "llhttpclient.h" #include "llsdserialize.h" #include "llproxy.h" - + LLPumpIO* gServicePump = NULL; BOOL gBreak = false; BOOL gSent = false; @@ -61,7 +63,7 @@ public: } virtual void result(const LLSD& content) - { + { gBreak = true; gSent = true; } @@ -74,14 +76,11 @@ LLCrashLogger::LLCrashLogger() : mSentCrashLogs(false), mCrashHost("") { - // Set up generic error handling - setupErrorHandling(); } LLCrashLogger::~LLCrashLogger() { - delete gServicePump; - gServicePump = NULL; + } // TRIM_SIZE must remain larger than LINE_SEARCH_SIZE. @@ -138,19 +137,67 @@ std::string getStartupStateFromLog(std::string& sllog) return startup_state; } -void LLCrashLogger::gatherFiles() +bool LLCrashLogger::readDebugFromXML(LLSD& dest, const std::string& filename ) { - updateApplication("Gathering logs..."); - - // Figure out the filename of the debug log - std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log"); - std::ifstream debug_log_file(db_file_name.c_str()); - + std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,filename); + 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); + LLSDSerialize::fromXML(dest, debug_log_file); + debug_log_file.close(); + return true; + } + return false; +} + +void LLCrashLogger::mergeLogs( LLSD src_sd ) +{ + LLSD::map_iterator iter = src_sd.beginMap(); + LLSD::map_iterator end = src_sd.endMap(); + for( ; iter != end; ++iter) + { + mDebugLog[iter->first] = iter->second; + } +} + +bool LLCrashLogger::readMinidump(std::string minidump_path) +{ + size_t length=0; + 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); + length = (size_t)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; + } + return (length>0?true:false); +} + +void LLCrashLogger::gatherFiles() +{ + updateApplication("Gathering logs..."); + + LLSD static_sd; + LLSD dynamic_sd; + + bool has_logs = readDebugFromXML( static_sd, "static_debug_info.log" ); + has_logs |= readDebugFromXML( dynamic_sd, "dynamic_debug_info.log" ); + + if ( has_logs ) + { + mDebugLog = static_sd; + mergeLogs(dynamic_sd); mCrashInPreviousExec = mDebugLog["CrashNotHandled"].asBoolean(); mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString(); @@ -171,20 +218,15 @@ void LLCrashLogger::gatherFiles() { // Figure out the filename of the second life log LLCurl::setCAFile(gDirUtilp->getCAFile()); - mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log"); - mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml"); + + mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log"); + 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; - } + if (!gDirUtilp->fileExists(mFileMap["SecondLifeLog"]) ) //We would prefer to get this from the per-run but here's our fallback. + { + mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old"); + } gatherPlatformSpecificFiles(); @@ -215,7 +257,7 @@ void LLCrashLogger::gatherFiles() mAltCrashHost = "https://login.agni.lindenlab.com:12043/crash/report"; mCrashInfo["DebugLog"] = mDebugLog; - mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log"); + mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"stats.log"); updateApplication("Encoding files..."); @@ -224,7 +266,7 @@ void LLCrashLogger::gatherFiles() std::ifstream f((*itr).second.c_str()); if(!f.is_open()) { - std::cout << "Can't find file " << (*itr).second << std::endl; + LL_INFOS("CRASHREPORT") << "Can't find file " << (*itr).second << LL_ENDL; continue; } std::stringstream s; @@ -243,32 +285,55 @@ void LLCrashLogger::gatherFiles() mCrashInfo[(*itr).first] = LLStringFn::strip_invalid_xml(rawstr_to_utf8(crash_info)); } + std::string minidump_path; // Add minidump as binary. - std::string minidump_path = mDebugLog["MinidumpPath"]; - if(minidump_path != "") + bool has_minidump = mDebugLog.has("MinidumpPath"); + + if (has_minidump) { - 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 = (size_t)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; - } + minidump_path = mDebugLog["MinidumpPath"].asString(); } - mCrashInfo["DebugLog"].erase("MinidumpPath"); + + if (has_minidump) + { + has_minidump = readMinidump(minidump_path); + } + + if (!has_minidump) //Viewer was probably so hosed it couldn't write remaining data. Try brute force. + { + //Look for a filename at least 30 characters long in the dump dir which contains the characters MDMP as the first 4 characters in the file. + 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) + { + if ( ( iter->length() > 30 ) && (iter->rfind(".dmp") == (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)) + { + minidump_path = *iter; + has_minidump = readMinidump(fullname); + mDebugLog["MinidumpPath"] = fullname; + if (has_minidump) + { + break; + } + } + } + } + } + } } LLSD LLCrashLogger::constructPostData() { - LLSD ret; return mCrashInfo; } @@ -338,39 +403,106 @@ bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg return gSent; } -bool LLCrashLogger::sendCrashLogs() +bool LLCrashLogger::sendCrashLog(std::string dump_dir) { + gDirUtilp->setDumpDir( dump_dir ); + + std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLifeCrashReport"); + std::string report_file = dump_path + ".log"; + gatherFiles(); - + LLSD post_data; post_data = constructPostData(); - + updateApplication("Sending reports..."); - std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - "SecondLifeCrashReport"); - std::string report_file = dump_path + ".log"; - std::ofstream out_file(report_file.c_str()); LLSDSerialize::toPrettyXML(post_data, out_file); out_file.close(); - + bool sent = false; - + //*TODO: Translate if(mCrashHost != "") { sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3, 5); } - + if(!sent) { sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3, 5); } - + mSentCrashLogs = sent; + + return sent; +} - return true; +bool LLCrashLogger::sendCrashLogs() +{ + + //pertinent code from below moved into a subroutine. + LLSD locks = mKeyMaster.getProcessList(); + LLSD newlocks = LLSD::emptyArray(); + + LLSD opts = getOptionData(PRIORITY_COMMAND_LINE); + LLSD rec; + + if ( opts.has("pid") && opts.has("dumpdir") && opts.has("procname") ) + { + rec["pid"]=opts["pid"]; + rec["dumpdir"]=opts["dumpdir"]; + rec["procname"]=opts["procname"]; + } + + if (locks.isArray()) + { + for (LLSD::array_iterator lock=locks.beginArray(); + lock !=locks.endArray(); + ++lock) + { + if ( (*lock).has("pid") && (*lock).has("dumpdir") && (*lock).has("procname") ) + { + if ( mKeyMaster.isProcessAlive( (*lock)["pid"].asInteger(), (*lock)["procname"].asString() ) ) + { + newlocks.append(*lock); + } + else + { + //TODO: This is a hack but I didn't want to include boost in another file or retest everything related to lldir + if (LLCrashLock::fileExists((*lock)["dumpdir"].asString())) + { + //the viewer cleans up the log directory on clean shutdown + //but is ignorant of the locking table. + if (!sendCrashLog((*lock)["dumpdir"].asString())) + { + newlocks.append(*lock); //Failed to send log so don't delete it. + } + else + { + //mCrashInfo["DebugLog"].erase("MinidumpPath"); + + mKeyMaster.cleanupProcess((*lock)["dumpdir"].asString()); + } + } + } + } + else + { + llwarns << "Discarding corrupted entry from lock table." << llendl; + } + } + } + + if (rec) + { + newlocks.append(rec); + } + + mKeyMaster.putProcessList(newlocks); + return true; } void LLCrashLogger::updateApplication(const std::string& message) @@ -395,44 +527,62 @@ bool LLCrashLogger::init() // Rename current log file to ".old" std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log.old"); std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log"); - LLFile::rename(log_file.c_str(), old_log_file.c_str()); +#if LL_WINDOWS + LLAPRFile::remove(old_log_file); +#endif + + LLFile::rename(log_file.c_str(), old_log_file.c_str()); + // Set the log file to crashreport.log - LLError::logToFile(log_file); - - mCrashSettings.declareS32("CrashSubmitBehavior", CRASH_BEHAVIOR_ALWAYS_SEND, + LLError::logToFile(log_file); //NOTE: Until this line, LL_INFOS LL_WARNS, etc are blown to the ether. + + // Handle locking + bool locked = mKeyMaster.requestMaster(); //Request master locking file. wait time is defaulted to 300S + + while (!locked && mKeyMaster.isWaiting()) + { + LL_INFOS("CRASHREPORT") << "Waiting for lock." << LL_ENDL; +#if LL_WINDOWS + Sleep(1000); +#else + sleep(1); +#endif + locked = mKeyMaster.checkMaster(); + } + + if (!locked) + { + LL_WARNS("CRASHREPORT") << "Unable to get master lock. Another crash reporter may be hung." << LL_ENDL; + return false; + } + + mCrashSettings.declareS32("CrashSubmitBehavior", CRASH_BEHAVIOR_ALWAYS_SEND, "Controls behavior when viewer crashes " "(0 = ask before sending crash report, " "1 = always send crash report, " "2 = never send crash report)"); - + // llinfos << "Loading crash behavior setting" << llendl; // mCrashBehavior = loadCrashBehaviorSetting(); - + // If user doesn't want to send, bail out if (mCrashBehavior == CRASH_BEHAVIOR_NEVER_SEND) { llinfos << "Crash behavior is never_send, quitting" << llendl; return false; } - + gServicePump = new LLPumpIO(gAPRPoolp); gServicePump->prime(gAPRPoolp); LLHTTPClient::setPump(*gServicePump); - - //If we've opened the crash logger, assume we can delete the marker file if it exists - if( gDirUtilp ) - { - std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - "SecondLife.exec_marker"); - LLAPRFile::remove( marker_file ); - } - + return true; } // For cleanup code common to all platforms. void LLCrashLogger::commonCleanup() { + LLError::logToFile(""); //close crashreport.log LLProxy::cleanupClass(); } |