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