1 // Copyright (c) 2011 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 #include <fcntl.h>
31 #include <sys/poll.h>
32 #include <sys/stat.h>
33 #include <sys/syscall.h>
34 #include <sys/types.h>
35 #include <ucontext.h>
36 #include <unistd.h>
37 
38 #include <string>
39 
40 #include "breakpad_googletest_includes.h"
41 #include "client/linux/handler/exception_handler.h"
42 #include "client/linux/minidump_writer/linux_dumper.h"
43 #include "client/linux/minidump_writer/minidump_writer.h"
44 #include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
45 #include "common/linux/eintr_wrapper.h"
46 #include "common/linux/file_id.h"
47 #include "common/linux/ignore_ret.h"
48 #include "common/linux/safe_readlink.h"
49 #include "common/scoped_ptr.h"
50 #include "common/tests/auto_tempdir.h"
51 #include "common/tests/file_utils.h"
52 #include "common/using_std_string.h"
53 #include "google_breakpad/processor/minidump.h"
54 
55 using namespace google_breakpad;
56 
57 // Length of a formatted GUID string =
58 // sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
59 const int kGUIDStringSize = 37;
60 
61 namespace {
62 
63 typedef testing::Test MinidumpWriterTest;
64 
65 const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest";
66 
TEST(MinidumpWriterTest,SetupWithPath)67 TEST(MinidumpWriterTest, SetupWithPath) {
68   int fds[2];
69   ASSERT_NE(-1, pipe(fds));
70 
71   const pid_t child = fork();
72   if (child == 0) {
73     close(fds[1]);
74     char b;
75     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
76     close(fds[0]);
77     syscall(__NR_exit);
78   }
79   close(fds[0]);
80 
81   ExceptionHandler::CrashContext context;
82   memset(&context, 0, sizeof(context));
83 
84   AutoTempDir temp_dir;
85   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
86   // Set a non-zero tid to avoid tripping asserts.
87   context.tid = child;
88   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
89   struct stat st;
90   ASSERT_EQ(0, stat(templ.c_str(), &st));
91   ASSERT_GT(st.st_size, 0);
92 
93   close(fds[1]);
94 }
95 
TEST(MinidumpWriterTest,SetupWithFD)96 TEST(MinidumpWriterTest, SetupWithFD) {
97   int fds[2];
98   ASSERT_NE(-1, pipe(fds));
99 
100   const pid_t child = fork();
101   if (child == 0) {
102     close(fds[1]);
103     char b;
104     HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
105     close(fds[0]);
106     syscall(__NR_exit);
107   }
108   close(fds[0]);
109 
110   ExceptionHandler::CrashContext context;
111   memset(&context, 0, sizeof(context));
112 
113   AutoTempDir temp_dir;
114   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
115   int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU);
116   // Set a non-zero tid to avoid tripping asserts.
117   context.tid = child;
118   ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context)));
119   struct stat st;
120   ASSERT_EQ(0, stat(templ.c_str(), &st));
121   ASSERT_GT(st.st_size, 0);
122 
123   close(fds[1]);
124 }
125 
126 // Test that mapping info can be specified when writing a minidump,
127 // and that it ends up in the module list of the minidump.
TEST(MinidumpWriterTest,MappingInfo)128 TEST(MinidumpWriterTest, MappingInfo) {
129   int fds[2];
130   ASSERT_NE(-1, pipe(fds));
131 
132   // These are defined here so the parent can use them to check the
133   // data from the minidump afterwards.
134   const uint32_t memory_size = sysconf(_SC_PAGESIZE);
135   const char* kMemoryName = "a fake module";
136   const uint8_t kModuleGUID[sizeof(MDGUID)] = {
137     0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
138     0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
139   };
140   char module_identifier_buffer[kGUIDStringSize];
141   FileID::ConvertIdentifierToString(kModuleGUID,
142                                     module_identifier_buffer,
143                                     sizeof(module_identifier_buffer));
144   string module_identifier(module_identifier_buffer);
145   // Strip out dashes
146   size_t pos;
147   while ((pos = module_identifier.find('-')) != string::npos) {
148     module_identifier.erase(pos, 1);
149   }
150   // And append a zero, because module IDs include an "age" field
151   // which is always zero on Linux.
152   module_identifier += "0";
153 
154   // Get some memory.
155   char* memory =
156     reinterpret_cast<char*>(mmap(NULL,
157                                  memory_size,
158                                  PROT_READ | PROT_WRITE,
159                                  MAP_PRIVATE | MAP_ANON,
160                                  -1,
161                                  0));
162   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
163   ASSERT_TRUE(memory);
164 
165   const pid_t child = fork();
166   if (child == 0) {
167     close(fds[1]);
168     char b;
169     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
170     close(fds[0]);
171     syscall(__NR_exit);
172   }
173   close(fds[0]);
174 
175   ExceptionHandler::CrashContext context;
176   memset(&context, 0, sizeof(context));
177   ASSERT_EQ(0, getcontext(&context.context));
178   context.tid = child;
179 
180   AutoTempDir temp_dir;
181   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
182 
183   // Add information about the mapped memory.
184   MappingInfo info;
185   info.start_addr = kMemoryAddress;
186   info.size = memory_size;
187   info.offset = 0;
188   strcpy(info.name, kMemoryName);
189 
190   MappingList mappings;
191   AppMemoryList memory_list;
192   MappingEntry mapping;
193   mapping.first = info;
194   memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
195   mappings.push_back(mapping);
196   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
197                             mappings, memory_list));
198 
199   // Read the minidump. Load the module list, and ensure that
200   // the mmap'ed |memory| is listed with the given module name
201   // and debug ID.
202   Minidump minidump(templ);
203   ASSERT_TRUE(minidump.Read());
204 
205   MinidumpModuleList* module_list = minidump.GetModuleList();
206   ASSERT_TRUE(module_list);
207   const MinidumpModule* module =
208     module_list->GetModuleForAddress(kMemoryAddress);
209   ASSERT_TRUE(module);
210 
211   EXPECT_EQ(kMemoryAddress, module->base_address());
212   EXPECT_EQ(memory_size, module->size());
213   EXPECT_EQ(kMemoryName, module->code_file());
214   EXPECT_EQ(module_identifier, module->debug_identifier());
215 
216   uint32_t len;
217   // These streams are expected to be there
218   EXPECT_TRUE(minidump.SeekToStreamType(MD_THREAD_LIST_STREAM, &len));
219   EXPECT_TRUE(minidump.SeekToStreamType(MD_MEMORY_LIST_STREAM, &len));
220   EXPECT_TRUE(minidump.SeekToStreamType(MD_EXCEPTION_STREAM, &len));
221   EXPECT_TRUE(minidump.SeekToStreamType(MD_SYSTEM_INFO_STREAM, &len));
222   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CPU_INFO, &len));
223   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_PROC_STATUS, &len));
224   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CMD_LINE, &len));
225   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_ENVIRON, &len));
226   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_AUXV, &len));
227   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_MAPS, &len));
228   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len));
229 
230   close(fds[1]);
231 }
232 
233 // Test that mapping info can be specified, and that it overrides
234 // existing mappings that are wholly contained within the specified
235 // range.
236 #ifdef ENABLE_FLAKY_TESTS
TEST(MinidumpWriterTest,MappingInfoContained)237 TEST(MinidumpWriterTest, MappingInfoContained) {
238   int fds[2];
239   ASSERT_NE(-1, pipe(fds));
240 
241   // These are defined here so the parent can use them to check the
242   // data from the minidump afterwards.
243   const int32_t memory_size = sysconf(_SC_PAGESIZE);
244   const char* kMemoryName = "a fake module";
245   const uint8_t kModuleGUID[sizeof(MDGUID)] = {
246     0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
247     0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
248   };
249   char module_identifier_buffer[kGUIDStringSize];
250   FileID::ConvertIdentifierToString(kModuleGUID,
251                                     module_identifier_buffer,
252                                     sizeof(module_identifier_buffer));
253   string module_identifier(module_identifier_buffer);
254   // Strip out dashes
255   size_t pos;
256   while ((pos = module_identifier.find('-')) != string::npos) {
257     module_identifier.erase(pos, 1);
258   }
259   // And append a zero, because module IDs include an "age" field
260   // which is always zero on Linux.
261   module_identifier += "0";
262 
263   // mmap a file
264   AutoTempDir temp_dir;
265   string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
266   int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
267   ASSERT_NE(-1, fd);
268   unlink(tempfile.c_str());
269   // fill with zeros
270   google_breakpad::scoped_array<char> buffer(new char[memory_size]);
271   memset(buffer.get(), 0, memory_size);
272   ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size));
273   lseek(fd, 0, SEEK_SET);
274 
275   char* memory =
276     reinterpret_cast<char*>(mmap(NULL,
277                                  memory_size,
278                                  PROT_READ | PROT_WRITE,
279                                  MAP_PRIVATE,
280                                  fd,
281                                  0));
282   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
283   ASSERT_TRUE(memory);
284   close(fd);
285 
286   const pid_t child = fork();
287   if (child == 0) {
288     close(fds[1]);
289     char b;
290     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
291     close(fds[0]);
292     syscall(__NR_exit);
293   }
294   close(fds[0]);
295 
296   ExceptionHandler::CrashContext context;
297   memset(&context, 0, sizeof(context));
298   context.tid = 1;
299 
300   string dumpfile = temp_dir.path() + kMDWriterUnitTestFileName;
301 
302   // Add information about the mapped memory. Report it as being larger than
303   // it actually is.
304   MappingInfo info;
305   info.start_addr = kMemoryAddress - memory_size;
306   info.size = memory_size * 3;
307   info.offset = 0;
308   strcpy(info.name, kMemoryName);
309 
310   MappingList mappings;
311   AppMemoryList memory_list;
312   MappingEntry mapping;
313   mapping.first = info;
314   memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
315   mappings.push_back(mapping);
316   ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
317                             mappings, memory_list));
318 
319   // Read the minidump. Load the module list, and ensure that
320   // the mmap'ed |memory| is listed with the given module name
321   // and debug ID.
322   Minidump minidump(dumpfile);
323   ASSERT_TRUE(minidump.Read());
324 
325   MinidumpModuleList* module_list = minidump.GetModuleList();
326   ASSERT_TRUE(module_list);
327   const MinidumpModule* module =
328     module_list->GetModuleForAddress(kMemoryAddress);
329   ASSERT_TRUE(module);
330 
331   EXPECT_EQ(info.start_addr, module->base_address());
332   EXPECT_EQ(info.size, module->size());
333   EXPECT_EQ(kMemoryName, module->code_file());
334   EXPECT_EQ(module_identifier, module->debug_identifier());
335 
336   close(fds[1]);
337 }
338 #endif
339 
TEST(MinidumpWriterTest,DeletedBinary)340 TEST(MinidumpWriterTest, DeletedBinary) {
341   const string kNumberOfThreadsArgument = "1";
342   const string helper_path(GetHelperBinary());
343   if (helper_path.empty()) {
344     FAIL() << "Couldn't find helper binary";
345     exit(1);
346   }
347 
348   // Copy binary to a temp file.
349   AutoTempDir temp_dir;
350   string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
351   ASSERT_TRUE(CopyFile(helper_path.c_str(), binpath.c_str()))
352       << "Failed to copy " << helper_path << " to " << binpath;
353   ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
354 
355   int fds[2];
356   ASSERT_NE(-1, pipe(fds));
357 
358   pid_t child_pid = fork();
359   if (child_pid == 0) {
360     // In child process.
361     close(fds[0]);
362 
363     // Pass the pipe fd and the number of threads as arguments.
364     char pipe_fd_string[8];
365     sprintf(pipe_fd_string, "%d", fds[1]);
366     execl(binpath.c_str(),
367           binpath.c_str(),
368           pipe_fd_string,
369           kNumberOfThreadsArgument.c_str(),
370           NULL);
371   }
372   close(fds[1]);
373   // Wait for the child process to signal that it's ready.
374   struct pollfd pfd;
375   memset(&pfd, 0, sizeof(pfd));
376   pfd.fd = fds[0];
377   pfd.events = POLLIN | POLLERR;
378 
379   const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
380   ASSERT_EQ(1, r);
381   ASSERT_TRUE(pfd.revents & POLLIN);
382   uint8_t junk;
383   const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk)));
384   ASSERT_EQ(static_cast<ssize_t>(sizeof(junk)), nr);
385   close(fds[0]);
386 
387   // Child is ready now.
388   // Unlink the test binary.
389   unlink(binpath.c_str());
390 
391   ExceptionHandler::CrashContext context;
392   memset(&context, 0, sizeof(context));
393 
394   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
395   // Set a non-zero tid to avoid tripping asserts.
396   context.tid = child_pid;
397   ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
398                             sizeof(context)));
399   kill(child_pid, SIGKILL);
400 
401   struct stat st;
402   ASSERT_EQ(0, stat(templ.c_str(), &st));
403   ASSERT_GT(st.st_size, 0);
404 
405   Minidump minidump(templ);
406   ASSERT_TRUE(minidump.Read());
407 
408   // Check that the main module filename is correct.
409   MinidumpModuleList* module_list = minidump.GetModuleList();
410   ASSERT_TRUE(module_list);
411   const MinidumpModule* module = module_list->GetMainModule();
412   EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
413   // Check that the file ID is correct.
414   FileID fileid(helper_path.c_str());
415   uint8_t identifier[sizeof(MDGUID)];
416   EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
417   char identifier_string[kGUIDStringSize];
418   FileID::ConvertIdentifierToString(identifier,
419                                     identifier_string,
420                                     kGUIDStringSize);
421   string module_identifier(identifier_string);
422   // Strip out dashes
423   size_t pos;
424   while ((pos = module_identifier.find('-')) != string::npos) {
425     module_identifier.erase(pos, 1);
426   }
427   // And append a zero, because module IDs include an "age" field
428   // which is always zero on Linux.
429   module_identifier += "0";
430   EXPECT_EQ(module_identifier, module->debug_identifier());
431 }
432 
433 // Test that an additional memory region can be added to the minidump.
TEST(MinidumpWriterTest,AdditionalMemory)434 TEST(MinidumpWriterTest, AdditionalMemory) {
435   int fds[2];
436   ASSERT_NE(-1, pipe(fds));
437 
438   // These are defined here so the parent can use them to check the
439   // data from the minidump afterwards.
440   const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
441 
442   // Get some heap memory.
443   uint8_t* memory = new uint8_t[kMemorySize];
444   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
445   ASSERT_TRUE(memory);
446 
447   // Stick some data into the memory so the contents can be verified.
448   for (uint32_t i = 0; i < kMemorySize; ++i) {
449     memory[i] = i % 255;
450   }
451 
452   const pid_t child = fork();
453   if (child == 0) {
454     close(fds[1]);
455     char b;
456     HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
457     close(fds[0]);
458     syscall(__NR_exit);
459   }
460   close(fds[0]);
461 
462   ExceptionHandler::CrashContext context;
463 
464   // This needs a valid context for minidump writing to work, but getting
465   // a useful one from the child is too much work, so just use one from
466   // the parent since the child is just a forked copy anyway.
467   ASSERT_EQ(0, getcontext(&context.context));
468   context.tid = child;
469 
470   AutoTempDir temp_dir;
471   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
472   unlink(templ.c_str());
473 
474   MappingList mappings;
475   AppMemoryList memory_list;
476 
477   // Add the memory region to the list of memory to be included.
478   AppMemory app_memory;
479   app_memory.ptr = memory;
480   app_memory.length = kMemorySize;
481   memory_list.push_back(app_memory);
482   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
483                             mappings, memory_list));
484 
485   // Read the minidump. Ensure that the memory region is present
486   Minidump minidump(templ);
487   ASSERT_TRUE(minidump.Read());
488 
489   MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
490   ASSERT_TRUE(dump_memory_list);
491   const MinidumpMemoryRegion* region =
492     dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
493   ASSERT_TRUE(region);
494 
495   EXPECT_EQ(kMemoryAddress, region->GetBase());
496   EXPECT_EQ(kMemorySize, region->GetSize());
497 
498   // Verify memory contents.
499   EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
500 
501   delete[] memory;
502   close(fds[1]);
503 }
504 
505 // Test that an invalid thread stack pointer still results in a minidump.
TEST(MinidumpWriterTest,InvalidStackPointer)506 TEST(MinidumpWriterTest, InvalidStackPointer) {
507   int fds[2];
508   ASSERT_NE(-1, pipe(fds));
509 
510   const pid_t child = fork();
511   if (child == 0) {
512     close(fds[1]);
513     char b;
514     HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
515     close(fds[0]);
516     syscall(__NR_exit);
517   }
518   close(fds[0]);
519 
520   ExceptionHandler::CrashContext context;
521 
522   // This needs a valid context for minidump writing to work, but getting
523   // a useful one from the child is too much work, so just use one from
524   // the parent since the child is just a forked copy anyway.
525   ASSERT_EQ(0, getcontext(&context.context));
526   context.tid = child;
527 
528   // Fake the child's stack pointer for its crashing thread.  NOTE: This must
529   // be an invalid memory address for the child process (stack or otherwise).
530   // Try 1MB below the current stack.
531   uintptr_t invalid_stack_pointer =
532       reinterpret_cast<uintptr_t>(&context) - 1024*1024;
533 #if defined(__i386)
534   context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer;
535 #elif defined(__x86_64)
536   context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer;
537 #elif defined(__ARM_EABI__)
538   context.context.uc_mcontext.arm_sp = invalid_stack_pointer;
539 #elif defined(__aarch64__)
540   context.context.uc_mcontext.sp = invalid_stack_pointer;
541 #elif defined(__mips__)
542   context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] =
543       invalid_stack_pointer;
544 #else
545 # error "This code has not been ported to your platform yet."
546 #endif
547 
548   AutoTempDir temp_dir;
549   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
550   // NOTE: In previous versions of Breakpad, WriteMinidump() would fail if
551   // presented with an invalid stack pointer.
552   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
553 
554   // Read the minidump. Ensure that the memory region is present
555   Minidump minidump(templ);
556   ASSERT_TRUE(minidump.Read());
557 
558   // TODO(ted.mielczarek,mkrebs): Enable this part of the test once
559   // https://breakpad.appspot.com/413002/ is committed.
560 #if 0
561   // Make sure there's a thread without a stack.  NOTE: It's okay if
562   // GetThreadList() shows the error: "ERROR: MinidumpThread has a memory
563   // region problem".
564   MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
565   ASSERT_TRUE(dump_thread_list);
566   bool found_empty_stack = false;
567   for (int i = 0; i < dump_thread_list->thread_count(); i++) {
568     MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
569     ASSERT_TRUE(thread->thread() != NULL);
570     // When the stack size is zero bytes, GetMemory() returns NULL.
571     if (thread->GetMemory() == NULL) {
572       found_empty_stack = true;
573       break;
574     }
575   }
576   // NOTE: If you fail this, first make sure that "invalid_stack_pointer"
577   // above is indeed set to an invalid address.
578   ASSERT_TRUE(found_empty_stack);
579 #endif
580 
581   close(fds[1]);
582 }
583 
584 // Test that limiting the size of the minidump works.
TEST(MinidumpWriterTest,MinidumpSizeLimit)585 TEST(MinidumpWriterTest, MinidumpSizeLimit) {
586   static const int kNumberOfThreadsInHelperProgram = 40;
587 
588   char number_of_threads_arg[3];
589   sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram);
590 
591   string helper_path(GetHelperBinary());
592   if (helper_path.empty()) {
593     FAIL() << "Couldn't find helper binary";
594     exit(1);
595   }
596 
597   int fds[2];
598   ASSERT_NE(-1, pipe(fds));
599 
600   pid_t child_pid = fork();
601   if (child_pid == 0) {
602     // In child process.
603     close(fds[0]);
604 
605     // Pass the pipe fd and the number of threads as arguments.
606     char pipe_fd_string[8];
607     sprintf(pipe_fd_string, "%d", fds[1]);
608     execl(helper_path.c_str(),
609           helper_path.c_str(),
610           pipe_fd_string,
611           number_of_threads_arg,
612           NULL);
613   }
614   close(fds[1]);
615 
616   // Wait for all child threads to indicate that they have started
617   for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
618     struct pollfd pfd;
619     memset(&pfd, 0, sizeof(pfd));
620     pfd.fd = fds[0];
621     pfd.events = POLLIN | POLLERR;
622 
623     const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
624     ASSERT_EQ(1, r);
625     ASSERT_TRUE(pfd.revents & POLLIN);
626     uint8_t junk;
627     ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
628               static_cast<ssize_t>(sizeof(junk)));
629   }
630   close(fds[0]);
631 
632   // There is a race here because we may stop a child thread before
633   // it is actually running the busy loop. Empirically this sleep
634   // is sufficient to avoid the race.
635   usleep(100000);
636 
637   // Child and its threads are ready now.
638 
639 
640   off_t normal_file_size;
641   int total_normal_stack_size = 0;
642   AutoTempDir temp_dir;
643 
644   // First, write a minidump with no size limit.
645   {
646     string normal_dump = temp_dir.path() +
647         "/minidump-writer-unittest.dmp";
648     ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1,
649                               child_pid, NULL, 0,
650                               MappingList(), AppMemoryList()));
651     struct stat st;
652     ASSERT_EQ(0, stat(normal_dump.c_str(), &st));
653     ASSERT_GT(st.st_size, 0);
654     normal_file_size = st.st_size;
655 
656     Minidump minidump(normal_dump);
657     ASSERT_TRUE(minidump.Read());
658     MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
659     ASSERT_TRUE(dump_thread_list);
660     for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
661       MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
662       ASSERT_TRUE(thread->thread() != NULL);
663       // When the stack size is zero bytes, GetMemory() returns NULL.
664       MinidumpMemoryRegion* memory = thread->GetMemory();
665       ASSERT_TRUE(memory != NULL);
666       total_normal_stack_size += memory->GetSize();
667     }
668   }
669 
670   // Second, write a minidump with a size limit big enough to not trigger
671   // anything.
672   {
673     // Set size limit arbitrarily 1MB larger than the normal file size -- such
674     // that the limiting code will not kick in.
675     const off_t minidump_size_limit = normal_file_size + 1024*1024;
676 
677     string same_dump = temp_dir.path() +
678         "/minidump-writer-unittest-same.dmp";
679     ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit,
680                               child_pid, NULL, 0,
681                               MappingList(), AppMemoryList()));
682     struct stat st;
683     ASSERT_EQ(0, stat(same_dump.c_str(), &st));
684     // Make sure limiting wasn't actually triggered.  NOTE: If you fail this,
685     // first make sure that "minidump_size_limit" above is indeed set to a
686     // large enough value -- the limit-checking code in minidump_writer.cc
687     // does just a rough estimate.
688     ASSERT_EQ(normal_file_size, st.st_size);
689   }
690 
691   // Third, write a minidump with a size limit small enough to be triggered.
692   {
693     // Set size limit to some arbitrary amount, such that the limiting code
694     // will kick in.  The equation used to set this value was determined by
695     // simply reversing the size-limit logic a little bit in order to pick a
696     // size we know will trigger it.  The definition of
697     // kLimitAverageThreadStackLength here was copied from class
698     // MinidumpWriter in minidump_writer.cc.
699     static const unsigned kLimitAverageThreadStackLength = 8 * 1024;
700     off_t minidump_size_limit = kNumberOfThreadsInHelperProgram *
701         kLimitAverageThreadStackLength;
702     // If, in reality, each of the threads' stack is *smaller* than
703     // kLimitAverageThreadStackLength, the normal file size could very well be
704     // smaller than the arbitrary limit that was just set.  In that case,
705     // either of these numbers should trigger the size-limiting code, but we
706     // might as well pick the smallest.
707     if (normal_file_size < minidump_size_limit)
708       minidump_size_limit = normal_file_size;
709 
710     string limit_dump = temp_dir.path() +
711         "/minidump-writer-unittest-limit.dmp";
712     ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit,
713                               child_pid, NULL, 0,
714                               MappingList(), AppMemoryList()));
715     struct stat st;
716     ASSERT_EQ(0, stat(limit_dump.c_str(), &st));
717     ASSERT_GT(st.st_size, 0);
718     // Make sure the file size is at least smaller than the original.  If this
719     // fails because it's the same size, then the size-limit logic didn't kick
720     // in like it was supposed to.
721     EXPECT_LT(st.st_size, normal_file_size);
722 
723     Minidump minidump(limit_dump);
724     ASSERT_TRUE(minidump.Read());
725     MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
726     ASSERT_TRUE(dump_thread_list);
727     int total_limit_stack_size = 0;
728     for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
729       MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
730       ASSERT_TRUE(thread->thread() != NULL);
731       // When the stack size is zero bytes, GetMemory() returns NULL.
732       MinidumpMemoryRegion* memory = thread->GetMemory();
733       ASSERT_TRUE(memory != NULL);
734       total_limit_stack_size += memory->GetSize();
735     }
736 
737     // Make sure stack size shrunk by at least 1KB per extra thread.  The
738     // definition of kLimitBaseThreadCount here was copied from class
739     // MinidumpWriter in minidump_writer.cc.
740     // Note: The 1KB is arbitrary, and assumes that the thread stacks are big
741     // enough to shrink by that much.  For example, if each thread stack was
742     // originally only 2KB, the current size-limit logic wouldn't actually
743     // shrink them because that's the size to which it tries to shrink.  If
744     // you fail this part of the test due to something like that, the test
745     // logic should probably be improved to account for your situation.
746     const unsigned kLimitBaseThreadCount = 20;
747     const unsigned kMinPerExtraThreadStackReduction = 1024;
748     const int min_expected_reduction = (kNumberOfThreadsInHelperProgram -
749         kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction;
750     EXPECT_LT(total_limit_stack_size,
751               total_normal_stack_size - min_expected_reduction);
752   }
753 
754   // Kill the helper program.
755   kill(child_pid, SIGKILL);
756 }
757 
758 }  // namespace
759