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 |