summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/metrics/metrics_log_serializer.cc226
-rw-r--r--chrome/browser/metrics/metrics_log_serializer.h82
-rw-r--r--chrome/browser/metrics/metrics_log_serializer_unittest.cc274
-rw-r--r--chrome/browser/metrics/metrics_service.cc23
-rw-r--r--chrome/browser/metrics/metrics_service_unittest.cc2
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--chrome/common/metrics/metrics_service_base.cc6
-rw-r--r--chrome/common/metrics/metrics_service_base.h5
-rw-r--r--chrome/common/pref_names.cc13
-rw-r--r--chrome/common/pref_names.h2
-rw-r--r--components/components_tests.gyp1
-rw-r--r--components/metrics.gypi4
-rw-r--r--components/metrics/metrics_log_manager.cc197
-rw-r--r--components/metrics/metrics_log_manager.h142
-rw-r--r--components/metrics/metrics_log_manager_unittest.cc226
-rw-r--r--components/metrics/metrics_pref_names.cc24
-rw-r--r--components/metrics/metrics_pref_names.h19
-rw-r--r--components/metrics/persisted_logs.cc246
-rw-r--r--components/metrics/persisted_logs.h164
-rw-r--r--components/metrics/persisted_logs_unittest.cc415
21 files changed, 1078 insertions, 996 deletions
diff --git a/chrome/browser/metrics/metrics_log_serializer.cc b/chrome/browser/metrics/metrics_log_serializer.cc
deleted file mode 100644
index 0d545ec..0000000
--- a/chrome/browser/metrics/metrics_log_serializer.cc
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/metrics/metrics_log_serializer.h"
-
-#include <string>
-
-#include "base/base64.h"
-#include "base/md5.h"
-#include "base/metrics/histogram.h"
-#include "base/prefs/pref_service.h"
-#include "base/prefs/scoped_user_pref_update.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/common/pref_names.h"
-
-using metrics::MetricsLogBase;
-using metrics::MetricsLogManager;
-
-namespace {
-
-// The number of "initial" logs to save, and hope to send during a future Chrome
-// session. Initial logs contain crash stats, and are pretty small.
-const size_t kInitialLogsPersistLimit = 20;
-
-// The number of ongoing logs to save persistently, and hope to
-// send during a this or future sessions. Note that each log may be pretty
-// large, as presumably the related "initial" log wasn't sent (probably nothing
-// was, as the user was probably off-line). As a result, the log probably kept
-// accumulating while the "initial" log was stalled, and couldn't be sent. As a
-// result, we don't want to save too many of these mega-logs.
-// A "standard shutdown" will create a small log, including just the data that
-// was not yet been transmitted, and that is normal (to have exactly one
-// ongoing_log_ at startup).
-const size_t kOngoingLogsPersistLimit = 8;
-
-// The number of bytes each of initial and ongoing logs that must be stored.
-// This ensures that a reasonable amount of history will be stored even if there
-// is a long series of very small logs.
-const size_t kStorageByteLimitPerLogType = 300000;
-
-// We append (2) more elements to persisted lists: the size of the list and a
-// checksum of the elements.
-const size_t kChecksumEntryCount = 2;
-
-MetricsLogSerializer::LogReadStatus MakeRecallStatusHistogram(
- MetricsLogSerializer::LogReadStatus status) {
- UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs",
- status, MetricsLogSerializer::END_RECALL_STATUS);
- return status;
-}
-
-} // namespace
-
-
-MetricsLogSerializer::MetricsLogSerializer() {}
-
-MetricsLogSerializer::~MetricsLogSerializer() {}
-
-void MetricsLogSerializer::SerializeLogs(
- const std::vector<MetricsLogManager::SerializedLog>& logs,
- MetricsLogManager::LogType log_type) {
- PrefService* local_state = g_browser_process->local_state();
- DCHECK(local_state);
- const char* pref = NULL;
- size_t store_length_limit = 0;
- switch (log_type) {
- case MetricsLogBase::INITIAL_STABILITY_LOG:
- pref = prefs::kMetricsInitialLogs;
- store_length_limit = kInitialLogsPersistLimit;
- break;
- case MetricsLogBase::ONGOING_LOG:
- pref = prefs::kMetricsOngoingLogs;
- store_length_limit = kOngoingLogsPersistLimit;
- break;
- case MetricsLogBase::NO_LOG:
- NOTREACHED();
- return;
- };
-
- ListPrefUpdate update(local_state, pref);
- WriteLogsToPrefList(logs, store_length_limit, kStorageByteLimitPerLogType,
- update.Get());
-}
-
-void MetricsLogSerializer::DeserializeLogs(
- MetricsLogManager::LogType log_type,
- std::vector<MetricsLogManager::SerializedLog>* logs) {
- DCHECK(logs);
- PrefService* local_state = g_browser_process->local_state();
- DCHECK(local_state);
-
- const char* pref;
- if (log_type == MetricsLogBase::INITIAL_STABILITY_LOG)
- pref = prefs::kMetricsInitialLogs;
- else
- pref = prefs::kMetricsOngoingLogs;
-
- const base::ListValue* unsent_logs = local_state->GetList(pref);
- ReadLogsFromPrefList(*unsent_logs, logs);
-}
-
-// static
-void MetricsLogSerializer::WriteLogsToPrefList(
- const std::vector<MetricsLogManager::SerializedLog>& local_list,
- size_t list_length_limit,
- size_t byte_limit,
- base::ListValue* list) {
- // One of the limit arguments must be non-zero.
- DCHECK(list_length_limit > 0 || byte_limit > 0);
-
- list->Clear();
- if (local_list.size() == 0)
- return;
-
- size_t start = 0;
- // If there are too many logs, keep the most recent logs up to the length
- // limit, and at least to the minimum number of bytes.
- if (local_list.size() > list_length_limit) {
- start = local_list.size();
- size_t bytes_used = 0;
- for (std::vector<MetricsLogManager::SerializedLog>::const_reverse_iterator
- it = local_list.rbegin(); it != local_list.rend(); ++it) {
- size_t log_size = it->log_text().length();
- if (bytes_used >= byte_limit &&
- (local_list.size() - start) >= list_length_limit)
- break;
- bytes_used += log_size;
- --start;
- }
- }
- DCHECK_LT(start, local_list.size());
- if (start >= local_list.size())
- return;
-
- // Store size at the beginning of the list.
- list->Append(base::Value::CreateIntegerValue(local_list.size() - start));
-
- base::MD5Context ctx;
- base::MD5Init(&ctx);
- std::string encoded_log;
- for (std::vector<MetricsLogManager::SerializedLog>::const_iterator it =
- local_list.begin() + start;
- it != local_list.end(); ++it) {
- // We encode the compressed log as Value::CreateStringValue() expects to
- // take a valid UTF8 string.
- base::Base64Encode(it->log_text(), &encoded_log);
- base::MD5Update(&ctx, encoded_log);
- list->Append(base::Value::CreateStringValue(encoded_log));
- }
-
- // Append hash to the end of the list.
- base::MD5Digest digest;
- base::MD5Final(&digest, &ctx);
- list->Append(base::Value::CreateStringValue(base::MD5DigestToBase16(digest)));
- DCHECK(list->GetSize() >= 3); // Minimum of 3 elements (size, data, hash).
-}
-
-// static
-MetricsLogSerializer::LogReadStatus MetricsLogSerializer::ReadLogsFromPrefList(
- const base::ListValue& list,
- std::vector<MetricsLogManager::SerializedLog>* local_list) {
- if (list.GetSize() == 0)
- return MakeRecallStatusHistogram(LIST_EMPTY);
- if (list.GetSize() < 3)
- return MakeRecallStatusHistogram(LIST_SIZE_TOO_SMALL);
-
- // The size is stored at the beginning of the list.
- int size;
- bool valid = (*list.begin())->GetAsInteger(&size);
- if (!valid)
- return MakeRecallStatusHistogram(LIST_SIZE_MISSING);
- // Account for checksum and size included in the list.
- if (static_cast<unsigned int>(size) !=
- list.GetSize() - kChecksumEntryCount) {
- return MakeRecallStatusHistogram(LIST_SIZE_CORRUPTION);
- }
-
- // Allocate strings for all of the logs we are going to read in.
- // Do this ahead of time so that we can decode the string values directly into
- // the elements of |local_list|, and thereby avoid making copies of the
- // serialized logs, which can be fairly large.
- DCHECK(local_list->empty());
- local_list->resize(size);
-
- base::MD5Context ctx;
- base::MD5Init(&ctx);
- std::string encoded_log;
- size_t local_index = 0;
- for (base::ListValue::const_iterator it = list.begin() + 1;
- it != list.end() - 1; // Last element is the checksum.
- ++it, ++local_index) {
- bool valid = (*it)->GetAsString(&encoded_log);
- if (!valid) {
- local_list->clear();
- return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION);
- }
-
- base::MD5Update(&ctx, encoded_log);
-
- std::string log_text;
- if (!base::Base64Decode(encoded_log, &log_text)) {
- local_list->clear();
- return MakeRecallStatusHistogram(DECODE_FAIL);
- }
-
- DCHECK_LT(local_index, local_list->size());
- (*local_list)[local_index].SwapLogText(&log_text);
- }
-
- // Verify checksum.
- base::MD5Digest digest;
- base::MD5Final(&digest, &ctx);
- std::string recovered_md5;
- // We store the hash at the end of the list.
- valid = (*(list.end() - 1))->GetAsString(&recovered_md5);
- if (!valid) {
- local_list->clear();
- return MakeRecallStatusHistogram(CHECKSUM_STRING_CORRUPTION);
- }
- if (recovered_md5 != base::MD5DigestToBase16(digest)) {
- local_list->clear();
- return MakeRecallStatusHistogram(CHECKSUM_CORRUPTION);
- }
- return MakeRecallStatusHistogram(RECALL_SUCCESS);
-}
diff --git a/chrome/browser/metrics/metrics_log_serializer.h b/chrome/browser/metrics/metrics_log_serializer.h
deleted file mode 100644
index 74d39e5..0000000
--- a/chrome/browser/metrics/metrics_log_serializer.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_METRICS_METRICS_LOG_SERIALIZER_H_
-#define CHROME_BROWSER_METRICS_METRICS_LOG_SERIALIZER_H_
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/gtest_prod_util.h"
-#include "components/metrics/metrics_log_manager.h"
-
-namespace base {
-class ListValue;
-}
-
-// Serializer for persisting metrics logs to prefs.
-class MetricsLogSerializer : public metrics::MetricsLogManager::LogSerializer {
- public:
- // Used to produce a histogram that keeps track of the status of recalling
- // persisted per logs.
- enum LogReadStatus {
- RECALL_SUCCESS, // We were able to correctly recall a persisted log.
- LIST_EMPTY, // Attempting to recall from an empty list.
- LIST_SIZE_MISSING, // Failed to recover list size using GetAsInteger().
- LIST_SIZE_TOO_SMALL, // Too few elements in the list (less than 3).
- LIST_SIZE_CORRUPTION, // List size is not as expected.
- LOG_STRING_CORRUPTION, // Failed to recover log string using GetAsString().
- CHECKSUM_CORRUPTION, // Failed to verify checksum.
- CHECKSUM_STRING_CORRUPTION, // Failed to recover checksum string using
- // GetAsString().
- DECODE_FAIL, // Failed to decode log.
- DEPRECATED_XML_PROTO_MISMATCH, // The XML and protobuf logs have
- // inconsistent data.
- END_RECALL_STATUS // Number of bins to use to create the histogram.
- };
-
- MetricsLogSerializer();
- virtual ~MetricsLogSerializer();
-
- // Implementation of metrics::MetricsLogManager::LogSerializer
- virtual void SerializeLogs(
- const std::vector<metrics::MetricsLogManager::SerializedLog>& logs,
- metrics::MetricsLogManager::LogType log_type) OVERRIDE;
- virtual void DeserializeLogs(
- metrics::MetricsLogManager::LogType log_type,
- std::vector<metrics::MetricsLogManager::SerializedLog>* logs) OVERRIDE;
-
- private:
- // Encodes the textual log data from |local_list| and writes it to the given
- // pref list, along with list size and checksum. Logs will be stored starting
- // with the most recent, and working backward until at least
- // |list_length_limit| logs and |byte_limit| bytes of logs have been
- // stored. At least one of those two arguments must be non-zero.
- static void WriteLogsToPrefList(
- const std::vector<metrics::MetricsLogManager::SerializedLog>& local_list,
- size_t list_length_limit,
- size_t byte_limit,
- base::ListValue* list);
-
- // Decodes and verifies the textual log data from |list|, populating
- // |local_list| and returning a status code.
- static LogReadStatus ReadLogsFromPrefList(
- const base::ListValue& list,
- std::vector<metrics::MetricsLogManager::SerializedLog>* local_list);
-
- FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, EmptyLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, SingleElementLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, LongButTinyLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, LongButSmallLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, ShortButLargeLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, LongAndLargeLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, SmallRecoveredListSize);
- FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, RemoveSizeFromLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, CorruptSizeOfLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, CorruptChecksumOfLogList);
-
- DISALLOW_COPY_AND_ASSIGN(MetricsLogSerializer);
-};
-
-#endif // CHROME_BROWSER_METRICS_METRICS_LOG_SERIALIZER_H_
diff --git a/chrome/browser/metrics/metrics_log_serializer_unittest.cc b/chrome/browser/metrics/metrics_log_serializer_unittest.cc
deleted file mode 100644
index 386a3cb..0000000
--- a/chrome/browser/metrics/metrics_log_serializer_unittest.cc
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/base64.h"
-#include "base/md5.h"
-#include "base/values.h"
-#include "chrome/browser/metrics/metrics_log_serializer.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using metrics::MetricsLogManager;
-
-namespace {
-
-const size_t kListLengthLimit = 3;
-const size_t kLogByteLimit = 1000;
-
-void SetLogText(const std::string& log_text,
- MetricsLogManager::SerializedLog* log) {
- std::string log_text_copy = log_text;
- log->SwapLogText(&log_text_copy);
-}
-
-} // namespace
-
-// Store and retrieve empty list.
-TEST(MetricsLogSerializerTest, EmptyLogList) {
- base::ListValue list;
- std::vector<MetricsLogManager::SerializedLog> local_list;
-
- MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit,
- kLogByteLimit, &list);
- EXPECT_EQ(0U, list.GetSize());
-
- local_list.clear(); // ReadLogsFromPrefList() expects empty |local_list|.
- EXPECT_EQ(
- MetricsLogSerializer::LIST_EMPTY,
- MetricsLogSerializer::ReadLogsFromPrefList(list, &local_list));
- EXPECT_EQ(0U, local_list.size());
-}
-
-// Store and retrieve a single log value.
-TEST(MetricsLogSerializerTest, SingleElementLogList) {
- base::ListValue list;
-
- std::vector<MetricsLogManager::SerializedLog> local_list(1);
- SetLogText("Hello world!", &local_list[0]);
-
- MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit,
- kLogByteLimit, &list);
-
- // |list| will now contain the following:
- // [1, Base64Encode("Hello world!"), MD5("Hello world!")].
- ASSERT_EQ(3U, list.GetSize());
-
- // Examine each element.
- base::ListValue::const_iterator it = list.begin();
- int size = 0;
- (*it)->GetAsInteger(&size);
- EXPECT_EQ(1, size);
-
- ++it;
- std::string str;
- (*it)->GetAsString(&str); // Base64 encoded "Hello world!" string.
- std::string encoded;
- base::Base64Encode("Hello world!", &encoded);
- EXPECT_TRUE(encoded == str);
-
- ++it;
- (*it)->GetAsString(&str); // MD5 for encoded "Hello world!" string.
- EXPECT_TRUE(base::MD5String(encoded) == str);
-
- ++it;
- EXPECT_TRUE(it == list.end()); // Reached end of list.
-
- local_list.clear();
- EXPECT_EQ(
- MetricsLogSerializer::RECALL_SUCCESS,
- MetricsLogSerializer::ReadLogsFromPrefList(list, &local_list));
- EXPECT_EQ(1U, local_list.size());
-}
-
-// Store a set of logs over the length limit, but smaller than the min number of
-// bytes.
-TEST(MetricsLogSerializerTest, LongButTinyLogList) {
- base::ListValue list;
-
- size_t log_count = kListLengthLimit * 5;
- std::vector<MetricsLogManager::SerializedLog> local_list(log_count);
- for (size_t i = 0; i < local_list.size(); ++i)
- SetLogText("x", &local_list[i]);
-
- MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit,
- kLogByteLimit, &list);
- std::vector<MetricsLogManager::SerializedLog> result_list;
- EXPECT_EQ(
- MetricsLogSerializer::RECALL_SUCCESS,
- MetricsLogSerializer::ReadLogsFromPrefList(list, &result_list));
- EXPECT_EQ(local_list.size(), result_list.size());
-
- EXPECT_TRUE(result_list.front().log_text().find("x") == 0);
-}
-
-// Store a set of logs over the length limit, but that doesn't reach the minimum
-// number of bytes until after passing the length limit.
-TEST(MetricsLogSerializerTest, LongButSmallLogList) {
- base::ListValue list;
-
- size_t log_count = kListLengthLimit * 5;
- // Make log_count logs each slightly larger than
- // kLogByteLimit / (log_count - 2)
- // so that the minimum is reached before the oldest (first) two logs.
- std::vector<MetricsLogManager::SerializedLog> local_list(log_count);
- size_t log_size = (kLogByteLimit / (log_count - 2)) + 2;
- SetLogText("one", &local_list[0]);
- SetLogText("two", &local_list[1]);
- SetLogText("three", &local_list[2]);
- SetLogText("last", &local_list[log_count - 1]);
- for (size_t i = 0; i < local_list.size(); ++i) {
- std::string log_text = local_list[i].log_text();
- log_text.resize(log_size, ' ');
- local_list[i].SwapLogText(&log_text);
- }
-
- MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit,
- kLogByteLimit, &list);
- std::vector<MetricsLogManager::SerializedLog> result_list;
- EXPECT_EQ(
- MetricsLogSerializer::RECALL_SUCCESS,
- MetricsLogSerializer::ReadLogsFromPrefList(list, &result_list));
- EXPECT_EQ(local_list.size() - 2, result_list.size());
-
- EXPECT_TRUE(result_list.front().log_text().find("three") == 0);
- EXPECT_TRUE(result_list.back().log_text().find("last") == 0);
-}
-
-// Store a set of logs within the length limit, but well over the minimum
-// number of bytes.
-TEST(MetricsLogSerializerTest, ShortButLargeLogList) {
- base::ListValue list;
-
- std::vector<MetricsLogManager::SerializedLog> local_list(kListLengthLimit);
- // Make the total byte count about twice the minimum.
- size_t log_size = (kLogByteLimit / local_list.size()) * 2;
- for (size_t i = 0; i < local_list.size(); ++i) {
- std::string log_text = local_list[i].log_text();
- log_text.resize(log_size, ' ');
- local_list[i].SwapLogText(&log_text);
- }
-
- MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit,
- kLogByteLimit, &list);
- std::vector<MetricsLogManager::SerializedLog> result_list;
- EXPECT_EQ(
- MetricsLogSerializer::RECALL_SUCCESS,
- MetricsLogSerializer::ReadLogsFromPrefList(list, &result_list));
- EXPECT_EQ(local_list.size(), result_list.size());
-}
-
-// Store a set of logs over the length limit, and over the minimum number of
-// bytes.
-TEST(MetricsLogSerializerTest, LongAndLargeLogList) {
- base::ListValue list;
-
- // Include twice the max number of logs.
- std::vector<MetricsLogManager::SerializedLog>
- local_list(kListLengthLimit * 2);
- // Make the total byte count about four times the minimum.
- size_t log_size = (kLogByteLimit / local_list.size()) * 4;
- SetLogText("First to keep",
- &local_list[local_list.size() - kListLengthLimit]);
- for (size_t i = 0; i < local_list.size(); ++i) {
- std::string log_text = local_list[i].log_text();
- log_text.resize(log_size, ' ');
- local_list[i].SwapLogText(&log_text);
- }
-
- MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit,
- kLogByteLimit, &list);
- std::vector<MetricsLogManager::SerializedLog> result_list;
- EXPECT_EQ(
- MetricsLogSerializer::RECALL_SUCCESS,
- MetricsLogSerializer::ReadLogsFromPrefList(list, &result_list));
- // The max length should control the resulting size.
- EXPECT_EQ(kListLengthLimit, result_list.size());
- EXPECT_TRUE(result_list.front().log_text().find("First to keep") == 0);
-}
-
-// Induce LIST_SIZE_TOO_SMALL corruption
-TEST(MetricsLogSerializerTest, SmallRecoveredListSize) {
- base::ListValue list;
-
- std::vector<MetricsLogManager::SerializedLog> local_list(1);
- SetLogText("Hello world!", &local_list[0]);
-
- MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit,
- kLogByteLimit, &list);
- EXPECT_EQ(3U, list.GetSize());
-
- // Remove last element.
- list.Remove(list.GetSize() - 1, NULL);
- EXPECT_EQ(2U, list.GetSize());
-
- local_list.clear();
- EXPECT_EQ(
- MetricsLogSerializer::LIST_SIZE_TOO_SMALL,
- MetricsLogSerializer::ReadLogsFromPrefList(list, &local_list));
-}
-
-// Remove size from the stored list.
-TEST(MetricsLogSerializerTest, RemoveSizeFromLogList) {
- base::ListValue list;
-
- std::vector<MetricsLogManager::SerializedLog> local_list(2);
- SetLogText("one", &local_list[0]);
- SetLogText("two", &local_list[1]);
- EXPECT_EQ(2U, local_list.size());
- MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit,
- kLogByteLimit, &list);
- EXPECT_EQ(4U, list.GetSize());
-
- list.Remove(0, NULL); // Delete size (1st element).
- EXPECT_EQ(3U, list.GetSize());
-
- local_list.clear();
- EXPECT_EQ(
- MetricsLogSerializer::LIST_SIZE_MISSING,
- MetricsLogSerializer::ReadLogsFromPrefList(list, &local_list));
-}
-
-// Corrupt size of stored list.
-TEST(MetricsLogSerializerTest, CorruptSizeOfLogList) {
- base::ListValue list;
-
- std::vector<MetricsLogManager::SerializedLog> local_list(1);
- SetLogText("Hello world!", &local_list[0]);
-
- MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit,
- kLogByteLimit, &list);
- EXPECT_EQ(3U, list.GetSize());
-
- // Change list size from 1 to 2.
- EXPECT_TRUE(list.Set(0, base::Value::CreateIntegerValue(2)));
- EXPECT_EQ(3U, list.GetSize());
-
- local_list.clear();
- EXPECT_EQ(
- MetricsLogSerializer::LIST_SIZE_CORRUPTION,
- MetricsLogSerializer::ReadLogsFromPrefList(list, &local_list));
-}
-
-// Corrupt checksum of stored list.
-TEST(MetricsLogSerializerTest, CorruptChecksumOfLogList) {
- base::ListValue list;
-
- std::vector<MetricsLogManager::SerializedLog> local_list(1);
- SetLogText("Hello world!", &local_list[0]);
-
- MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit,
- kLogByteLimit, &list);
- EXPECT_EQ(3U, list.GetSize());
-
- // Fetch checksum (last element) and change it.
- std::string checksum;
- EXPECT_TRUE((*(list.end() - 1))->GetAsString(&checksum));
- checksum[0] = (checksum[0] == 'a') ? 'b' : 'a';
- EXPECT_TRUE(list.Set(2, base::Value::CreateStringValue(checksum)));
- EXPECT_EQ(3U, list.GetSize());
-
- local_list.clear();
- EXPECT_EQ(
- MetricsLogSerializer::CHECKSUM_CORRUPTION,
- MetricsLogSerializer::ReadLogsFromPrefList(list, &local_list));
-}
diff --git a/chrome/browser/metrics/metrics_service.cc b/chrome/browser/metrics/metrics_service.cc
index 1cb20ec..700cc8a 100644
--- a/chrome/browser/metrics/metrics_service.cc
+++ b/chrome/browser/metrics/metrics_service.cc
@@ -187,7 +187,6 @@
#include "chrome/browser/memory_details.h"
#include "chrome/browser/metrics/compression_utils.h"
#include "chrome/browser/metrics/metrics_log.h"
-#include "chrome/browser/metrics/metrics_log_serializer.h"
#include "chrome/browser/metrics/metrics_reporting_scheduler.h"
#include "chrome/browser/metrics/metrics_state_manager.h"
#include "chrome/browser/metrics/time_ticks_experiment_win.h"
@@ -204,6 +203,7 @@
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "components/metrics/metrics_log_manager.h"
+#include "components/metrics/metrics_pref_names.h"
#include "components/variations/entropy_provider.h"
#include "components/variations/metrics_util.h"
#include "content/public/browser/child_process_data.h"
@@ -444,8 +444,8 @@ void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(prefs::kStabilitySavedSystemProfileHash,
std::string());
- registry->RegisterListPref(prefs::kMetricsInitialLogs);
- registry->RegisterListPref(prefs::kMetricsOngoingLogs);
+ registry->RegisterListPref(metrics::prefs::kMetricsInitialLogs);
+ registry->RegisterListPref(metrics::prefs::kMetricsOngoingLogs);
registry->RegisterInt64Pref(prefs::kInstallDate, 0);
registry->RegisterInt64Pref(prefs::kUninstallMetricsPageLoadCount, 0);
@@ -460,7 +460,9 @@ void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
}
MetricsService::MetricsService(metrics::MetricsStateManager* state_manager)
- : state_manager_(state_manager),
+ : MetricsServiceBase(g_browser_process->local_state(),
+ kUploadLogAvoidRetransmitSize),
+ state_manager_(state_manager),
recording_active_(false),
reporting_active_(false),
test_mode_active_(false),
@@ -476,9 +478,6 @@ MetricsService::MetricsService(metrics::MetricsStateManager* state_manager)
DCHECK(IsSingleThreaded());
DCHECK(state_manager_);
- log_manager_.set_log_serializer(new MetricsLogSerializer);
- log_manager_.set_max_ongoing_log_store_size(kUploadLogAvoidRetransmitSize);
-
BrowserChildProcessObserver::Add(this);
}
@@ -1188,11 +1187,11 @@ void MetricsService::PushPendingLogsToPersistentStorage() {
if (log_manager_.has_staged_log()) {
// We may race here, and send second copy of the log later.
- MetricsLogManager::StoreType store_type;
+ metrics::PersistedLogs::StoreType store_type;
if (current_fetch_.get())
- store_type = MetricsLogManager::PROVISIONAL_STORE;
+ store_type = metrics::PersistedLogs::PROVISIONAL_STORE;
else
- store_type = MetricsLogManager::NORMAL_STORE;
+ store_type = metrics::PersistedLogs::NORMAL_STORE;
log_manager_.StoreStagedLogAsUnsent(store_type);
}
DCHECK(!log_manager_.has_staged_log());
@@ -1512,7 +1511,7 @@ void MetricsService::PrepareFetchWithStagedLog() {
current_fetch_->SetRequestContext(
g_browser_process->system_request_context());
- std::string log_text = log_manager_.staged_log_text();
+ std::string log_text = log_manager_.staged_log();
std::string compressed_log_text;
bool compression_successful = chrome::GzipCompress(log_text,
&compressed_log_text);
@@ -1566,7 +1565,7 @@ void MetricsService::OnURLFetchComplete(const net::URLFetcher* source) {
// Provide boolean for error recovery (allow us to ignore response_code).
bool discard_log = false;
- const size_t log_size = log_manager_.staged_log_text().length();
+ const size_t log_size = log_manager_.staged_log().length();
if (!upload_succeeded && log_size > kUploadLogAvoidRetransmitSize) {
UMA_HISTOGRAM_COUNTS("UMA.Large Rejected Log was Discarded",
static_cast<int>(log_size));
diff --git a/chrome/browser/metrics/metrics_service_unittest.cc b/chrome/browser/metrics/metrics_service_unittest.cc
index a8a12a1..c3bef76 100644
--- a/chrome/browser/metrics/metrics_service_unittest.cc
+++ b/chrome/browser/metrics/metrics_service_unittest.cc
@@ -219,7 +219,7 @@ TEST_F(MetricsServiceTest, InitialStabilityLogAfterCrash) {
EXPECT_TRUE(log_manager->has_staged_log());
metrics::ChromeUserMetricsExtension uma_log;
- EXPECT_TRUE(uma_log.ParseFromString(log_manager->staged_log_text()));
+ EXPECT_TRUE(uma_log.ParseFromString(log_manager->staged_log()));
EXPECT_TRUE(uma_log.has_client_id());
EXPECT_TRUE(uma_log.has_session_id());
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index b38e4ac..704badd 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1193,8 +1193,6 @@
'browser/metrics/metrics_log.h',
'browser/metrics/metrics_log_chromeos.cc',
'browser/metrics/metrics_log_chromeos.h',
- 'browser/metrics/metrics_log_serializer.cc',
- 'browser/metrics/metrics_log_serializer.h',
'browser/metrics/metrics_network_observer.cc',
'browser/metrics/metrics_network_observer.h',
'browser/metrics/metrics_reporting_scheduler.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index e153095..4f23d91 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -1080,7 +1080,6 @@
'browser/metrics/extension_metrics_unittest.cc',
'browser/metrics/machine_id_provider_win_unittest.cc',
'browser/metrics/metrics_log_unittest.cc',
- 'browser/metrics/metrics_log_serializer_unittest.cc',
'browser/metrics/metrics_reporting_scheduler_unittest.cc',
'browser/metrics/metrics_service_unittest.cc',
'browser/metrics/metrics_state_manager_unittest.cc',
diff --git a/chrome/common/metrics/metrics_service_base.cc b/chrome/common/metrics/metrics_service_base.cc
index a0a8182..3bad34e 100644
--- a/chrome/common/metrics/metrics_service_base.cc
+++ b/chrome/common/metrics/metrics_service_base.cc
@@ -10,8 +10,10 @@
using base::Histogram;
-MetricsServiceBase::MetricsServiceBase()
- : histogram_snapshot_manager_(this) {
+MetricsServiceBase::MetricsServiceBase(PrefService* local_state,
+ size_t max_ongoing_log_size)
+ : log_manager_(local_state, max_ongoing_log_size),
+ histogram_snapshot_manager_(this) {
}
MetricsServiceBase::~MetricsServiceBase() {
diff --git a/chrome/common/metrics/metrics_service_base.h b/chrome/common/metrics/metrics_service_base.h
index d2e9043..e777a67 100644
--- a/chrome/common/metrics/metrics_service_base.h
+++ b/chrome/common/metrics/metrics_service_base.h
@@ -30,7 +30,10 @@ class MetricsServiceBase : public base::HistogramFlattener {
virtual void InconsistencyDetectedInLoggedCount(int amount) OVERRIDE;
protected:
- MetricsServiceBase();
+ // The metrics service will persist it's unsent logs by storing them in
+ // |local_state|, and will not persist ongoing logs over
+ // |max_ongoing_log_size|.
+ MetricsServiceBase(PrefService* local_state, size_t max_ongoing_log_size);
virtual ~MetricsServiceBase();
// The metrics server's URL.
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 65db79a..419a76f 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1385,19 +1385,6 @@ const char kCrashReportingEnabled[] =
"user_experience_metrics_crash.reporting_enabled";
#endif
-// Array of strings that are each UMA logs that were supposed to be sent in the
-// first minute of a browser session. These logs include things like crash count
-// info, etc.
-const char kMetricsInitialLogs[] =
- "user_experience_metrics.initial_logs_as_protobufs";
-
-// Array of strings that are each UMA logs that were not sent because the
-// browser terminated before these accumulated metrics could be sent. These
-// logs typically include histograms and memory reports, as well as ongoing
-// user activities.
-const char kMetricsOngoingLogs[] =
- "user_experience_metrics.ongoing_logs_as_protobufs";
-
// 64-bit integer serialization of the base::Time from the last successful seed
// fetch (i.e. when the Variations server responds with 200 or 304).
const char kVariationsLastFetchTime[] = "variations_last_fetch_time";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 14cd0a2..4f10be7 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -441,8 +441,6 @@ extern const char kMetricsResetIds[];
#if defined(OS_ANDROID)
extern const char kCrashReportingEnabled[];
#endif
-extern const char kMetricsInitialLogs[];
-extern const char kMetricsOngoingLogs[];
extern const char kVariationsLastFetchTime[];
extern const char kVariationsRestrictParameter[];
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index f51868a..6a3052f 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -106,6 +106,7 @@
'metrics/metrics_hashes_unittest.cc',
'metrics/metrics_log_base_unittest.cc',
'metrics/metrics_log_manager_unittest.cc',
+ 'metrics/persisted_logs_unittest.cc',
'navigation_interception/intercept_navigation_resource_throttle_unittest.cc',
'os_crypt/ie7_password_win_unittest.cc',
'os_crypt/keychain_password_mac_unittest.mm',
diff --git a/components/metrics.gypi b/components/metrics.gypi
index e0fe848..61e9855 100644
--- a/components/metrics.gypi
+++ b/components/metrics.gypi
@@ -21,6 +21,10 @@
'metrics/metrics_log_base.h',
'metrics/metrics_log_manager.cc',
'metrics/metrics_log_manager.h',
+ 'metrics/metrics_pref_names.cc',
+ 'metrics/metrics_pref_names.h',
+ 'metrics/persisted_logs.cc',
+ 'metrics/persisted_logs.h',
],
},
{
diff --git a/components/metrics/metrics_log_manager.cc b/components/metrics/metrics_log_manager.cc
index e537eef..2260b64 100644
--- a/components/metrics/metrics_log_manager.cc
+++ b/components/metrics/metrics_log_manager.cc
@@ -7,45 +7,50 @@
#include <algorithm>
#include "base/metrics/histogram.h"
-#include "base/sha1.h"
#include "base/strings/string_util.h"
#include "base/timer/elapsed_timer.h"
#include "components/metrics/metrics_log_base.h"
+#include "components/metrics/metrics_pref_names.h"
namespace metrics {
-MetricsLogManager::SerializedLog::SerializedLog() {}
-MetricsLogManager::SerializedLog::~SerializedLog() {}
+namespace {
-bool MetricsLogManager::SerializedLog::IsEmpty() const {
- return log_text_.empty();
-}
+// The number of "initial" logs to save, and hope to send during a future Chrome
+// session. Initial logs contain crash stats, and are pretty small.
+const size_t kInitialLogsPersistLimit = 20;
-void MetricsLogManager::SerializedLog::SwapLogText(std::string* log_text) {
- log_text_.swap(*log_text);
- if (log_text_.empty())
- log_hash_.clear();
- else
- log_hash_ = base::SHA1HashString(log_text_);
-}
+// The number of ongoing logs to save persistently, and hope to
+// send during a this or future sessions. Note that each log may be pretty
+// large, as presumably the related "initial" log wasn't sent (probably nothing
+// was, as the user was probably off-line). As a result, the log probably kept
+// accumulating while the "initial" log was stalled, and couldn't be sent. As a
+// result, we don't want to save too many of these mega-logs.
+// A "standard shutdown" will create a small log, including just the data that
+// was not yet been transmitted, and that is normal (to have exactly one
+// ongoing_log_ at startup).
+const size_t kOngoingLogsPersistLimit = 8;
-void MetricsLogManager::SerializedLog::Clear() {
- log_text_.clear();
- log_hash_.clear();
-}
+// The number of bytes each of initial and ongoing logs that must be stored.
+// This ensures that a reasonable amount of history will be stored even if there
+// is a long series of very small logs.
+const size_t kStorageByteLimitPerLogType = 300000;
-void MetricsLogManager::SerializedLog::Swap(
- MetricsLogManager::SerializedLog* other) {
- log_text_.swap(other->log_text_);
- log_hash_.swap(other->log_hash_);
-}
+} // namespace
-MetricsLogManager::MetricsLogManager()
+MetricsLogManager::MetricsLogManager(PrefService* local_state,
+ size_t max_ongoing_log_size)
: unsent_logs_loaded_(false),
- staged_log_type_(MetricsLogBase::NO_LOG),
- max_ongoing_log_store_size_(0),
- last_provisional_store_index_(-1),
- last_provisional_store_type_(MetricsLogBase::INITIAL_STABILITY_LOG) {}
+ initial_log_queue_(local_state,
+ prefs::kMetricsInitialLogs,
+ kInitialLogsPersistLimit,
+ kStorageByteLimitPerLogType,
+ 0),
+ ongoing_log_queue_(local_state,
+ prefs::kMetricsOngoingLogs,
+ kOngoingLogsPersistLimit,
+ kStorageByteLimitPerLogType,
+ max_ongoing_log_size) {}
MetricsLogManager::~MetricsLogManager() {}
@@ -57,46 +62,28 @@ void MetricsLogManager::BeginLoggingWithLog(MetricsLogBase* log) {
void MetricsLogManager::FinishCurrentLog() {
DCHECK(current_log_.get());
current_log_->CloseLog();
- SerializedLog compressed_log;
- CompressCurrentLog(&compressed_log);
- if (!compressed_log.IsEmpty())
- StoreLog(&compressed_log, current_log_->log_type(), NORMAL_STORE);
+ std::string log_text;
+ current_log_->GetEncodedLog(&log_text);
+ if (!log_text.empty())
+ StoreLog(&log_text, current_log_->log_type());
current_log_.reset();
}
void MetricsLogManager::StageNextLogForUpload() {
- // Prioritize initial logs for uploading.
- std::vector<SerializedLog>* source_list =
- unsent_initial_logs_.empty() ? &unsent_ongoing_logs_
- : &unsent_initial_logs_;
- LogType source_type = (source_list == &unsent_ongoing_logs_) ?
- MetricsLogBase::ONGOING_LOG : MetricsLogBase::INITIAL_STABILITY_LOG;
- // CHECK, rather than DCHECK, because swap()ing with an empty list causes
- // hard-to-identify crashes much later.
- CHECK(!source_list->empty());
- DCHECK(staged_log_.IsEmpty());
- DCHECK_EQ(MetricsLogBase::NO_LOG, staged_log_type_);
- staged_log_.Swap(&source_list->back());
- staged_log_type_ = source_type;
- source_list->pop_back();
-
- // If the staged log was the last provisional store, clear that.
- if (last_provisional_store_index_ != -1) {
- if (source_type == last_provisional_store_type_ &&
- static_cast<unsigned int>(last_provisional_store_index_) ==
- source_list->size()) {
- last_provisional_store_index_ = -1;
- }
- }
-}
-
-bool MetricsLogManager::has_staged_log() const {
- return !staged_log_.IsEmpty();
+ DCHECK(!has_staged_log());
+ if (!initial_log_queue_.empty())
+ initial_log_queue_.StageLog();
+ else
+ ongoing_log_queue_.StageLog();
}
void MetricsLogManager::DiscardStagedLog() {
- staged_log_.Clear();
- staged_log_type_ = MetricsLogBase::NO_LOG;
+ DCHECK(has_staged_log());
+ if (initial_log_queue_.has_staged_log())
+ initial_log_queue_.DiscardStagedLog();
+ else
+ ongoing_log_queue_.DiscardStagedLog();
+ DCHECK(!has_staged_log());
}
void MetricsLogManager::DiscardCurrentLog() {
@@ -114,95 +101,49 @@ void MetricsLogManager::ResumePausedLog() {
current_log_.reset(paused_log_.release());
}
-void MetricsLogManager::StoreStagedLogAsUnsent(StoreType store_type) {
- DCHECK(has_staged_log());
-
- // If compressing the log failed, there's nothing to store.
- if (staged_log_.IsEmpty())
- return;
+void MetricsLogManager::StoreLog(std::string* log, LogType log_type) {
+ DCHECK_NE(MetricsLogBase::NO_LOG, log_type);
+ metrics::PersistedLogs* destination_queue =
+ (log_type == MetricsLogBase::INITIAL_STABILITY_LOG) ?
+ &initial_log_queue_ : &ongoing_log_queue_;
- StoreLog(&staged_log_, staged_log_type_, store_type);
- DiscardStagedLog();
+ destination_queue->StoreLog(log);
}
-void MetricsLogManager::StoreLog(SerializedLog* log,
- LogType log_type,
- StoreType store_type) {
- DCHECK_NE(MetricsLogBase::NO_LOG, log_type);
- std::vector<SerializedLog>* destination_list =
- (log_type == MetricsLogBase::INITIAL_STABILITY_LOG) ?
- &unsent_initial_logs_ : &unsent_ongoing_logs_;
- destination_list->push_back(SerializedLog());
- destination_list->back().Swap(log);
-
- if (store_type == PROVISIONAL_STORE) {
- last_provisional_store_index_ = destination_list->size() - 1;
- last_provisional_store_type_ = log_type;
- }
+void MetricsLogManager::StoreStagedLogAsUnsent(
+ metrics::PersistedLogs::StoreType store_type) {
+ DCHECK(has_staged_log());
+ if (initial_log_queue_.has_staged_log())
+ initial_log_queue_.StoreStagedLogAsUnsent(store_type);
+ else
+ ongoing_log_queue_.StoreStagedLogAsUnsent(store_type);
}
void MetricsLogManager::DiscardLastProvisionalStore() {
- if (last_provisional_store_index_ == -1)
- return;
- std::vector<SerializedLog>* source_list =
- (last_provisional_store_type_ == MetricsLogBase::ONGOING_LOG)
- ? &unsent_ongoing_logs_
- : &unsent_initial_logs_;
- DCHECK_LT(static_cast<unsigned int>(last_provisional_store_index_),
- source_list->size());
- source_list->erase(source_list->begin() + last_provisional_store_index_);
- last_provisional_store_index_ = -1;
+ // We have at most one provisional store, (since at most one log is being
+ // uploaded at a time), so at least one of these will be a no-op.
+ initial_log_queue_.DiscardLastProvisionalStore();
+ ongoing_log_queue_.DiscardLastProvisionalStore();
}
void MetricsLogManager::PersistUnsentLogs() {
- DCHECK(log_serializer_.get());
- if (!log_serializer_.get())
- return;
DCHECK(unsent_logs_loaded_);
if (!unsent_logs_loaded_)
return;
base::ElapsedTimer timer;
- // Remove any ongoing logs that are over the serialization size limit.
- if (max_ongoing_log_store_size_) {
- for (std::vector<SerializedLog>::iterator it = unsent_ongoing_logs_.begin();
- it != unsent_ongoing_logs_.end();) {
- size_t log_size = it->log_text().length();
- if (log_size > max_ongoing_log_store_size_) {
- UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted",
- static_cast<int>(log_size));
- it = unsent_ongoing_logs_.erase(it);
- } else {
- ++it;
- }
- }
- }
- log_serializer_->SerializeLogs(unsent_initial_logs_,
- MetricsLogBase::INITIAL_STABILITY_LOG);
- log_serializer_->SerializeLogs(unsent_ongoing_logs_,
- MetricsLogBase::ONGOING_LOG);
+ initial_log_queue_.SerializeLogs();
+ ongoing_log_queue_.SerializeLogs();
UMA_HISTOGRAM_TIMES("UMA.StoreLogsTime", timer.Elapsed());
}
void MetricsLogManager::LoadPersistedUnsentLogs() {
- DCHECK(log_serializer_.get());
- if (!log_serializer_.get())
- return;
-
base::ElapsedTimer timer;
- log_serializer_->DeserializeLogs(MetricsLogBase::INITIAL_STABILITY_LOG,
- &unsent_initial_logs_);
- log_serializer_->DeserializeLogs(MetricsLogBase::ONGOING_LOG,
- &unsent_ongoing_logs_);
+ initial_log_queue_.DeserializeLogs();
+ ongoing_log_queue_.DeserializeLogs();
UMA_HISTOGRAM_TIMES("UMA.LoadLogsTime", timer.Elapsed());
unsent_logs_loaded_ = true;
}
-void MetricsLogManager::CompressCurrentLog(SerializedLog* compressed_log) {
- std::string log_text;
- current_log_->GetEncodedLog(&log_text);
- compressed_log->SwapLogText(&log_text);
-}
-
-} // namespace metrics
+} // namespace metrics
diff --git a/components/metrics/metrics_log_manager.h b/components/metrics/metrics_log_manager.h
index 006d4ed..46463eb 100644
--- a/components/metrics/metrics_log_manager.h
+++ b/components/metrics/metrics_log_manager.h
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "components/metrics/metrics_log_base.h"
+#include "components/metrics/persisted_logs.h"
namespace metrics {
@@ -21,46 +22,12 @@ class MetricsLogManager {
public:
typedef MetricsLogBase::LogType LogType;
- MetricsLogManager();
+ // The metrics log manager will persist it's unsent logs by storing them in
+ // |local_state|, and will not persist ongoing logs over
+ // |max_ongoing_log_size|.
+ MetricsLogManager(PrefService* local_state, size_t max_ongoing_log_size);
~MetricsLogManager();
- class SerializedLog {
- public:
- SerializedLog();
- ~SerializedLog();
-
- const std::string& log_text() const { return log_text_; }
- const std::string& log_hash() const { return log_hash_; }
-
- // Returns true if the log is empty.
- bool IsEmpty() const;
-
- // Swaps log text with |log_text| and updates the hash. This is more
- // performant than a regular setter as it avoids doing a large string copy.
- void SwapLogText(std::string* log_text);
-
- // Clears the log.
- void Clear();
-
- // Swaps log contents with |other|.
- void Swap(SerializedLog* other);
-
- private:
- // Non-human readable log text (serialized proto).
- std::string log_text_;
-
- // Non-human readable SHA1 of |log_text| or empty if |log_text| is empty.
- std::string log_hash_;
-
- // Intentionally omits DISALLOW_COPY_AND_ASSIGN() so that it can be used
- // in std::vector<SerializedLog>.
- };
-
- enum StoreType {
- NORMAL_STORE, // A standard store operation.
- PROVISIONAL_STORE, // A store operation that can be easily reverted later.
- };
-
// Takes ownership of |log| and makes it the current_log. This should only be
// called if there is not a current log.
void BeginLoggingWithLog(MetricsLogBase* log);
@@ -74,7 +41,7 @@ class MetricsLogManager {
// Returns true if there are any logs waiting to be uploaded.
bool has_unsent_logs() const {
- return !unsent_initial_logs_.empty() || !unsent_ongoing_logs_.empty();
+ return initial_log_queue_.size() || ongoing_log_queue_.size();
}
// Populates staged_log_text() with the next stored log to send.
@@ -82,15 +49,25 @@ class MetricsLogManager {
void StageNextLogForUpload();
// Returns true if there is a log that needs to be, or is being, uploaded.
- bool has_staged_log() const;
+ bool has_staged_log() const {
+ return initial_log_queue_.has_staged_log() ||
+ ongoing_log_queue_.has_staged_log();
+ }
- // The text of the staged log, as a serialized protobuf. Empty if there is no
- // staged log, or if compression of the staged log failed.
- const std::string& staged_log_text() const { return staged_log_.log_text(); }
+ // The text of the staged log, as a serialized protobuf.
+ // Will trigger a DCHECK if there is no staged log.
+ const std::string& staged_log() const {
+ return initial_log_queue_.has_staged_log() ?
+ initial_log_queue_.staged_log() : ongoing_log_queue_.staged_log();
+ }
- // The SHA1 hash (non-human readable) of the staged log or empty if there is
- // no staged log.
- const std::string& staged_log_hash() const { return staged_log_.log_hash(); }
+ // The SHA1 hash of the staged log.
+ // Will trigger a DCHECK if there is no staged log.
+ const std::string& staged_log_hash() const {
+ return initial_log_queue_.has_staged_log() ?
+ initial_log_queue_.staged_log_hash() :
+ ongoing_log_queue_.staged_log_hash();
+ }
// Discards the staged log.
void DiscardStagedLog();
@@ -116,60 +93,24 @@ class MetricsLogManager {
// This is intended to be used when logs are being saved while an upload is in
// progress, in case the upload later succeeds.
// This can only be called if has_staged_log() is true.
- void StoreStagedLogAsUnsent(StoreType store_type);
+ void StoreStagedLogAsUnsent(metrics::PersistedLogs::StoreType store_type);
// Discards the last log stored with StoreStagedLogAsUnsent with |store_type|
// set to PROVISIONAL_STORE, as long as it hasn't already been re-staged. If
// the log is no longer present, this is a no-op.
void DiscardLastProvisionalStore();
- // Sets the threshold for how large an onging log can be and still be written
- // to persistant storage. Ongoing logs larger than this will be discarded
- // before persisting. 0 is interpreted as no limit.
- void set_max_ongoing_log_store_size(size_t max_size) {
- max_ongoing_log_store_size_ = max_size;
- }
-
- // Interface for a utility class to serialize and deserialize logs for
- // persistent storage.
- class LogSerializer {
- public:
- virtual ~LogSerializer() {}
-
- // Serializes |logs| to persistent storage, replacing any previously
- // serialized logs of the same type.
- virtual void SerializeLogs(const std::vector<SerializedLog>& logs,
- LogType log_type) = 0;
-
- // Populates |logs| with logs of type |log_type| deserialized from
- // persistent storage.
- virtual void DeserializeLogs(LogType log_type,
- std::vector<SerializedLog>* logs) = 0;
- };
-
- // Sets the serializer to use for persisting and loading logs; takes ownership
- // of |serializer|.
- void set_log_serializer(LogSerializer* serializer) {
- log_serializer_.reset(serializer);
- }
-
- // Saves any unsent logs to persistent storage using the current log
- // serializer. Can only be called after set_log_serializer.
+ // Saves any unsent logs to persistent storage.
void PersistUnsentLogs();
- // Loads any unsent logs from persistent storage using the current log
- // serializer. Can only be called after set_log_serializer.
+ // Loads any unsent logs from persistent storage.
void LoadPersistedUnsentLogs();
private:
- // Saves |log| as the given type (or discards it in accordance with
- // |max_ongoing_log_store_size_|).
+ // Saves |log| as the given type.
// NOTE: This clears the contents of |log| (to avoid an expensive copy),
// so the log should be discarded after this call.
- void StoreLog(SerializedLog* log, LogType log_type, StoreType store_type);
-
- // Compresses |current_log_| into |compressed_log|.
- void CompressCurrentLog(SerializedLog* compressed_log);
+ void StoreLog(std::string* log, LogType log_type);
// Tracks whether unsent logs (if any) have been loaded from the serializer.
bool unsent_logs_loaded_;
@@ -180,30 +121,9 @@ class MetricsLogManager {
// A paused, previously-current log.
scoped_ptr<MetricsLogBase> paused_log_;
- // Helper class to handle serialization/deserialization of logs for persistent
- // storage. May be NULL.
- scoped_ptr<LogSerializer> log_serializer_;
-
- // The current staged log, ready for upload to the server.
- SerializedLog staged_log_;
- LogType staged_log_type_;
-
- // Logs from a previous session that have not yet been sent.
- // Note that the vector has the oldest logs listed first (early in the
- // vector), and we'll discard old logs if we have gathered too many logs.
- std::vector<SerializedLog> unsent_initial_logs_;
- std::vector<SerializedLog> unsent_ongoing_logs_;
-
- size_t max_ongoing_log_store_size_;
-
- // The index and type of the last provisional store. If nothing has been
- // provisionally stored, or the last provisional store has already been
- // re-staged, the index will be -1;
- // This is necessary because during an upload there are two logs (staged
- // and current) and a client might store them in either order, so it's
- // not necessarily the case that the provisional store is the last store.
- int last_provisional_store_index_;
- LogType last_provisional_store_type_;
+ // Logs that have not yet been sent.
+ metrics::PersistedLogs initial_log_queue_;
+ metrics::PersistedLogs ongoing_log_queue_;
DISALLOW_COPY_AND_ASSIGN(MetricsLogManager);
};
diff --git a/components/metrics/metrics_log_manager_unittest.cc b/components/metrics/metrics_log_manager_unittest.cc
index ec0ca38f..c5c2b1a 100644
--- a/components/metrics/metrics_log_manager_unittest.cc
+++ b/components/metrics/metrics_log_manager_unittest.cc
@@ -8,8 +8,10 @@
#include <utility>
#include <vector>
-#include "base/sha1.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/testing_pref_service.h"
#include "components/metrics/metrics_log_base.h"
+#include "components/metrics/metrics_pref_names.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
@@ -17,34 +19,28 @@ namespace metrics {
namespace {
// Dummy serializer that just stores logs in memory.
-class DummyLogSerializer : public MetricsLogManager::LogSerializer {
+class TestLogPrefService : public TestingPrefServiceSimple {
public:
- virtual void SerializeLogs(
- const std::vector<MetricsLogManager::SerializedLog>& logs,
- MetricsLogManager::LogType log_type) OVERRIDE {
- persisted_logs_[log_type] = logs;
+ TestLogPrefService() {
+ registry()->RegisterListPref(prefs::kMetricsInitialLogs);
+ registry()->RegisterListPref(prefs::kMetricsOngoingLogs);
}
-
- virtual void DeserializeLogs(
- MetricsLogManager::LogType log_type,
- std::vector<MetricsLogManager::SerializedLog>* logs) OVERRIDE {
- ASSERT_NE(static_cast<void*>(NULL), logs);
- *logs = persisted_logs_[log_type];
- }
-
// Returns the number of logs of the given type.
size_t TypeCount(MetricsLogManager::LogType log_type) {
- return persisted_logs_[log_type].size();
+ int list_length = 0;
+ if (log_type == MetricsLogBase::INITIAL_STABILITY_LOG)
+ list_length = GetList(prefs::kMetricsInitialLogs)->GetSize();
+ else
+ list_length = GetList(prefs::kMetricsOngoingLogs)->GetSize();
+ return list_length ? list_length - 2 : 0;
}
-
- // In-memory "persitent storage".
- std::vector<MetricsLogManager::SerializedLog> persisted_logs_[2];
};
} // namespace
TEST(MetricsLogManagerTest, StandardFlow) {
- MetricsLogManager log_manager;
+ TestLogPrefService pref_service;
+ MetricsLogManager log_manager(&pref_service, 0);
// Make sure a new manager has a clean slate.
EXPECT_EQ(NULL, log_manager.current_log());
@@ -70,19 +66,19 @@ TEST(MetricsLogManagerTest, StandardFlow) {
log_manager.StageNextLogForUpload();
EXPECT_TRUE(log_manager.has_staged_log());
- EXPECT_FALSE(log_manager.staged_log_text().empty());
+ EXPECT_FALSE(log_manager.staged_log().empty());
log_manager.DiscardStagedLog();
EXPECT_EQ(second_log, log_manager.current_log());
EXPECT_FALSE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.has_unsent_logs());
- EXPECT_TRUE(log_manager.staged_log_text().empty());
EXPECT_FALSE(log_manager.has_unsent_logs());
}
TEST(MetricsLogManagerTest, AbandonedLog) {
- MetricsLogManager log_manager;
+ TestLogPrefService pref_service;
+ MetricsLogManager log_manager(&pref_service, 0);
MetricsLogBase* dummy_log =
new MetricsLogBase("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "v");
@@ -95,7 +91,8 @@ TEST(MetricsLogManagerTest, AbandonedLog) {
}
TEST(MetricsLogManagerTest, InterjectedLog) {
- MetricsLogManager log_manager;
+ TestLogPrefService pref_service;
+ MetricsLogManager log_manager(&pref_service, 0);
MetricsLogBase* ongoing_log =
new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "v");
@@ -123,9 +120,8 @@ TEST(MetricsLogManagerTest, InterjectedLog) {
}
TEST(MetricsLogManagerTest, InterjectedLogPreservesType) {
- MetricsLogManager log_manager;
- DummyLogSerializer* serializer = new DummyLogSerializer;
- log_manager.set_log_serializer(serializer);
+ TestLogPrefService pref_service;
+ MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
MetricsLogBase* ongoing_log =
@@ -145,27 +141,27 @@ TEST(MetricsLogManagerTest, InterjectedLogPreservesType) {
// has the right type.
log_manager.FinishCurrentLog();
log_manager.PersistUnsentLogs();
- EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
- EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ EXPECT_EQ(0U, pref_service.TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(1U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
}
TEST(MetricsLogManagerTest, StoreAndLoad) {
- std::vector<MetricsLogManager::SerializedLog> initial_logs;
- std::vector<MetricsLogManager::SerializedLog> ongoing_logs;
-
+ TestLogPrefService pref_service;
// Set up some in-progress logging in a scoped log manager simulating the
// leadup to quitting, then persist as would be done on quit.
{
- MetricsLogManager log_manager;
- DummyLogSerializer* serializer = new DummyLogSerializer;
- log_manager.set_log_serializer(serializer);
+ MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
// Simulate a log having already been unsent from a previous session.
- MetricsLogManager::SerializedLog log;
- std::string text = "proto";
- log.SwapLogText(&text);
- serializer->persisted_logs_[MetricsLogBase::ONGOING_LOG].push_back(log);
+ {
+ std::string log("proto");
+ metrics::PersistedLogs ongoing_logs(
+ &pref_service, prefs::kMetricsOngoingLogs, 1, 1, 0);
+ ongoing_logs.StoreLog(&log);
+ ongoing_logs.SerializeLogs();
+ }
+ EXPECT_EQ(1U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
EXPECT_FALSE(log_manager.has_unsent_logs());
log_manager.LoadPersistedUnsentLogs();
EXPECT_TRUE(log_manager.has_unsent_logs());
@@ -178,35 +174,24 @@ TEST(MetricsLogManagerTest, StoreAndLoad) {
log_manager.FinishCurrentLog();
log_manager.BeginLoggingWithLog(log2);
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(metrics::PersistedLogs::NORMAL_STORE);
log_manager.FinishCurrentLog();
// Nothing should be written out until PersistUnsentLogs is called.
- EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
- EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ EXPECT_EQ(0U, pref_service.TypeCount(
+ MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(1U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
log_manager.PersistUnsentLogs();
- EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
- EXPECT_EQ(2U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
-
- // Save the logs to transfer over to a new serializer (since log_manager
- // owns |serializer|, so it's about to go away.
- initial_logs =
- serializer->persisted_logs_[MetricsLogBase::INITIAL_STABILITY_LOG];
- ongoing_logs = serializer->persisted_logs_[MetricsLogBase::ONGOING_LOG];
+ EXPECT_EQ(1U, pref_service.TypeCount(
+ MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(2U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
}
// Now simulate the relaunch, ensure that the log manager restores
// everything correctly, and verify that once the are handled they are not
// re-persisted.
{
- MetricsLogManager log_manager;
-
- DummyLogSerializer* serializer = new DummyLogSerializer;
- serializer->persisted_logs_[MetricsLogBase::INITIAL_STABILITY_LOG] =
- initial_logs;
- serializer->persisted_logs_[MetricsLogBase::ONGOING_LOG] = ongoing_logs;
-
- log_manager.set_log_serializer(serializer);
+ MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
EXPECT_TRUE(log_manager.has_unsent_logs());
@@ -215,8 +200,9 @@ TEST(MetricsLogManagerTest, StoreAndLoad) {
// The initial log should be sent first; update the persisted storage to
// verify.
log_manager.PersistUnsentLogs();
- EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
- EXPECT_EQ(2U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ EXPECT_EQ(0U, pref_service.TypeCount(
+ MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(2U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
// Handle the first ongoing log.
log_manager.StageNextLogForUpload();
@@ -230,20 +216,20 @@ TEST(MetricsLogManagerTest, StoreAndLoad) {
// Nothing should have changed "on disk" since PersistUnsentLogs hasn't been
// called again.
- EXPECT_EQ(2U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ EXPECT_EQ(2U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
// Persist, and make sure nothing is left.
log_manager.PersistUnsentLogs();
- EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
- EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ EXPECT_EQ(0U, pref_service.TypeCount(
+ MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(0U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
}
}
TEST(MetricsLogManagerTest, StoreStagedLogTypes) {
// Ensure that types are preserved when storing staged logs.
{
- MetricsLogManager log_manager;
- DummyLogSerializer* serializer = new DummyLogSerializer;
- log_manager.set_log_serializer(serializer);
+ TestLogPrefService pref_service;
+ MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
MetricsLogBase* log =
@@ -251,17 +237,17 @@ TEST(MetricsLogManagerTest, StoreStagedLogTypes) {
log_manager.BeginLoggingWithLog(log);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(metrics::PersistedLogs::NORMAL_STORE);
log_manager.PersistUnsentLogs();
- EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
- EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ EXPECT_EQ(0U, pref_service.TypeCount(
+ MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(1U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
}
{
- MetricsLogManager log_manager;
- DummyLogSerializer* serializer = new DummyLogSerializer;
- log_manager.set_log_serializer(serializer);
+ TestLogPrefService pref_service;
+ MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
MetricsLogBase* log =
@@ -269,22 +255,20 @@ TEST(MetricsLogManagerTest, StoreStagedLogTypes) {
log_manager.BeginLoggingWithLog(log);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(metrics::PersistedLogs::NORMAL_STORE);
log_manager.PersistUnsentLogs();
- EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
- EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ EXPECT_EQ(1U, pref_service.TypeCount(
+ MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(0U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
}
}
TEST(MetricsLogManagerTest, LargeLogDiscarding) {
- MetricsLogManager log_manager;
- DummyLogSerializer* serializer = new DummyLogSerializer;
- log_manager.set_log_serializer(serializer);
- log_manager.LoadPersistedUnsentLogs();
-
+ TestLogPrefService pref_service;
// Set the size threshold very low, to verify that it's honored.
- log_manager.set_max_ongoing_log_store_size(1);
+ MetricsLogManager log_manager(&pref_service, 1);
+ log_manager.LoadPersistedUnsentLogs();
MetricsLogBase* log1 =
new MetricsLogBase("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "v");
@@ -297,16 +281,15 @@ TEST(MetricsLogManagerTest, LargeLogDiscarding) {
// Only the ongoing log should be written out, due to the threshold.
log_manager.PersistUnsentLogs();
- EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
- EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ EXPECT_EQ(1U, pref_service.TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(0U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
}
TEST(MetricsLogManagerTest, ProvisionalStoreStandardFlow) {
// Ensure that provisional store works, and discards the correct log.
{
- MetricsLogManager log_manager;
- DummyLogSerializer* serializer = new DummyLogSerializer;
- log_manager.set_log_serializer(serializer);
+ TestLogPrefService pref_service;
+ MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
MetricsLogBase* log1 =
@@ -317,13 +300,15 @@ TEST(MetricsLogManagerTest, ProvisionalStoreStandardFlow) {
log_manager.FinishCurrentLog();
log_manager.BeginLoggingWithLog(log2);
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(MetricsLogManager::PROVISIONAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(
+ metrics::PersistedLogs::PROVISIONAL_STORE);
log_manager.FinishCurrentLog();
log_manager.DiscardLastProvisionalStore();
log_manager.PersistUnsentLogs();
- EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
- EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ EXPECT_EQ(0U, pref_service.TypeCount(
+ MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(1U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
}
}
@@ -331,9 +316,8 @@ TEST(MetricsLogManagerTest, ProvisionalStoreNoop) {
// Ensure that trying to drop a sent log is a no-op, even if another log has
// since been staged.
{
- MetricsLogManager log_manager;
- DummyLogSerializer* serializer = new DummyLogSerializer;
- log_manager.set_log_serializer(serializer);
+ TestLogPrefService pref_service;
+ MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
MetricsLogBase* log1 =
@@ -343,24 +327,24 @@ TEST(MetricsLogManagerTest, ProvisionalStoreNoop) {
log_manager.BeginLoggingWithLog(log1);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(MetricsLogManager::PROVISIONAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(
+ metrics::PersistedLogs::PROVISIONAL_STORE);
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLog();
log_manager.BeginLoggingWithLog(log2);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(metrics::PersistedLogs::NORMAL_STORE);
log_manager.DiscardLastProvisionalStore();
log_manager.PersistUnsentLogs();
- EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ EXPECT_EQ(1U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
}
// Ensure that trying to drop more than once is a no-op
{
- MetricsLogManager log_manager;
- DummyLogSerializer* serializer = new DummyLogSerializer;
- log_manager.set_log_serializer(serializer);
+ TestLogPrefService pref_service;
+ MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
MetricsLogBase* log1 =
@@ -370,58 +354,18 @@ TEST(MetricsLogManagerTest, ProvisionalStoreNoop) {
log_manager.BeginLoggingWithLog(log1);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(metrics::PersistedLogs::NORMAL_STORE);
log_manager.BeginLoggingWithLog(log2);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(MetricsLogManager::PROVISIONAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(
+ metrics::PersistedLogs::PROVISIONAL_STORE);
log_manager.DiscardLastProvisionalStore();
log_manager.DiscardLastProvisionalStore();
log_manager.PersistUnsentLogs();
- EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ EXPECT_EQ(1U, pref_service.TypeCount(MetricsLogBase::ONGOING_LOG));
}
}
-TEST(MetricsLogManagerTest, SerializedLog) {
- const char kFooText[] = "foo";
- const std::string foo_hash = base::SHA1HashString(kFooText);
- const char kBarText[] = "bar";
- const std::string bar_hash = base::SHA1HashString(kBarText);
-
- MetricsLogManager::SerializedLog log;
- EXPECT_TRUE(log.log_text().empty());
- EXPECT_TRUE(log.log_hash().empty());
-
- std::string foo = kFooText;
- log.SwapLogText(&foo);
- EXPECT_TRUE(foo.empty());
- EXPECT_FALSE(log.IsEmpty());
- EXPECT_EQ(kFooText, log.log_text());
- EXPECT_EQ(foo_hash, log.log_hash());
-
- std::string bar = kBarText;
- log.SwapLogText(&bar);
- EXPECT_EQ(kFooText, bar);
- EXPECT_FALSE(log.IsEmpty());
- EXPECT_EQ(kBarText, log.log_text());
- EXPECT_EQ(bar_hash, log.log_hash());
-
- log.Clear();
- EXPECT_TRUE(log.IsEmpty());
- EXPECT_TRUE(log.log_text().empty());
- EXPECT_TRUE(log.log_hash().empty());
-
- MetricsLogManager::SerializedLog log2;
- foo = kFooText;
- log2.SwapLogText(&foo);
- log.Swap(&log2);
- EXPECT_FALSE(log.IsEmpty());
- EXPECT_EQ(kFooText, log.log_text());
- EXPECT_EQ(foo_hash, log.log_hash());
- EXPECT_TRUE(log2.IsEmpty());
- EXPECT_TRUE(log2.log_text().empty());
- EXPECT_TRUE(log2.log_hash().empty());
-}
-
} // namespace metrics
diff --git a/components/metrics/metrics_pref_names.cc b/components/metrics/metrics_pref_names.cc
new file mode 100644
index 0000000..92a1890
--- /dev/null
+++ b/components/metrics/metrics_pref_names.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/metrics_pref_names.h"
+
+namespace metrics {
+namespace prefs {
+
+// Array of strings that are each UMA logs that were supposed to be sent in the
+// first minute of a browser session. These logs include things like crash count
+// info, etc.
+const char kMetricsInitialLogs[] =
+ "user_experience_metrics.initial_logs_as_protobufs";
+
+// Array of strings that are each UMA logs that were not sent because the
+// browser terminated before these accumulated metrics could be sent. These
+// logs typically include histograms and memory reports, as well as ongoing
+// user activities.
+const char kMetricsOngoingLogs[] =
+ "user_experience_metrics.ongoing_logs_as_protobufs";
+
+} // namespace prefs
+} // namespace metrics
diff --git a/components/metrics/metrics_pref_names.h b/components/metrics/metrics_pref_names.h
new file mode 100644
index 0000000..750d58d
--- /dev/null
+++ b/components/metrics/metrics_pref_names.h
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_METRICS_METRICS_PREF_NAMES_H_
+#define COMPONENTS_METRICS_METRICS_PREF_NAMES_H_
+
+namespace metrics {
+namespace prefs {
+
+// Alphabetical list of preference names specific to the metrics
+// component. Keep alphabetized, and document each in the .cc file.
+extern const char kMetricsInitialLogs[];
+extern const char kMetricsOngoingLogs[];
+
+} // namespace prefs
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_METRICS_PREF_NAMES_H_
diff --git a/components/metrics/persisted_logs.cc b/components/metrics/persisted_logs.cc
new file mode 100644
index 0000000..006f095
--- /dev/null
+++ b/components/metrics/persisted_logs.cc
@@ -0,0 +1,246 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/persisted_logs.h"
+
+#include <string>
+
+#include "base/base64.h"
+#include "base/md5.h"
+#include "base/metrics/histogram.h"
+#include "base/prefs/pref_service.h"
+#include "base/prefs/scoped_user_pref_update.h"
+#include "base/sha1.h"
+#include "base/timer/elapsed_timer.h"
+
+namespace metrics {
+
+namespace {
+
+// We append (2) more elements to persisted lists: the size of the list and a
+// checksum of the elements.
+const size_t kChecksumEntryCount = 2;
+
+PersistedLogs::LogReadStatus MakeRecallStatusHistogram(
+ PersistedLogs::LogReadStatus status) {
+ UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs",
+ status, PersistedLogs::END_RECALL_STATUS);
+ return status;
+}
+
+} // namespace
+
+void PersistedLogs::LogHashPair::SwapLog(std::string* input) {
+ log.swap(*input);
+ if (!log.empty())
+ hash = base::SHA1HashString(log);
+ else
+ hash.clear();
+}
+
+void PersistedLogs::LogHashPair::Swap(PersistedLogs::LogHashPair* input) {
+ log.swap(input->log);
+ hash.swap(input->hash);
+}
+
+PersistedLogs::PersistedLogs(PrefService* local_state,
+ const char* pref_name,
+ size_t min_log_count,
+ size_t min_log_bytes,
+ size_t max_log_size)
+ : local_state_(local_state),
+ pref_name_(pref_name),
+ min_log_count_(min_log_count),
+ min_log_bytes_(min_log_bytes),
+ max_log_size_(max_log_size),
+ last_provisional_store_index_(-1) {
+ DCHECK(local_state_);
+ // One of the limit arguments must be non-zero.
+ DCHECK(min_log_count_ > 0 || min_log_bytes_ > 0);
+}
+
+PersistedLogs::~PersistedLogs() {}
+
+void PersistedLogs::SerializeLogs() {
+ // Remove any logs that are over the serialization size limit.
+ if (max_log_size_) {
+ for (std::vector<LogHashPair>::iterator it = list_.begin();
+ it != list_.end();) {
+ size_t log_size = it->log.length();
+ if (log_size > max_log_size_) {
+ UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted",
+ static_cast<int>(log_size));
+ it = list_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ ListPrefUpdate update(local_state_, pref_name_);
+ WriteLogsToPrefList(update.Get());
+}
+
+PersistedLogs::LogReadStatus PersistedLogs::DeserializeLogs() {
+ const base::ListValue* unsent_logs = local_state_->GetList(pref_name_);
+ return ReadLogsFromPrefList(*unsent_logs);
+}
+
+void PersistedLogs::StoreLog(std::string* input) {
+ list_.push_back(LogHashPair());
+ list_.back().SwapLog(input);
+}
+
+void PersistedLogs::StageLog() {
+ // CHECK, rather than DCHECK, because swap()ing with an empty list causes
+ // hard-to-identify crashes much later.
+ CHECK(!list_.empty());
+ DCHECK(!has_staged_log());
+ staged_log_.Swap(&list_.back());
+ list_.pop_back();
+
+ // If the staged log was the last provisional store, clear that.
+ if (static_cast<size_t>(last_provisional_store_index_) == list_.size())
+ last_provisional_store_index_ = -1;
+ DCHECK(has_staged_log());
+}
+
+void PersistedLogs::DiscardStagedLog() {
+ DCHECK(has_staged_log());
+ staged_log_.log.clear();
+}
+
+void PersistedLogs::StoreStagedLogAsUnsent(StoreType store_type) {
+ list_.push_back(LogHashPair());
+ list_.back().Swap(&staged_log_);
+ if (store_type == PROVISIONAL_STORE)
+ last_provisional_store_index_ = list_.size() - 1;
+}
+
+void PersistedLogs::DiscardLastProvisionalStore() {
+ if (last_provisional_store_index_ == -1)
+ return;
+ DCHECK_LT(static_cast<size_t>(last_provisional_store_index_), list_.size());
+ list_.erase(list_.begin() + last_provisional_store_index_);
+ last_provisional_store_index_ = -1;
+}
+
+void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) {
+ list_value->Clear();
+
+ // Leave the list completely empty if there are no storable values.
+ if (list_.empty())
+ return;
+
+ size_t start = 0;
+ // If there are too many logs, keep the most recent logs up to the length
+ // limit, and at least to the minimum number of bytes.
+ if (list_.size() > min_log_count_) {
+ start = list_.size();
+ size_t bytes_used = 0;
+ std::vector<LogHashPair>::const_reverse_iterator end = list_.rend();
+ for (std::vector<LogHashPair>::const_reverse_iterator it = list_.rbegin();
+ it != end; ++it) {
+ size_t log_size = it->log.length();
+ if (bytes_used >= min_log_bytes_ &&
+ (list_.size() - start) >= min_log_count_) {
+ break;
+ }
+ bytes_used += log_size;
+ --start;
+ }
+ }
+ DCHECK_LT(start, list_.size());
+ if (start >= list_.size())
+ return;
+
+ // Store size at the beginning of the list_value.
+ list_value->Append(base::Value::CreateIntegerValue(list_.size() - start));
+
+ base::MD5Context ctx;
+ base::MD5Init(&ctx);
+ std::string encoded_log;
+ for (std::vector<LogHashPair>::const_iterator it = list_.begin() + start;
+ it != list_.end(); ++it) {
+ // We encode the compressed log as Value::CreateStringValue() expects to
+ // take a valid UTF8 string.
+ base::Base64Encode(it->log, &encoded_log);
+ base::MD5Update(&ctx, encoded_log);
+ list_value->Append(base::Value::CreateStringValue(encoded_log));
+ }
+
+ // Append hash to the end of the list_value.
+ base::MD5Digest digest;
+ base::MD5Final(&digest, &ctx);
+ list_value->Append(base::Value::CreateStringValue(
+ base::MD5DigestToBase16(digest)));
+ // Minimum of 3 elements (size, data, hash).
+ DCHECK_GE(list_value->GetSize(), 3U);
+}
+
+PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList(
+ const base::ListValue& list_value) {
+ if (list_value.GetSize() == 0)
+ return MakeRecallStatusHistogram(LIST_EMPTY);
+ if (list_value.GetSize() <= kChecksumEntryCount)
+ return MakeRecallStatusHistogram(LIST_SIZE_TOO_SMALL);
+
+ // The size is stored at the beginning of the list_value.
+ int size;
+ bool valid = (*list_value.begin())->GetAsInteger(&size);
+ if (!valid)
+ return MakeRecallStatusHistogram(LIST_SIZE_MISSING);
+ // Account for checksum and size included in the list_value.
+ if (static_cast<size_t>(size) != list_value.GetSize() - kChecksumEntryCount)
+ return MakeRecallStatusHistogram(LIST_SIZE_CORRUPTION);
+
+ // Allocate strings for all of the logs we are going to read in.
+ // Do this ahead of time so that we can decode the string values directly into
+ // the elements of |list_|, and thereby avoid making copies of the
+ // serialized logs, which can be fairly large.
+ DCHECK(list_.empty());
+ list_.resize(size);
+
+ base::MD5Context ctx;
+ base::MD5Init(&ctx);
+ std::string encoded_log;
+ size_t local_index = 0;
+ for (base::ListValue::const_iterator it = list_value.begin() + 1;
+ it != list_value.end() - 1; // Last element is the checksum.
+ ++it, ++local_index) {
+ bool valid = (*it)->GetAsString(&encoded_log);
+ if (!valid) {
+ list_.clear();
+ return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION);
+ }
+
+ base::MD5Update(&ctx, encoded_log);
+
+ std::string log_text;
+ if (!base::Base64Decode(encoded_log, &log_text)) {
+ list_.clear();
+ return MakeRecallStatusHistogram(DECODE_FAIL);
+ }
+
+ DCHECK_LT(local_index, list_.size());
+ list_[local_index].SwapLog(&log_text);
+ }
+
+ // Verify checksum.
+ base::MD5Digest digest;
+ base::MD5Final(&digest, &ctx);
+ std::string recovered_md5;
+ // We store the hash at the end of the list_value.
+ valid = (*(list_value.end() - 1))->GetAsString(&recovered_md5);
+ if (!valid) {
+ list_.clear();
+ return MakeRecallStatusHistogram(CHECKSUM_STRING_CORRUPTION);
+ }
+ if (recovered_md5 != base::MD5DigestToBase16(digest)) {
+ list_.clear();
+ return MakeRecallStatusHistogram(CHECKSUM_CORRUPTION);
+ }
+ return MakeRecallStatusHistogram(RECALL_SUCCESS);
+}
+
+} // namespace metrics
diff --git a/components/metrics/persisted_logs.h b/components/metrics/persisted_logs.h
new file mode 100644
index 0000000..c8457e3
--- /dev/null
+++ b/components/metrics/persisted_logs.h
@@ -0,0 +1,164 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_METRICS_PERSISTED_LOGS_H_
+#define COMPONENTS_METRICS_PERSISTED_LOGS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/values.h"
+
+class PrefService;
+
+namespace metrics {
+
+// Maintains a list of unsent logs that are written and restored from disk.
+class PersistedLogs {
+ public:
+ // Used to produce a histogram that keeps track of the status of recalling
+ // persisted per logs.
+ enum LogReadStatus {
+ RECALL_SUCCESS, // We were able to correctly recall a persisted log.
+ LIST_EMPTY, // Attempting to recall from an empty list.
+ LIST_SIZE_MISSING, // Failed to recover list size using GetAsInteger().
+ LIST_SIZE_TOO_SMALL, // Too few elements in the list (less than 3).
+ LIST_SIZE_CORRUPTION, // List size is not as expected.
+ LOG_STRING_CORRUPTION, // Failed to recover log string using GetAsString().
+ CHECKSUM_CORRUPTION, // Failed to verify checksum.
+ CHECKSUM_STRING_CORRUPTION, // Failed to recover checksum string using
+ // GetAsString().
+ DECODE_FAIL, // Failed to decode log.
+ DEPRECATED_XML_PROTO_MISMATCH, // The XML and protobuf logs have
+ // inconsistent data.
+ END_RECALL_STATUS // Number of bins to use to create the histogram.
+ };
+
+ enum StoreType {
+ NORMAL_STORE, // A standard store operation.
+ PROVISIONAL_STORE, // A store operation that can be easily reverted later.
+ };
+
+ // Constructs a PersistedLogs that stores data in |local_state| under
+ // the preference |pref_name|. Calling code is responsible for ensuring that
+ // the lifetime of |local_state| is longer than the lifetime of PersistedLogs.
+ // When saving logs to disk, we will store either the first |min_log_count|
+ // logs, or at least |min_log_bytes| bytes of logs, whichever is more.
+ // If the optional max_log_size parameter is non-zero, all logs larger than
+ // that limit will be dropped before logs are written to disk.
+ PersistedLogs(PrefService* local_state,
+ const char* pref_name,
+ size_t min_log_count,
+ size_t min_log_bytes,
+ size_t max_log_size);
+ ~PersistedLogs();
+
+ // Write list to storage.
+ void SerializeLogs();
+
+ // Reads the list from the preference.
+ LogReadStatus DeserializeLogs();
+
+ // Adds a log to the list. |input| will be swapped with an empty string.
+ void StoreLog(std::string* input);
+
+ // Stages the most recent log. The staged_log will remain the same even if
+ // additional logs are added.
+ void StageLog();
+
+ // Remove the staged log.
+ void DiscardStagedLog();
+
+ // Saves the staged log, then clears staged_log().
+ // If |store_type| is PROVISIONAL_STORE, it can be dropped from storage with
+ // a later call to DiscardLastProvisionalStore (if it hasn't already been
+ // staged again).
+ // This is intended to be used when logs are being saved while an upload is in
+ // progress, in case the upload later succeeds.
+ // This can only be called if has_staged_log() is true.
+ void StoreStagedLogAsUnsent(StoreType store_type);
+
+ // Discards the last log stored with StoreStagedLogAsUnsent with |store_type|
+ // set to PROVISIONAL_STORE, as long as it hasn't already been re-staged. If
+ // the log is no longer present, this is a no-op.
+ void DiscardLastProvisionalStore();
+
+ // True if a log has been staged.
+ bool has_staged_log() const { return !staged_log_.log.empty(); };
+
+ // Returns the element in the front of the list.
+ const std::string& staged_log() const {
+ DCHECK(has_staged_log());
+ return staged_log_.log;
+ }
+
+ // Returns the element in the front of the list.
+ const std::string& staged_log_hash() const {
+ DCHECK(has_staged_log());
+ return staged_log_.hash;
+ }
+
+ // The number of elements currently stored.
+ size_t size() const { return list_.size(); }
+
+ // True if there are no stored logs.
+ bool empty() const { return list_.empty(); }
+
+ private:
+ // Writes the list to the ListValue.
+ void WriteLogsToPrefList(base::ListValue* list);
+
+ // Reads the list from the ListValue.
+ LogReadStatus ReadLogsFromPrefList(const base::ListValue& list);
+
+ // A weak pointer to the PrefService object to read and write the preference
+ // from. Calling code should ensure this object continues to exist for the
+ // lifetime of the PersistedLogs object.
+ PrefService* local_state_;
+
+ // The name of the preference this object stores logs in.
+ const char* pref_name_;
+
+ // We will keep at least this |min_log_count_| logs or |min_log_bytes_| bytes
+ // of logs, whichever is greater, when writing to disk. These apply after
+ // skipping logs greater than |max_log_size_|.
+ const size_t min_log_count_;
+ const size_t min_log_bytes_;
+
+ // Logs greater than this size will not be written to disk.
+ const size_t max_log_size_;
+
+ struct LogHashPair {
+ // Raw log text, typically a serialized protobuf.
+ std::string log;
+ // The SHA1 hash of log, stored to catch errors from memory corruption.
+ std::string hash;
+ // Swap the content of input into log and update the hash.
+ void SwapLog(std::string* input);
+ // Swap both log and hash from another LogHashPair.
+ void Swap(LogHashPair* input);
+ };
+ // A list of all of the stored logs, stored with SHA1 hashes to check for
+ // corruption while they are stored in memory.
+ std::vector<LogHashPair> list_;
+
+ // The log staged for upload.
+ LogHashPair staged_log_;
+
+ // The index and type of the last provisional store. If nothing has been
+ // provisionally stored, or the last provisional store has already been
+ // re-staged, the index will be -1;
+ // This is necessary because during an upload there are two logs (staged
+ // and current) and a client might store them in either order, so it's
+ // not necessarily the case that the provisional store is the last store.
+ int last_provisional_store_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(PersistedLogs);
+};
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_PERSISTED_LOGS_H_
diff --git a/components/metrics/persisted_logs_unittest.cc b/components/metrics/persisted_logs_unittest.cc
new file mode 100644
index 0000000..97e9f41
--- /dev/null
+++ b/components/metrics/persisted_logs_unittest.cc
@@ -0,0 +1,415 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/base64.h"
+#include "base/md5.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/scoped_user_pref_update.h"
+#include "base/prefs/testing_pref_service.h"
+#include "base/sha1.h"
+#include "base/values.h"
+#include "components/metrics/persisted_logs.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+namespace {
+
+const char kTestPrefName[] = "TestPref";
+const size_t kLogCountLimit = 3;
+const size_t kLogByteLimit = 1000;
+
+
+class PersistedLogsTest : public testing::Test {
+ public:
+ PersistedLogsTest() {
+ prefs_.registry()->RegisterListPref(kTestPrefName);
+ }
+
+ protected:
+ TestingPrefServiceSimple prefs_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PersistedLogsTest);
+};
+
+class TestPersistedLogs : public PersistedLogs {
+ public:
+ TestPersistedLogs(PrefService* service)
+ : PersistedLogs(service, kTestPrefName, kLogCountLimit, kLogByteLimit, 0) {
+ }
+
+ // Make a copy of the string and store the copy.
+ void StoreLogCopy(std::string tmp) {
+ StoreLog(&tmp);
+ }
+
+ // Stages and removes the next log, while testing it's value.
+ void ExpectNextLog(const std::string& expected_log) {
+ StageLog();
+ EXPECT_EQ(staged_log(), expected_log);
+ DiscardStagedLog();
+ }
+};
+
+} // namespace
+
+// Store and retrieve empty list_value.
+TEST_F(PersistedLogsTest, EmptyLogList) {
+ TestPersistedLogs persisted_logs(&prefs_);
+
+ persisted_logs.SerializeLogs();
+ const base::ListValue* list_value = prefs_.GetList(kTestPrefName);
+ EXPECT_EQ(0U, list_value->GetSize());
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::LIST_EMPTY, result_persisted_logs.DeserializeLogs());
+ EXPECT_EQ(0U, result_persisted_logs.size());
+}
+
+// Store and retrieve a single log value.
+TEST_F(PersistedLogsTest, SingleElementLogList) {
+ TestPersistedLogs persisted_logs(&prefs_);
+
+ persisted_logs.StoreLogCopy("Hello world!");
+ persisted_logs.SerializeLogs();
+
+ const base::ListValue* list_value = prefs_.GetList(kTestPrefName);
+ // |list_value| will now contain the following:
+ // [1, Base64Encode("Hello world!"), MD5("Hello world!")].
+ ASSERT_EQ(3U, list_value->GetSize());
+
+ // Examine each element.
+ base::ListValue::const_iterator it = list_value->begin();
+ int size = 0;
+ (*it)->GetAsInteger(&size);
+ EXPECT_EQ(1, size);
+
+ ++it;
+ std::string str;
+ (*it)->GetAsString(&str); // Base64 encoded "Hello world!" string.
+ std::string encoded;
+ base::Base64Encode("Hello world!", &encoded);
+ EXPECT_TRUE(encoded == str);
+
+ ++it;
+ (*it)->GetAsString(&str); // MD5 for encoded "Hello world!" string.
+ EXPECT_TRUE(base::MD5String(encoded) == str);
+
+ ++it;
+ EXPECT_TRUE(it == list_value->end()); // Reached end of list_value.
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
+ result_persisted_logs.DeserializeLogs());
+ EXPECT_EQ(1U, result_persisted_logs.size());
+}
+
+// Store a set of logs over the length limit, but smaller than the min number of
+// bytes.
+TEST_F(PersistedLogsTest, LongButTinyLogList) {
+ TestPersistedLogs persisted_logs(&prefs_);
+
+ size_t log_count = kLogCountLimit * 5;
+ for (size_t i = 0; i < log_count; ++i)
+ persisted_logs.StoreLogCopy("x");
+
+ persisted_logs.SerializeLogs();
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
+ result_persisted_logs.DeserializeLogs());
+ EXPECT_EQ(persisted_logs.size(), result_persisted_logs.size());
+
+ result_persisted_logs.ExpectNextLog("x");
+}
+
+// Store a set of logs over the length limit, but that doesn't reach the minimum
+// number of bytes until after passing the length limit.
+TEST_F(PersistedLogsTest, LongButSmallLogList) {
+ TestPersistedLogs persisted_logs(&prefs_);
+
+ // Make log_count logs each slightly larger than
+ // kLogByteLimit / (log_count - 2)
+ // so that the minimum is reached before the oldest (first) two logs.
+ size_t log_count = kLogCountLimit * 5;
+ size_t log_size = (kLogByteLimit / (log_count - 2)) + 2;
+ persisted_logs.StoreLogCopy("one");
+ persisted_logs.StoreLogCopy("two");
+ std::string first_kept = "First to keep";
+ first_kept.resize(log_size, ' ');
+ persisted_logs.StoreLogCopy(first_kept);
+ std::string blank_log = std::string(log_size, ' ');
+ for (size_t i = persisted_logs.size(); i < log_count - 1; ++i) {
+ persisted_logs.StoreLogCopy(blank_log);
+ }
+ std::string last_kept = "Last to keep";
+ last_kept.resize(log_size, ' ');
+ persisted_logs.StoreLogCopy(last_kept);
+
+ persisted_logs.SerializeLogs();
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
+ result_persisted_logs.DeserializeLogs());
+ EXPECT_EQ(persisted_logs.size() - 2, result_persisted_logs.size());
+
+ result_persisted_logs.ExpectNextLog(last_kept);
+ while (result_persisted_logs.size() > 1) {
+ result_persisted_logs.ExpectNextLog(blank_log);
+ }
+ result_persisted_logs.ExpectNextLog(first_kept);
+}
+
+// Store a set of logs within the length limit, but well over the minimum
+// number of bytes.
+TEST_F(PersistedLogsTest, ShortButLargeLogList) {
+ TestPersistedLogs persisted_logs(&prefs_);
+
+ // Make the total byte count about twice the minimum.
+ size_t log_count = kLogCountLimit;
+ size_t log_size = (kLogByteLimit / log_count) * 2;
+ std::string blank_log = std::string(log_size, ' ');
+ for (size_t i = 0; i < log_count; ++i) {
+ persisted_logs.StoreLogCopy(blank_log);
+ }
+
+ persisted_logs.SerializeLogs();
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
+ result_persisted_logs.DeserializeLogs());
+ EXPECT_EQ(persisted_logs.size(), result_persisted_logs.size());
+}
+
+// Store a set of logs over the length limit, and over the minimum number of
+// bytes.
+TEST_F(PersistedLogsTest, LongAndLargeLogList) {
+ TestPersistedLogs persisted_logs(&prefs_);
+
+ // Include twice the max number of logs.
+ size_t log_count = kLogCountLimit * 2;
+ // Make the total byte count about four times the minimum.
+ size_t log_size = (kLogByteLimit / log_count) * 4;
+ std::string target_log = "First to keep";
+ target_log.resize(log_size, ' ');
+ std::string blank_log = std::string(log_size, ' ');
+ for (size_t i = 0; i < log_count; ++i) {
+ if (i == log_count - kLogCountLimit)
+ persisted_logs.StoreLogCopy(target_log);
+ else
+ persisted_logs.StoreLogCopy(blank_log);
+ }
+
+ persisted_logs.SerializeLogs();
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
+ result_persisted_logs.DeserializeLogs());
+ EXPECT_EQ(kLogCountLimit, result_persisted_logs.size());
+
+ while (result_persisted_logs.size() > 1) {
+ result_persisted_logs.ExpectNextLog(blank_log);
+ }
+ result_persisted_logs.ExpectNextLog(target_log);
+}
+
+// Induce LIST_SIZE_TOO_SMALL corruption
+TEST_F(PersistedLogsTest, SmallRecoveredListSize) {
+ TestPersistedLogs persisted_logs(&prefs_);
+
+ persisted_logs.StoreLogCopy("Hello world!");
+
+ persisted_logs.SerializeLogs();
+
+ {
+ ListPrefUpdate update(&prefs_, kTestPrefName);
+ base::ListValue* list_value = update.Get();
+ EXPECT_EQ(3U, list_value->GetSize());
+
+ // Remove last element.
+ list_value->Remove(list_value->GetSize() - 1, NULL);
+ EXPECT_EQ(2U, list_value->GetSize());
+ }
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::LIST_SIZE_TOO_SMALL,
+ result_persisted_logs.DeserializeLogs());
+}
+
+// Remove size from the stored list_value.
+TEST_F(PersistedLogsTest, RemoveSizeFromLogList) {
+ TestPersistedLogs persisted_logs(&prefs_);
+
+ persisted_logs.StoreLogCopy("one");
+ persisted_logs.StoreLogCopy("two");
+ EXPECT_EQ(2U, persisted_logs.size());
+
+ persisted_logs.SerializeLogs();
+
+ {
+ ListPrefUpdate update(&prefs_, kTestPrefName);
+ base::ListValue* list_value = update.Get();
+ EXPECT_EQ(4U, list_value->GetSize());
+
+ list_value->Remove(0, NULL); // Delete size (1st element).
+ EXPECT_EQ(3U, list_value->GetSize());
+ }
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::LIST_SIZE_MISSING,
+ result_persisted_logs.DeserializeLogs());
+}
+
+// Corrupt size of stored list_value.
+TEST_F(PersistedLogsTest, CorruptSizeOfLogList) {
+ TestPersistedLogs persisted_logs(&prefs_);
+
+ persisted_logs.StoreLogCopy("Hello world!");
+
+ persisted_logs.SerializeLogs();
+
+ {
+ ListPrefUpdate update(&prefs_, kTestPrefName);
+ base::ListValue* list_value = update.Get();
+ EXPECT_EQ(3U, list_value->GetSize());
+
+ // Change list_value size from 1 to 2.
+ EXPECT_TRUE(list_value->Set(0, base::Value::CreateIntegerValue(2)));
+ EXPECT_EQ(3U, list_value->GetSize());
+ }
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::LIST_SIZE_CORRUPTION,
+ result_persisted_logs.DeserializeLogs());
+}
+
+// Corrupt checksum of stored list_value.
+TEST_F(PersistedLogsTest, CorruptChecksumOfLogList) {
+ TestPersistedLogs persisted_logs(&prefs_);
+
+ persisted_logs.StoreLogCopy("Hello world!");
+
+ persisted_logs.SerializeLogs();
+
+ {
+ ListPrefUpdate update(&prefs_, kTestPrefName);
+ base::ListValue* list_value = update.Get();
+ EXPECT_EQ(3U, list_value->GetSize());
+
+ // Fetch checksum (last element) and change it.
+ std::string checksum;
+ EXPECT_TRUE((*(list_value->end() - 1))->GetAsString(&checksum));
+ checksum[0] = (checksum[0] == 'a') ? 'b' : 'a';
+ EXPECT_TRUE(list_value->Set(2, base::Value::CreateStringValue(checksum)));
+ EXPECT_EQ(3U, list_value->GetSize());
+ }
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::CHECKSUM_CORRUPTION,
+ result_persisted_logs.DeserializeLogs());
+}
+
+// Check that the store/stage/discard functions work as expected.
+TEST_F(PersistedLogsTest, Staging) {
+ TestPersistedLogs persisted_logs(&prefs_);
+ std::string tmp;
+
+ EXPECT_FALSE(persisted_logs.has_staged_log());
+ persisted_logs.StoreLogCopy("one");
+ EXPECT_FALSE(persisted_logs.has_staged_log());
+ persisted_logs.StoreLogCopy("two");
+ persisted_logs.StageLog();
+ EXPECT_TRUE(persisted_logs.has_staged_log());
+ EXPECT_EQ(persisted_logs.staged_log(), std::string("two"));
+ persisted_logs.StoreLogCopy("three");
+ EXPECT_EQ(persisted_logs.staged_log(), std::string("two"));
+ EXPECT_EQ(persisted_logs.size(), 2U);
+ persisted_logs.DiscardStagedLog();
+ EXPECT_FALSE(persisted_logs.has_staged_log());
+ EXPECT_EQ(persisted_logs.size(), 2U);
+ persisted_logs.StageLog();
+ EXPECT_EQ(persisted_logs.staged_log(), std::string("three"));
+ persisted_logs.DiscardStagedLog();
+ persisted_logs.StageLog();
+ EXPECT_EQ(persisted_logs.staged_log(), std::string("one"));
+ persisted_logs.DiscardStagedLog();
+ EXPECT_FALSE(persisted_logs.has_staged_log());
+ EXPECT_EQ(persisted_logs.size(), 0U);
+}
+
+TEST_F(PersistedLogsTest, ProvisionalStoreStandardFlow) {
+ // Ensure that provisional store works, and discards the correct log.
+ TestPersistedLogs persisted_logs(&prefs_);
+
+ persisted_logs.StoreLogCopy("one");
+ persisted_logs.StageLog();
+ persisted_logs.StoreStagedLogAsUnsent(PersistedLogs::PROVISIONAL_STORE);
+ persisted_logs.StoreLogCopy("two");
+ persisted_logs.DiscardLastProvisionalStore();
+ persisted_logs.SerializeLogs();
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
+ result_persisted_logs.DeserializeLogs());
+ EXPECT_EQ(1U, result_persisted_logs.size());
+ result_persisted_logs.ExpectNextLog("two");
+}
+
+TEST_F(PersistedLogsTest, ProvisionalStoreNoop1) {
+ // Ensure that trying to drop a sent log is a no-op, even if another log has
+ // since been staged.
+ TestPersistedLogs persisted_logs(&prefs_);
+ persisted_logs.DeserializeLogs();
+ persisted_logs.StoreLogCopy("one");
+ persisted_logs.StageLog();
+ persisted_logs.StoreStagedLogAsUnsent(PersistedLogs::PROVISIONAL_STORE);
+ persisted_logs.StageLog();
+ persisted_logs.DiscardStagedLog();
+ persisted_logs.StoreLogCopy("two");
+ persisted_logs.StageLog();
+ persisted_logs.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
+ persisted_logs.DiscardLastProvisionalStore();
+ persisted_logs.SerializeLogs();
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
+ result_persisted_logs.DeserializeLogs());
+ EXPECT_EQ(1U, result_persisted_logs.size());
+ result_persisted_logs.ExpectNextLog("two");
+}
+
+TEST_F(PersistedLogsTest, ProvisionalStoreNoop2) {
+ // Ensure that trying to drop more than once is a no-op
+ TestPersistedLogs persisted_logs(&prefs_);
+ persisted_logs.DeserializeLogs();
+ persisted_logs.StoreLogCopy("one");
+ persisted_logs.StageLog();
+ persisted_logs.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
+ persisted_logs.StoreLogCopy("two");
+ persisted_logs.StageLog();
+ persisted_logs.StoreStagedLogAsUnsent(PersistedLogs::PROVISIONAL_STORE);
+ persisted_logs.DiscardLastProvisionalStore();
+ persisted_logs.DiscardLastProvisionalStore();
+ persisted_logs.SerializeLogs();
+
+ TestPersistedLogs result_persisted_logs(&prefs_);
+ EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
+ result_persisted_logs.DeserializeLogs());
+ EXPECT_EQ(1U, result_persisted_logs.size());
+ result_persisted_logs.ExpectNextLog("one");
+}
+
+TEST_F(PersistedLogsTest, Hashes) {
+ const char kFooText[] = "foo";
+ const std::string foo_hash = base::SHA1HashString(kFooText);
+ TestingPrefServiceSimple prefs_;
+ prefs_.registry()->RegisterListPref(kTestPrefName);
+ TestPersistedLogs persisted_logs(&prefs_);
+ persisted_logs.StoreLogCopy(kFooText);
+ persisted_logs.StageLog();
+ EXPECT_EQ(foo_hash, persisted_logs.staged_log_hash());
+}
+
+} // namespace metrics