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 // crash_generation_server_test.cc
     31 // Unit tests for CrashGenerationServer
     32 
     33 #include <dirent.h>
     34 #include <glob.h>
     35 #include <stdint.h>
     36 #include <sys/wait.h>
     37 #include <unistd.h>
     38 
     39 #include <string>
     40 
     41 #include "breakpad_googletest_includes.h"
     42 #include "client/mac/crash_generation/client_info.h"
     43 #include "client/mac/crash_generation/crash_generation_client.h"
     44 #include "client/mac/crash_generation/crash_generation_server.h"
     45 #include "client/mac/handler/exception_handler.h"
     46 #include "client/mac/tests/spawn_child_process.h"
     47 #include "common/tests/auto_tempdir.h"
     48 #include "google_breakpad/processor/minidump.h"
     49 
     50 namespace google_breakpad {
     51 // This acts as the log sink for INFO logging from the processor
     52 // logging code. The logging output confuses XCode and makes it think
     53 // there are unit test failures. testlogging.h handles the overriding.
     54 std::ostringstream info_log;
     55 }
     56 
     57 namespace {
     58 using std::string;
     59 using google_breakpad::AutoTempDir;
     60 using google_breakpad::ClientInfo;
     61 using google_breakpad::CrashGenerationClient;
     62 using google_breakpad::CrashGenerationServer;
     63 using google_breakpad::ExceptionHandler;
     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 testing::Test;
     73 using namespace google_breakpad_test;
     74 
     75 class CrashGenerationServerTest : public Test {
     76 public:
     77   // The port name to receive messages on
     78   char mach_port_name[128];
     79   // Filename of the last dump that was generated
     80   string last_dump_name;
     81   // PID of the child process
     82   pid_t child_pid;
     83   // A temp dir
     84   AutoTempDir temp_dir;
     85   // Counter just to ensure that we don't hit the same port again
     86   static int i;
     87   bool filter_callback_called;
     88 
     89   void SetUp() {
     90     sprintf(mach_port_name,
     91             "com.google.breakpad.ServerTest.%d.%d", getpid(),
     92             CrashGenerationServerTest::i++);
     93     child_pid = (pid_t)-1;
     94     filter_callback_called = false;
     95   }
     96 };
     97 int CrashGenerationServerTest::i = 0;
     98 
     99 // Test that starting and stopping a server works
    100 TEST_F(CrashGenerationServerTest, testStartStopServer) {
    101   CrashGenerationServer server(mach_port_name,
    102                                NULL,  // filter callback
    103                                NULL,  // filter context
    104                                NULL,  // dump callback
    105                                NULL,  // dump context
    106                                NULL,  // exit callback
    107                                NULL,  // exit context
    108                                false, // generate dumps
    109                                ""); // dump path
    110   ASSERT_TRUE(server.Start());
    111   ASSERT_TRUE(server.Stop());
    112 }
    113 
    114 // Test that requesting a dump via CrashGenerationClient works
    115 // Test without actually dumping
    116 TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
    117   CrashGenerationServer server(mach_port_name,
    118                                NULL,  // filter callback
    119                                NULL,  // filter context
    120                                NULL,  // dump callback
    121                                NULL,  // dump context
    122                                NULL,  // exit callback
    123                                NULL,  // exit context
    124                                false, // don't generate dumps
    125                                temp_dir.path()); // dump path
    126   ASSERT_TRUE(server.Start());
    127 
    128   pid_t pid = fork();
    129   ASSERT_NE(-1, pid);
    130   if (pid == 0) {
    131     CrashGenerationClient client(mach_port_name);
    132     bool result = client.RequestDump();
    133     exit(result ? 0 : 1);
    134   }
    135 
    136   int ret;
    137   ASSERT_EQ(pid, waitpid(pid, &ret, 0));
    138   EXPECT_TRUE(WIFEXITED(ret));
    139   EXPECT_EQ(0, WEXITSTATUS(ret));
    140   EXPECT_TRUE(server.Stop());
    141   // check that no minidump was written
    142   string pattern = temp_dir.path() + "/*";
    143   glob_t dirContents;
    144   ret = glob(pattern.c_str(), GLOB_NOSORT, NULL, &dirContents);
    145   EXPECT_EQ(GLOB_NOMATCH, ret);
    146   if (ret != GLOB_NOMATCH)
    147     globfree(&dirContents);
    148 }
    149 
    150 void dumpCallback(void *context, const ClientInfo &client_info,
    151                   const std::string &file_path) {
    152   if (context) {
    153     CrashGenerationServerTest* self =
    154         reinterpret_cast<CrashGenerationServerTest*>(context);
    155     if (!file_path.empty())
    156       self->last_dump_name = file_path;
    157     self->child_pid = client_info.pid();
    158   }
    159 }
    160 
    161 void *RequestDump(void *context) {
    162   CrashGenerationClient client((const char*)context);
    163   bool result = client.RequestDump();
    164   return (void*)(result ? 0 : 1);
    165 }
    166 
    167 // Test that actually writing a minidump works
    168 TEST_F(CrashGenerationServerTest, testRequestDump) {
    169   CrashGenerationServer server(mach_port_name,
    170                                NULL,  // filter callback
    171                                NULL,  // filter context
    172                                dumpCallback,  // dump callback
    173                                this,  // dump context
    174                                NULL,  // exit callback
    175                                NULL,  // exit context
    176                                true, //  generate dumps
    177                                temp_dir.path()); // dump path
    178   ASSERT_TRUE(server.Start());
    179 
    180   pid_t pid = fork();
    181   ASSERT_NE(-1, pid);
    182   if (pid == 0) {
    183     // Have to spawn off a separate thread to request the dump,
    184     // because MinidumpGenerator assumes the handler thread is not
    185     // the only thread
    186     pthread_t thread;
    187     if (pthread_create(&thread, NULL, RequestDump, (void*)mach_port_name) != 0)
    188       exit(1);
    189     void* result;
    190     pthread_join(thread, &result);
    191     exit(reinterpret_cast<intptr_t>(result));
    192   }
    193 
    194   int ret;
    195   ASSERT_EQ(pid, waitpid(pid, &ret, 0));
    196   EXPECT_TRUE(WIFEXITED(ret));
    197   EXPECT_EQ(0, WEXITSTATUS(ret));
    198   EXPECT_TRUE(server.Stop());
    199   // check that minidump was written
    200   ASSERT_FALSE(last_dump_name.empty());
    201   struct stat st;
    202   EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
    203   EXPECT_LT(0, st.st_size);
    204   // check client's PID
    205   ASSERT_EQ(pid, child_pid);
    206 }
    207 
    208 static void Crasher() {
    209   int *a = (int*)0x42;
    210 
    211   fprintf(stdout, "Going to crash...\n");
    212   fprintf(stdout, "A = %d", *a);
    213 }
    214 
    215 // Test that crashing a child process with an OOP ExceptionHandler installed
    216 // results in a minidump being written by the CrashGenerationServer in
    217 // the parent.
    218 TEST_F(CrashGenerationServerTest, testChildProcessCrash) {
    219   CrashGenerationServer server(mach_port_name,
    220                                NULL,  // filter callback
    221                                NULL,  // filter context
    222                                dumpCallback,  // dump callback
    223                                this,  // dump context
    224                                NULL,  // exit callback
    225                                NULL,  // exit context
    226                                true, //  generate dumps
    227                                temp_dir.path()); // dump path
    228   ASSERT_TRUE(server.Start());
    229 
    230   pid_t pid = fork();
    231   ASSERT_NE(-1, pid);
    232   if (pid == 0) {
    233     // Instantiate an OOP exception handler.
    234     ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name);
    235     Crasher();
    236     // not reached
    237     exit(0);
    238   }
    239 
    240   int ret;
    241   ASSERT_EQ(pid, waitpid(pid, &ret, 0));
    242   EXPECT_FALSE(WIFEXITED(ret));
    243   EXPECT_TRUE(server.Stop());
    244   // check that minidump was written
    245   ASSERT_FALSE(last_dump_name.empty());
    246   struct stat st;
    247   EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
    248   EXPECT_LT(0, st.st_size);
    249 
    250   // Read the minidump, sanity check some data.
    251   Minidump minidump(last_dump_name.c_str());
    252   ASSERT_TRUE(minidump.Read());
    253 
    254   MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
    255   ASSERT_TRUE(system_info);
    256   const MDRawSystemInfo* raw_info = system_info->system_info();
    257   ASSERT_TRUE(raw_info);
    258   EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
    259 
    260   MinidumpThreadList* thread_list = minidump.GetThreadList();
    261   ASSERT_TRUE(thread_list);
    262   ASSERT_EQ((unsigned int)1, thread_list->thread_count());
    263 
    264   MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
    265   ASSERT_TRUE(main_thread);
    266   MinidumpContext* context = main_thread->GetContext();
    267   ASSERT_TRUE(context);
    268   EXPECT_EQ(kNativeContext, context->GetContextCPU());
    269 
    270   MinidumpModuleList* module_list = minidump.GetModuleList();
    271   ASSERT_TRUE(module_list);
    272   const MinidumpModule* main_module = module_list->GetMainModule();
    273   ASSERT_TRUE(main_module);
    274   EXPECT_EQ(GetExecutablePath(), main_module->code_file());
    275 }
    276 
    277 #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \
    278   (defined(__x86_64__) || defined(__i386__))
    279 // Test that crashing a child process of a different architecture
    280 // produces a valid minidump.
    281 TEST_F(CrashGenerationServerTest, testChildProcessCrashCrossArchitecture) {
    282   CrashGenerationServer server(mach_port_name,
    283                                NULL,  // filter callback
    284                                NULL,  // filter context
    285                                dumpCallback,  // dump callback
    286                                this,  // dump context
    287                                NULL,  // exit callback
    288                                NULL,  // exit context
    289                                true, //  generate dumps
    290                                temp_dir.path()); // dump path
    291   ASSERT_TRUE(server.Start());
    292 
    293   // Spawn a child process
    294   string helper_path = GetHelperPath();
    295   const char* argv[] = {
    296     helper_path.c_str(),
    297     "crash",
    298     mach_port_name,
    299     NULL
    300   };
    301   pid_t pid = spawn_child_process(argv);
    302   ASSERT_NE(-1, pid);
    303 
    304   int ret;
    305   ASSERT_EQ(pid, waitpid(pid, &ret, 0));
    306   EXPECT_FALSE(WIFEXITED(ret));
    307   EXPECT_TRUE(server.Stop());
    308   // check that minidump was written
    309   ASSERT_FALSE(last_dump_name.empty());
    310   struct stat st;
    311   EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
    312   EXPECT_LT(0, st.st_size);
    313 
    314 const MDCPUArchitecture kExpectedArchitecture =
    315 #if defined(__x86_64__)
    316   MD_CPU_ARCHITECTURE_X86
    317 #elif defined(__i386__)
    318   MD_CPU_ARCHITECTURE_AMD64
    319 #endif
    320   ;
    321 const uint32_t kExpectedContext =
    322 #if defined(__i386__)
    323   MD_CONTEXT_AMD64
    324 #elif defined(__x86_64__)
    325   MD_CONTEXT_X86
    326 #endif
    327   ;
    328 
    329   // Read the minidump, sanity check some data.
    330   Minidump minidump(last_dump_name.c_str());
    331   ASSERT_TRUE(minidump.Read());
    332 
    333   MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
    334   ASSERT_TRUE(system_info);
    335   const MDRawSystemInfo* raw_info = system_info->system_info();
    336   ASSERT_TRUE(raw_info);
    337   EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture);
    338 
    339   MinidumpThreadList* thread_list = minidump.GetThreadList();
    340   ASSERT_TRUE(thread_list);
    341   ASSERT_EQ((unsigned int)1, thread_list->thread_count());
    342 
    343   MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
    344   ASSERT_TRUE(main_thread);
    345   MinidumpContext* context = main_thread->GetContext();
    346   ASSERT_TRUE(context);
    347   EXPECT_EQ(kExpectedContext, context->GetContextCPU());
    348 
    349   MinidumpModuleList* module_list = minidump.GetModuleList();
    350   ASSERT_TRUE(module_list);
    351   const MinidumpModule* main_module = module_list->GetMainModule();
    352   ASSERT_TRUE(main_module);
    353   EXPECT_EQ(helper_path, main_module->code_file());
    354 }
    355 #endif
    356 
    357 bool filter_callback(void* context) {
    358   CrashGenerationServerTest* self =
    359     reinterpret_cast<CrashGenerationServerTest*>(context);
    360   self->filter_callback_called = true;
    361   // veto dump generation
    362   return false;
    363 }
    364 
    365 // Test that a filter callback can veto minidump writing.
    366 TEST_F(CrashGenerationServerTest, testFilter) {
    367   CrashGenerationServer server(mach_port_name,
    368                                filter_callback,  // filter callback
    369                                this,            // filter context
    370                                dumpCallback,  // dump callback
    371                                this,  // dump context
    372                                NULL,  // exit callback
    373                                NULL,  // exit context
    374                                true, //  generate dumps
    375                                temp_dir.path()); // dump path
    376   ASSERT_TRUE(server.Start());
    377 
    378   pid_t pid = fork();
    379   ASSERT_NE(-1, pid);
    380   if (pid == 0) {
    381     // Instantiate an OOP exception handler.
    382     ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name);
    383     Crasher();
    384     // not reached
    385     exit(0);
    386   }
    387 
    388   int ret;
    389   ASSERT_EQ(pid, waitpid(pid, &ret, 0));
    390   EXPECT_FALSE(WIFEXITED(ret));
    391   EXPECT_TRUE(server.Stop());
    392 
    393   // check that no minidump was written
    394   EXPECT_TRUE(last_dump_name.empty());
    395   EXPECT_TRUE(filter_callback_called);
    396 }
    397 
    398 }  // namespace
    399