summaryrefslogtreecommitdiff
path: root/indra/llcrashlogger/llcrashlogger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcrashlogger/llcrashlogger.cpp')
-rwxr-xr-xindra/llcrashlogger/llcrashlogger.cpp300
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();
}