diff options
Diffstat (limited to 'externals/breakpad/src/client/mac/tests/minidump_generator_test.cc')
-rw-r--r-- | externals/breakpad/src/client/mac/tests/minidump_generator_test.cc | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/externals/breakpad/src/client/mac/tests/minidump_generator_test.cc b/externals/breakpad/src/client/mac/tests/minidump_generator_test.cc new file mode 100644 index 0000000000..2606e14f57 --- /dev/null +++ b/externals/breakpad/src/client/mac/tests/minidump_generator_test.cc @@ -0,0 +1,323 @@ +// Copyright 2010 Google LLC +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// minidump_generator_test.cc: Unit tests for google_breakpad::MinidumpGenerator + +#ifdef HAVE_CONFIG_H +#include <config.h> // Must come first +#endif + +#include <AvailabilityMacros.h> +#ifndef MAC_OS_X_VERSION_10_6 +#define MAC_OS_X_VERSION_10_6 1060 +#endif +#include <sys/stat.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include "breakpad_googletest_includes.h" +#include "client/mac/handler/minidump_generator.h" +#include "client/mac/tests/spawn_child_process.h" +#include "common/linux/ignore_ret.h" +#include "common/mac/MachIPC.h" +#include "common/tests/auto_tempdir.h" +#include "google_breakpad/processor/minidump.h" + +namespace google_breakpad { +// This acts as the log sink for INFO logging from the processor +// logging code. The logging output confuses XCode and makes it think +// there are unit test failures. testlogging.h handles the overriding. +std::ostringstream info_log; +} + +namespace { +using std::string; +using std::vector; +using google_breakpad::AutoTempDir; +using google_breakpad::MinidumpGenerator; +using google_breakpad::MachPortSender; +using google_breakpad::MachReceiveMessage; +using google_breakpad::MachSendMessage; +using google_breakpad::Minidump; +using google_breakpad::MinidumpContext; +using google_breakpad::MinidumpException; +using google_breakpad::MinidumpModule; +using google_breakpad::MinidumpModuleList; +using google_breakpad::MinidumpSystemInfo; +using google_breakpad::MinidumpThread; +using google_breakpad::MinidumpThreadList; +using google_breakpad::ReceivePort; +using testing::Test; +using namespace google_breakpad_test; + +class MinidumpGeneratorTest : public Test { + public: + AutoTempDir tempDir; +}; + +static void* Junk(void* data) { + bool* wait = reinterpret_cast<bool*>(data); + while (!*wait) { + usleep(10000); + } + return NULL; +} + +TEST_F(MinidumpGeneratorTest, InProcess) { + MinidumpGenerator generator; + string dump_filename = + MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL); + + // Run an extra thread since MinidumpGenerator assumes there + // are 2 or more threads. + pthread_t junk_thread; + bool quit = false; + ASSERT_EQ(0, pthread_create(&junk_thread, NULL, Junk, &quit)); + + ASSERT_TRUE(generator.Write(dump_filename.c_str())); + // Ensure that minidump file exists and is > 0 bytes. + struct stat st; + ASSERT_EQ(0, stat(dump_filename.c_str(), &st)); + ASSERT_LT(0, st.st_size); + + // join the background thread + quit = true; + pthread_join(junk_thread, NULL); + + // Read the minidump, sanity check some data. + Minidump minidump(dump_filename.c_str()); + ASSERT_TRUE(minidump.Read()); + + MinidumpSystemInfo* system_info = minidump.GetSystemInfo(); + ASSERT_TRUE(system_info); + const MDRawSystemInfo* raw_info = system_info->system_info(); + ASSERT_TRUE(raw_info); + EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture); + + MinidumpThreadList* thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list); + ASSERT_EQ((unsigned int)1, thread_list->thread_count()); + + MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0); + ASSERT_TRUE(main_thread); + MinidumpContext* context = main_thread->GetContext(); + ASSERT_TRUE(context); + EXPECT_EQ(kNativeContext, context->GetContextCPU()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* main_module = module_list->GetMainModule(); + ASSERT_TRUE(main_module); + EXPECT_EQ(GetExecutablePath(), main_module->code_file()); +} + +TEST_F(MinidumpGeneratorTest, OutOfProcess) { + const int kTimeoutMs = 2000; + // Create a mach port to receive the child task on. + char machPortName[128]; + sprintf(machPortName, "MinidumpGeneratorTest.OutOfProcess.%d", getpid()); + ReceivePort parent_recv_port(machPortName); + + // Give the child process a pipe to block on. + int fds[2]; + ASSERT_EQ(0, pipe(fds)); + + // Fork off a child process to dump. + pid_t pid = fork(); + if (pid == 0) { + // In the child process + close(fds[1]); + + // Send parent process the task port. + MachSendMessage child_message(0); + child_message.AddDescriptor(mach_task_self()); + + MachPortSender child_sender(machPortName); + if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) { + fprintf(stderr, "Error sending message from child process!\n"); + exit(1); + } + + // Wait for the parent process. + uint8_t data; + read(fds[0], &data, 1); + exit(0); + } + // In the parent process. + ASSERT_NE(-1, pid); + close(fds[0]); + + // Read the child's task port. + MachReceiveMessage child_message; + ASSERT_EQ(KERN_SUCCESS, + parent_recv_port.WaitForMessage(&child_message, kTimeoutMs)); + mach_port_t child_task = child_message.GetTranslatedPort(0); + ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task); + + // Write a minidump of the child process. + MinidumpGenerator generator(child_task, MACH_PORT_NULL); + string dump_filename = + MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL); + ASSERT_TRUE(generator.Write(dump_filename.c_str())); + + // Ensure that minidump file exists and is > 0 bytes. + struct stat st; + ASSERT_EQ(0, stat(dump_filename.c_str(), &st)); + ASSERT_LT(0, st.st_size); + + // Unblock child process + uint8_t data = 1; + IGNORE_RET(write(fds[1], &data, 1)); + + // Child process should have exited with a zero status. + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_NE(0, WIFEXITED(ret)); + EXPECT_EQ(0, WEXITSTATUS(ret)); + + // Read the minidump, sanity check some data. + Minidump minidump(dump_filename.c_str()); + ASSERT_TRUE(minidump.Read()); + + MinidumpSystemInfo* system_info = minidump.GetSystemInfo(); + ASSERT_TRUE(system_info); + const MDRawSystemInfo* raw_info = system_info->system_info(); + ASSERT_TRUE(raw_info); + EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture); + + MinidumpThreadList* thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list); + ASSERT_EQ((unsigned int)1, thread_list->thread_count()); + + MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0); + ASSERT_TRUE(main_thread); + MinidumpContext* context = main_thread->GetContext(); + ASSERT_TRUE(context); + EXPECT_EQ(kNativeContext, context->GetContextCPU()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* main_module = module_list->GetMainModule(); + ASSERT_TRUE(main_module); + EXPECT_EQ(GetExecutablePath(), main_module->code_file()); +} + +// This test fails on 10.5, but I don't have easy access to a 10.5 machine, +// so it's simpler to just limit it to 10.6 for now. +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \ + (defined(__x86_64__) || defined(__i386__)) + +TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) { + const int kTimeoutMs = 5000; + // Create a mach port to receive the child task on. + char machPortName[128]; + sprintf(machPortName, + "MinidumpGeneratorTest.CrossArchitectureDump.%d", getpid()); + + ReceivePort parent_recv_port(machPortName); + + // Spawn a child process to dump. + string helper_path = GetHelperPath(); + const char* argv[] = { + helper_path.c_str(), + machPortName, + NULL + }; + pid_t pid = spawn_child_process(argv); + ASSERT_NE(-1, pid); + + // Read the child's task port. + MachReceiveMessage child_message; + ASSERT_EQ(KERN_SUCCESS, + parent_recv_port.WaitForMessage(&child_message, kTimeoutMs)); + mach_port_t child_task = child_message.GetTranslatedPort(0); + ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task); + + // Write a minidump of the child process. + MinidumpGenerator generator(child_task, MACH_PORT_NULL); + string dump_filename = + MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL); + ASSERT_TRUE(generator.Write(dump_filename.c_str())); + + // Ensure that minidump file exists and is > 0 bytes. + struct stat st; + ASSERT_EQ(0, stat(dump_filename.c_str(), &st)); + ASSERT_LT(0, st.st_size); + + // Kill child process. + kill(pid, SIGKILL); + + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + +const MDCPUArchitecture kExpectedArchitecture = +#if defined(__x86_64__) + MD_CPU_ARCHITECTURE_X86 +#elif defined(__i386__) + MD_CPU_ARCHITECTURE_AMD64 +#endif + ; +const uint32_t kExpectedContext = +#if defined(__i386__) + MD_CONTEXT_AMD64 +#elif defined(__x86_64__) + MD_CONTEXT_X86 +#endif + ; + + // Read the minidump, sanity check some data. + Minidump minidump(dump_filename.c_str()); + ASSERT_TRUE(minidump.Read()); + + MinidumpSystemInfo* system_info = minidump.GetSystemInfo(); + ASSERT_TRUE(system_info); + const MDRawSystemInfo* raw_info = system_info->system_info(); + ASSERT_TRUE(raw_info); + EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture); + + MinidumpThreadList* thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list); + ASSERT_EQ((unsigned int)1, thread_list->thread_count()); + + MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0); + ASSERT_TRUE(main_thread); + MinidumpContext* context = main_thread->GetContext(); + ASSERT_TRUE(context); + EXPECT_EQ(kExpectedContext, context->GetContextCPU()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* main_module = module_list->GetMainModule(); + ASSERT_TRUE(main_module); + EXPECT_EQ(helper_path, main_module->code_file()); +} +#endif // 10.6 && (x86-64 || i386) + +} |