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