Home | History | Annotate | Download | only in crash_generation_app
      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(&current_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