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