xref: /aosp_15_r20/external/google-breakpad/src/client/mac/tests/minidump_generator_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 // 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