summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/linux_crash_logger/linux_crash_logger.cpp538
-rw-r--r--indra/linux_crash_logger/llcrashloggerlinux.cpp140
-rw-r--r--indra/linux_crash_logger/llcrashloggerlinux.h49
-rwxr-xr-xindra/llcrashlogger/llcrashlogger.cpp309
-rwxr-xr-xindra/llcrashlogger/llcrashlogger.h85
-rw-r--r--indra/llmessage/llcircuit.cpp19
-rw-r--r--indra/llmessage/llcircuit.h5
-rw-r--r--indra/llmessage/message.cpp7
-rw-r--r--indra/llmessage/message.h2
-rw-r--r--indra/llvfs/lldir_win32.cpp1
-rw-r--r--indra/llwindow/lldxhardware.cpp102
-rw-r--r--indra/llwindow/lldxhardware.h3
-rw-r--r--indra/mac_crash_logger/llcrashloggermac.cpp310
-rw-r--r--indra/mac_crash_logger/llcrashloggermac.h51
-rw-r--r--indra/mac_crash_logger/mac_crash_logger.cpp668
-rw-r--r--indra/newview/llappviewer.cpp89
-rw-r--r--indra/newview/llappviewer.h6
-rw-r--r--indra/newview/llappviewerwin32.cpp13
-rw-r--r--indra/newview/llstartup.cpp21
-rw-r--r--indra/newview/llviewerregion.cpp22
-rw-r--r--indra/newview/llviewerregion.h2
-rw-r--r--indra/newview/llwindebug.cpp441
-rw-r--r--indra/newview/llwindebug.h2
-rw-r--r--indra/newview/llworld.cpp10
-rw-r--r--indra/newview/llworld.h2
-rw-r--r--indra/newview/macutil_Prefix.h3
-rw-r--r--indra/win_crash_logger/StdAfx.h2
-rw-r--r--indra/win_crash_logger/llcrashloggerwindows.cpp348
-rw-r--r--indra/win_crash_logger/llcrashloggerwindows.h59
-rw-r--r--indra/win_crash_logger/resource.h34
-rw-r--r--indra/win_crash_logger/win_crash_logger.cpp874
-rw-r--r--indra/win_crash_logger/win_crash_logger.rc65
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"),
- &params,
- &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