Home | History | Annotate | Download | only in tests
      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 // minidump_generator_test.cc: Unit tests for google_breakpad::MinidumpGenerator
     31 
     32 #include <AvailabilityMacros.h>
     33 #ifndef MAC_OS_X_VERSION_10_6
     34 #define MAC_OS_X_VERSION_10_6 1060
     35 #endif
     36 #include <sys/stat.h>
     37 #include <unistd.h>
     38 
     39 #include <string>
     40 #include <vector>
     41 
     42 #include "breakpad_googletest_includes.h"
     43 #include "client/mac/handler/minidump_generator.h"
     44 #include "client/mac/tests/spawn_child_process.h"
     45 #include "common/mac/MachIPC.h"
     46 #include "common/tests/auto_tempdir.h"
     47 #include "google_breakpad/processor/minidump.h"
     48 
     49 namespace google_breakpad {
     50 // This acts as the log sink for INFO logging from the processor
     51 // logging code. The logging output confuses XCode and makes it think
     52 // there are unit test failures. testlogging.h handles the overriding.
     53 std::ostringstream info_log;
     54 }
     55 
     56 namespace {
     57 using std::string;
     58 using std::vector;
     59 using google_breakpad::AutoTempDir;
     60 using google_breakpad::MinidumpGenerator;
     61 using google_breakpad::MachPortSender;
     62 using google_breakpad::MachReceiveMessage;
     63 using google_breakpad::MachSendMessage;
     64 using google_breakpad::Minidump;
     65 using google_breakpad::MinidumpContext;
     66 using google_breakpad::MinidumpException;
     67 using google_breakpad::MinidumpModule;
     68 using google_breakpad::MinidumpModuleList;
     69 using google_breakpad::MinidumpSystemInfo;
     70 using google_breakpad::MinidumpThread;
     71 using google_breakpad::MinidumpThreadList;
     72 using google_breakpad::ReceivePort;
     73 using testing::Test;
     74 using namespace google_breakpad_test;
     75 
     76 class MinidumpGeneratorTest : public Test {
     77  public:
     78   AutoTempDir tempDir;
     79 };
     80 
     81 static void *Junk(void* data) {
     82   bool* wait = reinterpret_cast<bool*>(data);
     83   while (!*wait) {
     84     usleep(10000);
     85   }
     86   return NULL;
     87 }
     88 
     89 TEST_F(MinidumpGeneratorTest, InProcess) {
     90   MinidumpGenerator generator;
     91   string dump_filename =
     92       MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
     93 
     94   // Run an extra thread since MinidumpGenerator assumes there
     95   // are 2 or more threads.
     96   pthread_t junk_thread;
     97   bool quit = false;
     98   ASSERT_EQ(0, pthread_create(&junk_thread, NULL, Junk, &quit));
     99 
    100   ASSERT_TRUE(generator.Write(dump_filename.c_str()));
    101   // Ensure that minidump file exists and is > 0 bytes.
    102   struct stat st;
    103   ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
    104   ASSERT_LT(0, st.st_size);
    105 
    106   // join the background thread
    107   quit = true;
    108   pthread_join(junk_thread, NULL);
    109 
    110   // Read the minidump, sanity check some data.
    111   Minidump minidump(dump_filename.c_str());
    112   ASSERT_TRUE(minidump.Read());
    113 
    114   MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
    115   ASSERT_TRUE(system_info);
    116   const MDRawSystemInfo* raw_info = system_info->system_info();
    117   ASSERT_TRUE(raw_info);
    118   EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
    119 
    120   MinidumpThreadList* thread_list = minidump.GetThreadList();
    121   ASSERT_TRUE(thread_list);
    122   ASSERT_EQ((unsigned int)1, thread_list->thread_count());
    123 
    124   MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
    125   ASSERT_TRUE(main_thread);
    126   MinidumpContext* context = main_thread->GetContext();
    127   ASSERT_TRUE(context);
    128   EXPECT_EQ(kNativeContext, context->GetContextCPU());
    129 
    130   MinidumpModuleList* module_list = minidump.GetModuleList();
    131   ASSERT_TRUE(module_list);
    132   const MinidumpModule* main_module = module_list->GetMainModule();
    133   ASSERT_TRUE(main_module);
    134   EXPECT_EQ(GetExecutablePath(), main_module->code_file());
    135 }
    136 
    137 TEST_F(MinidumpGeneratorTest, OutOfProcess) {
    138   const int kTimeoutMs = 2000;
    139   // Create a mach port to receive the child task on.
    140   char machPortName[128];
    141   sprintf(machPortName, "MinidumpGeneratorTest.OutOfProcess.%d", getpid());
    142   ReceivePort parent_recv_port(machPortName);
    143 
    144   // Give the child process a pipe to block on.
    145   int fds[2];
    146   ASSERT_EQ(0, pipe(fds));
    147 
    148   // Fork off a child process to dump.
    149   pid_t pid = fork();
    150   if (pid == 0) {
    151     // In the child process
    152     close(fds[1]);
    153 
    154     // Send parent process the task port.
    155     MachSendMessage child_message(0);
    156     child_message.AddDescriptor(mach_task_self());
    157 
    158     MachPortSender child_sender(machPortName);
    159     if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) {
    160       fprintf(stderr, "Error sending message from child process!\n");
    161       exit(1);
    162     }
    163 
    164     // Wait for the parent process.
    165     uint8_t data;
    166     read(fds[0], &data, 1);
    167     exit(0);
    168   }
    169   // In the parent process.
    170   ASSERT_NE(-1, pid);
    171   close(fds[0]);
    172 
    173   // Read the child's task port.
    174   MachReceiveMessage child_message;
    175   ASSERT_EQ(KERN_SUCCESS,
    176 	    parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
    177   mach_port_t child_task = child_message.GetTranslatedPort(0);
    178   ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
    179 
    180   // Write a minidump of the child process.
    181   MinidumpGenerator generator(child_task, MACH_PORT_NULL);
    182   string dump_filename =
    183       MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
    184   ASSERT_TRUE(generator.Write(dump_filename.c_str()));
    185 
    186   // Ensure that minidump file exists and is > 0 bytes.
    187   struct stat st;
    188   ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
    189   ASSERT_LT(0, st.st_size);
    190 
    191   // Unblock child process
    192   uint8_t data = 1;
    193   (void)write(fds[1], &data, 1);
    194 
    195   // Child process should have exited with a zero status.
    196   int ret;
    197   ASSERT_EQ(pid, waitpid(pid, &ret, 0));
    198   EXPECT_NE(0, WIFEXITED(ret));
    199   EXPECT_EQ(0, WEXITSTATUS(ret));
    200 
    201   // Read the minidump, sanity check some data.
    202   Minidump minidump(dump_filename.c_str());
    203   ASSERT_TRUE(minidump.Read());
    204 
    205   MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
    206   ASSERT_TRUE(system_info);
    207   const MDRawSystemInfo* raw_info = system_info->system_info();
    208   ASSERT_TRUE(raw_info);
    209   EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
    210 
    211   MinidumpThreadList* thread_list = minidump.GetThreadList();
    212   ASSERT_TRUE(thread_list);
    213   ASSERT_EQ((unsigned int)1, thread_list->thread_count());
    214 
    215   MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
    216   ASSERT_TRUE(main_thread);
    217   MinidumpContext* context = main_thread->GetContext();
    218   ASSERT_TRUE(context);
    219   EXPECT_EQ(kNativeContext, context->GetContextCPU());
    220 
    221   MinidumpModuleList* module_list = minidump.GetModuleList();
    222   ASSERT_TRUE(module_list);
    223   const MinidumpModule* main_module = module_list->GetMainModule();
    224   ASSERT_TRUE(main_module);
    225   EXPECT_EQ(GetExecutablePath(), main_module->code_file());
    226 }
    227 
    228 // This test fails on 10.5, but I don't have easy access to a 10.5 machine,
    229 // so it's simpler to just limit it to 10.6 for now.
    230 #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \
    231   (defined(__x86_64__) || defined(__i386__))
    232 
    233 TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) {
    234   const int kTimeoutMs = 5000;
    235   // Create a mach port to receive the child task on.
    236   char machPortName[128];
    237   sprintf(machPortName,
    238           "MinidumpGeneratorTest.CrossArchitectureDump.%d", getpid());
    239 
    240   ReceivePort parent_recv_port(machPortName);
    241 
    242   // Spawn a child process to dump.
    243   string helper_path = GetHelperPath();
    244   const char* argv[] = {
    245     helper_path.c_str(),
    246     machPortName,
    247     NULL
    248   };
    249   pid_t pid = spawn_child_process(argv);
    250   ASSERT_NE(-1, pid);
    251 
    252   // Read the child's task port.
    253   MachReceiveMessage child_message;
    254   ASSERT_EQ(KERN_SUCCESS,
    255 	    parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
    256   mach_port_t child_task = child_message.GetTranslatedPort(0);
    257   ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
    258 
    259   // Write a minidump of the child process.
    260   MinidumpGenerator generator(child_task, MACH_PORT_NULL);
    261   string dump_filename =
    262       MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
    263   ASSERT_TRUE(generator.Write(dump_filename.c_str()));
    264 
    265   // Ensure that minidump file exists and is > 0 bytes.
    266   struct stat st;
    267   ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
    268   ASSERT_LT(0, st.st_size);
    269 
    270   // Kill child process.
    271   kill(pid, SIGKILL);
    272 
    273   int ret;
    274   ASSERT_EQ(pid, waitpid(pid, &ret, 0));
    275 
    276 const MDCPUArchitecture kExpectedArchitecture =
    277 #if defined(__x86_64__)
    278   MD_CPU_ARCHITECTURE_X86
    279 #elif defined(__i386__)
    280   MD_CPU_ARCHITECTURE_AMD64
    281 #endif
    282   ;
    283 const uint32_t kExpectedContext =
    284 #if defined(__i386__)
    285   MD_CONTEXT_AMD64
    286 #elif defined(__x86_64__)
    287   MD_CONTEXT_X86
    288 #endif
    289   ;
    290 
    291   // Read the minidump, sanity check some data.
    292   Minidump minidump(dump_filename.c_str());
    293   ASSERT_TRUE(minidump.Read());
    294 
    295   MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
    296   ASSERT_TRUE(system_info);
    297   const MDRawSystemInfo* raw_info = system_info->system_info();
    298   ASSERT_TRUE(raw_info);
    299   EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture);
    300 
    301   MinidumpThreadList* thread_list = minidump.GetThreadList();
    302   ASSERT_TRUE(thread_list);
    303   ASSERT_EQ((unsigned int)1, thread_list->thread_count());
    304 
    305   MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
    306   ASSERT_TRUE(main_thread);
    307   MinidumpContext* context = main_thread->GetContext();
    308   ASSERT_TRUE(context);
    309   EXPECT_EQ(kExpectedContext, context->GetContextCPU());
    310 
    311   MinidumpModuleList* module_list = minidump.GetModuleList();
    312   ASSERT_TRUE(module_list);
    313   const MinidumpModule* main_module = module_list->GetMainModule();
    314   ASSERT_TRUE(main_module);
    315   EXPECT_EQ(helper_path, main_module->code_file());
    316 }
    317 #endif  // 10.6 && (x86-64 || i386)
    318 
    319 }
    320