exception_handler_test.cc
1 // Copyright 2010 Google LLC 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google LLC nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 // exception_handler_test.cc: Unit tests for google_breakpad::ExceptionHandler 30 31 #ifdef HAVE_CONFIG_H 32 #include <config.h> // Must come first 33 #endif 34 35 #include <pthread.h> 36 #include <sys/mman.h> 37 #include <sys/stat.h> 38 #include <unistd.h> 39 40 #include "breakpad_googletest_includes.h" 41 #include "client/mac/handler/exception_handler.h" 42 #include "common/linux/ignore_ret.h" 43 #include "common/mac/MachIPC.h" 44 #include "common/tests/auto_tempdir.h" 45 #include "google_breakpad/processor/minidump.h" 46 47 namespace google_breakpad { 48 // This acts as the log sink for INFO logging from the processor 49 // logging code. The logging output confuses XCode and makes it think 50 // there are unit test failures. testlogging.h handles the overriding. 51 std::ostringstream info_log; 52 } 53 54 namespace { 55 using std::string; 56 using google_breakpad::AutoTempDir; 57 using google_breakpad::ExceptionHandler; 58 using google_breakpad::MachPortSender; 59 using google_breakpad::MachReceiveMessage; 60 using google_breakpad::MachSendMessage; 61 using google_breakpad::Minidump; 62 using google_breakpad::MinidumpContext; 63 using google_breakpad::MinidumpException; 64 using google_breakpad::MinidumpMemoryList; 65 using google_breakpad::MinidumpMemoryRegion; 66 using google_breakpad::ReceivePort; 67 using testing::Test; 68 69 class ExceptionHandlerTest : public Test { 70 public: 71 void InProcessCrash(bool aborting); 72 AutoTempDir tempDir; 73 string lastDumpName; 74 }; 75 76 static void Crasher() { 77 int* a = (int*)0x42; 78 79 fprintf(stdout, "Going to crash...\n"); 80 fprintf(stdout, "A = %d", *a); 81 } 82 83 static void AbortCrasher() { 84 fprintf(stdout, "Going to crash...\n"); 85 abort(); 86 } 87 88 static void SoonToCrash(void(*crasher)()) { 89 crasher(); 90 } 91 92 static bool MDCallback(const char* dump_dir, const char* file_name, 93 void* context, bool success) { 94 string path(dump_dir); 95 path.append("/"); 96 path.append(file_name); 97 path.append(".dmp"); 98 99 int fd = *reinterpret_cast<int*>(context); 100 IGNORE_RET(write(fd, path.c_str(), path.length() + 1)); 101 close(fd); 102 exit(0); 103 // not reached 104 return true; 105 } 106 107 void ExceptionHandlerTest::InProcessCrash(bool aborting) { 108 // Give the child process a pipe to report back on. 109 int fds[2]; 110 ASSERT_EQ(0, pipe(fds)); 111 // Fork off a child process so it can crash. 112 pid_t pid = fork(); 113 if (pid == 0) { 114 // In the child process. 115 close(fds[0]); 116 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); 117 // crash 118 SoonToCrash(aborting ? &AbortCrasher : &Crasher); 119 // not reached 120 exit(1); 121 } 122 // In the parent process. 123 ASSERT_NE(-1, pid); 124 // Wait for the background process to return the minidump file. 125 close(fds[1]); 126 char minidump_file[PATH_MAX]; 127 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); 128 ASSERT_NE(0, nbytes); 129 130 Minidump minidump(minidump_file); 131 ASSERT_TRUE(minidump.Read()); 132 133 MinidumpException* exception = minidump.GetException(); 134 ASSERT_TRUE(exception); 135 136 const MDRawExceptionStream* raw_exception = exception->exception(); 137 ASSERT_TRUE(raw_exception); 138 139 if (aborting) { 140 EXPECT_EQ(MD_EXCEPTION_MAC_SOFTWARE, 141 raw_exception->exception_record.exception_code); 142 EXPECT_EQ(MD_EXCEPTION_CODE_MAC_ABORT, 143 raw_exception->exception_record.exception_flags); 144 } else { 145 EXPECT_EQ(MD_EXCEPTION_MAC_BAD_ACCESS, 146 raw_exception->exception_record.exception_code); 147 #if defined(__x86_64__) 148 EXPECT_EQ(MD_EXCEPTION_CODE_MAC_INVALID_ADDRESS, 149 raw_exception->exception_record.exception_flags); 150 #elif defined(__i386__) 151 EXPECT_EQ(MD_EXCEPTION_CODE_MAC_PROTECTION_FAILURE, 152 raw_exception->exception_record.exception_flags); 153 #endif 154 } 155 156 const MinidumpContext* context = exception->GetContext(); 157 ASSERT_TRUE(context); 158 159 uint64_t instruction_pointer; 160 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); 161 162 // Ideally would like to sanity check that abort() is on the stack 163 // but that's hard. 164 MinidumpMemoryList* memory_list = minidump.GetMemoryList(); 165 ASSERT_TRUE(memory_list); 166 MinidumpMemoryRegion* region = 167 memory_list->GetMemoryRegionForAddress(instruction_pointer); 168 EXPECT_TRUE(region); 169 170 // Child process should have exited with a zero status. 171 int ret; 172 ASSERT_EQ(pid, waitpid(pid, &ret, 0)); 173 EXPECT_NE(0, WIFEXITED(ret)); 174 EXPECT_EQ(0, WEXITSTATUS(ret)); 175 } 176 177 TEST_F(ExceptionHandlerTest, InProcess) { 178 InProcessCrash(false); 179 } 180 181 TEST_F(ExceptionHandlerTest, InProcessAbort) { 182 InProcessCrash(true); 183 } 184 185 static bool DumpNameMDCallback(const char* dump_dir, const char* file_name, 186 void* context, bool success) { 187 ExceptionHandlerTest* self = reinterpret_cast<ExceptionHandlerTest*>(context); 188 if (dump_dir && file_name) { 189 self->lastDumpName = dump_dir; 190 self->lastDumpName += "/"; 191 self->lastDumpName += file_name; 192 self->lastDumpName += ".dmp"; 193 } 194 return true; 195 } 196 197 TEST_F(ExceptionHandlerTest, WriteMinidump) { 198 ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true, 199 NULL); 200 ASSERT_TRUE(eh.WriteMinidump()); 201 202 // Ensure that minidump file exists and is > 0 bytes. 203 ASSERT_FALSE(lastDumpName.empty()); 204 struct stat st; 205 ASSERT_EQ(0, stat(lastDumpName.c_str(), &st)); 206 ASSERT_LT(0, st.st_size); 207 208 // The minidump should not contain an exception stream. 209 Minidump minidump(lastDumpName); 210 ASSERT_TRUE(minidump.Read()); 211 212 MinidumpException* exception = minidump.GetException(); 213 EXPECT_FALSE(exception); 214 } 215 216 TEST_F(ExceptionHandlerTest, WriteMinidumpWithException) { 217 ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true, 218 NULL); 219 ASSERT_TRUE(eh.WriteMinidump(true)); 220 221 // Ensure that minidump file exists and is > 0 bytes. 222 ASSERT_FALSE(lastDumpName.empty()); 223 struct stat st; 224 ASSERT_EQ(0, stat(lastDumpName.c_str(), &st)); 225 ASSERT_LT(0, st.st_size); 226 227 // The minidump should contain an exception stream. 228 Minidump minidump(lastDumpName); 229 ASSERT_TRUE(minidump.Read()); 230 231 MinidumpException* exception = minidump.GetException(); 232 ASSERT_TRUE(exception); 233 const MDRawExceptionStream* raw_exception = exception->exception(); 234 ASSERT_TRUE(raw_exception); 235 236 EXPECT_EQ(MD_EXCEPTION_MAC_BREAKPOINT, 237 raw_exception->exception_record.exception_code); 238 } 239 240 TEST_F(ExceptionHandlerTest, DumpChildProcess) { 241 const int kTimeoutMs = 2000; 242 // Create a mach port to receive the child task on. 243 char machPortName[128]; 244 sprintf(machPortName, "ExceptionHandlerTest.%d", getpid()); 245 ReceivePort parent_recv_port(machPortName); 246 247 // Give the child process a pipe to block on. 248 int fds[2]; 249 ASSERT_EQ(0, pipe(fds)); 250 251 // Fork off a child process to dump. 252 pid_t pid = fork(); 253 if (pid == 0) { 254 // In the child process 255 close(fds[1]); 256 257 // Send parent process the task and thread ports. 258 MachSendMessage child_message(0); 259 child_message.AddDescriptor(mach_task_self()); 260 child_message.AddDescriptor(mach_thread_self()); 261 262 MachPortSender child_sender(machPortName); 263 if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) 264 exit(1); 265 266 // Wait for the parent process. 267 uint8_t data; 268 read(fds[0], &data, 1); 269 exit(0); 270 } 271 // In the parent process. 272 ASSERT_NE(-1, pid); 273 close(fds[0]); 274 275 // Read the child's task and thread ports. 276 MachReceiveMessage child_message; 277 ASSERT_EQ(KERN_SUCCESS, 278 parent_recv_port.WaitForMessage(&child_message, kTimeoutMs)); 279 mach_port_t child_task = child_message.GetTranslatedPort(0); 280 mach_port_t child_thread = child_message.GetTranslatedPort(1); 281 ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task); 282 ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_thread); 283 284 // Write a minidump of the child process. 285 bool result = ExceptionHandler::WriteMinidumpForChild(child_task, 286 child_thread, 287 tempDir.path(), 288 DumpNameMDCallback, 289 this); 290 ASSERT_EQ(true, result); 291 292 // Ensure that minidump file exists and is > 0 bytes. 293 ASSERT_FALSE(lastDumpName.empty()); 294 struct stat st; 295 ASSERT_EQ(0, stat(lastDumpName.c_str(), &st)); 296 ASSERT_LT(0, st.st_size); 297 298 // Unblock child process 299 uint8_t data = 1; 300 IGNORE_RET(write(fds[1], &data, 1)); 301 302 // Child process should have exited with a zero status. 303 int ret; 304 ASSERT_EQ(pid, waitpid(pid, &ret, 0)); 305 EXPECT_NE(0, WIFEXITED(ret)); 306 EXPECT_EQ(0, WEXITSTATUS(ret)); 307 } 308 309 // Test that memory around the instruction pointer is written 310 // to the dump as a MinidumpMemoryRegion. 311 TEST_F(ExceptionHandlerTest, InstructionPointerMemory) { 312 // Give the child process a pipe to report back on. 313 int fds[2]; 314 ASSERT_EQ(0, pipe(fds)); 315 316 // These are defined here so the parent can use them to check the 317 // data from the minidump afterwards. 318 const uint32_t kMemorySize = 256; // bytes 319 const int kOffset = kMemorySize / 2; 320 // This crashes with SIGILL on x86/x86-64/arm. 321 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; 322 323 pid_t pid = fork(); 324 if (pid == 0) { 325 close(fds[0]); 326 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); 327 // Get some executable memory. 328 char* memory = 329 reinterpret_cast<char*>(mmap(NULL, 330 kMemorySize, 331 PROT_READ | PROT_WRITE | PROT_EXEC, 332 MAP_PRIVATE | MAP_ANON, 333 -1, 334 0)); 335 if (!memory) 336 exit(0); 337 338 // Write some instructions that will crash. Put them in the middle 339 // of the block of memory, because the minidump should contain 128 340 // bytes on either side of the instruction pointer. 341 memcpy(memory + kOffset, instructions, sizeof(instructions)); 342 343 // Now execute the instructions, which should crash. 344 typedef void (*void_function)(void); 345 void_function memory_function = 346 reinterpret_cast<void_function>(memory + kOffset); 347 memory_function(); 348 // not reached 349 exit(1); 350 } 351 // In the parent process. 352 ASSERT_NE(-1, pid); 353 close(fds[1]); 354 355 // Wait for the background process to return the minidump file. 356 close(fds[1]); 357 char minidump_file[PATH_MAX]; 358 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); 359 ASSERT_NE(0, nbytes); 360 // Ensure that minidump file exists and is > 0 bytes. 361 struct stat st; 362 ASSERT_EQ(0, stat(minidump_file, &st)); 363 ASSERT_LT(0, st.st_size); 364 365 // Child process should have exited with a zero status. 366 int ret; 367 ASSERT_EQ(pid, waitpid(pid, &ret, 0)); 368 EXPECT_NE(0, WIFEXITED(ret)); 369 EXPECT_EQ(0, WEXITSTATUS(ret)); 370 371 // Read the minidump. Locate the exception record and the 372 // memory list, and then ensure that there is a memory region 373 // in the memory list that covers the instruction pointer from 374 // the exception record. 375 Minidump minidump(minidump_file); 376 ASSERT_TRUE(minidump.Read()); 377 378 MinidumpException* exception = minidump.GetException(); 379 MinidumpMemoryList* memory_list = minidump.GetMemoryList(); 380 ASSERT_TRUE(exception); 381 ASSERT_TRUE(memory_list); 382 ASSERT_NE((unsigned int)0, memory_list->region_count()); 383 384 MinidumpContext* context = exception->GetContext(); 385 ASSERT_TRUE(context); 386 387 uint64_t instruction_pointer; 388 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); 389 390 MinidumpMemoryRegion* region = 391 memory_list->GetMemoryRegionForAddress(instruction_pointer); 392 EXPECT_TRUE(region); 393 394 EXPECT_EQ(kMemorySize, region->GetSize()); 395 const uint8_t* bytes = region->GetMemory(); 396 ASSERT_TRUE(bytes); 397 398 uint8_t prefix_bytes[kOffset]; 399 uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)]; 400 memset(prefix_bytes, 0, sizeof(prefix_bytes)); 401 memset(suffix_bytes, 0, sizeof(suffix_bytes)); 402 EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); 403 EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); 404 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), 405 suffix_bytes, sizeof(suffix_bytes)) == 0); 406 } 407 408 // Test that the memory region around the instruction pointer is 409 // bounded correctly on the low end. 410 TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { 411 // Give the child process a pipe to report back on. 412 int fds[2]; 413 ASSERT_EQ(0, pipe(fds)); 414 415 // These are defined here so the parent can use them to check the 416 // data from the minidump afterwards. 417 const uint32_t kMemorySize = 256; // bytes 418 const int kOffset = 0; 419 // This crashes with SIGILL on x86/x86-64/arm. 420 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; 421 422 pid_t pid = fork(); 423 if (pid == 0) { 424 close(fds[0]); 425 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); 426 // Get some executable memory. 427 char* memory = 428 reinterpret_cast<char*>(mmap(NULL, 429 kMemorySize, 430 PROT_READ | PROT_WRITE | PROT_EXEC, 431 MAP_PRIVATE | MAP_ANON, 432 -1, 433 0)); 434 if (!memory) 435 exit(0); 436 437 // Write some instructions that will crash. Put them at the start 438 // of the block of memory, to ensure that the memory bounding 439 // works properly. 440 memcpy(memory + kOffset, instructions, sizeof(instructions)); 441 442 // Now execute the instructions, which should crash. 443 typedef void (*void_function)(void); 444 void_function memory_function = 445 reinterpret_cast<void_function>(memory + kOffset); 446 memory_function(); 447 // not reached 448 exit(1); 449 } 450 // In the parent process. 451 ASSERT_NE(-1, pid); 452 close(fds[1]); 453 454 // Wait for the background process to return the minidump file. 455 close(fds[1]); 456 char minidump_file[PATH_MAX]; 457 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); 458 ASSERT_NE(0, nbytes); 459 // Ensure that minidump file exists and is > 0 bytes. 460 struct stat st; 461 ASSERT_EQ(0, stat(minidump_file, &st)); 462 ASSERT_LT(0, st.st_size); 463 464 // Child process should have exited with a zero status. 465 int ret; 466 ASSERT_EQ(pid, waitpid(pid, &ret, 0)); 467 EXPECT_NE(0, WIFEXITED(ret)); 468 EXPECT_EQ(0, WEXITSTATUS(ret)); 469 470 // Read the minidump. Locate the exception record and the 471 // memory list, and then ensure that there is a memory region 472 // in the memory list that covers the instruction pointer from 473 // the exception record. 474 Minidump minidump(minidump_file); 475 ASSERT_TRUE(minidump.Read()); 476 477 MinidumpException* exception = minidump.GetException(); 478 MinidumpMemoryList* memory_list = minidump.GetMemoryList(); 479 ASSERT_TRUE(exception); 480 ASSERT_TRUE(memory_list); 481 ASSERT_NE((unsigned int)0, memory_list->region_count()); 482 483 MinidumpContext* context = exception->GetContext(); 484 ASSERT_TRUE(context); 485 486 uint64_t instruction_pointer; 487 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); 488 489 MinidumpMemoryRegion* region = 490 memory_list->GetMemoryRegionForAddress(instruction_pointer); 491 EXPECT_TRUE(region); 492 493 EXPECT_EQ(kMemorySize / 2, region->GetSize()); 494 const uint8_t* bytes = region->GetMemory(); 495 ASSERT_TRUE(bytes); 496 497 uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)]; 498 memset(suffix_bytes, 0, sizeof(suffix_bytes)); 499 EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); 500 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), 501 suffix_bytes, sizeof(suffix_bytes)) == 0); 502 } 503 504 // Test that the memory region around the instruction pointer is 505 // bounded correctly on the high end. 506 TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { 507 // Give the child process a pipe to report back on. 508 int fds[2]; 509 ASSERT_EQ(0, pipe(fds)); 510 511 // These are defined here so the parent can use them to check the 512 // data from the minidump afterwards. 513 // Use 4k here because the OS will hand out a single page even 514 // if a smaller size is requested, and this test wants to 515 // test the upper bound of the memory range. 516 const uint32_t kMemorySize = 4096; // bytes 517 // This crashes with SIGILL on x86/x86-64/arm. 518 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; 519 const int kOffset = kMemorySize - sizeof(instructions); 520 521 pid_t pid = fork(); 522 if (pid == 0) { 523 close(fds[0]); 524 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); 525 // Get some executable memory. 526 char* memory = 527 reinterpret_cast<char*>(mmap(NULL, 528 kMemorySize, 529 PROT_READ | PROT_WRITE | PROT_EXEC, 530 MAP_PRIVATE | MAP_ANON, 531 -1, 532 0)); 533 if (!memory) 534 exit(0); 535 536 // Write some instructions that will crash. Put them at the start 537 // of the block of memory, to ensure that the memory bounding 538 // works properly. 539 memcpy(memory + kOffset, instructions, sizeof(instructions)); 540 541 // Now execute the instructions, which should crash. 542 typedef void (*void_function)(void); 543 void_function memory_function = 544 reinterpret_cast<void_function>(memory + kOffset); 545 memory_function(); 546 // not reached 547 exit(1); 548 } 549 // In the parent process. 550 ASSERT_NE(-1, pid); 551 close(fds[1]); 552 553 // Wait for the background process to return the minidump file. 554 close(fds[1]); 555 char minidump_file[PATH_MAX]; 556 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); 557 ASSERT_NE(0, nbytes); 558 // Ensure that minidump file exists and is > 0 bytes. 559 struct stat st; 560 ASSERT_EQ(0, stat(minidump_file, &st)); 561 ASSERT_LT(0, st.st_size); 562 563 // Child process should have exited with a zero status. 564 int ret; 565 ASSERT_EQ(pid, waitpid(pid, &ret, 0)); 566 EXPECT_NE(0, WIFEXITED(ret)); 567 EXPECT_EQ(0, WEXITSTATUS(ret)); 568 569 // Read the minidump. Locate the exception record and the 570 // memory list, and then ensure that there is a memory region 571 // in the memory list that covers the instruction pointer from 572 // the exception record. 573 Minidump minidump(minidump_file); 574 ASSERT_TRUE(minidump.Read()); 575 576 MinidumpException* exception = minidump.GetException(); 577 MinidumpMemoryList* memory_list = minidump.GetMemoryList(); 578 ASSERT_TRUE(exception); 579 ASSERT_TRUE(memory_list); 580 ASSERT_NE((unsigned int)0, memory_list->region_count()); 581 582 MinidumpContext* context = exception->GetContext(); 583 ASSERT_TRUE(context); 584 585 uint64_t instruction_pointer; 586 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); 587 588 MinidumpMemoryRegion* region = 589 memory_list->GetMemoryRegionForAddress(instruction_pointer); 590 EXPECT_TRUE(region); 591 592 const size_t kPrefixSize = 128; // bytes 593 EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize()); 594 const uint8_t* bytes = region->GetMemory(); 595 ASSERT_TRUE(bytes); 596 597 uint8_t prefix_bytes[kPrefixSize]; 598 memset(prefix_bytes, 0, sizeof(prefix_bytes)); 599 EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); 600 EXPECT_TRUE(memcmp(bytes + kPrefixSize, 601 instructions, sizeof(instructions)) == 0); 602 } 603 604 // Ensure that an extra memory block doesn't get added when the 605 // instruction pointer is not in mapped memory. 606 TEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { 607 // Give the child process a pipe to report back on. 608 int fds[2]; 609 ASSERT_EQ(0, pipe(fds)); 610 611 pid_t pid = fork(); 612 if (pid == 0) { 613 close(fds[0]); 614 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); 615 // Try calling a NULL pointer. 616 typedef void (*void_function)(void); 617 // Volatile markings are needed to keep Clang from generating invalid 618 // opcodes. See http://crbug.com/498354 for details. 619 volatile void_function memory_function = 620 reinterpret_cast<void_function>(NULL); 621 memory_function(); 622 // not reached 623 exit(1); 624 } 625 // In the parent process. 626 ASSERT_NE(-1, pid); 627 close(fds[1]); 628 629 // Wait for the background process to return the minidump file. 630 close(fds[1]); 631 char minidump_file[PATH_MAX]; 632 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); 633 ASSERT_NE(0, nbytes); 634 // Ensure that minidump file exists and is > 0 bytes. 635 struct stat st; 636 ASSERT_EQ(0, stat(minidump_file, &st)); 637 ASSERT_LT(0, st.st_size); 638 639 // Child process should have exited with a zero status. 640 int ret; 641 ASSERT_EQ(pid, waitpid(pid, &ret, 0)); 642 EXPECT_NE(0, WIFEXITED(ret)); 643 EXPECT_EQ(0, WEXITSTATUS(ret)); 644 645 // Read the minidump. Locate the exception record and the 646 // memory list, and then ensure that there is only one memory region 647 // in the memory list (the thread memory from the single thread). 648 Minidump minidump(minidump_file); 649 ASSERT_TRUE(minidump.Read()); 650 651 MinidumpException* exception = minidump.GetException(); 652 MinidumpMemoryList* memory_list = minidump.GetMemoryList(); 653 ASSERT_TRUE(exception); 654 ASSERT_TRUE(memory_list); 655 ASSERT_EQ((unsigned int)1, memory_list->region_count()); 656 } 657 658 static void* Junk(void*) { 659 sleep(1000000); 660 return NULL; 661 } 662 663 // Test that the memory list gets written correctly when multiple 664 // threads are running. 665 TEST_F(ExceptionHandlerTest, MemoryListMultipleThreads) { 666 // Give the child process a pipe to report back on. 667 int fds[2]; 668 ASSERT_EQ(0, pipe(fds)); 669 670 pid_t pid = fork(); 671 if (pid == 0) { 672 close(fds[0]); 673 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); 674 675 // Run an extra thread so >2 memory regions will be written. 676 pthread_t junk_thread; 677 if (pthread_create(&junk_thread, NULL, Junk, NULL) == 0) 678 pthread_detach(junk_thread); 679 680 // Just crash. 681 Crasher(); 682 683 // not reached 684 exit(1); 685 } 686 // In the parent process. 687 ASSERT_NE(-1, pid); 688 close(fds[1]); 689 690 // Wait for the background process to return the minidump file. 691 close(fds[1]); 692 char minidump_file[PATH_MAX]; 693 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); 694 ASSERT_NE(0, nbytes); 695 // Ensure that minidump file exists and is > 0 bytes. 696 struct stat st; 697 ASSERT_EQ(0, stat(minidump_file, &st)); 698 ASSERT_LT(0, st.st_size); 699 700 // Child process should have exited with a zero status. 701 int ret; 702 ASSERT_EQ(pid, waitpid(pid, &ret, 0)); 703 EXPECT_NE(0, WIFEXITED(ret)); 704 EXPECT_EQ(0, WEXITSTATUS(ret)); 705 706 // Read the minidump, and verify that the memory list can be read. 707 Minidump minidump(minidump_file); 708 ASSERT_TRUE(minidump.Read()); 709 710 MinidumpMemoryList* memory_list = minidump.GetMemoryList(); 711 ASSERT_TRUE(memory_list); 712 // Verify that there are three memory regions: 713 // one per thread, and one for the instruction pointer memory. 714 ASSERT_EQ((unsigned int)3, memory_list->region_count()); 715 } 716 717 }