summaryrefslogtreecommitdiffstats
path: root/chromecast
diff options
context:
space:
mode:
authorbcf <bcf@google.com>2015-08-05 13:23:41 -0700
committerCommit bot <commit-bot@chromium.org>2015-08-05 20:24:08 +0000
commit3dc182443da4eec83a698f0f544485a0939aa389 (patch)
tree31d418d1d508a307795289c54b5f770d73d8952b /chromecast
parent36e30ccdbf10d8669f55f2908759e4546ee44cae (diff)
downloadchromium_src-3dc182443da4eec83a698f0f544485a0939aa389.zip
chromium_src-3dc182443da4eec83a698f0f544485a0939aa389.tar.gz
chromium_src-3dc182443da4eec83a698f0f544485a0939aa389.tar.bz2
[Chromecast] Moved ratelimit information to lockfile.
This allows ratelimit information to persist across reboots. Also previous implementation of ratelimiting only counted dumps in the lockfile, but they are uploaded and cleared every minute by crash_uploader. TEST=modified tests pass BUG=internal b/19210655 Review URL: https://codereview.chromium.org/1252883007 Cr-Commit-Position: refs/heads/master@{#341957}
Diffstat (limited to 'chromecast')
-rw-r--r--chromecast/crash/linux/crash_testing_utils.cc46
-rw-r--r--chromecast/crash/linux/crash_testing_utils.h8
-rw-r--r--chromecast/crash/linux/minidump_writer.cc35
-rw-r--r--chromecast/crash/linux/minidump_writer.h14
-rw-r--r--chromecast/crash/linux/minidump_writer_unittest.cc38
-rw-r--r--chromecast/crash/linux/synchronized_minidump_manager.cc180
-rw-r--r--chromecast/crash/linux/synchronized_minidump_manager.h18
-rw-r--r--chromecast/crash/linux/synchronized_minidump_manager_unittest.cc69
8 files changed, 331 insertions, 77 deletions
diff --git a/chromecast/crash/linux/crash_testing_utils.cc b/chromecast/crash/linux/crash_testing_utils.cc
index 61b9ea1..752fd9e 100644
--- a/chromecast/crash/linux/crash_testing_utils.cc
+++ b/chromecast/crash/linux/crash_testing_utils.cc
@@ -13,6 +13,9 @@ namespace chromecast {
namespace {
const char kDumpsKey[] = "dumps";
+const char kRatelimitKey[] = "ratelimit";
+const char kRatelimitPeriodStartKey[] = "period_start";
+const char kRatelimitPeriodDumpsKey[] = "period_dumps";
// Gets the list of deserialized DumpInfo given a deserialized |lockfile|.
base::ListValue* GetDumpList(base::Value* lockfile) {
@@ -58,13 +61,30 @@ bool FetchDumps(const std::string& lockfile_path,
return true;
}
+bool ClearDumps(const std::string& lockfile_path) {
+ base::FilePath path(lockfile_path);
+ scoped_ptr<base::Value> contents(DeserializeJsonFromFile(path));
+ base::ListValue* dump_list = GetDumpList(contents.get());
+ if (!dump_list) {
+ return false;
+ }
+
+ dump_list->Clear();
+
+ return SerializeJsonToFile(path, *contents);
+}
+
bool CreateLockFile(const std::string& lockfile_path) {
- scoped_ptr<base::DictionaryValue> output =
- make_scoped_ptr(new base::DictionaryValue());
+ scoped_ptr<base::DictionaryValue> output(
+ make_scoped_ptr(new base::DictionaryValue()));
output->Set(kDumpsKey, make_scoped_ptr(new base::ListValue()));
- base::FilePath path(lockfile_path);
+ base::DictionaryValue* ratelimit_fields = new base::DictionaryValue();
+ output->Set(kRatelimitKey, make_scoped_ptr(ratelimit_fields));
+ ratelimit_fields->SetString(kRatelimitPeriodStartKey, "0");
+ ratelimit_fields->SetInteger(kRatelimitPeriodDumpsKey, 0);
+ base::FilePath path(lockfile_path);
const base::Value* val = output.get();
return SerializeJsonToFile(path, *val);
}
@@ -90,4 +110,24 @@ bool AppendLockFile(const std::string& lockfile_path, const DumpInfo& dump) {
return SerializeJsonToFile(path, *contents);
}
+bool SetRatelimitPeriodStart(const std::string& lockfile_path, time_t start) {
+ base::FilePath path(lockfile_path);
+
+ scoped_ptr<base::Value> contents(DeserializeJsonFromFile(path));
+
+ base::DictionaryValue* dict;
+ base::DictionaryValue* ratelimit_params;
+ if (!contents || !contents->GetAsDictionary(&dict) ||
+ !dict->GetDictionary(kRatelimitKey, &ratelimit_params)) {
+ return false;
+ }
+
+ char buf[128];
+ snprintf(buf, sizeof(buf), "%lld", static_cast<long long>(start));
+ std::string period_start_str(buf);
+ ratelimit_params->SetString(kRatelimitPeriodStartKey, period_start_str);
+
+ return SerializeJsonToFile(path, *contents);
+}
+
} // namespace chromecast
diff --git a/chromecast/crash/linux/crash_testing_utils.h b/chromecast/crash/linux/crash_testing_utils.h
index be23b77..8b570af 100644
--- a/chromecast/crash/linux/crash_testing_utils.h
+++ b/chromecast/crash/linux/crash_testing_utils.h
@@ -22,6 +22,10 @@ scoped_ptr<DumpInfo> CreateDumpInfo(const std::string& json_string);
bool FetchDumps(const std::string& lockfile_path,
ScopedVector<DumpInfo>* dumps);
+// Clear all dumps in the lockfile at |lockfile_path|.
+// Returns true on success, false on error.
+bool ClearDumps(const std::string& lockfile_path);
+
// Creates an empty lockfile at |lockfile_path|. Returns true on success, false
// on error.
bool CreateLockFile(const std::string& lockfile_path);
@@ -30,6 +34,10 @@ bool CreateLockFile(const std::string& lockfile_path);
// Returns true on success, false on error.
bool AppendLockFile(const std::string& lockfile_path, const DumpInfo& dump);
+// Set the ratelimit period start in the lockfile at |lockfile_path| to |start|.
+// Returns true on success, false on error.
+bool SetRatelimitPeriodStart(const std::string& lockfile_path, time_t start);
+
} // namespace chromecast
#endif // CHROMECAST_CRASH_LINUX_CRASH_TESTING_UTILS_H_
diff --git a/chromecast/crash/linux/minidump_writer.cc b/chromecast/crash/linux/minidump_writer.cc
index 082a8a3..cb79147 100644
--- a/chromecast/crash/linux/minidump_writer.cc
+++ b/chromecast/crash/linux/minidump_writer.cc
@@ -18,10 +18,6 @@ namespace {
const char kDumpStateSuffix[] = ".txt.gz";
-const int kDefaultDumpIntervalHours = 24;
-const int kDefaultMaxDumps = 5;
-const int kDefaultMaxRecentDumps = 5;
-
// Fork and run dumpstate, saving results to minidump_name + ".txt.gz".
int DumpState(const std::string& minidump_name) {
std::vector<std::string> argv;
@@ -50,9 +46,6 @@ MinidumpWriter::MinidumpWriter(MinidumpGenerator* minidump_generator,
: minidump_generator_(minidump_generator),
minidump_path_(minidump_filename),
params_(params),
- max_dumps_(kDefaultMaxDumps),
- dump_interval_(base::TimeDelta::FromHours(kDefaultDumpIntervalHours)),
- max_recent_dumps_(kDefaultMaxRecentDumps),
dump_state_cb_(dump_state_cb) {
}
@@ -80,12 +73,6 @@ int MinidumpWriter::DoWork() {
return -1;
}
- // Query if we are able to write another minidump.
- if (!CanWriteDump()) {
- LOG(INFO) << "Skipping writing of dump due to limits";
- return -1;
- }
-
// Generate a minidump at the specified |minidump_path_|.
if (!minidump_generator_->Generate(minidump_path_.value())) {
LOG(ERROR) << "Generate minidump failed " << minidump_path_.value();
@@ -102,7 +89,7 @@ int MinidumpWriter::DoWork() {
// Add this entry to the lockfile.
const DumpInfo info(minidump_path_.value(),
minidump_path_.value() + kDumpStateSuffix,
- time(NULL),
+ time(nullptr),
params_);
if (AddEntryToLockFile(info) < 0) {
LOG(ERROR) << "lockfile logging failed";
@@ -112,24 +99,4 @@ int MinidumpWriter::DoWork() {
return 0;
}
-bool MinidumpWriter::CanWriteDump() {
- const auto dumps(GetDumps());
-
- // If no more dumps can be written, return false.
- if (static_cast<int>(dumps.size()) >= max_dumps_)
- return false;
-
- // If too many dumps have been written recently, return false.
- time_t cur_time = time(0);
- int recent_dumps = 0;
- for (auto dump : dumps) {
- if (difftime(cur_time, dump->dump_time()) <= dump_interval_.InSecondsF()) {
- if (++recent_dumps >= max_recent_dumps_)
- return false;
- }
- }
-
- return true;
-}
-
} // namespace crash_manager
diff --git a/chromecast/crash/linux/minidump_writer.h b/chromecast/crash/linux/minidump_writer.h
index 69e6233..e37dbaf 100644
--- a/chromecast/crash/linux/minidump_writer.h
+++ b/chromecast/crash/linux/minidump_writer.h
@@ -48,29 +48,15 @@ class MinidumpWriter : public SynchronizedMinidumpManager {
// otherwise.
int Write() { return AcquireLockAndDoWork(); }
- int max_dumps() const { return max_dumps_; }
- int max_recent_dumps() const { return max_recent_dumps_; }
- const base::TimeDelta& dump_interval() const { return dump_interval_; };
-
protected:
// MinidumpManager implementation:
int DoWork() override;
private:
- // Returns true if we can write another dump, false otherwise. We can write
- // another dump if the number of minidumps is strictly less than |max_dumps_|
- // and the number of minidumps which occurred within the last |dump_interval_|
- // is strictly less than |max_recent_dumps_|.
- bool CanWriteDump();
-
MinidumpGenerator* const minidump_generator_;
base::FilePath minidump_path_;
const MinidumpParams params_;
- const int max_dumps_;
- const base::TimeDelta dump_interval_;
- const int max_recent_dumps_;
-
// This callback is Run() to dump a log to |minidump_path_|.txt.gz, taking
// |minidump_path_| as a parameter. It returns 0 on success, and a negative
// integer otherwise. If a callback is not passed in the constructor, the
diff --git a/chromecast/crash/linux/minidump_writer_unittest.cc b/chromecast/crash/linux/minidump_writer_unittest.cc
index d623085..c359e31 100644
--- a/chromecast/crash/linux/minidump_writer_unittest.cc
+++ b/chromecast/crash/linux/minidump_writer_unittest.cc
@@ -126,7 +126,7 @@ TEST_F(MinidumpWriterTest, Write_FailsWhenTooManyDumpsPresent) {
base::Bind(&FakeDumpState));
// Write dump logs to the lockfile.
- size_t too_many_dumps = writer.max_dumps() + 1;
+ size_t too_many_dumps = SynchronizedMinidumpManager::kMaxLockfileDumps + 1;
for (size_t i = 0; i < too_many_dumps; ++i) {
scoped_ptr<DumpInfo> info(CreateDumpInfo(
"{"
@@ -148,16 +148,34 @@ TEST_F(MinidumpWriterTest, Write_FailsWhenTooManyRecentDumpsPresent) {
dumplog_file_.value(),
MinidumpParams(),
base::Bind(&FakeDumpState));
+ // Multiple iters to make sure period resets work correctly
+ for (int iter = 0; iter < 3; ++iter) {
+ time_t now = time(nullptr);
- // Write dump logs to the lockfile.
- size_t too_many_recent_dumps = writer.max_recent_dumps() + 1;
- for (size_t i = 0; i < too_many_recent_dumps; ++i) {
- MinidumpParams params;
- DumpInfo info("dump", "/dump/path", time(nullptr), params);
- ASSERT_TRUE(AppendLockFile(info));
+ // Write dump logs to the lockfile.
+ size_t too_many_recent_dumps =
+ SynchronizedMinidumpManager::kRatelimitPeriodMaxDumps;
+ for (size_t i = 0; i < too_many_recent_dumps; ++i) {
+ ASSERT_EQ(0, writer.Write());
+
+ // Clear dumps so we don't reach max dumps in lockfile
+ ASSERT_TRUE(ClearDumps(lockfile_path_.value()));
+ }
+
+ // Should fail with too many dumps
+ ASSERT_EQ(-1, writer.Write());
+
+ int64 period = SynchronizedMinidumpManager::kRatelimitPeriodSeconds;
+
+ // Half period shouldn't trigger reset
+ SetRatelimitPeriodStart(lockfile_path_.value(), now - period / 2);
+ ASSERT_EQ(-1, writer.Write());
+
+ // Set period starting time to trigger a reset
+ SetRatelimitPeriodStart(lockfile_path_.value(), now - period);
}
- ASSERT_EQ(-1, writer.Write());
+ ASSERT_EQ(0, writer.Write());
}
TEST_F(MinidumpWriterTest, Write_SucceedsWhenDumpLimitsNotExceeded) {
@@ -166,8 +184,8 @@ TEST_F(MinidumpWriterTest, Write_SucceedsWhenDumpLimitsNotExceeded) {
MinidumpParams(),
base::Bind(&FakeDumpState));
- ASSERT_GT(writer.max_dumps(), 1);
- ASSERT_GT(writer.max_recent_dumps(), 0);
+ ASSERT_GT(SynchronizedMinidumpManager::kMaxLockfileDumps, 1);
+ ASSERT_GT(SynchronizedMinidumpManager::kRatelimitPeriodMaxDumps, 0);
// Write an old dump logs to the lockfile.
scoped_ptr<DumpInfo> info(CreateDumpInfo(
diff --git a/chromecast/crash/linux/synchronized_minidump_manager.cc b/chromecast/crash/linux/synchronized_minidump_manager.cc
index e9714ac..c8c1a4d 100644
--- a/chromecast/crash/linux/synchronized_minidump_manager.cc
+++ b/chromecast/crash/linux/synchronized_minidump_manager.cc
@@ -12,15 +12,22 @@
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <time.h>
#include <unistd.h>
-#include <fstream>
-
#include "base/logging.h"
#include "chromecast/base/path_utils.h"
#include "chromecast/base/serializers.h"
#include "chromecast/crash/linux/dump_info.h"
+// if |cond| is false, returns |retval|.
+#define RCHECK(cond, retval) \
+ do { \
+ if (!(cond)) { \
+ return (retval); \
+ } \
+ } while (0)
+
namespace chromecast {
namespace {
@@ -30,22 +37,120 @@ const mode_t kFileMode = 0660;
const char kLockfileName[] = "lockfile";
const char kMinidumpsDir[] = "minidumps";
-const char kLockfileDumpField[] = "dumps";
+const char kLockfileDumpKey[] = "dumps";
+const char kLockfileRatelimitKey[] = "ratelimit";
+const char kLockfileRatelimitPeriodStartKey[] = "period_start";
+const char kLockfileRatelimitPeriodDumpsKey[] = "period_dumps";
// Gets the list of deserialized DumpInfo given a deserialized |lockfile|.
+// Returns nullptr if invalid.
base::ListValue* GetDumpList(base::Value* lockfile) {
base::DictionaryValue* dict;
base::ListValue* dump_list;
if (!lockfile || !lockfile->GetAsDictionary(&dict) ||
- !dict->GetList(kLockfileDumpField, &dump_list)) {
+ !dict->GetList(kLockfileDumpKey, &dump_list)) {
return nullptr;
}
return dump_list;
}
+// Gets the ratelimit parameter dictionary given a deserialized |lockfile|.
+// Returns nullptr if invalid.
+base::DictionaryValue* GetRatelimitParams(base::Value* lockfile) {
+ base::DictionaryValue* dict;
+ base::DictionaryValue* ratelimit_params;
+ if (!lockfile || !lockfile->GetAsDictionary(&dict) ||
+ !dict->GetDictionary(kLockfileRatelimitKey, &ratelimit_params)) {
+ return nullptr;
+ }
+
+ return ratelimit_params;
+}
+
+// Returns the time of the current ratelimit period's start in |lockfile|.
+// Returns (time_t)-1 if an error occurs.
+time_t GetRatelimitPeriodStart(base::Value* lockfile) {
+ time_t result = static_cast<time_t>(-1);
+ base::DictionaryValue* ratelimit_params = GetRatelimitParams(lockfile);
+ RCHECK(ratelimit_params, result);
+
+ std::string period_start_str;
+ RCHECK(ratelimit_params->GetString(kLockfileRatelimitPeriodStartKey,
+ &period_start_str),
+ result);
+
+ errno = 0;
+ result = static_cast<time_t>(strtoll(period_start_str.c_str(), nullptr, 0));
+ if (errno != 0) {
+ return static_cast<time_t>(-1);
+ }
+
+ return result;
+}
+
+// Sets the time of the current ratelimit period's start in |lockfile| to
+// |period_start|. Returns 0 on success, < 0 on error.
+int SetRatelimitPeriodStart(base::Value* lockfile, time_t period_start) {
+ DCHECK_GE(period_start, 0);
+
+ base::DictionaryValue* ratelimit_params = GetRatelimitParams(lockfile);
+ RCHECK(ratelimit_params, -1);
+
+ char buf[128];
+ snprintf(buf, sizeof(buf), "%lld", static_cast<long long>(period_start));
+ std::string period_start_str(buf);
+ ratelimit_params->SetString(kLockfileRatelimitPeriodStartKey,
+ period_start_str);
+ return 0;
+}
+
+// Gets the number of dumps added to |lockfile| in the current ratelimit
+// period. Returns < 0 on error.
+int GetRatelimitPeriodDumps(base::Value* lockfile) {
+ int period_dumps = -1;
+
+ base::DictionaryValue* ratelimit_params = GetRatelimitParams(lockfile);
+ if (!ratelimit_params ||
+ !ratelimit_params->GetInteger(kLockfileRatelimitPeriodDumpsKey,
+ &period_dumps)) {
+ return -1;
+ }
+
+ return period_dumps;
+}
+
+// Sets the current ratelimit period's number of dumps in |lockfile| to
+// |period_dumps|. Returns 0 on success, < 0 on error.
+int SetRatelimitPeriodDumps(base::Value* lockfile, int period_dumps) {
+ DCHECK_GE(period_dumps, 0);
+
+ base::DictionaryValue* ratelimit_params = GetRatelimitParams(lockfile);
+ RCHECK(ratelimit_params, -1);
+
+ ratelimit_params->SetInteger(kLockfileRatelimitPeriodDumpsKey, period_dumps);
+
+ return 0;
+}
+
+// Increment the number of dumps in the current ratelimit period in deserialized
+// |lockfile| by |increment|. Returns 0 on success, < 0 on error.
+int IncrementCurrentPeriodDumps(base::Value* lockfile, int increment) {
+ DCHECK_GE(increment, 0);
+ int last_dumps = GetRatelimitPeriodDumps(lockfile);
+ RCHECK(last_dumps >= 0, -1);
+
+ return SetRatelimitPeriodDumps(lockfile, last_dumps + increment);
+}
+
} // namespace
+const int SynchronizedMinidumpManager::kMaxLockfileDumps = 5;
+
+// One day
+const int SynchronizedMinidumpManager::kRatelimitPeriodSeconds = 24 * 3600;
+const int SynchronizedMinidumpManager::kRatelimitPeriodMaxDumps = 100;
+
SynchronizedMinidumpManager::SynchronizedMinidumpManager()
: non_blocking_(false), lockfile_fd_(-1) {
dump_path_ = GetHomePathASCII(kMinidumpsDir);
@@ -154,7 +259,15 @@ int SynchronizedMinidumpManager::ParseLockFile() {
base::FilePath lockfile_path(lockfile_path_);
lockfile_contents_ = DeserializeJsonFromFile(lockfile_path);
- return lockfile_contents_ ? 0 : -1;
+ // Verify validity of lockfile
+ if (!GetDumpList(lockfile_contents_.get()) ||
+ GetRatelimitPeriodStart(lockfile_contents_.get()) < 0 ||
+ GetRatelimitPeriodDumps(lockfile_contents_.get()) < 0) {
+ lockfile_contents_ = nullptr;
+ return -1;
+ }
+
+ return 0;
}
int SynchronizedMinidumpManager::WriteLockFile(const base::Value& contents) {
@@ -163,9 +276,14 @@ int SynchronizedMinidumpManager::WriteLockFile(const base::Value& contents) {
}
int SynchronizedMinidumpManager::CreateEmptyLockFile() {
- scoped_ptr<base::DictionaryValue> output =
- make_scoped_ptr(new base::DictionaryValue());
- output->Set(kLockfileDumpField, make_scoped_ptr(new base::ListValue()));
+ scoped_ptr<base::DictionaryValue> output(
+ make_scoped_ptr(new base::DictionaryValue()));
+ output->Set(kLockfileDumpKey, make_scoped_ptr(new base::ListValue()));
+
+ base::DictionaryValue* ratelimit_fields = new base::DictionaryValue();
+ output->Set(kLockfileRatelimitKey, make_scoped_ptr(ratelimit_fields));
+ ratelimit_fields->SetString(kLockfileRatelimitPeriodStartKey, "0");
+ ratelimit_fields->SetInteger(kLockfileRatelimitPeriodDumpsKey, 0);
return WriteLockFile(*output);
}
@@ -179,12 +297,19 @@ int SynchronizedMinidumpManager::AddEntryToLockFile(const DumpInfo& dump_info) {
return -1;
}
+ if (!CanWriteDumps(1)) {
+ LOG(ERROR) << "Can't Add Dump: Ratelimited";
+ return -1;
+ }
+
base::ListValue* entry_list = GetDumpList(lockfile_contents_.get());
if (!entry_list) {
LOG(ERROR) << "Failed to parse lock file";
return -1;
}
+ IncrementCurrentPeriodDumps(lockfile_contents_.get(), 1);
+
entry_list->Append(dump_info.GetAsValue());
return 0;
@@ -197,10 +322,7 @@ int SynchronizedMinidumpManager::RemoveEntryFromLockFile(int index) {
return -1;
}
- if (!entry_list->Remove(static_cast<size_t>(index), nullptr)) {
- return -1;
- }
-
+ RCHECK(entry_list->Remove(static_cast<size_t>(index), nullptr), -1);
return 0;
}
@@ -224,10 +346,7 @@ void SynchronizedMinidumpManager::ReleaseLockFile() {
ScopedVector<DumpInfo> SynchronizedMinidumpManager::GetDumps() {
ScopedVector<DumpInfo> dumps;
const base::ListValue* dump_list = GetDumpList(lockfile_contents_.get());
-
- if (dump_list == nullptr) {
- return dumps.Pass();
- }
+ RCHECK(dump_list, dumps.Pass());
for (const base::Value* elem : *dump_list) {
dumps.push_back(new DumpInfo(elem));
@@ -253,4 +372,33 @@ int SynchronizedMinidumpManager::SetCurrentDumps(
return 0;
}
+bool SynchronizedMinidumpManager::CanWriteDumps(int num_dumps) {
+ const auto dumps(GetDumps());
+
+ // If no more dumps can be written, return false.
+ if (static_cast<int>(dumps.size()) + num_dumps > kMaxLockfileDumps)
+ return false;
+
+ // If too many dumps have been written recently, return false.
+ time_t cur_time = time(nullptr);
+ time_t period_start = GetRatelimitPeriodStart(lockfile_contents_.get());
+ int period_dumps_count = GetRatelimitPeriodDumps(lockfile_contents_.get());
+
+ // If we're in invalid state, or we passed the period, reset the ratelimit.
+ // When the device reboots, |cur_time| may be incorrectly reported to be a
+ // very small number for a short period of time. So only consider
+ // |period_start| invalid when |cur_time| is less if |cur_time| is not very
+ // close to 0.
+ if (period_dumps_count < 0 ||
+ (cur_time < period_start && cur_time > kRatelimitPeriodSeconds) ||
+ difftime(cur_time, period_start) >= kRatelimitPeriodSeconds) {
+ period_start = cur_time;
+ period_dumps_count = 0;
+ SetRatelimitPeriodStart(lockfile_contents_.get(), period_start);
+ SetRatelimitPeriodDumps(lockfile_contents_.get(), period_dumps_count);
+ }
+
+ return period_dumps_count + num_dumps <= kRatelimitPeriodMaxDumps;
+}
+
} // namespace chromecast
diff --git a/chromecast/crash/linux/synchronized_minidump_manager.h b/chromecast/crash/linux/synchronized_minidump_manager.h
index c8277ba..bd5045b 100644
--- a/chromecast/crash/linux/synchronized_minidump_manager.h
+++ b/chromecast/crash/linux/synchronized_minidump_manager.h
@@ -5,6 +5,8 @@
#ifndef CHROMECAST_CRASH_LINUX_SYNCHRONIZED_MINIDUMP_MANAGER_H_
#define CHROMECAST_CRASH_LINUX_SYNCHRONIZED_MINIDUMP_MANAGER_H_
+#include <time.h>
+
#include <string>
#include "base/files/file_path.h"
@@ -27,6 +29,15 @@ namespace chromecast {
// lockfile is released, the in memory representation is written to file.
class SynchronizedMinidumpManager {
public:
+ // Max number of dumps allowed in lockfile.
+ static const int kMaxLockfileDumps;
+
+ // Length of a ratelimit period in seconds.
+ static const int kRatelimitPeriodSeconds;
+
+ // Number of dumps allowed per period.
+ static const int kRatelimitPeriodMaxDumps;
+
virtual ~SynchronizedMinidumpManager();
// Returns whether this object's file locking method is nonblocking or not.
@@ -98,6 +109,13 @@ class SynchronizedMinidumpManager {
// Release the lock file with the associated *fd*.
void ReleaseLockFile();
+ // Returns true if |num_dumps| can be written to the lockfile. We can write
+ // the dumps if the number of dumps in the lockfile after |num_dumps| is added
+ // is less than or equal to |kMaxLockfileDumps| and the number of dumps in the
+ // current ratelimit period after |num_dumps| is added is less than or equal
+ // to |kRatelimitPeriodMaxDumps|.
+ bool CanWriteDumps(int num_dumps);
+
std::string lockfile_path_;
int lockfile_fd_;
scoped_ptr<base::Value> lockfile_contents_;
diff --git a/chromecast/crash/linux/synchronized_minidump_manager_unittest.cc b/chromecast/crash/linux/synchronized_minidump_manager_unittest.cc
index 41a84f0..ce1d853 100644
--- a/chromecast/crash/linux/synchronized_minidump_manager_unittest.cc
+++ b/chromecast/crash/linux/synchronized_minidump_manager_unittest.cc
@@ -380,4 +380,73 @@ TEST_F(SynchronizedMinidumpManagerTest,
EXPECT_EQ(2u, dumps.size());
}
+TEST_F(SynchronizedMinidumpManagerTest,
+ AddEntryFailsWhenTooManyRecentDumpsPresent) {
+ // Sample parameters.
+ time_t now = time(0);
+ MinidumpParams params;
+ params.process_name = "process";
+
+ SynchronizedMinidumpManagerSimple manager;
+ manager.SetDumpInfoToWrite(
+ make_scoped_ptr(new DumpInfo("dump1", "log1", now, params)));
+
+ for (int i = 0; i < SynchronizedMinidumpManager::kMaxLockfileDumps; ++i) {
+ // Adding these should succeed
+ ASSERT_EQ(0, manager.DoWorkLocked());
+ ASSERT_EQ(0, manager.add_entry_return_code());
+ }
+
+ ASSERT_EQ(0, manager.DoWorkLocked());
+
+ // This one should fail
+ ASSERT_GT(0, manager.add_entry_return_code());
+}
+
+TEST_F(SynchronizedMinidumpManagerTest,
+ AddEntryFailsWhenRatelimitPeriodExceeded) {
+ // Sample parameters.
+ time_t now = time(0);
+ MinidumpParams params;
+ params.process_name = "process";
+
+ SynchronizedMinidumpManagerSimple manager;
+ manager.SetDumpInfoToWrite(
+ make_scoped_ptr(new DumpInfo("dump1", "log1", now, params)));
+
+ // Multiple iters to make sure period resets work correctly
+ for (int iter = 0; iter < 3; ++iter) {
+ time_t now = time(nullptr);
+
+ // Write dump logs to the lockfile.
+ size_t too_many_recent_dumps =
+ SynchronizedMinidumpManager::kRatelimitPeriodMaxDumps;
+ for (size_t i = 0; i < too_many_recent_dumps; ++i) {
+ // Adding these should succeed
+ ASSERT_EQ(0, manager.DoWorkLocked());
+ ASSERT_EQ(0, manager.add_entry_return_code());
+
+ // Clear dumps so we don't reach max dumps in lockfile
+ ASSERT_TRUE(ClearDumps(lockfile_.value()));
+ }
+
+ ASSERT_EQ(0, manager.DoWorkLocked());
+ // Should fail with too many dumps
+ ASSERT_GT(0, manager.add_entry_return_code());
+
+ int64 period = SynchronizedMinidumpManager::kRatelimitPeriodSeconds;
+
+ // Half period shouldn't trigger reset
+ SetRatelimitPeriodStart(lockfile_.value(), now - period / 2);
+ ASSERT_EQ(0, manager.DoWorkLocked());
+ ASSERT_GT(0, manager.add_entry_return_code());
+
+ // Set period starting time to trigger a reset
+ SetRatelimitPeriodStart(lockfile_.value(), now - period);
+ }
+
+ ASSERT_EQ(0, manager.DoWorkLocked());
+ ASSERT_EQ(0, manager.add_entry_return_code());
+}
+
} // namespace chromecast