OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Crashpad Authors. All rights reserved. |
| 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at |
| 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. |
| 14 |
| 15 #include "snapshot/win/exception_snapshot_win.h" |
| 16 |
| 17 #include <string> |
| 18 |
| 19 #include "base/strings/stringprintf.h" |
| 20 #include "base/strings/utf_string_conversions.h" |
| 21 #include "client/crashpad_client.h" |
| 22 #include "client/crashpad_info.h" |
| 23 #include "handler/win/registration_server.h" |
| 24 #include "gtest/gtest.h" |
| 25 #include "snapshot/win/process_reader_win.h" |
| 26 #include "snapshot/win/process_snapshot_win.h" |
| 27 #include "test/win/win_child_process.h" |
| 28 #include "util/thread/thread.h" |
| 29 #include "util/win/scoped_handle.h" |
| 30 |
| 31 namespace crashpad { |
| 32 namespace test { |
| 33 namespace { |
| 34 |
| 35 HANDLE DuplicateEvent(HANDLE process, HANDLE event) { |
| 36 HANDLE handle; |
| 37 if (DuplicateHandle(GetCurrentProcess(), |
| 38 event, |
| 39 process, |
| 40 &handle, |
| 41 SYNCHRONIZE | EVENT_MODIFY_STATE, |
| 42 false, |
| 43 0)) { |
| 44 return handle; |
| 45 } |
| 46 return nullptr; |
| 47 } |
| 48 |
| 49 class ExceptionSnapshotWinTest : public testing::Test { |
| 50 public: |
| 51 class Delegate : public RegistrationServer::Delegate { |
| 52 public: |
| 53 Delegate() |
| 54 : crashpad_info_address_(0), |
| 55 client_process_(), |
| 56 started_event_(CreateEvent(nullptr, false, false, nullptr)), |
| 57 request_dump_event_(CreateEvent(nullptr, false, false, nullptr)), |
| 58 dump_complete_event_(CreateEvent(nullptr, true, false, nullptr)) { |
| 59 EXPECT_TRUE(started_event_.is_valid()); |
| 60 EXPECT_TRUE(request_dump_event_.is_valid()); |
| 61 EXPECT_TRUE(dump_complete_event_.is_valid()); |
| 62 } |
| 63 |
| 64 ~Delegate() override { |
| 65 } |
| 66 |
| 67 void OnStarted() override { |
| 68 EXPECT_EQ(WAIT_TIMEOUT, WaitForSingleObject(started_event_.get(), 0)); |
| 69 SetEvent(started_event_.get()); |
| 70 } |
| 71 |
| 72 bool RegisterClient(ScopedKernelHANDLE client_process, |
| 73 WinVMAddress crashpad_info_address, |
| 74 HANDLE* request_dump_event, |
| 75 HANDLE* dump_complete_event) override { |
| 76 client_process_ = client_process.Pass(); |
| 77 crashpad_info_address_ = crashpad_info_address; |
| 78 *request_dump_event = |
| 79 DuplicateEvent(client_process_.get(), request_dump_event_.get()); |
| 80 *dump_complete_event = |
| 81 DuplicateEvent(client_process_.get(), dump_complete_event_.get()); |
| 82 return true; |
| 83 } |
| 84 |
| 85 void WaitForStart() { |
| 86 DWORD wait_result = WaitForSingleObject(started_event_.get(), INFINITE); |
| 87 if (wait_result == WAIT_FAILED) |
| 88 PLOG(ERROR); |
| 89 ASSERT_EQ(wait_result, WAIT_OBJECT_0); |
| 90 } |
| 91 |
| 92 void WaitForDumpRequestAndValidateException(void* break_near) { |
| 93 // Wait until triggered, and then grab information from the child. |
| 94 WaitForSingleObject(request_dump_event_.get(), INFINITE); |
| 95 |
| 96 // Snapshot the process and exception. |
| 97 ProcessReaderWin process_reader; |
| 98 ASSERT_TRUE(process_reader.Initialize(client_process_.get())); |
| 99 CrashpadInfo crashpad_info; |
| 100 ASSERT_TRUE(process_reader.ReadMemory( |
| 101 crashpad_info_address_, sizeof(crashpad_info), &crashpad_info)); |
| 102 ProcessSnapshotWin snapshot; |
| 103 snapshot.Initialize(client_process_.get()); |
| 104 snapshot.InitializeException( |
| 105 crashpad_info.thread_id(), |
| 106 reinterpret_cast<WinVMAddress>(crashpad_info.exception_pointers())); |
| 107 |
| 108 // Confirm the exception record was read correctly. |
| 109 EXPECT_NE(snapshot.Exception()->ThreadID(), 0u); |
| 110 EXPECT_EQ(snapshot.Exception()->Exception(), EXCEPTION_BREAKPOINT); |
| 111 |
| 112 // Verify the exception happened at the expected location with a bit of |
| 113 // slop space to allow for reading the current PC before the exception |
| 114 // happens. See CrashingChildProcess::Run(). |
| 115 const uint64_t kAllowedOffset = 64; |
| 116 EXPECT_GT(snapshot.Exception()->ExceptionAddress(), |
| 117 reinterpret_cast<uint64_t>(break_near)); |
| 118 EXPECT_LT(snapshot.Exception()->ExceptionAddress(), |
| 119 reinterpret_cast<uint64_t>(break_near) + kAllowedOffset); |
| 120 |
| 121 // Notify the child that we're done. |
| 122 SetEvent(dump_complete_event_.get()); |
| 123 } |
| 124 |
| 125 ScopedKernelHANDLE* request_dump_event() { return &request_dump_event_; } |
| 126 ScopedKernelHANDLE* dump_complete_event() { return &dump_complete_event_; } |
| 127 |
| 128 private: |
| 129 WinVMAddress crashpad_info_address_; |
| 130 ScopedKernelHANDLE client_process_; |
| 131 ScopedKernelHANDLE started_event_; |
| 132 ScopedKernelHANDLE request_dump_event_; |
| 133 ScopedKernelHANDLE dump_complete_event_; |
| 134 }; |
| 135 }; |
| 136 |
| 137 // Runs the RegistrationServer on a background thread. |
| 138 class RunServerThread : public Thread { |
| 139 public: |
| 140 // Instantiates a thread which will invoke server->Run(pipe_name, delegate). |
| 141 RunServerThread(RegistrationServer* server, |
| 142 const base::string16& pipe_name, |
| 143 RegistrationServer::Delegate* delegate) |
| 144 : server_(server), pipe_name_(pipe_name), delegate_(delegate) {} |
| 145 ~RunServerThread() override {} |
| 146 |
| 147 private: |
| 148 // Thread: |
| 149 void ThreadMain() override { server_->Run(pipe_name_, delegate_); } |
| 150 |
| 151 RegistrationServer* server_; |
| 152 base::string16 pipe_name_; |
| 153 RegistrationServer::Delegate* delegate_; |
| 154 |
| 155 DISALLOW_COPY_AND_ASSIGN(RunServerThread); |
| 156 }; |
| 157 |
| 158 // During destruction, ensures that the server is stopped and the background |
| 159 // thread joined. |
| 160 class ScopedStopServerAndJoinThread { |
| 161 public: |
| 162 explicit ScopedStopServerAndJoinThread(RegistrationServer* server, |
| 163 Thread* thread) |
| 164 : server_(server), thread_(thread) {} |
| 165 ~ScopedStopServerAndJoinThread() { |
| 166 server_->Stop(); |
| 167 thread_->Join(); |
| 168 } |
| 169 |
| 170 private: |
| 171 RegistrationServer* server_; |
| 172 Thread* thread_; |
| 173 DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread); |
| 174 }; |
| 175 |
| 176 std::string ReadString(FileHandle handle) { |
| 177 size_t length = 0; |
| 178 EXPECT_TRUE(LoggingReadFile(handle, &length, sizeof(length))); |
| 179 scoped_ptr<char[]> buffer(new char[length]); |
| 180 EXPECT_TRUE(LoggingReadFile(handle, &buffer[0], length)); |
| 181 return std::string(&buffer[0], length); |
| 182 } |
| 183 |
| 184 void WriteString(FileHandle handle, const std::string& str) { |
| 185 size_t length = str.size(); |
| 186 EXPECT_TRUE(LoggingWriteFile(handle, &length, sizeof(length))); |
| 187 EXPECT_TRUE(LoggingWriteFile(handle, &str[0], length)); |
| 188 } |
| 189 |
| 190 __declspec(noinline) void* CurrentAddress() { |
| 191 return _ReturnAddress(); |
| 192 } |
| 193 |
| 194 class CrashingChildProcess final : public WinChildProcess { |
| 195 public: |
| 196 CrashingChildProcess() : WinChildProcess() {} |
| 197 ~CrashingChildProcess() {} |
| 198 |
| 199 private: |
| 200 int Run() override { |
| 201 std::string pipe_name = ReadString(ReadPipeHandle()); |
| 202 CrashpadClient client; |
| 203 EXPECT_TRUE(client.SetHandler(pipe_name)); |
| 204 EXPECT_TRUE(client.UseHandler()); |
| 205 // Save the address where we're about to crash so the exception handler can |
| 206 // verify it's in approximately the right location (with a bit of fudge for |
| 207 // the code between here and the __debugbreak()). |
| 208 void* break_address = CurrentAddress(); |
| 209 LoggingWriteFile(WritePipeHandle(), &break_address, sizeof(break_address)); |
| 210 __debugbreak(); |
| 211 return 0; |
| 212 }; |
| 213 }; |
| 214 |
| 215 TEST_F(ExceptionSnapshotWinTest, ChildCrash) { |
| 216 // Set up the registration server on a background thread. |
| 217 RegistrationServer server; |
| 218 std::string pipe_name = "\\\\.\\pipe\\handler_test_pipe_" + |
| 219 base::StringPrintf("%08x", GetCurrentProcessId()); |
| 220 base::string16 pipe_name_16 = base::UTF8ToUTF16(pipe_name); |
| 221 Delegate delegate; |
| 222 RunServerThread server_thread(&server, pipe_name_16, &delegate); |
| 223 server_thread.Start(); |
| 224 ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( |
| 225 &server, &server_thread); |
| 226 ASSERT_NO_FATAL_FAILURE(delegate.WaitForStart()); |
| 227 |
| 228 // Spawn a child process that immediately crashes. |
| 229 WinChildProcess::EntryPoint<CrashingChildProcess>(); |
| 230 scoped_ptr<WinChildProcess::Handles> handle = WinChildProcess::Launch(); |
| 231 WriteString(handle->write.get(), pipe_name); |
| 232 |
| 233 void* break_near_address; |
| 234 LoggingReadFile( |
| 235 handle->read.get(), &break_near_address, sizeof(break_near_address)); |
| 236 |
| 237 // Verify the exception information is as expected. |
| 238 delegate.WaitForDumpRequestAndValidateException(break_near_address); |
| 239 } |
| 240 |
| 241 } // namespace |
| 242 } // namespace test |
| 243 } // namespace crashpad |
OLD | NEW |