Skip to content
Snippets Groups Projects
Commit 32bbb07f authored by Oz Linden's avatar Oz Linden
Browse files

merge changes for 3.7.5-release

parents 0908535c 7e966f28
No related branches found
No related tags found
No related merge requests found
Showing
with 872 additions and 307 deletions
......@@ -477,3 +477,4 @@ dcb4981ce255841b6083d8f65444b65d5a733a17 3.7.1-release
b842534cb4d76c9ef87676a62b1d2d19e79c015f 3.7.2-release
962d3f98955bfc7310a7867c8cbc3df075e54aa9 3.7.3-release
d076568ff7883b41c149e6afb421f39c29dbfe2b 3.7.4-release
fc066b82343fca51f9c1b8eda0abc6bee9bb4503 3.7.5-release
......@@ -786,9 +786,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>d812a6dfcabe6528198a3191068dac09</string>
<string>a1e519d08c507c12f9d412b2ae8328c8</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-google-breakpad/rev/273073/arch/CYGWIN/installer/google_breakpad-0.0.0-rev1099-windows-20130329.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-google-breakpad/rev/279804/arch/CYGWIN/installer/google_breakpad-0.0.0-rev1099-windows-20130813.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
......
......@@ -13,7 +13,7 @@ else (STANDALONE)
set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES breakpad_client)
endif (LINUX)
if (WINDOWS)
set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler crash_generation_client common)
set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler crash_generation_client crash_generation_server common)
endif (WINDOWS)
# yes, this does look dumb, no, it's not incorrect
#
......
......@@ -26,6 +26,7 @@
#include "linden_common.h"
#include "llcrashloggerlinux.h"
#include "llsdutil.h"
int main(int argc, char **argv)
{
......@@ -34,6 +35,16 @@ int main(int argc, char **argv)
LLCrashLoggerLinux app;
app.parseCommandOptions(argc, argv);
LLSD options = LLApp::instance()->getOptionData(
LLApp::PRIORITY_COMMAND_LINE);
//LLApp::PRIORITY_RUNTIME_OVERRIDE);
if (!(options.has("pid") && options.has("dumpdir")))
{
llwarns << "Insufficient parameters to crash report." << llendl;
}
if (! app.init())
{
llwarns << "Unable to initialize application." << llendl;
......
......@@ -136,6 +136,7 @@ bool LLCrashLoggerLinux::mainLoop()
bool LLCrashLoggerLinux::cleanup()
{
commonCleanup();
mKeyMaster.releaseMaster();
return true;
}
......
......@@ -46,8 +46,8 @@
#include "llstl.h" // for DeletePointer()
#include "llstring.h"
#include "lleventtimer.h"
#include "google_breakpad/exception_handler.h"
#include "stringize.h"
//
// Signal handling
......@@ -55,6 +55,8 @@
// Windows uses structured exceptions, so it's handled a bit differently.
//
#if LL_WINDOWS
#include "windows.h"
LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop);
BOOL ConsoleCtrlHandler(DWORD fdwCtrlType);
bool windows_post_minidump_callback(const wchar_t* dump_path,
......@@ -71,7 +73,9 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *);
#if LL_LINUX
#include "google_breakpad/minidump_descriptor.h"
bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_desc, void* context, bool succeeded);
static bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_desc,
void* context,
bool succeeded);
#else
// Called by breakpad exception handler after the minidump has been generated.
bool unix_post_minidump_callback(const char *dump_dir,
......@@ -109,11 +113,6 @@ BOOL LLApp::sLogInSignal = FALSE;
LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status
LLAppErrorHandler LLApp::sErrorHandler = NULL;
BOOL LLApp::sErrorThreadRunning = FALSE;
#if !LL_WINDOWS
LLApp::child_map LLApp::sChildMap;
LLAtomicU32* LLApp::sSigChildCount = NULL;
LLAppChildCallback LLApp::sDefaultChildCallback = NULL;
#endif
LLApp::LLApp() : mThreadErrorp(NULL)
......@@ -128,11 +127,6 @@ void LLApp::commonCtor()
LLCommon::initClass();
#if !LL_WINDOWS
// This must be initialized before the error handler.
sSigChildCount = new LLAtomicU32(0);
#endif
// initialize the options structure. We need to make this an array
// because the structured data will not auto-allocate if we
// reference an invalid location with the [] operator.
......@@ -149,12 +143,13 @@ void LLApp::commonCtor()
// Set the application to this instance.
sApplication = this;
mExceptionHandler = 0;
// initialize the buffer to write the minidump filename to
// (this is used to avoid allocating memory in the crash handler)
memset(minidump_path, 0, MAX_MINDUMP_PATH_LENGTH);
memset(mMinidumpPath, 0, MAX_MINDUMP_PATH_LENGTH);
mCrashReportPipeStr = L"\\\\.\\pipe\\LLCrashReporterPipe";
}
LLApp::LLApp(LLErrorThread *error_thread) :
......@@ -166,10 +161,6 @@ LLApp::LLApp(LLErrorThread *error_thread) :
LLApp::~LLApp()
{
#if !LL_WINDOWS
delete sSigChildCount;
sSigChildCount = NULL;
#endif
// reclaim live file memory
std::for_each(mLiveFiles.begin(), mLiveFiles.end(), DeletePointer());
......@@ -244,6 +235,20 @@ bool LLApp::parseCommandOptions(int argc, char** argv)
}
++ii;
value.assign(argv[ii]);
#if LL_WINDOWS
//Windows changed command line parsing. Deal with it.
S32 slen = value.length() - 1;
S32 start = 0;
S32 end = slen;
if (argv[ii][start]=='"')start++;
if (argv[ii][end]=='"')end--;
if (start!=0 || end!=slen)
{
value = value.substr (start,end);
}
#endif
commands[name] = value;
}
setOptionData(PRIORITY_COMMAND_LINE, commands);
......@@ -288,6 +293,32 @@ void LLApp::stepFrame()
mRunner.run();
}
#if LL_WINDOWS
//The following code is needed for 32-bit apps on 64-bit windows to keep it from eating
//crashes. It is a lovely undocumented 'feature' in SP1 of Windows 7. An excellent
//in-depth article on the issue may be found here: http://randomascii.wordpress.com/2012/07/05/when-even-crashing-doesn-work/
void EnableCrashingOnCrashes()
{
typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags);
typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags);
const DWORD EXCEPTION_SWALLOWING = 0x1;
HMODULE kernel32 = LoadLibraryA("kernel32.dll");
tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32,
"GetProcessUserModeExceptionPolicy");
tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32,
"SetProcessUserModeExceptionPolicy");
if (pGetPolicy && pSetPolicy)
{
DWORD dwFlags;
if (pGetPolicy(&dwFlags))
{
// Turn off the filter
pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
}
}
}
#endif
void LLApp::setupErrorHandling()
{
......@@ -295,7 +326,10 @@ void LLApp::setupErrorHandling()
// occasionally checks to see if the app is in an error state, and sees if it needs to be run.
#if LL_WINDOWS
#if LL_SEND_CRASH_REPORTS
EnableCrashingOnCrashes();
// This sets a callback to handle w32 signals to the console window.
// The viewer shouldn't be affected, sicne its a windowed app.
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ConsoleCtrlHandler, TRUE);
......@@ -304,8 +338,44 @@ void LLApp::setupErrorHandling()
if(mExceptionHandler == 0)
{
llwarns << "adding breakpad exception handler" << llendl;
mExceptionHandler = new google_breakpad::ExceptionHandler(
L"C:\\Temp\\", 0, windows_post_minidump_callback, 0, google_breakpad::ExceptionHandler::HANDLER_ALL);
std::wstring wpipe_name;
wpipe_name = mCrashReportPipeStr + wstringize(getPid());
const std::wstring wdump_path(wstringize(mDumpPath));
int retries = 30;
for (; retries > 0; --retries)
{
if (mExceptionHandler != 0) delete mExceptionHandler;
mExceptionHandler = new google_breakpad::ExceptionHandler(
wdump_path,
NULL, //No filter
windows_post_minidump_callback,
0,
google_breakpad::ExceptionHandler::HANDLER_ALL,
MiniDumpNormal, //Generate a 'normal' minidump.
wpipe_name.c_str(),
NULL); //No custom client info.
if (mExceptionHandler->IsOutOfProcess())
{
LL_INFOS("CRASHREPORT") << "Successfully attached to Out of Process exception handler." << LL_ENDL;
break;
}
else
{
LL_WARNS("CRASHREPORT") << "Unable to attach to Out of Process exception handler. " << retries << " retries remaining." << LL_ENDL;
::Sleep(100); //Wait a tick and try again.
}
}
if (retries == 0) LL_WARNS("CRASHREPORT") << "Unable to attach to Out of Process exception handler." << LL_ENDL;
if (mExceptionHandler)
{
mExceptionHandler->set_handle_debug_exceptions(true);
}
}
#endif
#else
......@@ -355,14 +425,17 @@ void LLApp::setupErrorHandling()
if(installHandler && (mExceptionHandler == 0))
{
std::string dumpPath = "/tmp/";
mExceptionHandler = new google_breakpad::ExceptionHandler(dumpPath, 0, &unix_post_minidump_callback, 0, true, 0);
mExceptionHandler = new google_breakpad::ExceptionHandler(mDumpPath, 0, &unix_post_minidump_callback, 0, true, 0);
}
#elif LL_LINUX
if(installHandler && (mExceptionHandler == 0))
{
google_breakpad::MinidumpDescriptor desc("/tmp");
new google_breakpad::ExceptionHandler(desc, 0, &unix_minidump_callback, 0, true, 0);
if (mDumpPath.empty())
{
mDumpPath = "/tmp";
}
google_breakpad::MinidumpDescriptor desc(mDumpPath);
mExceptionHandler = new google_breakpad::ExceptionHandler(desc, NULL, unix_minidump_callback, NULL, true, -1);
}
#endif
......@@ -418,19 +491,35 @@ void LLApp::setError()
void LLApp::setMiniDumpDir(const std::string &path)
{
if (path.empty())
{
mDumpPath = "/tmp";
}
else
{
mDumpPath = path;
}
if(mExceptionHandler == 0) return;
#ifdef LL_WINDOWS
wchar_t buffer[MAX_MINDUMP_PATH_LENGTH];
mbstowcs(buffer, path.c_str(), MAX_MINDUMP_PATH_LENGTH);
mbstowcs(buffer, mDumpPath.c_str(), MAX_MINDUMP_PATH_LENGTH);
mExceptionHandler->set_dump_path(std::wstring(buffer));
#elif LL_LINUX
google_breakpad::MinidumpDescriptor desc(path);
//google_breakpad::MinidumpDescriptor desc("/tmp"); //path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched.
google_breakpad::MinidumpDescriptor desc(mDumpPath); //path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched.
mExceptionHandler->set_minidump_descriptor(desc);
#else
mExceptionHandler->set_dump_path(path);
mExceptionHandler->set_dump_path(mDumpPath);
#endif
}
void LLApp::setDebugFileNames(const std::string &path)
{
mStaticDebugFileName = path + "static_debug_info.log";
mDynamicDebugFileName = path + "dynamic_debug_info.log";
}
void LLApp::writeMiniDump()
{
if(mExceptionHandler == 0) return;
......@@ -507,34 +596,11 @@ bool LLApp::isCrashloggerDisabled()
return (sDisableCrashlogger == TRUE);
}
#if !LL_WINDOWS
// static
U32 LLApp::getSigChildCount()
{
if (sSigChildCount)
{
return U32(*sSigChildCount);
}
return 0;
}
// static
void LLApp::incSigChildCount()
{
if (sSigChildCount)
{
(*sSigChildCount)++;
}
}
#endif
// static
int LLApp::getPid()
{
#if LL_WINDOWS
return 0;
return GetCurrentProcessId();
#else
return getpid();
#endif
......@@ -610,43 +676,6 @@ BOOL ConsoleCtrlHandler(DWORD fdwCtrlType)
}
#else //!LL_WINDOWS
void LLApp::setChildCallback(pid_t pid, LLAppChildCallback callback)
{
LLChildInfo child_info;
child_info.mCallback = callback;
LLApp::sChildMap[pid] = child_info;
}
void LLApp::setDefaultChildCallback(LLAppChildCallback callback)
{
LLApp::sDefaultChildCallback = callback;
}
pid_t LLApp::fork()
{
fflush(NULL); // flush all buffers before the child inherits them
pid_t pid = ::fork();
if( pid < 0 )
{
int system_error = errno;
llwarns << "Unable to fork! Operating system error code: "
<< system_error << llendl;
}
else if (pid == 0)
{
// Sleep a bit to allow the parent to set up child callbacks.
ms_sleep(10);
// We need to disable signal handling, because we don't have a
// signal handling thread anymore.
setupErrorHandling();
}
else
{
llinfos << "Forked child process " << pid << llendl;
}
return pid;
}
void setup_signals()
{
......@@ -747,19 +776,6 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
llinfos << "Signal handler - Got SIGCHLD from " << info->si_pid << llendl;
}
// Check result code for all child procs for which we've
// registered callbacks THIS WILL NOT WORK IF SIGCHLD IS SENT
// w/o killing the child (Go, launcher!)
// TODO: Now that we're using SIGACTION, we can actually
// implement the launcher behavior to determine who sent the
// SIGCHLD even if it doesn't result in child termination
if (LLApp::sChildMap.count(info->si_pid))
{
LLApp::sChildMap[info->si_pid].mGotSigChild = TRUE;
}
LLApp::incSigChildCount();
return;
case SIGABRT:
// Abort just results in termination of the app, no funky error handling.
......@@ -880,21 +896,26 @@ bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_
// heap allocations in a crash handler.
// path format: <dump_dir>/<minidump_id>.dmp
int dirPathLength = strlen(minidump_desc.path());
//HACK: *path points to the buffer in getMiniDumpFilename which has already allocated space
//to avoid doing allocation during crash.
char * path = LLApp::instance()->getMiniDumpFilename();
int dir_path_len = strlen(path);
// The path must not be truncated.
llassert((dirPathLength + 5) <= LLApp::MAX_MINDUMP_PATH_LENGTH);
S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH - dir_path_len;
llassert( (remaining - strlen(minidump_desc.path())) > 5);
char * path = LLApp::instance()->getMiniDumpFilename();
S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH;
strncpy(path, minidump_desc.path(), remaining);
remaining -= dirPathLength;
path += dirPathLength;
if (remaining > 0 && dirPathLength > 0 && path[-1] != '/')
path += dir_path_len;
if (dir_path_len > 0 && path[-1] != '/')
{
*path++ = '/';
--remaining;
}
strncpy(path, minidump_desc.path(), remaining);
llinfos << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << llendl;
LLApp::runErrorHandler();
......@@ -942,7 +963,7 @@ bool unix_post_minidump_callback(const char *dump_dir,
strncpy(path, ".dmp", remaining);
}
llinfos << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << llendl;
llinfos << "generated minidump: " << path << llendl;
LLApp::runErrorHandler();
#ifndef LL_RELEASE_FOR_DOWNLOAD
......@@ -966,6 +987,7 @@ bool windows_post_minidump_callback(const wchar_t* dump_path,
S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH;
size_t bytesUsed;
LL_INFOS("MINIDUMPCALLBACK") << "Dump file was generated." << LL_ENDL;
bytesUsed = wcstombs(path, dump_path, static_cast<size_t>(remaining));
remaining -= bytesUsed;
path += bytesUsed;
......
......@@ -31,7 +31,6 @@
#include "llrun.h"
#include "llsd.h"
#include "lloptioninterface.h"
// Forward declarations
template <typename Type> class LLAtomic32;
typedef LLAtomic32<U32> LLAtomicU32;
......@@ -42,7 +41,6 @@ class LLLiveFile;
#endif
typedef void (*LLAppErrorHandler)();
typedef void (*LLAppChildCallback)(int pid, bool exited, int status);
#if !LL_WINDOWS
extern S32 LL_SMACKDOWN_SIGNAL;
......@@ -51,13 +49,6 @@ extern S32 LL_HEARTBEAT_SIGNAL;
// Clear all of the signal handlers (which we want to do for the child process when we fork
void clear_signals();
class LLChildInfo
{
public:
LLChildInfo() : mGotSigChild(FALSE), mCallback(NULL) {}
BOOL mGotSigChild;
LLAppChildCallback mCallback;
};
#endif
namespace google_breakpad {
......@@ -206,10 +197,6 @@ class LL_COMMON_API LLApp : public LLOptionInterface
static bool isQuitting();
static bool isError();
static bool isExiting(); // Either quitting or error (app is exiting, cleanly or not)
#if !LL_WINDOWS
static U32 getSigChildCount();
static void incSigChildCount();
#endif
static int getPid();
/** @name Error handling methods */
......@@ -238,32 +225,16 @@ class LL_COMMON_API LLApp : public LLOptionInterface
// change the directory where Breakpad minidump files are written to
void setMiniDumpDir(const std::string &path);
void setDebugFileNames(const std::string &path);
// Return the Google Breakpad minidump filename after a crash.
char *getMiniDumpFilename() { return minidump_path; }
char *getMiniDumpFilename() { return mMinidumpPath; }
std::string* getStaticDebugFile() { return &mStaticDebugFileName; }
std::string* getDynamicDebugFile() { return &mDynamicDebugFileName; }
// Write out a Google Breakpad minidump file.
void writeMiniDump();
#if !LL_WINDOWS
//
// Child process handling (Unix only for now)
//
// Set a callback to be run on exit of a child process
// WARNING! This callback is run from the signal handler due to
// Linux threading requiring waitpid() to be called from the thread that spawned the process.
// At some point I will make this more behaved, but I'm not going to fix this right now - djs
void setChildCallback(pid_t pid, LLAppChildCallback callback);
// The child callback to run if no specific handler is set
void setDefaultChildCallback(LLAppChildCallback callback);
// Fork and do the proper signal handling/error handling mojo
// *NOTE: You need to make sure your signal handling callback is
// correct after you fork, because not all threads are duplicated
// when you fork!
pid_t fork();
#endif
/**
* @brief Get a reference to the application runner
......@@ -286,13 +257,9 @@ class LL_COMMON_API LLApp : public LLOptionInterface
static EAppStatus sStatus; // Reflects current application status
static BOOL sErrorThreadRunning; // Set while the error thread is running
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
std::wstring mCrashReportPipeStr; //Name of pipe to use for crash reporting.
#if !LL_WINDOWS
static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
typedef std::map<pid_t, LLChildInfo> child_map; // Map key is a PID
static child_map sChildMap;
static LLAppChildCallback sDefaultChildCallback;
#endif
std::string mDumpPath; //output path for google breakpad. Dependency workaround.
/**
* @brief This method is called once a frame to do once a frame tasks.
......@@ -303,7 +270,10 @@ class LL_COMMON_API LLApp : public LLOptionInterface
void startErrorThread();
// Contains the filename of the minidump file after a crash.
char minidump_path[MAX_MINDUMP_PATH_LENGTH];
char mMinidumpPath[MAX_MINDUMP_PATH_LENGTH];
std::string mStaticDebugFileName;
std::string mDynamicDebugFileName;
// *NOTE: On Windows, we need a routine to reset the structured
// exception handler when some evil driver has taken it over for
......
......@@ -109,79 +109,8 @@ void LLErrorThread::run()
llinfos << "thread_error - Waiting for an error" << llendl;
S32 counter = 0;
#if !LL_WINDOWS
U32 last_sig_child_count = 0;
#endif
while (! (LLApp::isError() || LLApp::isStopped()))
{
#if !LL_WINDOWS
// Check whether or not the main thread had a sig child we haven't handled.
U32 current_sig_child_count = LLApp::getSigChildCount();
if (last_sig_child_count != current_sig_child_count)
{
int status = 0;
pid_t child_pid = 0;
last_sig_child_count = current_sig_child_count;
if (LLApp::sLogInSignal)
{
llinfos << "thread_error handling SIGCHLD #" << current_sig_child_count << llendl;
}
for (LLApp::child_map::iterator iter = LLApp::sChildMap.begin(); iter != LLApp::sChildMap.end();)
{
child_pid = iter->first;
LLChildInfo &child_info = iter->second;
// check the status of *all* children, in case we missed a signal
if (0 != waitpid(child_pid, &status, WNOHANG))
{
bool exited = false;
int exit_status = -1;
get_child_status(status, exit_status, exited, LLApp::sLogInSignal);
if (child_info.mCallback)
{
if (LLApp::sLogInSignal)
{
llinfos << "Signal handler - Running child callback" << llendl;
}
child_info.mCallback(child_pid, exited, status);
}
LLApp::sChildMap.erase(iter++);
}
else
{
// Child didn't terminate, yet we got a sigchild somewhere...
if (child_info.mGotSigChild && child_info.mCallback)
{
child_info.mCallback(child_pid, false, 0);
}
child_info.mGotSigChild = FALSE;
iter++;
}
}
// check the status of *all* children, in case we missed a signal
// Same as above, but use the default child callback
while(0 < (child_pid = waitpid( -1, &status, WNOHANG )))
{
if (0 != waitpid(child_pid, &status, WNOHANG))
{
bool exited = false;
int exit_status = -1;
get_child_status(status, exit_status, exited, LLApp::sLogInSignal);
if (LLApp::sDefaultChildCallback)
{
if (LLApp::sLogInSignal)
{
llinfos << "Signal handler - Running default child callback" << llendl;
}
LLApp::sDefaultChildCallback(child_pid, true, status);
}
}
}
}
#endif
ms_sleep(10);
counter++;
}
......
......@@ -265,6 +265,37 @@ int LLFile::rename(const std::string& filename, const std::string& newname)
return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc);
}
bool LLFile::copy(const std::string from, const std::string to)
{
bool copied = false;
LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */
if (in)
{
LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */
if (out)
{
char buf[16384]; /* Flawfinder: ignore */
size_t readbytes;
bool write_ok = true;
while(write_ok && (readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
{
if (fwrite(buf, 1, readbytes, out) != readbytes)
{
LL_WARNS("LLFile") << "Short write" << LL_ENDL;
write_ok = false;
}
}
if ( write_ok )
{
copied = true;
}
fclose(out);
}
fclose(in);
}
return copied;
}
int LLFile::stat(const std::string& filename, llstat* filestatus)
{
#if LL_WINDOWS
......
......@@ -75,6 +75,8 @@ class LL_COMMON_API LLFile
static int rmdir(const std::string& filename);
static int remove(const std::string& filename);
static int rename(const std::string& filename,const std::string& newname);
static bool copy(const std::string from, const std::string to);
static int stat(const std::string& filename,llstat* file_status);
static bool isdir(const std::string& filename);
static bool isfile(const std::string& filename);
......
......@@ -518,10 +518,13 @@ LL_COMMON_API S32 wchar_to_utf8chars(llwchar inchar, char* outchars);
LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str, S32 len);
LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str);
LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len);
LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str);
#if LL_WINDOWS
inline std::string wstring_to_utf8str(const llutf16string &utf16str) { return utf16str_to_utf8str(utf16str);}
#endif
// Length of this UTF32 string in bytes when transformed to UTF8
LL_COMMON_API S32 wstring_utf8_length(const LLWString& wstr);
......
......@@ -31,20 +31,63 @@
#include <sstream>
#include <boost/lambda/lambda.hpp>
#include <llstring.h>
/**
* stringize(item) encapsulates an idiom we use constantly, using
* gstringize(item) encapsulates an idiom we use constantly, using
* operator<<(std::ostringstream&, TYPE) followed by std::ostringstream::str()
* or their wstring equivalents
* to render a string expressing some item.
*/
template <typename T>
std::string stringize(const T& item)
template <typename CHARTYPE, typename T>
std::basic_string<CHARTYPE> gstringize(const T& item)
{
std::ostringstream out;
std::basic_ostringstream<CHARTYPE> out;
out << item;
return out.str();
}
/**
*partial specialization of stringize for handling wstring
*TODO: we should have similar specializations for wchar_t[] but not until it is needed.
*/
inline std::string stringize(const std::wstring& item)
{
llwarns << "WARNING: Possible narrowing" << llendl;
std::string s;
s = wstring_to_utf8str(item);
return gstringize<char>(s);
}
/**
* Specialization of gstringize for std::string return types
*/
template <typename T>
std::string stringize(const T& item)
{
return gstringize<char>(item);
}
/**
* Specialization for generating wstring from string.
* Both a convenience function and saves a miniscule amount of overhead.
*/
inline std::wstring wstringize(const std::string& item)
{
return gstringize<wchar_t>(item.c_str());
}
/**
* Specialization of gstringize for std::wstring return types
*/
template <typename T>
std::wstring wstringize(const T& item)
{
return gstringize<wchar_t>(item);
}
/**
* stringize_f(functor)
*/
......
......@@ -67,6 +67,8 @@ namespace tut
llsd["i"] = i;
llsd["d"] = d;
llsd["abc"] = abc;
def = L"def ghi";
}
char c;
......@@ -76,6 +78,7 @@ namespace tut
float f;
double d;
std::string abc;
std::wstring def;
LLSD llsd;
};
typedef test_group<stringize_data> stringize_group;
......@@ -92,6 +95,7 @@ namespace tut
ensure_equals(stringize(f), "3.14159");
ensure_equals(stringize(d), "3.14159");
ensure_equals(stringize(abc), "abc def");
ensure_equals(stringize(def), "def ghi"); //Will generate llwarns due to narrowing.
ensure_equals(stringize(llsd), "{'abc':'abc def','d':r3.14159,'i':i34}");
}
......@@ -101,4 +105,20 @@ namespace tut
ensure_equals(STRINGIZE("c is " << c), "c is c");
ensure_equals(STRINGIZE(std::setprecision(4) << d), "3.142");
}
template<> template<>
void stringize_object::test<3>()
{
//Tests rely on validity of wstring_to_utf8str()
ensure_equals(wstring_to_utf8str(wstringize(c)), wstring_to_utf8str(L"c"));
ensure_equals(wstring_to_utf8str(wstringize(s)), wstring_to_utf8str(L"17"));
ensure_equals(wstring_to_utf8str(wstringize(i)), wstring_to_utf8str(L"34"));
ensure_equals(wstring_to_utf8str(wstringize(l)), wstring_to_utf8str(L"68"));
ensure_equals(wstring_to_utf8str(wstringize(f)), wstring_to_utf8str(L"3.14159"));
ensure_equals(wstring_to_utf8str(wstringize(d)), wstring_to_utf8str(L"3.14159"));
ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(L"abc def"));
ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(wstringize(abc.c_str())));
ensure_equals(wstring_to_utf8str(wstringize(def)), wstring_to_utf8str(L"def ghi"));
// ensure_equals(wstring_to_utf8str(wstringize(llsd)), wstring_to_utf8str(L"{'abc':'abc def','d':r3.14159,'i':i34}"));
}
} // namespace tut
......@@ -23,12 +23,14 @@ include_directories(SYSTEM
set(llcrashlogger_SOURCE_FILES
llcrashlogger.cpp
llcrashlock.cpp
)
set(llcrashlogger_HEADER_FILES
CMakeLists.txt
llcrashlogger.h
llcrashlock.h
)
set_source_files_properties(${llcrashlogger_HEADER_FILES}
......
/**
* @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 "llcrashlock.h"
#include "lldir.h"
#include "llsd.h"
#include "llsdserialize.h"
#include "llnametable.h"
#include "llframetimer.h"
#include <boost/filesystem.hpp>
#include <string>
#include <iostream>
#include <stdio.h>
#if LL_WINDOWS //For windows platform.
#include <windows.h>
#include <TlHelp32.h>
namespace {
inline DWORD getpid() {
return GetCurrentProcessId();
}
}
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;
}
#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);
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);
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)
{
return boost::filesystem::exists(filename.c_str());
}
void LLCrashLock::cleanupProcess(std::string proc_dir)
{
boost::filesystem::remove_all(proc_dir);
}
bool LLCrashLock::putProcessList(const LLSD& proc_sd)
{
return putLockFile(mDumpTable,proc_sd);
}
/**
* @file llpidlock.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
......@@ -23,12 +23,14 @@
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <map>
#include "llcrashlogger.h"
#include "llcrashlock.h"
#include "linden_common.h"
#include "llstring.h"
#include "indra_constants.h" // CRASH_BEHAVIOR_...
......@@ -43,7 +45,7 @@
#include "llhttpclient.h"
#include "llsdserialize.h"
#include "llproxy.h"
LLPumpIO* gServicePump = NULL;
BOOL gBreak = false;
BOOL gSent = false;
......@@ -61,7 +63,7 @@ class LLCrashLoggerResponder : public LLHTTPClient::Responder
}
virtual void result(const LLSD& content)
{
{
gBreak = true;
gSent = true;
}
......@@ -74,14 +76,11 @@ LLCrashLogger::LLCrashLogger() :
mSentCrashLogs(false),
mCrashHost("")
{
// Set up generic error handling
setupErrorHandling();
}
LLCrashLogger::~LLCrashLogger()
{
delete gServicePump;
gServicePump = NULL;
}
// TRIM_SIZE must remain larger than LINE_SEARCH_SIZE.
......@@ -138,19 +137,67 @@ std::string getStartupStateFromLog(std::string& sllog)
return startup_state;
}
void LLCrashLogger::gatherFiles()
bool LLCrashLogger::readDebugFromXML(LLSD& dest, const std::string& filename )
{
updateApplication("Gathering logs...");
// Figure out the filename of the debug log
std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
std::ifstream debug_log_file(db_file_name.c_str());
std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,filename);
std::ifstream debug_log_file(db_file_name.c_str());
// Look for it in the debug_info.log file
if (debug_log_file.is_open())
{
LLSDSerialize::fromXML(mDebugLog, debug_log_file);
LLSDSerialize::fromXML(dest, debug_log_file);
debug_log_file.close();
return true;
}
return false;
}
void LLCrashLogger::mergeLogs( LLSD src_sd )
{
LLSD::map_iterator iter = src_sd.beginMap();
LLSD::map_iterator end = src_sd.endMap();
for( ; iter != end; ++iter)
{
mDebugLog[iter->first] = iter->second;
}
}
bool LLCrashLogger::readMinidump(std::string minidump_path)
{
size_t length=0;
std::ifstream minidump_stream(minidump_path.c_str(), std::ios_base::in | std::ios_base::binary);
if(minidump_stream.is_open())
{
minidump_stream.seekg(0, std::ios::end);
length = (size_t)minidump_stream.tellg();
minidump_stream.seekg(0, std::ios::beg);
LLSD::Binary data;
data.resize(length);
minidump_stream.read(reinterpret_cast<char *>(&(data[0])),length);
minidump_stream.close();
mCrashInfo["Minidump"] = data;
}
return (length>0?true:false);
}
void LLCrashLogger::gatherFiles()
{
updateApplication("Gathering logs...");
LLSD static_sd;
LLSD dynamic_sd;
bool has_logs = readDebugFromXML( static_sd, "static_debug_info.log" );
has_logs |= readDebugFromXML( dynamic_sd, "dynamic_debug_info.log" );
if ( has_logs )
{
mDebugLog = static_sd;
mergeLogs(dynamic_sd);
mCrashInPreviousExec = mDebugLog["CrashNotHandled"].asBoolean();
mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString();
......@@ -171,20 +218,15 @@ void LLCrashLogger::gatherFiles()
{
// Figure out the filename of the second life log
LLCurl::setCAFile(gDirUtilp->getCAFile());
mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log");
mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log");
mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
}
if(mCrashInPreviousExec)
{
// Restarting after freeze.
// Replace the log file ext with .old, since the
// instance that launched this process has overwritten
// SecondLife.log
std::string log_filename = mFileMap["SecondLifeLog"];
log_filename.replace(log_filename.size() - 4, 4, ".old");
mFileMap["SecondLifeLog"] = log_filename;
}
if (!gDirUtilp->fileExists(mFileMap["SecondLifeLog"]) ) //We would prefer to get this from the per-run but here's our fallback.
{
mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old");
}
gatherPlatformSpecificFiles();
......@@ -215,7 +257,7 @@ void LLCrashLogger::gatherFiles()
mAltCrashHost = "https://login.agni.lindenlab.com:12043/crash/report";
mCrashInfo["DebugLog"] = mDebugLog;
mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"stats.log");
updateApplication("Encoding files...");
......@@ -224,7 +266,7 @@ void LLCrashLogger::gatherFiles()
std::ifstream f((*itr).second.c_str());
if(!f.is_open())
{
std::cout << "Can't find file " << (*itr).second << std::endl;
LL_INFOS("CRASHREPORT") << "Can't find file " << (*itr).second << LL_ENDL;
continue;
}
std::stringstream s;
......@@ -243,32 +285,55 @@ void LLCrashLogger::gatherFiles()
mCrashInfo[(*itr).first] = LLStringFn::strip_invalid_xml(rawstr_to_utf8(crash_info));
}
std::string minidump_path;
// Add minidump as binary.
std::string minidump_path = mDebugLog["MinidumpPath"];
if(minidump_path != "")
bool has_minidump = mDebugLog.has("MinidumpPath");
if (has_minidump)
{
std::ifstream minidump_stream(minidump_path.c_str(), std::ios_base::in | std::ios_base::binary);
if(minidump_stream.is_open())
{
minidump_stream.seekg(0, std::ios::end);
size_t length = (size_t)minidump_stream.tellg();
minidump_stream.seekg(0, std::ios::beg);
LLSD::Binary data;
data.resize(length);
minidump_stream.read(reinterpret_cast<char *>(&(data[0])),length);
minidump_stream.close();
mCrashInfo["Minidump"] = data;
}
minidump_path = mDebugLog["MinidumpPath"].asString();
}
mCrashInfo["DebugLog"].erase("MinidumpPath");
if (has_minidump)
{
has_minidump = readMinidump(minidump_path);
}
if (!has_minidump) //Viewer was probably so hosed it couldn't write remaining data. Try brute force.
{
//Look for a filename at least 30 characters long in the dump dir which contains the characters MDMP as the first 4 characters in the file.
typedef std::vector<std::string> vec;
std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"");
vec file_vec = gDirUtilp->getFilesInDir(pathname);
for(vec::const_iterator iter=file_vec.begin(); iter!=file_vec.end(); ++iter)
{
if ( ( iter->length() > 30 ) && (iter->rfind(".dmp") == (iter->length()-4) ) )
{
std::string fullname = pathname + *iter;
std::ifstream fdat( fullname.c_str(), std::ifstream::binary);
if (fdat)
{
char buf[5];
fdat.read(buf,4);
fdat.close();
if (!strncmp(buf,"MDMP",4))
{
minidump_path = *iter;
has_minidump = readMinidump(fullname);
mDebugLog["MinidumpPath"] = fullname;
if (has_minidump)
{
break;
}
}
}
}
}
}
}
LLSD LLCrashLogger::constructPostData()
{
LLSD ret;
return mCrashInfo;
}
......@@ -338,39 +403,106 @@ bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg
return gSent;
}
bool LLCrashLogger::sendCrashLogs()
bool LLCrashLogger::sendCrashLog(std::string dump_dir)
{
gDirUtilp->setDumpDir( dump_dir );
std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
"SecondLifeCrashReport");
std::string report_file = dump_path + ".log";
gatherFiles();
LLSD post_data;
post_data = constructPostData();
updateApplication("Sending reports...");
std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
"SecondLifeCrashReport");
std::string report_file = dump_path + ".log";
std::ofstream out_file(report_file.c_str());
LLSDSerialize::toPrettyXML(post_data, out_file);
out_file.close();
bool sent = false;
//*TODO: Translate
if(mCrashHost != "")
{
sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3, 5);
}
if(!sent)
{
sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3, 5);
}
mSentCrashLogs = sent;
return sent;
}
return true;
bool LLCrashLogger::sendCrashLogs()
{
//pertinent code from below moved into a subroutine.
LLSD locks = mKeyMaster.getProcessList();
LLSD newlocks = LLSD::emptyArray();
LLSD opts = getOptionData(PRIORITY_COMMAND_LINE);
LLSD rec;
if ( opts.has("pid") && opts.has("dumpdir") && opts.has("procname") )
{
rec["pid"]=opts["pid"];
rec["dumpdir"]=opts["dumpdir"];
rec["procname"]=opts["procname"];
}
if (locks.isArray())
{
for (LLSD::array_iterator lock=locks.beginArray();
lock !=locks.endArray();
++lock)
{
if ( (*lock).has("pid") && (*lock).has("dumpdir") && (*lock).has("procname") )
{
if ( mKeyMaster.isProcessAlive( (*lock)["pid"].asInteger(), (*lock)["procname"].asString() ) )
{
newlocks.append(*lock);
}
else
{
//TODO: This is a hack but I didn't want to include boost in another file or retest everything related to lldir
if (LLCrashLock::fileExists((*lock)["dumpdir"].asString()))
{
//the viewer cleans up the log directory on clean shutdown
//but is ignorant of the locking table.
if (!sendCrashLog((*lock)["dumpdir"].asString()))
{
newlocks.append(*lock); //Failed to send log so don't delete it.
}
else
{
//mCrashInfo["DebugLog"].erase("MinidumpPath");
mKeyMaster.cleanupProcess((*lock)["dumpdir"].asString());
}
}
}
}
else
{
llwarns << "Discarding corrupted entry from lock table." << llendl;
}
}
}
if (rec)
{
newlocks.append(rec);
}
mKeyMaster.putProcessList(newlocks);
return true;
}
void LLCrashLogger::updateApplication(const std::string& message)
......@@ -395,44 +527,62 @@ bool LLCrashLogger::init()
// Rename current log file to ".old"
std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log.old");
std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log");
LLFile::rename(log_file.c_str(), old_log_file.c_str());
#if LL_WINDOWS
LLAPRFile::remove(old_log_file);
#endif
LLFile::rename(log_file.c_str(), old_log_file.c_str());
// Set the log file to crashreport.log
LLError::logToFile(log_file);
mCrashSettings.declareS32("CrashSubmitBehavior", CRASH_BEHAVIOR_ALWAYS_SEND,
LLError::logToFile(log_file); //NOTE: Until this line, LL_INFOS LL_WARNS, etc are blown to the ether.
// Handle locking
bool locked = mKeyMaster.requestMaster(); //Request master locking file. wait time is defaulted to 300S
while (!locked && mKeyMaster.isWaiting())
{
LL_INFOS("CRASHREPORT") << "Waiting for lock." << LL_ENDL;
#if LL_WINDOWS
Sleep(1000);
#else
sleep(1);
#endif
locked = mKeyMaster.checkMaster();
}
if (!locked)
{
LL_WARNS("CRASHREPORT") << "Unable to get master lock. Another crash reporter may be hung." << LL_ENDL;
return false;
}
mCrashSettings.declareS32("CrashSubmitBehavior", CRASH_BEHAVIOR_ALWAYS_SEND,
"Controls behavior when viewer crashes "
"(0 = ask before sending crash report, "
"1 = always send crash report, "
"2 = never send crash report)");
// llinfos << "Loading crash behavior setting" << llendl;
// mCrashBehavior = loadCrashBehaviorSetting();
// If user doesn't want to send, bail out
if (mCrashBehavior == CRASH_BEHAVIOR_NEVER_SEND)
{
llinfos << "Crash behavior is never_send, quitting" << llendl;
return false;
}
gServicePump = new LLPumpIO(gAPRPoolp);
gServicePump->prime(gAPRPoolp);
LLHTTPClient::setPump(*gServicePump);
//If we've opened the crash logger, assume we can delete the marker file if it exists
if( gDirUtilp )
{
std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
"SecondLife.exec_marker");
LLAPRFile::remove( marker_file );
}
return true;
}
// For cleanup code common to all platforms.
void LLCrashLogger::commonCleanup()
{
LLError::logToFile(""); //close crashreport.log
LLProxy::cleanupClass();
}
......@@ -33,6 +33,7 @@
#include "llapp.h"
#include "llsd.h"
#include "llcontrol.h"
#include "llcrashlock.h"
class LLCrashLogger : public LLApp
{
......@@ -40,9 +41,13 @@ class LLCrashLogger : public LLApp
LLCrashLogger();
virtual ~LLCrashLogger();
S32 loadCrashBehaviorSetting();
bool readDebugFromXML(LLSD& dest, const std::string& filename );
void gatherFiles();
void mergeLogs( LLSD src_sd );
virtual void gatherPlatformSpecificFiles() {}
bool saveCrashBehaviorSetting(S32 crash_behavior);
bool sendCrashLog(std::string dump_dir);
bool sendCrashLogs();
LLSD constructPostData();
virtual void updateApplication(const std::string& message = LLStringUtil::null);
......@@ -53,6 +58,8 @@ class LLCrashLogger : public LLApp
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:
S32 mCrashBehavior;
BOOL mCrashInPreviousExec;
......@@ -65,6 +72,7 @@ class LLCrashLogger : public LLApp
std::string mAltCrashHost;
LLSD mDebugLog;
bool mSentCrashLogs;
LLCrashLock mKeyMaster;
};
#endif //LLCRASHLOGGER_H
......@@ -42,6 +42,7 @@
#include "lldiriterator.h"
#include "stringize.h"
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
......@@ -76,6 +77,7 @@ const char
*LLDir::SKINBASE = "";
static const char* const empty = "";
std::string LLDir::sDumpDir = "";
LLDir::LLDir()
: mAppName(""),
......@@ -99,7 +101,32 @@ LLDir::~LLDir()
{
}
std::vector<std::string> LLDir::getFilesInDir(const std::string &dirname)
{
//Returns a vector of fullpath filenames.
boost::filesystem::path p (dirname);
std::vector<std::string> v;
if (exists(p))
{
if (is_directory(p))
{
boost::filesystem::directory_iterator end_iter;
for (boost::filesystem::directory_iterator dir_itr(p);
dir_itr != end_iter;
++dir_itr)
{
if (boost::filesystem::is_regular_file(dir_itr->status()))
{
v.push_back(dir_itr->path().filename().string());
}
}
}
}
return v;
}
S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
{
S32 count = 0;
......@@ -158,6 +185,34 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
return count;
}
U32 LLDir::deleteDirAndContents(const std::string& dir_name)
{
//Removes the directory and its contents. Returns number of files deleted.
U32 num_deleted = 0;
try
{
boost::filesystem::path dir_path(dir_name);
if (boost::filesystem::exists (dir_path))
{
if (!boost::filesystem::is_empty (dir_path))
{ // Directory has content
num_deleted = boost::filesystem::remove_all (dir_path);
}
else
{ // Directory is empty
boost::filesystem::remove (dir_path);
}
}
}
catch (boost::filesystem::filesystem_error &er)
{
llwarns << "Failed to delete " << dir_name << " with error " << er.code().message() << llendl;
}
return num_deleted;
}
const std::string LLDir::findFile(const std::string &filename,
const std::string& searchPath1,
const std::string& searchPath2,
......@@ -244,11 +299,36 @@ const std::string &LLDir::getLindenUserDir() const
return mLindenUserDir;
}
const std::string &LLDir::getChatLogsDir() const
const std::string& LLDir::getChatLogsDir() const
{
return mChatLogsDir;
}
void LLDir::setDumpDir( const std::string& path )
{
LLDir::sDumpDir = path;
if (! sDumpDir.empty() && sDumpDir.rbegin() == mDirDelimiter.rbegin() )
{
sDumpDir.erase(sDumpDir.size() -1);
}
}
const std::string &LLDir::getDumpDir() const
{
if (sDumpDir.empty() )
{
LLUUID uid;
uid.generate();
sDumpDir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "")
+ "dump-" + uid.asString();
dir_exists_or_crash(sDumpDir);
}
return LLDir::sDumpDir;
}
const std::string &LLDir::getPerAccountChatLogsDir() const
{
return mPerAccountChatLogsDir;
......@@ -420,6 +500,10 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd
prefix = getCacheDir();
break;
case LL_PATH_DUMP:
prefix=getDumpDir();
break;
case LL_PATH_USER_SETTINGS:
prefix = add(getOSUserAppDir(), "user_settings");
break;
......
......@@ -53,6 +53,7 @@ typedef enum ELLPath
LL_PATH_EXECUTABLE = 16,
LL_PATH_DEFAULT_SKIN = 17,
LL_PATH_FONTS = 18,
LL_PATH_DUMP = 19,
LL_PATH_LAST
} ELLPath;
......@@ -71,7 +72,8 @@ class LLDir
const std::string& app_read_only_data_dir = "") = 0;
virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask);
U32 deleteDirAndContents(const std::string& dir_name);
std::vector<std::string> getFilesInDir(const std::string &dirname);
// pure virtual functions
virtual std::string getCurPath() = 0;
virtual bool fileExists(const std::string &filename) const = 0;
......@@ -92,6 +94,7 @@ class LLDir
const std::string &getOSUserAppDir() const; // Location of the os-specific user app dir
const std::string &getLindenUserDir() const; // Location of the Linden user dir.
const std::string &getChatLogsDir() const; // Location of the chat logs dir.
const std::string &getDumpDir() const; // Location of the per-run dump dir.
const std::string &getPerAccountChatLogsDir() const; // Location of the per account chat logs dir.
const std::string &getTempDir() const; // Common temporary directory
const std::string getCacheDir(bool get_default = false) const; // Location of the cache.
......@@ -179,6 +182,8 @@ class LLDir
// For producing safe download file names from potentially unsafe ones
static std::string getScrubbedFileName(const std::string uncleanFileName);
static std::string getForbiddenFileChars();
void setDumpDir( const std::string& path );
virtual void setChatLogsDir(const std::string &path); // Set the chat logs dir to this user's dir
virtual void setPerAccountChatLogsDir(const std::string &username); // Set the per user chat log directory.
......@@ -245,6 +250,7 @@ class LLDir
std::vector<std::string> mSearchSkinDirs;
std::string mLanguage; // Current viewer language
std::string mLLPluginDir; // Location for plugins and plugin shell
static std::string sDumpDir; // Per-run crash report subdir of log directory.
std::string mUserName; // Current user name
};
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment