Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(353)

Side by Side Diff: client/simulate_crash_mac_test.cc

Issue 658853002: Add CRASHPAD_SIMULATE_CRASH() and supporting client code (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« client/simulate_crash_mac.h ('K') | « client/simulate_crash_mac.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 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 "client/simulate_crash.h"
16
17 #include <mach/mach.h>
18 #include <string.h>
19
20 #include "base/basictypes.h"
21 #include "base/strings/stringprintf.h"
22 #include "build/build_config.h"
23 #include "gtest/gtest.h"
24 #include "util/mach/exc_server_variants.h"
25 #include "util/mach/exception_behaviors.h"
26 #include "util/mach/exception_ports.h"
27 #include "util/mach/mach_extensions.h"
28 #include "util/mach/mach_message_server.h"
29 #include "util/mach/symbolic_constants_mach.h"
30 #include "util/test/mac/mach_errors.h"
31 #include "util/test/mac/mach_multiprocess.h"
32
33 namespace crashpad {
34 namespace test {
35 namespace {
36
37 class TestSimulateCrashMac final : public MachMultiprocess,
38 public UniversalMachExcServer {
39 public:
40 // Defines which targets the child should set an EXC_CRASH exception handler
41 // for.
42 enum ExceptionPortsTarget {
43 // The child should clear its EXC_CRASH handler for both its task and thread
44 // targets. SimulateCrash() will attempt to deliver the exception to the
45 // host target, which will fail if not running as root. In any case, the
46 // parent should not expect to receive any exception message from the child.
47 kExceptionPortsTargetNone = 0,
48
49 // The child will set an EXC_CRASH handler for its task target, and clear it
50 // for its thread target. The parent runs an exception server to receive
51 // the child’s simulated crash message.
52 kExceptionPortsTargetTask,
53
54 // The child will set an EXC_CRASH handler for its thread target, and clear
55 // it for its task target. The parent runs an exception server to receive
56 // the child’s simulated crash message.
57 kExceptionPortsTargetThread,
58
59 // The child sets an EXC_CRASH handler for both its task and thread targets.
60 // The parent runs an exception server to receive the message expected to be
61 // delivered to the thread target, but returns an error code. The child will
62 // then fall back to trying the server registered for the task target,
63 // sending a second message to the parent. The server in the parent will
64 // handle this one successfully.
65 kExceptionPortsTargetBoth,
66 };
67
68 TestSimulateCrashMac(ExceptionPortsTarget target,
69 exception_behavior_t behavior,
70 thread_state_flavor_t flavor)
71 : target_(target),
72 behavior_(behavior),
73 flavor_(flavor),
74 succeed_(true) {
75 }
76
77 ~TestSimulateCrashMac() {}
78
79 // UniversalMachExcServer:
80 kern_return_t CatchMachException(exception_behavior_t behavior,
81 exception_handler_t exception_port,
82 thread_t thread,
83 task_t task,
84 exception_type_t exception,
85 const mach_exception_data_type_t* code,
86 mach_msg_type_number_t code_count,
87 thread_state_flavor_t* flavor,
88 const natural_t* old_state,
89 mach_msg_type_number_t old_state_count,
90 thread_state_t new_state,
91 mach_msg_type_number_t* new_state_count,
92 bool* destroy_complex_request) override {
93 *destroy_complex_request = true;
94
95 // Check the entire exception message, because most or all of it was
96 // generated by SimulateCrash() instead of the kernel.
97
98 EXPECT_EQ(behavior_, behavior);
99 EXPECT_EQ(LocalPort(), exception_port);
100 if (ExceptionBehaviorHasIdentity(behavior)) {
101 EXPECT_NE(THREAD_NULL, thread);
102 EXPECT_EQ(ChildTask(), task);
103 } else {
104 EXPECT_EQ(THREAD_NULL, thread);
105 EXPECT_EQ(TASK_NULL, task);
106 }
107 EXPECT_EQ(kMachExceptionSimulated, exception);
108 EXPECT_EQ(2u, code_count);
109 if (code_count >= 1) {
110 EXPECT_EQ(0, code[0]);
111 }
112 if (code_count >= 2) {
113 EXPECT_EQ(0, code[1]);
114 }
115 if (!ExceptionBehaviorHasState(behavior)) {
116 EXPECT_EQ(THREAD_STATE_NONE, *flavor);
117 } else {
118 EXPECT_EQ(flavor_, *flavor);
119 switch (*flavor) {
120 #if defined(ARCH_CPU_X86_FAMILY)
121 case x86_THREAD_STATE: {
122 EXPECT_EQ(x86_THREAD_STATE_COUNT, old_state_count);
123 const x86_thread_state* state =
124 reinterpret_cast<const x86_thread_state*>(old_state);
125 switch (state->tsh.flavor) {
126 case x86_THREAD_STATE32:
127 EXPECT_EQ(static_cast<int>(x86_THREAD_STATE32_COUNT),
128 state->tsh.count);
129 break;
130 case x86_THREAD_STATE64:
131 EXPECT_EQ(static_cast<int>(x86_THREAD_STATE64_COUNT),
132 state->tsh.count);
133 break;
134 default:
135 ADD_FAILURE() << "unexpected tsh.flavor " << state->tsh.flavor;
136 break;
137 }
138 break;
139 }
140 case x86_FLOAT_STATE: {
141 EXPECT_EQ(x86_FLOAT_STATE_COUNT, old_state_count);
142 const x86_float_state* state =
143 reinterpret_cast<const x86_float_state*>(old_state);
144 switch (state->fsh.flavor) {
145 case x86_FLOAT_STATE32:
146 EXPECT_EQ(static_cast<int>(x86_FLOAT_STATE32_COUNT),
147 state->fsh.count);
148 break;
149 case x86_FLOAT_STATE64:
150 EXPECT_EQ(static_cast<int>(x86_FLOAT_STATE64_COUNT),
151 state->fsh.count);
152 break;
153 default:
154 ADD_FAILURE() << "unexpected fsh.flavor " << state->fsh.flavor;
155 break;
156 }
157 break;
158 }
159 case x86_DEBUG_STATE: {
160 EXPECT_EQ(x86_DEBUG_STATE_COUNT, old_state_count);
161 const x86_debug_state* state =
162 reinterpret_cast<const x86_debug_state*>(old_state);
163 switch (state->dsh.flavor) {
164 case x86_DEBUG_STATE32:
165 EXPECT_EQ(static_cast<int>(x86_DEBUG_STATE32_COUNT),
166 state->dsh.count);
167 break;
168 case x86_DEBUG_STATE64:
169 EXPECT_EQ(static_cast<int>(x86_DEBUG_STATE64_COUNT),
170 state->dsh.count);
171 break;
172 default:
173 ADD_FAILURE() << "unexpected dsh.flavor " << state->dsh.flavor;
174 break;
175 }
176 break;
177 }
178 case x86_THREAD_STATE32:
179 EXPECT_EQ(x86_THREAD_STATE32_COUNT, old_state_count);
180 break;
181 case x86_FLOAT_STATE32:
182 EXPECT_EQ(x86_FLOAT_STATE32_COUNT, old_state_count);
183 break;
184 case x86_DEBUG_STATE32:
185 EXPECT_EQ(x86_DEBUG_STATE32_COUNT, old_state_count);
186 break;
187 case x86_THREAD_STATE64:
188 EXPECT_EQ(x86_THREAD_STATE64_COUNT, old_state_count);
189 break;
190 case x86_FLOAT_STATE64:
191 EXPECT_EQ(x86_FLOAT_STATE64_COUNT, old_state_count);
192 break;
193 case x86_DEBUG_STATE64:
194 EXPECT_EQ(x86_DEBUG_STATE64_COUNT, old_state_count);
195 break;
196 #else
197 #error Port to your CPU architecture
198 #endif
199 default:
200 ADD_FAILURE() << "unexpected flavor " << *flavor;
201 break;
202 }
203
204 // Attempt to set a garbage thread state, which would cause the child to
205 // crash inside SimulateCrash() if it actually succeeded. This tests that
206 // SimulateCrash() ignores new_state instead of attempting to set the
207 // state as the kernel would do. This operates in conjunction with the
208 // |true| argument to ExcServerSuccessfulReturnValue() below.
209 *new_state_count = old_state_count;
210 size_t new_state_size = sizeof(natural_t) * old_state_count;
211 memset(new_state, 0xa5, new_state_size);
212 }
213
214 if (!succeed_) {
215 // The client has registered EXC_CRASH handlers for both its thread and
216 // task targets, and sent a simulated exception message to its
217 // thread-level EXC_CRASH handler. To test that it will fall back to
218 // trying the task-level EXC_CRASH handler, return a failure code, which
219 // should cause SimulateCrash() to try the next target.
220 EXPECT_EQ(kExceptionPortsTargetBoth, target_);
221 return KERN_ABORTED;
222 }
223
224 return ExcServerSuccessfulReturnValue(behavior, true);
225 }
226
227 private:
228 // MachMultiprocess:
229
230 void MachMultiprocessParent() override {
231 if (target_ == kExceptionPortsTargetNone) {
232 // The child does not have any EXC_CRASH handlers registered for its
233 // thread or task targets, so no exception message is expected to be
234 // generated. Don’t run the server at all.
235 return;
236 }
237
238 mach_msg_return_t mr;
239 if (target_ == kExceptionPortsTargetBoth) {
240 // The client has registered EXC_CRASH handlers for both its thread and
241 // task targets. Run a server that will return a failure code when the
242 // exception message is sent to the thread target, which will cause the
243 // client to fall back to the task target and send another message.
244 succeed_ = false;
245 mr = MachMessageServer::Run(this,
246 LocalPort(),
247 MACH_MSG_OPTION_NONE,
248 MachMessageServer::kOneShot,
249 MachMessageServer::kBlocking,
250 MACH_MSG_TIMEOUT_NONE);
251 EXPECT_EQ(MACH_MSG_SUCCESS, mr)
252 << MachErrorMessage(mr, "MachMessageServer::Run");
253 }
254
255 succeed_ = true;
256 mr = MachMessageServer::Run(this,
257 LocalPort(),
258 MACH_MSG_OPTION_NONE,
259 MachMessageServer::kOneShot,
260 MachMessageServer::kBlocking,
261 MACH_MSG_TIMEOUT_NONE);
262 EXPECT_EQ(MACH_MSG_SUCCESS, mr)
263 << MachErrorMessage(mr, "MachMessageServer::Run");
264 }
265
266 void MachMultiprocessChild() override {
267 bool task_valid = target_ == kExceptionPortsTargetTask ||
268 target_ == kExceptionPortsTargetBoth;
269 ExceptionPorts task_exception_ports(ExceptionPorts::kTargetTypeTask,
270 TASK_NULL);
271 ASSERT_TRUE(task_exception_ports.SetExceptionPort(
272 EXC_MASK_CRASH,
273 task_valid ? RemotePort() : MACH_PORT_NULL,
274 behavior_,
275 flavor_));
276
277 bool thread_valid = target_ == kExceptionPortsTargetThread ||
278 target_ == kExceptionPortsTargetBoth;
279 ExceptionPorts thread_exception_ports(ExceptionPorts::kTargetTypeThread,
280 THREAD_NULL);
281 ASSERT_TRUE(thread_exception_ports.SetExceptionPort(
282 EXC_MASK_CRASH,
283 thread_valid ? RemotePort() : MACH_PORT_NULL,
284 behavior_,
285 flavor_));
286
287 CRASHPAD_SIMULATE_CRASH();
288 }
289
290 ExceptionPortsTarget target_;
291 exception_behavior_t behavior_;
292 thread_state_flavor_t flavor_;
293 bool succeed_;
294
295 DISALLOW_COPY_AND_ASSIGN(TestSimulateCrashMac);
296 };
297
298 TEST(SimulateCrash, SimulateCrash) {
299 const TestSimulateCrashMac::ExceptionPortsTarget kTargets[] = {
300 TestSimulateCrashMac::kExceptionPortsTargetNone,
301 TestSimulateCrashMac::kExceptionPortsTargetTask,
302 TestSimulateCrashMac::kExceptionPortsTargetThread,
303 TestSimulateCrashMac::kExceptionPortsTargetBoth,
304 };
305
306 const exception_behavior_t kBehaviors[] = {
307 EXCEPTION_DEFAULT,
308 EXCEPTION_STATE,
309 EXCEPTION_STATE_IDENTITY,
310 EXCEPTION_DEFAULT | kMachExceptionCodes,
311 EXCEPTION_STATE | kMachExceptionCodes,
312 EXCEPTION_STATE_IDENTITY | kMachExceptionCodes,
313 };
314
315 const thread_state_flavor_t kFlavors[] = {
316 #if defined(ARCH_CPU_X86_FAMILY)
317 x86_THREAD_STATE,
318 x86_FLOAT_STATE,
319 x86_DEBUG_STATE,
320 #if defined(ARCH_CPU_X86)
321 x86_THREAD_STATE32,
322 x86_FLOAT_STATE32,
323 x86_DEBUG_STATE32,
324 #elif defined(ARCH_CPU_X86_64)
325 x86_THREAD_STATE64,
326 x86_FLOAT_STATE64,
327 x86_DEBUG_STATE64,
328 #endif
329 #else
330 #error Port to your CPU architecture
331 #endif
332 };
333
334 for (size_t target_index = 0;
335 target_index < arraysize(kTargets);
336 ++target_index) {
337 TestSimulateCrashMac::ExceptionPortsTarget target = kTargets[target_index];
338 SCOPED_TRACE(base::StringPrintf(
339 "target_index %zu, target %d", target_index, target));
340
341 for (size_t behavior_index = 0;
342 behavior_index < arraysize(kBehaviors);
343 ++behavior_index) {
344 exception_behavior_t behavior = kBehaviors[behavior_index];
345 SCOPED_TRACE(base::StringPrintf(
346 "behavior_index %zu, behavior %s",
347 behavior_index,
348 ExceptionBehaviorToString(behavior, kUseFullName | kUnknownIsNumeric)
349 .c_str()));
350
351 if (!ExceptionBehaviorHasState(behavior)) {
352 TestSimulateCrashMac test_simulate_crash_mac(
353 target, behavior, THREAD_STATE_NONE);
354 test_simulate_crash_mac.Run();
355 } else {
356 for (size_t flavor_index = 0;
357 flavor_index < arraysize(kFlavors);
358 ++flavor_index) {
359 thread_state_flavor_t flavor = kFlavors[flavor_index];
360 SCOPED_TRACE(base::StringPrintf(
361 "flavor_index %zu, flavor %s",
362 flavor_index,
363 ThreadStateFlavorToString(
364 flavor, kUseFullName | kUnknownIsNumeric).c_str()));
365
366 TestSimulateCrashMac test_simulate_crash_mac(
367 target, behavior, flavor);
368 test_simulate_crash_mac.Run();
369 }
370 }
371 }
372 }
373 }
374
375 } // namespace
376 } // namespace test
377 } // namespace crashpad
OLDNEW
« client/simulate_crash_mac.h ('K') | « client/simulate_crash_mac.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698