diff options
Diffstat (limited to 'src/client/windows/crash_generation')
6 files changed, 181 insertions, 2 deletions
diff --git a/src/client/windows/crash_generation/client_info.cc b/src/client/windows/crash_generation/client_info.cc index 94f9c3cd..12f4bb30 100644 --- a/src/client/windows/crash_generation/client_info.cc +++ b/src/client/windows/crash_generation/client_info.cc @@ -50,6 +50,8 @@ ClientInfo::ClientInfo(CrashGenerationServer* crash_server, thread_id_(thread_id), process_handle_(NULL), dump_requested_handle_(NULL), + parent_dump_requested_handle_(NULL), + parent_dump_request_wait_handle_(NULL), dump_generated_handle_(NULL), dump_request_wait_handle_(NULL), process_exit_wait_handle_(NULL) { @@ -70,6 +72,14 @@ bool ClientInfo::Initialize() { return false; } + parent_dump_requested_handle_ = CreateEvent(NULL, // Security attributes. + TRUE, // Manual reset. + FALSE, // Initial state. + NULL); // Name. + if (!parent_dump_requested_handle_) { + return false; + } + dump_generated_handle_ = CreateEvent(NULL, // Security attributes. TRUE, // Manual reset. FALSE, // Initial state. @@ -88,6 +98,10 @@ ClientInfo::~ClientInfo() { UnregisterWaitEx(process_exit_wait_handle_, INVALID_HANDLE_VALUE); } + if (parent_dump_request_wait_handle_) { + UnregisterWaitEx(parent_dump_request_wait_handle_, INVALID_HANDLE_VALUE); + } + if (process_handle_) { CloseHandle(process_handle_); } @@ -96,6 +110,10 @@ ClientInfo::~ClientInfo() { CloseHandle(dump_requested_handle_); } + if (parent_dump_requested_handle_) { + CloseHandle(parent_dump_requested_handle_); + } + if (dump_generated_handle_) { CloseHandle(dump_generated_handle_); } @@ -107,6 +125,11 @@ void ClientInfo::UnregisterWaits() { dump_request_wait_handle_ = NULL; } + if (parent_dump_request_wait_handle_) { + UnregisterWait(parent_dump_request_wait_handle_); + parent_dump_request_wait_handle_ = NULL; + } + if (process_exit_wait_handle_) { UnregisterWait(process_exit_wait_handle_); process_exit_wait_handle_ = NULL; diff --git a/src/client/windows/crash_generation/client_info.h b/src/client/windows/crash_generation/client_info.h index 47a5d21f..6a8e0b94 100644 --- a/src/client/windows/crash_generation/client_info.h +++ b/src/client/windows/crash_generation/client_info.h @@ -65,6 +65,7 @@ class ClientInfo { 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 parent_dump_requested_handle() const { return parent_dump_requested_handle_; } HANDLE dump_request_wait_handle() const { return dump_request_wait_handle_; @@ -82,6 +83,14 @@ class ClientInfo { process_exit_wait_handle_ = value; } + HANDLE parent_dump_request_wait_handle() const { + return parent_dump_request_wait_handle_; + } + + void set_parent_dump_request_wait_handle(HANDLE value) { + parent_dump_request_wait_handle_ = value; + } + // Unregister all waits for the client. void UnregisterWaits(); @@ -150,12 +159,18 @@ class ClientInfo { // Dump generated event handle. HANDLE dump_generated_handle_; + // Parent dump request event handle. + HANDLE parent_dump_requested_handle_; + // Wait handle for dump request event. HANDLE dump_request_wait_handle_; // Wait handle for process exit event. HANDLE process_exit_wait_handle_; + // Wait handle for parent dump request event. + HANDLE parent_dump_request_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_; diff --git a/src/client/windows/crash_generation/crash_generation_client.cc b/src/client/windows/crash_generation/crash_generation_client.cc index 5e4e3cb9..304c6061 100644 --- a/src/client/windows/crash_generation/crash_generation_client.cc +++ b/src/client/windows/crash_generation/crash_generation_client.cc @@ -194,6 +194,7 @@ bool CrashGenerationClient::RegisterClient(HANDLE pipe) { custom_info_, NULL, NULL, + NULL, NULL); ProtocolMessage reply; DWORD bytes_count = 0; @@ -220,8 +221,10 @@ bool CrashGenerationClient::RegisterClient(HANDLE pipe) { 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; + parent_dump_request_event_ = reply.parent_dump_request_handle; server_alive_ = reply.server_alive_handle; server_process_id_ = reply.pid; @@ -271,6 +274,39 @@ bool CrashGenerationClient::IsRegistered() const { return crash_event_ != NULL; } +bool CrashGenerationClient::RequestParentDump() { + if (!IsRegistered()) { + return false; + } + + assert(parent_dump_request_event_); + 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; + } + + // Signal we want a server side crash dump generated + if (!SetEvent(parent_dump_request_event_)) { + return false; + } + + // Wait for the crash dump process to complete + 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; +} + bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info, MDRawAssertionInfo* assert_info) { if (!IsRegistered()) { diff --git a/src/client/windows/crash_generation/crash_generation_client.h b/src/client/windows/crash_generation/crash_generation_client.h index 01d13dde..e026599e 100644 --- a/src/client/windows/crash_generation/crash_generation_client.h +++ b/src/client/windows/crash_generation/crash_generation_client.h @@ -91,6 +91,15 @@ class CrashGenerationClient { // if the registration step was not performed or it was not successful, // false will be returned. bool RequestDump(MDRawAssertionInfo* assert_info); + + // Requests the crash server generate a minidump on the parent side + // of the connection. Blocks until the dump has been completed or + // the request times out. + // + // 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 RequestParentDump(); private: // Connects to the appropriate pipe and sets the pipe handle state. @@ -132,6 +141,10 @@ class CrashGenerationClient { // Event to signal in case of a crash. HANDLE crash_event_; + // Event signaling a server side minidump is being requested + // by the client. + HANDLE parent_dump_request_event_; + // Handle to wait on after signaling a crash for the server // to finish generating crash dump. HANDLE crash_generated_; diff --git a/src/client/windows/crash_generation/crash_generation_server.cc b/src/client/windows/crash_generation/crash_generation_server.cc index 61af1b2d..369768df 100644 --- a/src/client/windows/crash_generation/crash_generation_server.cc +++ b/src/client/windows/crash_generation/crash_generation_server.cc @@ -119,7 +119,8 @@ CrashGenerationServer::CrashGenerationServer( shutting_down_(false), overlapped_(), client_info_(NULL), - cleanup_item_count_(0) { + cleanup_item_count_(0), + preferred_parent_thread_id_(0) { InitializeCriticalSection(&clients_sync_); if (dump_path) { @@ -600,6 +601,9 @@ bool CrashGenerationServer::PrepareReply(const ClientInfo& client_info, CloseHandle(reply->server_alive_handle); } + if (reply->parent_dump_request_handle) { + CloseHandle(reply->parent_dump_request_handle); + } return false; } @@ -627,6 +631,16 @@ bool CrashGenerationServer::CreateClientHandles(const ClientInfo& client_info, } if (!DuplicateHandle(current_process, + client_info.parent_dump_requested_handle(), + client_info.process_handle(), + &reply->parent_dump_request_handle, + kDumpRequestEventAccess, + FALSE, + 0)) { + return false; + } + + if (!DuplicateHandle(current_process, server_alive_handle_, client_info.process_handle(), &reply->server_alive_handle, @@ -754,6 +768,20 @@ bool CrashGenerationServer::AddClient(ClientInfo* client_info) { client_info->set_process_exit_wait_handle(process_wait_handle); + // OnParentDumpRequest will be called if the client requests + // a server side minidump be generated. + HANDLE parent_request_wait_handle = NULL; + if (!RegisterWaitForSingleObject(&parent_request_wait_handle, + client_info->parent_dump_requested_handle(), + OnParentDumpRequest, + client_info, + INFINITE, + kDumpRequestThreadFlags)) { + return false; + } + + client_info->set_parent_dump_request_wait_handle(parent_request_wait_handle); + // New scope to hold the lock for the shortest time. { AutoCriticalSection lock(&clients_sync_); @@ -786,6 +814,20 @@ void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) { } // static +void CALLBACK CrashGenerationServer::OnParentDumpRequest(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->HandleParentDumpRequest(*client_info); + + ResetEvent(client_info->parent_dump_requested_handle()); +} + +// static void CALLBACK CrashGenerationServer::OnClientEnd(void* context, BOOLEAN) { assert(context); ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context); @@ -843,7 +885,34 @@ void CrashGenerationServer::HandleDumpRequest(const ClientInfo& client_info) { if (dump_callback_) { std::wstring* ptr_dump_path = (dump_path == L"") ? NULL : &dump_path; - dump_callback_(dump_context_, &client_info, ptr_dump_path); + dump_callback_(dump_context_, &client_info, DUMP_REQ_CHILD, ptr_dump_path); + } + + SetEvent(client_info.dump_generated_handle()); +} + +void CrashGenerationServer::HandleParentDumpRequest(const ClientInfo& client_info) { + std::wstring dump_path; + if (generate_dumps_) { + DWORD preferred_thread_id = !preferred_parent_thread_id_ ? + GetCurrentThreadId() : preferred_parent_thread_id_; + bool success = + dump_generator_->WriteMinidump(GetCurrentProcess(), + GetCurrentProcessId(), + preferred_thread_id, + GetCurrentThreadId(), + NULL, // no exception + NULL, // no assert info + MiniDumpNormal, + true, + &dump_path); + if (!success) + return; + } + + if (dump_callback_) { + std::wstring* ptr_dump_path = (dump_path == L"") ? NULL : &dump_path; + dump_callback_(dump_context_, &client_info, DUMP_REQ_PARENT, ptr_dump_path); } SetEvent(client_info.dump_generated_handle()); diff --git a/src/client/windows/crash_generation/crash_generation_server.h b/src/client/windows/crash_generation/crash_generation_server.h index 31a353bf..c31f91cd 100644 --- a/src/client/windows/crash_generation/crash_generation_server.h +++ b/src/client/windows/crash_generation/crash_generation_server.h @@ -49,11 +49,19 @@ class ClientInfo; // generation in this way, the server generates Windows minidump files. class CrashGenerationServer { public: + // For client minidump request callbacks indicates the type of minidump + // the request generated. + enum ClientDumpRequestType { + DUMP_REQ_PARENT, + DUMP_REQ_CHILD, + }; + typedef void (*OnClientConnectedCallback)(void* context, const ClientInfo* client_info); typedef void (*OnClientDumpRequestCallback)(void* context, const ClientInfo* client_info, + const ClientDumpRequestType request_type, const std::wstring* file_path); typedef void (*OnClientExitedCallback)(void* context, @@ -97,6 +105,11 @@ class CrashGenerationServer { // Returns true if initialization is successful; false otherwise. bool Start(); + // Sets the preferred parent side minidump thread id passed + // to breakpad when a server side minidump is requested by + // a client. + void SetParentDumpThreadId(DWORD thread_id) { preferred_parent_thread_id_ = thread_id; } + private: // Various states the client can be in during the handshake with // the server. @@ -175,12 +188,18 @@ class CrashGenerationServer { // Handles a dump request from the client. void HandleDumpRequest(const ClientInfo& client_info); + // Handles a server side minidump request from the client. + void HandleParentDumpRequest(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 a server minidump request. + static void CALLBACK OnParentDumpRequest(void* context, BOOLEAN timer_or_wait); + // Callback for client process exit event. static void CALLBACK OnClientEnd(void* context, BOOLEAN timer_or_wait); @@ -278,6 +297,10 @@ class CrashGenerationServer { // already queued to run. volatile LONG cleanup_item_count_; + // For client requested server side minidumps, the preferred + // minidump thread id passed to breakpad. + DWORD preferred_parent_thread_id_; + // Disable copy ctor and operator=. CrashGenerationServer(const CrashGenerationServer& crash_server); CrashGenerationServer& operator=(const CrashGenerationServer& crash_server); |