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