PageRenderTime 57ms CodeModel.GetById 1ms app.highlight 50ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/win_crash_logger/llcrashloggerwindows.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 375 lines | 271 code | 47 blank | 57 comment | 35 complexity | 36c22101fdd88c2482083ae87062a172 MD5 | raw file
  1/** 
  2* @file llcrashloggerwindows.cpp
  3* @brief Windows crash logger implementation
  4*
  5* $LicenseInfo:firstyear=2003&license=viewerlgpl$
  6* Second Life Viewer Source Code
  7* Copyright (C) 2010, Linden Research, Inc.
  8* 
  9* This library is free software; you can redistribute it and/or
 10* modify it under the terms of the GNU Lesser General Public
 11* License as published by the Free Software Foundation;
 12* version 2.1 of the License only.
 13* 
 14* This library is distributed in the hope that it will be useful,
 15* but WITHOUT ANY WARRANTY; without even the implied warranty of
 16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17* Lesser General Public License for more details.
 18* 
 19* You should have received a copy of the GNU Lesser General Public
 20* License along with this library; if not, write to the Free Software
 21* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22* 
 23* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24* $/LicenseInfo$
 25*/
 26
 27#include "linden_common.h"
 28
 29#include "stdafx.h"
 30#include "resource.h"
 31#include "llcrashloggerwindows.h"
 32
 33#include <sstream>
 34
 35#include "boost/tokenizer.hpp"
 36
 37#include "indra_constants.h"	// CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
 38#include "llerror.h"
 39#include "llfile.h"
 40#include "lltimer.h"
 41#include "llstring.h"
 42#include "lldxhardware.h"
 43#include "lldir.h"
 44#include "llsdserialize.h"
 45
 46#define MAX_LOADSTRING 100
 47#define MAX_STRING 2048
 48const char* const SETTINGS_FILE_HEADER = "version";
 49const S32 SETTINGS_FILE_VERSION = 101;
 50
 51// Windows Message Handlers
 52
 53// Global Variables:
 54HINSTANCE hInst= NULL;					// current instance
 55TCHAR szTitle[MAX_LOADSTRING];				/* Flawfinder: ignore */		// The title bar text
 56TCHAR szWindowClass[MAX_LOADSTRING];		/* Flawfinder: ignore */		// The title bar text
 57
 58std::string gProductName;
 59HWND gHwndReport = NULL;	// Send/Don't Send dialog
 60HWND gHwndProgress = NULL;	// Progress window
 61HCURSOR gCursorArrow = NULL;
 62HCURSOR gCursorWait = NULL;
 63BOOL gFirstDialog = TRUE;	// Are we currently handling the Send/Don't Send dialog?
 64std::stringstream gDXInfo;
 65bool gSendLogs = false;
 66
 67
 68//Conversion from char* to wchar*
 69//Replacement for ATL macros, doesn't allocate memory
 70//For more info see: http://www.codeguru.com/forum/showthread.php?t=337247
 71void ConvertLPCSTRToLPWSTR (const char* pCstring, WCHAR* outStr)
 72{
 73    if (pCstring != NULL)
 74    {
 75        int nInputStrLen = strlen (pCstring);
 76        // Double NULL Termination
 77        int nOutputStrLen = MultiByteToWideChar(CP_ACP, 0, pCstring, nInputStrLen, NULL, 0) + 2;
 78        if (outStr)
 79        {
 80            memset (outStr, 0x00, sizeof (WCHAR)*nOutputStrLen);
 81            MultiByteToWideChar (CP_ACP, 0, pCstring, nInputStrLen, outStr, nInputStrLen);
 82        }
 83    }
 84}
 85
 86void write_debug(const char *str)
 87{
 88	gDXInfo << str;		/* Flawfinder: ignore */
 89}
 90
 91void write_debug(std::string& str)
 92{
 93	write_debug(str.c_str());
 94}
 95
 96void show_progress(const std::string& message)
 97{
 98	std::wstring msg = wstring_to_utf16str(utf8str_to_wstring(message));
 99	if (gHwndProgress)
100	{
101		SendDlgItemMessage(gHwndProgress,       // handle to destination window 
102							IDC_LOG,
103							WM_SETTEXT,			// message to send
104							FALSE,				// undo option
105							(LPARAM)msg.c_str());
106	}
107}
108
109void update_messages()
110{
111	MSG msg;
112	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
113	{
114		if (msg.message == WM_QUIT)
115		{
116			exit(0);
117		}
118		TranslateMessage(&msg);
119		DispatchMessage(&msg);
120	}
121}
122
123void sleep_and_pump_messages( U32 seconds )
124{
125	const U32 CYCLES_PER_SECOND = 10;
126	U32 cycles = seconds * CYCLES_PER_SECOND;
127	while( cycles-- )
128	{
129		update_messages();
130		ms_sleep(1000 / CYCLES_PER_SECOND);
131	}
132}
133
134// Include product name in the window caption.
135void LLCrashLoggerWindows::ProcessCaption(HWND hWnd)
136{
137	TCHAR templateText[MAX_STRING];		/* Flawfinder: ignore */
138	TCHAR header[MAX_STRING];
139	std::string final;
140	GetWindowText(hWnd, templateText, sizeof(templateText));
141	final = llformat(ll_convert_wide_to_string(templateText, CP_ACP).c_str(), gProductName.c_str());
142	ConvertLPCSTRToLPWSTR(final.c_str(), header);
143	SetWindowText(hWnd, header);
144}
145
146
147// Include product name in the diaog item text.
148void LLCrashLoggerWindows::ProcessDlgItemText(HWND hWnd, int nIDDlgItem)
149{
150	TCHAR templateText[MAX_STRING];		/* Flawfinder: ignore */
151	TCHAR header[MAX_STRING];
152	std::string final;
153	GetDlgItemText(hWnd, nIDDlgItem, templateText, sizeof(templateText));
154	final = llformat(ll_convert_wide_to_string(templateText, CP_ACP).c_str(), gProductName.c_str());
155	ConvertLPCSTRToLPWSTR(final.c_str(), header);
156	SetDlgItemText(hWnd, nIDDlgItem, header);
157}
158
159bool handle_button_click(WORD button_id)
160{
161	// Is this something other than Send or Don't Send?
162	if (button_id != IDOK
163		&& button_id != IDCANCEL)
164	{
165		return false;
166	}
167
168	// See if "do this next time" is checked and save state
169	S32 crash_behavior = CRASH_BEHAVIOR_ASK;
170	LRESULT result = SendDlgItemMessage(gHwndReport, IDC_CHECK_AUTO, BM_GETCHECK, 0, 0);
171	if (result == BST_CHECKED)
172	{
173		if (button_id == IDOK)
174		{
175			crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND;
176		}
177		else if (button_id == IDCANCEL)
178		{
179			crash_behavior = CRASH_BEHAVIOR_NEVER_SEND;
180		}
181		((LLCrashLoggerWindows*)LLCrashLogger::instance())->saveCrashBehaviorSetting(crash_behavior);
182	}
183	
184	// We're done with this dialog.
185	gFirstDialog = FALSE;
186
187	// Send the crash report if requested
188	if (button_id == IDOK)
189	{
190		gSendLogs = TRUE;
191		WCHAR wbuffer[20000];
192		GetDlgItemText(gHwndReport, // handle to dialog box
193						IDC_EDIT1,  // control identifier
194						wbuffer, // pointer to buffer for text
195						20000 // maximum size of string
196						);
197		std::string user_text(ll_convert_wide_to_string(wbuffer, CP_ACP));
198		// Activate and show the window.
199		ShowWindow(gHwndProgress, SW_SHOW); 
200		// Try doing this second to make the progress window go frontmost.
201		ShowWindow(gHwndReport, SW_HIDE);
202		((LLCrashLoggerWindows*)LLCrashLogger::instance())->setUserText(user_text);
203		((LLCrashLoggerWindows*)LLCrashLogger::instance())->sendCrashLogs();
204	}
205	// Quit the app
206	LLApp::setQuitting();
207	return true;
208}
209
210
211LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
212{
213	switch( message )
214	{
215	case WM_CREATE:
216		return 0;
217
218	case WM_COMMAND:
219		if( gFirstDialog )
220		{
221			WORD button_id = LOWORD(wParam);
222			bool handled = handle_button_click(button_id);
223			if (handled)
224			{
225				return 0;
226			}
227		}
228		break;
229
230	case WM_DESTROY:
231		// Closing the window cancels
232		LLApp::setQuitting();
233		PostQuitMessage(0);
234		return 0;
235	}
236
237	return DefWindowProc(hwnd, message, wParam, lParam);
238}
239
240
241LLCrashLoggerWindows::LLCrashLoggerWindows(void)
242{
243}
244
245LLCrashLoggerWindows::~LLCrashLoggerWindows(void)
246{
247}
248
249bool LLCrashLoggerWindows::init(void)
250{	
251	bool ok = LLCrashLogger::init();
252	if(!ok) return false;
253
254	/*
255	mbstowcs( gProductName, mProductName.c_str(), LL_ARRAY_SIZE(gProductName) );
256	gProductName[ LL_ARRY_SIZE(gProductName) - 1 ] = 0;
257	swprintf(gProductName, L"Second Life");
258	*/
259
260	llinfos << "Loading dialogs" << llendl;
261
262	// Initialize global strings
263	LoadString(mhInst, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
264	LoadString(mhInst, IDC_WIN_CRASH_LOGGER, szWindowClass, MAX_LOADSTRING);
265
266	gCursorArrow = LoadCursor(NULL, IDC_ARROW);
267	gCursorWait = LoadCursor(NULL, IDC_WAIT);
268
269	// Register a window class that will be used by our dialogs
270	WNDCLASS wndclass;
271	wndclass.style = CS_HREDRAW | CS_VREDRAW;
272	wndclass.lpfnWndProc = WndProc;
273	wndclass.cbClsExtra = 0;
274	wndclass.cbWndExtra = DLGWINDOWEXTRA;  // Required, since this is used for dialogs!
275	wndclass.hInstance = mhInst;
276	wndclass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE( IDI_WIN_CRASH_LOGGER ) );
277	wndclass.hCursor = gCursorArrow;
278	wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
279	wndclass.lpszMenuName = NULL;
280	wndclass.lpszClassName = szWindowClass;
281	RegisterClass( &wndclass );
282	
283	return true;
284}
285
286void LLCrashLoggerWindows::gatherPlatformSpecificFiles()
287{
288	updateApplication("Gathering hardware information. App may appear frozen.");
289	// DX hardware probe blocks, so we can't cancel during it
290	//Generate our dx_info.log file 
291	SetCursor(gCursorWait);
292	// At this point we're responsive enough the user could click the close button
293	SetCursor(gCursorArrow);
294	mDebugLog["DisplayDeviceInfo"] = gDXHardware.getDisplayInfo();
295}
296
297bool LLCrashLoggerWindows::mainLoop()
298{	
299	llinfos << "CrashSubmitBehavior is " << mCrashBehavior << llendl;
300
301	// Note: parent hwnd is 0 (the desktop).  No dlg proc.  See Petzold (5th ed) HexCalc example, Chapter 11, p529
302	// win_crash_logger.rc has been edited by hand.
303	// Dialogs defined with CLASS "WIN_CRASH_LOGGER" (must be same as szWindowClass)
304	gProductName = mProductName;
305
306	gHwndProgress = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PROGRESS), 0, NULL);
307	ProcessCaption(gHwndProgress);
308	ShowWindow(gHwndProgress, SW_HIDE );
309
310	if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND)
311	{
312		llinfos << "Showing crash report submit progress window." << llendl;
313		ShowWindow(gHwndProgress, SW_SHOW );
314		sendCrashLogs();
315	}
316	else if (mCrashBehavior == CRASH_BEHAVIOR_ASK)
317	{
318		gHwndReport = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PREVREPORTBOX), 0, NULL);
319		// Ignore result
320		(void) SendDlgItemMessage(gHwndReport, IDC_CHECK_AUTO, BM_SETCHECK, 0, 0);
321		// Include the product name in the caption and various dialog items.
322		ProcessCaption(gHwndReport);
323		ProcessDlgItemText(gHwndReport, IDC_STATIC_MSG);
324
325		// Update the header to include whether or not we crashed on the last run.
326		std::string headerStr;
327		TCHAR header[MAX_STRING];
328		if (mCrashInPreviousExec)
329		{
330			headerStr = llformat("%s appears to have crashed or frozen the last time it ran.", mProductName.c_str());
331		}
332		else
333		{
334			headerStr = llformat("%s appears to have crashed.", mProductName.c_str());
335		}
336		ConvertLPCSTRToLPWSTR(headerStr.c_str(), header);
337		SetDlgItemText(gHwndReport, IDC_STATIC_HEADER, header);		
338		ShowWindow(gHwndReport, SW_SHOW );
339		
340		MSG msg;
341		memset(&msg, 0, sizeof(msg));
342		while (!LLApp::isQuitting() && GetMessage(&msg, NULL, 0, 0))
343		{
344			TranslateMessage(&msg);
345			DispatchMessage(&msg);
346		}
347		return msg.wParam;
348	}
349	else
350	{
351		llwarns << "Unknown crash behavior " << mCrashBehavior << llendl;
352		return 1;
353	}
354	return 0;
355}
356
357void LLCrashLoggerWindows::updateApplication(const std::string& message)
358{
359	LLCrashLogger::updateApplication(message);
360	if(!message.empty()) show_progress(message);
361	update_messages();
362}
363
364bool LLCrashLoggerWindows::cleanup()
365{
366	if(gSendLogs)
367	{
368		if(mSentCrashLogs) show_progress("Done");
369		else show_progress("Could not connect to servers, logs not sent");
370		sleep_and_pump_messages(3);
371	}
372	PostQuitMessage(0);
373	commonCleanup();
374	return true;
375}