xref: /aosp_15_r20/external/google-breakpad/src/client/mac/tests/crash_generation_server_test.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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