1 // Copyright 2010 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 // * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 // minidump_generator_test.cc: Unit tests for google_breakpad::MinidumpGenerator
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h> // Must come first
33 #endif
34
35 #include <AvailabilityMacros.h>
36 #ifndef MAC_OS_X_VERSION_10_6
37 #define MAC_OS_X_VERSION_10_6 1060
38 #endif
39 #include <sys/stat.h>
40 #include <unistd.h>
41
42 #include <string>
43 #include <vector>
44
45 #include "breakpad_googletest_includes.h"
46 #include "client/mac/handler/minidump_generator.h"
47 #include "client/mac/tests/spawn_child_process.h"
48 #include "common/linux/ignore_ret.h"
49 #include "common/mac/MachIPC.h"
50 #include "common/tests/auto_tempdir.h"
51 #include "google_breakpad/processor/minidump.h"
52
53 namespace google_breakpad {
54 // This acts as the log sink for INFO logging from the processor
55 // logging code. The logging output confuses XCode and makes it think
56 // there are unit test failures. testlogging.h handles the overriding.
57 std::ostringstream info_log;
58 }
59
60 namespace {
61 using std::string;
62 using std::vector;
63 using google_breakpad::AutoTempDir;
64 using google_breakpad::MinidumpGenerator;
65 using google_breakpad::MachPortSender;
66 using google_breakpad::MachReceiveMessage;
67 using google_breakpad::MachSendMessage;
68 using google_breakpad::Minidump;
69 using google_breakpad::MinidumpContext;
70 using google_breakpad::MinidumpException;
71 using google_breakpad::MinidumpModule;
72 using google_breakpad::MinidumpModuleList;
73 using google_breakpad::MinidumpSystemInfo;
74 using google_breakpad::MinidumpThread;
75 using google_breakpad::MinidumpThreadList;
76 using google_breakpad::ReceivePort;
77 using testing::Test;
78 using namespace google_breakpad_test;
79
80 class MinidumpGeneratorTest : public Test {
81 public:
82 AutoTempDir tempDir;
83 };
84
Junk(void * data)85 static void* Junk(void* data) {
86 bool* wait = reinterpret_cast<bool*>(data);
87 while (!*wait) {
88 usleep(10000);
89 }
90 return NULL;
91 }
92
TEST_F(MinidumpGeneratorTest,InProcess)93 TEST_F(MinidumpGeneratorTest, InProcess) {
94 MinidumpGenerator generator;
95 string dump_filename =
96 MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
97
98 // Run an extra thread since MinidumpGenerator assumes there
99 // are 2 or more threads.
100 pthread_t junk_thread;
101 bool quit = false;
102 ASSERT_EQ(0, pthread_create(&junk_thread, NULL, Junk, &quit));
103
104 ASSERT_TRUE(generator.Write(dump_filename.c_str()));
105 // Ensure that minidump file exists and is > 0 bytes.
106 struct stat st;
107 ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
108 ASSERT_LT(0, st.st_size);
109
110 // join the background thread
111 quit = true;
112 pthread_join(junk_thread, NULL);
113
114 // Read the minidump, sanity check some data.
115 Minidump minidump(dump_filename.c_str());
116 ASSERT_TRUE(minidump.Read());
117
118 MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
119 ASSERT_TRUE(system_info);
120 const MDRawSystemInfo* raw_info = system_info->system_info();
121 ASSERT_TRUE(raw_info);
122 EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
123
124 MinidumpThreadList* thread_list = minidump.GetThreadList();
125 ASSERT_TRUE(thread_list);
126 ASSERT_EQ((unsigned int)1, thread_list->thread_count());
127
128 MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
129 ASSERT_TRUE(main_thread);
130 MinidumpContext* context = main_thread->GetContext();
131 ASSERT_TRUE(context);
132 EXPECT_EQ(kNativeContext, context->GetContextCPU());
133
134 MinidumpModuleList* module_list = minidump.GetModuleList();
135 ASSERT_TRUE(module_list);
136 const MinidumpModule* main_module = module_list->GetMainModule();
137 ASSERT_TRUE(main_module);
138 EXPECT_EQ(GetExecutablePath(), main_module->code_file());
139 }
140
TEST_F(MinidumpGeneratorTest,OutOfProcess)141 TEST_F(MinidumpGeneratorTest, OutOfProcess) {
142 const int kTimeoutMs = 2000;
143 // Create a mach port to receive the child task on.
144 char machPortName[128];
145 sprintf(machPortName, "MinidumpGeneratorTest.OutOfProcess.%d", getpid());
146 ReceivePort parent_recv_port(machPortName);
147
148 // Give the child process a pipe to block on.
149 int fds[2];
150 ASSERT_EQ(0, pipe(fds));
151
152 // Fork off a child process to dump.
153 pid_t pid = fork();
154 if (pid == 0) {
155 // In the child process
156 close(fds[1]);
157
158 // Send parent process the task port.
159 MachSendMessage child_message(0);
160 child_message.AddDescriptor(mach_task_self());
161
162 MachPortSender child_sender(machPortName);
163 if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) {
164 fprintf(stderr, "Error sending message from child process!\n");
165 exit(1);
166 }
167
168 // Wait for the parent process.
169 uint8_t data;
170 read(fds[0], &data, 1);
171 exit(0);
172 }
173 // In the parent process.
174 ASSERT_NE(-1, pid);
175 close(fds[0]);
176
177 // Read the child's task port.
178 MachReceiveMessage child_message;
179 ASSERT_EQ(KERN_SUCCESS,
180 parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
181 mach_port_t child_task = child_message.GetTranslatedPort(0);
182 ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
183
184 // Write a minidump of the child process.
185 MinidumpGenerator generator(child_task, MACH_PORT_NULL);
186 string dump_filename =
187 MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
188 ASSERT_TRUE(generator.Write(dump_filename.c_str()));
189
190 // Ensure that minidump file exists and is > 0 bytes.
191 struct stat st;
192 ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
193 ASSERT_LT(0, st.st_size);
194
195 // Unblock child process
196 uint8_t data = 1;
197 IGNORE_RET(write(fds[1], &data, 1));
198
199 // Child process should have exited with a zero status.
200 int ret;
201 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
202 EXPECT_NE(0, WIFEXITED(ret));
203 EXPECT_EQ(0, WEXITSTATUS(ret));
204
205 // Read the minidump, sanity check some data.
206 Minidump minidump(dump_filename.c_str());
207 ASSERT_TRUE(minidump.Read());
208
209 MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
210 ASSERT_TRUE(system_info);
211 const MDRawSystemInfo* raw_info = system_info->system_info();
212 ASSERT_TRUE(raw_info);
213 EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
214
215 MinidumpThreadList* thread_list = minidump.GetThreadList();
216 ASSERT_TRUE(thread_list);
217 ASSERT_EQ((unsigned int)1, thread_list->thread_count());
218
219 MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
220 ASSERT_TRUE(main_thread);
221 MinidumpContext* context = main_thread->GetContext();
222 ASSERT_TRUE(context);
223 EXPECT_EQ(kNativeContext, context->GetContextCPU());
224
225 MinidumpModuleList* module_list = minidump.GetModuleList();
226 ASSERT_TRUE(module_list);
227 const MinidumpModule* main_module = module_list->GetMainModule();
228 ASSERT_TRUE(main_module);
229 EXPECT_EQ(GetExecutablePath(), main_module->code_file());
230 }
231
232 // This test fails on 10.5, but I don't have easy access to a 10.5 machine,
233 // so it's simpler to just limit it to 10.6 for now.
234 #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \
235 (defined(__x86_64__) || defined(__i386__))
236
TEST_F(MinidumpGeneratorTest,CrossArchitectureDump)237 TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) {
238 const int kTimeoutMs = 5000;
239 // Create a mach port to receive the child task on.
240 char machPortName[128];
241 sprintf(machPortName,
242 "MinidumpGeneratorTest.CrossArchitectureDump.%d", getpid());
243
244 ReceivePort parent_recv_port(machPortName);
245
246 // Spawn a child process to dump.
247 string helper_path = GetHelperPath();
248 const char* argv[] = {
249 helper_path.c_str(),
250 machPortName,
251 NULL
252 };
253 pid_t pid = spawn_child_process(argv);
254 ASSERT_NE(-1, pid);
255
256 // Read the child's task port.
257 MachReceiveMessage child_message;
258 ASSERT_EQ(KERN_SUCCESS,
259 parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
260 mach_port_t child_task = child_message.GetTranslatedPort(0);
261 ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
262
263 // Write a minidump of the child process.
264 MinidumpGenerator generator(child_task, MACH_PORT_NULL);
265 string dump_filename =
266 MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
267 ASSERT_TRUE(generator.Write(dump_filename.c_str()));
268
269 // Ensure that minidump file exists and is > 0 bytes.
270 struct stat st;
271 ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
272 ASSERT_LT(0, st.st_size);
273
274 // Kill child process.
275 kill(pid, SIGKILL);
276
277 int ret;
278 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
279
280 const MDCPUArchitecture kExpectedArchitecture =
281 #if defined(__x86_64__)
282 MD_CPU_ARCHITECTURE_X86
283 #elif defined(__i386__)
284 MD_CPU_ARCHITECTURE_AMD64
285 #endif
286 ;
287 const uint32_t kExpectedContext =
288 #if defined(__i386__)
289 MD_CONTEXT_AMD64
290 #elif defined(__x86_64__)
291 MD_CONTEXT_X86
292 #endif
293 ;
294
295 // Read the minidump, sanity check some data.
296 Minidump minidump(dump_filename.c_str());
297 ASSERT_TRUE(minidump.Read());
298
299 MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
300 ASSERT_TRUE(system_info);
301 const MDRawSystemInfo* raw_info = system_info->system_info();
302 ASSERT_TRUE(raw_info);
303 EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture);
304
305 MinidumpThreadList* thread_list = minidump.GetThreadList();
306 ASSERT_TRUE(thread_list);
307 ASSERT_EQ((unsigned int)1, thread_list->thread_count());
308
309 MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
310 ASSERT_TRUE(main_thread);
311 MinidumpContext* context = main_thread->GetContext();
312 ASSERT_TRUE(context);
313 EXPECT_EQ(kExpectedContext, context->GetContextCPU());
314
315 MinidumpModuleList* module_list = minidump.GetModuleList();
316 ASSERT_TRUE(module_list);
317 const MinidumpModule* main_module = module_list->GetMainModule();
318 ASSERT_TRUE(main_module);
319 EXPECT_EQ(helper_path, main_module->code_file());
320 }
321 #endif // 10.6 && (x86-64 || i386)
322
323 }
324