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
55using namespace google_breakpad;
56
57// Length of a formatted GUID string =
58// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
59const int kGUIDStringSize = 37;
60
61namespace {
62
63typedef testing::Test MinidumpWriterTest;
64
65const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest";
66
67TEST(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
96TEST(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.
128TEST(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
237TEST(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
340TEST(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.
434TEST(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.
506TEST(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.
585TEST(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