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 "handler/win/registrar.h" | |
16 | |
17 #include <windows.h> | |
18 | |
19 #include <vector> | |
20 | |
21 #include "base/basictypes.h" | |
22 #include "base/logging.h" | |
23 #include "base/memory/scoped_ptr.h" | |
24 #include "gtest/gtest.h" | |
25 #include "test/win/win_child_process.h" | |
26 #include "util/win/scoped_handle.h" | |
27 | |
28 namespace crashpad { | |
29 namespace test { | |
30 namespace { | |
31 | |
32 // Reads a `HANDLE` value from |handle|. Assume sender has same bitness as | |
33 // reader. CHECKs upon failure. | |
34 ScopedKernelHANDLE ReadHandle(HANDLE handle) { | |
35 HANDLE value = nullptr; | |
36 DWORD bytes_read = 0; | |
scottmg
2015/06/29 20:29:05
Replace 36-38 with CheckReadFile() from util/file/
| |
37 CHECK(::ReadFile(handle, &value, sizeof(value), &bytes_read, nullptr)); | |
38 CHECK_EQ(sizeof(value), bytes_read); | |
39 return ScopedKernelHANDLE(value); | |
40 } | |
41 | |
42 // Writes |value| to |handle|. Logs a GTest failure upon failure. | |
43 void WriteHandle(HANDLE handle, HANDLE value) { | |
44 DWORD bytes_written = 0; | |
scottmg
2015/06/29 20:29:05
And CheckedWriteFile here.
| |
45 EXPECT_TRUE( | |
46 ::WriteFile(handle, &value, sizeof(value), &bytes_written, nullptr)); | |
47 EXPECT_EQ(sizeof(value), bytes_written); | |
48 } | |
49 | |
50 // Implements a client process that may be registered. Reads the handle pair | |
51 // from its parent and proceeds to request a dump. After receiving notice that | |
52 // the dump completed, exits with 0. | |
53 class ClientProcess : public test::WinChildProcess { | |
54 public: | |
55 ClientProcess() {} | |
56 | |
57 private: | |
58 int Run() override { | |
59 // In the worst case, this process will exit due to a broken pipe if the | |
60 // parent process disappears. | |
61 ScopedKernelHANDLE request_report_event = ReadHandle(ReadPipeHandle()); | |
62 ScopedKernelHANDLE report_complete_event = ReadHandle(ReadPipeHandle()); | |
63 PCHECK(SetEvent(request_report_event.get())); | |
64 CHECK_EQ(WAIT_OBJECT_0, | |
65 WaitForSingleObject(report_complete_event.get(), INFINITE)); | |
66 return 0; | |
67 } | |
68 | |
69 DISALLOW_COPY_AND_ASSIGN(ClientProcess); | |
70 }; | |
71 | |
72 // Handles crash dump requests, logging the requesting client's PID. | |
73 class MockDelegate : Registrar::Delegate { | |
74 public: | |
75 // Instantiates a Registrar::Delegate that logs requesting clients' PIDs to | |
76 // |process_ids|. | |
77 explicit MockDelegate(std::vector<DWORD>* process_ids) | |
78 : process_ids_(process_ids) {} | |
79 | |
80 // Registrar::Delegate: | |
81 void GenerateReportForClient(HANDLE client_process) override { | |
82 process_ids_->push_back(GetProcessId(client_process)); | |
83 } | |
84 | |
85 private: | |
86 std::vector<DWORD>* process_ids_; | |
87 | |
88 DISALLOW_COPY_AND_ASSIGN(MockDelegate); | |
89 }; | |
90 | |
91 // Duplicates |handle| with PROCESS_ALL_ACCESS. Logs a GTest failure upon | |
92 // failure. | |
93 ScopedKernelHANDLE DuplicateProcessHandleWithAllAccess(HANDLE handle) { | |
94 HANDLE result = nullptr; | |
95 if (!DuplicateHandle(GetCurrentProcess(), | |
96 handle, | |
97 GetCurrentProcess(), | |
98 &result, | |
99 PROCESS_ALL_ACCESS, | |
100 false, | |
101 0)) { | |
102 PLOG(ERROR) << "DuplicateHandle"; | |
103 ADD_FAILURE() << "DuplicateHandle"; | |
104 return ScopedKernelHANDLE(); | |
105 } | |
106 return ScopedKernelHANDLE(result); | |
107 } | |
108 | |
109 class RegistrarTest : public testing::Test { | |
110 public: | |
111 RegistrarTest() | |
112 : registrar_( | |
113 scoped_ptr<Registrar::Delegate>(new MockDelegate(&process_ids_))) {} | |
114 | |
115 protected: | |
116 Registrar& registrar() { return registrar_; } | |
117 std::vector<DWORD>& triggered_process_ids() { return process_ids_; } | |
118 | |
119 private: | |
120 std::vector<DWORD> process_ids_; | |
121 Registrar registrar_; | |
122 | |
123 DISALLOW_COPY_AND_ASSIGN(RegistrarTest); | |
124 }; | |
125 | |
126 // Exercises registration, report request handling, and process termination. | |
127 TEST_F(RegistrarTest, RegisterProcess) { | |
128 test::WinChildProcess::EntryPoint<ClientProcess>(); | |
129 | |
130 struct ProcessState { | |
131 scoped_ptr<test::WinChildProcess::Handles> child_handles; | |
132 HANDLE request_handle; | |
133 HANDLE done_handle; | |
134 } process_states[3]; | |
135 | |
136 for (int i = 0; i < arraysize(process_states); ++i) { | |
137 process_states[i].child_handles = test::WinChildProcess::Launch(); | |
138 ASSERT_TRUE(registrar().RegisterProcess( | |
139 DuplicateProcessHandleWithAllAccess( | |
140 process_states[i].child_handles->process.get()), | |
141 &process_states[i].request_handle, | |
142 &process_states[i].done_handle)); | |
143 } | |
144 | |
145 // Kill a process. This should not cause any hiccups for the Registrar. | |
146 ASSERT_TRUE( | |
147 TerminateProcess(process_states[0].child_handles->process.get(), 0)); | |
148 | |
149 // Send the event pairs over to each of the child processes, and verify that | |
150 // they are able to trigger a report request, that the request is handed to | |
151 // our delegate, and that the "report complete" event is triggered and may be | |
152 // detected by the client. | |
153 for (int i = 1; i < arraysize(process_states); ++i) { | |
154 triggered_process_ids().clear(); | |
155 | |
156 // Verify that the child process is still running. | |
157 ASSERT_EQ( | |
158 WAIT_TIMEOUT, | |
159 WaitForSingleObject(process_states[i].child_handles->process.get(), 0)); | |
160 | |
161 // Send the handle pair along to the child, which will proceed to request a | |
162 // dump and wait for the "dump complete" signal. | |
163 WriteHandle(process_states[i].child_handles->write.get(), | |
164 process_states[i].request_handle); | |
165 WriteHandle(process_states[i].child_handles->write.get(), | |
166 process_states[i].done_handle); | |
167 | |
168 // Wait for the process to exit with code 0 (which indicates that the "dump | |
169 // complete" signal was received). | |
170 ASSERT_EQ(WAIT_OBJECT_0, | |
171 WaitForSingleObject( | |
172 process_states[i].child_handles->process.get(), INFINITE)); | |
173 DWORD exit_code = 0; | |
174 ASSERT_TRUE(GetExitCodeProcess( | |
175 process_states[i].child_handles->process.get(), &exit_code)); | |
176 ASSERT_EQ(0, exit_code); | |
177 | |
178 // Verify that our delegate was invoked, with the correct process handle. | |
179 ASSERT_EQ(1, triggered_process_ids().size()); | |
180 ASSERT_EQ(GetProcessId(process_states[i].child_handles->process.get()), | |
181 triggered_process_ids()[0]); | |
182 } | |
183 } | |
184 | |
185 } // namespace | |
186 } // namespace test | |
187 } // namespace crashpad | |
188 | |
OLD | NEW |