diff --git a/src/client/windows/breakpad_client.sln b/src/client/windows/breakpad_client.sln index 990fa546..9894954b 100644 --- a/src/client/windows/breakpad_client.sln +++ b/src/client/windows/breakpad_client.sln @@ -5,6 +5,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exception_handler", "handle EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_report_sender", "sender\crash_report_sender.vcproj", "{9946A048-043B-4F8F-9E07-9297B204714C}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_generation", "crash_generation\crash_generation.vcproj", "{A820AF62-6239-4693-8430-4F516C1838F4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -29,6 +31,14 @@ Global {9946A048-043B-4F8F-9E07-9297B204714C}.Release|Win32.Build.0 = Release|Win32 {9946A048-043B-4F8F-9E07-9297B204714C}.ReleaseStaticCRT|Win32.ActiveCfg = ReleaseStaticCRT|Win32 {9946A048-043B-4F8F-9E07-9297B204714C}.ReleaseStaticCRT|Win32.Build.0 = ReleaseStaticCRT|Win32 + {A820AF62-6239-4693-8430-4F516C1838F4}.Debug|Win32.ActiveCfg = Debug|Win32 + {A820AF62-6239-4693-8430-4F516C1838F4}.Debug|Win32.Build.0 = Debug|Win32 + {A820AF62-6239-4693-8430-4F516C1838F4}.DebugStaticCRT|Win32.ActiveCfg = Debug|Win32 + {A820AF62-6239-4693-8430-4F516C1838F4}.DebugStaticCRT|Win32.Build.0 = Debug|Win32 + {A820AF62-6239-4693-8430-4F516C1838F4}.Release|Win32.ActiveCfg = Release|Win32 + {A820AF62-6239-4693-8430-4F516C1838F4}.Release|Win32.Build.0 = Release|Win32 + {A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.ActiveCfg = Release|Win32 + {A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/client/windows/common/auto_critical_section.h b/src/client/windows/common/auto_critical_section.h new file mode 100644 index 00000000..9bf3d492 --- /dev/null +++ b/src/client/windows/common/auto_critical_section.h @@ -0,0 +1,63 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ +#define CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ + +#include + +namespace google_breakpad { + +// Automatically enters the critical section in the constructor and leaves +// the critical section in the destructor. +class AutoCriticalSection { + public: + // Creates a new instance with the given critical section object + // and enters the critical section immediately. + explicit AutoCriticalSection(CRITICAL_SECTION* cs) : cs_(cs) { + assert(cs_); + EnterCriticalSection(cs_); + } + + // Destructor: leaves the critical section. + ~AutoCriticalSection() { + LeaveCriticalSection(cs_); + } + + private: + // Disable copy ctor and operator=. + AutoCriticalSection(const AutoCriticalSection&); + AutoCriticalSection& operator=(const AutoCriticalSection&); + + CRITICAL_SECTION* cs_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ diff --git a/src/client/windows/common/ipc_protocol.h b/src/client/windows/common/ipc_protocol.h new file mode 100644 index 00000000..aa3751f3 --- /dev/null +++ b/src/client/windows/common/ipc_protocol.h @@ -0,0 +1,122 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ +#define CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ + +#include +#include +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// Constants for the protocol between client and the server. + +// Tags sent with each message indicating the purpose of +// the message. +enum MessageTag { + MESSAGE_TAG_NONE = 0, + MESSAGE_TAG_REGISTRATION_REQUEST = 1, + MESSAGE_TAG_REGISTRATION_RESPONSE = 2, + MESSAGE_TAG_REGISTRATION_ACK = 3 +}; + +// Message structure for IPC between crash client and crash server. +struct ProtocolMessage { + ProtocolMessage() + : tag(MESSAGE_TAG_NONE), + pid(0), + dump_type(MiniDumpNormal), + thread_id(0), + exception_pointers(NULL), + assert_info(NULL), + dump_request_handle(NULL), + dump_generated_handle(NULL), + server_alive_handle(NULL) { + } + + // Creates an instance with the given parameters. + ProtocolMessage(MessageTag arg_tag, + DWORD arg_pid, + MINIDUMP_TYPE arg_dump_type, + DWORD* arg_thread_id, + EXCEPTION_POINTERS** arg_exception_pointers, + MDRawAssertionInfo* arg_assert_info, + HANDLE arg_dump_request_handle, + HANDLE arg_dump_generated_handle, + HANDLE arg_server_alive) + : tag(arg_tag), + pid(arg_pid), + dump_type(arg_dump_type), + thread_id(arg_thread_id), + exception_pointers(arg_exception_pointers), + assert_info(arg_assert_info), + dump_request_handle(arg_dump_request_handle), + dump_generated_handle(arg_dump_generated_handle), + server_alive_handle(arg_server_alive) { + } + + // Tag in the message. + MessageTag tag; + + // Process id. + DWORD pid; + + // Dump type requested. + MINIDUMP_TYPE dump_type; + + // Client thread id pointer. + DWORD* thread_id; + + // Exception information. + EXCEPTION_POINTERS** exception_pointers; + + // Assert information in case of an invalid parameter or + // pure call failure. + MDRawAssertionInfo* assert_info; + + // Handle to signal the crash event. + HANDLE dump_request_handle; + + // Handle to check if server is done generating crash. + HANDLE dump_generated_handle; + + // Handle to a mutex that becomes signaled (WAIT_ABANDONED) + // if server process goes down. + HANDLE server_alive_handle; + + private: + // Disable copy ctor and operator=. + ProtocolMessage(const ProtocolMessage& msg); + ProtocolMessage& operator=(const ProtocolMessage& msg); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ diff --git a/src/client/windows/crash_generation/ReadMe.txt b/src/client/windows/crash_generation/ReadMe.txt new file mode 100644 index 00000000..d9ded287 --- /dev/null +++ b/src/client/windows/crash_generation/ReadMe.txt @@ -0,0 +1,58 @@ +========================================================================= + State machine transitions for the Crash Generation Server +========================================================================= + +========================================================================= + | + STATE | ACTIONS + | +========================================================================= + ERROR | Clean up resources used to serve clients. + | Always remain in ERROR state. +------------------------------------------------------------------------- + INITIAL | Connect to the pipe asynchronously. + | If connection is successfully queued up asynchronously, + | go into CONNECTING state. + | If connection is done synchronously, go into CONNECTED + | state. + | For any unexpected problems, go into ERROR state. +------------------------------------------------------------------------- + CONNECTING | Get the result of async connection request. + | If I/O is still incomplete, remain in the CONNECTING + | state. + | If connection is complete, go into CONNECTED state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + CONNECTED | Read from the pipe asynchronously. + | If read request is successfully queued up asynchronously, + | go into READING state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + READING | Get the result of async read request. + | If read is done, go into READ_DONE state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + READ_DONE | Register the client, prepare the reply and write the + | reply to the pipe asynchronously. + | If write request is successfully queued up asynchronously, + | go into WRITING state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + WRITING | Get the result of the async write request. + | If write is done, go into WRITE_DONE state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + WRITE_DONE | Read from the pipe asynchronously (for an ACK). + | If read request is successfully queued up asynchonously, + | go into READING_ACK state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + READING_ACK | Get the result of the async read request. + | If read is done, perform action for successful client + | connection. + | Go into DISCONNECTING state. +------------------------------------------------------------------------- + DISCONNECTING | Disconnect from the pipe, reset the event and go into + | INITIAL state and signal the event again. If anything + | fails, go into ERROR state. +========================================================================= diff --git a/src/client/windows/crash_generation/client_info.cc b/src/client/windows/crash_generation/client_info.cc new file mode 100644 index 00000000..efd3b029 --- /dev/null +++ b/src/client/windows/crash_generation/client_info.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "client/windows/crash_generation/client_info.h" + +namespace google_breakpad { + +ClientInfo::ClientInfo(CrashGenerationServer* crash_server, + DWORD pid, + MINIDUMP_TYPE dump_type, + DWORD* thread_id, + EXCEPTION_POINTERS** ex_info, + MDRawAssertionInfo* assert_info) + : crash_server_(crash_server), + pid_(pid), + dump_type_(dump_type), + ex_info_(ex_info), + assert_info_(assert_info), + thread_id_(thread_id), + process_handle_(NULL), + dump_requested_handle_(NULL), + dump_generated_handle_(NULL), + dump_request_wait_handle_(NULL), + process_exit_wait_handle_(NULL) { +} + +bool ClientInfo::Initialize() { + process_handle_ = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid_); + if (!process_handle_) { + return false; + } + + dump_requested_handle_ = CreateEvent(NULL, // Security attributes. + TRUE, // Manual reset. + FALSE, // Initial state. + NULL); // Name. + if (!dump_requested_handle_) { + return false; + } + + dump_generated_handle_ = CreateEvent(NULL, // Security attributes. + TRUE, // Manual reset. + FALSE, // Initial state. + NULL); // Name. + return dump_generated_handle_ != NULL; +} + +ClientInfo::~ClientInfo() { + if (process_handle_) { + CloseHandle(process_handle_); + } + + if (dump_requested_handle_) { + CloseHandle(dump_requested_handle_); + } + + if (dump_generated_handle_) { + CloseHandle(dump_generated_handle_); + } + + if (dump_request_wait_handle_) { + // Wait for callbacks that might already be running to finish. + UnregisterWaitEx(dump_request_wait_handle_, INVALID_HANDLE_VALUE); + } + + if (process_exit_wait_handle_) { + // Wait for the callback that might already be running to finish. + UnregisterWaitEx(process_exit_wait_handle_, INVALID_HANDLE_VALUE); + } +} + +bool ClientInfo::UnregisterWaits() { + bool success = true; + + if (dump_request_wait_handle_) { + if (!UnregisterWait(dump_request_wait_handle_)) { + success = false; + } else { + dump_request_wait_handle_ = NULL; + } + } + + if (process_exit_wait_handle_) { + if (!UnregisterWait(process_exit_wait_handle_)) { + success = false; + } else { + process_exit_wait_handle_ = NULL; + } + } + + return success; +} + +bool ClientInfo::GetClientExceptionInfo( + EXCEPTION_POINTERS** ex_info) const { + SIZE_T bytes_count = 0; + if (!ReadProcessMemory(process_handle_, + ex_info_, + ex_info, + sizeof(*ex_info), + &bytes_count)) { + return false; + } + + return bytes_count == sizeof(*ex_info); +} + +bool ClientInfo::GetClientThreadId(DWORD* thread_id) const { + SIZE_T bytes_count = 0; + if (!ReadProcessMemory(process_handle_, + thread_id_, + thread_id, + sizeof(*thread_id), + &bytes_count)) { + return false; + } + + return bytes_count == sizeof(*thread_id); +} + +} // namespace google_breakpad diff --git a/src/client/windows/crash_generation/client_info.h b/src/client/windows/crash_generation/client_info.h new file mode 100644 index 00000000..06de6311 --- /dev/null +++ b/src/client/windows/crash_generation/client_info.h @@ -0,0 +1,145 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__ +#define CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__ + +#include +#include +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +class CrashGenerationServer; + +// Abstraction for a crash client process. +class ClientInfo { + public: + // Creates an instance with the given values. Gets the process + // handle for the given process id and creates necessary event + // objects. + ClientInfo(CrashGenerationServer* crash_server, + DWORD pid, + MINIDUMP_TYPE dump_type, + DWORD* thread_id, + EXCEPTION_POINTERS** ex_info, + MDRawAssertionInfo* assert_info); + + ~ClientInfo(); + + CrashGenerationServer* crash_server() const { return crash_server_; } + DWORD pid() const { return pid_; } + MINIDUMP_TYPE dump_type() const { return dump_type_; } + EXCEPTION_POINTERS** ex_info() const { return ex_info_; } + MDRawAssertionInfo* assert_info() const { return assert_info_; } + DWORD* thread_id() const { return thread_id_; } + HANDLE process_handle() const { return process_handle_; } + HANDLE dump_requested_handle() const { return dump_requested_handle_; } + HANDLE dump_generated_handle() const { return dump_generated_handle_; } + + HANDLE dump_request_wait_handle() const { + return dump_request_wait_handle_; + } + + void set_dump_request_wait_handle(HANDLE value) { + dump_request_wait_handle_ = value; + } + + HANDLE process_exit_wait_handle() const { + return process_exit_wait_handle_; + } + + void set_process_exit_wait_handle(HANDLE value) { + process_exit_wait_handle_ = value; + } + + // Unregister all waits for the client. + bool UnregisterWaits(); + + bool Initialize(); + bool GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const; + bool GetClientThreadId(DWORD* thread_id) const; + + private: + // Crash generation server. + CrashGenerationServer* crash_server_; + + // Client process ID. + DWORD pid_; + + // Dump type requested by the client. + MINIDUMP_TYPE dump_type_; + + // Address of an EXCEPTION_POINTERS* variable in the client + // process address space that will point to an instance of + // EXCEPTION_POINTERS containing information about crash. + // + // WARNING: Do not dereference these pointers as they are pointers + // in the address space of another process. + EXCEPTION_POINTERS** ex_info_; + + // Address of an instance of MDRawAssertionInfo in the client + // process address space that will contain information about + // non-exception related crashes like invalid parameter assertion + // failures and pure calls. + // + // WARNING: Do not dereference these pointers as they are pointers + // in the address space of another process. + MDRawAssertionInfo* assert_info_; + + // Address of a variable in the client process address space that + // will contain the thread id of the crashing client thread. + // + // WARNING: Do not dereference these pointers as they are pointers + // in the address space of another process. + DWORD* thread_id_; + + // Client process handle. + HANDLE process_handle_; + + // Dump request event handle. + HANDLE dump_requested_handle_; + + // Dump generated event handle. + HANDLE dump_generated_handle_; + + // Wait handle for dump request event. + HANDLE dump_request_wait_handle_; + + // Wait handle for process exit event. + HANDLE process_exit_wait_handle_; + + // Disallow copy ctor and operator=. + ClientInfo(const ClientInfo& client_info); + ClientInfo& operator=(const ClientInfo& client_info); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__ diff --git a/src/client/windows/crash_generation/crash_generation.vcproj b/src/client/windows/crash_generation/crash_generation.vcproj new file mode 100644 index 00000000..e84c7aee --- /dev/null +++ b/src/client/windows/crash_generation/crash_generation.vcproj @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/client/windows/crash_generation/crash_generation_client.cc b/src/client/windows/crash_generation/crash_generation_client.cc new file mode 100644 index 00000000..77c698b1 --- /dev/null +++ b/src/client/windows/crash_generation/crash_generation_client.cc @@ -0,0 +1,329 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "client/windows/crash_generation/crash_generation_client.h" +#include +#include "client/windows/common/ipc_protocol.h" + +namespace google_breakpad { + +const int kPipeBusyWaitTimeoutMs = 2000; + +#ifdef _DEBUG +const DWORD kWaitForServerTimeoutMs = INFINITE; +#else +const DWORD kWaitForServerTimeoutMs = 15000; +#endif + +const int kPipeConnectMaxAttempts = 2; + +const DWORD kPipeDesiredAccess = FILE_READ_DATA | + FILE_WRITE_DATA | + FILE_WRITE_ATTRIBUTES; + +const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION | + SECURITY_SQOS_PRESENT; + +const DWORD kPipeMode = PIPE_READMODE_MESSAGE; + +const size_t kWaitEventCount = 2; + +// This function is orphan for production code. It can be used +// for debugging to help repro some scenarios like the client +// is slow in writing to the pipe after connecting, the client +// is slow in reading from the pipe after writing, etc. The parameter +// overlapped below is not used and it is present to match the signature +// of this function to TransactNamedPipe Win32 API. Uncomment if needed +// for debugging. +/** +static bool TransactNamedPipeDebugHelper(HANDLE pipe, + const void* in_buffer, + DWORD in_size, + void* out_buffer, + DWORD out_size, + DWORD* bytes_count, + LPOVERLAPPED) { + // Uncomment the next sleep to create a gap before writing + // to pipe. + // Sleep(5000); + + if (!WriteFile(pipe, + in_buffer, + in_size, + bytes_count, + NULL)) { + return false; + } + + // Uncomment the next sleep to create a gap between write + // and read. + // Sleep(5000); + + return ReadFile(pipe, out_buffer, out_size, bytes_count, NULL) != FALSE; +} +**/ + +CrashGenerationClient::CrashGenerationClient(const wchar_t* pipe_name, + MINIDUMP_TYPE dump_type) + : pipe_name_(pipe_name), + dump_type_(dump_type), + thread_id_(0), + server_process_id_(0), + crash_event_(NULL), + crash_generated_(NULL), + server_alive_(NULL), + exception_pointers_(NULL) { + memset(&assert_info_, 0, sizeof(assert_info_)); +} + +CrashGenerationClient::~CrashGenerationClient() { + if (crash_event_) { + CloseHandle(crash_event_); + } + + if (crash_generated_) { + CloseHandle(crash_generated_); + } + + if (server_alive_) { + CloseHandle(server_alive_); + } +} + +// Performs the registration step with the server process. +// The registration step involves communicating with the server +// via a named pipe. The client sends the following pieces of +// data to the server: +// +// * Message tag indicating the client is requesting registration. +// * Process id of the client process. +// * Address of a DWORD variable in the client address space +// that will contain the thread id of the client thread that +// caused the crash. +// * Address of a EXCEPTION_POINTERS* variable in the client +// address space that will point to an instance of EXCEPTION_POINTERS +// when the crash happens. +// * Address of an instance of MDRawAssertionInfo that will contain +// relevant information in case of non-exception crashes like assertion +// failures and pure calls. +// +// In return the client expects the following information from the server: +// +// * Message tag indicating successful registration. +// * Server process id. +// * Handle to an object that client can signal to request dump +// generation from the server. +// * Handle to an object that client can wait on after requesting +// dump generation for the server to finish dump generation. +// * Handle to a mutex object that client can wait on to make sure +// server is still alive. +// +// If any step of the expected behavior mentioned above fails, the +// registration step is not considered successful and hence out-of-process +// dump generation service is not available. +// +// Returns true if the registration is successful; false otherwise. +bool CrashGenerationClient::Register() { + HANDLE pipe = ConnectToServer(); + if (!pipe) { + return false; + } + + bool success = RegisterClient(pipe); + CloseHandle(pipe); + return success; +} + +HANDLE CrashGenerationClient::ConnectToServer() { + HANDLE pipe = ConnectToPipe(pipe_name_.c_str(), + kPipeDesiredAccess, + kPipeFlagsAndAttributes); + if (!pipe) { + return NULL; + } + + DWORD mode = kPipeMode; + if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) { + CloseHandle(pipe); + pipe = NULL; + } + + return pipe; +} + +bool CrashGenerationClient::RegisterClient(HANDLE pipe) { + ProtocolMessage msg(MESSAGE_TAG_REGISTRATION_REQUEST, + GetCurrentProcessId(), + dump_type_, + &thread_id_, + &exception_pointers_, + &assert_info_, + NULL, + NULL, + NULL); + ProtocolMessage reply; + DWORD bytes_count = 0; + // The call to TransactNamedPipe below can be changed to a call + // to TransactNamedPipeDebugHelper to help repro some scenarios. + // For details see comments for TransactNamedPipeDebugHelper. + if (!TransactNamedPipe(pipe, + &msg, + sizeof(msg), + &reply, + sizeof(ProtocolMessage), + &bytes_count, + NULL)) { + return false; + } + + if (!ValidateResponse(reply)) { + return false; + } + + ProtocolMessage ack_msg; + ack_msg.tag = MESSAGE_TAG_REGISTRATION_ACK; + + if (!WriteFile(pipe, &ack_msg, sizeof(ack_msg), &bytes_count, NULL)) { + return false; + } + crash_event_ = reply.dump_request_handle; + crash_generated_ = reply.dump_generated_handle; + server_alive_ = reply.server_alive_handle; + server_process_id_ = reply.pid; + + return true; +} + +HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name, + DWORD pipe_access, + DWORD flags_attrs) { + for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { + HANDLE pipe = CreateFile(pipe_name, + pipe_access, + 0, + NULL, + OPEN_EXISTING, + flags_attrs, + NULL); + if (pipe != INVALID_HANDLE_VALUE) { + return pipe; + } + + // Cannot continue retrying if error is something other than + // ERROR_PIPE_BUSY. + if (GetLastError() != ERROR_PIPE_BUSY) { + break; + } + + // Cannot continue retrying if wait on pipe fails. + if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) { + break; + } + } + + return NULL; +} + +bool CrashGenerationClient::ValidateResponse( + const ProtocolMessage& msg) const { + return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) && + (msg.pid != 0) && + (msg.dump_request_handle != NULL) && + (msg.dump_generated_handle != NULL) && + (msg.server_alive_handle != NULL); +} + +bool CrashGenerationClient::IsRegistered() const { + return crash_event_ != NULL; +} + +bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) { + if (!IsRegistered()) { + return false; + } + + exception_pointers_ = ex_info; + thread_id_ = GetCurrentThreadId(); + + assert_info_.line = 0; + assert_info_.type = 0; + assert_info_.expression[0] = 0; + assert_info_.file[0] = 0; + assert_info_.function[0] = 0; + + return SignalCrashEventAndWait(); +} + +bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) { + if (!IsRegistered()) { + return false; + } + + exception_pointers_ = NULL; + + if (assert_info) { + memcpy(&assert_info_, assert_info, sizeof(assert_info_)); + } else { + memset(&assert_info_, 0, sizeof(assert_info_)); + } + + thread_id_ = GetCurrentThreadId(); + + return SignalCrashEventAndWait(); +} + +bool CrashGenerationClient::SignalCrashEventAndWait() { + assert(crash_event_); + assert(crash_generated_); + assert(server_alive_); + + // Reset the dump generated event before signaling the crash + // event so that the server can set the dump generated event + // once it is done generating the event. + if (!ResetEvent(crash_generated_)) { + return false; + } + + if (!SetEvent(crash_event_)) { + return false; + } + + HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_}; + + DWORD result = WaitForMultipleObjects(kWaitEventCount, + wait_handles, + FALSE, + kWaitForServerTimeoutMs); + + // Crash dump was successfully generated only if the server + // signaled the crash generated event. + return result == WAIT_OBJECT_0; +} + +} // namespace google_breakpad diff --git a/src/client/windows/crash_generation/crash_generation_client.h b/src/client/windows/crash_generation/crash_generation_client.h new file mode 100644 index 00000000..dbf701f4 --- /dev/null +++ b/src/client/windows/crash_generation/crash_generation_client.h @@ -0,0 +1,149 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H__ +#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H__ + +#include +#include "client/windows/common/ipc_protocol.h" + +namespace google_breakpad { + +// Abstraction of client-side implementation of out of process +// crash generation. +// +// The process that desires to have out-of-process crash dump +// generation service can use this class in the following way: +// +// * Create an instance. +// * Call Register method so that the client tries to register +// with the server process and check the return value. If +// registration is not successful, out-of-process crash dump +// generation will not be available +// * Request dump generation by calling either of the two +// overloaded RequestDump methods - one in case of exceptions +// and the other in case of assertion failures +// +// Note that it is the responsibility of the client code of +// this class to set the unhandled exception filter with the +// system by calling the SetUnhandledExceptionFilter function +// and the client code should explicitly request dump generation. +class CrashGenerationClient { + public: + CrashGenerationClient(const wchar_t* pipe_name, + MINIDUMP_TYPE dump_type); + + ~CrashGenerationClient(); + + // Registers the client process with the crash server. + // + // Returns true if the registration is successful; false otherwise. + bool Register(); + + // Requests the crash server to generate a dump with the given + // exception information. + // + // Returns true if the dump was successful; false otherwise. Note that + // if the registration step was not performed or it was not successful, + // false will be returned. + bool RequestDump(EXCEPTION_POINTERS* ex_info); + + // Requests the crash server to generate a dump with the given + // assertion information. + // + // Returns true if the dump was successful; false otherwise. Note that + // if the registration step was not performed or it was not successful, + // false will be returned. + bool RequestDump(MDRawAssertionInfo* assert_info); + + private: + // Connects to the appropriate pipe and sets the pipe handle state. + // + // Returns the pipe handle if everything goes well; otherwise Returns NULL. + HANDLE ConnectToServer(); + + // Performs a handshake with the server over the given pipe which should be + // already connected to the server. + // + // Returns true if handshake with the server was successful; false otherwise. + bool RegisterClient(HANDLE pipe); + + // Validates the given server response. + bool ValidateResponse(const ProtocolMessage& msg) const; + + // Returns true if the registration step succeeded; false otherwise. + bool IsRegistered() const; + + // Connects to the given named pipe with given parameters. + // + // Returns true if the connection is successful; false otherwise. + HANDLE ConnectToPipe(const wchar_t* pipe_name, + DWORD pipe_access, + DWORD flags_attrs); + + // Signals the crash event and wait for the server to generate crash. + bool SignalCrashEventAndWait(); + + // Pipe name to use to talk to server. + std::wstring pipe_name_; + + // Type of dump to generate. + MINIDUMP_TYPE dump_type_; + + // Event to signal in case of a crash. + HANDLE crash_event_; + + // Handle to wait on after signaling a crash for the server + // to finish generating crash dump. + HANDLE crash_generated_; + + // Handle to a mutex that will become signaled with WAIT_ABANDONED + // if the server process goes down. + HANDLE server_alive_; + + // Server process id. + DWORD server_process_id_; + + // Id of the thread that caused the crash. + DWORD thread_id_; + + // Exception pointers for an exception crash. + EXCEPTION_POINTERS* exception_pointers_; + + // Assertion info for an invalid parameter or pure call crash. + MDRawAssertionInfo assert_info_; + + // Disable copy ctor and operator=. + CrashGenerationClient(const CrashGenerationClient& crash_client); + CrashGenerationClient& operator=(const CrashGenerationClient& crash_client); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H__ diff --git a/src/client/windows/crash_generation/crash_generation_server.cc b/src/client/windows/crash_generation/crash_generation_server.cc new file mode 100644 index 00000000..dbdf0014 --- /dev/null +++ b/src/client/windows/crash_generation/crash_generation_server.cc @@ -0,0 +1,821 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "client/windows/crash_generation/crash_generation_server.h" +#include +#include +#include "client/windows/common/auto_critical_section.h" +#include "processor/scoped_ptr.h" + +namespace google_breakpad { + +// Output buffer size. +const size_t kOutBufferSize = 64; + +// Input buffer size. +const size_t kInBufferSize = 64; + +// Access flags for the client on the dump request event. +const DWORD kDumpRequestEventAccess = EVENT_MODIFY_STATE; + +// Access flags for the client on the dump generated event. +const DWORD kDumpGeneratedEventAccess = EVENT_MODIFY_STATE | + SYNCHRONIZE; + +// Access flags for the client on the mutex. +const DWORD kMutexAccess = SYNCHRONIZE; + +// Attribute flags for the pipe. +const DWORD kPipeAttr = FILE_FLAG_FIRST_PIPE_INSTANCE | + PIPE_ACCESS_DUPLEX | + FILE_FLAG_OVERLAPPED; + +// Mode for the pipe. +const DWORD kPipeMode = PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE | + PIPE_WAIT; + +// For pipe I/O, execute the callback in the wait thread itself, +// since the callback does very little work. The callback executes +// the code for one of the states of the server state machine and +// the code for all of the states perform async I/O and hence +// finish very quickly. +const ULONG kPipeIOThreadFlags = WT_EXECUTEINWAITTHREAD; + +// Dump request threads will, most likely, generate dumps. That may +// take some time to finish, so specify WT_EXECUTELONGFUNCTION flag. +const ULONG kDumpRequestThreadFlags = WT_EXECUTEINWAITTHREAD | + WT_EXECUTELONGFUNCTION; + +// Maximum delay during server shutdown if some work items +// are still executing. +const int kShutdownDelayMs = 10000; + +// Interval for each sleep during server shutdown. +const int kShutdownSleepIntervalMs = 5; + +static bool ValidateClientRequest(const ProtocolMessage& msg) { + return msg.tag == MESSAGE_TAG_REGISTRATION_REQUEST && + msg.pid != 0 && + msg.thread_id != NULL && + msg.exception_pointers != NULL && + msg.assert_info != NULL; +} + +CrashGenerationServer::CrashGenerationServer( + const wchar_t* pipe_name, + 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) + : pipe_name_(pipe_name), + pipe_(NULL), + pipe_wait_handle_(NULL), + server_alive_handle_(NULL), + connect_callback_(connect_callback), + connect_context_(connect_context), + dump_callback_(dump_callback), + dump_context_(dump_context), + exit_callback_(exit_callback), + exit_context_(exit_context), + generate_dumps_(generate_dumps), + dump_generator_(NULL), + server_state_(IPC_SERVER_STATE_INITIAL), + shutting_down_(false), + overlapped_(), + client_info_(NULL), + cleanup_item_count_(0) { + InitializeCriticalSection(&clients_sync_); + + if (dump_path) { + dump_generator_.reset(new MinidumpGenerator(*dump_path)); + } +} + +CrashGenerationServer::~CrashGenerationServer() { + // Indicate to existing threads that server is shutting down. + shutting_down_ = true; + + // Unregister wait on the pipe. + if (pipe_wait_handle_) { + // Wait for already executing callbacks to finish. + UnregisterWaitEx(pipe_wait_handle_, INVALID_HANDLE_VALUE); + } + + // Close the pipe to avoid further client connections. + if (pipe_) { + CloseHandle(pipe_); + } + + // Request all ClientInfo objects to unregister all waits. + // New scope to hold the lock for the shortest time. + { + AutoCriticalSection lock(&clients_sync_); + + std::list::iterator iter; + for (iter = clients_.begin(); iter != clients_.end(); ++iter) { + ClientInfo* client_info = *iter; + client_info->UnregisterWaits(); + } + } + + // Now that all waits have been unregistered, wait for some time + // for all pending work items to finish. + int total_wait = 0; + while (cleanup_item_count_ > 0) { + Sleep(kShutdownSleepIntervalMs); + + total_wait += kShutdownSleepIntervalMs; + + if (total_wait >= kShutdownDelayMs) { + break; + } + } + + // Clean up all the ClientInfo objects. + // New scope to hold the lock for the shortest time. + { + AutoCriticalSection lock(&clients_sync_); + + std::list::iterator iter; + for (iter = clients_.begin(); iter != clients_.end(); ++iter) { + ClientInfo* client_info = *iter; + delete client_info; + } + } + + if (server_alive_handle_) { + // Release the mutex before closing the handle so that clients requesting + // dumps wait for a long time for the server to generate a dump. + ReleaseMutex(server_alive_handle_); + CloseHandle(server_alive_handle_); + } + + DeleteCriticalSection(&clients_sync_); +} + +bool CrashGenerationServer::Start() { + server_state_ = IPC_SERVER_STATE_INITIAL; + + server_alive_handle_ = CreateMutex(NULL, TRUE, NULL); + if (!server_alive_handle_) { + return false; + } + + // Event to signal the client connection and pipe reads and writes. + overlapped_.hEvent = CreateEvent(NULL, // Security descriptor. + TRUE, // Manual reset. + FALSE, // Initially signaled. + NULL); // Name. + if (!overlapped_.hEvent) { + return false; + } + + // Register a callback with the thread pool for the client connection. + RegisterWaitForSingleObject(&pipe_wait_handle_, + overlapped_.hEvent, + OnPipeConnected, + this, + INFINITE, + kPipeIOThreadFlags); + + pipe_ = CreateNamedPipe(pipe_name_.c_str(), + kPipeAttr, + kPipeMode, + 1, + kOutBufferSize, + kInBufferSize, + 0, + NULL); + if (!pipe_) { + return false; + } + + // Signal the event to start a separate thread to handle + // client connections. + return SetEvent(overlapped_.hEvent) != FALSE; +} + +// If the server thread serving clients ever gets into the +// ERROR state, reset the event, close the pipe and remain +// in the error state forever. Error state means something +// that we didn't account for has happened, and it's dangerous +// to do anything unknowingly. +void CrashGenerationServer::HandleErrorState() { + assert(server_state_ == IPC_SERVER_STATE_ERROR); + + // If the server is shutting down anyway, don't clean up + // here since shut down process will clean up. + if (shutting_down_) { + return; + } + + if (pipe_wait_handle_) { + UnregisterWait(pipe_wait_handle_); + pipe_wait_handle_ = NULL; + } + + if (pipe_) { + CloseHandle(pipe_); + pipe_ = NULL; + } + + if (overlapped_.hEvent) { + CloseHandle(overlapped_.hEvent); + overlapped_.hEvent = NULL; + } +} + +// When the server thread serving clients is in the INITIAL state, +// try to connect to the pipe asynchronously. If the connection +// finishes synchronously, directly go into the CONNECTED state; +// otherwise go into the CONNECTING state. For any problems, go +// into the ERROR state. +void CrashGenerationServer::HandleInitialState() { + assert(server_state_ == IPC_SERVER_STATE_INITIAL); + + if (!ResetEvent(overlapped_.hEvent)) { + server_state_ = IPC_SERVER_STATE_ERROR; + return; + } + + bool success = ConnectNamedPipe(pipe_, &overlapped_) != FALSE; + + // From MSDN, it is not clear that when ConnectNamedPipe is used + // in an overlapped mode, will it ever return non-zero value, and + // if so, in what cases. + assert(!success); + + DWORD error_code = GetLastError(); + switch (error_code) { + case ERROR_IO_PENDING: + server_state_ = IPC_SERVER_STATE_CONNECTING; + break; + + case ERROR_PIPE_CONNECTED: + if (SetEvent(overlapped_.hEvent)) { + server_state_ = IPC_SERVER_STATE_CONNECTED; + } else { + server_state_ = IPC_SERVER_STATE_ERROR; + } + break; + + default: + server_state_ = IPC_SERVER_STATE_ERROR; + break; + } +} + +// When the server thread serving the clients is in the CONNECTING state, +// try to get the result of the asynchronous connection request using +// the OVERLAPPED object. If the result indicates the connection is done, +// go into the CONNECTED state. If the result indicates I/O is still +// INCOMPLETE, remain in the CONNECTING state. For any problems, +// go into the DISCONNECTING state. +void CrashGenerationServer::HandleConnectingState() { + assert(server_state_ == IPC_SERVER_STATE_CONNECTING); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + + if (success) { + server_state_ = IPC_SERVER_STATE_CONNECTED; + return; + } + + if (GetLastError() != ERROR_IO_INCOMPLETE) { + server_state_ = IPC_SERVER_STATE_DISCONNECTING; + } +} + +// When the server thread serving the clients is in the CONNECTED state, +// try to issue an asynchronous read from the pipe. If read completes +// synchronously or if I/O is pending then go into the READING state. +// For any problems, go into the DISCONNECTING state. +void CrashGenerationServer::HandleConnectedState() { + assert(server_state_ == IPC_SERVER_STATE_CONNECTED); + + DWORD bytes_count = 0; + memset(&msg_, 0, sizeof(msg_)); + bool success = ReadFile(pipe_, + &msg_, + sizeof(msg_), + &bytes_count, + &overlapped_) != FALSE; + + // Note that the asynchronous read issued above can finish before the + // code below executes. But, it is okay to change state after issuing + // the asynchronous read. This is because even if the asynchronous read + // is done, the callback for it would not be executed until the current + // thread finishes its execution. + if (success || GetLastError() == ERROR_IO_PENDING) { + server_state_ = IPC_SERVER_STATE_READING; + } else { + server_state_ = IPC_SERVER_STATE_DISCONNECTING; + } +} + +// When the server thread serving the clients is in the READING state, +// try to get the result of the async read. If async read is done, +// go into the READ_DONE state. For any problems, go into the +// DISCONNECTING state. +void CrashGenerationServer::HandleReadingState() { + assert(server_state_ == IPC_SERVER_STATE_READING); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + + if (success && bytes_count == sizeof(ProtocolMessage)){ + server_state_ = IPC_SERVER_STATE_READ_DONE; + return; + } + + DWORD error_code = GetLastError(); + + // We should never get an I/O incomplete since we should not execute this + // unless the Read has finished and the overlapped event is signaled. If + // we do get INCOMPLETE, we have a bug in our code. + assert(error_code != ERROR_IO_INCOMPLETE); + + server_state_ = IPC_SERVER_STATE_DISCONNECTING; +} + +// When the server thread serving the client is in the READ_DONE state, +// validate the client's request message, register the client by +// creating appropriate objects and prepare the response. Then try to +// write the response to the pipe asynchronously. If that succeeds, +// go into the WRITING state. For any problems, go into the DISCONNECTING +// state. +void CrashGenerationServer::HandleReadDoneState() { + assert(server_state_ == IPC_SERVER_STATE_READ_DONE); + + if (!ValidateClientRequest(msg_)) { + server_state_ = IPC_SERVER_STATE_DISCONNECTING; + return; + } + + scoped_ptr client_info( + new ClientInfo(this, + msg_.pid, + msg_.dump_type, + msg_.thread_id, + msg_.exception_pointers, + msg_.assert_info)); + + if (!client_info->Initialize()) { + server_state_ = IPC_SERVER_STATE_DISCONNECTING; + return; + } + + if (!RespondToClient(client_info.get())) { + server_state_ = IPC_SERVER_STATE_DISCONNECTING; + return; + } + + // Note that the asynchronous write issued by RespondToClient function + // can finish before the code below executes. But it is okay to change + // state after issuing the asynchronous write. This is because even if + // the asynchronous write is done, the callback for it would not be + // executed until the current thread finishes its execution. + server_state_ = IPC_SERVER_STATE_WRITING; + client_info_ = client_info.release(); +} + +// When the server thread serving the clients is in the WRITING state, +// try to get the result of the async write. If the async write is done, +// go into the WRITE_DONE state. For any problems, go into the +// DISONNECTING state. +void CrashGenerationServer::HandleWritingState() { + assert(server_state_ == IPC_SERVER_STATE_WRITING); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + + if (success) { + server_state_ = IPC_SERVER_STATE_WRITE_DONE; + return; + } + + DWORD error_code = GetLastError(); + + // We should never get an I/O incomplete since we should not execute this + // unless the Write has finished and the overlapped event is signaled. If + // we do get INCOMPLETE, we have a bug in our code. + assert(error_code != ERROR_IO_INCOMPLETE); + + server_state_ = IPC_SERVER_STATE_DISCONNECTING; +} + +// When the server thread serving the clients is in the WRITE_DONE state, +// try to issue an async read on the pipe. If the read completes synchronously +// or if I/O is still pending then go into the READING_ACK state. For any +// issues, go into the DISCONNECTING state. +void CrashGenerationServer::HandleWriteDoneState() { + assert(server_state_ == IPC_SERVER_STATE_WRITE_DONE); + + server_state_ = IPC_SERVER_STATE_READING_ACK; + + DWORD bytes_count = 0; + bool success = ReadFile(pipe_, + &msg_, + sizeof(msg_), + &bytes_count, + &overlapped_) != FALSE; + + if (success) { + return; + } + + DWORD error_code = GetLastError(); + + if (error_code != ERROR_IO_PENDING) { + server_state_ = IPC_SERVER_STATE_DISCONNECTING; + } +} + +// When the server thread serving the clients is in the READING_ACK state, +// try to get result of async read. Go into the DISCONNECTING state. +void CrashGenerationServer::HandleReadingAckState() { + assert(server_state_ == IPC_SERVER_STATE_READING_ACK); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + + if (success) { + // The connection handshake with the client is now complete; perform + // the callback. + if (connect_callback_) { + connect_callback_(connect_context_, client_info_); + } + } else { + DWORD error_code = GetLastError(); + + // We should never get an I/O incomplete since we should not execute this + // unless the Read has finished and the overlapped event is signaled. If + // we do get INCOMPLETE, we have a bug in our code. + assert(error_code != ERROR_IO_INCOMPLETE); + } + + server_state_ = IPC_SERVER_STATE_DISCONNECTING; +} + +// When the server thread serving the client is in the DISCONNECTING state, +// disconnect from the pipe and reset the event. If anything fails, go into +// the ERROR state. If it goes well, go into the INITIAL state and set the +// event to start all over again. +void CrashGenerationServer::HandleDisconnectingState() { + assert(server_state_ == IPC_SERVER_STATE_DISCONNECTING); + + // Done serving the client. + client_info_ = NULL; + + overlapped_.Internal = NULL; + overlapped_.InternalHigh = NULL; + overlapped_.Offset = 0; + overlapped_.OffsetHigh = 0; + overlapped_.Pointer = NULL; + + if (!ResetEvent(overlapped_.hEvent)) { + server_state_ = IPC_SERVER_STATE_ERROR; + return; + } + + if (!DisconnectNamedPipe(pipe_)) { + server_state_ = IPC_SERVER_STATE_ERROR; + return; + } + + // If the server is shutting down do not connect to the + // next client. + if (shutting_down_) { + return; + } + + server_state_ = IPC_SERVER_STATE_INITIAL; + if (!SetEvent(overlapped_.hEvent)) { + server_state_ = IPC_SERVER_STATE_ERROR; + } +} + +bool CrashGenerationServer::PrepareReply(const ClientInfo& client_info, + ProtocolMessage* reply) const { + reply->tag = MESSAGE_TAG_REGISTRATION_RESPONSE; + reply->pid = GetCurrentProcessId(); + + if (CreateClientHandles(client_info, reply)) { + return true; + } + + if (reply->dump_request_handle) { + CloseHandle(reply->dump_request_handle); + } + + if (reply->dump_generated_handle) { + CloseHandle(reply->dump_generated_handle); + } + + if (reply->server_alive_handle) { + CloseHandle(reply->server_alive_handle); + } + + return false; +} + +bool CrashGenerationServer::CreateClientHandles(const ClientInfo& client_info, + ProtocolMessage* reply) const { + HANDLE current_process = GetCurrentProcess(); + if (!DuplicateHandle(current_process, + client_info.dump_requested_handle(), + client_info.process_handle(), + &reply->dump_request_handle, + kDumpRequestEventAccess, + FALSE, + 0)) { + return false; + } + + if (!DuplicateHandle(current_process, + client_info.dump_generated_handle(), + client_info.process_handle(), + &reply->dump_generated_handle, + kDumpGeneratedEventAccess, + FALSE, + 0)) { + return false; + } + + if (!DuplicateHandle(current_process, + server_alive_handle_, + client_info.process_handle(), + &reply->server_alive_handle, + kMutexAccess, + FALSE, + 0)) { + return false; + } + + return true; +} + +bool CrashGenerationServer::RespondToClient(ClientInfo* client_info) { + ProtocolMessage reply; + if (!PrepareReply(*client_info, &reply)) { + return false; + } + + if (!AddClient(client_info)) { + return false; + } + + DWORD bytes_count = 0; + bool success = WriteFile(pipe_, + &reply, + sizeof(reply), + &bytes_count, + &overlapped_) != FALSE; + + return success || GetLastError() == ERROR_IO_PENDING; +} + +// The server thread servicing the clients runs this method. The method +// implements the state machine described in ReadMe.txt along with the +// helper methods HandleXXXState. +void CrashGenerationServer::HandleConnectionRequest() { + switch (server_state_) { + case IPC_SERVER_STATE_ERROR: + HandleErrorState(); + break; + + case IPC_SERVER_STATE_INITIAL: + HandleInitialState(); + break; + + case IPC_SERVER_STATE_CONNECTING: + HandleConnectingState(); + break; + + case IPC_SERVER_STATE_CONNECTED: + HandleConnectedState(); + break; + + case IPC_SERVER_STATE_READING: + HandleReadingState(); + break; + + case IPC_SERVER_STATE_READ_DONE: + HandleReadDoneState(); + break; + + case IPC_SERVER_STATE_WRITING: + HandleWritingState(); + break; + + case IPC_SERVER_STATE_WRITE_DONE: + HandleWriteDoneState(); + break; + + case IPC_SERVER_STATE_READING_ACK: + HandleReadingAckState(); + break; + + case IPC_SERVER_STATE_DISCONNECTING: + HandleDisconnectingState(); + break; + + default: + assert(false); + // This indicates that we added one more state without + // adding handling code. + server_state_ = IPC_SERVER_STATE_ERROR; + break; + } +} + +bool CrashGenerationServer::AddClient(ClientInfo* client_info) { + HANDLE request_wait_handle = NULL; + if (!RegisterWaitForSingleObject(&request_wait_handle, + client_info->dump_requested_handle(), + OnDumpRequest, + client_info, + INFINITE, + kDumpRequestThreadFlags)) { + return false; + } + + client_info->set_dump_request_wait_handle(request_wait_handle); + + // OnClientEnd will be called when the client process terminates. + HANDLE process_wait_handle = NULL; + if (!RegisterWaitForSingleObject(&process_wait_handle, + client_info->process_handle(), + OnClientEnd, + client_info, + INFINITE, + WT_EXECUTEONLYONCE)) { + return false; + } + + client_info->set_process_exit_wait_handle(process_wait_handle); + + // New scope to hold the lock for the shortest time. + { + AutoCriticalSection lock(&clients_sync_); + clients_.push_back(client_info); + } + + return true; +} + +// static +void CALLBACK CrashGenerationServer::OnPipeConnected(void* context, BOOLEAN) { + assert (context); + + CrashGenerationServer* obj = + reinterpret_cast(context); + obj->HandleConnectionRequest(); +} + +// static +void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) { + assert(context); + ClientInfo* client_info = reinterpret_cast(context); + + CrashGenerationServer* crash_server = client_info->crash_server(); + assert(crash_server); + crash_server->HandleDumpRequest(*client_info); + + ResetEvent(client_info->dump_requested_handle()); +} + +// static +void CALLBACK CrashGenerationServer::OnClientEnd(void* context, BOOLEAN) { + assert(context); + ClientInfo* client_info = reinterpret_cast(context); + + CrashGenerationServer* crash_server = client_info->crash_server(); + assert(crash_server); + + InterlockedIncrement(&crash_server->cleanup_item_count_); + + if (!QueueUserWorkItem(CleanupClient, context, WT_EXECUTEDEFAULT)) + { + InterlockedDecrement(&crash_server->cleanup_item_count_); + } +} + +// static +DWORD WINAPI CrashGenerationServer::CleanupClient(void* context) { + assert(context); + ClientInfo* client_info = reinterpret_cast(context); + + CrashGenerationServer* crash_server = client_info->crash_server(); + assert(crash_server); + + if (crash_server->exit_callback_) { + crash_server->exit_callback_(crash_server->exit_context_, client_info); + } + + crash_server->DoCleanup(client_info); + + InterlockedDecrement(&crash_server->cleanup_item_count_); + return 0; +} + +void CrashGenerationServer::DoCleanup(ClientInfo* client_info) { + assert(client_info); + + // Start a new scope to release lock automatically. + { + AutoCriticalSection lock(&clients_sync_); + clients_.remove(client_info); + } + + delete client_info; +} + +void CrashGenerationServer::HandleDumpRequest(const ClientInfo& client_info) { + // Generate the dump only if it's explicitly requested by the + // server application; otherwise the server might want to generate + // dump in the callback. + if (generate_dumps_) { + if (!GenerateDump(client_info)) { + return; + } + } + + if (dump_callback_) { + dump_callback_(dump_context_, &client_info); + } + + SetEvent(client_info.dump_generated_handle()); +} + +bool CrashGenerationServer::GenerateDump(const ClientInfo& client) { + assert(client.pid() != 0); + assert(client.process_handle()); + + // We have to get the address of EXCEPTION_INFORMATION from + // the client process address space. + EXCEPTION_POINTERS* client_ex_info = NULL; + if (!client.GetClientExceptionInfo(&client_ex_info)) { + return false; + } + + DWORD client_thread_id = 0; + if (!client.GetClientThreadId(&client_thread_id)) { + return false; + } + + return dump_generator_->WriteMinidump(client.process_handle(), + client.pid(), + client_thread_id, + GetCurrentThreadId(), + client_ex_info, + client.assert_info(), + client.dump_type(), + true); +} + +} // namespace google_breakpad diff --git a/src/client/windows/crash_generation/crash_generation_server.h b/src/client/windows/crash_generation/crash_generation_server.h new file mode 100644 index 00000000..864bacb1 --- /dev/null +++ b/src/client/windows/crash_generation/crash_generation_server.h @@ -0,0 +1,255 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__ +#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__ + +#include +#include +#include "client/windows/common/ipc_protocol.h" +#include "client/windows/crash_generation/client_info.h" +#include "client/windows/crash_generation/minidump_generator.h" +#include "processor/scoped_ptr.h" + +namespace google_breakpad { + +// Abstraction for server side implementation of out-of-process crash +// generation protocol. It generates minidumps (Windows platform) for +// client processes that request dump generation. +class CrashGenerationServer { + public: + typedef void (*OnClientConnectedCallback)(void* context, + const ClientInfo* client_info); + + typedef void (*OnClientDumpRequestCallback)(void* context, + const ClientInfo* client_info); + + typedef void (*OnClientExitedCallback)(void* context, + const ClientInfo* client_info); + + // Creates an instance with the given parameters. + // + // Parameter pipe_name: Name of the pipe + // Parameter connect_callback: Callback for a new client connection. + // Parameter connect_context: Context for client connection callback. + // Parameter crash_callback: Callback for a client crash dump request. + // Parameter crash_context: Context for client crash dump request callback. + // Parameter exit_callback: Callback for client process exit. + // Parameter exit_context: Context for client exit callback. + // Parameter generate_dumps: Whether to automatically generate dumps or not. + // Client code of this class might want to generate dumps explicitly in the + // crash dump request callback. In that case, false can be passed for this + // parameter. + // Parameter dump_path: Path for generating dumps; required only if true is + // passed for generateDumps parameter; NULL can be passed otherwise. + CrashGenerationServer(const wchar_t* pipe_name, + 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); + + ~CrashGenerationServer(); + + // Performs initialization steps needed to start listening to clients. + // + // Returns true if initialization is successful; false otherwise. + bool Start(); + + private: + // Various states the client can be in during the handshake with + // the server. + enum IPCServerState { + // Server is in error state and it cannot serve any clients. + IPC_SERVER_STATE_ERROR, + + // Server starts in this state. + IPC_SERVER_STATE_INITIAL, + + // Server has issued an async connect to the pipe and it is waiting + // for the connection to be established. + IPC_SERVER_STATE_CONNECTING, + + // Server is connected successfully. + IPC_SERVER_STATE_CONNECTED, + + // Server has issued an async read from the pipe and it is waiting for + // the read to finish. + IPC_SERVER_STATE_READING, + + // Server is done reading from the pipe. + IPC_SERVER_STATE_READ_DONE, + + // Server has issued an async write to the pipe and it is waiting for + // the write to finish. + IPC_SERVER_STATE_WRITING, + + // Server is done writing to the pipe. + IPC_SERVER_STATE_WRITE_DONE, + + // Server has issued an async read from the pipe for an ack and it + // is waiting for the read to finish. + IPC_SERVER_STATE_READING_ACK, + + // Server is done writing to the pipe and it is now ready to disconnect + // and reconnect. + IPC_SERVER_STATE_DISCONNECTING + }; + + // + // Helper methods to handle various server IPC states. + // + void HandleErrorState(); + void HandleInitialState(); + void HandleConnectingState(); + void HandleConnectedState(); + void HandleReadingState(); + void HandleReadDoneState(); + void HandleWritingState(); + void HandleWriteDoneState(); + void HandleReadingAckState(); + void HandleDisconnectingState(); + + // Prepares reply for a client from the given parameters. + bool PrepareReply(const ClientInfo& client_info, + ProtocolMessage* reply) const; + + // Duplicates various handles in the ClientInfo object for the client + // process and stores them in the given ProtocolMessage instance. If + // creating any handle fails, ProtocolMessage will contain the handles + // already created successfully, which should be closed by the caller. + bool CreateClientHandles(const ClientInfo& client_info, + ProtocolMessage* reply) const; + + // Response to the given client. Return true if all steps of + // responding to the client succeed, false otherwise. + bool RespondToClient(ClientInfo* client_info); + + // Handles a connection request from the client. + void HandleConnectionRequest(); + + // Handles a dump request from the client. + void HandleDumpRequest(const ClientInfo& client_info); + + // Callback for pipe connected event. + static void CALLBACK OnPipeConnected(void* context, BOOLEAN timer_or_wait); + + // Callback for a dump request. + static void CALLBACK OnDumpRequest(void* context, BOOLEAN timer_or_wait); + + // Callback for client process exit event. + static void CALLBACK OnClientEnd(void* context, BOOLEAN timer_or_wait); + + // Releases resources for a client. + static DWORD WINAPI CleanupClient(void* context); + + // Cleans up for the given client. + void DoCleanup(ClientInfo* client_info); + + // Adds the given client to the list of registered clients. + bool AddClient(ClientInfo* client_info); + + // Generates dump for the given client. + bool GenerateDump(const ClientInfo& client); + + // Sync object for thread-safe access to the shared list of clients. + CRITICAL_SECTION clients_sync_; + + // List of clients. + std::list clients_; + + // Pipe name. + std::wstring pipe_name_; + + // Handle to the pipe used for handshake with clients. + HANDLE pipe_; + + // Pipe wait handle. + HANDLE pipe_wait_handle_; + + // Handle to server-alive mutex. + HANDLE server_alive_handle_; + + // Callback for a successful client connection. + OnClientConnectedCallback connect_callback_; + + // Context for client connected callback. + void* connect_context_; + + // Callback for a client dump request. + OnClientDumpRequestCallback dump_callback_; + + // Context for client dump request callback. + void* dump_context_; + + // Callback for client process exit. + OnClientExitedCallback exit_callback_; + + // Context for client process exit callback. + void* exit_context_; + + // Whether to generate dumps or not. + bool generate_dumps_; + + // Instance of a mini dump generator. + scoped_ptr dump_generator_; + + // State of the server in performing the IPC with the client. + // Note that since we restrict the pipe to one instance, we + // only need to keep one state of the server. Otherwise, server + // would have one state per client it is talking to. + IPCServerState server_state_; + + // Whether the server is shutting down. + volatile bool shutting_down_; + + // Overlapped instance for async I/O on the pipe. + OVERLAPPED overlapped_; + + // Message object used in IPC with the client. + ProtocolMessage msg_; + + // Client Info for the client that's connecting to the server. + ClientInfo* client_info_; + + // Count of clean-up work items that are currently running or are + // already queued to run. + volatile LONG cleanup_item_count_; + + // Disable copy ctor and operator=. + CrashGenerationServer(const CrashGenerationServer& crash_server); + CrashGenerationServer& operator=(const CrashGenerationServer& crash_server); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__ diff --git a/src/client/windows/crash_generation/minidump_generator.cc b/src/client/windows/crash_generation/minidump_generator.cc new file mode 100644 index 00000000..6081e4b2 --- /dev/null +++ b/src/client/windows/crash_generation/minidump_generator.cc @@ -0,0 +1,228 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "client/windows/crash_generation/minidump_generator.h" +#include +#include "client/windows/common/auto_critical_section.h" +#include "common/windows/guid_string.h" + +using std::wstring; + +namespace google_breakpad { + +MinidumpGenerator::MinidumpGenerator(const wstring& dump_path) + : dbghelp_module_(NULL), + rpcrt4_module_(NULL), + dump_path_(dump_path), + write_dump_(NULL), + create_uuid_(NULL) { + InitializeCriticalSection(&module_load_sync_); + InitializeCriticalSection(&get_proc_address_sync_); +} + +MinidumpGenerator::~MinidumpGenerator() { + if (dbghelp_module_) { + FreeLibrary(dbghelp_module_); + } + + if (rpcrt4_module_) { + FreeLibrary(rpcrt4_module_); + } + + DeleteCriticalSection(&get_proc_address_sync_); + DeleteCriticalSection(&module_load_sync_); +} + +bool MinidumpGenerator::WriteMinidump(HANDLE process_handle, + DWORD process_id, + DWORD thread_id, + DWORD requesting_thread_id, + EXCEPTION_POINTERS* exception_pointers, + MDRawAssertionInfo* assert_info, + MINIDUMP_TYPE dump_type, + bool is_client_pointers) { + MiniDumpWriteDumpType write_dump = GetWriteDump(); + if (!write_dump) { + return false; + } + + wstring dump_file_path; + if (!GenerateDumpFilePath(&dump_file_path)) { + return false; + } + + HANDLE dump_file = CreateFile(dump_file_path.c_str(), + GENERIC_WRITE, + 0, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (dump_file == INVALID_HANDLE_VALUE) { + return false; + } + + MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL; + MINIDUMP_EXCEPTION_INFORMATION dump_exception_info; + + // Setup the exception information object only if it's a dump + // due to an exception. + if (exception_pointers) { + dump_exception_pointers = &dump_exception_info; + dump_exception_info.ThreadId = thread_id; + dump_exception_info.ExceptionPointers = exception_pointers; + dump_exception_info.ClientPointers = is_client_pointers; + } + + // Add an MDRawBreakpadInfo stream to the minidump, to provide additional + // information about the exception handler to the Breakpad processor. + // The information will help the processor determine which threads are + // relevant. The Breakpad processor does not require this information but + // can function better with Breakpad-generated dumps when it is present. + // The native debugger is not harmed by the presence of this information. + MDRawBreakpadInfo breakpad_info; + breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; + breakpad_info.dump_thread_id = thread_id; + breakpad_info.requesting_thread_id = requesting_thread_id; + + // Leave room in user_stream_array for a possible assertion info stream. + MINIDUMP_USER_STREAM user_stream_array[2]; + user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM; + user_stream_array[0].BufferSize = sizeof(breakpad_info); + user_stream_array[0].Buffer = &breakpad_info; + + MINIDUMP_USER_STREAM_INFORMATION user_streams; + user_streams.UserStreamCount = 1; + user_streams.UserStreamArray = user_stream_array; + + MDRawAssertionInfo* actual_assert_info = assert_info; + MDRawAssertionInfo client_assert_info = {0}; + + if (assert_info) { + // If the assertion info object lives in the client process, + // read the memory of the client process. + if (is_client_pointers) { + SIZE_T bytes_read = 0; + if (!ReadProcessMemory(process_handle, + assert_info, + &client_assert_info, + sizeof(client_assert_info), + &bytes_read)) { + CloseHandle(dump_file); + return false; + } + + if (bytes_read != sizeof(client_assert_info)) { + CloseHandle(dump_file); + return false; + } + + actual_assert_info = &client_assert_info; + } + + user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM; + user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo); + user_stream_array[1].Buffer = actual_assert_info; + ++user_streams.UserStreamCount; + } + + bool result = write_dump(process_handle, + process_id, + dump_file, + dump_type, + exception_pointers ? &dump_exception_info : NULL, + &user_streams, + NULL) != FALSE; + + CloseHandle(dump_file); + return result; +} + +HMODULE MinidumpGenerator::GetDbghelpModule() { + AutoCriticalSection lock(&module_load_sync_); + if (!dbghelp_module_) { + dbghelp_module_ = LoadLibrary(TEXT("dbghelp.dll")); + } + + return dbghelp_module_; +} + +MinidumpGenerator::MiniDumpWriteDumpType MinidumpGenerator::GetWriteDump() { + AutoCriticalSection lock(&get_proc_address_sync_); + if (!write_dump_) { + HMODULE module = GetDbghelpModule(); + if (module) { + FARPROC proc = GetProcAddress(module, "MiniDumpWriteDump"); + write_dump_ = reinterpret_cast(proc); + } + } + + return write_dump_; +} + +HMODULE MinidumpGenerator::GetRpcrt4Module() { + AutoCriticalSection lock(&module_load_sync_); + if (!rpcrt4_module_) { + rpcrt4_module_ = LoadLibrary(TEXT("rpcrt4.dll")); + } + + return rpcrt4_module_; +} + +MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() { + AutoCriticalSection lock(&module_load_sync_); + if (!create_uuid_) { + HMODULE module = GetRpcrt4Module(); + if (module) { + FARPROC proc = GetProcAddress(module, "UuidCreate"); + create_uuid_ = reinterpret_cast(proc); + } + } + + return create_uuid_; +} + +bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) { + UUID id = {0}; + + UuidCreateType create_uuid = GetCreateUuid(); + if(!create_uuid) { + return false; + } + + create_uuid(&id); + wstring id_str = GUIDString::GUIDToWString(&id); + + *file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp"); + return true; +} + +} // namespace google_breakpad diff --git a/src/client/windows/crash_generation/minidump_generator.h b/src/client/windows/crash_generation/minidump_generator.h new file mode 100644 index 00000000..71af6e9c --- /dev/null +++ b/src/client/windows/crash_generation/minidump_generator.h @@ -0,0 +1,118 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATION_H__ +#define CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATION_H__ + +#include +#include +#include +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// Abstraction for various objects and operations needed to generate +// minidump on Windows. This abstraction is useful to hide all the gory +// details for minidump generation and provide a clean interface to +// the clients to generate minidumps. +class MinidumpGenerator { + public: + // Creates an instance with the given dump path. + explicit MinidumpGenerator(const std::wstring& dump_path); + + ~MinidumpGenerator(); + + // Writes the minidump with the given parameters. + bool WriteMinidump(HANDLE process_handle, + DWORD process_id, + DWORD thread_id, + DWORD requesting_thread_id, + EXCEPTION_POINTERS* exception_pointers, + MDRawAssertionInfo* assert_info, + MINIDUMP_TYPE dump_type, + bool is_client_pointers); + + private: + // Function pointer type for MiniDumpWriteDump, which is looked up + // dynamically. + typedef BOOL (WINAPI* MiniDumpWriteDumpType)( + HANDLE hProcess, + DWORD ProcessId, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); + + // Function pointer type for UuidCreate, which is looked up dynamically. + typedef RPC_STATUS (RPC_ENTRY* UuidCreateType)(UUID* Uuid); + + // Loads the appropriate DLL lazily in a thread safe way. + HMODULE GetDbghelpModule(); + + // Loads the appropriate DLL and gets a pointer to the MiniDumpWriteDump + // function lazily and in a thread-safe manner. + MiniDumpWriteDumpType GetWriteDump(); + + // Loads the appropriate DLL lazily in a thread safe way. + HMODULE GetRpcrt4Module(); + + // Loads the appropriate DLL and gets a pointer to the UuidCreate + // function lazily and in a thread-safe manner. + UuidCreateType GetCreateUuid(); + + // Returns the path for the file to write dump to. + bool GenerateDumpFilePath(std::wstring* file_path); + + // Handle to dynamically loaded DbgHelp.dll. + HMODULE dbghelp_module_; + + // Pointer to the MiniDumpWriteDump function. + MiniDumpWriteDumpType write_dump_; + + // Handle to dynamically loaded rpcrt4.dll. + HMODULE rpcrt4_module_; + + // Pointer to the UuidCreate function. + UuidCreateType create_uuid_; + + // Folder path to store dump files. + std::wstring dump_path_; + + // Critical section to sychronize action of loading modules dynamically. + CRITICAL_SECTION module_load_sync_; + + // Critical section to synchronize action of dynamically getting function + // addresses from modules. + CRITICAL_SECTION get_proc_address_sync_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATION_H__ diff --git a/src/client/windows/handler/exception_handler.cc b/src/client/windows/handler/exception_handler.cc index cd1d7f26..fc678a91 100644 --- a/src/client/windows/handler/exception_handler.cc +++ b/src/client/windows/handler/exception_handler.cc @@ -41,80 +41,127 @@ namespace google_breakpad { static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024; -vector *ExceptionHandler::handler_stack_ = NULL; +vector* ExceptionHandler::handler_stack_ = NULL; LONG ExceptionHandler::handler_stack_index_ = 0; CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_; bool ExceptionHandler::handler_stack_critical_section_initialized_ = false; +ExceptionHandler::ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name) { + Initialize(dump_path, + filter, + callback, + callback_context, + handler_types, + dump_type, + pipe_name); +} + ExceptionHandler::ExceptionHandler(const wstring &dump_path, FilterCallback filter, MinidumpCallback callback, - void *callback_context, - int handler_types) - : filter_(filter), - callback_(callback), - callback_context_(callback_context), - dump_path_(), - next_minidump_id_(), - next_minidump_path_(), - dump_path_c_(), - next_minidump_id_c_(NULL), - next_minidump_path_c_(NULL), - dbghelp_module_(NULL), - minidump_write_dump_(NULL), - rpcrt4_module_(NULL), - uuid_create_(NULL), - handler_types_(handler_types), - previous_filter_(NULL), - previous_pch_(NULL), - handler_thread_(0), - handler_critical_section_(), - handler_start_semaphore_(NULL), - handler_finish_semaphore_(NULL), - requesting_thread_id_(0), - exception_info_(NULL), - assertion_(NULL), - handler_return_value_(false), - handle_debug_exceptions_(false) { + void* callback_context, + int handler_types) { + Initialize(dump_path, + filter, + callback, + callback_context, + handler_types, + MiniDumpNormal, + NULL); +} + +void ExceptionHandler::Initialize(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name) { + filter_ = filter; + callback_ = callback; + callback_context_ = callback_context; + dump_path_c_ = NULL; + next_minidump_id_c_ = NULL; + next_minidump_path_c_ = NULL; + dbghelp_module_ = NULL; + minidump_write_dump_ = NULL; + dump_type_ = dump_type; + rpcrt4_module_ = NULL; + uuid_create_ = NULL; + handler_types_ = handler_types; + previous_filter_ = NULL; #if _MSC_VER >= 1400 // MSVC 2005/8 previous_iph_ = NULL; #endif // _MSC_VER >= 1400 + previous_pch_ = NULL; + handler_thread_ = NULL; + handler_start_semaphore_ = NULL; + handler_finish_semaphore_ = NULL; + requesting_thread_id_ = 0; + exception_info_ = NULL; + assertion_ = NULL; + handler_return_value_ = false; + handle_debug_exceptions_ = false; - // Set synchronization primitives and the handler thread. Each - // ExceptionHandler object gets its own handler thread because that's the - // only way to reliably guarantee sufficient stack space in an exception, - // and it allows an easy way to get a snapshot of the requesting thread's - // context outside of an exception. - InitializeCriticalSection(&handler_critical_section_); - handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); - handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); + // Attempt to use out-of-process if user has specified pipe name. + if (pipe_name != NULL) { + scoped_ptr client( + new CrashGenerationClient(pipe_name, dump_type_)); - DWORD thread_id; - handler_thread_ = CreateThread(NULL, // lpThreadAttributes - kExceptionHandlerThreadInitialStackSize, - ExceptionHandlerThreadMain, - this, // lpParameter - 0, // dwCreationFlags - &thread_id); - - dbghelp_module_ = LoadLibrary(L"dbghelp.dll"); - if (dbghelp_module_) { - minidump_write_dump_ = reinterpret_cast( - GetProcAddress(dbghelp_module_, "MiniDumpWriteDump")); + // If successful in registering with the monitoring process, + // there is no need to setup in-process crash generation. + if (client->Register()) { + crash_generation_client_.reset(client.release()); + } } - // Load this library dynamically to not affect existing projects. Most - // projects don't link against this directly, it's usually dynamically - // loaded by dependent code. - rpcrt4_module_ = LoadLibrary(L"rpcrt4.dll"); - if (rpcrt4_module_) { - uuid_create_ = reinterpret_cast( - GetProcAddress(rpcrt4_module_, "UuidCreate")); - } + if (!IsOutOfProcess()) { + // Either client did not ask for out-of-process crash generation + // or registration with the server process failed. In either case, + // setup to do in-process crash generation. - // set_dump_path calls UpdateNextID. This sets up all of the path and id - // strings, and their equivalent c_str pointers. - set_dump_path(dump_path); + // Set synchronization primitives and the handler thread. Each + // ExceptionHandler object gets its own handler thread because that's the + // only way to reliably guarantee sufficient stack space in an exception, + // and it allows an easy way to get a snapshot of the requesting thread's + // context outside of an exception. + InitializeCriticalSection(&handler_critical_section_); + handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); + handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); + + DWORD thread_id; + handler_thread_ = CreateThread(NULL, // lpThreadAttributes + kExceptionHandlerThreadInitialStackSize, + ExceptionHandlerThreadMain, + this, // lpParameter + 0, // dwCreationFlags + &thread_id); + + dbghelp_module_ = LoadLibrary(L"dbghelp.dll"); + if (dbghelp_module_) { + minidump_write_dump_ = reinterpret_cast( + GetProcAddress(dbghelp_module_, "MiniDumpWriteDump")); + } + + // Load this library dynamically to not affect existing projects. Most + // projects don't link against this directly, it's usually dynamically + // loaded by dependent code. + rpcrt4_module_ = LoadLibrary(L"rpcrt4.dll"); + if (rpcrt4_module_) { + uuid_create_ = reinterpret_cast( + GetProcAddress(rpcrt4_module_, "UuidCreate")); + } + + // set_dump_path calls UpdateNextID. This sets up all of the path and id + // strings, and their equivalent c_str pointers. + set_dump_path(dump_path); + } if (handler_types != HANDLER_NONE) { if (!handler_stack_critical_section_initialized_) { @@ -127,7 +174,7 @@ ExceptionHandler::ExceptionHandler(const wstring &dump_path, // The first time an ExceptionHandler that installs a handler is // created, set up the handler stack. if (!handler_stack_) { - handler_stack_ = new vector(); + handler_stack_ = new vector(); } handler_stack_->push_back(this); @@ -175,7 +222,7 @@ ExceptionHandler::~ExceptionHandler() { // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the // system's application event log. fprintf(stderr, "warning: removing Breakpad handler out of order\n"); - for (vector::iterator iterator = + for (vector::iterator iterator = handler_stack_->begin(); iterator != handler_stack_->end(); ++iterator) { @@ -195,16 +242,20 @@ ExceptionHandler::~ExceptionHandler() { LeaveCriticalSection(&handler_stack_critical_section_); } - // Clean up the handler thread and synchronization primitives. - TerminateThread(handler_thread_, 1); - DeleteCriticalSection(&handler_critical_section_); - CloseHandle(handler_start_semaphore_); - CloseHandle(handler_finish_semaphore_); + // Some of the objects were only initialized if out of process + // registration was not done. + if (!IsOutOfProcess()) { + // Clean up the handler thread and synchronization primitives. + TerminateThread(handler_thread_, 1); + DeleteCriticalSection(&handler_critical_section_); + CloseHandle(handler_start_semaphore_); + CloseHandle(handler_finish_semaphore_); + } } // static -DWORD ExceptionHandler::ExceptionHandlerThreadMain(void *lpParameter) { - ExceptionHandler *self = reinterpret_cast(lpParameter); +DWORD ExceptionHandler::ExceptionHandlerThreadMain(void* lpParameter) { + ExceptionHandler* self = reinterpret_cast(lpParameter); assert(self); while (true) { @@ -276,16 +327,16 @@ class AutoExceptionHandler { LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_); } - ExceptionHandler *get_handler() const { return handler_; } + ExceptionHandler* get_handler() const { return handler_; } private: - ExceptionHandler *handler_; + ExceptionHandler* handler_; }; // static -LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS *exinfo) { +LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) { AutoExceptionHandler auto_exception_handler; - ExceptionHandler *current_handler = auto_exception_handler.get_handler(); + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); // Ignore EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP exceptions. This // logic will short-circuit before calling WriteMinidumpOnHandlerThread, @@ -296,15 +347,35 @@ LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS *exinfo) { LONG action; bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) || (code == EXCEPTION_SINGLE_STEP); - if ((!is_debug_exception || current_handler->get_handle_debug_exceptions()) && - current_handler->WriteMinidumpOnHandlerThread(exinfo, NULL)) { - // The handler fully handled the exception. Returning - // EXCEPTION_EXECUTE_HANDLER indicates this to the system, and usually - // results in the applicaiton being terminated. - // - // Note: If the application was launched from within the Cygwin - // environment, returning EXCEPTION_EXECUTE_HANDLER seems to cause the - // application to be restarted. + + bool success = false; + + if (!is_debug_exception || + current_handler->get_handle_debug_exceptions()) { + // If out-of-proc crash handler client is available, we have to use that + // to generate dump and we cannot fall back on in-proc dump generation + // because we never prepared for an in-proc dump generation + + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + if (current_handler->IsOutOfProcess()) { + success = current_handler->WriteMinidumpWithException( + GetCurrentThreadId(), + exinfo, + NULL); + } else { + success = current_handler->WriteMinidumpOnHandlerThread(exinfo, NULL); + } + } + + // The handler fully handled the exception. Returning + // EXCEPTION_EXECUTE_HANDLER indicates this to the system, and usually + // results in the application being terminated. + // + // Note: If the application was launched from within the Cygwin + // environment, returning EXCEPTION_EXECUTE_HANDLER seems to cause the + // application to be restarted. + if (success) { action = EXCEPTION_EXECUTE_HANDLER; } else { // There was an exception, it was a breakpoint or something else ignored @@ -327,35 +398,51 @@ LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS *exinfo) { #if _MSC_VER >= 1400 // MSVC 2005/8 // static -void ExceptionHandler::HandleInvalidParameter(const wchar_t *expression, - const wchar_t *function, - const wchar_t *file, +void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, unsigned int line, uintptr_t reserved) { // This is an invalid parameter, not an exception. It's safe to play with // sprintf here. AutoExceptionHandler auto_exception_handler; - ExceptionHandler *current_handler = auto_exception_handler.get_handler(); + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); MDRawAssertionInfo assertion; memset(&assertion, 0, sizeof(assertion)); - _snwprintf_s(reinterpret_cast(assertion.expression), + _snwprintf_s(reinterpret_cast(assertion.expression), sizeof(assertion.expression) / sizeof(assertion.expression[0]), _TRUNCATE, L"%s", expression); - _snwprintf_s(reinterpret_cast(assertion.function), + _snwprintf_s(reinterpret_cast(assertion.function), sizeof(assertion.function) / sizeof(assertion.function[0]), _TRUNCATE, L"%s", function); - _snwprintf_s(reinterpret_cast(assertion.file), + _snwprintf_s(reinterpret_cast(assertion.file), sizeof(assertion.file) / sizeof(assertion.file[0]), _TRUNCATE, L"%s", file); assertion.line = line; assertion.type = MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER; - if (!current_handler->WriteMinidumpOnHandlerThread(NULL, &assertion)) { + bool success = false; + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + if (current_handler->IsOutOfProcess()) { + success = current_handler->WriteMinidumpWithException( + GetCurrentThreadId(), + NULL, + &assertion); + } else { + success = current_handler->WriteMinidumpOnHandlerThread(NULL, &assertion); + } + + if (!success) { if (current_handler->previous_iph_) { // The handler didn't fully handle the exception. Give it to the // previous invalid parameter handler. - current_handler->previous_iph_(expression, function, file, line, reserved); + current_handler->previous_iph_(expression, + function, + file, + line, + reserved); } else { // If there's no previous handler, pass the exception back in to the // invalid parameter handler's core. That's the routine that called this @@ -384,13 +471,26 @@ void ExceptionHandler::HandleInvalidParameter(const wchar_t *expression, // static void ExceptionHandler::HandlePureVirtualCall() { AutoExceptionHandler auto_exception_handler; - ExceptionHandler *current_handler = auto_exception_handler.get_handler(); + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); MDRawAssertionInfo assertion; memset(&assertion, 0, sizeof(assertion)); assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL; - if (!current_handler->WriteMinidumpOnHandlerThread(NULL, &assertion)) { + bool success = false; + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + + if (current_handler->IsOutOfProcess()) { + success = current_handler->WriteMinidumpWithException( + GetCurrentThreadId(), + NULL, + &assertion); + } else { + success = current_handler->WriteMinidumpOnHandlerThread(NULL, &assertion); + } + + if (!success) { if (current_handler->previous_pch_) { // The handler didn't fully handle the exception. Give it to the // previous purecall handler. @@ -409,7 +509,7 @@ void ExceptionHandler::HandlePureVirtualCall() { } bool ExceptionHandler::WriteMinidumpOnHandlerThread( - EXCEPTION_POINTERS *exinfo, MDRawAssertionInfo *assertion) { + EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion) { EnterCriticalSection(&handler_critical_section_); // Set up data to be passed in to the handler thread. @@ -438,7 +538,15 @@ bool ExceptionHandler::WriteMinidump() { return WriteMinidumpForException(NULL); } -bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS *exinfo) { +bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS* exinfo) { + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + if (IsOutOfProcess()) { + return WriteMinidumpWithException(GetCurrentThreadId(), + exinfo, + NULL); + } + bool success = WriteMinidumpOnHandlerThread(exinfo, NULL); UpdateNextID(); return success; @@ -447,7 +555,7 @@ bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS *exinfo) { // static bool ExceptionHandler::WriteMinidump(const wstring &dump_path, MinidumpCallback callback, - void *callback_context) { + void* callback_context) { ExceptionHandler handler(dump_path, NULL, callback, callback_context, HANDLER_NONE); return handler.WriteMinidump(); @@ -455,8 +563,8 @@ bool ExceptionHandler::WriteMinidump(const wstring &dump_path, bool ExceptionHandler::WriteMinidumpWithException( DWORD requesting_thread_id, - EXCEPTION_POINTERS *exinfo, - MDRawAssertionInfo *assertion) { + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion) { // Give user code a chance to approve or prevent writing a minidump. If the // filter returns false, don't handle the exception at all. If this method // was called as a result of an exception, returning false will cause @@ -468,63 +576,77 @@ bool ExceptionHandler::WriteMinidumpWithException( } bool success = false; - if (minidump_write_dump_) { - HANDLE dump_file = CreateFile(next_minidump_path_c_, - GENERIC_WRITE, - 0, // no sharing - NULL, - CREATE_NEW, // fail if exists - FILE_ATTRIBUTE_NORMAL, - NULL); - if (dump_file != INVALID_HANDLE_VALUE) { - MINIDUMP_EXCEPTION_INFORMATION except_info; - except_info.ThreadId = requesting_thread_id; - except_info.ExceptionPointers = exinfo; - except_info.ClientPointers = FALSE; + if (IsOutOfProcess()) { + // Use the EXCEPTION_POINTERS overload for RequestDump if + // both exinfo and assertion are NULL. + if (!assertion) { + success = crash_generation_client_->RequestDump(exinfo); + } else { + success = crash_generation_client_->RequestDump(assertion); + } + } else { + if (minidump_write_dump_) { + HANDLE dump_file = CreateFile(next_minidump_path_c_, + GENERIC_WRITE, + 0, // no sharing + NULL, + CREATE_NEW, // fail if exists + FILE_ATTRIBUTE_NORMAL, + NULL); + if (dump_file != INVALID_HANDLE_VALUE) { + MINIDUMP_EXCEPTION_INFORMATION except_info; + except_info.ThreadId = requesting_thread_id; + except_info.ExceptionPointers = exinfo; + except_info.ClientPointers = FALSE; - // Add an MDRawBreakpadInfo stream to the minidump, to provide additional - // information about the exception handler to the Breakpad processor. The - // information will help the processor determine which threads are - // relevant. The Breakpad processor does not require this information but - // can function better with Breakpad-generated dumps when it is present. - // The native debugger is not harmed by the presence of this information. - MDRawBreakpadInfo breakpad_info; - breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | - MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; - breakpad_info.dump_thread_id = GetCurrentThreadId(); - breakpad_info.requesting_thread_id = requesting_thread_id; + // Add an MDRawBreakpadInfo stream to the minidump, to provide additional + // information about the exception handler to the Breakpad processor. The + // information will help the processor determine which threads are + // relevant. The Breakpad processor does not require this information but + // can function better with Breakpad-generated dumps when it is present. + // The native debugger is not harmed by the presence of this information. + MDRawBreakpadInfo breakpad_info; + breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; + breakpad_info.dump_thread_id = GetCurrentThreadId(); + breakpad_info.requesting_thread_id = requesting_thread_id; - // Leave room in user_stream_array for a possible assertion info stream. - MINIDUMP_USER_STREAM user_stream_array[2]; - user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM; - user_stream_array[0].BufferSize = sizeof(breakpad_info); - user_stream_array[0].Buffer = &breakpad_info; + // Leave room in user_stream_array for a possible assertion info stream. + MINIDUMP_USER_STREAM user_stream_array[2]; + user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM; + user_stream_array[0].BufferSize = sizeof(breakpad_info); + user_stream_array[0].Buffer = &breakpad_info; - MINIDUMP_USER_STREAM_INFORMATION user_streams; - user_streams.UserStreamCount = 1; - user_streams.UserStreamArray = user_stream_array; + MINIDUMP_USER_STREAM_INFORMATION user_streams; + user_streams.UserStreamCount = 1; + user_streams.UserStreamArray = user_stream_array; - if (assertion) { - user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM; - user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo); - user_stream_array[1].Buffer = assertion; - ++user_streams.UserStreamCount; + if (assertion) { + user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM; + user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo); + user_stream_array[1].Buffer = assertion; + ++user_streams.UserStreamCount; + } + + // The explicit comparison to TRUE avoids a warning (C4800). + success = (minidump_write_dump_(GetCurrentProcess(), + GetCurrentProcessId(), + dump_file, + dump_type_, + exinfo ? &except_info : NULL, + &user_streams, + NULL) == TRUE); + + CloseHandle(dump_file); } - - // The explicit comparison to TRUE avoids a warning (C4800). - success = (minidump_write_dump_(GetCurrentProcess(), - GetCurrentProcessId(), - dump_file, - MiniDumpNormal, - exinfo ? &except_info : NULL, - &user_streams, - NULL) == TRUE); - - CloseHandle(dump_file); } } if (callback_) { + // TODO(munjal): In case of out-of-process dump generation, both + // dump_path_c_ and next_minidump_id_ will be NULL. For out-of-process + // scenario, the server process ends up creating the dump path and dump + // id so they are not known to the client. success = callback_(dump_path_c_, next_minidump_id_c_, callback_context_, exinfo, assertion, success); } diff --git a/src/client/windows/handler/exception_handler.h b/src/client/windows/handler/exception_handler.h index fc9af326..870025fb 100644 --- a/src/client/windows/handler/exception_handler.h +++ b/src/client/windows/handler/exception_handler.h @@ -68,7 +68,9 @@ #include #include +#include "client/windows/crash_generation/crash_generation_client.h" #include "google_breakpad/common/minidump_format.h" +#include "processor/scoped_ptr.h" namespace google_breakpad { @@ -88,8 +90,8 @@ class ExceptionHandler { // attempting to write a minidump. If a FilterCallback returns false, Breakpad // will immediately report the exception as unhandled without writing a // minidump, allowing another handler the opportunity to handle it. - typedef bool (*FilterCallback)(void *context, EXCEPTION_POINTERS *exinfo, - MDRawAssertionInfo *assertion); + typedef bool (*FilterCallback)(void* context, EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion); // A callback function to run after the minidump has been written. // minidump_id is a unique id for the dump, so the minidump @@ -110,11 +112,16 @@ class ExceptionHandler { // should normally return the value of |succeeded|, or when they wish to // not report an exception of handled, false. Callbacks will rarely want to // return true directly (unless |succeeded| is true). - typedef bool (*MinidumpCallback)(const wchar_t *dump_path, - const wchar_t *minidump_id, - void *context, - EXCEPTION_POINTERS *exinfo, - MDRawAssertionInfo *assertion, + // + // For out-of-process dump generation, dump path and minidump ID will always + // be NULL. In case of out-of-process dump generation, the dump path and + // minidump id are controlled by the server process and are not communicated + // back to the crashing process. + typedef bool (*MinidumpCallback)(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, bool succeeded); // HandlerType specifies which types of handlers should be installed, if @@ -139,11 +146,25 @@ class ExceptionHandler { // minidump. Minidump files will be written to dump_path, and the optional // callback is called after writing the dump file, as described above. // handler_types specifies the types of handlers that should be installed. - ExceptionHandler(const wstring &dump_path, + ExceptionHandler(const wstring& dump_path, FilterCallback filter, MinidumpCallback callback, - void *callback_context, + void* callback_context, int handler_types); + + // Creates a new ExcetpionHandler instance that can attempt to perform + // out-of-process dump generation if pipe_name is not NULL. If pipe_name is + // NULL, or if out-of-process dump generation registration step fails, + // in-process dump generation will be used. This also allows specifying + // the dump type to generate. + ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name); + ~ExceptionHandler(); // Get and set the minidump path. @@ -160,12 +181,12 @@ class ExceptionHandler { // Writes a minidump immediately, with the user-supplied exception // information. - bool WriteMinidumpForException(EXCEPTION_POINTERS *exinfo); + bool WriteMinidumpForException(EXCEPTION_POINTERS* exinfo); // Convenience form of WriteMinidump which does not require an // ExceptionHandler instance. static bool WriteMinidump(const wstring &dump_path, - MinidumpCallback callback, void *callback_context); + MinidumpCallback callback, void* callback_context); // Get the thread ID of the thread requesting the dump (either the exception // thread or any other thread that called WriteMinidump directly). This @@ -179,9 +200,21 @@ class ExceptionHandler { handle_debug_exceptions_ = handle_debug_exceptions; } + // Returns whether out-of-process dump generation is used or not. + bool IsOutOfProcess() const { return crash_generation_client_.get() != NULL; } + private: friend class AutoExceptionHandler; + // Initializes the instance with given values. + void Initialize(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name); + // Function pointer type for MiniDumpWriteDump, which is looked up // dynamically. typedef BOOL (WINAPI *MiniDumpWriteDump_type)( @@ -194,14 +227,14 @@ class ExceptionHandler { CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); // Function pointer type for UuidCreate, which is looked up dynamically. - typedef RPC_STATUS (RPC_ENTRY *UuidCreate_type)(UUID *Uuid); + typedef RPC_STATUS (RPC_ENTRY *UuidCreate_type)(UUID* Uuid); // Runs the main loop for the exception handler thread. - static DWORD WINAPI ExceptionHandlerThreadMain(void *lpParameter); + static DWORD WINAPI ExceptionHandlerThreadMain(void* lpParameter); // Called on the exception thread when an unhandled exception occurs. // Signals the exception handler thread to handle the exception. - static LONG WINAPI HandleException(EXCEPTION_POINTERS *exinfo); + static LONG WINAPI HandleException(EXCEPTION_POINTERS* exinfo); #if _MSC_VER >= 1400 // MSVC 2005/8 // This function will be called by some CRT functions when they detect @@ -209,9 +242,9 @@ class ExceptionHandler { // the CRT may display an assertion dialog before calling this function, // and the function will not be called unless the assertion dialog is // dismissed by clicking "Ignore." - static void HandleInvalidParameter(const wchar_t *expression, - const wchar_t *function, - const wchar_t *file, + static void HandleInvalidParameter(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, unsigned int line, uintptr_t reserved); #endif // _MSC_VER >= 1400 @@ -229,8 +262,8 @@ class ExceptionHandler { // is NULL. If the dump is requested as a result of an assertion // (such as an invalid parameter being passed to a CRT function), // assertion contains data about the assertion, otherwise, it is NULL. - bool WriteMinidumpOnHandlerThread(EXCEPTION_POINTERS *exinfo, - MDRawAssertionInfo *assertion); + bool WriteMinidumpOnHandlerThread(EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion); // This function does the actual writing of a minidump. It is called // on the handler thread. requesting_thread_id is the ID of the thread @@ -238,8 +271,8 @@ class ExceptionHandler { // an exception, exinfo contains exception information, otherwise, // it is NULL. bool WriteMinidumpWithException(DWORD requesting_thread_id, - EXCEPTION_POINTERS *exinfo, - MDRawAssertionInfo *assertion); + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion); // Generates a new ID and stores it in next_minidump_id_, and stores the // path of the next minidump to be written in next_minidump_path_. @@ -247,7 +280,9 @@ class ExceptionHandler { FilterCallback filter_; MinidumpCallback callback_; - void *callback_context_; + void* callback_context_; + + scoped_ptr crash_generation_client_; // The directory in which a minidump will be written, set by the dump_path // argument to the constructor, or set_dump_path. @@ -266,12 +301,13 @@ class ExceptionHandler { // pointers are not owned by the ExceptionHandler object, but their lifetimes // should be equivalent to the lifetimes of the associated wstring, provided // that the wstrings are not altered. - const wchar_t *dump_path_c_; - const wchar_t *next_minidump_id_c_; - const wchar_t *next_minidump_path_c_; + const wchar_t* dump_path_c_; + const wchar_t* next_minidump_id_c_; + const wchar_t* next_minidump_path_c_; HMODULE dbghelp_module_; MiniDumpWriteDump_type minidump_write_dump_; + MINIDUMP_TYPE dump_type_; HMODULE rpcrt4_module_; UuidCreate_type uuid_create_; @@ -322,11 +358,11 @@ class ExceptionHandler { // The exception info passed to the exception handler on the exception // thread, if an exception occurred. NULL for user-requested dumps. - EXCEPTION_POINTERS *exception_info_; + EXCEPTION_POINTERS* exception_info_; // If the handler is invoked due to an assertion, this will contain a // pointer to the assertion information. It is NULL at other times. - MDRawAssertionInfo *assertion_; + MDRawAssertionInfo* assertion_; // The return value of the handler, passed from the handler thread back to // the requesting thread. @@ -342,7 +378,7 @@ class ExceptionHandler { // which ExceptionHandler object to route an exception to. When an // ExceptionHandler is created with install_handler true, it will append // itself to this list. - static vector *handler_stack_; + static vector* handler_stack_; // The index of the ExceptionHandler in handler_stack_ that will handle the // next exception. Note that 0 means the last entry in handler_stack_, 1 diff --git a/src/client/windows/tests/crash_generation_app/abstract_class.cc b/src/client/windows/tests/crash_generation_app/abstract_class.cc new file mode 100644 index 00000000..9abc79eb --- /dev/null +++ b/src/client/windows/tests/crash_generation_app/abstract_class.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "precompile.h" + +namespace google_breakpad { + +Base::Base(Derived* derived) + : derived_(derived) { +} + +Base::~Base() { + derived_->DoSomething(); +} + +#pragma warning(push) +#pragma warning(disable:4355) +// Disable warning C4355: 'this' : used in base member initializer list. +Derived::Derived() + : Base(this) { // C4355 +} +#pragma warning(pop) + +void Derived::DoSomething() { +} + +} // namespace google_breakpad diff --git a/src/client/windows/tests/crash_generation_app/abstract_class.h b/src/client/windows/tests/crash_generation_app/abstract_class.h new file mode 100644 index 00000000..c516f50e --- /dev/null +++ b/src/client/windows/tests/crash_generation_app/abstract_class.h @@ -0,0 +1,57 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_ABSTRACT_CLASS_H__ +#define CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_ABSTRACT_CLASS_H__ + +namespace google_breakpad { + +// Dummy classes to help generate a pure call violation. + +class Derived; + +class Base { + public: + Base(Derived* derived); + virtual ~Base(); + virtual void DoSomething() = 0; + + private: + Derived* derived_; +}; + +class Derived : public Base { + public: + Derived(); + virtual void DoSomething(); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_CRASH_GENERATION_APP_H__ diff --git a/src/client/windows/tests/crash_generation_app/crash_generation_app.cc b/src/client/windows/tests/crash_generation_app/crash_generation_app.cc new file mode 100644 index 00000000..314ae8d1 --- /dev/null +++ b/src/client/windows/tests/crash_generation_app/crash_generation_app.cc @@ -0,0 +1,467 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// crash_generation_app.cpp : Defines the entry point for the application. +// + +#include "precompile.h" + +namespace google_breakpad { + +const int kMaxLoadString = 100; +const wchar_t kPipeName[] = L"\\\\.\\pipe\\GoogleCrashServices"; + +const DWORD kEditBoxStyles = WS_CHILD | + WS_VISIBLE | + WS_VSCROLL | + ES_LEFT | + ES_MULTILINE | + ES_AUTOVSCROLL | + ES_READONLY; + +// Maximum length of a line in the edit box. +const size_t kMaximumLineLength = 256; + +// CS to access edit control in a thread safe way. +static CRITICAL_SECTION* cs_edit = NULL; + +// Edit control. +static HWND client_status_edit_box; + +HINSTANCE current_instance; // Current instance. +TCHAR title[kMaxLoadString]; // Title bar text. +TCHAR window_class[kMaxLoadString]; // Main window class name. + +ATOM MyRegisterClass(HINSTANCE instance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); + +static ExceptionHandler* handler = NULL; +static CrashGenerationServer* crash_server = NULL; + +// Registers the window class. +// +// This function and its usage are only necessary if you want this code +// to be compatible with Win32 systems prior to the 'RegisterClassEx' +// function that was added to Windows 95. It is important to call this +// function so that the application will get 'well formed' small icons +// associated with it. +ATOM MyRegisterClass(HINSTANCE instance) { + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = instance; + wcex.hIcon = LoadIcon(instance, + MAKEINTRESOURCE(IDI_CRASHGENERATIONAPP)); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = MAKEINTRESOURCE(IDC_CRASHGENERATIONAPP); + wcex.lpszClassName = window_class; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + return RegisterClassEx(&wcex); +} + +// Saves instance handle and creates main window +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +BOOL InitInstance(HINSTANCE instance, int command_show) { + current_instance = instance; + HWND wnd = CreateWindow(window_class, + title, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + 0, + CW_USEDEFAULT, + 0, + NULL, + NULL, + instance, + NULL); + + if (!wnd) { + return FALSE; + } + + ShowWindow(wnd, command_show); + UpdateWindow(wnd); + + return TRUE; +} + +static void AppendTextToEditBox(TCHAR* text) { + EnterCriticalSection(cs_edit); + SYSTEMTIME current_time; + GetLocalTime(¤t_time); + TCHAR line[kMaximumLineLength]; + int result = swprintf_s(line, + kMaximumLineLength, + L"[%.2d-%.2d-%.4d %.2d:%.2d:%.2d] %s", + current_time.wMonth, + current_time.wDay, + current_time.wYear, + current_time.wHour, + current_time.wMinute, + current_time.wSecond, + text); + + if (result == -1) { + return; + } + + int length = GetWindowTextLength(client_status_edit_box); + SendMessage(client_status_edit_box, + EM_SETSEL, + (WPARAM)length, + (LPARAM)length); + SendMessage(client_status_edit_box, + EM_REPLACESEL, + (WPARAM)FALSE, + (LPARAM)line); + LeaveCriticalSection(cs_edit); +} + +static DWORD WINAPI AppendTextWorker(void* context) { + TCHAR* text = reinterpret_cast(context); + + AppendTextToEditBox(text); + delete[] text; + + return 0; +} + +bool ShowDumpResults(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded) { + TCHAR* text = new TCHAR[kMaximumLineLength]; + int result = swprintf_s(text, + kMaximumLineLength, + TEXT("Dump generation request %s\r\n"), + succeeded ? TEXT("succeeded") : TEXT("failed")); + if (result == -1) { + delete [] text; + } + + AppendTextWorker(text); + return succeeded; +} + +static void _cdecl ShowClientConnected(void* context, + const ClientInfo* client_info) { + TCHAR* line = new TCHAR[kMaximumLineLength]; + int result = swprintf_s(line, + kMaximumLineLength, + L"Client connected:\t\t%d\r\n", + client_info->pid()); + + if (result == -1) { + delete[] line; + return; + } + + QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); +} + +static void _cdecl ShowClientCrashed(void* context, + const ClientInfo* client_info) { + TCHAR* line = new TCHAR[kMaximumLineLength]; + int result = swprintf_s(line, + kMaximumLineLength, + TEXT("Client requested dump:\t%d\r\n"), + client_info->pid()); + + if (result == -1) { + delete[] line; + return; + } + + QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); +} + +static void _cdecl ShowClientExited(void* context, + const ClientInfo* client_info) { + TCHAR* line = new TCHAR[kMaximumLineLength]; + int result = swprintf_s(line, + kMaximumLineLength, + TEXT("Client exited:\t\t%d\r\n"), + client_info->pid()); + + if (result == -1) { + delete[] line; + return; + } + + QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); +} + +void CrashServerStart() { + // Do not create another instance of the server. + if (crash_server) { + return; + } + + std::wstring dump_path = L"C:\\Dumps\\"; + crash_server = new CrashGenerationServer(kPipeName, + ShowClientConnected, + NULL, + ShowClientCrashed, + NULL, + ShowClientExited, + NULL, + true, + &dump_path); + + if (!crash_server->Start()) { + MessageBoxW(NULL, L"Unable to start server", L"Dumper", MB_OK); + delete crash_server; + crash_server = NULL; + } +} + +void CrashServerStop() { + delete crash_server; + crash_server = NULL; +} + +void DerefZeroCrash() { + int* x = 0; + *x = 1; +} + +void InvalidParamCrash() { + printf(NULL); +} + +void PureCallCrash() { + Derived derived; +} + +void RequestDump() { + if (!handler->WriteMinidump()) { + MessageBoxW(NULL, L"Dump request failed", L"Dumper", MB_OK); + } +} + +void CleanUp() { + if (cs_edit) { + DeleteCriticalSection(cs_edit); + delete cs_edit; + } + + if (handler) { + delete handler; + } + + if (crash_server) { + delete crash_server; + } +} + +// Processes messages for the main window. +// +// WM_COMMAND - process the application menu. +// WM_PAINT - Paint the main window. +// WM_DESTROY - post a quit message and return. +LRESULT CALLBACK WndProc(HWND wnd, + UINT message, + WPARAM w_param, + LPARAM l_param) { + int message_id; + int message_event; + PAINTSTRUCT ps; + HDC hdc; + +#pragma warning(push) +#pragma warning(disable:4312) + // Disable warning C4312: 'type cast' : conversion from 'LONG' to + // 'HINSTANCE' of greater size. + // The value returned by GetwindowLong in the case below returns unsigned. + HINSTANCE instance = (HINSTANCE)GetWindowLong(wnd, GWL_HINSTANCE); +#pragma warning(pop) + + switch (message) { + case WM_COMMAND: + // Parse the menu selections. + message_id = LOWORD(w_param); + message_event = HIWORD(w_param); + switch (message_id) { + case IDM_ABOUT: + DialogBox(current_instance, + MAKEINTRESOURCE(IDD_ABOUTBOX), + wnd, + About); + break; + case IDM_EXIT: + DestroyWindow(wnd); + break; + case ID_SERVER_START: + CrashServerStart(); + break; + case ID_SERVER_STOP: + CrashServerStop(); + break; + case ID_CLIENT_DEREFZERO: + DerefZeroCrash(); + break; + case ID_CLIENT_INVALIDPARAM: + InvalidParamCrash(); + break; + case ID_CLIENT_PURECALL: + PureCallCrash(); + break; + case ID_CLIENT_REQUESTEXPLICITDUMP: + RequestDump(); + break; + default: + return DefWindowProc(wnd, message, w_param, l_param); + } + break; + case WM_CREATE: + client_status_edit_box = CreateWindow(TEXT("EDIT"), + NULL, + kEditBoxStyles, + 0, + 0, + 0, + 0, + wnd, + NULL, + instance, + NULL); + break; + case WM_SIZE: + // Make the edit control the size of the window's client area. + MoveWindow(client_status_edit_box, + 0, + 0, + LOWORD(l_param), // width of client area. + HIWORD(l_param), // height of client area. + TRUE); // repaint window. + break; + case WM_SETFOCUS: + SetFocus(client_status_edit_box); + break; + case WM_PAINT: + hdc = BeginPaint(wnd, &ps); + EndPaint(wnd, &ps); + break; + case WM_DESTROY: + CleanUp(); + PostQuitMessage(0); + break; + default: + return DefWindowProc(wnd, message, w_param, l_param); + } + + return 0; +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND dlg, + UINT message, + WPARAM w_param, + LPARAM l_param) { + UNREFERENCED_PARAMETER(l_param); + switch (message) { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(w_param) == IDOK || LOWORD(w_param) == IDCANCEL) { + EndDialog(dlg, LOWORD(w_param)); + return (INT_PTR)TRUE; + } + break; + } + + return (INT_PTR)FALSE; +} + +} // namespace google_breakpad + +int APIENTRY _tWinMain(HINSTANCE instance, + HINSTANCE previous_instance, + LPTSTR command_line, + int command_show) { + using namespace google_breakpad; + + UNREFERENCED_PARAMETER(previous_instance); + UNREFERENCED_PARAMETER(command_line); + + cs_edit = new CRITICAL_SECTION(); + InitializeCriticalSection(cs_edit); + + // This is needed for CRT to not show dialog for invalid param + // failures and instead let the code handle it. + _CrtSetReportMode(_CRT_ASSERT, 0); + handler = new ExceptionHandler(L"C:\\dumps\\", + NULL, + google_breakpad::ShowDumpResults, + NULL, + ExceptionHandler::HANDLER_ALL, + MiniDumpNormal, + kPipeName); + + // Initialize global strings. + LoadString(instance, IDS_APP_TITLE, title, kMaxLoadString); + LoadString(instance, + IDC_CRASHGENERATIONAPP, + window_class, + kMaxLoadString); + MyRegisterClass(instance); + + // Perform application initialization. + if (!InitInstance (instance, command_show)) { + return FALSE; + } + + HACCEL accel_table = LoadAccelerators( + instance, + MAKEINTRESOURCE(IDC_CRASHGENERATIONAPP)); + + // Main message loop. + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + if (!TranslateAccelerator(msg.hwnd, accel_table, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return (int)msg.wParam; +} + diff --git a/src/client/windows/tests/crash_generation_app/crash_generation_app.h b/src/client/windows/tests/crash_generation_app/crash_generation_app.h new file mode 100644 index 00000000..2273912d --- /dev/null +++ b/src/client/windows/tests/crash_generation_app/crash_generation_app.h @@ -0,0 +1,35 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_CRASH_GENERATION_APP_H__ +#define CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_CRASH_GENERATION_APP_H__ + +#include "resource.h" + +#endif // CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_CRASH_GENERATION_APP_H__ diff --git a/src/client/windows/tests/crash_generation_app/crash_generation_app.ico b/src/client/windows/tests/crash_generation_app/crash_generation_app.ico new file mode 100644 index 00000000..d551aa3a Binary files /dev/null and b/src/client/windows/tests/crash_generation_app/crash_generation_app.ico differ diff --git a/src/client/windows/tests/crash_generation_app/crash_generation_app.rc b/src/client/windows/tests/crash_generation_app/crash_generation_app.rc new file mode 100644 index 00000000..c31b9c20 --- /dev/null +++ b/src/client/windows/tests/crash_generation_app/crash_generation_app.rc @@ -0,0 +1,144 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_CRASHGENERATIONAPP ICON "crash_generation_app.ico" +IDI_SMALL ICON "small.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDC_CRASHGENERATIONAPP MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "E&xit", IDM_EXIT + END + POPUP "&Server" + BEGIN + MENUITEM "&Start", ID_SERVER_START + MENUITEM "S&top", ID_SERVER_STOP + END + POPUP "&Client" + BEGIN + MENUITEM "&Deref Zero", ID_CLIENT_DEREFZERO + MENUITEM "&Invalid Param", ID_CLIENT_INVALIDPARAM + MENUITEM "&Pure Call", ID_CLIENT_PURECALL + MENUITEM "&Request Dump", ID_CLIENT_REQUESTEXPLICITDUMP + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDC_CRASHGENERATIONAPP ACCELERATORS +BEGIN + "?", IDM_ABOUT, ASCII, ALT + "/", IDM_ABOUT, ASCII, ALT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG 22, 17, 230, 75 +STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "System" +BEGIN + ICON IDI_CRASHGENERATIONAPP,IDC_MYICON,14,9,16,16 + LTEXT "CrashGenerationApp Version 1.0",IDC_STATIC,49,10,119,8,SS_NOPREFIX + LTEXT "Copyright (C) 2008",IDC_STATIC,49,20,119,8 + DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_APP_TITLE "CrashGenerationApp" + IDC_CRASHGENERATIONAPP "CRASHGENERATIONAPP" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/client/windows/tests/crash_generation_app/crash_generation_app.sln b/src/client/windows/tests/crash_generation_app/crash_generation_app.sln new file mode 100644 index 00000000..85d06c6d --- /dev/null +++ b/src/client/windows/tests/crash_generation_app/crash_generation_app.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_generation_app", "crash_generation_app.vcproj", "{A15674ED-713D-4B37-B1D2-0C29C7E533C8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A15674ED-713D-4B37-B1D2-0C29C7E533C8}.Debug|Win32.ActiveCfg = Debug|Win32 + {A15674ED-713D-4B37-B1D2-0C29C7E533C8}.Debug|Win32.Build.0 = Debug|Win32 + {A15674ED-713D-4B37-B1D2-0C29C7E533C8}.Release|Win32.ActiveCfg = Release|Win32 + {A15674ED-713D-4B37-B1D2-0C29C7E533C8}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/client/windows/tests/crash_generation_app/crash_generation_app.vcproj b/src/client/windows/tests/crash_generation_app/crash_generation_app.vcproj new file mode 100644 index 00000000..b5aa93c0 --- /dev/null +++ b/src/client/windows/tests/crash_generation_app/crash_generation_app.vcproj @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/client/windows/tests/crash_generation_app/precompile.cc b/src/client/windows/tests/crash_generation_app/precompile.cc new file mode 100644 index 00000000..409b4a4b --- /dev/null +++ b/src/client/windows/tests/crash_generation_app/precompile.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// precompile.cpp : source file that includes just the standard includes +// CrashGenerationApp.pch will be the pre-compiled header +// precompile.obj will contain the pre-compiled type information + +#include "precompile.h" + +// Reference any additional headers you need in PRECOMPILE.H +// and not in this file. diff --git a/src/client/windows/tests/crash_generation_app/precompile.h b/src/client/windows/tests/crash_generation_app/precompile.h new file mode 100644 index 00000000..596409e9 --- /dev/null +++ b/src/client/windows/tests/crash_generation_app/precompile.h @@ -0,0 +1,89 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// PreCompile.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#ifndef CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_PRECOMPILE_H__ +#define CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_PRECOMPILE_H__ + +// Modify the following defines if you have to target a platform prior to +// the ones specified below. Refer to MSDN for the latest info on +// corresponding values for different platforms. + +// Allow use of features specific to Windows XP or later. +#ifndef WINVER +// Change this to the appropriate value to target other versions of Windows. +#define WINVER 0x0501 +#endif + +// Allow use of features specific to Windows XP or later. +#ifndef _WIN32_WINNT +// Change this to the appropriate value to target other versions of Windows. +#define _WIN32_WINNT 0x0501 +#endif + +// Allow use of features specific to Windows 98 or later. +#ifndef _WIN32_WINDOWS +// Change this to the appropriate value to target Windows Me or later. +#define _WIN32_WINDOWS 0x0410 +#endif + +// Allow use of features specific to IE 6.0 or later. +#ifndef _WIN32_IE +// Change this to the appropriate value to target other versions of IE. +#define _WIN32_IE 0x0600 +#endif + +// Exclude rarely-used stuff from Windows headers +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "client/windows/crash_generation/client_info.h" +#include "client/windows/crash_generation/crash_generation_client.h" +#include "client/windows/crash_generation/crash_generation_server.h" +#include "client/windows/crash_generation/minidump_generator.h" +#include "client/windows/handler/exception_handler.h" +#include "client/windows/tests/crash_generation_app/abstract_class.h" +#include "client/windows/tests/crash_generation_app/crash_generation_app.h" +#include "google_breakpad/common/minidump_format.h" + +#endif // CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_PRECOMPILE_H__ diff --git a/src/client/windows/tests/crash_generation_app/resource.h b/src/client/windows/tests/crash_generation_app/resource.h new file mode 100644 index 00000000..8e37512e --- /dev/null +++ b/src/client/windows/tests/crash_generation_app/resource.h @@ -0,0 +1,73 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// PreCompile.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#ifndef CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_RESOURCE_H__ +#define CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_RESOURCE_H__ + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by crash_generation_app.rc +// +#define IDC_MYICON 2 +#define IDD_CRASHGENERATIONAPP_DIALOG 102 +#define IDS_APP_TITLE 103 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_CRASHGENERATIONAPP 107 +#define IDI_SMALL 108 +#define IDC_CRASHGENERATIONAPP 109 +#define IDR_MAINFRAME 128 +#define ID_SERVER_START 32771 +#define ID_SERVER_STOP 32772 +#define ID_CLIENT_INVALIDPARAM 32773 +#define ID_CLIENT_ASSERTFAILURE 32774 +#define ID_CLIENT_DEREFZERO 32775 +#define ID_CLIENT_PURECALL 32777 +#define ID_CLIENT_REQUESTEXPLICITDUMP 32778 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32780 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif + +#endif // CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_RESOURCE_H__ diff --git a/src/client/windows/tests/crash_generation_app/small.ico b/src/client/windows/tests/crash_generation_app/small.ico new file mode 100644 index 00000000..d551aa3a Binary files /dev/null and b/src/client/windows/tests/crash_generation_app/small.ico differ