• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/linux/ignore_ret.h"
46 #include "common/mac/MachIPC.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 std::vector;
60 using google_breakpad::AutoTempDir;
61 using google_breakpad::MinidumpGenerator;
62 using google_breakpad::MachPortSender;
63 using google_breakpad::MachReceiveMessage;
64 using google_breakpad::MachSendMessage;
65 using google_breakpad::Minidump;
66 using google_breakpad::MinidumpContext;
67 using google_breakpad::MinidumpException;
68 using google_breakpad::MinidumpModule;
69 using google_breakpad::MinidumpModuleList;
70 using google_breakpad::MinidumpSystemInfo;
71 using google_breakpad::MinidumpThread;
72 using google_breakpad::MinidumpThreadList;
73 using google_breakpad::ReceivePort;
74 using testing::Test;
75 using namespace google_breakpad_test;
76 
77 class MinidumpGeneratorTest : public Test {
78  public:
79   AutoTempDir tempDir;
80 };
81 
Junk(void * data)82 static void *Junk(void* data) {
83   bool* wait = reinterpret_cast<bool*>(data);
84   while (!*wait) {
85     usleep(10000);
86   }
87   return NULL;
88 }
89 
TEST_F(MinidumpGeneratorTest,InProcess)90 TEST_F(MinidumpGeneratorTest, InProcess) {
91   MinidumpGenerator generator;
92   string dump_filename =
93       MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
94 
95   // Run an extra thread since MinidumpGenerator assumes there
96   // are 2 or more threads.
97   pthread_t junk_thread;
98   bool quit = false;
99   ASSERT_EQ(0, pthread_create(&junk_thread, NULL, Junk, &quit));
100 
101   ASSERT_TRUE(generator.Write(dump_filename.c_str()));
102   // Ensure that minidump file exists and is > 0 bytes.
103   struct stat st;
104   ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
105   ASSERT_LT(0, st.st_size);
106 
107   // join the background thread
108   quit = true;
109   pthread_join(junk_thread, NULL);
110 
111   // Read the minidump, sanity check some data.
112   Minidump minidump(dump_filename.c_str());
113   ASSERT_TRUE(minidump.Read());
114 
115   MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
116   ASSERT_TRUE(system_info);
117   const MDRawSystemInfo* raw_info = system_info->system_info();
118   ASSERT_TRUE(raw_info);
119   EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
120 
121   MinidumpThreadList* thread_list = minidump.GetThreadList();
122   ASSERT_TRUE(thread_list);
123   ASSERT_EQ((unsigned int)1, thread_list->thread_count());
124 
125   MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
126   ASSERT_TRUE(main_thread);
127   MinidumpContext* context = main_thread->GetContext();
128   ASSERT_TRUE(context);
129   EXPECT_EQ(kNativeContext, context->GetContextCPU());
130 
131   MinidumpModuleList* module_list = minidump.GetModuleList();
132   ASSERT_TRUE(module_list);
133   const MinidumpModule* main_module = module_list->GetMainModule();
134   ASSERT_TRUE(main_module);
135   EXPECT_EQ(GetExecutablePath(), main_module->code_file());
136 }
137 
TEST_F(MinidumpGeneratorTest,OutOfProcess)138 TEST_F(MinidumpGeneratorTest, OutOfProcess) {
139   const int kTimeoutMs = 2000;
140   // Create a mach port to receive the child task on.
141   char machPortName[128];
142   sprintf(machPortName, "MinidumpGeneratorTest.OutOfProcess.%d", getpid());
143   ReceivePort parent_recv_port(machPortName);
144 
145   // Give the child process a pipe to block on.
146   int fds[2];
147   ASSERT_EQ(0, pipe(fds));
148 
149   // Fork off a child process to dump.
150   pid_t pid = fork();
151   if (pid == 0) {
152     // In the child process
153     close(fds[1]);
154 
155     // Send parent process the task port.
156     MachSendMessage child_message(0);
157     child_message.AddDescriptor(mach_task_self());
158 
159     MachPortSender child_sender(machPortName);
160     if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) {
161       fprintf(stderr, "Error sending message from child process!\n");
162       exit(1);
163     }
164 
165     // Wait for the parent process.
166     uint8_t data;
167     read(fds[0], &data, 1);
168     exit(0);
169   }
170   // In the parent process.
171   ASSERT_NE(-1, pid);
172   close(fds[0]);
173 
174   // Read the child's task port.
175   MachReceiveMessage child_message;
176   ASSERT_EQ(KERN_SUCCESS,
177 	    parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
178   mach_port_t child_task = child_message.GetTranslatedPort(0);
179   ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
180 
181   // Write a minidump of the child process.
182   MinidumpGenerator generator(child_task, MACH_PORT_NULL);
183   string dump_filename =
184       MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
185   ASSERT_TRUE(generator.Write(dump_filename.c_str()));
186 
187   // Ensure that minidump file exists and is > 0 bytes.
188   struct stat st;
189   ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
190   ASSERT_LT(0, st.st_size);
191 
192   // Unblock child process
193   uint8_t data = 1;
194   IGNORE_RET(write(fds[1], &data, 1));
195 
196   // Child process should have exited with a zero status.
197   int ret;
198   ASSERT_EQ(pid, waitpid(pid, &ret, 0));
199   EXPECT_NE(0, WIFEXITED(ret));
200   EXPECT_EQ(0, WEXITSTATUS(ret));
201 
202   // Read the minidump, sanity check some data.
203   Minidump minidump(dump_filename.c_str());
204   ASSERT_TRUE(minidump.Read());
205 
206   MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
207   ASSERT_TRUE(system_info);
208   const MDRawSystemInfo* raw_info = system_info->system_info();
209   ASSERT_TRUE(raw_info);
210   EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
211 
212   MinidumpThreadList* thread_list = minidump.GetThreadList();
213   ASSERT_TRUE(thread_list);
214   ASSERT_EQ((unsigned int)1, thread_list->thread_count());
215 
216   MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
217   ASSERT_TRUE(main_thread);
218   MinidumpContext* context = main_thread->GetContext();
219   ASSERT_TRUE(context);
220   EXPECT_EQ(kNativeContext, context->GetContextCPU());
221 
222   MinidumpModuleList* module_list = minidump.GetModuleList();
223   ASSERT_TRUE(module_list);
224   const MinidumpModule* main_module = module_list->GetMainModule();
225   ASSERT_TRUE(main_module);
226   EXPECT_EQ(GetExecutablePath(), main_module->code_file());
227 }
228 
229 // This test fails on 10.5, but I don't have easy access to a 10.5 machine,
230 // so it's simpler to just limit it to 10.6 for now.
231 #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \
232   (defined(__x86_64__) || defined(__i386__))
233 
TEST_F(MinidumpGeneratorTest,CrossArchitectureDump)234 TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) {
235   const int kTimeoutMs = 5000;
236   // Create a mach port to receive the child task on.
237   char machPortName[128];
238   sprintf(machPortName,
239           "MinidumpGeneratorTest.CrossArchitectureDump.%d", getpid());
240 
241   ReceivePort parent_recv_port(machPortName);
242 
243   // Spawn a child process to dump.
244   string helper_path = GetHelperPath();
245   const char* argv[] = {
246     helper_path.c_str(),
247     machPortName,
248     NULL
249   };
250   pid_t pid = spawn_child_process(argv);
251   ASSERT_NE(-1, pid);
252 
253   // Read the child's task port.
254   MachReceiveMessage child_message;
255   ASSERT_EQ(KERN_SUCCESS,
256 	    parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
257   mach_port_t child_task = child_message.GetTranslatedPort(0);
258   ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
259 
260   // Write a minidump of the child process.
261   MinidumpGenerator generator(child_task, MACH_PORT_NULL);
262   string dump_filename =
263       MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
264   ASSERT_TRUE(generator.Write(dump_filename.c_str()));
265 
266   // Ensure that minidump file exists and is > 0 bytes.
267   struct stat st;
268   ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
269   ASSERT_LT(0, st.st_size);
270 
271   // Kill child process.
272   kill(pid, SIGKILL);
273 
274   int ret;
275   ASSERT_EQ(pid, waitpid(pid, &ret, 0));
276 
277 const MDCPUArchitecture kExpectedArchitecture =
278 #if defined(__x86_64__)
279   MD_CPU_ARCHITECTURE_X86
280 #elif defined(__i386__)
281   MD_CPU_ARCHITECTURE_AMD64
282 #endif
283   ;
284 const uint32_t kExpectedContext =
285 #if defined(__i386__)
286   MD_CONTEXT_AMD64
287 #elif defined(__x86_64__)
288   MD_CONTEXT_X86
289 #endif
290   ;
291 
292   // Read the minidump, sanity check some data.
293   Minidump minidump(dump_filename.c_str());
294   ASSERT_TRUE(minidump.Read());
295 
296   MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
297   ASSERT_TRUE(system_info);
298   const MDRawSystemInfo* raw_info = system_info->system_info();
299   ASSERT_TRUE(raw_info);
300   EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture);
301 
302   MinidumpThreadList* thread_list = minidump.GetThreadList();
303   ASSERT_TRUE(thread_list);
304   ASSERT_EQ((unsigned int)1, thread_list->thread_count());
305 
306   MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
307   ASSERT_TRUE(main_thread);
308   MinidumpContext* context = main_thread->GetContext();
309   ASSERT_TRUE(context);
310   EXPECT_EQ(kExpectedContext, context->GetContextCPU());
311 
312   MinidumpModuleList* module_list = minidump.GetModuleList();
313   ASSERT_TRUE(module_list);
314   const MinidumpModule* main_module = module_list->GetMainModule();
315   ASSERT_TRUE(main_module);
316   EXPECT_EQ(helper_path, main_module->code_file());
317 }
318 #endif  // 10.6 && (x86-64 || i386)
319 
320 }
321