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