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(¤t_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