1 // Copyright (c) 2008, Google Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above 11 // copyright notice, this list of conditions and the following disclaimer 12 // in the documentation and/or other materials provided with the 13 // distribution. 14 // * Neither the name of Google Inc. nor the names of its 15 // contributors may be used to endorse or promote products derived from 16 // this software without specific prior written permission. 17 // 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30 // crash_generation_app.cpp : Defines the entry point for the application. 31 // 32 33 #include "client/windows/tests/crash_generation_app/crash_generation_app.h" 34 35 #include <windows.h> 36 #include <tchar.h> 37 38 #include "client/windows/crash_generation/client_info.h" 39 #include "client/windows/crash_generation/crash_generation_server.h" 40 #include "client/windows/handler/exception_handler.h" 41 #include "client/windows/common/ipc_protocol.h" 42 43 #include "client/windows/tests/crash_generation_app/abstract_class.h" 44 45 namespace google_breakpad { 46 47 const int kMaxLoadString = 100; 48 const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashServices\\TestServer"; 49 50 const DWORD kEditBoxStyles = WS_CHILD | 51 WS_VISIBLE | 52 WS_VSCROLL | 53 ES_LEFT | 54 ES_MULTILINE | 55 ES_AUTOVSCROLL | 56 ES_READONLY; 57 58 // Maximum length of a line in the edit box. 59 const size_t kMaximumLineLength = 256; 60 61 // CS to access edit control in a thread safe way. 62 static CRITICAL_SECTION* cs_edit = NULL; 63 64 // Edit control. 65 static HWND client_status_edit_box; 66 67 HINSTANCE current_instance; // Current instance. 68 TCHAR title[kMaxLoadString]; // Title bar text. 69 TCHAR window_class[kMaxLoadString]; // Main window class name. 70 71 ATOM MyRegisterClass(HINSTANCE instance); 72 BOOL InitInstance(HINSTANCE, int); 73 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 74 INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); 75 76 static int kCustomInfoCount = 2; 77 static CustomInfoEntry kCustomInfoEntries[] = { 78 CustomInfoEntry(L"prod", L"CrashTestApp"), 79 CustomInfoEntry(L"ver", L"1.0"), 80 }; 81 82 static ExceptionHandler* handler = NULL; 83 static CrashGenerationServer* crash_server = NULL; 84 85 // Registers the window class. 86 // 87 // This function and its usage are only necessary if you want this code 88 // to be compatible with Win32 systems prior to the 'RegisterClassEx' 89 // function that was added to Windows 95. It is important to call this 90 // function so that the application will get 'well formed' small icons 91 // associated with it. 92 ATOM MyRegisterClass(HINSTANCE instance) { 93 WNDCLASSEX wcex; 94 wcex.cbSize = sizeof(WNDCLASSEX); 95 wcex.style = CS_HREDRAW | CS_VREDRAW; 96 wcex.lpfnWndProc = WndProc; 97 wcex.cbClsExtra = 0; 98 wcex.cbWndExtra = 0; 99 wcex.hInstance = instance; 100 wcex.hIcon = LoadIcon(instance, 101 MAKEINTRESOURCE(IDI_CRASHGENERATIONAPP)); 102 wcex.hCursor = LoadCursor(NULL, IDC_ARROW); 103 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 104 wcex.lpszMenuName = MAKEINTRESOURCE(IDC_CRASHGENERATIONAPP); 105 wcex.lpszClassName = window_class; 106 wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 107 108 return RegisterClassEx(&wcex); 109 } 110 111 // Saves instance handle and creates main window 112 // 113 // In this function, we save the instance handle in a global variable and 114 // create and display the main program window. 115 BOOL InitInstance(HINSTANCE instance, int command_show) { 116 current_instance = instance; 117 HWND wnd = CreateWindow(window_class, 118 title, 119 WS_OVERLAPPEDWINDOW, 120 CW_USEDEFAULT, 121 0, 122 CW_USEDEFAULT, 123 0, 124 NULL, 125 NULL, 126 instance, 127 NULL); 128 129 if (!wnd) { 130 return FALSE; 131 } 132 133 ShowWindow(wnd, command_show); 134 UpdateWindow(wnd); 135 136 return TRUE; 137 } 138 139 static void AppendTextToEditBox(TCHAR* text) { 140 EnterCriticalSection(cs_edit); 141 SYSTEMTIME current_time; 142 GetLocalTime(¤t_time); 143 TCHAR line[kMaximumLineLength]; 144 int result = swprintf_s(line, 145 kMaximumLineLength, 146 L"[%.2d-%.2d-%.4d %.2d:%.2d:%.2d] %s", 147 current_time.wMonth, 148 current_time.wDay, 149 current_time.wYear, 150 current_time.wHour, 151 current_time.wMinute, 152 current_time.wSecond, 153 text); 154 155 if (result == -1) { 156 return; 157 } 158 159 int length = GetWindowTextLength(client_status_edit_box); 160 SendMessage(client_status_edit_box, 161 EM_SETSEL, 162 (WPARAM)length, 163 (LPARAM)length); 164 SendMessage(client_status_edit_box, 165 EM_REPLACESEL, 166 (WPARAM)FALSE, 167 (LPARAM)line); 168 LeaveCriticalSection(cs_edit); 169 } 170 171 static DWORD WINAPI AppendTextWorker(void* context) { 172 TCHAR* text = reinterpret_cast<TCHAR*>(context); 173 174 AppendTextToEditBox(text); 175 delete[] text; 176 177 return 0; 178 } 179 180 bool ShowDumpResults(const wchar_t* dump_path, 181 const wchar_t* minidump_id, 182 void* context, 183 EXCEPTION_POINTERS* exinfo, 184 MDRawAssertionInfo* assertion, 185 bool succeeded) { 186 TCHAR* text = new TCHAR[kMaximumLineLength]; 187 text[0] = _T('\0'); 188 int result = swprintf_s(text, 189 kMaximumLineLength, 190 TEXT("Dump generation request %s\r\n"), 191 succeeded ? TEXT("succeeded") : TEXT("failed")); 192 if (result == -1) { 193 delete [] text; 194 } 195 196 QueueUserWorkItem(AppendTextWorker, text, WT_EXECUTEDEFAULT); 197 return succeeded; 198 } 199 200 static void ShowClientConnected(void* context, 201 const ClientInfo* client_info) { 202 TCHAR* line = new TCHAR[kMaximumLineLength]; 203 line[0] = _T('\0'); 204 int result = swprintf_s(line, 205 kMaximumLineLength, 206 L"Client connected:\t\t%d\r\n", 207 client_info->pid()); 208 209 if (result == -1) { 210 delete[] line; 211 return; 212 } 213 214 QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); 215 } 216 217 static void ShowClientCrashed(void* context, 218 const ClientInfo* client_info, 219 const wstring* dump_path) { 220 TCHAR* line = new TCHAR[kMaximumLineLength]; 221 line[0] = _T('\0'); 222 int result = swprintf_s(line, 223 kMaximumLineLength, 224 TEXT("Client requested dump:\t%d\r\n"), 225 client_info->pid()); 226 227 if (result == -1) { 228 delete[] line; 229 return; 230 } 231 232 QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); 233 234 CustomClientInfo custom_info = client_info->GetCustomInfo(); 235 if (custom_info.count <= 0) { 236 return; 237 } 238 239 wstring str_line; 240 for (size_t i = 0; i < custom_info.count; ++i) { 241 if (i > 0) { 242 str_line += L", "; 243 } 244 str_line += custom_info.entries[i].name; 245 str_line += L": "; 246 str_line += custom_info.entries[i].value; 247 } 248 249 line = new TCHAR[kMaximumLineLength]; 250 line[0] = _T('\0'); 251 result = swprintf_s(line, 252 kMaximumLineLength, 253 L"%s\n", 254 str_line.c_str()); 255 if (result == -1) { 256 delete[] line; 257 return; 258 } 259 QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); 260 } 261 262 static void ShowClientExited(void* context, 263 const ClientInfo* client_info) { 264 TCHAR* line = new TCHAR[kMaximumLineLength]; 265 line[0] = _T('\0'); 266 int result = swprintf_s(line, 267 kMaximumLineLength, 268 TEXT("Client exited:\t\t%d\r\n"), 269 client_info->pid()); 270 271 if (result == -1) { 272 delete[] line; 273 return; 274 } 275 276 QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); 277 } 278 279 void CrashServerStart() { 280 // Do not create another instance of the server. 281 if (crash_server) { 282 return; 283 } 284 285 std::wstring dump_path = L"C:\\Dumps\\"; 286 287 if (_wmkdir(dump_path.c_str()) && (errno != EEXIST)) { 288 MessageBoxW(NULL, L"Unable to create dump directory", L"Dumper", MB_OK); 289 return; 290 } 291 292 crash_server = new CrashGenerationServer(kPipeName, 293 NULL, 294 ShowClientConnected, 295 NULL, 296 ShowClientCrashed, 297 NULL, 298 ShowClientExited, 299 NULL, 300 NULL, 301 NULL, 302 true, 303 &dump_path); 304 305 if (!crash_server->Start()) { 306 MessageBoxW(NULL, L"Unable to start server", L"Dumper", MB_OK); 307 delete crash_server; 308 crash_server = NULL; 309 } 310 } 311 312 void CrashServerStop() { 313 delete crash_server; 314 crash_server = NULL; 315 } 316 317 void DerefZeroCrash() { 318 int* x = 0; 319 *x = 1; 320 } 321 322 void InvalidParamCrash() { 323 printf(NULL); 324 } 325 326 void PureCallCrash() { 327 Derived derived; 328 } 329 330 void RequestDump() { 331 if (!handler->WriteMinidump()) { 332 MessageBoxW(NULL, L"Dump request failed", L"Dumper", MB_OK); 333 } 334 kCustomInfoEntries[1].set_value(L"1.1"); 335 } 336 337 void CleanUp() { 338 if (cs_edit) { 339 DeleteCriticalSection(cs_edit); 340 delete cs_edit; 341 } 342 343 if (handler) { 344 delete handler; 345 } 346 347 if (crash_server) { 348 delete crash_server; 349 } 350 } 351 352 // Processes messages for the main window. 353 // 354 // WM_COMMAND - process the application menu. 355 // WM_PAINT - Paint the main window. 356 // WM_DESTROY - post a quit message and return. 357 LRESULT CALLBACK WndProc(HWND wnd, 358 UINT message, 359 WPARAM w_param, 360 LPARAM l_param) { 361 int message_id; 362 int message_event; 363 PAINTSTRUCT ps; 364 HDC hdc; 365 366 HINSTANCE instance = (HINSTANCE)GetWindowLongPtr(wnd, GWLP_HINSTANCE); 367 368 switch (message) { 369 case WM_COMMAND: 370 // Parse the menu selections. 371 message_id = LOWORD(w_param); 372 message_event = HIWORD(w_param); 373 switch (message_id) { 374 case IDM_ABOUT: 375 DialogBox(current_instance, 376 MAKEINTRESOURCE(IDD_ABOUTBOX), 377 wnd, 378 About); 379 break; 380 case IDM_EXIT: 381 DestroyWindow(wnd); 382 break; 383 case ID_SERVER_START: 384 CrashServerStart(); 385 break; 386 case ID_SERVER_STOP: 387 CrashServerStop(); 388 break; 389 case ID_CLIENT_DEREFZERO: 390 DerefZeroCrash(); 391 break; 392 case ID_CLIENT_INVALIDPARAM: 393 InvalidParamCrash(); 394 break; 395 case ID_CLIENT_PURECALL: 396 PureCallCrash(); 397 break; 398 case ID_CLIENT_REQUESTEXPLICITDUMP: 399 RequestDump(); 400 break; 401 default: 402 return DefWindowProc(wnd, message, w_param, l_param); 403 } 404 break; 405 case WM_CREATE: 406 client_status_edit_box = CreateWindow(TEXT("EDIT"), 407 NULL, 408 kEditBoxStyles, 409 0, 410 0, 411 0, 412 0, 413 wnd, 414 NULL, 415 instance, 416 NULL); 417 break; 418 case WM_SIZE: 419 // Make the edit control the size of the window's client area. 420 MoveWindow(client_status_edit_box, 421 0, 422 0, 423 LOWORD(l_param), // width of client area. 424 HIWORD(l_param), // height of client area. 425 TRUE); // repaint window. 426 break; 427 case WM_SETFOCUS: 428 SetFocus(client_status_edit_box); 429 break; 430 case WM_PAINT: 431 hdc = BeginPaint(wnd, &ps); 432 EndPaint(wnd, &ps); 433 break; 434 case WM_DESTROY: 435 CleanUp(); 436 PostQuitMessage(0); 437 break; 438 default: 439 return DefWindowProc(wnd, message, w_param, l_param); 440 } 441 442 return 0; 443 } 444 445 // Message handler for about box. 446 INT_PTR CALLBACK About(HWND dlg, 447 UINT message, 448 WPARAM w_param, 449 LPARAM l_param) { 450 UNREFERENCED_PARAMETER(l_param); 451 switch (message) { 452 case WM_INITDIALOG: 453 return (INT_PTR)TRUE; 454 455 case WM_COMMAND: 456 if (LOWORD(w_param) == IDOK || LOWORD(w_param) == IDCANCEL) { 457 EndDialog(dlg, LOWORD(w_param)); 458 return (INT_PTR)TRUE; 459 } 460 break; 461 } 462 463 return (INT_PTR)FALSE; 464 } 465 466 } // namespace google_breakpad 467 468 int APIENTRY _tWinMain(HINSTANCE instance, 469 HINSTANCE previous_instance, 470 LPTSTR command_line, 471 int command_show) { 472 using namespace google_breakpad; 473 474 UNREFERENCED_PARAMETER(previous_instance); 475 UNREFERENCED_PARAMETER(command_line); 476 477 cs_edit = new CRITICAL_SECTION(); 478 InitializeCriticalSection(cs_edit); 479 480 CustomClientInfo custom_info = {kCustomInfoEntries, kCustomInfoCount}; 481 482 CrashServerStart(); 483 // This is needed for CRT to not show dialog for invalid param 484 // failures and instead let the code handle it. 485 _CrtSetReportMode(_CRT_ASSERT, 0); 486 handler = new ExceptionHandler(L"C:\\dumps\\", 487 NULL, 488 google_breakpad::ShowDumpResults, 489 NULL, 490 ExceptionHandler::HANDLER_ALL, 491 MiniDumpNormal, 492 kPipeName, 493 &custom_info); 494 495 // Initialize global strings. 496 LoadString(instance, IDS_APP_TITLE, title, kMaxLoadString); 497 LoadString(instance, 498 IDC_CRASHGENERATIONAPP, 499 window_class, 500 kMaxLoadString); 501 MyRegisterClass(instance); 502 503 // Perform application initialization. 504 if (!InitInstance(instance, command_show)) { 505 return FALSE; 506 } 507 508 HACCEL accel_table = LoadAccelerators( 509 instance, 510 MAKEINTRESOURCE(IDC_CRASHGENERATIONAPP)); 511 512 // Main message loop. 513 MSG msg; 514 while (GetMessage(&msg, NULL, 0, 0)) { 515 if (!TranslateAccelerator(msg.hwnd, accel_table, &msg)) { 516 TranslateMessage(&msg); 517 DispatchMessage(&msg); 518 } 519 } 520 521 return static_cast<int>(msg.wParam); 522 } 523