summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/metrics/metrics_log_serializer.cc198
-rw-r--r--chrome/browser/metrics/metrics_log_serializer.h69
-rw-r--r--chrome/browser/metrics/metrics_log_serializer_unittest.cc192
-rw-r--r--chrome/browser/metrics/metrics_service.cc399
-rw-r--r--chrome/browser/metrics/metrics_service.h92
-rw-r--r--chrome/browser/metrics/metrics_service_unittest.cc173
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_common.gypi2
-rw-r--r--chrome/chrome_tests.gypi2
-rw-r--r--chrome/common/metrics_helpers.cc71
-rw-r--r--chrome/common/metrics_helpers.h26
-rw-r--r--chrome/common/metrics_log_manager.cc162
-rw-r--r--chrome/common/metrics_log_manager.h154
-rw-r--r--chrome/common/metrics_log_manager_unittest.cc211
-rw-r--r--chrome_frame/metrics_service.cc60
-rw-r--r--chrome_frame/metrics_service.h3
16 files changed, 1118 insertions, 698 deletions
diff --git a/chrome/browser/metrics/metrics_log_serializer.cc b/chrome/browser/metrics/metrics_log_serializer.cc
new file mode 100644
index 0000000..bbddf60
--- /dev/null
+++ b/chrome/browser/metrics/metrics_log_serializer.cc
@@ -0,0 +1,198 @@
+// Copyright (c) 2011 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 "base/base64.h"
+#include "base/md5.h"
+#include "base/metrics/histogram.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/common/pref_names.h"
+
+// The number of "initial" logs we're willing to save, and hope to send during
+// a future Chrome session. Initial logs contain crash stats, and are pretty
+// small.
+static const size_t kMaxInitialLogsPersisted = 20;
+
+// The number of ongoing logs we're willing 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).
+static const size_t kMaxOngoingLogsPersisted = 8;
+
+// We append (2) more elements to persisted lists: the size of the list and a
+// checksum of the elements.
+static const size_t kChecksumEntryCount = 2;
+
+namespace {
+// TODO(ziadh): This is here temporarily for a side experiment. Remove later
+// on.
+enum LogStoreStatus {
+ STORE_SUCCESS, // Successfully presisted log.
+ ENCODE_FAIL, // Failed to encode log.
+ COMPRESS_FAIL, // Failed to compress log.
+ END_STORE_STATUS // Number of bins to use to create the histogram.
+};
+
+MetricsLogSerializer::LogReadStatus MakeRecallStatusHistogram(
+ MetricsLogSerializer::LogReadStatus status) {
+ UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecall", status,
+ MetricsLogSerializer::END_RECALL_STATUS);
+ return status;
+}
+
+// TODO(ziadh): Remove this when done with experiment.
+void MakeStoreStatusHistogram(LogStoreStatus status) {
+ UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogStore2", status,
+ END_STORE_STATUS);
+}
+} // namespace
+
+
+MetricsLogSerializer::MetricsLogSerializer() {}
+
+MetricsLogSerializer::~MetricsLogSerializer() {}
+
+void MetricsLogSerializer::SerializeLogs(const std::vector<std::string>& logs,
+ MetricsLogManager::LogType log_type) {
+ PrefService* local_state = g_browser_process->local_state();
+ DCHECK(local_state);
+ const char* pref = NULL;
+ size_t max_store_count = 0;
+ switch (log_type) {
+ case MetricsLogManager::INITIAL_LOG:
+ pref = prefs::kMetricsInitialLogs;
+ max_store_count = kMaxInitialLogsPersisted;
+ break;
+ case MetricsLogManager::ONGOING_LOG:
+ pref = prefs::kMetricsOngoingLogs;
+ max_store_count = kMaxOngoingLogsPersisted;
+ break;
+ default:
+ NOTREACHED();
+ return;
+ };
+ ListPrefUpdate update(local_state, pref);
+ ListValue* pref_list = update.Get();
+ WriteLogsToPrefList(logs, max_store_count, pref_list);
+}
+
+void MetricsLogSerializer::DeserializeLogs(MetricsLogManager::LogType log_type,
+ std::vector<std::string>* logs) {
+ DCHECK(logs);
+ PrefService* local_state = g_browser_process->local_state();
+ DCHECK(local_state);
+
+ const char* pref = (log_type == MetricsLogManager::INITIAL_LOG) ?
+ prefs::kMetricsInitialLogs : prefs::kMetricsOngoingLogs;
+ const ListValue* unsent_logs = local_state->GetList(pref);
+ ReadLogsFromPrefList(*unsent_logs, logs);
+}
+
+// static
+void MetricsLogSerializer::WriteLogsToPrefList(
+ const std::vector<std::string>& local_list,
+ const size_t kMaxLocalListSize,
+ ListValue* list) {
+ list->Clear();
+ size_t start = 0;
+ if (local_list.size() > kMaxLocalListSize)
+ start = local_list.size() - kMaxLocalListSize;
+ DCHECK(start <= local_list.size());
+ if (local_list.size() <= start)
+ return;
+
+ // Store size at the beginning of the list.
+ list->Append(Value::CreateIntegerValue(local_list.size() - start));
+
+ base::MD5Context ctx;
+ base::MD5Init(&ctx);
+ std::string encoded_log;
+ for (std::vector<std::string>::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.
+ if (!base::Base64Encode(*it, &encoded_log)) {
+ MakeStoreStatusHistogram(ENCODE_FAIL);
+ list->Clear();
+ return;
+ }
+ base::MD5Update(&ctx, encoded_log);
+ list->Append(Value::CreateStringValue(encoded_log));
+ }
+
+ // Append hash to the end of the list.
+ base::MD5Digest digest;
+ base::MD5Final(&digest, &ctx);
+ list->Append(Value::CreateStringValue(base::MD5DigestToBase16(digest)));
+ DCHECK(list->GetSize() >= 3); // Minimum of 3 elements (size, data, hash).
+ MakeStoreStatusHistogram(STORE_SUCCESS);
+}
+
+// static
+MetricsLogSerializer::LogReadStatus MetricsLogSerializer::ReadLogsFromPrefList(
+ const ListValue& list,
+ std::vector<std::string>* local_list) {
+ DCHECK(local_list->empty());
+ 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);
+ }
+
+ base::MD5Context ctx;
+ base::MD5Init(&ctx);
+ std::string encoded_log;
+ std::string decoded_log;
+ for (ListValue::const_iterator it = list.begin() + 1;
+ it != list.end() - 1; ++it) { // Last element is the checksum.
+ valid = (*it)->GetAsString(&encoded_log);
+ if (!valid) {
+ local_list->clear();
+ return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION);
+ }
+
+ base::MD5Update(&ctx, encoded_log);
+
+ if (!base::Base64Decode(encoded_log, &decoded_log)) {
+ local_list->clear();
+ return MakeRecallStatusHistogram(DECODE_FAIL);
+ }
+ local_list->push_back(decoded_log);
+ }
+
+ // 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
new file mode 100644
index 0000000..d698164
--- /dev/null
+++ b/chrome/browser/metrics/metrics_log_serializer.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "chrome/common/metrics_log_manager.h"
+
+namespace base {
+class ListValue;
+}
+
+// Serializer for persisting metrics logs to prefs.
+class MetricsLogSerializer : public MetricsLogManager::LogSerializer {
+ public:
+ // Used to produce a historgram 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.
+ END_RECALL_STATUS // Number of bins to use to create the histogram.
+ };
+
+ MetricsLogSerializer();
+ virtual ~MetricsLogSerializer();
+
+ // Implementation of MetricsLogManager::LogSerializer
+ virtual void SerializeLogs(const std::vector<std::string>& logs,
+ MetricsLogManager::LogType log_type);
+ virtual void DeserializeLogs(MetricsLogManager::LogType log_type,
+ std::vector<std::string>* logs);
+
+ private:
+ // Encodes the textual log data from |local_list| and writes it to the given
+ // pref list, along with list size and checksum.
+ static void WriteLogsToPrefList(const std::vector<std::string>& local_list,
+ const size_t kMaxLocalListSize,
+ 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<std::string>* local_list);
+
+ FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, EmptyLogList);
+ FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, SingleElementLogList);
+ FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, OverLimitLogList);
+ 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
new file mode 100644
index 0000000..59c257b
--- /dev/null
+++ b/chrome/browser/metrics/metrics_log_serializer_unittest.cc
@@ -0,0 +1,192 @@
+// Copyright (c) 2011 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"
+
+namespace {
+ class MetricsLogSerializerTest : public ::testing::Test {
+ };
+}
+
+static const size_t kMaxLocalListSize = 3;
+
+// Store and retrieve empty list.
+TEST(MetricsLogSerializerTest, EmptyLogList) {
+ ListValue list;
+ std::vector<std::string> local_list;
+
+ MetricsLogSerializer::WriteLogsToPrefList(local_list, kMaxLocalListSize,
+ &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) {
+ ListValue list;
+ std::vector<std::string> local_list;
+
+ local_list.push_back("Hello world!");
+ EXPECT_EQ(1U, local_list.size());
+
+ MetricsLogSerializer::WriteLogsToPrefList(local_list, kMaxLocalListSize,
+ &list);
+
+ // |list| will now contain the following:
+ // [1, Base64Encode("Hello world!"), MD5("Hello world!")].
+ EXPECT_EQ(3U, list.GetSize());
+
+ // Examine each element.
+ 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 elements greater than the limit.
+TEST(MetricsLogSerializerTest, OverLimitLogList) {
+ ListValue list;
+ std::vector<std::string> local_list;
+
+ local_list.push_back("one");
+ local_list.push_back("two");
+ local_list.push_back("three");
+ local_list.push_back("four");
+ EXPECT_EQ(4U, local_list.size());
+
+ std::string expected_first;
+ base::Base64Encode(local_list[local_list.size() - kMaxLocalListSize],
+ &expected_first);
+ std::string expected_last;
+ base::Base64Encode(local_list[local_list.size() - 1],
+ &expected_last);
+
+ MetricsLogSerializer::WriteLogsToPrefList(local_list, kMaxLocalListSize,
+ &list);
+ EXPECT_EQ(kMaxLocalListSize + 2, list.GetSize());
+
+ std::string actual_first;
+ EXPECT_TRUE((*(list.begin() + 1))->GetAsString(&actual_first));
+ EXPECT_TRUE(expected_first == actual_first);
+
+ std::string actual_last;
+ EXPECT_TRUE((*(list.end() - 2))->GetAsString(&actual_last));
+ EXPECT_TRUE(expected_last == actual_last);
+
+ local_list.clear();
+ EXPECT_EQ(MetricsLogSerializer::RECALL_SUCCESS,
+ MetricsLogSerializer::ReadLogsFromPrefList(list, &local_list));
+ EXPECT_EQ(kMaxLocalListSize, local_list.size());
+}
+
+// Induce LIST_SIZE_TOO_SMALL corruption
+TEST(MetricsLogSerializerTest, SmallRecoveredListSize) {
+ ListValue list;
+ std::vector<std::string> local_list;
+
+ local_list.push_back("Hello world!");
+ EXPECT_EQ(1U, local_list.size());
+ MetricsLogSerializer::WriteLogsToPrefList(local_list, kMaxLocalListSize,
+ &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) {
+ ListValue list;
+ std::vector<std::string> local_list;
+
+ local_list.push_back("one");
+ local_list.push_back("two");
+ EXPECT_EQ(2U, local_list.size());
+ MetricsLogSerializer::WriteLogsToPrefList(local_list, kMaxLocalListSize,
+ &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) {
+ ListValue list;
+ std::vector<std::string> local_list;
+
+ local_list.push_back("Hello world!");
+ EXPECT_EQ(1U, local_list.size());
+ MetricsLogSerializer::WriteLogsToPrefList(local_list, kMaxLocalListSize,
+ &list);
+ EXPECT_EQ(3U, list.GetSize());
+
+ // Change list size from 1 to 2.
+ EXPECT_TRUE(list.Set(0, 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) {
+ ListValue list;
+ std::vector<std::string> local_list;
+
+ local_list.clear();
+ local_list.push_back("Hello world!");
+ EXPECT_EQ(1U, local_list.size());
+ MetricsLogSerializer::WriteLogsToPrefList(local_list, kMaxLocalListSize,
+ &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, 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 4efdc8c..2072626 100644
--- a/chrome/browser/metrics/metrics_service.cc
+++ b/chrome/browser/metrics/metrics_service.cc
@@ -49,10 +49,7 @@
//
// When the browser shuts down, there will typically be a fragment of an ongoing
// log that has not yet been transmitted. At shutdown time, that fragment
-// is closed (including snapshotting histograms), and converted to text. Note
-// that memory stats are not gathered during shutdown, as gathering *might* be
-// too time consuming. The textual representation of the fragment of the
-// ongoing log is then stored persistently as a string in the PrefServices, for
+// is closed (including snapshotting histograms), and persisted, for
// potential transmission during a future run of the product.
//
// There are two slightly abnormal shutdown conditions. There is a
@@ -82,7 +79,6 @@
// INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to complete.
// INIT_TASK_DONE, // Waiting for timer to send initial log.
// INITIAL_LOG_READY, // Initial log generated, and waiting for reply.
-// SEND_OLD_INITIAL_LOGS, // Sending unsent logs from previous session.
// SENDING_OLD_LOGS, // Sending unsent logs from previous session.
// SENDING_CURRENT_LOGS, // Sending standard current logs as they accrue.
//
@@ -109,17 +105,12 @@
// prepared for transmission. It is also the case that any previously unsent
// logs have been loaded into instance variables for possible transmission.
//
-// SEND_OLD_INITIAL_LOGS, // Sending unsent logs from previous session.
-// This state indicates that the initial log for this session has been
-// successfully sent and it is now time to send any "initial logs" that were
-// saved from previous sessions. Most commonly, there are none, but all old
-// logs that were "initial logs" must be sent before this state is exited.
-//
// SENDING_OLD_LOGS, // Sending unsent logs from previous session.
-// This state indicates that there are no more unsent initial logs, and now any
-// ongoing logs from previous sessions should be transmitted. All such logs
-// will be transmitted before exiting this state, and proceeding with ongoing
-// logs from the current session (see next state).
+// This state indicates that the initial log for this session has been
+// successfully sent and it is now time to send any logs that were
+// saved from previous sessions. All such logs will be transmitted before
+// exiting this state, and proceeding with ongoing logs from the current session
+// (see next state).
//
// SENDING_CURRENT_LOGS, // Sending standard current logs as they accrue.
// Current logs are being accumulated. Typically every 20 minutes a log is
@@ -131,19 +122,16 @@
// and remain in the latter until shutdown.
//
// The one unusual case is when the user asks that we stop logging. When that
-// happens, any pending (transmission in progress) log is pushed into the list
-// of old unsent logs (the appropriate list, depending on whether it is an
-// initial log, or an ongoing log). An addition, any log that is currently
-// accumulating is also finalized, and pushed into the unsent log list. With
-// those pushes performed, we regress back to the SEND_OLD_INITIAL_LOGS state in
-// case the user enables log recording again during this session. This way
-// anything we have "pushed back" will be sent automatically if/when we progress
-// back to SENDING_CURRENT_LOG state.
+// happens, any staged (transmission in progress) log is persisted, and any log
+// log that is currently accumulating is also finalized and persisted. We then
+// regress back to the SEND_OLD_LOGS state in case the user enables log
+// recording again during this session. This way anything we have persisted
+// will be sent automatically if/when we progress back to SENDING_CURRENT_LOG
+// state.
//
-// Also note that whenever the member variables containing unsent logs are
-// modified (i.e., when we send an old log), we mirror the list of logs into
-// the PrefServices. This ensures that IF we crash, we won't start up and
-// retransmit our old logs again.
+// Also note that whenever we successfully send an old log, we mirror the list
+// of logs into the PrefService. This ensures that IF we crash, we won't start
+// up and retransmit our old logs again.
//
// Due to race conditions, it is always possible that a log file could be sent
// twice. For example, if a log file is sent, but not yet acknowledged by
@@ -156,14 +144,12 @@
#include "chrome/browser/metrics/metrics_service.h"
-#include "base/base64.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/md5.h"
#include "base/metrics/histogram.h"
#include "base/string_number_conversions.h"
-#include "base/string_util.h" // For WriteInto().
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "base/utf_string_conversions.h"
@@ -173,6 +159,7 @@
#include "chrome/browser/memory_details.h"
#include "chrome/browser/metrics/histogram_synchronizer.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/net/network_stats.h"
#include "chrome/browser/prefs/pref_service.h"
@@ -184,6 +171,7 @@
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/guid.h"
+#include "chrome/common/metrics_log_manager.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "content/browser/load_notification_details.h"
@@ -205,21 +193,6 @@
#include "chrome/browser/chromeos/system/statistics_provider.h"
#endif
-namespace {
-MetricsService::LogRecallStatus MakeRecallStatusHistogram(
- MetricsService::LogRecallStatus status) {
- UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecall", status,
- MetricsService::END_RECALL_STATUS);
- return status;
-}
-
-// TODO(ziadh): Remove this when done with experiment.
-void MakeStoreStatusHistogram(MetricsService::LogStoreStatus status) {
- UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogStore2", status,
- MetricsService::END_STORE_STATUS);
-}
-} // namespace
-
using base::Time;
// Check to see that we're being called on only one thread.
@@ -247,26 +220,6 @@ static const int kUploadLogAvoidRetransmitSize = 50000;
// Interval, in seconds, between state saves.
static const int kSaveStateInterval = 5 * 60; // five minutes
-// The number of "initial" logs we're willing to save, and hope to send during
-// a future Chrome session. Initial logs contain crash stats, and are pretty
-// small.
-static const size_t kMaxInitialLogsPersisted = 20;
-
-// The number of ongoing logs we're willing 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 (pending_), 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).
-static const size_t kMaxOngoingLogsPersisted = 8;
-
-// We append (2) more elements to persisted lists: the size of the list and a
-// checksum of the elements.
-static const size_t kChecksumEntryCount = 2;
-
// static
MetricsService::ShutdownCleanliness MetricsService::clean_shutdown_status_ =
MetricsService::CLEANLY_SHUTDOWN;
@@ -419,6 +372,8 @@ MetricsService::MetricsService()
base::Closure callback = base::Bind(&MetricsService::StartScheduledUpload,
base::Unretained(this));
scheduler_.reset(new MetricsReportingScheduler(callback));
+ log_manager_.set_log_serializer(new MetricsLogSerializer());
+ log_manager_.set_max_ongoing_log_store_size(kUploadLogAvoidRetransmitSize);
}
MetricsService::~MetricsService() {
@@ -472,10 +427,10 @@ void MetricsService::SetRecording(bool enabled) {
SetUpNotifications(&registrar_, this);
} else {
registrar_.RemoveAll();
- PushPendingLogsToUnsentLists();
- DCHECK(!pending_log());
- if (state_ > INITIAL_LOG_READY && unsent_logs())
- state_ = SEND_OLD_INITIAL_LOGS;
+ PushPendingLogsToPersistentStorage();
+ DCHECK(!log_manager_.has_staged_log());
+ if (state_ > INITIAL_LOG_READY && log_manager_.has_unsent_logs())
+ state_ = SENDING_OLD_LOGS;
}
recording_active_ = enabled;
}
@@ -536,7 +491,7 @@ void MetricsService::SetUpNotifications(NotificationRegistrar* registrar,
void MetricsService::Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) {
- DCHECK(current_log_);
+ DCHECK(log_manager_.current_log());
DCHECK(IsSingleThreaded());
if (!CanLogNotification(type, source, details))
@@ -544,7 +499,8 @@ void MetricsService::Observe(int type,
switch (type) {
case content::NOTIFICATION_USER_ACTION:
- current_log_->RecordUserAction(*Details<const char*>(details).ptr());
+ log_manager_.current_log()->RecordUserAction(
+ *Details<const char*>(details).ptr());
break;
case chrome::NOTIFICATION_BROWSER_OPENED:
@@ -596,7 +552,7 @@ void MetricsService::Observe(int type,
break;
case chrome::NOTIFICATION_OMNIBOX_OPENED_URL: {
- MetricsLog* current_log = current_log_->AsMetricsLog();
+ MetricsLog* current_log = log_manager_.current_log()->AsMetricsLog();
DCHECK(current_log);
current_log->RecordOmniboxOpenedURL(
*Details<AutocompleteLog>(details).ptr());
@@ -616,8 +572,9 @@ void MetricsService::Observe(int type,
HandleIdleSinceLastTransmission(false);
- if (current_log_)
- DVLOG(1) << "METRICS: NUMBER OF EVENTS = " << current_log_->num_events();
+ if (log_manager_.current_log())
+ DVLOG(1) << "METRICS: NUMBER OF EVENTS = "
+ << log_manager_.current_log()->num_events();
}
void MetricsService::HandleIdleSinceLastTransmission(bool in_idle) {
@@ -830,10 +787,10 @@ void MetricsService::SaveLocalState() {
// Recording control methods
void MetricsService::StartRecording() {
- if (current_log_)
+ if (log_manager_.current_log())
return;
- current_log_ = new MetricsLog(client_id_, session_id_);
+ log_manager_.BeginLoggingWithLog(new MetricsLog(client_id_, session_id_));
if (state_ == INITIALIZED) {
// We only need to schedule that run once.
state_ = INIT_TASK_SCHEDULED;
@@ -851,71 +808,54 @@ void MetricsService::StartRecording() {
}
void MetricsService::StopRecording() {
- if (!current_log_)
+ if (!log_manager_.current_log())
return;
// TODO(jar): Integrate bounds on log recording more consistently, so that we
// can stop recording logs that are too big much sooner.
- if (current_log_->num_events() >= kEventLimit) {
+ if (log_manager_.current_log()->num_events() > kEventLimit) {
UMA_HISTOGRAM_COUNTS("UMA.Discarded Log Events",
- current_log_->num_events());
- current_log_->CloseLog();
- delete current_log_;
- current_log_ = NULL;
+ log_manager_.current_log()->num_events());
+ log_manager_.DiscardCurrentLog();
StartRecording(); // Start trivial log to hold our histograms.
}
- current_log_->set_hardware_class(hardware_class_); // Adds to ongoing logs.
+ // Adds to ongoing logs.
+ log_manager_.current_log()->set_hardware_class(hardware_class_);
// Put incremental data (histogram deltas, and realtime stats deltas) at the
// end of all log transmissions (initial log handles this separately).
// RecordIncrementalStabilityElements only exists on the derived
// MetricsLog class.
- MetricsLog* current_log = current_log_->AsMetricsLog();
+ MetricsLog* current_log = log_manager_.current_log()->AsMetricsLog();
DCHECK(current_log);
current_log->RecordIncrementalStabilityElements();
RecordCurrentHistograms();
- current_log_->CloseLog();
- pending_log_ = current_log_;
- current_log_ = NULL;
+ log_manager_.StageCurrentLogForUpload();
}
-void MetricsService::PushPendingLogsToUnsentLists() {
+void MetricsService::PushPendingLogsToPersistentStorage() {
if (state_ < INITIAL_LOG_READY)
return; // We didn't and still don't have time to get plugin list etc.
- if (pending_log()) {
- PreparePendingLogText();
+ if (log_manager_.has_staged_log()) {
if (state_ == INITIAL_LOG_READY) {
// We may race here, and send second copy of initial log later.
- unsent_initial_logs_.push_back(compressed_log_);
- state_ = SEND_OLD_INITIAL_LOGS;
+ log_manager_.StoreStagedLogAsUnsent(MetricsLogManager::INITIAL_LOG);
+ state_ = SENDING_OLD_LOGS;
} else {
// TODO(jar): Verify correctness in other states, including sending unsent
// initial logs.
- PushPendingLogTextToUnsentOngoingLogs();
+ log_manager_.StoreStagedLogAsUnsent(MetricsLogManager::ONGOING_LOG);
}
- DiscardPendingLog();
}
- DCHECK(!pending_log());
+ DCHECK(!log_manager_.has_staged_log());
StopRecording();
- PreparePendingLogText();
- PushPendingLogTextToUnsentOngoingLogs();
- DiscardPendingLog();
+ log_manager_.StoreStagedLogAsUnsent(MetricsLogManager::ONGOING_LOG);
StoreUnsentLogs();
}
-void MetricsService::PushPendingLogTextToUnsentOngoingLogs() {
- if (compressed_log_.length() >
- static_cast<size_t>(kUploadLogAvoidRetransmitSize)) {
- UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted",
- static_cast<int>(compressed_log_.length()));
- return;
- }
- unsent_ongoing_logs_.push_back(compressed_log_);
-}
-
//------------------------------------------------------------------------------
// Transmission of logs methods
@@ -998,20 +938,20 @@ void MetricsService::OnHistogramSynchronizationDone() {
return;
}
- MakePendingLog();
+ MakeStagedLog();
- // MakePendingLog should have put something in the pending log, if it didn't,
- // we skip this upload and hope things work out next time.
- if (!pending_log()) {
+ // MakeStagedLog should have prepared log text; if it didn't, skip this
+ // upload and hope things work out next time.
+ if (log_manager_.staged_log_text().empty()) {
scheduler_->UploadCancelled();
return;
}
- PrepareFetchWithPendingLog();
+ PrepareFetchWithStagedLog();
if (!current_fetch_.get()) {
// Compression failed, and log discarded :-/.
- DiscardPendingLog();
+ log_manager_.DiscardStagedLog();
scheduler_->UploadCancelled();
// TODO(jar): If compression failed, we should have created a tiny log and
// compressed that, so that we can signal that we're losing logs.
@@ -1027,8 +967,8 @@ void MetricsService::OnHistogramSynchronizationDone() {
}
-void MetricsService::MakePendingLog() {
- if (pending_log())
+void MetricsService::MakeStagedLog() {
+ if (log_manager_.has_staged_log())
return;
switch (state_) {
@@ -1043,23 +983,13 @@ void MetricsService::MakePendingLog() {
// from us.
PrepareInitialLog();
DCHECK(state_ == INIT_TASK_DONE);
- RecallUnsentLogs();
+ log_manager_.LoadPersistedUnsentLogs();
state_ = INITIAL_LOG_READY;
break;
- case SEND_OLD_INITIAL_LOGS:
- if (!unsent_initial_logs_.empty()) {
- compressed_log_ = unsent_initial_logs_.back();
- unsent_initial_logs_.pop_back();
- break;
- }
- state_ = SENDING_OLD_LOGS;
- // Fall through.
-
case SENDING_OLD_LOGS:
- if (!unsent_ongoing_logs_.empty()) {
- compressed_log_ = unsent_ongoing_logs_.back();
- unsent_ongoing_logs_.pop_back();
+ if (log_manager_.has_unsent_logs()) {
+ log_manager_.StageNextStoredLogForUpload();
break;
}
state_ = SENDING_CURRENT_LOGS;
@@ -1075,7 +1005,7 @@ void MetricsService::MakePendingLog() {
return;
}
- DCHECK(pending_log());
+ DCHECK(log_manager_.has_staged_log());
}
void MetricsService::PrepareInitialLog() {
@@ -1085,187 +1015,35 @@ void MetricsService::PrepareInitialLog() {
log->set_hardware_class(hardware_class_); // Adds to initial log.
log->RecordEnvironment(plugins_, profile_dictionary_.get());
- // Histograms only get written to current_log_, so setup for the write.
- MetricsLogBase* save_log = current_log_;
- current_log_ = log;
- RecordCurrentHistograms(); // Into current_log_... which is really log.
- current_log_ = save_log;
-
- log->CloseLog();
- DCHECK(!pending_log());
- pending_log_ = log;
-}
-
-// static
-MetricsService::LogRecallStatus MetricsService::RecallUnsentLogsHelper(
- const ListValue& list,
- std::vector<std::string>* local_list) {
- DCHECK(local_list->empty());
- 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);
-
- base::MD5Context ctx;
- base::MD5Init(&ctx);
- std::string encoded_log;
- std::string decoded_log;
- for (ListValue::const_iterator it = list.begin() + 1;
- it != list.end() - 1; ++it) { // Last element is the checksum.
- valid = (*it)->GetAsString(&encoded_log);
- if (!valid) {
- local_list->clear();
- return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION);
- }
-
- base::MD5Update(&ctx, encoded_log);
-
- if (!base::Base64Decode(encoded_log, &decoded_log)) {
- local_list->clear();
- return MakeRecallStatusHistogram(DECODE_FAIL);
- }
- local_list->push_back(decoded_log);
- }
-
- // 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);
-}
-void MetricsService::RecallUnsentLogs() {
- PrefService* local_state = g_browser_process->local_state();
- DCHECK(local_state);
-
- const ListValue* unsent_initial_logs = local_state->GetList(
- prefs::kMetricsInitialLogs);
- RecallUnsentLogsHelper(*unsent_initial_logs, &unsent_initial_logs_);
-
- const ListValue* unsent_ongoing_logs = local_state->GetList(
- prefs::kMetricsOngoingLogs);
- RecallUnsentLogsHelper(*unsent_ongoing_logs, &unsent_ongoing_logs_);
-}
-
-// static
-void MetricsService::StoreUnsentLogsHelper(
- const std::vector<std::string>& local_list,
- const size_t kMaxLocalListSize,
- ListValue* list) {
- list->Clear();
- size_t start = 0;
- if (local_list.size() > kMaxLocalListSize)
- start = local_list.size() - kMaxLocalListSize;
- DCHECK(start <= local_list.size());
- if (local_list.size() == start)
- return;
-
- // Store size at the beginning of the list.
- list->Append(Value::CreateIntegerValue(local_list.size() - start));
-
- base::MD5Context ctx;
- base::MD5Init(&ctx);
- std::string encoded_log;
- for (std::vector<std::string>::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.
- if (!base::Base64Encode(*it, &encoded_log)) {
- MakeStoreStatusHistogram(ENCODE_FAIL);
- list->Clear();
- return;
- }
- base::MD5Update(&ctx, encoded_log);
- list->Append(Value::CreateStringValue(encoded_log));
- }
+ // Histograms only get written to the current log, so make the new log current
+ // before writing them.
+ log_manager_.PauseCurrentLog();
+ log_manager_.BeginLoggingWithLog(log);
+ RecordCurrentHistograms();
- // Append hash to the end of the list.
- base::MD5Digest digest;
- base::MD5Final(&digest, &ctx);
- list->Append(Value::CreateStringValue(base::MD5DigestToBase16(digest)));
- DCHECK(list->GetSize() >= 3); // Minimum of 3 elements (size, data, hash).
- MakeStoreStatusHistogram(STORE_SUCCESS);
+ DCHECK(!log_manager_.has_staged_log());
+ log_manager_.StageCurrentLogForUpload();
+ log_manager_.ResumePausedLog();
}
void MetricsService::StoreUnsentLogs() {
if (state_ < INITIAL_LOG_READY)
return; // We never Recalled the prior unsent logs.
- PrefService* local_state = g_browser_process->local_state();
- DCHECK(local_state);
-
- {
- ListPrefUpdate update(local_state, prefs::kMetricsInitialLogs);
- ListValue* unsent_initial_logs = update.Get();
- StoreUnsentLogsHelper(unsent_initial_logs_, kMaxInitialLogsPersisted,
- unsent_initial_logs);
- }
-
- {
- ListPrefUpdate update(local_state, prefs::kMetricsOngoingLogs);
- ListValue* unsent_ongoing_logs = update.Get();
- StoreUnsentLogsHelper(unsent_ongoing_logs_, kMaxOngoingLogsPersisted,
- unsent_ongoing_logs);
- }
-}
-
-void MetricsService::PreparePendingLogText() {
- DCHECK(pending_log());
- if (!compressed_log_.empty())
- return;
- int text_size = pending_log_->GetEncodedLogSize();
-
- std::string pending_log_text;
- // Leave room for the NULL terminator.
- pending_log_->GetEncodedLog(WriteInto(&pending_log_text, text_size + 1),
- text_size);
-
- if (Bzip2Compress(pending_log_text, &compressed_log_)) {
- // Allow security conscious users to see all metrics logs that we send.
- VLOG(1) << "COMPRESSED FOLLOWING METRICS LOG: " << pending_log_text;
- } else {
- LOG(DFATAL) << "Failed to compress log for transmission.";
- // We can't discard the logs as other caller functions expect that
- // |compressed_log_| not be empty. We can detect this failure at the server
- // after we transmit.
- compressed_log_ = "Unable to compress!";
- MakeStoreStatusHistogram(COMPRESS_FAIL);
- return;
- }
+ log_manager_.PersistUnsentLogs();
}
-void MetricsService::PrepareFetchWithPendingLog() {
- DCHECK(pending_log());
+void MetricsService::PrepareFetchWithStagedLog() {
+ DCHECK(!log_manager_.staged_log_text().empty());
DCHECK(!current_fetch_.get());
- PreparePendingLogText();
- DCHECK(!compressed_log_.empty());
current_fetch_.reset(new URLFetcher(GURL(WideToUTF16(server_url_)),
URLFetcher::POST,
this));
current_fetch_->set_request_context(
g_browser_process->system_request_context());
- current_fetch_->set_upload_data(kMetricsType, compressed_log_);
+ current_fetch_->set_upload_data(kMetricsType,
+ log_manager_.staged_log_text());
}
static const char* StatusToString(const net::URLRequestStatus& status) {
@@ -1312,10 +1090,11 @@ void MetricsService::OnURLFetchComplete(const URLFetcher* source,
bool discard_log = false;
if (!upload_succeeded &&
- (compressed_log_.length() >
+ (log_manager_.staged_log_text().length() >
static_cast<size_t>(kUploadLogAvoidRetransmitSize))) {
- UMA_HISTOGRAM_COUNTS("UMA.Large Rejected Log was Discarded",
- static_cast<int>(compressed_log_.length()));
+ UMA_HISTOGRAM_COUNTS(
+ "UMA.Large Rejected Log was Discarded",
+ static_cast<int>(log_manager_.staged_log_text().length()));
discard_log = true;
} else if (response_code == 400) {
// Bad syntax. Retransmission won't work.
@@ -1331,10 +1110,9 @@ void MetricsService::OnURLFetchComplete(const URLFetcher* source,
VLOG(1) << "METRICS RESPONSE DATA: " << data;
switch (state_) {
case INITIAL_LOG_READY:
- state_ = SEND_OLD_INITIAL_LOGS;
+ state_ = SENDING_OLD_LOGS;
break;
- case SEND_OLD_INITIAL_LOGS:
case SENDING_OLD_LOGS:
// Store the updated list to disk now that the removed log is uploaded.
StoreUnsentLogs();
@@ -1348,14 +1126,14 @@ void MetricsService::OnURLFetchComplete(const URLFetcher* source,
break;
}
- DiscardPendingLog();
+ log_manager_.DiscardStagedLog();
// Since we sent a log, make sure our in-memory state is recorded to disk.
PrefService* local_state = g_browser_process->local_state();
DCHECK(local_state);
if (local_state)
local_state->ScheduleSavePersistentPrefs();
- if (unsent_logs())
+ if (log_manager_.has_unsent_logs())
DCHECK(state_ < SENDING_CURRENT_LOGS);
}
@@ -1363,7 +1141,8 @@ void MetricsService::OnURLFetchComplete(const URLFetcher* source,
// don't consider that a sign that the server is in trouble.
bool server_is_healthy = upload_succeeded || response_code == 400;
- scheduler_->UploadFinished(server_is_healthy, unsent_logs());
+ scheduler_->UploadFinished(server_is_healthy,
+ log_manager_.has_unsent_logs());
// Collect network stats if UMA upload succeeded.
if (server_is_healthy && io_thread_)
@@ -1373,11 +1152,11 @@ void MetricsService::OnURLFetchComplete(const URLFetcher* source,
void MetricsService::LogBadResponseCode() {
VLOG(1) << "Verify your metrics logs are formatted correctly. Verify server "
"is active at " << server_url_;
- if (!pending_log()) {
+ if (!log_manager_.has_staged_log()) {
VLOG(1) << "METRICS: Recorder shutdown during log transmission.";
} else {
VLOG(1) << "METRICS: transmission retry being scheduled for "
- << compressed_log_;
+ << log_manager_.staged_log_text();
}
}
@@ -1417,7 +1196,7 @@ void MetricsService::LogWindowChange(int type,
}
// TODO(brettw) we should have some kind of ID for the parent.
- current_log_->RecordWindowEvent(window_type, controller_id, 0);
+ log_manager_.current_log()->RecordWindowEvent(window_type, controller_id, 0);
}
void MetricsService::LogLoadComplete(int type,
@@ -1433,11 +1212,11 @@ void MetricsService::LogLoadComplete(int type,
const Details<LoadNotificationDetails> load_details(details);
int controller_id = window_map_[details.map_key()];
- current_log_->RecordLoadEvent(controller_id,
- load_details->url(),
- load_details->origin(),
- load_details->session_index(),
- load_details->load_time());
+ log_manager_.current_log()->RecordLoadEvent(controller_id,
+ load_details->url(),
+ load_details->origin(),
+ load_details->session_index(),
+ load_details->load_time());
}
void MetricsService::IncrementPrefValue(const char* path) {
diff --git a/chrome/browser/metrics/metrics_service.h b/chrome/browser/metrics/metrics_service.h
index b3e38f2..dc781e4 100644
--- a/chrome/browser/metrics/metrics_service.h
+++ b/chrome/browser/metrics/metrics_service.h
@@ -37,7 +37,6 @@ class TemplateURLService;
namespace base {
class DictionaryValue;
-class ListValue;
class MessageLoopProxy;
}
@@ -54,31 +53,6 @@ class MetricsService : public NotificationObserver,
public URLFetcher::Delegate,
public MetricsServiceBase {
public:
- // Used to produce a historgram that keeps track of the status of recalling
- // persisted per logs.
- enum LogRecallStatus {
- 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.
- END_RECALL_STATUS // Number of bins to use to create the histogram.
- };
-
- // TODO(ziadh): This is here temporarily for a side experiment. Remove later
- // on.
- enum LogStoreStatus {
- STORE_SUCCESS, // Successfully presisted log.
- ENCODE_FAIL, // Failed to encode log.
- COMPRESS_FAIL, // Failed to compress log.
- END_STORE_STATUS // Number of bins to use to create the histogram.
- };
-
MetricsService();
virtual ~MetricsService();
@@ -156,7 +130,6 @@ class MetricsService : public NotificationObserver,
INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to complete.
INIT_TASK_DONE, // Waiting for timer to send initial log.
INITIAL_LOG_READY, // Initial log generated, and waiting for reply.
- SEND_OLD_INITIAL_LOGS, // Sending unsent logs from previous session.
SENDING_OLD_LOGS, // Sending unsent logs from previous session.
SENDING_CURRENT_LOGS, // Sending standard current logs as they acrue.
};
@@ -221,17 +194,12 @@ class MetricsService : public NotificationObserver,
void StartRecording();
// Called to stop recording user experience metrics.
- // Adds any last information to current_log_ and then moves it to pending_log_
- // for upload.
+ // Adds any last information to current_log_ and then stages it for upload.
void StopRecording();
- // Deletes pending_log_ and current_log_, and pushes their text into the
- // appropriate unsent_log vectors. Called when Chrome shuts down.
- void PushPendingLogsToUnsentLists();
-
- // Save the pending_log_text_ persistently in a pref for transmission when we
- // next run. Note that IF this text is "too large," we just dicard it.
- void PushPendingLogTextToUnsentOngoingLogs();
+ // Pushes the text of the current and staged logs into persistent storage.
+ // Called when Chrome shuts down.
+ void PushPendingLogsToPersistentStorage();
// Ensures that scheduler is running, assuming the current settings are such
// that metrics should be reported. If not, this is a no-op.
@@ -247,34 +215,17 @@ class MetricsService : public NotificationObserver,
void OnHistogramSynchronizationDone();
// Takes whatever log should be uploaded next (according to the state_)
- // and makes it the pending log. If pending_log_ is not NULL,
- // MakePendingLog does nothing and returns.
- void MakePendingLog();
-
- // Check to see if there are any unsent logs from previous sessions.
- bool unsent_logs() const {
- return !unsent_initial_logs_.empty() || !unsent_ongoing_logs_.empty();
- }
+ // and makes it the staged log. If there is already a staged log, this is a
+ // no-op.
+ void MakeStagedLog();
+
// Record stats, client ID, Session ID, etc. in a special "first" log.
void PrepareInitialLog();
- // Pull copies of unsent logs from prefs into instance variables.
- void RecallUnsentLogs();
- // Decode and verify written pref log data.
- static MetricsService::LogRecallStatus RecallUnsentLogsHelper(
- const base::ListValue& list,
- std::vector<std::string>* local_list);
- // Encode and write list size and checksum for perf log data.
- static void StoreUnsentLogsHelper(const std::vector<std::string>& local_list,
- const size_t kMaxLocalListSize,
- base::ListValue* list);
- // Convert |pending_log_| to XML in |compressed_log_|, and compress it for
- // transmission.
- void PreparePendingLogText();
-
- // Convert pending_log_ to XML, compress it, and prepare to pass to server.
- // Upon return, current_fetch_ should be reset with its upload data set to
- // a compressed copy of the pending log.
- void PrepareFetchWithPendingLog();
+
+ // Prepared the staged log to be passed to the server. Upon return,
+ // current_fetch_ should be reset with its upload data set to a compressed
+ // copy of the staged log.
+ void PrepareFetchWithStagedLog();
// Implementation of URLFetcher::Delegate. Called after transmission
// completes (either successfully or with failure).
@@ -403,16 +354,6 @@ class MetricsService : public NotificationObserver,
// A number that identifies the how many times the app has been launched.
int session_id_;
- // When logs were not sent during a previous session they are queued to be
- // sent instead of currently accumulating logs. We give preference to sending
- // our inital log first, then unsent intial logs, then unsent ongoing logs.
- // Unsent logs are gathered at shutdown, and save in a persistent pref, one
- // log in each string in the following arrays.
- // 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<std::string> unsent_initial_logs_;
- std::vector<std::string> unsent_ongoing_logs_;
-
// Maps NavigationControllers (corresponding to tabs) or Browser
// (corresponding to Windows) to a unique integer that we will use to identify
// it. |next_window_id_| is used to track which IDs we have used so far.
@@ -448,13 +389,6 @@ class MetricsService : public NotificationObserver,
// exited-cleanly bit in the prefs.
static ShutdownCleanliness clean_shutdown_status_;
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, EmptyLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, SingleElementLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, OverLimitLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, SmallRecoveredListSize);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, RemoveSizeFromLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, CorruptSizeOfLogList);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, CorruptChecksumOfLogList);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ClientIdGeneratesAllZeroes);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ClientIdGeneratesCorrectly);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ClientIdCorrectlyFormatted);
diff --git a/chrome/browser/metrics/metrics_service_unittest.cc b/chrome/browser/metrics/metrics_service_unittest.cc
index 2ddb2f8..01263de 100644
--- a/chrome/browser/metrics/metrics_service_unittest.cc
+++ b/chrome/browser/metrics/metrics_service_unittest.cc
@@ -5,19 +5,14 @@
#include "chrome/browser/metrics/metrics_service.h"
#include <string>
-#include <vector>
#include "base/base64.h"
-#include "base/md5.h"
-#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
class MetricsServiceTest : public ::testing::Test {
};
-static const size_t kMaxLocalListSize = 3;
-
// Ensure the ClientId is formatted as expected.
TEST(MetricsServiceTest, ClientIdCorrectlyFormatted) {
std::string clientid = MetricsService::GenerateClientID();
@@ -33,171 +28,3 @@ TEST(MetricsServiceTest, ClientIdCorrectlyFormatted) {
}
}
-// Store and retrieve empty list.
-TEST(MetricsServiceTest, EmptyLogList) {
- ListValue list;
- std::vector<std::string> local_list;
-
- MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list);
- EXPECT_EQ(0U, list.GetSize());
-
- local_list.clear(); // RecallUnsentLogsHelper() expects empty |local_list|.
- EXPECT_EQ(MetricsService::LIST_EMPTY,
- MetricsService::RecallUnsentLogsHelper(list, &local_list));
- EXPECT_EQ(0U, local_list.size());
-}
-
-// Store and retrieve a single log value.
-TEST(MetricsServiceTest, SingleElementLogList) {
- ListValue list;
- std::vector<std::string> local_list;
-
- local_list.push_back("Hello world!");
- EXPECT_EQ(1U, local_list.size());
-
- MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list);
-
- // |list| will now contain the following:
- // [1, Base64Encode("Hello world!"), MD5("Hello world!")].
- EXPECT_EQ(3U, list.GetSize());
-
- // Examine each element.
- 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(MetricsService::RECALL_SUCCESS,
- MetricsService::RecallUnsentLogsHelper(list, &local_list));
- EXPECT_EQ(1U, local_list.size());
-}
-
-// Store elements greater than the limit.
-TEST(MetricsServiceTest, OverLimitLogList) {
- ListValue list;
- std::vector<std::string> local_list;
-
- local_list.push_back("one");
- local_list.push_back("two");
- local_list.push_back("three");
- local_list.push_back("four");
- EXPECT_EQ(4U, local_list.size());
-
- std::string expected_first;
- base::Base64Encode(local_list[local_list.size() - kMaxLocalListSize],
- &expected_first);
- std::string expected_last;
- base::Base64Encode(local_list[local_list.size() - 1],
- &expected_last);
-
- MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list);
- EXPECT_EQ(kMaxLocalListSize + 2, list.GetSize());
-
- std::string actual_first;
- EXPECT_TRUE((*(list.begin() + 1))->GetAsString(&actual_first));
- EXPECT_TRUE(expected_first == actual_first);
-
- std::string actual_last;
- EXPECT_TRUE((*(list.end() - 2))->GetAsString(&actual_last));
- EXPECT_TRUE(expected_last == actual_last);
-
- local_list.clear();
- EXPECT_EQ(MetricsService::RECALL_SUCCESS,
- MetricsService::RecallUnsentLogsHelper(list, &local_list));
- EXPECT_EQ(kMaxLocalListSize, local_list.size());
-}
-
-// Induce LIST_SIZE_TOO_SMALL corruption
-TEST(MetricsServiceTest, SmallRecoveredListSize) {
- ListValue list;
- std::vector<std::string> local_list;
-
- local_list.push_back("Hello world!");
- EXPECT_EQ(1U, local_list.size());
- MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &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(MetricsService::LIST_SIZE_TOO_SMALL,
- MetricsService::RecallUnsentLogsHelper(list, &local_list));
-}
-
-// Remove size from the stored list.
-TEST(MetricsServiceTest, RemoveSizeFromLogList) {
- ListValue list;
- std::vector<std::string> local_list;
-
- local_list.push_back("one");
- local_list.push_back("two");
- EXPECT_EQ(2U, local_list.size());
- MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list);
- EXPECT_EQ(4U, list.GetSize());
-
- list.Remove(0, NULL); // Delete size (1st element).
- EXPECT_EQ(3U, list.GetSize());
-
- local_list.clear();
- EXPECT_EQ(MetricsService::LIST_SIZE_MISSING,
- MetricsService::RecallUnsentLogsHelper(list, &local_list));
-}
-
-// Corrupt size of stored list.
-TEST(MetricsServiceTest, CorruptSizeOfLogList) {
- ListValue list;
- std::vector<std::string> local_list;
-
- local_list.push_back("Hello world!");
- EXPECT_EQ(1U, local_list.size());
- MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list);
- EXPECT_EQ(3U, list.GetSize());
-
- // Change list size from 1 to 2.
- EXPECT_TRUE(list.Set(0, Value::CreateIntegerValue(2)));
- EXPECT_EQ(3U, list.GetSize());
-
- local_list.clear();
- EXPECT_EQ(MetricsService::LIST_SIZE_CORRUPTION,
- MetricsService::RecallUnsentLogsHelper(list, &local_list));
-}
-
-// Corrupt checksum of stored list.
-TEST(MetricsServiceTest, CorruptChecksumOfLogList) {
- ListValue list;
- std::vector<std::string> local_list;
-
- local_list.clear();
- local_list.push_back("Hello world!");
- EXPECT_EQ(1U, local_list.size());
- MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &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, Value::CreateStringValue(checksum)));
- EXPECT_EQ(3U, list.GetSize());
-
- local_list.clear();
- EXPECT_EQ(MetricsService::CHECKSUM_CORRUPTION,
- MetricsService::RecallUnsentLogsHelper(list, &local_list));
-}
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 4b58b51..cc3b1a3 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1493,6 +1493,8 @@
'browser/metrics/metric_event_duration_details.h',
'browser/metrics/metrics_log.cc',
'browser/metrics/metrics_log.h',
+ 'browser/metrics/metrics_log_serializer.cc',
+ 'browser/metrics/metrics_log_serializer.h',
'browser/metrics/metrics_reporting_scheduler.cc',
'browser/metrics/metrics_reporting_scheduler.h',
'browser/metrics/metrics_response.cc',
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index d3d8496..0f15ea6 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -172,6 +172,8 @@
'common/mac/objc_zombie.mm',
'common/metrics_helpers.cc',
'common/metrics_helpers.h',
+ 'common/metrics_log_manager.cc',
+ 'common/metrics_log_manager.h',
'common/multi_process_lock.h',
'common/multi_process_lock_linux.cc',
'common/multi_process_lock_mac.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index b89b478..4b859e1 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1323,6 +1323,7 @@
'browser/media/media_internals_unittest.cc',
'browser/metrics/display_utils_unittest.cc',
'browser/metrics/metrics_log_unittest.cc',
+ 'browser/metrics/metrics_log_serializer_unittest.cc',
'browser/metrics/metrics_response_unittest.cc',
'browser/metrics/metrics_service_unittest.cc',
'browser/metrics/thread_watcher_unittest.cc',
@@ -1791,6 +1792,7 @@
'common/json_schema_validator_unittest_base.cc',
'common/json_schema_validator_unittest_base.h',
'common/json_value_serializer_unittest.cc',
+ 'common/metrics_log_manager_unittest.cc',
'common/multi_process_lock_unittest.cc',
'common/mac/objc_method_swizzle_unittest.mm',
'common/mac/objc_zombie_unittest.mm',
diff --git a/chrome/common/metrics_helpers.cc b/chrome/common/metrics_helpers.cc
index 028d0b3..e5e32cd 100644
--- a/chrome/common/metrics_helpers.cc
+++ b/chrome/common/metrics_helpers.cc
@@ -4,12 +4,6 @@
#include "chrome/common/metrics_helpers.h"
-#if defined(USE_SYSTEM_LIBBZ2)
-#include <bzlib.h>
-#else
-#include "third_party/bzip2/bzlib.h"
-#endif
-
#include "base/base64.h"
#include "base/basictypes.h"
#include "base/file_util.h"
@@ -21,6 +15,7 @@
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/common/logging_chrome.h"
+#include "chrome/common/metrics_log_manager.h"
#include "googleurl/src/gurl.h"
#include "libxml/xmlwriter.h"
@@ -416,79 +411,21 @@ void MetricsLogBase::RecordHistogramDelta(
// MetricsServiceBase
-MetricsServiceBase::MetricsServiceBase()
- : pending_log_(NULL),
- compressed_log_(),
- current_log_(NULL) {
+MetricsServiceBase::MetricsServiceBase() {
}
MetricsServiceBase::~MetricsServiceBase() {
- if (pending_log_) {
- delete pending_log_;
- pending_log_ = NULL;
- }
- if (current_log_) {
- delete current_log_;
- current_log_ = NULL;
- }
-}
-
-// This implementation is based on the Firefox MetricsService implementation.
-bool MetricsServiceBase::Bzip2Compress(const std::string& input,
- std::string* output) {
- bz_stream stream = {0};
- // As long as our input is smaller than the bzip2 block size, we should get
- // the best compression. For example, if your input was 250k, using a block
- // size of 300k or 500k should result in the same compression ratio. Since
- // our data should be under 100k, using the minimum block size of 100k should
- // allocate less temporary memory, but result in the same compression ratio.
- int result = BZ2_bzCompressInit(&stream,
- 1, // 100k (min) block size
- 0, // quiet
- 0); // default "work factor"
- if (result != BZ_OK) { // out of memory?
- return false;
- }
-
- output->clear();
-
- stream.next_in = const_cast<char*>(input.data());
- stream.avail_in = static_cast<int>(input.size());
- // NOTE: we don't need a BZ_RUN phase since our input buffer contains
- // the entire input
- do {
- output->resize(output->size() + 1024);
- stream.next_out = &((*output)[stream.total_out_lo32]);
- stream.avail_out = static_cast<int>(output->size()) - stream.total_out_lo32;
- result = BZ2_bzCompress(&stream, BZ_FINISH);
- } while (result == BZ_FINISH_OK);
- if (result != BZ_STREAM_END) // unknown failure?
- return false;
- result = BZ2_bzCompressEnd(&stream);
- DCHECK(result == BZ_OK);
-
- output->resize(stream.total_out_lo32);
-
- return true;
-}
-
-void MetricsServiceBase::DiscardPendingLog() {
- if (pending_log_) { // Shutdown might have deleted it!
- delete pending_log_;
- pending_log_ = NULL;
- }
- compressed_log_.clear();
}
void MetricsServiceBase::RecordCurrentHistograms() {
- DCHECK(current_log_);
+ DCHECK(log_manager_.current_log());
TransmitAllHistograms(base::Histogram::kNoFlags, true);
}
void MetricsServiceBase::TransmitHistogramDelta(
const base::Histogram& histogram,
const base::Histogram::SampleSet& snapshot) {
- current_log_->RecordHistogramDelta(histogram, snapshot);
+ log_manager_.current_log()->RecordHistogramDelta(histogram, snapshot);
}
void MetricsServiceBase::InconsistencyDetected(int problem) {
diff --git a/chrome/common/metrics_helpers.h b/chrome/common/metrics_helpers.h
index 68777dc..373d551 100644
--- a/chrome/common/metrics_helpers.h
+++ b/chrome/common/metrics_helpers.h
@@ -16,6 +16,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
#include "base/time.h"
+#include "chrome/common/metrics_log_manager.h"
#include "content/common/page_transition_types.h"
class GURL;
@@ -232,33 +233,12 @@ class MetricsServiceBase : public HistogramSender {
MetricsServiceBase();
virtual ~MetricsServiceBase();
- // Check to see if there is a log that needs to be, or is being, transmitted.
- bool pending_log() const {
- return pending_log_ || !compressed_log_.empty();
- }
-
- // Compress the report log in |input| using bzip2, store the result in
- // |output|.
- bool Bzip2Compress(const std::string& input, std::string* output);
-
- // Discard |pending_log_|, and clear |compressed_log_|. Called after
- // processing of this log is complete.
- void DiscardPendingLog();
-
// Record complete list of histograms into the current log.
// Called when we close a log.
void RecordCurrentHistograms();
- // A log that we are currently transmiting, or about to try to transmit.
- MetricsLogBase* pending_log_;
-
- // An alternate form of |pending_log_|. We persistently save this version
- // into prefs if we can't transmit it. As a result, sometimes all we have is
- // the compressed text version.
- std::string compressed_log_;
-
- // The log that we are still appending to.
- MetricsLogBase* current_log_;
+ // Manager for the various in-flight logs.
+ MetricsLogManager log_manager_;
private:
// HistogramSender interface (override) methods.
diff --git a/chrome/common/metrics_log_manager.cc b/chrome/common/metrics_log_manager.cc
new file mode 100644
index 0000000..572e62a
--- /dev/null
+++ b/chrome/common/metrics_log_manager.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2011 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/common/metrics_log_manager.h"
+
+#if defined(USE_SYSTEM_LIBBZ2)
+#include <bzlib.h>
+#else
+#include "third_party/bzip2/bzlib.h"
+#endif
+
+#include "base/metrics/histogram.h"
+#include "base/string_util.h"
+#include "chrome/common/metrics_helpers.h"
+
+MetricsLogManager::MetricsLogManager() : max_ongoing_log_store_size_(0) {}
+
+MetricsLogManager::~MetricsLogManager() {}
+
+void MetricsLogManager::BeginLoggingWithLog(MetricsLogBase* log) {
+ DCHECK(!current_log_.get());
+ current_log_.reset(log);
+}
+
+void MetricsLogManager::StageCurrentLogForUpload() {
+ DCHECK(current_log_.get());
+ current_log_->CloseLog();
+ staged_log_.reset(current_log_.release());
+ CompressStagedLog();
+}
+
+bool MetricsLogManager::has_staged_log() const {
+ return staged_log_.get() || !compressed_staged_log_text_.empty();
+}
+
+void MetricsLogManager::DiscardStagedLog() {
+ staged_log_.reset();
+ compressed_staged_log_text_.clear();
+}
+
+void MetricsLogManager::DiscardCurrentLog() {
+ current_log_->CloseLog();
+ current_log_.reset();
+}
+
+void MetricsLogManager::PauseCurrentLog() {
+ DCHECK(!paused_log_.get());
+ paused_log_.reset(current_log_.release());
+}
+
+void MetricsLogManager::ResumePausedLog() {
+ DCHECK(!current_log_.get());
+ current_log_.reset(paused_log_.release());
+}
+
+void MetricsLogManager::StoreStagedLogAsUnsent(LogType log_type) {
+ DCHECK(staged_log_.get());
+ // If compressing the log failed, there's nothing to store.
+ if (compressed_staged_log_text_.empty())
+ return;
+
+ if (log_type == INITIAL_LOG) {
+ unsent_initial_logs_.push_back(compressed_staged_log_text_);
+ } else {
+ // If it's too large, just note that and discard it.
+ if (max_ongoing_log_store_size_ &&
+ compressed_staged_log_text_.length() > max_ongoing_log_store_size_) {
+ UMA_HISTOGRAM_COUNTS(
+ "UMA.Large Accumulated Log Not Persisted",
+ static_cast<int>(compressed_staged_log_text_.length()));
+ } else {
+ unsent_ongoing_logs_.push_back(compressed_staged_log_text_);
+ }
+ }
+ DiscardStagedLog();
+}
+
+void MetricsLogManager::StageNextStoredLogForUpload() {
+ // Prioritize initial logs for uploading.
+ std::vector<std::string>* source_list = unsent_initial_logs_.empty() ?
+ &unsent_ongoing_logs_ : &unsent_initial_logs_;
+ DCHECK(!source_list->empty());
+ DCHECK(compressed_staged_log_text_.empty());
+ compressed_staged_log_text_ = source_list->back();
+ source_list->pop_back();
+}
+
+void MetricsLogManager::PersistUnsentLogs() {
+ DCHECK(log_serializer_.get());
+ if (!log_serializer_.get())
+ return;
+ log_serializer_->SerializeLogs(unsent_initial_logs_, INITIAL_LOG);
+ log_serializer_->SerializeLogs(unsent_ongoing_logs_, ONGOING_LOG);
+}
+
+void MetricsLogManager::LoadPersistedUnsentLogs() {
+ DCHECK(log_serializer_.get());
+ if (!log_serializer_.get())
+ return;
+ log_serializer_->DeserializeLogs(INITIAL_LOG, &unsent_initial_logs_);
+ log_serializer_->DeserializeLogs(ONGOING_LOG, &unsent_ongoing_logs_);
+}
+
+void MetricsLogManager::CompressStagedLog() {
+ int text_size = staged_log_->GetEncodedLogSize();
+ std::string staged_log_text;
+ // Leave room for the NULL terminator.
+ staged_log_->GetEncodedLog(WriteInto(&staged_log_text, text_size + 1),
+ text_size);
+
+ bool success = Bzip2Compress(staged_log_text, &compressed_staged_log_text_);
+ if (success) {
+ // Allow security-conscious users to see all metrics logs that we send.
+ VLOG(1) << "METRICS LOG: " << staged_log_text;
+ } else {
+ LOG(DFATAL) << "Failed to compress log for transmission.";
+ }
+}
+
+// static
+// This implementation is based on the Firefox MetricsService implementation.
+bool MetricsLogManager::Bzip2Compress(const std::string& input,
+ std::string* output) {
+ bz_stream stream = {0};
+ // As long as our input is smaller than the bzip2 block size, we should get
+ // the best compression. For example, if your input was 250k, using a block
+ // size of 300k or 500k should result in the same compression ratio. Since
+ // our data should be under 100k, using the minimum block size of 100k should
+ // allocate less temporary memory, but result in the same compression ratio.
+ int result = BZ2_bzCompressInit(&stream,
+ 1, // 100k (min) block size
+ 0, // quiet
+ 0); // default "work factor"
+ if (result != BZ_OK) { // out of memory?
+ return false;
+ }
+
+ output->clear();
+
+ stream.next_in = const_cast<char*>(input.data());
+ stream.avail_in = static_cast<int>(input.size());
+ // NOTE: we don't need a BZ_RUN phase since our input buffer contains
+ // the entire input
+ do {
+ output->resize(output->size() + 1024);
+ stream.next_out = &((*output)[stream.total_out_lo32]);
+ stream.avail_out = static_cast<int>(output->size()) - stream.total_out_lo32;
+ result = BZ2_bzCompress(&stream, BZ_FINISH);
+ } while (result == BZ_FINISH_OK);
+ if (result != BZ_STREAM_END) { // unknown failure?
+ output->clear();
+ // TODO(jar): See if it would be better to do a CHECK() here.
+ return false;
+ }
+ result = BZ2_bzCompressEnd(&stream);
+ DCHECK(result == BZ_OK);
+
+ output->resize(stream.total_out_lo32);
+
+ return true;
+}
diff --git a/chrome/common/metrics_log_manager.h b/chrome/common/metrics_log_manager.h
new file mode 100644
index 0000000..c843d6e
--- /dev/null
+++ b/chrome/common/metrics_log_manager.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2011 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_COMMON_METRICS_LOG_MANAGER_H_
+#define CHROME_COMMON_METRICS_LOG_MANAGER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+#include <string>
+#include <vector>
+
+class MetricsLogBase;
+
+// Manages all the log objects used by a MetricsService implementation. Keeps
+// track of both an in progress log and a log that is staged for uploading as
+// text, as well as saving logs to, and loading logs from, persistent storage.
+class MetricsLogManager {
+ public:
+ MetricsLogManager();
+ ~MetricsLogManager();
+
+ // 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);
+
+ // Returns the in-progress log.
+ MetricsLogBase* current_log() { return current_log_.get(); }
+
+ // Closes |current_log| and stages it for upload, leaving |current_log| NULL.
+ void StageCurrentLogForUpload();
+
+ // Returns true if there is a log that needs to be, or is being, uploaded.
+ // Note that this returns true even if compressing the log text failed.
+ bool has_staged_log() const;
+
+ // The compressed text of the staged log. Empty if there is no staged log,
+ // or if compression of the staged log failed.
+ const std::string& staged_log_text() { return compressed_staged_log_text_; }
+
+ // Discards the staged log.
+ void DiscardStagedLog();
+
+ // Closes and discards |current_log|.
+ void DiscardCurrentLog();
+
+ // Sets current_log to NULL, but saves the current log for future use with
+ // ResumePausedLog(). Only one log may be paused at a time.
+ // TODO(stuartmorgan): Pause/resume support is really a workaround for a
+ // design issue in initial log writing; that should be fixed, and pause/resume
+ // removed.
+ void PauseCurrentLog();
+
+ // Restores the previously paused log (if any) to current_log.
+ // This should only be called if there is not a current log.
+ void ResumePausedLog();
+
+ // Returns true if there are any logs left over from previous sessions that
+ // need to be uploaded.
+ bool has_unsent_logs() const {
+ return !unsent_initial_logs_.empty() || !unsent_ongoing_logs_.empty();
+ }
+
+ enum LogType {
+ INITIAL_LOG, // The first log of a session.
+ ONGOING_LOG, // Subsequent logs in a session.
+ };
+
+ // Saves the staged log as the given type (or discards it in accordance with
+ // set_max_ongoing_log_store_size), then clears the staged log.
+ // This can only be called after StageCurrentLogForUpload.
+ void StoreStagedLogAsUnsent(LogType log_type);
+
+ // Populates staged_log_text with the next stored log to send.
+ void StageNextStoredLogForUpload();
+
+ // Sets the threshold for how large an onging log can be and still be stored.
+ // Ongoing logs larger than this will be discarded. 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<std::string>& 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<std::string>* 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.
+ void PersistUnsentLogs();
+
+ // Loads any unsent logs from persistent storage using the current log
+ // serializer. Can only be called after set_log_serializer.
+ void LoadPersistedUnsentLogs();
+
+ private:
+ // Compresses staged_log_ and stores the result in
+ // compressed_staged_log_text_.
+ void CompressStagedLog();
+
+ // Compresses the text in |input| using bzip2, store the result in |output|.
+ static bool Bzip2Compress(const std::string& input, std::string* output);
+
+ // The log that we are still appending to.
+ scoped_ptr<MetricsLogBase> current_log_;
+
+ // A paused, previously-current log.
+ scoped_ptr<MetricsLogBase> paused_log_;
+
+ // The log that we are currently transmiting, or about to try to transmit.
+ // Note that when using StageNextStoredLogForUpload, this can be NULL while
+ // compressed_staged_log_text_ is non-NULL.
+ scoped_ptr<MetricsLogBase> staged_log_;
+
+ // Helper class to handle serialization/deserialization of logs for persistent
+ // storage. May be NULL.
+ scoped_ptr<LogSerializer> log_serializer_;
+
+ // The compressed text of the staged log, ready for upload to the server.
+ std::string compressed_staged_log_text_;
+
+ // 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<std::string> unsent_initial_logs_;
+ std::vector<std::string> unsent_ongoing_logs_;
+
+ size_t max_ongoing_log_store_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricsLogManager);
+};
+
+#endif // CHROME_COMMON_METRICS_LOG_MANAGER_H_
diff --git a/chrome/common/metrics_log_manager_unittest.cc b/chrome/common/metrics_log_manager_unittest.cc
new file mode 100644
index 0000000..a7c894e
--- /dev/null
+++ b/chrome/common/metrics_log_manager_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2011 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/common/metrics_helpers.h"
+#include "chrome/common/metrics_log_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include <string>
+#include <vector>
+
+namespace {
+class MetricsLogManagerTest : public testing::Test {
+};
+
+// Dummy serializer that just stores logs in memory.
+class DummyLogSerializer : public MetricsLogManager::LogSerializer {
+ public:
+ virtual void SerializeLogs(const std::vector<std::string>& logs,
+ MetricsLogManager::LogType log_type) {
+ persisted_logs_[log_type] = logs;
+ }
+
+ virtual void DeserializeLogs(MetricsLogManager::LogType log_type,
+ std::vector<std::string>* logs) {
+ 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();
+ }
+
+ // In-memory "persitent storage".
+ std::vector<std::string> persisted_logs_[2];
+};
+}; // namespace
+
+TEST(MetricsLogManagerTest, StandardFlow) {
+ MetricsLogManager log_manager;
+
+ // Make sure a new manager has a clean slate.
+ EXPECT_EQ(NULL, log_manager.current_log());
+ EXPECT_FALSE(log_manager.has_staged_log());
+ EXPECT_FALSE(log_manager.has_unsent_logs());
+
+ // Check that the normal flow works.
+ MetricsLogBase* initial_log = new MetricsLogBase("id", 0, "version");
+ log_manager.BeginLoggingWithLog(initial_log);
+ EXPECT_EQ(initial_log, log_manager.current_log());
+ EXPECT_FALSE(log_manager.has_staged_log());
+
+ log_manager.StageCurrentLogForUpload();
+ EXPECT_EQ(NULL, log_manager.current_log());
+ EXPECT_TRUE(log_manager.has_staged_log());
+ EXPECT_FALSE(log_manager.staged_log_text().empty());
+
+ MetricsLogBase* second_log = new MetricsLogBase("id", 0, "version");
+ log_manager.BeginLoggingWithLog(second_log);
+ EXPECT_EQ(second_log, log_manager.current_log());
+ EXPECT_TRUE(log_manager.has_staged_log());
+
+ log_manager.DiscardStagedLog();
+ EXPECT_EQ(second_log, log_manager.current_log());
+ EXPECT_FALSE(log_manager.has_staged_log());
+ EXPECT_TRUE(log_manager.staged_log_text().empty());
+
+ EXPECT_FALSE(log_manager.has_unsent_logs());
+}
+
+TEST(MetricsLogManagerTest, AbandonedLog) {
+ MetricsLogManager log_manager;
+
+ MetricsLogBase* dummy_log = new MetricsLogBase("id", 0, "version");
+ log_manager.BeginLoggingWithLog(dummy_log);
+ EXPECT_EQ(dummy_log, log_manager.current_log());
+
+ log_manager.DiscardCurrentLog();
+ EXPECT_EQ(NULL, log_manager.current_log());
+ EXPECT_FALSE(log_manager.has_staged_log());
+}
+
+TEST(MetricsLogManagerTest, InterjectedLog) {
+ MetricsLogManager log_manager;
+
+ MetricsLogBase* ongoing_log = new MetricsLogBase("id", 0, "version");
+ MetricsLogBase* temp_log = new MetricsLogBase("id", 0, "version");
+
+ log_manager.BeginLoggingWithLog(ongoing_log);
+ EXPECT_EQ(ongoing_log, log_manager.current_log());
+
+ log_manager.PauseCurrentLog();
+ EXPECT_EQ(NULL, log_manager.current_log());
+
+ log_manager.BeginLoggingWithLog(temp_log);
+ EXPECT_EQ(temp_log, log_manager.current_log());
+ log_manager.StageCurrentLogForUpload();
+ EXPECT_TRUE(log_manager.has_staged_log());
+ log_manager.DiscardStagedLog();
+ EXPECT_EQ(NULL, log_manager.current_log());
+ EXPECT_FALSE(log_manager.has_staged_log());
+
+ log_manager.ResumePausedLog();
+ EXPECT_EQ(ongoing_log, log_manager.current_log());
+ EXPECT_FALSE(log_manager.has_staged_log());
+
+ EXPECT_FALSE(log_manager.has_unsent_logs());
+}
+
+TEST(MetricsLogManagerTest, StoreAndLoad) {
+ std::vector<std::string> initial_logs;
+ std::vector<std::string> ongoing_logs;
+
+ // 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);
+ // Simulate a log having already been unsent from a previous session.
+ serializer->persisted_logs_[MetricsLogManager::ONGOING_LOG].push_back("a");
+ EXPECT_FALSE(log_manager.has_unsent_logs());
+ log_manager.LoadPersistedUnsentLogs();
+ EXPECT_TRUE(log_manager.has_unsent_logs());
+
+ MetricsLogBase* log1 = new MetricsLogBase("id", 0, "version");
+ MetricsLogBase* log2 = new MetricsLogBase("id", 0, "version");
+ log_manager.BeginLoggingWithLog(log1);
+ log_manager.StageCurrentLogForUpload();
+ log_manager.BeginLoggingWithLog(log2);
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::INITIAL_LOG);
+ log_manager.StageCurrentLogForUpload();
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::ONGOING_LOG);
+
+ // Nothing should be written out until PersistUnsentLogs is called.
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
+ EXPECT_EQ(2U, serializer->TypeCount(MetricsLogManager::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_[MetricsLogManager::INITIAL_LOG];
+ ongoing_logs = serializer->persisted_logs_[MetricsLogManager::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_[MetricsLogManager::INITIAL_LOG] = initial_logs;
+ serializer->persisted_logs_[MetricsLogManager::ONGOING_LOG] = ongoing_logs;
+
+ log_manager.set_log_serializer(serializer);
+ log_manager.LoadPersistedUnsentLogs();
+ EXPECT_TRUE(log_manager.has_unsent_logs());
+
+ log_manager.StageNextStoredLogForUpload();
+ log_manager.DiscardStagedLog();
+ // The initial log should be sent first; update the persisted storage to
+ // verify.
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
+ EXPECT_EQ(2U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
+
+ // Handle the first ongoing log.
+ log_manager.StageNextStoredLogForUpload();
+ log_manager.DiscardStagedLog();
+ EXPECT_TRUE(log_manager.has_unsent_logs());
+
+ // Handle the last log.
+ log_manager.StageNextStoredLogForUpload();
+ log_manager.DiscardStagedLog();
+ EXPECT_FALSE(log_manager.has_unsent_logs());
+
+ // Nothing should have changed "on disk" since PersistUnsentLogs hasn't been
+ // called again.
+ EXPECT_EQ(2U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
+ // Persist, and make sure nothing is left.
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
+ }
+}
+
+TEST(MetricsLogManagerTest, LargeLogDiscarding) {
+ MetricsLogManager log_manager;
+ DummyLogSerializer* serializer = new DummyLogSerializer;
+ log_manager.set_log_serializer(serializer);
+ // Set the size threshold very low, to verify that it's honored.
+ log_manager.set_max_ongoing_log_store_size(1);
+
+ MetricsLogBase* log1 = new MetricsLogBase("id", 0, "version");
+ MetricsLogBase* log2 = new MetricsLogBase("id", 0, "version");
+ log_manager.BeginLoggingWithLog(log1);
+ log_manager.StageCurrentLogForUpload();
+ log_manager.BeginLoggingWithLog(log2);
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::INITIAL_LOG);
+ log_manager.StageCurrentLogForUpload();
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::ONGOING_LOG);
+
+ // Only the ongoing log should be written out, due to the threshold.
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
+}
diff --git a/chrome_frame/metrics_service.cc b/chrome_frame/metrics_service.cc
index ee7d393..9af220b 100644
--- a/chrome_frame/metrics_service.cc
+++ b/chrome_frame/metrics_service.cc
@@ -57,6 +57,7 @@
#include "base/utf_string_conversions.h"
#include "base/win/scoped_comptr.h"
#include "chrome/common/chrome_version_info.h"
+#include "chrome/common/metrics_log_manager.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/google_update_settings.h"
#include "chrome_frame/bind_status_callback_impl.h"
@@ -250,14 +251,6 @@ MetricsService::MetricsService()
MetricsService::~MetricsService() {
SetRecording(false);
- if (pending_log_) {
- delete pending_log_;
- pending_log_ = NULL;
- }
- if (current_log_) {
- delete current_log_;
- current_log_ = NULL;
- }
}
void MetricsService::InitializeMetricsState() {
@@ -369,36 +362,36 @@ void MetricsService::SetReporting(bool enable) {
void MetricsService::StartRecording() {
DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
- if (current_log_)
+ if (log_manager_.current_log())
return;
- current_log_ = new MetricsLogBase(client_id_, session_id_,
- GetVersionString());
+ log_manager_.BeginLoggingWithLog(new MetricsLogBase(client_id_, session_id_,
+ GetVersionString()));
if (state_ == INITIALIZED)
state_ = ACTIVE;
}
void MetricsService::StopRecording(bool save_log) {
DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
- if (!current_log_)
+ if (!log_manager_.current_log())
return;
// Put incremental histogram deltas at the end of all log transmissions.
- // Don't bother if we're going to discard current_log_.
+ // Don't bother if we're going to discard current_log.
if (save_log) {
CrashMetricsReporter::GetInstance()->RecordCrashMetrics();
RecordCurrentHistograms();
}
- if (save_log) {
- pending_log_ = current_log_;
- }
- current_log_ = NULL;
+ if (save_log)
+ log_manager_.StageCurrentLogForUpload();
+ else
+ log_manager_.DiscardCurrentLog();
}
void MetricsService::MakePendingLog() {
DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
- if (pending_log())
+ if (log_manager_.has_staged_log())
return;
switch (state_) {
@@ -416,7 +409,7 @@ void MetricsService::MakePendingLog() {
return;
}
- DCHECK(pending_log());
+ DCHECK(log_manager_.has_staged_log());
}
bool MetricsService::TransmissionPermitted() const {
@@ -425,22 +418,6 @@ bool MetricsService::TransmissionPermitted() const {
return user_permits_upload_;
}
-std::string MetricsService::PrepareLogSubmissionString() {
- DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
-
- MakePendingLog();
- DCHECK(pending_log());
- if (pending_log_== NULL) {
- return std::string();
- }
-
- pending_log_->CloseLog();
- std::string pending_log_text = pending_log_->GetEncodedLogString();
- DCHECK(!pending_log_text.empty());
- DiscardPendingLog();
- return pending_log_text;
-}
-
bool MetricsService::UploadData() {
DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
@@ -453,23 +430,20 @@ bool MetricsService::UploadData() {
return false;
}
- std::string pending_log_text = PrepareLogSubmissionString();
- DCHECK(!pending_log_text.empty());
-
- // Allow security conscious users to see all metrics logs that we send.
- VLOG(1) << "METRICS LOG: " << pending_log_text;
+ MakePendingLog();
+ DCHECK(log_manager_.has_staged_log());
bool ret = true;
- if (!Bzip2Compress(pending_log_text, &compressed_log_)) {
+ if (log_manager_.staged_log_text().empty()) {
NOTREACHED() << "Failed to compress log for transmission.";
ret = false;
} else {
HRESULT hr = ChromeFrameMetricsDataUploader::UploadDataHelper(
- compressed_log_);
+ log_manager_.staged_log_text());
DCHECK(SUCCEEDED(hr));
}
- DiscardPendingLog();
+ log_manager_.DiscardStagedLog();
currently_uploading = 0;
return ret;
diff --git a/chrome_frame/metrics_service.h b/chrome_frame/metrics_service.h
index 7b5f84f..e29ad85 100644
--- a/chrome_frame/metrics_service.h
+++ b/chrome_frame/metrics_service.h
@@ -103,9 +103,6 @@ class MetricsService : public MetricsServiceBase {
return reporting_active_;
}
- // Convert pending_log_ to XML in pending_log_text_ for transmission.
- std::string PrepareLogSubmissionString();
-
// Upload pending data to the server by converting it to XML and compressing
// it. Returns true on success.
bool UploadData();