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 #include "client/windows/crash_generation/crash_generation_server.h"
31 #include <windows.h>
32 #include <cassert>
33 #include <list>
34 #include "client/windows/common/auto_critical_section.h"
35 #include "processor/scoped_ptr.h"
36
37 namespace google_breakpad {
38
39 // Output buffer size.
40 static const size_t kOutBufferSize = 64;
41
42 // Input buffer size.
43 static const size_t kInBufferSize = 64;
44
45 // Access flags for the client on the dump request event.
46 static const DWORD kDumpRequestEventAccess = EVENT_MODIFY_STATE;
47
48 // Access flags for the client on the dump generated event.
49 static const DWORD kDumpGeneratedEventAccess = EVENT_MODIFY_STATE |
50 SYNCHRONIZE;
51
52 // Access flags for the client on the mutex.
53 static const DWORD kMutexAccess = SYNCHRONIZE;
54
55 // Attribute flags for the pipe.
56 static const DWORD kPipeAttr = FILE_FLAG_FIRST_PIPE_INSTANCE |
57 PIPE_ACCESS_DUPLEX |
58 FILE_FLAG_OVERLAPPED;
59
60 // Mode for the pipe.
61 static const DWORD kPipeMode = PIPE_TYPE_MESSAGE |
62 PIPE_READMODE_MESSAGE |
63 PIPE_WAIT;
64
65 // For pipe I/O, execute the callback in the wait thread itself,
66 // since the callback does very little work. The callback executes
67 // the code for one of the states of the server state machine and
68 // the code for all of the states perform async I/O and hence
69 // finish very quickly.
70 static const ULONG kPipeIOThreadFlags = WT_EXECUTEINWAITTHREAD;
71
72 // Dump request threads will, most likely, generate dumps. That may
73 // take some time to finish, so specify WT_EXECUTELONGFUNCTION flag.
74 static const ULONG kDumpRequestThreadFlags = WT_EXECUTEINWAITTHREAD |
75 WT_EXECUTELONGFUNCTION;
76
77 // Maximum delay during server shutdown if some work items
78 // are still executing.
79 static const int kShutdownDelayMs = 10000;
80
81 // Interval for each sleep during server shutdown.
82 static const int kShutdownSleepIntervalMs = 5;
83
IsClientRequestValid(const ProtocolMessage & msg)84 static bool IsClientRequestValid(const ProtocolMessage& msg) {
85 return msg.tag == MESSAGE_TAG_REGISTRATION_REQUEST &&
86 msg.pid != 0 &&
87 msg.thread_id != NULL &&
88 msg.exception_pointers != NULL &&
89 msg.assert_info != NULL;
90 }
91
CrashGenerationServer(const std::wstring & pipe_name,SECURITY_ATTRIBUTES * pipe_sec_attrs,OnClientConnectedCallback connect_callback,void * connect_context,OnClientDumpRequestCallback dump_callback,void * dump_context,OnClientExitedCallback exit_callback,void * exit_context,bool generate_dumps,const std::wstring * dump_path)92 CrashGenerationServer::CrashGenerationServer(
93 const std::wstring& pipe_name,
94 SECURITY_ATTRIBUTES* pipe_sec_attrs,
95 OnClientConnectedCallback connect_callback,
96 void* connect_context,
97 OnClientDumpRequestCallback dump_callback,
98 void* dump_context,
99 OnClientExitedCallback exit_callback,
100 void* exit_context,
101 bool generate_dumps,
102 const std::wstring* dump_path)
103 : pipe_name_(pipe_name),
104 pipe_sec_attrs_(pipe_sec_attrs),
105 pipe_(NULL),
106 pipe_wait_handle_(NULL),
107 server_alive_handle_(NULL),
108 connect_callback_(connect_callback),
109 connect_context_(connect_context),
110 dump_callback_(dump_callback),
111 dump_context_(dump_context),
112 exit_callback_(exit_callback),
113 exit_context_(exit_context),
114 generate_dumps_(generate_dumps),
115 dump_generator_(NULL),
116 server_state_(IPC_SERVER_STATE_INITIAL),
117 shutting_down_(false),
118 overlapped_(),
119 client_info_(NULL),
120 cleanup_item_count_(0) {
121 InitializeCriticalSection(&clients_sync_);
122
123 if (dump_path) {
124 dump_generator_.reset(new MinidumpGenerator(*dump_path));
125 }
126 }
127
~CrashGenerationServer()128 CrashGenerationServer::~CrashGenerationServer() {
129 // Indicate to existing threads that server is shutting down.
130 shutting_down_ = true;
131
132 // Even if there are no current worker threads running, it is possible that
133 // an I/O request is pending on the pipe right now but not yet done. In fact,
134 // it's very likely this is the case unless we are in an ERROR state. If we
135 // don't wait for the pending I/O to be done, then when the I/O completes,
136 // it may write to invalid memory. AppVerifier will flag this problem too.
137 // So we disconnect from the pipe and then wait for the server to get into
138 // error state so that the pending I/O will fail and get cleared.
139 DisconnectNamedPipe(pipe_);
140 int num_tries = 100;
141 while (num_tries-- && server_state_ != IPC_SERVER_STATE_ERROR) {
142 Sleep(10);
143 }
144
145 // Unregister wait on the pipe.
146 if (pipe_wait_handle_) {
147 // Wait for already executing callbacks to finish.
148 UnregisterWaitEx(pipe_wait_handle_, INVALID_HANDLE_VALUE);
149 }
150
151 // Close the pipe to avoid further client connections.
152 if (pipe_) {
153 CloseHandle(pipe_);
154 }
155
156 // Request all ClientInfo objects to unregister all waits.
157 // New scope to hold the lock for the shortest time.
158 {
159 AutoCriticalSection lock(&clients_sync_);
160
161 std::list<ClientInfo*>::iterator iter;
162 for (iter = clients_.begin(); iter != clients_.end(); ++iter) {
163 ClientInfo* client_info = *iter;
164 client_info->UnregisterWaits();
165 }
166 }
167
168 // Now that all waits have been unregistered, wait for some time
169 // for all pending work items to finish.
170 int total_wait = 0;
171 while (cleanup_item_count_ > 0) {
172 Sleep(kShutdownSleepIntervalMs);
173
174 total_wait += kShutdownSleepIntervalMs;
175
176 if (total_wait >= kShutdownDelayMs) {
177 break;
178 }
179 }
180
181 // Clean up all the ClientInfo objects.
182 // New scope to hold the lock for the shortest time.
183 {
184 AutoCriticalSection lock(&clients_sync_);
185
186 std::list<ClientInfo*>::iterator iter;
187 for (iter = clients_.begin(); iter != clients_.end(); ++iter) {
188 ClientInfo* client_info = *iter;
189 delete client_info;
190 }
191 }
192
193 if (server_alive_handle_) {
194 // Release the mutex before closing the handle so that clients requesting
195 // dumps wait for a long time for the server to generate a dump.
196 ReleaseMutex(server_alive_handle_);
197 CloseHandle(server_alive_handle_);
198 }
199
200 DeleteCriticalSection(&clients_sync_);
201 }
202
Start()203 bool CrashGenerationServer::Start() {
204 server_state_ = IPC_SERVER_STATE_INITIAL;
205
206 server_alive_handle_ = CreateMutex(NULL, TRUE, NULL);
207 if (!server_alive_handle_) {
208 return false;
209 }
210
211 // Event to signal the client connection and pipe reads and writes.
212 overlapped_.hEvent = CreateEvent(NULL, // Security descriptor.
213 TRUE, // Manual reset.
214 FALSE, // Initially signaled.
215 NULL); // Name.
216 if (!overlapped_.hEvent) {
217 return false;
218 }
219
220 // Register a callback with the thread pool for the client connection.
221 if (!RegisterWaitForSingleObject(&pipe_wait_handle_,
222 overlapped_.hEvent,
223 OnPipeConnected,
224 this,
225 INFINITE,
226 kPipeIOThreadFlags)) {
227 return false;
228 }
229
230 pipe_ = CreateNamedPipe(pipe_name_.c_str(),
231 kPipeAttr,
232 kPipeMode,
233 1,
234 kOutBufferSize,
235 kInBufferSize,
236 0,
237 pipe_sec_attrs_);
238 if (pipe_ == INVALID_HANDLE_VALUE) {
239 return false;
240 }
241
242 // Signal the event to start a separate thread to handle
243 // client connections.
244 return SetEvent(overlapped_.hEvent) != FALSE;
245 }
246
247 // If the server thread serving clients ever gets into the
248 // ERROR state, reset the event, close the pipe and remain
249 // in the error state forever. Error state means something
250 // that we didn't account for has happened, and it's dangerous
251 // to do anything unknowingly.
HandleErrorState()252 void CrashGenerationServer::HandleErrorState() {
253 assert(server_state_ == IPC_SERVER_STATE_ERROR);
254
255 // If the server is shutting down anyway, don't clean up
256 // here since shut down process will clean up.
257 if (shutting_down_) {
258 return;
259 }
260
261 if (pipe_wait_handle_) {
262 UnregisterWait(pipe_wait_handle_);
263 pipe_wait_handle_ = NULL;
264 }
265
266 if (pipe_) {
267 CloseHandle(pipe_);
268 pipe_ = NULL;
269 }
270
271 if (overlapped_.hEvent) {
272 CloseHandle(overlapped_.hEvent);
273 overlapped_.hEvent = NULL;
274 }
275 }
276
277 // When the server thread serving clients is in the INITIAL state,
278 // try to connect to the pipe asynchronously. If the connection
279 // finishes synchronously, directly go into the CONNECTED state;
280 // otherwise go into the CONNECTING state. For any problems, go
281 // into the ERROR state.
HandleInitialState()282 void CrashGenerationServer::HandleInitialState() {
283 assert(server_state_ == IPC_SERVER_STATE_INITIAL);
284
285 if (!ResetEvent(overlapped_.hEvent)) {
286 server_state_ = IPC_SERVER_STATE_ERROR;
287 return;
288 }
289
290 bool success = ConnectNamedPipe(pipe_, &overlapped_) != FALSE;
291
292 // From MSDN, it is not clear that when ConnectNamedPipe is used
293 // in an overlapped mode, will it ever return non-zero value, and
294 // if so, in what cases.
295 assert(!success);
296
297 DWORD error_code = GetLastError();
298 switch (error_code) {
299 case ERROR_IO_PENDING:
300 server_state_ = IPC_SERVER_STATE_CONNECTING;
301 break;
302
303 case ERROR_PIPE_CONNECTED:
304 if (SetEvent(overlapped_.hEvent)) {
305 server_state_ = IPC_SERVER_STATE_CONNECTED;
306 } else {
307 server_state_ = IPC_SERVER_STATE_ERROR;
308 }
309 break;
310
311 default:
312 server_state_ = IPC_SERVER_STATE_ERROR;
313 break;
314 }
315 }
316
317 // When the server thread serving the clients is in the CONNECTING state,
318 // try to get the result of the asynchronous connection request using
319 // the OVERLAPPED object. If the result indicates the connection is done,
320 // go into the CONNECTED state. If the result indicates I/O is still
321 // INCOMPLETE, remain in the CONNECTING state. For any problems,
322 // go into the DISCONNECTING state.
HandleConnectingState()323 void CrashGenerationServer::HandleConnectingState() {
324 assert(server_state_ == IPC_SERVER_STATE_CONNECTING);
325
326 DWORD bytes_count = 0;
327 bool success = GetOverlappedResult(pipe_,
328 &overlapped_,
329 &bytes_count,
330 FALSE) != FALSE;
331
332 if (success) {
333 server_state_ = IPC_SERVER_STATE_CONNECTED;
334 return;
335 }
336
337 if (GetLastError() != ERROR_IO_INCOMPLETE) {
338 server_state_ = IPC_SERVER_STATE_DISCONNECTING;
339 }
340 }
341
342 // When the server thread serving the clients is in the CONNECTED state,
343 // try to issue an asynchronous read from the pipe. If read completes
344 // synchronously or if I/O is pending then go into the READING state.
345 // For any problems, go into the DISCONNECTING state.
HandleConnectedState()346 void CrashGenerationServer::HandleConnectedState() {
347 assert(server_state_ == IPC_SERVER_STATE_CONNECTED);
348
349 DWORD bytes_count = 0;
350 memset(&msg_, 0, sizeof(msg_));
351 bool success = ReadFile(pipe_,
352 &msg_,
353 sizeof(msg_),
354 &bytes_count,
355 &overlapped_) != FALSE;
356
357 // Note that the asynchronous read issued above can finish before the
358 // code below executes. But, it is okay to change state after issuing
359 // the asynchronous read. This is because even if the asynchronous read
360 // is done, the callback for it would not be executed until the current
361 // thread finishes its execution.
362 if (success || GetLastError() == ERROR_IO_PENDING) {
363 server_state_ = IPC_SERVER_STATE_READING;
364 } else {
365 server_state_ = IPC_SERVER_STATE_DISCONNECTING;
366 }
367 }
368
369 // When the server thread serving the clients is in the READING state,
370 // try to get the result of the async read. If async read is done,
371 // go into the READ_DONE state. For any problems, go into the
372 // DISCONNECTING state.
HandleReadingState()373 void CrashGenerationServer::HandleReadingState() {
374 assert(server_state_ == IPC_SERVER_STATE_READING);
375
376 DWORD bytes_count = 0;
377 bool success = GetOverlappedResult(pipe_,
378 &overlapped_,
379 &bytes_count,
380 FALSE) != FALSE;
381
382 if (success && bytes_count == sizeof(ProtocolMessage)) {
383 server_state_ = IPC_SERVER_STATE_READ_DONE;
384 return;
385 }
386
387 DWORD error_code;
388 error_code = GetLastError();
389
390 // We should never get an I/O incomplete since we should not execute this
391 // unless the Read has finished and the overlapped event is signaled. If
392 // we do get INCOMPLETE, we have a bug in our code.
393 assert(error_code != ERROR_IO_INCOMPLETE);
394
395 server_state_ = IPC_SERVER_STATE_DISCONNECTING;
396 }
397
398 // When the server thread serving the client is in the READ_DONE state,
399 // validate the client's request message, register the client by
400 // creating appropriate objects and prepare the response. Then try to
401 // write the response to the pipe asynchronously. If that succeeds,
402 // go into the WRITING state. For any problems, go into the DISCONNECTING
403 // state.
HandleReadDoneState()404 void CrashGenerationServer::HandleReadDoneState() {
405 assert(server_state_ == IPC_SERVER_STATE_READ_DONE);
406
407 if (!IsClientRequestValid(msg_)) {
408 server_state_ = IPC_SERVER_STATE_DISCONNECTING;
409 return;
410 }
411
412 scoped_ptr<ClientInfo> client_info(
413 new ClientInfo(this,
414 msg_.pid,
415 msg_.dump_type,
416 msg_.thread_id,
417 msg_.exception_pointers,
418 msg_.assert_info,
419 msg_.custom_client_info));
420
421 if (!client_info->Initialize()) {
422 server_state_ = IPC_SERVER_STATE_DISCONNECTING;
423 return;
424 }
425
426 if (!RespondToClient(client_info.get())) {
427 server_state_ = IPC_SERVER_STATE_DISCONNECTING;
428 return;
429 }
430
431 // Note that the asynchronous write issued by RespondToClient function
432 // can finish before the code below executes. But it is okay to change
433 // state after issuing the asynchronous write. This is because even if
434 // the asynchronous write is done, the callback for it would not be
435 // executed until the current thread finishes its execution.
436 server_state_ = IPC_SERVER_STATE_WRITING;
437 client_info_ = client_info.release();
438 }
439
440 // When the server thread serving the clients is in the WRITING state,
441 // try to get the result of the async write. If the async write is done,
442 // go into the WRITE_DONE state. For any problems, go into the
443 // DISONNECTING state.
HandleWritingState()444 void CrashGenerationServer::HandleWritingState() {
445 assert(server_state_ == IPC_SERVER_STATE_WRITING);
446
447 DWORD bytes_count = 0;
448 bool success = GetOverlappedResult(pipe_,
449 &overlapped_,
450 &bytes_count,
451 FALSE) != FALSE;
452
453 if (success) {
454 server_state_ = IPC_SERVER_STATE_WRITE_DONE;
455 return;
456 }
457
458 DWORD error_code;
459 error_code = GetLastError();
460
461 // We should never get an I/O incomplete since we should not execute this
462 // unless the Write has finished and the overlapped event is signaled. If
463 // we do get INCOMPLETE, we have a bug in our code.
464 assert(error_code != ERROR_IO_INCOMPLETE);
465
466 server_state_ = IPC_SERVER_STATE_DISCONNECTING;
467 }
468
469 // When the server thread serving the clients is in the WRITE_DONE state,
470 // try to issue an async read on the pipe. If the read completes synchronously
471 // or if I/O is still pending then go into the READING_ACK state. For any
472 // issues, go into the DISCONNECTING state.
HandleWriteDoneState()473 void CrashGenerationServer::HandleWriteDoneState() {
474 assert(server_state_ == IPC_SERVER_STATE_WRITE_DONE);
475
476 server_state_ = IPC_SERVER_STATE_READING_ACK;
477
478 DWORD bytes_count = 0;
479 bool success = ReadFile(pipe_,
480 &msg_,
481 sizeof(msg_),
482 &bytes_count,
483 &overlapped_) != FALSE;
484
485 if (success) {
486 return;
487 }
488
489 DWORD error_code = GetLastError();
490
491 if (error_code != ERROR_IO_PENDING) {
492 server_state_ = IPC_SERVER_STATE_DISCONNECTING;
493 }
494 }
495
496 // When the server thread serving the clients is in the READING_ACK state,
497 // try to get result of async read. Go into the DISCONNECTING state.
HandleReadingAckState()498 void CrashGenerationServer::HandleReadingAckState() {
499 assert(server_state_ == IPC_SERVER_STATE_READING_ACK);
500
501 DWORD bytes_count = 0;
502 bool success = GetOverlappedResult(pipe_,
503 &overlapped_,
504 &bytes_count,
505 FALSE) != FALSE;
506
507 if (success) {
508 // The connection handshake with the client is now complete; perform
509 // the callback.
510 if (connect_callback_) {
511 connect_callback_(connect_context_, client_info_);
512 }
513 } else {
514 DWORD error_code = GetLastError();
515
516 // We should never get an I/O incomplete since we should not execute this
517 // unless the Read has finished and the overlapped event is signaled. If
518 // we do get INCOMPLETE, we have a bug in our code.
519 assert(error_code != ERROR_IO_INCOMPLETE);
520 }
521
522 server_state_ = IPC_SERVER_STATE_DISCONNECTING;
523 }
524
525 // When the server thread serving the client is in the DISCONNECTING state,
526 // disconnect from the pipe and reset the event. If anything fails, go into
527 // the ERROR state. If it goes well, go into the INITIAL state and set the
528 // event to start all over again.
HandleDisconnectingState()529 void CrashGenerationServer::HandleDisconnectingState() {
530 assert(server_state_ == IPC_SERVER_STATE_DISCONNECTING);
531
532 // Done serving the client.
533 client_info_ = NULL;
534
535 overlapped_.Internal = NULL;
536 overlapped_.InternalHigh = NULL;
537 overlapped_.Offset = 0;
538 overlapped_.OffsetHigh = 0;
539 overlapped_.Pointer = NULL;
540
541 if (!ResetEvent(overlapped_.hEvent)) {
542 server_state_ = IPC_SERVER_STATE_ERROR;
543 return;
544 }
545
546 if (!DisconnectNamedPipe(pipe_)) {
547 server_state_ = IPC_SERVER_STATE_ERROR;
548 return;
549 }
550
551 // If the server is shutting down do not connect to the
552 // next client.
553 if (shutting_down_) {
554 return;
555 }
556
557 server_state_ = IPC_SERVER_STATE_INITIAL;
558 if (!SetEvent(overlapped_.hEvent)) {
559 server_state_ = IPC_SERVER_STATE_ERROR;
560 }
561 }
562
PrepareReply(const ClientInfo & client_info,ProtocolMessage * reply) const563 bool CrashGenerationServer::PrepareReply(const ClientInfo& client_info,
564 ProtocolMessage* reply) const {
565 reply->tag = MESSAGE_TAG_REGISTRATION_RESPONSE;
566 reply->pid = GetCurrentProcessId();
567
568 if (CreateClientHandles(client_info, reply)) {
569 return true;
570 }
571
572 if (reply->dump_request_handle) {
573 CloseHandle(reply->dump_request_handle);
574 }
575
576 if (reply->dump_generated_handle) {
577 CloseHandle(reply->dump_generated_handle);
578 }
579
580 if (reply->server_alive_handle) {
581 CloseHandle(reply->server_alive_handle);
582 }
583
584 return false;
585 }
586
CreateClientHandles(const ClientInfo & client_info,ProtocolMessage * reply) const587 bool CrashGenerationServer::CreateClientHandles(const ClientInfo& client_info,
588 ProtocolMessage* reply) const {
589 HANDLE current_process = GetCurrentProcess();
590 if (!DuplicateHandle(current_process,
591 client_info.dump_requested_handle(),
592 client_info.process_handle(),
593 &reply->dump_request_handle,
594 kDumpRequestEventAccess,
595 FALSE,
596 0)) {
597 return false;
598 }
599
600 if (!DuplicateHandle(current_process,
601 client_info.dump_generated_handle(),
602 client_info.process_handle(),
603 &reply->dump_generated_handle,
604 kDumpGeneratedEventAccess,
605 FALSE,
606 0)) {
607 return false;
608 }
609
610 if (!DuplicateHandle(current_process,
611 server_alive_handle_,
612 client_info.process_handle(),
613 &reply->server_alive_handle,
614 kMutexAccess,
615 FALSE,
616 0)) {
617 return false;
618 }
619
620 return true;
621 }
622
RespondToClient(ClientInfo * client_info)623 bool CrashGenerationServer::RespondToClient(ClientInfo* client_info) {
624 ProtocolMessage reply;
625 if (!PrepareReply(*client_info, &reply)) {
626 return false;
627 }
628
629 if (!AddClient(client_info)) {
630 return false;
631 }
632
633 DWORD bytes_count = 0;
634 bool success = WriteFile(pipe_,
635 &reply,
636 sizeof(reply),
637 &bytes_count,
638 &overlapped_) != FALSE;
639
640 return success || GetLastError() == ERROR_IO_PENDING;
641 }
642
643 // The server thread servicing the clients runs this method. The method
644 // implements the state machine described in ReadMe.txt along with the
645 // helper methods HandleXXXState.
HandleConnectionRequest()646 void CrashGenerationServer::HandleConnectionRequest() {
647 // If we are shutting doen then get into ERROR state, reset the event so more
648 // workers don't run and return immediately.
649 if (shutting_down_) {
650 server_state_ = IPC_SERVER_STATE_ERROR;
651 ResetEvent(overlapped_.hEvent);
652 return;
653 }
654
655 switch (server_state_) {
656 case IPC_SERVER_STATE_ERROR:
657 HandleErrorState();
658 break;
659
660 case IPC_SERVER_STATE_INITIAL:
661 HandleInitialState();
662 break;
663
664 case IPC_SERVER_STATE_CONNECTING:
665 HandleConnectingState();
666 break;
667
668 case IPC_SERVER_STATE_CONNECTED:
669 HandleConnectedState();
670 break;
671
672 case IPC_SERVER_STATE_READING:
673 HandleReadingState();
674 break;
675
676 case IPC_SERVER_STATE_READ_DONE:
677 HandleReadDoneState();
678 break;
679
680 case IPC_SERVER_STATE_WRITING:
681 HandleWritingState();
682 break;
683
684 case IPC_SERVER_STATE_WRITE_DONE:
685 HandleWriteDoneState();
686 break;
687
688 case IPC_SERVER_STATE_READING_ACK:
689 HandleReadingAckState();
690 break;
691
692 case IPC_SERVER_STATE_DISCONNECTING:
693 HandleDisconnectingState();
694 break;
695
696 default:
697 assert(false);
698 // This indicates that we added one more state without
699 // adding handling code.
700 server_state_ = IPC_SERVER_STATE_ERROR;
701 break;
702 }
703 }
704
AddClient(ClientInfo * client_info)705 bool CrashGenerationServer::AddClient(ClientInfo* client_info) {
706 HANDLE request_wait_handle = NULL;
707 if (!RegisterWaitForSingleObject(&request_wait_handle,
708 client_info->dump_requested_handle(),
709 OnDumpRequest,
710 client_info,
711 INFINITE,
712 kDumpRequestThreadFlags)) {
713 return false;
714 }
715
716 client_info->set_dump_request_wait_handle(request_wait_handle);
717
718 // OnClientEnd will be called when the client process terminates.
719 HANDLE process_wait_handle = NULL;
720 if (!RegisterWaitForSingleObject(&process_wait_handle,
721 client_info->process_handle(),
722 OnClientEnd,
723 client_info,
724 INFINITE,
725 WT_EXECUTEONLYONCE)) {
726 return false;
727 }
728
729 client_info->set_process_exit_wait_handle(process_wait_handle);
730
731 // New scope to hold the lock for the shortest time.
732 {
733 AutoCriticalSection lock(&clients_sync_);
734 clients_.push_back(client_info);
735 }
736
737 return true;
738 }
739
740 // static
OnPipeConnected(void * context,BOOLEAN)741 void CALLBACK CrashGenerationServer::OnPipeConnected(void* context, BOOLEAN) {
742 assert (context);
743
744 CrashGenerationServer* obj =
745 reinterpret_cast<CrashGenerationServer*>(context);
746 obj->HandleConnectionRequest();
747 }
748
749 // static
OnDumpRequest(void * context,BOOLEAN)750 void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) {
751 assert(context);
752 ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
753 client_info->PopulateCustomInfo();
754
755 CrashGenerationServer* crash_server = client_info->crash_server();
756 assert(crash_server);
757 crash_server->HandleDumpRequest(*client_info);
758
759 ResetEvent(client_info->dump_requested_handle());
760 }
761
762 // static
OnClientEnd(void * context,BOOLEAN)763 void CALLBACK CrashGenerationServer::OnClientEnd(void* context, BOOLEAN) {
764 assert(context);
765 ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
766
767 CrashGenerationServer* crash_server = client_info->crash_server();
768 assert(crash_server);
769
770 InterlockedIncrement(&crash_server->cleanup_item_count_);
771
772 if (!QueueUserWorkItem(CleanupClient, context, WT_EXECUTEDEFAULT)) {
773 InterlockedDecrement(&crash_server->cleanup_item_count_);
774 }
775 }
776
777 // static
CleanupClient(void * context)778 DWORD WINAPI CrashGenerationServer::CleanupClient(void* context) {
779 assert(context);
780 ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
781
782 CrashGenerationServer* crash_server = client_info->crash_server();
783 assert(crash_server);
784
785 if (crash_server->exit_callback_) {
786 crash_server->exit_callback_(crash_server->exit_context_, client_info);
787 }
788
789 crash_server->DoCleanup(client_info);
790
791 InterlockedDecrement(&crash_server->cleanup_item_count_);
792 return 0;
793 }
794
DoCleanup(ClientInfo * client_info)795 void CrashGenerationServer::DoCleanup(ClientInfo* client_info) {
796 assert(client_info);
797
798 // Start a new scope to release lock automatically.
799 {
800 AutoCriticalSection lock(&clients_sync_);
801 clients_.remove(client_info);
802 }
803
804 delete client_info;
805 }
806
HandleDumpRequest(const ClientInfo & client_info)807 void CrashGenerationServer::HandleDumpRequest(const ClientInfo& client_info) {
808 // Generate the dump only if it's explicitly requested by the
809 // server application; otherwise the server might want to generate
810 // dump in the callback.
811 std::wstring dump_path;
812 if (generate_dumps_) {
813 if (!GenerateDump(client_info, &dump_path)) {
814 return;
815 }
816 }
817
818 if (dump_callback_) {
819 std::wstring* ptr_dump_path = (dump_path == L"") ? NULL : &dump_path;
820 dump_callback_(dump_context_, &client_info, ptr_dump_path);
821 }
822
823 SetEvent(client_info.dump_generated_handle());
824 }
825
GenerateDump(const ClientInfo & client,std::wstring * dump_path)826 bool CrashGenerationServer::GenerateDump(const ClientInfo& client,
827 std::wstring* dump_path) {
828 assert(client.pid() != 0);
829 assert(client.process_handle());
830
831 // We have to get the address of EXCEPTION_INFORMATION from
832 // the client process address space.
833 EXCEPTION_POINTERS* client_ex_info = NULL;
834 if (!client.GetClientExceptionInfo(&client_ex_info)) {
835 return false;
836 }
837
838 DWORD client_thread_id = 0;
839 if (!client.GetClientThreadId(&client_thread_id)) {
840 return false;
841 }
842
843 return dump_generator_->WriteMinidump(client.process_handle(),
844 client.pid(),
845 client_thread_id,
846 GetCurrentThreadId(),
847 client_ex_info,
848 client.assert_info(),
849 client.dump_type(),
850 true,
851 dump_path);
852 }
853
854 } // namespace google_breakpad
855