summaryrefslogtreecommitdiffstats
path: root/google_apis/gcm
diff options
context:
space:
mode:
authorjuyik@chromium.org <juyik@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-16 21:41:44 +0000
committerjuyik@chromium.org <juyik@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-16 21:41:44 +0000
commit268d23f13de7d41e2dc42b90205fbd86a66676a0 (patch)
tree445afa9ef278cd1462bdab4eb13dd12c555dbe09 /google_apis/gcm
parente888193914dc13c6961a64c810852fd0d6268afb (diff)
downloadchromium_src-268d23f13de7d41e2dc42b90205fbd86a66676a0.zip
chromium_src-268d23f13de7d41e2dc42b90205fbd86a66676a0.tar.gz
chromium_src-268d23f13de7d41e2dc42b90205fbd86a66676a0.tar.bz2
Add activity recording capability to gcm internals page. User can refresh, start/stop recording, and clear recording logs.
Also added more information to the device info section and fixed a bug there. arv: owner review of chrome/browser/resources/*. jianli & fgorski please review the rest of the code, and zea for owner review of these code. BUG=341256 Review URL: https://codereview.chromium.org/202083005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@264313 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'google_apis/gcm')
-rw-r--r--google_apis/gcm/engine/checkin_request.h1
-rw-r--r--google_apis/gcm/engine/gcm_store.h9
-rw-r--r--google_apis/gcm/engine/mcs_client.cc25
-rw-r--r--google_apis/gcm/engine/mcs_client.h24
-rw-r--r--google_apis/gcm/engine/mcs_client_unittest.cc11
-rw-r--r--google_apis/gcm/gcm.gyp5
-rw-r--r--google_apis/gcm/gcm_client.cc7
-rw-r--r--google_apis/gcm/gcm_client.h13
-rw-r--r--google_apis/gcm/gcm_client_impl.cc36
-rw-r--r--google_apis/gcm/gcm_client_impl.h14
-rw-r--r--google_apis/gcm/gcm_client_impl_unittest.cc18
-rw-r--r--google_apis/gcm/monitoring/gcm_stats_recorder.cc150
-rw-r--r--google_apis/gcm/monitoring/gcm_stats_recorder.h101
-rw-r--r--google_apis/gcm/monitoring/gcm_stats_recorder_unittest.cc136
14 files changed, 522 insertions, 28 deletions
diff --git a/google_apis/gcm/engine/checkin_request.h b/google_apis/gcm/engine/checkin_request.h
index 1d706a7..5ae8dd3 100644
--- a/google_apis/gcm/engine/checkin_request.h
+++ b/google_apis/gcm/engine/checkin_request.h
@@ -12,6 +12,7 @@
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "google_apis/gcm/base/gcm_export.h"
+#include "google_apis/gcm/protocol/android_checkin.pb.h"
#include "google_apis/gcm/protocol/checkin.pb.h"
#include "net/base/backoff_entry.h"
#include "net/url_request/url_fetcher_delegate.h"
diff --git a/google_apis/gcm/engine/gcm_store.h b/google_apis/gcm/engine/gcm_store.h
index 1b15e56b..8b9891d0 100644
--- a/google_apis/gcm/engine/gcm_store.h
+++ b/google_apis/gcm/engine/gcm_store.h
@@ -9,6 +9,8 @@
#include <string>
#include <vector>
+#include <google/protobuf/message_lite.h>
+
#include "base/basictypes.h"
#include "base/callback_forward.h"
#include "base/memory/linked_ptr.h"
@@ -17,13 +19,6 @@
#include "base/time/time.h"
#include "google_apis/gcm/base/gcm_export.h"
#include "google_apis/gcm/engine/registration_info.h"
-#include "google_apis/gcm/protocol/mcs.pb.h"
-
-namespace google {
-namespace protobuf {
-class MessageLite;
-} // namespace protobuf
-} // namespace google
namespace gcm {
diff --git a/google_apis/gcm/engine/mcs_client.cc b/google_apis/gcm/engine/mcs_client.cc
index da8300c7..a99a333 100644
--- a/google_apis/gcm/engine/mcs_client.cc
+++ b/google_apis/gcm/engine/mcs_client.cc
@@ -15,6 +15,7 @@
#include "google_apis/gcm/base/mcs_util.h"
#include "google_apis/gcm/base/socket_stream.h"
#include "google_apis/gcm/engine/connection_factory.h"
+#include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
using namespace google::protobuf::io;
@@ -128,6 +129,14 @@ ReliablePacketInfo::ReliablePacketInfo()
}
ReliablePacketInfo::~ReliablePacketInfo() {}
+int MCSClient::GetSendQueueSize() const {
+ return to_send_.size();
+}
+
+int MCSClient::GetResendQueueSize() const {
+ return to_resend_.size();
+}
+
std::string MCSClient::GetStateString() const {
switch(state_) {
case UNINITIALIZED:
@@ -147,7 +156,8 @@ std::string MCSClient::GetStateString() const {
MCSClient::MCSClient(const std::string& version_string,
base::Clock* clock,
ConnectionFactory* connection_factory,
- GCMStore* gcm_store)
+ GCMStore* gcm_store,
+ GCMStatsRecorder* recorder)
: version_string_(version_string),
clock_(clock),
state_(UNINITIALIZED),
@@ -160,6 +170,7 @@ MCSClient::MCSClient(const std::string& version_string,
stream_id_out_(0),
stream_id_in_(0),
gcm_store_(gcm_store),
+ recorder_(recorder),
weak_ptr_factory_(this) {
}
@@ -495,6 +506,11 @@ void MCSClient::SendPacketToWire(ReliablePacketInfo* packet_info) {
base::Time::kMicrosecondsPerSecond) - sent;
DVLOG(1) << "Message was queued for " << queued << " seconds.";
data_message->set_queued(queued);
+ recorder_->RecordDataSentToWire(
+ data_message->category(),
+ data_message->to(),
+ data_message->id(),
+ queued);
}
// Set the proper last received stream id to acknowledge received server
@@ -862,6 +878,13 @@ void MCSClient::NotifyMessageSendStatus(
const mcs_proto::DataMessageStanza* data_message_stanza =
reinterpret_cast<const mcs_proto::DataMessageStanza*>(&protobuf);
+ recorder_->RecordNotifySendStatus(
+ data_message_stanza->category(),
+ data_message_stanza->to(),
+ data_message_stanza->id(),
+ status,
+ protobuf.ByteSize(),
+ data_message_stanza->ttl());
message_sent_callback_.Run(
data_message_stanza->device_user_id(),
data_message_stanza->category(),
diff --git a/google_apis/gcm/engine/mcs_client.h b/google_apis/gcm/engine/mcs_client.h
index 1943be6..cc915e4 100644
--- a/google_apis/gcm/engine/mcs_client.h
+++ b/google_apis/gcm/engine/mcs_client.h
@@ -37,6 +37,7 @@ namespace gcm {
class CollapseKey;
class ConnectionFactory;
+class GCMStatsRecorder;
struct ReliablePacketInfo;
// An MCS client. This client is in charge of all communications with an
@@ -54,6 +55,8 @@ class GCM_EXPORT MCSClient {
CONNECTED, // Connected and running.
};
+ // Any change made to this enum should have corresponding change in the
+ // GetMessageSendStatusString(...) function in mcs_client.cc.
enum MessageSendStatus {
// Message was queued succcessfully.
QUEUED,
@@ -61,14 +64,19 @@ class GCM_EXPORT MCSClient {
SENT,
// Message not saved, because total queue size limit reached.
QUEUE_SIZE_LIMIT_REACHED,
- // Messgae not saved, because app queue size limit reached.
+ // Message not saved, because app queue size limit reached.
APP_QUEUE_SIZE_LIMIT_REACHED,
// Message too large to send.
MESSAGE_TOO_LARGE,
// Message not send becuase of TTL = 0 and no working connection.
NO_CONNECTION_ON_ZERO_TTL,
// Message exceeded TTL.
- TTL_EXCEEDED
+ TTL_EXCEEDED,
+
+ // NOTE: always keep this entry at the end. Add new status types only
+ // immediately above this line. Make sure to update the corresponding
+ // histogram enum accordingly.
+ SEND_STATUS_COUNT
};
// Callback for MCSClient's error conditions.
@@ -89,7 +97,8 @@ class GCM_EXPORT MCSClient {
MCSClient(const std::string& version_string,
base::Clock* clock,
ConnectionFactory* connection_factory,
- GCMStore* gcm_store);
+ GCMStore* gcm_store,
+ GCMStatsRecorder* recorder);
virtual ~MCSClient();
// Initialize the client. Will load any previous id/token information as well
@@ -127,6 +136,12 @@ class GCM_EXPORT MCSClient {
// Returns the current state of the client.
State state() const { return state_; }
+ // Returns the size of the send message queue.
+ int GetSendQueueSize() const;
+
+ // Returns the size of the resend messaage queue.
+ int GetResendQueueSize() const;
+
// Returns text representation of the state enum.
std::string GetStateString() const;
@@ -259,6 +274,9 @@ class GCM_EXPORT MCSClient {
// Manager to handle triggering/detecting heartbeats.
HeartbeatManager heartbeat_manager_;
+ // Recorder that records GCM activities for debugging purpose. Not owned.
+ GCMStatsRecorder* recorder_;
+
base::WeakPtrFactory<MCSClient> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(MCSClient);
diff --git a/google_apis/gcm/engine/mcs_client_unittest.cc b/google_apis/gcm/engine/mcs_client_unittest.cc
index b3e2f0d..a062136 100644
--- a/google_apis/gcm/engine/mcs_client_unittest.cc
+++ b/google_apis/gcm/engine/mcs_client_unittest.cc
@@ -15,6 +15,7 @@
#include "google_apis/gcm/engine/fake_connection_factory.h"
#include "google_apis/gcm/engine/fake_connection_handler.h"
#include "google_apis/gcm/engine/gcm_store_impl.h"
+#include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace gcm {
@@ -67,8 +68,9 @@ class TestMCSClient : public MCSClient {
public:
TestMCSClient(base::Clock* clock,
ConnectionFactory* connection_factory,
- GCMStore* gcm_store)
- : MCSClient("", clock, connection_factory, gcm_store),
+ GCMStore* gcm_store,
+ gcm::GCMStatsRecorder* recorder)
+ : MCSClient("", clock, connection_factory, gcm_store, recorder),
next_id_(0) {
}
@@ -136,6 +138,8 @@ class MCSClientTest : public testing::Test {
scoped_ptr<MCSMessage> received_message_;
std::string sent_message_id_;
MCSClient::MessageSendStatus message_send_status_;
+
+ gcm::GCMStatsRecorder recorder_;
};
MCSClientTest::MCSClientTest()
@@ -166,7 +170,8 @@ void MCSClientTest::BuildMCSClient() {
message_loop_.message_loop_proxy()));
mcs_client_.reset(new TestMCSClient(&clock_,
&connection_factory_,
- gcm_store_.get()));
+ gcm_store_.get(),
+ &recorder_));
}
void MCSClientTest::InitializeClient() {
diff --git a/google_apis/gcm/gcm.gyp b/google_apis/gcm/gcm.gyp
index 9843776..8f99174 100644
--- a/google_apis/gcm/gcm.gyp
+++ b/google_apis/gcm/gcm.gyp
@@ -72,6 +72,8 @@
'gcm_client.h',
'gcm_client_impl.cc',
'gcm_client_impl.h',
+ 'monitoring/gcm_stats_recorder.cc',
+ 'monitoring/gcm_stats_recorder.h',
'protocol/android_checkin.proto',
'protocol/checkin.proto',
'protocol/mcs.proto',
@@ -138,7 +140,8 @@
'engine/mcs_client_unittest.cc',
'engine/registration_request_unittest.cc',
'engine/unregistration_request_unittest.cc',
- 'gcm_client_impl_unittest.cc'
+ 'gcm_client_impl_unittest.cc',
+ 'monitoring/gcm_stats_recorder_unittest.cc'
]
},
],
diff --git a/google_apis/gcm/gcm_client.cc b/google_apis/gcm/gcm_client.cc
index 3bdd699..75884d6 100644
--- a/google_apis/gcm/gcm_client.cc
+++ b/google_apis/gcm/gcm_client.cc
@@ -24,7 +24,12 @@ GCMClient::SendErrorDetails::SendErrorDetails() : result(UNKNOWN_ERROR) {}
GCMClient::SendErrorDetails::~SendErrorDetails() {}
GCMClient::GCMStatistics::GCMStatistics()
- : gcm_client_created(false), connection_client_created(false) {
+ : is_recording(false),
+ gcm_client_created(false),
+ connection_client_created(false),
+ android_id(0),
+ send_queue_size(0),
+ resend_queue_size(0) {
}
GCMClient::GCMStatistics::~GCMStatistics() {
diff --git a/google_apis/gcm/gcm_client.h b/google_apis/gcm/gcm_client.h
index 16ba53b..5996c06 100644
--- a/google_apis/gcm/gcm_client.h
+++ b/google_apis/gcm/gcm_client.h
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
#include "google_apis/gcm/base/gcm_export.h"
+#include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
template <class T> class scoped_refptr;
@@ -94,11 +95,17 @@ class GCM_EXPORT GCMClient {
GCMStatistics();
~GCMStatistics();
+ bool is_recording;
bool gcm_client_created;
std::string gcm_client_state;
bool connection_client_created;
std::string connection_state;
uint64 android_id;
+ std::vector<std::string> registered_app_ids;
+ int send_queue_size;
+ int resend_queue_size;
+
+ std::vector<GCMStatsRecorder::SendingActivity> sending_activities;
};
// A delegate interface that allows the GCMClient instance to interact with
@@ -208,6 +215,12 @@ class GCM_EXPORT GCMClient {
const std::string& receiver_id,
const OutgoingMessage& message) = 0;
+ // Enables or disables internal activity recording.
+ virtual void SetRecording(bool recording) = 0;
+
+ // Clear all recorded GCM activity logs.
+ virtual void ClearActivityLogs() = 0;
+
// Gets internal states and statistics.
virtual GCMStatistics GetStatistics() const = 0;
};
diff --git a/google_apis/gcm/gcm_client_impl.cc b/google_apis/gcm/gcm_client_impl.cc
index 80c58d8..e152d63 100644
--- a/google_apis/gcm/gcm_client_impl.cc
+++ b/google_apis/gcm/gcm_client_impl.cc
@@ -12,6 +12,7 @@
#include "base/metrics/histogram.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
#include "base/time/default_clock.h"
#include "google_apis/gcm/base/mcs_message.h"
#include "google_apis/gcm/base/mcs_util.h"
@@ -19,6 +20,7 @@
#include "google_apis/gcm/engine/connection_factory_impl.h"
#include "google_apis/gcm/engine/gcm_store_impl.h"
#include "google_apis/gcm/engine/mcs_client.h"
+#include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
#include "google_apis/gcm/protocol/mcs.pb.h"
#include "net/http/http_network_session.h"
#include "net/url_request/url_request_context.h"
@@ -130,12 +132,14 @@ scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
const std::string& version,
base::Clock* clock,
ConnectionFactory* connection_factory,
- GCMStore* gcm_store) {
+ GCMStore* gcm_store,
+ GCMStatsRecorder* recorder) {
return make_scoped_ptr<MCSClient>(
new MCSClient(version,
clock,
connection_factory,
- gcm_store));
+ gcm_store,
+ recorder));
}
scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
@@ -241,7 +245,8 @@ void GCMClientImpl::InitializeMCSClient(
chrome_build_proto_.chrome_version(),
clock_.get(),
connection_factory_.get(),
- gcm_store_.get()).Pass();
+ gcm_store_.get(),
+ &recorder_).Pass();
mcs_client_->Initialize(
base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
@@ -557,16 +562,33 @@ std::string GCMClientImpl::GetStateString() const {
}
}
+void GCMClientImpl::SetRecording(bool recording) {
+ recorder_.SetRecording(recording);
+}
+
+void GCMClientImpl::ClearActivityLogs() {
+ recorder_.Clear();
+}
+
GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
GCMClient::GCMStatistics stats;
- stats.gcm_client_state = GCMClientImpl::GetStateString();
+ stats.gcm_client_created = true;
+ stats.is_recording = recorder_.is_recording();
+ stats.gcm_client_state = GetStateString();
stats.connection_client_created = mcs_client_.get() != NULL;
if (mcs_client_.get()) {
stats.connection_state = mcs_client_->GetStateString();
- // TODO(juyik): add more statistics such as message metadata list, etc.
+ stats.send_queue_size = mcs_client_->GetSendQueueSize();
+ stats.resend_queue_size = mcs_client_->GetResendQueueSize();
}
if (device_checkin_info_.android_id > 0)
stats.android_id = device_checkin_info_.android_id;
+ recorder_.CollectSendingActivities(&stats.sending_activities);
+
+ for (RegistrationInfoMap::const_iterator it = registrations_.begin();
+ it != registrations_.end(); ++it) {
+ stats.registered_app_ids.push_back(it->first);
+ }
return stats;
}
@@ -695,6 +717,10 @@ void GCMClientImpl::HandleIncomingSendError(
send_error_details.additional_data.erase(iter);
}
+ recorder_.RecordIncomingSendError(
+ data_message_stanza.category(),
+ data_message_stanza.to(),
+ data_message_stanza.id());
delegate_->OnMessageSendError(data_message_stanza.category(),
send_error_details);
}
diff --git a/google_apis/gcm/gcm_client_impl.h b/google_apis/gcm/gcm_client_impl.h
index 7458c70..c4e6068 100644
--- a/google_apis/gcm/gcm_client_impl.h
+++ b/google_apis/gcm/gcm_client_impl.h
@@ -19,6 +19,8 @@
#include "google_apis/gcm/engine/registration_request.h"
#include "google_apis/gcm/engine/unregistration_request.h"
#include "google_apis/gcm/gcm_client.h"
+#include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
+#include "google_apis/gcm/protocol/android_checkin.pb.h"
#include "google_apis/gcm/protocol/checkin.pb.h"
#include "net/base/net_log.h"
#include "net/url_request/url_request_context_getter.h"
@@ -29,6 +31,10 @@ namespace base {
class Clock;
} // namespace base
+namespace mcs_proto {
+class DataMessageStanza;
+} // namespace mcs_proto
+
namespace net {
class HttpNetworkSession;
} // namespace net
@@ -51,7 +57,8 @@ class GCM_EXPORT GCMInternalsBuilder {
const std::string& version,
base::Clock* clock,
ConnectionFactory* connection_factory,
- GCMStore* gcm_store);
+ GCMStore* gcm_store,
+ GCMStatsRecorder* recorder);
virtual scoped_ptr<ConnectionFactory> BuildConnectionFactory(
const std::vector<GURL>& endpoints,
const net::BackoffEntry::Policy& backoff_policy,
@@ -86,6 +93,8 @@ class GCM_EXPORT GCMClientImpl : public GCMClient {
virtual void Send(const std::string& app_id,
const std::string& receiver_id,
const OutgoingMessage& message) OVERRIDE;
+ virtual void SetRecording(bool recording) OVERRIDE;
+ virtual void ClearActivityLogs() OVERRIDE;
virtual GCMStatistics GetStatistics() const OVERRIDE;
private:
@@ -210,6 +219,9 @@ class GCM_EXPORT GCMClientImpl : public GCMClient {
// Builder for the GCM internals (mcs client, etc.).
scoped_ptr<GCMInternalsBuilder> internals_builder_;
+ // Recorder that logs GCM activities.
+ GCMStatsRecorder recorder_;
+
// State of the GCM Client Implementation.
State state_;
diff --git a/google_apis/gcm/gcm_client_impl_unittest.cc b/google_apis/gcm/gcm_client_impl_unittest.cc
index 709418c..7f6a937 100644
--- a/google_apis/gcm/gcm_client_impl_unittest.cc
+++ b/google_apis/gcm/gcm_client_impl_unittest.cc
@@ -14,6 +14,7 @@
#include "google_apis/gcm/base/mcs_util.h"
#include "google_apis/gcm/engine/fake_connection_factory.h"
#include "google_apis/gcm/engine/fake_connection_handler.h"
+#include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
#include "google_apis/gcm/protocol/android_checkin.pb.h"
#include "google_apis/gcm/protocol/checkin.pb.h"
#include "google_apis/gcm/protocol/mcs.pb.h"
@@ -67,7 +68,8 @@ class FakeMCSClient : public MCSClient {
public:
FakeMCSClient(base::Clock* clock,
ConnectionFactory* connection_factory,
- GCMStore* gcm_store);
+ GCMStore* gcm_store,
+ GCMStatsRecorder* recorder);
virtual ~FakeMCSClient();
virtual void Login(uint64 android_id, uint64 security_token) OVERRIDE;
virtual void SendMessage(const MCSMessage& message) OVERRIDE;
@@ -88,8 +90,9 @@ class FakeMCSClient : public MCSClient {
FakeMCSClient::FakeMCSClient(base::Clock* clock,
ConnectionFactory* connection_factory,
- GCMStore* gcm_store)
- : MCSClient("", clock, connection_factory, gcm_store),
+ GCMStore* gcm_store,
+ GCMStatsRecorder* recorder)
+ : MCSClient("", clock, connection_factory, gcm_store, recorder),
last_android_id_(0u),
last_security_token_(0u),
last_message_tag_(kNumProtoTypes) {
@@ -122,7 +125,8 @@ class FakeGCMInternalsBuilder : public GCMInternalsBuilder {
const std::string& version,
base::Clock* clock,
ConnectionFactory* connection_factory,
- GCMStore* gcm_store) OVERRIDE;
+ GCMStore* gcm_store,
+ GCMStatsRecorder* recorder) OVERRIDE;
virtual scoped_ptr<ConnectionFactory> BuildConnectionFactory(
const std::vector<GURL>& endpoints,
const net::BackoffEntry::Policy& backoff_policy,
@@ -142,10 +146,12 @@ scoped_ptr<MCSClient> FakeGCMInternalsBuilder::BuildMCSClient(
const std::string& version,
base::Clock* clock,
ConnectionFactory* connection_factory,
- GCMStore* gcm_store) {
+ GCMStore* gcm_store,
+ GCMStatsRecorder* recorder) {
return make_scoped_ptr<MCSClient>(new FakeMCSClient(clock,
connection_factory,
- gcm_store));
+ gcm_store,
+ recorder));
}
scoped_ptr<ConnectionFactory> FakeGCMInternalsBuilder::BuildConnectionFactory(
diff --git a/google_apis/gcm/monitoring/gcm_stats_recorder.cc b/google_apis/gcm/monitoring/gcm_stats_recorder.cc
new file mode 100644
index 0000000..6e0cb48
--- /dev/null
+++ b/google_apis/gcm/monitoring/gcm_stats_recorder.cc
@@ -0,0 +1,150 @@
+// 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 "google_apis/gcm/monitoring/gcm_stats_recorder.h"
+
+#include <deque>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace gcm {
+
+const uint32 MAX_LOGGED_ACTIVITY_COUNT = 100;
+
+namespace {
+
+// Insert an itme to the front of deque while maintaining the size of the deque.
+// Overflow item is discarded.
+template <typename T>
+T* InsertCircularBuffer(std::deque<T>* q, const T& item) {
+ DCHECK(q);
+ q->push_front(item);
+ if (q->size() > MAX_LOGGED_ACTIVITY_COUNT) {
+ q->pop_back();
+ }
+ return &q->front();
+}
+
+// Helper for getting string representation of the MessageSendStatus enum.
+std::string GetMessageSendStatusString(
+ gcm::MCSClient::MessageSendStatus status) {
+ switch (status) {
+ case gcm::MCSClient::QUEUED:
+ return "QUEUED";
+ case gcm::MCSClient::SENT:
+ return "SENT";
+ case gcm::MCSClient::QUEUE_SIZE_LIMIT_REACHED:
+ return "QUEUE_SIZE_LIMIT_REACHED";
+ case gcm::MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
+ return "APP_QUEUE_SIZE_LIMIT_REACHED";
+ case gcm::MCSClient::MESSAGE_TOO_LARGE:
+ return "MESSAGE_TOO_LARGE";
+ case gcm::MCSClient::NO_CONNECTION_ON_ZERO_TTL:
+ return "NO_CONNECTION_ON_ZERO_TTL";
+ case gcm::MCSClient::TTL_EXCEEDED:
+ return "TTL_EXCEEDED";
+ default:
+ NOTREACHED();
+ return "UNKNOWN";
+ }
+}
+
+} // namespace
+
+GCMStatsRecorder::Activity::Activity()
+ : time(base::Time::Now()) {
+}
+
+GCMStatsRecorder::Activity::~Activity() {
+}
+
+GCMStatsRecorder::SendingActivity::SendingActivity() {
+}
+
+GCMStatsRecorder::SendingActivity::~SendingActivity() {
+}
+
+GCMStatsRecorder::GCMStatsRecorder() : is_recording_(false) {
+}
+
+GCMStatsRecorder::~GCMStatsRecorder() {
+}
+
+void GCMStatsRecorder::SetRecording(bool recording) {
+ is_recording_ = recording;
+}
+
+void GCMStatsRecorder::Clear() {
+ sending_activities_.clear();
+}
+
+void GCMStatsRecorder::CollectSendingActivities(
+ std::vector<SendingActivity>* activities) const {
+ activities->insert(activities->begin(),
+ sending_activities_.begin(),
+ sending_activities_.end());
+}
+
+void GCMStatsRecorder::RecordSending(const std::string& app_id,
+ const std::string& receiver_id,
+ const std::string& message_id,
+ const std::string& event,
+ const std::string& details) {
+ SendingActivity data;
+ SendingActivity* inserted_data = InsertCircularBuffer(
+ &sending_activities_, data);
+ inserted_data->app_id = app_id;
+ inserted_data->receiver_id = receiver_id;
+ inserted_data->message_id = message_id;
+ inserted_data->event = event;
+ inserted_data->details = details;
+}
+
+void GCMStatsRecorder::RecordDataSentToWire(
+ const std::string& app_id,
+ const std::string& receiver_id,
+ const std::string& message_id,
+ int queued) {
+ if (is_recording_) {
+ RecordSending(app_id, receiver_id, message_id, "Data msg sent to wire",
+ base::StringPrintf("Msg queued for %d seconds", queued));
+ }
+}
+
+void GCMStatsRecorder::RecordNotifySendStatus(
+ const std::string& app_id,
+ const std::string& receiver_id,
+ const std::string& message_id,
+ gcm::MCSClient::MessageSendStatus status,
+ int byte_size,
+ int ttl) {
+ UMA_HISTOGRAM_ENUMERATION("GCM.SendMessageStatus", status,
+ gcm::MCSClient::SEND_STATUS_COUNT);
+ if (is_recording_) {
+ RecordSending(
+ app_id,
+ receiver_id,
+ message_id,
+ base::StringPrintf("SEND status: %s",
+ GetMessageSendStatusString(status).c_str()),
+ base::StringPrintf("Msg size: %d bytes, TTL: %d", byte_size, ttl));
+ }
+}
+
+void GCMStatsRecorder::RecordIncomingSendError(
+ const std::string& app_id,
+ const std::string& receiver_id,
+ const std::string& message_id) {
+ UMA_HISTOGRAM_COUNTS("GCM.IncomingSendErrors", 1);
+ if (is_recording_) {
+ RecordSending(app_id, receiver_id, message_id, "Received 'send error' msg",
+ std::string());
+ }
+}
+
+} // namespace gcm
diff --git a/google_apis/gcm/monitoring/gcm_stats_recorder.h b/google_apis/gcm/monitoring/gcm_stats_recorder.h
new file mode 100644
index 0000000..71b6c6a
--- /dev/null
+++ b/google_apis/gcm/monitoring/gcm_stats_recorder.h
@@ -0,0 +1,101 @@
+// 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 GOOGLE_APIS_GCM_GCM_STATS_RECORDER_H_
+#define GOOGLE_APIS_GCM_GCM_STATS_RECORDER_H_
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+#include "google_apis/gcm/base/gcm_export.h"
+#include "google_apis/gcm/engine/mcs_client.h"
+
+namespace gcm {
+
+// Records GCM internal stats and activities for debugging purpose. Recording
+// can be turned on/off by calling SetRecording(...) function. It is turned off
+// by default.
+// This class is not thread safe. It is meant to be owned by a gcm client
+// instance.
+class GCM_EXPORT GCMStatsRecorder {
+ public:
+ // Contains data that are common to all activity kinds below.
+ struct GCM_EXPORT Activity {
+ Activity();
+ virtual ~Activity();
+
+ base::Time time;
+ std::string event; // A short description of the event.
+ std::string details; // Any additional detail about the event.
+ };
+
+ // Contains relevant data of a send-message step.
+ struct GCM_EXPORT SendingActivity : Activity {
+ SendingActivity();
+ virtual ~SendingActivity();
+
+ std::string app_id;
+ std::string receiver_id;
+ std::string message_id;
+ };
+
+ GCMStatsRecorder();
+ virtual ~GCMStatsRecorder();
+
+ // Indicates whether the recorder is currently recording activities or not.
+ bool is_recording() const {
+ return is_recording_;
+ }
+
+ // Turns recording on/off.
+ void SetRecording(bool recording);
+
+ // Clear all recorded activities.
+ void Clear();
+
+ // Records that an outgoing data message was sent over the wire.
+ void RecordDataSentToWire(const std::string& app_id,
+ const std::string& receiver_id,
+ const std::string& message_id,
+ int queued);
+ // Records that the MCS client sent a 'send status' notification to callback.
+ void RecordNotifySendStatus(const std::string& app_id,
+ const std::string& receiver_id,
+ const std::string& message_id,
+ MCSClient::MessageSendStatus status,
+ int byte_size,
+ int ttl);
+ // Records that a 'send error' message was received.
+ void RecordIncomingSendError(const std::string& app_id,
+ const std::string& receiver_id,
+ const std::string& message_id);
+
+ // Records that a sending activity has occurred. It will be inserted to the
+ // front of a queue ao that entries in the queue had reverse chronological
+ // order.
+ void CollectSendingActivities(std::vector<SendingActivity>* activities) const;
+
+ const std::deque<SendingActivity>& sending_activities() const {
+ return sending_activities_;
+ }
+
+ protected:
+ void RecordSending(const std::string& app_id,
+ const std::string& receiver_id,
+ const std::string& message_id,
+ const std::string& event,
+ const std::string& details);
+
+ bool is_recording_;
+
+ std::deque<SendingActivity> sending_activities_;
+
+ DISALLOW_COPY_AND_ASSIGN(GCMStatsRecorder);
+};
+
+} // namespace gcm
+
+#endif // GOOGLE_APIS_GCM_GCM_STATS_RECORDER_H_
diff --git a/google_apis/gcm/monitoring/gcm_stats_recorder_unittest.cc b/google_apis/gcm/monitoring/gcm_stats_recorder_unittest.cc
new file mode 100644
index 0000000..488b31b
--- /dev/null
+++ b/google_apis/gcm/monitoring/gcm_stats_recorder_unittest.cc
@@ -0,0 +1,136 @@
+// 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 "google_apis/gcm/monitoring/gcm_stats_recorder.h"
+
+#include <deque>
+#include <string>
+
+#include "google_apis/gcm/engine/mcs_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gcm {
+
+namespace {
+
+static const char kAppId[] = "app id 1";
+static const char kReceiverId[] = "receiver 1";
+static const char kMessageId[] = "message id 1";
+static const int kQueuedSec = 5;
+static const gcm::MCSClient::MessageSendStatus kMessageSendStatus =
+ gcm::MCSClient::QUEUED;
+static const int kByteSize = 99;
+static const int kTTL = 7;
+
+static const char kDataSentToWireEvent[] = "Data msg sent to wire";
+static const char kSentToWireDetails[] = "Msg queued for 5 seconds";
+static const char kNotifySendStatusEvent[] = "SEND status: QUEUED";
+static const char kNotifySendStatusDetails[] = "Msg size: 99 bytes, TTL: 7";
+static const char kIncomingSendErrorEvent[] = "Received 'send error' msg";
+static const char kIncomingSendErrorDetails[] = "";
+
+} // namespace
+
+class GCMStatsRecorderTest : public testing::Test {
+ public:
+ GCMStatsRecorderTest();
+ virtual ~GCMStatsRecorderTest();
+ virtual void SetUp() OVERRIDE;
+
+ void VerifyRecordedSendingCount(int expected_count) {
+ EXPECT_EQ(expected_count,
+ static_cast<int>(recorder_.sending_activities().size()));
+ }
+
+ void VerifyDataSentToWire(const std::string& remark){
+ VerifyData(recorder_.sending_activities(),
+ kDataSentToWireEvent,
+ kSentToWireDetails,
+ remark);
+ }
+
+ void VerifyNotifySendStatus(const std::string& remark){
+ VerifyData(recorder_.sending_activities(),
+ kNotifySendStatusEvent,
+ kNotifySendStatusDetails,
+ remark);
+ }
+
+ void VerifyIncomingSendError(const std::string& remark){
+ VerifyData(recorder_.sending_activities(),
+ kIncomingSendErrorEvent,
+ kIncomingSendErrorDetails,
+ remark);
+ }
+
+ protected:
+ template <typename T>
+ void VerifyData(const std::deque<T>& queue, const std::string& event,
+ const std::string& details, const std::string& remark) {
+ EXPECT_EQ(kAppId, queue.front().app_id) << remark;
+ EXPECT_EQ(kReceiverId, queue.front().receiver_id) << remark;
+ EXPECT_EQ(kMessageId, queue.front().message_id) << remark;
+ EXPECT_EQ(event, queue.front().event) << remark;
+ EXPECT_EQ(details, queue.front().details) << remark;
+ }
+
+ GCMStatsRecorder recorder_;
+};
+
+GCMStatsRecorderTest::GCMStatsRecorderTest(){
+}
+
+GCMStatsRecorderTest::~GCMStatsRecorderTest() {}
+
+void GCMStatsRecorderTest::SetUp(){
+ recorder_.SetRecording(true);
+}
+
+TEST_F(GCMStatsRecorderTest, StartStopRecordingTest) {
+ EXPECT_TRUE(recorder_.is_recording());
+ recorder_.RecordDataSentToWire(kAppId, kReceiverId, kMessageId, kQueuedSec);
+ VerifyRecordedSendingCount(1);
+ VerifyDataSentToWire("1st call");
+
+ recorder_.SetRecording(false);
+ EXPECT_FALSE(recorder_.is_recording());
+ recorder_.RecordDataSentToWire(kAppId, kReceiverId, kMessageId, kQueuedSec);
+ VerifyRecordedSendingCount(1);
+ VerifyDataSentToWire("2nd call");
+}
+
+TEST_F(GCMStatsRecorderTest, ClearLogTest) {
+ recorder_.RecordDataSentToWire(kAppId, kReceiverId, kMessageId, kQueuedSec);
+ VerifyRecordedSendingCount(1);
+ VerifyDataSentToWire("1st call");
+
+ recorder_.RecordNotifySendStatus(kAppId, kReceiverId, kMessageId,
+ kMessageSendStatus, kByteSize, kTTL);
+ VerifyRecordedSendingCount(2);
+ VerifyNotifySendStatus("2nd call");
+
+ recorder_.Clear();
+ VerifyRecordedSendingCount(0);
+}
+
+TEST_F(GCMStatsRecorderTest, RecordSendingTest) {
+ recorder_.RecordDataSentToWire(kAppId, kReceiverId, kMessageId, kQueuedSec);
+ VerifyRecordedSendingCount(1);
+ VerifyDataSentToWire("1st call");
+
+ recorder_.RecordNotifySendStatus(kAppId, kReceiverId, kMessageId,
+ kMessageSendStatus, kByteSize, kTTL);
+ VerifyRecordedSendingCount(2);
+ VerifyNotifySendStatus("2nd call");
+
+ recorder_.RecordIncomingSendError(kAppId, kReceiverId, kMessageId);
+ VerifyRecordedSendingCount(3);
+ VerifyIncomingSendError("3rd call");
+
+ recorder_.RecordDataSentToWire(kAppId, kReceiverId, kMessageId, kQueuedSec);
+ VerifyRecordedSendingCount(4);
+ VerifyDataSentToWire("4th call");
+}
+
+} // namespace gcm