aboutsummaryrefslogtreecommitdiff
path: root/src/client/linux/minidump_writer/minidump_writer_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/linux/minidump_writer/minidump_writer_unittest.cc')
-rw-r--r--src/client/linux/minidump_writer/minidump_writer_unittest.cc152
1 files changed, 152 insertions, 0 deletions
diff --git a/src/client/linux/minidump_writer/minidump_writer_unittest.cc b/src/client/linux/minidump_writer/minidump_writer_unittest.cc
index 153f3528..6beae05d 100644
--- a/src/client/linux/minidump_writer/minidump_writer_unittest.cc
+++ b/src/client/linux/minidump_writer/minidump_writer_unittest.cc
@@ -580,4 +580,156 @@ TEST(MinidumpWriterTest, InvalidStackPointer) {
close(fds[1]);
}
+// Test that limiting the size of the minidump works.
+TEST(MinidumpWriterTest, MinidumpSizeLimit) {
+ static const int kNumberOfThreadsInHelperProgram = 40;
+
+ char number_of_threads_arg[3];
+ sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram);
+
+ string helper_path(GetHelperBinary());
+ if (helper_path.empty()) {
+ FAIL() << "Couldn't find helper binary";
+ exit(1);
+ }
+
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ pid_t child_pid = fork();
+ if (child_pid == 0) {
+ // In child process.
+ close(fds[0]);
+
+ // Pass the pipe fd and the number of threads as arguments.
+ char pipe_fd_string[8];
+ sprintf(pipe_fd_string, "%d", fds[1]);
+ execl(helper_path.c_str(),
+ helper_path.c_str(),
+ pipe_fd_string,
+ number_of_threads_arg,
+ NULL);
+ }
+ close(fds[1]);
+
+ // Wait for all child threads to indicate that they have started
+ for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
+ struct pollfd pfd;
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = fds[0];
+ pfd.events = POLLIN | POLLERR;
+
+ const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
+ ASSERT_EQ(1, r);
+ ASSERT_TRUE(pfd.revents & POLLIN);
+ uint8_t junk;
+ ASSERT_EQ(read(fds[0], &junk, sizeof(junk)), sizeof(junk));
+ }
+ close(fds[0]);
+
+ // There is a race here because we may stop a child thread before
+ // it is actually running the busy loop. Empirically this sleep
+ // is sufficient to avoid the race.
+ usleep(100000);
+
+ // Child and its threads are ready now.
+
+
+ off_t normal_file_size;
+ int total_normal_stack_size = 0;
+ AutoTempDir temp_dir;
+
+ // First, write a minidump with no size limit.
+ {
+ string normal_dump = temp_dir.path() +
+ "/minidump-writer-unittest.dmp";
+ ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1,
+ child_pid, NULL, 0,
+ MappingList(), AppMemoryList()));
+ struct stat st;
+ ASSERT_EQ(0, stat(normal_dump.c_str(), &st));
+ ASSERT_GT(st.st_size, 0u);
+ normal_file_size = st.st_size;
+
+ Minidump minidump(normal_dump.c_str());
+ ASSERT_TRUE(minidump.Read());
+ MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(dump_thread_list);
+ for (int i = 0; i < dump_thread_list->thread_count(); i++) {
+ MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
+ ASSERT_TRUE(thread->thread() != NULL);
+ // When the stack size is zero bytes, GetMemory() returns NULL.
+ MinidumpMemoryRegion* memory = thread->GetMemory();
+ ASSERT_TRUE(memory != NULL);
+ total_normal_stack_size += memory->GetSize();
+ }
+ }
+
+ // Second, write a minidump with a size limit big enough to not trigger
+ // anything.
+ {
+ // Set size limit arbitrarily 1MB larger than the normal file size -- such
+ // that the limiting code will not kick in.
+ const off_t minidump_size_limit = normal_file_size + 1024*1024;
+
+ string same_dump = temp_dir.path() +
+ "/minidump-writer-unittest-same.dmp";
+ ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit,
+ child_pid, NULL, 0,
+ MappingList(), AppMemoryList()));
+ struct stat st;
+ ASSERT_EQ(0, stat(same_dump.c_str(), &st));
+ // Make sure limiting wasn't actually triggered. NOTE: If you fail this,
+ // first make sure that "minidump_size_limit" above is indeed set to a
+ // large enough value -- the limit-checking code in minidump_writer.cc
+ // does just a rough estimate.
+ ASSERT_EQ(normal_file_size, st.st_size);
+ }
+
+ // Third, write a minidump with a size limit small enough to be triggered.
+ {
+ // Set size limit to the normal file size minus some arbitrary amount --
+ // enough to make the limiting code kick in.
+ const off_t minidump_size_limit = normal_file_size - 64*1024;
+
+ string limit_dump = temp_dir.path() +
+ "/minidump-writer-unittest-limit.dmp";
+ ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit,
+ child_pid, NULL, 0,
+ MappingList(), AppMemoryList()));
+ struct stat st;
+ ASSERT_EQ(0, stat(limit_dump.c_str(), &st));
+ ASSERT_GT(st.st_size, 0u);
+ // Make sure the file size is at least smaller than the original.
+ EXPECT_LT(st.st_size, normal_file_size);
+
+ Minidump minidump(limit_dump.c_str());
+ ASSERT_TRUE(minidump.Read());
+ MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(dump_thread_list);
+ int total_limit_stack_size = 0;
+ for (int i = 0; i < dump_thread_list->thread_count(); i++) {
+ MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
+ ASSERT_TRUE(thread->thread() != NULL);
+ // When the stack size is zero bytes, GetMemory() returns NULL.
+ MinidumpMemoryRegion* memory = thread->GetMemory();
+ ASSERT_TRUE(memory != NULL);
+ total_limit_stack_size += memory->GetSize();
+ }
+
+ // Make sure stack size shrunk by at least 1KB per extra thread. The
+ // definition of kLimitBaseThreadCount here was copied from class
+ // MinidumpWriter in minidump_writer.cc.
+ const unsigned kLimitBaseThreadCount = 20;
+ const unsigned kMinPerExtraThreadStackReduction = 1024;
+ const int min_expected_reduction = (kNumberOfThreadsInHelperProgram -
+ kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction;
+ EXPECT_LT(total_limit_stack_size,
+ total_normal_stack_size - min_expected_reduction);
+ }
+
+ // Kill the helper program.
+ kill(child_pid, SIGKILL);
+}
+
} // namespace