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 // crash_generation_server_test.cc
31 // Unit tests for CrashGenerationServer
32
33 #include <dirent.h>
34 #include <glob.h>
35 #include <stdint.h>
36 #include <sys/wait.h>
37 #include <unistd.h>
38
39 #include <string>
40
41 #include "breakpad_googletest_includes.h"
42 #include "client/mac/crash_generation/client_info.h"
43 #include "client/mac/crash_generation/crash_generation_client.h"
44 #include "client/mac/crash_generation/crash_generation_server.h"
45 #include "client/mac/handler/exception_handler.h"
46 #include "client/mac/tests/spawn_child_process.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 google_breakpad::AutoTempDir;
60 using google_breakpad::ClientInfo;
61 using google_breakpad::CrashGenerationClient;
62 using google_breakpad::CrashGenerationServer;
63 using google_breakpad::ExceptionHandler;
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 testing::Test;
73 using namespace google_breakpad_test;
74
75 class CrashGenerationServerTest : public Test {
76 public:
77 // The port name to receive messages on
78 char mach_port_name[128];
79 // Filename of the last dump that was generated
80 string last_dump_name;
81 // PID of the child process
82 pid_t child_pid;
83 // A temp dir
84 AutoTempDir temp_dir;
85 // Counter just to ensure that we don't hit the same port again
86 static int i;
87 bool filter_callback_called;
88
SetUp()89 void SetUp() {
90 sprintf(mach_port_name,
91 "com.google.breakpad.ServerTest.%d.%d", getpid(),
92 CrashGenerationServerTest::i++);
93 child_pid = (pid_t)-1;
94 filter_callback_called = false;
95 }
96 };
97 int CrashGenerationServerTest::i = 0;
98
99 // Test that starting and stopping a server works
TEST_F(CrashGenerationServerTest,testStartStopServer)100 TEST_F(CrashGenerationServerTest, testStartStopServer) {
101 CrashGenerationServer server(mach_port_name,
102 NULL, // filter callback
103 NULL, // filter context
104 NULL, // dump callback
105 NULL, // dump context
106 NULL, // exit callback
107 NULL, // exit context
108 false, // generate dumps
109 ""); // dump path
110 ASSERT_TRUE(server.Start());
111 ASSERT_TRUE(server.Stop());
112 }
113
114 // Test that requesting a dump via CrashGenerationClient works
115 // Test without actually dumping
TEST_F(CrashGenerationServerTest,testRequestDumpNoDump)116 TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
117 CrashGenerationServer server(mach_port_name,
118 NULL, // filter callback
119 NULL, // filter context
120 NULL, // dump callback
121 NULL, // dump context
122 NULL, // exit callback
123 NULL, // exit context
124 false, // don't generate dumps
125 temp_dir.path()); // dump path
126 ASSERT_TRUE(server.Start());
127
128 pid_t pid = fork();
129 ASSERT_NE(-1, pid);
130 if (pid == 0) {
131 CrashGenerationClient client(mach_port_name);
132 bool result = client.RequestDump();
133 exit(result ? 0 : 1);
134 }
135
136 int ret;
137 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
138 EXPECT_TRUE(WIFEXITED(ret));
139 EXPECT_EQ(0, WEXITSTATUS(ret));
140 EXPECT_TRUE(server.Stop());
141 // check that no minidump was written
142 string pattern = temp_dir.path() + "/*";
143 glob_t dirContents;
144 ret = glob(pattern.c_str(), GLOB_NOSORT, NULL, &dirContents);
145 EXPECT_EQ(GLOB_NOMATCH, ret);
146 if (ret != GLOB_NOMATCH)
147 globfree(&dirContents);
148 }
149
dumpCallback(void * context,const ClientInfo & client_info,const std::string & file_path)150 void dumpCallback(void *context, const ClientInfo &client_info,
151 const std::string &file_path) {
152 if (context) {
153 CrashGenerationServerTest* self =
154 reinterpret_cast<CrashGenerationServerTest*>(context);
155 if (!file_path.empty())
156 self->last_dump_name = file_path;
157 self->child_pid = client_info.pid();
158 }
159 }
160
RequestDump(void * context)161 void *RequestDump(void *context) {
162 CrashGenerationClient client((const char*)context);
163 bool result = client.RequestDump();
164 return (void*)(result ? 0 : 1);
165 }
166
167 // Test that actually writing a minidump works
TEST_F(CrashGenerationServerTest,testRequestDump)168 TEST_F(CrashGenerationServerTest, testRequestDump) {
169 CrashGenerationServer server(mach_port_name,
170 NULL, // filter callback
171 NULL, // filter context
172 dumpCallback, // dump callback
173 this, // dump context
174 NULL, // exit callback
175 NULL, // exit context
176 true, // generate dumps
177 temp_dir.path()); // dump path
178 ASSERT_TRUE(server.Start());
179
180 pid_t pid = fork();
181 ASSERT_NE(-1, pid);
182 if (pid == 0) {
183 // Have to spawn off a separate thread to request the dump,
184 // because MinidumpGenerator assumes the handler thread is not
185 // the only thread
186 pthread_t thread;
187 if (pthread_create(&thread, NULL, RequestDump, (void*)mach_port_name) != 0)
188 exit(1);
189 void* result;
190 pthread_join(thread, &result);
191 exit(reinterpret_cast<intptr_t>(result));
192 }
193
194 int ret;
195 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
196 EXPECT_TRUE(WIFEXITED(ret));
197 EXPECT_EQ(0, WEXITSTATUS(ret));
198 EXPECT_TRUE(server.Stop());
199 // check that minidump was written
200 ASSERT_FALSE(last_dump_name.empty());
201 struct stat st;
202 EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
203 EXPECT_LT(0, st.st_size);
204 // check client's PID
205 ASSERT_EQ(pid, child_pid);
206 }
207
Crasher()208 static void Crasher() {
209 int *a = (int*)0x42;
210
211 fprintf(stdout, "Going to crash...\n");
212 fprintf(stdout, "A = %d", *a);
213 }
214
215 // Test that crashing a child process with an OOP ExceptionHandler installed
216 // results in a minidump being written by the CrashGenerationServer in
217 // the parent.
TEST_F(CrashGenerationServerTest,testChildProcessCrash)218 TEST_F(CrashGenerationServerTest, testChildProcessCrash) {
219 CrashGenerationServer server(mach_port_name,
220 NULL, // filter callback
221 NULL, // filter context
222 dumpCallback, // dump callback
223 this, // dump context
224 NULL, // exit callback
225 NULL, // exit context
226 true, // generate dumps
227 temp_dir.path()); // dump path
228 ASSERT_TRUE(server.Start());
229
230 pid_t pid = fork();
231 ASSERT_NE(-1, pid);
232 if (pid == 0) {
233 // Instantiate an OOP exception handler.
234 ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name);
235 Crasher();
236 // not reached
237 exit(0);
238 }
239
240 int ret;
241 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
242 EXPECT_FALSE(WIFEXITED(ret));
243 EXPECT_TRUE(server.Stop());
244 // check that minidump was written
245 ASSERT_FALSE(last_dump_name.empty());
246 struct stat st;
247 EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
248 EXPECT_LT(0, st.st_size);
249
250 // Read the minidump, sanity check some data.
251 Minidump minidump(last_dump_name.c_str());
252 ASSERT_TRUE(minidump.Read());
253
254 MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
255 ASSERT_TRUE(system_info);
256 const MDRawSystemInfo* raw_info = system_info->system_info();
257 ASSERT_TRUE(raw_info);
258 EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
259
260 MinidumpThreadList* thread_list = minidump.GetThreadList();
261 ASSERT_TRUE(thread_list);
262 ASSERT_EQ((unsigned int)1, thread_list->thread_count());
263
264 MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
265 ASSERT_TRUE(main_thread);
266 MinidumpContext* context = main_thread->GetContext();
267 ASSERT_TRUE(context);
268 EXPECT_EQ(kNativeContext, context->GetContextCPU());
269
270 MinidumpModuleList* module_list = minidump.GetModuleList();
271 ASSERT_TRUE(module_list);
272 const MinidumpModule* main_module = module_list->GetMainModule();
273 ASSERT_TRUE(main_module);
274 EXPECT_EQ(GetExecutablePath(), main_module->code_file());
275 }
276
277 #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \
278 (defined(__x86_64__) || defined(__i386__))
279 // Test that crashing a child process of a different architecture
280 // produces a valid minidump.
TEST_F(CrashGenerationServerTest,testChildProcessCrashCrossArchitecture)281 TEST_F(CrashGenerationServerTest, testChildProcessCrashCrossArchitecture) {
282 CrashGenerationServer server(mach_port_name,
283 NULL, // filter callback
284 NULL, // filter context
285 dumpCallback, // dump callback
286 this, // dump context
287 NULL, // exit callback
288 NULL, // exit context
289 true, // generate dumps
290 temp_dir.path()); // dump path
291 ASSERT_TRUE(server.Start());
292
293 // Spawn a child process
294 string helper_path = GetHelperPath();
295 const char* argv[] = {
296 helper_path.c_str(),
297 "crash",
298 mach_port_name,
299 NULL
300 };
301 pid_t pid = spawn_child_process(argv);
302 ASSERT_NE(-1, pid);
303
304 int ret;
305 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
306 EXPECT_FALSE(WIFEXITED(ret));
307 EXPECT_TRUE(server.Stop());
308 // check that minidump was written
309 ASSERT_FALSE(last_dump_name.empty());
310 struct stat st;
311 EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
312 EXPECT_LT(0, st.st_size);
313
314 const MDCPUArchitecture kExpectedArchitecture =
315 #if defined(__x86_64__)
316 MD_CPU_ARCHITECTURE_X86
317 #elif defined(__i386__)
318 MD_CPU_ARCHITECTURE_AMD64
319 #endif
320 ;
321 const uint32_t kExpectedContext =
322 #if defined(__i386__)
323 MD_CONTEXT_AMD64
324 #elif defined(__x86_64__)
325 MD_CONTEXT_X86
326 #endif
327 ;
328
329 // Read the minidump, sanity check some data.
330 Minidump minidump(last_dump_name.c_str());
331 ASSERT_TRUE(minidump.Read());
332
333 MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
334 ASSERT_TRUE(system_info);
335 const MDRawSystemInfo* raw_info = system_info->system_info();
336 ASSERT_TRUE(raw_info);
337 EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture);
338
339 MinidumpThreadList* thread_list = minidump.GetThreadList();
340 ASSERT_TRUE(thread_list);
341 ASSERT_EQ((unsigned int)1, thread_list->thread_count());
342
343 MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
344 ASSERT_TRUE(main_thread);
345 MinidumpContext* context = main_thread->GetContext();
346 ASSERT_TRUE(context);
347 EXPECT_EQ(kExpectedContext, context->GetContextCPU());
348
349 MinidumpModuleList* module_list = minidump.GetModuleList();
350 ASSERT_TRUE(module_list);
351 const MinidumpModule* main_module = module_list->GetMainModule();
352 ASSERT_TRUE(main_module);
353 EXPECT_EQ(helper_path, main_module->code_file());
354 }
355 #endif
356
filter_callback(void * context)357 bool filter_callback(void* context) {
358 CrashGenerationServerTest* self =
359 reinterpret_cast<CrashGenerationServerTest*>(context);
360 self->filter_callback_called = true;
361 // veto dump generation
362 return false;
363 }
364
365 // Test that a filter callback can veto minidump writing.
TEST_F(CrashGenerationServerTest,testFilter)366 TEST_F(CrashGenerationServerTest, testFilter) {
367 CrashGenerationServer server(mach_port_name,
368 filter_callback, // filter callback
369 this, // filter context
370 dumpCallback, // dump callback
371 this, // dump context
372 NULL, // exit callback
373 NULL, // exit context
374 true, // generate dumps
375 temp_dir.path()); // dump path
376 ASSERT_TRUE(server.Start());
377
378 pid_t pid = fork();
379 ASSERT_NE(-1, pid);
380 if (pid == 0) {
381 // Instantiate an OOP exception handler.
382 ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name);
383 Crasher();
384 // not reached
385 exit(0);
386 }
387
388 int ret;
389 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
390 EXPECT_FALSE(WIFEXITED(ret));
391 EXPECT_TRUE(server.Stop());
392
393 // check that no minidump was written
394 EXPECT_TRUE(last_dump_name.empty());
395 EXPECT_TRUE(filter_callback_called);
396 }
397
398 } // namespace
399