Limit the number of crash reports to enqueue at once
BUG=5357
Change-Id: Ib21086cdd34c938def885d625a165ba2fa0879a4
Review URL: http://codereview.chromium.org/3209003
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
index 492f2e8..0194fc4 100644
--- a/crash_reporter/crash_collector.cc
+++ b/crash_reporter/crash_collector.cc
@@ -4,6 +4,7 @@
#include "crash-reporter/crash_collector.h"
+#include <dirent.h>
#include <pwd.h> // For struct passwd.
#include <sys/types.h> // for mode_t.
@@ -25,6 +26,9 @@
static const uid_t kRootOwner = 0;
static const uid_t kRootGroup = 0;
+// Maximum of 8 crash reports per directory.
+const int CrashCollector::kMaxCrashDirectorySize = 8;
+
CrashCollector::CrashCollector() : forced_crash_directory_(NULL) {
}
@@ -146,5 +150,46 @@
return false;
}
+ if (!CheckHasCapacity(*crash_directory)) {
+ return false;
+ }
+
return true;
}
+
+// Return true if the given crash directory has not already reached
+// maximum capacity.
+bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
+ DIR* dir = opendir(crash_directory.value().c_str());
+ if (!dir) {
+ return false;
+ }
+ struct dirent ent_buf;
+ struct dirent* ent;
+ int count_non_core = 0;
+ int count_core = 0;
+ bool full = false;
+ while (readdir_r(dir, &ent_buf, &ent) == 0 && ent != NULL) {
+ if ((strcmp(ent->d_name, ".") == 0) ||
+ (strcmp(ent->d_name, "..") == 0))
+ continue;
+
+ if (strcmp(ent->d_name + strlen(ent->d_name) - 5, ".core") == 0) {
+ ++count_core;
+ } else {
+ ++count_non_core;
+ }
+
+ if (count_core >= kMaxCrashDirectorySize ||
+ count_non_core >= kMaxCrashDirectorySize) {
+ logger_->LogWarning(
+ "Crash directory %s already full with %d pending reports",
+ crash_directory.value().c_str(),
+ kMaxCrashDirectorySize);
+ full = true;
+ break;
+ }
+ }
+ closedir(dir);
+ return !full;
+}
diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h
index ef0d2a7..a75d791 100644
--- a/crash_reporter/crash_collector.h
+++ b/crash_reporter/crash_collector.h
@@ -32,10 +32,15 @@
protected:
friend class CrashCollectorTest;
+ FRIEND_TEST(CrashCollectorTest, CheckHasCapacityOverCore);
+ FRIEND_TEST(CrashCollectorTest, CheckHasCapacityOverNonCore);
FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo);
FRIEND_TEST(CrashCollectorTest, FormatDumpBasename);
FRIEND_TEST(CrashCollectorTest, Initialize);
+ // Set maximum enqueued crashes in a crash directory.
+ static const int kMaxCrashDirectorySize;
+
// For testing, set the directory always returned by
// GetCreatedCrashDirectoryByEuid.
void ForceCrashDirectory(const char *forced_directory) {
@@ -54,13 +59,19 @@
// Determines the crash directory for given eud, and creates the
// directory if necessary with appropriate permissions. Returns
// true whether or not directory needed to be created, false on any
- // failure.
+ // failure. If the crash directory is already full, returns false.
bool GetCreatedCrashDirectoryByEuid(uid_t euid,
FilePath *crash_file_path);
+
+ // Format crash name based on components.
std::string FormatDumpBasename(const std::string &exec_name,
time_t timestamp,
pid_t pid);
+ // Check given crash directory still has remaining capacity for another
+ // crash.
+ bool CheckHasCapacity(const FilePath &crash_directory);
+
CountCrashFunction count_crash_function_;
IsFeedbackAllowedFunction is_feedback_allowed_function_;
SystemLogging *logger_;
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index 48b7fa0..ed8fff7 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -5,6 +5,7 @@
#include <unistd.h>
#include "base/file_util.h"
+#include "base/string_util.h"
#include "crash-reporter/crash_collector.h"
#include "crash-reporter/system_logging_mock.h"
#include "gflags/gflags.h"
@@ -20,15 +21,25 @@
}
class CrashCollectorTest : public ::testing::Test {
+ public:
void SetUp() {
collector_.Initialize(CountCrash,
IsMetrics,
&logging_);
+ test_dir_ = FilePath("test");
+ file_util::CreateDirectory(test_dir_);
}
+
+ void TearDown() {
+ file_util::Delete(test_dir_, true);
+ }
+
+ bool CheckHasCapacity();
+
protected:
SystemLoggingMock logging_;
CrashCollector collector_;
- pid_t pid_;
+ FilePath test_dir_;
};
TEST_F(CrashCollectorTest, Initialize) {
@@ -99,6 +110,49 @@
ASSERT_EQ("foo.20100523.135015.100", basename);
}
+bool CrashCollectorTest::CheckHasCapacity() {
+ static const char kFullMessage[] = "Crash directory test already full";
+ bool has_capacity = collector_.CheckHasCapacity(test_dir_);
+ bool has_message = (logging_.log().find(kFullMessage) != std::string::npos);
+ EXPECT_EQ(has_message, !has_capacity);
+ return has_capacity;
+}
+
+TEST_F(CrashCollectorTest, CheckHasCapacityOverNonCore) {
+ // Test up to kMaxCrashDirectorySize-1 non-core files can be added.
+ for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+ EXPECT_TRUE(CheckHasCapacity());
+ file_util::WriteFile(test_dir_.Append(StringPrintf("file%d", i)), "", 0);
+ }
+
+ // Test an additional kMaxCrashDirectorySize - 1 core files fit.
+ for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+ EXPECT_TRUE(CheckHasCapacity());
+ file_util::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)),
+ "", 0);
+ }
+
+ // Test an additional kMaxCrashDirectorySize non-core files don't fit.
+ for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) {
+ file_util::WriteFile(test_dir_.Append(StringPrintf("overage%d", i)), "", 0);
+ EXPECT_FALSE(CheckHasCapacity());
+ }
+}
+
+TEST_F(CrashCollectorTest, CheckHasCapacityOverCore) {
+ // Set up kMaxCrashDirectorySize - 1 core files.
+ for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+ file_util::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)),
+ "", 0);
+ }
+
+ EXPECT_TRUE(CheckHasCapacity());
+
+ // Test an additional core file does not fit.
+ file_util::WriteFile(test_dir_.Append("overage.core"), "", 0);
+ EXPECT_FALSE(CheckHasCapacity());
+}
+
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();