mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2026-01-02 12:44:38 +01:00
Fix CrashGenerationServer to recover from protocol errors and a test for same.
R=siggi at http://breakpad.appspot.com/196001/show git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@695 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
378e28e301
commit
b6ee7dcb22
6 changed files with 442 additions and 77 deletions
298
src/client/windows/unittests/crash_generation_server_test.cc
Normal file
298
src/client/windows/unittests/crash_generation_server_test.cc
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
// Copyright 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.
|
||||
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/include/gmock/gmock.h"
|
||||
|
||||
#include "client/windows/crash_generation/crash_generation_server.h"
|
||||
#include "client/windows/common/ipc_protocol.h"
|
||||
|
||||
using testing::_;
|
||||
|
||||
namespace {
|
||||
|
||||
const wchar_t kPipeName[] =
|
||||
L"\\\\.\\pipe\\CrashGenerationServerTest\\TestCaseServer";
|
||||
|
||||
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;
|
||||
|
||||
int kCustomInfoCount = 2;
|
||||
|
||||
google_breakpad::CustomInfoEntry kCustomInfoEntries[] = {
|
||||
google_breakpad::CustomInfoEntry(L"prod", L"CrashGenerationServerTest"),
|
||||
google_breakpad::CustomInfoEntry(L"ver", L"1.0"),
|
||||
};
|
||||
|
||||
class CrashGenerationServerTest : public ::testing::Test {
|
||||
public:
|
||||
CrashGenerationServerTest()
|
||||
: crash_generation_server_(kPipeName,
|
||||
NULL,
|
||||
CallOnClientConnected, &mock_callbacks_,
|
||||
CallOnClientDumpRequested, &mock_callbacks_,
|
||||
CallOnClientExited, &mock_callbacks_,
|
||||
false,
|
||||
NULL),
|
||||
thread_id_(0),
|
||||
exception_pointers_(NULL) {
|
||||
memset(&assert_info_, 0, sizeof(assert_info_));
|
||||
}
|
||||
|
||||
protected:
|
||||
class MockCrashGenerationServerCallbacks {
|
||||
public:
|
||||
MOCK_METHOD1(OnClientConnected,
|
||||
void(const google_breakpad::ClientInfo* client_info));
|
||||
MOCK_METHOD2(OnClientDumpRequested,
|
||||
void(const google_breakpad::ClientInfo* client_info,
|
||||
const std::wstring* file_path));
|
||||
MOCK_METHOD1(OnClientExited,
|
||||
void(const google_breakpad::ClientInfo* client_info));
|
||||
};
|
||||
|
||||
enum ClientFault {
|
||||
NO_FAULT,
|
||||
CLOSE_AFTER_CONNECT,
|
||||
SEND_INVALID_REGISTRATION,
|
||||
TRUNCATE_REGISTRATION,
|
||||
CLOSE_AFTER_REGISTRATION,
|
||||
RESPONSE_BUFFER_TOO_SMALL,
|
||||
CLOSE_AFTER_RESPONSE,
|
||||
SEND_INVALID_ACK
|
||||
};
|
||||
|
||||
void SetUp() {
|
||||
ASSERT_TRUE(crash_generation_server_.Start());
|
||||
}
|
||||
|
||||
void FaultyClient(ClientFault fault_type) {
|
||||
HANDLE pipe = CreateFile(kPipeName,
|
||||
kPipeDesiredAccess,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
kPipeFlagsAndAttributes,
|
||||
NULL);
|
||||
|
||||
if (pipe == INVALID_HANDLE_VALUE) {
|
||||
ASSERT_EQ(ERROR_PIPE_BUSY, GetLastError());
|
||||
|
||||
// Cannot continue retrying if wait on pipe fails.
|
||||
ASSERT_TRUE(WaitNamedPipe(kPipeName, 500));
|
||||
|
||||
pipe = CreateFile(kPipeName,
|
||||
kPipeDesiredAccess,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
kPipeFlagsAndAttributes,
|
||||
NULL);
|
||||
}
|
||||
|
||||
ASSERT_NE(pipe, INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD mode = kPipeMode;
|
||||
ASSERT_TRUE(SetNamedPipeHandleState(pipe, &mode, NULL, NULL));
|
||||
|
||||
DoFaultyClient(fault_type, pipe);
|
||||
|
||||
CloseHandle(pipe);
|
||||
}
|
||||
|
||||
void DoTestFault(ClientFault fault) {
|
||||
EXPECT_CALL(mock_callbacks_, OnClientConnected(_)).Times(0);
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
|
||||
|
||||
EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT));
|
||||
|
||||
// Slight hack. The OnClientConnected is only invoked after the ack is
|
||||
// received by the server. At that point, the FaultyClient call has already
|
||||
// returned. The best way to wait until the server is done handling that is
|
||||
// to send one more ping, whose processing will be blocked by delivery of
|
||||
// the OnClientConnected message.
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
|
||||
}
|
||||
|
||||
MockCrashGenerationServerCallbacks mock_callbacks_;
|
||||
|
||||
private:
|
||||
// Depends on the caller to successfully open the pipe before invocation and
|
||||
// to close it immediately afterwards.
|
||||
void DoFaultyClient(ClientFault fault_type, HANDLE pipe) {
|
||||
if (fault_type == CLOSE_AFTER_CONNECT) {
|
||||
return;
|
||||
}
|
||||
|
||||
google_breakpad::CustomClientInfo custom_info = {kCustomInfoEntries,
|
||||
kCustomInfoCount};
|
||||
|
||||
google_breakpad::ProtocolMessage msg(
|
||||
fault_type == SEND_INVALID_REGISTRATION ?
|
||||
google_breakpad::MESSAGE_TAG_NONE :
|
||||
google_breakpad::MESSAGE_TAG_REGISTRATION_REQUEST,
|
||||
GetCurrentProcessId(),
|
||||
MiniDumpNormal,
|
||||
&thread_id_,
|
||||
&exception_pointers_,
|
||||
&assert_info_,
|
||||
custom_info,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
DWORD bytes_count = 0;
|
||||
|
||||
ASSERT_TRUE(WriteFile(pipe,
|
||||
&msg,
|
||||
fault_type == TRUNCATE_REGISTRATION ?
|
||||
sizeof(msg) / 2 : sizeof(msg),
|
||||
&bytes_count,
|
||||
NULL));
|
||||
|
||||
if (fault_type == CLOSE_AFTER_REGISTRATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
google_breakpad::ProtocolMessage reply;
|
||||
|
||||
if (!ReadFile(pipe,
|
||||
&reply,
|
||||
fault_type == RESPONSE_BUFFER_TOO_SMALL ?
|
||||
sizeof(google_breakpad::ProtocolMessage) / 2 :
|
||||
sizeof(google_breakpad::ProtocolMessage),
|
||||
&bytes_count,
|
||||
NULL)) {
|
||||
switch (fault_type) {
|
||||
case TRUNCATE_REGISTRATION:
|
||||
case RESPONSE_BUFFER_TOO_SMALL:
|
||||
case SEND_INVALID_REGISTRATION:
|
||||
return;
|
||||
|
||||
default:
|
||||
FAIL() << "Unexpectedly failed to register.";
|
||||
}
|
||||
}
|
||||
|
||||
if (fault_type == CLOSE_AFTER_RESPONSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
google_breakpad::ProtocolMessage ack_msg;
|
||||
ack_msg.tag = google_breakpad::MESSAGE_TAG_REGISTRATION_ACK;
|
||||
|
||||
ASSERT_TRUE(WriteFile(pipe,
|
||||
&ack_msg,
|
||||
SEND_INVALID_ACK ?
|
||||
sizeof(ack_msg) : sizeof(ack_msg) / 2,
|
||||
&bytes_count,
|
||||
NULL));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void CallOnClientConnected(
|
||||
void* context, const google_breakpad::ClientInfo* client_info) {
|
||||
static_cast<MockCrashGenerationServerCallbacks*>(context)->
|
||||
OnClientConnected(client_info);
|
||||
}
|
||||
|
||||
static void CallOnClientDumpRequested(
|
||||
void* context,
|
||||
const google_breakpad::ClientInfo* client_info,
|
||||
const std::wstring* file_path) {
|
||||
static_cast<MockCrashGenerationServerCallbacks*>(context)->
|
||||
OnClientDumpRequested(client_info, file_path);
|
||||
}
|
||||
|
||||
static void CallOnClientExited(
|
||||
void* context, const google_breakpad::ClientInfo* client_info) {
|
||||
static_cast<MockCrashGenerationServerCallbacks*>(context)->
|
||||
OnClientExited(client_info);
|
||||
}
|
||||
|
||||
DWORD thread_id_;
|
||||
EXCEPTION_POINTERS* exception_pointers_;
|
||||
MDRawAssertionInfo assert_info_;
|
||||
|
||||
google_breakpad::CrashGenerationServer crash_generation_server_;
|
||||
};
|
||||
|
||||
TEST_F(CrashGenerationServerTest, PingServerTest) {
|
||||
DoTestFault(CLOSE_AFTER_CONNECT);
|
||||
}
|
||||
|
||||
TEST_F(CrashGenerationServerTest, InvalidRegistration) {
|
||||
DoTestFault(SEND_INVALID_REGISTRATION);
|
||||
}
|
||||
|
||||
TEST_F(CrashGenerationServerTest, TruncateRegistration) {
|
||||
DoTestFault(TRUNCATE_REGISTRATION);
|
||||
}
|
||||
|
||||
TEST_F(CrashGenerationServerTest, CloseAfterRegistration) {
|
||||
DoTestFault(CLOSE_AFTER_REGISTRATION);
|
||||
}
|
||||
|
||||
TEST_F(CrashGenerationServerTest, ResponseBufferTooSmall) {
|
||||
DoTestFault(RESPONSE_BUFFER_TOO_SMALL);
|
||||
}
|
||||
|
||||
TEST_F(CrashGenerationServerTest, CloseAfterResponse) {
|
||||
DoTestFault(CLOSE_AFTER_RESPONSE);
|
||||
}
|
||||
|
||||
// It turns out that, as long as you send one byte, the ACK is accepted and
|
||||
// registration succeeds.
|
||||
TEST_F(CrashGenerationServerTest, SendInvalidAck) {
|
||||
EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(SEND_INVALID_ACK));
|
||||
|
||||
// See DoTestFault for an explanation of this line
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
|
||||
|
||||
EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT));
|
||||
|
||||
// See DoTestFault for an explanation of this line
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
Loading…
Add table
Add a link
Reference in a new issue