OLD | NEW |
---|---|
(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 "util/mach/exc_server_variants.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 "gmock/gmock.h" | |
23 #include "gtest/gtest.h" | |
24 #include "util/test/mac/mach_errors.h" | |
25 #include "util/test/mac/mach_multiprocess.h" | |
26 | |
27 namespace { | |
28 | |
29 using namespace crashpad; | |
30 using namespace crashpad::test; | |
31 using namespace testing; | |
32 | |
33 // Fake Mach ports. These aren’t used as ports in these tests, they’re just used | |
34 // as cookies to make sure that the correct values get passed to the correct | |
35 // places. | |
36 const mach_port_t kClientRemotePort = 0x01010101; | |
37 const mach_port_t kServerLocalPort = 0x02020202; | |
38 const mach_port_t kExceptionThreadPort = 0x03030303; | |
39 const mach_port_t kExceptionTaskPort = 0x04040404; | |
40 | |
41 // Other fake exception values. | |
42 const exception_type_t kExceptionType = EXC_BAD_ACCESS; | |
43 | |
44 // Test using an exception code with the high bit set to ensure that it gets | |
45 // promoted to the wider mach_exception_data_type_t type as a signed quantity. | |
46 const exception_data_type_t kExceptionCodes[] = { | |
47 KERN_PROTECTION_FAILURE, | |
48 static_cast<exception_data_type_t>(0xfedcba98), | |
49 }; | |
50 | |
51 const exception_data_type_t kMachExceptionCodes[] = { | |
52 KERN_PROTECTION_FAILURE, | |
53 static_cast<exception_data_type_t>(0xfedcba9876543210), | |
54 }; | |
55 | |
56 const thread_state_flavor_t kThreadStateFlavor = MACHINE_THREAD_STATE; | |
57 const mach_msg_type_number_t kThreadStateFlavorCount = | |
58 MACHINE_THREAD_STATE_COUNT; | |
59 | |
60 void InitializeMachMsgPortDescriptor(mach_msg_port_descriptor_t* descriptor, | |
61 mach_port_t port) { | |
62 descriptor->name = port; | |
63 descriptor->disposition = MACH_MSG_TYPE_MOVE_SEND; | |
64 descriptor->type = MACH_MSG_PORT_DESCRIPTOR; | |
65 } | |
66 | |
67 // The definitions of the request and reply structures from mach_exc.h aren’t | |
68 // available here. They need custom initialization code, and the reply | |
69 // structures need verification code too, so duplicate the expected definitions | |
70 // of the structures from both exc.h and mach_exc.h here in this file, and | |
71 // provide the initialization and verification code as methods in true | |
72 // object-oriented fashion. | |
73 | |
74 struct __attribute__((packed, aligned(4))) ExceptionRaiseRequest { | |
75 mach_msg_header_t Head; | |
76 mach_msg_body_t msgh_body; | |
77 mach_msg_port_descriptor_t thread; | |
78 mach_msg_port_descriptor_t task; | |
79 NDR_record_t NDR; | |
80 exception_type_t exception; | |
81 mach_msg_type_number_t codeCnt; | |
82 integer_t code[2]; | |
83 | |
84 void InitializeForTesting() { | |
85 memset(this, 0xa5, sizeof(*this)); | |
86 Head.msgh_bits = | |
87 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | | |
88 MACH_MSGH_BITS_COMPLEX; | |
89 Head.msgh_size = sizeof(*this); | |
90 Head.msgh_remote_port = kClientRemotePort; | |
91 Head.msgh_local_port = kServerLocalPort; | |
92 Head.msgh_id = 2401; | |
93 msgh_body.msgh_descriptor_count = 2; | |
94 InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); | |
95 InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); | |
96 NDR = NDR_record; | |
97 exception = kExceptionType; | |
98 codeCnt = 2; | |
99 code[0] = kExceptionCodes[0]; | |
100 code[1] = kExceptionCodes[1]; | |
101 } | |
102 }; | |
103 | |
104 struct __attribute__((packed, aligned(4))) ExceptionRaiseReply { | |
105 mach_msg_header_t Head; | |
106 NDR_record_t NDR; | |
107 kern_return_t RetCode; | |
108 | |
109 void InitializeForTesting() { | |
110 memset(this, 0x5a, sizeof(*this)); | |
111 RetCode = KERN_FAILURE; | |
112 } | |
113 | |
114 // Verify accepts a |behavior| parameter because the same message format and | |
115 // verification function is used for ExceptionRaiseReply and | |
116 // MachExceptionRaiseReply. Knowing which behavior is expected allows the | |
117 // message ID to be checked. | |
118 void Verify(exception_behavior_t behavior) { | |
119 EXPECT_EQ(static_cast<mach_msg_bits_t>( | |
120 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0)), | |
121 Head.msgh_bits); | |
122 EXPECT_EQ(sizeof(*this), Head.msgh_size); | |
123 EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port); | |
124 EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL), Head.msgh_local_port); | |
125 switch (behavior) { | |
126 case EXCEPTION_DEFAULT: | |
127 EXPECT_EQ(2501, Head.msgh_id); | |
128 break; | |
129 case static_cast<exception_behavior_t>(EXCEPTION_DEFAULT | | |
130 MACH_EXCEPTION_CODES): | |
131 EXPECT_EQ(2505, Head.msgh_id); | |
132 break; | |
133 default: | |
134 ADD_FAILURE() << "behavior " << behavior << ", Head.msgh_id " | |
135 << Head.msgh_id; | |
136 break; | |
137 } | |
138 EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR))); | |
139 EXPECT_EQ(KERN_SUCCESS, RetCode); | |
140 } | |
141 }; | |
142 | |
143 struct __attribute__((packed, aligned(4))) ExceptionRaiseStateRequest { | |
144 mach_msg_header_t Head; | |
145 NDR_record_t NDR; | |
146 exception_type_t exception; | |
147 mach_msg_type_number_t codeCnt; | |
148 integer_t code[2]; | |
149 int flavor; | |
150 mach_msg_type_number_t old_stateCnt; | |
151 natural_t old_state[THREAD_STATE_MAX]; | |
152 | |
153 void InitializeForTesting() { | |
154 memset(this, 0xa5, sizeof(*this)); | |
155 Head.msgh_bits = | |
156 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); | |
157 Head.msgh_size = sizeof(*this); | |
158 Head.msgh_remote_port = kClientRemotePort; | |
159 Head.msgh_local_port = kServerLocalPort; | |
160 Head.msgh_id = 2402; | |
161 NDR = NDR_record; | |
162 exception = kExceptionType; | |
163 codeCnt = 2; | |
164 code[0] = kExceptionCodes[0]; | |
165 code[1] = kExceptionCodes[1]; | |
166 flavor = kThreadStateFlavor; | |
167 old_stateCnt = kThreadStateFlavorCount; | |
168 | |
169 // Adjust the message size for the data that it’s actually carrying, which | |
170 // may be smaller than the maximum that it can carry. | |
171 Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); | |
172 } | |
173 }; | |
174 | |
175 struct __attribute__((packed, aligned(4))) ExceptionRaiseStateReply { | |
176 mach_msg_header_t Head; | |
177 NDR_record_t NDR; | |
178 kern_return_t RetCode; | |
179 int flavor; | |
180 mach_msg_type_number_t new_stateCnt; | |
181 natural_t new_state[THREAD_STATE_MAX]; | |
182 | |
183 void InitializeForTesting() { | |
184 memset(this, 0x5a, sizeof(*this)); | |
185 RetCode = KERN_FAILURE; | |
186 } | |
187 | |
188 // Verify accepts a |behavior| parameter because the same message format and | |
189 // verification function is used for ExceptionRaiseStateReply, | |
190 // ExceptionRaiseStateIdentityReply, MachExceptionRaiseStateReply, and | |
191 // MachExceptionRaiseStateIdentityReply. Knowing which behavior is expected | |
192 // allows the message ID to be checked. | |
193 void Verify(exception_behavior_t behavior) { | |
194 EXPECT_EQ(static_cast<mach_msg_bits_t>( | |
195 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0)), | |
196 Head.msgh_bits); | |
197 EXPECT_EQ(sizeof(*this), Head.msgh_size); | |
198 EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port); | |
199 EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL), Head.msgh_local_port); | |
200 switch (behavior) { | |
201 case EXCEPTION_STATE: | |
202 EXPECT_EQ(2502, Head.msgh_id); | |
203 break; | |
204 case EXCEPTION_STATE_IDENTITY: | |
205 EXPECT_EQ(2503, Head.msgh_id); | |
206 break; | |
207 case static_cast<exception_behavior_t>(EXCEPTION_STATE | | |
208 MACH_EXCEPTION_CODES): | |
209 EXPECT_EQ(2506, Head.msgh_id); | |
210 break; | |
211 case static_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY | | |
212 MACH_EXCEPTION_CODES): | |
213 EXPECT_EQ(2507, Head.msgh_id); | |
214 break; | |
215 default: | |
216 ADD_FAILURE() << "behavior " << behavior << ", Head.msgh_id " | |
217 << Head.msgh_id; | |
218 break; | |
219 } | |
220 EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR))); | |
221 EXPECT_EQ(KERN_SUCCESS, RetCode); | |
222 EXPECT_EQ(kThreadStateFlavor, flavor); | |
223 EXPECT_EQ(arraysize(new_state), new_stateCnt); | |
224 } | |
225 }; | |
226 | |
227 struct __attribute__((packed, aligned(4))) ExceptionRaiseStateIdentityRequest { | |
228 mach_msg_header_t Head; | |
229 mach_msg_body_t msgh_body; | |
230 mach_msg_port_descriptor_t thread; | |
231 mach_msg_port_descriptor_t task; | |
232 NDR_record_t NDR; | |
233 exception_type_t exception; | |
234 mach_msg_type_number_t codeCnt; | |
235 integer_t code[2]; | |
236 int flavor; | |
237 mach_msg_type_number_t old_stateCnt; | |
238 natural_t old_state[THREAD_STATE_MAX]; | |
239 | |
240 void InitializeForTesting() { | |
241 memset(this, 0xa5, sizeof(*this)); | |
242 Head.msgh_bits = | |
243 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | | |
244 MACH_MSGH_BITS_COMPLEX; | |
245 Head.msgh_size = sizeof(*this); | |
246 Head.msgh_remote_port = kClientRemotePort; | |
247 Head.msgh_local_port = kServerLocalPort; | |
248 Head.msgh_id = 2403; | |
249 msgh_body.msgh_descriptor_count = 2; | |
250 InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); | |
251 InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); | |
252 NDR = NDR_record; | |
253 exception = kExceptionType; | |
254 codeCnt = 2; | |
255 code[0] = kExceptionCodes[0]; | |
256 code[1] = kExceptionCodes[1]; | |
257 flavor = kThreadStateFlavor; | |
258 old_stateCnt = kThreadStateFlavorCount; | |
259 | |
260 // Adjust the message size for the data that it’s actually carrying, which | |
261 // may be smaller than the maximum that it can carry. | |
262 Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); | |
263 } | |
264 }; | |
265 | |
266 // The reply messages for exception_raise_state and | |
267 // exception_raise_state_identity are identical. | |
268 typedef ExceptionRaiseStateReply ExceptionRaiseStateIdentityReply; | |
269 | |
270 struct __attribute__((packed, aligned(4))) MachExceptionRaiseRequest { | |
271 mach_msg_header_t Head; | |
272 mach_msg_body_t msgh_body; | |
273 mach_msg_port_descriptor_t thread; | |
274 mach_msg_port_descriptor_t task; | |
275 NDR_record_t NDR; | |
276 exception_type_t exception; | |
277 mach_msg_type_number_t codeCnt; | |
278 int64_t code[2]; | |
279 | |
280 void InitializeForTesting() { | |
281 memset(this, 0xa5, sizeof(*this)); | |
282 Head.msgh_bits = | |
283 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | | |
284 MACH_MSGH_BITS_COMPLEX; | |
285 Head.msgh_size = sizeof(*this); | |
286 Head.msgh_remote_port = kClientRemotePort; | |
287 Head.msgh_local_port = kServerLocalPort; | |
288 Head.msgh_id = 2405; | |
289 msgh_body.msgh_descriptor_count = 2; | |
290 InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); | |
291 InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); | |
292 NDR = NDR_record; | |
293 exception = kExceptionType; | |
294 codeCnt = 2; | |
295 code[0] = kMachExceptionCodes[0]; | |
296 code[1] = kMachExceptionCodes[1]; | |
297 } | |
298 }; | |
299 | |
300 // The reply messages for exception_raise and mach_exception_raise are | |
301 // identical. | |
302 typedef ExceptionRaiseReply MachExceptionRaiseReply; | |
303 | |
304 struct __attribute__((packed, aligned(4))) MachExceptionRaiseStateRequest { | |
305 mach_msg_header_t Head; | |
306 NDR_record_t NDR; | |
307 exception_type_t exception; | |
308 mach_msg_type_number_t codeCnt; | |
309 int64_t code[2]; | |
310 int flavor; | |
311 mach_msg_type_number_t old_stateCnt; | |
312 natural_t old_state[THREAD_STATE_MAX]; | |
313 | |
314 void InitializeForTesting() { | |
315 memset(this, 0xa5, sizeof(*this)); | |
316 Head.msgh_bits = | |
317 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); | |
318 Head.msgh_size = sizeof(*this); | |
319 Head.msgh_remote_port = kClientRemotePort; | |
320 Head.msgh_local_port = kServerLocalPort; | |
321 Head.msgh_id = 2406; | |
322 NDR = NDR_record; | |
323 exception = kExceptionType; | |
324 codeCnt = 2; | |
325 code[0] = kMachExceptionCodes[0]; | |
326 code[1] = kMachExceptionCodes[1]; | |
327 flavor = kThreadStateFlavor; | |
328 old_stateCnt = kThreadStateFlavorCount; | |
329 | |
330 // Adjust the message size for the data that it’s actually carrying, which | |
331 // may be smaller than the maximum that it can carry. | |
332 Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); | |
333 } | |
334 }; | |
335 | |
336 // The reply messages for exception_raise_state and mach_exception_raise_state | |
337 // are identical. | |
338 typedef ExceptionRaiseStateReply MachExceptionRaiseStateReply; | |
339 | |
340 struct __attribute__((packed, | |
341 aligned(4))) MachExceptionRaiseStateIdentityRequest { | |
342 mach_msg_header_t Head; | |
343 mach_msg_body_t msgh_body; | |
344 mach_msg_port_descriptor_t thread; | |
345 mach_msg_port_descriptor_t task; | |
346 NDR_record_t NDR; | |
347 exception_type_t exception; | |
348 mach_msg_type_number_t codeCnt; | |
349 int64_t code[2]; | |
350 int flavor; | |
351 mach_msg_type_number_t old_stateCnt; | |
352 natural_t old_state[THREAD_STATE_MAX]; | |
353 | |
354 void InitializeForTesting() { | |
355 memset(this, 0xa5, sizeof(*this)); | |
356 Head.msgh_bits = | |
357 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | | |
358 MACH_MSGH_BITS_COMPLEX; | |
359 Head.msgh_size = sizeof(*this); | |
360 Head.msgh_remote_port = kClientRemotePort; | |
361 Head.msgh_local_port = kServerLocalPort; | |
362 Head.msgh_id = 2407; | |
363 msgh_body.msgh_descriptor_count = 2; | |
364 InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); | |
365 InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); | |
366 NDR = NDR_record; | |
367 exception = kExceptionType; | |
368 codeCnt = 2; | |
369 code[0] = kMachExceptionCodes[0]; | |
370 code[1] = kMachExceptionCodes[1]; | |
371 flavor = kThreadStateFlavor; | |
372 old_stateCnt = kThreadStateFlavorCount; | |
373 | |
374 // Adjust the message size for the data that it’s actually carrying, which | |
375 // may be smaller than the maximum that it can carry. | |
376 Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); | |
377 } | |
378 }; | |
379 | |
380 // The reply messages for exception_raise_state_identity and | |
381 // mach_exception_raise_state_identity are identical. | |
382 typedef ExceptionRaiseStateIdentityReply MachExceptionRaiseStateIdentityReply; | |
383 | |
384 // InvalidRequest and BadIDErrorReply are used to test that | |
385 // UniversalMachExcServer deals appropriately with messages that it does not | |
386 // understand: messages with an unknown Head.msgh_id. | |
387 | |
388 struct __attribute__((packed, aligned(4))) InvalidRequest | |
389 : public mach_msg_empty_send_t { | |
390 void InitializeForTesting(mach_msg_id_t id) { | |
391 memset(this, 0xa5, sizeof(*this)); | |
392 header.msgh_bits = | |
393 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); | |
394 header.msgh_size = sizeof(*this); | |
395 header.msgh_remote_port = kClientRemotePort; | |
396 header.msgh_local_port = kServerLocalPort; | |
397 header.msgh_id = id; | |
398 } | |
399 }; | |
400 | |
401 struct __attribute__((packed, aligned(4))) BadIDErrorReply | |
402 : public mig_reply_error_t { | |
403 void InitializeForTesting() { | |
404 memset(this, 0x5a, sizeof(*this)); | |
405 RetCode = KERN_FAILURE; | |
406 } | |
407 | |
408 void Verify(mach_msg_id_t id) { | |
409 EXPECT_EQ(static_cast<mach_msg_bits_t>( | |
410 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0)), | |
411 Head.msgh_bits); | |
412 EXPECT_EQ(sizeof(*this), Head.msgh_size); | |
413 EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port); | |
414 EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL), Head.msgh_local_port); | |
415 EXPECT_EQ(id + 100, Head.msgh_id); | |
416 EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR))); | |
417 EXPECT_EQ(MIG_BAD_ID, RetCode); | |
418 } | |
419 }; | |
420 | |
421 class MockUniversalMachExcServer : public UniversalMachExcServer { | |
422 public: | |
423 struct ConstExceptionCodes { | |
424 const mach_exception_data_type_t* code; | |
425 mach_msg_type_number_t code_count; | |
426 }; | |
427 struct ThreadState { | |
428 thread_state_t state; | |
429 mach_msg_type_number_t* state_count; | |
430 }; | |
431 struct ConstThreadState { | |
432 const natural_t* state; | |
433 mach_msg_type_number_t* state_count; | |
434 }; | |
435 | |
436 // CatchMachException is the method to mock, but it has 13 parameters, and | |
437 // gmock can only mock methods with up to 10 parameters. Coalesce some related | |
438 // parameters together into structs, and call a mocked method. | |
439 virtual kern_return_t CatchMachException( | |
440 exception_behavior_t behavior, | |
441 exception_handler_t exception_port, | |
442 thread_t thread, | |
443 task_t task, | |
444 exception_type_t exception, | |
445 const mach_exception_data_type_t* code, | |
446 mach_msg_type_number_t code_count, | |
447 thread_state_flavor_t* flavor, | |
448 const natural_t* old_state, | |
449 mach_msg_type_number_t old_state_count, | |
450 thread_state_t new_state, | |
451 mach_msg_type_number_t* new_state_count, | |
452 bool* destroy_complex_request) override { | |
453 *destroy_complex_request = true; | |
454 const ConstExceptionCodes exception_codes = {code, code_count}; | |
455 const ConstThreadState old_thread_state = {old_state, &old_state_count}; | |
456 ThreadState new_thread_state = {new_state, new_state_count}; | |
457 return MockCatchMachException(behavior, | |
458 exception_port, | |
459 thread, | |
460 task, | |
461 exception, | |
462 &exception_codes, | |
463 flavor, | |
464 &old_thread_state, | |
465 &new_thread_state); | |
466 } | |
467 | |
468 MOCK_METHOD9(MockCatchMachException, | |
469 kern_return_t(exception_behavior_t behavior, | |
470 exception_handler_t exception_port, | |
471 thread_t thread, | |
472 task_t task, | |
473 exception_type_t exception, | |
474 const ConstExceptionCodes* exception_codes, | |
475 thread_state_flavor_t* flavor, | |
476 const ConstThreadState* old_thread_state, | |
477 ThreadState* new_thread_state)); | |
478 }; | |
479 | |
480 // Matcher for ConstExceptionCodes, testing that it carries 2 codes matching | |
481 // code_0 and code_1. | |
482 MATCHER_P2(AreExceptionCodes, code_0, code_1, "") { | |
483 if (!arg) { | |
484 return false; | |
485 } | |
486 | |
487 if (arg->code_count == 2 && arg->code[0] == code_0 && | |
488 arg->code[1] == code_1) { | |
489 return true; | |
490 } | |
491 | |
492 *result_listener << "codes ("; | |
493 for (size_t index = 0; index < arg->code_count; ++index) { | |
494 *result_listener << arg->code[index]; | |
495 if (index < arg->code_count - 1) { | |
496 *result_listener << ", "; | |
497 } | |
498 } | |
499 *result_listener << ")"; | |
500 | |
501 return false; | |
502 } | |
503 | |
504 // Matcher for ThreadState and ConstThreadState, testing that *state_count is | |
505 // present and matches the specified value. If 0 is specified for the count, | |
506 // state must be NULL (not present), otherwise state must be non-NULL (present). | |
507 MATCHER_P(IsThreadStateCount, state_count, "") { | |
508 if (!arg) { | |
509 return false; | |
510 } | |
511 if (!arg->state_count) { | |
512 *result_listener << "state_count NULL"; | |
513 return false; | |
514 } | |
515 if (*(arg->state_count) != state_count) { | |
516 *result_listener << "*state_count " << *(arg->state_count); | |
517 return false; | |
518 } | |
519 if (state_count) { | |
520 if (!arg->state) { | |
521 *result_listener << "*state_count " << state_count << ", state NULL"; | |
522 return false; | |
523 } | |
524 } else { | |
525 if (arg->state) { | |
526 *result_listener << "*state_count 0, state non-NULL (" << arg->state | |
527 << ")"; | |
528 return false; | |
529 } | |
530 } | |
531 return true; | |
532 } | |
533 | |
534 template <typename T> | |
535 class ScopedDefaultValue { | |
536 public: | |
537 explicit ScopedDefaultValue(const T& default_value) { | |
538 DefaultValue<T>::Set(default_value); | |
Robert Sesek
2014/09/10 16:44:41
Where's DefaultValue defined?
Mark Mentovai
2014/09/10 20:12:56
rsesek wrote:
| |
539 } | |
540 | |
541 ~ScopedDefaultValue() { DefaultValue<T>::Clear(); } | |
542 }; | |
543 | |
544 TEST(ExcServerVariants, MockExceptionRaise) { | |
545 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); | |
Robert Sesek
2014/09/10 16:44:41
… I don't understand how this is used.
| |
546 | |
547 MockUniversalMachExcServer server; | |
548 | |
549 ExceptionRaiseRequest request; | |
550 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); | |
551 request.InitializeForTesting(); | |
552 | |
553 ExceptionRaiseReply reply; | |
554 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); | |
555 reply.InitializeForTesting(); | |
556 | |
557 const exception_behavior_t kExceptionBehavior = EXCEPTION_DEFAULT; | |
558 | |
559 EXPECT_CALL(server, | |
560 MockCatchMachException( | |
561 kExceptionBehavior, | |
562 kServerLocalPort, | |
563 kExceptionThreadPort, | |
564 kExceptionTaskPort, | |
565 kExceptionType, | |
566 AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]), | |
567 Pointee(Eq(THREAD_STATE_NONE)), | |
568 IsThreadStateCount(0u), | |
569 IsThreadStateCount(0u))) | |
570 .WillOnce(Return(KERN_SUCCESS)) | |
571 .RetiresOnSaturation(); | |
572 | |
573 bool destroy_complex_request = false; | |
574 EXPECT_TRUE(server.MachMessageServerFunction( | |
575 reinterpret_cast<mach_msg_header_t*>(&request), | |
576 reinterpret_cast<mach_msg_header_t*>(&reply), | |
577 &destroy_complex_request)); | |
578 EXPECT_TRUE(destroy_complex_request); | |
579 | |
580 reply.Verify(kExceptionBehavior); | |
581 } | |
582 | |
583 TEST(ExcServerVariants, MockExceptionRaiseState) { | |
584 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); | |
585 | |
586 MockUniversalMachExcServer server; | |
587 | |
588 ExceptionRaiseStateRequest request; | |
589 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); | |
590 request.InitializeForTesting(); | |
591 | |
592 ExceptionRaiseStateReply reply; | |
593 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); | |
594 reply.InitializeForTesting(); | |
595 | |
596 const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE; | |
597 | |
598 EXPECT_CALL(server, | |
599 MockCatchMachException( | |
600 kExceptionBehavior, | |
601 kServerLocalPort, | |
602 MACH_PORT_NULL, | |
603 MACH_PORT_NULL, | |
604 kExceptionType, | |
605 AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]), | |
606 Pointee(Eq(kThreadStateFlavor)), | |
607 IsThreadStateCount(kThreadStateFlavorCount), | |
608 IsThreadStateCount(arraysize(reply.new_state)))) | |
609 .WillOnce(Return(KERN_SUCCESS)) | |
610 .RetiresOnSaturation(); | |
611 | |
612 bool destroy_complex_request = false; | |
613 EXPECT_TRUE(server.MachMessageServerFunction( | |
614 reinterpret_cast<mach_msg_header_t*>(&request), | |
615 reinterpret_cast<mach_msg_header_t*>(&reply), | |
616 &destroy_complex_request)); | |
617 | |
618 // The request wasn’t complex, so nothing got a chance to change the value of | |
619 // this variable. | |
620 EXPECT_FALSE(destroy_complex_request); | |
621 | |
622 reply.Verify(kExceptionBehavior); | |
623 } | |
624 | |
625 TEST(ExcServerVariants, MockExceptionRaiseStateIdentity) { | |
626 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); | |
627 | |
628 MockUniversalMachExcServer server; | |
629 | |
630 ExceptionRaiseStateIdentityRequest request; | |
631 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); | |
632 request.InitializeForTesting(); | |
633 | |
634 ExceptionRaiseStateIdentityReply reply; | |
635 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); | |
636 reply.InitializeForTesting(); | |
637 | |
638 const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE_IDENTITY; | |
639 | |
640 EXPECT_CALL(server, | |
641 MockCatchMachException( | |
642 kExceptionBehavior, | |
643 kServerLocalPort, | |
644 kExceptionThreadPort, | |
645 kExceptionTaskPort, | |
646 kExceptionType, | |
647 AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]), | |
648 Pointee(Eq(kThreadStateFlavor)), | |
649 IsThreadStateCount(kThreadStateFlavorCount), | |
650 IsThreadStateCount(arraysize(reply.new_state)))) | |
651 .WillOnce(Return(KERN_SUCCESS)) | |
652 .RetiresOnSaturation(); | |
653 | |
654 bool destroy_complex_request = false; | |
655 EXPECT_TRUE(server.MachMessageServerFunction( | |
656 reinterpret_cast<mach_msg_header_t*>(&request), | |
657 reinterpret_cast<mach_msg_header_t*>(&reply), | |
658 &destroy_complex_request)); | |
659 EXPECT_TRUE(destroy_complex_request); | |
660 | |
661 reply.Verify(kExceptionBehavior); | |
662 } | |
663 | |
664 TEST(ExcServerVariants, MockMachExceptionRaise) { | |
665 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); | |
666 | |
667 MockUniversalMachExcServer server; | |
668 | |
669 MachExceptionRaiseRequest request; | |
670 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); | |
671 request.InitializeForTesting(); | |
672 | |
673 MachExceptionRaiseReply reply; | |
674 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); | |
675 reply.InitializeForTesting(); | |
676 | |
677 const exception_behavior_t kExceptionBehavior = | |
678 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES; | |
679 | |
680 EXPECT_CALL( | |
681 server, | |
682 MockCatchMachException( | |
683 kExceptionBehavior, | |
684 kServerLocalPort, | |
685 kExceptionThreadPort, | |
686 kExceptionTaskPort, | |
687 kExceptionType, | |
688 AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]), | |
689 Pointee(Eq(THREAD_STATE_NONE)), | |
690 IsThreadStateCount(0u), | |
691 IsThreadStateCount(0u))) | |
692 .WillOnce(Return(KERN_SUCCESS)) | |
693 .RetiresOnSaturation(); | |
694 | |
695 bool destroy_complex_request = false; | |
696 EXPECT_TRUE(server.MachMessageServerFunction( | |
697 reinterpret_cast<mach_msg_header_t*>(&request), | |
698 reinterpret_cast<mach_msg_header_t*>(&reply), | |
699 &destroy_complex_request)); | |
700 EXPECT_TRUE(destroy_complex_request); | |
701 | |
702 reply.Verify(kExceptionBehavior); | |
703 } | |
704 | |
705 TEST(ExcServerVariants, MockMachExceptionRaiseState) { | |
706 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); | |
707 | |
708 MockUniversalMachExcServer server; | |
709 | |
710 MachExceptionRaiseStateRequest request; | |
711 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); | |
712 request.InitializeForTesting(); | |
713 | |
714 MachExceptionRaiseStateReply reply; | |
715 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); | |
716 reply.InitializeForTesting(); | |
717 | |
718 const exception_behavior_t kExceptionBehavior = | |
719 EXCEPTION_STATE | MACH_EXCEPTION_CODES; | |
720 | |
721 EXPECT_CALL( | |
722 server, | |
723 MockCatchMachException( | |
724 kExceptionBehavior, | |
725 kServerLocalPort, | |
726 MACH_PORT_NULL, | |
727 MACH_PORT_NULL, | |
728 kExceptionType, | |
729 AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]), | |
730 Pointee(Eq(kThreadStateFlavor)), | |
731 IsThreadStateCount(kThreadStateFlavorCount), | |
732 IsThreadStateCount(arraysize(reply.new_state)))) | |
733 .WillOnce(Return(KERN_SUCCESS)) | |
734 .RetiresOnSaturation(); | |
735 | |
736 bool destroy_complex_request = false; | |
737 EXPECT_TRUE(server.MachMessageServerFunction( | |
738 reinterpret_cast<mach_msg_header_t*>(&request), | |
739 reinterpret_cast<mach_msg_header_t*>(&reply), | |
740 &destroy_complex_request)); | |
741 | |
742 // The request wasn’t complex, so nothing got a chance to change the value of | |
743 // this variable. | |
744 EXPECT_FALSE(destroy_complex_request); | |
745 | |
746 reply.Verify(kExceptionBehavior); | |
747 } | |
748 | |
749 TEST(ExcServerVariants, MockMachExceptionRaiseStateIdentity) { | |
750 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); | |
751 | |
752 MockUniversalMachExcServer server; | |
753 | |
754 MachExceptionRaiseStateIdentityRequest request; | |
755 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); | |
756 request.InitializeForTesting(); | |
757 | |
758 MachExceptionRaiseStateIdentityReply reply; | |
759 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); | |
760 reply.InitializeForTesting(); | |
761 | |
762 const exception_behavior_t kExceptionBehavior = | |
763 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES; | |
764 | |
765 EXPECT_CALL( | |
766 server, | |
767 MockCatchMachException( | |
768 kExceptionBehavior, | |
769 kServerLocalPort, | |
770 kExceptionThreadPort, | |
771 kExceptionTaskPort, | |
772 kExceptionType, | |
773 AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]), | |
774 Pointee(Eq(kThreadStateFlavor)), | |
775 IsThreadStateCount(kThreadStateFlavorCount), | |
776 IsThreadStateCount(arraysize(reply.new_state)))) | |
777 .WillOnce(Return(KERN_SUCCESS)) | |
778 .RetiresOnSaturation(); | |
779 | |
780 bool destroy_complex_request = false; | |
781 EXPECT_TRUE(server.MachMessageServerFunction( | |
782 reinterpret_cast<mach_msg_header_t*>(&request), | |
783 reinterpret_cast<mach_msg_header_t*>(&reply), | |
784 &destroy_complex_request)); | |
785 EXPECT_TRUE(destroy_complex_request); | |
786 | |
787 reply.Verify(kExceptionBehavior); | |
788 } | |
789 | |
790 TEST(ExcServerVariants, MockUnknownID) { | |
791 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); | |
792 | |
793 MockUniversalMachExcServer server; | |
794 | |
795 // Make sure that a message with an unknown ID is handled appropriately. | |
796 // UniversalMachExcServer should not dispatch the message to | |
797 // MachMessageServerFunction, but should generate a MIG_BAD_ID error reply. | |
798 | |
799 const mach_msg_id_t unknown_ids[] = { | |
800 // Reasonable things to check. | |
801 -101, | |
802 -100, | |
803 -99, | |
804 -1, | |
805 0, | |
806 1, | |
807 99, | |
808 100, | |
809 101, | |
810 | |
811 // Invalid IDs right around valid ones. | |
812 2400, | |
813 2404, | |
814 2408, | |
815 | |
816 // Valid and invalid IDs in the range used for replies, not requests. | |
817 2500, | |
818 2501, | |
819 2502, | |
820 2503, | |
821 2504, | |
822 2505, | |
823 2506, | |
824 2507, | |
825 2508, | |
826 }; | |
827 | |
828 for (size_t index = 0; index < arraysize(unknown_ids); ++index) { | |
829 mach_msg_id_t id = unknown_ids[index]; | |
830 | |
831 SCOPED_TRACE(base::StringPrintf("unknown id %d", id)); | |
832 | |
833 InvalidRequest request; | |
834 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); | |
835 request.InitializeForTesting(id); | |
836 | |
837 BadIDErrorReply reply; | |
838 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); | |
839 reply.InitializeForTesting(); | |
840 | |
841 bool destroy_complex_request = false; | |
842 EXPECT_FALSE(server.MachMessageServerFunction( | |
843 reinterpret_cast<mach_msg_header_t*>(&request), | |
844 reinterpret_cast<mach_msg_header_t*>(&reply), | |
845 &destroy_complex_request)); | |
846 | |
847 // The request wasn’t handled, nothing got a chance to change the value of | |
848 // this variable. MachMessageServer would destroy the request if it was | |
849 // complex, regardless of what was done to this variable, because the | |
850 // return code was not KERN_SUCCESS or MIG_NO_REPLY. | |
851 EXPECT_FALSE(destroy_complex_request); | |
852 | |
853 reply.Verify(id); | |
854 } | |
855 } | |
856 | |
857 class TestExcServerVariants : public UniversalMachExcServer, | |
858 public MachMultiprocess { | |
859 public: | |
860 TestExcServerVariants(exception_behavior_t behavior, | |
861 thread_state_flavor_t flavor, | |
862 mach_msg_type_number_t state_count) | |
863 : UniversalMachExcServer(), | |
864 MachMultiprocess(), | |
865 behavior_(behavior), | |
866 flavor_(flavor), | |
867 state_count_(state_count), | |
868 handled_(false) { | |
869 } | |
870 | |
871 // UniversalMachExcServer: | |
872 | |
873 virtual kern_return_t CatchMachException( | |
874 exception_behavior_t behavior, | |
875 exception_handler_t exception_port, | |
876 thread_t thread, | |
877 task_t task, | |
878 exception_type_t exception, | |
879 const mach_exception_data_type_t* code, | |
880 mach_msg_type_number_t code_count, | |
881 thread_state_flavor_t* flavor, | |
882 const natural_t* old_state, | |
883 mach_msg_type_number_t old_state_count, | |
884 thread_state_t new_state, | |
885 mach_msg_type_number_t* new_state_count, | |
886 bool* destroy_complex_request) override { | |
887 *destroy_complex_request = true; | |
888 | |
889 EXPECT_FALSE(handled_); | |
890 handled_ = true; | |
891 | |
892 EXPECT_EQ(behavior_, behavior); | |
893 exception_behavior_t basic_behavior = behavior & ~MACH_EXCEPTION_CODES; | |
894 const bool has_identity = basic_behavior == EXCEPTION_DEFAULT || | |
895 basic_behavior == EXCEPTION_STATE_IDENTITY; | |
896 const bool has_state = basic_behavior == EXCEPTION_STATE || | |
897 basic_behavior == EXCEPTION_STATE_IDENTITY; | |
898 | |
899 EXPECT_EQ(LocalPort(), exception_port); | |
900 | |
901 if (has_identity) { | |
902 EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), thread); | |
Robert Sesek
2014/09/10 16:44:41
You write this cast enough that it could be a hand
Mark Mentovai
2014/09/10 20:12:56
rsesek wrote:
| |
903 EXPECT_EQ(ChildTask(), task); | |
904 } else { | |
905 EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL), thread); | |
906 EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL), task); | |
907 } | |
908 | |
909 EXPECT_EQ(EXC_CRASH, exception); | |
910 EXPECT_EQ(2u, code_count); | |
911 | |
912 if (code_count > 1) { | |
913 // The signal that terminated the process is stored in code[0] along with | |
914 // some other data. See 10.9.4 xnu-2422.110.17/bsd/kern/kern_exit.c | |
915 // proc_prepareexit(). | |
916 int sig = (code[0] >> 24) & 0xff; | |
917 SetExpectedChildTermination(kTerminationSignal, sig); | |
918 } | |
919 | |
920 if (has_state) { | |
921 EXPECT_EQ(flavor_, *flavor); | |
922 EXPECT_EQ(state_count_, old_state_count); | |
923 EXPECT_NE(static_cast<const natural_t*>(NULL), old_state); | |
924 EXPECT_EQ(static_cast<mach_msg_type_number_t>(THREAD_STATE_MAX), | |
925 *new_state_count); | |
926 EXPECT_NE(static_cast<const natural_t*>(NULL), new_state); | |
927 } else { | |
928 EXPECT_EQ(THREAD_STATE_NONE, *flavor); | |
929 EXPECT_EQ(0u, old_state_count); | |
930 EXPECT_EQ(NULL, old_state); | |
931 EXPECT_EQ(0u, *new_state_count); | |
932 EXPECT_EQ(NULL, new_state); | |
933 } | |
934 | |
935 // Even for an EXC_CRASH handler, returning KERN_SUCCESS with a | |
936 // state-carrying reply will cause the kernel to try to set a new thread | |
937 // state, leading to a perceptible waste of time. Returning | |
938 // MACH_RCV_PORT_DIED is the only way to suppress this behavior while also | |
939 // preventing the kernel from looking for another (host-level) EXC_CRASH | |
940 // handler. See 10.9.4 xnu-2422.110.17/osfmk/kern/exception.c | |
941 // exception_triage(). | |
942 return has_state ? MACH_RCV_PORT_DIED : KERN_SUCCESS; | |
943 } | |
944 | |
945 | |
Robert Sesek
2014/09/10 16:44:41
nit: double blank line
| |
946 private: | |
947 // MachMultiprocess: | |
948 | |
949 virtual void MachMultiprocessParent() override { | |
950 kern_return_t kr = MachMessageServer::Run(this, | |
951 LocalPort(), | |
952 MACH_MSG_OPTION_NONE, | |
953 MachMessageServer::kOneShot, | |
954 MachMessageServer::kBlocking, | |
955 0); | |
956 EXPECT_EQ(KERN_SUCCESS, kr) | |
957 << MachErrorMessage(kr, "MachMessageServer::Run"); | |
958 | |
959 EXPECT_TRUE(handled_); | |
960 } | |
961 | |
962 virtual void MachMultiprocessChild() override { | |
963 // Set the parent as the exception handler for EXC_CRASH. | |
964 kern_return_t kr = task_set_exception_ports( | |
965 mach_task_self(), EXC_MASK_CRASH, RemotePort(), behavior_, flavor_); | |
966 ASSERT_EQ(KERN_SUCCESS, kr) | |
967 << MachErrorMessage(kr, "task_set_exception_ports"); | |
968 | |
969 // Now crash. | |
970 __builtin_trap(); | |
971 } | |
972 | |
973 exception_behavior_t behavior_; | |
974 thread_state_flavor_t flavor_; | |
975 mach_msg_type_number_t state_count_; | |
976 bool handled_; | |
977 | |
978 DISALLOW_COPY_AND_ASSIGN(TestExcServerVariants); | |
979 }; | |
980 | |
981 TEST(ExcServerVariants, ExceptionRaise) { | |
982 TestExcServerVariants test_exc_server_variants( | |
983 EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0); | |
984 test_exc_server_variants.Run(); | |
985 } | |
986 | |
987 TEST(ExcServerVariants, ExceptionRaiseState) { | |
988 TestExcServerVariants test_exc_server_variants( | |
989 EXCEPTION_STATE, MACHINE_THREAD_STATE, MACHINE_THREAD_STATE_COUNT); | |
990 test_exc_server_variants.Run(); | |
991 } | |
992 | |
993 TEST(ExcServerVariants, ExceptionRaiseStateIdentity) { | |
994 TestExcServerVariants test_exc_server_variants(EXCEPTION_STATE_IDENTITY, | |
995 MACHINE_THREAD_STATE, | |
996 MACHINE_THREAD_STATE_COUNT); | |
997 test_exc_server_variants.Run(); | |
998 } | |
999 | |
1000 TEST(ExcServerVariants, MachExceptionRaise) { | |
1001 TestExcServerVariants test_exc_server_variants( | |
1002 MACH_EXCEPTION_CODES | EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0); | |
1003 test_exc_server_variants.Run(); | |
1004 } | |
1005 | |
1006 TEST(ExcServerVariants, MachExceptionRaiseState) { | |
1007 TestExcServerVariants test_exc_server_variants( | |
1008 MACH_EXCEPTION_CODES | EXCEPTION_STATE, | |
1009 MACHINE_THREAD_STATE, | |
1010 MACHINE_THREAD_STATE_COUNT); | |
1011 test_exc_server_variants.Run(); | |
1012 } | |
1013 | |
1014 TEST(ExcServerVariants, MachExceptionRaiseStateIdentity) { | |
1015 TestExcServerVariants test_exc_server_variants( | |
1016 MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY, | |
1017 MACHINE_THREAD_STATE, | |
1018 MACHINE_THREAD_STATE_COUNT); | |
1019 test_exc_server_variants.Run(); | |
1020 } | |
1021 | |
1022 TEST(ExcServerVariants, ThreadStates) { | |
1023 // So far, all of the tests worked with MACHINE_THREAD_STATE. Now try all of | |
1024 // the other thread state flavors that are expected to work. | |
1025 | |
1026 struct TestData { | |
1027 thread_state_flavor_t flavor; | |
1028 mach_msg_type_number_t count; | |
1029 }; | |
1030 const TestData test_data[] = { | |
1031 #if defined(ARCH_CPU_X86_FAMILY) | |
1032 #if defined(ARCH_CPU_X86) | |
1033 { x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT }, | |
1034 { x86_FLOAT_STATE32, x86_FLOAT_STATE32_COUNT }, | |
1035 { x86_EXCEPTION_STATE32, x86_EXCEPTION_STATE32_COUNT }, | |
1036 { x86_DEBUG_STATE32, x86_DEBUG_STATE32_COUNT }, | |
1037 // Don’t test x86_AVX_STATE32 because it’s not available on all CPUs and | |
1038 // OS versionns. | |
1039 #endif | |
1040 #if defined(ARCH_CPU_X86_64) | |
1041 { x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT }, | |
1042 { x86_FLOAT_STATE64, x86_FLOAT_STATE64_COUNT }, | |
1043 { x86_EXCEPTION_STATE64, x86_EXCEPTION_STATE64_COUNT }, | |
1044 { x86_DEBUG_STATE64, x86_DEBUG_STATE64_COUNT }, | |
1045 // Don’t test x86_AVX_STATE64 because it’s not available on all CPUs and | |
1046 // OS versions. | |
1047 #endif | |
1048 { x86_THREAD_STATE, x86_THREAD_STATE_COUNT }, | |
1049 { x86_FLOAT_STATE, x86_FLOAT_STATE_COUNT }, | |
1050 { x86_EXCEPTION_STATE, x86_EXCEPTION_STATE_COUNT }, | |
1051 { x86_DEBUG_STATE, x86_DEBUG_STATE_COUNT }, | |
1052 // Don’t test x86_AVX_STATE because it’s not available on all CPUs and OS | |
1053 // versions. | |
1054 #else | |
1055 #error Port this test to your CPU architecture. | |
1056 #endif | |
1057 }; | |
1058 | |
1059 for (size_t index = 0; index < arraysize(test_data); ++index) { | |
1060 const TestData& test = test_data[index]; | |
1061 SCOPED_TRACE(base::StringPrintf( | |
1062 "index %zu, flavor %d", index, test.flavor)); | |
1063 | |
1064 TestExcServerVariants test_exc_server_variants( | |
1065 MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY, | |
1066 test.flavor, | |
1067 test.count); | |
1068 test_exc_server_variants.Run(); | |
1069 } | |
1070 } | |
1071 | |
1072 } // namespace | |
OLD | NEW |