diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/llcrashlogger | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
Diffstat (limited to 'indra/llcrashlogger')
-rw-r--r-- | indra/llcrashlogger/llcrashlock.cpp | 150 | ||||
-rw-r--r-- | indra/llcrashlogger/llcrashlock.h | 16 | ||||
-rw-r--r-- | indra/llcrashlogger/llcrashlogger.cpp | 1372 | ||||
-rw-r--r-- | indra/llcrashlogger/llcrashlogger.h | 194 |
4 files changed, 866 insertions, 866 deletions
diff --git a/indra/llcrashlogger/llcrashlock.cpp b/indra/llcrashlogger/llcrashlock.cpp index 18d164abde..506232ab2a 100644 --- a/indra/llcrashlogger/llcrashlock.cpp +++ b/indra/llcrashlogger/llcrashlock.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llformat.cpp * @date January 2007 * @brief string formatting utility @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * 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. - * + * * 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. - * + * * 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 - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -33,7 +33,7 @@ #include "llsd.h" #include "llsdserialize.h" #include "llframetimer.h" -#include <boost/filesystem.hpp> +#include <boost/filesystem.hpp> #include <string> #include <iostream> #include <stdio.h> @@ -45,49 +45,49 @@ bool LLCrashLock::isProcessAlive(U32 pid, const std::string& pname) { - std::wstring wpname; - wpname = std::wstring(pname.begin(), pname.end()); - - HANDLE snapshot; - PROCESSENTRY32 pe32; - - bool matched = false; - - snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (snapshot == INVALID_HANDLE_VALUE) - { - return false; - } - else - { - pe32.dwSize = sizeof(PROCESSENTRY32); - if (Process32First(snapshot, &pe32)) - { - do { - std::wstring wexecname = pe32.szExeFile; - std::string execname = std::string(wexecname.begin(), wexecname.end()); - if (!wpname.compare(pe32.szExeFile)) - { - if (pid == (U32)pe32.th32ProcessID) - { - matched = true; - break; - } - } - } while (Process32Next(snapshot, &pe32)); - } - } - - CloseHandle(snapshot); - return matched; + std::wstring wpname; + wpname = std::wstring(pname.begin(), pname.end()); + + HANDLE snapshot; + PROCESSENTRY32 pe32; + + bool matched = false; + + snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot == INVALID_HANDLE_VALUE) + { + return false; + } + else + { + pe32.dwSize = sizeof(PROCESSENTRY32); + if (Process32First(snapshot, &pe32)) + { + do { + std::wstring wexecname = pe32.szExeFile; + std::string execname = std::string(wexecname.begin(), wexecname.end()); + if (!wpname.compare(pe32.szExeFile)) + { + if (pid == (U32)pe32.th32ProcessID) + { + matched = true; + break; + } + } + } while (Process32Next(snapshot, &pe32)); + } + } + + CloseHandle(snapshot); + return matched; } #else //Everyone Else bool LLCrashLock::isProcessAlive(U32 pid, const std::string& pname) { - //Will boost.process ever become a reality? + //Will boost.process ever become a reality? std::stringstream cmd; - + cmd << "pgrep '" << pname << "' | grep '^" << pid << "$'"; return (!system(cmd.str().c_str())); } @@ -101,46 +101,46 @@ LLCrashLock::LLCrashLock() : mCleanUp(true), mWaitingPID(0) void LLCrashLock::setCleanUp(bool cleanup) { mCleanUp = cleanup; //Allow cleanup to be disabled for debugging. -} +} LLSD LLCrashLock::getLockFile(std::string filename) { - LLSD lock_sd = LLSD::emptyMap(); - - llifstream ifile(filename.c_str()); - - if (ifile.is_open()) - { + LLSD lock_sd = LLSD::emptyMap(); + + llifstream ifile(filename.c_str()); + + if (ifile.is_open()) + { LLSDSerialize::fromXML(lock_sd, ifile); - ifile.close(); - } + ifile.close(); + } return lock_sd; } bool LLCrashLock::putLockFile(std::string filename, const LLSD& data) -{ +{ bool result = true; llofstream ofile(filename.c_str()); - - if (!LLSDSerialize::toXML(data,ofile)) - { + + if (!LLSDSerialize::toXML(data,ofile)) + { result=false; - } - ofile.close(); + } + ofile.close(); return result; } bool LLCrashLock::requestMaster( F32 timeout ) { - if (mMaster.empty()) - { - mMaster = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + if (mMaster.empty()) + { + mMaster = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crash_master.lock"); - } + } LLSD lock_sd=getLockFile(mMaster); - + if (lock_sd.has("pid")) { mWaitingPID = lock_sd["pid"].asInteger(); @@ -150,8 +150,8 @@ bool LLCrashLock::requestMaster( F32 timeout ) return false; } } - - U32 pid = getpid(); + + U32 pid = getpid(); lock_sd["pid"] = (LLSD::Integer)pid; return putLockFile(mMaster,lock_sd); } @@ -179,10 +179,10 @@ void LLCrashLock::releaseMaster() LLSD LLCrashLock::getProcessList() { if (mDumpTable.empty()) - { - mDumpTable= gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - "crash_table.lock"); - } + { + mDumpTable= gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "crash_table.lock"); + } return getLockFile(mDumpTable); } @@ -190,21 +190,21 @@ LLSD LLCrashLock::getProcessList() bool LLCrashLock::fileExists(std::string filename) { #ifdef LL_WINDOWS // or BOOST_WINDOWS_API - boost::filesystem::path file_path(utf8str_to_utf16str(filename)); + boost::filesystem::path file_path(utf8str_to_utf16str(filename)); #else - boost::filesystem::path file_path(filename); + boost::filesystem::path file_path(filename); #endif - return boost::filesystem::exists(file_path); + return boost::filesystem::exists(file_path); } void LLCrashLock::cleanupProcess(std::string proc_dir) { #ifdef LL_WINDOWS // or BOOST_WINDOWS_API - boost::filesystem::path dir_path(utf8str_to_utf16str(proc_dir)); + boost::filesystem::path dir_path(utf8str_to_utf16str(proc_dir)); #else - boost::filesystem::path dir_path(proc_dir); + boost::filesystem::path dir_path(proc_dir); #endif - boost::filesystem::remove_all(dir_path); + boost::filesystem::remove_all(dir_path); } bool LLCrashLock::putProcessList(const LLSD& proc_sd) diff --git a/indra/llcrashlogger/llcrashlock.h b/indra/llcrashlogger/llcrashlock.h index 60b060b736..b20555b746 100644 --- a/indra/llcrashlogger/llcrashlock.h +++ b/indra/llcrashlogger/llcrashlock.h @@ -1,25 +1,25 @@ -/** +/** * @file llcrashlock.h * @brief Maintainence of disk locking files for crash reporting * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * 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. - * + * * 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. - * + * * 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 - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -31,7 +31,7 @@ class LLSD; -#if !LL_WINDOWS //For non-windows platforms. +#if !LL_WINDOWS //For non-windows platforms. #include <signal.h> #endif @@ -51,8 +51,8 @@ public: LLSD getProcessList(); //Get next process pid/dir pairs void cleanupProcess(std::string proc_dir); //Remove from list, clean up working dir. bool putProcessList(const LLSD& processlist); //Write pid/dir pairs back to disk. - static bool fileExists(std::string filename); - + static bool fileExists(std::string filename); + //getters S32 getPID(); diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index e02894c271..46f7c40f06 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -1,686 +1,686 @@ - /** -* @file llcrashlogger.cpp -* @brief Crash logger implementation -* -* $LicenseInfo:firstyear=2003&license=viewerlgpl$ -* Second Life Viewer Source Code -* 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. -* -* 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. -* -* 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 -* -* 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_... -#include "llerror.h" -#include "llerrorcontrol.h" -#include "lltimer.h" -#include "lldir.h" -#include "llfile.h" -#include "llsdserialize.h" -#include "llproxy.h" -#include "llcorehttputil.h" -#include "llhttpsdhandler.h" -#include "httpcommon.h" -#include "httpresponse.h" -#include "llcleanup.h" - -#include <curl/curl.h> -#include <openssl/crypto.h> - -bool gBreak = false; -bool gSent = false; - -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(LLCrashLoggerHandler); -public: - LLCrashLoggerHandler() {} - -protected: - virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content); - virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status); - -}; - -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), - mCrashSettings("CrashSettings"), - mSentCrashLogs(false), - mCrashHost("") -{ -} - -LLCrashLogger::~LLCrashLogger() -{ - -} - -// 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) -{ - if(sllog.length() > TRIM_SIZE * 2) - { - 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) - { - 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; -} - -bool LLCrashLogger::readFromXML(LLSD& dest, const std::string& filename ) -{ - std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,filename); - llifstream log_file(db_file_name.c_str()); - - // Look for it in the given file - if (log_file.is_open()) - { - LLSDSerialize::fromXML(dest, log_file); - log_file.close(); - return true; - } - else - { - LL_WARNS("CRASHREPORT") << "Failed to open " << db_file_name << LL_ENDL; - } - 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; - - llifstream 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(); - LL_WARNS("CRASHREPORT") << "minidump length "<< length <<LL_ENDL; - 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; - } - else - { - LL_WARNS("CRASHREPORT") << "failed to open minidump "<<minidump_path<<LL_ENDL; - } - - return length > 0; -} - -void LLCrashLogger::gatherFiles() -{ - updateApplication("Gathering logs..."); - - 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 = readFromXML( static_sd, "static_debug_info.log" ); - has_logs |= readFromXML( dynamic_sd, "dynamic_debug_info.log" ); - - - if ( has_logs ) - { - mDebugLog = static_sd; - mergeLogs(dynamic_sd); - mCrashInPreviousExec = mDebugLog["CrashNotHandled"].asBoolean(); - - mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString(); - mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString(); - mFileMap["CrashHostUrl"] = loadCrashURLSetting(); - if(mDebugLog.has("CAFilename")) - { - LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE, - LLCore::HttpRequest::GLOBAL_POLICY_ID, mDebugLog["CAFilename"].asString(), NULL); - } - else - { - LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE, - LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL); - } - - 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 - 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"); - } - - 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(); - - - if ( has_logs && (mFileMap["CrashHostUrl"] != "") ) - { - mCrashHost = mFileMap["CrashHostUrl"]; - } - - //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"); - - updateApplication("Encoding files..."); - - for(std::map<std::string, std::string>::iterator itr = mFileMap.begin(); itr != mFileMap.end(); ++itr) - { - std::string file = (*itr).second; - if (!file.empty()) - { - LL_DEBUGS("CRASHREPORT") << "trying to read " << itr->first << ": " << file << LL_ENDL; - llifstream 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); - } - - 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; - // Add minidump as binary. - bool has_minidump = mDebugLog.has("MinidumpPath"); - - if (has_minidump) - { - minidump_path = mDebugLog["MinidumpPath"].asString(); - 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. - 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(); !has_minidump && iter!=file_vec.end(); ++iter) - { - if ( ( iter->length() > 30 ) && (iter->rfind(".dmp") == (iter->length()-4) ) ) - { - std::string fullname = pathname + *iter; - llifstream 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; - } - 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() -{ - return mCrashInfo; -} - -const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml"; - -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)) - { - // Next check app_settings (in the SL program dir) - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, CRASH_SETTINGS_FILE); - mCrashSettings.loadFromFile(filename); - } - - if (! mCrashSettings.controlExists("CrashHostUrl")) - { - return ""; - } - else - { - return mCrashSettings.getString("CrashHostUrl"); - } -} - -bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout) -{ - LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); - - httpOpts->setTimeout(timeout); - httpOpts->setSSLVerifyPeer(false); - - for(int i = 0; i < retries; ++i) - { - updateApplication(llformat("%s, try %d...", msg.c_str(), i+1)); - - LL_INFOS("CRASHREPORT") << "POST crash data to " << host << LL_ENDL; - LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(httpRequest.get(), LLCore::HttpRequest::DEFAULT_POLICY_ID, - 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; - post_data = constructPostData(); - - updateApplication("Sending reports..."); - - llofstream out_file(report_file.c_str()); - LLSDSerialize::toPrettyXML(post_data, out_file); - out_file.flush(); - out_file.close(); - - bool sent = false; - - if(mCrashHost != "") - { - 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) - { - updateApplication("Using default server..."); - sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to default server"), CRASH_UPLOAD_RETRIES, CRASH_UPLOAD_TIMEOUT); - } - - mSentCrashLogs = sent; - - return sent; -} - -bool LLCrashLogger::sendCrashLogs() -{ - 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 - { - mKeyMaster.cleanupProcess((*lock)["dumpdir"].asString()); - } - } - } - } - else - { - LL_INFOS() << "Discarding corrupted entry from lock table." << LL_ENDL; - } - } - } - - if (rec) - { - newlocks.append(rec); - } - - mKeyMaster.putProcessList(newlocks); - return true; -} - -void LLCrashLogger::updateApplication(const std::string& message) -{ - if (!message.empty()) LL_INFOS("CRASHREPORT") << message << LL_ENDL; -} - -bool LLCrashLogger::init() -{ - 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"); - - LLError::initForApplication(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - - // Default to the product name "Second Life" (this is overridden by the -name argument) - mProductName = "Second Life"; - - // 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"); - -#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); //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 - - 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)"); - - 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_THREADID_set_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; -} - - -void LLCrashLogger::ssl_thread_id_callback(CRYPTO_THREADID* pthreadid) -{ -#if LL_WINDOWS - CRYPTO_THREADID_set_pointer(pthreadid, GetCurrentThread()); -#else - CRYPTO_THREADID_set_pointer(pthreadid, reinterpret_cast<void*>(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(); - } - } -} - + /**
+* @file llcrashlogger.cpp
+* @brief Crash logger implementation
+*
+* $LicenseInfo:firstyear=2003&license=viewerlgpl$
+* Second Life Viewer Source Code
+* 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.
+*
+* 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.
+*
+* 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
+*
+* 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_...
+#include "llerror.h"
+#include "llerrorcontrol.h"
+#include "lltimer.h"
+#include "lldir.h"
+#include "llfile.h"
+#include "llsdserialize.h"
+#include "llproxy.h"
+#include "llcorehttputil.h"
+#include "llhttpsdhandler.h"
+#include "httpcommon.h"
+#include "httpresponse.h"
+#include "llcleanup.h"
+
+#include <curl/curl.h>
+#include <openssl/crypto.h>
+
+bool gBreak = false;
+bool gSent = false;
+
+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(LLCrashLoggerHandler);
+public:
+ LLCrashLoggerHandler() {}
+
+protected:
+ virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content);
+ virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status);
+
+};
+
+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),
+ mCrashSettings("CrashSettings"),
+ mSentCrashLogs(false),
+ mCrashHost("")
+{
+}
+
+LLCrashLogger::~LLCrashLogger()
+{
+
+}
+
+// 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)
+{
+ if(sllog.length() > TRIM_SIZE * 2)
+ {
+ 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)
+ {
+ 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;
+}
+
+bool LLCrashLogger::readFromXML(LLSD& dest, const std::string& filename )
+{
+ std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,filename);
+ llifstream log_file(db_file_name.c_str());
+
+ // Look for it in the given file
+ if (log_file.is_open())
+ {
+ LLSDSerialize::fromXML(dest, log_file);
+ log_file.close();
+ return true;
+ }
+ else
+ {
+ LL_WARNS("CRASHREPORT") << "Failed to open " << db_file_name << LL_ENDL;
+ }
+ 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;
+
+ llifstream 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();
+ LL_WARNS("CRASHREPORT") << "minidump length "<< length <<LL_ENDL;
+ 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;
+ }
+ else
+ {
+ LL_WARNS("CRASHREPORT") << "failed to open minidump "<<minidump_path<<LL_ENDL;
+ }
+
+ return length > 0;
+}
+
+void LLCrashLogger::gatherFiles()
+{
+ updateApplication("Gathering logs...");
+
+ 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 = readFromXML( static_sd, "static_debug_info.log" );
+ has_logs |= readFromXML( dynamic_sd, "dynamic_debug_info.log" );
+
+
+ if ( has_logs )
+ {
+ mDebugLog = static_sd;
+ mergeLogs(dynamic_sd);
+ mCrashInPreviousExec = mDebugLog["CrashNotHandled"].asBoolean();
+
+ mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString();
+ mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString();
+ mFileMap["CrashHostUrl"] = loadCrashURLSetting();
+ if(mDebugLog.has("CAFilename"))
+ {
+ LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
+ LLCore::HttpRequest::GLOBAL_POLICY_ID, mDebugLog["CAFilename"].asString(), NULL);
+ }
+ else
+ {
+ LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
+ LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL);
+ }
+
+ 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
+ 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");
+ }
+
+ 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();
+
+
+ if ( has_logs && (mFileMap["CrashHostUrl"] != "") )
+ {
+ mCrashHost = mFileMap["CrashHostUrl"];
+ }
+
+ //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");
+
+ updateApplication("Encoding files...");
+
+ for(std::map<std::string, std::string>::iterator itr = mFileMap.begin(); itr != mFileMap.end(); ++itr)
+ {
+ std::string file = (*itr).second;
+ if (!file.empty())
+ {
+ LL_DEBUGS("CRASHREPORT") << "trying to read " << itr->first << ": " << file << LL_ENDL;
+ llifstream 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);
+ }
+
+ 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;
+ // Add minidump as binary.
+ bool has_minidump = mDebugLog.has("MinidumpPath");
+
+ if (has_minidump)
+ {
+ minidump_path = mDebugLog["MinidumpPath"].asString();
+ 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.
+ 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(); !has_minidump && iter!=file_vec.end(); ++iter)
+ {
+ if ( ( iter->length() > 30 ) && (iter->rfind(".dmp") == (iter->length()-4) ) )
+ {
+ std::string fullname = pathname + *iter;
+ llifstream 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;
+ }
+ 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()
+{
+ return mCrashInfo;
+}
+
+const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml";
+
+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))
+ {
+ // Next check app_settings (in the SL program dir)
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, CRASH_SETTINGS_FILE);
+ mCrashSettings.loadFromFile(filename);
+ }
+
+ if (! mCrashSettings.controlExists("CrashHostUrl"))
+ {
+ return "";
+ }
+ else
+ {
+ return mCrashSettings.getString("CrashHostUrl");
+ }
+}
+
+bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout)
+{
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
+
+ httpOpts->setTimeout(timeout);
+ httpOpts->setSSLVerifyPeer(false);
+
+ for(int i = 0; i < retries; ++i)
+ {
+ updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
+
+ LL_INFOS("CRASHREPORT") << "POST crash data to " << host << LL_ENDL;
+ LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(httpRequest.get(), LLCore::HttpRequest::DEFAULT_POLICY_ID,
+ 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;
+ post_data = constructPostData();
+
+ updateApplication("Sending reports...");
+
+ llofstream out_file(report_file.c_str());
+ LLSDSerialize::toPrettyXML(post_data, out_file);
+ out_file.flush();
+ out_file.close();
+
+ bool sent = false;
+
+ if(mCrashHost != "")
+ {
+ 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)
+ {
+ updateApplication("Using default server...");
+ sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to default server"), CRASH_UPLOAD_RETRIES, CRASH_UPLOAD_TIMEOUT);
+ }
+
+ mSentCrashLogs = sent;
+
+ return sent;
+}
+
+bool LLCrashLogger::sendCrashLogs()
+{
+ 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
+ {
+ mKeyMaster.cleanupProcess((*lock)["dumpdir"].asString());
+ }
+ }
+ }
+ }
+ else
+ {
+ LL_INFOS() << "Discarding corrupted entry from lock table." << LL_ENDL;
+ }
+ }
+ }
+
+ if (rec)
+ {
+ newlocks.append(rec);
+ }
+
+ mKeyMaster.putProcessList(newlocks);
+ return true;
+}
+
+void LLCrashLogger::updateApplication(const std::string& message)
+{
+ if (!message.empty()) LL_INFOS("CRASHREPORT") << message << LL_ENDL;
+}
+
+bool LLCrashLogger::init()
+{
+ 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");
+
+ LLError::initForApplication(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
+
+ // Default to the product name "Second Life" (this is overridden by the -name argument)
+ mProductName = "Second Life";
+
+ // 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");
+
+#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); //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
+
+ 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)");
+
+ 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_THREADID_set_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;
+}
+
+
+void LLCrashLogger::ssl_thread_id_callback(CRYPTO_THREADID* pthreadid)
+{
+#if LL_WINDOWS
+ CRYPTO_THREADID_set_pointer(pthreadid, GetCurrentThread());
+#else
+ CRYPTO_THREADID_set_pointer(pthreadid, reinterpret_cast<void*>(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();
+ }
+ }
+}
+
diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h index ddab0d01eb..9952255810 100644 --- a/indra/llcrashlogger/llcrashlogger.h +++ b/indra/llcrashlogger/llcrashlogger.h @@ -1,97 +1,97 @@ -/** -* @file llcrashlogger.h -* @brief Crash Logger Definition -* -* $LicenseInfo:firstyear=2003&license=viewerlgpl$ -* Second Life Viewer Source Code -* 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. -* -* 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. -* -* 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 -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ -#ifndef LLCRASHLOGGER_H -#define LLCRASHLOGGER_H - -#include <vector> - -#include "linden_common.h" - -#include "llapp.h" -#include "llsd.h" -#include "llcontrol.h" -#include "llcrashlock.h" -#include "_mutex.h" - -// We shouldn't have to know the exact declaration of CRYPTO_THREADID, but VS -// 2017 complains if we forward-declare it as simply 'struct CRYPTO_THREADID'. -struct crypto_threadid_st; -typedef crypto_threadid_st CRYPTO_THREADID; - -// Crash reporter behavior -const S32 CRASH_BEHAVIOR_ASK = 0; -const S32 CRASH_BEHAVIOR_ALWAYS_SEND = 1; -const S32 CRASH_BEHAVIOR_NEVER_SEND = 2; - -class LLCrashLogger : public LLApp -{ -public: - LLCrashLogger(); - virtual ~LLCrashLogger(); - std::string loadCrashURLSetting(); - bool readFromXML(LLSD& dest, const std::string& filename ); - void gatherFiles(); - void mergeLogs( LLSD src_sd ); - - virtual void gatherPlatformSpecificFiles() {} - bool sendCrashLog(std::string dump_dir); - bool sendCrashLogs(); - LLSD constructPostData(); - virtual void updateApplication(const std::string& message = LLStringUtil::null); - virtual bool init(); - virtual bool frame() = 0; - virtual bool cleanup() = 0; - void commonCleanup(); - void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; } - S32 getCrashBehavior() { return mCrashBehavior; } - bool runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout); - bool readMinidump(std::string minidump_path); - -protected: - static void init_curl(); - static void term_curl(); - static void ssl_thread_id_callback(CRYPTO_THREADID*); - static void ssl_locking_callback(int mode, int type, const char * file, int line); - - S32 mCrashBehavior; - bool mCrashInPreviousExec; - std::map<std::string, std::string> mFileMap; - std::string mGridName; - LLControlGroup mCrashSettings; - std::string mProductName; - LLSD mCrashInfo; - std::string mCrashHost; - std::string mAltCrashHost; - LLSD mDebugLog; - bool mSentCrashLogs; - LLCrashLock mKeyMaster; - - static int ssl_mutex_count; - static LLCoreInt::HttpMutex ** ssl_mutex_list; - -}; - -#endif //LLCRASHLOGGER_H +/**
+* @file llcrashlogger.h
+* @brief Crash Logger Definition
+*
+* $LicenseInfo:firstyear=2003&license=viewerlgpl$
+* Second Life Viewer Source Code
+* 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.
+*
+* 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.
+*
+* 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
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LLCRASHLOGGER_H
+#define LLCRASHLOGGER_H
+
+#include <vector>
+
+#include "linden_common.h"
+
+#include "llapp.h"
+#include "llsd.h"
+#include "llcontrol.h"
+#include "llcrashlock.h"
+#include "_mutex.h"
+
+// We shouldn't have to know the exact declaration of CRYPTO_THREADID, but VS
+// 2017 complains if we forward-declare it as simply 'struct CRYPTO_THREADID'.
+struct crypto_threadid_st;
+typedef crypto_threadid_st CRYPTO_THREADID;
+
+// Crash reporter behavior
+const S32 CRASH_BEHAVIOR_ASK = 0;
+const S32 CRASH_BEHAVIOR_ALWAYS_SEND = 1;
+const S32 CRASH_BEHAVIOR_NEVER_SEND = 2;
+
+class LLCrashLogger : public LLApp
+{
+public:
+ LLCrashLogger();
+ virtual ~LLCrashLogger();
+ std::string loadCrashURLSetting();
+ bool readFromXML(LLSD& dest, const std::string& filename );
+ void gatherFiles();
+ void mergeLogs( LLSD src_sd );
+
+ virtual void gatherPlatformSpecificFiles() {}
+ bool sendCrashLog(std::string dump_dir);
+ bool sendCrashLogs();
+ LLSD constructPostData();
+ virtual void updateApplication(const std::string& message = LLStringUtil::null);
+ virtual bool init();
+ virtual bool frame() = 0;
+ virtual bool cleanup() = 0;
+ void commonCleanup();
+ void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; }
+ S32 getCrashBehavior() { return mCrashBehavior; }
+ bool runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout);
+ bool readMinidump(std::string minidump_path);
+
+protected:
+ static void init_curl();
+ static void term_curl();
+ static void ssl_thread_id_callback(CRYPTO_THREADID*);
+ static void ssl_locking_callback(int mode, int type, const char * file, int line);
+
+ S32 mCrashBehavior;
+ bool mCrashInPreviousExec;
+ std::map<std::string, std::string> mFileMap;
+ std::string mGridName;
+ LLControlGroup mCrashSettings;
+ std::string mProductName;
+ LLSD mCrashInfo;
+ std::string mCrashHost;
+ std::string mAltCrashHost;
+ LLSD mDebugLog;
+ bool mSentCrashLogs;
+ LLCrashLock mKeyMaster;
+
+ static int ssl_mutex_count;
+ static LLCoreInt::HttpMutex ** ssl_mutex_list;
+
+};
+
+#endif //LLCRASHLOGGER_H
|