• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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