/indra/win_crash_logger/llcrashloggerwindows.cpp

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