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