diff options
32 files changed, 1986 insertions, 2296 deletions
diff --git a/indra/linux_crash_logger/linux_crash_logger.cpp b/indra/linux_crash_logger/linux_crash_logger.cpp index a49dfe1b2b..5310093f69 100644 --- a/indra/linux_crash_logger/linux_crash_logger.cpp +++ b/indra/linux_crash_logger/linux_crash_logger.cpp @@ -29,542 +29,16 @@ * $/LicenseInfo$ */ -#include "linden_common.h" - -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> - -#include <curl/curl.h> - -#if LL_GTK -# include "gtk/gtk.h" -#endif // LL_GTK - -#include "indra_constants.h" // CRASH_BEHAVIOR_ASK -#include "llerror.h" -#include "lltimer.h" -#include "lldir.h" - -#include "llstring.h" - - -// These need to be localized. -static const char dialog_text[] = -"Second Life appears to have crashed.\n" -"This crash reporter collects information about your computer's hardware, operating system, and some Second Life logs, which are used for debugging purposes only.\n" -"Sending crash reports is the best way to help us improve the quality of Second Life.\n" -"If you continue to experience this problem, please try:\n" -"- Contacting support by visiting http://www.secondlife.com/support\n" -"\n" -"Send crash report?"; - -static const char dialog_title[] = -"Second Life Crash Logger"; - - -class LLFileEncoder -{ -public: - LLFileEncoder(const char *formname, const char *filename, bool isCrashLog = false); - - BOOL isValid() const { return mIsValid; } - LLString encodeURL(const S32 max_length = 0); -public: - BOOL mIsValid; - LLString mFilename; - LLString mFormname; - LLString mBuf; -}; - -LLString encode_string(const char *formname, const LLString &str); - -LLString gServerResponse; -BOOL gSendReport = FALSE; -LLString gUserserver; -LLString gUserText; -BOOL gCrashInPreviousExec = FALSE; -time_t gLaunchTime; - -static size_t curl_download_callback(void *data, size_t size, size_t nmemb, - void *user_data) -{ - S32 bytes = size * nmemb; - char *cdata = (char *) data; - for (int i =0; i < bytes; i += 1) - { - gServerResponse += (cdata[i]); - } - return bytes; -} - -#if LL_GTK -static void response_callback (GtkDialog *dialog, - gint arg1, - gpointer user_data) -{ - gint *response = (gint*)user_data; - *response = arg1; - gtk_widget_destroy(GTK_WIDGET(dialog)); - gtk_main_quit(); -} -#endif // LL_GTK - -static BOOL do_ask_dialog(void) -{ -#if LL_GTK - gtk_disable_setlocale(); - if (!gtk_init_check(NULL, NULL)) { - llinfos << "Could not initialize GTK for 'ask to send crash report' dialog; not sending report." << llendl; - return FALSE; - } - - GtkWidget *win = NULL; - GtkDialogFlags flags = GTK_DIALOG_MODAL; - GtkMessageType messagetype = GTK_MESSAGE_QUESTION; - GtkButtonsType buttons = GTK_BUTTONS_YES_NO; - gint response = GTK_RESPONSE_NONE; - - win = gtk_message_dialog_new(NULL, - flags, messagetype, buttons, - dialog_text); - gtk_window_set_type_hint(GTK_WINDOW(win), - GDK_WINDOW_TYPE_HINT_DIALOG); - gtk_window_set_title(GTK_WINDOW(win), dialog_title); - g_signal_connect (win, - "response", - G_CALLBACK (response_callback), - &response); - gtk_widget_show_all (win); - gtk_main(); - - return (GTK_RESPONSE_OK == response || - GTK_RESPONSE_YES == response || - GTK_RESPONSE_APPLY == response); -#else - return FALSE; -#endif // LL_GTK -} - +#include "llcrashloggerlinux.h" int main(int argc, char **argv) { - const S32 BT_MAX_SIZE = 100000; // Maximum size to transmit of the backtrace file - const S32 SL_MAX_SIZE = 100000; // Maximum size of the Second Life log file. - int i; - S32 crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND; - - time(&gLaunchTime); - - llinfos << "Starting Second Life Viewer Crash Reporter" << llendl; - - for(i=1; i<argc; i++) - { - if(!strcmp(argv[i], "-dialog")) - { - llinfos << "Show the user dialog" << llendl; - crash_behavior = CRASH_BEHAVIOR_ASK; - } - if(!strcmp(argv[i], "-previous")) - { - gCrashInPreviousExec = TRUE; - } - if(!strcmp(argv[i], "-user")) - { - if ((i + 1) < argc) - { - i++; - gUserserver = argv[i]; - llinfos << "Got userserver " << gUserserver << llendl; - } - } - } - - if( gCrashInPreviousExec ) - { - llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl; - } - - if(!gCrashInPreviousExec) - { - // Wait a while to let the crashed client finish exiting, - // freeing up the screen/etc. - sleep(5); - } - - // *FIX: do some dialog stuff here? - if (CRASH_BEHAVIOR_ALWAYS_SEND == crash_behavior) - { - gSendReport = TRUE; - } - else if (CRASH_BEHAVIOR_ASK == crash_behavior) - { - gSendReport = do_ask_dialog(); - } - - if(!gSendReport) - { - // Only send the report if the user agreed to it. - llinfos << "User cancelled, not sending report" << llendl; - - return(0); - } - - // We assume that all the logs we're looking for reside on the current drive - gDirUtilp->initAppDirs("SecondLife"); - - // Lots of silly variable, replicated for each log file. - LLString db_file_name; - LLString sl_file_name; - LLString bt_file_name; // stack_trace.log file - LLString st_file_name; // stats.log file - LLString si_file_name; // settings.xml file - - LLFileEncoder *db_filep = NULL; - LLFileEncoder *sl_filep = NULL; - LLFileEncoder *st_filep = NULL; - LLFileEncoder *bt_filep = NULL; - LLFileEncoder *si_filep = NULL; - - /////////////////////////////////// - // - // We do the parsing for the debug_info file first, as that will - // give us the location of the SecondLife.log file. - // - - // Figure out the filename of the debug log - db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str(); - db_filep = new LLFileEncoder("DB", db_file_name.c_str()); - - // Get the filename of the SecondLife.log file - // *NOTE: These buffer sizes are hardcoded into a scanf() below. - char tmp_sl_name[LL_MAX_PATH]; - tmp_sl_name[0] = '\0'; - char tmp_space[256]; - tmp_space[0] = '\0'; - - // Look for it in the debug_info.log file - if (db_filep->isValid()) - { - // This was originally scanning for "SL Log: %[^\r\n]", which happily skipped to the next line - // on debug logs (which don't have anything after "SL Log:" and tried to open a nonsensical filename. - sscanf(db_filep->mBuf.c_str(), "SL Log:%255[ ]%1023[^\r\n]", tmp_space, tmp_sl_name); - } - else - { - delete db_filep; - db_filep = NULL; - } - - // If we actually have a legitimate file name, use it. - if (gCrashInPreviousExec) - { - // If we froze, the crash log this time around isn't useful. - // Use the old one. - sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old"); - } - else if (tmp_sl_name[0]) - { - sl_file_name = tmp_sl_name; - llinfos << "Using log file from debug log: " << sl_file_name << llendl; - } - else - { - // Figure out the filename of the second life log - sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log").c_str(); - } - - // Now we get the SecondLife.log file if it's there, and recent enough... - sl_filep = new LLFileEncoder("SL", sl_file_name.c_str()); - if (!sl_filep->isValid()) - { - delete sl_filep; - sl_filep = NULL; - } - - st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log").c_str(); - st_filep = new LLFileEncoder("ST", st_file_name.c_str()); - if (!st_filep->isValid()) - { - delete st_filep; - st_filep = NULL; - } - - si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml").c_str(); - si_filep = new LLFileEncoder("SI", si_file_name.c_str()); - if (!si_filep->isValid()) - { - delete si_filep; - si_filep = NULL; - } - - // encode this as if it were a 'Dr Watson' plain-text backtrace - bt_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log").c_str(); - bt_filep = new LLFileEncoder("DW", bt_file_name.c_str()); - if (!bt_filep->isValid()) - { - delete bt_filep; - bt_filep = NULL; - } - - LLString post_data; - LLString tmp_url_buf; - - // Append the userserver - tmp_url_buf = encode_string("USER", gUserserver); - post_data += tmp_url_buf; - llinfos << "PostData:" << post_data << llendl; - - if (gCrashInPreviousExec) - { - post_data.append("&"); - tmp_url_buf = encode_string("EF", "Y"); - post_data += tmp_url_buf; - } - - if (db_filep) - { - post_data.append("&"); - tmp_url_buf = db_filep->encodeURL(); - post_data += tmp_url_buf; - llinfos << "Sending DB log file" << llendl; - } - else - { - llinfos << "Not sending DB log file" << llendl; - } - - if (sl_filep) - { - post_data.append("&"); - tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE); - post_data += tmp_url_buf; - llinfos << "Sending SL log file" << llendl; - } - else - { - llinfos << "Not sending SL log file" << llendl; - } - - if (st_filep) - { - post_data.append("&"); - tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE); - post_data += tmp_url_buf; - llinfos << "Sending stats log file" << llendl; - } - else - { - llinfos << "Not sending stats log file" << llendl; - } - - if (bt_filep) - { - post_data.append("&"); - tmp_url_buf = bt_filep->encodeURL(BT_MAX_SIZE); - post_data += tmp_url_buf; - llinfos << "Sending crash log file" << llendl; - } - else - { - llinfos << "Not sending crash log file" << llendl; - } - - if (si_filep) - { - post_data.append("&"); - tmp_url_buf = si_filep->encodeURL(); - post_data += tmp_url_buf; - llinfos << "Sending settings log file" << llendl; - } - else - { - llinfos << "Not sending settings.xml file" << llendl; - } - - if (gUserText.size()) - { - post_data.append("&"); - tmp_url_buf = encode_string("UN", gUserText); - post_data += tmp_url_buf; - } - - delete db_filep; - db_filep = NULL; - delete sl_filep; - sl_filep = NULL; - delete bt_filep; - bt_filep = NULL; - - // Debugging spam -#if 0 - printf("Crash report post data:\n--------\n"); - printf("%s", post_data.getString()); - printf("\n--------\n"); -#endif - - // Send the report. Yes, it's this easy. - { - CURL *curl = curl_easy_init(); - - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback); - curl_easy_setopt(curl, CURLOPT_POST, 1); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str()); - curl_easy_setopt(curl, CURLOPT_URL, "http://secondlife.com/cgi-bin/viewer_crash_reporter2"); - - llinfos << "Connecting to crash report server" << llendl; - CURLcode result = curl_easy_perform(curl); - - curl_easy_cleanup(curl); - - if(result != CURLE_OK) - { - llinfos << "Couldn't talk to crash report server" << llendl; - } - else - { - llinfos << "Response from crash report server:" << llendl; - llinfos << gServerResponse << llendl; - } - } - + LLCrashLoggerLinux app; + app.parseCommandOptions(argc, argv); + app.init(); + app.mainLoop(); + app.cleanup(); return 0; } -LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename, bool isCrashLog) -{ - mFormname = form_name; - mFilename = filename; - mIsValid = FALSE; - - int res; - - struct stat stat_data; - res = stat(mFilename.c_str(), &stat_data); - if (res) - { - llwarns << "File " << mFilename << " is missing!" << llendl; - return; - } - else - { - // Debugging spam -// llinfos << "File " << mFilename << " is present..." << llendl; - - if(!gCrashInPreviousExec && isCrashLog) - { - // Make sure the file isn't too old. - double age = difftime(gLaunchTime, stat_data.st_mtim.tv_sec); - -// llinfos << "age is " << age << llendl; - if(age > 60.0) - { - // The file was last modified more than 60 seconds before the crash reporter was launched. Assume it's stale. - llwarns << "File " << mFilename << " is too old!" << llendl; - return; - } - } - - } - - S32 buf_size = stat_data.st_size; - FILE *fp = fopen(mFilename.c_str(), "rb"); - U8 *buf = new U8[buf_size + 1]; - size_t nread = fread(buf, 1, buf_size, fp); - fclose(fp); - buf[nread] = 0; - - mBuf = (char *)buf; - - if(isCrashLog) - { - // Crash logs consist of a number of entries, one per crash. - // Each entry is preceeded by "**********" on a line by itself. - // We want only the most recent (i.e. last) one. - const char *sep = "**********"; - const char *start = mBuf.c_str(); - const char *cur = start; - const char *temp = strstr(cur, sep); - - while(temp != NULL) - { - // Skip past the marker we just found - cur = temp + strlen(sep); - - // and try to find another - temp = strstr(cur, sep); - } - - // If there's more than one entry in the log file, strip all but the last one. - if(cur != start) - { - mBuf.erase(0, cur - start); - } - } - - mIsValid = TRUE; - delete[] buf; -} - -LLString LLFileEncoder::encodeURL(const S32 max_length) -{ - LLString result = mFormname; - result.append("="); - - S32 i = 0; - - if (max_length) - { - if ((S32)mBuf.size() > max_length) - { - i = mBuf.size() - max_length; - } - } - -#if 0 - // Plain text version for debugging - result.append(mBuf); -#else - // Not using LLString because of bad performance issues - S32 buf_size = mBuf.size(); - S32 url_buf_size = 3*mBuf.size() + 1; - char *url_buf = new char[url_buf_size]; - - S32 cur_pos = 0; - for (; i < buf_size; i++) - { - sprintf(url_buf + cur_pos, "%%%02x", mBuf[i]); - cur_pos += 3; - } - url_buf[i*3] = 0; - - result.append(url_buf); - delete[] url_buf; -#endif - return result; -} - -LLString encode_string(const char *formname, const LLString &str) -{ - LLString result = formname; - result.append("="); - // Not using LLString because of bad performance issues - S32 buf_size = str.size(); - S32 url_buf_size = 3*str.size() + 1; - char *url_buf = new char[url_buf_size]; - - S32 cur_pos = 0; - S32 i; - for (i = 0; i < buf_size; i++) - { - sprintf(url_buf + cur_pos, "%%%02x", str[i]); - cur_pos += 3; - } - url_buf[i*3] = 0; - - result.append(url_buf); - delete[] url_buf; - return result; -} diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp new file mode 100644 index 0000000000..26a8f0cb2a --- /dev/null +++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp @@ -0,0 +1,140 @@ +/** + * @file llcrashloggerlinux.cpp + * @brief Linux crash logger implementation + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llcrashloggerlinux.h" + +#include <iostream> + +#include "linden_common.h" + +#include "boost/tokenizer.hpp" + +#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME +#include "llerror.h" +#include "llfile.h" +#include "lltimer.h" +#include "llstring.h" +#include "lldir.h" +#include "llsdserialize.h" + +#if LL_GTK +# include "gtk/gtk.h" +#endif // LL_GTK + +#define MAX_LOADSTRING 100 + +// These need to be localized. +static const char dialog_text[] = +"Second Life appears to have crashed or frozen last time it ran.\n" +"This crash reporter collects information about your computer's hardware, operating system, and some Second Life logs, all of which are used for debugging purposes only.\n" +"In the space below, please briefly describe what you were doing or trying to do just prior to the crash. Thank you for your help!\n" +"This report is NOT read by Customer Support. If you have billing or other questions, contact support by visiting http://www.secondlife.com/support\n" +"\n" +"Send crash report?"; + +static const char dialog_title[] = +"Second Life Crash Logger"; + +#if LL_GTK +static void response_callback (GtkDialog *dialog, + gint arg1, + gpointer user_data) +{ + gint *response = (gint*)user_data; + *response = arg1; + gtk_widget_destroy(GTK_WIDGET(dialog)); + gtk_main_quit(); +} +#endif // LL_GTK + +static BOOL do_ask_dialog(void) +{ +#if LL_GTK + gtk_disable_setlocale(); + if (!gtk_init_check(NULL, NULL)) { + llinfos << "Could not initialize GTK for 'ask to send crash report' dialog; not sending report." << llendl; + return FALSE; + } + + GtkWidget *win = NULL; + GtkDialogFlags flags = GTK_DIALOG_MODAL; + GtkMessageType messagetype = GTK_MESSAGE_QUESTION; + GtkButtonsType buttons = GTK_BUTTONS_YES_NO; + gint response = GTK_RESPONSE_NONE; + + win = gtk_message_dialog_new(NULL, + flags, messagetype, buttons, + dialog_text); + gtk_window_set_type_hint(GTK_WINDOW(win), + GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_window_set_title(GTK_WINDOW(win), dialog_title); + g_signal_connect (win, + "response", + G_CALLBACK (response_callback), + &response); + gtk_widget_show_all (win); + gtk_main(); + + return (GTK_RESPONSE_OK == response || + GTK_RESPONSE_YES == response || + GTK_RESPONSE_APPLY == response); +#else + return FALSE; +#endif // LL_GTK +} + +LLCrashLoggerLinux::LLCrashLoggerLinux(void) +{ +} + +LLCrashLoggerLinux::~LLCrashLoggerLinux(void) +{ +} + +void LLCrashLoggerLinux::gatherPlatformSpecificFiles() +{ + mFileMap["CrashLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log").c_str(); +} + +bool LLCrashLoggerLinux::mainLoop() +{ + if(!do_ask_dialog()) + { + return true; + } + sendCrashLogs(); + return true; +} + +void LLCrashLoggerLinux::updateApplication(LLString message) +{ + LLCrashLogger::updateApplication(message); +} diff --git a/indra/linux_crash_logger/llcrashloggerlinux.h b/indra/linux_crash_logger/llcrashloggerlinux.h new file mode 100644 index 0000000000..a84ee00e1c --- /dev/null +++ b/indra/linux_crash_logger/llcrashloggerlinux.h @@ -0,0 +1,49 @@ +/** + * @file llcrashloggerlinux.h + * @brief Linux crash logger definition + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LLCRASHLOGGERLINUX_H +#define LLCRASHLOGGERLINUX_H + +#include "linden_common.h" +#include "llcrashlogger.h" +#include "llstring.h" + +class LLCrashLoggerLinux : public LLCrashLogger +{ +public: + LLCrashLoggerLinux(void); + ~LLCrashLoggerLinux(void); + virtual bool mainLoop(); + virtual void updateApplication(LLString message = ""); + virtual void gatherPlatformSpecificFiles(); +}; + +#endif diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp new file mode 100755 index 0000000000..cec2b2e2e9 --- /dev/null +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -0,0 +1,309 @@ +/**
+* @file llcrashlogger.cpp
+* @brief Crash logger implementation
+*
+* $LicenseInfo:firstyear=2003&license=viewergpl$
+*
+* Copyright (c) 2003-2007, Linden Research, Inc.
+*
+* Second Life Viewer Source Code
+* The source code in this file ("Source Code") is provided by Linden Lab
+* to you under the terms of the GNU General Public License, version 2.0
+* ("GPL"), unless you have obtained a separate licensing agreement
+* ("Other License"), formally executed by you and Linden Lab. Terms of
+* the GPL can be found in doc/GPL-license.txt in this distribution, or
+* online at http://secondlife.com/developers/opensource/gplv2
+*
+* There are special exceptions to the terms and conditions of the GPL as
+* it is applied to this Source Code. View the full text of the exception
+* in the file doc/FLOSS-exception.txt in this software distribution, or
+* online at http://secondlife.com/developers/opensource/flossexception
+*
+* By copying, modifying or distributing this software, you acknowledge
+* that you have read and understood your obligations described above,
+* and agree to abide by those obligations.
+*
+* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+* COMPLETENESS OR PERFORMANCE.
+* $/LicenseInfo$
+*/
+#include <cstdio>
+#include <cstdlib>
+#include <sstream>
+#include <map>
+
+#include "llcrashlogger.h"
+#include "linden_common.h"
+#include "llstring.h"
+#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
+#include "llerror.h"
+#include "lltimer.h"
+#include "lldir.h"
+#include "llsdserialize.h"
+#include "lliopipe.h"
+#include "llpumpio.h"
+#include "llhttpclient.h"
+#include "llsdserialize.h"
+
+LLPumpIO* gServicePump;
+BOOL gBreak = false;
+BOOL gSent = false;
+
+class LLCrashLoggerResponder : public LLHTTPClient::Responder
+{
+public:
+ LLCrashLoggerResponder()
+ {
+ }
+
+ virtual void error(U32 status, const std::string& reason)
+ {
+ gBreak = true;
+ }
+
+ virtual void result(const LLSD& content)
+ {
+ gBreak = true;
+ gSent = true;
+ }
+};
+
+bool LLCrashLoggerText::mainLoop()
+{
+ std::cout << "Entering main loop" << std::endl;
+ sendCrashLogs();
+ return true;
+}
+
+void LLCrashLoggerText::updateApplication(LLString message)
+{
+ LLCrashLogger::updateApplication(message);
+ std::cout << message << std::endl;
+}
+
+LLCrashLogger::LLCrashLogger() :
+mSentCrashLogs(false)
+{
+
+}
+
+LLCrashLogger::~LLCrashLogger()
+{
+
+}
+
+void LLCrashLogger::gatherFiles()
+{
+
+ /*
+ //TODO:This function needs to be reimplemented somewhere in here...
+ if(!previous_crash && is_crash_log)
+ {
+ // Make sure the file isn't too old.
+ double age = difftime(gLaunchTime, stat_data.st_mtimespec.tv_sec);
+
+ // llinfos << "age is " << age << llendl;
+
+ if(age > 60.0)
+ {
+ // The file was last modified more than 60 seconds before the crash reporter was launched. Assume it's stale.
+ llwarns << "File " << mFilename << " is too old!" << llendl;
+ return;
+ }
+ }
+ */
+
+ updateApplication("Gathering logs...");
+
+ // Figure out the filename of the debug log
+ LLString db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str();
+ 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);
+ mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString();
+ mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString();
+ LLHTTPClient::setCABundle(mDebugLog["CAFilename"].asString());
+ llinfos << "Using log file from debug log " << mFileMap["SecondLifeLog"] << llendl;
+ llinfos << "Using settings file from debug log " << mFileMap["SettingsXml"] << llendl;
+ }
+ else
+ {
+ // Figure out the filename of the second life log
+ LLHTTPClient::setCABundle(gDirUtilp->getCAFile());
+ mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log");
+ mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
+ }
+
+ gatherPlatformSpecificFiles();
+
+ //Use the debug log to reconstruct the URL to send the crash report to
+ mCrashHost = "https://";
+ mCrashHost += mDebugLog["CurrentSimHost"].asString();
+ mCrashHost += ":12043/crash/report";
+ mAltCrashHost = "https://";
+ mAltCrashHost += mDebugLog["GridUtilHost"].asString();
+ mAltCrashHost += ":12043/crash/report";
+
+ mCrashInfo["DebugLog"] = mDebugLog;
+ mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
+ mFileMap["StackTrace"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
+
+ updateApplication("Encoding files...");
+
+ for(std::map<LLString, LLString>::iterator itr = mFileMap.begin(); itr != mFileMap.end(); ++itr)
+ {
+ std::ifstream f((*itr).second.c_str());
+ if(!f.is_open())
+ {
+ std::cout << "Can't find file " << (*itr).second.c_str() << std::endl;
+ continue;
+ }
+ std::stringstream s;
+ s << f.rdbuf();
+ mCrashInfo[(*itr).first] = s.str();
+ }
+}
+
+LLSD LLCrashLogger::constructPostData()
+{
+ LLSD ret;
+
+ if(mCrashInPreviousExec)
+ {
+ mCrashInfo["CrashInPreviousExecution"] = "Y";
+ }
+
+ return mCrashInfo;
+}
+
+S32 LLCrashLogger::loadCrashBehaviorSetting()
+{
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+
+ mCrashSettings.loadFromFile(filename);
+
+ S32 value = mCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
+
+ if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK;
+
+ return value;
+}
+
+bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
+{
+ if (crash_behavior < CRASH_BEHAVIOR_ASK) return false;
+ if (crash_behavior > CRASH_BEHAVIOR_NEVER_SEND) return false;
+
+ mCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior);
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+
+ mCrashSettings.saveToFile(filename, FALSE);
+
+ return true;
+}
+
+bool LLCrashLogger::sendCrashLogs()
+{
+ 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();
+ LLHTTPClient::post(mCrashHost, post_data, new LLCrashLoggerResponder(), 5);
+
+ gBreak = false;
+ while(!gBreak)
+ {
+ updateApplication("Sending logs...");
+ }
+
+ if(!gSent)
+ {
+ gBreak = false;
+ LLHTTPClient::post(mAltCrashHost, post_data, new LLCrashLoggerResponder(), 5);
+
+ while(!gBreak)
+ {
+ updateApplication("Sending logs to Alternate Server...");
+ }
+ }
+ mSentCrashLogs = gSent;
+
+ return true;
+}
+
+void LLCrashLogger::updateApplication(LLString message)
+{
+ gServicePump->pump();
+ gServicePump->callback();
+}
+
+bool LLCrashLogger::init()
+{
+ // We assume that all the logs we're looking for reside on the current drive
+ gDirUtilp->initAppDirs("SecondLife");
+
+ // Default to the product name "Second Life" (this is overridden by the -name argument)
+ mProductName = "Second Life";
+
+ mCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "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();
+
+ //Run through command line options
+ if(getOption("previous").isDefined())
+ {
+ llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
+ mCrashInPreviousExec = TRUE;
+ }
+
+ if(getOption("dialog").isDefined())
+ {
+ llinfos << "Show the user dialog" << llendl;
+ mCrashBehavior = CRASH_BEHAVIOR_ASK;
+ }
+
+ LLSD server = getOption("user");
+ if(server.isDefined())
+ {
+ mGridName = server.asString();
+ llinfos << "Got userserver " << mGridName << llendl;
+ }
+ else
+ {
+ mGridName = "agni";
+ }
+
+ LLSD name = getOption("name");
+ if(name.isDefined())
+ {
+ mProductName = name.asString();
+ }
+
+ // 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);
+ return true;
+}
diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h new file mode 100755 index 0000000000..d3218ab81c --- /dev/null +++ b/indra/llcrashlogger/llcrashlogger.h @@ -0,0 +1,85 @@ +/**
+* @file llcrashlogger.h
+* @brief Crash Logger Definition
+*
+* $LicenseInfo:firstyear=2003&license=viewergpl$
+*
+* Copyright (c) 2003-2007, Linden Research, Inc.
+*
+* Second Life Viewer Source Code
+* The source code in this file ("Source Code") is provided by Linden Lab
+* to you under the terms of the GNU General Public License, version 2.0
+* ("GPL"), unless you have obtained a separate licensing agreement
+* ("Other License"), formally executed by you and Linden Lab. Terms of
+* the GPL can be found in doc/GPL-license.txt in this distribution, or
+* online at http://secondlife.com/developers/opensource/gplv2
+*
+* There are special exceptions to the terms and conditions of the GPL as
+* it is applied to this Source Code. View the full text of the exception
+* in the file doc/FLOSS-exception.txt in this software distribution, or
+* online at http://secondlife.com/developers/opensource/flossexception
+*
+* By copying, modifying or distributing this software, you acknowledge
+* that you have read and understood your obligations described above,
+* and agree to abide by those obligations.
+*
+* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+* COMPLETENESS OR PERFORMANCE.
+* $/LicenseInfo$
+*/
+#ifndef LLCRASHLOGGER_H
+#define LLCRASHLOGGER_H
+
+#include <vector>
+
+#include "linden_common.h"
+
+#include "llapp.h"
+#include "llsd.h"
+#include "llcontrol.h"
+
+class LLCrashLogger : public LLApp
+{
+public:
+ LLCrashLogger();
+ virtual ~LLCrashLogger();
+ S32 loadCrashBehaviorSetting();
+ void gatherFiles();
+ virtual void gatherPlatformSpecificFiles() {}
+ bool saveCrashBehaviorSetting(S32 crash_behavior);
+ bool sendCrashLogs();
+ LLSD constructPostData();
+ virtual void updateApplication(LLString message = "");
+ virtual bool init();
+ virtual bool mainLoop() = 0;
+ virtual bool cleanup() { return true; }
+ void setUserText(LLString& text) { mCrashInfo["UserNotes"] = text; }
+ S32 getCrashBehavior() { return mCrashBehavior; }
+protected:
+ S32 mCrashBehavior;
+ BOOL mCrashInPreviousExec;
+ std::map<LLString, LLString> mFileMap;
+ static const int mMaxSendSize = 200000;
+ LLString mGridName;
+ LLControlGroup mCrashSettings;
+ LLString mProductName;
+ LLSD mCrashInfo;
+ LLString mCrashHost;
+ LLString mAltCrashHost;
+ LLSD mDebugLog;
+ bool mSentCrashLogs;
+};
+
+class LLCrashLoggerText : public LLCrashLogger
+{
+public:
+ LLCrashLoggerText(void) {}
+ ~LLCrashLoggerText(void) {}
+
+ virtual bool mainLoop();
+ virtual void updateApplication(LLString message = "");
+};
+
+
+#endif //LLCRASHLOGGER_H
diff --git a/indra/llmessage/llcircuit.cpp b/indra/llmessage/llcircuit.cpp index 1d1be56349..0db9f8e2f1 100644 --- a/indra/llmessage/llcircuit.cpp +++ b/indra/llmessage/llcircuit.cpp @@ -1170,13 +1170,11 @@ std::ostream& operator<<(std::ostream& s, LLCircuitData& circuit) return s; } -const LLString LLCircuitData::getInfoString() const +void LLCircuitData::getInfo(LLSD& info) const { - std::ostringstream info; - info << "Circuit: " << mHost << std::endl - << (mbAlive ? "Alive" : "Not Alive") << std::endl - << "Age: " << mExistenceTimer.getElapsedTimeF32() << std::endl; - return LLString(info.str()); + info["Host"] = mHost.getIPandPort(); + info["Alive"] = mbAlive; + info["Age"] = mExistenceTimer.getElapsedTimeF32(); } void LLCircuitData::dumpResendCountAndReset() @@ -1200,17 +1198,16 @@ std::ostream& operator<<(std::ostream& s, LLCircuit &circuit) return s; } -const LLString LLCircuit::getInfoString() const +void LLCircuit::getInfo(LLSD& info) const { - std::ostringstream info; - info << "Circuit Info:" << std::endl; LLCircuit::circuit_data_map::const_iterator end = mCircuitData.end(); LLCircuit::circuit_data_map::const_iterator it; + LLSD circuit_info; for(it = mCircuitData.begin(); it != end; ++it) { - info << (*it).second->getInfoString() << std::endl; + (*it).second->getInfo(circuit_info); + info["Circuits"].append(circuit_info); } - return LLString(info.str()); } void LLCircuit::getCircuitRange( diff --git a/indra/llmessage/llcircuit.h b/indra/llmessage/llcircuit.h index 128b1bc222..1a6611f5d4 100644 --- a/indra/llmessage/llcircuit.h +++ b/indra/llmessage/llcircuit.h @@ -75,6 +75,7 @@ const S32 LL_MAX_ACKED_PACKETS_PER_FRAME = 200; // class LLMessageSystem; class LLEncodedDatagramService; +class LLSD; // // Classes @@ -158,7 +159,7 @@ public: // void checkPeriodTime(); // Reset per-period counters if necessary. friend std::ostream& operator<<(std::ostream& s, LLCircuitData &circuit); - const LLString getInfoString() const; + void getInfo(LLSD& info) const; friend class LLCircuit; friend class LLMessageSystem; @@ -304,7 +305,7 @@ public: void sendAcks(); friend std::ostream& operator<<(std::ostream& s, LLCircuit &circuit); - const LLString getInfoString() const; + void getInfo(LLSD& info) const; void dumpResends(); diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index e2cd19b264..d446730c33 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -1562,12 +1562,9 @@ U32 LLMessageSystem::getOurCircuitCode() return mOurCircuitCode; } -LLString LLMessageSystem::getCircuitInfoString() +void LLMessageSystem::getCircuitInfo(LLSD& info) const { - LLString info_string; - - info_string += mCircuitInfo.getInfoString(); - return info_string; + mCircuitInfo.getInfo(info); } // returns whether the given host is on a trusted circuit diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index 4454b40ab9..3381ece222 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -529,7 +529,7 @@ public: bool isMatchingDigestForWindow(const char* digest, const S32 window) const; void showCircuitInfo(); - LLString getCircuitInfoString(); + void getCircuitInfo(LLSD& info) const; U32 getOurCircuitCode(); diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llvfs/lldir_win32.cpp index b5db6efb4d..f415fe56ed 100644 --- a/indra/llvfs/lldir_win32.cpp +++ b/indra/llvfs/lldir_win32.cpp @@ -42,7 +42,6 @@ #include <errno.h> #include <sys/types.h> #include <sys/stat.h> -#include <errno.h> // Utility stuff to get versions of the sh #define PACKVERSION(major,minor) MAKELONG(minor,major) diff --git a/indra/llwindow/lldxhardware.cpp b/indra/llwindow/lldxhardware.cpp index 1a39e68e3c..2aff05232e 100644 --- a/indra/llwindow/lldxhardware.cpp +++ b/indra/llwindow/lldxhardware.cpp @@ -411,6 +411,7 @@ BOOL LLDXHardware::getInfo(BOOL vram_only) } std::string device_name = get_string(device_containerp, L"szDescription"); + std::string device_id = get_string(device_containerp, L"szDeviceID"); LLDXDevice *dxdevicep = new LLDXDevice; @@ -451,6 +452,8 @@ BOOL LLDXHardware::getInfo(BOOL vram_only) } + + // Now, iterate through the related drivers hr = device_containerp->GetChildContainer(L"Drivers", &driver_containerp); if (FAILED(hr) || !driver_containerp) @@ -468,6 +471,7 @@ BOOL LLDXHardware::getInfo(BOOL vram_only) S32 file_num = 0; for (file_num = 0; file_num < (S32)num_files; file_num++ ) { + hr = driver_containerp->EnumChildContainerNames(file_num, wszContainer, 256); if (FAILED(hr)) { @@ -522,6 +526,104 @@ LCleanup: return ok; } +LLSD LLDXHardware::getDisplayInfo() +{ + LLTimer hw_timer; + HRESULT hr; + LLSD ret; + CoInitialize(NULL); + + IDxDiagProvider *dx_diag_providerp = NULL; + IDxDiagContainer *dx_diag_rootp = NULL; + IDxDiagContainer *devices_containerp = NULL; + IDxDiagContainer *device_containerp = NULL; + IDxDiagContainer *file_containerp = NULL; + IDxDiagContainer *driver_containerp = NULL; + + // CoCreate a IDxDiagProvider* + llinfos << "CoCreateInstance IID_IDxDiagProvider" << llendl; + hr = CoCreateInstance(CLSID_DxDiagProvider, + NULL, + CLSCTX_INPROC_SERVER, + IID_IDxDiagProvider, + (LPVOID*) &dx_diag_providerp); + + if (FAILED(hr)) + { + llwarns << "No DXDiag provider found! DirectX 9 not installed!" << llendl; + gWriteDebug("No DXDiag provider found! DirectX 9 not installed!\n"); + goto LCleanup; + } + if (SUCCEEDED(hr)) // if FAILED(hr) then dx9 is not installed + { + // Fill out a DXDIAG_INIT_PARAMS struct and pass it to IDxDiagContainer::Initialize + // Passing in TRUE for bAllowWHQLChecks, allows dxdiag to check if drivers are + // digital signed as logo'd by WHQL which may connect via internet to update + // WHQL certificates. + DXDIAG_INIT_PARAMS dx_diag_init_params; + ZeroMemory(&dx_diag_init_params, sizeof(DXDIAG_INIT_PARAMS)); + + dx_diag_init_params.dwSize = sizeof(DXDIAG_INIT_PARAMS); + dx_diag_init_params.dwDxDiagHeaderVersion = DXDIAG_DX9_SDK_VERSION; + dx_diag_init_params.bAllowWHQLChecks = TRUE; + dx_diag_init_params.pReserved = NULL; + + llinfos << "dx_diag_providerp->Initialize" << llendl; + hr = dx_diag_providerp->Initialize(&dx_diag_init_params); + if(FAILED(hr)) + { + goto LCleanup; + } + + llinfos << "dx_diag_providerp->GetRootContainer" << llendl; + hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp ); + if(FAILED(hr) || !dx_diag_rootp) + { + goto LCleanup; + } + + HRESULT hr; + + // Get display driver information + llinfos << "dx_diag_rootp->GetChildContainer" << llendl; + hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp); + if(FAILED(hr) || !devices_containerp) + { + goto LCleanup; + } + + // Get device 0 + llinfos << "devices_containerp->GetChildContainer" << llendl; + hr = devices_containerp->GetChildContainer(L"0", &device_containerp); + if(FAILED(hr) || !device_containerp) + { + goto LCleanup; + } + + // Get the English VRAM string + std::string ram_str = get_string(device_containerp, L"szDisplayMemoryEnglish"); + + + // Dump the string as an int into the structure + char *stopstring; + ret["VRAM"] = strtol(ram_str.c_str(), &stopstring, 10); + std::string device_name = get_string(device_containerp, L"szDescription"); + ret["DeviceName"] = device_name; + std::string device_driver= get_string(device_containerp, L"szDriverVersion"); + ret["DriverVersion"] = device_driver; + } +LCleanup: + SAFE_RELEASE(file_containerp); + SAFE_RELEASE(driver_containerp); + SAFE_RELEASE(device_containerp); + SAFE_RELEASE(devices_containerp); + SAFE_RELEASE(dx_diag_rootp); + SAFE_RELEASE(dx_diag_providerp); + + CoUninitialize(); + return ret; +} + void LLDXHardware::setWriteDebugFunc(void (*func)(const char*)) { gWriteDebug = func; diff --git a/indra/llwindow/lldxhardware.h b/indra/llwindow/lldxhardware.h index 1d5d69d8ca..e2a255da76 100644 --- a/indra/llwindow/lldxhardware.h +++ b/indra/llwindow/lldxhardware.h @@ -36,6 +36,7 @@ #include "stdtypes.h" #include "llstring.h" +#include "llsd.h" class LLVersion { @@ -93,6 +94,8 @@ public: S32 getVRAM() const { return mVRAM; } + LLSD getDisplayInfo(); + // Find a particular device that matches the following specs. // Empty strings indicate that you don't care. // You can separate multiple devices with '|' chars to indicate you want diff --git a/indra/mac_crash_logger/llcrashloggermac.cpp b/indra/mac_crash_logger/llcrashloggermac.cpp new file mode 100644 index 0000000000..ba50af9d00 --- /dev/null +++ b/indra/mac_crash_logger/llcrashloggermac.cpp @@ -0,0 +1,310 @@ +/** + * @file llcrashloggermac.cpp + * @brief Mac OSX crash logger implementation + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + + +#include "llcrashloggermac.h" + +#include <Carbon/Carbon.h> +#include <iostream> +#include <sstream> + +#include "boost/tokenizer.hpp" + +#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME +#include "llerror.h" +#include "llfile.h" +#include "lltimer.h" +#include "llstring.h" +#include "lldir.h" +#include "llsdserialize.h" + +#define MAX_LOADSTRING 100 +const char* const SETTINGS_FILE_HEADER = "version"; +const S32 SETTINGS_FILE_VERSION = 101; + +// Windows Message Handlers + +BOOL gFirstDialog = TRUE; // Are we currently handling the Send/Don't Send dialog? +FILE *gDebugFile = NULL; + +WindowRef gWindow = NULL; +EventHandlerRef gEventHandler = NULL; +LLString gUserNotes = ""; +bool gSendReport = false; +bool gRememberChoice = false; +IBNibRef nib = NULL; + +OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata) +{ + OSStatus result = eventNotHandledErr; + OSStatus err; + UInt32 evtClass = GetEventClass(event); + UInt32 evtKind = GetEventKind(event); + if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess)) + { + HICommand cmd; + err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd); + + + + if(err == noErr) + { + //Get the value of the checkbox + ControlID id; + ControlRef checkBox = NULL; + id.signature = 'remb'; + id.id = 0; + err = GetControlByID(gWindow, &id, &checkBox); + + if(err == noErr) + { + if(GetControl32BitValue(checkBox) == kControlCheckBoxCheckedValue) + { + gRememberChoice = true; + } + else + { + gRememberChoice = false; + } + } + switch(cmd.commandID) + { + case kHICommandOK: + { + char buffer[65535]; /* Flawfinder: ignore */ + Size size = sizeof(buffer) - 1; + ControlRef textField = NULL; + + id.signature = 'text'; + id.id = 0; + + err = GetControlByID(gWindow, &id, &textField); + if(err == noErr) + { + // Get the user response text + err = GetControlData(textField, kControlNoPart, kControlEditTextTextTag, size, (Ptr)buffer, &size); + } + if(err == noErr) + { + // Make sure the string is terminated. + buffer[size] = 0; + //setUserText(buffer); + llinfos << buffer << llendl; + } + + // Send the report. + + QuitAppModalLoopForWindow(gWindow); + gSendReport = true; + result = noErr; + } + break; + + case kHICommandCancel: + QuitAppModalLoopForWindow(gWindow); + result = noErr; + break; + } + } + } + + return(result); +} + + +LLCrashLoggerMac::LLCrashLoggerMac(void) +{ +} + +LLCrashLoggerMac::~LLCrashLoggerMac(void) +{ +} + +bool LLCrashLoggerMac::init(void) +{ + bool ok = LLCrashLogger::init(); + if(!ok) return false; + // Real UI... + OSStatus err; + + err = CreateNibReference(CFSTR("CrashReporter"), &nib); + + if(err == noErr) + { + err = CreateWindowFromNib(nib, CFSTR("CrashReporter"), &gWindow); + } + + if(err == noErr) + { + // Set focus to the edit text area + ControlRef textField = NULL; + ControlID id; + + id.signature = 'text'; + id.id = 0; + + // Don't set err if any of this fails, since it's non-critical. + if(GetControlByID(gWindow, &id, &textField) == noErr) + { + SetKeyboardFocus(gWindow, textField, kControlFocusNextPart); + } + } + + if(err == noErr) + { + ShowWindow(gWindow); + } + + if(err == noErr) + { + // Set up an event handler for the window. + EventTypeSpec handlerEvents[] = + { + { kEventClassCommand, kEventCommandProcess } + }; + + InstallWindowEventHandler( + gWindow, + NewEventHandlerUPP(dialogHandler), + GetEventTypeCount (handlerEvents), + handlerEvents, + 0, + &gEventHandler); + } + return true; +} + +void LLCrashLoggerMac::gatherPlatformSpecificFiles() +{ + updateApplication("Gathering hardware information..."); + char path[MAX_PATH]; + FSRef folder; + + if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr) + { + // folder is an FSRef to ~/Library/Logs/ + if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr) + { + struct stat dw_stat; + LLString mBuf; + // Try the 10.3 path first... + LLString dw_file_name = LLString(path) + LLString("/CrashReporter/Second Life.crash.log"); + int res = stat(dw_file_name.c_str(), &dw_stat); + + if (res) + { + // Try the 10.2 one next... + dw_file_name = LLString(path) + LLString("/Second Life.crash.log"); + res = stat(dw_file_name.c_str(), &dw_stat); + } + + if (!res) + { + std::ifstream fp(dw_file_name.c_str()); + std::stringstream str; + if(!fp.is_open()) return; + str << fp.rdbuf(); + mBuf = str.str(); + + // Crash logs consist of a number of entries, one per crash. + // Each entry is preceeded by "**********" on a line by itself. + // We want only the most recent (i.e. last) one. + const char *sep = "**********"; + const char *start = mBuf.c_str(); + const char *cur = start; + const char *temp = strstr(cur, sep); + + while(temp != NULL) + { + // Skip past the marker we just found + cur = temp + strlen(sep); /* Flawfinder: ignore */ + + // and try to find another + temp = strstr(cur, sep); + } + + // If there's more than one entry in the log file, strip all but the last one. + if(cur != start) + { + mBuf.erase(0, cur - start); + } + mDebugLog["CrashInfo"] = mBuf; + } + else + { + llwarns << "Couldn't find any CrashReporter files..." << llendl; + } + } + } +} + +bool LLCrashLoggerMac::mainLoop() +{ + OSStatus err = noErr; + + if(err == noErr) + { + RunAppModalLoopForWindow(gWindow); + } + + if(gRememberChoice) + { + if(gSendReport) saveCrashBehaviorSetting(CRASH_BEHAVIOR_ALWAYS_SEND); + else saveCrashBehaviorSetting(CRASH_BEHAVIOR_NEVER_SEND); + } + + if(gSendReport) + { + sendCrashLogs(); + } + + if(gWindow != NULL) + { + DisposeWindow(gWindow); + } + + if(nib != NULL) + { + DisposeNibReference(nib); + } + + return true; +} + +void LLCrashLoggerMac::updateApplication(LLString message) +{ + LLCrashLogger::updateApplication(); +} + +bool LLCrashLoggerMac::cleanup() +{ + return true; +} diff --git a/indra/mac_crash_logger/llcrashloggermac.h b/indra/mac_crash_logger/llcrashloggermac.h new file mode 100644 index 0000000000..cf4e766bda --- /dev/null +++ b/indra/mac_crash_logger/llcrashloggermac.h @@ -0,0 +1,51 @@ +/** + * @file llcrashloggermac.h + * @brief Mac OSX crash logger definition + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LLCRASHLOGGERMAC_H +#define LLCRASHLOGGERMAC_H + +#include "linden_common.h" +#include "llcrashlogger.h" +#include "llstring.h" + +class LLCrashLoggerMac : public LLCrashLogger +{ +public: + LLCrashLoggerMac(void); + ~LLCrashLoggerMac(void); + virtual bool init(); + virtual bool mainLoop(); + virtual void updateApplication(LLString message = ""); + virtual bool cleanup(); + virtual void gatherPlatformSpecificFiles(); +}; + +#endif diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp index 2501b4a521..bf3151a490 100644 --- a/indra/mac_crash_logger/mac_crash_logger.cpp +++ b/indra/mac_crash_logger/mac_crash_logger.cpp @@ -31,675 +31,21 @@ #include "linden_common.h" -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> - -#include <curl/curl.h> - -#include "llerror.h" -#include "lltimer.h" -#include "lldir.h" - -#include "llstring.h" - -class LLFileEncoder -{ -public: - LLFileEncoder(const char *formname, const char *filename, bool isCrashLog = false); - - BOOL isValid() const { return mIsValid; } - LLString encodeURL(const S32 max_length = 0); -public: - BOOL mIsValid; - LLString mFilename; - LLString mFormname; - LLString mBuf; -}; - -LLString encode_string(const char *formname, const LLString &str); - -#include <Carbon/Carbon.h> - -LLString gServerResponse; -BOOL gSendReport = FALSE; -LLString gUserserver; -LLString gUserText; -WindowRef gWindow = NULL; -EventHandlerRef gEventHandler = NULL; -BOOL gCrashInPreviousExec = FALSE; -time_t gLaunchTime; - -size_t curl_download_callback(void *data, size_t size, size_t nmemb, - void *user_data) -{ - S32 bytes = size * nmemb; - char *cdata = (char *) data; - for (int i =0; i < bytes; i += 1) - { - gServerResponse += (cdata[i]); - } - return bytes; -} - -OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata) -{ - OSStatus result = eventNotHandledErr; - OSStatus err; - UInt32 evtClass = GetEventClass(event); - UInt32 evtKind = GetEventKind(event); - - if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess)) - { - HICommand cmd; - err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd); - - if(err == noErr) - { - switch(cmd.commandID) - { - case kHICommandOK: - { - char buffer[65535]; /* Flawfinder: ignore */ - Size size = sizeof(buffer) - 1; - ControlRef textField = NULL; - ControlID id; - - id.signature = 'text'; - id.id = 0; - - err = GetControlByID(gWindow, &id, &textField); - if(err == noErr) - { - // Get the user response text - err = GetControlData(textField, kControlNoPart, kControlEditTextTextTag, size, (Ptr)buffer, &size); - } - if(err == noErr) - { - // Make sure the string is terminated. - buffer[size] = 0; - gUserText = buffer; - llinfos << buffer << llendl; - } - - // Send the report. - gSendReport = TRUE; - - QuitAppModalLoopForWindow(gWindow); - result = noErr; - } - break; - - case kHICommandCancel: - QuitAppModalLoopForWindow(gWindow); - result = noErr; - break; - } - } - } - - return(result); -} +#include "llcrashloggermac.h" int main(int argc, char **argv) { - const S32 DW_MAX_SIZE = 100000; // Maximum size to transmit of the Dr. Watson log file - const S32 SL_MAX_SIZE = 100000; // Maximum size of the Second Life log file. - int i; - - time(&gLaunchTime); + //time(&gLaunchTime); llinfos << "Starting Second Life Viewer Crash Reporter" << llendl; - - for(i=1; i<argc; i++) - { - if(!strcmp(argv[i], "-previous")) - { - gCrashInPreviousExec = TRUE; - } - if(!strcmp(argv[i], "-user")) - { - if ((i + 1) < argc) - { - i++; - gUserserver = argv[i]; - llinfos << "Got userserver " << gUserserver << llendl; - } - } - } - - if( gCrashInPreviousExec ) - { - llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl; - } - - if(!gCrashInPreviousExec) - { - // Delay five seconds to let CrashReporter do its thing. - sleep(5); - } - -#if 1 - // Real UI... - OSStatus err; - IBNibRef nib = NULL; - - err = CreateNibReference(CFSTR("CrashReporter"), &nib); - - if(err == noErr) - { - if(gCrashInPreviousExec) - { - err = CreateWindowFromNib(nib, CFSTR("CrashReporterDelayed"), &gWindow); - } - else - { - err = CreateWindowFromNib(nib, CFSTR("CrashReporter"), &gWindow); - } - } - - if(err == noErr) - { - // Set focus to the edit text area - ControlRef textField = NULL; - ControlID id; - - id.signature = 'text'; - id.id = 0; - - // Don't set err if any of this fails, since it's non-critical. - if(GetControlByID(gWindow, &id, &textField) == noErr) - { - SetKeyboardFocus(gWindow, textField, kControlFocusNextPart); - } - } - - if(err == noErr) - { - ShowWindow(gWindow); - } - - if(err == noErr) - { - // Set up an event handler for the window. - EventTypeSpec handlerEvents[] = - { - { kEventClassCommand, kEventCommandProcess } - }; - - InstallWindowEventHandler( - gWindow, - NewEventHandlerUPP(dialogHandler), - GetEventTypeCount (handlerEvents), - handlerEvents, - 0, - &gEventHandler); - } - - if(err == noErr) - { - RunAppModalLoopForWindow(gWindow); - } - - if(gWindow != NULL) - { - DisposeWindow(gWindow); - } - - if(nib != NULL) - { - DisposeNibReference(nib); - } -#else - // Cheap version -- just use the standard system alert. - SInt16 itemHit = 0; - AlertStdCFStringAlertParamRec params; - OSStatus err = noErr; - DialogRef alert = NULL; - - params.version = kStdCFStringAlertVersionOne; - params.movable = false; - params.helpButton = false; - params.defaultText = CFSTR("Send Report"); - params.cancelText = CFSTR("Don't Send Report"); - params.otherText = 0; - params.defaultButton = kAlertStdAlertOKButton; - params.cancelButton = kAlertStdAlertCancelButton; - params.position = kWindowDefaultPosition; - params.flags = 0; - - err = CreateStandardAlert( - kAlertCautionAlert, - CFSTR("Second Life appears to have crashed."), - CFSTR( - "To help us debug the problem, you can send a crash report to Linden Lab. " - "The report contains information about your microprocessor type, graphics card, " - "memory, things that happened during the last run of the program, and the Crash Log " - "of where the crash occurred.\r" - "\r" - "You may also report crashes in the forums, or by sending e-mail to peter@lindenlab.com"), - ¶ms, - &alert); - - if(err == noErr) - { - err = RunStandardAlert( - alert, - NULL, - &itemHit); - } - - if(itemHit == kAlertStdAlertOKButton) - gSendReport = TRUE; -#endif - - if(!gSendReport) - { - // Only send the report if the user agreed to it. - llinfos << "User cancelled, not sending report" << llendl; - - return(0); - } - - // We assume that all the logs we're looking for reside on the current drive - gDirUtilp->initAppDirs("SecondLife"); - - int res; - - // Lots of silly variable, replicated for each log file. - LLString db_file_name; - LLString sl_file_name; - LLString dw_file_name; // DW file name is a hack for now... - LLString st_file_name; // stats.log file - LLString si_file_name; // settings.ini file - - LLFileEncoder *db_filep = NULL; - LLFileEncoder *sl_filep = NULL; - LLFileEncoder *st_filep = NULL; - LLFileEncoder *dw_filep = NULL; - LLFileEncoder *si_filep = NULL; - - /////////////////////////////////// - // - // We do the parsing for the debug_info file first, as that will - // give us the location of the SecondLife.log file. - // - - // Figure out the filename of the debug log - db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str(); - db_filep = new LLFileEncoder("DB", db_file_name.c_str()); - - // Get the filename of the SecondLife.log file - - // *NOTE: changing the size of either of these buffers will - // require changing the sscanf() format string to correctly - // account for it. - char tmp_sl_name[LL_MAX_PATH]; /* Flawfinder: ignore */ - tmp_sl_name[0] = '\0'; - char tmp_space[MAX_STRING]; /* Flawfinder: ignore */ - tmp_space[0] = '\0'; - - // Look for it in the debug_info.log file - if (db_filep->isValid()) - { - // This was originally scanning for "SL Log: %[^\r\n]", which happily skipped to the next line - // on debug logs (which don't have anything after "SL Log:" and tried to open a nonsensical filename. - sscanf( - db_filep->mBuf.c_str(), - "SL Log:%254[ ]%1023[^\r\n]", - tmp_space, - tmp_sl_name); - } - else - { - delete db_filep; - db_filep = NULL; - } - - // If we actually have a legitimate file name, use it. - if (tmp_sl_name[0]) - { - sl_file_name = tmp_sl_name; - llinfos << "Using log file from debug log " << sl_file_name << llendl; - } - else - { - // Figure out the filename of the second life log - sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log").c_str(); - } - - // Now we get the SecondLife.log file if it's there, and recent enough... - sl_filep = new LLFileEncoder("SL", sl_file_name.c_str()); - if (!sl_filep->isValid()) - { - delete sl_filep; - sl_filep = NULL; - } - - st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log").c_str(); - st_filep = new LLFileEncoder("ST", st_file_name.c_str()); - if (!st_filep->isValid()) - { - delete st_filep; - st_filep = NULL; - } - - si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.ini").c_str(); - si_filep = new LLFileEncoder("SI", si_file_name.c_str()); - if (!si_filep->isValid()) - { - delete si_filep; - si_filep = NULL; - } - - // MBW -- This needs to go find "~/Library/Logs/CrashReporter/Second Life.crash.log" on 10.3 - // or "~/Library/Logs/Second Life.crash.log" on 10.2. - { - char path[MAX_PATH]; /* Flawfinder: ignore */ - FSRef folder; - - if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr) - { - // folder is an FSRef to ~/Library/Logs/ - if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr) - { - struct stat dw_stat; -// printf("path is %s\n", path); - - // Try the 10.3 path first... - dw_file_name = LLString(path) + LLString("/CrashReporter/Second Life.crash.log"); - res = stat(dw_file_name.c_str(), &dw_stat); - - if (res) - { - // Try the 10.2 one next... - dw_file_name = LLString(path) + LLString("/Second Life.crash.log"); - res = stat(dw_file_name.c_str(), &dw_stat); - } - - if (!res) - { - dw_filep = new LLFileEncoder("DW", dw_file_name.c_str(), true); - if (!dw_filep->isValid()) - { - delete dw_filep; - dw_filep = NULL; - } - } - else - { - llwarns << "Couldn't find any CrashReporter files..." << llendl; - } - } - } - } - - LLString post_data; - LLString tmp_url_buf; - - // Append the userserver - tmp_url_buf = encode_string("USER", gUserserver); - post_data += tmp_url_buf; - llinfos << "PostData:" << post_data << llendl; - - if (gCrashInPreviousExec) - { - post_data.append("&"); - tmp_url_buf = encode_string("EF", "Y"); - post_data += tmp_url_buf; - } - - if (db_filep) - { - post_data.append("&"); - tmp_url_buf = db_filep->encodeURL(); - post_data += tmp_url_buf; - llinfos << "Sending DB log file" << llendl; - } - else - { - llinfos << "Not sending DB log file" << llendl; - } - - if (sl_filep) - { - post_data.append("&"); - tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE); - post_data += tmp_url_buf; - llinfos << "Sending SL log file" << llendl; - } - else - { - llinfos << "Not sending SL log file" << llendl; - } - - if (st_filep) - { - post_data.append("&"); - tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE); - post_data += tmp_url_buf; - llinfos << "Sending stats log file" << llendl; - } - else - { - llinfos << "Not sending stats log file" << llendl; - } - if (dw_filep) + LLCrashLoggerMac app; + app.parseCommandOptions(argc, argv); + if(!app.init()) { - post_data.append("&"); - tmp_url_buf = dw_filep->encodeURL(DW_MAX_SIZE); - post_data += tmp_url_buf; + return 0; } - else - { - llinfos << "Not sending crash log file" << llendl; - } - - if (si_filep) - { - post_data.append("&"); - tmp_url_buf = si_filep->encodeURL(); - post_data += tmp_url_buf; - llinfos << "Sending settings log file" << llendl; - } - else - { - llinfos << "Not sending settings.ini file" << llendl; - } - - if (gUserText.size()) - { - post_data.append("&"); - tmp_url_buf = encode_string("UN", gUserText); - post_data += tmp_url_buf; - } - - delete db_filep; - db_filep = NULL; - delete sl_filep; - sl_filep = NULL; - delete dw_filep; - dw_filep = NULL; - - // Debugging spam -#if 0 - printf("Crash report post data:\n--------\n"); - printf("%s", post_data.getString()); - printf("\n--------\n"); -#endif - - // Send the report. Yes, it's this easy. - { - CURL *curl = curl_easy_init(); - - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback); - curl_easy_setopt(curl, CURLOPT_POST, 1); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str()); - curl_easy_setopt(curl, CURLOPT_URL, "http://secondlife.com/cgi-bin/viewer_crash_reporter2"); - - llinfos << "Connecting to crash report server" << llendl; - CURLcode result = curl_easy_perform(curl); - - curl_easy_cleanup(curl); + app.mainLoop(); - if(result != CURLE_OK) - { - llinfos << "Couldn't talk to crash report server" << llendl; - } - else - { - llinfos << "Response from crash report server:" << llendl; - llinfos << gServerResponse << llendl; - } - } - return 0; } - -LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename, bool isCrashLog) -{ - mFormname = form_name; - mFilename = filename; - mIsValid = FALSE; - - int res; - - struct stat stat_data; - res = stat(mFilename.c_str(), &stat_data); - if (res) - { - llwarns << "File " << mFilename << " is missing!" << llendl; - return; - } - else - { - // Debugging spam -// llinfos << "File " << mFilename << " is present..." << llendl; - - if(!gCrashInPreviousExec && isCrashLog) - { - // Make sure the file isn't too old. - double age = difftime(gLaunchTime, stat_data.st_mtimespec.tv_sec); - -// llinfos << "age is " << age << llendl; - - if(age > 60.0) - { - // The file was last modified more than 60 seconds before the crash reporter was launched. Assume it's stale. - llwarns << "File " << mFilename << " is too old!" << llendl; - return; - } - } - - } - - S32 buf_size = stat_data.st_size; - FILE* fp = fopen(mFilename.c_str(), "rb"); /* Flawfinder: ignore */ - U8 *buf = new U8[buf_size + 1]; - fread(buf, 1, buf_size, fp); - fclose(fp); - buf[buf_size] = 0; - - mBuf = (char *)buf; - - if(isCrashLog) - { - // Crash logs consist of a number of entries, one per crash. - // Each entry is preceeded by "**********" on a line by itself. - // We want only the most recent (i.e. last) one. - const char *sep = "**********"; - const char *start = mBuf.c_str(); - const char *cur = start; - const char *temp = strstr(cur, sep); - - while(temp != NULL) - { - // Skip past the marker we just found - cur = temp + strlen(sep); /* Flawfinder: ignore */ - - // and try to find another - temp = strstr(cur, sep); - } - - // If there's more than one entry in the log file, strip all but the last one. - if(cur != start) - { - mBuf.erase(0, cur - start); - } - } - - mIsValid = TRUE; - delete[] buf; -} - -LLString LLFileEncoder::encodeURL(const S32 max_length) -{ - LLString result = mFormname; - result.append("="); - - S32 i = 0; - - if (max_length) - { - if (mBuf.size() > max_length) - { - i = mBuf.size() - max_length; - } - } - -#if 0 - // Plain text version for debugging - result.append(mBuf); -#else - // Not using LLString because of bad performance issues - S32 buf_size = mBuf.size(); - S32 url_buf_size = 3*mBuf.size() + 1; - char *url_buf = new char[url_buf_size]; - if (url_buf == NULL) - { - llerrs << "Memory Allocation Failed" << llendl; - return result; - } - S32 cur_pos = 0; - for (; i < buf_size; i++) - { - sprintf(url_buf + cur_pos, "%%%02x", mBuf[i]); /* Flawfinder: ignore */ - cur_pos += 3; - } - url_buf[i*3] = 0; - - result.append(url_buf); - delete[] url_buf; -#endif - return result; -} - -LLString encode_string(const char *formname, const LLString &str) -{ - LLString result = formname; - result.append("="); - // Not using LLString because of bad performance issues - S32 buf_size = str.size(); - S32 url_buf_size = 3*str.size() + 1; - char *url_buf = new char[url_buf_size]; - if (url_buf == NULL) - { - llerrs << "Memory Allocation Failed" << llendl; - return result; - } - - S32 cur_pos = 0; - S32 i; - for (i = 0; i < buf_size; i++) - { - sprintf(url_buf + cur_pos, "%%%02x", str[i]); /* Flawfinder: ignore */ - cur_pos += 3; - } - url_buf[i*3] = 0; - - result.append(url_buf); - delete[] url_buf; - return result; -} diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c0e9833829..63d1986dec 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -106,6 +106,8 @@ #include "llcontainerview.h" #include "llhoverview.h" +#include "llsdserialize.h" + #if LL_WINDOWS && LL_LCD_COMPILE #include "lllcd.h" #endif @@ -267,6 +269,8 @@ BOOL gAcceptCriticalMessage = FALSE; LLUUID gViewerDigest; // MD5 digest of the viewer's executable file. BOOL gLastExecFroze = FALSE; +LLSD gDebugInfo; + U32 gFrameCount = 0; U32 gForegroundFrameCount = 0; // number of frames that app window was in foreground LLPumpIO* gServicePump = NULL; @@ -915,7 +919,6 @@ LLTextureFetch* LLAppViewer::sTextureFetch = NULL; LLAppViewer::LLAppViewer() : mMarkerFile(NULL), mLastExecFroze(false), - mDebugFile(NULL), mCrashBehavior(CRASH_BEHAVIOR_ASK), mReportedCrash(false), mNumSessions(0), @@ -1220,7 +1223,7 @@ bool LLAppViewer::init() CreateLCDDebugWindows(); #endif - writeDebug(gGLManager.getGLInfoString()); + gGLManager.getGLInfo(gDebugInfo); llinfos << gGLManager.getGLInfoString() << llendl; //load key settings @@ -2351,30 +2354,13 @@ bool LLAppViewer::initWindow() return true; } -void LLAppViewer::writeDebug(const char *str) -{ - if (!mDebugFile) - { - std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log"); - llinfos << "Opening debug file " << debug_filename << llendl; - mDebugFile = LLFile::fopen(debug_filename.c_str(), "w"); /* Flawfinder: ignore */ - if (!mDebugFile) - { - llinfos << "Opening debug file " << debug_filename << " failed. Using stderr." << llendl; - mDebugFile = stderr; - } - } - fputs(str, mDebugFile); - fflush(mDebugFile); -} - void LLAppViewer::closeDebug() { - if (mDebugFile) - { - fclose(mDebugFile); - } - mDebugFile = NULL; + std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log"); + llinfos << "Opening debug file " << debug_filename << llendl; + std::ofstream out_file(debug_filename.c_str()); + LLSDSerialize::toPrettyXML(gDebugInfo, out_file); + out_file.close(); } void LLAppViewer::cleanupSavedSettings() @@ -2443,23 +2429,22 @@ void LLAppViewer::removeCacheFiles(const char* file_mask) void LLAppViewer::writeSystemInfo() { - writeDebug("SL Log: "); - writeDebug(LLError::logFileName()); - writeDebug("\n"); - - std::string tmp_str = gSecondLife - + llformat(" version %d.%d.%d build %d", - LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VERSION_BUILD); - writeDebug(tmp_str.c_str()); - writeDebug("\n"); - writeDebug(gSysCPU.getCPUString()); - writeDebug("\n"); + gDebugInfo["SLLog"] = LLError::logFileName(); + + gDebugInfo["ClientInfo"]["Name"] = gSecondLife; + gDebugInfo["ClientInfo"]["MajorVersion"] = LL_VERSION_MAJOR; + gDebugInfo["ClientInfo"]["MinorVersion"] = LL_VERSION_MINOR; + gDebugInfo["ClientInfo"]["PatchVersion"] = LL_VERSION_PATCH; + gDebugInfo["ClientInfo"]["BuildVersion"] = LL_VERSION_BUILD; + + gDebugInfo["CPUInfo"]["CPUFamily"] = gSysCPU.getFamily(); + gDebugInfo["CPUInfo"]["CPUMhz"] = gSysCPU.getMhz(); + gDebugInfo["CPUInfo"]["CPUAltivec"] = gSysCPU.hasAltivec(); + gDebugInfo["CPUInfo"]["CPUSSE"] = gSysCPU.hasSSE(); + gDebugInfo["CPUInfo"]["CPUSSE2"] = gSysCPU.hasSSE2(); - tmp_str = llformat("RAM: %u KB\n", gSysMemory.getPhysicalMemoryKB()); - writeDebug(tmp_str.c_str()); - writeDebug("OS: "); - writeDebug(getOSInfo().getOSString().c_str()); - writeDebug("\n"); + gDebugInfo["RAMInfo"] = llformat("%u", gSysMemory.getPhysicalMemoryKB()); + gDebugInfo["OSInfo"] = mSysOSInfo.getOSString().c_str(); // Dump some debugging info llinfos << gSecondLife << " version " @@ -2498,16 +2483,11 @@ void LLAppViewer::handleViewerCrash() } pApp->mReportedCrash = TRUE; - BOOL do_crash_report = FALSE; - - do_crash_report = TRUE; - - pApp->writeDebug("Viewer exe: "); - pApp->writeDebug(gDirUtilp->getExecutablePathAndName().c_str()); - pApp->writeDebug("\n"); - pApp->writeDebug("Cur path: "); - pApp->writeDebug(gDirUtilp->getCurPath().c_str()); - pApp->writeDebug("\n\n"); + gDebugInfo["SettingsFilename"] = gSettingsFileName; + gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); + gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName().c_str(); + gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath().c_str(); + gDebugInfo["CurrentSimHost"] = gAgent.getRegionHost().getHostName(); if (gMessageSystem && gDirUtilp) { @@ -2517,27 +2497,24 @@ void LLAppViewer::handleViewerCrash() if(file.good()) { gMessageSystem->summarizeLogs(file); + file.close(); } } if (gMessageSystem) { - pApp->writeDebug(gMessageSystem->getCircuitInfoString()); + gMessageSystem->getCircuitInfo(gDebugInfo["CircuitInfo"]); gMessageSystem->stopLogging(); } - pApp->writeDebug("\n"); if (gWorldp) { - pApp->writeDebug(gWorldp->getInfoString()); + gWorldp->getInfo(gDebugInfo); } // Close the debug file pApp->closeDebug(); LLError::logToFile(""); - // Close the SecondLife.log - //pApp->removeMarkerFile(); - // Call to pure virtual, handled by platform specifc llappviewer instance. pApp->handleCrashReporting(); diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 227a27a8ac..e97aead955 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -68,9 +68,6 @@ public: // This version stores the argc and argv for later usage, make sure the params passed in last as long as this class. bool tempStoreCommandOptions(int argc, char** argv); - // write string to "debug_info.log", used for crash reporting. - void writeDebug(const char *str); - void writeDebug(const std::string& str) { writeDebug(str.c_str()); }; void closeDebug(); const LLOSInfo& getOSInfo() const { return mSysOSInfo; } @@ -165,8 +162,6 @@ private: FILE *mMarkerFile; // A file created to indicate the app is running. bool mLastExecFroze; // Set on init if the marker file was found. - FILE* mDebugFile; // output stream written to via writeDebug() - LLOSInfo mSysOSInfo; S32 mCrashBehavior; bool mReportedCrash; @@ -202,6 +197,7 @@ extern BOOL gProbeHardware; extern LLString gDisabledMessage; // llstartup extern BOOL gHideLinks; // used by llpanellogin, lllfloaterbuycurrency, llstartup extern BOOL gInProductionGrid; +extern LLSD gDebugInfo; extern BOOL gAllowIdleAFK; extern F32 gAFKTimeout; diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 1d7a6690fc..814c209e67 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -322,9 +322,16 @@ bool LLAppViewerWin32::initWindow() return LLAppViewer::initWindow(); } -void write_debug_callback(const char* str) +void write_debug_dx(const char* str) { - LLAppViewer::instance()->writeDebug(str); + LLString value = gDebugInfo["DXInfo"].asString(); + value += str; + gDebugInfo["DXInfo"] = value; +} + +void write_debug_dx(const std::string& str) +{ + write_debug_dx(str.c_str()); } bool LLAppViewerWin32::initHardwareTest() @@ -340,7 +347,7 @@ bool LLAppViewerWin32::initHardwareTest() LLSplashScreen::update("Detecting hardware..."); llinfos << "Attempting to poll DirectX for hardware info" << llendl; - gDXHardware.setWriteDebugFunc(write_debug_callback); + gDXHardware.setWriteDebugFunc(write_debug_dx); BOOL probe_ok = gDXHardware.getInfo(vram_only); if (!probe_ok diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index c43c4e8685..dda155d0df 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -773,12 +773,11 @@ BOOL idle_startup() gSavedSettings.setString("FirstName", firstname); gSavedSettings.setString("LastName", lastname); + + + llinfos << "Attempting login as: " << firstname << " " << lastname << llendl; - LLAppViewer::instance()->writeDebug("Attempting login as: "); - LLAppViewer::instance()->writeDebug(firstname); - LLAppViewer::instance()->writeDebug(" "); - LLAppViewer::instance()->writeDebug(lastname); - LLAppViewer::instance()->writeDebug("\n"); + gDebugInfo["LoginName"] = firstname + " " + lastname; } // create necessary directories @@ -826,6 +825,7 @@ BOOL idle_startup() LLPanelLogin::close(); } + //For HTML parsing in text boxes. LLTextEditor::setLinkColor( gSavedSettings.getColor4("HTMLLinkColor") ); LLTextEditor::setURLCallbacks ( &LLWeb::loadURL, &LLURLDispatcher::dispatch, &LLURLDispatcher::dispatch ); @@ -895,6 +895,8 @@ BOOL idle_startup() if(STATE_LOGIN_AUTH_INIT == LLStartUp::getStartupState()) { //#define LL_MINIMIAL_REQUESTED_OPTIONS + gDebugInfo["GridUtilHost"] = gGridInfo[gGridChoice].mName; + lldebugs << "STATE_LOGIN_AUTH_INIT" << llendl; if (!gUserAuthp) { @@ -932,6 +934,7 @@ BOOL idle_startup() } LLAppViewer::instance()->getLoginURIs(); sAuthUris = LLAppViewer::instance()->getLoginURIs(); + sAuthUriNum = 0; auth_method = "login_to_simulator"; auth_desc = "Logging in. "; @@ -1226,15 +1229,11 @@ BOOL idle_startup() const char* text; text = gUserAuthp->getResponse("agent_id"); if(text) gAgentID.set(text); - LLAppViewer::instance()->writeDebug("AgentID: "); - LLAppViewer::instance()->writeDebug(text); - LLAppViewer::instance()->writeDebug("\n"); + gDebugInfo["AgentID"] = text; text = gUserAuthp->getResponse("session_id"); if(text) gAgentSessionID.set(text); - LLAppViewer::instance()->writeDebug("SessionID: "); - LLAppViewer::instance()->writeDebug(text); - LLAppViewer::instance()->writeDebug("\n"); + gDebugInfo["SessionID"] = text; text = gUserAuthp->getResponse("secure_session_id"); if(text) gAgent.mSecureSessionID.set(text); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 95a1db12df..5bfe023168 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -964,28 +964,16 @@ void LLViewerRegion::updateCoarseLocations(LLMessageSystem* msg) } } -LLString LLViewerRegion::getInfoString() +void LLViewerRegion::getInfo(LLSD& info) { - char tmp_buf[256]; /* Flawfinder: ignore */ - LLString info; - - info = "Region: "; - getHost().getString(tmp_buf, 256); - info += tmp_buf; - info += ":"; - info += getName(); - info += "\n"; - + info["Region"]["Host"] = getHost().getIPandPort(); + info["Region"]["Name"] = getName(); U32 x, y; from_region_handle(getHandle(), &x, &y); - snprintf(tmp_buf, sizeof(tmp_buf), "%d:%d", x, y); /* Flawfinder: ignore */ - info += "Handle:"; - info += tmp_buf; - info += "\n"; - return info; + info["Region"]["Handle"]["x"] = (LLSD::Integer)x; + info["Region"]["Handle"]["y"] = (LLSD::Integer)y; } - void LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp) { U32 local_id = objectp->getLocalID(); diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 013a96f2d1..a0953e561e 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -223,7 +223,7 @@ public: F32 getLandHeightRegion(const LLVector3& region_pos); - LLString getInfoString(); + void getInfo(LLSD& info); // handle a full update message void cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp); diff --git a/indra/newview/llwindebug.cpp b/indra/newview/llwindebug.cpp index 88ba5822eb..13c5ac3bbb 100644 --- a/indra/newview/llwindebug.cpp +++ b/indra/newview/llwindebug.cpp @@ -33,12 +33,70 @@ #ifdef LL_WINDOWS +#include <tchar.h> +#include <tlhelp32.h> +#include <atlbase.h> +#include "llappviewer.h" #include "llwindebug.h" #include "llviewercontrol.h" #include "lldir.h" - -#include "llappviewer.h" - +#include "llsd.h" +#include "llsdserialize.h" + +#pragma warning(disable: 4200) //nonstandard extension used : zero-sized array in struct/union +#pragma warning(disable: 4100) //unreferenced formal parameter + +/* +LLSD Block for Windows Dump Information +<llsd> + <map> + <key>Platform</key> + <string></string> + <key>Process</key> + <string></string> + <key>Module</key> + <string></string> + <key>Date Modified</key> + <string></string> + <key>Exception Code</key> + <string></string> + <key>Exception Read/Write Address</key> + <string></string> + <key>Instruction</key> + <string></string> + <key>Registers</key> + <map> + <!-- Continued for all registers --> + <key>EIP</key> + <string>...</string> + <!-- ... --> + </map> + <key>Call Stack</key> + <array> + <!-- One map per stack frame --> + <map> + <key>Module Name</key> + <string></string> + <key>Module Base Address</key> + <string></string> + <key>Module Offset Address</key> + <string></string> + <key>Parameters</key> + <array> + <string></string> + </array> + </map> + <!-- ... --> + </array> + </map> +</llsd> + +*/ + +// From viewer.h +extern BOOL gInProductionGrid; + +extern void (*gCrashCallback)(void); // based on dbghelp.h typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, @@ -49,6 +107,255 @@ typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hF MINIDUMPWRITEDUMP f_mdwp = NULL; +#undef UNICODE + +HMODULE hDbgHelp; + +// Tool Help functions. +typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID); +typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); +typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + +CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_; +MODULE32_FIRST Module32First_; +MODULE32_NEST Module32Next_; + +#define DUMP_SIZE_MAX 8000 //max size of our dump +#define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls +#define NL L"\r\n" //new line + +//**************************************************************************************** +BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr) +//**************************************************************************************** +// Find module by Ret_Addr (address in the module). +// Return Module_Name (full path) and Module_Addr (start address). +// Return TRUE if found. +{ + MODULEENTRY32 M = {sizeof(M)}; + HANDLE hSnapshot; + + bool found = false; + + if (CreateToolhelp32Snapshot_) + { + hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0); + + if ((hSnapshot != INVALID_HANDLE_VALUE) && + Module32First_(hSnapshot, &M)) + { + do + { + if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize) + { + lstrcpyn(Module_Name, M.szExePath, MAX_PATH); + Module_Addr = M.modBaseAddr; + found = true; + break; + } + } while (Module32Next_(hSnapshot, &M)); + } + + CloseHandle(hSnapshot); + } + + return found; +} //Get_Module_By_Ret_Addr + +//****************************************************************** +void WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, LLSD& info) +//****************************************************************** +// Fill Str with call stack info. +// pException can be either GetExceptionInformation() or NULL. +// If pException = NULL - get current call stack. +{ + + USES_CONVERSION; + + LPWSTR Module_Name = new WCHAR[MAX_PATH]; + PBYTE Module_Addr = 0; + + typedef struct STACK + { + STACK * Ebp; + PBYTE Ret_Addr; + DWORD Param[0]; + } STACK, * PSTACK; + + STACK Stack = {0, 0}; + PSTACK Ebp; + + if (pException) //fake frame for exception address + { + Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp; + Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress; + Ebp = &Stack; + } + else + { + Ebp = (PSTACK)&pException - 1; //frame addr of Get_Call_Stack() + + // Skip frame of Get_Call_Stack(). + if (!IsBadReadPtr(Ebp, sizeof(PSTACK))) + Ebp = Ebp->Ebp; //caller ebp + } + + // Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX. + // Break trace on wrong stack frame. + for (int Ret_Addr_I = 0, i = 0; + (Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr)); + Ret_Addr_I++, Ebp = Ebp->Ebp, ++i) + { + // If module with Ebp->Ret_Addr found. + + if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr)) + { + // Save module's address and full path. + info["Call Stack"][i]["Module Name"] = W2A(Module_Name); + info["Call Stack"][i]["Module Address"] = (int)Module_Addr; + info["Call Stack"][i]["Call Offset"] = (int)(Ebp->Ret_Addr - Module_Addr); + + LLSD params; + // Save 5 params of the call. We don't know the real number of params. + if (pException && !Ret_Addr_I) //fake frame for exception address + params[0] = "Exception Offset"; + else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD))) + { + for(int j = 0; j < 5; ++j) + { + params[j] = (int)Ebp->Param[j]; + } + } + info["Call Stack"][i]["Parameters"] = params; + } + info["Call Stack"][i]["Return Address"] = (int)Ebp->Ret_Addr; + } +} //Get_Call_Stack + +//*********************************** +void WINAPI Get_Version_Str(LLSD& info) +//*********************************** +// Fill Str with Windows version. +{ + OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)}; //EX for NT 5.0 and later + + if (!GetVersionEx((POSVERSIONINFO)&V)) + { + ZeroMemory(&V, sizeof(V)); + V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx((POSVERSIONINFO)&V); + } + + if (V.dwPlatformId != VER_PLATFORM_WIN32_NT) + V.dwBuildNumber = LOWORD(V.dwBuildNumber); //for 9x HIWORD(dwBuildNumber) = 0x04xx + + info["Platform"] = llformat("Windows: %d.%d.%d, SP %d.%d, Product Type %d", //SP - service pack, Product Type - VER_NT_WORKSTATION,... + V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor, V.wProductType); +} //Get_Version_Str + +//************************************************************* +LLSD WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException) +//************************************************************* +// Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str. +{ + USES_CONVERSION; + + LLSD info; + LPWSTR Str; + int Str_Len; + int i; + LPWSTR Module_Name = new WCHAR[MAX_PATH]; + PBYTE Module_Addr; + HANDLE hFile; + FILETIME Last_Write_Time; + FILETIME Local_File_Time; + SYSTEMTIME T; + + Str = new WCHAR[DUMP_SIZE_MAX]; + Str_Len = 0; + if (!Str) + return NULL; + + Get_Version_Str(info); + + + GetModuleFileName(NULL, Str, MAX_PATH); + info["Process"] = W2A(Str); + + // If exception occurred. + if (pException) + { + EXCEPTION_RECORD & E = *pException->ExceptionRecord; + CONTEXT & C = *pException->ContextRecord; + + // If module with E.ExceptionAddress found - save its path and date. + if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr)) + { + info["Module"] = W2A(Module_Name); + + if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE) + { + if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time)) + { + FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time); + FileTimeToSystemTime(&Local_File_Time, &T); + + info["Date Modified"] = llformat("%02d/%02d/%d", T.wMonth, T.wDay, T.wYear); + } + CloseHandle(hFile); + } + } + else + { + info["Exception Addr"] = (int)E.ExceptionAddress; + } + + info["Exception Code"] = (int)E.ExceptionCode; + + /* + //TODO: Fix this + if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) + { + // Access violation type - Write/Read. + LLSD exception_info; + exception_info["Type"] = E.ExceptionInformation[0] ? "Write" : "Read"; + exception_info["Address"] = llformat("%08x", E.ExceptionInformation[1]); + info["Exception Information"] = exception_info; + } + */ + + + // Save instruction that caused exception. + Str_Len = 0; + for (i = 0; i < 16; i++) + Str_Len += wsprintf(Str + Str_Len, L" %02X", PBYTE(E.ExceptionAddress)[i]); + info["Instruction"] = W2A(Str); + + LLSD registers; + registers["EAX"] = (int)C.Eax; + registers["EBX"] = (int)C.Ebx; + registers["ECX"] = (int)C.Ecx; + registers["EDX"] = (int)C.Edx; + registers["ESI"] = (int)C.Esi; + registers["EDI"] = (int)C.Edi; + registers["ESP"] = (int)C.Esp; + registers["EBP"] = (int)C.Ebp; + registers["EIP"] = (int)C.Eip; + registers["EFlags"] = (int)C.EFlags; + info["Registers"] = registers; + } //if (pException) + + // Save call stack info. + Get_Call_Stack(pException, info); + + if (Str[0] == NL[0]) + lstrcpy(Str, Str + sizeof(NL) - 1); + + + return info; +} //Get_Exception_Info + +#define UNICODE class LLMemoryReserve { @@ -92,7 +399,6 @@ static LLMemoryReserve gEmergencyMemoryReserve; // static BOOL LLWinDebug::setupExceptionHandler() { -#ifdef LL_RELEASE_FOR_DOWNLOAD static BOOL s_first_run = TRUE; // Load the dbghelp dll now, instead of waiting for the crash. @@ -119,7 +425,7 @@ BOOL LLWinDebug::setupExceptionHandler() msg += local_dll_name; msg += "!\n"; - LLAppViewer::instance()->writeDebug(msg.c_str()); + //write_debug(msg.c_str()); ok = FALSE; } @@ -129,7 +435,7 @@ BOOL LLWinDebug::setupExceptionHandler() if (!f_mdwp) { - LLAppViewer::instance()->writeDebug("No MiniDumpWriteDump!\n"); + //write_debug("No MiniDumpWriteDump!\n"); FreeLibrary(hDll); hDll = NULL; ok = FALSE; @@ -139,76 +445,37 @@ BOOL LLWinDebug::setupExceptionHandler() gEmergencyMemoryReserve.reserve(); } - // *REMOVE: LLApp now handles the exception handing. - // LLAppViewerWin32 calls SetUnhandledExceptionFilter() - - //LPTOP_LEVEL_EXCEPTION_FILTER prev_filter; - //prev_filter = SetUnhandledExceptionFilter(LLWinDebug::handleException); - - //if (s_first_run) - //{ - // // We're fine, this is the first run. - // s_first_run = FALSE; - // return ok; - //} - //if (!prev_filter) - //{ - // llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with NULL!" << llendl; - // ok = FALSE; - //} - //if (prev_filter != LLWinDebug::handleException) - //{ - // llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with " << prev_filter << "!" << llendl; - // ok = FALSE; - //} + LPTOP_LEVEL_EXCEPTION_FILTER prev_filter; + prev_filter = SetUnhandledExceptionFilter(LLWinDebug::handleException); - return ok; -#else - // Internal builds don't mess with exception handling. - return TRUE; -#endif -} + // Try to get Tool Help library functions. + HMODULE hKernel32; + hKernel32 = GetModuleHandle(_T("KERNEL32")); + CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot"); + Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32FirstW"); + Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32NextW"); -void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename) -{ - if(f_mdwp == NULL) + if (s_first_run) { - LLAppViewer::instance()->writeDebug("No way to generate a minidump, no MiniDumpWriteDump function!\n"); + // We're fine, this is the first run. + s_first_run = FALSE; + return ok; } - else if(gDirUtilp == NULL) + if (!prev_filter) { - LLAppViewer::instance()->writeDebug("No way to generate a minidump, no gDirUtilp!\n"); + llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with NULL!" << llendl; + ok = FALSE; } - else + if (prev_filter != LLWinDebug::handleException) { - std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - filename); - - HANDLE hFile = CreateFileA(dump_path.c_str(), - GENERIC_WRITE, - FILE_SHARE_WRITE, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (hFile != INVALID_HANDLE_VALUE) - { - // Write the dump, ignoring the return value - f_mdwp(GetCurrentProcess(), - GetCurrentProcessId(), - hFile, - type, - ExInfop, - NULL, - NULL); - - CloseHandle(hFile); - } - + llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with " << prev_filter << "!" << llendl; + ok = FALSE; } -} + return ok; + // Internal builds don't mess with exception handling. + //return TRUE; +} // static LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop) { @@ -222,42 +489,35 @@ LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop) // gEmergencyMemoryReserve.release(); - BOOL userWantsMaxiDump = - (stricmp(gSavedSettings.getString("LastName").c_str(), "linden") == 0) - || (stricmp(gSavedSettings.getString("LastName").c_str(), "tester") == 0); - - BOOL alsoSaveMaxiDump = userWantsMaxiDump && !gInProductionGrid; - - /* Calculate alsoSaveMaxiDump here */ - if (exception_infop) { - _MINIDUMP_EXCEPTION_INFORMATION ExInfo; - ExInfo.ThreadId = ::GetCurrentThreadId(); - ExInfo.ExceptionPointers = exception_infop; - ExInfo.ClientPointers = NULL; + std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLifeException"); - writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp"); + std::string log_path = dump_path + ".log"; - if(alsoSaveMaxiDump) - writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp"); + LLSD info; + info = Get_Exception_Info(exception_infop); + if (info) + { + std::ofstream out_file(log_path.c_str()); + LLSDSerialize::toPrettyXML(info, out_file); + out_file.close(); + } } else { - writeDumpToFile(MiniDumpNormal, NULL, "SecondLife.dmp"); - - if(alsoSaveMaxiDump) - writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), NULL, "SecondLifePlus.dmp"); - } - - if (!exception_infop) - { // We're calling this due to a network error, not due to an actual exception. // It doesn't realy matter what we return. return EXCEPTION_CONTINUE_SEARCH; } + //handle viewer crash must be called here since + //we don't return handling of the application + //back to the process. + LLAppViewer::handleViewerCrash(); + // // At this point, we always want to exit the app. There's no graceful // recovery for an unhandled exception. @@ -269,4 +529,3 @@ LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop) } #endif - diff --git a/indra/newview/llwindebug.h b/indra/newview/llwindebug.h index bb1f11df67..e420138216 100644 --- a/indra/newview/llwindebug.h +++ b/indra/newview/llwindebug.h @@ -41,7 +41,7 @@ public: static BOOL setupExceptionHandler(); static LONG WINAPI handleException(struct _EXCEPTION_POINTERS *pExceptionInfo); - static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename); + //static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename); }; #endif // LL_LLWINDEBUG_H diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 36be05fe7f..e76123557c 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -972,16 +972,16 @@ void LLWorld::requestCacheMisses() } } -LLString LLWorld::getInfoString() +void LLWorld::getInfo(LLSD& info) { - LLString info_string("World Info:\n"); + LLSD region_info; for (region_list_t::iterator iter = mRegionList.begin(); iter != mRegionList.end(); ++iter) - { + { LLViewerRegion* regionp = *iter; - info_string += regionp->getInfoString(); + regionp->getInfo(region_info); + info["World"].append(region_info); } - return info_string; } void LLWorld::disconnectRegions() diff --git a/indra/newview/llworld.h b/indra/newview/llworld.h index 91685cd297..e634459acd 100644 --- a/indra/newview/llworld.h +++ b/indra/newview/llworld.h @@ -142,7 +142,7 @@ public: void setSpaceTimeUSec(const U64 space_time_usec); U64 getSpaceTimeUSec() const; - LLString getInfoString(); + void getInfo(LLSD& info); public: typedef std::list<LLViewerRegion*> region_list_t; diff --git a/indra/newview/macutil_Prefix.h b/indra/newview/macutil_Prefix.h index f8050c8d35..145a01c702 100644 --- a/indra/newview/macutil_Prefix.h +++ b/indra/newview/macutil_Prefix.h @@ -39,3 +39,6 @@ #include <Carbon/Carbon.h> +#undef check +#undef verify +#undef require diff --git a/indra/win_crash_logger/StdAfx.h b/indra/win_crash_logger/StdAfx.h index 6f046f9820..d954823c74 100644 --- a/indra/win_crash_logger/StdAfx.h +++ b/indra/win_crash_logger/StdAfx.h @@ -52,6 +52,8 @@ #include <malloc.h> #include <memory.h> #include <tchar.h> +#include <atlbase.h> + // Local Header Files diff --git a/indra/win_crash_logger/llcrashloggerwindows.cpp b/indra/win_crash_logger/llcrashloggerwindows.cpp new file mode 100644 index 0000000000..007e2b0ed5 --- /dev/null +++ b/indra/win_crash_logger/llcrashloggerwindows.cpp @@ -0,0 +1,348 @@ +/** +* @file llcrashloggerwindows.cpp +* @brief Windows crash logger implementation +* +* $LicenseInfo:firstyear=2003&license=viewergpl$ +* +* Copyright (c) 2003-2007, Linden Research, Inc. +* +* Second Life Viewer Source Code +* The source code in this file ("Source Code") is provided by Linden Lab +* to you under the terms of the GNU General Public License, version 2.0 +* ("GPL"), unless you have obtained a separate licensing agreement +* ("Other License"), formally executed by you and Linden Lab. Terms of +* the GPL can be found in doc/GPL-license.txt in this distribution, or +* online at http://secondlife.com/developers/opensource/gplv2 +* +* There are special exceptions to the terms and conditions of the GPL as +* it is applied to this Source Code. View the full text of the exception +* in the file doc/FLOSS-exception.txt in this software distribution, or +* online at http://secondlife.com/developers/opensource/flossexception +* +* By copying, modifying or distributing this software, you acknowledge +* that you have read and understood your obligations described above, +* and agree to abide by those obligations. +* +* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +* COMPLETENESS OR PERFORMANCE. +* $/LicenseInfo$ +*/ + +#include "stdafx.h" +#include "resource.h" +#include "llcrashloggerwindows.h" + +#include <sstream> + +#include "boost/tokenizer.hpp" + +#include "dbghelp.h" +#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME +#include "llerror.h" +#include "llfile.h" +#include "lltimer.h" +#include "llstring.h" +#include "lldxhardware.h" +#include "lldir.h" +#include "llsdserialize.h" + +#define MAX_LOADSTRING 100 +const char* const SETTINGS_FILE_HEADER = "version"; +const S32 SETTINGS_FILE_VERSION = 101; + +// Windows Message Handlers + +// Global Variables: +HINSTANCE hInst= NULL; // current instance +TCHAR szTitle[MAX_LOADSTRING]; /* Flawfinder: ignore */ // The title bar text +TCHAR szWindowClass[MAX_LOADSTRING]; /* Flawfinder: ignore */ // The title bar text + +HWND gHwndReport = NULL; // Send/Don't Send dialog +HWND gHwndProgress = NULL; // Progress window +HCURSOR gCursorArrow = NULL; +HCURSOR gCursorWait = NULL; +BOOL gFirstDialog = TRUE; // Are we currently handling the Send/Don't Send dialog? +std::stringstream gDXInfo; + +void write_debug(const char *str) +{ + gDXInfo << str; /* Flawfinder: ignore */ +} + +void write_debug(std::string& str) +{ + write_debug(str.c_str()); +} + +void show_progress(const char* message) +{ + std::wstring msg = wstring_to_utf16str(utf8str_to_wstring(message)); + if (gHwndProgress) + { + SendDlgItemMessage(gHwndProgress, // handle to destination window + IDC_LOG, + WM_SETTEXT, // message to send + FALSE, // undo option + (LPARAM)msg.c_str()); + } +} + +void update_messages() +{ + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + { + exit(0); + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +void sleep_and_pump_messages( U32 seconds ) +{ + const U32 CYCLES_PER_SECOND = 10; + U32 cycles = seconds * CYCLES_PER_SECOND; + while( cycles-- ) + { + update_messages(); + ms_sleep(1000 / CYCLES_PER_SECOND); + } +} + +// Include product name in the window caption. +void LLCrashLoggerWindows::ProcessCaption(HWND hWnd) +{ + TCHAR templateText[1024]; /* Flawfinder: ignore */ + TCHAR finalText[2048]; /* Flawfinder: ignore */ + GetWindowText(hWnd, templateText, sizeof(templateText)); + swprintf(finalText, sizeof(CA2T(mProductName.c_str())), templateText, CA2T(mProductName.c_str())); /* Flawfinder: ignore */ + SetWindowText(hWnd, finalText); +} + + +// Include product name in the diaog item text. +void LLCrashLoggerWindows::ProcessDlgItemText(HWND hWnd, int nIDDlgItem) +{ + TCHAR templateText[1024]; /* Flawfinder: ignore */ + TCHAR finalText[2048]; /* Flawfinder: ignore */ + GetDlgItemText(hWnd, nIDDlgItem, templateText, sizeof(templateText)); + swprintf(finalText, sizeof(CA2T(mProductName.c_str())), templateText, CA2T(mProductName.c_str())); /* Flawfinder: ignore */ + SetDlgItemText(hWnd, nIDDlgItem, finalText); +} + +bool handle_button_click(WORD button_id) +{ + USES_CONVERSION; + // Is this something other than Send or Don't Send? + if (button_id != IDOK + && button_id != IDCANCEL) + { + return false; + } + + // See if "do this next time" is checked and save state + S32 crash_behavior = CRASH_BEHAVIOR_ASK; + LRESULT result = SendDlgItemMessage(gHwndReport, IDC_CHECK_AUTO, BM_GETCHECK, 0, 0); + if (result == BST_CHECKED) + { + if (button_id == IDOK) + { + crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND; + } + else if (button_id == IDCANCEL) + { + crash_behavior = CRASH_BEHAVIOR_NEVER_SEND; + } + ((LLCrashLoggerWindows*)LLCrashLogger::instance())->saveCrashBehaviorSetting(crash_behavior); + } + + // We're done with this dialog. + gFirstDialog = FALSE; + + // Send the crash report if requested + if (button_id == IDOK) + { + WCHAR wbuffer[20000]; + GetDlgItemText(gHwndReport, // handle to dialog box + IDC_EDIT1, // control identifier + wbuffer, // pointer to buffer for text + 20000 // maximum size of string + ); + LLString user_text(T2CA(wbuffer)); + // Activate and show the window. + ShowWindow(gHwndProgress, SW_SHOW); + // Try doing this second to make the progress window go frontmost. + ShowWindow(gHwndReport, SW_HIDE); + ((LLCrashLoggerWindows*)LLCrashLogger::instance())->setUserText(user_text); + ((LLCrashLoggerWindows*)LLCrashLogger::instance())->sendCrashLogs(); + } + // Quit the app + LLApp::setQuitting(); + return true; +} + + +LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + switch( message ) + { + case WM_CREATE: + return 0; + + case WM_COMMAND: + if( gFirstDialog ) + { + WORD button_id = LOWORD(wParam); + bool handled = handle_button_click(button_id); + if (handled) + { + return 0; + } + } + break; + + case WM_DESTROY: + // Closing the window cancels + LLApp::setQuitting(); + PostQuitMessage(0); + return 0; + } + + return DefWindowProc(hwnd, message, wParam, lParam); +} + + +LLCrashLoggerWindows::LLCrashLoggerWindows(void) +{ +} + +LLCrashLoggerWindows::~LLCrashLoggerWindows(void) +{ +} + +bool LLCrashLoggerWindows::init(void) +{ + bool ok = LLCrashLogger::init(); + if(!ok) return false; + + /* + mbstowcs(gProductName, mProductName.c_str(), sizeof(gProductName)/sizeof(gProductName[0])); + gProductName[ sizeof(gProductName)/sizeof(gProductName[0]) - 1 ] = 0; + swprintf(gProductName, L"Second Life"); + */ + + llinfos << "Loading dialogs" << llendl; + + // Initialize global strings + LoadString(mhInst, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadString(mhInst, IDC_WIN_CRASH_LOGGER, szWindowClass, MAX_LOADSTRING); + + gCursorArrow = LoadCursor(NULL, IDC_ARROW); + gCursorWait = LoadCursor(NULL, IDC_WAIT); + + // Register a window class that will be used by our dialogs + WNDCLASS wndclass; + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = DLGWINDOWEXTRA; // Required, since this is used for dialogs! + wndclass.hInstance = mhInst; + wndclass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE( IDI_WIN_CRASH_LOGGER ) ); + wndclass.hCursor = gCursorArrow; + wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = szWindowClass; + RegisterClass( &wndclass ); + + return true; +} + +void LLCrashLoggerWindows::gatherPlatformSpecificFiles() +{ + updateApplication("Gathering hardware information. App may appear frozen."); + // DX hardware probe blocks, so we can't cancel during it + //Generate our dx_info.log file + SetCursor(gCursorWait); + // At this point we're responsive enough the user could click the close button + SetCursor(gCursorArrow); + mDebugLog["DisplayDeviceInfo"] = gDXHardware.getDisplayInfo(); + mFileMap["CrashLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLifeException.log"); +} + +bool LLCrashLoggerWindows::mainLoop() +{ + + USES_CONVERSION; + + // Note: parent hwnd is 0 (the desktop). No dlg proc. See Petzold (5th ed) HexCalc example, Chapter 11, p529 + // win_crash_logger.rc has been edited by hand. + // Dialogs defined with CLASS "WIN_CRASH_LOGGER" (must be same as szWindowClass) + + gHwndProgress = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PROGRESS), 0, NULL); + ProcessCaption(gHwndProgress); + ShowWindow(gHwndProgress, SW_HIDE ); + + if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND) + { + ShowWindow(gHwndProgress, SW_SHOW ); + sendCrashLogs(); + } + else if (mCrashBehavior == CRASH_BEHAVIOR_ASK) + { + gHwndReport = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PREVREPORTBOX), 0, NULL); + + // Include the product name in the caption and various dialog items. + ProcessCaption(gHwndReport); + ProcessDlgItemText(gHwndReport, IDC_STATIC_MSG); + + // Update the header to include whether or not we crashed on the last run. + TCHAR header[2048]; + CA2T product(mProductName.c_str()); + if (mCrashInPreviousExec) + { + swprintf(header, _T("%s appears to have crashed or frozen the last time it ran."), product); /* Flawfinder: ignore */ + } + else + { + swprintf(header, _T("%s appears to have crashed."), product); /* Flawfinder: ignore */ + } + SetDlgItemText(gHwndReport, IDC_STATIC_HEADER, header); + ShowWindow(gHwndReport, SW_SHOW ); + + MSG msg; + while (!LLApp::isQuitting() && GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return msg.wParam; + } + else + { + llwarns << "Unknown crash behavior " << mCrashBehavior << llendl; + return 1; + } + return 0; +} + +void LLCrashLoggerWindows::updateApplication(LLString message) +{ + LLCrashLogger::updateApplication(); + if(message != "") show_progress(message.c_str()); + update_messages(); +} + +bool LLCrashLoggerWindows::cleanup() +{ + if(mSentCrashLogs) show_progress("Done"); + else show_progress("Could not connect to servers, logs not sent"); + sleep_and_pump_messages(3); + + PostQuitMessage(0); + return true; +} + diff --git a/indra/win_crash_logger/llcrashloggerwindows.h b/indra/win_crash_logger/llcrashloggerwindows.h new file mode 100644 index 0000000000..1f5a216947 --- /dev/null +++ b/indra/win_crash_logger/llcrashloggerwindows.h @@ -0,0 +1,59 @@ +/** +* @file llcrashloggerwindows.h +* @brief Windows crash logger definition +* +* $LicenseInfo:firstyear=2003&license=viewergpl$ +* +* Copyright (c) 2003-2007, Linden Research, Inc. +* +* Second Life Viewer Source Code +* The source code in this file ("Source Code") is provided by Linden Lab +* to you under the terms of the GNU General Public License, version 2.0 +* ("GPL"), unless you have obtained a separate licensing agreement +* ("Other License"), formally executed by you and Linden Lab. Terms of +* the GPL can be found in doc/GPL-license.txt in this distribution, or +* online at http://secondlife.com/developers/opensource/gplv2 +* +* There are special exceptions to the terms and conditions of the GPL as +* it is applied to this Source Code. View the full text of the exception +* in the file doc/FLOSS-exception.txt in this software distribution, or +* online at http://secondlife.com/developers/opensource/flossexception +* +* By copying, modifying or distributing this software, you acknowledge +* that you have read and understood your obligations described above, +* and agree to abide by those obligations. +* +* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +* COMPLETENESS OR PERFORMANCE. +* $/LicenseInfo$ +*/ + +#ifndef LLCRASHLOGGERWINDOWS_H +#define LLCRASHLOGGERWINDOWS_H + +#include "linden_common.h" +#include "llcrashlogger.h" +#include "windows.h" +#include "llstring.h" + +class LLCrashLoggerWindows : public LLCrashLogger +{ +public: + LLCrashLoggerWindows(void); + ~LLCrashLoggerWindows(void); + virtual bool init(); + virtual bool mainLoop(); + virtual void updateApplication(LLString message = ""); + virtual bool cleanup(); + virtual void gatherPlatformSpecificFiles(); + //void annotateCallStack(); + void setHandle(HINSTANCE hInst) { mhInst = hInst; } +private: + void ProcessDlgItemText(HWND hWnd, int nIDDlgItem); + void ProcessCaption(HWND hWnd); + HINSTANCE mhInst; + +}; + +#endif diff --git a/indra/win_crash_logger/resource.h b/indra/win_crash_logger/resource.h index ee6bf3d575..f1ef850f76 100644 --- a/indra/win_crash_logger/resource.h +++ b/indra/win_crash_logger/resource.h @@ -1,3 +1,34 @@ +/** +* @file resource.h +* @brief Windows crash logger windows resources +* +* $LicenseInfo:firstyear=2003&license=viewergpl$ +* +* Copyright (c) 2003-2007, Linden Research, Inc. +* +* Second Life Viewer Source Code +* The source code in this file ("Source Code") is provided by Linden Lab +* to you under the terms of the GNU General Public License, version 2.0 +* ("GPL"), unless you have obtained a separate licensing agreement +* ("Other License"), formally executed by you and Linden Lab. Terms of +* the GPL can be found in doc/GPL-license.txt in this distribution, or +* online at http://secondlife.com/developers/opensource/gplv2 +* +* There are special exceptions to the terms and conditions of the GPL as +* it is applied to this Source Code. View the full text of the exception +* in the file doc/FLOSS-exception.txt in this software distribution, or +* online at http://secondlife.com/developers/opensource/flossexception +* +* By copying, modifying or distributing this software, you acknowledge +* that you have read and understood your obligations described above, +* and agree to abide by those obligations. +* +* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +* COMPLETENESS OR PERFORMANCE. +* $/LicenseInfo$ +*/ + //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by win_crash_logger.rc @@ -22,6 +53,7 @@ #define IDC_STATIC_HEADER 1007 #define IDC_STATIC_WHATINFO 1008 #define IDC_STATIC_MOTIVATION 1009 +#define IDC_STATIC_MSG 1010 #define IDC_STATIC -1 // Next default values for new objects @@ -30,7 +62,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 131 #define _APS_NEXT_COMMAND_VALUE 32771 -#define _APS_NEXT_CONTROL_VALUE 1010 +#define _APS_NEXT_CONTROL_VALUE 1011 #define _APS_NEXT_SYMED_VALUE 110 #endif #endif diff --git a/indra/win_crash_logger/win_crash_logger.cpp b/indra/win_crash_logger/win_crash_logger.cpp index 266940741b..c6b4ff1c34 100644 --- a/indra/win_crash_logger/win_crash_logger.cpp +++ b/indra/win_crash_logger/win_crash_logger.cpp @@ -35,98 +35,22 @@ // Must be first include, precompiled headers. #include "stdafx.h" -#include "linden_common.h" -#include "llcontrol.h" -#include "resource.h" +#include <stdlib.h> -#include <direct.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <wininet.h> +#include "llcrashloggerwindows.h" -#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME -#include "llerror.h" -#include "lltimer.h" -#include "lldir.h" -#include "llstring.h" -#include "lldxhardware.h" - -LLControlGroup gCrashSettings; // saved at end of session - -// Constants -#define MAX_LOADSTRING 100 -const char* const SETTINGS_FILE_HEADER = "version"; -const S32 SETTINGS_FILE_VERSION = 101; - -// Functions -LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); -bool handle_button_click(WORD button_id); -S32 load_crash_behavior_setting(); -bool save_crash_behavior_setting(S32 crash_behavior); -void send_crash_report(); -void write_debug(const char *str); -void write_debug(std::string& str); - -// Global Variables: -HINSTANCE hInst= NULL; // current instance -TCHAR szTitle[MAX_LOADSTRING]; /* Flawfinder: ignore */ // The title bar text -TCHAR szWindowClass[MAX_LOADSTRING]; /* Flawfinder: ignore */ // The title bar text - -LLString gUserText; // User's description of the problem -time_t gStartTime = 0; -HWND gHwndReport = NULL; // Send/Don't Send dialog -HWND gHwndProgress = NULL; // Progress window -HCURSOR gCursorArrow = NULL; -HCURSOR gCursorWait = NULL; -BOOL gFirstDialog = TRUE; // Are we currently handling the Send/Don't Send dialog? -BOOL gCrashInPreviousExec = FALSE; -FILE *gDebugFile = NULL; -LLString gUserserver; -WCHAR gProductName[512]; // // Implementation // -// Include product name in the window caption. -void ProcessCaption(HWND hWnd) -{ - TCHAR templateText[1024]; /* Flawfinder: ignore */ - TCHAR finalText[2048]; /* Flawfinder: ignore */ - GetWindowText(hWnd, templateText, sizeof(templateText)); - swprintf(finalText, templateText, gProductName); /* Flawfinder: ignore */ - SetWindowText(hWnd, finalText); -} - - -// Include product name in the diaog item text. -void ProcessDlgItemText(HWND hWnd, int nIDDlgItem) -{ - TCHAR templateText[1024]; /* Flawfinder: ignore */ - TCHAR finalText[2048]; /* Flawfinder: ignore */ - GetDlgItemText(hWnd, nIDDlgItem, templateText, sizeof(templateText)); - swprintf(finalText, templateText, gProductName); /* Flawfinder: ignore */ - SetDlgItemText(hWnd, nIDDlgItem, finalText); -} - int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { llinfos << "Starting crash reporter" << llendl; - // We assume that all the logs we're looking for reside on the current drive - gDirUtilp->initAppDirs("SecondLife"); - - // Default to the product name "Second Life" (this is overridden by the -name argument) - swprintf(gProductName, L"Second Life"); /* Flawfinder: ignore */ - - gCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "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; - S32 crash_behavior = load_crash_behavior_setting(); // In Win32, we need to generate argc and argv ourselves... // Note: GetCommandLine() returns a potentially return a LPTSTR @@ -139,7 +63,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, const S32 MAX_ARGS = 100; int argc = 0; - char *argv[MAX_ARGS]; /* Flawfinder: ignore */ + char *argv[MAX_ARGS]; char *token = NULL; if( cmd_line_including_exe_name[0] == '\"' ) @@ -159,7 +83,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, { argv[argc++] = token; /* Get next token: */ - if (*(token + strlen(token) + 1) == '\"') /* Flawfinder: ignore */ + if (*(token + strlen(token) + 1) == '\"') { token = strtok( NULL, "\""); } @@ -169,784 +93,32 @@ int APIENTRY WinMain(HINSTANCE hInstance, } } - S32 i; - for (i=0; i<argc; i++) - { - if(!strcmp(argv[i], "-previous")) - { - llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl; - gCrashInPreviousExec = TRUE; - } - - if(!strcmp(argv[i], "-dialog")) - { - llinfos << "Show the user dialog" << llendl; - crash_behavior = CRASH_BEHAVIOR_ASK; - } - - if(!strcmp(argv[i], "-user")) - { - if ((i + 1) < argc) - { - i++; - gUserserver = argv[i]; - llinfos << "Got userserver " << gUserserver << llendl; - } - } - - if(!strcmp(argv[i], "-name")) - { - if ((i + 1) < argc) - { - i++; - - mbstowcs(gProductName, argv[i], sizeof(gProductName)/sizeof(gProductName[0])); - gProductName[ sizeof(gProductName)/sizeof(gProductName[0]) - 1 ] = 0; - llinfos << "Got product name " << argv[i] << llendl; - } - } - } - - // If user doesn't want to send, bail out - if (crash_behavior == CRASH_BEHAVIOR_NEVER_SEND) - { - llinfos << "Crash behavior is never_send, quitting" << llendl; - return 0; - } - - // Get the current time - time(&gStartTime); - - llinfos << "Loading dialogs" << llendl; - - // Store instance handle in our global variable - hInst = hInstance; - - // Initialize global strings - LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); - LoadString(hInstance, IDC_WIN_CRASH_LOGGER, szWindowClass, MAX_LOADSTRING); - - gCursorArrow = LoadCursor(NULL, IDC_ARROW); - gCursorWait = LoadCursor(NULL, IDC_WAIT); - - // Register a window class that will be used by our dialogs - WNDCLASS wndclass; - wndclass.style = CS_HREDRAW | CS_VREDRAW; - wndclass.lpfnWndProc = WndProc; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = DLGWINDOWEXTRA; // Required, since this is used for dialogs! - wndclass.hInstance = hInst; - wndclass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE( IDI_WIN_CRASH_LOGGER ) ); - wndclass.hCursor = gCursorArrow; - wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); - wndclass.lpszMenuName = NULL; - wndclass.lpszClassName = szWindowClass; - RegisterClass( &wndclass ); - - // Note: parent hwnd is 0 (the desktop). No dlg proc. See Petzold (5th ed) HexCalc example, Chapter 11, p529 - // win_crash_logger.rc has been edited by hand. - // Dialogs defined with CLASS "WIN_CRASH_LOGGER" (must be same as szWindowClass) - - gHwndProgress = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PROGRESS), 0, NULL); - ProcessCaption(gHwndProgress); - ShowWindow(gHwndProgress, SW_HIDE ); - - if (crash_behavior == CRASH_BEHAVIOR_ALWAYS_SEND) - { - ShowWindow(gHwndProgress, SW_SHOW ); - send_crash_report(); - return 0; - } - - if (crash_behavior == CRASH_BEHAVIOR_ASK) - { - gHwndReport = CreateDialog(hInst, MAKEINTRESOURCE(IDD_REPORT), 0, NULL); - - // Include the product name in the caption and various dialog items. - ProcessCaption(gHwndReport); - ProcessDlgItemText(gHwndReport, IDC_STATIC_WHATINFO); - ProcessDlgItemText(gHwndReport, IDC_STATIC_MOTIVATION); - - // Update the header to include whether or not we crashed on the last run. - WCHAR header[2048]; - if (gCrashInPreviousExec) - { - swprintf(header, L"%s appears to have crashed or frozen the last time it ran.", gProductName); /* Flawfinder: ignore */ - } - else - { - swprintf(header, L"%s appears to have crashed.", gProductName); /* Flawfinder: ignore */ - } - SetDlgItemText(gHwndReport, IDC_STATIC_HEADER, header); - ShowWindow(gHwndReport, SW_SHOW ); - - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - return msg.wParam; - } - else - { - llwarns << "Unknown crash behavior " << crash_behavior << llendl; - return 1; - } -} - - -LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) -{ - switch( message ) - { - case WM_CREATE: - return 0; - - case WM_COMMAND: - if( gFirstDialog ) - { - WORD button_id = LOWORD(wParam); - bool handled = handle_button_click(button_id); - if (handled) - { - return 0; - } - } - break; - - case WM_DESTROY: - // Closing the window cancels - PostQuitMessage(0); - return 0; - } - - return DefWindowProc(hwnd, message, wParam, lParam); -} - - -bool handle_button_click(WORD button_id) -{ - // Is this something other than Send or Don't Send? - if (button_id != IDOK - && button_id != IDCANCEL) - { - return false; - } - - // See if "do this next time" is checked and save state - S32 crash_behavior = CRASH_BEHAVIOR_ASK; - LRESULT result = SendDlgItemMessage(gHwndReport, IDC_CHECK_AUTO, BM_GETCHECK, 0, 0); - if (result == BST_CHECKED) - { - if (button_id == IDOK) - { - crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND; - } - else if (button_id == IDCANCEL) - { - crash_behavior = CRASH_BEHAVIOR_NEVER_SEND; - } - } - bool success = save_crash_behavior_setting(crash_behavior); - if (!success) - { - llwarns << "Failed to save crash settings" << llendl; - } - - // We're done with this dialog. - gFirstDialog = FALSE; - - // Send the crash report if requested - if (button_id == IDOK) - { - // Don't let users type anything. They believe the reports - // get read by humans, and get angry when we don't respond. JC - //WCHAR wbuffer[20000]; - //GetDlgItemText(gHwndReport, // handle to dialog box - // IDC_EDIT1, // control identifier - // wbuffer, // pointer to buffer for text - // 20000 // maximum size of string - // ); - //gUserText = wstring_to_utf8str(utf16str_to_wstring(wbuffer)).c_str(); - //llinfos << gUserText << llendl; - - // Activate and show the window. - ShowWindow(gHwndProgress, SW_SHOW); - // Try doing this second to make the progress window go frontmost. - ShowWindow(gHwndReport, SW_HIDE); - - send_crash_report(); - } - - // Quit the app - PostQuitMessage(0); - - return true; -} - - -class LLFileEncoder -{ -public: - LLFileEncoder(const char *formname, const char *filename); - ~LLFileEncoder(); - - BOOL isValid() const { return mIsValid; } - LLString encodeURL(const S32 max_length = 0); -public: - BOOL mIsValid; - LLString mFilename; - LLString mFormname; - S32 mBufLength; - U8 *mBuf; -}; - -LLString encode_string(const char *formname, const LLString &str); - -void update_messages() -{ - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - { - if (msg.message == WM_QUIT) - { - exit(0); - } - TranslateMessage(&msg); - DispatchMessage(&msg); - } -} - -void sleep_and_pump_messages( U32 seconds ) -{ - const U32 CYCLES_PER_SECOND = 10; - U32 cycles = seconds * CYCLES_PER_SECOND; - while( cycles-- ) + LLCrashLoggerWindows app; + bool ok = app.parseCommandOptions(argc, argv); + if(!ok) { - update_messages(); - ms_sleep(1000 / CYCLES_PER_SECOND); - } -} - - -void show_progress(const char* message) -{ - std::wstring msg = wstring_to_utf16str(utf8str_to_wstring(message)); - if (gHwndProgress) - { - SendDlgItemMessage(gHwndProgress, // handle to destination window - IDC_LOG, - WM_SETTEXT, // message to send - FALSE, // undo option - (LPARAM)msg.c_str()); - } -} - - -void send_crash_report() -{ - update_messages(); - show_progress("Starting up..."); - update_messages(); - - const S32 SL_MAX_SIZE = 100000; // Maximum size of the Second Life log file. - - update_messages(); - - // Lots of silly variable, replicated for each log file. - std::string db_file_name; // debug.log - std::string sl_file_name; // SecondLife.log - std::string md_file_name; // minidump (SecondLife.dmp) file name - std::string st_file_name; // stats.log file - std::string si_file_name; // settings.ini file - std::string ml_file_name; // message.log file - - LLFileEncoder *db_filep = NULL; - LLFileEncoder *sl_filep = NULL; - LLFileEncoder *st_filep = NULL; - LLFileEncoder *md_filep = NULL; - LLFileEncoder *si_filep = NULL; - LLFileEncoder *ml_filep = NULL; - - // DX hardware probe blocks, so we can't cancel during it - SetCursor(gCursorWait); - - // Need to do hardware detection before we grab the files, otherwise we don't send the debug log updates - // to the server (including the list of hardware). - update_messages(); - show_progress("Detecting hardware, please wait..."); - update_messages(); - gDXHardware.setWriteDebugFunc(write_debug); - gDXHardware.getInfo(FALSE); - update_messages(); - gDXHardware.dumpDevices(); - update_messages(); - fclose(gDebugFile); - gDebugFile = NULL; - - // At this point we're responsive enough the user could click the close button - SetCursor(gCursorArrow); - - /////////////////////////////////// - // - // We do the parsing for the debug_info file first, as that will - // give us the location of the SecondLife.log file. - // - - // Figure out the filename of the debug log - db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log"); - db_filep = new LLFileEncoder("DB", db_file_name.c_str()); - - // Get the filename of the SecondLife.log file - // *NOTE: This buffer size is hard coded into scanf() below. - char tmp_sl_name[256]; /* Flawfinder: ignore */ - tmp_sl_name[0] = '\0'; - - update_messages(); - show_progress("Looking for files..."); - update_messages(); - - // Look for it in the debug_info.log file - if (db_filep->isValid()) - { - sscanf( - (const char*)db_filep->mBuf, - "SL Log: %255[^\r\n]", - tmp_sl_name); - } - else - { - delete db_filep; - db_filep = NULL; - } - - if (gCrashInPreviousExec) - { - // If we froze, the crash log this time around isn't useful. Use the - // old one. - sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old"); - } - else if (tmp_sl_name[0]) - { - // If debug_info.log gives us a valid log filename, use that. - sl_file_name = tmp_sl_name; - llinfos << "Using log file from debug log " << sl_file_name << llendl; - } - else - { - // Figure out the filename of the default second life log - sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log"); - } - - // Now we get the SecondLife.log file if it's there - sl_filep = new LLFileEncoder("SL", sl_file_name.c_str()); - if (!sl_filep->isValid()) - { - delete sl_filep; - sl_filep = NULL; - } - - update_messages(); - show_progress("Looking for stats file..."); - update_messages(); - - st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log"); - st_filep = new LLFileEncoder("ST", st_file_name.c_str()); - if (!st_filep->isValid()) - { - delete st_filep; - st_filep = NULL; - } - - si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.ini"); - si_filep = new LLFileEncoder("SI", si_file_name.c_str()); - if (!si_filep->isValid()) - { - delete si_filep; - si_filep = NULL; - } - - // Now we get the minidump - md_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.dmp"); - md_filep = new LLFileEncoder("MD", md_file_name.c_str()); - if (!md_filep->isValid()) - { - delete md_filep; - md_filep = NULL; - } - - // Now we get the message log - ml_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"message.log"); - ml_filep = new LLFileEncoder("ML", ml_file_name.c_str()); - if (!ml_filep->isValid()) - { - delete ml_filep; - ml_filep = NULL; - } - - LLString post_data; - LLString tmp_url_buf; - - // Append the userserver - tmp_url_buf = encode_string("USER", gUserserver); - post_data += tmp_url_buf; - llinfos << "PostData:" << post_data << llendl; - - if (gCrashInPreviousExec) - { - post_data.append(1, '&'); - tmp_url_buf = encode_string("EF", "Y"); - post_data += tmp_url_buf; - } - - update_messages(); - show_progress("Encoding data"); - update_messages(); - if (db_filep) - { - post_data.append(1, '&'); - tmp_url_buf = db_filep->encodeURL(); - post_data += tmp_url_buf; - llinfos << "Sending DB log file" << llendl; - } - else - { - llinfos << "Not sending DB log file" << llendl; - } - show_progress("Encoding data."); - update_messages(); - - if (sl_filep) - { - post_data.append(1, '&'); - tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE); - post_data += tmp_url_buf; - llinfos << "Sending SL log file" << llendl; - } - else - { - llinfos << "Not sending SL log file" << llendl; - } - show_progress("Encoding data.."); - update_messages(); - - if (st_filep) - { - post_data.append(1, '&'); - tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE); - post_data += tmp_url_buf; - llinfos << "Sending stats log file" << llendl; - } - else - { - llinfos << "Not sending stats log file" << llendl; - } - show_progress("Encoding data..."); - update_messages(); - - if (md_filep) - { - post_data.append(1, '&'); - tmp_url_buf = md_filep->encodeURL(); - post_data += tmp_url_buf; - llinfos << "Sending minidump log file" << llendl; - } - else - { - llinfos << "Not sending minidump log file" << llendl; - } - show_progress("Encoding data...."); - update_messages(); - - if (si_filep) - { - post_data.append(1, '&'); - tmp_url_buf = si_filep->encodeURL(); - post_data += tmp_url_buf; - llinfos << "Sending settings log file" << llendl; - } - else - { - llinfos << "Not sending settings.ini file" << llendl; - } - show_progress("Encoding data...."); - update_messages(); - - if (ml_filep) - { - post_data.append(1, '&'); - tmp_url_buf = ml_filep->encodeURL(SL_MAX_SIZE); - post_data += tmp_url_buf; - llinfos << "Sending message log file" << llendl; - } - else - { - llinfos << "Not sending message.log file" << llendl; - } - show_progress("Encoding data...."); - update_messages(); - - if (gUserText.size()) - { - post_data.append(1, '&'); - tmp_url_buf = encode_string("UN", gUserText); - post_data += tmp_url_buf; - } - - delete db_filep; - db_filep = NULL; - delete sl_filep; - sl_filep = NULL; - delete md_filep; - md_filep = NULL; - - // Post data to web server - const S32 BUFSIZE = 65536; - HINTERNET hinet, hsession, hrequest; - char data[BUFSIZE]; /* Flawfinder: ignore */ - unsigned long bytes_read; - - llinfos << "Connecting to crash report server" << llendl; - update_messages(); - show_progress("Connecting to server..."); - update_messages(); - - // Init wininet subsystem - hinet = InternetOpen(L"LindenCrashReporter", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); - if (hinet == NULL) - { - llinfos << "Couldn't open connection" << llendl; - sleep_and_pump_messages( 5 ); -// return FALSE; - } - - hsession = InternetConnect(hinet, - L"secondlife.com", - INTERNET_DEFAULT_HTTP_PORT, - NULL, - NULL, - INTERNET_SERVICE_HTTP, - NULL, - NULL); - - if (!hsession) - { - llinfos << "Couldn't talk to crash report server" << llendl; - } - - hrequest = HttpOpenRequest(hsession, L"POST", L"/cgi-bin/viewer_crash_reporter2", NULL, L"", NULL, 0, 0); - if (!hrequest) - { - llinfos << "Couldn't open crash report URL!" << llendl; + llwarns << "Unable to parse command line." << llendl; } - llinfos << "Transmitting data" << llendl; - llinfos << "Bytes: " << (post_data.size()) << llendl; - - update_messages(); - show_progress("Transmitting data..."); - update_messages(); - - BOOL ok = HttpSendRequest(hrequest, NULL, 0, (void *)(post_data.c_str()), post_data.size()); - if (!ok) - { - llinfos << "Error posting data!" << llendl; - sleep_and_pump_messages( 5 ); - } - - llinfos << "Response from crash report server:" << llendl; - do - { - if (InternetReadFile(hrequest, data, BUFSIZE, &bytes_read)) - { - if (bytes_read == 0) - { - // If InternetFileRead returns TRUE AND bytes_read == 0 - // we've successfully downloaded the entire file - break; - } - else - { - data[bytes_read] = 0; - llinfos << data << llendl; - } - } - else - { - llinfos << "Couldn't read file!" << llendl; - sleep_and_pump_messages( 5 ); -// return FALSE; - } - } while(TRUE); - - InternetCloseHandle(hrequest); - InternetCloseHandle(hsession); - InternetCloseHandle(hinet); - update_messages(); - show_progress("Done."); - sleep_and_pump_messages( 3 ); -// return TRUE; -} - -LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename) -{ - mFormname = form_name; - mFilename = filename; - mIsValid = FALSE; - mBuf = NULL; - - int res; - - llstat stat_data; - res = LLFile::stat(mFilename.c_str(), &stat_data); - if (res) - { - llwarns << "File " << mFilename << " is missing!" << llendl; - return; - } - - FILE *fp = NULL; - S32 buf_size = 0; - S32 count = 0; - while (count < 5) - { - buf_size = stat_data.st_size; - fp = LLFile::fopen(mFilename.c_str(), "rb"); /* Flawfinder: ignore */ - if (!fp) - { - llwarns << "Can't open file " << mFilename << ", wait for a second" << llendl; - // Couldn't open the file, wait a bit and try again - count++; - ms_sleep(1000); - } - else - { - break; - } - } - if (!fp) - { - return; - } - U8 *buf = new U8[buf_size + 1]; - fread(buf, 1, buf_size, fp); - fclose(fp); - - mBuf = buf; - mBufLength = buf_size; - - mIsValid = TRUE; -} - -LLFileEncoder::~LLFileEncoder() -{ - if (mBuf) - { - delete mBuf; - mBuf = NULL; - } -} - -LLString LLFileEncoder::encodeURL(const S32 max_length) -{ - LLString result = mFormname; - result.append(1, '='); - - S32 i = 0; - - if (max_length) - { - if (mBufLength > max_length) - { - i = mBufLength - max_length; - } - } - - S32 url_buf_size = 3*mBufLength + 1; - char *url_buf = new char[url_buf_size]; - - S32 cur_pos = 0; - for (; i < mBufLength; i++) + app.setHandle(hInstance); + ok = app.init(); + if(!ok) { - S32 byte_val = mBuf[i]; - sprintf(url_buf + cur_pos, "%%%02x", byte_val); - cur_pos += 3; + llwarns << "Unable to initialize application." << llendl; + return -1; } - url_buf[i*3] = 0; - result.append(url_buf); - delete[] url_buf; - return result; -} - -LLString encode_string(const char *formname, const LLString &str) -{ - LLString result = formname; - result.append(1, '='); - // Not using LLString because of bad performance issues - S32 buf_size = str.size(); - S32 url_buf_size = 3*str.size() + 1; - char *url_buf = new char[url_buf_size]; + // Run the application main loop + if(!LLApp::isQuitting()) app.mainLoop(); - S32 cur_pos = 0; - S32 i; - for (i = 0; i < buf_size; i++) + if (!app.isError()) { - sprintf(url_buf + cur_pos, "%%%02x", str[i]); - cur_pos += 3; + // + // We don't want to do cleanup here if the error handler got called - + // the assumption is that the error handler is responsible for doing + // app cleanup if there was a problem. + // + app.cleanup(); } - url_buf[i*3] = 0; - - result.append(url_buf); - delete[] url_buf; - return result; -} - -void write_debug(const char *str) -{ - if (!gDebugFile) - { - std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log"); - llinfos << "Opening debug file " << debug_filename << llendl; - gDebugFile = LLFile::fopen(debug_filename.c_str(), "a+"); /* Flawfinder: ignore */ - if (!gDebugFile) - { - fprintf(stderr, "Couldn't open %s: debug log to stderr instead.\n", debug_filename.c_str()); - gDebugFile = stderr; - } - } - fprintf(gDebugFile, str); /* Flawfinder: ignore */ - fflush(gDebugFile); -} - -void write_debug(std::string& str) -{ - write_debug(str.c_str()); -} - -S32 load_crash_behavior_setting() -{ - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); - - gCrashSettings.loadFromFile(filename); - - S32 value = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING); - - if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK; - - return value; -} - -bool save_crash_behavior_setting(S32 crash_behavior) -{ - if (crash_behavior < CRASH_BEHAVIOR_ASK) return false; - if (crash_behavior > CRASH_BEHAVIOR_NEVER_SEND) return false; - - gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior); - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); - - gCrashSettings.saveToFile(filename, FALSE); - - return true; + return 0; } diff --git a/indra/win_crash_logger/win_crash_logger.rc b/indra/win_crash_logger/win_crash_logger.rc index 2c46859b95..2819722f63 100644 --- a/indra/win_crash_logger/win_crash_logger.rc +++ b/indra/win_crash_logger/win_crash_logger.rc @@ -56,13 +56,13 @@ END // Dialog // -IDD_PROGRESS DIALOG 100, 100, 186, 33 +IDD_PROGRESS DIALOGEX 100, 100, 234, 33 STYLE DS_SETFONT | DS_SETFOREGROUND | WS_CAPTION | WS_SYSMENU CAPTION "%s Crash Logger" CLASS "WIN_CRASH_LOGGER" -FONT 8, "MS Sans Serif" +FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN - LTEXT "Static",IDC_LOG,7,7,172,8 + LTEXT "Static",IDC_LOG,7,7,220,8 END IDD_REPORT DIALOGEX 100, 100, 297, 125 @@ -73,48 +73,33 @@ FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN DEFPUSHBUTTON "Send",IDOK,198,104,45,15,WS_GROUP PUSHBUTTON "Don't Send",IDCANCEL,247,104,45,15,WS_GROUP - LTEXT "%s appears to have crashed.",IDC_STATIC_HEADER,4,4,288, - 14 - LTEXT "This crash reporter collects information about your computer's hardware, operating system, and some %s logs, which are used for debugging purposes only.", - IDC_STATIC_WHATINFO,4,23,288,19,NOT WS_GROUP - CONTROL "Remember this choice",IDC_CHECK_AUTO,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,4,106,89,13 - LTEXT "Sending crash reports is the best way to help us improve the quality of %s.", - IDC_STATIC_MOTIVATION,4,38,288,8 - LTEXT "If you continue to experience this problem, please try:", - IDC_STATIC,4,57,251,8 - LTEXT "- Contacting support by visiting http://www.secondlife.com/support", - IDC_STATIC,4,67,231,8 + LTEXT "%s appears to have crashed.",IDC_STATIC_HEADER,4,4,288,14 + LTEXT "This crash reporter collects information about your computer's hardware, operating system, and some %s logs, which are used for debugging purposes only.",IDC_STATIC_WHATINFO,4,23,288,19,NOT WS_GROUP + CONTROL "Remember this choice",IDC_CHECK_AUTO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,4,106,89,13 + LTEXT "Sending crash reports is the best way to help us improve the quality of %s.",IDC_STATIC_MOTIVATION,4,43,288,8 + LTEXT "If you continue to experience this problem, please try:",IDC_STATIC,4,57,251,8 + LTEXT "- Contacting support by visiting http://www.secondlife.com/support",IDC_STATIC,4,67,231,8 END -IDD_PREVREPORTBOX DIALOG 100, 100, 232, 213 +IDD_PREVREPORTBOX DIALOGEX 100, 100, 232, 213 STYLE DS_SETFONT | DS_SETFOREGROUND | WS_CAPTION | WS_SYSMENU CAPTION "%s Crash Logger" CLASS "WIN_CRASH_LOGGER" -FONT 8, "MS Sans Serif" +FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN - DEFPUSHBUTTON "OK",IDOK,131,193,45,15,WS_GROUP - EDITTEXT IDC_EDIT1,4,102,223,89,ES_MULTILINE | ES_WANTRETURN | - WS_VSCROLL - PUSHBUTTON "Cancel",IDCANCEL,181,193,45,15,WS_GROUP - LTEXT "%s appears to have crashed or frozen the last time it ran.", - IDC_STATIC,4,4,214,8 - LTEXT "This crash reporter collects information about your computer's", - IDC_STATIC,4,17,201,8 - LTEXT "hardware configuration, operating system, and some %s", - IDC_STATIC,4,25,212,8 - LTEXT "logs, all of which are used for debugging purposes only.", - IDC_STATIC,4,33,210,8 - LTEXT "In the space below, please briefly describe what you were doing", - IDC_STATIC,3,48,208,8 - LTEXT "or trying to do just prior to the crash.",IDC_STATIC,3, - 56,204,8 - LTEXT "If you don't wish to send Linden Lab a crash report, press Cancel.", - IDC_STATIC,3,90,214,8 - LTEXT "This report is NOT read by customer support. If you have billing or", - IDC_STATIC,3,68,208,8 - LTEXT "other questions, please go to: www.secondlife.com/support", - IDC_STATIC,3,76,206,8 + DEFPUSHBUTTON "Send Report",IDOK,131,193,45,15,WS_GROUP + EDITTEXT IDC_EDIT1,3,100,223,89,ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL + PUSHBUTTON "Don't Send",IDCANCEL,181,193,45,15,WS_GROUP + LTEXT "%s appears to have crashed or frozen the last time it ran.",IDC_STATIC_HEADER,4,4,214,8 + LTEXT "This crash reporter collects information about your computer's",IDC_STATIC,4,17,201,8 + LTEXT "hardware configuration, operating system, and some %s",IDC_STATIC_MSG,4,25,212,8 + LTEXT "logs, all of which are used for debugging purposes only.",IDC_STATIC,4,33,210,8 + LTEXT "In the space below, please briefly describe what you were doing",IDC_STATIC,3,48,208,8 + LTEXT "or trying to do just prior to the crash.",IDC_STATIC,3,56,204,8 + LTEXT "If you don't wish to send Linden Lab a crash report, press Don't Send.",IDC_STATIC,3,90,223,8 + LTEXT "This report is NOT read by customer support. If you have billing or",IDC_STATIC,3,68,208,8 + LTEXT "other questions, please go to: www.secondlife.com/support",IDC_STATIC,3,76,206,8 + CONTROL "Remember this choice",IDC_CHECK_AUTO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,193,89,13 END @@ -158,7 +143,7 @@ BEGIN IDD_PROGRESS, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 179 + RIGHTMARGIN, 227 TOPMARGIN, 7 BOTTOMMARGIN, 26 END |