aboutsummaryrefslogtreecommitdiff
path: root/src/client/linux/minidump_writer/minidump_writer_unittest.cc
diff options
context:
space:
mode:
authormkrebs@chromium.org <mkrebs@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-11-15 00:01:13 +0000
committermkrebs@chromium.org <mkrebs@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-11-15 00:01:13 +0000
commitd80f175c1aa2a66b5e5095824240fa3ac2575cd0 (patch)
treee7cd6f5d29fc45740a070c78db642b53b64dd55f /src/client/linux/minidump_writer/minidump_writer_unittest.cc
parentFix assertion failure in WriteMappings() for zero modules (diff)
downloadbreakpad-d80f175c1aa2a66b5e5095824240fa3ac2575cd0.tar.xz
Add optional file size limit for minidumps
When there are upwards of 200 threads in a crashing process, each having an 8KB stack, this can result in a huge, 1.8MB minidump file. So I added a parameter that, if set, can compel the minidump writer to dump less stack. More specifically, if the writer expects to go over the limit (due to the number of threads), then it will dump less of a thread's stack after the first 20 threads. There are two ways to specify the limit, depending on how you write minidumps: 1) If you call WriteMinidump() directly, there's now a version of the function that takes the minidump size limit as an argument. 2) If you use the ExceptionHandler class, the MinidumpDescriptor object you pass to it now has a set_size_limit() method you would call before passing it to the constructor. BUG=chromium-os:31447, chromium:154546 TEST=Wrote a size-limit unittest; Ran unittests Review URL: https://breakpad.appspot.com/487002 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1082 4c0a9323-5329-0410-9bdc-e9ce6186880e
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