diff options
Diffstat (limited to 'indra/llcrashlogger/llcrashlogger.cpp')
-rw-r--r--[-rwxr-xr-x] | indra/llcrashlogger/llcrashlogger.cpp | 395 |
1 files changed, 244 insertions, 151 deletions
diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index 0d239c9435..dca49be051 100755..100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -40,39 +40,51 @@ #include "lldir.h" #include "llfile.h" #include "llsdserialize.h" -#include "lliopipe.h" -#include "llpumpio.h" -#include "llhttpclient.h" -#include "llsdserialize.h" #include "llproxy.h" +#include "llcorehttputil.h" +#include "llhttpsdhandler.h" +#include "httpcommon.h" +#include "httpresponse.h" #include "llcleanup.h" - -LLPumpIO* gServicePump = NULL; + +#include <curl/curl.h> +#include <openssl/crypto.h> + BOOL gBreak = false; BOOL gSent = false; -class LLCrashLoggerResponder : public LLHTTPClient::Responder +int LLCrashLogger::ssl_mutex_count = 0; +LLCoreInt::HttpMutex ** LLCrashLogger::ssl_mutex_list = NULL; + +#define CRASH_UPLOAD_RETRIES 3 /* seconds */ +#define CRASH_UPLOAD_TIMEOUT 180 /* seconds */ + +class LLCrashLoggerHandler : public LLHttpSDHandler { - LOG_CLASS(LLCrashLoggerResponder); + LOG_CLASS(LLCrashLoggerHandler); public: - LLCrashLoggerResponder() - { - } + LLCrashLoggerHandler() {} protected: - virtual void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - gBreak = true; - } + virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content); + virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status); - virtual void httpSuccess() - { - gBreak = true; - gSent = true; - } }; +void LLCrashLoggerHandler::onSuccess(LLCore::HttpResponse * response, const LLSD &content) +{ + LL_DEBUGS("CRASHREPORT") << "Request to " << response->getRequestURL() << "succeeded" << LL_ENDL; + gBreak = true; + gSent = true; +} + +void LLCrashLoggerHandler::onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status) +{ + LL_WARNS("CRASHREPORT") << "Request to " << response->getRequestURL() + << " failed: " << status.toString() << LL_ENDL; + gBreak = true; +} + LLCrashLogger::LLCrashLogger() : mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND), mCrashInPreviousExec(false), @@ -141,18 +153,22 @@ std::string getStartupStateFromLog(std::string& sllog) return startup_state; } -bool LLCrashLogger::readDebugFromXML(LLSD& dest, const std::string& filename ) +bool LLCrashLogger::readFromXML(LLSD& dest, const std::string& filename ) { std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,filename); - std::ifstream debug_log_file(db_file_name.c_str()); + std::ifstream log_file(db_file_name.c_str()); - // Look for it in the debug_info.log file - if (debug_log_file.is_open()) + // Look for it in the given file + if (log_file.is_open()) { - LLSDSerialize::fromXML(dest, debug_log_file); - debug_log_file.close(); + LLSDSerialize::fromXML(dest, log_file); + log_file.close(); return true; } + else + { + LL_WARNS("CRASHREPORT") << "Failed to open " << db_file_name << LL_ENDL; + } return false; } @@ -175,6 +191,7 @@ bool LLCrashLogger::readMinidump(std::string minidump_path) { minidump_stream.seekg(0, std::ios::end); length = (size_t)minidump_stream.tellg(); + LL_WARNS("CRASHREPORT") << "minidump length "<< length <<LL_ENDL; minidump_stream.seekg(0, std::ios::beg); LLSD::Binary data; @@ -185,6 +202,11 @@ bool LLCrashLogger::readMinidump(std::string minidump_path) mCrashInfo["Minidump"] = data; } + else + { + LL_WARNS("CRASHREPORT") << "failed to open minidump "<<minidump_path<<LL_ENDL; + } + return (length>0?true:false); } @@ -194,9 +216,16 @@ void LLCrashLogger::gatherFiles() LLSD static_sd; LLSD dynamic_sd; + //if we ever want to change the endpoint we send crashes to + //we can construct a file download ( a la feature table filename for example) + //containing the new endpoint + LLSD endpoint; + std::string grid; + std::string fqdn; - bool has_logs = readDebugFromXML( static_sd, "static_debug_info.log" ); - has_logs |= readDebugFromXML( dynamic_sd, "dynamic_debug_info.log" ); + bool has_logs = readFromXML( static_sd, "static_debug_info.log" ); + has_logs |= readFromXML( dynamic_sd, "dynamic_debug_info.log" ); + if ( has_logs ) { @@ -206,22 +235,26 @@ void LLCrashLogger::gatherFiles() mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString(); mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString(); + mFileMap["CrashHostUrl"] = loadCrashURLSetting(); if(mDebugLog.has("CAFilename")) { - LLCurl::setCAFile(mDebugLog["CAFilename"].asString()); + LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE, + LLCore::HttpRequest::GLOBAL_POLICY_ID, mDebugLog["CAFilename"].asString(), NULL); } else { - LLCurl::setCAFile(gDirUtilp->getCAFile()); + LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE, + LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL); } - LL_INFOS() << "Using log file from debug log " << mFileMap["SecondLifeLog"] << LL_ENDL; - LL_INFOS() << "Using settings file from debug log " << mFileMap["SettingsXml"] << LL_ENDL; + LL_INFOS("CRASHREPORT") << "Using log file from debug log " << mFileMap["SecondLifeLog"] << LL_ENDL; + LL_INFOS("CRASHREPORT") << "Using settings file from debug log " << mFileMap["SettingsXml"] << LL_ENDL; } else { // Figure out the filename of the second life log - LLCurl::setCAFile(gDirUtilp->getCAFile()); + LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE, + LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL); mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log"); mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml"); @@ -234,31 +267,14 @@ void LLCrashLogger::gatherFiles() gatherPlatformSpecificFiles(); - //Use the debug log to reconstruct the URL to send the crash report to - if(mDebugLog.has("CrashHostUrl")) - { - // Crash log receiver has been manually configured. - mCrashHost = mDebugLog["CrashHostUrl"].asString(); - } - else if(mDebugLog.has("CurrentSimHost")) - { - mCrashHost = "https://"; - mCrashHost += mDebugLog["CurrentSimHost"].asString(); - mCrashHost += ":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"; - } + if ( has_logs && (mFileMap["CrashHostUrl"] != "") ) + { + mCrashHost = mFileMap["CrashHostUrl"]; + } - // 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"; + //default to agni, per product + mAltCrashHost = "http://viewercrashreport.agni.lindenlab.com/cgi-bin/viewercrashreceiver.py"; mCrashInfo["DebugLog"] = mDebugLog; mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"stats.log"); @@ -267,26 +283,37 @@ void LLCrashLogger::gatherFiles() 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()) - { - LL_INFOS("CRASHREPORT") << "Can't find file " << (*itr).second << LL_ENDL; - continue; - } - std::stringstream s; - s << f.rdbuf(); + std::string file = (*itr).second; + if (!file.empty()) + { + LL_DEBUGS("CRASHREPORT") << "trying to read " << itr->first << ": " << file << LL_ENDL; + std::ifstream f(file.c_str()); + if(f.is_open()) + { + std::stringstream s; + s << f.rdbuf(); - std::string crash_info = s.str(); - if(itr->first == "SecondLifeLog") - { - if(!mCrashInfo["DebugLog"].has("StartupState")) - { - mCrashInfo["DebugLog"]["StartupState"] = getStartupStateFromLog(crash_info); - } - trimSLLog(crash_info); - } + 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)); + mCrashInfo[(*itr).first] = LLStringFn::strip_invalid_xml(rawstr_to_utf8(crash_info)); + } + else + { + LL_WARNS("CRASHREPORT") << "Failed to open file " << file << LL_ENDL; + } + } + else + { + LL_DEBUGS("CRASHREPORT") << "empty file in list for " << itr->first << LL_ENDL; + } } std::string minidump_path; @@ -296,20 +323,21 @@ void LLCrashLogger::gatherFiles() if (has_minidump) { minidump_path = mDebugLog["MinidumpPath"].asString(); - } - - if (has_minidump) - { has_minidump = readMinidump(minidump_path); } + else + { + LL_WARNS("CRASHREPORT") << "DebugLog does not have MinidumpPath" << LL_ENDL; + } 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. + //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,""); + LL_WARNS("CRASHREPORT") << "Searching for minidump in " << pathname << LL_ENDL; vec file_vec = gDirUtilp->getFilesInDir(pathname); - for(vec::const_iterator iter=file_vec.begin(); iter!=file_vec.end(); ++iter) + for(vec::const_iterator iter=file_vec.begin(); !has_minidump && iter!=file_vec.end(); ++iter) { if ( ( iter->length() > 30 ) && (iter->rfind(".dmp") == (iter->length()-4) ) ) { @@ -325,15 +353,27 @@ void LLCrashLogger::gatherFiles() minidump_path = *iter; has_minidump = readMinidump(fullname); mDebugLog["MinidumpPath"] = fullname; - if (has_minidump) - { - break; - } + } + else + { + LL_DEBUGS("CRASHREPORT") << "MDMP not found in " << fullname << LL_ENDL; } } + else + { + LL_DEBUGS("CRASHREPORT") << "failed to open " << fullname << LL_ENDL; + } } + else + { + LL_DEBUGS("CRASHREPORT") << "Name does not match minidump name pattern " << *iter << LL_ENDL; + } } } + else + { + LL_WARNS("CRASHREPORT") << "readMinidump returned no minidump" << LL_ENDL; + } } LLSD LLCrashLogger::constructPostData() @@ -343,8 +383,9 @@ LLSD LLCrashLogger::constructPostData() const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml"; -S32 LLCrashLogger::loadCrashBehaviorSetting() +std::string LLCrashLogger::loadCrashURLSetting() { + // First check user_settings (in the user's home dir) std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); if (! mCrashSettings.loadFromFile(filename)) @@ -354,67 +395,66 @@ S32 LLCrashLogger::loadCrashBehaviorSetting() mCrashSettings.loadFromFile(filename); } - // If we didn't load any files above, this will return the default - S32 value = mCrashSettings.getS32("CrashSubmitBehavior"); - - // Whatever value we got, make sure it's valid - switch (value) - { - case CRASH_BEHAVIOR_NEVER_SEND: - return CRASH_BEHAVIOR_NEVER_SEND; - case CRASH_BEHAVIOR_ALWAYS_SEND: - return CRASH_BEHAVIOR_ALWAYS_SEND; - } - - return CRASH_BEHAVIOR_ASK; + if (! mCrashSettings.controlExists("CrashHostUrl")) + { + return ""; + } + else + { + return mCrashSettings.getString("CrashHostUrl"); + } } -bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior) +bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout) { - switch (crash_behavior) - { - case CRASH_BEHAVIOR_ASK: - case CRASH_BEHAVIOR_NEVER_SEND: - case CRASH_BEHAVIOR_ALWAYS_SEND: - break; - default: - return false; - } - - mCrashSettings.setS32("CrashSubmitBehavior", crash_behavior); - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); - mCrashSettings.saveToFile(filename, FALSE); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); - return true; -} + httpOpts->setTimeout(timeout); -bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout) -{ - gBreak = false; for(int i = 0; i < retries; ++i) { updateApplication(llformat("%s, try %d...", msg.c_str(), i+1)); - LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout); - while(!gBreak) - { - updateApplication(); // No new message, just pump the IO - } + + LL_INFOS("CRASHREPORT") << "POST crash data to " << host << LL_ENDL; + LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(httpRequest.get(), LLCore::HttpRequest::DEFAULT_POLICY_ID, 0, + host, data, httpOpts, LLCore::HttpHeaders::ptr_t(), LLCore::HttpHandler::ptr_t(new LLCrashLoggerHandler)); + + if (handle == LLCORE_HTTP_HANDLE_INVALID) + { + LLCore::HttpStatus status = httpRequest->getStatus(); + LL_WARNS("CRASHREPORT") << "Request POST failed to " << host << " with status of [" << + status.getType() << "]\"" << status.toString() << "\"" << LL_ENDL; + return false; + } + + while(!gBreak) + { + ms_sleep(250); + updateApplication(); // No new message, just pump the IO + httpRequest->update(0L); + } if(gSent) { return gSent; } + + LL_WARNS("CRASHREPORT") << "Failed to send crash report to \"" << host << "\"" << LL_ENDL; } return gSent; } 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"; - + + LL_DEBUGS("CRASHREPORT") << "sending " << report_file << LL_ENDL; + gatherFiles(); LLSD post_data; @@ -424,19 +464,26 @@ bool LLCrashLogger::sendCrashLog(std::string dump_dir) std::ofstream out_file(report_file.c_str()); LLSDSerialize::toPrettyXML(post_data, out_file); + out_file.flush(); out_file.close(); bool sent = false; - //*TODO: Translate - if(mCrashHost != "") + if(mCrashHost != "") { - sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3, 5); + LL_WARNS("CRASHREPORT") << "Sending crash data to server from CrashHostUrl '" << mCrashHost << "'" << LL_ENDL; + + std::string msg = "Using override crash server... "; + msg = msg+mCrashHost.c_str(); + updateApplication(msg.c_str()); + + sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), CRASH_UPLOAD_RETRIES, CRASH_UPLOAD_TIMEOUT); } if(!sent) { - sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3, 5); + updateApplication("Using default server..."); + sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to default server"), CRASH_UPLOAD_RETRIES, CRASH_UPLOAD_TIMEOUT); } mSentCrashLogs = sent; @@ -446,8 +493,6 @@ bool LLCrashLogger::sendCrashLog(std::string dump_dir) bool LLCrashLogger::sendCrashLogs() { - - //pertinent code from below moved into a subroutine. LLSD locks = mKeyMaster.getProcessList(); LLSD newlocks = LLSD::emptyArray(); @@ -486,8 +531,6 @@ bool LLCrashLogger::sendCrashLogs() } else { - //mCrashInfo["DebugLog"].erase("MinidumpPath"); - mKeyMaster.cleanupProcess((*lock)["dumpdir"].asString()); } } @@ -495,7 +538,7 @@ bool LLCrashLogger::sendCrashLogs() } else { - LL_WARNS() << "Discarding corrupted entry from lock table." << LL_ENDL; + LL_INFOS() << "Discarding corrupted entry from lock table." << LL_ENDL; } } } @@ -511,14 +554,14 @@ bool LLCrashLogger::sendCrashLogs() void LLCrashLogger::updateApplication(const std::string& message) { - gServicePump->pump(); - gServicePump->callback(); - if (!message.empty()) LL_INFOS() << message << LL_ENDL; + if (!message.empty()) LL_INFOS("CRASHREPORT") << message << LL_ENDL; } bool LLCrashLogger::init() { - LLCurl::initClass(false); + LL_DEBUGS("CRASHREPORT") << LL_ENDL; + + LLCore::LLHttp::initialize(); // We assume that all the logs we're looking for reside on the current drive gDirUtilp->initAppDirs("SecondLife"); @@ -541,6 +584,8 @@ bool LLCrashLogger::init() // Set the log file to crashreport.log LLError::logToFile(log_file); //NOTE: Until this line, LL_INFOS LL_WARNS, etc are blown to the ether. + LL_INFOS("CRASHREPORT") << "Crash reporter file rotation complete." << LL_ENDL; + // Handle locking bool locked = mKeyMaster.requestMaster(); //Request master locking file. wait time is defaulted to 300S @@ -567,26 +612,74 @@ bool LLCrashLogger::init() "1 = always send crash report, " "2 = never send crash report)"); - // LL_INFOS() << "Loading crash behavior setting" << LL_ENDL; - // mCrashBehavior = loadCrashBehaviorSetting(); - - // If user doesn't want to send, bail out - if (mCrashBehavior == CRASH_BEHAVIOR_NEVER_SEND) - { - LL_INFOS() << "Crash behavior is never_send, quitting" << LL_ENDL; - return false; - } - - gServicePump = new LLPumpIO(gAPRPoolp); - gServicePump->prime(gAPRPoolp); - LLHTTPClient::setPump(*gServicePump); - + init_curl(); + LLCore::HttpRequest::createService(); + LLCore::HttpRequest::startThread(); + return true; } // For cleanup code common to all platforms. void LLCrashLogger::commonCleanup() { + term_curl(); LLError::logToFile(""); //close crashreport.log SUBSYSTEM_CLEANUP(LLProxy); } + +void LLCrashLogger::init_curl() +{ + curl_global_init(CURL_GLOBAL_ALL); + + ssl_mutex_count = CRYPTO_num_locks(); + if (ssl_mutex_count > 0) + { + ssl_mutex_list = new LLCoreInt::HttpMutex *[ssl_mutex_count]; + + for (int i(0); i < ssl_mutex_count; ++i) + { + ssl_mutex_list[i] = new LLCoreInt::HttpMutex; + } + + CRYPTO_set_locking_callback(ssl_locking_callback); + CRYPTO_set_id_callback(ssl_thread_id_callback); + } +} + + +void LLCrashLogger::term_curl() +{ + CRYPTO_set_locking_callback(NULL); + for (int i(0); i < ssl_mutex_count; ++i) + { + delete ssl_mutex_list[i]; + } + delete[] ssl_mutex_list; +} + + +unsigned long LLCrashLogger::ssl_thread_id_callback(void) +{ +#if LL_WINDOWS + return (unsigned long)GetCurrentThread(); +#else + return (unsigned long)pthread_self(); +#endif +} + + +void LLCrashLogger::ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */) +{ + if (type >= 0 && type < ssl_mutex_count) + { + if (mode & CRYPTO_LOCK) + { + ssl_mutex_list[type]->lock(); + } + else + { + ssl_mutex_list[type]->unlock(); + } + } +} + |