| OLD | NEW |
| 1 // Copyright 2015 The Crashpad Authors. All rights reserved. | 1 // Copyright 2015 The Crashpad Authors. All rights reserved. |
| 2 // | 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with 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 | 5 // You may obtain a copy of the License at |
| 6 // | 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // | 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. | 13 // limitations under the License. |
| 14 | 14 |
| 15 #include "snapshot/win/exception_snapshot_win.h" | 15 #include "snapshot/win/exception_snapshot_win.h" |
| 16 | 16 |
| 17 #include <string> | 17 #include <string> |
| 18 | 18 |
| 19 #include "base/files/file_path.h" |
| 19 #include "base/strings/stringprintf.h" | 20 #include "base/strings/stringprintf.h" |
| 21 #include "base/strings/string16.h" |
| 20 #include "base/strings/utf_string_conversions.h" | 22 #include "base/strings/utf_string_conversions.h" |
| 21 #include "client/crashpad_client.h" | 23 #include "client/crashpad_client.h" |
| 22 #include "gtest/gtest.h" | 24 #include "gtest/gtest.h" |
| 23 #include "snapshot/win/process_snapshot_win.h" | 25 #include "snapshot/win/process_snapshot_win.h" |
| 26 #include "test/paths.h" |
| 24 #include "test/win/win_child_process.h" | 27 #include "test/win/win_child_process.h" |
| 25 #include "util/thread/thread.h" | 28 #include "util/thread/thread.h" |
| 26 #include "util/win/exception_handler_server.h" | 29 #include "util/win/exception_handler_server.h" |
| 27 #include "util/win/registration_protocol_win.h" | 30 #include "util/win/registration_protocol_win.h" |
| 28 #include "util/win/scoped_handle.h" | 31 #include "util/win/scoped_handle.h" |
| 29 #include "util/win/scoped_process_suspend.h" | 32 #include "util/win/scoped_process_suspend.h" |
| 30 | 33 |
| 31 namespace crashpad { | 34 namespace crashpad { |
| 32 namespace test { | 35 namespace test { |
| 33 namespace { | 36 namespace { |
| 34 | 37 |
| 35 HANDLE DuplicateEvent(HANDLE process, HANDLE event) { | 38 HANDLE DuplicateEvent(HANDLE process, HANDLE event) { |
| 36 HANDLE handle; | 39 HANDLE handle; |
| 37 if (DuplicateHandle(GetCurrentProcess(), | 40 if (DuplicateHandle(GetCurrentProcess(), |
| 38 event, | 41 event, |
| 39 process, | 42 process, |
| 40 &handle, | 43 &handle, |
| 41 SYNCHRONIZE | EVENT_MODIFY_STATE, | 44 SYNCHRONIZE | EVENT_MODIFY_STATE, |
| 42 false, | 45 false, |
| 43 0)) { | 46 0)) { |
| 44 return handle; | 47 return handle; |
| 45 } | 48 } |
| 46 return nullptr; | 49 return nullptr; |
| 47 } | 50 } |
| 48 | 51 |
| 49 class ExceptionSnapshotWinTest : public testing::Test { | 52 class Delegate : public ExceptionHandlerServer::Delegate { |
| 50 public: | 53 public: |
| 51 class Delegate : public ExceptionHandlerServer::Delegate { | 54 Delegate(HANDLE server_ready, HANDLE completed_test_event) |
| 52 public: | 55 : server_ready_(server_ready), |
| 53 Delegate(HANDLE server_ready, HANDLE completed_test_event) | 56 completed_test_event_(completed_test_event), |
| 54 : server_ready_(server_ready), | 57 break_near_(0) {} |
| 55 completed_test_event_(completed_test_event), | 58 ~Delegate() override {} |
| 56 break_near_(nullptr) {} | |
| 57 ~Delegate() override {} | |
| 58 | 59 |
| 59 void set_break_near(void* break_near) { break_near_ = break_near; } | 60 void set_break_near(WinVMAddress break_near) { break_near_ = break_near; } |
| 60 | 61 |
| 61 void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); } | 62 void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); } |
| 62 | 63 |
| 63 unsigned int ExceptionHandlerServerException( | 64 unsigned int ExceptionHandlerServerException( |
| 64 HANDLE process, | 65 HANDLE process, |
| 65 WinVMAddress exception_information_address) override { | 66 WinVMAddress exception_information_address) override { |
| 66 ScopedProcessSuspend suspend(process); | 67 ScopedProcessSuspend suspend(process); |
| 67 ProcessSnapshotWin snapshot; | 68 ProcessSnapshotWin snapshot; |
| 68 snapshot.Initialize(process, ProcessSuspensionState::kSuspended); | 69 snapshot.Initialize(process, ProcessSuspensionState::kSuspended); |
| 69 snapshot.InitializeException(exception_information_address); | 70 snapshot.InitializeException(exception_information_address); |
| 70 | 71 |
| 71 // Confirm the exception record was read correctly. | 72 // Confirm the exception record was read correctly. |
| 72 EXPECT_NE(snapshot.Exception()->ThreadID(), 0u); | 73 EXPECT_NE(snapshot.Exception()->ThreadID(), 0u); |
| 73 EXPECT_EQ(snapshot.Exception()->Exception(), EXCEPTION_BREAKPOINT); | 74 EXPECT_EQ(snapshot.Exception()->Exception(), EXCEPTION_BREAKPOINT); |
| 74 | 75 |
| 75 // Verify the exception happened at the expected location with a bit of | 76 // Verify the exception happened at the expected location with a bit of |
| 76 // slop space to allow for reading the current PC before the exception | 77 // slop space to allow for reading the current PC before the exception |
| 77 // happens. See CrashingChildProcess::Run(). | 78 // happens. See CrashingChildProcess::Run(). |
| 78 const uint64_t kAllowedOffset = 64; | 79 const uint64_t kAllowedOffset = 64; |
| 79 EXPECT_GT(snapshot.Exception()->ExceptionAddress(), | 80 EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_); |
| 80 reinterpret_cast<uint64_t>(break_near_)); | 81 EXPECT_LT(snapshot.Exception()->ExceptionAddress(), |
| 81 EXPECT_LT(snapshot.Exception()->ExceptionAddress(), | 82 break_near_ + kAllowedOffset); |
| 82 reinterpret_cast<uint64_t>(break_near_) + kAllowedOffset); | |
| 83 | 83 |
| 84 SetEvent(completed_test_event_); | 84 SetEvent(completed_test_event_); |
| 85 | 85 |
| 86 return snapshot.Exception()->Exception(); | 86 return snapshot.Exception()->Exception(); |
| 87 } | 87 } |
| 88 | |
| 89 private: | |
| 90 HANDLE server_ready_; // weak | |
| 91 HANDLE completed_test_event_; // weak | |
| 92 void* break_near_; | |
| 93 | |
| 94 DISALLOW_COPY_AND_ASSIGN(Delegate); | |
| 95 }; | |
| 96 | 88 |
| 97 private: | 89 private: |
| 98 ScopedKernelHANDLE exception_happened_; | 90 HANDLE server_ready_; // weak |
| 91 HANDLE completed_test_event_; // weak |
| 92 WinVMAddress break_near_; |
| 93 |
| 94 DISALLOW_COPY_AND_ASSIGN(Delegate); |
| 99 }; | 95 }; |
| 100 | 96 |
| 101 // Runs the ExceptionHandlerServer on a background thread. | 97 // Runs the ExceptionHandlerServer on a background thread. |
| 102 class RunServerThread : public Thread { | 98 class RunServerThread : public Thread { |
| 103 public: | 99 public: |
| 104 // Instantiates a thread which will invoke server->Run(delegate, pipe_name); | 100 // Instantiates a thread which will invoke server->Run(delegate, pipe_name); |
| 105 RunServerThread(ExceptionHandlerServer* server, | 101 RunServerThread(ExceptionHandlerServer* server, |
| 106 ExceptionHandlerServer::Delegate* delegate, | 102 ExceptionHandlerServer::Delegate* delegate, |
| 107 const std::string& pipe_name) | 103 const std::string& pipe_name) |
| 108 : server_(server), delegate_(delegate), pipe_name_(pipe_name) {} | 104 : server_(server), delegate_(delegate), pipe_name_(pipe_name) {} |
| (...skipping 20 matching lines...) Expand all Loading... |
| 129 server_->Stop(); | 125 server_->Stop(); |
| 130 thread_->Join(); | 126 thread_->Join(); |
| 131 } | 127 } |
| 132 | 128 |
| 133 private: | 129 private: |
| 134 ExceptionHandlerServer* server_; | 130 ExceptionHandlerServer* server_; |
| 135 Thread* thread_; | 131 Thread* thread_; |
| 136 DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread); | 132 DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread); |
| 137 }; | 133 }; |
| 138 | 134 |
| 139 std::string ReadString(FileHandle handle) { | 135 void TestCrashingChild(const base::string16& directory_modification) { |
| 140 size_t length = 0; | |
| 141 EXPECT_TRUE(LoggingReadFile(handle, &length, sizeof(length))); | |
| 142 scoped_ptr<char[]> buffer(new char[length]); | |
| 143 EXPECT_TRUE(LoggingReadFile(handle, &buffer[0], length)); | |
| 144 return std::string(&buffer[0], length); | |
| 145 } | |
| 146 | |
| 147 void WriteString(FileHandle handle, const std::string& str) { | |
| 148 size_t length = str.size(); | |
| 149 EXPECT_TRUE(LoggingWriteFile(handle, &length, sizeof(length))); | |
| 150 EXPECT_TRUE(LoggingWriteFile(handle, &str[0], length)); | |
| 151 } | |
| 152 | |
| 153 __declspec(noinline) void* CurrentAddress() { | |
| 154 return _ReturnAddress(); | |
| 155 } | |
| 156 | |
| 157 class CrashingChildProcess final : public WinChildProcess { | |
| 158 public: | |
| 159 CrashingChildProcess() : WinChildProcess() {} | |
| 160 ~CrashingChildProcess() {} | |
| 161 | |
| 162 private: | |
| 163 int Run() override { | |
| 164 std::string pipe_name = ReadString(ReadPipeHandle()); | |
| 165 CrashpadClient client; | |
| 166 EXPECT_TRUE(client.SetHandler(pipe_name)); | |
| 167 EXPECT_TRUE(client.UseHandler()); | |
| 168 // Save the address where we're about to crash so the exception handler can | |
| 169 // verify it's in approximately the right location (with a bit of fudge for | |
| 170 // the code between here and the __debugbreak()). | |
| 171 void* break_address = CurrentAddress(); | |
| 172 LoggingWriteFile(WritePipeHandle(), &break_address, sizeof(break_address)); | |
| 173 __debugbreak(); | |
| 174 return 0; | |
| 175 }; | |
| 176 }; | |
| 177 | |
| 178 TEST_F(ExceptionSnapshotWinTest, ChildCrash) { | |
| 179 // Spawn a child process that will immediately crash (once we let it | |
| 180 // run below by telling it what to connect to). | |
| 181 WinChildProcess::EntryPoint<CrashingChildProcess>(); | |
| 182 scoped_ptr<WinChildProcess::Handles> handle = WinChildProcess::Launch(); | |
| 183 | |
| 184 // Set up the registration server on a background thread. | 136 // Set up the registration server on a background thread. |
| 185 std::string pipe_name = "\\\\.\\pipe\\handler_test_pipe_" + | 137 std::string pipe_name = "\\\\.\\pipe\\handler_test_pipe_" + |
| 186 base::StringPrintf("%08x", GetCurrentProcessId()); | 138 base::StringPrintf("%08x", GetCurrentProcessId()); |
| 187 ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr)); | 139 ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr)); |
| 188 ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr)); | 140 ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr)); |
| 189 Delegate delegate(server_ready.get(), completed.get()); | 141 Delegate delegate(server_ready.get(), completed.get()); |
| 190 | 142 |
| 191 ExceptionHandlerServer exception_handler_server; | 143 ExceptionHandlerServer exception_handler_server; |
| 192 RunServerThread server_thread( | 144 RunServerThread server_thread( |
| 193 &exception_handler_server, &delegate, pipe_name); | 145 &exception_handler_server, &delegate, pipe_name); |
| 194 server_thread.Start(); | 146 server_thread.Start(); |
| 195 ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( | 147 ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( |
| 196 &exception_handler_server, &server_thread); | 148 &exception_handler_server, &server_thread); |
| 197 | 149 |
| 198 WaitForSingleObject(server_ready.get(), INFINITE); | 150 WaitForSingleObject(server_ready.get(), INFINITE); |
| 199 // Allow the child to continue and tell it where to connect to. | 151 |
| 200 WriteString(handle->write.get(), pipe_name); | 152 // Spawn a child process, passing it the pipe name to connect to. |
| 153 base::FilePath test_executable = Paths::Executable(); |
| 154 std::wstring child_test_executable = |
| 155 test_executable.DirName() |
| 156 .Append(directory_modification) |
| 157 .Append(test_executable.BaseName().RemoveFinalExtension().value() + |
| 158 L"_crashing_child.exe") |
| 159 .value(); |
| 160 |
| 161 // Create a pipe for the stdout of the child. |
| 162 SECURITY_ATTRIBUTES security_attributes = {0}; |
| 163 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); |
| 164 security_attributes.bInheritHandle = true; |
| 165 HANDLE stdout_read; |
| 166 HANDLE stdout_write; |
| 167 ASSERT_TRUE(CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0)); |
| 168 ScopedFileHANDLE read_handle(stdout_read); |
| 169 ScopedFileHANDLE write_handle(stdout_write); |
| 170 ASSERT_TRUE(SetHandleInformation(read_handle.get(), HANDLE_FLAG_INHERIT, 0)); |
| 171 |
| 172 std::wstring command_line = |
| 173 child_test_executable + L" " + base::UTF8ToUTF16(pipe_name); |
| 174 STARTUPINFO startup_info = {0}; |
| 175 startup_info.cb = sizeof(startup_info); |
| 176 startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); |
| 177 startup_info.hStdOutput = write_handle.get(); |
| 178 startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); |
| 179 startup_info.dwFlags = STARTF_USESTDHANDLES; |
| 180 PROCESS_INFORMATION process_information; |
| 181 ASSERT_TRUE(CreateProcess(child_test_executable.c_str(), |
| 182 &command_line[0], |
| 183 nullptr, |
| 184 nullptr, |
| 185 true, |
| 186 0, |
| 187 nullptr, |
| 188 nullptr, |
| 189 &startup_info, |
| 190 &process_information)); |
| 191 // Take ownership of the two process handles returned. |
| 192 ScopedKernelHANDLE process_main_thread_handle(process_information.hThread); |
| 193 ScopedKernelHANDLE process_handle(process_information.hProcess); |
| 201 | 194 |
| 202 // The child tells us (approximately) where it will crash. | 195 // The child tells us (approximately) where it will crash. |
| 203 void* break_near_address; | 196 WinVMAddress break_near_address; |
| 204 LoggingReadFile( | 197 LoggingReadFile( |
| 205 handle->read.get(), &break_near_address, sizeof(break_near_address)); | 198 read_handle.get(), &break_near_address, sizeof(break_near_address)); |
| 206 delegate.set_break_near(break_near_address); | 199 delegate.set_break_near(break_near_address); |
| 207 | 200 |
| 208 // Wait for the child to crash and the exception information to be validated. | 201 // Wait for the child to crash and the exception information to be validated. |
| 209 WaitForSingleObject(completed.get(), INFINITE); | 202 WaitForSingleObject(completed.get(), INFINITE); |
| 210 } | 203 } |
| 211 | 204 |
| 205 TEST(ExceptionSnapshotWinTest, ChildCrash) { |
| 206 TestCrashingChild(FILE_PATH_LITERAL(".")); |
| 207 } |
| 208 |
| 209 #if defined(ARCH_CPU_64_BITS) |
| 210 TEST(ExceptionSnapshotWinTest, ChildCrashWOW64) { |
| 211 #ifndef NDEBUG |
| 212 TestCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Debug")); |
| 213 #else |
| 214 TestCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Release")); |
| 215 #endif |
| 216 } |
| 217 #endif // ARCH_CPU_64_BITS |
| 218 |
| 212 } // namespace | 219 } // namespace |
| 213 } // namespace test | 220 } // namespace test |
| 214 } // namespace crashpad | 221 } // namespace crashpad |
| OLD | NEW |