mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-30 01:00:13 +02:00
* Added breakpad support for Linux.
This commit is contained in:
58
thirdparty/breakpad/client/windows/crash_generation/ReadMe.txt
vendored
Normal file
58
thirdparty/breakpad/client/windows/crash_generation/ReadMe.txt
vendored
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.
|
||||
=========================================================================
|
200
thirdparty/breakpad/client/windows/crash_generation/client_info.cc
vendored
Normal file
200
thirdparty/breakpad/client/windows/crash_generation/client_info.cc
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
// 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"
|
||||
#include "client/windows/common/ipc_protocol.h"
|
||||
|
||||
static const wchar_t kCustomInfoProcessUptimeName[] = L"ptime";
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
ClientInfo::ClientInfo(CrashGenerationServer* crash_server,
|
||||
DWORD pid,
|
||||
MINIDUMP_TYPE dump_type,
|
||||
DWORD* thread_id,
|
||||
EXCEPTION_POINTERS** ex_info,
|
||||
MDRawAssertionInfo* assert_info,
|
||||
const CustomClientInfo& custom_client_info)
|
||||
: crash_server_(crash_server),
|
||||
pid_(pid),
|
||||
dump_type_(dump_type),
|
||||
ex_info_(ex_info),
|
||||
assert_info_(assert_info),
|
||||
custom_client_info_(custom_client_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) {
|
||||
GetSystemTimeAsFileTime(&start_time_);
|
||||
}
|
||||
|
||||
bool ClientInfo::Initialize() {
|
||||
process_handle_ = OpenProcess(GENERIC_ALL, 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 (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);
|
||||
}
|
||||
|
||||
if (process_handle_) {
|
||||
CloseHandle(process_handle_);
|
||||
}
|
||||
|
||||
if (dump_requested_handle_) {
|
||||
CloseHandle(dump_requested_handle_);
|
||||
}
|
||||
|
||||
if (dump_generated_handle_) {
|
||||
CloseHandle(dump_generated_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientInfo::UnregisterWaits() {
|
||||
if (dump_request_wait_handle_) {
|
||||
UnregisterWait(dump_request_wait_handle_);
|
||||
dump_request_wait_handle_ = NULL;
|
||||
}
|
||||
|
||||
if (process_exit_wait_handle_) {
|
||||
UnregisterWait(process_exit_wait_handle_);
|
||||
process_exit_wait_handle_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void ClientInfo::SetProcessUptime() {
|
||||
FILETIME now = {0};
|
||||
GetSystemTimeAsFileTime(&now);
|
||||
|
||||
ULARGE_INTEGER time_start;
|
||||
time_start.HighPart = start_time_.dwHighDateTime;
|
||||
time_start.LowPart = start_time_.dwLowDateTime;
|
||||
|
||||
ULARGE_INTEGER time_now;
|
||||
time_now.HighPart = now.dwHighDateTime;
|
||||
time_now.LowPart = now.dwLowDateTime;
|
||||
|
||||
// Calculate the delay and convert it from 100-nanoseconds to milliseconds.
|
||||
__int64 delay = (time_now.QuadPart - time_start.QuadPart) / 10 / 1000;
|
||||
|
||||
// Convert it to a string.
|
||||
wchar_t* value = custom_info_entries_.get()[custom_client_info_.count].value;
|
||||
_i64tow_s(delay, value, CustomInfoEntry::kValueMaxLength, 10);
|
||||
}
|
||||
|
||||
bool ClientInfo::PopulateCustomInfo() {
|
||||
SIZE_T bytes_count = 0;
|
||||
SIZE_T read_count = sizeof(CustomInfoEntry) * custom_client_info_.count;
|
||||
|
||||
// If the scoped array for custom info already has an array, it will be
|
||||
// the same size as what we need. This is because the number of custom info
|
||||
// entries is always the same. So allocate memory only if scoped array has
|
||||
// a NULL pointer.
|
||||
if (!custom_info_entries_.get()) {
|
||||
// Allocate an extra entry for reporting uptime for the client process.
|
||||
custom_info_entries_.reset(
|
||||
new CustomInfoEntry[custom_client_info_.count + 1]);
|
||||
// Use the last element in the array for uptime.
|
||||
custom_info_entries_.get()[custom_client_info_.count].set_name(
|
||||
kCustomInfoProcessUptimeName);
|
||||
}
|
||||
|
||||
if (!ReadProcessMemory(process_handle_,
|
||||
custom_client_info_.entries,
|
||||
custom_info_entries_.get(),
|
||||
read_count,
|
||||
&bytes_count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SetProcessUptime();
|
||||
return (bytes_count != read_count);
|
||||
}
|
||||
|
||||
CustomClientInfo ClientInfo::GetCustomInfo() const {
|
||||
CustomClientInfo custom_info;
|
||||
custom_info.entries = custom_info_entries_.get();
|
||||
// Add 1 to the count from the client process to account for extra entry for
|
||||
// process uptime.
|
||||
custom_info.count = custom_client_info_.count + 1;
|
||||
return custom_info;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
170
thirdparty/breakpad/client/windows/crash_generation/client_info.h
vendored
Normal file
170
thirdparty/breakpad/client/windows/crash_generation/client_info.h
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
// 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 "client/windows/common/ipc_protocol.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "processor/scoped_ptr.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,
|
||||
const CustomClientInfo& custom_client_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.
|
||||
void UnregisterWaits();
|
||||
|
||||
bool Initialize();
|
||||
bool GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const;
|
||||
bool GetClientThreadId(DWORD* thread_id) const;
|
||||
|
||||
// Reads the custom information from the client process address space.
|
||||
bool PopulateCustomInfo();
|
||||
|
||||
// Returns the client custom information.
|
||||
CustomClientInfo GetCustomInfo() const;
|
||||
|
||||
private:
|
||||
// Calcualtes the uptime for the client process, converts it to a string and
|
||||
// stores it in the last entry of client custom info.
|
||||
void SetProcessUptime();
|
||||
|
||||
// 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_;
|
||||
|
||||
// Custom information about the client.
|
||||
CustomClientInfo custom_client_info_;
|
||||
|
||||
// Contains the custom client info entries read from the client process
|
||||
// memory. This will be populated only if the method GetClientCustomInfo
|
||||
// is called.
|
||||
scoped_array<CustomInfoEntry> custom_info_entries_;
|
||||
|
||||
// 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_;
|
||||
|
||||
// Time when the client process started. It is used to determine the uptime
|
||||
// for the client process when it signals a crash.
|
||||
FILETIME start_time_;
|
||||
|
||||
// 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__
|
64
thirdparty/breakpad/client/windows/crash_generation/crash_generation.gyp
vendored
Executable file
64
thirdparty/breakpad/client/windows/crash_generation/crash_generation.gyp
vendored
Executable file
@@ -0,0 +1,64 @@
|
||||
# Copyright (c) 2010, 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.
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../build/common.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'crash_generation_server',
|
||||
'type': '<(library)',
|
||||
'sources': [
|
||||
'client_info.cc',
|
||||
'crash_generation_server.cc',
|
||||
'minidump_generator.cc',
|
||||
'client_info.h',
|
||||
'crash_generation_client.h',
|
||||
'crash_generation_server.h',
|
||||
'minidump_generator.h',
|
||||
],
|
||||
'dependencies': [
|
||||
'../breakpad_client.gyp:common'
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'crash_generation_client',
|
||||
'type': '<(library)',
|
||||
'include_dirs': [
|
||||
'<(DEPTH)',
|
||||
],
|
||||
'sources': [
|
||||
'crash_generation_client.h',
|
||||
'crash_generation_client.cc',
|
||||
'crash_generation_server.h',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
328
thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.cc
vendored
Normal file
328
thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.cc
vendored
Normal file
@@ -0,0 +1,328 @@
|
||||
// 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 <utility>
|
||||
#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,
|
||||
const CustomClientInfo* custom_info)
|
||||
: 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),
|
||||
custom_info_() {
|
||||
memset(&assert_info_, 0, sizeof(assert_info_));
|
||||
if (custom_info) {
|
||||
custom_info_ = *custom_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_,
|
||||
custom_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,
|
||||
MDRawAssertionInfo* assert_info) {
|
||||
if (!IsRegistered()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
exception_pointers_ = ex_info;
|
||||
thread_id_ = GetCurrentThreadId();
|
||||
|
||||
if (assert_info) {
|
||||
memcpy(&assert_info_, assert_info, sizeof(assert_info_));
|
||||
} else {
|
||||
memset(&assert_info_, 0, sizeof(assert_info_));
|
||||
}
|
||||
|
||||
return SignalCrashEventAndWait();
|
||||
}
|
||||
|
||||
bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
|
||||
return RequestDump(ex_info, NULL);
|
||||
}
|
||||
|
||||
bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
|
||||
return RequestDump(NULL, assert_info);
|
||||
}
|
||||
|
||||
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
|
162
thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.h
vendored
Normal file
162
thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.h
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
// 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 <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "client/windows/common/ipc_protocol.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
struct CustomClientInfo;
|
||||
|
||||
// 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,
|
||||
const CustomClientInfo* custom_info);
|
||||
|
||||
~CrashGenerationClient();
|
||||
|
||||
// Registers the client process with the crash server.
|
||||
//
|
||||
// Returns true if the registration is successful; false otherwise.
|
||||
bool Register();
|
||||
|
||||
bool RequestDump(EXCEPTION_POINTERS* ex_info,
|
||||
MDRawAssertionInfo* assert_info);
|
||||
|
||||
// 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_;
|
||||
|
||||
// Custom client information
|
||||
CustomClientInfo custom_info_;
|
||||
|
||||
// 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_
|
880
thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.cc
vendored
Normal file
880
thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.cc
vendored
Normal file
@@ -0,0 +1,880 @@
|
||||
// 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 <list>
|
||||
#include "client/windows/common/auto_critical_section.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
#include "client/windows/crash_generation/client_info.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Output buffer size.
|
||||
static const size_t kOutBufferSize = 64;
|
||||
|
||||
// Input buffer size.
|
||||
static const size_t kInBufferSize = 64;
|
||||
|
||||
// Access flags for the client on the dump request event.
|
||||
static const DWORD kDumpRequestEventAccess = EVENT_MODIFY_STATE;
|
||||
|
||||
// Access flags for the client on the dump generated event.
|
||||
static const DWORD kDumpGeneratedEventAccess = EVENT_MODIFY_STATE |
|
||||
SYNCHRONIZE;
|
||||
|
||||
// Access flags for the client on the mutex.
|
||||
static const DWORD kMutexAccess = SYNCHRONIZE;
|
||||
|
||||
// Attribute flags for the pipe.
|
||||
static const DWORD kPipeAttr = FILE_FLAG_FIRST_PIPE_INSTANCE |
|
||||
PIPE_ACCESS_DUPLEX |
|
||||
FILE_FLAG_OVERLAPPED;
|
||||
|
||||
// Mode for the pipe.
|
||||
static 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.
|
||||
static 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.
|
||||
static const ULONG kDumpRequestThreadFlags = WT_EXECUTEINWAITTHREAD |
|
||||
WT_EXECUTELONGFUNCTION;
|
||||
|
||||
// Maximum delay during server shutdown if some work items
|
||||
// are still executing.
|
||||
static const int kShutdownDelayMs = 10000;
|
||||
|
||||
// Interval for each sleep during server shutdown.
|
||||
static const int kShutdownSleepIntervalMs = 5;
|
||||
|
||||
static bool IsClientRequestValid(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 std::wstring& pipe_name,
|
||||
SECURITY_ATTRIBUTES* pipe_sec_attrs,
|
||||
OnClientConnectedCallback connect_callback,
|
||||
void* connect_context,
|
||||
OnClientDumpRequestCallback dump_callback,
|
||||
void* dump_context,
|
||||
OnClientExitedCallback exit_callback,
|
||||
void* exit_context,
|
||||
bool generate_dumps,
|
||||
const std::wstring* dump_path)
|
||||
: pipe_name_(pipe_name),
|
||||
pipe_sec_attrs_(pipe_sec_attrs),
|
||||
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_UNINITIALIZED),
|
||||
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;
|
||||
|
||||
// Even if there are no current worker threads running, it is possible that
|
||||
// an I/O request is pending on the pipe right now but not yet done. In fact,
|
||||
// it's very likely this is the case unless we are in an ERROR state. If we
|
||||
// don't wait for the pending I/O to be done, then when the I/O completes,
|
||||
// it may write to invalid memory. AppVerifier will flag this problem too.
|
||||
// So we disconnect from the pipe and then wait for the server to get into
|
||||
// error state so that the pending I/O will fail and get cleared.
|
||||
DisconnectNamedPipe(pipe_);
|
||||
int num_tries = 100;
|
||||
while (num_tries-- && server_state_ != IPC_SERVER_STATE_ERROR) {
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
// 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_);
|
||||
}
|
||||
|
||||
if (overlapped_.hEvent) {
|
||||
CloseHandle(overlapped_.hEvent);
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&clients_sync_);
|
||||
}
|
||||
|
||||
bool CrashGenerationServer::Start() {
|
||||
if (server_state_ != IPC_SERVER_STATE_UNINITIALIZED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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.
|
||||
if (!RegisterWaitForSingleObject(&pipe_wait_handle_,
|
||||
overlapped_.hEvent,
|
||||
OnPipeConnected,
|
||||
this,
|
||||
INFINITE,
|
||||
kPipeIOThreadFlags)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pipe_ = CreateNamedPipe(pipe_name_.c_str(),
|
||||
kPipeAttr,
|
||||
kPipeMode,
|
||||
1,
|
||||
kOutBufferSize,
|
||||
kInBufferSize,
|
||||
0,
|
||||
pipe_sec_attrs_);
|
||||
if (pipe_ == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Kick-start the state machine. This will initiate an asynchronous wait
|
||||
// for client connections.
|
||||
HandleInitialState();
|
||||
|
||||
// If we are in error state, it's because we failed to start listening.
|
||||
return server_state_ != IPC_SERVER_STATE_ERROR;
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
EnterErrorState();
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = ConnectNamedPipe(pipe_, &overlapped_) != FALSE;
|
||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
||||
|
||||
// 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);
|
||||
|
||||
switch (error_code) {
|
||||
case ERROR_IO_PENDING:
|
||||
EnterStateWhenSignaled(IPC_SERVER_STATE_CONNECTING);
|
||||
break;
|
||||
|
||||
case ERROR_PIPE_CONNECTED:
|
||||
EnterStateImmediately(IPC_SERVER_STATE_CONNECTED);
|
||||
break;
|
||||
|
||||
default:
|
||||
EnterErrorState();
|
||||
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;
|
||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
||||
|
||||
if (success) {
|
||||
EnterStateImmediately(IPC_SERVER_STATE_CONNECTED);
|
||||
} else if (error_code != ERROR_IO_INCOMPLETE) {
|
||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
||||
} else {
|
||||
// remain in CONNECTING state
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
||||
|
||||
// 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 || error_code == ERROR_IO_PENDING) {
|
||||
EnterStateWhenSignaled(IPC_SERVER_STATE_READING);
|
||||
} else {
|
||||
EnterStateImmediately(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;
|
||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
||||
|
||||
if (success && bytes_count == sizeof(ProtocolMessage)) {
|
||||
EnterStateImmediately(IPC_SERVER_STATE_READ_DONE);
|
||||
} else {
|
||||
// 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);
|
||||
|
||||
EnterStateImmediately(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 (!IsClientRequestValid(msg_)) {
|
||||
EnterStateImmediately(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,
|
||||
msg_.custom_client_info));
|
||||
|
||||
if (!client_info->Initialize()) {
|
||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
||||
return;
|
||||
}
|
||||
|
||||
// Issues an asynchronous WriteFile call if successful.
|
||||
// Iff successful, assigns ownership of the client_info pointer to the server
|
||||
// instance, in which case we must be sure not to free it in this function.
|
||||
if (!RespondToClient(client_info.get())) {
|
||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
||||
return;
|
||||
}
|
||||
|
||||
client_info_ = client_info.release();
|
||||
|
||||
// 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.
|
||||
EnterStateWhenSignaled(IPC_SERVER_STATE_WRITING);
|
||||
}
|
||||
|
||||
// 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;
|
||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
||||
|
||||
if (success) {
|
||||
EnterStateImmediately(IPC_SERVER_STATE_WRITE_DONE);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
EnterStateImmediately(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);
|
||||
|
||||
DWORD bytes_count = 0;
|
||||
bool success = ReadFile(pipe_,
|
||||
&msg_,
|
||||
sizeof(msg_),
|
||||
&bytes_count,
|
||||
&overlapped_) != FALSE;
|
||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
||||
|
||||
if (success) {
|
||||
EnterStateImmediately(IPC_SERVER_STATE_READING_ACK);
|
||||
} else if (error_code == ERROR_IO_PENDING) {
|
||||
EnterStateWhenSignaled(IPC_SERVER_STATE_READING_ACK);
|
||||
} else {
|
||||
EnterStateImmediately(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;
|
||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
||||
|
||||
if (success) {
|
||||
// The connection handshake with the client is now complete; perform
|
||||
// the callback.
|
||||
if (connect_callback_) {
|
||||
connect_callback_(connect_context_, client_info_);
|
||||
}
|
||||
} else {
|
||||
// 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);
|
||||
}
|
||||
|
||||
EnterStateImmediately(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)) {
|
||||
EnterErrorState();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DisconnectNamedPipe(pipe_)) {
|
||||
EnterErrorState();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the server is shutting down do not connect to the
|
||||
// next client.
|
||||
if (shutting_down_) {
|
||||
return;
|
||||
}
|
||||
|
||||
EnterStateImmediately(IPC_SERVER_STATE_INITIAL);
|
||||
}
|
||||
|
||||
void CrashGenerationServer::EnterErrorState() {
|
||||
SetEvent(overlapped_.hEvent);
|
||||
server_state_ = IPC_SERVER_STATE_ERROR;
|
||||
}
|
||||
|
||||
void CrashGenerationServer::EnterStateWhenSignaled(IPCServerState state) {
|
||||
server_state_ = state;
|
||||
}
|
||||
|
||||
void CrashGenerationServer::EnterStateImmediately(IPCServerState state) {
|
||||
server_state_ = state;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
DWORD bytes_count = 0;
|
||||
bool success = WriteFile(pipe_,
|
||||
&reply,
|
||||
sizeof(reply),
|
||||
&bytes_count,
|
||||
&overlapped_) != FALSE;
|
||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
||||
|
||||
if (!success && error_code != ERROR_IO_PENDING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Takes over ownership of client_info. We MUST return true if AddClient
|
||||
// succeeds.
|
||||
if (!AddClient(client_info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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() {
|
||||
// If we are shutting doen then get into ERROR state, reset the event so more
|
||||
// workers don't run and return immediately.
|
||||
if (shutting_down_) {
|
||||
server_state_ = IPC_SERVER_STATE_ERROR;
|
||||
ResetEvent(overlapped_.hEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
client_info->PopulateCustomInfo();
|
||||
|
||||
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.
|
||||
std::wstring dump_path;
|
||||
if (generate_dumps_) {
|
||||
if (!GenerateDump(client_info, &dump_path)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dump_callback_) {
|
||||
std::wstring* ptr_dump_path = (dump_path == L"") ? NULL : &dump_path;
|
||||
dump_callback_(dump_context_, &client_info, ptr_dump_path);
|
||||
}
|
||||
|
||||
SetEvent(client_info.dump_generated_handle());
|
||||
}
|
||||
|
||||
bool CrashGenerationServer::GenerateDump(const ClientInfo& client,
|
||||
std::wstring* dump_path) {
|
||||
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,
|
||||
dump_path);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
288
thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.h
vendored
Normal file
288
thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.h
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
// 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/minidump_generator.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
class ClientInfo;
|
||||
|
||||
// Abstraction for server side implementation of out-of-process crash
|
||||
// generation protocol for Windows platform only. It generates Windows
|
||||
// minidump files for client processes that request dump generation. When
|
||||
// the server is requested to start listening for clients (by calling the
|
||||
// Start method), it creates a named pipe and waits for the clients to
|
||||
// register. In response, it hands them event handles that the client can
|
||||
// signal to request dump generation. When the clients request dump
|
||||
// generation in this way, the server generates Windows minidump files.
|
||||
class CrashGenerationServer {
|
||||
public:
|
||||
typedef void (*OnClientConnectedCallback)(void* context,
|
||||
const ClientInfo* client_info);
|
||||
|
||||
typedef void (*OnClientDumpRequestCallback)(void* context,
|
||||
const ClientInfo* client_info,
|
||||
const std::wstring* file_path);
|
||||
|
||||
typedef void (*OnClientExitedCallback)(void* context,
|
||||
const ClientInfo* client_info);
|
||||
|
||||
// Creates an instance with the given parameters.
|
||||
//
|
||||
// Parameter pipe_name: Name of the Windows named pipe
|
||||
// Parameter pipe_sec_attrs Security attributes to set on the pipe. Pass
|
||||
// NULL to use default security on the pipe. By default, the pipe created
|
||||
// allows Local System, Administrators and the Creator full control and
|
||||
// the Everyone group read access on 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.
|
||||
// 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 std::wstring& pipe_name,
|
||||
SECURITY_ATTRIBUTES* pipe_sec_attrs,
|
||||
OnClientConnectedCallback connect_callback,
|
||||
void* connect_context,
|
||||
OnClientDumpRequestCallback dump_callback,
|
||||
void* dump_context,
|
||||
OnClientExitedCallback exit_callback,
|
||||
void* exit_context,
|
||||
bool generate_dumps,
|
||||
const std::wstring* dump_path);
|
||||
|
||||
~CrashGenerationServer();
|
||||
|
||||
// Performs initialization steps needed to start listening to clients. Upon
|
||||
// successful return clients may connect to this server's pipe.
|
||||
//
|
||||
// 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 starts in this state.
|
||||
IPC_SERVER_STATE_UNINITIALIZED,
|
||||
|
||||
// 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, std::wstring* dump_path);
|
||||
|
||||
// Puts the server in a permanent error state and sets a signal such that
|
||||
// the state will be immediately entered after the current state transition
|
||||
// is complete.
|
||||
void EnterErrorState();
|
||||
|
||||
// Puts the server in the specified state and sets a signal such that the
|
||||
// state is immediately entered after the current state transition is
|
||||
// complete.
|
||||
void EnterStateImmediately(IPCServerState state);
|
||||
|
||||
// Puts the server in the specified state. No signal will be set, so the state
|
||||
// transition will only occur when signaled manually or by completion of an
|
||||
// asynchronous IO operation.
|
||||
void EnterStateWhenSignaled(IPCServerState state);
|
||||
|
||||
// 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_;
|
||||
|
||||
// Pipe security attributes
|
||||
SECURITY_ATTRIBUTES* pipe_sec_attrs_;
|
||||
|
||||
// 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.
|
||||
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.
|
||||
volatile 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__
|
309
thirdparty/breakpad/client/windows/crash_generation/minidump_generator.cc
vendored
Normal file
309
thirdparty/breakpad/client/windows/crash_generation/minidump_generator.cc
vendored
Normal file
@@ -0,0 +1,309 @@
|
||||
// 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,
|
||||
wstring* dump_path) {
|
||||
// Just call the full WriteMinidump with NULL as the full_dump_path.
|
||||
return this->WriteMinidump(process_handle, process_id, thread_id,
|
||||
requesting_thread_id, exception_pointers,
|
||||
assert_info, dump_type, is_client_pointers,
|
||||
dump_path, NULL);
|
||||
}
|
||||
|
||||
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,
|
||||
wstring* dump_path,
|
||||
wstring* full_dump_path) {
|
||||
MiniDumpWriteDumpType write_dump = GetWriteDump();
|
||||
if (!write_dump) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring dump_file_path;
|
||||
if (!GenerateDumpFilePath(&dump_file_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the client requests a full memory dump, we will write a normal mini
|
||||
// dump and a full memory dump. Both dump files use the same uuid as file
|
||||
// name prefix.
|
||||
bool full_memory_dump = (dump_type & MiniDumpWithFullMemory) != 0;
|
||||
wstring full_dump_file_path;
|
||||
if (full_memory_dump) {
|
||||
full_dump_file_path.assign(dump_file_path);
|
||||
full_dump_file_path.resize(full_dump_file_path.size() - 4); // strip .dmp
|
||||
full_dump_file_path.append(TEXT("-full.dmp"));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
HANDLE full_dump_file = INVALID_HANDLE_VALUE;
|
||||
if (full_memory_dump) {
|
||||
full_dump_file = CreateFile(full_dump_file_path.c_str(),
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
if (full_dump_file == INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(dump_file);
|
||||
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 = {0};
|
||||
if (!is_client_pointers) {
|
||||
// Set the dump thread id and requesting thread id only in case of
|
||||
// in-process dump generation.
|
||||
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);
|
||||
if (full_dump_file != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(full_dump_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytes_read != sizeof(client_assert_info)) {
|
||||
CloseHandle(dump_file);
|
||||
if (full_dump_file != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(full_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_minidump = write_dump(
|
||||
process_handle,
|
||||
process_id,
|
||||
dump_file,
|
||||
static_cast<MINIDUMP_TYPE>((dump_type & (~MiniDumpWithFullMemory))
|
||||
| MiniDumpNormal),
|
||||
exception_pointers ? &dump_exception_info : NULL,
|
||||
&user_streams,
|
||||
NULL) != FALSE;
|
||||
|
||||
bool result_full_memory = true;
|
||||
if (full_memory_dump) {
|
||||
result_full_memory = write_dump(
|
||||
process_handle,
|
||||
process_id,
|
||||
full_dump_file,
|
||||
static_cast<MINIDUMP_TYPE>(dump_type & (~MiniDumpNormal)),
|
||||
exception_pointers ? &dump_exception_info : NULL,
|
||||
&user_streams,
|
||||
NULL) != FALSE;
|
||||
}
|
||||
|
||||
bool result = result_minidump && result_full_memory;
|
||||
|
||||
CloseHandle(dump_file);
|
||||
if (full_dump_file != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(full_dump_file);
|
||||
|
||||
// Store the path of the dump file in the out parameter if dump generation
|
||||
// succeeded.
|
||||
if (result && dump_path) {
|
||||
*dump_path = dump_file_path;
|
||||
}
|
||||
if (result && full_memory_dump && full_dump_path) {
|
||||
*full_dump_path = full_dump_file_path;
|
||||
}
|
||||
|
||||
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
|
135
thirdparty/breakpad/client/windows/crash_generation/minidump_generator.h
vendored
Normal file
135
thirdparty/breakpad/client/windows/crash_generation/minidump_generator.h
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
// 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_GENERATOR_H_
|
||||
#define CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_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. Stores the
|
||||
// dump file path in the dump_path parameter if dump generation
|
||||
// succeeds.
|
||||
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,
|
||||
std::wstring* dump_path);
|
||||
|
||||
// Writes the minidump with the given parameters. Stores the dump file
|
||||
// path in the dump_path (and full_dump_path) parameter if dump
|
||||
// generation succeeds. full_dump_path and dump_path can be NULL.
|
||||
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,
|
||||
std::wstring* dump_path,
|
||||
std::wstring* full_dump_path);
|
||||
|
||||
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_GENERATOR_H_
|
Reference in New Issue
Block a user