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