1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <stdio.h> 6 #include <string> 7 8 #include "base/compiler_specific.h" 9 #include "base/environment.h" 10 #include "base/logging.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "breakpad/src/client/windows/crash_generation/client_info.h" 15 #include "breakpad/src/client/windows/crash_generation/crash_generation_server.h" 16 #include "testing/gmock/include/gmock/gmock.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 19 namespace { 20 21 // The name of the environment variable used to pass the crash server pipe name 22 // to the crashing child process. 23 const char kPipeVariableName[] = "REMOTING_BREAKPAD_WIN_DEATH_TEST_PIPE_NAME"; 24 25 // The prefix string used to generate a unique crash server pipe name. 26 // The name has to be unique as multiple test instances can be running 27 // simultaneously. 28 const wchar_t kPipeNamePrefix[] = L"\\\\.\\pipe\\"; 29 30 class MockCrashServerCallbacks { 31 public: 32 MockCrashServerCallbacks(); 33 virtual ~MockCrashServerCallbacks(); 34 35 // |google_breakpad::CrashGenerationServer| invokes callbacks from artitrary 36 // thread pool threads. |OnClientDumpRequested| is the only one that happened 37 // to be called in synchronous manner. While it is still called on 38 // a thread pool thread, the crashing process will wait until the server 39 // signals an event after |OnClientDumpRequested| completes (or until 15 40 // seconds timeout expires). 41 MOCK_METHOD0(OnClientDumpRequested, void()); 42 43 static void OnClientDumpRequestCallback( 44 void* context, 45 const google_breakpad::ClientInfo* client_info, 46 const std::wstring* file_path); 47 }; 48 49 MockCrashServerCallbacks::MockCrashServerCallbacks() { 50 } 51 52 MockCrashServerCallbacks::~MockCrashServerCallbacks() { 53 } 54 55 // static 56 void MockCrashServerCallbacks::OnClientDumpRequestCallback( 57 void* context, 58 const google_breakpad::ClientInfo* /* client_info */, 59 const std::wstring* /* file_path */) { 60 reinterpret_cast<MockCrashServerCallbacks*>(context)->OnClientDumpRequested(); 61 } 62 63 } // namespace 64 65 namespace remoting { 66 67 void InitializeCrashReportingForTest(const wchar_t* pipe_name); 68 69 class BreakpadWinDeathTest : public testing::Test { 70 public: 71 BreakpadWinDeathTest(); 72 virtual ~BreakpadWinDeathTest(); 73 74 virtual void SetUp() OVERRIDE; 75 76 protected: 77 scoped_ptr<google_breakpad::CrashGenerationServer> crash_server_; 78 scoped_ptr<MockCrashServerCallbacks> callbacks_; 79 std::wstring pipe_name_; 80 }; 81 82 BreakpadWinDeathTest::BreakpadWinDeathTest() { 83 } 84 85 BreakpadWinDeathTest::~BreakpadWinDeathTest() { 86 } 87 88 void BreakpadWinDeathTest::SetUp() { 89 scoped_ptr<base::Environment> environment(base::Environment::Create()); 90 std::string pipe_name; 91 if (environment->GetVar(kPipeVariableName, &pipe_name)) { 92 // This is a child process. Initialize crash dump reporting to the crash 93 // dump server. 94 pipe_name_ = UTF8ToWide(pipe_name); 95 InitializeCrashReportingForTest(pipe_name_.c_str()); 96 } else { 97 // This is the parent process. Generate a unique pipe name and setup 98 // a dummy crash dump server. 99 UUID guid = {0}; 100 RPC_STATUS status = UuidCreate(&guid); 101 EXPECT_TRUE(status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY); 102 103 pipe_name_ = 104 base::StringPrintf( 105 L"%ls%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 106 kPipeNamePrefix, 107 guid.Data1, 108 guid.Data2, 109 guid.Data3, 110 guid.Data4[0], 111 guid.Data4[1], 112 guid.Data4[2], 113 guid.Data4[3], 114 guid.Data4[4], 115 guid.Data4[5], 116 guid.Data4[6], 117 guid.Data4[7]); 118 EXPECT_TRUE(environment->SetVar(kPipeVariableName, 119 WideToUTF8(pipe_name_))); 120 121 // Setup a dummy crash dump server. 122 callbacks_.reset(new MockCrashServerCallbacks()); 123 crash_server_.reset( 124 new google_breakpad::CrashGenerationServer( 125 pipe_name_, 126 NULL, 127 NULL, 128 NULL, 129 MockCrashServerCallbacks::OnClientDumpRequestCallback, 130 callbacks_.get(), 131 NULL, 132 NULL, 133 NULL, 134 NULL, 135 false, 136 NULL)); 137 ASSERT_TRUE(crash_server_->Start()); 138 } 139 } 140 141 TEST_F(BreakpadWinDeathTest, TestAccessViolation) { 142 if (callbacks_.get()) { 143 EXPECT_CALL(*callbacks_, OnClientDumpRequested()); 144 } 145 146 // Generate access violation exception. 147 ASSERT_DEATH(*reinterpret_cast<int*>(NULL) = 1, ""); 148 } 149 150 TEST_F(BreakpadWinDeathTest, TestInvalidParameter) { 151 if (callbacks_.get()) { 152 EXPECT_CALL(*callbacks_, OnClientDumpRequested()); 153 } 154 155 // Cause the invalid parameter callback to be called. 156 ASSERT_EXIT(printf(NULL), testing::ExitedWithCode(0), ""); 157 } 158 159 TEST_F(BreakpadWinDeathTest, TestDebugbreak) { 160 if (callbacks_.get()) { 161 EXPECT_CALL(*callbacks_, OnClientDumpRequested()); 162 } 163 164 // See if __debugbreak() is intercepted. 165 ASSERT_DEATH(__debugbreak(), ""); 166 } 167 168 } // namespace remoting 169