Overview:
Implement out-of-process dump generation for Windows platform. Details: - Created a lib, crash_generation.lib, that implements the out-of-process dump generation protocol. - The lib code is under client/windows/crash_generation folder and is organized in the following way: - CrashGenerationServer class (crash_generation_server.h/.cc) implements the server side of the protocol. - CrashGenerationClient class (crash_generation_client.h/.cc) implements the client side of the protocol. - MinidumpGenerator class (minidump_generator.h/.cc) serves as an abstractino for generating dump files using Windows APIs, coming up with new file names by creating GUIDs, etc. - ProtocolMessage class (ipc_protocol.h) represents the message format between the client and server for pipe IPC. - Server allows one client at a time on the pipe in the current implementation. - ReadMe.txt explains the state machine the server uses to serve clients. - ExceptionHandler is modified and a new constructor is added that allows specifying the pipe name. If the pipe name is NULL, the behavior is backward compatible - in-process dump generation is done as before. If the pipe name is specified, out-of-process dump generation registration is attempted. If that fails, the behavior is again backward compatible. - If out-of-process registration succeeds, all write dump requests, direct or indirect, are directed to crash server process that served the registration request. NOTE that the explicit dump requests made by calling the static method of ExceptionHandler are not directed to theserver. - client/windows/tests/crash_generation_app implements a simple Win32 GUI application to help test the out-of-process dump generation client and server. Typical use of the app is to start one instance, click Server --> Start and then start the other instance. The other instance will register with the first instance automatically at start-up. Then the second instance can be used to request various typoes of dump requests by using options under the Client menu. git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@244 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
98cb0aebff
commit
c79141e306
27 changed files with 4222 additions and 173 deletions
|
@ -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
|
||||
|
|
63
src/client/windows/common/auto_critical_section.h
Normal file
63
src/client/windows/common/auto_critical_section.h
Normal file
|
@ -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 <Windows.h>
|
||||
|
||||
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__
|
122
src/client/windows/common/ipc_protocol.h
Normal file
122
src/client/windows/common/ipc_protocol.h
Normal file
|
@ -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 <Windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#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__
|
58
src/client/windows/crash_generation/ReadMe.txt
Normal file
58
src/client/windows/crash_generation/ReadMe.txt
Normal file
|
@ -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.
|
||||
=========================================================================
|
147
src/client/windows/crash_generation/client_info.cc
Normal file
147
src/client/windows/crash_generation/client_info.cc
Normal file
|
@ -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
|
145
src/client/windows/crash_generation/client_info.h
Normal file
145
src/client/windows/crash_generation/client_info.h
Normal file
|
@ -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 <Windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#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__
|
218
src/client/windows/crash_generation/crash_generation.vcproj
Normal file
218
src/client/windows/crash_generation/crash_generation.vcproj
Normal file
|
@ -0,0 +1,218 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="crash_generation"
|
||||
ProjectGUID="{A820AF62-6239-4693-8430-4F516C1838F4}"
|
||||
RootNamespace="CrashGenerationServer"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\.."
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0500"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="2"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\client_info.cc"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\crash_generation_client.cc"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\crash_generation_server.cc"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\common\windows\guid_string.cc"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\minidump_generator.cc"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\common\auto_critical_section.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\client_info.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\crash_generation_client.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\crash_generation_server.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\common\ipc_protocol.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\google_breakpad\common\minidump_format.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\minidump_generator.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath=".\ReadMe.txt"
|
||||
>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
329
src/client/windows/crash_generation/crash_generation_client.cc
Normal file
329
src/client/windows/crash_generation/crash_generation_client.cc
Normal file
|
@ -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 <cassert>
|
||||
#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
|
149
src/client/windows/crash_generation/crash_generation_client.h
Normal file
149
src/client/windows/crash_generation/crash_generation_client.h
Normal file
|
@ -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 <string>
|
||||
#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__
|
821
src/client/windows/crash_generation/crash_generation_server.cc
Normal file
821
src/client/windows/crash_generation/crash_generation_server.cc
Normal file
|
@ -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 <Windows.h>
|
||||
#include <cassert>
|
||||
#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<ClientInfo*>::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<ClientInfo*>::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<ClientInfo> 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<CrashGenerationServer*>(context);
|
||||
obj->HandleConnectionRequest();
|
||||
}
|
||||
|
||||
// static
|
||||
void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) {
|
||||
assert(context);
|
||||
ClientInfo* client_info = reinterpret_cast<ClientInfo*>(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<ClientInfo*>(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<ClientInfo*>(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
|
255
src/client/windows/crash_generation/crash_generation_server.h
Normal file
255
src/client/windows/crash_generation/crash_generation_server.h
Normal file
|
@ -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 <list>
|
||||
#include <string>
|
||||
#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<ClientInfo*> 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<MinidumpGenerator> 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__
|
228
src/client/windows/crash_generation/minidump_generator.cc
Normal file
228
src/client/windows/crash_generation/minidump_generator.cc
Normal file
|
@ -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 <cassert>
|
||||
#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<MiniDumpWriteDumpType>(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<UuidCreateType>(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
|
118
src/client/windows/crash_generation/minidump_generator.h
Normal file
118
src/client/windows/crash_generation/minidump_generator.h
Normal file
|
@ -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 <Windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <list>
|
||||
#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__
|
|
@ -41,80 +41,127 @@ namespace google_breakpad {
|
|||
|
||||
static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
|
||||
|
||||
vector<ExceptionHandler *> *ExceptionHandler::handler_stack_ = NULL;
|
||||
vector<ExceptionHandler*>* 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<CrashGenerationClient> 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<MiniDumpWriteDump_type>(
|
||||
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<UuidCreate_type>(
|
||||
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<MiniDumpWriteDump_type>(
|
||||
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<UuidCreate_type>(
|
||||
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<ExceptionHandler *>();
|
||||
handler_stack_ = new vector<ExceptionHandler*>();
|
||||
}
|
||||
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<ExceptionHandler *>::iterator iterator =
|
||||
for (vector<ExceptionHandler*>::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<ExceptionHandler *>(lpParameter);
|
||||
DWORD ExceptionHandler::ExceptionHandlerThreadMain(void* lpParameter) {
|
||||
ExceptionHandler* self = reinterpret_cast<ExceptionHandler *>(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<wchar_t *>(assertion.expression),
|
||||
_snwprintf_s(reinterpret_cast<wchar_t*>(assertion.expression),
|
||||
sizeof(assertion.expression) / sizeof(assertion.expression[0]),
|
||||
_TRUNCATE, L"%s", expression);
|
||||
_snwprintf_s(reinterpret_cast<wchar_t *>(assertion.function),
|
||||
_snwprintf_s(reinterpret_cast<wchar_t*>(assertion.function),
|
||||
sizeof(assertion.function) / sizeof(assertion.function[0]),
|
||||
_TRUNCATE, L"%s", function);
|
||||
_snwprintf_s(reinterpret_cast<wchar_t *>(assertion.file),
|
||||
_snwprintf_s(reinterpret_cast<wchar_t*>(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);
|
||||
}
|
||||
|
|
|
@ -68,7 +68,9 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<CrashGenerationClient> 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<ExceptionHandler *> *handler_stack_;
|
||||
static vector<ExceptionHandler*>* 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
|
||||
|
|
|
@ -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
|
|
@ -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__
|
|
@ -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<TCHAR*>(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;
|
||||
}
|
||||
|
|
@ -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__
|
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
|
@ -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
|
||||
|
|
@ -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
|
|
@ -0,0 +1,253 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="crash_generation_app"
|
||||
ProjectGUID="{A15674ED-713D-4B37-B1D2-0C29C7E533C8}"
|
||||
RootNamespace="CrashGenerationServerApp"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\..\..\"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="2"
|
||||
PrecompiledHeaderThrough="PreCompile.h"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="crash_generation.lib exception_handler.lib"
|
||||
LinkIncremental="2"
|
||||
AdditionalLibraryDirectories="..\..\$(ConfigurationName)"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="2"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\abstract_class.cc"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\crash_generation_app.cc"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\precompile.cc"
|
||||
>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\abstract_class.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\crash_generation_app.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\precompile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Resource.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\crash_generation_app.ico"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\crash_generation_app.rc"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\small.ico"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
37
src/client/windows/tests/crash_generation_app/precompile.cc
Normal file
37
src/client/windows/tests/crash_generation_app/precompile.cc
Normal file
|
@ -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.
|
89
src/client/windows/tests/crash_generation_app/precompile.h
Normal file
89
src/client/windows/tests/crash_generation_app/precompile.h
Normal file
|
@ -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 <windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <malloc.h>
|
||||
#include <memory.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <list>
|
||||
|
||||
#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__
|
73
src/client/windows/tests/crash_generation_app/resource.h
Normal file
73
src/client/windows/tests/crash_generation_app/resource.h
Normal file
|
@ -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__
|
BIN
src/client/windows/tests/crash_generation_app/small.ico
Normal file
BIN
src/client/windows/tests/crash_generation_app/small.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Loading…
Reference in a new issue