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