Apertura del repositorio

This commit is contained in:
2025-05-24 18:09:39 -03:00
parent 76e6359dad
commit d883ddd0d0
35253 changed files with 2891973 additions and 2 deletions
+25
View File
@@ -0,0 +1,25 @@
# -*- cmake -*-
project(llcrashlogger)
include(00-Common)
include(LLCoreHttp)
include(LLCommon)
set(llcrashlogger_SOURCE_FILES
llcrashlogger.cpp
llcrashlock.cpp
llcrashlookup.cpp
)
set(llcrashlogger_HEADER_FILES
CMakeLists.txt
llcrashlogger.h
llcrashlock.h
llcrashlookup.h
)
list(APPEND llcrashlogger_SOURCE_FILES ${llcrashlogger_HEADER_FILES})
add_library(llcrashlogger ${llcrashlogger_SOURCE_FILES})
target_link_libraries( llcrashlogger llcommon llmessage llcorehttp llxml llfilesystem )
+3
View File
@@ -0,0 +1,3 @@
This component is no longer used in Linden Lab builds.
Change requests to support continued use by open source
builds are welcome.
+212
View File
@@ -0,0 +1,212 @@
/**
* @file llformat.cpp
* @date January 2007
* @brief string formatting utility
*
* $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$
*/
#include "linden_common.h"
#include "llapr.h" // thread-related functions
#include "llcrashlock.h"
#include "lldir.h"
#include "llsd.h"
#include "llsdserialize.h"
#include "llframetimer.h"
#include <boost/filesystem.hpp>
#include <string>
#include <iostream>
#include <stdio.h>
#if LL_WINDOWS //For windows platform.
#include <llwin32headers.h>
#include <TlHelp32.h>
bool LLCrashLock::isProcessAlive(U32 pid, const std::string& pname)
{
std::wstring wpname = ll_convert_string_to_wide(pname);
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 = ll_convert_wide_to_string(wexecname);
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?
std::stringstream cmd;
cmd << "pgrep '" << pname << "' | grep '^" << pid << "$'";
return (!system(cmd.str().c_str()));
}
#endif //Everyone else.
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())
{
LLSDSerialize::fromXML(lock_sd, ifile);
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))
{
result=false;
}
ofile.close();
return result;
}
bool LLCrashLock::requestMaster( F32 timeout )
{
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();
if ( isProcessAlive(mWaitingPID, gDirUtilp->getExecutableFilename()) )
{
mTimer.resetWithExpiry(timeout);
return false;
}
}
U32 pid = getpid();
lock_sd["pid"] = (LLSD::Integer)pid;
return putLockFile(mMaster,lock_sd);
}
bool LLCrashLock::checkMaster()
{
if (mWaitingPID)
{
return (!isProcessAlive(mWaitingPID, gDirUtilp->getExecutableFilename()));
}
return false;
}
bool LLCrashLock::isWaiting()
{
return !mTimer.hasExpired();
}
void LLCrashLock::releaseMaster()
{
//Yeeeeeeehaw
unlink(mMaster.c_str());
}
LLSD LLCrashLock::getProcessList()
{
if (mDumpTable.empty())
{
mDumpTable= gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
"crash_table.lock");
}
return getLockFile(mDumpTable);
}
//static
bool LLCrashLock::fileExists(std::string filename)
{
#ifdef LL_WINDOWS // or BOOST_WINDOWS_API
boost::filesystem::path file_path(utf8str_to_utf16str(filename));
#else
boost::filesystem::path file_path(filename);
#endif
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));
#else
boost::filesystem::path dir_path(proc_dir);
#endif
boost::filesystem::remove_all(dir_path);
}
bool LLCrashLock::putProcessList(const LLSD& proc_sd)
{
return putLockFile(mDumpTable,proc_sd);
}
+73
View File
@@ -0,0 +1,73 @@
/**
* @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$
*/
#ifndef LL_CRASHLOCK_H
#define LL_CRASHLOCK_H
#include "llframetimer.h"
class LLSD;
#if !LL_WINDOWS //For non-windows platforms.
#include <signal.h>
#endif
//Crash reporter will now be kicked off by the viewer but otherwise
//run independent of the viewer.
class LLCrashLock
{
public:
LLCrashLock();
bool requestMaster( F32 timeout=300.0); //Wait until timeout for master lock.
bool checkMaster(); //True if available. False if not.
void releaseMaster( ); //Release master lockfile.
bool isLockPresent(std::string filename); //Check if lockfile exists.
bool isProcessAlive(U32 pid, const std::string& pname); //Check if pid is alive.
bool isWaiting(); //Waiting for master lock to be released.
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);
//getters
S32 getPID();
//setters
void setCleanUp(bool cleanup=true);
void setSaveName(std::string savename);
private:
LLSD getLockFile(std::string filename);
bool putLockFile(std::string filename, const LLSD& data);
bool mCleanUp;
std::string mMaster;
std::string mDumpTable;
U32 mWaitingPID; //The process we're waiting on if any.
LLFrameTimer mTimer;
};
#endif // LL_CRASHLOCK_H
+826
View File
@@ -0,0 +1,826 @@
/**
* @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>
// [SL:KB] - Patch: Viewer-CrashLookup | Checked: 2011-03-24 (Catznip-2.6.0a) | Added: Catznip-2.6.0a
#ifdef LL_WINDOWS
#include <shellapi.h>
#endif // LL_WINDOWS
// [/SL:KB]
bool gBreak = false;
bool gSent = false;
// <FS:CR> Various missing prototypes
void trimSLLog(std::string& sllog);
std::string getFormDataField(const std::string& strFieldName, const std::string& strFieldValue, const std::string& strBoundary);
std::string getStartupStateFromLog(std::string& sllog);
// </FS:CR>
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() :
// [SL:KB] - Patch: Viewer-CrashLookup | Checked: 2011-03-24 (Catznip-2.6.0a) | Added: Catznip-2.6.0a
mCrashLookup(NULL),
// [/SL:KB]
mCrashBehavior(CRASH_BEHAVIOR_ASK),
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 ";
auto index = sllog.rfind(startup_token);
if (index != std::string::npos || 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();
mFileMap.erase( "SecondLifeLog" ); // <FS:ND/> Don't send any Firestorm.log. It's likely huge and won't help for crashdump processing.
mDebugLog.erase( "SLLog" ); // <FS:ND/> Remove SLLog, as it's a path that contains the OS user name.
if ( has_logs && (mFileMap["CrashHostUrl"] != "") )
{
mCrashHost = mFileMap["CrashHostUrl"];
}
// <FS:ND> Might hardcode mCrashHost to crashlogs.phoenixviewer.com if unset
// <FS:ND> Do not send out crash reports to Linden Labs. They won't have much use for them without symbols.
//default to agni, per product
//mAltCrashHost = "http://viewercrashreport.agni.lindenlab.com/cgi-bin/viewercrashreceiver.py";
// </FS:ND>
mCrashInfo["DebugLog"] = mDebugLog;
mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"stats.log");
updateApplication("Encoding files...");
// <FS:ND> We're not using this. We do not send a LLSD xml with all data embedded.
//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;
//}
//}
// </FS:ND>
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;
}
// <FS:ND> Put minidump file into mFileMap. Otherwise it does not get uploaded to the crashlog server.
if( has_minidump )
{
std::string fullName = mDebugLog["MinidumpPath"];
std::string dmpName( fullName );
if( dmpName.size() )
{
size_t nStart( dmpName.size()-1 );
for( std::string::reverse_iterator itr = dmpName.rbegin(); itr != dmpName.rend(); ++itr )
{
if( *itr == '/' || *itr == '\\' )
break;
--nStart;
}
dmpName = dmpName.substr( nStart+1 );
}
mFileMap[ dmpName ] = fullName;
}
// </FS:ND>
}
LLSD LLCrashLogger::constructPostData()
{
return mCrashInfo;
}
const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml";
// <FS:Ansariel> Restore crash report user settings
S32 LLCrashLogger::loadCrashBehaviorSetting()
{
// 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 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;
}
bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
{
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);
return true;
}
// </FS:Ansariel>
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");
}
}
// [SL:KB] - Patch: Viewer-CrashReporting | Checked: 2011-03-24 (Catznip-2.6.0a) | Added: Catznip-2.6.0a
std::string getFormDataField(const std::string& strFieldName, const std::string& strFieldValue, const std::string& strBoundary)
{
std::ostringstream streamFormPart;
streamFormPart << "--" << strBoundary << "\r\n"
<< "Content-Disposition: form-data; name=\"" << strFieldName << "\"\r\n\r\n"
<< strFieldValue << "\r\n";
return streamFormPart.str();
}
// [/SL:KB]
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 dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "FirestormCrashReport");
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);
}
// <FS:ND> We do not send to mAltCrashHost ever.
// if(!sent)
// {
//updateApplication("Using default server...");
// sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), CRASH_UPLOAD_RETRIES, CRASH_UPLOAD_TIMEOUT);
// }
// </FS:ND>
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"];
}
// <FS:ND> Try to send the current crash right away, if that fails queue it for next time.
if( rec && rec.has("dumpdir") )
if( !sendCrashLog( rec["dumpdir"].asString() ) )
newlocks.append(rec);
// </FS:ND>
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;
}
}
}
// <FS:ND> We want this appended right away, or this crash only gets send the next time the crashreporter runs.
//if (rec)
//{
// newlocks.append(rec);
//}
// </FS:ND>
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
#if ADDRESS_SIZE == 64
gDirUtilp->initAppDirs( "Firestorm_x64" );
#else
gDirUtilp->initAppDirs("Firestorm");
#endif
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)
// <FS:ND> Change default to Firestorm
// mProductName = "Second Life";
mProductName = "Firestorm";
// </FS:ND>
// 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)");
// <FS:Ansariel> Restore crash report user settings
LL_INFOS("CRASHREPORT") << "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("CRASHREPORT") << "Crash behavior is never_send, quitting" << LL_ENDL;
return false;
}
// </FS:Ansariel>
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);
(void)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();
}
}
}
+113
View File
@@ -0,0 +1,113 @@
/**
* @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"
// [SL:KB] - Patch: Viewer-CrashLookup | Checked: 2011-03-24 (Catznip-2.6.0a) | Added: Catznip-2.6.0a
#include "llcrashlookup.h"
// [/SL:KB]
// 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;
// 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);
// <FS:Ansariel> Restore crash report user settings
S32 loadCrashBehaviorSetting();
bool saveCrashBehaviorSetting(S32 crash_behavior);
// </FS:Ansariel>
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;
// [SL:KB] - Patch: Viewer-CrashLookup | Checked: 2011-03-24 (Catznip-2.6.0a) | Added: Catznip-2.6.0a
LLCrashLookup* mCrashLookup;
// [/SL:KB]
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
+33
View File
@@ -0,0 +1,33 @@
/**
* @file llcrashlookup.cpp
* @brief Base crash analysis class
*
* Copyright (C) 2011, Kitty Barnett
*
* 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
*
*/
#include "linden_common.h"
#include "llcrashlookup.h"
std::string LLCrashLookup::getModuleVersionString() const
{
std::string strVersion = llformat("%d.%d.%d.%d",
(U32)(m_nModuleVersion >> 48), (U32)((m_nModuleVersion >> 32) & 0xFFFF),
(U32)((m_nModuleVersion >> 16) & 0xFFFF), (U32)(m_nModuleVersion & 0xFFFF));
return strVersion;
}
+56
View File
@@ -0,0 +1,56 @@
/**
* @file llcrashlookup.h
* @brief Base crash analysis class
*
* Copyright (C) 2011, Kitty Barnett
*
* 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
*
*/
#ifndef LLCRASHLOOKUP_H
#define LLCRASHLOOKUP_H
#include <string>
class LLCrashLookup
{
public:
LLCrashLookup() {}
virtual ~LLCrashLookup() {}
public:
virtual bool initFromDump(const std::string& strDumpPath) = 0;
public:
U64 getInstructionAddress() const { return m_nInstructionAddr; }
const std::string& getModuleName() const { return m_strModule; }
U64 getModuleBaseAdress() const { return m_nModuleBaseAddr; }
U64 getModuleDisplacement() const { return m_nInstructionAddr - m_nModuleBaseAddr; }
U32 getModuleTimeStamp() const { return m_nModuleTimeStamp; }
U32 getModuleChecksum() const { return m_nModuleChecksum; }
U64 getModuleVersion() const { return m_nModuleVersion; }
std::string getModuleVersionString() const;
protected:
U64 m_nInstructionAddr; // Memory pony where the crash occurred
std::string m_strModule; // Name of the module in which the crash occurred
U64 m_nModuleBaseAddr; // Base memory address of the module
U32 m_nModuleTimeStamp; // The date and time stamp of the module
U32 m_nModuleChecksum; // The 32-bit checksum of the module image
U64 m_nModuleVersion; // The version of the module, packed into 64-bits
};
#endif // LLCRASHLOOKUP_H