PageRenderTime 42ms CodeModel.GetById 17ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/client/mac/tests/minidump_generator_test.cc

http://github.com/tomahawk-player/tomahawk
C++ | 319 lines | 211 code | 49 blank | 59 comment | 7 complexity | fe8a020879168a2103877315244f8bac MD5 | raw file
  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
 49namespace 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.
 53std::ostringstream info_log;
 54}
 55
 56namespace {
 57using std::string;
 58using std::vector;
 59using google_breakpad::AutoTempDir;
 60using google_breakpad::MinidumpGenerator;
 61using google_breakpad::MachPortSender;
 62using google_breakpad::MachReceiveMessage;
 63using google_breakpad::MachSendMessage;
 64using google_breakpad::Minidump;
 65using google_breakpad::MinidumpContext;
 66using google_breakpad::MinidumpException;
 67using google_breakpad::MinidumpModule;
 68using google_breakpad::MinidumpModuleList;
 69using google_breakpad::MinidumpSystemInfo;
 70using google_breakpad::MinidumpThread;
 71using google_breakpad::MinidumpThreadList;
 72using google_breakpad::ReceivePort;
 73using testing::Test;
 74using namespace google_breakpad_test;
 75
 76class MinidumpGeneratorTest : public Test {
 77 public:
 78  AutoTempDir tempDir;
 79};
 80
 81static void *Junk(void* data) {
 82  bool* wait = reinterpret_cast<bool*>(data);
 83  while (!*wait) {
 84    usleep(10000);
 85  }
 86  return NULL;
 87}
 88
 89TEST_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
137TEST_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
233TEST_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
276const MDCPUArchitecture kExpectedArchitecture =
277#if defined(__x86_64__)
278  MD_CPU_ARCHITECTURE_X86
279#elif defined(__i386__)
280  MD_CPU_ARCHITECTURE_AMD64
281#endif
282  ;
283const u_int32_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}