• 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/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 
Junk(void * data)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 
TEST_F(MinidumpGeneratorTest,InProcess)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 
TEST_F(MinidumpGeneratorTest,OutOfProcess)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 
TEST_F(MinidumpGeneratorTest,CrossArchitectureDump)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